1use anyhow::Result;
2use clap::Parser;
3
4use crate::{cmd, errors, eval, model, query};
5
6#[derive(Parser, Clone, Debug)]
8#[command(author, about, long_about)]
9pub struct ShellOptions {
10 #[arg(short, long, action = clap::ArgAction::Count)]
12 verbose: u8,
13 #[arg(default_value = ".")]
15 query: String,
16 tree: Option<String>,
18}
19
20pub fn main(app_context: &model::ApplicationContext, options: &ShellOptions) -> Result<()> {
21 let config = app_context.get_root_config_mut();
22 let contexts = query::resolve_trees(app_context, config, None, &options.query);
23 if contexts.is_empty() {
24 return Err(errors::GardenError::EmptyTreeQueryResult(options.query.clone()).into());
25 }
26 let mut context = contexts[0].clone();
27
28 for ctx in &contexts {
32 if ctx.tree == options.query {
33 context = ctx.clone();
34 break;
35 }
36 }
37
38 if let Some(tree) = &options.tree {
39 let mut found = false;
40 if let Some(ctx) = query::tree_from_name(config, tree, None, None) {
41 for query_ctx in &contexts {
42 if ctx.tree == query_ctx.tree {
43 context = query_ctx.clone();
44 found = true;
45 break;
46 }
47 }
48 } else {
49 error!("unable to find '{}': No tree exists with that name", tree);
50 }
51 if !found {
52 error!(
53 "'{}' was not found in the tree query '{}'",
54 tree, options.query
55 );
56 }
57 }
58
59 let graft_config = context.config.map(|id| app_context.get_config(id));
61 let shell_expr = if config.interactive_shell.is_empty() {
62 &config.shell
63 } else {
64 &config.interactive_shell
65 };
66 let shell = eval::tree_value(
67 app_context,
68 config,
69 graft_config,
70 shell_expr,
71 &context.tree,
72 context.garden.as_ref(),
73 );
74
75 let verbose = app_context.options.verbose + options.verbose;
76 let quiet = verbose == 0;
77 if let Some(value) = shlex::split(&shell) {
78 cmd::exec_in_context(
79 app_context,
80 config,
81 &context,
82 quiet,
83 verbose,
84 false,
85 &value,
86 )
87 .map_err(|err| err.into())
88 } else {
89 Err(errors::GardenError::InvalidConfiguration {
90 msg: format!("unable to shlex::split '{shell}'"),
91 }
92 .into())
93 }
94}