1use anyhow::Result;
9use clap::Subcommand;
10use colored::Colorize;
11use serde::Serialize;
12
13use raps_acc::admin::AccountAdminClient;
14
15use crate::output::OutputFormat;
16
17#[derive(Debug, Subcommand)]
18pub enum TemplateCommands {
19 List {
21 #[arg(short, long)]
23 account: Option<String>,
24
25 #[arg(long)]
27 limit: Option<usize>,
28 },
29
30 Info {
32 #[arg(short, long)]
34 account: Option<String>,
35
36 template_id: String,
38 },
39
40 Create {
42 #[arg(short, long)]
44 account: Option<String>,
45
46 #[arg(long)]
48 name: String,
49 },
50
51 Update {
53 #[arg(short, long)]
55 account: Option<String>,
56
57 template_id: String,
59
60 #[arg(long)]
62 name: Option<String>,
63 },
64
65 Archive {
67 #[arg(short, long)]
69 account: Option<String>,
70
71 template_id: String,
73 },
74}
75
76#[derive(Serialize)]
77struct TemplateOutput {
78 id: String,
79 name: String,
80 status: String,
81}
82
83impl TemplateCommands {
84 pub async fn execute(
85 self,
86 admin_client: &AccountAdminClient,
87 output_format: OutputFormat,
88 ) -> Result<()> {
89 match self {
90 TemplateCommands::List { account, limit } => {
91 let account_id = get_account_id(account)?;
92 let limit = limit.unwrap_or(100);
93
94 if output_format.supports_colors() {
95 println!("{}", "Fetching templates...".dimmed());
96 }
97
98 let templates = admin_client.list_all_templates(&account_id).await?;
99 let templates: Vec<_> = templates.into_iter().take(limit).collect();
100
101 let outputs: Vec<TemplateOutput> = templates
102 .iter()
103 .map(|t| TemplateOutput {
104 id: t.id.clone(),
105 name: t.name.clone(),
106 status: t.status.clone().unwrap_or_else(|| "unknown".to_string()),
107 })
108 .collect();
109
110 if outputs.is_empty() {
111 match output_format {
112 OutputFormat::Table => {
113 println!("{}", "No templates found.".yellow());
114 }
115 _ => output_format.write(&Vec::<TemplateOutput>::new())?,
116 }
117 return Ok(());
118 }
119
120 match output_format {
121 OutputFormat::Table => {
122 println!("\n{}", "Templates:".bold());
123 println!("{}", "─".repeat(80));
124 for t in &outputs {
125 println!("{:<40} {:<30} {}", t.id.cyan(), t.name, t.status.green());
126 }
127 println!("{}", "─".repeat(80));
128 println!("{} {} template(s)", "→".cyan(), outputs.len());
129 }
130 _ => output_format.write(&outputs)?,
131 }
132
133 Ok(())
134 }
135
136 TemplateCommands::Info {
137 account,
138 template_id,
139 } => {
140 let account_id = get_account_id(account)?;
141 let project = admin_client.get_project(&account_id, &template_id).await?;
142
143 let output = TemplateOutput {
144 id: project.id.clone(),
145 name: project.name.clone(),
146 status: project
147 .status
148 .clone()
149 .unwrap_or_else(|| "unknown".to_string()),
150 };
151
152 match output_format {
153 OutputFormat::Table => {
154 println!("\n{}", "Template Details:".bold());
155 println!("{}", "─".repeat(60));
156 println!("{:<15} {}", "ID:".bold(), output.id.cyan());
157 println!("{:<15} {}", "Name:".bold(), output.name);
158 println!("{:<15} {}", "Status:".bold(), output.status);
159 println!("{}", "─".repeat(60));
160 }
161 _ => output_format.write(&output)?,
162 }
163
164 Ok(())
165 }
166
167 TemplateCommands::Create { account, name } => {
168 let account_id = get_account_id(account)?;
169
170 let request = raps_acc::admin::CreateProjectRequest {
171 name,
172 r#type: Some("Office".to_string()),
173 ..Default::default()
176 };
177
178 let project = admin_client.create_project(&account_id, request).await?;
179
180 match output_format {
181 OutputFormat::Table => {
182 println!(
183 "\n{} Template created: {} ({})",
184 "✓".green().bold(),
185 project.name,
186 project.id.cyan()
187 );
188 }
189 _ => {
190 let output = TemplateOutput {
191 id: project.id,
192 name: project.name,
193 status: project.status.unwrap_or_else(|| "pending".to_string()),
194 };
195 output_format.write(&output)?;
196 }
197 }
198
199 Ok(())
200 }
201
202 TemplateCommands::Update {
203 account,
204 template_id,
205 name,
206 } => {
207 let account_id = get_account_id(account)?;
208
209 let request = raps_acc::admin::UpdateProjectRequest {
210 name,
211 ..Default::default()
212 };
213
214 let project = admin_client
215 .update_project(&account_id, &template_id, request)
216 .await?;
217
218 match output_format {
219 OutputFormat::Table => {
220 println!(
221 "\n{} Template updated: {} ({})",
222 "✓".green().bold(),
223 project.name,
224 project.id.cyan()
225 );
226 }
227 _ => {
228 let output = TemplateOutput {
229 id: project.id,
230 name: project.name,
231 status: project.status.unwrap_or_else(|| "unknown".to_string()),
232 };
233 output_format.write(&output)?;
234 }
235 }
236
237 Ok(())
238 }
239
240 TemplateCommands::Archive {
241 account,
242 template_id,
243 } => {
244 let account_id = get_account_id(account)?;
245 admin_client
246 .archive_project(&account_id, &template_id)
247 .await?;
248
249 match output_format {
250 OutputFormat::Table => {
251 println!(
252 "\n{} Template archived: {}",
253 "✓".green().bold(),
254 template_id.cyan()
255 );
256 }
257 _ => {
258 let output = TemplateOutput {
259 id: template_id,
260 name: String::new(),
261 status: "archived".to_string(),
262 };
263 output_format.write(&output)?;
264 }
265 }
266
267 Ok(())
268 }
269 }
270 }
271}
272
273fn get_account_id(account: Option<String>) -> Result<String> {
274 let account_id = account.or_else(|| std::env::var("APS_ACCOUNT_ID").ok());
275 match account_id {
276 Some(id) if !id.is_empty() => Ok(id),
277 _ => {
278 anyhow::bail!(
279 "Account ID is required. Use --account or set APS_ACCOUNT_ID environment variable."
280 );
281 }
282 }
283}