mirror of https://codeberg.org/topola/topola.git
feat(specctra_derive): implement for enums containing unnamed fields
This commit is contained in:
parent
f7ac667e1d
commit
ae40efbfa2
|
|
@ -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")]
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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!(),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue