mod xcoder;
use quote::ToTokens;
use std::collections::HashMap;
use xcoder::FieldXcoder;
use crate::ast::attr::container::default::DefaultXcoder;
use crate::ast::types::{
DefaultValue, LatebindXcoder, SiguledXcoder, XcoderLike, XcoderSigil, XcoderType,
};
use crate::symbol;
use crate::Ctxt;
#[derive(Debug)]
pub(crate) struct Field {
pub contents: FieldXcoder,
}
impl Field {
pub fn from_ast(
cx: &Ctxt,
field: &syn::Field,
name: &syn::Ident,
container_defaults: &HashMap<syn::Type, DefaultXcoder>,
allow_unimplemented_encode: bool,
allow_unimplemented_decode: bool,
trait_fallback: bool,
) -> Option<Self> {
if field.attrs.is_empty() {
return None;
}
let mut all_field_xcoders = Vec::new();
for attr in &field.attrs {
if attr.path() != symbol::KLV_ATTR {
continue;
}
match attr.meta {
syn::Meta::List(ref contents) => {
all_field_xcoders.push(FieldXcoder::from(contents))
}
_ => {
cx.error_spanned_by(attr, err!(MalformedField));
}
}
}
if all_field_xcoders.is_empty() {
return None;
}
all_field_xcoders
.iter()
.filter_map(|f| f.errors.clone())
.for_each(|e| cx.syn_error(e));
let mut field_xcoder = FieldXcoder::default();
let keys = all_field_xcoders
.iter()
.filter_map(|f| f.key.clone())
.collect::<Vec<_>>();
field_xcoder.key = match keys.len() {
0 => {
cx.error_spanned_by(field, err!(MissingKeyInField));
None
}
1 => Some(keys[0].clone()),
_ => {
cx.error_spanned_by(field, err!(DuplicateKey));
None
}
};
let mut keep_enc_none = false;
let encoders = all_field_xcoders
.iter()
.filter_map(|f| f.enc.clone())
.collect::<Vec<_>>();
field_xcoder.enc = match encoders.len() {
0 => None,
1 => Some(encoders[0].clone()),
_ => {
cx.error_spanned_by(field, err!(DuplicateEncoderInField));
keep_enc_none = true;
None
}
};
let mut keep_dec_none = false;
let decoders = all_field_xcoders
.iter()
.filter_map(|f| f.dec.clone())
.collect::<Vec<_>>();
field_xcoder.dec = match decoders.len() {
0 => None,
1 => Some(decoders[0].clone()),
_ => {
cx.error_spanned_by(field, err!(DuplicateDecoderInField));
keep_dec_none = true;
None
}
};
let vars = all_field_xcoders
.iter()
.filter_map(|f| f.varlen.clone())
.collect::<Vec<_>>();
field_xcoder.varlen = match vars.len() {
0 => None,
1 => Some(vars[0].clone()),
_ => {
cx.error_spanned_by(field, err!(DuplicateVariableLengthInField));
None
}
};
let latebinds = all_field_xcoders
.iter()
.filter_map(|f| f.latebind.clone())
.collect::<Vec<_>>();
field_xcoder.latebind = match latebinds.len() {
0 => None,
1 => Some(latebinds[0].clone()),
_ => {
cx.error_spanned_by(field, err!(DuplicateLatebindInField));
None
}
};
let defaults = all_field_xcoders
.iter()
.filter_map(|f| f.default.clone())
.collect::<Vec<_>>();
field_xcoder.default = match defaults.len() {
0 => None,
1 => Some(defaults[0].clone()),
_ => {
cx.error_spanned_by(field, err!(DuplicateDefaultInField));
None
}
};
let typ_maybe_unwrapped =
crate::expand::helpers::unwrap_option_type(&field.ty).unwrap_or(&field.ty);
if let Some(default) = container_defaults.get(typ_maybe_unwrapped) {
if let (Some(default_enc), false) = (&default.enc, keep_enc_none) {
field_xcoder.enc = Some(default_enc.clone());
}
if let (Some(default_dec), false) = (&default.dec, keep_dec_none) {
field_xcoder.dec = Some(default_dec.clone());
field_xcoder.varlen = default.var.clone();
}
}
if trait_fallback
&& !keep_enc_none
&& !allow_unimplemented_encode
&& field_xcoder.enc.is_none()
{
let placeholder: syn::Path = syn::parse_quote! { __tinyklv_fallback_enc };
field_xcoder.enc = Some(SiguledXcoder {
sigil: XcoderSigil::None,
inner: XcoderLike::Path(placeholder),
});
field_xcoder.fallback_enc = true;
}
if trait_fallback
&& !keep_dec_none
&& !allow_unimplemented_decode
&& field_xcoder.dec.is_none()
{
let varlen_set = field_xcoder
.varlen
.as_ref()
.map(|v| v.value)
.unwrap_or(false);
if varlen_set {
cx.error_spanned_by(
name.clone(),
err!(VarlenFallbackRequiresExplicitDec(name, typ_maybe_unwrapped)),
);
} else {
let placeholder: syn::Path = syn::parse_quote! { __tinyklv_fallback_dec };
field_xcoder.dec = Some(XcoderLike::Path(placeholder));
field_xcoder.fallback_dec = true;
}
}
if !allow_unimplemented_encode && field_xcoder.enc.is_none() {
cx.error_spanned_by(
name.clone(),
err!(UnimplementedEncode(name, typ_maybe_unwrapped)),
);
}
if !allow_unimplemented_decode && field_xcoder.dec.is_none() {
cx.error_spanned_by(
name.clone(),
err!(UnimplementedDecode(name, typ_maybe_unwrapped)),
);
}
Some(Field {
contents: field_xcoder,
})
}
}
pub(crate) struct FieldParsed {
pub key: syn::Lit,
pub enc: Option<SiguledXcoder>,
pub dec: Option<XcoderType>,
pub var: Option<syn::LitBool>,
pub latebind: Option<LatebindXcoder>,
pub default: Option<DefaultValue>,
pub fallback_enc: bool,
pub fallback_dec: bool,
}
impl FieldParsed {
pub fn from_field(cx: &Ctxt, sf: &syn::Field, f: &Field) -> Option<FieldParsed> {
let key = match &f.contents.key {
Some(k) => k,
None => {
cx.error_spanned_by(sf.ident.clone(), err!(MissingKeyInField));
return None;
}
};
Some(FieldParsed {
key: key.clone(),
enc: f.contents.enc.clone(),
dec: f.contents.dec.clone(),
var: f.contents.varlen.clone(),
latebind: f.contents.latebind.clone(),
default: f.contents.default.clone(),
fallback_enc: f.contents.fallback_enc,
fallback_dec: f.contents.fallback_dec,
})
}
}