1use crate::verify::ExpectedArg;
68use clap::ArgMatches;
69use serde::Serialize;
70use std::any::{Any, TypeId};
71use std::collections::HashMap;
72use std::fmt;
73use std::rc::Rc;
74
75#[derive(Default)]
108pub struct Extensions {
109 map: HashMap<TypeId, Box<dyn Any>>,
110}
111
112impl Extensions {
113 pub fn new() -> Self {
115 Self::default()
116 }
117
118 pub fn insert<T: 'static>(&mut self, val: T) -> Option<T> {
122 self.map
123 .insert(TypeId::of::<T>(), Box::new(val))
124 .and_then(|boxed| boxed.downcast().ok().map(|b| *b))
125 }
126
127 pub fn get<T: 'static>(&self) -> Option<&T> {
131 self.map
132 .get(&TypeId::of::<T>())
133 .and_then(|boxed| boxed.downcast_ref())
134 }
135
136 pub fn get_mut<T: 'static>(&mut self) -> Option<&mut T> {
140 self.map
141 .get_mut(&TypeId::of::<T>())
142 .and_then(|boxed| boxed.downcast_mut())
143 }
144
145 pub fn get_required<T: 'static>(&self) -> Result<&T, anyhow::Error> {
149 self.get::<T>().ok_or_else(|| {
150 anyhow::anyhow!(
151 "Extension missing: type {} not found in context",
152 std::any::type_name::<T>()
153 )
154 })
155 }
156
157 pub fn get_mut_required<T: 'static>(&mut self) -> Result<&mut T, anyhow::Error> {
161 self.get_mut::<T>().ok_or_else(|| {
162 anyhow::anyhow!(
163 "Extension missing: type {} not found in context",
164 std::any::type_name::<T>()
165 )
166 })
167 }
168
169 pub fn remove<T: 'static>(&mut self) -> Option<T> {
171 self.map
172 .remove(&TypeId::of::<T>())
173 .and_then(|boxed| boxed.downcast().ok().map(|b| *b))
174 }
175
176 pub fn contains<T: 'static>(&self) -> bool {
178 self.map.contains_key(&TypeId::of::<T>())
179 }
180
181 pub fn len(&self) -> usize {
183 self.map.len()
184 }
185
186 pub fn is_empty(&self) -> bool {
188 self.map.is_empty()
189 }
190
191 pub fn clear(&mut self) {
193 self.map.clear();
194 }
195}
196
197impl fmt::Debug for Extensions {
198 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
199 f.debug_struct("Extensions")
200 .field("len", &self.map.len())
201 .finish_non_exhaustive()
202 }
203}
204
205impl Clone for Extensions {
206 fn clone(&self) -> Self {
207 Self::new()
211 }
212}
213
214#[derive(Debug)]
294pub struct CommandContext {
295 pub command_path: Vec<String>,
297
298 pub app_state: Rc<Extensions>,
305
306 pub extensions: Extensions,
311}
312
313impl CommandContext {
314 pub fn new(command_path: Vec<String>, app_state: Rc<Extensions>) -> Self {
318 Self {
319 command_path,
320 app_state,
321 extensions: Extensions::new(),
322 }
323 }
324}
325
326impl Default for CommandContext {
327 fn default() -> Self {
328 Self {
329 command_path: Vec::new(),
330 app_state: Rc::new(Extensions::new()),
331 extensions: Extensions::new(),
332 }
333 }
334}
335
336#[derive(Debug)]
340pub enum Output<T: Serialize> {
341 Render(T),
343 Silent,
345 Binary {
347 data: Vec<u8>,
349 filename: String,
351 },
352}
353
354impl<T: Serialize> Output<T> {
355 pub fn is_render(&self) -> bool {
357 matches!(self, Output::Render(_))
358 }
359
360 pub fn is_silent(&self) -> bool {
362 matches!(self, Output::Silent)
363 }
364
365 pub fn is_binary(&self) -> bool {
367 matches!(self, Output::Binary { .. })
368 }
369}
370
371pub type HandlerResult<T> = Result<Output<T>, anyhow::Error>;
375
376pub trait IntoHandlerResult<T: Serialize> {
402 fn into_handler_result(self) -> HandlerResult<T>;
404}
405
406impl<T, E> IntoHandlerResult<T> for Result<T, E>
411where
412 T: Serialize,
413 E: Into<anyhow::Error>,
414{
415 fn into_handler_result(self) -> HandlerResult<T> {
416 self.map(Output::Render).map_err(Into::into)
417 }
418}
419
420impl<T: Serialize> IntoHandlerResult<T> for HandlerResult<T> {
425 fn into_handler_result(self) -> HandlerResult<T> {
426 self
427 }
428}
429
430#[derive(Debug)]
438#[non_exhaustive]
439pub enum RunResult {
440 Handled(String),
442 Binary(Vec<u8>, String),
444 Silent,
446 Error(String),
449 NoMatch(ArgMatches),
451}
452
453impl RunResult {
454 pub fn is_handled(&self) -> bool {
456 matches!(self, RunResult::Handled(_))
457 }
458
459 pub fn is_binary(&self) -> bool {
461 matches!(self, RunResult::Binary(_, _))
462 }
463
464 pub fn is_silent(&self) -> bool {
466 matches!(self, RunResult::Silent)
467 }
468
469 pub fn is_error(&self) -> bool {
471 matches!(self, RunResult::Error(_))
472 }
473
474 pub fn output(&self) -> Option<&str> {
476 match self {
477 RunResult::Handled(s) => Some(s),
478 _ => None,
479 }
480 }
481
482 pub fn error(&self) -> Option<&str> {
484 match self {
485 RunResult::Error(s) => Some(s),
486 _ => None,
487 }
488 }
489
490 pub fn binary(&self) -> Option<(&[u8], &str)> {
492 match self {
493 RunResult::Binary(bytes, filename) => Some((bytes, filename)),
494 _ => None,
495 }
496 }
497
498 pub fn matches(&self) -> Option<&ArgMatches> {
500 match self {
501 RunResult::NoMatch(m) => Some(m),
502 _ => None,
503 }
504 }
505}
506
507pub trait Handler {
531 type Output: Serialize;
533
534 fn handle(&mut self, matches: &ArgMatches, ctx: &CommandContext)
536 -> HandlerResult<Self::Output>;
537
538 fn expected_args(&self) -> Vec<ExpectedArg> {
543 Vec::new()
544 }
545}
546
547pub struct FnHandler<F, T, R = HandlerResult<T>>
570where
571 T: Serialize,
572{
573 f: F,
574 _phantom: std::marker::PhantomData<fn() -> (T, R)>,
575}
576
577impl<F, T, R> FnHandler<F, T, R>
578where
579 F: FnMut(&ArgMatches, &CommandContext) -> R,
580 R: IntoHandlerResult<T>,
581 T: Serialize,
582{
583 pub fn new(f: F) -> Self {
585 Self {
586 f,
587 _phantom: std::marker::PhantomData,
588 }
589 }
590}
591
592impl<F, T, R> Handler for FnHandler<F, T, R>
593where
594 F: FnMut(&ArgMatches, &CommandContext) -> R,
595 R: IntoHandlerResult<T>,
596 T: Serialize,
597{
598 type Output = T;
599
600 fn handle(&mut self, matches: &ArgMatches, ctx: &CommandContext) -> HandlerResult<T> {
601 (self.f)(matches, ctx).into_handler_result()
602 }
603}
604
605pub struct SimpleFnHandler<F, T, R = HandlerResult<T>>
632where
633 T: Serialize,
634{
635 f: F,
636 _phantom: std::marker::PhantomData<fn() -> (T, R)>,
637}
638
639impl<F, T, R> SimpleFnHandler<F, T, R>
640where
641 F: FnMut(&ArgMatches) -> R,
642 R: IntoHandlerResult<T>,
643 T: Serialize,
644{
645 pub fn new(f: F) -> Self {
647 Self {
648 f,
649 _phantom: std::marker::PhantomData,
650 }
651 }
652}
653
654impl<F, T, R> Handler for SimpleFnHandler<F, T, R>
655where
656 F: FnMut(&ArgMatches) -> R,
657 R: IntoHandlerResult<T>,
658 T: Serialize,
659{
660 type Output = T;
661
662 fn handle(&mut self, matches: &ArgMatches, _ctx: &CommandContext) -> HandlerResult<T> {
663 (self.f)(matches).into_handler_result()
664 }
665}
666
667#[cfg(test)]
668mod tests {
669 use super::*;
670 use serde_json::json;
671
672 #[test]
673 fn test_command_context_creation() {
674 let ctx = CommandContext {
675 command_path: vec!["config".into(), "get".into()],
676 app_state: Rc::new(Extensions::new()),
677 extensions: Extensions::new(),
678 };
679 assert_eq!(ctx.command_path, vec!["config", "get"]);
680 }
681
682 #[test]
683 fn test_command_context_default() {
684 let ctx = CommandContext::default();
685 assert!(ctx.command_path.is_empty());
686 assert!(ctx.extensions.is_empty());
687 assert!(ctx.app_state.is_empty());
688 }
689
690 #[test]
691 fn test_command_context_with_app_state() {
692 struct Database {
693 url: String,
694 }
695 struct Config {
696 debug: bool,
697 }
698
699 let mut app_state = Extensions::new();
701 app_state.insert(Database {
702 url: "postgres://localhost".into(),
703 });
704 app_state.insert(Config { debug: true });
705 let app_state = Rc::new(app_state);
706
707 let ctx = CommandContext {
709 command_path: vec!["list".into()],
710 app_state: app_state.clone(),
711 extensions: Extensions::new(),
712 };
713
714 let db = ctx.app_state.get::<Database>().unwrap();
716 assert_eq!(db.url, "postgres://localhost");
717
718 let config = ctx.app_state.get::<Config>().unwrap();
719 assert!(config.debug);
720
721 assert_eq!(Rc::strong_count(&ctx.app_state), 2);
723 }
724
725 #[test]
726 fn test_command_context_app_state_get_required() {
727 struct Present;
728
729 let mut app_state = Extensions::new();
730 app_state.insert(Present);
731
732 let ctx = CommandContext {
733 command_path: vec![],
734 app_state: Rc::new(app_state),
735 extensions: Extensions::new(),
736 };
737
738 assert!(ctx.app_state.get_required::<Present>().is_ok());
740
741 #[derive(Debug)]
743 struct Missing;
744 let err = ctx.app_state.get_required::<Missing>();
745 assert!(err.is_err());
746 assert!(err.unwrap_err().to_string().contains("Extension missing"));
747 }
748
749 #[test]
751 fn test_extensions_insert_and_get() {
752 struct MyState {
753 value: i32,
754 }
755
756 let mut ext = Extensions::new();
757 assert!(ext.is_empty());
758
759 ext.insert(MyState { value: 42 });
760 assert!(!ext.is_empty());
761 assert_eq!(ext.len(), 1);
762
763 let state = ext.get::<MyState>().unwrap();
764 assert_eq!(state.value, 42);
765 }
766
767 #[test]
768 fn test_extensions_get_mut() {
769 struct Counter {
770 count: i32,
771 }
772
773 let mut ext = Extensions::new();
774 ext.insert(Counter { count: 0 });
775
776 if let Some(counter) = ext.get_mut::<Counter>() {
777 counter.count += 1;
778 }
779
780 assert_eq!(ext.get::<Counter>().unwrap().count, 1);
781 }
782
783 #[test]
784 fn test_extensions_multiple_types() {
785 struct TypeA(i32);
786 struct TypeB(String);
787
788 let mut ext = Extensions::new();
789 ext.insert(TypeA(1));
790 ext.insert(TypeB("hello".into()));
791
792 assert_eq!(ext.len(), 2);
793 assert_eq!(ext.get::<TypeA>().unwrap().0, 1);
794 assert_eq!(ext.get::<TypeB>().unwrap().0, "hello");
795 }
796
797 #[test]
798 fn test_extensions_replace() {
799 struct Value(i32);
800
801 let mut ext = Extensions::new();
802 ext.insert(Value(1));
803
804 let old = ext.insert(Value(2));
805 assert_eq!(old.unwrap().0, 1);
806 assert_eq!(ext.get::<Value>().unwrap().0, 2);
807 }
808
809 #[test]
810 fn test_extensions_remove() {
811 struct Value(i32);
812
813 let mut ext = Extensions::new();
814 ext.insert(Value(42));
815
816 let removed = ext.remove::<Value>();
817 assert_eq!(removed.unwrap().0, 42);
818 assert!(ext.is_empty());
819 assert!(ext.get::<Value>().is_none());
820 }
821
822 #[test]
823 fn test_extensions_contains() {
824 struct Present;
825 struct Absent;
826
827 let mut ext = Extensions::new();
828 ext.insert(Present);
829
830 assert!(ext.contains::<Present>());
831 assert!(!ext.contains::<Absent>());
832 }
833
834 #[test]
835 fn test_extensions_clear() {
836 struct A;
837 struct B;
838
839 let mut ext = Extensions::new();
840 ext.insert(A);
841 ext.insert(B);
842 assert_eq!(ext.len(), 2);
843
844 ext.clear();
845 assert!(ext.is_empty());
846 }
847
848 #[test]
849 fn test_extensions_missing_type_returns_none() {
850 struct NotInserted;
851
852 let ext = Extensions::new();
853 assert!(ext.get::<NotInserted>().is_none());
854 }
855
856 #[test]
857 fn test_extensions_get_required() {
858 #[derive(Debug)]
859 struct Config {
860 value: i32,
861 }
862
863 let mut ext = Extensions::new();
864 ext.insert(Config { value: 100 });
865
866 let val = ext.get_required::<Config>();
868 assert!(val.is_ok());
869 assert_eq!(val.unwrap().value, 100);
870
871 #[derive(Debug)]
873 struct Missing;
874 let err = ext.get_required::<Missing>();
875 assert!(err.is_err());
876 assert!(err
877 .unwrap_err()
878 .to_string()
879 .contains("Extension missing: type"));
880 }
881
882 #[test]
883 fn test_extensions_get_mut_required() {
884 #[derive(Debug)]
885 struct State {
886 count: i32,
887 }
888
889 let mut ext = Extensions::new();
890 ext.insert(State { count: 0 });
891
892 {
894 let val = ext.get_mut_required::<State>();
895 assert!(val.is_ok());
896 val.unwrap().count += 1;
897 }
898 assert_eq!(ext.get_required::<State>().unwrap().count, 1);
899
900 #[derive(Debug)]
902 struct Missing;
903 let err = ext.get_mut_required::<Missing>();
904 assert!(err.is_err());
905 }
906
907 #[test]
908 fn test_extensions_clone_behavior() {
909 struct Data(i32);
911
912 let mut original = Extensions::new();
913 original.insert(Data(42));
914
915 let cloned = original.clone();
916
917 assert!(original.get::<Data>().is_some());
919
920 assert!(cloned.is_empty());
922 assert!(cloned.get::<Data>().is_none());
923 }
924
925 #[test]
926 fn test_output_render() {
927 let output: Output<String> = Output::Render("success".into());
928 assert!(output.is_render());
929 assert!(!output.is_silent());
930 assert!(!output.is_binary());
931 }
932
933 #[test]
934 fn test_output_silent() {
935 let output: Output<String> = Output::Silent;
936 assert!(!output.is_render());
937 assert!(output.is_silent());
938 assert!(!output.is_binary());
939 }
940
941 #[test]
942 fn test_output_binary() {
943 let output: Output<String> = Output::Binary {
944 data: vec![0x25, 0x50, 0x44, 0x46],
945 filename: "report.pdf".into(),
946 };
947 assert!(!output.is_render());
948 assert!(!output.is_silent());
949 assert!(output.is_binary());
950 }
951
952 #[test]
953 fn test_run_result_handled() {
954 let result = RunResult::Handled("output".into());
955 assert!(result.is_handled());
956 assert!(!result.is_binary());
957 assert!(!result.is_silent());
958 assert_eq!(result.output(), Some("output"));
959 assert!(result.matches().is_none());
960 }
961
962 #[test]
963 fn test_run_result_silent() {
964 let result = RunResult::Silent;
965 assert!(!result.is_handled());
966 assert!(!result.is_binary());
967 assert!(result.is_silent());
968 }
969
970 #[test]
971 fn test_run_result_binary() {
972 let bytes = vec![0x25, 0x50, 0x44, 0x46];
973 let result = RunResult::Binary(bytes.clone(), "report.pdf".into());
974 assert!(!result.is_handled());
975 assert!(result.is_binary());
976 assert!(!result.is_silent());
977
978 let (data, filename) = result.binary().unwrap();
979 assert_eq!(data, &bytes);
980 assert_eq!(filename, "report.pdf");
981 }
982
983 #[test]
984 fn test_run_result_no_match() {
985 let matches = clap::Command::new("test").get_matches_from(vec!["test"]);
986 let result = RunResult::NoMatch(matches);
987 assert!(!result.is_handled());
988 assert!(!result.is_binary());
989 assert!(result.matches().is_some());
990 }
991
992 #[test]
993 fn test_fn_handler() {
994 let mut handler = FnHandler::new(|_m: &ArgMatches, _ctx: &CommandContext| {
995 Ok(Output::Render(json!({"status": "ok"})))
996 });
997
998 let ctx = CommandContext::default();
999 let matches = clap::Command::new("test").get_matches_from(vec!["test"]);
1000
1001 let result = handler.handle(&matches, &ctx);
1002 assert!(result.is_ok());
1003 }
1004
1005 #[test]
1006 fn test_fn_handler_mutation() {
1007 let mut counter = 0u32;
1008
1009 let mut handler = FnHandler::new(|_m: &ArgMatches, _ctx: &CommandContext| {
1010 counter += 1;
1011 Ok(Output::Render(counter))
1012 });
1013
1014 let ctx = CommandContext::default();
1015 let matches = clap::Command::new("test").get_matches_from(vec!["test"]);
1016
1017 let _ = handler.handle(&matches, &ctx);
1018 let _ = handler.handle(&matches, &ctx);
1019 let result = handler.handle(&matches, &ctx);
1020
1021 assert!(result.is_ok());
1022 if let Ok(Output::Render(count)) = result {
1023 assert_eq!(count, 3);
1024 }
1025 }
1026
1027 #[test]
1029 fn test_into_handler_result_from_result_ok() {
1030 use super::IntoHandlerResult;
1031
1032 let result: Result<String, anyhow::Error> = Ok("hello".to_string());
1033 let handler_result = result.into_handler_result();
1034
1035 assert!(handler_result.is_ok());
1036 match handler_result.unwrap() {
1037 Output::Render(s) => assert_eq!(s, "hello"),
1038 _ => panic!("Expected Output::Render"),
1039 }
1040 }
1041
1042 #[test]
1043 fn test_into_handler_result_from_result_err() {
1044 use super::IntoHandlerResult;
1045
1046 let result: Result<String, anyhow::Error> = Err(anyhow::anyhow!("test error"));
1047 let handler_result = result.into_handler_result();
1048
1049 assert!(handler_result.is_err());
1050 assert!(handler_result
1051 .unwrap_err()
1052 .to_string()
1053 .contains("test error"));
1054 }
1055
1056 #[test]
1057 fn test_into_handler_result_passthrough_render() {
1058 use super::IntoHandlerResult;
1059
1060 let handler_result: HandlerResult<String> = Ok(Output::Render("hello".to_string()));
1061 let result = handler_result.into_handler_result();
1062
1063 assert!(result.is_ok());
1064 match result.unwrap() {
1065 Output::Render(s) => assert_eq!(s, "hello"),
1066 _ => panic!("Expected Output::Render"),
1067 }
1068 }
1069
1070 #[test]
1071 fn test_into_handler_result_passthrough_silent() {
1072 use super::IntoHandlerResult;
1073
1074 let handler_result: HandlerResult<String> = Ok(Output::Silent);
1075 let result = handler_result.into_handler_result();
1076
1077 assert!(result.is_ok());
1078 assert!(matches!(result.unwrap(), Output::Silent));
1079 }
1080
1081 #[test]
1082 fn test_into_handler_result_passthrough_binary() {
1083 use super::IntoHandlerResult;
1084
1085 let handler_result: HandlerResult<String> = Ok(Output::Binary {
1086 data: vec![1, 2, 3],
1087 filename: "test.bin".to_string(),
1088 });
1089 let result = handler_result.into_handler_result();
1090
1091 assert!(result.is_ok());
1092 match result.unwrap() {
1093 Output::Binary { data, filename } => {
1094 assert_eq!(data, vec![1, 2, 3]);
1095 assert_eq!(filename, "test.bin");
1096 }
1097 _ => panic!("Expected Output::Binary"),
1098 }
1099 }
1100
1101 #[test]
1102 fn test_fn_handler_with_auto_wrap() {
1103 let mut handler = FnHandler::new(|_m: &ArgMatches, _ctx: &CommandContext| {
1105 Ok::<_, anyhow::Error>("auto-wrapped".to_string())
1106 });
1107
1108 let ctx = CommandContext::default();
1109 let matches = clap::Command::new("test").get_matches_from(vec!["test"]);
1110
1111 let result = handler.handle(&matches, &ctx);
1112 assert!(result.is_ok());
1113 match result.unwrap() {
1114 Output::Render(s) => assert_eq!(s, "auto-wrapped"),
1115 _ => panic!("Expected Output::Render"),
1116 }
1117 }
1118
1119 #[test]
1120 fn test_fn_handler_with_explicit_output() {
1121 let mut handler =
1123 FnHandler::new(|_m: &ArgMatches, _ctx: &CommandContext| Ok(Output::<()>::Silent));
1124
1125 let ctx = CommandContext::default();
1126 let matches = clap::Command::new("test").get_matches_from(vec!["test"]);
1127
1128 let result = handler.handle(&matches, &ctx);
1129 assert!(result.is_ok());
1130 assert!(matches!(result.unwrap(), Output::Silent));
1131 }
1132
1133 #[test]
1134 fn test_fn_handler_with_custom_error_type() {
1135 #[derive(Debug)]
1137 struct CustomError(String);
1138
1139 impl std::fmt::Display for CustomError {
1140 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1141 write!(f, "CustomError: {}", self.0)
1142 }
1143 }
1144
1145 impl std::error::Error for CustomError {}
1146
1147 let mut handler = FnHandler::new(|_m: &ArgMatches, _ctx: &CommandContext| {
1148 Err::<String, CustomError>(CustomError("oops".to_string()))
1149 });
1150
1151 let ctx = CommandContext::default();
1152 let matches = clap::Command::new("test").get_matches_from(vec!["test"]);
1153
1154 let result = handler.handle(&matches, &ctx);
1155 assert!(result.is_err());
1156 assert!(result
1157 .unwrap_err()
1158 .to_string()
1159 .contains("CustomError: oops"));
1160 }
1161
1162 #[test]
1164 fn test_simple_fn_handler_basic() {
1165 use super::SimpleFnHandler;
1166
1167 let mut handler = SimpleFnHandler::new(|_m: &ArgMatches| {
1168 Ok::<_, anyhow::Error>("no context needed".to_string())
1169 });
1170
1171 let ctx = CommandContext::default();
1172 let matches = clap::Command::new("test").get_matches_from(vec!["test"]);
1173
1174 let result = handler.handle(&matches, &ctx);
1175 assert!(result.is_ok());
1176 match result.unwrap() {
1177 Output::Render(s) => assert_eq!(s, "no context needed"),
1178 _ => panic!("Expected Output::Render"),
1179 }
1180 }
1181
1182 #[test]
1183 fn test_simple_fn_handler_with_args() {
1184 use super::SimpleFnHandler;
1185
1186 let mut handler = SimpleFnHandler::new(|m: &ArgMatches| {
1187 let verbose = m.get_flag("verbose");
1188 Ok::<_, anyhow::Error>(verbose)
1189 });
1190
1191 let ctx = CommandContext::default();
1192 let matches = clap::Command::new("test")
1193 .arg(
1194 clap::Arg::new("verbose")
1195 .short('v')
1196 .action(clap::ArgAction::SetTrue),
1197 )
1198 .get_matches_from(vec!["test", "-v"]);
1199
1200 let result = handler.handle(&matches, &ctx);
1201 assert!(result.is_ok());
1202 match result.unwrap() {
1203 Output::Render(v) => assert!(v),
1204 _ => panic!("Expected Output::Render"),
1205 }
1206 }
1207
1208 #[test]
1209 fn test_simple_fn_handler_explicit_output() {
1210 use super::SimpleFnHandler;
1211
1212 let mut handler = SimpleFnHandler::new(|_m: &ArgMatches| Ok(Output::<()>::Silent));
1213
1214 let ctx = CommandContext::default();
1215 let matches = clap::Command::new("test").get_matches_from(vec!["test"]);
1216
1217 let result = handler.handle(&matches, &ctx);
1218 assert!(result.is_ok());
1219 assert!(matches!(result.unwrap(), Output::Silent));
1220 }
1221
1222 #[test]
1223 fn test_simple_fn_handler_error() {
1224 use super::SimpleFnHandler;
1225
1226 let mut handler = SimpleFnHandler::new(|_m: &ArgMatches| {
1227 Err::<String, _>(anyhow::anyhow!("simple error"))
1228 });
1229
1230 let ctx = CommandContext::default();
1231 let matches = clap::Command::new("test").get_matches_from(vec!["test"]);
1232
1233 let result = handler.handle(&matches, &ctx);
1234 assert!(result.is_err());
1235 assert!(result.unwrap_err().to_string().contains("simple error"));
1236 }
1237
1238 #[test]
1239 fn test_simple_fn_handler_mutation() {
1240 use super::SimpleFnHandler;
1241
1242 let mut counter = 0u32;
1243 let mut handler = SimpleFnHandler::new(|_m: &ArgMatches| {
1244 counter += 1;
1245 Ok::<_, anyhow::Error>(counter)
1246 });
1247
1248 let ctx = CommandContext::default();
1249 let matches = clap::Command::new("test").get_matches_from(vec!["test"]);
1250
1251 let _ = handler.handle(&matches, &ctx);
1252 let _ = handler.handle(&matches, &ctx);
1253 let result = handler.handle(&matches, &ctx);
1254
1255 assert!(result.is_ok());
1256 match result.unwrap() {
1257 Output::Render(n) => assert_eq!(n, 3),
1258 _ => panic!("Expected Output::Render"),
1259 }
1260 }
1261}