# Nestmap 
[![Please don't upload to GitHub](https://nogithub.codeberg.page/badge.svg)](https://nogithub.codeberg.page)
![Version](https://img.shields.io/badge/version-0.1.0-green)
![Development](https://img.shields.io/badge/Development-Actively_Developed_%3A3-green)

[Source Code (Codeberg)](https://codeberg.org/TheBigBakedBean/Nestmap)

`Nestmap` is an ergonomic and dependency free implementation of a tree data structure by nesting [`HashMap`]s. This design enables high performance unordered trees for use in cases such as hierarchical config files (`toml`, `yaml` or `json`) or potentially as a basic file system. 

Since this library is built around the [`HashMap`], tools that work with hashmaps also work with this library, using the [`NestMap::into_inner`] method.

I developed this library since there were no good solutions for nested hashmaps. The available solutions were nonstandard, making them hard to integrate into my existing project.

### Design Decisions
This project was designed to be readable and correct, not fast. I believe the functions I use are sufficiently fast for 99% of use cases but the concept is more to make it an easy library to work with. This is why the hashmap was chosen as a medium. 
The other major design goal was for it to be able to support many types of data. Therefore the trait based approach was chosen. This allowed for the project to support any type as the Value, assuming it is capable of holding a map. 

### Future Ideas
This project is still in beta, so there will be many changes before full release. These are the main concepts I wish to focus on though:
 - A `Derive` macro for implementing [`NestableValue`]
 - Additional utilities such as `remove_path` and searching algorithms
 - A macro to make creating complex maps more pain free

(more may be added in the future)

### Example
A standard implementation may look like this:

```rust
use nestmap::{NestMap, NestableValue, check_value};

#[derive(Debug, PartialEq, Clone)]
enum Value {
    Int(i32),
    Map(NestMap<String, Value>),
}
impl NestableValue<String, Value> for Value {
    type IntoMapError = &'static str;
    type IntoMapMutError = &'static str;
    fn empty_map() -> Self {
        Self::Map(NestMap::new())
    }
    fn as_map_mut(
        &mut self,
    ) -> Result<&mut NestMap<String, Value>, Self::IntoMapMutError> {
        match self {
            Self::Map(m) => Ok(m),
            _ => Err("wrong value!"),
        }
    }
    fn as_map(&self) -> Result<&NestMap<String, Value>, Self::IntoMapError> {
        match self {
            Self::Map(m) => Ok(m),
            _ => Err("wrong value!"),
        }
    }
}

type Map = NestMap<String, Value>;
fn main() {
    // Make sure the value is ok, this will panic if the value is incorrectly set up
    check_value::<_, _, Value>();

    let mut map = Map::new();

    map.insert("simple".into(), Value::Int(8));

    map.insert_path(["a".into(), "long".into(), "path".into()], Value::Int(16));

    let minimap = Map::from([("key1".into(), Value::Int(1)), ("key2".into(), Value::Int(2))]);

    map.insert_path(["minimap".into(), "path".into()], Value::Map(minimap));

    // Get and get_path both work for single item values
    assert_eq!(map.get(&"simple".into()), Some(&Value::Int(8)));
    assert_eq!(map.get_path(["simple".into()]), Ok(&Value::Int(8)));

    // Try to find an item
    match map.get_path(["wrong".into(), "path".into()]) {
        Ok(_item) => unreachable!(),
        Err(e) => {
            if e.is_actual_error() {
                println!("There's an error!");
            }
            else {
                println!("Can't find item :(");
            }
        }
    }

    // You can also use a nifty trait
    use nestmap::GetResultExt;

    // The `into_option` function (which is made by the trait) allows you to unwrap to get an option
    // The option will be Some if the value is found and None if the value wasn't found
    // A regular Err will be emitted if something went wrong, such as an empty path
    assert_eq!(map
        .get_path(["a".into(), "long".into(), "path".into()])
        .into_option()
        .unwrap(),
    Some(&Value::Int(16)));
}
```

### Panics
This library is **guaranteed** not to panic. All `unwrap` calls are prefaced with a safety section which explains why it is safe to unwrap. All error handling is propagated up through the code and returned as an result, meaning assuming you handle the errors correctly, panicking will be impossible. In addition, example of possible panics are shown and tested in the documentation, and rust's `doc_tests` feature ensures all code is to high quality standards. A collection of linters are also used to ensure high code quality.

### Contributing
See [Contributing.md](https://codeberg.org/TheBigBakedBean/Nestmap/src/branch/main/Contributing.md) for more details on how to contribute to the project

### Buy me a coffee
i dont drink coffee :ε
