// Copyright 2018-2021 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 std::any::Any; use std::any::TypeId; use std::collections::HashMap; #[derive(Default)] pub struct GothamState { data: HashMap>, } 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() .expect("required type is not present in GothamState container") } /// 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() .expect("required type is not present in GothamState container") } /// 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() .expect("required type is not present in GothamState container") } } #[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()); } }