ap_manual 0.2.0

A rust package to cannonically interact with manual AP worlds
Documentation
use std::{collections::HashMap, ops::{Deref, DerefMut}, str::FromStr};

use serde::{Deserialize, Serialize};
use crate::Either;

use super::{ItemOrList,BoolInt,bool_is_false,bool_is_true};


fn filler_default()->bool{
    true
}
#[derive(Debug,Clone,PartialEq, Eq)]
pub struct AOrString<T: FromStr>{
    x: T
}
impl<T: Serialize+FromStr> Serialize for AOrString<T>{
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where
            S: serde::Serializer {
        self.x.serialize(serializer)
    }
}
impl<'de,T: Deserialize<'de> + FromStr> Deserialize<'de> for AOrString<T>{
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
        where
            D: serde::Deserializer<'de> 
            {
        let o=<Either<T,String> as Deserialize<'de>>::deserialize(deserializer)?;
        Ok(match o{
            Either::Left(v)=>AOrString {x:v},
            Either::Right(v)=>AOrString{
                x: T::from_str(&v).ok().expect(&format!("Failed to parse {}",v))
            }
        })
    }
}
impl<T: FromStr> Deref for AOrString<T>{
    type Target = T;
    fn deref(&self) -> &Self::Target {
        &self.x
    }
}
impl<T: FromStr> DerefMut for AOrString<T>{
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.x
    }
}
fn default_count()->AOrString<u32>{
    AOrString { x: 1 }
}
impl<T:FromStr> From<T> for AOrString<T>{
    fn from(value: T) -> Self {
        Self { x: value }
    }
}

#[derive(Debug,Serialize,Deserialize,PartialEq, Eq,Clone)]
pub struct Item{
    pub name: String,
    #[serde(default,skip_serializing_if="ItemOrList::is_empty")]
    pub category: ItemOrList<String>,
    #[serde(default="default_count")]
    pub count: AOrString<u32>,
    #[serde(default,skip_serializing_if="HashMap::is_empty")]
    pub value: HashMap<String,u32>,
    #[serde(default,skip_serializing_if="bool_is_false")]
    pub progression: bool,
    #[serde(default,skip_serializing_if="bool_is_false")]
    pub progression_skip_balancing: bool,
    #[serde(default,skip_serializing_if="bool_is_false")]
    pub useful: bool,
    #[serde(default,skip_serializing_if="bool_is_false")]
    pub trap: bool,
    #[serde(default="filler_default",skip_serializing_if="bool_is_true")]
    pub filler: bool,
    #[serde(default,skip_serializing_if="BoolInt::is_false_or_zero")]
    pub early: BoolInt,
    #[serde(default,skip_serializing_if="bool_is_false")]
    pub local: bool,
    #[serde(default,skip_serializing_if="BoolInt::is_false_or_zero")]
    pub local_early: BoolInt,
    #[serde(default,skip_serializing_if="HashMap::is_empty")]
    pub classification_count: HashMap<String,u32>,
    #[serde(default,skip_serializing_if = "Option::is_none")]
    pub id: Option<u32>
}

impl Item{
    ///construct a simple new item
    pub fn new(name: String)->Self{
        Self { name, category: ItemOrList::default(), count: 1.into(), value: HashMap::default(), progression: false, progression_skip_balancing: false, useful: false, trap: false, filler:true, early: BoolInt::default(), local: false, local_early: BoolInt::default(), classification_count: HashMap::default(), id: None }
    }
    ///Add a category to this item
    pub fn add_category(&mut self,c: String){
        self.category.push(c);
    }
    ///Remove a category from this item at the given index, returning it if it exists
    pub fn remove_category(&mut self,idx: usize)->Option<String>{
        self.category.remove(idx)
    }
    ///Get the number of categories on this item
    pub fn num_categories(&self)->usize{
        self.category.len()
    }
    pub fn with_category(mut self,c: String)->Self{
        self.add_category(c);
        self
    }
    pub fn as_progression(mut self)->Self{
        self.progression=true;
        self.filler=false;
        self
    }
    pub fn as_progression_skip_balancing(mut self)->Self{
        self.progression_skip_balancing=true;
        self.filler=false;
        self
    }
    pub fn as_useful(mut self)->Self{
        self.useful=true;
        self.filler=false;
        self
    }
    pub fn as_trap(mut self)->Self{
        self.trap=true;
        self.filler=false;
        self
    }
    pub fn builder(name: String)->Self{
        Self::new(name)
    }
}

#[cfg(feature="item_flags")]
pub mod flagged{
    use bitflags::bitflags;
    use super::Item;
    bitflags! {
        pub struct ItemFlags: u8{
            const progression=1;
            const skip_balancing=8;
            const trap=4;
            const useful=2;
        }
    }
    impl Item{
        /// Set progression related flags using a flag object
        pub fn set_flags(&mut self,f: ItemFlags){
            self.progression = f.contains(ItemFlags::progression);
            self.progression_skip_balancing=f.contains(ItemFlags::progression | ItemFlags::skip_balancing);
            self.trap=f.contains(ItemFlags::trap);
            self.useful=f.contains(ItemFlags::useful);
            self.filler=f.is_empty(); 
        }
        /// Get the flags as a flag object
        pub fn get_flags(&self)->ItemFlags{
            let mut out = ItemFlags::empty();
            out.set(ItemFlags::skip_balancing,self.progression_skip_balancing);
            out.set(ItemFlags::progression,self.progression_skip_balancing || self.progression);
            out.set(ItemFlags::trap,self.trap);
            out.set(ItemFlags::useful,self.useful);
            out
        }
    }
}

#[cfg(feature="item_flags")]
pub use flagged::{ItemFlags};