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::Array`

use {
    core::iter::FromIterator,
    crate::{Array, ArrayIndexes, Error, Json, Result},
};

/// # Helper macro for Json::at()/mut_at()
macro_rules! at_or_mut_at { ($self: ident, $indexes: ident, $code: tt) => {{
    let indexes = $indexes.into();
    let mut value = Some($self);
    for (nth, idx) in indexes.iter().enumerate() {
        match value {
            Some(Json::Array(array)) => {
                value = array.$code(*idx);
                if nth + 1 == indexes.len() {
                    return value.ok_or_else(|| err!("Indexes are invalid: {indexes:?}"));
                }
            },
            Some(_) => return Err(Error::from(match nth {
                0 => e!("Json is not an Array"),
                _ => err!("Json at {:?} is not an Array", indexes.index_with_range_to(..nth)),
            })),
            None => return Err(err!("There is no value at {:?}", indexes.index_with_range_to(..nth))),
        };
    }

    Err(e!("Indexes must not be empty"))
}}}

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

    /// # If the value is an array, pushes new item into it
    ///
    /// Returns an error if the value is not an array.
    pub fn push<T>(&mut self, value: T) -> Result<()> where T: Into<Self> {
        match self {
            Json::Array(array) => Ok(crate::push(array, value)),
            _ => Err(e!("Json is not an Array")),
        }
    }

    /// # Gets an immutable item from this array and its sub arrays
    ///
    /// The function returns an error on one of these conditions:
    ///
    /// - Indexes are empty or invalid.
    /// - The value or any of its sub items is not an array.
    ///
    /// ## Examples
    ///
    /// ```
    /// let mut array = sj::array();
    /// array.push("first")?;
    /// array.push(vec![false.into(), "second".into()])?;
    ///
    /// assert_eq!(array.at(0)?.as_str()?, "first");
    /// assert_eq!(array.at([1, 1])?.as_str()?, "second");
    ///
    /// assert!(array.at(&[][..]).is_err());
    /// assert!(array.at([0, 1]).is_err());
    /// assert!(array.at([1, 2]).is_err());
    /// assert!(array.at([1, 0, 0]).is_err());
    /// assert!(array.at([1, 1, 2]).is_err());
    ///
    /// # Ok::<_, sj::Error>(())
    /// ```
    pub fn at<'a, I, const N: usize>(&self, indexes: I) -> Result<&Self> where I: Into<ArrayIndexes<'a, N>> {
        at_or_mut_at!(self, indexes, get)
    }

    /// # Gets a mutable item from this array and its sub arrays
    ///
    /// The function returns an error on one of these conditions:
    ///
    /// - Indexes are empty or invalid.
    /// - The value or any of its sub items is not an array.
    pub fn mut_at<'a, I, const N: usize>(&mut self, indexes: I) -> Result<&mut Self> where I: Into<ArrayIndexes<'a, N>> {
        at_or_mut_at!(self, indexes, get_mut)
    }

    /// # Takes an item from this array and its sub arrays
    ///
    /// The function returns an error on one of these conditions:
    ///
    /// - Indexes are empty or invalid.
    /// - The value or any of its sub items is not an array.
    ///
    /// ## Examples
    ///
    /// ```
    /// let mut array = sj::array();
    /// array.push("earth")?;
    /// array.push(vec![false.into(), "moon".into()])?;
    ///
    /// assert_eq!(array.take_at(0)?.as_str()?, "earth");
    /// assert_eq!(array.take_at([0, 1])?.as_str()?, "moon");
    ///
    /// assert!(array.take_at(&[][..]).is_err());
    /// assert!(array.take_at([0, 1]).is_err());
    ///
    /// # Ok::<_, sj::Error>(())
    /// ```
    pub fn take_at<'a, I, const N: usize>(&mut self, indexes: I) -> Result<Self> where I: Into<ArrayIndexes<'a, N>> {
        let mut value = Some(self);
        let indexes = indexes.into();
        for (nth, idx) in indexes.iter().enumerate() {
            match value {
                Some(Json::Array(array)) => if nth + 1 == indexes.len() {
                    return if idx >= &0 && idx < &array.len() {
                        Ok(array.remove(*idx))
                    } else {
                        Err(err!("Invalid indexes: {indexes:?}"))
                    };
                } else {
                    value = array.get_mut(*idx);
                },
                Some(_) => return Err(Error::from(match nth {
                    0 => e!("Json is not an Array"),
                    _ => err!("Json at {:?} is not an Array", indexes.index_with_range_to(..nth)),
                })),
                None => return Err(err!("There is no value at {:?}", indexes.index_with_range_to(..nth))),
            };
        }

        Err(e!("Indexes must not be empty"))
    }

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

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

}

impl From<Array> for Json {

    fn from(values: Array) -> Self {
        Json::Array(values)
    }

}

impl<T> FromIterator<T> for Json where T: Into<Self> {

    fn from_iter<I>(iter: I) -> Self where I: IntoIterator<Item=T> {
        Self::Array(iter.into_iter().map(|i| i.into()).collect())
    }

}

impl TryFrom<Json> for Array {

    type Error = Error;

    fn try_from(value: Json) -> core::result::Result<Self, Self::Error> {
        match value {
            Json::Array(array) => Ok(array),
            _ => Err(e!("Json is not an Array")),
        }
    }

}

impl<const N: usize> TryFrom<Json> for [Json; N] {

    type Error = Error;

    fn try_from(value: Json) -> Result<Self> {
        match value {
            Json::Array(array) => Self::try_from(array).map_err(|array|
                err!("Vector of {size} cannot be converted into array of {N}", size=array.len()),
            ),
            _ => Err(e!("Not an Array")),
        }
    }

}