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