//! # dynojson
//!
//! **Marshall / unmarshall JSON to and from DynamoDB JSON format.**
//!
//! This crate provides a fast, pure-Rust implementation of the DynamoDB JSON
//! conversion routines (equivalent to the AWS SDK `@aws-sdk/util-dynamodb`
//! `marshall` / `unmarshall` helpers) plus a JSON property-path extractor
//! with dot-notation and wildcard support.
//!
//! It can be used as a **Rust library** (`cargo add dynojson`) or as a
//! **Python package** (`pip install dynojson`) — the Python extension is
//! compiled via [PyO3](https://pyo3.rs) when the `python` feature is enabled.
//!
//! ## Rust usage
//!
//! ```rust
//! use dynojson::marshall::marshall_top_level;
//! use dynojson::unmarshall::unmarshall_top_level;
//!
//! let regular = serde_json::json!({"name": "Alice", "age": 30});
//! let ddb = marshall_top_level(&regular);
//! let back = unmarshall_top_level(&ddb).unwrap();
//! assert_eq!(regular, back);
//! ```
//!
//! ## Modules
//!
//! - [`marshall`] — regular JSON → DynamoDB JSON
//! - [`unmarshall`] — DynamoDB JSON → regular JSON
//! - [`property`] — dot-path property extraction with `*` wildcard
//! - [`error`] — error types

pub mod error;
pub mod marshall;
pub mod property;
pub mod unmarshall;

// ── Python bindings (only compiled with the "python" feature) ──────────────

#[cfg(feature = "python")]
mod python {
    use pyo3::exceptions::PyValueError;
    use pyo3::prelude::*;

    use crate::error::DynoError;
    use crate::{marshall, property, unmarshall};

    /// Package version, injected at compile time from Cargo.toml.
    const VERSION: &str = env!("CARGO_PKG_VERSION");

    /// Convert a [`DynoError`] into a Python `ValueError`.
    fn dyno_err(err: DynoError) -> PyErr {
        PyValueError::new_err(err.to_string())
    }

    /// Convert a [`serde_json::Error`] into a Python `ValueError`.
    fn json_err(err: serde_json::Error) -> PyErr {
        PyValueError::new_err(err.to_string())
    }

    /// Convert a regular JSON string to DynamoDB JSON format.
    #[pyfunction]
    #[pyo3(name = "marshall")]
    fn py_marshall(json_str: &str) -> PyResult<String> {
        let value: serde_json::Value = serde_json::from_str(json_str).map_err(json_err)?;
        let converted = marshall::marshall_top_level(&value);
        serde_json::to_string(&converted).map_err(json_err)
    }

    /// Convert a DynamoDB JSON string to regular JSON format.
    #[pyfunction]
    #[pyo3(name = "unmarshall")]
    fn py_unmarshall(json_str: &str) -> PyResult<String> {
        let value: serde_json::Value = serde_json::from_str(json_str).map_err(json_err)?;
        let converted = unmarshall::unmarshall_top_level(&value).map_err(dyno_err)?;
        serde_json::to_string(&converted).map_err(json_err)
    }

    /// Extract a nested property from a JSON string by dot-separated path.
    #[pyfunction]
    #[pyo3(name = "get_property")]
    fn py_get_property(json_str: &str, path: &str) -> PyResult<String> {
        let value: serde_json::Value = serde_json::from_str(json_str).map_err(json_err)?;
        let result = property::get_property(&value, path).map_err(dyno_err)?;

        match result {
            Some(v) => serde_json::to_string(&v).map_err(json_err),
            None => Err(dyno_err(DynoError::PropertyNotFound {
                path: path.to_owned(),
            })),
        }
    }

    /// The native extension module exposed to Python as `dynojson._dynojson`.
    #[pymodule]
    #[pyo3(name = "_dynojson")]
    pub fn init(m: &Bound<'_, PyModule>) -> PyResult<()> {
        m.add("__version__", VERSION)?;
        m.add_function(wrap_pyfunction!(py_marshall, m)?)?;
        m.add_function(wrap_pyfunction!(py_unmarshall, m)?)?;
        m.add_function(wrap_pyfunction!(py_get_property, m)?)?;
        Ok(())
    }
}
