use url::Url; use serde::{ Serialize, Serializer, ser::SerializeStruct, }; use super::attribute_types::{FixedSpace,ID,NameToken,AlignHV,AlignH,Measure,EnumeratedListType}; pub trait ExtraAttributes { // fn with_extra(extra: A) -> Self; fn extra (& self) -> & A; fn extra_mut(&mut self) -> &mut A; } macro_rules! count { () => (0usize); ( $x:tt $($xs:tt)* ) => (1usize + count!($($xs)*)); } macro_rules! ser_url { ($self:ident, refuri ) => { $self.refuri.as_ref().map(|uri| uri.to_string()) }; ($self:ident, uri ) => { $self.uri.to_string() }; ($self:ident, $param:ident) => { $self.$param }; } macro_rules! impl_extra { ( $name:ident { $( $param:ident : $type:ty ),* $(,)* } ) => ( impl_extra!( #[derive(Default,Debug)] $name { $( $param : $type, )* } ); ); ( $(#[$attr:meta])+ $name:ident { $( $param:ident : $type:ty ),* $(,)* } ) => ( $(#[$attr])+ pub struct $name { $( pub $param : $type, )* } impl Serialize for $name { fn serialize(&self, serializer: S) -> Result where S: Serializer { #[allow(unused_mut)] let mut state = serializer.serialize_struct(stringify!($name), count!($($param)*))?; $( state.serialize_field(stringify!($param), &ser_url!(self, $param))?; )* state.end() } } ); } impl_extra!(Address { space: FixedSpace }); impl_extra!(LiteralBlock { space: FixedSpace }); impl_extra!(DoctestBlock { space: FixedSpace }); impl_extra!(SubstitutionDefinition { ltrim: Option, rtrim: Option }); impl_extra!(Comment { space: FixedSpace }); impl_extra!(Target { refuri: Option, refid: Option, refname: Vec, anonymous: bool }); impl_extra!(Raw { space: FixedSpace, format: Vec }); impl_extra!(#[derive(Debug)] Image { align: Option, uri: Url, alt: Option, height: Option, width: Option, scale: Option, }); //bools usually are XML yesorno. “auto” however either exists and is set to something random like “1” or doesn’t exist impl_extra!(BulletList { bullet: Option }); impl_extra!(EnumeratedList { enumtype: Option, prefix: Option, suffix: Option }); impl_extra!(Footnote { backrefs: Vec, auto: Option }); impl_extra!(Citation { backrefs: Vec }); impl_extra!(SystemMessage { backrefs: Vec, level: Option, line: Option, type_: Option }); impl_extra!(Figure { align: Option, width: Option }); impl_extra!(Table {}); //TODO impl_extra!(OptionArgument { delimiter: Option }); impl_extra!(Reference { name: Option, refuri: Option, refid: Option, refname: Vec }); impl_extra!(FootnoteReference { refid: Option, refname: Vec, auto: Option }); impl_extra!(CitationReference { refid: Option, refname: Vec }); impl_extra!(SubstitutionReference { refname: Vec }); impl_extra!(Problematic { refid: Option }); //also have non-inline versions. Inline image is no figure child, inline target has content impl_extra!(TargetInline { refuri: Option, refid: Option, refname: Vec, anonymous: bool }); impl_extra!(RawInline { space: FixedSpace, format: Vec }); impl_extra!(#[derive(Debug)] ImageInline { align: Option, uri: Url, alt: Option, height: Option, width: Option, scale: Option, });