summaryrefslogtreecommitdiffstats
path: root/third_party/rust/clap_builder/src/builder/ext.rs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--third_party/rust/clap_builder/src/builder/ext.rs216
1 files changed, 216 insertions, 0 deletions
diff --git a/third_party/rust/clap_builder/src/builder/ext.rs b/third_party/rust/clap_builder/src/builder/ext.rs
new file mode 100644
index 0000000000..2fb0d96b9d
--- /dev/null
+++ b/third_party/rust/clap_builder/src/builder/ext.rs
@@ -0,0 +1,216 @@
+use crate::util::AnyValueId;
+use crate::util::FlatMap;
+
+#[derive(Default, Clone, Debug)]
+pub(crate) struct Extensions {
+ extensions: FlatMap<AnyValueId, BoxedExtension>,
+}
+
+impl Extensions {
+ #[allow(dead_code)]
+ pub(crate) fn get<T: Extension>(&self) -> Option<&T> {
+ let id = AnyValueId::of::<T>();
+ self.extensions.get(&id).map(|e| e.as_ref::<T>())
+ }
+
+ #[allow(dead_code)]
+ pub(crate) fn get_mut<T: Extension>(&mut self) -> Option<&mut T> {
+ let id = AnyValueId::of::<T>();
+ self.extensions.get_mut(&id).map(|e| e.as_mut::<T>())
+ }
+
+ #[allow(dead_code)]
+ pub(crate) fn get_or_insert_default<T: Extension + Default>(&mut self) -> &mut T {
+ let id = AnyValueId::of::<T>();
+ self.extensions
+ .entry(id)
+ .or_insert_with(|| BoxedExtension::new(T::default()))
+ .as_mut::<T>()
+ }
+
+ #[allow(dead_code)]
+ pub(crate) fn set<T: Extension + Into<BoxedEntry>>(&mut self, tagged: T) -> bool {
+ let BoxedEntry { id, value } = tagged.into();
+ self.extensions.insert(id, value).is_some()
+ }
+
+ #[allow(dead_code)]
+ pub(crate) fn remove<T: Extension>(&mut self) -> Option<Box<dyn Extension>> {
+ let id = AnyValueId::of::<T>();
+ self.extensions.remove(&id).map(BoxedExtension::into_inner)
+ }
+
+ pub(crate) fn update(&mut self, other: &Self) {
+ for (key, value) in other.extensions.iter() {
+ self.extensions.insert(*key, value.clone());
+ }
+ }
+}
+
+/// Supports conversion to `Any`. Traits to be extended by `impl_downcast!` must extend `Extension`.
+pub(crate) trait Extension: std::fmt::Debug + Send + Sync + 'static {
+ /// Convert `Box<dyn Trait>` (where `Trait: Extension`) to `Box<dyn Any>`.
+ ///
+ /// `Box<dyn Any>` can /// then be further `downcast` into
+ /// `Box<ConcreteType>` where `ConcreteType` implements `Trait`.
+ fn into_any(self: Box<Self>) -> Box<dyn std::any::Any>;
+ /// Clone `&Box<dyn Trait>` (where `Trait: Extension`) to `Box<dyn Extension>`.
+ ///
+ /// `Box<dyn Any>` can /// then be further `downcast` into
+ // `Box<ConcreteType>` where `ConcreteType` implements `Trait`.
+ fn clone_extension(&self) -> Box<dyn Extension>;
+ /// Convert `&Trait` (where `Trait: Extension`) to `&Any`.
+ ///
+ /// This is needed since Rust cannot /// generate `&Any`'s vtable from
+ /// `&Trait`'s.
+ fn as_any(&self) -> &dyn std::any::Any;
+ /// Convert `&mut Trait` (where `Trait: Extension`) to `&Any`.
+ ///
+ /// This is needed since Rust cannot /// generate `&mut Any`'s vtable from
+ /// `&mut Trait`'s.
+ fn as_any_mut(&mut self) -> &mut dyn std::any::Any;
+}
+
+impl<T> Extension for T
+where
+ T: Clone + std::fmt::Debug + Send + Sync + 'static,
+{
+ fn into_any(self: Box<Self>) -> Box<dyn std::any::Any> {
+ self
+ }
+ fn clone_extension(&self) -> Box<dyn Extension> {
+ Box::new(self.clone())
+ }
+ fn as_any(&self) -> &dyn std::any::Any {
+ self
+ }
+ fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
+ self
+ }
+}
+
+impl Clone for Box<dyn Extension> {
+ fn clone(&self) -> Self {
+ self.as_ref().clone_extension()
+ }
+}
+
+#[derive(Clone)]
+#[repr(transparent)]
+struct BoxedExtension(Box<dyn Extension>);
+
+impl BoxedExtension {
+ fn new<T: Extension>(inner: T) -> Self {
+ Self(Box::new(inner))
+ }
+
+ fn into_inner(self) -> Box<dyn Extension> {
+ self.0
+ }
+
+ fn as_ref<T: Extension>(&self) -> &T {
+ self.0.as_ref().as_any().downcast_ref::<T>().unwrap()
+ }
+
+ fn as_mut<T: Extension>(&mut self) -> &mut T {
+ self.0.as_mut().as_any_mut().downcast_mut::<T>().unwrap()
+ }
+}
+
+impl std::fmt::Debug for BoxedExtension {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
+ self.0.fmt(f)
+ }
+}
+
+#[derive(Clone)]
+pub(crate) struct BoxedEntry {
+ id: AnyValueId,
+ value: BoxedExtension,
+}
+
+impl BoxedEntry {
+ pub(crate) fn new(r: impl Extension) -> Self {
+ let id = AnyValueId::from(&r);
+ let value = BoxedExtension::new(r);
+ BoxedEntry { id, value }
+ }
+}
+
+impl<R: Extension> From<R> for BoxedEntry {
+ fn from(inner: R) -> Self {
+ BoxedEntry::new(inner)
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ #[derive(Default, Copy, Clone, Debug, PartialEq, Eq)]
+ struct Number(usize);
+
+ #[test]
+ fn get() {
+ let mut ext = Extensions::default();
+ ext.set(Number(10));
+ assert_eq!(ext.get::<Number>(), Some(&Number(10)));
+ }
+
+ #[test]
+ fn get_mut() {
+ let mut ext = Extensions::default();
+ ext.set(Number(10));
+ *ext.get_mut::<Number>().unwrap() = Number(20);
+ assert_eq!(ext.get::<Number>(), Some(&Number(20)));
+ }
+
+ #[test]
+ fn get_or_insert_default_empty() {
+ let mut ext = Extensions::default();
+ assert_eq!(ext.get_or_insert_default::<Number>(), &Number(0));
+ }
+
+ #[test]
+ fn get_or_insert_default_full() {
+ let mut ext = Extensions::default();
+ ext.set(Number(10));
+ assert_eq!(ext.get_or_insert_default::<Number>(), &Number(10));
+ }
+
+ #[test]
+ fn set() {
+ let mut ext = Extensions::default();
+ assert!(!ext.set(Number(10)));
+ assert_eq!(ext.get::<Number>(), Some(&Number(10)));
+ assert!(ext.set(Number(20)));
+ assert_eq!(ext.get::<Number>(), Some(&Number(20)));
+ }
+
+ #[test]
+ fn reset() {
+ let mut ext = Extensions::default();
+ assert_eq!(ext.get::<Number>(), None);
+
+ assert!(ext.remove::<Number>().is_none());
+ assert_eq!(ext.get::<Number>(), None);
+
+ assert!(!ext.set(Number(10)));
+ assert_eq!(ext.get::<Number>(), Some(&Number(10)));
+
+ assert!(ext.remove::<Number>().is_some());
+ assert_eq!(ext.get::<Number>(), None);
+ }
+
+ #[test]
+ fn update() {
+ let mut ext = Extensions::default();
+ assert_eq!(ext.get::<Number>(), None);
+
+ let mut new = Extensions::default();
+ assert!(!new.set(Number(10)));
+
+ ext.update(&new);
+ assert_eq!(ext.get::<Number>(), Some(&Number(10)));
+ }
+}