soma_cli/
provider_command.rs1use anyhow::{anyhow, Result};
9use serde_json::{json, Value};
10use soma_application::{ExecuteActionRequest, SomaApplication};
11use std::path::PathBuf;
12
13use crate::{cli_execution_context, Command};
14
15#[derive(Debug, PartialEq, Eq)]
16pub enum ProviderCommand {
17 Validate,
18 Inspect,
19 Test {
20 action: String,
21 json: Value,
22 },
23 List {
25 dir: Option<PathBuf>,
26 json: bool,
27 },
28 Lint {
30 dir: Option<PathBuf>,
31 json: bool,
32 },
33 Status {
35 dir: Option<PathBuf>,
36 json: bool,
37 },
38}
39
40impl ProviderCommand {
41 pub fn is_non_executing(&self) -> bool {
45 matches!(
46 self,
47 ProviderCommand::List { .. }
48 | ProviderCommand::Lint { .. }
49 | ProviderCommand::Status { .. }
50 )
51 }
52}
53
54pub(crate) async fn run_provider_management_command(
55 command: &ProviderCommand,
56 application: &SomaApplication,
57 destructive_confirmed: bool,
58) -> Result<Value> {
59 match command {
60 ProviderCommand::Validate => Ok(application.provider_validation_summary()),
61 ProviderCommand::Inspect => Ok(application.provider_inspection_report()),
62 ProviderCommand::Test { action, json } => {
63 let provider = application.provider_for_action(action);
64 match application
65 .execute_action(
66 ExecuteActionRequest {
67 action: action.clone(),
68 params: json.clone(),
69 },
70 cli_execution_context(destructive_confirmed),
71 )
72 .await
73 {
74 Ok(output) => Ok(json!({
75 "schema_version": 1,
76 "ok": true,
77 "action": action,
78 "provider": provider,
79 "result": output.output
80 })),
81 Err(error) => Err(anyhow!(error)),
82 }
83 }
84 ProviderCommand::List { .. }
85 | ProviderCommand::Lint { .. }
86 | ProviderCommand::Status { .. } => {
87 unreachable!("non-executing provider commands are handled before registry construction")
88 }
89 }
90}
91
92pub(crate) fn parse_providers_command(args: &[String]) -> Result<Command> {
93 match args {
94 [action] if action == "validate" => Ok(Command::Providers(ProviderCommand::Validate)),
95 [action] if action == "inspect" => Ok(Command::Providers(ProviderCommand::Inspect)),
96 [action, provider_action] if action == "test" => {
97 Ok(Command::Providers(ProviderCommand::Test {
98 action: provider_action.clone(),
99 json: json!({}),
100 }))
101 }
102 [action, provider_action, flag, payload] if action == "test" && flag == "--json" => {
103 Ok(Command::Providers(ProviderCommand::Test {
104 action: provider_action.clone(),
105 json: serde_json::from_str(payload).map_err(|error| {
106 anyhow!("providers test {provider_action} --json must be valid JSON: {error}")
107 })?,
108 }))
109 }
110 [action, rest @ ..] if action == "list" || action == "lint" || action == "status" => {
111 let (dir, json) = parse_providers_dir_flags(action, rest)?;
112 Ok(Command::Providers(match action.as_str() {
113 "list" => ProviderCommand::List { dir, json },
114 "lint" => ProviderCommand::Lint { dir, json },
115 _ => ProviderCommand::Status { dir, json },
116 }))
117 }
118 [] => Err(anyhow!(
119 "providers requires list, lint, status, validate, inspect, or test ACTION"
120 )),
121 [unexpected, ..] => Err(anyhow!("providers does not accept argument `{unexpected}`")),
122 }
123}
124
125fn parse_providers_dir_flags(command: &str, args: &[String]) -> Result<(Option<PathBuf>, bool)> {
126 let mut dir = None;
127 let mut json = false;
128 let mut index = 0;
129 while index < args.len() {
130 match args[index].as_str() {
131 "--dir" => {
132 let value = args
133 .get(index + 1)
134 .ok_or_else(|| anyhow!("providers {command} --dir requires a value"))?;
135 if value.starts_with("--") {
136 return Err(anyhow!("providers {command} --dir requires a value"));
137 }
138 dir = Some(PathBuf::from(value));
139 index += 2;
140 }
141 "--json" => {
142 json = true;
143 index += 1;
144 }
145 unknown => {
146 return Err(anyhow!(
147 "providers {command} does not accept argument `{unknown}`"
148 ))
149 }
150 }
151 }
152 Ok((dir, json))
153}
154
155#[cfg(test)]
156#[path = "provider_command_tests.rs"]
157mod tests;