diff --git a/leo/commands/cli.rs b/leo/cli.rs similarity index 95% rename from leo/commands/cli.rs rename to leo/cli.rs index df9b988909..4de644a0a9 100644 --- a/leo/commands/cli.rs +++ b/leo/cli.rs @@ -1,4 +1,4 @@ -use crate::commands::cli_types::*; +use crate::cli_types::*; use crate::errors::CLIError; use clap::{App, AppSettings, Arg, ArgMatches, SubCommand}; @@ -64,5 +64,5 @@ pub trait CLI { fn parse(arguments: &ArgMatches) -> Result; #[cfg_attr(tarpaulin, skip)] - fn output(&mut self, options: Self::Options) -> Result<(), CLIError>; + fn output(options: Self::Options) -> Result<(), CLIError>; } diff --git a/leo/commands/cli_types.rs b/leo/cli_types.rs similarity index 100% rename from leo/commands/cli_types.rs rename to leo/cli_types.rs diff --git a/leo/commands/init.rs b/leo/commands/init.rs new file mode 100644 index 0000000000..5b7a5be6f9 --- /dev/null +++ b/leo/commands/init.rs @@ -0,0 +1,63 @@ +use crate::{cli::*, cli_types::*}; +use crate::directories::SourceDirectory; +use crate::errors::{CLIError, InitError}; +use crate::manifest::Manifest; + +use clap::ArgMatches; +use std::env::current_dir; + +#[derive(Debug)] +pub struct InitCommand; + +impl CLI for InitCommand { + type Options = Option; + + const NAME: NameType = "init"; + const ABOUT: AboutType = "Creates a new Leo package (include -h for more options)"; + const FLAGS: &'static [FlagType] = &[]; + const OPTIONS: &'static [OptionType] = &[]; + const SUBCOMMANDS: &'static [SubCommandType] = &[]; + + #[cfg_attr(tarpaulin, skip)] + fn parse(_arguments: &ArgMatches) -> Result { + Ok(None) + } + + #[cfg_attr(tarpaulin, skip)] + fn output(options: Self::Options) -> Result<(), CLIError> { + let name = options; + let path = current_dir()?; + + // Derive the package name + let package_name = match name { + Some(name) => name, + None => path + .file_stem() + .ok_or_else(|| InitError::ProjectNameInvalid(path.as_os_str().to_owned()))? + .to_string_lossy() + .to_string(), + }; + + // Verify the directory exists + 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()); + } + Manifest::new(&package_name).write_to(&path)?; + + // Create the source directory + SourceDirectory::create(&path)?; + + // if !MainFile::exists_at(&path) { + // MainFile::new(&package_name) + // .write_to(&path) + // .map_err(Error::MainFile)?; + // } + + Ok(()) + } +} diff --git a/leo/commands/init/init.rs b/leo/commands/init/init.rs deleted file mode 100644 index 6505eeb710..0000000000 --- a/leo/commands/init/init.rs +++ /dev/null @@ -1,66 +0,0 @@ -use crate::commands::{cli::*, cli_types::*}; -use crate::errors::{CLIError, ManifestError, NewError}; -use crate::manifest::Manifest; - -use clap::{ArgMatches, Values}; -use colored::*; -use rand::{rngs::StdRng, Rng}; -use rand_core::SeedableRng; -use serde::{Deserialize, Serialize}; -use serde_json::from_str; -use std::{fmt, fmt::Display, fs, str::FromStr}; -use std::path::PathBuf; -use structopt::StructOpt; - -#[derive(Debug, StructOpt)] -pub struct InitCommand { - #[structopt(parse(from_os_str))] - path: PathBuf, -} - -impl CLI for InitCommand { - type Options = (); - - const NAME: NameType = "init"; - const ABOUT: AboutType = "Creates a new Leo package (include -h for more options)"; - const FLAGS: &'static [FlagType] = &[]; - const OPTIONS: &'static [OptionType] = &[]; - const SUBCOMMANDS: &'static [SubCommandType] = &[]; - - #[cfg_attr(tarpaulin, skip)] - fn parse(arguments: &ArgMatches) -> Result { - let mut options = (); - // options.parse(arguments, &["count", "format", "json", "network"]); - // - // match arguments.subcommand() { - // ("hd", Some(arguments)) => { - // options.subcommand = Some("hd".into()); - // options.parse(arguments, &["count", "json", "network"]); - // options.parse(arguments, &["derivation", "language", "password", "word count"]); - // } - // _ => {} - // }; - - Ok(options) - } - - #[cfg_attr(tarpaulin, skip)] - fn output(&mut self, options: Self::Options) -> Result<(), CLIError> { - let package_name = self.path - .file_stem() - .ok_or_else(|| NewError::ProjectNameInvalid(self.path.as_os_str().to_owned()))? - .to_string_lossy() - .to_string(); - - if self.path.exists() { - return Err(NewError::DirectoryAlreadyExists(self.path.as_os_str().to_owned()).into()); - } - fs::create_dir_all(&self.path).map_err(|error| { - NewError::CreatingRootDirectory(self.path.as_os_str().to_owned(), error) - })?; - - Manifest::new(&package_name).write_to(&self.path).map_err(NewError::ManifestError)?; - - Ok(()) - } -} diff --git a/leo/commands/init/mod.rs b/leo/commands/init/mod.rs deleted file mode 100644 index 2d5b8771e7..0000000000 --- a/leo/commands/init/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod init; -pub use self::init::*; diff --git a/leo/commands/mod.rs b/leo/commands/mod.rs index dda8b56111..2d5b8771e7 100644 --- a/leo/commands/mod.rs +++ b/leo/commands/mod.rs @@ -1,6 +1,2 @@ -#[cfg_attr(tarpaulin, skip)] -pub mod cli; -pub mod cli_types; - pub mod init; pub use self::init::*; diff --git a/leo/directories/mod.rs b/leo/directories/mod.rs new file mode 100644 index 0000000000..6dd9d696d3 --- /dev/null +++ b/leo/directories/mod.rs @@ -0,0 +1,2 @@ +pub mod source; +pub use self::source::*; diff --git a/leo/directories/source.rs b/leo/directories/source.rs new file mode 100644 index 0000000000..e3c2c07436 --- /dev/null +++ b/leo/directories/source.rs @@ -0,0 +1,61 @@ +use crate::errors::SourceDirectoryError; + +use std::fs; +use std::path::PathBuf; + +pub(self) static DIRECTORY_NAME_DEFAULT: &str = "src/"; + +static SOURCE_FILE_EXTENSION_DEFAULT: &str = "leo"; + +pub struct SourceDirectory; + +impl SourceDirectory { + /// Creates a directory at the provided path with the default directory name. + pub fn create(path: &PathBuf) -> Result<(), SourceDirectoryError> { + let mut path = path.to_owned(); + if path.is_dir() && !path.ends_with(DIRECTORY_NAME_DEFAULT) { + path.push(PathBuf::from(DIRECTORY_NAME_DEFAULT)); + } + + fs::create_dir_all(&path).map_err(SourceDirectoryError::Creating) + } + + /// Returns a list of files in the source directory. + pub fn files(path: &PathBuf) -> Result, SourceDirectoryError> { + let mut path = path.to_owned(); + path.push(PathBuf::from(DIRECTORY_NAME_DEFAULT)); + let directory = fs::read_dir(&path).map_err(SourceDirectoryError::Reading)?; + + let mut file_paths = Vec::new(); + for file_entry in directory.into_iter() { + let file_entry = file_entry.map_err(SourceDirectoryError::GettingFileEntry)?; + let file_path = file_entry.path(); + + // Verify that the entry is structured as a valid file + let file_type = file_entry + .file_type() + .map_err(|error| SourceDirectoryError::GettingFileType(file_path.as_os_str().to_owned(), error))?; + if !file_type.is_file() { + return Err(SourceDirectoryError::InvalidFileType( + file_path.as_os_str().to_owned(), + file_type, + )); + } + + // Verify that the file has the default file extension + let file_extension = file_path + .extension() + .ok_or_else(|| SourceDirectoryError::GettingFileExtension(file_path.as_os_str().to_owned()))?; + if file_extension != SOURCE_FILE_EXTENSION_DEFAULT { + return Err(SourceDirectoryError::InvalidFileExtension( + file_path.as_os_str().to_owned(), + file_extension.to_owned(), + )); + } + + file_paths.push(file_path); + } + + Ok(file_paths) + } +} diff --git a/leo/errors/cli.rs b/leo/errors/cli.rs index af8eecf728..a40e3785b5 100644 --- a/leo/errors/cli.rs +++ b/leo/errors/cli.rs @@ -1,4 +1,4 @@ -use crate::errors::{ManifestError, NewError}; +use crate::errors::{ManifestError, InitError, SourceDirectoryError}; #[derive(Debug, Fail)] pub enum CLIError { @@ -9,7 +9,10 @@ pub enum CLIError { ManifestError(ManifestError), #[fail(display = "{}", _0)] - NewError(NewError), + InitError(InitError), + + #[fail(display = "{}", _0)] + SourceDirectoryError(SourceDirectoryError), } impl From for CLIError { @@ -18,14 +21,26 @@ impl From for CLIError { } } -impl From for CLIError { - fn from(error: NewError) -> Self { - CLIError::NewError(error) +impl From for CLIError { + fn from(error: InitError) -> Self { + CLIError::InitError(error) + } +} + +impl From for CLIError { + fn from(error: SourceDirectoryError) -> Self { + CLIError::SourceDirectoryError(error) } } impl From for CLIError { fn from(error: serde_json::error::Error) -> Self { - CLIError::Crate("serde_json", format!("{:?}", error)) + CLIError::Crate("serde_json", format!("{}", error)) + } +} + +impl From for CLIError { + fn from(error: std::io::Error) -> Self { + CLIError::Crate("std::io", format!("{}", error)) } } diff --git a/leo/errors/commands/new.rs b/leo/errors/commands/init.rs similarity index 58% rename from leo/errors/commands/new.rs rename to leo/errors/commands/init.rs index 6be242b583..d721c5a80f 100644 --- a/leo/errors/commands/new.rs +++ b/leo/errors/commands/init.rs @@ -4,24 +4,27 @@ use std::ffi::OsString; use std::io; #[derive(Debug, Fail)] -pub enum NewError { +pub enum InitError { #[fail(display = "root directory {:?} creating: {}", _0, _1)] CreatingRootDirectory(OsString, io::Error), - #[fail(display = "directory {:?} already exists", _0)] - DirectoryAlreadyExists(OsString), + #[fail(display = "directory {:?} does not exist", _0)] + DirectoryDoesNotExist(OsString), #[fail(display = "{}", _0)] ManifestError(ManifestError), + #[fail(display = "package at path {:?} already exists", _0)] + PackageAlreadyExists(OsString), + #[fail(display = "package name is missing - {:?}", _0)] ProjectNameInvalid(OsString), } -impl From for NewError { +impl From for InitError { fn from(error: ManifestError) -> Self { - NewError::ManifestError(error) + InitError::ManifestError(error) } } diff --git a/leo/errors/commands/mod.rs b/leo/errors/commands/mod.rs index cbfcd90e50..2d5b8771e7 100644 --- a/leo/errors/commands/mod.rs +++ b/leo/errors/commands/mod.rs @@ -1,2 +1,2 @@ -pub mod new; -pub use self::new::*; +pub mod init; +pub use self::init::*; diff --git a/leo/errors/directory/mod.rs b/leo/errors/directory/mod.rs new file mode 100644 index 0000000000..6dd9d696d3 --- /dev/null +++ b/leo/errors/directory/mod.rs @@ -0,0 +1,2 @@ +pub mod source; +pub use self::source::*; diff --git a/leo/errors/directory/source.rs b/leo/errors/directory/source.rs new file mode 100644 index 0000000000..2ac823ad3b --- /dev/null +++ b/leo/errors/directory/source.rs @@ -0,0 +1,27 @@ +use std::{ffi::OsString, fs::FileType, io}; + +#[derive(Debug, Fail)] +pub enum SourceDirectoryError { + + #[fail(display = "creating: {}", _0)] + Creating(io::Error), + + #[fail(display = "file entry getting: {}", _0)] + GettingFileEntry(io::Error), + + #[fail(display = "file {:?} extension getting", _0)] + GettingFileExtension(OsString), + + #[fail(display = "file {:?} type getting: {}", _0, _1)] + GettingFileType(OsString, io::Error), + + #[fail(display = "invalid file {:?} extension: {:?}", _0, _1)] + InvalidFileExtension(OsString, OsString), + + #[fail(display = "invalid file {:?} type: {:?}", _0, _1)] + InvalidFileType(OsString, FileType), + + #[fail(display = "reading: {}", _0)] + Reading(io::Error), + +} diff --git a/leo/errors/mod.rs b/leo/errors/mod.rs index 48cc961b38..3eec4a7fe6 100644 --- a/leo/errors/mod.rs +++ b/leo/errors/mod.rs @@ -4,5 +4,8 @@ pub use self::cli::*; pub mod commands; pub use self::commands::*; +pub mod directory; +pub use self::directory::*; + pub mod manifest; pub use self::manifest::*; diff --git a/leo/lib.rs b/leo/lib.rs index 3ef9fb50aa..942ef4ddff 100644 --- a/leo/lib.rs +++ b/leo/lib.rs @@ -1,6 +1,10 @@ #[macro_use] extern crate failure; +#[cfg_attr(tarpaulin, skip)] +pub mod cli; +pub mod cli_types; pub mod commands; +pub mod directories; pub mod errors; pub mod manifest; diff --git a/leo/main.rs b/leo/main.rs index a04b94bb1c..bf3e8110d2 100644 --- a/leo/main.rs +++ b/leo/main.rs @@ -110,7 +110,7 @@ // // // } -use leo::commands::{cli::*, InitCommand}; +use leo::{cli::*, commands::*}; use leo::errors::CLIError; use clap::{App, AppSettings}; @@ -135,9 +135,9 @@ fn main() -> Result<(), CLIError> { match arguments.subcommand() { - // ("init", Some(arguments)) => { - // NewCommand::().output(NewCommand::parse(arguments)?) - // }, + ("init", Some(arguments)) => { + InitCommand::output(InitCommand::parse(arguments)?) + }, _ => unreachable!(), } } \ No newline at end of file diff --git a/leo/manifest.rs b/leo/manifest.rs index 5e92322c0e..61e885d853 100644 --- a/leo/manifest.rs +++ b/leo/manifest.rs @@ -1,6 +1,5 @@ use crate::errors::ManifestError; -use failure::Fail; use serde::Deserialize; use std::convert::TryFrom; use std::fs::File;