leo_lang/cli/commands/
remove.rs

1// Copyright (C) 2019-2025 Provable Inc.
2// This file is part of the Leo library.
3
4// The Leo library is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// The Leo library is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
16
17use super::*;
18use leo_package::Manifest;
19
20/// Remove a dependency from the current package.
21#[derive(Parser, Debug)]
22#[clap(name = "leo", author = "The Leo Team <leo@provable.com>", version)]
23pub struct LeoRemove {
24    #[clap(
25        name = "NAME",
26        help = "The dependency name. Ex: `credits.aleo` or `credits`.",
27        required_unless_present = "all"
28    )]
29    pub(crate) name: Option<String>,
30
31    #[clap(
32        long,
33        help = "Clear all previous dependencies (or dev dependencies, if used with --dev).",
34        default_value = "false"
35    )]
36    pub(crate) all: bool,
37
38    #[clap(long, help = "This is a dev dependency.", default_value = "false")]
39    pub(crate) dev: bool,
40}
41
42impl Command for LeoRemove {
43    type Input = ();
44    type Output = ();
45
46    fn log_span(&self) -> Span {
47        tracing::span!(tracing::Level::INFO, "Leo")
48    }
49
50    fn prelude(&self, _: Context) -> Result<Self::Input> {
51        Ok(())
52    }
53
54    fn apply(self, context: Context, _: Self::Input) -> Result<Self::Output> {
55        let path = context.dir()?;
56
57        let manifest_path = path.join(leo_package::MANIFEST_FILENAME);
58        let mut manifest = Manifest::read_from_file(&manifest_path)?;
59
60        let dependencies = if self.dev {
61            if manifest.dev_dependencies.is_none() {
62                manifest.dev_dependencies = Some(Vec::new())
63            }
64            manifest.dev_dependencies.as_mut().unwrap()
65        } else {
66            if manifest.dependencies.is_none() {
67                manifest.dependencies = Some(Vec::new())
68            }
69            manifest.dependencies.as_mut().unwrap()
70        };
71
72        if self.all {
73            *dependencies = Vec::new();
74        } else {
75            let name =
76                self.name.map(|name| if name.ends_with(".aleo") { name } else { format!("{name}.aleo") }).unwrap();
77            let original_len = dependencies.len();
78            for dependency in dependencies.iter() {
79                if dependency.name == name {
80                    if let Some(local_path) = &dependency.path {
81                        tracing::warn!(
82                            "✅ Successfully removed the local dependency {} with path {}.",
83                            dependency.name,
84                            local_path.display()
85                        );
86                    } else {
87                        tracing::warn!("✅ Successfully removed the network dependency {}.", dependency.name);
88                    }
89                }
90            }
91
92            dependencies.retain(|dep| dep.name != name);
93
94            if dependencies.len() == original_len {
95                return Err(PackageError::dependency_not_found(name).into());
96            }
97        }
98
99        manifest.write_to_file(&manifest_path)?;
100
101        Ok(())
102    }
103}