Complete command

This commit is contained in:
howardwu 2020-04-24 23:56:26 -07:00
parent 93b47602dd
commit 83065845df
17 changed files with 199 additions and 92 deletions

View File

@ -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<Self::Options, CLIError>;
#[cfg_attr(tarpaulin, skip)]
fn output(&mut self, options: Self::Options) -> Result<(), CLIError>;
fn output(options: Self::Options) -> Result<(), CLIError>;
}

63
leo/commands/init.rs Normal file
View File

@ -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<String>;
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<Self::Options, CLIError> {
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(())
}
}

View File

@ -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<Self::Options, CLIError> {
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(())
}
}

View File

@ -1,2 +0,0 @@
pub mod init;
pub use self::init::*;

View File

@ -1,6 +1,2 @@
#[cfg_attr(tarpaulin, skip)]
pub mod cli;
pub mod cli_types;
pub mod init;
pub use self::init::*;

2
leo/directories/mod.rs Normal file
View File

@ -0,0 +1,2 @@
pub mod source;
pub use self::source::*;

61
leo/directories/source.rs Normal file
View File

@ -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<Vec<PathBuf>, 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)
}
}

View File

@ -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<ManifestError> for CLIError {
@ -18,14 +21,26 @@ impl From<ManifestError> for CLIError {
}
}
impl From<NewError> for CLIError {
fn from(error: NewError) -> Self {
CLIError::NewError(error)
impl From<InitError> for CLIError {
fn from(error: InitError) -> Self {
CLIError::InitError(error)
}
}
impl From<SourceDirectoryError> for CLIError {
fn from(error: SourceDirectoryError) -> Self {
CLIError::SourceDirectoryError(error)
}
}
impl From<serde_json::error::Error> for CLIError {
fn from(error: serde_json::error::Error) -> Self {
CLIError::Crate("serde_json", format!("{:?}", error))
CLIError::Crate("serde_json", format!("{}", error))
}
}
impl From<std::io::Error> for CLIError {
fn from(error: std::io::Error) -> Self {
CLIError::Crate("std::io", format!("{}", error))
}
}

View File

@ -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<ManifestError> for NewError {
impl From<ManifestError> for InitError {
fn from(error: ManifestError) -> Self {
NewError::ManifestError(error)
InitError::ManifestError(error)
}
}

View File

@ -1,2 +1,2 @@
pub mod new;
pub use self::new::*;
pub mod init;
pub use self::init::*;

View File

@ -0,0 +1,2 @@
pub mod source;
pub use self::source::*;

View File

@ -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),
}

View File

@ -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::*;

View File

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

View File

@ -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!(),
}
}

View File

@ -1,6 +1,5 @@
use crate::errors::ManifestError;
use failure::Fail;
use serde::Deserialize;
use std::convert::TryFrom;
use std::fs::File;