diff options
author | Brian Olsen <brian@maven-group.org> | 2024-07-22T14·51+0200 |
---|---|---|
committer | clbot <clbot@tvl.fyi> | 2024-08-25T15·05+0000 |
commit | ced05a2bb6b66d30208520d0791f4fa298c429e2 (patch) | |
tree | d295eb9766db79df8f3053bc5022e24059012f16 /tvix/nix-compat-derive/src/internal/mod.rs | |
parent | 9af69204787d47cfe551f524d01b1a726971f06e (diff) |
feat(tvix/nix-compat-derive): Add deriver for NixDeserialize r/8586
This adds a nix-compat-derive derive crate that implements a deriver for NixDeserialize implementations. This is to reduce the amount of code needed to implement deserialization for all the types used by the Nix daemon protocol. Change-Id: I484724b550e8a1d5e9adad9555d9dc1374ae95c2 Reviewed-on: https://cl.tvl.fyi/c/depot/+/12022 Autosubmit: Brian Olsen <me@griff.name> Tested-by: BuildkiteCI Reviewed-by: flokli <flokli@flokli.de>
Diffstat (limited to 'tvix/nix-compat-derive/src/internal/mod.rs')
-rw-r--r-- | tvix/nix-compat-derive/src/internal/mod.rs | 183 |
1 files changed, 183 insertions, 0 deletions
diff --git a/tvix/nix-compat-derive/src/internal/mod.rs b/tvix/nix-compat-derive/src/internal/mod.rs new file mode 100644 index 000000000000..20b243221619 --- /dev/null +++ b/tvix/nix-compat-derive/src/internal/mod.rs @@ -0,0 +1,183 @@ +use syn::punctuated::Punctuated; +use syn::spanned::Spanned; +use syn::Token; + +pub mod attrs; +mod ctx; +pub mod inputs; +mod symbol; + +pub use ctx::Context; + +pub struct Field<'a> { + pub member: syn::Member, + pub ty: &'a syn::Type, + pub attrs: attrs::Field, + pub original: &'a syn::Field, +} + +impl<'a> Field<'a> { + pub fn from_ast(ctx: &Context, idx: usize, field: &'a syn::Field) -> Field<'a> { + let attrs = attrs::Field::from_ast(ctx, &field.attrs); + let member = match &field.ident { + Some(id) => syn::Member::Named(id.clone()), + None => syn::Member::Unnamed(idx.into()), + }; + Field { + member, + attrs, + ty: &field.ty, + original: field, + } + } + + pub fn var_ident(&self) -> syn::Ident { + match &self.member { + syn::Member::Named(name) => name.clone(), + syn::Member::Unnamed(idx) => { + syn::Ident::new(&format!("field{}", idx.index), self.original.span()) + } + } + } +} + +pub struct Variant<'a> { + pub ident: &'a syn::Ident, + pub attrs: attrs::Variant, + pub style: Style, + pub fields: Vec<Field<'a>>, + //pub original: &'a syn::Variant, +} + +impl<'a> Variant<'a> { + pub fn from_ast(ctx: &Context, variant: &'a syn::Variant) -> Self { + let attrs = attrs::Variant::from_ast(ctx, &variant.attrs); + let (style, fields) = match &variant.fields { + syn::Fields::Named(fields) => (Style::Struct, fields_ast(ctx, &fields.named)), + syn::Fields::Unnamed(fields) => (Style::Tuple, fields_ast(ctx, &fields.unnamed)), + syn::Fields::Unit => (Style::Unit, Vec::new()), + }; + Variant { + ident: &variant.ident, + attrs, + style, + fields, + //original: variant, + } + } +} + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)] +pub enum Style { + Struct, + Tuple, + Unit, +} + +pub enum Data<'a> { + Enum(Vec<Variant<'a>>), + Struct(Style, Vec<Field<'a>>), +} + +pub struct Container<'a> { + pub ident: &'a syn::Ident, + pub attrs: attrs::Container, + pub data: Data<'a>, + pub crate_path: syn::Path, + pub original: &'a syn::DeriveInput, +} + +impl<'a> Container<'a> { + pub fn from_ast( + ctx: &Context, + crate_path: syn::Path, + input: &'a mut syn::DeriveInput, + ) -> Option<Container<'a>> { + let attrs = attrs::Container::from_ast(ctx, &input.attrs); + let data = match &input.data { + syn::Data::Struct(s) => match &s.fields { + syn::Fields::Named(fields) => { + Data::Struct(Style::Struct, fields_ast(ctx, &fields.named)) + } + syn::Fields::Unnamed(fields) => { + Data::Struct(Style::Tuple, fields_ast(ctx, &fields.unnamed)) + } + syn::Fields::Unit => Data::Struct(Style::Unit, Vec::new()), + }, + syn::Data::Enum(e) => { + let variants = e + .variants + .iter() + .map(|variant| Variant::from_ast(ctx, variant)) + .collect(); + Data::Enum(variants) + } + syn::Data::Union(u) => { + ctx.error_spanned(u.union_token, "Union not supported by nixrs"); + return None; + } + }; + Some(Container { + ident: &input.ident, + attrs, + data, + crate_path, + original: input, + }) + } + + pub fn crate_path(&self) -> &syn::Path { + if let Some(crate_path) = self.attrs.crate_path.as_ref() { + crate_path + } else { + &self.crate_path + } + } + + pub fn ident_type(&self) -> syn::Type { + let path: syn::Path = self.ident.clone().into(); + let tp = syn::TypePath { qself: None, path }; + tp.into() + } +} + +pub struct Remote<'a> { + pub attrs: attrs::Container, + pub ty: &'a syn::Type, + pub crate_path: syn::Path, +} + +impl<'a> Remote<'a> { + pub fn from_ast( + ctx: &Context, + crate_path: syn::Path, + input: &'a inputs::RemoteInput, + ) -> Option<Remote<'a>> { + let attrs = attrs::Container::from_ast(ctx, &input.attrs); + if attrs.from_str.is_none() && attrs.type_from.is_none() && attrs.type_try_from.is_none() { + ctx.error_spanned(input, "Missing from_str, from or try_from attribute"); + return None; + } + Some(Remote { + ty: &input.ident, + attrs, + crate_path, + }) + } + + pub fn crate_path(&self) -> &syn::Path { + if let Some(crate_path) = self.attrs.crate_path.as_ref() { + crate_path + } else { + &self.crate_path + } + } +} + +fn fields_ast<'a>(ctx: &Context, fields: &'a Punctuated<syn::Field, Token![,]>) -> Vec<Field<'a>> { + fields + .iter() + .enumerate() + .map(|(idx, field)| Field::from_ast(ctx, idx, field)) + .collect() +} |