greentic_bundle/cli/
add.rs1use std::path::PathBuf;
2
3use anyhow::Result;
4use clap::{Args, Subcommand};
5use serde::Serialize;
6
7#[derive(Debug, Args)]
8pub struct AddArgs {
9 #[command(subcommand)]
10 pub command: AddCommand,
11}
12
13#[derive(Debug, Subcommand)]
14pub enum AddCommand {
15 #[command(about = "cli.add.app_pack.about")]
16 AppPack(MutationArgs),
17 #[command(about = "cli.add.extension_provider.about")]
18 ExtensionProvider(MutationArgs),
19}
20
21#[derive(Debug, Args)]
22pub struct MutationArgs {
23 #[arg(value_name = "ID")]
24 pub id: String,
25
26 #[arg(
27 long,
28 value_name = "PATH",
29 default_value = ".",
30 help = "cli.add.root.option"
31 )]
32 pub root: PathBuf,
33
34 #[arg(long, default_value_t = false, help = "cli.option.dry_run")]
35 pub dry_run: bool,
36
37 #[arg(long, default_value_t = false, help = "cli.option.execute")]
38 pub execute: bool,
39}
40
41#[derive(Debug, Serialize)]
42struct MutationPreview {
43 root: String,
44 field: String,
45 action: String,
46 item: String,
47 execute: bool,
48 changed: bool,
49 current_values: Vec<String>,
50 next_values: Vec<String>,
51 expected_file_writes: Vec<String>,
52}
53
54pub fn run(args: AddArgs) -> Result<()> {
55 match args.command {
56 AddCommand::AppPack(args) => mutate(args, crate::project::ReferenceField::AppPack),
57 AddCommand::ExtensionProvider(args) => {
58 mutate(args, crate::project::ReferenceField::ExtensionProvider)
59 }
60 }
61}
62
63fn mutate(args: MutationArgs, field: crate::project::ReferenceField) -> Result<()> {
64 let mut workspace = crate::project::read_bundle_workspace(&args.root)?;
65 let current_values = workspace.references(field).to_vec();
66 if !workspace
67 .references(field)
68 .iter()
69 .any(|value| value == &args.id)
70 {
71 workspace.references_mut(field).push(args.id.clone());
72 }
73 workspace.canonicalize();
74 let next_values = workspace.references(field).to_vec();
75 let changed = current_values != next_values;
76 let execute = args.execute && !args.dry_run;
77 if execute {
78 crate::project::write_bundle_workspace(&args.root, &workspace)?;
79 crate::project::sync_lock_with_workspace(&args.root, &workspace)?;
80 crate::project::sync_project(&args.root)?;
81 }
82 let preview = MutationPreview {
83 root: args.root.display().to_string(),
84 field: field_name(field).to_string(),
85 action: "add".to_string(),
86 item: args.id,
87 execute,
88 changed,
89 current_values,
90 next_values,
91 expected_file_writes: if execute {
92 vec![
93 args.root
94 .join(crate::project::WORKSPACE_ROOT_FILE)
95 .display()
96 .to_string(),
97 args.root
98 .join(crate::project::LOCK_FILE)
99 .display()
100 .to_string(),
101 args.root
102 .join("resolved/default.yaml")
103 .display()
104 .to_string(),
105 args.root
106 .join("state/resolved/default.yaml")
107 .display()
108 .to_string(),
109 ]
110 } else {
111 vec![
112 args.root
113 .join(crate::project::WORKSPACE_ROOT_FILE)
114 .display()
115 .to_string(),
116 ]
117 },
118 };
119 println!("{}", serde_json::to_string_pretty(&preview)?);
120 Ok(())
121}
122
123fn field_name(field: crate::project::ReferenceField) -> &'static str {
124 match field {
125 crate::project::ReferenceField::AppPack => "app_packs",
126 crate::project::ReferenceField::ExtensionProvider => "extension_providers",
127 }
128}