aboutsummaryrefslogtreecommitdiff
path: root/arg_picker/src/picker/parse.rs
diff options
context:
space:
mode:
Diffstat (limited to 'arg_picker/src/picker/parse.rs')
-rw-r--r--arg_picker/src/picker/parse.rs177
1 files changed, 177 insertions, 0 deletions
diff --git a/arg_picker/src/picker/parse.rs b/arg_picker/src/picker/parse.rs
new file mode 100644
index 0000000..366028b
--- /dev/null
+++ b/arg_picker/src/picker/parse.rs
@@ -0,0 +1,177 @@
+// --------------------------------------------------------------------------------------------
+// I have to say, the code generated by this `internal_repeat!` macro is really UGLY.
+//
+// But I have to admit, this is a **trade-off**. To achieve the syntax of `pick().pick().pick()`
+// while ensuring type safety, this is the best approach I could think of.
+//
+// P.S. If there's a better way, please let me know. Thanks!
+// --------------------------------------------------------------------------------------------
+//
+// Then, I must disable `clippy::type_complexity` — this guy is way too noisy.
+#![allow(clippy::type_complexity)]
+
+use crate::{Pickable, PickerArgAttr, PickerArgInfo, PickerArgResult, PickerArgs, TagPhaseContext};
+use arg_picker_macros::internal_repeat;
+
+internal_repeat!(1..=32 => {
+ use crate::PickerPattern$;
+ use crate::PickerResult$;
+});
+
+internal_repeat!(1..=32 => {
+ impl<'a, (T$,+), Route> PickerPattern$<'a, (T$,+), Route>
+ where (T$: Pickable<'a>,+) {
+ /// Unwraps the result, panicking if a route was selected.
+ ///
+ /// # Panics
+ ///
+ /// Panics if a route was selected.
+ pub fn unwrap(self) -> ((T$,+)) {
+ let p = self.parse();
+ ((p.v$.unwrap(),+))
+ }
+
+ /// Returns the individual option values without checking the route.
+ pub fn unpack(self) -> ((Option<T$>,+)) {
+ let p = self.parse();
+ ((p.v$,+))
+ }
+
+ /// Converts to a `Result`, returning `Err(route)` if a route was selected,
+ /// or `Ok(values)` otherwise.
+ pub fn to_result(self) -> Result<((T$,+)), Route> {
+ let p = self.parse();
+ if let Some(r) = p.route {
+ return Err(r);
+ }
+ Ok(p.unwrap())
+ }
+
+ /// Converts to an `Option`, returning `None` if a route was selected,
+ /// or `Some(values)` otherwise.
+ pub fn to_option(self) -> Option<((T$,+))> {
+ let p = self.parse();
+ if p.route.is_some() {
+ return None;
+ }
+ Some(p.unwrap())
+ }
+ }
+});
+
+internal_repeat!(1..=32 => {
+ impl<'a, (T$,+), Route> PickerPattern$<'a, (T$,+), Route>
+ where (T$: Pickable<'a>,+)
+ {
+ pub fn parse(mut self) -> PickerResult$<(T$,+), Route> {
+ // ArgInfos
+ let arg_infos: [PickerArgInfo; $] = [
+ (
+ PickerArgInfo::from(self.arg_$),
+ +)
+ ];
+
+ let mut bundle: [
+ (
+ // Arg Attr
+ PickerArgAttr,
+
+ // Tag Func
+ Box<dyn FnOnce(&PickerArgs<'a>, &[u8]) -> Vec<usize>>,
+
+ // Pick Func
+ Box<dyn FnOnce(&[&str], &mut Option<Route>)>,
+
+ // Index
+ usize
+ )
+ ; $] = [
+ (
+ (
+ // Arg Attr
+ T$::get_attr(self.arg_$),
+
+ // Tag Func
+ Box::new(|args, mask| {
+ let ctx = TagPhaseContext {
+ arg_info: &arg_infos[$-],
+ args,
+ mask
+ };
+ T$::tag(ctx)
+ }),
+
+ // Pick Func
+ Box::new(|args, error_route| {
+ self.result_$ = match T$::pick(args) {
+ PickerArgResult::Parsed(mut value) => {
+ // Postprocess
+ if let Some(post) = self.post_$ {
+ value = post(value);
+ }
+ PickerArgResult::Parsed(value)
+ },
+ other => {
+ if let Some(get_default) = self.default_$ {
+ let mut value = get_default();
+
+ // Postprocess
+ if let Some(post) = self.post_$ {
+ value = post(value);
+ }
+
+ PickerArgResult::Parsed(value)
+ } else {
+ if error_route.is_none() {
+ if let Some(get_route) = self.route_$ {
+ *error_route = Some(get_route().into());
+ }
+ }
+ other
+ }
+ },
+
+ }
+ }),
+
+ // Index
+ $
+ ),
+ +)
+ ];
+
+ // Sort by Bundle Ord (descending)
+ bundle.sort_by(|a, b| b.0.cmp(&a.0));
+
+ // Mask — size = number of args (not args), so use args length
+ let mut mask: Vec<u8> = vec![0u8; self.args.len()];
+
+ // Parsing
+ for (_, tag_func, pick_func, _idx) in bundle {
+
+ // Tag phase
+ let tagged = tag_func(&self.args, mask.as_slice());
+ let mut args_to_pick: Vec<&str> = vec![];
+
+ for i in tagged {
+ mask[i] = 1;
+
+ // Update args to pick
+ args_to_pick.push(self.args.get(i).unwrap_or_default());
+ }
+
+ // Pick phase
+ pick_func(args_to_pick.as_slice(), &mut self.error_route);
+ }
+
+ // Combine Result
+ let result: PickerResult$<(T$,+), Route> = PickerResult$ {
+ route: self.error_route,
+ (
+ v$: self.result_$.to_option(),
+ +)
+ };
+ result
+ }
+ }
+});