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/>.

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

//! # Inner

use crate::{Error, Json, ObjectIndexes, Result};

/// # Helper macro for Json::maybe_by()/maybe_mut_by()
///
/// Keys will be returned for logging purpose (from caller).
macro_rules! maybe_by_or_mut_by { ($value: ident, $keys: ident, $code: tt) => {{
    let keys = $keys.into();
    if keys.len() == 0 {
        return Err(err!("Keys must not be empty"));
    }

    let mut value = Some($value);
    for (nth, key) in keys.iter().enumerate() {
        match value {
            Some(Json::Object(object)) => value = object.$code(key),
            Some(_) => return Err(Error::from(match nth {
                0 => e!("Json is not an Object"),
                _ => err!("Json at {:?} is not an Object", keys.index_with_range_to(..nth)),
            })),
            None => return Err(err!("There is no value at {:?}", keys.index_with_range_to(..nth))),
        };
    }

    Ok((value, keys))
}}}

/// # Helper function for macro maybe_by_or_mut_by!()
///
/// This provides one single place for calling macro `maybe_by_or_mut_by!()` with `get`
pub (super) fn maybe_by<'a, K, const N: usize>(value: &Json, keys: K) -> Result<(Option<&Json>, ObjectIndexes<'a, N>)>
where K: Into<ObjectIndexes<'a, N>> {
    maybe_by_or_mut_by!(value, keys, get)
}

/// # Helper function for macro maybe_by_or_mut_by!()
///
/// This provides one single place for calling macro `maybe_by_or_mut_by!()` with `get_mut`
pub (super) fn maybe_mut_by<'a, K, const N: usize>(value: &mut Json, keys: K) -> Result<(Option<&mut Json>, ObjectIndexes<'a, N>)>
where K: Into<ObjectIndexes<'a, N>> {
    maybe_by_or_mut_by!(value, keys, get_mut)
}

/// # Takes an optional item from an 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.
///
/// Keys will be returned for logging purpose (from caller).
pub (super) fn maybe_take_by<'a, K, const N: usize>(value: &mut Json, keys: K) -> Result<(Option<Json>, ObjectIndexes<'a, N>)>
where K: Into<ObjectIndexes<'a, N>> {
    let keys = keys.into();
    if keys.len() == 0 {
        return Err(e!("Keys must not be empty"));
    }

    let mut result = None;
    let mut value = Some(value);
    for (nth, key) in keys.iter().enumerate() {
        match value {
            Some(Json::Object(object)) => if nth + 1 == keys.len() {
                result = object.remove(key);
                break;
            } else {
                value = object.get_mut(key);
            },
            Some(_) => return Err(match nth {
                0 => e!("Json is not an Object"),
                _ => err!("Json at {:?} is not an Object", keys.index_with_range_to(..nth)),
            }),
            None => return Err(err!("There is no value at {:?}", keys.index_with_range_to(..nth))),
        };
    }

    Ok((result, keys))
}