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

[Failed] Functions v2 #139

Closed
wants to merge 1 commit into from
Closed
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
3 changes: 3 additions & 0 deletions examples/functions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fn main() {
// TODO: Show an example of building a function exporter.
}
2 changes: 1 addition & 1 deletion macros/src/data_type_from/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ pub fn derive(input: proc_macro::TokenStream) -> syn::Result<proc_macro::TokenSt
#[automatically_derived]
impl From<#ident> for #crate_ref::StructType {
fn from(t: #ident) -> #crate_ref::StructType {
#crate_ref::internal::construct::r#struct(#struct_name.into(), vec![], #crate_ref::internal::construct::struct_named(vec![#(#fields),*], None))
#crate_ref::internal::construct::r#struct(#struct_name, vec![], #crate_ref::internal::construct::struct_named(vec![#(#fields),*], None))
}
}

Expand Down
47 changes: 0 additions & 47 deletions macros/src/fn_datatype.rs

This file was deleted.

13 changes: 1 addition & 12 deletions macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@
//! You shouldn't need to use this crate directly.
//! Checkout [Specta](https://docs.rs/specta).
//!

#[macro_use]
mod utils;
mod data_type_from;
#[cfg(feature = "functions")]
mod fn_datatype;
#[cfg(feature = "functions")]
mod specta;
mod r#type;

Expand All @@ -31,13 +30,3 @@ pub fn specta(
) -> proc_macro::TokenStream {
specta::attribute(item).unwrap_or_else(|err| err.into_compile_error().into())
}

#[proc_macro]
#[cfg(feature = "functions")]
pub fn fn_datatype(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
use syn::parse_macro_input;

fn_datatype::proc_macro(parse_macro_input!(input as fn_datatype::FnDatatypeInput))
.unwrap_or_else(|err| err.into_compile_error())
.into()
}
83 changes: 59 additions & 24 deletions macros/src/specta.rs
Original file line number Diff line number Diff line change
@@ -1,40 +1,49 @@
// inspired by https://github.com/tauri-apps/tauri/blob/2901145c497299f033ba7120af5f2e7ead16c75a/core/tauri-macros/src/command/handler.rs

use quote::{quote, ToTokens};
use syn::{parse_macro_input, FnArg, ItemFn, Pat, Visibility};
use syn::{parse_macro_input, FnArg, ItemFn, Pat, ReturnType, Visibility};

Check warning on line 4 in macros/src/specta.rs

View workflow job for this annotation

GitHub Actions / clippy

unused import: `Pat`

warning: unused import: `Pat` --> macros/src/specta.rs:4:45 | 4 | use syn::{parse_macro_input, FnArg, ItemFn, Pat, ReturnType, Visibility}; | ^^^ | = note: `#[warn(unused_imports)]` on by default

use crate::utils::format_fn_wrapper;

pub fn attribute(item: proc_macro::TokenStream) -> syn::Result<proc_macro::TokenStream> {
let function = parse_macro_input::parse::<ItemFn>(item)?;
let wrapper = format_fn_wrapper(&function.sig.ident);
let crate_ref = quote!(specta);

let visibility = &function.vis;
let maybe_macro_export = match &visibility {
Visibility::Public(_) => quote!(#[macro_export]),
_ => Default::default(),
};

let function_name = &function.sig.ident;
let function_name_str = function_name.to_string();
let function_asyncness = match function.sig.asyncness {
Some(_) => true,
None => false,
};
let ident = &function.sig.ident;
let name = ident.to_string();
let asyncness = function.sig.asyncness.is_some();

let arg_names = function.sig.inputs.iter().map(|input| match input {
FnArg::Receiver(_) => unreachable!("Commands cannot take 'self'"),
FnArg::Typed(arg) => match &*arg.pat {
Pat::Ident(ident) => ident.ident.to_token_stream(),
Pat::Macro(m) => m.mac.tokens.to_token_stream(),
Pat::Struct(s) => s.path.to_token_stream(),
Pat::Slice(s) => s.attrs[0].to_token_stream(),
Pat::Tuple(s) => s.elems[0].to_token_stream(),
_ => unreachable!("Commands must take named arguments"),
},
});
let args = function.sig.inputs.iter().map(|input| match input {
FnArg::Receiver(_) => return Err(syn::Error::new_spanned(
input,
"functions with `#[specta]` cannot take 'self'",
)),
FnArg::Typed(arg) => {
let name = &arg.pat.to_token_stream().to_string();
let ty = &arg.ty;
Ok(quote!(
(
#name.into(),
<#ty as #crate_ref::internal::functions::FunctionArg<_>>::to_datatype(#crate_ref::DefOpts {
parent_inline: false,
type_map,
})
)
))
}
}).collect::<syn::Result<Vec<_>>>()?;

let arg_signatures = function.sig.inputs.iter().map(|_| quote!(_));
let output = match &function.sig.output {
ReturnType::Default => quote!(()),
ReturnType::Type(_, ty) => quote!(#ty),
};

let docs = function
.attrs
Expand All @@ -51,11 +60,37 @@
#maybe_macro_export
#[doc(hidden)]
macro_rules! #wrapper {
(@asyncness) => { #function_asyncness };
(@name) => { #function_name_str.into() };
(@arg_names) => { &[#(stringify!(#arg_names).into()),* ] };
(@signature) => { fn(#(#arg_signatures),*) -> _ };
(@docs) => { vec![#(#docs.into()),*] };
(@internal) => {{
// fn export_self(type_map: &mut #crate_ref::TypeMap) -> #crate_ref::FunctionType {
// #crate_ref::internal::construct::function_type(
// #asyncness,
// #name,
// vec![#(#args),*],
// <#output as #crate_ref::internal::functions::FunctionOutput<_>>::to_datatype(#crate_ref::DefOpts {
// parent_inline: false,
// type_map,
// }),
// vec![#(#docs.into()),*]
// )
// }
// export_self as fn(&mut _) -> _

let f: fn(&mut _) -> _ = |type_map| {
// todo!();
#crate_ref::internal::construct::function_type(
#asyncness,
#name,
vec![#(#args),*],
<#output as #crate_ref::internal::functions::FunctionOutput<_>>::to_datatype(#crate_ref::DefOpts {
parent_inline: false,
type_map,
}),
vec![#(#docs.into()),*]
)
};

#crate_ref::internal::construct::function(&f)
}}
}

// allow the macro to be resolved with the same path as the function
Expand Down
4 changes: 2 additions & 2 deletions macros/src/type/enum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,11 +202,11 @@ pub fn parse_enum(
};

Ok((
quote!(#crate_ref::DataType::Enum(#crate_ref::internal::construct::r#enum(#name.into(), #repr, vec![#(#definition_generics),*], vec![#(#variant_types),*]))),
quote!(#crate_ref::DataType::Enum(#crate_ref::internal::construct::r#enum(#name, #repr, vec![#(#definition_generics),*], vec![#(#variant_types),*]))),
quote!({
let generics = vec![#(#reference_generics),*];
#crate_ref::reference::reference::<Self>(opts, &generics, #crate_ref::internal::construct::data_type_reference(
#name.into(),
#name,
SID,
generics.clone() // TODO: This `clone` is cringe
))
Expand Down
6 changes: 3 additions & 3 deletions macros/src/type/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,9 @@ pub fn derive(input: proc_macro::TokenStream) -> syn::Result<proc_macro::TokenSt

quote! {
#[allow(non_snake_case)]
#[#crate_ref::internal::ctor::ctor]
#[#crate_ref::internal::__specta_ctor]
fn #export_fn_name() {
#crate_ref::export::register_ty::<#ident<#(#generic_params),*>>();
#crate_ref::internal::export::register_ty::<#ident<#(#generic_params),*>>();
}
}
});
Expand Down Expand Up @@ -152,7 +152,7 @@ pub fn derive(input: proc_macro::TokenStream) -> syn::Result<proc_macro::TokenSt

fn named_data_type(opts: #crate_ref::DefOpts, generics: &[#crate_ref::DataType]) -> #crate_ref::NamedDataType {
#crate_ref::internal::construct::named_data_type(
#name.into(),
#name,
#comments,
#deprecated,
SID,
Expand Down
4 changes: 2 additions & 2 deletions macros/src/type/struct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ pub fn parse_struct(
Fields::Unit => quote!(#crate_ref::internal::construct::struct_unit()),
};

quote!(#crate_ref::DataType::Struct(#crate_ref::internal::construct::r#struct(#name.into(), vec![#(#definition_generics),*], #fields)))
quote!(#crate_ref::DataType::Struct(#crate_ref::internal::construct::r#struct(#name, vec![#(#definition_generics),*], #fields)))
};


Expand All @@ -242,7 +242,7 @@ pub fn parse_struct(
quote!({
let generics = vec![#(#reference_generics),*];
#crate_ref::reference::reference::<Self>(opts, &generics, #crate_ref::internal::construct::data_type_reference(
#name.into(),
#name,
SID,
generics.clone() // TODO: This `clone` is cringe
))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ use std::{borrow::Cow, collections::HashMap};
use crate::{ImplLocation, TypeMap};

/// post process the type map to detect duplicate type names
#[doc(hidden)]
pub fn detect_duplicate_type_names(
type_map: &TypeMap,
) -> Vec<(Cow<'static, str>, ImplLocation, ImplLocation)> {
Expand Down
7 changes: 7 additions & 0 deletions src/advanced/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
//! Advanced functionality, targeted for people building libraries with Specta.

mod duplicate_ty_name;
mod serde;

pub use duplicate_ty_name::*;
pub use serde::*;
File renamed without changes.
49 changes: 49 additions & 0 deletions src/datatype/function.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
use std::borrow::Cow;

use crate::{DataType, Function, TypeMap};

/// Contains type information about a function annotated with [`specta`](macro@crate::specta).
/// Returned by [`func`].
#[derive(Debug, Clone, PartialEq)]
pub struct FunctionType {
pub(crate) asyncness: bool,
pub(crate) name: Cow<'static, str>,
pub(crate) args: Vec<(Cow<'static, str>, DataType)>,
pub(crate) result: DataType,
pub(crate) docs: Vec<Cow<'static, str>>,
}

impl FunctionType {
/// Constructs a [`FunctionType`] from a [`Function`].
pub fn new<const N: usize>(
function: [Function; N],

Check warning on line 19 in src/datatype/function.rs

View workflow job for this annotation

GitHub Actions / clippy

unused variable: `function`

warning: unused variable: `function` --> src/datatype/function.rs:19:9 | 19 | function: [Function; N], | ^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_function` | = note: `#[warn(unused_variables)]` on by default
type_map: &mut TypeMap,

Check warning on line 20 in src/datatype/function.rs

View workflow job for this annotation

GitHub Actions / clippy

unused variable: `type_map`

warning: unused variable: `type_map` --> src/datatype/function.rs:20:9 | 20 | type_map: &mut TypeMap, | ^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_type_map`
) -> [FunctionType; N] {
todo!();
}

/// Returns whether the function is async.
pub fn asyncness(&self) -> bool {
self.asyncness
}

/// Returns the function's name.
pub fn name(&self) -> &Cow<'static, str> {
&self.name
}

/// Returns the name and type of each of the function's arguments.
pub fn args(&self) -> &Vec<(Cow<'static, str>, DataType)> {
&self.args
}

/// Returns the return type of the function.
pub fn result(&self) -> &DataType {
&self.result
}

/// Returns the function's documentation. Detects both `///` and `#[doc = ...]` style documentation.
pub fn docs(&self) -> &Vec<Cow<'static, str>> {
&self.docs
}
}
5 changes: 5 additions & 0 deletions src/datatype/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,18 @@ use std::{

mod r#enum;
mod fields;
#[cfg(feature = "functions")]
mod function;
mod literal;
mod named;
mod primitive;
mod r#struct;
mod tuple;

pub use fields::*;
#[cfg(feature = "functions")]
#[cfg_attr(docsrs, doc(cfg(feature = "functions")))]
pub use function::*;
pub use literal::*;
pub use named::*;
pub use primitive::*;
Expand Down
17 changes: 1 addition & 16 deletions src/export/export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use once_cell::sync::Lazy;
use std::sync::{PoisonError, RwLock, RwLockReadGuard};

// Global type store for collecting custom types to export.
static TYPES: Lazy<RwLock<TypeMap>> = Lazy::new(Default::default);
pub(crate) static TYPES: Lazy<RwLock<TypeMap>> = Lazy::new(Default::default);

/// A lock type for iterating over the internal type map.
///
Expand Down Expand Up @@ -34,18 +34,3 @@ pub fn get_types() -> TypesIter {
lock: types,
}
}

// Called within ctor functions to register a type.
#[doc(hidden)]
pub fn register_ty<T: Type>() {
let type_map = &mut *TYPES.write().unwrap_or_else(PoisonError::into_inner);

// We call this for it's side effects on the `type_map`
T::reference(
DefOpts {
parent_inline: false,
type_map,
},
&[],
);
}
Loading
Loading