1use std::{
2 env,
3 error::Error,
4 fmt::{Debug, Display},
5 io,
6 panic::Location,
7 path::{self, Path, PathBuf},
8 sync::{Arc, LazyLock},
9};
10
11use miette::Diagnostic;
12use nu_cmd_base::hook::eval_repl_hooks;
13use nu_protocol::{
14 CompileError, Config, FromValue, IntoValue, LabeledError, ParseError, PipelineData,
15 PipelineExecutionData, ShellError, Span, Value,
16 ast::Block,
17 debugger::WithoutDebug,
18 engine::{Command, EngineState, Stack, StateDelta, StateWorkingSet},
19 shell_error::{io::IoError, network::NetworkError},
20};
21use nu_utils::{consts::ENV_PATH_SEPARATOR_CHAR, sync::KeyedLazyLock};
22use parking_lot::{RwLock, const_rwlock};
23
24use crate::harness::group::GroupKey;
25
26#[cfg(feature = "plugin")]
27use nu_plugin_engine::{GetPlugin, PersistentPlugin, PluginDeclaration};
28#[cfg(feature = "plugin")]
29use nu_protocol::{PluginIdentity, PluginSignature, RegisteredPlugin};
30
31pub static WORKSPACE_ROOT: LazyLock<PathBuf> = LazyLock::new(|| {
35 path::absolute(concat!(env!("CARGO_MANIFEST_DIR"), "/../.."))
36 .expect("could not absolutize root")
37});
38
39static INITIAL_ENGINE_STATES: KeyedLazyLock<GroupKey, EngineState> = KeyedLazyLock::new(|_| {
42 let engine_state = nu_cmd_lang::create_default_context();
45 #[cfg(feature = "plugin")]
46 let engine_state = nu_cmd_plugin::add_plugin_command_context(engine_state);
47 let engine_state = nu_command::add_shell_command_context(engine_state);
48 let engine_state = nu_cmd_extra::add_extra_command_context(engine_state);
49 #[cfg(feature = "os")]
50 let engine_state = nu_cli::add_cli_context(engine_state);
51 let mut engine_state = engine_state;
55
56 engine_state.generate_nu_constant();
57 [
58 ("PWD", Value::test_string(WORKSPACE_ROOT.to_string_lossy())),
59 ("config", Config::default().into_value(Span::unknown())),
60 ("NO_COLOR", Value::test_bool(true)),
61 ]
62 .into_iter()
63 .for_each(|(key, val)| engine_state.add_env_var(key.into(), val));
64
65 #[cfg(windows)]
67 if let Ok(path_ext) = env::var("PATHEXT") {
68 engine_state.add_env_var("PATHEXT".into(), Value::test_string(path_ext));
69 }
70
71 nu_std::load_standard_library(&mut engine_state).expect("could not load standard library");
72
73 engine_state
74});
75
76#[cfg(feature = "plugin")]
78#[derive(Debug, Clone)]
79pub struct PluginAutoLoader {
80 pub identity: Arc<PluginIdentity>,
81 pub plugin: Option<Arc<PersistentPlugin>>,
82 pub signatures: Option<Arc<[PluginSignature]>>,
83}
84
85pub static PATH_ENV_AUTO_LOAD: RwLock<Vec<PathBuf>> = const_rwlock(Vec::new());
87
88#[cfg(feature = "plugin")]
90pub static PLUGIN_AUTO_LOAD: RwLock<Vec<PluginAutoLoader>> = const_rwlock(Vec::new());
91
92#[cfg_attr(feature = "plugin", doc = "[`PLUGIN_AUTO_LOAD`]")]
124#[cfg_attr(not(feature = "plugin"), doc = "`PLUGIN_AUTO_LOAD`")]
125pub fn test() -> NuTester {
155 let mut engine_state = INITIAL_ENGINE_STATES.get(&GroupKey::current()).clone();
156 engine_state.make_session_state_unique();
157
158 let tester = NuTester {
159 engine_state,
160 stack: Stack::new().collect_value(),
161 fname_counter: Counter::default(),
162 };
163
164 let tester = tester.append_path(&*PATH_ENV_AUTO_LOAD.read());
165
166 #[cfg(feature = "plugin")]
167 let tester = tester.auto_load_plugins();
168
169 tester
170}
171
172#[derive(Clone)]
177#[non_exhaustive] pub struct NuTester {
179 pub engine_state: EngineState,
180 pub stack: Stack,
181
182 fname_counter: Counter,
184}
185
186#[derive(Default, Clone)]
187struct Counter(u64);
188
189impl Counter {
190 pub fn get(&mut self) -> u64 {
191 let value = self.0;
192 self.0 += 1;
193 value
194 }
195}
196
197impl Default for NuTester {
198 fn default() -> Self {
202 test()
203 }
204}
205
206#[cfg(feature = "plugin")]
207impl NuTester {
208 fn auto_load_plugins(self) -> Self {
212 let mut tester = self;
213 let auto_loaders = PLUGIN_AUTO_LOAD.read();
214 if auto_loaders.is_empty() {
215 return tester;
216 }
217
218 let mut working_set = StateWorkingSet::new(&tester.engine_state);
219 for auto_loader in auto_loaders.iter() {
220 let plugin = working_set.find_or_create_plugin(&auto_loader.identity, || {
221 auto_loader
222 .plugin
223 .as_ref()
224 .map(|plugin| plugin.clone())
225 .unwrap_or_else(|| {
226 Arc::new(PersistentPlugin::new(
227 (*auto_loader.identity).clone(),
228 Default::default(),
229 ))
230 })
231 });
232
233 let plugin: Arc<PersistentPlugin> = plugin
234 .as_any()
235 .downcast()
236 .expect("could not downcast to persistent plugin");
237
238 let mut interface = None;
241
242 if plugin.metadata().is_none() {
244 let interface = interface.get_or_insert_with(|| {
245 plugin
246 .clone()
247 .get_plugin(None)
248 .expect("could not get plugin")
249 });
250
251 plugin.set_metadata(Some(
252 interface
253 .get_metadata()
254 .expect("could not get plugin metadata"),
255 ));
256 }
257
258 let signatures = auto_loader
260 .signatures
261 .as_deref()
262 .map(|signatures| signatures.to_owned())
263 .unwrap_or_else(|| {
264 let interface = interface.get_or_insert_with(|| {
265 plugin
266 .clone()
267 .get_plugin(None)
268 .expect("could not get plugin")
269 });
270 interface
271 .get_signature()
272 .expect("could not get plugin signatures")
273 });
274
275 for signature in signatures {
276 let decl = PluginDeclaration::new(plugin.clone(), signature);
277 working_set.add_decl(Box::new(decl));
278 }
279 }
280
281 tester
282 .engine_state
283 .merge_delta(working_set.render())
284 .expect("could not merge plugin working set");
285
286 tester
287 }
288}
289
290impl NuTester {
291 pub fn new() -> Self {
295 test()
296 }
297
298 pub fn cwd(mut self, cwd: impl Into<PathBuf>) -> Self {
302 let cwd = cwd.into();
303
304 let cwd = match cwd.is_absolute() {
305 true => cwd,
306 false => WORKSPACE_ROOT
307 .join(cwd)
308 .canonicalize()
309 .expect("could not canonicalize path"),
310 };
311
312 self.engine_state
313 .add_env_var("PWD".into(), Value::test_string(cwd.to_string_lossy()));
314 self
315 }
316
317 pub fn locale(mut self, locale: impl Into<String>) -> Self {
319 self.engine_state.add_env_var(
320 "NU_TEST_LOCALE_OVERRIDE".into(),
321 Value::test_string(locale.into()),
322 );
323 self
324 }
325
326 pub fn locale_en(self) -> Self {
328 self.locale("en_US.utf8")
329 }
330
331 fn path(&self) -> Vec<Value> {
333 match self.engine_state.get_env_var("PATH") {
334 None => Vec::new(),
335 Some(Value::List { vals, .. }) => vals.to_vec(),
336 Some(Value::String { val, .. }) => val
337 .split(ENV_PATH_SEPARATOR_CHAR)
338 .map(Value::test_string)
339 .collect(),
340 Some(v) => panic!("PATH is neither a list nor a string, is {}", v.get_type()),
341 }
342 }
343
344 pub fn prepend_path(self, entries: impl IntoIterator<Item = impl AsRef<Path>>) -> Self {
346 let path = entries
347 .into_iter()
348 .map(|item| Value::test_string(item.as_ref().to_string_lossy()))
349 .chain(self.path())
350 .collect();
351 self.env("PATH", Value::test_list(path))
352 }
353
354 pub fn append_path(self, entries: impl IntoIterator<Item = impl AsRef<Path>>) -> Self {
356 let path = self
357 .path()
358 .into_iter()
359 .chain(
360 entries
361 .into_iter()
362 .map(|item| Value::test_string(item.as_ref().to_string_lossy())),
363 )
364 .collect();
365 self.env("PATH", Value::test_list(path))
366 }
367
368 pub fn inherit_path(self) -> Self {
375 let path = env::var("PATH").expect("PATH not available in env");
376 self.append_path(path.split(ENV_PATH_SEPARATOR_CHAR))
377 }
378
379 pub fn inherit_env_if_set(self, key: impl AsRef<str>) -> Self {
383 let key = key.as_ref();
384 match env::var(key) {
385 Ok(val) => self.env(key, val),
386 Err(_) => self,
387 }
388 }
389
390 pub fn inherit_rust_toolchain_env(self) -> Self {
413 self.inherit_path()
414 .inherit_env_if_set("PATH")
415 .inherit_env_if_set("CARGO_HOME")
416 .inherit_env_if_set("RUSTUP_HOME")
417 .inherit_env_if_set("RUSTUP_TOOLCHAIN")
418 .inherit_env_if_set("RUSTUP_DIST_SERVER")
419 .inherit_env_if_set("RUSTUP_UPDATE_ROOT")
420 .inherit_env_if_set("HTTP_PROXY")
421 .inherit_env_if_set("HTTPS_PROXY")
422 .inherit_env_if_set("NO_PROXY")
423 .inherit_env_if_set("http_proxy")
424 .inherit_env_if_set("https_proxy")
425 .inherit_env_if_set("no_proxy")
426 }
427
428 #[deprecated(note = "use `#[deps(NU)]` instead")]
432 pub fn add_nu_to_path(self) -> Self {
433 let nu_home = crate::fs::binaries();
434 let path = self.engine_state.get_env_var("PATH");
435 let path = match path {
436 None => nu_home.display().to_string(),
437 Some(path) => format!(
438 "{nu}{sep}{prev}",
439 nu = nu_home.display(),
440 sep = ENV_PATH_SEPARATOR_CHAR,
441 prev = path.as_str().expect("PATH should always be a string")
442 ),
443 };
444 self.env("PATH", path)
445 }
446
447 pub fn env(mut self, key: impl Into<String>, val: impl IntoValue) -> Self {
449 self.engine_state
450 .add_env_var(key.into(), val.into_value(Span::test_data()));
451 self
452 }
453
454 #[track_caller]
458 pub fn run<T: FromValue>(&mut self, code: impl AsRef<str>) -> Result<T> {
459 Self::extract_value(self.run_raw(code)?)
460 }
461
462 #[track_caller]
466 pub fn run_with_data<T: FromValue>(
467 &mut self,
468 code: impl AsRef<str>,
469 data: impl IntoValue,
470 ) -> Result<T> {
471 let input = PipelineData::value(data.into_value(Span::test_data()), None);
472 Self::extract_value(self.run_raw_with_data(code, input)?)
473 }
474
475 #[track_caller]
479 pub fn run_multiple<T: FromValue>(
480 &mut self,
481 pipelines: impl IntoIterator<Item = impl AsRef<str>>,
482 ) -> Result<T> {
483 let last = pipelines
484 .into_iter()
485 .map(|pipeline| self.run(pipeline))
486 .try_fold(Value::test_nothing(), |_, value| value)?;
487 Ok(T::from_value(last)?)
488 }
489
490 #[track_caller]
497 pub fn run_with_hooks<T: FromValue>(&mut self, code: impl AsRef<str>) -> Result<T> {
498 let location = TestLocation(Location::caller());
499 let code = code.as_ref();
500
501 eval_repl_hooks(&mut self.engine_state, &mut self.stack, code)
502 .map_err(|err| TestError {
503 location,
504 kind: TestErrorKind::Shell(err),
505 })
506 .and_then(|()| self.run(code))
507 }
508
509 #[track_caller]
511 pub fn run_raw(&mut self, code: impl AsRef<str>) -> Result<PipelineExecutionData> {
512 self.run_raw_with_data(code, PipelineData::empty())
513 }
514
515 #[track_caller]
519 pub fn run_raw_with_data(
520 &mut self,
521 code: impl AsRef<str>,
522 data: PipelineData,
523 ) -> Result<PipelineExecutionData> {
524 let location = TestLocation(Location::caller());
525 let (delta, block) = self.parse_and_compile(code)?;
526 self.engine_state.merge_delta(delta)?;
527 nu_engine::eval_block::<WithoutDebug>(&self.engine_state, &mut self.stack, &block, data)
528 .map_err(|err| TestError {
529 location,
530 kind: TestErrorKind::Shell(err),
531 })
532 }
533
534 #[track_caller]
535 pub fn parse_and_compile(&mut self, code: impl AsRef<str>) -> Result<(StateDelta, Arc<Block>)> {
536 let location = TestLocation(Location::caller());
537 let code = code.as_ref().as_bytes();
538
539 let mut working_set = StateWorkingSet::new(&self.engine_state);
540 let fname = format!("nu-tester-{}", self.fname_counter.get());
541 let block = nu_parser::parse(&mut working_set, Some(&fname), code, false);
542
543 if let Some(err) = working_set.parse_errors.into_iter().next() {
544 return Err(TestError {
545 location,
546 kind: TestErrorKind::Parse(err),
547 });
548 }
549
550 if let Some(err) = working_set.compile_errors.into_iter().next() {
551 return Err(TestError {
552 location,
553 kind: TestErrorKind::Compile(err),
554 });
555 }
556
557 Ok((working_set.delta, block))
558 }
559
560 #[track_caller]
561 fn extract_value<T: FromValue>(
562 pipeline_execution_data: PipelineExecutionData,
563 ) -> Result<T, TestError> {
564 let pipeline_data = pipeline_execution_data.body;
565 let value = pipeline_data.into_value(Span::test_data())?;
566 let value = T::from_value(value)?;
567 Ok(value)
568 }
569
570 #[track_caller]
572 pub fn examples(&mut self, command: impl Command + 'static) -> Result {
573 let location = TestLocation(Location::caller());
574 for example in command.examples() {
575 match example.result {
576 None => self
577 .parse_and_compile(example.example)
578 .map(|_| ())
579 .map_err(|err| TestError {
580 location,
581 kind: TestErrorKind::ExampleFailed {
582 command: command.name().to_string(),
583 description: example.description.to_string(),
584 code: example.example.to_string(),
585 err: Box::new(err.kind),
586 },
587 })?,
588 Some(expected) => {
589 let got = self.clone().run(example.example)?;
590 if got != expected {
591 return Err(TestError {
592 location,
593 kind: TestErrorKind::ExampleFailed {
594 command: command.name().to_string(),
595 description: example.description.to_string(),
596 code: example.example.to_string(),
597 err: Box::new(TestErrorKind::UnexpectedValue { expected, got }),
598 },
599 });
600 }
601 }
602 }
603 }
604
605 Ok(())
606 }
607}
608
609#[derive(Debug, Clone, PartialEq)]
610pub struct TestError {
611 location: TestLocation,
612 kind: TestErrorKind,
613}
614
615#[derive(Clone, Copy, PartialEq, derive_more::Debug)]
616#[debug("{_0}")]
617pub struct TestLocation(&'static Location<'static>);
618
619#[non_exhaustive]
623#[derive(Debug, Clone, PartialEq)]
624pub enum TestErrorKind {
625 Parse(ParseError),
626 Compile(CompileError),
627 Shell(ShellError),
628 GotValue {
629 got: Value,
630 },
631 NoInner,
632 MultipleInner {
633 count: usize,
634 },
635 UnexpectedErrorKind {
636 expected: &'static str,
637 got: ShellError,
638 },
639 UnexpectedValue {
640 expected: Value,
641 got: Value,
642 },
643 NoCode {
644 expected: String,
645 },
646 UnexpectedCode {
647 expected: String,
648 got: String,
649 },
650 ExampleFailed {
651 command: String,
652 description: String,
653 code: String,
654 err: Box<TestErrorKind>,
655 },
656 Io {
657 message: String,
658 kind: io::ErrorKind,
659 },
660}
661
662impl Display for TestError {
663 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
664 write!(f, "{self:#?}")
665 }
666}
667
668impl Error for TestError {}
669
670impl From<ShellError> for TestError {
671 #[track_caller]
672 fn from(err: ShellError) -> Self {
673 Self {
674 location: TestLocation(Location::caller()),
675 kind: TestErrorKind::Shell(err),
676 }
677 }
678}
679
680impl From<ParseError> for TestError {
681 #[track_caller]
682 fn from(err: ParseError) -> Self {
683 Self {
684 location: TestLocation(Location::caller()),
685 kind: TestErrorKind::Parse(err),
686 }
687 }
688}
689
690impl From<io::Error> for TestError {
691 #[track_caller]
692 fn from(value: io::Error) -> Self {
693 Self {
694 location: TestLocation(Location::caller()),
695 kind: TestErrorKind::Io {
696 message: value.to_string(),
697 kind: value.kind(),
698 },
699 }
700 }
701}
702
703impl TestError {
704 pub fn parse(self) -> Result<ParseError, TestError> {
706 match self.kind {
707 TestErrorKind::Parse(err) => Ok(err),
708 _ => Err(self),
709 }
710 }
711
712 pub fn compile(self) -> Result<CompileError, TestError> {
714 match self.kind {
715 TestErrorKind::Compile(err) => Ok(err),
716 _ => Err(self),
717 }
718 }
719
720 pub fn shell(self) -> Result<ShellError, TestError> {
722 match self.kind {
723 TestErrorKind::Shell(err) => Ok(err),
724 _ => Err(self),
725 }
726 }
727
728 #[track_caller]
730 pub fn update_location(self) -> Self {
731 Self {
732 location: TestLocation(Location::caller()),
733 ..self
734 }
735 }
736}
737
738pub type Result<T = (), E = TestError> = std::result::Result<T, E>;
740
741pub trait TestResultExt: Sized {
743 fn expect_value_eq<T: IntoValue>(self, value: T) -> Result;
745
746 fn expect_error_code_eq(self, code: impl AsRef<str>) -> Result;
748
749 fn expect_shell_error(self) -> Result<ShellError>;
751 fn expect_parse_error(self) -> Result<ParseError>;
753 fn expect_compile_error(self) -> Result<CompileError>;
755
756 fn expect_io_error(self) -> Result<IoError>;
758 fn expect_network_error(self) -> Result<NetworkError>;
760 fn expect_labeled_error(self) -> Result<LabeledError>;
762
763 #[track_caller]
765 fn expect_error(self) -> Result<ShellError> {
766 self.expect_shell_error()
767 }
768}
769
770impl TestResultExt for Result<Value> {
771 #[track_caller]
772 fn expect_value_eq<T: IntoValue>(self, expected: T) -> Result {
773 let expected = expected.into_value(Span::test_data());
774 match self {
775 Err(err) => Err(err.update_location()),
776 Ok(actual) if actual == expected => Ok(()),
777 Ok(actual) => Err(TestError {
778 location: TestLocation(Location::caller()),
779 kind: TestErrorKind::UnexpectedValue {
780 expected,
781 got: actual,
782 },
783 }),
784 }
785 }
786
787 #[track_caller]
788 fn expect_error_code_eq(self, code: impl AsRef<str>) -> Result {
789 let expected = code.as_ref();
790 let got = match self {
791 Ok(got) => {
792 return Err(TestError {
793 location: TestLocation(Location::caller()),
794 kind: TestErrorKind::GotValue { got },
795 });
796 }
797 Err(TestError {
798 kind: TestErrorKind::Shell(ref err),
799 ..
800 }) => err.code(),
801 Err(TestError {
802 kind: TestErrorKind::Compile(ref err),
803 ..
804 }) => err.code(),
805 Err(TestError {
806 kind: TestErrorKind::Parse(ref err),
807 ..
808 }) => err.code(),
809 Err(err) => return Err(err.update_location()),
810 };
811
812 let Some(got) = got else {
813 return Err(TestError {
814 location: TestLocation(Location::caller()),
815 kind: TestErrorKind::NoCode {
816 expected: expected.to_string(),
817 },
818 });
819 };
820
821 let got = got.to_string();
822 match got == expected {
823 true => Ok(()),
824 false => Err(TestError {
825 location: TestLocation(Location::caller()),
826 kind: TestErrorKind::UnexpectedCode {
827 expected: expected.to_string(),
828 got,
829 },
830 }),
831 }
832 }
833
834 #[track_caller]
835 fn expect_shell_error(self) -> Result<ShellError> {
836 match self {
837 Ok(got) => Err(TestError {
838 location: TestLocation(Location::caller()),
839 kind: TestErrorKind::GotValue { got },
840 }),
841 Err(TestError {
842 kind: TestErrorKind::Shell(err),
843 ..
844 }) => Ok(err),
845 Err(err) => Err(err.update_location()),
846 }
847 }
848
849 #[track_caller]
850 fn expect_parse_error(self) -> Result<ParseError> {
851 match self {
852 Ok(got) => Err(TestError {
853 location: TestLocation(Location::caller()),
854 kind: TestErrorKind::GotValue { got },
855 }),
856 Err(TestError {
857 kind: TestErrorKind::Parse(err),
858 ..
859 }) => Ok(err),
860 Err(err) => Err(err.update_location()),
861 }
862 }
863
864 #[track_caller]
865 fn expect_compile_error(self) -> Result<CompileError> {
866 match self {
867 Ok(got) => Err(TestError {
868 location: TestLocation(Location::caller()),
869 kind: TestErrorKind::GotValue { got },
870 }),
871 Err(TestError {
872 kind: TestErrorKind::Compile(err),
873 ..
874 }) => Ok(err),
875 Err(err) => Err(err.update_location()),
876 }
877 }
878
879 #[track_caller]
880 fn expect_io_error(self) -> Result<IoError> {
881 match self {
882 Ok(got) => Err(TestError {
883 location: TestLocation(Location::caller()),
884 kind: TestErrorKind::GotValue { got },
885 }),
886 Err(TestError {
887 kind: TestErrorKind::Shell(ShellError::Io(err)),
888 ..
889 }) => Ok(err),
890 Err(err) => Err(err.update_location()),
891 }
892 }
893
894 #[track_caller]
895 fn expect_network_error(self) -> Result<NetworkError> {
896 match self {
897 Ok(got) => Err(TestError {
898 location: TestLocation(Location::caller()),
899 kind: TestErrorKind::GotValue { got },
900 }),
901 Err(TestError {
902 kind: TestErrorKind::Shell(ShellError::Network(err)),
903 ..
904 }) => Ok(err),
905 Err(err) => Err(err.update_location()),
906 }
907 }
908
909 #[track_caller]
910 fn expect_labeled_error(self) -> Result<LabeledError> {
911 match self {
912 Ok(got) => Err(TestError {
913 location: TestLocation(Location::caller()),
914 kind: TestErrorKind::GotValue { got },
915 }),
916 Err(TestError {
917 kind: TestErrorKind::Shell(ShellError::LabeledError(err)),
918 ..
919 }) => Ok(*err),
920 Err(err) => Err(err.update_location()),
921 }
922 }
923}
924
925pub trait ShellErrorExt {
927 fn into_inner(self) -> Result<ShellError>;
940
941 fn into_labeled(self) -> Result<LabeledError>;
943
944 fn into_chained_iter(self) -> Result<impl Iterator<Item = ShellError>>;
947
948 fn generic_error(self) -> Result<String>;
950
951 fn generic_msg(self) -> Result<String>;
953}
954
955impl ShellErrorExt for ShellError {
956 #[track_caller]
957 fn into_inner(self) -> Result<ShellError> {
958 let no_inner = TestError {
959 location: TestLocation(Location::caller()),
960 kind: TestErrorKind::NoInner,
961 };
962
963 let iter: &mut dyn Iterator<Item = ShellError> = match self {
964 ShellError::Generic(err) => &mut err.inner.into_iter(),
965 ShellError::ChainedError(err) => &mut err.sources_iter(),
966 ShellError::EvalBlockWithInput { sources, .. } => &mut sources.into_iter(),
967 _ => return Err(no_inner),
968 };
969
970 let Some(inner) = iter.next() else {
971 return Err(no_inner);
972 };
973
974 let rest = iter.count();
975 if rest != 0 {
976 return Err(TestError {
977 location: TestLocation(Location::caller()),
978 kind: TestErrorKind::MultipleInner { count: rest + 1 },
979 });
980 }
981
982 Ok(inner)
983 }
984
985 #[track_caller]
986 fn into_labeled(self) -> Result<LabeledError> {
987 match self {
988 ShellError::LabeledError(err) => Ok(*err),
989 got => Err(TestError {
990 location: TestLocation(Location::caller()),
991 kind: TestErrorKind::UnexpectedErrorKind {
992 expected: "Labeled",
993 got,
994 },
995 }),
996 }
997 }
998
999 #[track_caller]
1000 fn into_chained_iter(self) -> Result<impl Iterator<Item = ShellError>> {
1001 match self {
1002 ShellError::ChainedError(err) => Ok(err.sources_iter()),
1003 got => Err(TestError {
1004 location: TestLocation(Location::caller()),
1005 kind: TestErrorKind::UnexpectedErrorKind {
1006 expected: "Chained",
1007 got,
1008 },
1009 }),
1010 }
1011 }
1012
1013 #[track_caller]
1014 fn generic_error(self) -> Result<String> {
1015 match self {
1016 ShellError::Generic(err) => Ok(err.error.into_owned()),
1017 got => Err(TestError {
1018 location: TestLocation(Location::caller()),
1019 kind: TestErrorKind::UnexpectedErrorKind {
1020 expected: "Generic",
1021 got,
1022 },
1023 }),
1024 }
1025 }
1026
1027 #[track_caller]
1028 fn generic_msg(self) -> Result<String> {
1029 match self {
1030 ShellError::Generic(err) => Ok(err.msg.into_owned()),
1031 got => Err(TestError {
1032 location: TestLocation(Location::caller()),
1033 kind: TestErrorKind::UnexpectedErrorKind {
1034 expected: "Generic",
1035 got,
1036 },
1037 }),
1038 }
1039 }
1040}