diff --git a/.github/workflows/leo.yml b/.github/workflows/release-tests.yml similarity index 86% rename from .github/workflows/leo.yml rename to .github/workflows/release-tests.yml index 750e79d6d8..f97ee3384e 100644 --- a/.github/workflows/leo.yml +++ b/.github/workflows/release-tests.yml @@ -1,4 +1,4 @@ -name: Leo Programs +name: Leo Release Tests on: pull_request: push: @@ -9,7 +9,7 @@ env: jobs: new: - name: Hello Leo (from 'leo new') + name: Hello Leo ('leo new hello-world') runs-on: ubuntu-latest steps: - name: Checkout @@ -39,16 +39,16 @@ jobs: command: install args: --path . - - name: 'leo new' + - name: 'leo new hello-world' run: | cd .. - leo new hello_world + leo new hello-world ls -la - cd hello_world && ls -la + cd hello-world && ls -la leo run init: - name: Hello Leo (from 'leo init') + name: Hello Leo ('leo init') runs-on: ubuntu-latest steps: - name: Checkout @@ -80,7 +80,7 @@ jobs: - name: 'leo init' run: | - cd .. && mkdir hello_world && cd hello_world + cd .. && mkdir hello-world && cd hello-world leo init ls -la leo run diff --git a/Cargo.lock b/Cargo.lock index 859062f9eb..c18c6695ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1326,6 +1326,7 @@ version = "1.0.2" name = "leo-package" version = "1.0.2" dependencies = [ + "lazy_static", "serde", "serde_json", "thiserror", diff --git a/leo/commands/build.rs b/leo/commands/build.rs index 429f3463a4..888003d8ce 100644 --- a/leo/commands/build.rs +++ b/leo/commands/build.rs @@ -25,7 +25,7 @@ use leo_package::{ inputs::*, outputs::{ChecksumFile, CircuitFile, OutputsDirectory, OUTPUTS_DIRECTORY_NAME}, root::Manifest, - source::{LibFile, MainFile, LIB_FILE_NAME, MAIN_FILE_NAME, SOURCE_DIRECTORY_NAME}, + source::{LibraryFile, MainFile, LIBRARY_FILENAME, MAIN_FILENAME, SOURCE_DIRECTORY_NAME}, }; use snarkos_curves::{bls12_377::Bls12_377, edwards_bls12::Fq}; @@ -81,11 +81,11 @@ impl CLI for BuildCommand { let start = Instant::now(); // Compile the package starting with the lib.leo file - if LibFile::exists_at(&package_path) { + if LibraryFile::exists_at(&package_path) { // Construct the path to the library file in the source directory let mut lib_file_path = package_path.clone(); lib_file_path.push(SOURCE_DIRECTORY_NAME); - lib_file_path.push(LIB_FILE_NAME); + lib_file_path.push(LIBRARY_FILENAME); // Log compilation of library file to console tracing::info!("Compiling library... ({:?})", lib_file_path); @@ -107,7 +107,7 @@ impl CLI for BuildCommand { // Construct the path to the main file in the source directory let mut main_file_path = package_path.clone(); main_file_path.push(SOURCE_DIRECTORY_NAME); - main_file_path.push(MAIN_FILE_NAME); + main_file_path.push(MAIN_FILENAME); // Load the input file at `package_name.in` let input_string = InputFile::new(&package_name).read_from(&path)?; diff --git a/leo/commands/deploy.rs b/leo/commands/deploy.rs index 44f73b0ef9..a04b8d9c33 100644 --- a/leo/commands/deploy.rs +++ b/leo/commands/deploy.rs @@ -22,7 +22,7 @@ use crate::{ }; use leo_package::{ root::Manifest, - source::{MAIN_FILE_NAME, SOURCE_DIRECTORY_NAME}, + source::{MAIN_FILENAME, SOURCE_DIRECTORY_NAME}, }; use clap::ArgMatches; @@ -67,7 +67,7 @@ impl CLI for DeployCommand { None => { let mut main_file_path = path.clone(); main_file_path.push(SOURCE_DIRECTORY_NAME); - main_file_path.push(MAIN_FILE_NAME); + main_file_path.push(MAIN_FILENAME); Err(CLIError::RunError(RunError::MainFileDoesNotExist( main_file_path.into_os_string(), diff --git a/leo/commands/init.rs b/leo/commands/init.rs index c5b2f43791..8f38690aa5 100644 --- a/leo/commands/init.rs +++ b/leo/commands/init.rs @@ -19,11 +19,7 @@ use crate::{ cli_types::*, errors::{CLIError, InitError}, }; -use leo_package::{ - inputs::*, - root::{Gitignore, Manifest, README}, - source::{LibFile, MainFile, SourceDirectory}, -}; +use leo_package::LeoPackage; use clap::ArgMatches; use std::env::current_dir; @@ -62,60 +58,12 @@ impl CLI for InitCommand { .to_string_lossy() .to_string(); - // Verify the directory exists + // Verify the directory does not exist if !path.exists() { return Err(InitError::DirectoryDoesNotExist(path.as_os_str().to_owned()).into()); } - // Verify a manifest file does not already exist - if Manifest::exists_at(&path) { - return Err(InitError::PackageAlreadyExists(path.as_os_str().to_owned()).into()); - } - - // Create the manifest file - Manifest::new(&package_name).write_to(&path)?; - - // Create the .gitignore file - Gitignore::new().write_to(&path)?; - - // Create the README.md file - README::new(&package_name).write_to(&path)?; - - // Create the source directory - SourceDirectory::create(&path)?; - - // Create a new library or binary file - - if options { - // Verify the library file does not exist - if !LibFile::exists_at(&path) { - // Create the library file in the source directory - LibFile::new(&package_name).write_to(&path)?; - } - } else { - // Create the input directory - InputsDirectory::create(&path)?; - - // Verify the input file does not exist - let input_file = InputFile::new(&package_name); - if !input_file.exists_at(&path) { - // Create the input file in the inputs directory - input_file.write_to(&path)?; - } - - // Verify the state file does not exist - let state_file = StateFile::new(&package_name); - if !state_file.exists_at(&path) { - // Create the state file in the inputs directory - state_file.write_to(&path)?; - } - - // Verify the main file does not exist - if !MainFile::exists_at(&path) { - // Create the main file in the source directory - MainFile::new(&package_name).write_to(&path)?; - } - } + LeoPackage::initialize(&package_name, options, &path)?; tracing::info!("Successfully initialized package \"{}\"\n", package_name); diff --git a/leo/commands/lint.rs b/leo/commands/lint.rs index 2c78f22cc1..4487f79b9f 100644 --- a/leo/commands/lint.rs +++ b/leo/commands/lint.rs @@ -22,7 +22,7 @@ use crate::{ }; use leo_package::{ root::Manifest, - source::{MAIN_FILE_NAME, SOURCE_DIRECTORY_NAME}, + source::{MAIN_FILENAME, SOURCE_DIRECTORY_NAME}, }; use clap::ArgMatches; @@ -67,7 +67,7 @@ impl CLI for LintCommand { None => { let mut main_file_path = path.clone(); main_file_path.push(SOURCE_DIRECTORY_NAME); - main_file_path.push(MAIN_FILE_NAME); + main_file_path.push(MAIN_FILENAME); Err(CLIError::RunError(RunError::MainFileDoesNotExist( main_file_path.into_os_string(), diff --git a/leo/commands/new.rs b/leo/commands/new.rs index 74e3d52251..d210ff6ebd 100644 --- a/leo/commands/new.rs +++ b/leo/commands/new.rs @@ -19,11 +19,7 @@ use crate::{ cli_types::*, errors::{CLIError, NewError}, }; -use leo_package::{ - inputs::*, - root::{Gitignore, Manifest, README}, - source::{LibFile, MainFile, SourceDirectory}, -}; +use leo_package::LeoPackage; use clap::ArgMatches; use std::{env::current_dir, fs}; @@ -89,35 +85,7 @@ impl CLI for NewCommand { fs::create_dir_all(&path) .map_err(|error| NewError::CreatingRootDirectory(path.as_os_str().to_owned(), error))?; - // Create the manifest file - Manifest::new(&package_name).write_to(&path)?; - - // Create the .gitignore file - Gitignore::new().write_to(&path)?; - - // Create the README.md file - README::new(&package_name).write_to(&path)?; - - // Create the source directory - SourceDirectory::create(&path)?; - - // Create a new library or binary file - if options.1 { - // Create the library file in the source directory - LibFile::new(&package_name).write_to(&path)?; - } else { - // Create the input directory - InputsDirectory::create(&path)?; - - // Create the input file in the inputs directory - InputFile::new(&package_name).write_to(&path)?; - - // Create the state file in the inputs directory - StateFile::new(&package_name).write_to(&path)?; - - // Create the main file in the source directory - MainFile::new(&package_name).write_to(&path)?; - } + LeoPackage::initialize(&package_name, options.1, &path)?; tracing::info!("Successfully initialized package \"{}\"\n", package_name); diff --git a/leo/commands/remove.rs b/leo/commands/remove.rs index 46e1ac5687..456b64f909 100644 --- a/leo/commands/remove.rs +++ b/leo/commands/remove.rs @@ -22,7 +22,7 @@ use crate::{ }; use leo_package::{ root::Manifest, - source::{MAIN_FILE_NAME, SOURCE_DIRECTORY_NAME}, + source::{MAIN_FILENAME, SOURCE_DIRECTORY_NAME}, }; use clap::ArgMatches; @@ -67,7 +67,7 @@ impl CLI for RemoveCommand { None => { let mut main_file_path = path.clone(); main_file_path.push(SOURCE_DIRECTORY_NAME); - main_file_path.push(MAIN_FILE_NAME); + main_file_path.push(MAIN_FILENAME); Err(CLIError::RunError(RunError::MainFileDoesNotExist( main_file_path.into_os_string(), diff --git a/leo/commands/setup.rs b/leo/commands/setup.rs index 80247a79f4..c1afbd4127 100644 --- a/leo/commands/setup.rs +++ b/leo/commands/setup.rs @@ -24,7 +24,7 @@ use leo_compiler::{compiler::Compiler, group::targets::edwards_bls12::EdwardsGro use leo_package::{ outputs::{ProvingKeyFile, VerificationKeyFile}, root::Manifest, - source::{MAIN_FILE_NAME, SOURCE_DIRECTORY_NAME}, + source::{MAIN_FILENAME, SOURCE_DIRECTORY_NAME}, }; use snarkos_algorithms::snark::groth16::{Groth16, Parameters, PreparedVerifyingKey, VerifyingKey}; @@ -148,7 +148,7 @@ impl CLI for SetupCommand { None => { let mut main_file_path = path.clone(); main_file_path.push(SOURCE_DIRECTORY_NAME); - main_file_path.push(MAIN_FILE_NAME); + main_file_path.push(MAIN_FILENAME); Err(CLIError::RunError(RunError::MainFileDoesNotExist( main_file_path.into_os_string(), diff --git a/leo/commands/test.rs b/leo/commands/test.rs index a069b38d07..91e8dbe6dc 100644 --- a/leo/commands/test.rs +++ b/leo/commands/test.rs @@ -24,7 +24,7 @@ use leo_package::{ inputs::*, outputs::{OutputsDirectory, OUTPUTS_DIRECTORY_NAME}, root::Manifest, - source::{LibFile, MainFile, LIB_FILE_NAME, MAIN_FILE_NAME, SOURCE_DIRECTORY_NAME}, + source::{LibraryFile, MainFile, LIBRARY_FILENAME, MAIN_FILENAME, SOURCE_DIRECTORY_NAME}, }; use snarkos_curves::edwards_bls12::Fq; @@ -70,9 +70,9 @@ impl CLI for TestCommand { // Verify a main or library file exists if MainFile::exists_at(&package_path) { - file_path.push(MAIN_FILE_NAME); - } else if LibFile::exists_at(&package_path) { - file_path.push(LIB_FILE_NAME); + file_path.push(MAIN_FILENAME); + } else if LibraryFile::exists_at(&package_path) { + file_path.push(LIBRARY_FILENAME); } else { return Err(ProgramFileDoesNotExist(package_path.into()).into()); } diff --git a/leo/errors/cli.rs b/leo/errors/cli.rs index 6a2c3b4f40..59cd13599f 100644 --- a/leo/errors/cli.rs +++ b/leo/errors/cli.rs @@ -54,7 +54,7 @@ pub enum CLIError { InputFileError(InputFileError), #[error("{}", _0)] - LibFileError(LibFileError), + LibraryFileError(LibraryFileError), #[error("{}", _0)] LoginError(LoginError), @@ -74,6 +74,9 @@ pub enum CLIError { #[error("{}", _0)] OutputsDirectoryError(OutputsDirectoryError), + #[error("{}", _0)] + PackageError(PackageError), + #[error("{}", _0)] ProofFileError(ProofFileError), @@ -133,13 +136,14 @@ impl_cli_error!( InitError, InputsDirectoryError, InputFileError, - LibFileError, + LibraryFileError, LoginError, MainFileError, ManifestError, NewError, OutputFileError, OutputsDirectoryError, + PackageError, ProofFileError, ProvingKeyFileError, PublishError, @@ -162,14 +166,14 @@ impl From for CLIError { impl From for CLIError { fn from(error: leo_compiler::errors::CompilerError) -> Self { tracing::error!("{}\n", error); - CLIError::Crate("leo_compiler", "Program failed due to previous error".into()) + CLIError::Crate("leo-compiler", "Program failed due to previous error".into()) } } impl From for CLIError { fn from(error: leo_input::errors::InputParserError) -> Self { tracing::error!("{}\n", error); - CLIError::Crate("leo_input", "Program failed due to previous error".into()) + CLIError::Crate("leo-input", "Program failed due to previous error".into()) } } diff --git a/leo/errors/commands/init.rs b/leo/errors/commands/init.rs index 95552cb02e..ff9042444e 100644 --- a/leo/errors/commands/init.rs +++ b/leo/errors/commands/init.rs @@ -29,9 +29,6 @@ pub enum InitError { #[error("{}", _0)] ManifestError(#[from] ManifestError), - #[error("package at path {:?} already exists", _0)] - PackageAlreadyExists(OsString), - #[error("package name is missing - {:?}", _0)] ProjectNameInvalid(OsString), } diff --git a/leo/errors/commands/new.rs b/leo/errors/commands/new.rs index fd816f75db..ffa4586c4d 100644 --- a/leo/errors/commands/new.rs +++ b/leo/errors/commands/new.rs @@ -29,9 +29,6 @@ pub enum NewError { #[error("{}", _0)] ManifestError(#[from] ManifestError), - #[error("package at path {:?} already exists", _0)] - PackageAlreadyExists(OsString), - #[error("package name is missing - {:?}", _0)] ProjectNameInvalid(OsString), } diff --git a/package/Cargo.toml b/package/Cargo.toml index 14afff3175..3bb2a74878 100644 --- a/package/Cargo.toml +++ b/package/Cargo.toml @@ -38,3 +38,6 @@ version = "2" [dependencies.zip] version = "0.5" + +[dev-dependencies.lazy_static] +version = "1.3.0" \ No newline at end of file diff --git a/package/src/errors/mod.rs b/package/src/errors/mod.rs index 84b6620f73..7f09f85707 100644 --- a/package/src/errors/mod.rs +++ b/package/src/errors/mod.rs @@ -23,6 +23,9 @@ pub use inputs::*; pub mod outputs; pub use outputs::*; +pub mod package; +pub use self::package::*; + pub mod root; pub use self::root::*; diff --git a/package/src/errors/package.rs b/package/src/errors/package.rs new file mode 100644 index 0000000000..b1a86d6419 --- /dev/null +++ b/package/src/errors/package.rs @@ -0,0 +1,88 @@ +use std::{ffi::OsString, io}; + +#[derive(Debug, Error)] +pub enum PackageError { + #[error("{}: {}", _0, _1)] + Crate(&'static str, String), + + #[error("`{}` creating: {}", _0, _1)] + Creating(&'static str, io::Error), + + #[error("Failed to initialize package {:?} ({:?})", _0, _1)] + FailedToInitialize(String, OsString), + + #[error("`{}` metadata: {}", _0, _1)] + Removing(&'static str, io::Error), +} + +impl From for PackageError { + fn from(error: std::io::Error) -> Self { + PackageError::Crate("std::io", format!("{}", error)) + } +} + +impl From for PackageError { + fn from(error: crate::errors::GitignoreError) -> Self { + PackageError::Crate("leo-package", format!("{}", error)) + } +} + +impl From for PackageError { + fn from(error: crate::errors::InputFileError) -> Self { + PackageError::Crate("leo-package", format!("{}", error)) + } +} + +impl From for PackageError { + fn from(error: crate::errors::InputsDirectoryError) -> Self { + PackageError::Crate("leo-package", format!("{}", error)) + } +} + +impl From for PackageError { + fn from(error: crate::errors::ImportsDirectoryError) -> Self { + PackageError::Crate("leo-package", format!("{}", error)) + } +} + +impl From for PackageError { + fn from(error: crate::errors::OutputsDirectoryError) -> Self { + PackageError::Crate("leo-package", format!("{}", error)) + } +} + +impl From for PackageError { + fn from(error: crate::errors::READMEError) -> Self { + PackageError::Crate("leo-package", format!("{}", error)) + } +} + +impl From for PackageError { + fn from(error: crate::errors::SourceDirectoryError) -> Self { + PackageError::Crate("leo-package", format!("{}", error)) + } +} + +impl From for PackageError { + fn from(error: crate::errors::StateFileError) -> Self { + PackageError::Crate("leo-package", format!("{}", error)) + } +} + +impl From for PackageError { + fn from(error: crate::errors::LibraryFileError) -> Self { + PackageError::Crate("leo-package", format!("{}", error)) + } +} + +impl From for PackageError { + fn from(error: crate::errors::ManifestError) -> Self { + PackageError::Crate("leo-package", format!("{}", error)) + } +} + +impl From for PackageError { + fn from(error: crate::errors::MainFileError) -> Self { + PackageError::Crate("leo-package", format!("{}", error)) + } +} diff --git a/package/src/errors/source/lib.rs b/package/src/errors/source/lib.rs index 2560ae4d8e..ee76aa2646 100644 --- a/package/src/errors/source/lib.rs +++ b/package/src/errors/source/lib.rs @@ -17,7 +17,7 @@ use std::io; #[derive(Debug, Error)] -pub enum LibFileError { +pub enum LibraryFileError { #[error("{}: {}", _0, _1)] Crate(&'static str, String), @@ -28,8 +28,8 @@ pub enum LibFileError { Writing(io::Error), } -impl From for LibFileError { +impl From for LibraryFileError { fn from(error: std::io::Error) -> Self { - LibFileError::Crate("std::io", format!("{}", error)) + LibraryFileError::Crate("std::io", format!("{}", error)) } } diff --git a/package/src/inputs/input.rs b/package/src/inputs/input.rs index 06415b0e51..c7c91266ae 100644 --- a/package/src/inputs/input.rs +++ b/package/src/inputs/input.rs @@ -39,6 +39,10 @@ impl InputFile { } } + pub fn filename(&self) -> String { + format!("{}{}{}", INPUTS_DIRECTORY_NAME, self.package_name, INPUT_FILE_EXTENSION) + } + pub fn exists_at(&self, path: &PathBuf) -> bool { let path = self.setup_file_path(path); path.exists() diff --git a/package/src/inputs/state.rs b/package/src/inputs/state.rs index df1a1fed17..a8c2043f5a 100644 --- a/package/src/inputs/state.rs +++ b/package/src/inputs/state.rs @@ -39,6 +39,10 @@ impl StateFile { } } + pub fn filename(&self) -> String { + format!("{}{}{}", INPUTS_DIRECTORY_NAME, self.package_name, STATE_FILE_EXTENSION) + } + pub fn exists_at(&self, path: &PathBuf) -> bool { let path = self.setup_file_path(path); path.exists() diff --git a/package/src/lib.rs b/package/src/lib.rs index 01b7a4d290..0b2909f31a 100644 --- a/package/src/lib.rs +++ b/package/src/lib.rs @@ -23,5 +23,17 @@ pub use errors::*; pub mod imports; pub mod inputs; pub mod outputs; +pub mod package; pub mod root; pub mod source; + +use std::path::PathBuf; + +pub struct LeoPackage; + +impl LeoPackage { + /// Initializes a Leo package at the given path. + pub fn initialize(package_name: &str, is_lib: bool, path: &PathBuf) -> Result<(), PackageError> { + package::Package::initialize(package_name, is_lib, path) + } +} diff --git a/package/src/package.rs b/package/src/package.rs new file mode 100644 index 0000000000..595d81f860 --- /dev/null +++ b/package/src/package.rs @@ -0,0 +1,173 @@ +use crate::{ + errors::PackageError, + inputs::{InputFile, InputsDirectory, StateFile}, + root::{Gitignore, Manifest, README}, + source::{LibraryFile, MainFile, SourceDirectory}, +}; + +use serde::Deserialize; +use std::path::PathBuf; + +#[derive(Deserialize)] +pub struct Package { + pub name: String, + pub version: String, + pub description: Option, + pub license: Option, +} + +impl Package { + pub fn new(package_name: &str) -> Self { + Self { + name: package_name.to_owned(), + version: "0.1.0".to_owned(), + description: None, + license: None, + } + } + + /// Returns `true` if a package is can be initialized at a given path. + pub fn can_initialize(package_name: &str, is_lib: bool, path: &PathBuf) -> bool { + let mut result = true; + let mut existing_files = vec![]; + + // Check if the manifest file already exists. + if Manifest::exists_at(&path) { + existing_files.push(Manifest::filename()); + result = false; + } + + if is_lib { + // Check if the library file already exists. + if LibraryFile::exists_at(&path) { + existing_files.push(LibraryFile::filename()); + result = false; + } + } else { + // Check if the input file already exists. + let input_file = InputFile::new(&package_name); + if input_file.exists_at(&path) { + existing_files.push(input_file.filename()); + result = false; + } + + // Check if the state file already exists. + let state_file = StateFile::new(&package_name); + if state_file.exists_at(&path) { + existing_files.push(state_file.filename()); + result = false; + } + + // Check if the main file already exists. + if MainFile::exists_at(&path) { + existing_files.push(MainFile::filename()); + result = false; + } + } + + if existing_files.len() > 0 { + tracing::error!("File(s) {:?} already exist", existing_files); + } + + return result; + } + + /// Returns `true` if a package is initialized at the given path + pub fn is_initialized(package_name: &str, is_lib: bool, path: &PathBuf) -> bool { + // Check if the manifest file exists. + if !Manifest::exists_at(&path) { + return false; + } + + if is_lib { + // Check if the library file exists. + if !LibraryFile::exists_at(&path) { + return false; + } + } else { + // Check if the input file exists. + let input_file = InputFile::new(&package_name); + if !input_file.exists_at(&path) { + return false; + } + + // Check if the state file exists. + let state_file = StateFile::new(&package_name); + if !state_file.exists_at(&path) { + return false; + } + + // Check if the main file exists. + if !MainFile::exists_at(&path) { + return false; + } + } + + return true; + } + + /// Creates a package at the given path + pub fn initialize(package_name: &str, is_lib: bool, path: &PathBuf) -> Result<(), PackageError> { + // First, verify that this directory is not already initialized as a Leo package. + { + if !Self::can_initialize(package_name, is_lib, path) { + return Err( + PackageError::FailedToInitialize(package_name.to_owned(), path.as_os_str().to_owned()).into(), + ); + } + } + // Next, initialize this directory as a Leo package. + { + // Create the manifest file. + Manifest::new(&package_name).write_to(&path)?; + + // Verify that the .gitignore file does not exist. + if !Gitignore::exists_at(&path) { + // Create the .gitignore file. + Gitignore::new().write_to(&path)?; + } + + // Verify that the README.md file does not exist. + if !README::exists_at(&path) { + // Create the README.md file. + README::new(package_name).write_to(&path)?; + } + + // Create the source directory. + SourceDirectory::create(&path)?; + + // Create a new library or binary file. + if is_lib { + // Create the library file in the source directory. + LibraryFile::new(&package_name).write_to(&path)?; + } else { + // Create the input directory. + InputsDirectory::create(&path)?; + + // Create the input file in the inputs directory. + InputFile::new(&package_name).write_to(&path)?; + + // Create the state file in the inputs directory. + StateFile::new(&package_name).write_to(&path)?; + + // Create the main file in the source directory. + MainFile::new(&package_name).write_to(&path)?; + } + } + // Next, verify that a valid Leo package has been initialized in this directory + { + if !Self::is_initialized(package_name, is_lib, path) { + return Err( + PackageError::FailedToInitialize(package_name.to_owned(), path.as_os_str().to_owned()).into(), + ); + } + } + + Ok(()) + } + + /// Removes the package at the given path + pub fn remove_package(_package_name: &str) -> Result<(), PackageError> { + unimplemented!() + } +} diff --git a/package/src/root/gitignore.rs b/package/src/root/gitignore.rs index 9ecb3f7adc..306dd5220a 100644 --- a/package/src/root/gitignore.rs +++ b/package/src/root/gitignore.rs @@ -21,7 +21,7 @@ use crate::errors::GitignoreError; use serde::Deserialize; use std::{fs::File, io::Write, path::PathBuf}; -pub static GITIGNORE_FILE_NAME: &str = ".gitignore"; +pub static GITIGNORE_FILENAME: &str = ".gitignore"; #[derive(Deserialize)] pub struct Gitignore; @@ -34,7 +34,7 @@ impl Gitignore { pub fn exists_at(path: &PathBuf) -> bool { let mut path = path.to_owned(); if path.is_dir() { - path.push(PathBuf::from(GITIGNORE_FILE_NAME)); + path.push(PathBuf::from(GITIGNORE_FILENAME)); } path.exists() } @@ -42,7 +42,7 @@ impl Gitignore { pub fn write_to(self, path: &PathBuf) -> Result<(), GitignoreError> { let mut path = path.to_owned(); if path.is_dir() { - path.push(PathBuf::from(GITIGNORE_FILE_NAME)); + path.push(PathBuf::from(GITIGNORE_FILENAME)); } let mut file = File::create(&path)?; diff --git a/package/src/root/manifest.rs b/package/src/root/manifest.rs index 293e993683..bb33dd63ac 100644 --- a/package/src/root/manifest.rs +++ b/package/src/root/manifest.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with the Leo library. If not, see . -use crate::errors::ManifestError; +use crate::{errors::ManifestError, package::Package}; use serde::Deserialize; use std::{ @@ -24,44 +24,35 @@ use std::{ path::PathBuf, }; -pub const MANIFEST_FILE_NAME: &str = "Leo.toml"; +pub const MANIFEST_FILENAME: &str = "Leo.toml"; #[derive(Clone, Deserialize)] pub struct Remote { pub author: String, } -#[derive(Deserialize)] -pub struct Package { - pub name: String, - pub version: String, - pub description: Option, - pub license: Option, - pub remote: Option, -} - #[derive(Deserialize)] pub struct Manifest { pub package: Package, + pub remote: Option, } impl Manifest { pub fn new(package_name: &str) -> Self { Self { - package: Package { - name: package_name.to_owned(), - version: "0.1.0".to_owned(), - description: None, - license: None, - remote: None, - }, + package: Package::new(package_name), + remote: None, } } + pub fn filename() -> String { + MANIFEST_FILENAME.to_string() + } + pub fn exists_at(path: &PathBuf) -> bool { let mut path = path.to_owned(); if path.is_dir() { - path.push(PathBuf::from(MANIFEST_FILE_NAME)); + path.push(PathBuf::from(MANIFEST_FILENAME)); } path.exists() } @@ -83,18 +74,18 @@ impl Manifest { } pub fn get_package_remote(&self) -> Option { - self.package.remote.clone() + self.remote.clone() } pub fn write_to(self, path: &PathBuf) -> Result<(), ManifestError> { let mut path = path.to_owned(); if path.is_dir() { - path.push(PathBuf::from(MANIFEST_FILE_NAME)); + path.push(PathBuf::from(MANIFEST_FILENAME)); } - let mut file = File::create(&path).map_err(|error| ManifestError::Creating(MANIFEST_FILE_NAME, error))?; + let mut file = File::create(&path).map_err(|error| ManifestError::Creating(MANIFEST_FILENAME, error))?; file.write_all(self.template().as_bytes()) - .map_err(|error| ManifestError::Writing(MANIFEST_FILE_NAME, error)) + .map_err(|error| ManifestError::Writing(MANIFEST_FILENAME, error)) } fn template(&self) -> String { @@ -119,18 +110,18 @@ impl TryFrom<&PathBuf> for Manifest { fn try_from(path: &PathBuf) -> Result { let mut path = path.to_owned(); if path.is_dir() { - path.push(PathBuf::from(MANIFEST_FILE_NAME)); + path.push(PathBuf::from(MANIFEST_FILENAME)); } - let mut file = File::open(path.clone()).map_err(|error| ManifestError::Opening(MANIFEST_FILE_NAME, error))?; + let mut file = File::open(path.clone()).map_err(|error| ManifestError::Opening(MANIFEST_FILENAME, error))?; let size = file .metadata() - .map_err(|error| ManifestError::Metadata(MANIFEST_FILE_NAME, error))? + .map_err(|error| ManifestError::Metadata(MANIFEST_FILENAME, error))? .len() as usize; let mut buffer = String::with_capacity(size); file.read_to_string(&mut buffer) - .map_err(|error| ManifestError::Reading(MANIFEST_FILE_NAME, error))?; + .map_err(|error| ManifestError::Reading(MANIFEST_FILENAME, error))?; // Determine if the old remote format is being used, and update to new convention @@ -185,12 +176,12 @@ author = "{author}" // Rewrite the toml file if it has been updated if buffer != new_toml { - let mut file = File::create(&path).map_err(|error| ManifestError::Creating(MANIFEST_FILE_NAME, error))?; + let mut file = File::create(&path).map_err(|error| ManifestError::Creating(MANIFEST_FILENAME, error))?; file.write_all(new_toml.as_bytes()) - .map_err(|error| ManifestError::Writing(MANIFEST_FILE_NAME, error))?; + .map_err(|error| ManifestError::Writing(MANIFEST_FILENAME, error))?; } // Read the toml file - Ok(toml::from_str(&new_toml).map_err(|error| ManifestError::Parsing(MANIFEST_FILE_NAME, error))?) + Ok(toml::from_str(&new_toml).map_err(|error| ManifestError::Parsing(MANIFEST_FILENAME, error))?) } } diff --git a/package/src/root/readme.rs b/package/src/root/readme.rs index f59adfaafd..8631211e82 100644 --- a/package/src/root/readme.rs +++ b/package/src/root/readme.rs @@ -21,7 +21,7 @@ use crate::errors::READMEError; use serde::Deserialize; use std::{fs::File, io::Write, path::PathBuf}; -pub static README_FILE_NAME: &str = "README.md"; +pub static README_FILENAME: &str = "README.md"; #[derive(Deserialize)] pub struct README { @@ -42,7 +42,7 @@ impl README { pub fn exists_at(path: &PathBuf) -> bool { let mut path = path.to_owned(); if path.is_dir() { - path.push(PathBuf::from(README_FILE_NAME)); + path.push(PathBuf::from(README_FILENAME)); } path.exists() } @@ -50,7 +50,7 @@ impl README { pub fn write_to(self, path: &PathBuf) -> Result<(), READMEError> { let mut path = path.to_owned(); if path.is_dir() { - path.push(PathBuf::from(README_FILE_NAME)); + path.push(PathBuf::from(README_FILENAME)); } let mut file = File::create(&path)?; diff --git a/package/src/root/zip.rs b/package/src/root/zip.rs index 055452debf..7277fb3dd6 100644 --- a/package/src/root/zip.rs +++ b/package/src/root/zip.rs @@ -28,7 +28,7 @@ use crate::{ PROVING_KEY_FILE_EXTENSION, VERIFICATION_KEY_FILE_EXTENSION, }, - root::{MANIFEST_FILE_NAME, README_FILE_NAME}, + root::{MANIFEST_FILENAME, README_FILENAME}, source::{SOURCE_DIRECTORY_NAME, SOURCE_FILE_EXTENSION}, }; @@ -172,8 +172,7 @@ fn is_included(path: &Path) -> bool { } // Allow the README.md and Leo.toml files in the root directory - if (path.ends_with(README_FILE_NAME) | path.ends_with(MANIFEST_FILE_NAME)) & (path.parent() == Some(Path::new(""))) - { + if (path.ends_with(README_FILENAME) | path.ends_with(MANIFEST_FILENAME)) & (path.parent() == Some(Path::new(""))) { return true; } diff --git a/package/src/source/lib.rs b/package/src/source/library.rs similarity index 79% rename from package/src/source/lib.rs rename to package/src/source/library.rs index 426822a4f9..0531f0ed67 100644 --- a/package/src/source/lib.rs +++ b/package/src/source/library.rs @@ -16,43 +16,47 @@ //! The `lib.leo` file. -use crate::{errors::LibFileError, source::directory::SOURCE_DIRECTORY_NAME}; +use crate::{errors::LibraryFileError, source::directory::SOURCE_DIRECTORY_NAME}; use serde::Deserialize; use std::{fs::File, io::Write, path::PathBuf}; -pub static LIB_FILE_NAME: &str = "lib.leo"; +pub static LIBRARY_FILENAME: &str = "lib.leo"; #[derive(Deserialize)] -pub struct LibFile { +pub struct LibraryFile { pub package_name: String, } -impl LibFile { +impl LibraryFile { pub fn new(package_name: &str) -> Self { Self { package_name: package_name.to_string(), } } + pub fn filename() -> String { + format!("{}{}", SOURCE_DIRECTORY_NAME, LIBRARY_FILENAME) + } + pub fn exists_at(path: &PathBuf) -> bool { let mut path = path.to_owned(); if path.is_dir() { if !path.ends_with(SOURCE_DIRECTORY_NAME) { path.push(PathBuf::from(SOURCE_DIRECTORY_NAME)); } - path.push(PathBuf::from(LIB_FILE_NAME)); + path.push(PathBuf::from(LIBRARY_FILENAME)); } path.exists() } - pub fn write_to(self, path: &PathBuf) -> Result<(), LibFileError> { + pub fn write_to(self, path: &PathBuf) -> Result<(), LibraryFileError> { let mut path = path.to_owned(); if path.is_dir() { if !path.ends_with(SOURCE_DIRECTORY_NAME) { path.push(PathBuf::from(SOURCE_DIRECTORY_NAME)); } - path.push(PathBuf::from(LIB_FILE_NAME)); + path.push(PathBuf::from(LIBRARY_FILENAME)); } let mut file = File::create(&path)?; diff --git a/package/src/source/main.rs b/package/src/source/main.rs index 2fd5faf6e9..8136f12f5c 100644 --- a/package/src/source/main.rs +++ b/package/src/source/main.rs @@ -21,7 +21,7 @@ use crate::{errors::MainFileError, source::directory::SOURCE_DIRECTORY_NAME}; use serde::Deserialize; use std::{fs::File, io::Write, path::PathBuf}; -pub static MAIN_FILE_NAME: &str = "main.leo"; +pub static MAIN_FILENAME: &str = "main.leo"; #[derive(Deserialize)] pub struct MainFile { @@ -35,13 +35,17 @@ impl MainFile { } } + pub fn filename() -> String { + format!("{}{}", SOURCE_DIRECTORY_NAME, MAIN_FILENAME) + } + pub fn exists_at(path: &PathBuf) -> bool { let mut path = path.to_owned(); if path.is_dir() { if !path.ends_with(SOURCE_DIRECTORY_NAME) { path.push(PathBuf::from(SOURCE_DIRECTORY_NAME)); } - path.push(PathBuf::from(MAIN_FILE_NAME)); + path.push(PathBuf::from(MAIN_FILENAME)); } path.exists() } @@ -52,7 +56,7 @@ impl MainFile { if !path.ends_with(SOURCE_DIRECTORY_NAME) { path.push(PathBuf::from(SOURCE_DIRECTORY_NAME)); } - path.push(PathBuf::from(MAIN_FILE_NAME)); + path.push(PathBuf::from(MAIN_FILENAME)); } let mut file = File::create(&path)?; diff --git a/package/src/source/mod.rs b/package/src/source/mod.rs index 35f5b284ac..7c849f7f94 100644 --- a/package/src/source/mod.rs +++ b/package/src/source/mod.rs @@ -17,8 +17,8 @@ pub mod directory; pub use directory::*; -pub mod lib; -pub use lib::*; +pub mod library; +pub use library::*; pub mod main; pub use main::*; diff --git a/package/tests/initialize.rs b/package/tests/initialize.rs new file mode 100644 index 0000000000..62dc23eb85 --- /dev/null +++ b/package/tests/initialize.rs @@ -0,0 +1,192 @@ +// Copyright (C) 2019-2020 Aleo Systems Inc. +// This file is part of the Leo library. + +// The Leo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The Leo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with the Leo library. If not, see . + +use lazy_static::lazy_static; +use std::{ + cell::RefCell, + env, + fs, + path::PathBuf, + sync::atomic::{AtomicUsize, Ordering}, +}; + +const PACKAGE_TEST_DIRECTORY: &str = "package-testing"; + +thread_local! { + /// Establish a test id for each test. + static TEST_ID: RefCell> = RefCell::new(None); +} + +lazy_static! { + /// Create a testing directory for packages in `target/` + static ref TEST_DIR: PathBuf = { + let mut path = env::current_exe().unwrap(); + path.pop(); // Remove executable name + path.pop(); // Remove 'debug' + + // Attempt to point at the `target` directory + if path.file_name().and_then(|s| s.to_str()) != Some("target") { + path.pop(); + } + + path.push(PACKAGE_TEST_DIRECTORY); + fs::create_dir_all(&path).unwrap(); + + path + }; +} + +/// Create a new directory for each test based on the ID of the test. +fn test_dir() -> PathBuf { + static NEXT_ID: AtomicUsize = AtomicUsize::new(0); + + let id = NEXT_ID.fetch_add(1, Ordering::Relaxed); + TEST_ID.with(|n| *n.borrow_mut() = Some(id)); + + let path: PathBuf = TEST_DIR.join(&format!("t{}", id)).into(); + + if path.exists() { + if let Err(e) = fs::remove_dir_all(&path) { + panic!("failed to remove {:?}: {:?}", &path, e) + } + } + + fs::create_dir_all(&path).unwrap(); + + path +} + +// Tests for package initialization +mod initialize_package { + use super::*; + use leo_package::{ + inputs::{InputFile, InputsDirectory, StateFile}, + package::Package, + root::Manifest, + source::{LibraryFile, MainFile, SourceDirectory}, + }; + + const TEST_PACKAGE_NAME: &str = "test-package"; + + #[test] + fn initialize_valid_package() { + let test_directory = test_dir(); + + // Ensure a package can be initialized at the `test_directory` + assert!(Package::can_initialize(TEST_PACKAGE_NAME, false, &test_directory)); + + // Initialize a package at the `test_directory` + assert!(Package::initialize(TEST_PACKAGE_NAME, false, &test_directory).is_ok()); + + // Ensure a package is initialized at the `test_directory` + assert!(Package::is_initialized(TEST_PACKAGE_NAME, false, &test_directory)); + } + + #[test] + #[ignore] + fn initialize_fails_with_invalid_package_names() { + unimplemented!() + } + + #[test] + fn initialize_fails_with_existing_manifest() { + let test_directory = test_dir(); + + // Ensure a package can be initialized at the `test_directory` + 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(); + + // Attempt to initialize a package at the `test_directory` + assert!(Package::initialize(TEST_PACKAGE_NAME, false, &test_directory).is_err()); + + // Ensure package is not initialized at the `test_directory` + assert!(!Package::is_initialized(TEST_PACKAGE_NAME, false, &test_directory)); + } + + #[test] + fn initialize_fails_with_existing_library_file() { + let test_directory = test_dir(); + + // Ensure a package can be initialized at the `test_directory` + assert!(Package::can_initialize(TEST_PACKAGE_NAME, true, &test_directory)); + + // Manually add a source directory and a library file to the `test_directory` + SourceDirectory::create(&test_directory).unwrap(); + LibraryFile::new(TEST_PACKAGE_NAME).write_to(&test_directory).unwrap(); + + // Attempt to initialize a package at the `test_directory` + assert!(Package::initialize(TEST_PACKAGE_NAME, true, &test_directory).is_err()); + + // Ensure package is not initialized at the `test_directory` + assert!(!Package::is_initialized(TEST_PACKAGE_NAME, true, &test_directory)); + } + + #[test] + fn initialize_fails_with_existing_input_file() { + let test_directory = test_dir(); + + // Ensure a package can be initialized at the `test_directory` + assert!(Package::can_initialize(TEST_PACKAGE_NAME, false, &test_directory)); + + // Manually add an inputs directory and an input file to the `test_directory` + InputsDirectory::create(&test_directory).unwrap(); + InputFile::new(TEST_PACKAGE_NAME).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()); + + // Ensure package is not initialized at the `test_directory` + assert!(!Package::is_initialized(TEST_PACKAGE_NAME, false, &test_directory)); + } + + #[test] + fn initialize_fails_with_existing_state_file() { + let test_directory = test_dir(); + + // Ensure a package can be initialized at the `test_directory` + assert!(Package::can_initialize(TEST_PACKAGE_NAME, false, &test_directory)); + + // Manually add an inputs directory and a state file to the `test_directory` + InputsDirectory::create(&test_directory).unwrap(); + StateFile::new(TEST_PACKAGE_NAME).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()); + + // Ensure package is not initialized at the `test_directory` + assert!(!Package::is_initialized(TEST_PACKAGE_NAME, false, &test_directory)); + } + + #[test] + fn initialize_fails_with_existing_main_file() { + let test_directory = test_dir(); + + // Ensure a package can be initialized at the `test_directory` + assert!(Package::can_initialize(TEST_PACKAGE_NAME, false, &test_directory)); + + // Manually add a source directory and a main file to the `test_directory` + SourceDirectory::create(&test_directory).unwrap(); + MainFile::new(TEST_PACKAGE_NAME).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()); + + // Ensure package is not initialized at the `test_directory` + assert!(!Package::is_initialized(TEST_PACKAGE_NAME, false, &test_directory)); + } +}