Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move the model traits into their own fj::models module #934

Merged
merged 3 commits into from
Aug 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions crates/fj-host/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -382,16 +382,16 @@ pub enum Error {

/// Initializing a model failed.
#[error("Unable to initialize the model")]
InitializeModel(#[source] Box<dyn std::error::Error + Send + Sync>),
InitializeModel(#[source] fj::models::Error),

/// The user forgot to register a model when calling
/// [`fj::register_model!()`].
#[error("No model was registered")]
NoModelRegistered,

/// An error was returned from [`fj::Model::shape()`].
/// An error was returned from [`fj::models::Model::shape()`].
#[error("Unable to determine the model's geometry")]
Shape(#[source] Box<dyn std::error::Error + Send + Sync>),
Shape(#[source] fj::models::Error),

/// Error while watching the model code for changes
#[error("Error watching model for changes")]
Expand Down Expand Up @@ -422,16 +422,16 @@ pub enum Error {

struct Host<'a> {
args: &'a Parameters,
model: Option<Box<dyn fj::Model>>,
model: Option<Box<dyn fj::models::Model>>,
}

impl<'a> fj::Host for Host<'a> {
fn register_boxed_model(&mut self, model: Box<dyn fj::Model>) {
impl<'a> fj::models::Host for Host<'a> {
fn register_boxed_model(&mut self, model: Box<dyn fj::models::Model>) {
self.model = Some(model);
}
}

impl<'a> fj::Context for Host<'a> {
impl<'a> fj::models::Context for Host<'a> {
fn get_argument(&self, name: &str) -> Option<&str> {
self.args.get(name).map(|s| s.as_str())
}
Expand Down
16 changes: 8 additions & 8 deletions crates/fj-proc/src/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ impl Initializer {
quote! {
const _: () = {
fj::register_model!(|host| {
fj::HostExt::register_model(host, Model);
fj::models::HostExt::register_model(host, Model);

Ok(
fj::Metadata::new(env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"))
fj::models::Metadata::new(env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"))
.with_short_description(env!("CARGO_PKG_DESCRIPTION"))
.with_homepage(env!("CARGO_PKG_HOMEPAGE"))
.with_repository(env!("CARGO_PKG_REPOSITORY"))
Expand Down Expand Up @@ -44,7 +44,7 @@ impl Model {
let Model { metadata, geometry } = self;

quote! {
impl fj::Model for Model {
impl fj::models::Model for Model {
#metadata
#geometry
}
Expand All @@ -64,8 +64,8 @@ impl ToTokens for Metadata {
let Metadata { name, arguments } = self;

tokens.extend(quote! {
fn metadata(&self) -> fj::ModelMetadata {
fj::ModelMetadata::new(#name)
fn metadata(&self) -> fj::models::ModelMetadata {
fj::models::ModelMetadata::new(#name)
#( .with_argument(#arguments) )*
}
});
Expand All @@ -79,7 +79,7 @@ impl ToTokens for ArgumentMetadata {
default_value,
} = self;

tokens.extend(quote! { fj::ArgumentMetadata::new(#name) });
tokens.extend(quote! { fj::models::ArgumentMetadata::new(#name) });

if let Some(default_value) = default_value {
tokens.extend(quote! {
Expand Down Expand Up @@ -112,8 +112,8 @@ impl ToTokens for GeometryFunction {
tokens.extend(quote! {
fn shape(
&self,
ctx: &dyn fj::Context,
) -> Result<fj::Shape, Box<dyn std::error::Error + Send + Sync>> {
ctx: &dyn fj::models::Context,
) -> Result<fj::Shape, fj::models::Error> {
#( #arguments )*
#( #constraints )*
#invocation
Expand Down
14 changes: 14 additions & 0 deletions crates/fj-proc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,20 @@ use syn::{parse_macro_input, FnArg, ItemFn};
/// spacer.into()
/// }
/// ```
///
/// For more complex situations, model functions are allowed to return any
/// error type that converts into a model error.
///
/// ```rust
/// #[fj::model]
/// pub fn model() -> Result<fj::Shape, std::env::VarError> {
/// let home_dir = std::env::var("HOME")?;
///
/// todo!("Do something with {home_dir}")
/// }
///
/// fn assert_convertible(e: std::env::VarError) -> fj::models::Error { e.into() }
/// ```
#[proc_macro_attribute]
pub fn model(_: TokenStream, input: TokenStream) -> TokenStream {
let item = parse_macro_input!(input as syn::ItemFn);
Expand Down
11 changes: 6 additions & 5 deletions crates/fj/src/abi/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ pub struct Context<'a> {
_lifetime: PhantomData<&'a ()>,
}

impl<'a> From<&'a &dyn crate::Context> for Context<'a> {
fn from(ctx: &'a &dyn crate::Context) -> Self {
impl<'a> From<&'a &dyn crate::models::Context> for Context<'a> {
fn from(ctx: &'a &dyn crate::models::Context) -> Self {
unsafe extern "C" fn get_argument(
user_data: *const c_void,
name: StringSlice,
) -> StringSlice {
let ctx = &*(user_data as *const &dyn crate::Context);
let ctx = &*(user_data as *const &dyn crate::models::Context);

match std::panic::catch_unwind(AssertUnwindSafe(|| {
ctx.get_argument(&*name)
Expand All @@ -28,14 +28,15 @@ impl<'a> From<&'a &dyn crate::Context> for Context<'a> {
}

Context {
user_data: ctx as *const &dyn crate::Context as *const c_void,
user_data: ctx as *const &dyn crate::models::Context
as *const c_void,
get_argument,
_lifetime: PhantomData,
}
}
}

impl crate::Context for Context<'_> {
impl crate::models::Context for Context<'_> {
fn get_argument(&self, name: &str) -> Option<&str> {
unsafe {
let Context {
Expand Down
6 changes: 4 additions & 2 deletions crates/fj/src/abi/ffi_safe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ use std::{
ptr::NonNull,
};

use crate::models::Error;

/// A FFI-safe version of `Vec<T>`.
#[repr(C)]
pub(crate) struct Vec<T> {
Expand Down Expand Up @@ -312,8 +314,8 @@ impl Display for BoxedError {

impl std::error::Error for BoxedError {}

impl From<Box<dyn std::error::Error + Send + Sync>> for BoxedError {
fn from(err: Box<dyn std::error::Error + Send + Sync>) -> Self {
impl From<Error> for BoxedError {
fn from(err: Error) -> Self {
// Open question: is it worth capturing the message from each source
// error, too? We could have some sort of `sources: Vec<Source>` field
// where `Source` is a private wrapper around String that implements
Expand Down
8 changes: 4 additions & 4 deletions crates/fj/src/abi/host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ pub struct Host<'a> {
_lifetime: PhantomData<&'a mut ()>,
}

impl<'a, H: crate::Host + Sized> From<&'a mut H> for Host<'a> {
impl<'a, H: crate::models::Host + Sized> From<&'a mut H> for Host<'a> {
fn from(host: &'a mut H) -> Self {
extern "C" fn register_boxed_model<H: crate::Host + Sized>(
extern "C" fn register_boxed_model<H: crate::models::Host + Sized>(
user_data: *mut c_void,
model: Model,
) {
Expand All @@ -33,8 +33,8 @@ impl<'a, H: crate::Host + Sized> From<&'a mut H> for Host<'a> {
}
}

impl<'a> crate::Host for Host<'a> {
fn register_boxed_model(&mut self, model: Box<dyn crate::Model>) {
impl<'a> crate::models::Host for Host<'a> {
fn register_boxed_model(&mut self, model: Box<dyn crate::models::Model>) {
let Host {
user_data,
register_boxed_model,
Expand Down
30 changes: 15 additions & 15 deletions crates/fj/src/abi/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,25 @@ pub struct ModelMetadata {
arguments: ffi_safe::Vec<ArgumentMetadata>,
}

impl From<ModelMetadata> for crate::ModelMetadata {
impl From<ModelMetadata> for crate::models::ModelMetadata {
fn from(m: ModelMetadata) -> Self {
let ModelMetadata {
name,
description,
arguments,
} = m;

crate::ModelMetadata {
crate::models::ModelMetadata {
name: name.into(),
description: description.map(Into::into).into(),
arguments: arguments.iter().cloned().map(|a| a.into()).collect(),
}
}
}

impl From<crate::ModelMetadata> for ModelMetadata {
fn from(m: crate::ModelMetadata) -> Self {
let crate::ModelMetadata {
impl From<crate::models::ModelMetadata> for ModelMetadata {
fn from(m: crate::models::ModelMetadata) -> Self {
let crate::models::ModelMetadata {
name,
description,
arguments,
Expand All @@ -52,7 +52,7 @@ pub struct Metadata {
license: ffi_safe::Option<ffi_safe::String>,
}

impl From<Metadata> for crate::Metadata {
impl From<Metadata> for crate::models::Metadata {
fn from(m: Metadata) -> Self {
let Metadata {
name,
Expand All @@ -64,7 +64,7 @@ impl From<Metadata> for crate::Metadata {
license,
} = m;

crate::Metadata {
crate::models::Metadata {
name: name.into(),
version: version.into(),
short_description: short_description.map(Into::into).into(),
Expand All @@ -76,9 +76,9 @@ impl From<Metadata> for crate::Metadata {
}
}

impl From<crate::Metadata> for Metadata {
fn from(m: crate::Metadata) -> Self {
let crate::Metadata {
impl From<crate::models::Metadata> for Metadata {
fn from(m: crate::models::Metadata) -> Self {
let crate::models::Metadata {
name,
version,
short_description,
Expand Down Expand Up @@ -108,9 +108,9 @@ pub struct ArgumentMetadata {
default_value: ffi_safe::Option<ffi_safe::String>,
}

impl From<crate::ArgumentMetadata> for ArgumentMetadata {
fn from(meta: crate::ArgumentMetadata) -> Self {
let crate::ArgumentMetadata {
impl From<crate::models::ArgumentMetadata> for ArgumentMetadata {
fn from(meta: crate::models::ArgumentMetadata) -> Self {
let crate::models::ArgumentMetadata {
name,
description,
default_value,
Expand All @@ -124,15 +124,15 @@ impl From<crate::ArgumentMetadata> for ArgumentMetadata {
}
}

impl From<ArgumentMetadata> for crate::ArgumentMetadata {
impl From<ArgumentMetadata> for crate::models::ArgumentMetadata {
fn from(meta: ArgumentMetadata) -> Self {
let ArgumentMetadata {
name,
description,
default_value,
} = meta;

crate::ArgumentMetadata {
crate::models::ArgumentMetadata {
name: name.into(),
description: description.map(Into::into).into(),
default_value: default_value.map(Into::into).into(),
Expand Down
41 changes: 34 additions & 7 deletions crates/fj/src/abi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
/// If the macro generated the wrong code, this doctest would fail.
///
/// ```rust
/// use fj::{abi::INIT_FUNCTION_NAME, Metadata};
/// use fj::{abi::INIT_FUNCTION_NAME, models::Metadata};
///
/// fj::register_model!(|_| {
/// Ok(Metadata::new("My Model", "1.2.3"))
Expand All @@ -24,7 +24,7 @@
///
/// // We can also make sure the unmangled name is correct by calling it.
///
/// let metadata: fj::Metadata = unsafe {
/// let metadata: fj::models::Metadata = unsafe {
/// let mut host = Host;
/// let mut host = fj::abi::Host::from(&mut host);
/// x::fj_model_init(&mut host).unwrap().into()
Expand All @@ -33,8 +33,8 @@
/// assert_eq!(metadata.name, "My Model");
///
/// struct Host;
/// impl fj::Host for Host {
/// fn register_boxed_model(&mut self, model: Box<dyn fj::Model>) { todo!() }
/// impl fj::models::Host for Host {
/// fn register_boxed_model(&mut self, model: Box<dyn fj::models::Model>) { todo!() }
/// }
/// ```
mod context;
Expand All @@ -52,6 +52,33 @@ pub use self::{
model::Model,
};

/// Define the initialization routine used when registering models.
///
/// See the [`crate::model`] macro if your model can be implemented as a pure
/// function.
///
/// # Examples
///
/// ```rust
/// use fj::models::*;
///
/// fj::register_model!(|host: &mut dyn Host| {
/// host.register_model(MyModel::default());
///
/// Ok(Metadata::new(env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION")))
/// });
///
/// #[derive(Default)]
/// struct MyModel { }
///
/// impl Model for MyModel {
/// fn metadata(&self) -> ModelMetadata { todo!() }
///
/// fn shape(&self, ctx: &dyn Context) -> Result<fj::Shape, Error> {
/// todo!()
/// }
/// }
/// ```
#[macro_export]
macro_rules! register_model {
($init:expr) => {
Expand All @@ -60,10 +87,10 @@ macro_rules! register_model {
mut host: *mut $crate::abi::Host<'_>,
) -> $crate::abi::InitResult {
let init: fn(
&mut dyn $crate::Host,
&mut dyn $crate::models::Host,
) -> Result<
$crate::Metadata,
Box<dyn std::error::Error + Send + Sync>,
$crate::models::Metadata,
$crate::models::Error,
> = $init;

match init(&mut *host) {
Expand Down
Loading