feat(tvix/nix-compat-derive): Add deriver for NixDeserialize
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>
This commit is contained in:
parent
9af6920478
commit
ced05a2bb6
48 changed files with 2376 additions and 3 deletions
358
tvix/nix-compat-derive/src/internal/attrs.rs
Normal file
358
tvix/nix-compat-derive/src/internal/attrs.rs
Normal file
|
|
@ -0,0 +1,358 @@
|
|||
use quote::ToTokens;
|
||||
use syn::meta::ParseNestedMeta;
|
||||
use syn::parse::Parse;
|
||||
use syn::{parse_quote, Attribute, Expr, ExprLit, ExprPath, Lit, Token};
|
||||
|
||||
use super::symbol::{Symbol, CRATE, DEFAULT, FROM, FROM_STR, NIX, TRY_FROM, VERSION};
|
||||
use super::Context;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum Default {
|
||||
None,
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
Default,
|
||||
Path(ExprPath),
|
||||
}
|
||||
|
||||
impl Default {
|
||||
pub fn is_none(&self) -> bool {
|
||||
matches!(self, Default::None)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct Field {
|
||||
pub default: Default,
|
||||
pub version: Option<syn::ExprRange>,
|
||||
}
|
||||
|
||||
impl Field {
|
||||
pub fn from_ast(ctx: &Context, attrs: &Vec<Attribute>) -> Field {
|
||||
let mut version = None;
|
||||
let mut default = Default::None;
|
||||
for attr in attrs {
|
||||
if attr.path() != NIX {
|
||||
continue;
|
||||
}
|
||||
if let Err(err) = attr.parse_nested_meta(|meta| {
|
||||
if meta.path == VERSION {
|
||||
version = parse_lit(ctx, &meta, VERSION)?;
|
||||
} else if meta.path == DEFAULT {
|
||||
if meta.input.peek(Token![=]) {
|
||||
if let Some(path) = parse_lit(ctx, &meta, DEFAULT)? {
|
||||
default = Default::Path(path);
|
||||
}
|
||||
} else {
|
||||
default = Default::Default;
|
||||
}
|
||||
} else {
|
||||
let path = meta.path.to_token_stream().to_string();
|
||||
return Err(meta.error(format_args!("unknown nix field attribute '{}'", path)));
|
||||
}
|
||||
Ok(())
|
||||
}) {
|
||||
eprintln!("{:?}", err.span().source_text());
|
||||
ctx.syn_error(err);
|
||||
}
|
||||
}
|
||||
if version.is_some() && default.is_none() {
|
||||
default = Default::Default;
|
||||
}
|
||||
|
||||
Field { default, version }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct Variant {
|
||||
pub version: syn::ExprRange,
|
||||
}
|
||||
|
||||
impl Variant {
|
||||
pub fn from_ast(ctx: &Context, attrs: &Vec<Attribute>) -> Variant {
|
||||
let mut version = parse_quote!(..);
|
||||
for attr in attrs {
|
||||
if attr.path() != NIX {
|
||||
continue;
|
||||
}
|
||||
if let Err(err) = attr.parse_nested_meta(|meta| {
|
||||
if meta.path == VERSION {
|
||||
if let Some(v) = parse_lit(ctx, &meta, VERSION)? {
|
||||
version = v;
|
||||
}
|
||||
} else {
|
||||
let path = meta.path.to_token_stream().to_string();
|
||||
return Err(
|
||||
meta.error(format_args!("unknown nix variant attribute '{}'", path))
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}) {
|
||||
eprintln!("{:?}", err.span().source_text());
|
||||
ctx.syn_error(err);
|
||||
}
|
||||
}
|
||||
|
||||
Variant { version }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct Container {
|
||||
pub from_str: Option<syn::Path>,
|
||||
pub type_from: Option<syn::Type>,
|
||||
pub type_try_from: Option<syn::Type>,
|
||||
pub crate_path: Option<syn::Path>,
|
||||
}
|
||||
|
||||
impl Container {
|
||||
pub fn from_ast(ctx: &Context, attrs: &Vec<Attribute>) -> Container {
|
||||
let mut type_from = None;
|
||||
let mut type_try_from = None;
|
||||
let mut crate_path = None;
|
||||
let mut from_str = None;
|
||||
|
||||
for attr in attrs {
|
||||
if attr.path() != NIX {
|
||||
continue;
|
||||
}
|
||||
if let Err(err) = attr.parse_nested_meta(|meta| {
|
||||
if meta.path == FROM {
|
||||
type_from = parse_lit(ctx, &meta, FROM)?;
|
||||
} else if meta.path == TRY_FROM {
|
||||
type_try_from = parse_lit(ctx, &meta, TRY_FROM)?;
|
||||
} else if meta.path == FROM_STR {
|
||||
from_str = Some(meta.path);
|
||||
} else if meta.path == CRATE {
|
||||
crate_path = parse_lit(ctx, &meta, CRATE)?;
|
||||
} else {
|
||||
let path = meta.path.to_token_stream().to_string();
|
||||
return Err(
|
||||
meta.error(format_args!("unknown nix variant attribute '{}'", path))
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}) {
|
||||
eprintln!("{:?}", err.span().source_text());
|
||||
ctx.syn_error(err);
|
||||
}
|
||||
}
|
||||
|
||||
Container {
|
||||
from_str,
|
||||
type_from,
|
||||
type_try_from,
|
||||
crate_path,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_lit_str(
|
||||
ctx: &Context,
|
||||
meta: &ParseNestedMeta,
|
||||
attr: Symbol,
|
||||
) -> syn::Result<Option<syn::LitStr>> {
|
||||
let expr: Expr = meta.value()?.parse()?;
|
||||
let mut value = &expr;
|
||||
while let Expr::Group(e) = value {
|
||||
value = &e.expr;
|
||||
}
|
||||
if let Expr::Lit(ExprLit {
|
||||
lit: Lit::Str(s), ..
|
||||
}) = value
|
||||
{
|
||||
Ok(Some(s.clone()))
|
||||
} else {
|
||||
ctx.error_spanned(
|
||||
expr,
|
||||
format_args!("expected nix attribute {} to be string", attr),
|
||||
);
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_lit<T: Parse>(
|
||||
ctx: &Context,
|
||||
meta: &ParseNestedMeta,
|
||||
attr: Symbol,
|
||||
) -> syn::Result<Option<T>> {
|
||||
match get_lit_str(ctx, meta, attr)? {
|
||||
Some(lit) => Ok(Some(lit.parse()?)),
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use syn::{parse_quote, Attribute};
|
||||
|
||||
use crate::internal::Context;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn parse_field_version() {
|
||||
let attrs: Vec<Attribute> = vec![parse_quote!(#[nix(version="..34")])];
|
||||
let ctx = Context::new();
|
||||
let field = Field::from_ast(&ctx, &attrs);
|
||||
ctx.check().unwrap();
|
||||
assert_eq!(
|
||||
field,
|
||||
Field {
|
||||
default: Default::Default,
|
||||
version: Some(parse_quote!(..34)),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_field_default() {
|
||||
let attrs: Vec<Attribute> = vec![parse_quote!(#[nix(default)])];
|
||||
let ctx = Context::new();
|
||||
let field = Field::from_ast(&ctx, &attrs);
|
||||
ctx.check().unwrap();
|
||||
assert_eq!(
|
||||
field,
|
||||
Field {
|
||||
default: Default::Default,
|
||||
version: None,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_field_default_path() {
|
||||
let attrs: Vec<Attribute> = vec![parse_quote!(#[nix(default="Default::default")])];
|
||||
let ctx = Context::new();
|
||||
let field = Field::from_ast(&ctx, &attrs);
|
||||
ctx.check().unwrap();
|
||||
assert_eq!(
|
||||
field,
|
||||
Field {
|
||||
default: Default::Path(parse_quote!(Default::default)),
|
||||
version: None,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_field_both() {
|
||||
let attrs: Vec<Attribute> =
|
||||
vec![parse_quote!(#[nix(version="..", default="Default::default")])];
|
||||
let ctx = Context::new();
|
||||
let field = Field::from_ast(&ctx, &attrs);
|
||||
ctx.check().unwrap();
|
||||
assert_eq!(
|
||||
field,
|
||||
Field {
|
||||
default: Default::Path(parse_quote!(Default::default)),
|
||||
version: Some(parse_quote!(..)),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_field_both_rev() {
|
||||
let attrs: Vec<Attribute> =
|
||||
vec![parse_quote!(#[nix(default="Default::default", version="..")])];
|
||||
let ctx = Context::new();
|
||||
let field = Field::from_ast(&ctx, &attrs);
|
||||
ctx.check().unwrap();
|
||||
assert_eq!(
|
||||
field,
|
||||
Field {
|
||||
default: Default::Path(parse_quote!(Default::default)),
|
||||
version: Some(parse_quote!(..)),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_field_no_attr() {
|
||||
let attrs: Vec<Attribute> = vec![];
|
||||
let ctx = Context::new();
|
||||
let field = Field::from_ast(&ctx, &attrs);
|
||||
ctx.check().unwrap();
|
||||
assert_eq!(
|
||||
field,
|
||||
Field {
|
||||
default: Default::None,
|
||||
version: None,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_field_no_subattrs() {
|
||||
let attrs: Vec<Attribute> = vec![parse_quote!(#[nix()])];
|
||||
let ctx = Context::new();
|
||||
let field = Field::from_ast(&ctx, &attrs);
|
||||
ctx.check().unwrap();
|
||||
assert_eq!(
|
||||
field,
|
||||
Field {
|
||||
default: Default::None,
|
||||
version: None,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_variant_version() {
|
||||
let attrs: Vec<Attribute> = vec![parse_quote!(#[nix(version="..34")])];
|
||||
let ctx = Context::new();
|
||||
let variant = Variant::from_ast(&ctx, &attrs);
|
||||
ctx.check().unwrap();
|
||||
assert_eq!(
|
||||
variant,
|
||||
Variant {
|
||||
version: parse_quote!(..34),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_variant_no_attr() {
|
||||
let attrs: Vec<Attribute> = vec![];
|
||||
let ctx = Context::new();
|
||||
let variant = Variant::from_ast(&ctx, &attrs);
|
||||
ctx.check().unwrap();
|
||||
assert_eq!(
|
||||
variant,
|
||||
Variant {
|
||||
version: parse_quote!(..),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_variant_no_subattrs() {
|
||||
let attrs: Vec<Attribute> = vec![parse_quote!(#[nix()])];
|
||||
let ctx = Context::new();
|
||||
let variant = Variant::from_ast(&ctx, &attrs);
|
||||
ctx.check().unwrap();
|
||||
assert_eq!(
|
||||
variant,
|
||||
Variant {
|
||||
version: parse_quote!(..),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_container_try_from() {
|
||||
let attrs: Vec<Attribute> = vec![parse_quote!(#[nix(try_from="u64")])];
|
||||
let ctx = Context::new();
|
||||
let container = Container::from_ast(&ctx, &attrs);
|
||||
ctx.check().unwrap();
|
||||
assert_eq!(
|
||||
container,
|
||||
Container {
|
||||
from_str: None,
|
||||
type_from: None,
|
||||
type_try_from: Some(parse_quote!(u64)),
|
||||
crate_path: None,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
50
tvix/nix-compat-derive/src/internal/ctx.rs
Normal file
50
tvix/nix-compat-derive/src/internal/ctx.rs
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
use std::cell::RefCell;
|
||||
use std::fmt;
|
||||
use std::thread::panicking;
|
||||
|
||||
use quote::ToTokens;
|
||||
|
||||
pub struct Context {
|
||||
errors: RefCell<Option<Vec<syn::Error>>>,
|
||||
}
|
||||
|
||||
impl Context {
|
||||
pub fn new() -> Context {
|
||||
Context {
|
||||
errors: RefCell::new(Some(Vec::new())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn syn_error(&self, error: syn::Error) {
|
||||
self.errors
|
||||
.borrow_mut()
|
||||
.as_mut()
|
||||
.take()
|
||||
.unwrap()
|
||||
.push(error);
|
||||
}
|
||||
|
||||
pub fn error_spanned<T: ToTokens, D: fmt::Display>(&self, tokens: T, message: D) {
|
||||
self.syn_error(syn::Error::new_spanned(tokens, message));
|
||||
}
|
||||
|
||||
pub fn check(&self) -> syn::Result<()> {
|
||||
let mut iter = self.errors.borrow_mut().take().unwrap().into_iter();
|
||||
let mut err = match iter.next() {
|
||||
None => return Ok(()),
|
||||
Some(err) => err,
|
||||
};
|
||||
for next_err in iter {
|
||||
err.combine(next_err);
|
||||
}
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Context {
|
||||
fn drop(&mut self) {
|
||||
if self.errors.borrow().is_some() && !panicking() {
|
||||
panic!("Context dropped without checking errors");
|
||||
}
|
||||
}
|
||||
}
|
||||
110
tvix/nix-compat-derive/src/internal/inputs.rs
Normal file
110
tvix/nix-compat-derive/src/internal/inputs.rs
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct RemoteInput {
|
||||
pub attrs: Vec<syn::Attribute>,
|
||||
pub ident: syn::Type,
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for RemoteInput {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let attrs = input.call(syn::Attribute::parse_outer)?;
|
||||
|
||||
let ident = input.parse::<syn::Type>()?;
|
||||
Ok(RemoteInput { attrs, ident })
|
||||
}
|
||||
}
|
||||
|
||||
impl quote::ToTokens for RemoteInput {
|
||||
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
|
||||
fn is_outer(attr: &&syn::Attribute) -> bool {
|
||||
match attr.style {
|
||||
syn::AttrStyle::Outer => true,
|
||||
syn::AttrStyle::Inner(_) => false,
|
||||
}
|
||||
}
|
||||
for attr in self.attrs.iter().filter(is_outer) {
|
||||
attr.to_tokens(tokens);
|
||||
}
|
||||
self.ident.to_tokens(tokens);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use syn::parse_quote;
|
||||
//use syn::parse::Parse;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_input() {
|
||||
let p: RemoteInput = parse_quote!(u64);
|
||||
assert_eq!(
|
||||
p,
|
||||
RemoteInput {
|
||||
attrs: vec![],
|
||||
ident: parse_quote!(u64),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_input_attr() {
|
||||
let p: RemoteInput = parse_quote!(
|
||||
#[nix]
|
||||
u64
|
||||
);
|
||||
assert_eq!(
|
||||
p,
|
||||
RemoteInput {
|
||||
attrs: vec![parse_quote!(#[nix])],
|
||||
ident: parse_quote!(u64),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_input_attr_multiple() {
|
||||
let p: RemoteInput = parse_quote!(
|
||||
#[nix]
|
||||
#[hello]
|
||||
u64
|
||||
);
|
||||
assert_eq!(
|
||||
p,
|
||||
RemoteInput {
|
||||
attrs: vec![parse_quote!(#[nix]), parse_quote!(#[hello])],
|
||||
ident: parse_quote!(u64),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_input_attr_full() {
|
||||
let p: RemoteInput = parse_quote!(
|
||||
#[nix(try_from = "u64")]
|
||||
usize
|
||||
);
|
||||
assert_eq!(
|
||||
p,
|
||||
RemoteInput {
|
||||
attrs: vec![parse_quote!(#[nix(try_from="u64")])],
|
||||
ident: parse_quote!(usize),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_input_attr_other() {
|
||||
let p: RemoteInput = parse_quote!(
|
||||
#[muh]
|
||||
u64
|
||||
);
|
||||
assert_eq!(
|
||||
p,
|
||||
RemoteInput {
|
||||
attrs: vec![parse_quote!(#[muh])],
|
||||
ident: parse_quote!(u64),
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
183
tvix/nix-compat-derive/src/internal/mod.rs
Normal file
183
tvix/nix-compat-derive/src/internal/mod.rs
Normal file
|
|
@ -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()
|
||||
}
|
||||
32
tvix/nix-compat-derive/src/internal/symbol.rs
Normal file
32
tvix/nix-compat-derive/src/internal/symbol.rs
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
use std::fmt;
|
||||
|
||||
use syn::Path;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Symbol(&'static str);
|
||||
|
||||
pub const NIX: Symbol = Symbol("nix");
|
||||
pub const VERSION: Symbol = Symbol("version");
|
||||
pub const DEFAULT: Symbol = Symbol("default");
|
||||
pub const FROM: Symbol = Symbol("from");
|
||||
pub const TRY_FROM: Symbol = Symbol("try_from");
|
||||
pub const FROM_STR: Symbol = Symbol("from_str");
|
||||
pub const CRATE: Symbol = Symbol("crate");
|
||||
|
||||
impl PartialEq<Symbol> for Path {
|
||||
fn eq(&self, word: &Symbol) -> bool {
|
||||
self.is_ident(word.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialEq<Symbol> for &'a Path {
|
||||
fn eq(&self, word: &Symbol) -> bool {
|
||||
self.is_ident(word.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Symbol {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str(self.0)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue