Documentation
/*
==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--

SJ

Copyright (C) 2019-2025  Anonymous

There are several releases over multiple years,
they are listed as ranges, such as: "2019-2025".

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.

::--::--::--::--::--::--::--::--::--::--::--::--::--::--::--::--
*/

//! # Shortcuts for `Json::Object`

mod inner;

use crate::{BTreeMap, Error, Json, Map, MapKind, ObjectIndexes, ObjectKey, Result};

#[cfg(feature="std")]
use crate::HashMap;

/// # Shortcuts for [`Object`](#variant.Object)
impl Json {

    /// # If the value is an object, inserts new item into it
    ///
    /// On success, returns previous value (if it existed).
    ///
    /// Returns an error if the value is not an object.
    pub fn insert<K, V>(&mut self, key: K, value: V) -> Result<Option<Self>> where K: Into<ObjectKey>, V: Into<Self> {
        match self {
            Json::Object(map) => Ok(map.insert(key, value)),
            _ => Err(e!("Json is not an Object")),
        }
    }

    /// # Gets an immutable item from this object and its sub objects
    ///
    /// The function returns an error on one of these conditions:
    ///
    /// - Keys are empty.
    /// - The value or any of its sub items is not an object.
    ///
    /// ## Examples
    ///
    /// ```
    /// use sj::{Map, MapKind};
    ///
    /// let mut object = sj::object(MapKind::BTreeMap);
    /// object.insert("first", true)?;
    /// object.insert("second", {
    ///     let mut map = sj::Map::new(MapKind::BTreeMap);
    ///     map.insert("zero", 0);
    ///     map
    /// })?;
    ///
    /// assert_eq!(bool::try_from(object.by("first")?)?, true);
    /// assert_eq!(u8::try_from(object.by(["second", "zero"])?)?, 0);
    ///
    /// assert!(object.by("something").is_err());
    /// assert!(object.maybe_by("something")?.is_none());
    ///
    /// assert!(object.by(&[][..]).is_err());
    /// assert!(object.by(["first", "last"]).is_err());
    /// assert!(object.by(["second", "third", "fourth"]).is_err());
    ///
    /// # Ok::<_, sj::Error>(())
    /// ```
    pub fn by<'a, K, const N: usize>(&self, keys: K) -> Result<&Self> where K: Into<ObjectIndexes<'a, N>> {
        let (result, keys) = inner::maybe_by(self, keys)?;
        result.ok_or_else(|| err!("There is no value at: {keys:?}"))
    }

    /// # Gets an immutable item from this object and its sub objects
    ///
    /// The function returns an error on one of these conditions:
    ///
    /// - Keys are empty.
    /// - The value or any of its sub items is not an object.
    ///
    /// If there is no value, [`Null`](#variant.Null) is returned.
    pub fn by_or_null<'a, K, const N: usize>(&self, keys: K) -> Result<&Self> where K: Into<ObjectIndexes<'a, N>> {
        let (result, _) = inner::maybe_by(self, keys)?;
        Ok(result.unwrap_or(&Self::Null))
    }

    /// # Gets an optional immutable item from this object and its sub objects
    ///
    /// The function returns an error on one of these conditions:
    ///
    /// - Keys are empty.
    /// - The value or any of its sub items is not an object.
    pub fn maybe_by<'a, K, const N: usize>(&self, keys: K) -> Result<Option<&Self>> where K: Into<ObjectIndexes<'a, N>> {
        inner::maybe_by(self, keys).map(|(result, _)| result)
    }

    /// # Gets a mutable item from this object and its sub objects
    ///
    /// The function returns an error on one of these conditions:
    ///
    /// - Keys are empty.
    /// - The value or any of its sub items is not an object.
    pub fn mut_by<'a, K, const N: usize>(&mut self, keys: K) -> Result<&mut Self> where K: Into<ObjectIndexes<'a, N>> {
        let (result, keys) = inner::maybe_mut_by(self, keys)?;
        result.ok_or_else(|| err!("There is no value at: {keys:?}"))
    }

    /// # Gets an optional mutable item from this object and its sub objects
    ///
    /// The function returns an error on one of these conditions:
    ///
    /// - Keys are empty.
    /// - The value or any of its sub items is not an object.
    pub fn maybe_mut_by<'a, K, const N: usize>(&mut self, keys: K) -> Result<Option<&mut Self>> where K: Into<ObjectIndexes<'a, N>> {
        inner::maybe_mut_by(self, keys).map(|(result, _)| result)
    }

    /// # Takes an item from this object and its sub objects
    ///
    /// The function returns an error on one of these conditions:
    ///
    /// - Keys are empty.
    /// - The value or any of its sub items is not an object.
    ///
    /// ## Examples
    ///
    /// ```
    /// use sj::{Map, MapKind};
    ///
    /// let mut object = sj::object(MapKind::BTreeMap);
    /// object.insert("earth", "moon")?;
    /// object.insert("solar-system", {
    ///     let mut map = sj::Map::new(MapKind::BTreeMap);
    ///     map.insert("sun", "earth");
    ///     map
    /// })?;
    ///
    /// assert_eq!(object.take_by("earth")?.as_str()?, "moon");
    /// assert_eq!(object.take_by(["solar-system", "sun"])?.as_str()?, "earth");
    ///
    /// assert!(object.take_by("milky-way").is_err());
    /// assert!(object.maybe_take_by("milky-way")?.is_none());
    /// assert!(object.maybe_take_by(["solar-system", "mars"])?.is_none());
    ///
    /// assert!(object.take_by(&[][..]).is_err());
    /// assert!(object.take_by(["jupiter", "venus"]).is_err());
    ///
    /// # Ok::<_, sj::Error>(())
    /// ```
    pub fn take_by<'a, K, const N: usize>(&mut self, keys: K) -> Result<Self> where K: Into<ObjectIndexes<'a, N>> {
        let (result, keys) = inner::maybe_take_by(self, keys)?;
        result.ok_or_else(|| err!("There is no value at: {keys:?}"))
    }

    /// # Takes an item from this object and its sub objects
    ///
    /// The function returns an error on one of these conditions:
    ///
    /// - Keys are empty.
    /// - The value or any of its sub items is not an object.
    ///
    /// If there is no value, [`Null`](#variant.Null) is returned.
    pub fn take_by_or_null<'a, K, const N: usize>(&mut self, keys: K) -> Result<Self> where K: Into<ObjectIndexes<'a, N>> {
        let (result, _) = inner::maybe_take_by(self, keys)?;
        Ok(result.unwrap_or(Self::Null))
    }

    /// # Takes an optional item from this object and its sub objects
    ///
    /// The function returns an error on one of these conditions:
    ///
    /// - Keys are empty.
    /// - The value or any of its sub items is not an object.
    pub fn maybe_take_by<'a, K, const N: usize>(&mut self, keys: K) -> Result<Option<Self>> where K: Into<ObjectIndexes<'a, N>> {
        inner::maybe_take_by(self, keys).map(|(result, _)| result)
    }

    /// # If the value is an object, returns an immutable reference of it
    ///
    /// Returns an error if the value is not an object.
    pub fn as_object(&self) -> Result<&Map> {
        match self {
            Json::Object(object) => Ok(object),
            _ => Err(e!("Json is not an Object")),
        }
    }

    /// # If the value is an object, returns a mutable reference of it
    ///
    /// Returns an error if the value is not an object.
    pub fn as_mut_object(&mut self) -> Result<&mut Map> {
        match self {
            Json::Object(object) => Ok(object),
            _ => Err(e!("Json is not an Object")),
        }
    }

}

impl From<Map> for Json {

    fn from(map: Map) -> Self {
        Self::Object(map)
    }

}

impl From<BTreeMap> for Json {

    fn from(map: BTreeMap) -> Self {
        Self::Object(Map::BTreeMap(map))
    }

}

#[cfg(feature="std")]
impl From<HashMap> for Json {

    fn from(map: HashMap) -> Self {
        Self::Object(Map::HashMap(map))
    }

}

impl TryFrom<Json> for Map {

    type Error = Error;

    fn try_from(j: Json) -> Result<Self> {
        match j {
            Json::Object(map) => Ok(map),
            _ => Err(e!("Not a Map")),
        }
    }

}

impl TryFrom<Json> for BTreeMap {

    type Error = Error;

    fn try_from(j: Json) -> Result<Self> {
        match j {
            Json::Object(map) => Ok(map.into()),
            _ => Err(e!("Not a Map")),
        }
    }

}

#[cfg(feature="std")]
impl TryFrom<Json> for HashMap {

    type Error = Error;

    fn try_from(j: Json) -> Result<Self> {
        match j {
            Json::Object(map) => Ok(map.into()),
            _ => Err(e!("Not a Map")),
        }
    }

}

impl<I, K, V> From<(MapKind, I)> for Json where I: IntoIterator<Item=(K, V)>, K: Into<ObjectKey>, V: Into<Self> {

    fn from((map_kind, iter): (MapKind, I)) -> Self {
        let iter = iter.into_iter().map(|(k, v)| (k.into(), v.into()));
        Self::Object(match map_kind {
            MapKind::BTreeMap => Map::BTreeMap(BTreeMap::from_iter(iter)),
            #[cfg(feature="std")]
            MapKind::HashMap => Map::HashMap(HashMap::from_iter(iter)),
        })
    }

}