// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. // Forked from Gotham: // https://github.com/gotham-rs/gotham/blob/bcbbf8923789e341b7a0e62c59909428ca4e22e2/gotham/src/state/mod.rs // Copyright 2017 Gotham Project Developers. MIT license. use log::trace; use std::any::type_name; use std::any::Any; use std::any::TypeId; use std::collections::BTreeMap; #[derive(Default)] pub struct GothamState { data: BTreeMap>, } impl GothamState { /// Puts a value into the `GothamState` storage. One value of each type is retained. /// Successive calls to `put` will overwrite the existing value of the same /// type. pub fn put(&mut self, t: T) { let type_id = TypeId::of::(); trace!(" inserting record to state for type_id `{:?}`", type_id); self.data.insert(type_id, Box::new(t)); } /// Determines if the current value exists in `GothamState` storage. pub fn has(&self) -> bool { let type_id = TypeId::of::(); self.data.get(&type_id).is_some() } /// Tries to borrow a value from the `GothamState` storage. pub fn try_borrow(&self) -> Option<&T> { let type_id = TypeId::of::(); trace!(" borrowing state data for type_id `{:?}`", type_id); self.data.get(&type_id).and_then(|b| b.downcast_ref()) } /// Borrows a value from the `GothamState` storage. pub fn borrow(&self) -> &T { self.try_borrow().unwrap_or_else(|| missing::()) } /// Tries to mutably borrow a value from the `GothamState` storage. pub fn try_borrow_mut(&mut self) -> Option<&mut T> { let type_id = TypeId::of::(); trace!(" mutably borrowing state data for type_id `{:?}`", type_id); self.data.get_mut(&type_id).and_then(|b| b.downcast_mut()) } /// Mutably borrows a value from the `GothamState` storage. pub fn borrow_mut(&mut self) -> &mut T { self.try_borrow_mut().unwrap_or_else(|| missing::()) } /// Tries to move a value out of the `GothamState` storage and return ownership. pub fn try_take(&mut self) -> Option { let type_id = TypeId::of::(); trace!( " taking ownership from state data for type_id `{:?}`", type_id ); self .data .remove(&type_id) .and_then(|b| b.downcast().ok()) .map(|b| *b) } /// Moves a value out of the `GothamState` storage and returns ownership. /// /// # Panics /// /// If a value of type `T` is not present in `GothamState`. pub fn take(&mut self) -> T { self.try_take().unwrap_or_else(|| missing::()) } pub fn clear(&mut self) { self.data.clear(); } } fn missing() -> ! { panic!( "required type {} is not present in GothamState container", type_name::() ); } #[cfg(test)] mod tests { use super::GothamState; struct MyStruct { value: i32, } struct AnotherStruct { value: &'static str, } type Alias1 = String; type Alias2 = String; #[test] fn put_borrow1() { let mut state = GothamState::default(); state.put(MyStruct { value: 1 }); assert_eq!(state.borrow::().value, 1); } #[test] fn put_borrow2() { let mut state = GothamState::default(); assert!(!state.has::()); state.put(AnotherStruct { value: "a string" }); assert!(state.has::()); assert!(!state.has::()); state.put(MyStruct { value: 100 }); assert!(state.has::()); assert_eq!(state.borrow::().value, 100); assert_eq!(state.borrow::().value, "a string"); } #[test] fn try_borrow() { let mut state = GothamState::default(); state.put(MyStruct { value: 100 }); assert!(state.try_borrow::().is_some()); assert_eq!(state.try_borrow::().unwrap().value, 100); assert!(state.try_borrow::().is_none()); } #[test] fn try_borrow_mut() { let mut state = GothamState::default(); state.put(MyStruct { value: 100 }); if let Some(a) = state.try_borrow_mut::() { a.value += 10; } assert_eq!(state.borrow::().value, 110); } #[test] fn borrow_mut() { let mut state = GothamState::default(); state.put(MyStruct { value: 100 }); { let a = state.borrow_mut::(); a.value += 10; } assert_eq!(state.borrow::().value, 110); assert!(state.try_borrow_mut::().is_none()); } #[test] fn try_take() { let mut state = GothamState::default(); state.put(MyStruct { value: 100 }); assert_eq!(state.try_take::().unwrap().value, 100); assert!(state.try_take::().is_none()); assert!(state.try_borrow_mut::().is_none()); assert!(state.try_borrow::().is_none()); assert!(state.try_take::().is_none()); } #[test] fn take() { let mut state = GothamState::default(); state.put(MyStruct { value: 110 }); assert_eq!(state.take::().value, 110); assert!(state.try_take::().is_none()); assert!(state.try_borrow_mut::().is_none()); assert!(state.try_borrow::().is_none()); } #[test] fn type_alias() { let mut state = GothamState::default(); state.put::("alias1".to_string()); state.put::("alias2".to_string()); assert_eq!(state.take::(), "alias2"); assert!(state.try_take::().is_none()); assert!(state.try_take::().is_none()); } #[test] #[should_panic( expected = "required type deno_core::gotham_state::tests::MyStruct is not present in GothamState container" )] fn missing() { let state = GothamState::default(); let _ = state.borrow::(); } }