aopt_core/value/
storer.rs

1use std::ffi::OsStr;
2use std::fmt::Debug;
3
4use crate::ctx::Ctx;
5use crate::map::ErasedTy;
6use crate::opt::Action;
7use crate::trace;
8use crate::Error;
9
10use super::AnyValue;
11use super::RawValParser;
12use super::ValValidator;
13
14#[cfg(feature = "sync")]
15pub type StoreHandler<T> =
16    Box<dyn FnMut(Option<&OsStr>, &Ctx, &Action, &mut T) -> Result<(), Error> + Send + Sync>;
17
18#[cfg(not(feature = "sync"))]
19pub type StoreHandler<T> =
20    Box<dyn FnMut(Option<&OsStr>, &Ctx, &Action, &mut T) -> Result<(), Error>>;
21
22/// [`ValStorer`] perform the value storing action.
23pub struct ValStorer(StoreHandler<AnyValue>);
24
25impl Debug for ValStorer {
26    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27        f.debug_tuple("WriterHandler").field(&"{...}").finish()
28    }
29}
30
31impl ValStorer {
32    pub fn new(handler: StoreHandler<AnyValue>) -> Self {
33        Self(handler)
34    }
35
36    pub fn fallback<U: ErasedTy + RawValParser>() -> Self {
37        Self(Self::fallback_handler::<U>())
38    }
39
40    /// Create a [`ValStorer`] with a value validator.
41    /// The [`invoke`](ValStorer::invoke) will return a [`failure`](Error::is_failure)
42    /// if value check failed.
43    pub fn new_validator<U: ErasedTy + RawValParser>(validator: ValValidator<U>) -> Self {
44        Self(Self::validator(validator))
45    }
46
47    /// Invoke the inner value store handler on [`AnyValue`].
48    pub fn invoke(
49        &mut self,
50        raw: Option<&OsStr>,
51        ctx: &Ctx,
52        act: &Action,
53        arg: &mut AnyValue,
54    ) -> Result<(), Error> {
55        crate::trace!("saving raw value({:?}) for {}", raw, ctx.uid()?);
56        (self.0)(raw, ctx, act, arg)
57    }
58
59    pub fn validator<U: ErasedTy + RawValParser>(
60        validator: ValValidator<U>,
61    ) -> StoreHandler<AnyValue> {
62        Box::new(
63            move |raw: Option<&OsStr>, ctx: &Ctx, act: &Action, handler: &mut AnyValue| {
64                let val = U::parse(raw, ctx).map_err(Into::into)?;
65
66                if !validator.invoke(&val) {
67                    let uid = ctx.uid()?;
68
69                    trace!(
70                        "validator value storer failed, parsing {:?} -> {:?}",
71                        raw,
72                        val
73                    );
74                    Err(
75                        crate::failure!("value check failed: `{:?}`", ctx.inner_ctx().ok(),)
76                            .with_uid(uid),
77                    )
78                } else {
79                    trace!(
80                        "validator value storer okay, parsing {:?} -> {:?}",
81                        raw,
82                        val
83                    );
84                    act.store1(Some(val), handler);
85                    Ok(())
86                }
87            },
88        )
89    }
90
91    pub fn fallback_handler<U: ErasedTy + RawValParser>() -> StoreHandler<AnyValue> {
92        Box::new(
93            |raw: Option<&OsStr>, ctx: &Ctx, act: &Action, handler: &mut AnyValue| {
94                let val = U::parse(raw, ctx).map_err(Into::into);
95
96                trace!("in fallback value storer, parsing {:?} -> {:?}", raw, val);
97                act.store1(Some(val?), handler);
98                Ok(())
99            },
100        )
101    }
102}
103
104impl<U: ErasedTy + RawValParser> From<ValValidator<U>> for ValStorer {
105    fn from(validator: ValValidator<U>) -> Self {
106        Self::new_validator(validator)
107    }
108}
109
110impl<U: ErasedTy + RawValParser> From<Option<ValValidator<U>>> for ValStorer {
111    fn from(validator: Option<ValValidator<U>>) -> Self {
112        if let Some(validator) = validator {
113            Self::new_validator(validator)
114        } else {
115            Self::fallback::<U>()
116        }
117    }
118}