//! Bounded `LLSD`/`OSD` support corresponding to `LibreMetaverse.StructuredData`.
//!
//! `XML`, `JSON`, binary, and notation codecs share checked depth, node, and byte
//! limits. Choose this crate for standalone structured-data work without
//! constructing a grid client or enabling a native codec.

extern crate self as libremetaverse_structured_data;

mod binary;
mod dispatch;
mod generated;
#[cfg(test)]
mod hardening;
mod json_codec;
mod model;
mod notation;
mod protobuf;
mod xml_codec;

pub mod xml {
    use std::sync::{Arc, Mutex};

    /// Project-owned XML element boundary; external XML APIs are not copied.
    #[derive(Clone, Debug, Default, Eq, PartialEq)]
    pub struct Element;

    /// Native XML reader boundary; external XML APIs are not copied.
    #[derive(Clone, Debug, Default, Eq, PartialEq)]
    pub struct Reader(pub String);

    /// Native XML writer boundary; external XML APIs are not copied.
    #[derive(Clone, Debug, Default)]
    pub struct Writer(pub Arc<Mutex<String>>);

    impl Writer {
        #[must_use]
        pub fn contents(&self) -> String {
            self.0
                .lock()
                .unwrap_or_else(std::sync::PoisonError::into_inner)
                .clone()
        }

        pub(crate) fn append(&self, value: &str) -> Result<(), crate::Error> {
            let mut output = self.0.lock().map_err(|_| crate::Error::InvalidOperation)?;
            let length = output
                .len()
                .checked_add(value.len())
                .ok_or(crate::Error::Argument)?;
            if length > crate::OSD::DEFAULT_MAX_BINARY_BYTES {
                return Err(crate::Error::Argument);
            }
            output.push_str(value);
            Ok(())
        }
    }
}

pub use generated::*;
pub use libremetaverse_types as types;
pub use libremetaverse_types::Error;

/// Decodes one bounded binary LLSD value and returns the number of consumed
/// bytes, allowing native container formats to locate payloads appended after
/// an LLSD header without duplicating the codec.
///
/// # Errors
///
/// Returns a positioned parse error when the prefix is malformed, truncated,
/// or exceeds the shared LLSD depth, node, input, or allocation bounds.
pub fn deserialize_llsd_binary_prefix(data: &[u8]) -> Result<(OSD, usize), Error> {
    binary::deserialize_prefix(data)
}
