Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Add toml like configuration #194

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions src/command/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use command::utils::{create_pkg_dir, set_crate_path};
use emoji;
use error::Error;
use indicatif::HumanDuration;
use manifest;
use manifest::{self, BuildConfig};
use progressbar::Step;
use readme;
use slog::Logger;
Expand All @@ -19,7 +19,7 @@ pub(crate) struct Build {
pub disable_dts: bool,
pub target: String,
pub debug: bool,
// build_config: Option<BuildConfig>,
pub build_config: Option<BuildConfig>,
pub crate_name: String,
}

Expand Down Expand Up @@ -60,22 +60,20 @@ pub struct BuildOptions {
#[structopt(long = "debug")]
/// Build without --release.
debug: bool,
// build config from manifest
// build_config: Option<BuildConfig>,
}

impl From<BuildOptions> for Build {
fn from(build_opts: BuildOptions) -> Self {
let crate_path = set_crate_path(build_opts.path);
let crate_name = manifest::get_crate_name(&crate_path).unwrap();
// let build_config = manifest::xxx(&crate_path).xxx();
let manifest = manifest::read_cargo_toml(&crate_path).unwrap();
Build {
crate_path,
scope: build_opts.scope,
disable_dts: build_opts.disable_dts,
target: build_opts.target,
debug: build_opts.debug,
// build_config,
build_config: manifest.build_config,
crate_name,
}
}
Expand Down
9 changes: 9 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
//! Code related to error handling for wasm-pack
use failure;
use serde_json;
use std::borrow::Cow;
use std::io;
Expand Down Expand Up @@ -95,3 +96,11 @@ impl From<toml::de::Error> for Error {
Error::SerdeToml(e)
}
}

impl From<failure::Error> for Error {
fn from(e: failure::Error) -> Self {
Error::CrateConfig {
message: e.to_string(),
}
}
}
196 changes: 186 additions & 10 deletions src/manifest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,71 @@
use std::collections::HashMap;
use std::fs::File;
use std::io::prelude::*;
use std::path::Path;
use std::path::PathBuf;

use console::style;
use emoji;
use error::Error;
use progressbar::Step;
use serde_json;
use toml;
use toml::{self, value::Table, Value};
use PBAR;

/// Manifest in `Cargo.toml`.
#[derive(Deserialize)]
struct CargoManifest {
pub struct CargoManifest {
package: CargoPackage,
dependencies: Option<HashMap<String, CargoDependency>>,
lib: Option<CargoLib>,
/// build config.
pub build_config: Option<BuildConfig>,
}

/// Build configuration.
#[derive(Deserialize, Debug, Default, Clone)]
pub struct BuildConfig {
build: Option<BuildMode>,
}

/// Build mode.
#[derive(Deserialize, Debug, Default, Clone)]
pub struct BuildMode {
debug: Option<WasmPackConfig>,
profiling: Option<WasmPackConfig>,
production: Option<WasmPackConfig>,
}

// TODO(csmoe): introduce wasm-* config
// use xxx::snip::SnipOptions as WasmSnipConfig;

// macro_rules! tool_config {
// ($wasm_tool: ident,$config: ident) => {
// #[derive(Debug, Clone)]
// enum $wasm_tool {
// Bool(bool),
// Config($config),
// }
//
// impl Default for $wasm_tool {
// fn default() -> Self {
// $wasm_tool::Bool(false)
// }
// }
// };
// }
//
// tool_config!(WasmOpt, WasmOptConfig);
// tool_config!(WasmSnip, WasmSnipConfig);

/// wasm-pack config.
#[derive(Deserialize, Debug, Default, Clone)]
pub struct WasmPackConfig {
debug: Option<bool>,
features: Option<Vec<String>>,
rustc_opt_level: Option<String>,
// pub wasm_opt: Option<WasmOptConfig>,
// pub wasm_snip: Option<WasmSnipConfig>,
wasm_bindgen: Option<bool>,
}

#[derive(Deserialize)]
Expand Down Expand Up @@ -66,13 +116,48 @@ struct Repository {
url: String,
}

fn read_cargo_toml(path: &Path) -> Result<CargoManifest, Error> {
/// Read cargo toml.
pub fn read_cargo_toml(path: &PathBuf) -> Result<CargoManifest, Error> {
let manifest_path = path.join("Cargo.toml");
let mut cargo_file = File::open(manifest_path)?;
let mut cargo_contents = String::new();
cargo_file.read_to_string(&mut cargo_contents)?;

Ok(toml::from_str(&cargo_contents)?)
let build_toml = cargo_contents.parse::<toml::Value>()?;
let build_config = parse_build_config(&build_toml);

let mut cargo_manifest: CargoManifest = toml::from_str(&cargo_contents)?;
cargo_manifest.build_config = build_config.ok();

Ok(cargo_manifest)
}

fn parse_build_config(cargo_toml: &toml::Value) -> Result<BuildConfig, Error> {
let metadata = cargo_toml
.get("package")
.and_then(|table| table.get("metadata"))
.and_then(|table| table.get("wasm-pack"));
let metadata = match metadata {
None => return Ok(BuildConfig::default()),
Some(metadata) => metadata
.as_table()
.ok_or_else(|| format_err!("wasm-pack configuration invalid: {:?}", metadata))?,
};

let mut config = BuildConfig::default();
for (key, value) in metadata {
match (key.as_str(), value.clone()) {
("build", Value::Table(t)) => config.build = Some(BuildMode::from_table(t)?),
(key, value) => Err(format_err!(
"unexpected \
`package.metadata.wasm_pack` key `{}` with value `{}`",
key,
value
))?,
}
}

Ok(config)
}

impl CargoManifest {
Expand Down Expand Up @@ -129,9 +214,100 @@ impl CargoManifest {
}
}

impl BuildMode {
fn from_table(table: Table) -> Result<BuildMode, Error> {
let mut build_mode = BuildMode::default();
for (key, value) in table {
match (key.as_str(), value.clone()) {
("debug", Value::Table(t)) => {
build_mode.debug = Some(WasmPackConfig::from_table(t)?)
}
("profiling", Value::Table(t)) => {
build_mode.profiling = Some(WasmPackConfig::from_table(t)?);
}
("production", Value::Table(t)) => {
build_mode.production = Some(WasmPackConfig::from_table(t)?);
}
(key, value) => Err(format_err!(
"unexpected \
`package.metadata.wasm_pack.build` key `{}` with value `{}`",
key,
value
))?,
}
}
Ok(build_mode)
}
}

impl WasmPackConfig {
fn from_table(table: Table) -> Result<WasmPackConfig, Error> {
let mut wasm_pack_config = WasmPackConfig::default();
for (key, value) in table {
match (key.as_str(), value.clone()) {
("debug", Value::Boolean(b)) => wasm_pack_config.debug = From::from(b),
("features", Value::Array(a)) => {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how does features work?
@ashleygwilliams impl From<Features> for CargoTomlFeaturesSection?

let mut features = Vec::new();
for value in a {
match value {
Value::String(s) => features.push(s),
_ => Err(format_err!("features must be a list of strings"))?,
}
}
wasm_pack_config.features = Some(features);
}
("rustc-opt-level", Value::String(s)) => {
wasm_pack_config.rustc_opt_level = From::from(s)
}
// ("wasm-opt", opt) => match opt {
// WasmOpt::Bool(Value::Boolean(b)) => wasm_pack_config.wasm_opt = From::from(b),
// WasmOpt::Config(Value::Table(t)) => {
// let mut opt_config = WasmOptConfig::default();
// for (key, value) in t {
// match (key.as_str(), value.clone()) {
// ("opt-level", Value::String(s)) => {
// opt_config.opt_level = From::from(s)
// }
// }
// }
// wasm_pack_config.wasm_opt = Some(opt_config);
// }
// },
// ("wasm-snip", snip) => match snip {
// WasmSnip::Bool(Value::Boolean(b)) => wasm_pack_config.wasm_snip = From::from(b),
// WasmSnip::Config(Value::Table(t)) => {
// let mut snip_config = WasmSnipConfig::default();
// for (key, value) in t {
// match (key.as_str(), value.clone()) {
// ("snip-rust-fmt-code", Value::Boolean(b)) => {
// snip_config.snip_rust_fmt_code = From::from(b)
// }
// ("snip-rust-panicking-code", Value::Boolean(b)) => {
// snip_config.snip_rust_panicking_code = From::from(b)
// }
// }
// }
// wasm_pack_config.wasm_snip = Some(snip_config);
// }
// },
("wasm-bindgen", Value::Boolean(b)) => {
wasm_pack_config.wasm_bindgen = From::from(b)
}
(key, value) => Err(format_err!(
"unexpected \
`package.metadata.wasm_pack.build.<buildmode>` key `{}` with value `{}`",
key,
value
))?,
}
}
Ok(wasm_pack_config)
}
}

/// Generate a package.json file inside in `./pkg`.
pub fn write_package_json(
path: &Path,
path: &PathBuf,
scope: &Option<String>,
disable_dts: bool,
target: &str,
Expand Down Expand Up @@ -168,20 +344,20 @@ pub fn write_package_json(
}

/// Get the crate name for the crate at the given path.
pub fn get_crate_name(path: &Path) -> Result<String, Error> {
pub fn get_crate_name(path: &PathBuf) -> Result<String, Error> {
Ok(read_cargo_toml(path)?.package.name)
}

/// Check that the crate the given path is properly configured.
pub fn check_crate_config(path: &Path, step: &Step) -> Result<(), Error> {
pub fn check_crate_config(path: &PathBuf, step: &Step) -> Result<(), Error> {
let msg = format!("{}Checking crate configuration...", emoji::WRENCH);
PBAR.step(&step, &msg);
check_wasm_bindgen(path)?;
check_crate_type(path)?;
Ok(())
}

fn check_wasm_bindgen(path: &Path) -> Result<(), Error> {
fn check_wasm_bindgen(path: &PathBuf) -> Result<(), Error> {
let cargo_toml = read_cargo_toml(path)?;
if cargo_toml
.dependencies
Expand All @@ -195,7 +371,7 @@ fn check_wasm_bindgen(path: &Path) -> Result<(), Error> {
))
}

fn check_crate_type(path: &Path) -> Result<(), Error> {
fn check_crate_type(path: &PathBuf) -> Result<(), Error> {
if read_cargo_toml(path)?.lib.map_or(false, |lib| {
lib.crate_type
.map_or(false, |types| types.iter().any(|s| s == "cdylib"))
Expand Down