// -------------------------------------------------------------------------------------------- // 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, PickerArgInfo, PickerArgResult, PickerArgs, PickerFlagAttr, TagPhaseContext, }; use mingling_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,+)) { 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.flag_$), +) ]; let mut bundle: [ ( // Flag Attr PickerFlagAttr, // Tag Func Box, &[u8]) -> Vec>, // Pick Func Box)>, // Index usize ) ; $] = [ ( ( // Flag Attr T$::get_attr(self.flag_$), // 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()); } } other } }, } }), // Index $ ), +) ]; // Sort by Bundle Ord (descending) bundle.sort_by(|a, b| b.0.cmp(&a.0)); // Mask — size = number of args (not flags), so use args length let mut mask: Vec = 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 } } });