leo_lang/cli/commands/
add.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::{Dependency, Location, Manifest};
19use std::path::PathBuf;
20
21/// Add a new on-chain or local dependency to the current package.
22#[derive(Parser, Debug)]
23#[clap(name = "leo", author = "The Leo Team <leo@provable.com>", version)]
24pub struct LeoAdd {
25    #[clap(name = "NAME", help = "The dependency name. Ex: `credits.aleo` or `credits`.")]
26    pub(crate) name: String,
27
28    #[clap(flatten)]
29    pub(crate) source: DependencySource,
30
31    #[clap(short = 'c', long, help = "Clear all previous dependencies.", default_value = "false")]
32    pub(crate) clear: bool,
33
34    #[clap(long, help = "This is a development dependency.", default_value = "false")]
35    pub(crate) dev: bool,
36}
37
38#[derive(Parser, Debug)]
39#[group(required = true, multiple = false)]
40pub struct DependencySource {
41    #[clap(short = 'l', long, help = "Whether the dependency is local to the machine.", group = "source")]
42    pub(crate) local: Option<PathBuf>,
43
44    #[clap(short = 'n', long, help = "Whether the dependency is on a live network.", group = "source")]
45    pub(crate) network: bool,
46}
47
48impl Command for LeoAdd {
49    type Input = ();
50    type Output = ();
51
52    fn log_span(&self) -> Span {
53        tracing::span!(tracing::Level::INFO, "Leo")
54    }
55
56    fn prelude(&self, _: Context) -> Result<Self::Input> {
57        Ok(())
58    }
59
60    fn apply(self, context: Context, _: Self::Input) -> Result<Self::Output> {
61        let path = context.dir()?;
62
63        let manifest_path = path.join(leo_package::MANIFEST_FILENAME);
64        let mut manifest = Manifest::read_from_file(&manifest_path)?;
65
66        // Make sure the program name is valid.
67        // Allow both `credits.aleo` and `credits` syntax.
68        let name = if self.name.ends_with(".aleo") { self.name.clone() } else { format!("{}.aleo", self.name) };
69
70        if !leo_package::is_valid_aleo_name(&name) {
71            return Err(CliError::invalid_program_name(name).into());
72        }
73
74        let new_dependency = Dependency {
75            name: name.clone(),
76            location: if self.source.local.is_some() { Location::Local } else { Location::Network },
77            path: self.source.local.clone(),
78        };
79
80        let deps = if self.dev { &mut manifest.dev_dependencies } else { &mut manifest.dependencies };
81
82        if let Some(matched_dep) = deps.get_or_insert_default().iter_mut().find(|dep| dep.name == new_dependency.name) {
83            if let Some(path) = &matched_dep.path {
84                tracing::warn!(
85                    "⚠️  Program `{name}` already exists as a local dependency at `{}`. Overwriting.",
86                    path.display()
87                );
88            } else {
89                tracing::warn!("⚠️  Program `{name}` already exists as a network dependency. Overwriting.");
90            }
91            *matched_dep = new_dependency;
92        } else {
93            deps.as_mut().unwrap().push(new_dependency);
94            if let Some(path) = self.source.local.as_ref() {
95                tracing::info!("✅ Added local dependency to program `{name}` at path `{}`.", path.display());
96            } else {
97                tracing::info!("✅ Added network dependency `{name}` from network `{}`.", self.source.network);
98            }
99        }
100
101        manifest.write_to_file(manifest_path)?;
102
103        Ok(())
104    }
105}