diff --git a/.circleci/config.yml b/.circleci/config.yml
new file mode 100644
index 0000000000..d758e2dadd
--- /dev/null
+++ b/.circleci/config.yml
@@ -0,0 +1,163 @@
+version: 2.1
+commands:
+ setup_environment:
+ description: "Setup environment"
+ parameters:
+ cache_key:
+ type: string
+ default: leo-stable-cache
+ steps:
+ - run: set -e
+ - setup_remote_docker
+ - run:
+ name: Prepare environment and install dependencies
+ command: |
+ export SCCACHE_CACHE_SIZE=200M
+ export WORK_DIR="$CIRCLE_WORKING_DIRECTORY/.cache/sccache"
+ export SCCACHE_DIR="$CIRCLE_WORKING_DIRECTORY/.cache/sccache"
+ mkdir -p "$CIRCLE_WORKING_DIRECTORY/.bin"
+ wget https://github.com/mozilla/sccache/releases/download/0.2.13/sccache-0.2.13-x86_64-unknown-linux-musl.tar.gz
+ tar -C "$CIRCLE_WORKING_DIRECTORY/.bin" -xvf sccache-0.2.13-x86_64-unknown-linux-musl.tar.gz
+ mv $CIRCLE_WORKING_DIRECTORY/.bin/sccache-0.2.13-x86_64-unknown-linux-musl/sccache $CIRCLE_WORKING_DIRECTORY/.bin/sccache
+ export PATH="$PATH:$CIRCLE_WORKING_DIRECTORY/.bin"
+ export RUSTC_WRAPPER="sccache"
+ rm -rf "$CIRCLE_WORKING_DIRECTORY/.cargo/registry"
+ sudo apt-get update && sudo apt-get install -y clang llvm-dev llvm pkg-config xz-utils make libssl-dev libssl-dev
+ - restore_cache:
+ keys:
+ - << parameters.cache_key >>
+ clear_environment:
+ description: "Clear environment"
+ parameters:
+ cache_key:
+ type: string
+ default: leo-stable-cache
+ steps:
+ - run: (sccache -s||true)
+ - run: set +e
+ - save_cache:
+ key: << parameters.cache_key >>
+ paths:
+ - .cache/sccache
+ - .cargo
+jobs:
+
+ rust-stable:
+ docker:
+ - image: cimg/rust:1.50.0
+ resource_class: xlarge
+ steps:
+ - checkout
+ - setup_environment:
+ cache_key: leo-stable-cache
+ - run:
+ name: Build and run tests
+ no_output_timeout: 30m
+ command: cargo install --path . --root .
+ - persist_to_workspace:
+ root: ~/
+ paths: project/
+ - clear_environment:
+ cache_key: leo-stable-cache
+
+ leo-new:
+ docker:
+ - image: cimg/rust:1.50.0
+ resource_class: xlarge
+ steps:
+ - attach_workspace:
+ at: /home/circleci/project/
+ - run:
+ name: leo new
+ command: |
+ export LEO=/home/circleci/project/project/bin/leo
+ ./project/.circleci/leo-new.sh
+
+ leo-init:
+ docker:
+ - image: cimg/rust:1.50.0
+ resource_class: xlarge
+ steps:
+ - attach_workspace:
+ at: /home/circleci/project/
+ - run:
+ name: leo init
+ command: |
+ export LEO=/home/circleci/project/project/bin/leo
+ ./project/.circleci/leo-init.sh
+
+ leo-clean:
+ docker:
+ - image: cimg/rust:1.50.0
+ resource_class: xlarge
+ steps:
+ - attach_workspace:
+ at: /home/circleci/project/
+ - run:
+ name: leo clean
+ command: |
+ export LEO=/home/circleci/project/project/bin/leo
+ ./project/.circleci/leo-clean.sh
+
+ leo-setup:
+ docker:
+ - image: cimg/rust:1.50.0
+ resource_class: xlarge
+ steps:
+ - attach_workspace:
+ at: /home/circleci/project/
+ - run:
+ name: leo setup
+ command: |
+ export LEO=/home/circleci/project/project/bin/leo
+ ./project/.circleci/leo-setup.sh
+
+ leo-add-remove:
+ docker:
+ - image: cimg/rust:1.50.0
+ resource_class: xlarge
+ steps:
+ - attach_workspace:
+ at: /home/circleci/project/
+ - run:
+ name: leo add & remove
+ command: |
+ export LEO=/home/circleci/project/project/bin/leo
+ ./project/.circleci/leo-add-remove.sh
+
+ leo-login-logout:
+ docker:
+ - image: cimg/rust:1.50.0
+ resource_class: xlarge
+ steps:
+ - attach_workspace:
+ at: /home/circleci/project/
+ - run:
+ name: leo login & logout
+ command: |
+ export LEO=/home/circleci/project/project/bin/leo
+ ./project/.circleci/leo-login-logout.sh
+
+workflows:
+ version: 2
+ main-workflow:
+ jobs:
+ - rust-stable
+ - leo-new:
+ requires:
+ - rust-stable
+ - leo-init:
+ requires:
+ - rust-stable
+ - leo-clean:
+ requires:
+ - rust-stable
+ - leo-setup:
+ requires:
+ - rust-stable
+ - leo-add-remove:
+ requires:
+ - rust-stable
+ - leo-login-logout:
+ requires:
+ - rust-stable
\ No newline at end of file
diff --git a/.circleci/leo-add-remove.sh b/.circleci/leo-add-remove.sh
new file mode 100755
index 0000000000..e7eb352934
--- /dev/null
+++ b/.circleci/leo-add-remove.sh
@@ -0,0 +1,6 @@
+# leo add (w/o login) & remove
+
+$LEO new my-app && cd my-app
+$LEO add howard/silly-sudoku
+$LEO remove silly-sudoku
+$LEO clean
diff --git a/.circleci/leo-clean.sh b/.circleci/leo-clean.sh
new file mode 100755
index 0000000000..46bd90880c
--- /dev/null
+++ b/.circleci/leo-clean.sh
@@ -0,0 +1,34 @@
+# leo new hello-world
+
+$LEO new hello-world
+ls -la
+cd hello-world && ls -la
+$LEO run
+
+# Assert that the 'outputs' folder is not empty
+
+cd outputs || exit 1
+if [ "$(ls -A $DIR)" ]; then
+ echo "$DIR is not empty"
+else
+ echo "$DIR is empty"
+ exit 1
+fi
+cd ..
+
+# leo clean
+
+$LEO clean
+cd outputs && ls -la
+cd ..
+
+# Assert that the 'outputs' folder is empty
+
+cd outputs || exit 1
+if [ "$(ls -A $DIR)" ]; then
+ echo "$DIR is not empty"
+ exit 1
+else
+ echo "$DIR is empty"
+ exit 0
+fi
diff --git a/.circleci/leo-init.sh b/.circleci/leo-init.sh
new file mode 100755
index 0000000000..ab7724cd4a
--- /dev/null
+++ b/.circleci/leo-init.sh
@@ -0,0 +1,4 @@
+mkdir hello-world && cd hello-world || exit 1
+$LEO init
+ls -la
+$LEO run
diff --git a/.circleci/leo-login-logout.sh b/.circleci/leo-login-logout.sh
new file mode 100755
index 0000000000..1353b18d72
--- /dev/null
+++ b/.circleci/leo-login-logout.sh
@@ -0,0 +1,7 @@
+# leo login & logout
+
+$LEO new my-app && cd my-app || exit 1
+$LEO login -u "$ALEO_PM_USERNAME" -p "$ALEO_PM_PASSWORD"
+$LEO add howard/silly-sudoku
+$LEO remove silly-sudoku
+$LEO logout
diff --git a/.circleci/leo-new.sh b/.circleci/leo-new.sh
new file mode 100755
index 0000000000..cb24618546
--- /dev/null
+++ b/.circleci/leo-new.sh
@@ -0,0 +1,4 @@
+$LEO new hello-world
+ls -la
+cd hello-world && ls -la
+$LEO run
diff --git a/.circleci/leo-setup.sh b/.circleci/leo-setup.sh
new file mode 100755
index 0000000000..ae6e23803f
--- /dev/null
+++ b/.circleci/leo-setup.sh
@@ -0,0 +1,7 @@
+# leo setup
+
+cd ./project/examples/pedersen-hash || exit 1
+$LEO setup
+$LEO setup
+$LEO setup --skip-key-check
+$LEO clean
diff --git a/.github/workflows/leo-add-remove.yml b/.github/workflows/leo-add-remove.yml
deleted file mode 100644
index 8a0912ba96..0000000000
--- a/.github/workflows/leo-add-remove.yml
+++ /dev/null
@@ -1,43 +0,0 @@
-name: leo-add-remove
-on:
- pull_request:
- push:
- branches:
- - master
- paths-ignore:
- - 'docs/**'
- - 'documentation/**'
-env:
- RUST_BACKTRACE: 1
-
-jobs:
- add:
- name: Add Package ('leo add')
- runs-on: ubuntu-latest
- steps:
- - name: Checkout
- uses: actions/checkout@v1
-
- - name: Install Rust
- uses: actions-rs/toolchain@v1
- with:
- profile: minimal
- toolchain: nightly
- override: true
- components: rustfmt
-
- - name: Install Leo
- uses: actions-rs/cargo@v1
- env:
- CARGO_NET_GIT_FETCH_WITH_CLI: true
- with:
- command: install
- args: --path .
-
- - name: 'leo add (w/o login) & remove'
- run: |
- cd .. && leo new my-app && cd my-app
- leo add argus4130/xnor
- leo remove xnor
- leo clean
-
diff --git a/.github/workflows/leo-clean.yml b/.github/workflows/leo-clean.yml
deleted file mode 100644
index 177ed73fdc..0000000000
--- a/.github/workflows/leo-clean.yml
+++ /dev/null
@@ -1,76 +0,0 @@
-name: leo-clean
-on:
- pull_request:
- push:
- branches:
- - master
- paths-ignore:
- - 'docs/**'
- - 'documentation/**'
-env:
- RUST_BACKTRACE: 1
-
-jobs:
- new:
- name: Hello Leo ('leo new hello-world')
- runs-on: ubuntu-latest
- steps:
- - name: Checkout
- uses: actions/checkout@v1
-
- - name: Install Rust
- uses: actions-rs/toolchain@v1
- with:
- profile: minimal
- toolchain: nightly
- override: true
- components: rustfmt
-
- - uses: actions/cache@v2
- with:
- path: |
- ~/.cargo/registry
- ~/.cargo/git
- target
- key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
-
- - name: Install Leo
- uses: actions-rs/cargo@v1
- with:
- command: install
- args: --path .
-
- - name: 'leo new hello-world'
- run: |
- cd ..
- leo new hello-world
- ls -la
- cd hello-world && ls -la
- leo run
-
- - name: Assert that the 'outputs' folder is not empty
- run: |
- cd ../hello-world/outputs
- if [ "$(ls -A $DIR)" ]; then
- echo "$DIR is not empty"
- else
- echo "$DIR is empty"
- exit 1
- fi
-
- - name: 'leo clean'
- run: |
- cd ../hello-world
- leo clean
- cd outputs && ls -la
-
- - name: Assert that the 'outputs' folder is empty
- run: |
- cd ../hello-world/outputs
- if [ "$(ls -A $DIR)" ]; then
- echo "$DIR is not empty"
- exit 1
- else
- echo "$DIR is empty"
- exit 0
- fi
diff --git a/.github/workflows/leo-init.yml b/.github/workflows/leo-init.yml
deleted file mode 100644
index 1821d1de86..0000000000
--- a/.github/workflows/leo-init.yml
+++ /dev/null
@@ -1,48 +0,0 @@
-name: leo-init
-on:
- pull_request:
- push:
- branches:
- - master
- paths-ignore:
- - 'docs/**'
- - 'documentation/**'
-env:
- RUST_BACKTRACE: 1
-
-jobs:
- init:
- name: Hello Leo ('leo init')
- runs-on: ubuntu-latest
- steps:
- - name: Checkout
- uses: actions/checkout@v1
-
- - name: Install Rust
- uses: actions-rs/toolchain@v1
- with:
- profile: minimal
- toolchain: nightly
- override: true
- components: rustfmt
-
- - uses: actions/cache@v2
- with:
- path: |
- ~/.cargo/registry
- ~/.cargo/git
- target
- key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
-
- - name: Install Leo
- uses: actions-rs/cargo@v1
- with:
- command: install
- args: --path .
-
- - name: 'leo init'
- run: |
- cd .. && mkdir hello-world && cd hello-world
- leo init
- ls -la
- leo run
diff --git a/.github/workflows/leo-login-logout.yml b/.github/workflows/leo-login-logout.yml
deleted file mode 100644
index 95a17e42ed..0000000000
--- a/.github/workflows/leo-login-logout.yml
+++ /dev/null
@@ -1,47 +0,0 @@
-name: leo-login-logout
-on:
- pull_request:
- push:
- branches:
- - master
- paths-ignore:
- - 'docs/**'
- - 'documentation/**'
-env:
- RUST_BACKTRACE: 1
-
-jobs:
- add:
- name: Add Package ('leo add')
- runs-on: ubuntu-latest
- steps:
- - name: Checkout
- uses: actions/checkout@v1
-
- - name: Install Rust
- uses: actions-rs/toolchain@v1
- with:
- profile: minimal
- toolchain: nightly
- override: true
- components: rustfmt
-
- - name: Install Leo
- uses: actions-rs/cargo@v1
- env:
- CARGO_NET_GIT_FETCH_WITH_CLI: true
- with:
- command: install
- args: --path .
-
- - name: 'leo login & logout'
- env:
- USER: ${{ secrets.ALEO_PM_USERNAME }}
- PASS: ${{ secrets.ALEO_PM_PASSWORD }}
- run: |
- cd .. && leo new my-app && cd my-app
- leo login -u "$USER" -p "$PASS"
- leo add argus4130/xnor
- leo remove xnor
- leo logout
-
diff --git a/.github/workflows/leo-new.yml b/.github/workflows/leo-new.yml
deleted file mode 100644
index 842d553897..0000000000
--- a/.github/workflows/leo-new.yml
+++ /dev/null
@@ -1,49 +0,0 @@
-name: leo-new
-on:
- pull_request:
- push:
- branches:
- - master
- paths-ignore:
- - 'docs/**'
- - 'documentation/**'
-env:
- RUST_BACKTRACE: 1
-
-jobs:
- new:
- name: Hello Leo ('leo new hello-world')
- runs-on: ubuntu-latest
- steps:
- - name: Checkout
- uses: actions/checkout@v1
-
- - name: Install Rust
- uses: actions-rs/toolchain@v1
- with:
- profile: minimal
- toolchain: nightly
- override: true
- components: rustfmt
-
- - uses: actions/cache@v2
- with:
- path: |
- ~/.cargo/registry
- ~/.cargo/git
- target
- key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
-
- - name: Install Leo
- uses: actions-rs/cargo@v1
- with:
- command: install
- args: --path .
-
- - name: 'leo new hello-world'
- run: |
- cd ..
- leo new hello-world
- ls -la
- cd hello-world && ls -la
- leo run
diff --git a/.github/workflows/leo-setup.yml b/.github/workflows/leo-setup.yml
deleted file mode 100644
index 7bad2924c2..0000000000
--- a/.github/workflows/leo-setup.yml
+++ /dev/null
@@ -1,47 +0,0 @@
-name: leo-setup
-on:
- pull_request:
- push:
- branches:
- - master
- paths-ignore:
- - 'docs/**'
- - 'documentation/**'
-env:
- RUST_BACKTRACE: 1
-
-jobs:
- add:
- name: Add Package ('leo add')
- runs-on: ubuntu-latest
- steps:
- - name: Checkout
- uses: actions/checkout@v1
-
- - name: Install Rust
- uses: actions-rs/toolchain@v1
- with:
- profile: minimal
- toolchain: nightly
- override: true
- components: rustfmt
-
- - name: Install Leo
- uses: actions-rs/cargo@v1
- env:
- CARGO_NET_GIT_FETCH_WITH_CLI: true
- with:
- command: install
- args: --path .
-
- - name: 'leo setup for examples'
- env:
- USER: ${{ secrets.ALEO_PM_USERNAME }}
- PASS: ${{ secrets.ALEO_PM_PASSWORD }}
- run: |
- cd examples/pedersen-hash
- leo setup
- leo setup
- leo setup --skip-key-check
- leo clean
-
diff --git a/.gitignore b/.gitignore
index 169d72f25d..f9e224d6a5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,9 @@
/tmp/
**.idea/
*.DS_Store
+
+**/process.yml
+
+**/.crates.toml
+**/.crates2.json
+**/bin/
diff --git a/.resources/leo.png b/.resources/banner.png
similarity index 100%
rename from .resources/leo.png
rename to .resources/banner.png
diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md
new file mode 100644
index 0000000000..357d549c32
--- /dev/null
+++ b/DEVELOPMENT.md
@@ -0,0 +1,31 @@
+# Development Guide
+
+## Running CircleCI locally
+
+### Step 1: Install CircleCI
+
+If you wish to run CircleCI locally, start by installing it:
+
+- macOS
+```
+brew install circleci
+```
+
+- Linux (via Snap)
+```
+sudo snap install docker circleci
+sudo snap connect circleci:docker docker
+```
+
+- Windows (via Chocolatey)
+```
+choco install circleci-cli -y
+```
+
+### Step 2: Run CircleCI
+
+To run a job, export the config to `process.yml`, and specify it when executing:
+```shell
+circleci config process .circleci/config.yml > process.yml
+circleci local execute -c process.yml --job JOB_NAME
+```
diff --git a/README.md b/README.md
index ca19dd7fe9..e09580624c 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
-
+
The Leo Programming Language
@@ -27,6 +27,7 @@ Leo is a functional, statically-typed programming language built for writing pri
## 1. Overview
+
Welcome to the Leo programming language.
Leo provides a high-level language that abstracts low-level cryptographic concepts and makes it easy to
diff --git a/leo/commands/build.rs b/leo/commands/build.rs
index 1c2662f203..9f562dd85a 100644
--- a/leo/commands/build.rs
+++ b/leo/commands/build.rs
@@ -36,16 +36,10 @@ use structopt::StructOpt;
use tracing::span::Span;
/// Compile and build program command
-#[derive(StructOpt, Debug, Default)]
+#[derive(StructOpt, Debug)]
#[structopt(setting = structopt::clap::AppSettings::ColoredHelp)]
pub struct Build {}
-impl Build {
- pub fn new() -> Build {
- Build {}
- }
-}
-
impl Command for Build {
type Input = ();
type Output = Option<(Compiler<'static, Fq, EdwardsGroupType>, bool)>;
@@ -140,8 +134,8 @@ impl Command for Build {
let temporary_program = program.clone();
let output = temporary_program.compile_constraints(&mut cs)?;
- tracing::debug!("Compiled constraints - {:#?}", output);
- tracing::debug!("Number of constraints - {:#?}", cs.num_constraints());
+ tracing::debug!("Compiled output - {:#?}", output);
+ tracing::info!("Number of constraints - {:#?}", cs.num_constraints());
// Serialize the circuit
let circuit_object = SerializedCircuit::from(cs);
diff --git a/leo/commands/clean.rs b/leo/commands/clean.rs
index ecb13ea5a0..0b39661f07 100644
--- a/leo/commands/clean.rs
+++ b/leo/commands/clean.rs
@@ -23,16 +23,10 @@ use structopt::StructOpt;
use tracing::span::Span;
/// Clean outputs folder command
-#[derive(StructOpt, Debug, Default)]
+#[derive(StructOpt, Debug)]
#[structopt(setting = structopt::clap::AppSettings::ColoredHelp)]
pub struct Clean {}
-impl Clean {
- pub fn new() -> Clean {
- Clean {}
- }
-}
-
impl Command for Clean {
type Input = ();
type Output = ();
diff --git a/leo/commands/deploy.rs b/leo/commands/deploy.rs
index f27da1dae5..06eff0c2e6 100644
--- a/leo/commands/deploy.rs
+++ b/leo/commands/deploy.rs
@@ -21,16 +21,10 @@ use structopt::StructOpt;
use tracing::span::Span;
/// Deploy Leo program to the network
-#[derive(StructOpt, Debug, Default)]
+#[derive(StructOpt, Debug)]
#[structopt(setting = structopt::clap::AppSettings::ColoredHelp)]
pub struct Deploy {}
-impl Deploy {
- pub fn new() -> Deploy {
- Deploy {}
- }
-}
-
impl Command for Deploy {
type Input = ();
type Output = ();
diff --git a/leo/commands/init.rs b/leo/commands/init.rs
index 7ebe2a3214..7c6b3e7745 100644
--- a/leo/commands/init.rs
+++ b/leo/commands/init.rs
@@ -23,16 +23,10 @@ use structopt::StructOpt;
use tracing::span::Span;
/// Init Leo project command within current directory
-#[derive(StructOpt, Debug, Default)]
+#[derive(StructOpt, Debug)]
#[structopt(setting = structopt::clap::AppSettings::ColoredHelp)]
pub struct Init {}
-impl Init {
- pub fn new() -> Init {
- Init {}
- }
-}
-
impl Command for Init {
type Input = ();
type Output = ();
@@ -46,13 +40,20 @@ impl Command for Init {
}
fn apply(self, _: Context, _: Self::Input) -> Result {
+ // Derive the package directory path.
let path = current_dir()?;
+
+ // Check that the given package name is valid.
let package_name = path
.file_stem()
.ok_or_else(|| anyhow!("Project name invalid"))?
.to_string_lossy()
.to_string();
+ if !LeoPackage::is_package_name_valid(&package_name) {
+ return Err(anyhow!("Invalid Leo project name"));
+ }
+ // Check that the current package directory path exists.
if !path.exists() {
return Err(anyhow!("Directory does not exist"));
}
diff --git a/leo/commands/lint.rs b/leo/commands/lint.rs
index 0aeda51844..3e60d7ec6e 100644
--- a/leo/commands/lint.rs
+++ b/leo/commands/lint.rs
@@ -21,16 +21,10 @@ use structopt::StructOpt;
use tracing::span::Span;
/// Lint Leo code command
-#[derive(StructOpt, Debug, Default)]
+#[derive(StructOpt, Debug)]
#[structopt(setting = structopt::clap::AppSettings::ColoredHelp)]
pub struct Lint {}
-impl Lint {
- pub fn new() -> Lint {
- Lint {}
- }
-}
-
impl Command for Lint {
type Input = ();
type Output = ();
diff --git a/leo/commands/mod.rs b/leo/commands/mod.rs
index f59db22864..47d5994575 100644
--- a/leo/commands/mod.rs
+++ b/leo/commands/mod.rs
@@ -52,7 +52,7 @@ pub mod test;
pub use test::Test;
pub mod update;
-pub use update::{Sub as UpdateAutomatic, Update};
+pub use update::{Automatic as UpdateAutomatic, Update};
pub mod watch;
pub use watch::Watch;
@@ -60,57 +60,55 @@ pub use watch::Watch;
// Aleo PM related commands
pub mod package;
-/// Base trait for Leo CLI, see methods and their documentation for details
+/// Base trait for the Leo CLI, see methods and their documentation for details.
pub trait Command {
- /// If current command requires running another command before
- /// and needs its output results, this is the place to set.
+ /// If the current command requires running another command beforehand
+ /// and needs its output result, this is where the result type is defined.
/// Example: type Input: ::Out
type Input;
- /// Define output of the command to be reused as an Input for another
- /// command. If this command is not used as a prelude for another, keep empty
+ /// Defines the output of this command, which may be used as `Input` for another
+ /// command. If this command is not used as a prelude for another command,
+ /// this field may be left empty.
type Output;
- /// Returns project context, currently keeping it simple but it is possible
- /// that in the future leo will not depend on current directory, and we're keeping
- /// option for extending current core
+ /// Returns the project context, which is defined as the current directory.
fn context(&self) -> Result {
get_context()
}
- /// Add span to the logger tracing::span.
- /// Due to specifics of macro implementation it is impossible to set
- /// span name with non-literal i.e. dynamic variable even if this
- /// variable is &'static str
+ /// Adds a span to the logger via `tracing::span`.
+ /// Because of the specifics of the macro implementation, it is not possible
+ /// to set the span name with a non-literal i.e. a dynamic variable even if this
+ /// variable is a &'static str.
fn log_span(&self) -> Span {
tracing::span!(tracing::Level::INFO, "Leo")
}
- /// Run prelude and get Input for current command. As simple as that.
- /// But due to inability to pass default implementation of a type, this
- /// method must be present in every trait implementation.
+ /// Runs the prelude and returns the Input of the current command.
fn prelude(&self) -> Result
where
Self: std::marker::Sized;
- /// Core of the execution - do what is necessary. This function is run within
- /// context of 'execute' function, which sets logging and timers
+ /// Runs the main operation of this command. This function is run within
+ /// context of 'execute' function, which sets logging and timers.
fn apply(self, context: Context, input: Self::Input) -> Result
where
Self: std::marker::Sized;
- /// Wrapper around apply function, sets up tracing, time tracking and context
+ /// A wrapper around the `apply` method.
+ /// This function sets up tracing, timing, and the context.
fn execute(self) -> Result
where
Self: std::marker::Sized,
{
let input = self.prelude()?;
- // create span for this command
+ // Create the span for this command.
let span = self.log_span();
let span = span.enter();
- // calculate execution time for each run
+ // Calculate the execution time for this command.
let timer = Instant::now();
let context = self.context()?;
@@ -118,7 +116,7 @@ pub trait Command {
drop(span);
- // use done context to print time
+ // Use the done context to print the execution time for this command.
tracing::span!(tracing::Level::INFO, "Done").in_scope(|| {
tracing::info!("Finished in {} milliseconds \n", timer.elapsed().as_millis());
});
@@ -126,7 +124,7 @@ pub trait Command {
out
}
- /// Execute command but empty the result. Comes in handy where there's a
+ /// Executes command but empty the result. Comes in handy where there's a
/// need to make match arms compatible while keeping implementation-specific
/// output possible. Errors however are all of the type Error
fn try_execute(self) -> Result<()>
diff --git a/leo/commands/new.rs b/leo/commands/new.rs
index 2509e7563e..a4da67c309 100644
--- a/leo/commands/new.rs
+++ b/leo/commands/new.rs
@@ -30,12 +30,6 @@ pub struct New {
name: String,
}
-impl New {
- pub fn new(name: String) -> New {
- New { name }
- }
-}
-
impl Command for New {
type Input = ();
type Output = ();
@@ -49,13 +43,17 @@ impl Command for New {
}
fn apply(self, _: Context, _: Self::Input) -> Result {
- let mut path = current_dir()?;
+ // Check that the given package name is valid.
let package_name = self.name;
+ if !LeoPackage::is_package_name_valid(&package_name) {
+ return Err(anyhow!("Invalid Leo project name"));
+ }
- // Derive the package directory path
+ // Derive the package directory path.
+ let mut path = current_dir()?;
path.push(&package_name);
- // Verify the package directory path does not exist yet
+ // Verify the package directory path does not exist yet.
if path.exists() {
return Err(anyhow!("Directory already exists {:?}", path));
}
diff --git a/leo/commands/package/logout.rs b/leo/commands/package/logout.rs
index 56497d8b23..6bd76a2575 100644
--- a/leo/commands/package/logout.rs
+++ b/leo/commands/package/logout.rs
@@ -22,16 +22,10 @@ use structopt::StructOpt;
use tracing::Span;
/// Remove credentials for Aleo PM from .leo directory
-#[derive(StructOpt, Debug, Default)]
+#[derive(StructOpt, Debug)]
#[structopt(setting = structopt::clap::AppSettings::ColoredHelp)]
pub struct Logout {}
-impl Logout {
- pub fn new() -> Logout {
- Logout {}
- }
-}
-
impl Command for Logout {
type Input = ();
type Output = ();
diff --git a/leo/commands/package/publish.rs b/leo/commands/package/publish.rs
index 6fb949046f..ffcc1725e2 100644
--- a/leo/commands/package/publish.rs
+++ b/leo/commands/package/publish.rs
@@ -37,23 +37,17 @@ struct ResponseJson {
}
/// Publish package to Aleo Package Manager
-#[derive(StructOpt, Debug, Default)]
+#[derive(StructOpt, Debug)]
#[structopt(setting = structopt::clap::AppSettings::ColoredHelp)]
pub struct Publish {}
-impl Publish {
- pub fn new() -> Publish {
- Publish {}
- }
-}
-
impl Command for Publish {
type Input = ::Output;
type Output = Option;
/// Build program before publishing
fn prelude(&self) -> Result {
- Build::new().execute()
+ (Build {}).execute()
}
fn apply(self, context: Context, _input: Self::Input) -> Result {
diff --git a/leo/commands/package/remove.rs b/leo/commands/package/remove.rs
index 83de4a6ce8..f0df722146 100644
--- a/leo/commands/package/remove.rs
+++ b/leo/commands/package/remove.rs
@@ -22,19 +22,13 @@ use structopt::StructOpt;
use tracing::span::Span;
/// Remove imported package
-#[derive(StructOpt, Debug, Default)]
+#[derive(StructOpt, Debug)]
#[structopt(setting = structopt::clap::AppSettings::ColoredHelp)]
pub struct Remove {
#[structopt(name = "PACKAGE")]
name: String,
}
-impl Remove {
- pub fn new(name: String) -> Remove {
- Remove { name }
- }
-}
-
impl Command for Remove {
type Input = ();
type Output = ();
diff --git a/leo/commands/prove.rs b/leo/commands/prove.rs
index 202cb688d3..dbe19b5777 100644
--- a/leo/commands/prove.rs
+++ b/leo/commands/prove.rs
@@ -28,17 +28,11 @@ use structopt::StructOpt;
use tracing::span::Span;
/// Run the program and produce a proof
-#[derive(StructOpt, Debug, Default)]
+#[derive(StructOpt, Debug)]
#[structopt(setting = structopt::clap::AppSettings::ColoredHelp)]
pub struct Prove {
#[structopt(long = "skip-key-check", help = "Skip key verification on Setup stage")]
- skip_key_check: bool,
-}
-
-impl Prove {
- pub fn new(skip_key_check: bool) -> Prove {
- Prove { skip_key_check }
- }
+ pub(crate) skip_key_check: bool,
}
impl Command for Prove {
@@ -50,7 +44,8 @@ impl Command for Prove {
}
fn prelude(&self) -> Result {
- Setup::new(self.skip_key_check).execute()
+ let skip_key_check = self.skip_key_check;
+ (Setup { skip_key_check }).execute()
}
fn apply(self, context: Context, input: Self::Input) -> Result {
diff --git a/leo/commands/run.rs b/leo/commands/run.rs
index c9973dfd6b..b1bf652ed7 100644
--- a/leo/commands/run.rs
+++ b/leo/commands/run.rs
@@ -26,17 +26,11 @@ use structopt::StructOpt;
use tracing::span::Span;
/// Build, Prove and Run Leo program with inputs
-#[derive(StructOpt, Debug, Default)]
+#[derive(StructOpt, Debug)]
#[structopt(setting = structopt::clap::AppSettings::ColoredHelp)]
pub struct Run {
#[structopt(long = "skip-key-check", help = "Skip key verification on Setup stage")]
- skip_key_check: bool,
-}
-
-impl Run {
- pub fn new(skip_key_check: bool) -> Run {
- Run { skip_key_check }
- }
+ pub(crate) skip_key_check: bool,
}
impl Command for Run {
@@ -48,7 +42,8 @@ impl Command for Run {
}
fn prelude(&self) -> Result {
- Prove::new(self.skip_key_check).execute()
+ let skip_key_check = self.skip_key_check;
+ (Prove { skip_key_check }).execute()
}
fn apply(self, _context: Context, input: Self::Input) -> Result {
diff --git a/leo/commands/setup.rs b/leo/commands/setup.rs
index a2b3fb9388..fad11bbe20 100644
--- a/leo/commands/setup.rs
+++ b/leo/commands/setup.rs
@@ -31,17 +31,11 @@ use structopt::StructOpt;
use tracing::span::Span;
/// Executes the setup command for a Leo program
-#[derive(StructOpt, Debug, Default)]
+#[derive(StructOpt, Debug)]
#[structopt(setting = structopt::clap::AppSettings::ColoredHelp)]
pub struct Setup {
#[structopt(long = "skip-key-check", help = "Skip key verification")]
- skip_key_check: bool,
-}
-
-impl Setup {
- pub fn new(skip_key_check: bool) -> Setup {
- Setup { skip_key_check }
- }
+ pub(crate) skip_key_check: bool,
}
impl Command for Setup {
@@ -57,7 +51,7 @@ impl Command for Setup {
}
fn prelude(&self) -> Result {
- Build::new().execute()
+ (Build {}).execute()
}
fn apply(self, context: Context, input: Self::Input) -> Result {
@@ -70,7 +64,6 @@ impl Command for Setup {
let keys_exist = ProvingKeyFile::new(&package_name).exists_at(&path)
&& VerificationKeyFile::new(&package_name).exists_at(&path);
- // If keys do not exist or the checksum differs, run the program setup
// If keys do not exist or the checksum differs, run the program setup
let (proving_key, prepared_verifying_key) = if !keys_exist || checksum_differs {
tracing::info!("Starting...");
diff --git a/leo/commands/test.rs b/leo/commands/test.rs
index d6a3a30840..f5de8672f2 100644
--- a/leo/commands/test.rs
+++ b/leo/commands/test.rs
@@ -32,17 +32,11 @@ use structopt::StructOpt;
use tracing::span::Span;
/// Build program and run tests command
-#[derive(StructOpt, Debug, Default)]
+#[derive(StructOpt, Debug)]
#[structopt(setting = structopt::clap::AppSettings::ColoredHelp)]
pub struct Test {
#[structopt(short = "f", long = "file", name = "file")]
- files: Vec,
-}
-
-impl Test {
- pub fn new(files: Vec) -> Test {
- Test { files }
- }
+ pub(crate) files: Vec,
}
impl Command for Test {
diff --git a/leo/commands/update.rs b/leo/commands/update.rs
index 09e0ca640a..f3027e5907 100644
--- a/leo/commands/update.rs
+++ b/leo/commands/update.rs
@@ -22,7 +22,7 @@ use tracing::span::Span;
/// Setting for automatic updates of Leo
#[derive(Debug, StructOpt, PartialEq)]
-pub enum Sub {
+pub enum Automatic {
Automatic {
#[structopt(name = "bool", help = "Boolean value: true or false", parse(try_from_str))]
value: bool,
@@ -30,30 +30,20 @@ pub enum Sub {
}
/// Update Leo to the latest version
-#[derive(StructOpt, Debug, Default)]
+#[derive(StructOpt, Debug)]
#[structopt(setting = structopt::clap::AppSettings::ColoredHelp)]
pub struct Update {
/// List all available versions of Leo
#[structopt(short, long)]
- list: bool,
+ pub(crate) list: bool,
/// For Aleo Studio only
#[structopt(short, long)]
- studio: bool,
+ pub(crate) studio: bool,
/// Setting for automatic updates of Leo
#[structopt(subcommand)]
- automatic: Option,
-}
-
-impl Update {
- pub fn new(list: bool, studio: bool, automatic: Option) -> Update {
- Update {
- list,
- studio,
- automatic,
- }
- }
+ pub(crate) automatic: Option,
}
impl Command for Update {
@@ -69,13 +59,13 @@ impl Command for Update {
}
fn apply(self, _: Context, _: Self::Input) -> Result {
- // if --list is passed - simply list everything and exit
+ // If --list is passed, list all available versions and return.
if self.list {
return Updater::show_available_releases().map_err(|e| anyhow!("Could not fetch versions: {}", e));
}
- // in case automatic subcommand was called
- if let Some(Sub::Automatic { value }) = self.automatic {
+ // Handles enabling and disabling automatic updates in the config file.
+ if let Some(Automatic::Automatic { value }) = self.automatic {
Config::set_update_automatic(value)?;
match value {
diff --git a/leo/commands/watch.rs b/leo/commands/watch.rs
index 529dd0ce84..7a98b9604f 100644
--- a/leo/commands/watch.rs
+++ b/leo/commands/watch.rs
@@ -27,7 +27,7 @@ use tracing::span::Span;
const LEO_SOURCE_DIR: &str = "src/";
/// Watch file changes in src/ directory and run Build Command
-#[derive(StructOpt, Debug, Default)]
+#[derive(StructOpt, Debug)]
#[structopt(setting = structopt::clap::AppSettings::ColoredHelp)]
pub struct Watch {
/// Set up watch interval
@@ -35,12 +35,6 @@ pub struct Watch {
interval: u64,
}
-impl Watch {
- pub fn new(interval: u64) -> Watch {
- Watch { interval }
- }
-}
-
impl Command for Watch {
type Input = ();
type Output = ();
@@ -70,7 +64,7 @@ impl Command for Watch {
match rx.recv() {
// See changes on the write event
Ok(DebouncedEvent::Write(_write)) => {
- match Build::new().execute() {
+ match (Build {}).execute() {
Ok(_output) => {
tracing::info!("Built successfully");
}
diff --git a/leo/tests/mod.rs b/leo/tests/mod.rs
index 0ebc7b26ee..7bc611f49d 100644
--- a/leo/tests/mod.rs
+++ b/leo/tests/mod.rs
@@ -39,34 +39,34 @@ const PEDERSEN_HASH_PATH: &str = "./examples/pedersen-hash/";
#[test]
pub fn build_pedersen_hash() -> Result<()> {
- Build::new().apply(context()?, ())?;
+ (Build {}).apply(context()?, ())?;
Ok(())
}
#[test]
pub fn setup_pedersen_hash() -> Result<()> {
- let build = Build::new().apply(context()?, ())?;
- Setup::new(false).apply(context()?, build.clone())?;
- Setup::new(true).apply(context()?, build)?;
+ let build = (Build {}).apply(context()?, ())?;
+ (Setup { skip_key_check: false }).apply(context()?, build.clone())?;
+ (Setup { skip_key_check: true }).apply(context()?, build)?;
Ok(())
}
#[test]
pub fn prove_pedersen_hash() -> Result<()> {
- let build = Build::new().apply(context()?, ())?;
- let setup = Setup::new(false).apply(context()?, build)?;
- Prove::new(false).apply(context()?, setup.clone())?;
- Prove::new(true).apply(context()?, setup)?;
+ let build = (Build {}).apply(context()?, ())?;
+ let setup = (Setup { skip_key_check: false }).apply(context()?, build)?;
+ (Prove { skip_key_check: false }).apply(context()?, setup.clone())?;
+ (Prove { skip_key_check: true }).apply(context()?, setup)?;
Ok(())
}
#[test]
pub fn run_pedersen_hash() -> Result<()> {
- let build = Build::new().apply(context()?, ())?;
- let setup = Setup::new(false).apply(context()?, build)?;
- let prove = Prove::new(false).apply(context()?, setup)?;
- Run::new(false).apply(context()?, prove.clone())?;
- Run::new(true).apply(context()?, prove)?;
+ let build = (Build {}).apply(context()?, ())?;
+ let setup = (Setup { skip_key_check: false }).apply(context()?, build)?;
+ let prove = (Prove { skip_key_check: false }).apply(context()?, setup)?;
+ (Run { skip_key_check: false }).apply(context()?, prove.clone())?;
+ (Run { skip_key_check: true }).apply(context()?, prove)?;
Ok(())
}
@@ -75,14 +75,14 @@ pub fn test_pedersen_hash() -> Result<()> {
let mut main_file = PathBuf::from(PEDERSEN_HASH_PATH);
main_file.push("src/main.leo");
- Test::new(Vec::new()).apply(context()?, ())?;
- Test::new(vec![main_file]).apply(context()?, ())?;
+ (Test { files: vec![] }).apply(context()?, ())?;
+ (Test { files: vec![main_file] }).apply(context()?, ())?;
Ok(())
}
#[test]
pub fn test_logout() -> Result<()> {
- Logout::new().apply(context()?, ())?;
+ (Logout {}).apply(context()?, ())?;
Ok(())
}
@@ -111,12 +111,40 @@ pub fn login_incorrect_credentials_or_token() -> Result<()> {
#[test]
pub fn leo_update_and_update_automatic() -> Result<()> {
- Update::new(true, true, None).apply(context()?, ())?;
- Update::new(false, true, None).apply(context()?, ())?;
- Update::new(false, false, None).apply(context()?, ())?;
+ let update = Update {
+ list: true,
+ studio: true,
+ automatic: None,
+ };
+ update.apply(context()?, ())?;
- Update::new(false, false, Some(UpdateAutomatic::Automatic { value: true })).apply(context()?, ())?;
- Update::new(false, false, Some(UpdateAutomatic::Automatic { value: false })).apply(context()?, ())?;
+ let update = Update {
+ list: false,
+ studio: true,
+ automatic: None,
+ };
+ update.apply(context()?, ())?;
+
+ let update = Update {
+ list: false,
+ studio: false,
+ automatic: None,
+ };
+ update.apply(context()?, ())?;
+
+ let update = Update {
+ list: false,
+ studio: false,
+ automatic: Some(UpdateAutomatic::Automatic { value: true }),
+ };
+ update.apply(context()?, ())?;
+
+ let update = Update {
+ list: false,
+ studio: false,
+ automatic: Some(UpdateAutomatic::Automatic { value: false }),
+ };
+ update.apply(context()?, ())?;
Ok(())
}
diff --git a/package/src/errors/package.rs b/package/src/errors/package.rs
index 63bab79fd8..f0a07ea1ee 100644
--- a/package/src/errors/package.rs
+++ b/package/src/errors/package.rs
@@ -26,6 +26,9 @@ pub enum PackageError {
#[error("Failed to initialize package {:?} ({:?})", _0, _1)]
FailedToInitialize(String, OsString),
+ #[error("Invalid project name: {:?}", _0)]
+ InvalidPackageName(String),
+
#[error("`{}` metadata: {}", _0, _1)]
Removing(&'static str, io::Error),
}
diff --git a/package/src/errors/root/manifest.rs b/package/src/errors/root/manifest.rs
index b9645830d3..1e732a817f 100644
--- a/package/src/errors/root/manifest.rs
+++ b/package/src/errors/root/manifest.rs
@@ -18,6 +18,9 @@ use std::io;
#[derive(Debug, Error)]
pub enum ManifestError {
+ #[error("{}: {}", _0, _1)]
+ Crate(&'static str, String),
+
#[error("`{}` creating: {}", _0, _1)]
Creating(&'static str, io::Error),
@@ -36,3 +39,9 @@ pub enum ManifestError {
#[error("`{}` writing: {}", _0, _1)]
Writing(&'static str, io::Error),
}
+
+impl From for ManifestError {
+ fn from(error: crate::errors::PackageError) -> Self {
+ ManifestError::Crate("leo-package", error.to_string())
+ }
+}
diff --git a/package/src/lib.rs b/package/src/lib.rs
index fb3d000893..eb7cf4bb00 100644
--- a/package/src/lib.rs
+++ b/package/src/lib.rs
@@ -37,6 +37,11 @@ impl LeoPackage {
package::Package::initialize(package_name, is_lib, path)
}
+ /// Returns `true` if the given Leo package name is valid.
+ pub fn is_package_name_valid(package_name: &str) -> bool {
+ package::Package::is_package_name_valid(package_name)
+ }
+
/// Removes an imported Leo package
pub fn remove_imported_package(package_name: &str, path: &Path) -> Result<(), PackageError> {
package::Package::remove_imported_package(package_name, path)
diff --git a/package/src/package.rs b/package/src/package.rs
index f149c4ddf1..f5c46e2696 100644
--- a/package/src/package.rs
+++ b/package/src/package.rs
@@ -34,17 +34,79 @@ pub struct Package {
}
impl Package {
- pub fn new(package_name: &str) -> Self {
- Self {
+ pub fn new(package_name: &str) -> Result {
+ // Check that the package name is valid.
+ if !Self::is_package_name_valid(package_name) {
+ return Err(PackageError::InvalidPackageName(package_name.to_string()));
+ }
+
+ Ok(Self {
name: package_name.to_owned(),
version: "0.1.0".to_owned(),
description: None,
license: None,
+ })
+ }
+
+ /// Returns `true` if the package name is valid.
+ ///
+ /// Package names must be lowercase and composed solely
+ /// of ASCII alphanumeric characters, and may be word-separated
+ /// by a single dash '-'.
+ pub fn is_package_name_valid(package_name: &str) -> bool {
+ // Check that the package name is nonempty.
+ if package_name.is_empty() {
+ tracing::error!("Project names must be nonempty");
+ return false;
}
+
+ let mut previous = package_name.chars().next().unwrap();
+
+ // Check that the first character is not a dash.
+ if previous == '-' {
+ tracing::error!("Project names cannot begin with a dash");
+ return false;
+ }
+
+ // Iterate and check that the package name is valid.
+ for current in package_name.chars() {
+ // Check that the package name is lowercase.
+ if !current.is_ascii_lowercase() && current != '-' {
+ tracing::error!("Project names must be all lowercase");
+ return false;
+ }
+
+ // Check that the package name is only ASCII alphanumeric or a dash.
+ if !current.is_ascii_alphanumeric() && current != '-' {
+ tracing::error!("Project names must be ASCII alphanumeric, and may be word-separated with a dash");
+ return false;
+ }
+
+ // If the previous character was a dash, check that the current character is not a dash.
+ if previous == '-' && current == '-' {
+ tracing::error!("Project names may only be word-separated by one dash");
+ return false;
+ }
+
+ previous = current;
+ }
+
+ // Check that the last character is not a dash.
+ if previous == '-' {
+ tracing::error!("Project names cannot end with a dash");
+ return false;
+ }
+
+ true
}
/// Returns `true` if a package is can be initialized at a given path.
pub fn can_initialize(package_name: &str, is_lib: bool, path: &Path) -> bool {
+ // Check that the package name is valid.
+ if !Self::is_package_name_valid(package_name) {
+ return false;
+ }
+
let mut result = true;
let mut existing_files = vec![];
@@ -91,6 +153,11 @@ impl Package {
/// Returns `true` if a package is initialized at the given path
pub fn is_initialized(package_name: &str, is_lib: bool, path: &Path) -> bool {
+ // Check that the package name is valid.
+ if !Self::is_package_name_valid(package_name) {
+ return false;
+ }
+
// Check if the manifest file exists.
if !Manifest::exists_at(&path) {
return false;
@@ -137,7 +204,7 @@ impl Package {
// Next, initialize this directory as a Leo package.
{
// Create the manifest file.
- Manifest::new(&package_name).write_to(&path)?;
+ Manifest::new(&package_name)?.write_to(&path)?;
// Verify that the .gitignore file does not exist.
if !Gitignore::exists_at(&path) {
@@ -190,3 +257,27 @@ impl Package {
Ok(ImportsDirectory::remove_import(path, package_name)?)
}
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_is_package_name_valid() {
+ assert!(Package::is_package_name_valid("foo"));
+ assert!(Package::is_package_name_valid("foo-bar"));
+ assert!(Package::is_package_name_valid("foo-bar-baz"));
+
+ assert!(!Package::is_package_name_valid(""));
+ assert!(!Package::is_package_name_valid("-"));
+ assert!(!Package::is_package_name_valid("-foo"));
+ assert!(!Package::is_package_name_valid("-foo-"));
+ assert!(!Package::is_package_name_valid("foo--bar"));
+ assert!(!Package::is_package_name_valid("foo---bar"));
+ assert!(!Package::is_package_name_valid("foo--bar--baz"));
+ assert!(!Package::is_package_name_valid("foo---bar---baz"));
+ assert!(!Package::is_package_name_valid("foo*bar"));
+ assert!(!Package::is_package_name_valid("foo,bar"));
+ assert!(!Package::is_package_name_valid("foo_bar"));
+ }
+}
diff --git a/package/src/root/manifest.rs b/package/src/root/manifest.rs
index 3a85f60447..540749fcdc 100644
--- a/package/src/root/manifest.rs
+++ b/package/src/root/manifest.rs
@@ -39,11 +39,11 @@ pub struct Manifest {
}
impl Manifest {
- pub fn new(package_name: &str) -> Self {
- Self {
- project: Package::new(package_name),
+ pub fn new(package_name: &str) -> Result {
+ Ok(Self {
+ project: Package::new(package_name)?,
remote: None,
- }
+ })
}
pub fn filename() -> String {
diff --git a/package/tests/initialize/initialize.rs b/package/tests/initialize/initialize.rs
index e3cffcfd8c..5e1b98bde8 100644
--- a/package/tests/initialize/initialize.rs
+++ b/package/tests/initialize/initialize.rs
@@ -52,7 +52,10 @@ fn initialize_fails_with_existing_manifest() {
assert!(Package::can_initialize(TEST_PACKAGE_NAME, false, &test_directory));
// Manually add a manifest file to the `test_directory`
- Manifest::new(TEST_PACKAGE_NAME).write_to(&test_directory).unwrap();
+ Manifest::new(TEST_PACKAGE_NAME)
+ .unwrap()
+ .write_to(&test_directory)
+ .unwrap();
// Attempt to initialize a package at the `test_directory`
assert!(Package::initialize(TEST_PACKAGE_NAME, false, &test_directory).is_err());