/ adl-cli / cli / cli.rs
cli.rs
   1  // Copyright (C) 2019-2025 ADnet Contributors
   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_errors::Result;
  19  use clap::Parser;
  20  use std::{path::PathBuf, process::exit};
  21  
  22  /// CLI Arguments entry point - includes global parameters and subcommands
  23  #[derive(Parser, Debug)]
  24  #[clap(name = "adl", author = "The ADL Team <adl@blockblox.io>", version)]
  25  pub 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 ADL'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 ADL program root folder")]
  39      path: Option<PathBuf>,
  40  
  41      #[clap(long, global = true, help = "Path to ALPHA program registry")]
  42      pub home: Option<PathBuf>,
  43  }
  44  
  45  ///ADL compiler and package manager
  46  #[derive(Parser, Debug)]
  47  enum Commands {
  48      #[clap(about = "Create a new ALPHA account, sign and verify messages")]
  49      Account {
  50          #[clap(subcommand)]
  51          command: Account,
  52      },
  53      #[clap(about = "Create a new ADL package in a new directory")]
  54      New {
  55          #[clap(flatten)]
  56          command: AdlNew,
  57      },
  58      #[clap(about = "Run a program with input variables", visible_alias = "r")]
  59      Run {
  60          #[clap(flatten)]
  61          command: AdlRun,
  62      },
  63      #[clap(about = "Test a ADL program", visible_alias = "t")]
  64      Test {
  65          #[clap(flatten)]
  66          command: AdlTest,
  67      },
  68      #[clap(about = "Execute a program with input variables")]
  69      Execute {
  70          #[clap(flatten)]
  71          command: AdlExecute,
  72      },
  73      #[clap(about = "Deploy a program")]
  74      Deploy {
  75          #[clap(flatten)]
  76          command: AdlDeploy,
  77      },
  78      #[clap(about = "Run a local devnet")]
  79      Devnet {
  80          #[clap(flatten)]
  81          command: AdlDevnet,
  82      },
  83      #[clap(about = "Query live data from the ALPHA network")]
  84      Query {
  85          #[clap(flatten)]
  86          command: AdlQuery,
  87      },
  88      #[clap(about = "Compile the current package as a program", visible_alias = "b")]
  89      Build {
  90          #[clap(flatten)]
  91          command: AdlBuild,
  92      },
  93      #[clap(about = "Debug the current package via the interpreter")]
  94      Debug {
  95          #[clap(flatten)]
  96          command: AdlDebug,
  97      },
  98      #[clap(about = "Add a new on-chain or local dependency to the current package.")]
  99      Add {
 100          #[clap(flatten)]
 101          command: AdlAdd,
 102      },
 103      #[clap(about = "Remove a dependency from the current package.")]
 104      Remove {
 105          #[clap(flatten)]
 106          command: AdlRemove,
 107      },
 108      #[clap(about = "Clean the output directory")]
 109      Clean {
 110          #[clap(flatten)]
 111          command: AdlClean,
 112      },
 113      #[clap(about = "Synthesize individual keys")]
 114      Synthesize {
 115          #[clap(flatten)]
 116          command: AdlSynthesize,
 117      },
 118      #[clap(about = "Update the ADL CLI")]
 119      Update {
 120          #[clap(flatten)]
 121          command: AdlUpdate,
 122      },
 123      #[clap(about = "Upgrade the program on a network")]
 124      Upgrade {
 125          #[clap(flatten)]
 126          command: AdlUpgrade,
 127      },
 128  }
 129  
 130  pub 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.
 141  pub 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("adl", 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)]
 194  mod tests {
 195      use crate::cli::{
 196          CLI,
 197          cli::{Commands, test_helpers},
 198          run_with_args,
 199      };
 200      use adl_ast::NetworkName;
 201      use adl_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::AdlRun {
 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(".alpha")),
 237          };
 238  
 239          create_session_if_not_set_then(|_| {
 240              run_with_args(run).expect("Failed to execute `adl run`");
 241          });
 242  
 243          // TODO: Clear tmp directory
 244          // let registry = temp_dir.join(".alpha").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::AdlRun {
 272                      name: "double_wrapper_mint".to_string(),
 273                      inputs: vec![
 274                          "ax13tngrq7506zwdxj0cxjtvp28pk937jejhne0rt4zp0z370uezuysjz2prs".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 `adl 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::AdlRun {
 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 `adl 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::AdlRun {
 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 `adl run`");
 366          });
 367      }
 368  }
 369  
 370  #[cfg(test)]
 371  mod test_helpers {
 372      use crate::cli::{AdlAdd, AdlNew, CLI, DependencySource, cli::Commands, run_with_args};
 373      use adl_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 ADL project
 389          let new = CLI {
 390              debug: false,
 391              quiet: false,
 392              disable_update_check: false,
 393              command: Commands::New {
 394                  command: AdlNew {
 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 `adl run`");
 406          });
 407  
 408          // `nested.alpha` program
 409          let program_str = "
 410  import nested_example_layer_0.alpha;
 411  program nested.alpha {
 412      transition example(public a: u32, b: u32) -> u32 {
 413          let c: u32 = nested_example_layer_0.alpha/main(a, b);
 414          return c;
 415      }
 416  
 417      @noupgrade
 418      async constructor() {}
 419  }
 420  ";
 421          // `nested_example_layer_0.alpha` program
 422          let nested_example_layer_0 = "
 423  import nested_example_layer_2.alpha;
 424  import nested_example_layer_1.alpha;
 425  
 426  program nested_example_layer_0.alpha;
 427  
 428  function main:
 429      input r0 as u32.public;
 430      input r1 as u32.private;
 431      call nested_example_layer_1.alpha/external_function r0 r1 into r2;
 432      output r2 as u32.private;
 433  ";
 434  
 435          // `nested_example_layer_1.alpha` program
 436          let nested_example_layer_1 = "
 437  import nested_example_layer_2.alpha;
 438  
 439  program nested_example_layer_1.alpha;
 440  
 441  function external_function:
 442      input r0 as u32.public;
 443      input r1 as u32.private;
 444      call nested_example_layer_2.alpha/external_nested_function r0 r1 into r2;
 445      output r2 as u32.private;
 446  ";
 447  
 448          // `nested_example_layer_2.alpha` program
 449          let nested_example_layer_2 = "
 450  program nested_example_layer_2.alpha;
 451  
 452  function 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.adl` file
 460          std::fs::write(project_directory.join("src").join("main.adl"), 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: AdlAdd {
 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 `adl add`");
 481          });
 482  
 483          // Add custom `.alpha` directory with the appropriate cache entries.
 484          let registry = temp_dir.join(".alpha").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.acdc"), 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.acdc"), 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.acdc"), 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: AdlNew {
 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: AdlNew {
 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: AdlNew {
 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.adl`, `grandparent/parent/src/main.adl`, and `grandparent/parent/child/src/main.adl`
 556          let grandparent_program = "
 557  import child.alpha;
 558  import parent.alpha;
 559  program grandparent.alpha {
 560      transition double_wrapper_mint(owner: address, val: u32) -> child.alpha/A {
 561          return parent.alpha/wrapper_mint(owner, val);
 562      }
 563  
 564      @noupgrade
 565      async constructor() {}
 566  }
 567  ";
 568          let parent_program = "
 569  import child.alpha;
 570  program parent.alpha {
 571      transition wrapper_mint(owner: address, val: u32) ->  child.alpha/A {
 572          return child.alpha/mint(owner, val);
 573      }
 574  
 575      @noupgrade
 576      async constructor() {}
 577  }
 578  ";
 579  
 580          let child_program = "
 581  // The 'a' program.
 582  program child.alpha {
 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: AdlAdd {
 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: AdlAdd {
 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: AdlAdd {
 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.adl"), grandparent_program).unwrap();
 654              std::fs::write(parent_directory.join("src").join("main.adl"), parent_program).unwrap();
 655              std::fs::write(child_directory.join("src").join("main.adl"), 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: AdlNew {
 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: AdlNew {
 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: AdlNew {
 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.adl` and `outer/inner/src/main.adl`
 720          let outer_program = "import inner_1.alpha;
 721  import inner_2.alpha;
 722  program outer.alpha {
 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.alpha/inner_1_record, inner_2.alpha/inner_1_record, inner_1_record) {
 737          let c: ex_struct = ex_struct {arg1: 1u32, arg2: 1u32};
 738          let rec_1:inner_1.alpha/inner_1_record = inner_1.alpha/inner_1_main(1u32,1u32, c);
 739          let rec_2:inner_2.alpha/inner_1_record = inner_2.alpha/inner_1_main(1u32,1u32);
 740          return (rec_1, rec_2, inner_1_record {owner: ax14tnetva3xfvemqyg5ujzvr0qfcaxdanmgjx2wsuh2xrpvc03uc9s623ps7, arg1: 1u32, arg2: 1u32, arg3: 1u32});
 741      }
 742  
 743      @noupgrade
 744      async constructor() {}
 745  }";
 746          let inner_1_program = "program inner_1.alpha {
 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.alpha {
 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: AdlAdd {
 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: AdlAdd {
 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.adl"), outer_program).unwrap();
 825              std::fs::write(inner_1_directory.join("src").join("main.adl"), inner_1_program).unwrap();
 826              std::fs::write(inner_2_directory.join("src").join("main.adl"), 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: AdlNew {
 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: AdlNew {
 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: AdlNew {
 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.adl` and `outer_2/inner/src/main.adl`
 890          let outer_program = "
 891  import inner_1.alpha;
 892  import inner_2.alpha;
 893  program outer_2.alpha {
 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.alpha/Yoo, Hello) {
 913          let d: Foo = inner_1.alpha/main(1u32,1u32);
 914          let e: u32 = inner_1.alpha/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.alpha/main(1u32, 1u32);
 917          inner_2.alpha/Yo_Consumer(inner_2.alpha/Yo());
 918          let h: inner_2.alpha/Yoo = inner_2.alpha/Yo();
 919          let i: Goo = inner_2.alpha/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.alpha {
 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.alpha {
 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: AdlAdd {
 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: AdlAdd {
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.adl"), outer_program).unwrap();
1026              std::fs::write(inner_1_directory.join("src").join("main.adl"), inner_1_program).unwrap();
1027              std::fs::write(inner_2_directory.join("src").join("main.adl"), 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  }