mirror of
https://github.com/clap-rs/clap
synced 2024-12-13 14:22:34 +00:00
refactor: Design plugin system
This commit is contained in:
parent
fe70ddcb20
commit
1924f45289
2 changed files with 218 additions and 0 deletions
217
clap_builder/src/builder/ext.rs
Normal file
217
clap_builder/src/builder/ext.rs
Normal file
|
@ -0,0 +1,217 @@
|
|||
use crate::util::AnyValueId;
|
||||
use crate::util::FlatMap;
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[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)));
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@ mod arg_group;
|
|||
mod arg_predicate;
|
||||
mod arg_settings;
|
||||
mod command;
|
||||
mod ext;
|
||||
mod os_str;
|
||||
mod possible_value;
|
||||
mod range;
|
||||
|
|
Loading…
Reference in a new issue