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