Skip to main content

greentic_bundle/cli/
remove.rs

1use std::path::PathBuf;
2
3use anyhow::Result;
4use clap::{Args, Subcommand};
5use serde::Serialize;
6
7#[derive(Debug, Args)]
8pub struct RemoveArgs {
9    #[command(subcommand)]
10    pub command: RemoveCommand,
11}
12
13#[derive(Debug, Subcommand)]
14pub enum RemoveCommand {
15    #[command(about = "cli.remove.app_pack.about")]
16    AppPack(MutationArgs),
17    #[command(about = "cli.remove.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.remove.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: RemoveArgs) -> Result<()> {
55    match args.command {
56        RemoveCommand::AppPack(args) => mutate(args, crate::project::ReferenceField::AppPack),
57        RemoveCommand::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    workspace
67        .references_mut(field)
68        .retain(|value| value != &args.id);
69    workspace.canonicalize();
70    let next_values = workspace.references(field).to_vec();
71    let changed = current_values != next_values;
72    let execute = args.execute && !args.dry_run;
73    if execute {
74        crate::project::write_bundle_workspace(&args.root, &workspace)?;
75        crate::project::sync_lock_with_workspace(&args.root, &workspace)?;
76        crate::project::sync_project(&args.root)?;
77    }
78    let preview = MutationPreview {
79        root: args.root.display().to_string(),
80        field: field_name(field).to_string(),
81        action: "remove".to_string(),
82        item: args.id,
83        execute,
84        changed,
85        current_values,
86        next_values,
87        expected_file_writes: if execute {
88            vec![
89                args.root
90                    .join(crate::project::WORKSPACE_ROOT_FILE)
91                    .display()
92                    .to_string(),
93                args.root
94                    .join(crate::project::LOCK_FILE)
95                    .display()
96                    .to_string(),
97                args.root
98                    .join("resolved/default.yaml")
99                    .display()
100                    .to_string(),
101                args.root
102                    .join("state/resolved/default.yaml")
103                    .display()
104                    .to_string(),
105            ]
106        } else {
107            vec![
108                args.root
109                    .join(crate::project::WORKSPACE_ROOT_FILE)
110                    .display()
111                    .to_string(),
112            ]
113        },
114    };
115    println!("{}", serde_json::to_string_pretty(&preview)?);
116    Ok(())
117}
118
119fn field_name(field: crate::project::ReferenceField) -> &'static str {
120    match field {
121        crate::project::ReferenceField::AppPack => "app_packs",
122        crate::project::ReferenceField::ExtensionProvider => "extension_providers",
123    }
124}