1mod attr_name;
26mod enums;
27
28use proc_macro::TokenStream;
29use proc_macro2::TokenStream as Tokens;
30use quote::quote;
31use syn::{Data, DeriveInput, Fields, Ident, LitStr, Type, parse_macro_input};
32
33thread_local! {
34 static KRATE: std::cell::RefCell<Tokens> = std::cell::RefCell::new(quote!(::yog_data));
37 static CAMEL: std::cell::Cell<bool> = const { std::cell::Cell::new(false) };
39}
40
41fn krate() -> Tokens {
43 KRATE.with(|k| k.borrow().clone())
44}
45
46fn default_path() -> syn::Path {
49 use proc_macro_crate::{FoundCrate, crate_name};
50 use yog_ids::rust_crate;
51 let own = quote::format_ident!("{}", rust_crate::DATA_RUST_NAME);
52 match crate_name(rust_crate::DATA) {
53 Ok(FoundCrate::Name(name)) => {
54 let name = quote::format_ident!("{name}");
55 return syn::parse_quote!(::#name);
56 }
57 Ok(FoundCrate::Itself) => return syn::parse_quote!(::#own),
58 Err(_) => {}
59 }
60 match crate_name(rust_crate::API) {
61 Ok(FoundCrate::Name(api)) => {
62 let api = quote::format_ident!("{api}");
63 syn::parse_quote!(::#api::#own)
64 }
65 _ => syn::parse_quote!(::#own),
66 }
67}
68
69#[proc_macro_derive(Schema, attributes(data))]
70pub fn derive_schema(input: TokenStream) -> TokenStream {
71 let input = parse_macro_input!(input as DeriveInput);
72 let mut container = match attrs(&input.attrs) {
73 Ok(a) => a,
74 Err(e) => return e.to_compile_error().into(),
75 };
76 if let syn::Data::Enum(e) = &input.data
78 && container.tag.is_none()
79 && e.variants
80 .iter()
81 .any(|v| !matches!(v.fields, syn::Fields::Unit))
82 {
83 container.tag = Some(yog_ids::word::TYPE.to_owned());
84 }
85 let camel = match container.rename_all.as_deref() {
86 None => false,
87 Some(attr_name::CAMEL_CASE) => true,
88 Some(_) => {
89 return syn::Error::new_spanned(
90 &input.ident,
91 "rename_all = \"camelCase\" is the one case",
92 )
93 .to_compile_error()
94 .into();
95 }
96 };
97 CAMEL.with(|c| c.set(camel));
98 let path = container.krate.take().unwrap_or_else(default_path);
99 KRATE.with(|k| *k.borrow_mut() = quote!(#path));
100 match expand(&input, &container) {
101 Ok(t) => t.into(),
102 Err(e) => e.to_compile_error().into(),
103 }
104}
105
106#[derive(Default)]
107struct Attrs {
108 rename: Option<String>,
109 rename_all: Option<String>,
111 default: Option<Option<syn::Path>>,
112 skip: bool,
113 tag: Option<String>,
114 with: Option<Type>,
115 range: Option<(f64, f64)>,
116 other: bool,
118 krate: Option<syn::Path>,
120 view: bool,
122}
123
124fn attrs(list: &[syn::Attribute]) -> syn::Result<Attrs> {
125 let mut a = Attrs::default();
126 for attr in list.iter().filter(|a| a.path().is_ident(attr_name::DATA)) {
127 attr.parse_nested_meta(|meta| {
128 if meta.path.is_ident(attr_name::RENAME) {
129 a.rename = Some(meta.value()?.parse::<LitStr>()?.value());
130 } else if meta.path.is_ident(attr_name::RENAME_ALL) {
131 a.rename_all = Some(meta.value()?.parse::<LitStr>()?.value());
132 } else if meta.path.is_ident(attr_name::TAG) {
133 a.tag = Some(meta.value()?.parse::<LitStr>()?.value());
134 } else if meta.path.is_ident(attr_name::DEFAULT) {
135 a.default = Some(if meta.input.peek(syn::Token![=]) {
136 Some(meta.value()?.parse::<LitStr>()?.parse()?)
137 } else {
138 None
139 });
140 } else if meta.path.is_ident(attr_name::RANGE) {
141 let lit = meta.value()?.parse::<LitStr>()?;
142 let text = lit.value();
143 let parsed = text.split_once("..=").and_then(|(lo, hi)| {
144 Some((
145 lo.trim().parse::<f64>().ok()?,
146 hi.trim().parse::<f64>().ok()?,
147 ))
148 });
149 a.range = Some(parsed.ok_or_else(|| {
150 syn::Error::new_spanned(&lit, "range = \"lo..=hi\" (numbers)")
151 })?);
152 } else if meta.path.is_ident(attr_name::WITH) {
153 a.with = Some(meta.value()?.parse::<LitStr>()?.parse()?);
154 } else if meta.path.is_ident(attr_name::SKIP) {
155 a.skip = true;
156 } else if meta.path.is_ident(attr_name::VIEW) {
157 a.view = true;
158 } else if meta.path.is_ident(attr_name::OTHER) {
159 a.other = true;
160 } else if meta.path.is_ident(attr_name::CRATE) {
161 a.krate = Some(meta.value()?.parse::<LitStr>()?.parse()?);
162 } else {
163 return Err(meta
164 .error("unknown `data` attribute (rename, rename_all, default, skip, tag, with, range, other, crate, view)"));
165 }
166 Ok(())
167 })?;
168 }
169 Ok(a)
170}
171
172fn docs(list: &[syn::Attribute]) -> String {
173 let mut lines = Vec::new();
174 for attr in list.iter().filter(|a| a.path().is_ident(attr_name::DOC)) {
175 if let syn::Meta::NameValue(nv) = &attr.meta
176 && let syn::Expr::Lit(syn::ExprLit {
177 lit: syn::Lit::Str(s),
178 ..
179 }) = &nv.value
180 {
181 lines.push(s.value().trim().to_string());
182 }
183 }
184 let mut out = String::new();
186 for line in lines {
187 if line.is_empty() {
188 if !out.ends_with("\n\n") && !out.is_empty() {
189 out.push_str("\n\n");
190 }
191 } else {
192 if !out.is_empty() && !out.ends_with('\n') {
193 out.push(' ');
194 }
195 out.push_str(&line);
196 }
197 }
198 out.trim_end().to_string()
199}
200
201fn field_key(ident: &Ident) -> String {
203 let name = ident.to_string().trim_start_matches("r#").to_string();
204 if !CAMEL.with(std::cell::Cell::get) {
205 return name;
206 }
207 let mut out = String::new();
208 let mut upper = false;
209 for c in name.chars() {
210 match c {
211 '_' => upper = !out.is_empty(),
212 c if upper => {
213 out.push(c.to_ascii_uppercase());
214 upper = false;
215 }
216 c => out.push(c),
217 }
218 }
219 out
220}
221
222fn fields_as_written<T>(f: impl FnOnce() -> T) -> T {
225 let camel = CAMEL.with(|c| c.replace(false));
226 let out = f();
227 CAMEL.with(|c| c.set(camel));
228 out
229}
230
231fn variant_key(ident: &Ident) -> String {
233 if !CAMEL.with(std::cell::Cell::get) {
234 return snake(ident);
235 }
236 let name = ident.to_string();
237 let mut chars = name.chars();
238 chars
239 .next()
240 .map(|first| first.to_ascii_lowercase().to_string() + chars.as_str())
241 .unwrap_or_default()
242}
243
244fn snake(ident: &Ident) -> String {
245 let mut out = String::new();
246 for (i, c) in ident.to_string().chars().enumerate() {
247 if c.is_ascii_uppercase() {
248 if i > 0 {
249 out.push('_');
250 }
251 out.push(c.to_ascii_lowercase());
252 } else {
253 out.push(c);
254 }
255 }
256 out
257}
258
259struct NamedField {
260 ident: Ident,
261 ty: Type,
262 key: String,
263 doc: String,
264 default: Option<Option<syn::Path>>,
265 skip: bool,
266 with: Type,
268 codec: bool,
270 range: Option<(f64, f64)>,
271}
272
273fn named_fields(fields: &syn::FieldsNamed) -> syn::Result<Vec<NamedField>> {
274 let krate = krate();
275 fields
276 .named
277 .iter()
278 .map(|f| {
279 let a = attrs(&f.attrs)?;
280 let Some(ident) = f.ident.clone() else {
281 return Err(syn::Error::new_spanned(
282 f,
283 "Schema: a named field without a name",
284 ));
285 };
286 let key = a.rename.unwrap_or_else(|| field_key(&ident));
287 Ok(NamedField {
288 ident,
289 ty: f.ty.clone(),
290 key,
291 doc: docs(&f.attrs),
292 default: a.default,
293 skip: a.skip,
294 codec: a.with.is_some(),
295 with: a
296 .with
297 .unwrap_or_else(|| syn::parse_quote!(#krate::with::Plain)),
298 range: a.range,
299 })
300 })
301 .collect()
302}
303
304fn field_shapes(fields: &[NamedField]) -> Tokens {
306 let krate = krate();
307 let items = fields.iter().filter(|f| !f.skip).map(|f| {
308 let (key, doc, ty, w) = (&f.key, &f.doc, &f.ty, &f.with);
309 let has_default = f.default.is_some();
310 let range = match f.range {
311 Some((lo, hi)) => quote! { ::std::option::Option::Some((#lo, #hi)) },
312 None => quote! { ::std::option::Option::None },
313 };
314 quote! {
315 #krate::schema::Field {
316 name: #key,
317 doc: #doc,
318 shape: <#w as #krate::with::With<#ty>>::shape,
319 required: !#has_default && <#w as #krate::with::With<#ty>>::absent().is_none(),
320 range: #range,
321 }
322 }
323 });
324 quote! { ::std::vec![#(#items),*] }
325}
326
327fn read_fields(fields: &[NamedField], ctor: Tokens) -> Tokens {
329 let keys = fields.iter().filter(|f| !f.skip).map(|f| &f.key);
330 let lets = fields.iter().map(|f| {
331 let (ident, ty, key, w) = (&f.ident, &f.ty, &f.key, &f.with);
332 if f.skip {
333 return quote! { let #ident: ::std::option::Option<#ty> = ::std::option::Option::Some(::std::default::Default::default()); };
334 }
335 match &f.default {
336 None => quote! { let #ident = cx.field_with::<#w, #ty>(map, #key); },
337 Some(None) => quote! { let #ident = cx.field_or_with::<#w, #ty>(map, #key, ::std::default::Default::default); },
338 Some(Some(path)) => quote! { let #ident = cx.field_or_with::<#w, #ty>(map, #key, #path); },
339 }
340 });
341 let ranges = fields.iter().filter_map(|f| {
342 let (ident, key) = (&f.ident, &f.key);
343 let (lo, hi) = f.range?;
344 Some(quote! { let #ident = cx.in_range(#key, #ident, #lo, #hi); })
345 });
346 let inits = fields.iter().map(|f| {
347 let ident = &f.ident;
348 quote! { #ident: #ident? }
349 });
350 quote! {
351 cx.unknown_keys(map, &[#(#keys),*]);
352 cx.own_keys(|cx| {
353 #(#lets)*
354 #(#ranges)*
355 ::std::option::Option::Some(#ctor { #(#inits),* })
356 })
357 }
358}
359
360fn write_fields(fields: &[NamedField]) -> Tokens {
362 let krate = krate();
363 let puts = fields.iter().filter(|f| !f.skip).map(|f| {
364 let (ident, key, ty, w) = (&f.ident, &f.key, &f.ty, &f.with);
365 quote! {
366 if !<#w as #krate::with::With<#ty>>::skip(#ident) {
367 m.insert(#key, <#w as #krate::with::With<#ty>>::write(#ident));
368 }
369 }
370 });
371 quote! { #(#puts)* }
372}
373
374fn encode_fields(fields: &[NamedField]) -> Tokens {
376 let krate = krate();
377 let written: Vec<_> = fields.iter().filter(|f| !f.skip).collect();
378 let counts = written.iter().map(|f| {
379 let (ident, ty, w) = (&f.ident, &f.ty, &f.with);
380 quote! { + usize::from(!<#w as #krate::with::With<#ty>>::skip(#ident)) }
381 });
382 let puts = written.iter().map(|f| {
383 let (ident, key, ty, w) = (&f.ident, &f.key, &f.ty, &f.with);
384 quote! {
385 if !<#w as #krate::with::With<#ty>>::skip(#ident) {
386 __yog_w.key(#key);
387 <#w as #krate::with::With<#ty>>::encode(#ident, __yog_w);
388 }
389 }
390 });
391 quote! {
392 __yog_w.map(0 #(#counts)*);
393 #(#puts)*
394 }
395}
396
397fn decode_fields(fields: &[NamedField], ctor: Tokens) -> Option<Tokens> {
400 let krate = krate();
401 if fields.iter().any(|f| f.range.is_some()) {
402 return None;
403 }
404 let read: Vec<_> = fields.iter().filter(|f| !f.skip).collect();
405 let slot = |f: &NamedField| quote::format_ident!("__yog_field_{}", f.ident);
407 let slots = read.iter().map(|f| {
408 let (s, ty) = (slot(f), &f.ty);
409 quote! { let mut #s: ::std::option::Option<#ty> = ::std::option::Option::None; }
410 });
411 let arms = read.iter().map(|f| {
412 let (s, key, ty, w) = (slot(f), &f.key, &f.ty, &f.with);
413 quote! { #key => #s = ::std::option::Option::Some(<#w as #krate::with::With<#ty>>::decode(__yog_d)?), }
414 });
415 let inits = fields.iter().map(|f| {
416 let (ident, ty, w) = (&f.ident, &f.ty, &f.with);
417 let value = if f.skip {
418 quote! { ::std::default::Default::default() }
419 } else {
420 let missing = match &f.default {
421 None => quote! { <#w as #krate::with::With<#ty>>::absent()? },
422 Some(None) => quote! { ::std::default::Default::default() },
423 Some(Some(path)) => quote! { #path() },
424 };
425 let s = slot(f);
426 quote! { match #s { ::std::option::Option::Some(__yog_v) => __yog_v, ::std::option::Option::None => #missing } }
427 };
428 quote! { #ident: #value }
429 });
430 Some(quote! {
431 let __yog_n = __yog_d.map()?;
432 #(#slots)*
433 for _ in 0..__yog_n {
434 match __yog_d.key()? {
435 #(#arms)*
436 _ => return ::std::option::Option::None,
437 }
438 }
439 __yog_d.leave();
440 ::std::option::Option::Some(#ctor { #(#inits),* })
441 })
442}
443
444fn pack_fields(fields: &[NamedField]) -> Tokens {
446 let krate = krate();
447 let puts = fields.iter().filter(|f| !f.skip).map(|f| {
448 let (ident, ty, w) = (&f.ident, &f.ty, &f.with);
449 quote! { <#w as #krate::with::With<#ty>>::pack(#ident, __yog_p); }
450 });
451 quote! {
452 let __yog_at = __yog_p.begin();
453 #(#puts)*
454 __yog_p.end(__yog_at);
455 }
456}
457
458fn unpack_fields(fields: &[NamedField], ctor: Tokens) -> Tokens {
462 let krate = krate();
463 let slot = |f: &NamedField| quote::format_ident!("__yog_field_{}", f.ident);
464 let gets = fields.iter().filter(|f| !f.skip).map(|f| {
465 let (s, ty, w) = (slot(f), &f.ty, &f.with);
466 quote! {
467 let #s: ::std::option::Option<#ty> = if __yog_u.more(__yog_end) {
468 ::std::option::Option::Some(<#w as #krate::with::With<#ty>>::unpack(__yog_u)?)
469 } else {
470 ::std::option::Option::None
471 };
472 }
473 });
474 let inits = fields.iter().map(|f| {
475 let (ident, ty, w) = (&f.ident, &f.ty, &f.with);
476 let value = if f.skip {
477 quote! { ::std::default::Default::default() }
478 } else {
479 let s = slot(f);
480 let missing = match &f.default {
481 None => quote! { <#w as #krate::with::With<#ty>>::absent()? },
482 Some(None) => quote! { ::std::default::Default::default() },
483 Some(Some(path)) => quote! { #path() },
484 };
485 quote! { match #s { ::std::option::Option::Some(__yog_v) => __yog_v, ::std::option::Option::None => #missing } }
486 };
487 quote! { #ident: #value }
488 });
489 quote! {
490 let __yog_end = __yog_u.begin()?;
491 #(#gets)*
492 __yog_u.leave(__yog_end)?;
493 ::std::option::Option::Some(#ctor { #(#inits),* })
494 }
495}
496
497fn expand_named(named: &syn::FieldsNamed, shape_name: &str, doc: &str) -> syn::Result<Tokens> {
499 let krate = krate();
500 let fields = named_fields(named)?;
501 let shapes = field_shapes(&fields);
502 let read = read_fields(&fields, quote! { Self });
503 let idents: Vec<_> = fields.iter().map(|f| &f.ident).collect();
504 let write = write_fields(&fields);
505 let encode = encode_fields(&fields);
506 let decode = decode_fields(&fields, quote! { Self }).map(|body| {
507 quote! {
508 fn decode(__yog_d: &mut #krate::binary::Decoder<'_>) -> ::std::option::Option<Self> {
509 #body
510 }
511 }
512 });
513 let pack = pack_fields(&fields);
514 let unpack = unpack_fields(&fields, quote! { Self });
515 Ok(quote! {
516 #[allow(unused_variables)]
518 fn encode(&self, __yog_w: &mut #krate::binary::Writer) {
519 let Self { #(#idents),* } = self;
520 #encode
521 }
522 #decode
523 #[allow(unused_variables)]
525 fn pack(&self, __yog_p: &mut #krate::frame::Packer) {
526 let Self { #(#idents),* } = self;
527 #pack
528 }
529 fn unpack(__yog_u: &mut #krate::frame::Unpacker<'_>) -> ::std::option::Option<Self> {
530 #unpack
531 }
532 fn shape() -> #krate::Shape {
533 #krate::Shape::Struct(#krate::schema::StructShape { name: #shape_name, doc: #doc, fields: #shapes })
534 }
535 fn read(v: &#krate::ValueRef<'_>, cx: &mut #krate::Reader) -> ::std::option::Option<Self> {
536 let map = cx.map(v)?;
537 #read
538 }
539 #[allow(unused_variables)]
541 fn write(&self) -> #krate::Value {
542 let Self { #(#idents),* } = self;
543 let mut m = #krate::Map::new();
544 #write
545 #krate::Value::Map(m)
546 }
547 })
548}
549
550fn view_items(input: &DeriveInput, fields: &[NamedField]) -> syn::Result<Tokens> {
554 let krate = krate();
555 let (name, vis) = (&input.ident, &input.vis);
556 if !input.generics.params.is_empty() {
557 return Err(syn::Error::new_spanned(
558 name,
559 "Schema: `view` is for a struct without generics",
560 ));
561 }
562 let view = quote::format_ident!("{}View", name);
563 let shown: Vec<&NamedField> = fields.iter().filter(|f| !f.skip).collect();
564 let types = shown.iter().map(|f| {
565 let (ident, ty) = (&f.ident, &f.ty);
566 if f.codec {
567 quote! { pub #ident: #ty, }
568 } else {
569 quote! { pub #ident: <#ty as #krate::frame::View<'a>>::View, }
570 }
571 });
572 let reads = shown.iter().map(|f| {
573 let (ident, ty, w) = (&f.ident, &f.ty, &f.with);
574 let read = if f.codec {
575 quote! { <#w as #krate::with::With<#ty>>::unpack(__yog_u)? }
576 } else {
577 quote! { <#ty as #krate::frame::View<'a>>::unpack_view(__yog_u)? }
578 };
579 quote! { let #ident = if __yog_u.more(__yog_end) { #read } else { return ::std::option::Option::None }; }
580 });
581 let idents = shown.iter().map(|f| &f.ident);
582 let doc =
583 format!("[`{name}`] viewed in a frame: strings borrowed, packed numbers read in place.");
584 Ok(quote! {
585 #[doc = #doc]
586 #vis struct #view<'a> {
587 #(#types)*
588 _frame: ::std::marker::PhantomData<&'a ()>,
589 }
590
591 impl<'a> #krate::frame::View<'a> for #name {
592 type View = #view<'a>;
593 type ListView = #krate::frame::List<'a, Self>;
594 fn unpack_view(__yog_u: &mut #krate::frame::Unpacker<'a>) -> ::std::option::Option<#view<'a>> {
595 let __yog_end = __yog_u.begin()?;
596 #(#reads)*
597 __yog_u.leave(__yog_end)?;
598 ::std::option::Option::Some(#view { #(#idents,)* _frame: ::std::marker::PhantomData })
599 }
600 fn unpack_list_view(__yog_u: &mut #krate::frame::Unpacker<'a>) -> ::std::option::Option<Self::ListView> {
601 #krate::frame::list(__yog_u)
602 }
603 }
604 })
605}
606
607fn expand(input: &DeriveInput, top: &Attrs) -> syn::Result<Tokens> {
608 let krate = krate();
609 let name = &input.ident;
610 let (impl_g, ty_g, where_g) = input.generics.split_for_impl();
611 let shape_name = top.rename.clone().unwrap_or_else(|| name.to_string());
612 let doc = docs(&input.attrs);
613 let body = match &input.data {
614 Data::Struct(s) => match &s.fields {
615 Fields::Named(named) => expand_named(named, &shape_name, &doc)?,
616 Fields::Unnamed(u) if u.unnamed.len() == 1 => expand_newtype(&u.unnamed[0].ty),
617 _ => {
618 return Err(syn::Error::new_spanned(
619 name,
620 "Schema: a struct needs named fields or exactly one unnamed field",
621 ));
622 }
623 },
624 Data::Enum(e) => enums::expand_enum(e, top, &shape_name, &doc)?,
625 Data::Union(_) => {
626 return Err(syn::Error::new_spanned(
627 name,
628 "Schema: unions aren't supported",
629 ));
630 }
631 };
632 let view = view_of(input, top)?;
633 let fields = fields_of(input)?;
634 Ok(quote! {
635 impl #impl_g #krate::Schema for #name #ty_g #where_g {
636 #body
637 }
638 #view
639 #fields
640 })
641}
642
643fn fields_of(input: &DeriveInput) -> syn::Result<Tokens> {
645 let krate = krate();
646 let name = &input.ident;
647 let (impl_g, ty_g, where_g) = input.generics.split_for_impl();
648 let Data::Struct(syn::DataStruct {
649 fields: Fields::Named(named),
650 ..
651 }) = &input.data
652 else {
653 return Ok(quote! {});
654 };
655 let pairs = named_fields(named)?.into_iter().map(|f| {
656 let ident = f.ident.to_string();
657 let ident = ident.trim_start_matches("r#");
658 let key = &f.key;
659 quote! { (#ident, #key) }
660 });
661 Ok(quote! {
662 impl #impl_g #krate::schema::Fields for #name #ty_g #where_g {
663 const FIELDS: &'static [(&'static str, &'static str)] = &[#(#pairs),*];
664 }
665 })
666}
667
668fn expand_newtype(ty: &Type) -> Tokens {
670 let krate = krate();
671 quote! {
672 fn shape() -> #krate::Shape { <#ty as #krate::Schema>::shape() }
673 fn read(v: &#krate::ValueRef<'_>, cx: &mut #krate::Reader) -> ::std::option::Option<Self> {
674 <#ty as #krate::Schema>::read(v, cx).map(Self)
675 }
676 fn write(&self) -> #krate::Value { #krate::Schema::write(&self.0) }
677 fn encode(&self, w: &mut #krate::binary::Writer) { #krate::Schema::encode(&self.0, w) }
678 fn decode(d: &mut #krate::binary::Decoder<'_>) -> ::std::option::Option<Self> {
679 <#ty as #krate::Schema>::decode(d).map(Self)
680 }
681 fn pack(&self, p: &mut #krate::frame::Packer) { #krate::Schema::pack(&self.0, p) }
682 fn unpack(u: &mut #krate::frame::Unpacker<'_>) -> ::std::option::Option<Self> {
683 <#ty as #krate::Schema>::unpack(u).map(Self)
684 }
685 fn absent() -> ::std::option::Option<Self> { <#ty as #krate::Schema>::absent().map(Self) }
686 fn skip(&self) -> bool { #krate::Schema::skip(&self.0) }
687 }
688}
689
690fn view_of(input: &DeriveInput, top: &Attrs) -> syn::Result<Tokens> {
692 let krate = krate();
693 let name = &input.ident;
694 Ok(match (&input.data, top.view) {
695 (
696 Data::Struct(syn::DataStruct {
697 fields: Fields::Named(named),
698 ..
699 }),
700 true,
701 ) => view_items(input, &named_fields(named)?)?,
702 (_, true) => {
703 return Err(syn::Error::new_spanned(
704 name,
705 "Schema: `view` is for a struct with named fields",
706 ));
707 }
708 (Data::Enum(e), false)
709 if top.tag.is_none()
710 && input.generics.params.is_empty()
711 && e.variants.iter().all(|v| matches!(v.fields, Fields::Unit)) =>
712 {
713 quote! {
714 impl<'a> #krate::frame::View<'a> for #name {
715 type View = Self;
716 type ListView = ::std::vec::Vec<Self>;
717 fn unpack_view(u: &mut #krate::frame::Unpacker<'a>) -> ::std::option::Option<Self> {
718 <Self as #krate::Schema>::unpack(u)
719 }
720 fn unpack_list_view(u: &mut #krate::frame::Unpacker<'a>) -> ::std::option::Option<::std::vec::Vec<Self>> {
721 <Self as #krate::Schema>::unpack_list(u)
722 }
723 }
724 }
725 }
726 _ => quote! {},
727 })
728}