feat(specctra_derive): implement for enums containing unnamed fields

This commit is contained in:
Ellen Emilia Anna Zscheile 2025-02-14 19:38:05 +01:00 committed by mikolaj
parent f7ac667e1d
commit ae40efbfa2
4 changed files with 89 additions and 34 deletions

View File

@ -15,6 +15,8 @@ pub enum ParseError {
Expected(&'static str),
#[error("expected \"({0}\"")]
ExpectedStartOfList(&'static str),
#[error("expected one of: {0:?}")]
ExpectedStartOfListOneOf(&'static [&'static str]),
#[error("expected \")\"")]
ExpectedEndOfList,
#[error("expected leaf value")]

View File

@ -279,8 +279,7 @@ pub struct Padstack {
pub attach: Option<bool>,
}
// TODO: derive for enums if more than this single one is needed
#[derive(Debug, Clone, PartialEq)]
#[derive(ReadDsn, WriteSes, Debug, Clone, PartialEq)]
pub enum Shape {
Circle(Circle),
Rect(Rect),
@ -288,33 +287,6 @@ pub enum Shape {
Polygon(Polygon),
}
impl<R: std::io::BufRead> ReadDsn<R> for Shape {
fn read_dsn(tokenizer: &mut ListTokenizer<R>) -> Result<Self, ParseErrorContext> {
let ctx = tokenizer.context();
let name = tokenizer.consume_token()?.expect_any_start()?;
let value = match name.as_str() {
"circle" => Ok(Shape::Circle(tokenizer.read_value()?)),
"rect" => Ok(Shape::Rect(tokenizer.read_value()?)),
"path" => Ok(Shape::Path(tokenizer.read_value()?)),
"polygon" => Ok(Shape::Polygon(tokenizer.read_value()?)),
_ => Err(ParseError::Expected("a different keyword").add_context(ctx)),
};
tokenizer.consume_token()?.expect_end()?;
value
}
}
impl<W: std::io::Write> WriteSes<W> for Shape {
fn write_dsn(&self, writer: &mut ListWriter<W>) -> Result<(), std::io::Error> {
match self {
Self::Circle(inner) => writer.write_named("circle", inner),
Self::Rect(inner) => writer.write_named("rect", inner),
Self::Path(inner) => writer.write_named("path", inner),
Self::Polygon(inner) => writer.write_named("polygon", inner),
}
}
}
#[derive(ReadDsn, WriteSes, Debug, Clone, PartialEq)]
pub struct Circle {
#[anon]

View File

@ -6,7 +6,7 @@ use proc_macro2::TokenStream;
use quote::quote;
use syn::ext::IdentExt;
use syn::Type::Path;
use syn::{Data, DeriveInput, Field, Fields};
use syn::{Data, DeriveInput, Field, Fields, Variant};
use crate::parse_attributes;
use crate::FieldType;
@ -40,8 +40,19 @@ fn impl_body(data: &Data) -> TokenStream {
}
_ => unimplemented!(),
},
Data::Enum(_data) => {
todo!();
Data::Enum(data) => {
let (variantnames, variants): (TokenStream, TokenStream) =
data.variants.iter().map(impl_variant).unzip();
quote! {
let ctx = tokenizer.context();
let name = tokenizer.consume_token()?.expect_any_start()?;
let value = Ok(match name.as_str() {
#variants
_ => return Err(ParseError::ExpectedStartOfListOneOf(&[#variantnames]).add_context(ctx)),
});
tokenizer.consume_token()?.expect_end()?;
value
}
}
_ => unimplemented!(),
}
@ -88,3 +99,35 @@ fn impl_field(field: &Field) -> TokenStream {
}
}
}
fn impl_variant(variant: &Variant) -> (TokenStream, TokenStream) {
let name = &variant.ident;
let mut name_str = name.unraw().to_string();
name_str.make_ascii_lowercase();
let inner = match &variant.fields {
Fields::Unnamed(fields) => {
let all_parts =
core::iter::repeat(quote! { tokenizer.read_value()?, }).take(fields.unnamed.len());
quote! { Self::#name(#(#all_parts)*) }
}
Fields::Named(fields) => {
let fields = fields.named.iter().map(impl_field);
quote! {
Self::#name {
#(#fields)*
}
}
}
Fields::Unit => unimplemented!(),
};
(
quote! {
#name_str,
},
quote! {
#name_str => #inner,
},
)
}

View File

@ -2,11 +2,11 @@
//
// SPDX-License-Identifier: MIT
use proc_macro2::TokenStream;
use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::ext::IdentExt;
use syn::Type::Path;
use syn::{Data, DeriveInput, Field, Fields};
use syn::{punctuated::Punctuated, Data, DeriveInput, Field, Fields, Ident, Variant};
use crate::parse_attributes;
use crate::FieldType;
@ -41,6 +41,17 @@ fn impl_body(data: &Data) -> TokenStream {
}
_ => unimplemented!(),
},
Data::Enum(data) => {
let variants = data.variants.iter().map(impl_variant);
quote! {
match self {
#(#variants)*
}
Ok(())
}
}
_ => unimplemented!(),
}
}
@ -87,3 +98,30 @@ fn impl_field(field: &Field) -> TokenStream {
}
}
}
fn impl_variant(variant: &Variant) -> TokenStream {
let name = &variant.ident;
let mut name_str = name.unraw().to_string();
name_str.make_ascii_lowercase();
match &variant.fields {
Fields::Unnamed(fields) => {
let names: Vec<_> = (0..fields.unnamed.len())
.map(|i| Ident::new(&format!("inner__{}", i), Span::mixed_site()))
.collect();
let mut select = Punctuated::<_, syn::Token![,]>::new();
for i in &names {
select.push(i.clone());
}
let fields = names.into_iter().map(|name| {
quote! { writer.write_value(#name)?; }
});
quote! { Self::#name(#select) => {
writer.write_token(ListToken::Start { name: #name_str.to_string() })?;
#(#fields)*
writer.write_token(ListToken::End)?;
}, }
}
_ => unimplemented!(),
}
}