diff --git a/static-xml-derive/src/deserialize.rs b/static-xml-derive/src/deserialize.rs index ad72503..deb77f8 100644 --- a/static-xml-derive/src/deserialize.rs +++ b/static-xml-derive/src/deserialize.rs @@ -91,28 +91,22 @@ fn finalize_visitor_fields(struct_: &ElementStruct) -> Vec { struct_.fields.iter().map(|field| { let span = field.inner.span(); let ty = &field.inner.ty; - let default = field.default.then(|| { - quote_spanned! {span=> - <#ty as ::std::default::Default>::default - } - }); + let default = match field.default { + true => quote_spanned! {span=> Some(<#ty as ::std::default::Default>::default) }, + false => quote! { None } + }; match field.mode { ElementFieldMode::Element { sorted_elements_pos: p } => { let field_present = format_ident!("{}_present", field.ident); let field_mut = field_mut(field); - let d = if let Some(d) = default { - quote! { Some(#d) } - } else { - quote! { None } - }; quote_spanned! {span=> // SAFETY: #field_mut and #field_present are valid/consistent. unsafe { - ::static_xml::de::DeserializeElementField::finalize( + ::static_xml::de::ElementField::finalize( &mut *(#field_mut as *mut ::std::mem::MaybeUninit<#ty>), &mut self.#field_present, &ELEMENTS[#p], - #d, + #default, )?; } } @@ -120,28 +114,22 @@ fn finalize_visitor_fields(struct_: &ElementStruct) -> Vec { ElementFieldMode::Attribute { sorted_attributes_pos: p } => { let field_present = format_ident!("{}_present", field.ident); let field_mut = field_mut(field); - let value = match default { - Some(d) => quote! { #d() }, - None => quote_spanned! {span=> - <#ty as ::static_xml::de::DeserializeAttrField>::missing(&ATTRIBUTES[#p])? - } - }; quote_spanned! {span=> - if !self.#field_present { - // SAFETY: #field_mut is a valid pointer to uninitialized memory. - unsafe { ::std::ptr::write(#field_mut /*as *mut #ty*/, #value) }; + // SAFETY: #field_mut and #field_present are valid/consistent. + unsafe { + ::static_xml::de::AttrField::finalize( + &mut *(#field_mut as *mut ::std::mem::MaybeUninit<#ty>), + &mut self.#field_present, + &ATTRIBUTES[#p], + #default, + )?; } } } ElementFieldMode::Flatten => { let field_visitor = format_ident!("{}_visitor", field.ident); - let d = if let Some(d) = default { - quote! { Some(#d) } - } else { - quote! { None } - }; quote_spanned! {span=> - <#ty as ::static_xml::de::RawDeserialize>::Visitor::finalize(self.#field_visitor, #d)?; + <#ty as ::static_xml::de::RawDeserialize>::Visitor::finalize(self.#field_visitor, #default)?; } } ElementFieldMode::Text => todo!(), @@ -172,21 +160,19 @@ fn attribute_match_branches(struct_: &ElementStruct) -> Vec { .enumerate() .map(|(i, &p)| { let field = &struct_.fields[p]; - let field_present = format_ident!("{}_present", field.ident); + let ty = &field.inner.ty; + let span = field.inner.span(); + let field_present = format_ident!("{}_present", &field.ident); let field_mut = field_mut(field); - quote! { - Some(#i) if self.#field_present => { - Err(::static_xml::de::VisitorError::duplicate_attribute(name)) - } - Some(#i) => { - // SAFETY: #field_mut is a valid pointer to uninitialized memory. - unsafe { - ::std::ptr::write( - #field_mut, - ::static_xml::de::DeserializeAttrField::init(value)?, - ); - } - self.#field_present = true; + quote_spanned! {span=> + // SAFETY: #field_mut and #field_present are consistent/valid. + Some(#i) => unsafe { + ::static_xml::de::AttrField::attribute( + &mut *(#field_mut as *mut ::std::mem::MaybeUninit<#ty>), + &mut self.#field_present, + name, + value, + )?; Ok(None) } } @@ -208,7 +194,7 @@ fn element_match_branches(struct_: &ElementStruct) -> Vec { quote_spanned! {span=> // SAFETY: #field_mut and #field_present are consistent/valid. Some(#i) => unsafe { - ::static_xml::de::DeserializeElementField::element( + ::static_xml::de::ElementField::element( &mut *(#field_mut as *mut ::std::mem::MaybeUninit<#ty>), &mut self.#field_present, child, @@ -362,7 +348,7 @@ fn do_enum(enum_: &ElementEnum) -> TokenStream { // This is a bit silly when we already know it's // initialized, but it lets us reuse code. let mut initialized = true; - ::static_xml::de::DeserializeElementField::element( + ::static_xml::de::ElementField::element( std::mem::transmute::<&mut #ty, &mut std::mem::MaybeUninit<#ty>>(f), &mut initialized, child, @@ -393,7 +379,7 @@ fn do_enum(enum_: &ElementEnum) -> TokenStream { #ident::#vident(unsafe { let mut value = ::std::mem::MaybeUninit::<#ty>::uninit(); let mut initialized = false; - ::static_xml::de::DeserializeElementField::element( + ::static_xml::de::ElementField::element( &mut value, &mut initialized, child diff --git a/static-xml/src/de/mod.rs b/static-xml/src/de/mod.rs index fa2ff61..48b9b6b 100644 --- a/static-xml/src/de/mod.rs +++ b/static-xml/src/de/mod.rs @@ -682,7 +682,7 @@ pub trait Deserialize: Sized { /// 3. `Vec`, for repeated fields. In XML Schema terms, /// `minOccurs="0" maxOccurs="unbounded"`. #[doc(hidden)] -pub trait DeserializeElementField: Sized { +pub trait ElementField: Sized { unsafe fn element( field: &mut MaybeUninit, initialized: &mut bool, @@ -789,13 +789,13 @@ pub type DeserializeFn = unsafe fn(field: Field<'_>, child: ElementReader<'_>) - pub type FinalizeFn = unsafe fn(field: Field<'_>, default_fn: Option<&()>, err_fn: &dyn Fn() -> VisitorError) -> Result<(), VisitorError>; #[doc(hidden)] -pub struct ElementVtable { - pub type_: ElementVtableType, +pub struct FieldVtable { + pub type_: FieldVtableType, pub finalize: FinalizeFn, } #[doc(hidden)] -pub enum ElementVtableType { +pub enum FieldVtableType { Text { // or simple? parse: Option, // TODO: write. @@ -806,27 +806,42 @@ pub enum ElementVtableType { }, } -impl ElementVtable { +impl FieldVtable { pub unsafe fn deserialize(&self, field: Field, child: ElementReader<'_>) -> Result<(), VisitorError> { if *field.initialized && matches!(field.field_type, FieldType::Direct | FieldType::Option) { return Err(VisitorError::duplicate_element(&child.expanded_name())); } match self { - ElementVtable { type_: ElementVtableType::Text { parse }, .. } => { + FieldVtable { type_: FieldVtableType::Text { parse }, .. } => { let parse = parse.unwrap(); parse(field, child.read_string()?).map_err(VisitorError::Wrap) } - ElementVtable { type_: ElementVtableType::Element { deserialize }, .. } => { + FieldVtable { type_: FieldVtableType::Element { deserialize }, .. } => { let deserialize = deserialize.unwrap(); deserialize(field, child) } } } + + pub unsafe fn parse(&self, field: Field, name: &ExpandedNameRef, value: String) -> Result<(), VisitorError> { + debug_assert!(matches!(field.field_type, FieldType::Direct | FieldType::Option)); + if *field.initialized { + return Err(VisitorError::duplicate_attribute(name)); + } + match self { + FieldVtable { type_: FieldVtableType::Text { parse }, .. } => { + let parse = parse.unwrap(); + parse(field, value).map_err(VisitorError::Wrap) + } + FieldVtable { type_: FieldVtableType::Element { .. }, .. } => unreachable!(), + } + + } } #[doc(hidden)] -pub unsafe trait HasElementVtable: 'static { - const VTABLE: &'static ElementVtable; +pub unsafe trait HasFieldVtable: 'static { + const VTABLE: &'static FieldVtable; } #[doc(hidden)] @@ -846,15 +861,15 @@ macro_rules! text_vtables { ) -> Result<(), $crate::de::VisitorError> { field.finalize::<$t>(default_fn, err_fn) } - const VTABLE: $crate::de::ElementVtable = $crate::de::ElementVtable { - type_: $crate::de::ElementVtableType::Text { + const VTABLE: $crate::de::FieldVtable = $crate::de::FieldVtable { + type_: $crate::de::FieldVtableType::Text { // XXX: why isn't this working? https://github.com/rust-lang/rust/issues/39817 parse: Some(parse), }, finalize, }; - unsafe impl $crate::de::HasElementVtable for $t { - const VTABLE: &'static $crate::de::ElementVtable = &VTABLE; + unsafe impl $crate::de::HasFieldVtable for $t { + const VTABLE: &'static $crate::de::FieldVtable = &VTABLE; } }; // TODO: likewise for attr, text. @@ -881,14 +896,14 @@ macro_rules! deserialize_vtable { ) -> Result<(), $crate::de::VisitorError> { field.finalize::<$t>(default_fn, err_fn) } - const VTABLE: $crate::de::ElementVtable = $crate::de::ElementVtable { - type_: $crate::de::ElementVtableType::Element { + const VTABLE: $crate::de::FieldVtable = $crate::de::FieldVtable { + type_: $crate::de::FieldVtableType::Element { deserialize: Some(deserialize), }, finalize, }; - unsafe impl $crate::de::HasElementVtable for $t { - const VTABLE: &'static $crate::de::ElementVtable = &VTABLE; + unsafe impl $crate::de::HasFieldVtable for $t { + const VTABLE: &'static $crate::de::FieldVtable = &VTABLE; } }; } @@ -897,12 +912,21 @@ macro_rules! deserialize_vtable { /// Deserializes an attribute into a field. /// /// This is implemented via [`ParseText`] as noted there. -pub trait DeserializeAttrField: Sized { - /// Called iff this field's attribute was found within the parent. - fn init(value: String) -> Result; +#[doc(hidden)] +pub trait AttrField: Sized { + unsafe fn attribute( + field: &mut MaybeUninit, + initialized: &mut bool, + name: &ExpandedNameRef<'_>, + value: String, + ) -> Result<(), VisitorError>; - /// Called iff this field's attribute was not found within the parent. - fn missing(expected: &ExpandedNameRef<'_>) -> Result; + unsafe fn finalize( + field: &mut MaybeUninit, + initialized: &mut bool, + expected: &ExpandedNameRef<'_>, + default: Option Self>, + ) -> Result<(), VisitorError>; } #[doc(hidden)] @@ -1208,7 +1232,7 @@ pub fn find(name: &ExpandedNameRef<'_>, sorted_slice: &[ExpandedNameRef<'_>]) -> macro_rules! element_field { ( $out_type:ty, $field_type:ident ) => { - impl DeserializeElementField for $out_type { + impl ElementField for $out_type { #[inline] unsafe fn element( field: &mut MaybeUninit, @@ -1248,25 +1272,48 @@ element_field!(T, Direct); element_field!(Option, Option); element_field!(Vec, Vec); -impl DeserializeAttrField for T { - fn init(value: String) -> Result { - Ok(T::parse(value).map_err(VisitorError::Wrap)?) - } +macro_rules! attr_field { + ( $out_type:ty, $field_type:ident ) => { + impl AttrField for $out_type { + #[inline] + unsafe fn attribute( + field: &mut MaybeUninit, + initialized: &mut bool, + name: &ExpandedNameRef, + value: String, + ) -> Result<(), VisitorError> { + let field = Field { + ptr: field.as_mut_ptr() as *mut (), + field_type: FieldType::$field_type, + initialized, + }; + T::VTABLE.parse(field, name, value) + } - fn missing(expected: &ExpandedNameRef<'_>) -> Result { - Err(VisitorError::missing_attribute(expected)) + #[inline] + unsafe fn finalize( + field: &mut MaybeUninit, + initialized: &mut bool, + expected: &ExpandedNameRef<'_>, + default: Option Self>, + ) -> Result<(), VisitorError> { + let field = Field { + ptr: field.as_mut_ptr() as *mut (), + field_type: FieldType::$field_type, + initialized, + }; + (T::VTABLE.finalize)( + field, + std::mem::transmute::<_, _>(default), + &|| VisitorError::missing_element(expected), + ) + } + } } } +attr_field!(T, Direct); +attr_field!(Option, Option); -impl DeserializeAttrField for Option { - fn init(value: String) -> Result { - Ok(Some(T::parse(value).map_err(VisitorError::Wrap)?)) - } - - fn missing(_expected: &ExpandedNameRef<'_>) -> Result { - Ok(None) - } -} /* #[doc(hidden)]