cbor-edn 0.0.11

Converter and processor for CBOR Diagnostic Notation (EDN)
Documentation
use super::{
    ApplicationLiteralsVisitor, DelimiterPolicy, Item, Sequence, StandaloneItem, TagVisitor,
    TrailingNewlinePolicy, Visitor,
};

/// Trait that covers operations that are possible on a [`StandaloneItem`], a [`Sequence`] or an
/// [`Item`].
pub trait Transformable<'a>: sealed::Transformable<'a> {
    /// Alters how space and comments are placed inside the item.
    ///
    /// See the policy values for details.
    fn set_delimiters(&mut self, policy: DelimiterPolicy);

    // FIXME: Those could be provided if sealed::Transformable had the visit function.

    /// For each item in the tree that is a single application literal, call a callback.
    ///
    /// This is primarily used to apply custom EDN filtering:
    ///
    /// ```rust
    /// # use cbor_edn::*;
    /// let mut full = Sequence::parse("0 /unmodified/, german'zweiundvierzig'").unwrap();
    /// full.visit_application_literals(&mut |id, value: String, item: &mut cbor_edn::Item| {
    ///     if id == "german" {
    ///         let numeric = match value.as_str() {
    ///             "dreiundzwanzig" => 23,
    ///             "zweiundvierzig" => 42,
    ///             _ => todo!(),
    ///         };
    ///         *item = Item::new_integer_decimal(numeric).into();
    ///     }
    ///     Ok(())
    /// });
    /// assert_eq!(full.serialize(), "0 /unmodified/, 42");
    /// ```
    fn visit_application_literals<F>(&mut self, f: &mut F)
    where
        F: FnMut(String, String, &mut Item<'a>) -> Result<(), String> + ?Sized,
    {
        self.visit(&mut ApplicationLiteralsVisitor { user_fn: f });
    }

    /// For each item in the full tree (including embedded representations) that is tagged, call a
    /// callback.
    ///
    /// Any error string is placed in a comment next to the item. The function should return Ok(())
    /// on any tags it is not interested in visiting.
    ///
    /// This is primarily used to apply custom EDN application; see [crate::application::dt_tag_to_aol] for an
    /// example.
    fn visit_tag<F>(&mut self, f: &mut F)
    where
        F: FnMut(u64, &mut Item<'a>) -> Result<(), String> + ?Sized,
    {
        self.visit(&mut TagVisitor { user_fn: f });
    }
}

pub(crate) mod sealed {
    use super::*;

    // Private methods go in here
    pub trait Transformable<'a> {
        fn toplevel_items<'b>(&'b mut self) -> impl Iterator<Item = &'b mut crate::Item<'a>>
        where
            'a: 'b;

        #[allow(private_bounds)] // reason: we're in a pub(crate) module and sealed::Transformable
                                 // is not pub used
        fn visit(&mut self, visitor: &mut impl Visitor<'a>);
    }
}

impl<'a> sealed::Transformable<'a> for StandaloneItem<'a> {
    fn toplevel_items<'b>(&'b mut self) -> impl Iterator<Item = &'b mut crate::Item<'a>>
    where
        'a: 'b,
    {
        core::iter::once(self.item_mut())
    }

    #[allow(private_bounds)] // reason: see trait definition
    fn visit(&mut self, visitor: &mut impl Visitor<'a>) {
        self.1
            .visit(visitor)
            .use_space_before(&mut self.0)
            .use_space_after(&mut self.2)
            .done();
    }
}
impl<'a> Transformable<'a> for StandaloneItem<'a> {
    fn set_delimiters(&mut self, policy: DelimiterPolicy) {
        // On the top level, let's not add the leading \n, because that would cause an empty line
        // above the sole element formatted like this.
        self.0.set_delimiters(policy, false);
        self.1.set_delimiters(policy);
        // FIXME: Does this also need the code from the Sequence cases?
        self.2.set_delimiters(policy, true);
    }
}

impl<'a> sealed::Transformable<'a> for Sequence<'a> {
    fn toplevel_items<'b>(&'b mut self) -> impl Iterator<Item = &'b mut crate::Item<'a>>
    where
        'a: 'b,
    {
        self.items_mut()
    }

    #[allow(private_bounds)] // reason: see trait definition
    fn visit(&mut self, visitor: &mut impl Visitor<'a>) {
        if let Some(nmv) = self.items.as_mut() {
            nmv.visit(visitor).use_space_after(&mut self.s0).done();
        }
    }
}
impl<'a> Transformable<'a> for Sequence<'a> {
    fn set_delimiters(&mut self, policy: DelimiterPolicy) {
        // On the top level, let's not add the leading \n, because that would cause an empty line
        // above the sole element formatted like this.
        self.s0.set_delimiters(policy, false);
        if let Some(items) = self.items.as_mut() {
            items.first.set_delimiters(policy);
            for (msc, item) in items.tail.iter_mut() {
                msc.set_delimiters(policy, true);
                item.set_delimiters(policy);
            }
            match policy {
                DelimiterPolicy::IndentedRegularSpacing {
                    trailing_newline: TrailingNewlinePolicy::Always,
                    ..
                } => items.soc.set_delimiters(policy, true),
                DelimiterPolicy::IndentedRegularSpacing {
                    trailing_newline: TrailingNewlinePolicy::Never,
                    ..
                } => items.soc.set_delimiters(policy, false),
                // FIXME: Should we be more explicit for the other cases, maybe even abandoning the
                // 2nd argument?
                _ => items.soc.set_delimiters(policy, !items.tail.is_empty()),
            }
        }
        // else, I guess that even under TrailingNewlinePolicy::Always, not sending \n is OK
        // because after all, we did end every line with a newline.
    }
}