minimo 0.5.42

terminal ui library combining alot of things from here and there and making it slightly easier to play with
Documentation
    use std::any::{Any, TypeId};
    use std::collections::HashMap;
    use std::sync::{Arc, RwLock};
    use super::*;
    pub struct Context {
        data: Arc<RwLock<HashMap<TypeId, Box<dyn Any + Send + Sync >>>>,
        named_data: Arc<RwLock<HashMap<(TypeId, String), Box<dyn Any + Send + Sync>>>>,
    }

    impl Context {
        pub fn new() -> Self {
            Self {
                data: Arc::new(RwLock::new(HashMap::new())),
                named_data: Arc::new(RwLock::new(HashMap::new())),
            }
        }

        pub fn set<T: 'static + Clone + Send + Sync>(&self, value: T) {
            let mut data = self.data.write().expect("Failed to acquire write lock");
            data.insert(TypeId::of::<T>(), Box::new(value));
        }

        pub fn set_named<T: 'static + Clone + Send + Sync>(&self, name: &str, value: T) {
            let mut data = self.named_data.write().expect("Failed to acquire write lock");
            let key = (TypeId::of::<T>(), name.to_string());
            data.insert(key, Box::new(value));
        }

        pub fn get<T: 'static + Clone + Send + Sync>(&self) -> Option<T> {
            let data = self.data.read().expect("Failed to acquire read lock");
            data.get(&TypeId::of::<T>())
                .and_then(|boxed| boxed.downcast_ref::<T>())
                .cloned()
        }

        pub fn get_named<T: 'static + Clone + Send + Sync>(&self, name: &str) -> Option<T> {
            let data = self.named_data.read().expect("Failed to acquire read lock");
            let key = (TypeId::of::<T>(), name.to_string());
            data.get(&key)
                .and_then(|boxed| boxed.downcast_ref::<T>())
                .cloned()
        }

        pub fn update<T, F>(&self, f: F) -> crate::result::Result<()>
        where
            T: 'static + Clone + Send + Sync,
            F: FnOnce(&mut T),
        {
            let mut data = self.data.write().expect("Failed to acquire write lock");
            
            if let Some(value) = data.get_mut(&TypeId::of::<T>()) {
                if let Some(value) = value.downcast_mut::<T>() {
                    f(value);
                    Ok(())
                } else {
                    Err(format!("Type mismatch for {:?}", std::any::type_name::<T>()).into())
                }
            } else {
                Err(format!("Type not found: {:?}", std::any::type_name::<T>()).into())
            }
        }

        pub fn update_named<T, F>(&self, name: &str, f: F) -> crate::result::Result<()>
        where
            T: 'static + Clone + Send + Sync,
            F: FnOnce(&mut T),
        {
            let mut data = self.named_data.write().expect("Failed to acquire write lock");
            let key = (TypeId::of::<T>(), name.to_string());
            
            if let Some(value) = data.get_mut(&key) {
                if let Some(value) = value.downcast_mut::<T>() {
                    f(value);
                    Ok(())
                } else {
                    Err(format!("Type mismatch for {:?}", std::any::type_name::<T>()).into())
                }
            } else {
                Err(format!("Type not found: {:?}", std::any::type_name::<T>()).into())
            }
        }

        pub fn remove<T: 'static>(&self) -> Option<Box<dyn Any + Send + Sync>> {
            let mut data = self.data.write().expect("Failed to acquire write lock");
            data.remove(&TypeId::of::<T>())
        }

        pub fn remove_named<T: 'static>(&self, name: &str) -> Option<Box<dyn Any + Send + Sync>> {
            let mut data = self.named_data.write().expect("Failed to acquire write lock");
            let key = (TypeId::of::<T>(), name.to_string());
            data.remove(&key)
        }

        pub fn clear(&self) {
            let mut data = self.data.write().expect("Failed to acquire write lock");
            data.clear();
            let mut named_data = self.named_data.write().expect("Failed to acquire write lock");
            named_data.clear();
        }

        pub fn list(&self) {
            showln!(yellow_bold, "context");
            let data = self.data.read().expect("Failed to acquire read lock");
            showln!(cyan_bold,"unnamed");
            for (key, data) in data.iter() {
 
                let data_type = std::any::type_name_of_val(&data);
                showln!(gray_dim, format!("{:?}", key), yellow_bold, " → ", white_bold, format!("{:?}", data_type))  ;
            }
            let named_data = self.named_data.read().expect("Failed to acquire read lock");
            showln!(cyan_bold,"named");
            for (key, data) in named_data.iter() {
                showln!(gray_dim, format!("{:?}", key), yellow_bold, " → ", white_bold, format!("{:?}", data));
            }
        }
    }

    use std::sync::OnceLock;

    use crate::{cyan_bold, showln};

    pub fn global_context() -> &'static Context {
        static INSTANCE: OnceLock<Context> = OnceLock::new();
        INSTANCE.get_or_init(Context::new)
    }

    #[macro_export]
    macro_rules! set {
        ($value:expr) => {
            $crate::context::global_context().set($value)
        };
        ($name:ident => $value:expr) => {
            $crate::context::global_context().set_named(stringify!($name), $value)
        };
    }

    #[macro_export]
    macro_rules! get {
        ($type:ty) => {
            $crate::context::global_context().get::<$type>().unwrap()
        };
        ($type:ty, $name:ident) => {
            $crate::context::global_context().get_named::<$type>(stringify!($name)).unwrap()
        };
    }

    #[macro_export]
    macro_rules! maybe {
        ($type:ty) => {
            $crate::context::global_context().get::<$type>()
        };
        ($type:ty, $name:ident) => {
            $crate::context::global_context().get_named::<$type>(stringify!($name))
        };
    }

    #[macro_export]
    macro_rules! update {
        ($type:ty, $($field:ident : $value:expr),+ $(,)?) => {
            $crate::context::global_context().update::<$type, _>(|value| {
                $(value.$field = $value;)+
            })
        };
        ($type:ty, $name:ident, $($field:ident : $value:expr),+ $(,)?) => {
            $crate::context::global_context().update_named::<$type, _>(stringify!($name), |value| {
                $(value.$field = $value;)+
            })
        };
        ($type:ty, |$param:ident| $body:expr) => {
            $crate::context::global_context().update::<$type, _>(|$param| $body)
        };
        ($type:ty, $name:ident, |$param:ident| $body:expr) => {
            $crate::context::global_context().update_named::<$type, _>(stringify!($name), |$param| $body)
        };
    }

    #[macro_export]
    macro_rules! remove {
        ($type:ty) => {
            $crate::context::global_context().remove::<$type>()
        };
        ($type:ty, $name:ident) => {
            $crate::context::global_context().remove_named::<$type>(stringify!($name))
        };
    }

    #[macro_export]
    macro_rules! get_or {
        ($type:ty, $default:expr) => {
            $crate::context::global_context().get::<$type>().unwrap_or_else(|| $default)
        };
        ($type:ty, $name:ident, $default:expr) => {
            $crate::context::global_context().get_named::<$type>(stringify!($name)).unwrap_or_else(|| $default)
        };
    }

    #[macro_export]
    macro_rules! get_or_else {
        ($type:ty, $default:expr) => {
            $crate::context::global_context().get::<$type>().unwrap_or_else($default)
        };
        ($type:ty, $name:ident, $default:expr) => {
            $crate::context::global_context().get_named::<$type>(stringify!($name)).unwrap_or_else($default)
        };
    }

    #[macro_export]
    macro_rules! get_or_insert {
        ($type:ty, $default:expr) => {{
            if let Some(value) = $crate::context::global_context().get::<$type>() {
                value
            } else {
                let default = $default;
                $crate::context::global_context().set(default.clone());
                default
            }
        }};
        ($type:ty, $name:ident, $default:expr) => {{
            if let Some(value) = $crate::context::global_context().get_named::<$type>(stringify!($name)) {
                value
            } else {
                let default = $default;
                $crate::context::global_context().set_named(stringify!($name), default.clone());
                default
            }
        }};
    }

    #[cfg(test)]
    mod tests {
        use super::*;

        #[derive(Debug, Clone, PartialEq)]
        struct User {
            name: String,
            age: u32,
        }

        #[test]
        fn test_context() {
            // Set and get a simple value
            set!(42i32);
            assert_eq!(get!(i32), 42);
            assert_eq!(maybe!(i32), Some(42));

            // Set and get a named value
            set!(name => "John".to_string());
            assert_eq!(get!(String, name), "John".to_string());
            assert_eq!(maybe!(String, name), Some("John".to_string()));

            // Set and get a complex value
            let user = User {
                name: "Alice".to_string(),
                age: 30,
            };
            set!(user.clone());
            assert_eq!(get!(User), user);

            // Test getting a specific field using a closure
            assert_eq!(get!(User).name, "Alice");
            assert_eq!(maybe!(User).map(|u| u.age), Some(30));

            // Test updating a value using the update! macro
            update!(User, name: "Bob".to_string()).unwrap();
            update!(User, |u| u.age += 1).unwrap();

            assert_eq!(
                get!(User),
                User {
                    name: "Bob".to_string(),
                    age: 31
                }
            );

            // Test named updates
            set!(named_user => User { name: "Charlie".to_string(), age: 25 });
            update!(User, named_user, age: 26).unwrap();
            assert_eq!(get!(User, named_user).age, 26);

            // Test remove
            remove!(User);
            assert_eq!(maybe!(User), None);

            // Test convenience macros
            assert_eq!(get_or!(i32, 0), 42);
            assert_eq!(get_or!(Vec<i32>, vec![1, 2, 3]), vec![1, 2, 3]);
            
            assert_eq!(get_or_else!(i32, || 0), 42);
            assert_eq!(get_or_else!(Vec<i32>, || vec![4, 5, 6]), vec![4, 5, 6]);

            let inserted: Vec<i32> = get_or_insert!(Vec<i32>, vec![7, 8, 9]);
            assert_eq!(inserted, vec![7, 8, 9]);
            assert_eq!(get!(Vec<i32>), vec![7, 8, 9]);

            // Test named get_or, get_or_else, and get_or_insert
            assert_eq!(get_or!(String, other_name, "Default".to_string()), "Default".to_string());
            assert_eq!(get_or_else!(String, other_name, || "Default".to_string()), "Default".to_string());
            let inserted_named: String = get_or_insert!(String, new_name, "New".to_string());
            assert_eq!(inserted_named, "New");
            assert_eq!(get!(String, new_name), "New");

            // Test clear
            global_context().clear();
            assert_eq!(maybe!(i32), None);
            assert_eq!(maybe!(User), None);
            assert_eq!(maybe!(Vec<i32>), None);
            assert_eq!(maybe!(String, name), None);
        }
    }