Merge branch 'master' into refactor-binary-installation

master
Jesper Håkansson 7 years ago
commit a558ea5e50
  1. 39
      src/build.rs
  2. 8
      src/child.rs
  3. 36
      src/command/build.rs
  4. 18
      src/command/mod.rs
  5. 13
      src/command/pack.rs
  6. 4
      src/command/publish/access.rs
  7. 13
      src/command/publish/mod.rs
  8. 55
      src/command/test.rs
  9. 154
      src/error.rs
  10. 1
      src/lib.rs

@ -2,8 +2,7 @@
use child;
use emoji;
use error::Error;
use failure::ResultExt;
use failure::{Error, ResultExt};
use progressbar::Step;
use slog::Logger;
use std::path::Path;
@ -12,27 +11,22 @@ use std::str;
use PBAR;
/// Ensure that `rustc` is present and that it is >= 1.30.0
pub fn check_rustc_version(step: &Step) -> Result<String, failure::Error> {
pub fn check_rustc_version(step: &Step) -> Result<String, Error> {
let msg = format!("{}Checking `rustc` version...", emoji::CRAB);
PBAR.step(step, &msg);
let local_minor_version = rustc_minor_version();
match local_minor_version {
Some(mv) => {
if mv < 30 {
return Err(Error::RustcVersion {
message: format!(
"Your version of Rust, '1.{}', is not supported. Please install Rust version 1.30.0 or higher.",
mv.to_string()
),
local_minor_version: mv.to_string(),
}.into())
bail!(
"Your version of Rust, '1.{}', is not supported. Please install Rust version 1.30.0 or higher.",
mv.to_string()
)
} else {
Ok(mv.to_string())
Ok(mv.to_string())
}
},
None => Err(Error::RustcMissing {
message: "We can't figure out what your Rust version is- which means you might not have Rust installed. Please install Rust version 1.30.0 or higher.".to_string(),
}.into()),
},
None => bail!("We can't figure out what your Rust version is- which means you might not have Rust installed. Please install Rust version 1.30.0 or higher."),
}
}
@ -57,7 +51,7 @@ fn rustc_minor_version() -> Option<u32> {
/// Ensure that `rustup` has the `wasm32-unknown-unknown` target installed for
/// current toolchain
pub fn rustup_add_wasm_target(log: &Logger, step: &Step) -> Result<(), failure::Error> {
pub fn rustup_add_wasm_target(log: &Logger, step: &Step) -> Result<(), Error> {
let msg = format!("{}Adding WASM target...", emoji::TARGET);
PBAR.step(step, &msg);
let mut cmd = Command::new("rustup");
@ -68,12 +62,7 @@ pub fn rustup_add_wasm_target(log: &Logger, step: &Step) -> Result<(), failure::
}
/// Run `cargo build` targetting `wasm32-unknown-unknown`.
pub fn cargo_build_wasm(
log: &Logger,
path: &Path,
debug: bool,
step: &Step,
) -> Result<(), failure::Error> {
pub fn cargo_build_wasm(log: &Logger, path: &Path, debug: bool, step: &Step) -> Result<(), Error> {
let msg = format!("{}Compiling to WASM...", emoji::CYCLONE);
PBAR.step(step, &msg);
let mut cmd = Command::new("cargo");
@ -87,11 +76,7 @@ pub fn cargo_build_wasm(
}
/// Run `cargo build --tests` targetting `wasm32-unknown-unknown`.
pub fn cargo_build_wasm_tests(
log: &Logger,
path: &Path,
debug: bool,
) -> Result<(), failure::Error> {
pub fn cargo_build_wasm_tests(log: &Logger, path: &Path, debug: bool) -> Result<(), Error> {
let mut cmd = Command::new("cargo");
cmd.current_dir(path).arg("build").arg("--tests");
if !debug {

@ -3,8 +3,7 @@
//! This module helps us ensure that all child processes that we spawn get
//! properly logged and their output is logged as well.
use error::Error;
use failure;
use failure::Error;
use slog::Logger;
use std::{
io::{self, Read},
@ -120,7 +119,7 @@ pub fn run(
logger: &Logger,
mut command: process::Command,
command_name: &str,
) -> Result<String, failure::Error> {
) -> Result<String, Error> {
info!(logger, "Running {:?}", command);
let mut child = command
@ -171,6 +170,7 @@ pub fn run(
if exit.success() {
return Ok(stdout);
} else {
return Err(Error::cli(command_name, stdout.into(), stderr.into(), exit).into());
drop((stdout, stderr));
bail!("failed to execute `{}`: exited with {}", command_name, exit)
}
}

@ -4,7 +4,7 @@ use bindgen;
use build;
use command::utils::{create_pkg_dir, set_crate_path};
use emoji;
use error::Error;
use failure::Error;
use indicatif::HumanDuration;
use lockfile::Lockfile;
use manifest;
@ -59,7 +59,7 @@ impl FromStr for BuildMode {
"no-install" => Ok(BuildMode::Noinstall),
"normal" => Ok(BuildMode::Normal),
"force" => Ok(BuildMode::Force),
_ => Err(Error::crate_config(&format!("Unknown build mode: {}", s))),
_ => bail!("Unknown build mode: {}", s),
}
}
}
@ -98,11 +98,11 @@ pub struct BuildOptions {
pub out_dir: String,
}
type BuildStep = fn(&mut Build, &Step, &Logger) -> Result<(), failure::Error>;
type BuildStep = fn(&mut Build, &Step, &Logger) -> Result<(), Error>;
impl Build {
/// Construct a build command from the given options.
pub fn try_from_opts(build_opts: BuildOptions) -> Result<Self, failure::Error> {
pub fn try_from_opts(build_opts: BuildOptions) -> Result<Self, Error> {
let crate_path = set_crate_path(build_opts.path)?;
let crate_data = manifest::CrateData::new(&crate_path)?;
let out_dir = crate_path.join(PathBuf::from(build_opts.out_dir));
@ -128,7 +128,7 @@ impl Build {
}
/// Execute this `Build` command.
pub fn run(&mut self, log: &Logger) -> Result<(), failure::Error> {
pub fn run(&mut self, log: &Logger) -> Result<(), Error> {
let process_steps = Build::get_process_steps(&self.mode);
let mut step_counter = Step::new(process_steps.len());
@ -199,11 +199,7 @@ impl Build {
}
}
fn step_check_rustc_version(
&mut self,
step: &Step,
log: &Logger,
) -> Result<(), failure::Error> {
fn step_check_rustc_version(&mut self, step: &Step, log: &Logger) -> Result<(), Error> {
info!(&log, "Checking rustc version...");
let version = build::check_rustc_version(step)?;
let msg = format!("rustc version is {}.", version);
@ -211,21 +207,21 @@ impl Build {
Ok(())
}
fn step_check_crate_config(&mut self, step: &Step, log: &Logger) -> Result<(), failure::Error> {
fn step_check_crate_config(&mut self, step: &Step, log: &Logger) -> Result<(), Error> {
info!(&log, "Checking crate configuration...");
self.crate_data.check_crate_config(step)?;
info!(&log, "Crate is correctly configured.");
Ok(())
}
fn step_add_wasm_target(&mut self, step: &Step, log: &Logger) -> Result<(), failure::Error> {
fn step_add_wasm_target(&mut self, step: &Step, log: &Logger) -> Result<(), Error> {
info!(&log, "Adding wasm-target...");
build::rustup_add_wasm_target(log, step)?;
info!(&log, "Adding wasm-target was successful.");
Ok(())
}
fn step_build_wasm(&mut self, step: &Step, log: &Logger) -> Result<(), failure::Error> {
fn step_build_wasm(&mut self, step: &Step, log: &Logger) -> Result<(), Error> {
info!(&log, "Building wasm...");
build::cargo_build_wasm(log, &self.crate_path, self.debug, step)?;
@ -241,14 +237,14 @@ impl Build {
Ok(())
}
fn step_create_dir(&mut self, step: &Step, log: &Logger) -> Result<(), failure::Error> {
fn step_create_dir(&mut self, step: &Step, log: &Logger) -> Result<(), Error> {
info!(&log, "Creating a pkg directory...");
create_pkg_dir(&self.out_dir, step)?;
info!(&log, "Created a pkg directory at {:#?}.", &self.crate_path);
Ok(())
}
fn step_create_json(&mut self, step: &Step, log: &Logger) -> Result<(), failure::Error> {
fn step_create_json(&mut self, step: &Step, log: &Logger) -> Result<(), Error> {
info!(&log, "Writing a package.json...");
self.crate_data.write_package_json(
&self.out_dir,
@ -265,18 +261,14 @@ impl Build {
Ok(())
}
fn step_copy_readme(&mut self, step: &Step, log: &Logger) -> Result<(), failure::Error> {
fn step_copy_readme(&mut self, step: &Step, log: &Logger) -> Result<(), Error> {
info!(&log, "Copying readme from crate...");
readme::copy_from_crate(&self.crate_path, &self.out_dir, step)?;
info!(&log, "Copied readme from crate to {:#?}.", &self.out_dir);
Ok(())
}
fn step_install_wasm_bindgen(
&mut self,
step: &Step,
log: &Logger,
) -> Result<(), failure::Error> {
fn step_install_wasm_bindgen(&mut self, step: &Step, log: &Logger) -> Result<(), Error> {
info!(&log, "Identifying wasm-bindgen dependency...");
let lockfile = Lockfile::new(&self.crate_data)?;
let bindgen_version = lockfile.require_wasm_bindgen()?;
@ -298,7 +290,7 @@ impl Build {
Ok(())
}
fn step_run_wasm_bindgen(&mut self, step: &Step, log: &Logger) -> Result<(), failure::Error> {
fn step_run_wasm_bindgen(&mut self, step: &Step, log: &Logger) -> Result<(), Error> {
info!(&log, "Building the wasm bindings...");
bindgen::wasm_bindgen_build(
&self.crate_data,

@ -13,11 +13,10 @@ use self::login::login;
use self::pack::pack;
use self::publish::{access::Access, publish};
use self::test::{Test, TestOptions};
use error::Error;
use failure::Error;
use slog::Logger;
use std::path::PathBuf;
use std::result;
use PBAR;
/// The various kinds of commands that `wasm-pack` can execute.
#[derive(Debug, StructOpt)]
@ -84,7 +83,7 @@ pub enum Command {
}
/// Run a command with the given logger!
pub fn run_wasm_pack(command: Command, log: &Logger) -> result::Result<(), failure::Error> {
pub fn run_wasm_pack(command: Command, log: &Logger) -> result::Result<(), Error> {
// Run the correct command based off input and store the result of it so that we can clear
// the progress bar then return it
let status = match command {
@ -125,19 +124,6 @@ pub fn run_wasm_pack(command: Command, log: &Logger) -> result::Result<(), failu
}
};
match status {
Ok(_) => {}
Err(ref e) => {
error!(&log, "{}", e);
for c in e.iter_chain() {
if let Some(e) = c.downcast_ref::<Error>() {
PBAR.error(e.error_type());
break;
}
}
}
}
// Return the actual status of the program to the main function
status
}

@ -1,5 +1,5 @@
use command::utils::{find_pkg_directory, set_crate_path};
use error::Error;
use failure::Error;
use npm;
use slog::Logger;
use std::path::PathBuf;
@ -8,15 +8,16 @@ use PBAR;
/// Executes the 'npm pack' command on the 'pkg' directory
/// which creates a tarball that can be published to the NPM registry
pub fn pack(path: Option<PathBuf>, log: &Logger) -> result::Result<(), failure::Error> {
pub fn pack(path: Option<PathBuf>, log: &Logger) -> result::Result<(), Error> {
let crate_path = set_crate_path(path)?;
info!(&log, "Packing up the npm package...");
let pkg_directory = find_pkg_directory(&crate_path).ok_or(Error::PkgNotFound {
message: format!(
let pkg_directory = find_pkg_directory(&crate_path).ok_or_else(|| {
format_err!(
"Unable to find the pkg directory at path {:#?}, or in a child directory of {:#?}",
&crate_path, &crate_path
),
&crate_path,
&crate_path
)
})?;
npm::npm_pack(log, &pkg_directory.to_string_lossy())?;
info!(

@ -1,4 +1,4 @@
use error::Error;
use failure::Error;
use std::fmt;
use std::str::FromStr;
@ -19,7 +19,7 @@ impl FromStr for Access {
"public" => Ok(Access::Public),
"restricted" => Ok(Access::Restricted),
"private" => Ok(Access::Restricted),
_ => Err(Error::Unsupported { message: format!("{} is not a supported access level. See https://docs.npmjs.com/cli/access for more information on npm package access levels.", s)}),
_ => bail!("{} is not a supported access level. See https://docs.npmjs.com/cli/access for more information on npm package access levels.", s),
}
}
}

@ -3,7 +3,7 @@ pub mod access;
use self::access::Access;
use command::utils::{find_pkg_directory, set_crate_path};
use error::Error;
use failure::Error;
use npm;
use slog::Logger;
use std::path::PathBuf;
@ -16,16 +16,17 @@ pub fn publish(
path: Option<PathBuf>,
access: Option<Access>,
log: &Logger,
) -> result::Result<(), failure::Error> {
) -> result::Result<(), Error> {
let crate_path = set_crate_path(path)?;
info!(&log, "Publishing the npm package...");
info!(&log, "npm info located in the npm debug log");
let pkg_directory = find_pkg_directory(&crate_path).ok_or(Error::PkgNotFound {
message: format!(
let pkg_directory = find_pkg_directory(&crate_path).ok_or_else(|| {
format_err!(
"Unable to find the pkg directory at path '{:#?}', or in a child directory of '{:#?}'",
&crate_path, &crate_path
),
&crate_path,
&crate_path
)
})?;
npm::npm_publish(log, &pkg_directory.to_string_lossy(), access)?;

@ -6,7 +6,7 @@ use build;
use command::utils::set_crate_path;
use console::style;
use emoji;
use error::Error;
use failure::Error;
use indicatif::HumanDuration;
use lockfile::Lockfile;
use manifest;
@ -97,11 +97,11 @@ pub struct Test {
test_runner_path: Option<PathBuf>,
}
type TestStep = fn(&mut Test, &Step, &Logger) -> Result<(), failure::Error>;
type TestStep = fn(&mut Test, &Step, &Logger) -> Result<(), Error>;
impl Test {
/// Construct a test command from the given options.
pub fn try_from_opts(test_opts: TestOptions) -> Result<Self, failure::Error> {
pub fn try_from_opts(test_opts: TestOptions) -> Result<Self, Error> {
let TestOptions {
path,
node,
@ -121,16 +121,14 @@ impl Test {
let any_browser = chrome || firefox || safari;
if !node && !any_browser {
return Err(Error::crate_config(
"Must specify at least one of `--node`, `--chrome`, `--firefox`, or `--safari`",
).into());
bail!("Must specify at least one of `--node`, `--chrome`, `--firefox`, or `--safari`")
}
if headless && !any_browser {
return Err(Error::crate_config(
bail!(
"The `--headless` flag only applies to browser tests. Node does not provide a UI, \
so it doesn't make sense to talk about a headless version of Node tests.",
).into());
so it doesn't make sense to talk about a headless version of Node tests."
)
}
Ok(Test {
@ -157,7 +155,7 @@ impl Test {
}
/// Execute this test command.
pub fn run(mut self, log: &Logger) -> Result<(), failure::Error> {
pub fn run(mut self, log: &Logger) -> Result<(), Error> {
let process_steps = self.get_process_steps();
let mut step_counter = Step::new(process_steps.len());
@ -229,32 +227,28 @@ impl Test {
}
}
fn step_check_rustc_version(
&mut self,
step: &Step,
log: &Logger,
) -> Result<(), failure::Error> {
fn step_check_rustc_version(&mut self, step: &Step, log: &Logger) -> Result<(), Error> {
info!(log, "Checking rustc version...");
let _ = build::check_rustc_version(step)?;
info!(log, "Rustc version is correct.");
Ok(())
}
fn step_check_crate_config(&mut self, step: &Step, log: &Logger) -> Result<(), failure::Error> {
fn step_check_crate_config(&mut self, step: &Step, log: &Logger) -> Result<(), Error> {
info!(log, "Checking crate configuration...");
self.crate_data.check_crate_config(step)?;
info!(log, "Crate is correctly configured.");
Ok(())
}
fn step_add_wasm_target(&mut self, step: &Step, log: &Logger) -> Result<(), failure::Error> {
fn step_add_wasm_target(&mut self, step: &Step, log: &Logger) -> Result<(), Error> {
info!(&log, "Adding wasm-target...");
build::rustup_add_wasm_target(log, step)?;
info!(&log, "Adding wasm-target was successful.");
Ok(())
}
fn step_build_tests(&mut self, step: &Step, log: &Logger) -> Result<(), failure::Error> {
fn step_build_tests(&mut self, step: &Step, log: &Logger) -> Result<(), Error> {
info!(log, "Compiling tests to wasm...");
let msg = format!("{}Compiling tests to WASM...", emoji::CYCLONE);
@ -266,11 +260,7 @@ impl Test {
Ok(())
}
fn step_install_wasm_bindgen(
&mut self,
step: &Step,
log: &Logger,
) -> Result<(), failure::Error> {
fn step_install_wasm_bindgen(&mut self, step: &Step, log: &Logger) -> Result<(), Error> {
info!(&log, "Identifying wasm-bindgen dependency...");
let lockfile = Lockfile::new(&self.crate_data)?;
let bindgen_version = lockfile.require_wasm_bindgen()?;
@ -281,13 +271,12 @@ impl Test {
// `wasm32-unkown-unknown`. Don't enforce that it is the same version as
// `wasm-bindgen`.
if lockfile.wasm_bindgen_test_version().is_none() {
let message = format!(
bail!(
"Ensure that you have \"{}\" as a dependency in your Cargo.toml file:\n\
[dev-dependencies]\n\
wasm-bindgen-test = \"0.2\"",
style("wasm-bindgen-test").bold().dim(),
);
return Err(Error::CrateConfig { message }.into());
)
}
let install_permitted = match self.mode {
@ -319,7 +308,7 @@ impl Test {
Ok(())
}
fn step_test_node(&mut self, step: &Step, log: &Logger) -> Result<(), failure::Error> {
fn step_test_node(&mut self, step: &Step, log: &Logger) -> Result<(), Error> {
assert!(self.node);
info!(log, "Running tests in node...");
PBAR.step(step, "Running tests in node...");
@ -336,7 +325,7 @@ impl Test {
Ok(())
}
fn step_get_chromedriver(&mut self, step: &Step, _log: &Logger) -> Result<(), failure::Error> {
fn step_get_chromedriver(&mut self, step: &Step, _log: &Logger) -> Result<(), Error> {
PBAR.step(step, "Getting chromedriver...");
assert!(self.chrome && self.chromedriver.is_none());
@ -347,7 +336,7 @@ impl Test {
Ok(())
}
fn step_test_chrome(&mut self, step: &Step, log: &Logger) -> Result<(), failure::Error> {
fn step_test_chrome(&mut self, step: &Step, log: &Logger) -> Result<(), Error> {
PBAR.step(step, "Running tests in Chrome...");
let chromedriver = self.chromedriver.as_ref().unwrap().display().to_string();
@ -378,7 +367,7 @@ impl Test {
Ok(())
}
fn step_get_geckodriver(&mut self, step: &Step, _log: &Logger) -> Result<(), failure::Error> {
fn step_get_geckodriver(&mut self, step: &Step, _log: &Logger) -> Result<(), Error> {
PBAR.step(step, "Getting geckodriver...");
assert!(self.firefox && self.geckodriver.is_none());
@ -389,7 +378,7 @@ impl Test {
Ok(())
}
fn step_test_firefox(&mut self, step: &Step, log: &Logger) -> Result<(), failure::Error> {
fn step_test_firefox(&mut self, step: &Step, log: &Logger) -> Result<(), Error> {
PBAR.step(step, "Running tests in Firefox...");
let geckodriver = self.geckodriver.as_ref().unwrap().display().to_string();
@ -420,7 +409,7 @@ impl Test {
Ok(())
}
fn step_get_safaridriver(&mut self, step: &Step, _log: &Logger) -> Result<(), failure::Error> {
fn step_get_safaridriver(&mut self, step: &Step, _log: &Logger) -> Result<(), Error> {
PBAR.step(step, "Getting safaridriver...");
assert!(self.safari && self.safaridriver.is_none());
@ -428,7 +417,7 @@ impl Test {
Ok(())
}
fn step_test_safari(&mut self, step: &Step, log: &Logger) -> Result<(), failure::Error> {
fn step_test_safari(&mut self, step: &Step, log: &Logger) -> Result<(), Error> {
PBAR.step(step, "Running tests in Safari...");
let safaridriver = self.safaridriver.as_ref().unwrap().display().to_string();

@ -1,154 +0,0 @@
//! Code related to error handling for wasm-pack
use serde_json;
use std::borrow::Cow;
use std::io;
use std::process::ExitStatus;
use toml;
/// Errors that can potentially occur in `wasm-pack`.
#[derive(Debug, Fail)]
pub enum Error {
/// Maps any underlying I/O errors that are thrown to this variant
#[fail(display = "{}", _0)]
Io(#[cause] io::Error),
/// A JSON serialization or deserialization error.
#[fail(display = "{}", _0)]
SerdeJson(#[cause] serde_json::Error),
/// A TOML serialization or deserialization error.
#[fail(display = "{}", _0)]
SerdeToml(#[cause] toml::de::Error),
#[fail(display = "{}", _0)]
/// An error in parsing your rustc version.
RustcMissing {
/// Error message
message: String,
},
#[fail(display = "{}", _0)]
/// An error from having an unsupported rustc version.
RustcVersion {
/// Error message
message: String,
/// The minor version of the local rust
local_minor_version: String,
},
/// An error invoking another CLI tool.
#[fail(display = "`{}` exited with {}", tool, exit_status)]
Cli {
/// Error message.
tool: String,
/// The underlying CLI's `stdout` output.
stdout: String,
/// The underlying CLI's `stderr` output.
stderr: String,
/// The exit status of the subprocess
exit_status: ExitStatus,
},
/// A crate configuration error.
#[fail(display = "{}", message)]
CrateConfig {
/// A message describing the configuration error.
message: String,
},
#[fail(display = "{}", message)]
/// Error when the 'pkg' directory is not found.
PkgNotFound {
/// Message describing the error.
message: String,
},
#[fail(display = "{}", message)]
/// Error when some operation or feature is unsupported for the current
/// target or environment.
Unsupported {
/// Error message.
message: String,
},
}
impl Error {
/// Construct a CLI error.
pub fn cli(tool: &str, stdout: Cow<str>, stderr: Cow<str>, exit_status: ExitStatus) -> Self {
Error::Cli {
tool: tool.to_string(),
stdout: stdout.to_string(),
stderr: stderr.to_string(),
exit_status,
}
}
/// Construct a crate configuration error.
pub fn crate_config(message: &str) -> Self {
Error::CrateConfig {
message: message.to_string(),
}
}
/// Construct an unsupported error.
pub fn unsupported(message: &str) -> Self {
Error::Unsupported {
message: message.to_string(),
}
}
/// Construct a rustc version error.
pub fn rustc_version_error(message: &str, local_version: &str) -> Self {
Error::RustcVersion {
message: message.to_string(),
local_minor_version: local_version.to_string(),
}
}
/// Get a string description of this error's type.
pub fn error_type(&self) -> String {
match self {
Error::Io(_) => "There was an I/O error. Details:\n\n",
Error::SerdeJson(_) => "There was an JSON error. Details:\n\n",
Error::SerdeToml(_) => "There was an TOML error. Details:\n\n",
Error::RustcMissing {
message: _,
} => "We can't figure out what your Rust version is- which means you might not have Rust installed. Please install Rust version 1.30.0 or higher.",
Error::RustcVersion {
message: _,
local_minor_version: _,
} => "Your rustc version is not supported. Please install version 1.30.0 or higher.",
Error::Cli {
tool: _,
stdout: _,
stderr: _,
exit_status: _,
} => "There was an error while calling another CLI tool. Details:\n\n",
Error::CrateConfig { message: _ } => {
"There was a crate configuration error. Details:\n\n"
}
Error::PkgNotFound {
message: _,
} => "Unable to find the 'pkg' directory at the path, set the path as the parent of the 'pkg' directory \n\n",
Error::Unsupported {..} => "There was an unsupported operation attempted. Details:\n\n",
}.to_string()
}
}
impl From<io::Error> for Error {
fn from(e: io::Error) -> Self {
Error::Io(e)
}
}
impl From<serde_json::Error> for Error {
fn from(e: serde_json::Error) -> Self {
Error::SerdeJson(e)
}
}
impl From<toml::de::Error> for Error {
fn from(e: toml::de::Error) -> Self {
Error::SerdeToml(e)
}
}

@ -28,7 +28,6 @@ pub mod build;
pub mod child;
pub mod command;
pub mod emoji;
pub mod error;
pub mod lockfile;
pub mod logger;
pub mod manifest;

Loading…
Cancel
Save