leo_lang/cli/
cli.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 crate::cli::{commands::*, context::*, helpers::*};
18use clap::Parser;
19use leo_errors::Result;
20use std::{path::PathBuf, process::exit};
21
22/// CLI Arguments entry point - includes global parameters and subcommands
23#[derive(Parser, Debug)]
24#[clap(name = "leo", author = "The Leo Team <leo@provable.com>", version)]
25pub struct CLI {
26    #[clap(short, global = true, help = "Print additional information for debugging")]
27    debug: bool,
28
29    #[clap(short, global = true, help = "Suppress CLI output")]
30    quiet: bool,
31
32    #[clap(long, global = true, help = "Disable Leo's daily check for version updates")]
33    disable_update_check: bool,
34
35    #[clap(subcommand)]
36    command: Commands,
37
38    #[clap(long, global = true, help = "Path to Leo program root folder")]
39    path: Option<PathBuf>,
40
41    #[clap(long, global = true, help = "Path to aleo program registry")]
42    pub home: Option<PathBuf>,
43}
44
45///Leo compiler and package manager
46#[derive(Parser, Debug)]
47enum Commands {
48    #[clap(about = "Create a new Aleo account, sign and verify messages")]
49    Account {
50        #[clap(subcommand)]
51        command: Account,
52    },
53    #[clap(about = "Create a new Leo package in a new directory")]
54    New {
55        #[clap(flatten)]
56        command: LeoNew,
57    },
58    #[clap(about = "Run a program with input variables", visible_alias = "r")]
59    Run {
60        #[clap(flatten)]
61        command: LeoRun,
62    },
63    #[clap(about = "Test a Leo program", visible_alias = "t")]
64    Test {
65        #[clap(flatten)]
66        command: LeoTest,
67    },
68    #[clap(about = "Execute a program with input variables")]
69    Execute {
70        #[clap(flatten)]
71        command: LeoExecute,
72    },
73    #[clap(about = "Deploy a program")]
74    Deploy {
75        #[clap(flatten)]
76        command: LeoDeploy,
77    },
78    #[clap(about = "Run a local devnet")]
79    Devnet {
80        #[clap(flatten)]
81        command: LeoDevnet,
82    },
83    #[clap(about = "Query live data from the Aleo network")]
84    Query {
85        #[clap(flatten)]
86        command: LeoQuery,
87    },
88    #[clap(about = "Compile the current package as a program", visible_alias = "b")]
89    Build {
90        #[clap(flatten)]
91        command: LeoBuild,
92    },
93    #[clap(about = "Debug the current package via the interpreter")]
94    Debug {
95        #[clap(flatten)]
96        command: LeoDebug,
97    },
98    #[clap(about = "Add a new on-chain or local dependency to the current package.")]
99    Add {
100        #[clap(flatten)]
101        command: LeoAdd,
102    },
103    #[clap(about = "Remove a dependency from the current package.")]
104    Remove {
105        #[clap(flatten)]
106        command: LeoRemove,
107    },
108    #[clap(about = "Clean the output directory")]
109    Clean {
110        #[clap(flatten)]
111        command: LeoClean,
112    },
113    #[clap(about = "Synthesize individual keys")]
114    Synthesize {
115        #[clap(flatten)]
116        command: LeoSynthesize,
117    },
118    #[clap(about = "Update the Leo CLI")]
119    Update {
120        #[clap(flatten)]
121        command: LeoUpdate,
122    },
123    #[clap(about = "Upgrade the program on a network")]
124    Upgrade {
125        #[clap(flatten)]
126        command: LeoUpgrade,
127    },
128}
129
130pub fn handle_error<T>(res: Result<T>) -> T {
131    match res {
132        Ok(t) => t,
133        Err(err) => {
134            eprintln!("{err}");
135            exit(err.exit_code());
136        }
137    }
138}
139
140/// Run command with custom build arguments.
141pub fn run_with_args(cli: CLI) -> Result<()> {
142    // Print the variables found in the `.env` files.
143    if let Ok(vars) = dotenvy::dotenv_iter().map(|v| v.flatten().collect::<Vec<_>>()) {
144        if !vars.is_empty() {
145            println!("📢 Loading environment variables from a `.env` file in the directory tree.");
146        }
147        for (k, v) in vars {
148            println!("  - {k}={v}");
149        }
150    }
151    // Initialize the `.env` file.
152    dotenvy::dotenv().ok();
153
154    if !cli.quiet {
155        // Init logger with optional debug flag.
156        logger::init_logger("leo", match cli.debug {
157            false => 1,
158            true => 2,
159        })?;
160    }
161
162    //  Check for updates. If not forced, it checks once per day.
163    if !cli.disable_update_check
164        && let Ok(true) = updater::Updater::check_for_updates(false)
165    {
166        let _ = updater::Updater::print_cli();
167    }
168
169    // Get custom root folder and create context for it.
170    // If not specified, default context will be created in cwd.
171    let context = handle_error(Context::new(cli.path, cli.home, false));
172
173    match cli.command {
174        Commands::Add { command } => command.try_execute(context),
175        Commands::Account { command } => command.try_execute(context),
176        Commands::New { command } => command.try_execute(context),
177        Commands::Build { command } => command.try_execute(context),
178        Commands::Debug { command } => command.try_execute(context),
179        Commands::Query { command } => command.try_execute(context),
180        Commands::Clean { command } => command.try_execute(context),
181        Commands::Deploy { command } => command.try_execute(context),
182        Commands::Devnet { command } => command.try_execute(context),
183        Commands::Run { command } => command.try_execute(context),
184        Commands::Test { command } => command.try_execute(context),
185        Commands::Execute { command } => command.try_execute(context),
186        Commands::Remove { command } => command.try_execute(context),
187        Commands::Synthesize { command } => command.try_execute(context),
188        Commands::Update { command } => command.try_execute(context),
189        Commands::Upgrade { command } => command.try_execute(context),
190    }
191}
192
193#[cfg(test)]
194mod tests {
195    use crate::cli::{
196        CLI,
197        cli::{Commands, test_helpers},
198        run_with_args,
199    };
200    use leo_ast::NetworkName;
201    use leo_span::create_session_if_not_set_then;
202    use serial_test::serial;
203    use std::env::temp_dir;
204
205    #[test]
206    #[serial]
207    fn nested_network_dependency_run_test() {
208        // Set current directory to temporary directory
209        let temp_dir = temp_dir();
210        let project_directory = temp_dir.join("nested");
211
212        // Create file structure
213        test_helpers::sample_nested_package(&temp_dir);
214
215        // Set the env options.
216        let env_override = crate::cli::commands::EnvOptions {
217            network: Some(NetworkName::TestnetV0),
218            endpoint: Some("http://localhost:3030".to_string()),
219            ..Default::default()
220        };
221
222        // Run program
223        let run = CLI {
224            debug: false,
225            quiet: false,
226            disable_update_check: false,
227            command: Commands::Run {
228                command: crate::cli::commands::LeoRun {
229                    name: "example".to_string(),
230                    inputs: vec!["1u32".to_string(), "2u32".to_string()],
231                    env_override,
232                    build_options: Default::default(),
233                },
234            },
235            path: Some(project_directory.clone()),
236            home: Some(temp_dir.join(".aleo")),
237        };
238
239        create_session_if_not_set_then(|_| {
240            run_with_args(run).expect("Failed to execute `leo run`");
241        });
242
243        // TODO: Clear tmp directory
244        // let registry = temp_dir.join(".aleo").join("registry").join("mainnet");
245        // std::fs::remove_dir_all(registry).unwrap();
246        // std::fs::remove_dir_all(project_directory).unwrap();
247    }
248
249    #[test]
250    #[serial]
251    fn nested_local_dependency_run_test() {
252        // Set current directory to temporary directory
253        let temp_dir = temp_dir();
254        let project_name = "grandparent";
255        let project_directory = temp_dir.join(project_name);
256
257        // Remove it if it already exists
258        if project_directory.exists() {
259            std::fs::remove_dir_all(project_directory.clone()).unwrap();
260        }
261
262        // Create file structure
263        test_helpers::sample_grandparent_package(&temp_dir);
264
265        // Run program
266        let run = CLI {
267            debug: false,
268            quiet: false,
269            disable_update_check: false,
270            command: Commands::Run {
271                command: crate::cli::commands::LeoRun {
272                    name: "double_wrapper_mint".to_string(),
273                    inputs: vec![
274                        "aleo13tngrq7506zwdxj0cxjtvp28pk937jejhne0rt4zp0z370uezuysjz2prs".to_string(),
275                        "2u32".to_string(),
276                    ],
277                    env_override: Default::default(),
278                    build_options: Default::default(),
279                },
280            },
281            path: Some(project_directory.clone()),
282            home: None,
283        };
284
285        create_session_if_not_set_then(|_| {
286            run_with_args(run).expect("Failed to execute `leo run`");
287        });
288
289        // TODO: Clear tmp directory
290        // std::fs::remove_dir_all(project_directory).unwrap();
291    }
292
293    #[test]
294    #[serial]
295    fn relaxed_shadowing_run_test() {
296        // Set current directory to temporary directory
297        let temp_dir = temp_dir();
298        let project_name = "outer";
299        let project_directory = temp_dir.join(project_name);
300
301        // Remove it if it already exists
302        if project_directory.exists() {
303            std::fs::remove_dir_all(project_directory.clone()).unwrap();
304        }
305
306        // Create file structure
307        test_helpers::sample_shadowing_package(&temp_dir);
308
309        // Run program
310        let run = CLI {
311            debug: false,
312            quiet: false,
313            disable_update_check: false,
314            command: Commands::Run {
315                command: crate::cli::commands::LeoRun {
316                    name: "inner_1_main".to_string(),
317                    inputs: vec!["1u32".to_string(), "2u32".to_string()],
318                    build_options: Default::default(),
319                    env_override: Default::default(),
320                },
321            },
322            path: Some(project_directory.clone()),
323            home: None,
324        };
325
326        create_session_if_not_set_then(|_| {
327            run_with_args(run).expect("Failed to execute `leo run`");
328        });
329    }
330
331    #[test]
332    #[serial]
333    fn relaxed_struct_shadowing_run_test() {
334        // Set current directory to temporary directory
335        let temp_dir = temp_dir();
336        let project_name = "outer_2";
337        let project_directory = temp_dir.join(project_name);
338
339        // Remove it if it already exists
340        if project_directory.exists() {
341            std::fs::remove_dir_all(project_directory.clone()).unwrap();
342        }
343
344        // Create file structure
345        test_helpers::sample_struct_shadowing_package(&temp_dir);
346
347        // Run program
348        let run = CLI {
349            debug: false,
350            quiet: false,
351            disable_update_check: false,
352            command: Commands::Run {
353                command: crate::cli::commands::LeoRun {
354                    name: "main".to_string(),
355                    inputs: vec!["1u32".to_string(), "2u32".to_string()],
356                    env_override: Default::default(),
357                    build_options: Default::default(),
358                },
359            },
360            path: Some(project_directory.clone()),
361            home: None,
362        };
363
364        create_session_if_not_set_then(|_| {
365            run_with_args(run).expect("Failed to execute `leo run`");
366        });
367    }
368}
369
370#[cfg(test)]
371mod test_helpers {
372    use crate::cli::{CLI, DependencySource, LeoAdd, LeoNew, cli::Commands, run_with_args};
373    use leo_span::create_session_if_not_set_then;
374    use std::path::Path;
375
376    const NETWORK: &str = "testnet";
377    const ENDPOINT: &str = "https://api.explorer.provable.com/v1";
378
379    pub(crate) fn sample_nested_package(temp_dir: &Path) {
380        let name = "nested";
381
382        // Remove it if it already exists
383        let project_directory = temp_dir.join(name);
384        if project_directory.exists() {
385            std::fs::remove_dir_all(project_directory.clone()).unwrap();
386        }
387
388        // Create new Leo project
389        let new = CLI {
390            debug: false,
391            quiet: false,
392            disable_update_check: false,
393            command: Commands::New {
394                command: LeoNew {
395                    name: name.to_string(),
396                    network: NETWORK.to_string(),
397                    endpoint: ENDPOINT.to_string(),
398                },
399            },
400            path: Some(project_directory.clone()),
401            home: None,
402        };
403
404        create_session_if_not_set_then(|_| {
405            run_with_args(new).expect("Failed to execute `leo run`");
406        });
407
408        // `nested.aleo` program
409        let program_str = "
410import nested_example_layer_0.aleo;
411program nested.aleo {
412    transition example(public a: u32, b: u32) -> u32 {
413        let c: u32 = nested_example_layer_0.aleo/main(a, b);
414        return c;
415    }
416
417    @noupgrade
418    async constructor() {}
419}
420";
421        // `nested_example_layer_0.aleo` program
422        let nested_example_layer_0 = "
423import nested_example_layer_2.aleo;
424import nested_example_layer_1.aleo;
425
426program nested_example_layer_0.aleo;
427
428function main:
429    input r0 as u32.public;
430    input r1 as u32.private;
431    call nested_example_layer_1.aleo/external_function r0 r1 into r2;
432    output r2 as u32.private;
433";
434
435        // `nested_example_layer_1.aleo` program
436        let nested_example_layer_1 = "
437import nested_example_layer_2.aleo;
438
439program nested_example_layer_1.aleo;
440
441function external_function:
442    input r0 as u32.public;
443    input r1 as u32.private;
444    call nested_example_layer_2.aleo/external_nested_function r0 r1 into r2;
445    output r2 as u32.private;
446";
447
448        // `nested_example_layer_2.aleo` program
449        let nested_example_layer_2 = "
450program nested_example_layer_2.aleo;
451
452function external_nested_function:
453    input r0 as u32.public;
454    input r1 as u32.private;
455    add r0 r1 into r2;
456    output r2 as u32.private;
457";
458
459        // Overwrite `src/main.leo` file
460        std::fs::write(project_directory.join("src").join("main.leo"), program_str).unwrap();
461
462        // Add dependencies
463        let add = CLI {
464            debug: false,
465            quiet: false,
466            disable_update_check: false,
467            command: Commands::Add {
468                command: LeoAdd {
469                    name: "nested_example_layer_0".to_string(),
470                    source: DependencySource { local: None, network: true, edition: Some(0) },
471                    clear: false,
472                    dev: false,
473                },
474            },
475            path: Some(project_directory.clone()),
476            home: None,
477        };
478
479        create_session_if_not_set_then(|_| {
480            run_with_args(add).expect("Failed to execute `leo add`");
481        });
482
483        // Add custom `.aleo` directory with the appropriate cache entries.
484        let registry = temp_dir.join(".aleo").join("registry").join("testnet");
485        std::fs::create_dir_all(&registry).unwrap();
486
487        let dir = registry.join("nested_example_layer_0").join("0");
488        std::fs::create_dir_all(&dir).unwrap();
489        std::fs::write(dir.join("nested_example_layer_0.aleo"), nested_example_layer_0).unwrap();
490
491        let dir = registry.join("nested_example_layer_1").join("0");
492        std::fs::create_dir_all(&dir).unwrap();
493        std::fs::write(dir.join("nested_example_layer_1.aleo"), nested_example_layer_1).unwrap();
494
495        let dir = registry.join("nested_example_layer_2").join("0");
496        std::fs::create_dir_all(&dir).unwrap();
497        std::fs::write(dir.join("nested_example_layer_2.aleo"), nested_example_layer_2).unwrap();
498    }
499
500    pub(crate) fn sample_grandparent_package(temp_dir: &Path) {
501        let grandparent_directory = temp_dir.join("grandparent");
502        let parent_directory = grandparent_directory.join("parent");
503        let child_directory = parent_directory.join("child");
504
505        if grandparent_directory.exists() {
506            std::fs::remove_dir_all(grandparent_directory.clone()).unwrap();
507        }
508
509        // Create project file structure `grandparent/parent/child`
510        let create_grandparent_project = CLI {
511            debug: false,
512            quiet: false,
513            disable_update_check: false,
514            command: Commands::New {
515                command: LeoNew {
516                    name: "grandparent".to_string(),
517                    network: NETWORK.to_string(),
518                    endpoint: ENDPOINT.to_string(),
519                },
520            },
521            path: Some(grandparent_directory.clone()),
522            home: None,
523        };
524
525        let create_parent_project = CLI {
526            debug: false,
527            quiet: false,
528            disable_update_check: false,
529            command: Commands::New {
530                command: LeoNew {
531                    name: "parent".to_string(),
532                    network: NETWORK.to_string(),
533                    endpoint: ENDPOINT.to_string(),
534                },
535            },
536            path: Some(parent_directory.clone()),
537            home: None,
538        };
539
540        let create_child_project = CLI {
541            debug: false,
542            quiet: false,
543            disable_update_check: false,
544            command: Commands::New {
545                command: LeoNew {
546                    name: "child".to_string(),
547                    network: NETWORK.to_string(),
548                    endpoint: ENDPOINT.to_string(),
549                },
550            },
551            path: Some(child_directory.clone()),
552            home: None,
553        };
554
555        // Add source files `grandparent/src/main.leo`, `grandparent/parent/src/main.leo`, and `grandparent/parent/child/src/main.leo`
556        let grandparent_program = "
557import child.aleo;
558import parent.aleo;
559program grandparent.aleo {
560    transition double_wrapper_mint(owner: address, val: u32) -> child.aleo/A {
561        return parent.aleo/wrapper_mint(owner, val);
562    }
563
564    @noupgrade
565    async constructor() {}
566}
567";
568        let parent_program = "
569import child.aleo;
570program parent.aleo {
571    transition wrapper_mint(owner: address, val: u32) ->  child.aleo/A {
572        return child.aleo/mint(owner, val);
573    }
574
575    @noupgrade
576    async constructor() {}
577}
578";
579
580        let child_program = "
581// The 'a' program.
582program child.aleo {
583    record A {
584        owner: address,
585        val: u32,
586    }
587    transition mint(owner: address, val: u32) -> A {
588        return A {owner: owner, val: val};
589    }
590
591    @noupgrade
592    async constructor() {}
593}
594";
595
596        // Add dependencies `grandparent/program.json` and `grandparent/parent/program.json`
597        let add_grandparent_dependency_1 = CLI {
598            debug: false,
599            quiet: false,
600            disable_update_check: false,
601            command: Commands::Add {
602                command: LeoAdd {
603                    name: "parent".to_string(),
604                    source: DependencySource { local: Some(parent_directory.clone()), network: false, edition: None },
605                    clear: false,
606                    dev: false,
607                },
608            },
609            path: Some(grandparent_directory.clone()),
610            home: None,
611        };
612
613        let add_grandparent_dependency_2 = CLI {
614            debug: false,
615            quiet: false,
616            disable_update_check: false,
617            command: Commands::Add {
618                command: LeoAdd {
619                    name: "child".to_string(),
620                    source: DependencySource { local: Some(child_directory.clone()), network: false, edition: None },
621                    clear: false,
622                    dev: false,
623                },
624            },
625            path: Some(grandparent_directory.clone()),
626            home: None,
627        };
628
629        let add_parent_dependency = CLI {
630            debug: false,
631            quiet: false,
632            disable_update_check: false,
633            command: Commands::Add {
634                command: LeoAdd {
635                    name: "child".to_string(),
636                    source: DependencySource { local: Some(child_directory.clone()), network: false, edition: None },
637                    clear: false,
638                    dev: false,
639                },
640            },
641            path: Some(parent_directory.clone()),
642            home: None,
643        };
644
645        // Execute all commands
646        create_session_if_not_set_then(|_| {
647            // Create projects
648            run_with_args(create_grandparent_project).unwrap();
649            run_with_args(create_parent_project).unwrap();
650            run_with_args(create_child_project).unwrap();
651
652            // Write files
653            std::fs::write(grandparent_directory.join("src").join("main.leo"), grandparent_program).unwrap();
654            std::fs::write(parent_directory.join("src").join("main.leo"), parent_program).unwrap();
655            std::fs::write(child_directory.join("src").join("main.leo"), child_program).unwrap();
656
657            // Add dependencies
658            run_with_args(add_grandparent_dependency_1).unwrap();
659            run_with_args(add_grandparent_dependency_2).unwrap();
660            run_with_args(add_parent_dependency).unwrap();
661        });
662    }
663
664    pub(crate) fn sample_shadowing_package(temp_dir: &Path) {
665        let outer_directory = temp_dir.join("outer");
666        let inner_1_directory = outer_directory.join("inner_1");
667        let inner_2_directory = outer_directory.join("inner_2");
668
669        if outer_directory.exists() {
670            std::fs::remove_dir_all(outer_directory.clone()).unwrap();
671        }
672
673        // Create project file structure `outer/inner_1` and `outer/inner_2`
674        let create_outer_project = CLI {
675            debug: false,
676            quiet: false,
677            disable_update_check: false,
678            command: Commands::New {
679                command: LeoNew {
680                    name: "outer".to_string(),
681                    network: NETWORK.to_string(),
682                    endpoint: ENDPOINT.to_string(),
683                },
684            },
685            path: Some(outer_directory.clone()),
686            home: None,
687        };
688
689        let create_inner_1_project = CLI {
690            debug: false,
691            quiet: false,
692            disable_update_check: false,
693            command: Commands::New {
694                command: LeoNew {
695                    name: "inner_1".to_string(),
696                    network: NETWORK.to_string(),
697                    endpoint: ENDPOINT.to_string(),
698                },
699            },
700            path: Some(inner_1_directory.clone()),
701            home: None,
702        };
703
704        let create_inner_2_project = CLI {
705            debug: false,
706            quiet: false,
707            disable_update_check: false,
708            command: Commands::New {
709                command: LeoNew {
710                    name: "inner_2".to_string(),
711                    network: NETWORK.to_string(),
712                    endpoint: ENDPOINT.to_string(),
713                },
714            },
715            path: Some(inner_2_directory.clone()),
716            home: None,
717        };
718
719        // Add source files `outer/src/main.leo` and `outer/inner/src/main.leo`
720        let outer_program = "import inner_1.aleo;
721import inner_2.aleo;
722program outer.aleo {
723
724    struct ex_struct {
725        arg1: u32,
726        arg2: u32,
727    }
728
729    record inner_1_record {
730        owner: address,
731        arg1: u32,
732        arg2: u32,
733        arg3: u32,
734    }
735
736    transition inner_1_main(public a: u32, b: u32) -> (inner_1.aleo/inner_1_record, inner_2.aleo/inner_1_record, inner_1_record) {
737        let c: ex_struct = ex_struct {arg1: 1u32, arg2: 1u32};
738        let rec_1:inner_1.aleo/inner_1_record = inner_1.aleo/inner_1_main(1u32,1u32, c);
739        let rec_2:inner_2.aleo/inner_1_record = inner_2.aleo/inner_1_main(1u32,1u32);
740        return (rec_1, rec_2, inner_1_record {owner: aleo14tnetva3xfvemqyg5ujzvr0qfcaxdanmgjx2wsuh2xrpvc03uc9s623ps7, arg1: 1u32, arg2: 1u32, arg3: 1u32});
741    }
742
743    @noupgrade
744    async constructor() {}
745}";
746        let inner_1_program = "program inner_1.aleo {
747    mapping inner_1_mapping: u32 => u32;
748    record inner_1_record {
749        owner: address,
750        val: u32,
751    }
752    struct ex_struct {
753        arg1: u32,
754        arg2: u32,
755    }
756    transition inner_1_main(public a: u32, b: u32, c: ex_struct) -> inner_1_record {
757        return inner_1_record {
758            owner: self.caller,
759            val: c.arg1,
760        };
761    }
762
763    @noupgrade
764    async constructor() {}
765}";
766        let inner_2_program = "program inner_2.aleo {
767    mapping inner_2_mapping: u32 => u32;
768    record inner_1_record {
769        owner: address,
770        val: u32,
771    }
772    transition inner_1_main(public a: u32, b: u32) -> inner_1_record {
773        let c: u32 = a + b;
774        return inner_1_record {
775            owner: self.caller,
776            val: a,
777        };
778    }
779
780    @noupgrade
781    async constructor() {}
782}";
783        // Add dependencies `outer/program.json`
784        let add_outer_dependency_1 = CLI {
785            debug: false,
786            quiet: false,
787            disable_update_check: false,
788            command: Commands::Add {
789                command: LeoAdd {
790                    name: "inner_1".to_string(),
791                    source: DependencySource { local: Some(inner_1_directory.clone()), network: false, edition: None },
792                    clear: false,
793                    dev: false,
794                },
795            },
796            path: Some(outer_directory.clone()),
797            home: None,
798        };
799
800        let add_outer_dependency_2 = CLI {
801            debug: false,
802            quiet: false,
803            disable_update_check: false,
804            command: Commands::Add {
805                command: LeoAdd {
806                    name: "inner_2".to_string(),
807                    source: DependencySource { local: Some(inner_2_directory.clone()), network: false, edition: None },
808                    clear: false,
809                    dev: false,
810                },
811            },
812            path: Some(outer_directory.clone()),
813            home: None,
814        };
815
816        // Execute all commands
817        create_session_if_not_set_then(|_| {
818            // Create projects
819            run_with_args(create_outer_project).unwrap();
820            run_with_args(create_inner_1_project).unwrap();
821            run_with_args(create_inner_2_project).unwrap();
822
823            // Write files
824            std::fs::write(outer_directory.join("src").join("main.leo"), outer_program).unwrap();
825            std::fs::write(inner_1_directory.join("src").join("main.leo"), inner_1_program).unwrap();
826            std::fs::write(inner_2_directory.join("src").join("main.leo"), inner_2_program).unwrap();
827
828            // Add dependencies
829            run_with_args(add_outer_dependency_1).unwrap();
830            run_with_args(add_outer_dependency_2).unwrap();
831        });
832    }
833
834    pub(crate) fn sample_struct_shadowing_package(temp_dir: &Path) {
835        let outer_directory = temp_dir.join("outer_2");
836        let inner_1_directory = outer_directory.join("inner_1");
837        let inner_2_directory = outer_directory.join("inner_2");
838
839        if outer_directory.exists() {
840            std::fs::remove_dir_all(outer_directory.clone()).unwrap();
841        }
842
843        // Create project file structure `outer_2/inner_1` and `outer_2/inner_2`
844        let create_outer_project = CLI {
845            debug: false,
846            quiet: false,
847            disable_update_check: false,
848            command: Commands::New {
849                command: LeoNew {
850                    name: "outer_2".to_string(),
851                    network: NETWORK.to_string(),
852                    endpoint: ENDPOINT.to_string(),
853                },
854            },
855            path: Some(outer_directory.clone()),
856            home: None,
857        };
858
859        let create_inner_1_project = CLI {
860            debug: false,
861            quiet: false,
862            disable_update_check: false,
863            command: Commands::New {
864                command: LeoNew {
865                    name: "inner_1".to_string(),
866                    network: NETWORK.to_string(),
867                    endpoint: ENDPOINT.to_string(),
868                },
869            },
870            path: Some(inner_1_directory.clone()),
871            home: None,
872        };
873
874        let create_inner_2_project = CLI {
875            debug: false,
876            quiet: false,
877            disable_update_check: false,
878            command: Commands::New {
879                command: LeoNew {
880                    name: "inner_2".to_string(),
881                    network: NETWORK.to_string(),
882                    endpoint: ENDPOINT.to_string(),
883                },
884            },
885            path: Some(inner_2_directory.clone()),
886            home: None,
887        };
888
889        // Add source files `outer_2/src/main.leo` and `outer_2/inner/src/main.leo`
890        let outer_program = "
891import inner_1.aleo;
892import inner_2.aleo;
893program outer_2.aleo {
894    struct Foo {
895        a: u32,
896        b: u32,
897        c: Boo,
898    }
899    struct Boo {
900        a: u32,
901        b: u32,
902    }
903    struct Goo {
904        a: u32,
905        b: u32,
906        c: u32,
907    }
908    record Hello {
909        owner: address,
910        a: u32,
911    }
912    transition main(public a: u32, b: u32) -> (inner_2.aleo/Yoo, Hello) {
913        let d: Foo = inner_1.aleo/main(1u32,1u32);
914        let e: u32 = inner_1.aleo/main_2(Foo {a: a, b: b, c: Boo {a:1u32, b:1u32}});
915        let f: Boo = Boo {a:1u32, b:1u32};
916        let g: Foo = inner_2.aleo/main(1u32, 1u32);
917        inner_2.aleo/Yo_Consumer(inner_2.aleo/Yo());
918        let h: inner_2.aleo/Yoo = inner_2.aleo/Yo();
919        let i: Goo = inner_2.aleo/Goo_creator();
920        let j: Hello = Hello {owner: self.signer, a:1u32};
921
922        return (h, j);
923    }
924
925    @noupgrade
926    async constructor() {}
927}
928";
929        let inner_1_program = "program inner_1.aleo {
930    struct Foo {
931        a: u32,
932        b: u32,
933        c: Boo,
934    }
935    struct Boo {
936        a: u32,
937        b: u32,
938    }
939    transition main(public a: u32, b: u32) -> Foo {
940        return Foo {a: a, b: b, c: Boo {a:1u32, b:1u32}};
941    }
942    transition main_2(a:Foo)->u32{
943        return a.a;
944    }
945
946    @noupgrade
947    async constructor() {}
948}";
949        let inner_2_program = "program inner_2.aleo {
950    struct Foo {
951        a: u32,
952        b: u32,
953        c: Boo,
954    }
955    struct Boo {
956        a: u32,
957        b: u32,
958    }
959    record Yoo {
960        owner: address,
961        a: u32,
962    }
963    struct Goo {
964        a: u32,
965        b: u32,
966        c: u32,
967    }
968    transition main(public a: u32, b: u32) -> Foo {
969        return Foo {a: a, b: b, c: Boo {a:1u32, b:1u32}};
970    }
971    transition Yo()-> Yoo {
972        return Yoo {owner: self.signer, a:1u32};
973    }
974    transition Yo_Consumer(a: Yoo)->u32 {
975        return a.a;
976    }
977    transition Goo_creator() -> Goo {
978        return Goo {a:100u32, b:1u32, c:1u32};
979    }
980
981    @noupgrade
982    async constructor() {}
983}";
984        // Add dependencies `outer_2/program.json`
985        let add_outer_dependency_1 = CLI {
986            debug: false,
987            quiet: false,
988            disable_update_check: false,
989            command: Commands::Add {
990                command: LeoAdd {
991                    name: "inner_1".to_string(),
992                    source: DependencySource { local: Some(inner_1_directory.clone()), network: false, edition: None },
993                    clear: false,
994                    dev: false,
995                },
996            },
997            path: Some(outer_directory.clone()),
998            home: None,
999        };
1000
1001        let add_outer_dependency_2 = CLI {
1002            debug: false,
1003            quiet: false,
1004            disable_update_check: false,
1005            command: Commands::Add {
1006                command: LeoAdd {
1007                    name: "inner_2".to_string(),
1008                    source: DependencySource { local: Some(inner_2_directory.clone()), network: false, edition: None },
1009                    clear: false,
1010                    dev: false,
1011                },
1012            },
1013            path: Some(outer_directory.clone()),
1014            home: None,
1015        };
1016
1017        // Execute all commands
1018        create_session_if_not_set_then(|_| {
1019            // Create projects
1020            run_with_args(create_outer_project).unwrap();
1021            run_with_args(create_inner_1_project).unwrap();
1022            run_with_args(create_inner_2_project).unwrap();
1023
1024            // Write files
1025            std::fs::write(outer_directory.join("src").join("main.leo"), outer_program).unwrap();
1026            std::fs::write(inner_1_directory.join("src").join("main.leo"), inner_1_program).unwrap();
1027            std::fs::write(inner_2_directory.join("src").join("main.leo"), inner_2_program).unwrap();
1028
1029            // Add dependencies
1030            run_with_args(add_outer_dependency_1).unwrap();
1031            run_with_args(add_outer_dependency_2).unwrap();
1032        });
1033    }
1034}