Merge pull request #694 from AleoHQ/refactor/leo

Updates Leo CLI with safety enhancements, adds CircleCI resolving flaky tests
This commit is contained in:
Collin Chin 2021-02-25 00:02:22 -08:00 committed by GitHub
commit d85a057ddc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
49 changed files with 623 additions and 604 deletions

163
.circleci/config.yml Normal file
View File

@ -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

6
.circleci/leo-add-remove.sh Executable file
View File

@ -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

34
.circleci/leo-clean.sh Executable file
View File

@ -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

4
.circleci/leo-init.sh Executable file
View File

@ -0,0 +1,4 @@
mkdir hello-world && cd hello-world || exit 1
$LEO init
ls -la
$LEO run

7
.circleci/leo-login-logout.sh Executable file
View File

@ -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

4
.circleci/leo-new.sh Executable file
View File

@ -0,0 +1,4 @@
$LEO new hello-world
ls -la
cd hello-world && ls -la
$LEO run

7
.circleci/leo-setup.sh Executable file
View File

@ -0,0 +1,7 @@
# leo setup
cd ./project/examples/pedersen-hash || exit 1
$LEO setup
$LEO setup
$LEO setup --skip-key-check
$LEO clean

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

6
.gitignore vendored
View File

@ -2,3 +2,9 @@
/tmp/ /tmp/
**.idea/ **.idea/
*.DS_Store *.DS_Store
**/process.yml
**/.crates.toml
**/.crates2.json
**/bin/

View File

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 29 KiB

31
DEVELOPMENT.md Normal file
View File

@ -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
```

View File

@ -1,5 +1,5 @@
<p align="center"> <p align="center">
<img width="1412" src="./.resources/leo.png"> <img width="1412" src=".resources/banner.png">
</p> </p>
<h1 align="center">The Leo Programming Language</h1> <h1 align="center">The Leo Programming Language</h1>
@ -27,6 +27,7 @@ Leo is a functional, statically-typed programming language built for writing pri
## 1. Overview ## 1. Overview
Welcome to the Leo programming language. Welcome to the Leo programming language.
Leo provides a high-level language that abstracts low-level cryptographic concepts and makes it easy to Leo provides a high-level language that abstracts low-level cryptographic concepts and makes it easy to

View File

@ -40,7 +40,7 @@ fn load_asg_imports<'a, T: ImportResolver<'a>>(
InternalProgram::new(context, &ast.as_repr(), imports) InternalProgram::new(context, &ast.as_repr(), imports)
} }
fn mocked_resolver<'a>(_ctx: AsgContext<'a>) -> MockedImportResolver<'a> { fn mocked_resolver<'a>(_context: AsgContext<'a>) -> MockedImportResolver<'a> {
let packages = indexmap::IndexMap::new(); let packages = indexmap::IndexMap::new();
MockedImportResolver { packages } MockedImportResolver { packages }
} }

View File

@ -25,8 +25,8 @@ use std::path::Path;
#[test] #[test]
fn test_basic() { fn test_basic() {
let program_string = include_str!("./circuits/pedersen_mock.leo"); let program_string = include_str!("./circuits/pedersen_mock.leo");
let ctx = new_context(); let context = new_context();
let asg = load_asg(&ctx, program_string).unwrap(); let asg = load_asg(&context, program_string).unwrap();
let reformed_ast = leo_asg::reform_ast(&asg); let reformed_ast = leo_asg::reform_ast(&asg);
println!("{}", reformed_ast); println!("{}", reformed_ast);
// panic!(); // panic!();
@ -51,8 +51,8 @@ fn test_function_rename() {
console.assert(total == 20); console.assert(total == 20);
} }
"#; "#;
let ctx = new_context(); let context = new_context();
let asg = load_asg(&ctx, program_string).unwrap(); let asg = load_asg(&context, program_string).unwrap();
let reformed_ast = leo_asg::reform_ast(&asg); let reformed_ast = leo_asg::reform_ast(&asg);
println!("{}", reformed_ast); println!("{}", reformed_ast);
// panic!(); // panic!();
@ -60,8 +60,8 @@ fn test_function_rename() {
#[test] #[test]
fn test_imports() { fn test_imports() {
let ctx = new_context(); let context = new_context();
let mut imports = crate::mocked_resolver(&ctx); let mut imports = crate::mocked_resolver(&context);
let test_import = r#" let test_import = r#"
circuit Point { circuit Point {
x: u32 x: u32
@ -74,7 +74,7 @@ fn test_imports() {
"#; "#;
imports imports
.packages .packages
.insert("test-import".to_string(), load_asg(&ctx, test_import).unwrap()); .insert("test-import".to_string(), load_asg(&context, test_import).unwrap());
let program_string = r#" let program_string = r#"
import test-import.foo; import test-import.foo;
@ -95,7 +95,7 @@ fn test_imports() {
serde_json::to_string(Ast::new("test", &test_grammar).unwrap().as_repr()).unwrap() serde_json::to_string(Ast::new("test", &test_grammar).unwrap().as_repr()).unwrap()
); );
let asg = crate::load_asg_imports(&ctx, program_string, &mut imports).unwrap(); let asg = crate::load_asg_imports(&context, program_string, &mut imports).unwrap();
let reformed_ast = leo_asg::reform_ast(&asg); let reformed_ast = leo_asg::reform_ast(&asg);
println!("{}", serde_json::to_string(&reformed_ast).unwrap()); println!("{}", serde_json::to_string(&reformed_ast).unwrap());
// panic!(); // panic!();

View File

@ -20,138 +20,139 @@ use crate::{load_asg, load_asg_imports, mocked_resolver};
#[test] #[test]
fn test_basic() { fn test_basic() {
let ctx = new_context(); let context = new_context();
let mut imports = mocked_resolver(&ctx); let mut imports = mocked_resolver(&context);
imports.packages.insert( imports.packages.insert(
"test-import".to_string(), "test-import".to_string(),
load_asg(&ctx, include_str!("src/test-import.leo")).unwrap(), load_asg(&context, include_str!("src/test-import.leo")).unwrap(),
); );
let program_string = include_str!("basic.leo"); let program_string = include_str!("basic.leo");
load_asg_imports(&ctx, program_string, &mut imports).unwrap(); load_asg_imports(&context, program_string, &mut imports).unwrap();
} }
#[test] #[test]
fn test_multiple() { fn test_multiple() {
let ctx = new_context(); let context = new_context();
let mut imports = mocked_resolver(&ctx); let mut imports = mocked_resolver(&context);
imports.packages.insert( imports.packages.insert(
"test-import".to_string(), "test-import".to_string(),
load_asg(&ctx, include_str!("src/test-import.leo")).unwrap(), load_asg(&context, include_str!("src/test-import.leo")).unwrap(),
); );
let program_string = include_str!("multiple.leo"); let program_string = include_str!("multiple.leo");
load_asg_imports(&ctx, program_string, &mut imports).unwrap(); load_asg_imports(&context, program_string, &mut imports).unwrap();
} }
#[test] #[test]
fn test_star() { fn test_star() {
let ctx = new_context(); let context = new_context();
let mut imports = mocked_resolver(&ctx); let mut imports = mocked_resolver(&context);
imports.packages.insert( imports.packages.insert(
"test-import".to_string(), "test-import".to_string(),
load_asg(&ctx, include_str!("src/test-import.leo")).unwrap(), load_asg(&context, include_str!("src/test-import.leo")).unwrap(),
); );
let program_string = include_str!("star.leo"); let program_string = include_str!("star.leo");
load_asg_imports(&ctx, program_string, &mut imports).unwrap(); load_asg_imports(&context, program_string, &mut imports).unwrap();
} }
#[test] #[test]
fn test_alias() { fn test_alias() {
let ctx = new_context(); let context = new_context();
let mut imports = mocked_resolver(&ctx); let mut imports = mocked_resolver(&context);
imports.packages.insert( imports.packages.insert(
"test-import".to_string(), "test-import".to_string(),
load_asg(&ctx, include_str!("src/test-import.leo")).unwrap(), load_asg(&context, include_str!("src/test-import.leo")).unwrap(),
); );
let program_string = include_str!("alias.leo"); let program_string = include_str!("alias.leo");
load_asg_imports(&ctx, program_string, &mut imports).unwrap(); load_asg_imports(&context, program_string, &mut imports).unwrap();
} }
// naming tests // naming tests
#[test] #[test]
fn test_name() { fn test_name() {
let ctx = new_context(); let context = new_context();
let mut imports = mocked_resolver(&ctx); let mut imports = mocked_resolver(&context);
imports.packages.insert( imports.packages.insert(
"hello-world".to_string(), "hello-world".to_string(),
load_asg(&ctx, include_str!("src/hello-world.leo")).unwrap(), load_asg(&context, include_str!("src/hello-world.leo")).unwrap(),
); );
imports.packages.insert( imports.packages.insert(
"a0-f".to_string(), "a0-f".to_string(),
load_asg(&ctx, include_str!("src/a0-f.leo")).unwrap(), load_asg(&context, include_str!("src/a0-f.leo")).unwrap(),
);
imports.packages.insert(
"a-9".to_string(),
load_asg(&context, include_str!("src/a-9.leo")).unwrap(),
); );
imports
.packages
.insert("a-9".to_string(), load_asg(&ctx, include_str!("src/a-9.leo")).unwrap());
let program_string = include_str!("names.leo"); let program_string = include_str!("names.leo");
load_asg_imports(&ctx, program_string, &mut imports).unwrap(); load_asg_imports(&context, program_string, &mut imports).unwrap();
} }
// more complex tests // more complex tests
#[test] #[test]
fn test_many_import() { fn test_many_import() {
let ctx = new_context(); let context = new_context();
let mut imports = mocked_resolver(&ctx); let mut imports = mocked_resolver(&context);
imports.packages.insert( imports.packages.insert(
"test-import".to_string(), "test-import".to_string(),
load_asg(&ctx, include_str!("src/test-import.leo")).unwrap(), load_asg(&context, include_str!("src/test-import.leo")).unwrap(),
); );
imports.packages.insert( imports.packages.insert(
"bar".to_string(), "bar".to_string(),
load_asg(&ctx, include_str!("imports/bar/src/lib.leo")).unwrap(), load_asg(&context, include_str!("imports/bar/src/lib.leo")).unwrap(),
); );
imports.packages.insert( imports.packages.insert(
"bar.baz".to_string(), "bar.baz".to_string(),
load_asg(&ctx, include_str!("imports/bar/src/baz.leo")).unwrap(), load_asg(&context, include_str!("imports/bar/src/baz.leo")).unwrap(),
); );
imports.packages.insert( imports.packages.insert(
"bar.baz".to_string(), "bar.baz".to_string(),
load_asg(&ctx, include_str!("imports/bar/src/baz.leo")).unwrap(), load_asg(&context, include_str!("imports/bar/src/baz.leo")).unwrap(),
); );
imports.packages.insert( imports.packages.insert(
"bar.bat.bat".to_string(), "bar.bat.bat".to_string(),
load_asg(&ctx, include_str!("imports/bar/src/bat/bat.leo")).unwrap(), load_asg(&context, include_str!("imports/bar/src/bat/bat.leo")).unwrap(),
); );
imports.packages.insert( imports.packages.insert(
"car".to_string(), "car".to_string(),
load_asg(&ctx, include_str!("imports/car/src/lib.leo")).unwrap(), load_asg(&context, include_str!("imports/car/src/lib.leo")).unwrap(),
); );
let program_string = include_str!("many_import.leo"); let program_string = include_str!("many_import.leo");
load_asg_imports(&ctx, program_string, &mut imports).unwrap(); load_asg_imports(&context, program_string, &mut imports).unwrap();
} }
#[test] #[test]
fn test_many_import_star() { fn test_many_import_star() {
let ctx = new_context(); let context = new_context();
let mut imports = mocked_resolver(&ctx); let mut imports = mocked_resolver(&context);
imports.packages.insert( imports.packages.insert(
"test-import".to_string(), "test-import".to_string(),
load_asg(&ctx, include_str!("src/test-import.leo")).unwrap(), load_asg(&context, include_str!("src/test-import.leo")).unwrap(),
); );
imports.packages.insert( imports.packages.insert(
"bar".to_string(), "bar".to_string(),
load_asg(&ctx, include_str!("imports/bar/src/lib.leo")).unwrap(), load_asg(&context, include_str!("imports/bar/src/lib.leo")).unwrap(),
); );
imports.packages.insert( imports.packages.insert(
"bar.baz".to_string(), "bar.baz".to_string(),
load_asg(&ctx, include_str!("imports/bar/src/baz.leo")).unwrap(), load_asg(&context, include_str!("imports/bar/src/baz.leo")).unwrap(),
); );
imports.packages.insert( imports.packages.insert(
"bar.baz".to_string(), "bar.baz".to_string(),
load_asg(&ctx, include_str!("imports/bar/src/baz.leo")).unwrap(), load_asg(&context, include_str!("imports/bar/src/baz.leo")).unwrap(),
); );
imports.packages.insert( imports.packages.insert(
"bar.bat.bat".to_string(), "bar.bat.bat".to_string(),
load_asg(&ctx, include_str!("imports/bar/src/bat/bat.leo")).unwrap(), load_asg(&context, include_str!("imports/bar/src/bat/bat.leo")).unwrap(),
); );
imports.packages.insert( imports.packages.insert(
"car".to_string(), "car".to_string(),
load_asg(&ctx, include_str!("imports/car/src/lib.leo")).unwrap(), load_asg(&context, include_str!("imports/car/src/lib.leo")).unwrap(),
); );
let program_string = include_str!("many_import_star.leo"); let program_string = include_str!("many_import_star.leo");
load_asg_imports(&ctx, program_string, &mut imports).unwrap(); load_asg_imports(&context, program_string, &mut imports).unwrap();
} }

View File

@ -63,7 +63,7 @@ pub struct Compiler<'a, F: PrimeField, G: GroupType<F>> {
output_directory: PathBuf, output_directory: PathBuf,
program: Program, program: Program,
program_input: Input, program_input: Input,
ctx: AsgContext<'a>, context: AsgContext<'a>,
asg: Option<Asg<'a>>, asg: Option<Asg<'a>>,
_engine: PhantomData<F>, _engine: PhantomData<F>,
_group: PhantomData<G>, _group: PhantomData<G>,
@ -73,7 +73,12 @@ impl<'a, F: PrimeField, G: GroupType<F>> Compiler<'a, F, G> {
/// ///
/// Returns a new Leo program compiler. /// Returns a new Leo program compiler.
/// ///
pub fn new(package_name: String, main_file_path: PathBuf, output_directory: PathBuf, ctx: AsgContext<'a>) -> Self { pub fn new(
package_name: String,
main_file_path: PathBuf,
output_directory: PathBuf,
context: AsgContext<'a>,
) -> Self {
Self { Self {
program_name: package_name.clone(), program_name: package_name.clone(),
main_file_path, main_file_path,
@ -81,7 +86,7 @@ impl<'a, F: PrimeField, G: GroupType<F>> Compiler<'a, F, G> {
program: Program::new(package_name), program: Program::new(package_name),
program_input: Input::new(), program_input: Input::new(),
asg: None, asg: None,
ctx, context,
_engine: PhantomData, _engine: PhantomData,
_group: PhantomData, _group: PhantomData,
} }
@ -98,9 +103,9 @@ impl<'a, F: PrimeField, G: GroupType<F>> Compiler<'a, F, G> {
package_name: String, package_name: String,
main_file_path: PathBuf, main_file_path: PathBuf,
output_directory: PathBuf, output_directory: PathBuf,
ctx: AsgContext<'a>, context: AsgContext<'a>,
) -> Result<Self, CompilerError> { ) -> Result<Self, CompilerError> {
let mut compiler = Self::new(package_name, main_file_path, output_directory, ctx); let mut compiler = Self::new(package_name, main_file_path, output_directory, context);
compiler.parse_program()?; compiler.parse_program()?;
@ -124,9 +129,9 @@ impl<'a, F: PrimeField, G: GroupType<F>> Compiler<'a, F, G> {
input_path: &Path, input_path: &Path,
state_string: &str, state_string: &str,
state_path: &Path, state_path: &Path,
ctx: AsgContext<'a>, context: AsgContext<'a>,
) -> Result<Self, CompilerError> { ) -> Result<Self, CompilerError> {
let mut compiler = Self::new(package_name, main_file_path, output_directory, ctx); let mut compiler = Self::new(package_name, main_file_path, output_directory, context);
compiler.parse_input(input_string, input_path, state_string, state_path)?; compiler.parse_input(input_string, input_path, state_string, state_path)?;
@ -205,7 +210,7 @@ impl<'a, F: PrimeField, G: GroupType<F>> Compiler<'a, F, G> {
tracing::debug!("Program parsing complete\n{:#?}", self.program); tracing::debug!("Program parsing complete\n{:#?}", self.program);
// Create a new symbol table from the program, imported_programs, and program_input. // Create a new symbol table from the program, imported_programs, and program_input.
let asg = Asg::new(self.ctx, &core_ast, &mut leo_imports::ImportParser::default())?; let asg = Asg::new(self.context, &core_ast, &mut leo_imports::ImportParser::default())?;
tracing::debug!("ASG generation complete"); tracing::debug!("ASG generation complete");

View File

@ -34,7 +34,7 @@ pub struct ImportParser<'a> {
impl<'a> ImportResolver<'a> for ImportParser<'a> { impl<'a> ImportResolver<'a> for ImportParser<'a> {
fn resolve_package( fn resolve_package(
&mut self, &mut self,
ctx: AsgContext<'a>, context: AsgContext<'a>,
package_segments: &[&str], package_segments: &[&str],
span: &Span, span: &Span,
) -> Result<Option<Program<'a>>, AsgConvertError> { ) -> Result<Option<Program<'a>>, AsgConvertError> {
@ -51,7 +51,7 @@ impl<'a> ImportResolver<'a> for ImportParser<'a> {
self.partial_imports.insert(full_path.clone()); self.partial_imports.insert(full_path.clone());
let program = imports let program = imports
.parse_package(ctx, path, package_segments, span) .parse_package(context, path, package_segments, span)
.map_err(|x| -> AsgConvertError { x.into() })?; .map_err(|x| -> AsgConvertError { x.into() })?;
self.partial_imports.remove(&full_path); self.partial_imports.remove(&full_path);
self.imports.insert(full_path, program.clone()); self.imports.insert(full_path, program.clone());

View File

@ -26,16 +26,16 @@ static IMPORTS_DIRECTORY_NAME: &str = "imports/";
impl<'a> ImportParser<'a> { impl<'a> ImportParser<'a> {
fn parse_package_access( fn parse_package_access(
&mut self, &mut self,
ctx: AsgContext<'a>, context: AsgContext<'a>,
package: &DirEntry, package: &DirEntry,
remaining_segments: &[&str], remaining_segments: &[&str],
span: &Span, span: &Span,
) -> Result<Program<'a>, ImportParserError> { ) -> Result<Program<'a>, ImportParserError> {
if !remaining_segments.is_empty() { if !remaining_segments.is_empty() {
return self.parse_package(ctx, package.path(), remaining_segments, span); return self.parse_package(context, package.path(), remaining_segments, span);
} }
let program = Self::parse_import_file(package, span)?; let program = Self::parse_import_file(package, span)?;
let asg = leo_asg::InternalProgram::new(ctx, &program, self)?; let asg = leo_asg::InternalProgram::new(context, &program, self)?;
Ok(asg) Ok(asg)
} }
@ -47,7 +47,7 @@ impl<'a> ImportParser<'a> {
/// ///
pub(crate) fn parse_package( pub(crate) fn parse_package(
&mut self, &mut self,
ctx: AsgContext<'a>, context: AsgContext<'a>,
mut path: PathBuf, mut path: PathBuf,
segments: &[&str], segments: &[&str],
span: &Span, span: &Span,
@ -113,8 +113,8 @@ impl<'a> ImportParser<'a> {
package_name, package_name,
span, span,
))), ))),
(Some(source_entry), None) => self.parse_package_access(ctx, &source_entry, &segments[1..], span), (Some(source_entry), None) => self.parse_package_access(context, &source_entry, &segments[1..], span),
(None, Some(import_entry)) => self.parse_package_access(ctx, &import_entry, &segments[1..], span), (None, Some(import_entry)) => self.parse_package_access(context, &import_entry, &segments[1..], span),
(None, None) => Err(ImportParserError::unknown_package(Identifier::new_with_span( (None, None) => Err(ImportParserError::unknown_package(Identifier::new_with_span(
package_name, package_name,
span, span,
@ -123,7 +123,7 @@ impl<'a> ImportParser<'a> {
} else { } else {
// Enforce local package access with no found imports directory // Enforce local package access with no found imports directory
match matched_source_entry { match matched_source_entry {
Some(source_entry) => self.parse_package_access(ctx, &source_entry, &segments[1..], span), Some(source_entry) => self.parse_package_access(context, &source_entry, &segments[1..], span),
None => Err(ImportParserError::unknown_package(Identifier::new_with_span( None => Err(ImportParserError::unknown_package(Identifier::new_with_span(
package_name, package_name,
span, span,

View File

@ -36,16 +36,10 @@ use structopt::StructOpt;
use tracing::span::Span; use tracing::span::Span;
/// Compile and build program command /// Compile and build program command
#[derive(StructOpt, Debug, Default)] #[derive(StructOpt, Debug)]
#[structopt(setting = structopt::clap::AppSettings::ColoredHelp)] #[structopt(setting = structopt::clap::AppSettings::ColoredHelp)]
pub struct Build {} pub struct Build {}
impl Build {
pub fn new() -> Build {
Build {}
}
}
impl Command for Build { impl Command for Build {
type Input = (); type Input = ();
type Output = Option<(Compiler<'static, Fq, EdwardsGroupType>, bool)>; type Output = Option<(Compiler<'static, Fq, EdwardsGroupType>, bool)>;
@ -58,9 +52,9 @@ impl Command for Build {
Ok(()) Ok(())
} }
fn apply(self, ctx: Context, _: Self::Input) -> Result<Self::Output> { fn apply(self, context: Context, _: Self::Input) -> Result<Self::Output> {
let path = ctx.dir()?; let path = context.dir()?;
let package_name = ctx.manifest()?.get_package_name(); let package_name = context.manifest()?.get_package_name();
// Sanitize the package path to the root directory // Sanitize the package path to the root directory
let mut package_path = path.clone(); let mut package_path = path.clone();
@ -140,8 +134,8 @@ impl Command for Build {
let temporary_program = program.clone(); let temporary_program = program.clone();
let output = temporary_program.compile_constraints(&mut cs)?; let output = temporary_program.compile_constraints(&mut cs)?;
tracing::debug!("Compiled constraints - {:#?}", output); tracing::debug!("Compiled output - {:#?}", output);
tracing::debug!("Number of constraints - {:#?}", cs.num_constraints()); tracing::info!("Number of constraints - {:#?}", cs.num_constraints());
// Serialize the circuit // Serialize the circuit
let circuit_object = SerializedCircuit::from(cs); let circuit_object = SerializedCircuit::from(cs);

View File

@ -23,16 +23,10 @@ use structopt::StructOpt;
use tracing::span::Span; use tracing::span::Span;
/// Clean outputs folder command /// Clean outputs folder command
#[derive(StructOpt, Debug, Default)] #[derive(StructOpt, Debug)]
#[structopt(setting = structopt::clap::AppSettings::ColoredHelp)] #[structopt(setting = structopt::clap::AppSettings::ColoredHelp)]
pub struct Clean {} pub struct Clean {}
impl Clean {
pub fn new() -> Clean {
Clean {}
}
}
impl Command for Clean { impl Command for Clean {
type Input = (); type Input = ();
type Output = (); type Output = ();
@ -45,9 +39,9 @@ impl Command for Clean {
Ok(()) Ok(())
} }
fn apply(self, ctx: Context, _: Self::Input) -> Result<Self::Output> { fn apply(self, context: Context, _: Self::Input) -> Result<Self::Output> {
let path = ctx.dir()?; let path = context.dir()?;
let package_name = ctx.manifest()?.get_package_name(); let package_name = context.manifest()?.get_package_name();
// Remove the checksum from the output directory // Remove the checksum from the output directory
ChecksumFile::new(&package_name).remove(&path)?; ChecksumFile::new(&package_name).remove(&path)?;

View File

@ -21,16 +21,10 @@ use structopt::StructOpt;
use tracing::span::Span; use tracing::span::Span;
/// Deploy Leo program to the network /// Deploy Leo program to the network
#[derive(StructOpt, Debug, Default)] #[derive(StructOpt, Debug)]
#[structopt(setting = structopt::clap::AppSettings::ColoredHelp)] #[structopt(setting = structopt::clap::AppSettings::ColoredHelp)]
pub struct Deploy {} pub struct Deploy {}
impl Deploy {
pub fn new() -> Deploy {
Deploy {}
}
}
impl Command for Deploy { impl Command for Deploy {
type Input = (); type Input = ();
type Output = (); type Output = ();

View File

@ -23,16 +23,10 @@ use structopt::StructOpt;
use tracing::span::Span; use tracing::span::Span;
/// Init Leo project command within current directory /// Init Leo project command within current directory
#[derive(StructOpt, Debug, Default)] #[derive(StructOpt, Debug)]
#[structopt(setting = structopt::clap::AppSettings::ColoredHelp)] #[structopt(setting = structopt::clap::AppSettings::ColoredHelp)]
pub struct Init {} pub struct Init {}
impl Init {
pub fn new() -> Init {
Init {}
}
}
impl Command for Init { impl Command for Init {
type Input = (); type Input = ();
type Output = (); type Output = ();
@ -46,13 +40,20 @@ impl Command for Init {
} }
fn apply(self, _: Context, _: Self::Input) -> Result<Self::Output> { fn apply(self, _: Context, _: Self::Input) -> Result<Self::Output> {
// Derive the package directory path.
let path = current_dir()?; let path = current_dir()?;
// Check that the given package name is valid.
let package_name = path let package_name = path
.file_stem() .file_stem()
.ok_or_else(|| anyhow!("Project name invalid"))? .ok_or_else(|| anyhow!("Project name invalid"))?
.to_string_lossy() .to_string_lossy()
.to_string(); .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() { if !path.exists() {
return Err(anyhow!("Directory does not exist")); return Err(anyhow!("Directory does not exist"));
} }

View File

@ -21,16 +21,10 @@ use structopt::StructOpt;
use tracing::span::Span; use tracing::span::Span;
/// Lint Leo code command /// Lint Leo code command
#[derive(StructOpt, Debug, Default)] #[derive(StructOpt, Debug)]
#[structopt(setting = structopt::clap::AppSettings::ColoredHelp)] #[structopt(setting = structopt::clap::AppSettings::ColoredHelp)]
pub struct Lint {} pub struct Lint {}
impl Lint {
pub fn new() -> Lint {
Lint {}
}
}
impl Command for Lint { impl Command for Lint {
type Input = (); type Input = ();
type Output = (); type Output = ();

View File

@ -52,7 +52,7 @@ pub mod test;
pub use test::Test; pub use test::Test;
pub mod update; pub mod update;
pub use update::{Sub as UpdateAutomatic, Update}; pub use update::{Automatic as UpdateAutomatic, Update};
pub mod watch; pub mod watch;
pub use watch::Watch; pub use watch::Watch;
@ -60,57 +60,55 @@ pub use watch::Watch;
// Aleo PM related commands // Aleo PM related commands
pub mod package; 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 { pub trait Command {
/// If current command requires running another command before /// If the current command requires running another command beforehand
/// and needs its output results, this is the place to set. /// and needs its output result, this is where the result type is defined.
/// Example: type Input: <CommandA as Command>::Out /// Example: type Input: <CommandA as Command>::Out
type Input; type Input;
/// Define output of the command to be reused as an Input for another /// 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, keep empty /// command. If this command is not used as a prelude for another command,
/// this field may be left empty.
type Output; type Output;
/// Returns project context, currently keeping it simple but it is possible /// Returns the project context, which is defined as the current directory.
/// that in the future leo will not depend on current directory, and we're keeping
/// option for extending current core
fn context(&self) -> Result<Context> { fn context(&self) -> Result<Context> {
get_context() get_context()
} }
/// Add span to the logger tracing::span. /// Adds a span to the logger via `tracing::span`.
/// Due to specifics of macro implementation it is impossible to set /// Because of the specifics of the macro implementation, it is not possible
/// span name with non-literal i.e. dynamic variable even if this /// to set the span name with a non-literal i.e. a dynamic variable even if this
/// variable is &'static str /// variable is a &'static str.
fn log_span(&self) -> Span { fn log_span(&self) -> Span {
tracing::span!(tracing::Level::INFO, "Leo") tracing::span!(tracing::Level::INFO, "Leo")
} }
/// Run prelude and get Input for current command. As simple as that. /// Runs the prelude and returns the Input of the current command.
/// But due to inability to pass default implementation of a type, this
/// method must be present in every trait implementation.
fn prelude(&self) -> Result<Self::Input> fn prelude(&self) -> Result<Self::Input>
where where
Self: std::marker::Sized; Self: std::marker::Sized;
/// Core of the execution - do what is necessary. This function is run within /// Runs the main operation of this command. This function is run within
/// context of 'execute' function, which sets logging and timers /// context of 'execute' function, which sets logging and timers.
fn apply(self, ctx: Context, input: Self::Input) -> Result<Self::Output> fn apply(self, context: Context, input: Self::Input) -> Result<Self::Output>
where where
Self: std::marker::Sized; 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<Self::Output> fn execute(self) -> Result<Self::Output>
where where
Self: std::marker::Sized, Self: std::marker::Sized,
{ {
let input = self.prelude()?; let input = self.prelude()?;
// create span for this command // Create the span for this command.
let span = self.log_span(); let span = self.log_span();
let span = span.enter(); let span = span.enter();
// calculate execution time for each run // Calculate the execution time for this command.
let timer = Instant::now(); let timer = Instant::now();
let context = self.context()?; let context = self.context()?;
@ -118,7 +116,7 @@ pub trait Command {
drop(span); 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::span!(tracing::Level::INFO, "Done").in_scope(|| {
tracing::info!("Finished in {} milliseconds \n", timer.elapsed().as_millis()); tracing::info!("Finished in {} milliseconds \n", timer.elapsed().as_millis());
}); });
@ -126,7 +124,7 @@ pub trait Command {
out 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 /// need to make match arms compatible while keeping implementation-specific
/// output possible. Errors however are all of the type Error /// output possible. Errors however are all of the type Error
fn try_execute(self) -> Result<()> fn try_execute(self) -> Result<()>

View File

@ -30,12 +30,6 @@ pub struct New {
name: String, name: String,
} }
impl New {
pub fn new(name: String) -> New {
New { name }
}
}
impl Command for New { impl Command for New {
type Input = (); type Input = ();
type Output = (); type Output = ();
@ -49,13 +43,17 @@ impl Command for New {
} }
fn apply(self, _: Context, _: Self::Input) -> Result<Self::Output> { fn apply(self, _: Context, _: Self::Input) -> Result<Self::Output> {
let mut path = current_dir()?; // Check that the given package name is valid.
let package_name = self.name; 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); 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() { if path.exists() {
return Err(anyhow!("Directory already exists {:?}", path)); return Err(anyhow!("Directory already exists {:?}", path));
} }

View File

@ -90,9 +90,9 @@ impl Command for Add {
Ok(()) Ok(())
} }
fn apply(self, ctx: Context, _: Self::Input) -> Result<Self::Output> { fn apply(self, context: Context, _: Self::Input) -> Result<Self::Output> {
// checking that manifest exists... // checking that manifest exists...
if ctx.manifest().is_err() { if context.manifest().is_err() {
return Err(anyhow!("Package Manifest not found, try running leo init or leo new")); return Err(anyhow!("Package Manifest not found, try running leo init or leo new"));
}; };
@ -109,8 +109,8 @@ impl Command for Add {
version, version,
}; };
let bytes = ctx.api.run_route(fetch)?.bytes()?; let bytes = context.api.run_route(fetch)?.bytes()?;
let mut path = ctx.dir()?; let mut path = context.dir()?;
{ {
// setup directory structure since request was success // setup directory structure since request was success

View File

@ -58,14 +58,14 @@ impl Command for Login {
Ok(()) Ok(())
} }
fn apply(self, ctx: Context, _: Self::Input) -> Result<Self::Output> { fn apply(self, context: Context, _: Self::Input) -> Result<Self::Output> {
// quick hack to check if user is already logged in. ;) // quick hack to check if user is already logged in. ;)
if ctx.api.auth_token().is_some() { if context.api.auth_token().is_some() {
tracing::info!("You are already logged in"); tracing::info!("You are already logged in");
return Ok(ctx.api.auth_token().unwrap()); return Ok(context.api.auth_token().unwrap());
}; };
let mut api = ctx.api; let mut api = context.api;
// ...or trying to use arguments to either get token or user-pass // ...or trying to use arguments to either get token or user-pass
let token = match (self.token, self.user, self.pass) { let token = match (self.token, self.user, self.pass) {

View File

@ -22,16 +22,10 @@ use structopt::StructOpt;
use tracing::Span; use tracing::Span;
/// Remove credentials for Aleo PM from .leo directory /// Remove credentials for Aleo PM from .leo directory
#[derive(StructOpt, Debug, Default)] #[derive(StructOpt, Debug)]
#[structopt(setting = structopt::clap::AppSettings::ColoredHelp)] #[structopt(setting = structopt::clap::AppSettings::ColoredHelp)]
pub struct Logout {} pub struct Logout {}
impl Logout {
pub fn new() -> Logout {
Logout {}
}
}
impl Command for Logout { impl Command for Logout {
type Input = (); type Input = ();
type Output = (); type Output = ();
@ -44,7 +38,7 @@ impl Command for Logout {
Ok(()) Ok(())
} }
fn apply(self, _ctx: Context, _: Self::Input) -> Result<Self::Output> { fn apply(self, _context: Context, _: Self::Input) -> Result<Self::Output> {
// the only error we're interested here is NotFound // the only error we're interested here is NotFound
// however err in this case can also be of kind PermissionDenied or other // however err in this case can also be of kind PermissionDenied or other
if let Err(err) = remove_token() { if let Err(err) = remove_token() {

View File

@ -37,29 +37,23 @@ struct ResponseJson {
} }
/// Publish package to Aleo Package Manager /// Publish package to Aleo Package Manager
#[derive(StructOpt, Debug, Default)] #[derive(StructOpt, Debug)]
#[structopt(setting = structopt::clap::AppSettings::ColoredHelp)] #[structopt(setting = structopt::clap::AppSettings::ColoredHelp)]
pub struct Publish {} pub struct Publish {}
impl Publish {
pub fn new() -> Publish {
Publish {}
}
}
impl Command for Publish { impl Command for Publish {
type Input = <Build as Command>::Output; type Input = <Build as Command>::Output;
type Output = Option<String>; type Output = Option<String>;
/// Build program before publishing /// Build program before publishing
fn prelude(&self) -> Result<Self::Input> { fn prelude(&self) -> Result<Self::Input> {
Build::new().execute() (Build {}).execute()
} }
fn apply(self, ctx: Context, _input: Self::Input) -> Result<Self::Output> { fn apply(self, context: Context, _input: Self::Input) -> Result<Self::Output> {
// Get the package manifest // Get the package manifest
let path = ctx.dir()?; let path = context.dir()?;
let manifest = ctx.manifest()?; let manifest = context.manifest()?;
let package_name = manifest.get_package_name(); let package_name = manifest.get_package_name();
let package_version = manifest.get_package_version(); let package_version = manifest.get_package_version();
@ -99,7 +93,7 @@ impl Command for Publish {
// Client for make POST request // Client for make POST request
let client = Client::new(); let client = Client::new();
let token = match ctx.api.auth_token() { let token = match context.api.auth_token() {
Some(token) => token, Some(token) => token,
None => return Err(anyhow!("Login before publishing package: try leo login --help")), None => return Err(anyhow!("Login before publishing package: try leo login --help")),
}; };

View File

@ -22,19 +22,13 @@ use structopt::StructOpt;
use tracing::span::Span; use tracing::span::Span;
/// Remove imported package /// Remove imported package
#[derive(StructOpt, Debug, Default)] #[derive(StructOpt, Debug)]
#[structopt(setting = structopt::clap::AppSettings::ColoredHelp)] #[structopt(setting = structopt::clap::AppSettings::ColoredHelp)]
pub struct Remove { pub struct Remove {
#[structopt(name = "PACKAGE")] #[structopt(name = "PACKAGE")]
name: String, name: String,
} }
impl Remove {
pub fn new(name: String) -> Remove {
Remove { name }
}
}
impl Command for Remove { impl Command for Remove {
type Input = (); type Input = ();
type Output = (); type Output = ();
@ -47,8 +41,8 @@ impl Command for Remove {
Ok(()) Ok(())
} }
fn apply(self, ctx: Context, _: Self::Input) -> Result<Self::Output> { fn apply(self, context: Context, _: Self::Input) -> Result<Self::Output> {
let path = ctx.dir()?; let path = context.dir()?;
let package_name = self.name; let package_name = self.name;
LeoPackage::remove_imported_package(&package_name, &path)?; LeoPackage::remove_imported_package(&package_name, &path)?;

View File

@ -28,17 +28,11 @@ use structopt::StructOpt;
use tracing::span::Span; use tracing::span::Span;
/// Run the program and produce a proof /// Run the program and produce a proof
#[derive(StructOpt, Debug, Default)] #[derive(StructOpt, Debug)]
#[structopt(setting = structopt::clap::AppSettings::ColoredHelp)] #[structopt(setting = structopt::clap::AppSettings::ColoredHelp)]
pub struct Prove { pub struct Prove {
#[structopt(long = "skip-key-check", help = "Skip key verification on Setup stage")] #[structopt(long = "skip-key-check", help = "Skip key verification on Setup stage")]
skip_key_check: bool, pub(crate) skip_key_check: bool,
}
impl Prove {
pub fn new(skip_key_check: bool) -> Prove {
Prove { skip_key_check }
}
} }
impl Command for Prove { impl Command for Prove {
@ -50,15 +44,16 @@ impl Command for Prove {
} }
fn prelude(&self) -> Result<Self::Input> { fn prelude(&self) -> Result<Self::Input> {
Setup::new(self.skip_key_check).execute() let skip_key_check = self.skip_key_check;
(Setup { skip_key_check }).execute()
} }
fn apply(self, ctx: Context, input: Self::Input) -> Result<Self::Output> { fn apply(self, context: Context, input: Self::Input) -> Result<Self::Output> {
let (program, parameters, prepared_verifying_key) = input; let (program, parameters, prepared_verifying_key) = input;
// Get the package name // Get the package name
let path = ctx.dir()?; let path = context.dir()?;
let package_name = ctx.manifest()?.get_package_name(); let package_name = context.manifest()?.get_package_name();
tracing::info!("Starting..."); tracing::info!("Starting...");

View File

@ -26,17 +26,11 @@ use structopt::StructOpt;
use tracing::span::Span; use tracing::span::Span;
/// Build, Prove and Run Leo program with inputs /// Build, Prove and Run Leo program with inputs
#[derive(StructOpt, Debug, Default)] #[derive(StructOpt, Debug)]
#[structopt(setting = structopt::clap::AppSettings::ColoredHelp)] #[structopt(setting = structopt::clap::AppSettings::ColoredHelp)]
pub struct Run { pub struct Run {
#[structopt(long = "skip-key-check", help = "Skip key verification on Setup stage")] #[structopt(long = "skip-key-check", help = "Skip key verification on Setup stage")]
skip_key_check: bool, pub(crate) skip_key_check: bool,
}
impl Run {
pub fn new(skip_key_check: bool) -> Run {
Run { skip_key_check }
}
} }
impl Command for Run { impl Command for Run {
@ -48,10 +42,11 @@ impl Command for Run {
} }
fn prelude(&self) -> Result<Self::Input> { fn prelude(&self) -> Result<Self::Input> {
Prove::new(self.skip_key_check).execute() let skip_key_check = self.skip_key_check;
(Prove { skip_key_check }).execute()
} }
fn apply(self, _ctx: Context, input: Self::Input) -> Result<Self::Output> { fn apply(self, _context: Context, input: Self::Input) -> Result<Self::Output> {
let (proof, prepared_verifying_key) = input; let (proof, prepared_verifying_key) = input;
tracing::info!("Starting..."); tracing::info!("Starting...");

View File

@ -31,17 +31,11 @@ use structopt::StructOpt;
use tracing::span::Span; use tracing::span::Span;
/// Executes the setup command for a Leo program /// Executes the setup command for a Leo program
#[derive(StructOpt, Debug, Default)] #[derive(StructOpt, Debug)]
#[structopt(setting = structopt::clap::AppSettings::ColoredHelp)] #[structopt(setting = structopt::clap::AppSettings::ColoredHelp)]
pub struct Setup { pub struct Setup {
#[structopt(long = "skip-key-check", help = "Skip key verification")] #[structopt(long = "skip-key-check", help = "Skip key verification")]
skip_key_check: bool, pub(crate) skip_key_check: bool,
}
impl Setup {
pub fn new(skip_key_check: bool) -> Setup {
Setup { skip_key_check }
}
} }
impl Command for Setup { impl Command for Setup {
@ -57,12 +51,12 @@ impl Command for Setup {
} }
fn prelude(&self) -> Result<Self::Input> { fn prelude(&self) -> Result<Self::Input> {
Build::new().execute() (Build {}).execute()
} }
fn apply(self, ctx: Context, input: Self::Input) -> Result<Self::Output> { fn apply(self, context: Context, input: Self::Input) -> Result<Self::Output> {
let path = ctx.dir()?; let path = context.dir()?;
let package_name = ctx.manifest()?.get_package_name(); let package_name = context.manifest()?.get_package_name();
match input { match input {
Some((program, checksum_differs)) => { Some((program, checksum_differs)) => {
@ -70,7 +64,6 @@ impl Command for Setup {
let keys_exist = ProvingKeyFile::new(&package_name).exists_at(&path) let keys_exist = ProvingKeyFile::new(&package_name).exists_at(&path)
&& VerificationKeyFile::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 // If keys do not exist or the checksum differs, run the program setup
let (proving_key, prepared_verifying_key) = if !keys_exist || checksum_differs { let (proving_key, prepared_verifying_key) = if !keys_exist || checksum_differs {
tracing::info!("Starting..."); tracing::info!("Starting...");

View File

@ -32,17 +32,11 @@ use structopt::StructOpt;
use tracing::span::Span; use tracing::span::Span;
/// Build program and run tests command /// Build program and run tests command
#[derive(StructOpt, Debug, Default)] #[derive(StructOpt, Debug)]
#[structopt(setting = structopt::clap::AppSettings::ColoredHelp)] #[structopt(setting = structopt::clap::AppSettings::ColoredHelp)]
pub struct Test { pub struct Test {
#[structopt(short = "f", long = "file", name = "file")] #[structopt(short = "f", long = "file", name = "file")]
files: Vec<PathBuf>, pub(crate) files: Vec<PathBuf>,
}
impl Test {
pub fn new(files: Vec<PathBuf>) -> Test {
Test { files }
}
} }
impl Command for Test { impl Command for Test {
@ -57,12 +51,12 @@ impl Command for Test {
Ok(()) Ok(())
} }
fn apply(self, ctx: Context, _: Self::Input) -> Result<Self::Output> { fn apply(self, context: Context, _: Self::Input) -> Result<Self::Output> {
// Get the package name // Get the package name
let package_name = ctx.manifest()?.get_package_name(); let package_name = context.manifest()?.get_package_name();
// Sanitize the package path to the root directory // Sanitize the package path to the root directory
let mut package_path = ctx.dir()?; let mut package_path = context.dir()?;
if package_path.is_file() { if package_path.is_file() {
package_path.pop(); package_path.pop();
} }

View File

@ -22,7 +22,7 @@ use tracing::span::Span;
/// Setting for automatic updates of Leo /// Setting for automatic updates of Leo
#[derive(Debug, StructOpt, PartialEq)] #[derive(Debug, StructOpt, PartialEq)]
pub enum Sub { pub enum Automatic {
Automatic { Automatic {
#[structopt(name = "bool", help = "Boolean value: true or false", parse(try_from_str))] #[structopt(name = "bool", help = "Boolean value: true or false", parse(try_from_str))]
value: bool, value: bool,
@ -30,30 +30,20 @@ pub enum Sub {
} }
/// Update Leo to the latest version /// Update Leo to the latest version
#[derive(StructOpt, Debug, Default)] #[derive(StructOpt, Debug)]
#[structopt(setting = structopt::clap::AppSettings::ColoredHelp)] #[structopt(setting = structopt::clap::AppSettings::ColoredHelp)]
pub struct Update { pub struct Update {
/// List all available versions of Leo /// List all available versions of Leo
#[structopt(short, long)] #[structopt(short, long)]
list: bool, pub(crate) list: bool,
/// For Aleo Studio only /// For Aleo Studio only
#[structopt(short, long)] #[structopt(short, long)]
studio: bool, pub(crate) studio: bool,
/// Setting for automatic updates of Leo /// Setting for automatic updates of Leo
#[structopt(subcommand)] #[structopt(subcommand)]
automatic: Option<Sub>, pub(crate) automatic: Option<Automatic>,
}
impl Update {
pub fn new(list: bool, studio: bool, automatic: Option<Sub>) -> Update {
Update {
list,
studio,
automatic,
}
}
} }
impl Command for Update { impl Command for Update {
@ -69,13 +59,13 @@ impl Command for Update {
} }
fn apply(self, _: Context, _: Self::Input) -> Result<Self::Output> { fn apply(self, _: Context, _: Self::Input) -> Result<Self::Output> {
// if --list is passed - simply list everything and exit // If --list is passed, list all available versions and return.
if self.list { if self.list {
return Updater::show_available_releases().map_err(|e| anyhow!("Could not fetch versions: {}", e)); return Updater::show_available_releases().map_err(|e| anyhow!("Could not fetch versions: {}", e));
} }
// in case automatic subcommand was called // Handles enabling and disabling automatic updates in the config file.
if let Some(Sub::Automatic { value }) = self.automatic { if let Some(Automatic::Automatic { value }) = self.automatic {
Config::set_update_automatic(value)?; Config::set_update_automatic(value)?;
match value { match value {

View File

@ -27,7 +27,7 @@ use tracing::span::Span;
const LEO_SOURCE_DIR: &str = "src/"; const LEO_SOURCE_DIR: &str = "src/";
/// Watch file changes in src/ directory and run Build Command /// Watch file changes in src/ directory and run Build Command
#[derive(StructOpt, Debug, Default)] #[derive(StructOpt, Debug)]
#[structopt(setting = structopt::clap::AppSettings::ColoredHelp)] #[structopt(setting = structopt::clap::AppSettings::ColoredHelp)]
pub struct Watch { pub struct Watch {
/// Set up watch interval /// Set up watch interval
@ -35,12 +35,6 @@ pub struct Watch {
interval: u64, interval: u64,
} }
impl Watch {
pub fn new(interval: u64) -> Watch {
Watch { interval }
}
}
impl Command for Watch { impl Command for Watch {
type Input = (); type Input = ();
type Output = (); type Output = ();
@ -53,7 +47,7 @@ impl Command for Watch {
Ok(()) Ok(())
} }
fn apply(self, _ctx: Context, _: Self::Input) -> Result<Self::Output> { fn apply(self, _context: Context, _: Self::Input) -> Result<Self::Output> {
let (tx, rx) = channel(); let (tx, rx) = channel();
let mut watcher = watcher(tx, Duration::from_secs(self.interval)).unwrap(); let mut watcher = watcher(tx, Duration::from_secs(self.interval)).unwrap();
@ -70,7 +64,7 @@ impl Command for Watch {
match rx.recv() { match rx.recv() {
// See changes on the write event // See changes on the write event
Ok(DebouncedEvent::Write(_write)) => { Ok(DebouncedEvent::Write(_write)) => {
match Build::new().execute() { match (Build {}).execute() {
Ok(_output) => { Ok(_output) => {
tracing::info!("Built successfully"); tracing::info!("Built successfully");
} }

View File

@ -158,7 +158,12 @@ where
N: for<'a> FormatFields<'a> + 'static, N: for<'a> FormatFields<'a> + 'static,
T: FormatTime, T: FormatTime,
{ {
fn format_event(&self, ctx: &FmtContext<'_, S, N>, writer: &mut dyn fmt::Write, event: &Event<'_>) -> fmt::Result { fn format_event(
&self,
context: &FmtContext<'_, S, N>,
writer: &mut dyn fmt::Write,
event: &Event<'_>,
) -> fmt::Result {
let meta = event.metadata(); let meta = event.metadata();
if self.display_level { if self.display_level {
@ -174,7 +179,7 @@ where
let mut message = "".to_string(); let mut message = "".to_string();
let scope = ctx.scope(); let scope = context.scope();
for span in scope { for span in scope {
message += span.metadata().name(); message += span.metadata().name();
@ -190,7 +195,7 @@ where
write!(writer, "{:>10} ", colored_string(meta.level(), &message)).expect("Error writing event"); write!(writer, "{:>10} ", colored_string(meta.level(), &message)).expect("Error writing event");
} }
ctx.format_fields(writer, event)?; context.format_fields(writer, event)?;
writeln!(writer) writeln!(writer)
} }
} }

View File

@ -39,34 +39,34 @@ const PEDERSEN_HASH_PATH: &str = "./examples/pedersen-hash/";
#[test] #[test]
pub fn build_pedersen_hash() -> Result<()> { pub fn build_pedersen_hash() -> Result<()> {
Build::new().apply(ctx()?, ())?; (Build {}).apply(context()?, ())?;
Ok(()) Ok(())
} }
#[test] #[test]
pub fn setup_pedersen_hash() -> Result<()> { pub fn setup_pedersen_hash() -> Result<()> {
let build = Build::new().apply(ctx()?, ())?; let build = (Build {}).apply(context()?, ())?;
Setup::new(false).apply(ctx()?, build.clone())?; (Setup { skip_key_check: false }).apply(context()?, build.clone())?;
Setup::new(true).apply(ctx()?, build)?; (Setup { skip_key_check: true }).apply(context()?, build)?;
Ok(()) Ok(())
} }
#[test] #[test]
pub fn prove_pedersen_hash() -> Result<()> { pub fn prove_pedersen_hash() -> Result<()> {
let build = Build::new().apply(ctx()?, ())?; let build = (Build {}).apply(context()?, ())?;
let setup = Setup::new(false).apply(ctx()?, build)?; let setup = (Setup { skip_key_check: false }).apply(context()?, build)?;
Prove::new(false).apply(ctx()?, setup.clone())?; (Prove { skip_key_check: false }).apply(context()?, setup.clone())?;
Prove::new(true).apply(ctx()?, setup)?; (Prove { skip_key_check: true }).apply(context()?, setup)?;
Ok(()) Ok(())
} }
#[test] #[test]
pub fn run_pedersen_hash() -> Result<()> { pub fn run_pedersen_hash() -> Result<()> {
let build = Build::new().apply(ctx()?, ())?; let build = (Build {}).apply(context()?, ())?;
let setup = Setup::new(false).apply(ctx()?, build)?; let setup = (Setup { skip_key_check: false }).apply(context()?, build)?;
let prove = Prove::new(false).apply(ctx()?, setup)?; let prove = (Prove { skip_key_check: false }).apply(context()?, setup)?;
Run::new(false).apply(ctx()?, prove.clone())?; (Run { skip_key_check: false }).apply(context()?, prove.clone())?;
Run::new(true).apply(ctx()?, prove)?; (Run { skip_key_check: true }).apply(context()?, prove)?;
Ok(()) Ok(())
} }
@ -75,14 +75,14 @@ pub fn test_pedersen_hash() -> Result<()> {
let mut main_file = PathBuf::from(PEDERSEN_HASH_PATH); let mut main_file = PathBuf::from(PEDERSEN_HASH_PATH);
main_file.push("src/main.leo"); main_file.push("src/main.leo");
Test::new(Vec::new()).apply(ctx()?, ())?; (Test { files: vec![] }).apply(context()?, ())?;
Test::new(vec![main_file]).apply(ctx()?, ())?; (Test { files: vec![main_file] }).apply(context()?, ())?;
Ok(()) Ok(())
} }
#[test] #[test]
pub fn test_logout() -> Result<()> { pub fn test_logout() -> Result<()> {
Logout::new().apply(ctx()?, ())?; (Logout {}).apply(context()?, ())?;
Ok(()) Ok(())
} }
@ -91,19 +91,19 @@ pub fn test_logout() -> Result<()> {
#[test] #[test]
pub fn login_incorrect_credentials_or_token() -> Result<()> { pub fn login_incorrect_credentials_or_token() -> Result<()> {
// no credentials passed // no credentials passed
let login = Login::new(None, None, None).apply(ctx()?, ()); let login = Login::new(None, None, None).apply(context()?, ());
assert!(login.is_err()); assert!(login.is_err());
// incorrect token // incorrect token
let login = Login::new(Some("none".to_string()), None, None).apply(ctx()?, ()); let login = Login::new(Some("none".to_string()), None, None).apply(context()?, ());
assert!(login.is_err()); assert!(login.is_err());
// only user, no pass // only user, no pass
let login = Login::new(None, Some("user".to_string()), None).apply(ctx()?, ()); let login = Login::new(None, Some("user".to_string()), None).apply(context()?, ());
assert!(login.is_err()); assert!(login.is_err());
// no user, only pass // no user, only pass
let login = Login::new(None, None, Some("pass".to_string())).apply(ctx()?, ()); let login = Login::new(None, None, Some("pass".to_string())).apply(context()?, ());
assert!(login.is_err()); assert!(login.is_err());
Ok(()) Ok(())
@ -111,20 +111,48 @@ pub fn login_incorrect_credentials_or_token() -> Result<()> {
#[test] #[test]
pub fn leo_update_and_update_automatic() -> Result<()> { pub fn leo_update_and_update_automatic() -> Result<()> {
Update::new(true, true, None).apply(ctx()?, ())?; let update = Update {
Update::new(false, true, None).apply(ctx()?, ())?; list: true,
Update::new(false, false, None).apply(ctx()?, ())?; studio: true,
automatic: None,
};
update.apply(context()?, ())?;
Update::new(false, false, Some(UpdateAutomatic::Automatic { value: true })).apply(ctx()?, ())?; let update = Update {
Update::new(false, false, Some(UpdateAutomatic::Automatic { value: false })).apply(ctx()?, ())?; 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(()) Ok(())
} }
/// Create context for Pedersen Hash example /// Create context for Pedersen Hash example
fn ctx() -> Result<Context> { fn context() -> Result<Context> {
let path = PathBuf::from(&PEDERSEN_HASH_PATH); let path = PathBuf::from(&PEDERSEN_HASH_PATH);
let ctx = create_context(path)?; let context = create_context(path)?;
Ok(ctx) Ok(context)
} }

View File

@ -26,6 +26,9 @@ pub enum PackageError {
#[error("Failed to initialize package {:?} ({:?})", _0, _1)] #[error("Failed to initialize package {:?} ({:?})", _0, _1)]
FailedToInitialize(String, OsString), FailedToInitialize(String, OsString),
#[error("Invalid project name: {:?}", _0)]
InvalidPackageName(String),
#[error("`{}` metadata: {}", _0, _1)] #[error("`{}` metadata: {}", _0, _1)]
Removing(&'static str, io::Error), Removing(&'static str, io::Error),
} }

View File

@ -18,6 +18,9 @@ use std::io;
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum ManifestError { pub enum ManifestError {
#[error("{}: {}", _0, _1)]
Crate(&'static str, String),
#[error("`{}` creating: {}", _0, _1)] #[error("`{}` creating: {}", _0, _1)]
Creating(&'static str, io::Error), Creating(&'static str, io::Error),
@ -36,3 +39,9 @@ pub enum ManifestError {
#[error("`{}` writing: {}", _0, _1)] #[error("`{}` writing: {}", _0, _1)]
Writing(&'static str, io::Error), Writing(&'static str, io::Error),
} }
impl From<crate::errors::PackageError> for ManifestError {
fn from(error: crate::errors::PackageError) -> Self {
ManifestError::Crate("leo-package", error.to_string())
}
}

View File

@ -37,6 +37,11 @@ impl LeoPackage {
package::Package::initialize(package_name, is_lib, path) 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 /// Removes an imported Leo package
pub fn remove_imported_package(package_name: &str, path: &Path) -> Result<(), PackageError> { pub fn remove_imported_package(package_name: &str, path: &Path) -> Result<(), PackageError> {
package::Package::remove_imported_package(package_name, path) package::Package::remove_imported_package(package_name, path)

View File

@ -34,17 +34,79 @@ pub struct Package {
} }
impl Package { impl Package {
pub fn new(package_name: &str) -> Self { pub fn new(package_name: &str) -> Result<Self, PackageError> {
Self { // 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(), name: package_name.to_owned(),
version: "0.1.0".to_owned(), version: "0.1.0".to_owned(),
description: None, description: None,
license: 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. /// 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 { 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 result = true;
let mut existing_files = vec![]; let mut existing_files = vec![];
@ -91,6 +153,11 @@ impl Package {
/// Returns `true` if a package is initialized at the given path /// Returns `true` if a package is initialized at the given path
pub fn is_initialized(package_name: &str, is_lib: bool, path: &Path) -> bool { 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. // Check if the manifest file exists.
if !Manifest::exists_at(&path) { if !Manifest::exists_at(&path) {
return false; return false;
@ -137,7 +204,7 @@ impl Package {
// Next, initialize this directory as a Leo package. // Next, initialize this directory as a Leo package.
{ {
// Create the manifest file. // 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. // Verify that the .gitignore file does not exist.
if !Gitignore::exists_at(&path) { if !Gitignore::exists_at(&path) {
@ -190,3 +257,27 @@ impl Package {
Ok(ImportsDirectory::remove_import(path, package_name)?) 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"));
}
}

View File

@ -39,11 +39,11 @@ pub struct Manifest {
} }
impl Manifest { impl Manifest {
pub fn new(package_name: &str) -> Self { pub fn new(package_name: &str) -> Result<Self, ManifestError> {
Self { Ok(Self {
project: Package::new(package_name), project: Package::new(package_name)?,
remote: None, remote: None,
} })
} }
pub fn filename() -> String { pub fn filename() -> String {

View File

@ -52,7 +52,10 @@ fn initialize_fails_with_existing_manifest() {
assert!(Package::can_initialize(TEST_PACKAGE_NAME, false, &test_directory)); assert!(Package::can_initialize(TEST_PACKAGE_NAME, false, &test_directory));
// Manually add a manifest file to the `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` // Attempt to initialize a package at the `test_directory`
assert!(Package::initialize(TEST_PACKAGE_NAME, false, &test_directory).is_err()); assert!(Package::initialize(TEST_PACKAGE_NAME, false, &test_directory).is_err());