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),
|
Expected(&'static str),
|
||||||
#[error("expected \"({0}\"")]
|
#[error("expected \"({0}\"")]
|
||||||
ExpectedStartOfList(&'static str),
|
ExpectedStartOfList(&'static str),
|
||||||
|
#[error("expected one of: {0:?}")]
|
||||||
|
ExpectedStartOfListOneOf(&'static [&'static str]),
|
||||||
#[error("expected \")\"")]
|
#[error("expected \")\"")]
|
||||||
ExpectedEndOfList,
|
ExpectedEndOfList,
|
||||||
#[error("expected leaf value")]
|
#[error("expected leaf value")]
|
||||||
|
|
|
||||||
|
|
@ -279,8 +279,7 @@ pub struct Padstack {
|
||||||
pub attach: Option<bool>,
|
pub attach: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: derive for enums if more than this single one is needed
|
#[derive(ReadDsn, WriteSes, Debug, Clone, PartialEq)]
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
pub enum Shape {
|
pub enum Shape {
|
||||||
Circle(Circle),
|
Circle(Circle),
|
||||||
Rect(Rect),
|
Rect(Rect),
|
||||||
|
|
@ -288,33 +287,6 @@ pub enum Shape {
|
||||||
Polygon(Polygon),
|
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)]
|
#[derive(ReadDsn, WriteSes, Debug, Clone, PartialEq)]
|
||||||
pub struct Circle {
|
pub struct Circle {
|
||||||
#[anon]
|
#[anon]
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ use proc_macro2::TokenStream;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use syn::ext::IdentExt;
|
use syn::ext::IdentExt;
|
||||||
use syn::Type::Path;
|
use syn::Type::Path;
|
||||||
use syn::{Data, DeriveInput, Field, Fields};
|
use syn::{Data, DeriveInput, Field, Fields, Variant};
|
||||||
|
|
||||||
use crate::parse_attributes;
|
use crate::parse_attributes;
|
||||||
use crate::FieldType;
|
use crate::FieldType;
|
||||||
|
|
@ -40,8 +40,19 @@ fn impl_body(data: &Data) -> TokenStream {
|
||||||
}
|
}
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
},
|
},
|
||||||
Data::Enum(_data) => {
|
Data::Enum(data) => {
|
||||||
todo!();
|
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!(),
|
_ => 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
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::{Span, TokenStream};
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use syn::ext::IdentExt;
|
use syn::ext::IdentExt;
|
||||||
use syn::Type::Path;
|
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::parse_attributes;
|
||||||
use crate::FieldType;
|
use crate::FieldType;
|
||||||
|
|
@ -41,6 +41,17 @@ fn impl_body(data: &Data) -> TokenStream {
|
||||||
}
|
}
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
},
|
},
|
||||||
|
Data::Enum(data) => {
|
||||||
|
let variants = data.variants.iter().map(impl_variant);
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
match self {
|
||||||
|
#(#variants)*
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => unimplemented!(),
|
_ => 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