aoe2-probe 0.3.2

A rust library for editing aoe2scenario files from AoE2 DE.
Documentation
use std::{
    collections::HashMap,
    fmt,
    ops::{Index, IndexMut},
    sync::Arc,
};

use linked_hash_map::{Iter, LinkedHashMap};
use serde::{Deserialize, Serialize};

use crate::parse::{Encode, Token};

type Patch = Arc<dyn Fn(&mut PatchedMap, &mut Token) + Send + Sync>;
#[derive(Clone, Default, Serialize, Deserialize)]
pub struct PatchedMap {
    #[serde(flatten)]
    raw_hashmap: LinkedHashMap<String, Token>,
    #[serde(skip)]
    pub patches: HashMap<String, Patch>,
}

impl PatchedMap {
    pub fn new() -> Self {
        PatchedMap {
            raw_hashmap: LinkedHashMap::new(),
            patches: HashMap::new(),
        }
    }

    pub fn with_capacity(capacity: usize) -> Self {
        PatchedMap {
            raw_hashmap: LinkedHashMap::with_capacity(capacity),
            patches: HashMap::new(),
        }
    }

    pub fn push_back<T: Into<Token>>(&mut self, key_str: &str, value: T) -> bool {
        let key = key_str.to_string();

        if self.raw_hashmap.contains_key(&key) {
            return false;
        }

        self.raw_hashmap.insert(key, value.into());
        true
    }

    pub fn update<T: Into<Token>>(&mut self, key: &str, value: T) -> Result<(), String> {
        if !self.raw_hashmap.contains_key(key) {
            return Err("Key doesn't exist!".to_string());
        }

        self.raw_hashmap[key] = value.into();
        Ok(())
    }

    pub fn contains(&self, key: &str) -> bool {
        self.raw_hashmap.contains_key(&key.to_string())
    }

    pub fn keys(&self) -> linked_hash_map::Keys<String, Token> {
        self.raw_hashmap.keys()
    }

    pub fn iter(&self) -> SeqIter {
        SeqIter {
            index: 0,
            ele: self.raw_hashmap.iter(),
        }
    }
}

impl Index<&str> for PatchedMap {
    type Output = Token;
    fn index(&self, index: &str) -> &Self::Output {
        &self.raw_hashmap[&index.to_string()]
    }
}

impl IndexMut<&str> for PatchedMap {
    fn index_mut(&mut self, index: &str) -> &mut Self::Output {
        self.raw_hashmap.get_mut(&index.to_string()).unwrap()
    }
}

pub struct SeqIter<'a> {
    index: usize,
    ele: Iter<'a, String, Token>,
}

impl<'a> Iterator for SeqIter<'a> {
    type Item = (&'a String, &'a Token);

    fn next(&mut self) -> Option<Self::Item> {
        match self.ele.next() {
            Some((key, value)) => {
                self.index += 1;
                Some((key, value))
            }
            None => None,
        }
    }
}

impl Encode for PatchedMap {
    fn to_le_vec(&self) -> Vec<u8> {
        self.iter()
            .flat_map(|(_, token)| token.to_le_vec())
            .collect()
    }
}

impl fmt::Debug for PatchedMap {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str("PatchedMap")?;
        f.debug_map()
            .entries(self.iter().map(|(key, value)| (key, value)))
            .finish()
    }
}