diff --git a/build.rs b/build.rs index a2c690686015d80ff53cf0cddc793be73c58a6e1..1cc7f382e5bc4caff511d779191768e8eab074c8 100644 --- a/build.rs +++ b/build.rs @@ -1,5 +1,5 @@ -// Copyright 2018-2019 Parity Technologies (UK) Ltd. -// This file is part of ink!. +// Copyright 2018-2020 Parity Technologies (UK) Ltd. +// This file is part of cargo-contract. // // ink! is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by diff --git a/src/cmd/build.rs b/src/cmd/build.rs index 64984d68a0ebd318d0ee2a86a851197293511fd4..a9f34bbb4e8bc378b753fad9439da84d6ed3f868 100644 --- a/src/cmd/build.rs +++ b/src/cmd/build.rs @@ -1,5 +1,5 @@ -// Copyright 2018-2019 Parity Technologies (UK) Ltd. -// This file is part of ink!. +// Copyright 2018-2020 Parity Technologies (UK) Ltd. +// This file is part of cargo-contract. // // ink! is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by diff --git a/src/cmd/deploy.rs b/src/cmd/deploy.rs index b85ab89462d35b9d5858f21d7a74721ee1f7e3d0..53ba61dc16d27a4d120e66f51cdcded3d94b8f5b 100644 --- a/src/cmd/deploy.rs +++ b/src/cmd/deploy.rs @@ -1,5 +1,5 @@ -// Copyright 2018-2019 Parity Technologies (UK) Ltd. -// This file is part of ink!. +// Copyright 2018-2020 Parity Technologies (UK) Ltd. +// This file is part of cargo-contract. // // ink! is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by diff --git a/src/cmd/instantiate.rs b/src/cmd/instantiate.rs index 4f1f83c392c0546843b0a5ea281eb06181680241..29fc4d50d05523f34978ce3de4805b007a659e61 100644 --- a/src/cmd/instantiate.rs +++ b/src/cmd/instantiate.rs @@ -1,5 +1,5 @@ -// Copyright 2018-2019 Parity Technologies (UK) Ltd. -// This file is part of ink!. +// Copyright 2018-2020 Parity Technologies (UK) Ltd. +// This file is part of cargo-contract. // // ink! is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by diff --git a/src/cmd/metadata.rs b/src/cmd/metadata.rs index ca70f176426e8daaf56a7a670deb78315ab23cad..f87b1a7a7f1f19883bcd07ffb4c7810400726706 100644 --- a/src/cmd/metadata.rs +++ b/src/cmd/metadata.rs @@ -1,5 +1,5 @@ -// Copyright 2018-2019 Parity Technologies (UK) Ltd. -// This file is part of ink!. +// Copyright 2018-2020 Parity Technologies (UK) Ltd. +// This file is part of cargo-contract. // // ink! is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -23,9 +23,9 @@ use anyhow::Result; const METADATA_FILE: &str = "metadata.json"; -/// Executes build of the smart-contract which produces a wasm binary that is ready for deploying. +/// Generates a file with metadata describing the ABI of the smart-contract. /// -/// It does so by invoking build by cargo and then post processing the final binary. +/// It does so by generating and invoking a temporary workspace member. pub(crate) fn execute_generate_metadata( original_manifest_path: ManifestPath, verbosity: Option, diff --git a/src/cmd/mod.rs b/src/cmd/mod.rs index c47ca2c272b4a2d442f204e38e65a3e37187d490..b3678e40e4e2a04e5522d04e6b054f85659e8fcf 100644 --- a/src/cmd/mod.rs +++ b/src/cmd/mod.rs @@ -1,5 +1,5 @@ -// Copyright 2018-2019 Parity Technologies (UK) Ltd. -// This file is part of ink!. +// Copyright 2018-2020 Parity Technologies (UK) Ltd. +// This file is part of cargo-contract. // // ink! is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by diff --git a/src/cmd/new.rs b/src/cmd/new.rs index 4291ed50e261244199750aed1d2e9cdb56388f40..bb9891dc4a2d2ff220668fa20cfcf9ea7b3749d0 100644 --- a/src/cmd/new.rs +++ b/src/cmd/new.rs @@ -1,5 +1,5 @@ -// Copyright 2018-2019 Parity Technologies (UK) Ltd. -// This file is part of ink!. +// Copyright 2018-2020 Parity Technologies (UK) Ltd. +// This file is part of cargo-contract. // // ink! is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by diff --git a/src/main.rs b/src/main.rs index adeae4d3b4af5c8f357ccc088b6f40d5a6af2a06..3a9429c694ab2c866ec7bb5caf931e77de2220e4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,5 @@ -// Copyright 2018-2019 Parity Technologies (UK) Ltd. -// This file is part of ink!. +// Copyright 2018-2020 Parity Technologies (UK) Ltd. +// This file is part of cargo-contract. // // ink! is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by diff --git a/src/util.rs b/src/util.rs index 4dd2560277851c74aab1db30d51a624c80bfc955..3a6556e7687d5d22365f7c61d12af2f702cf840e 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,5 +1,5 @@ -// Copyright 2018-2019 Parity Technologies (UK) Ltd. -// This file is part of ink!. +// Copyright 2018-2020 Parity Technologies (UK) Ltd. +// This file is part of cargo-contract. // // ink! is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by diff --git a/src/workspace.rs b/src/workspace/manifest.rs similarity index 51% rename from src/workspace.rs rename to src/workspace/manifest.rs index 51ad47f3e57b17040f94bda4a05a422da3b2897a..8dfafa381e7ec6adce20708320c79919a3ed87b5 100644 --- a/src/workspace.rs +++ b/src/workspace/manifest.rs @@ -1,5 +1,5 @@ -// Copyright 2018-2019 Parity Technologies (UK) Ltd. -// This file is part of ink!. +// Copyright 2018-2020 Parity Technologies (UK) Ltd. +// This file is part of cargo-contract. // // ink! is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -15,10 +15,11 @@ // along with ink!. If not, see . use anyhow::{Context, Result}; -use cargo_metadata::{Metadata as CargoMetadata, Package, PackageId}; + +use super::Profile; use std::convert::{TryFrom, TryInto}; use std::{ - collections::{HashMap, HashSet}, + collections::HashSet, fs, path::{Path, PathBuf}, }; @@ -194,7 +195,7 @@ impl Manifest { /// - `[dependencies]` /// /// Dependencies with package names specified in `exclude_deps` will not be rewritten. - fn rewrite_relative_paths(&mut self, exclude_deps: I) -> Result<&mut Self> + pub(super) fn rewrite_relative_paths(&mut self, exclude_deps: I) -> Result<&mut Self> where I: IntoIterator, S: AsRef, @@ -312,281 +313,3 @@ fn crate_type_exists(crate_type: &str, crate_types: &value::Array) -> bool { .iter() .any(|v| v.as_str().map_or(false, |s| s == crate_type)) } - -/// Make a copy of a cargo workspace, maintaining only the directory structure and manifest -/// files. Relative paths to source files and non-workspace dependencies are rewritten to absolute -/// paths to the original locations. -/// -/// This allows custom amendments to be made to the manifest files without editing the originals -/// directly. -pub struct Workspace { - workspace_root: PathBuf, - root_package: PackageId, - members: HashMap, -} - -impl Workspace { - /// Create a new Workspace from the supplied cargo metadata. - pub fn new(metadata: &CargoMetadata, root_package: &PackageId) -> Result { - let member_manifest = |package_id: &PackageId| -> Result<(PackageId, (Package, Manifest))> { - let package = metadata - .packages - .iter() - .find(|p| p.id == *package_id) - .expect(&format!( - "Package '{}' is a member and should be in the packages list", - package_id - )); - let manifest = Manifest::new(&package.manifest_path)?; - Ok((package_id.clone(), (package.clone(), manifest))) - }; - - let members = metadata - .workspace_members - .iter() - .map(member_manifest) - .collect::>>()?; - - if !members.contains_key(root_package) { - anyhow::bail!("The root package should be a workspace member") - } - - Ok(Workspace { - workspace_root: metadata.workspace_root.clone(), - root_package: root_package.clone(), - members, - }) - } - - /// Amend the root package manifest using the supplied function. - /// - /// # Note - /// - /// The root package is the current workspace package being built, not to be confused with - /// the workspace root (where the top level workspace Cargo.toml is defined). - pub fn with_root_package_manifest(&mut self, f: F) -> Result<&mut Self> - where - F: FnOnce(&mut Manifest) -> Result<()>, - { - let root_package_manifest = self - .members - .get_mut(&self.root_package) - .map(|(_, m)| m) - .expect("The root package should be a workspace member"); - f(root_package_manifest)?; - Ok(self) - } - - /// Writes the amended manifests to the `target` directory, retaining the workspace directory - /// structure, but only with the `Cargo.toml` files. - /// - /// Relative paths will be rewritten to absolute paths from the original workspace root, except - /// intra-workspace relative dependency paths which will be preserved. - /// - /// Returns the paths of the new manifests. - pub fn write>(&mut self, target: P) -> Result> { - let exclude_member_package_names = self - .members - .iter() - .map(|(_, (p, _))| p.name.clone()) - .collect::>(); - let mut new_manifest_paths = Vec::new(); - for (package_id, (package, manifest)) in self.members.iter_mut() { - // replace the original workspace root with the temporary directory - let mut new_path: PathBuf = target.as_ref().into(); - new_path.push(package.manifest_path.strip_prefix(&self.workspace_root)?); - let new_manifest = ManifestPath::new(new_path)?; - - manifest.rewrite_relative_paths(&exclude_member_package_names)?; - manifest.write(&new_manifest)?; - - new_manifest_paths.push((package_id.clone(), new_manifest)); - } - Ok(new_manifest_paths) - } - - /// Copy the workspace with amended manifest files to a temporary directory, executing the - /// supplied function with the root manifest path before the directory is cleaned up. - pub fn using_temp(&mut self, f: F) -> Result<()> - where - F: FnOnce(&ManifestPath) -> Result<()>, - { - let tmp_dir = tempfile::Builder::new() - .prefix(".cargo-contract_") - .tempdir()?; - log::debug!("Using temp workspace at '{}'", tmp_dir.path().display()); - let new_paths = self.write(&tmp_dir)?; - let root_manifest_path = new_paths - .iter() - .find_map(|(pid, path)| { - if *pid == self.root_package { - Some(path) - } else { - None - } - }) - .expect("root package should be a member of the temp workspace"); - f(root_manifest_path) - } -} - -/// Subset of cargo profile settings to configure defaults for building contracts -pub struct Profile { - opt_level: OptLevel, - lto: Lto, - // `None` means use rustc default. - codegen_units: Option, - overflow_checks: bool, - panic: PanicStrategy, -} - -impl Profile { - /// The preferred set of defaults for compiling a release build of a contract - pub fn default_contract_release() -> Profile { - Profile { - opt_level: OptLevel::Z, - lto: Lto::Fat, - codegen_units: Some(1), - overflow_checks: true, - panic: PanicStrategy::Abort, - } - } - - /// Set any unset profile settings from the config. - /// - /// Therefore: - /// - If the user has explicitly defined a profile setting, it will not be overwritten. - /// - If a profile setting is not defined, the value from this profile instance will be added - fn merge(&self, profile: &mut value::Table) { - let mut set_value_if_vacant = |key: &'static str, value: value::Value| { - if !profile.contains_key(key) { - profile.insert(key.into(), value); - } - }; - set_value_if_vacant("opt-level", self.opt_level.to_toml_value()); - set_value_if_vacant("lto", self.lto.to_toml_value()); - if let Some(codegen_units) = self.codegen_units { - set_value_if_vacant("codegen-units", codegen_units.into()); - } - set_value_if_vacant("overflow-checks", self.overflow_checks.into()); - set_value_if_vacant("panic", self.panic.to_toml_value()); - } -} - -/// The [`opt-level`](https://doc.rust-lang.org/cargo/reference/profiles.html#opt-level) setting -#[allow(unused)] -#[derive(Clone, Copy)] -pub enum OptLevel { - NoOptimizations, - O1, - O2, - O3, - S, - Z, -} - -impl OptLevel { - fn to_toml_value(&self) -> value::Value { - match self { - OptLevel::NoOptimizations => 0.into(), - OptLevel::O1 => 1.into(), - OptLevel::O2 => 2.into(), - OptLevel::O3 => 3.into(), - OptLevel::S => "s".into(), - OptLevel::Z => "z".into(), - } - } -} - -/// The [`link-time-optimization`](https://doc.rust-lang.org/cargo/reference/profiles.html#lto) setting. -#[derive(Clone, Copy)] -#[allow(unused)] -pub enum Lto { - /// Sets `lto = false` - ThinLocal, - /// Sets `lto = "fat"`, the equivalent of `lto = true` - Fat, - /// Sets `lto = "thin"` - Thin, - /// Sets `lto = "off"` - Off, -} - -impl Lto { - fn to_toml_value(&self) -> value::Value { - match self { - Lto::ThinLocal => value::Value::Boolean(false), - Lto::Fat => value::Value::String("fat".into()), - Lto::Thin => value::Value::String("thin".into()), - Lto::Off => value::Value::String("off".into()), - } - } -} - -/// The `panic` setting. -#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, PartialOrd, Ord)] -#[allow(unused)] -pub enum PanicStrategy { - Unwind, - Abort, -} - -impl PanicStrategy { - fn to_toml_value(&self) -> value::Value { - match self { - PanicStrategy::Unwind => "unwind".into(), - PanicStrategy::Abort => "abort".into(), - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use pretty_assertions::assert_eq; - - #[test] - fn merge_profile_inserts_preferred_defaults() { - let profile = Profile::default_contract_release(); - - // no `[profile.release]` section specified - let manifest_toml = ""; - let mut expected = toml::value::Table::new(); - expected.insert("opt-level".into(), value::Value::String("z".into())); - expected.insert("lto".into(), value::Value::String("fat".into())); - expected.insert("codegen-units".into(), value::Value::Integer(1)); - expected.insert("overflow-checks".into(), value::Value::Boolean(true)); - expected.insert("panic".into(), value::Value::String("abort".into())); - - let mut manifest_profile = toml::from_str(manifest_toml).unwrap(); - - profile.merge(&mut manifest_profile); - - assert_eq!(expected, manifest_profile) - } - - #[test] - fn merge_profile_preserves_user_defined_settings() { - let profile = Profile::default_contract_release(); - - let manifest_toml = r#" - panic = "unwind" - lto = false - opt-level = 3 - overflow-checks = false - codegen-units = 256 - "#; - let mut expected = toml::value::Table::new(); - expected.insert("opt-level".into(), value::Value::Integer(3)); - expected.insert("lto".into(), value::Value::Boolean(false)); - expected.insert("codegen-units".into(), value::Value::Integer(256)); - expected.insert("overflow-checks".into(), value::Value::Boolean(false)); - expected.insert("panic".into(), value::Value::String("unwind".into())); - - let mut manifest_profile = toml::from_str(manifest_toml).unwrap(); - - profile.merge(&mut manifest_profile); - - assert_eq!(expected, manifest_profile) - } -} diff --git a/src/workspace/mod.rs b/src/workspace/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..7bd0dda04c828b4988f897e0dff2f72123fc1392 --- /dev/null +++ b/src/workspace/mod.rs @@ -0,0 +1,149 @@ +// Copyright 2018-2020 Parity Technologies (UK) Ltd. +// This file is part of cargo-contract. +// +// ink! 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. +// +// ink! 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 ink!. If not, see . + +mod manifest; +mod profile; + +#[doc(inline)] +pub use self::{ + manifest::{Manifest, ManifestPath}, + profile::Profile, +}; + +use anyhow::Result; +use cargo_metadata::{Metadata as CargoMetadata, Package, PackageId}; + +use std::{ + collections::HashMap, + path::{Path, PathBuf}, +}; + +/// Make a copy of a cargo workspace, maintaining only the directory structure and manifest +/// files. Relative paths to source files and non-workspace dependencies are rewritten to absolute +/// paths to the original locations. +/// +/// This allows custom amendments to be made to the manifest files without editing the originals +/// directly. +pub struct Workspace { + workspace_root: PathBuf, + root_package: PackageId, + members: HashMap, +} + +impl Workspace { + /// Create a new Workspace from the supplied cargo metadata. + pub fn new(metadata: &CargoMetadata, root_package: &PackageId) -> Result { + let member_manifest = |package_id: &PackageId| -> Result<(PackageId, (Package, Manifest))> { + let package = metadata + .packages + .iter() + .find(|p| p.id == *package_id) + .expect(&format!( + "Package '{}' is a member and should be in the packages list", + package_id + )); + let manifest = Manifest::new(&package.manifest_path)?; + Ok((package_id.clone(), (package.clone(), manifest))) + }; + + let members = metadata + .workspace_members + .iter() + .map(member_manifest) + .collect::>>()?; + + if !members.contains_key(root_package) { + anyhow::bail!("The root package should be a workspace member") + } + + Ok(Workspace { + workspace_root: metadata.workspace_root.clone(), + root_package: root_package.clone(), + members, + }) + } + + /// Amend the root package manifest using the supplied function. + /// + /// # Note + /// + /// The root package is the current workspace package being built, not to be confused with + /// the workspace root (where the top level workspace Cargo.toml is defined). + pub fn with_root_package_manifest(&mut self, f: F) -> Result<&mut Self> + where + F: FnOnce(&mut Manifest) -> Result<()>, + { + let root_package_manifest = self + .members + .get_mut(&self.root_package) + .map(|(_, m)| m) + .expect("The root package should be a workspace member"); + f(root_package_manifest)?; + Ok(self) + } + + /// Writes the amended manifests to the `target` directory, retaining the workspace directory + /// structure, but only with the `Cargo.toml` files. + /// + /// Relative paths will be rewritten to absolute paths from the original workspace root, except + /// intra-workspace relative dependency paths which will be preserved. + /// + /// Returns the paths of the new manifests. + pub fn write>(&mut self, target: P) -> Result> { + let exclude_member_package_names = self + .members + .iter() + .map(|(_, (p, _))| p.name.clone()) + .collect::>(); + let mut new_manifest_paths = Vec::new(); + for (package_id, (package, manifest)) in self.members.iter_mut() { + // replace the original workspace root with the temporary directory + let mut new_path: PathBuf = target.as_ref().into(); + new_path.push(package.manifest_path.strip_prefix(&self.workspace_root)?); + let new_manifest = ManifestPath::new(new_path)?; + + manifest.rewrite_relative_paths(&exclude_member_package_names)?; + manifest.write(&new_manifest)?; + + new_manifest_paths.push((package_id.clone(), new_manifest)); + } + Ok(new_manifest_paths) + } + + /// Copy the workspace with amended manifest files to a temporary directory, executing the + /// supplied function with the root manifest path before the directory is cleaned up. + pub fn using_temp(&mut self, f: F) -> Result<()> + where + F: FnOnce(&ManifestPath) -> Result<()>, + { + let tmp_dir = tempfile::Builder::new() + .prefix(".cargo-contract_") + .tempdir()?; + log::debug!("Using temp workspace at '{}'", tmp_dir.path().display()); + let new_paths = self.write(&tmp_dir)?; + let root_manifest_path = new_paths + .iter() + .find_map(|(pid, path)| { + if *pid == self.root_package { + Some(path) + } else { + None + } + }) + .expect("root package should be a member of the temp workspace"); + f(root_manifest_path) + } +} diff --git a/src/workspace/profile.rs b/src/workspace/profile.rs new file mode 100644 index 0000000000000000000000000000000000000000..2946677d38bcb2500be948a6aa150021d1e7229e --- /dev/null +++ b/src/workspace/profile.rs @@ -0,0 +1,178 @@ +// Copyright 2018-2020 Parity Technologies (UK) Ltd. +// This file is part of cargo-contract. +// +// ink! 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. +// +// ink! 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 ink!. If not, see . + +use toml::value; + +/// Subset of cargo profile settings to configure defaults for building contracts +pub struct Profile { + opt_level: OptLevel, + lto: Lto, + // `None` means use rustc default. + codegen_units: Option, + overflow_checks: bool, + panic: PanicStrategy, +} + +impl Profile { + /// The preferred set of defaults for compiling a release build of a contract + pub fn default_contract_release() -> Profile { + Profile { + opt_level: OptLevel::Z, + lto: Lto::Fat, + codegen_units: Some(1), + overflow_checks: true, + panic: PanicStrategy::Abort, + } + } + + /// Set any unset profile settings from the config. + /// + /// Therefore: + /// - If the user has explicitly defined a profile setting, it will not be overwritten. + /// - If a profile setting is not defined, the value from this profile instance will be added + pub(super) fn merge(&self, profile: &mut value::Table) { + let mut set_value_if_vacant = |key: &'static str, value: value::Value| { + if !profile.contains_key(key) { + profile.insert(key.into(), value); + } + }; + set_value_if_vacant("opt-level", self.opt_level.to_toml_value()); + set_value_if_vacant("lto", self.lto.to_toml_value()); + if let Some(codegen_units) = self.codegen_units { + set_value_if_vacant("codegen-units", codegen_units.into()); + } + set_value_if_vacant("overflow-checks", self.overflow_checks.into()); + set_value_if_vacant("panic", self.panic.to_toml_value()); + } +} + +/// The [`opt-level`](https://doc.rust-lang.org/cargo/reference/profiles.html#opt-level) setting +#[allow(unused)] +#[derive(Clone, Copy)] +pub enum OptLevel { + NoOptimizations, + O1, + O2, + O3, + S, + Z, +} + +impl OptLevel { + fn to_toml_value(&self) -> value::Value { + match self { + OptLevel::NoOptimizations => 0.into(), + OptLevel::O1 => 1.into(), + OptLevel::O2 => 2.into(), + OptLevel::O3 => 3.into(), + OptLevel::S => "s".into(), + OptLevel::Z => "z".into(), + } + } +} + +/// The [`link-time-optimization`](https://doc.rust-lang.org/cargo/reference/profiles.html#lto) setting. +#[derive(Clone, Copy)] +#[allow(unused)] +pub enum Lto { + /// Sets `lto = false` + ThinLocal, + /// Sets `lto = "fat"`, the equivalent of `lto = true` + Fat, + /// Sets `lto = "thin"` + Thin, + /// Sets `lto = "off"` + Off, +} + +impl Lto { + fn to_toml_value(&self) -> value::Value { + match self { + Lto::ThinLocal => false.into(), + Lto::Fat => "fat".into(), + Lto::Thin => "thin".into(), + Lto::Off => "off".into(), + } + } +} + +/// The `panic` setting. +#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, PartialOrd, Ord)] +#[allow(unused)] +pub enum PanicStrategy { + Unwind, + Abort, +} + +impl PanicStrategy { + fn to_toml_value(&self) -> value::Value { + match self { + PanicStrategy::Unwind => "unwind".into(), + PanicStrategy::Abort => "abort".into(), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use pretty_assertions::assert_eq; + + #[test] + fn merge_profile_inserts_preferred_defaults() { + let profile = Profile::default_contract_release(); + + // no `[profile.release]` section specified + let manifest_toml = ""; + let mut expected = toml::value::Table::new(); + expected.insert("opt-level".into(), value::Value::String("z".into())); + expected.insert("lto".into(), value::Value::String("fat".into())); + expected.insert("codegen-units".into(), value::Value::Integer(1)); + expected.insert("overflow-checks".into(), value::Value::Boolean(true)); + expected.insert("panic".into(), value::Value::String("abort".into())); + + let mut manifest_profile = toml::from_str(manifest_toml).unwrap(); + + profile.merge(&mut manifest_profile); + + assert_eq!(expected, manifest_profile) + } + + #[test] + fn merge_profile_preserves_user_defined_settings() { + let profile = Profile::default_contract_release(); + + let manifest_toml = r#" + panic = "unwind" + lto = false + opt-level = 3 + overflow-checks = false + codegen-units = 256 + "#; + let mut expected = toml::value::Table::new(); + expected.insert("opt-level".into(), value::Value::Integer(3)); + expected.insert("lto".into(), value::Value::Boolean(false)); + expected.insert("codegen-units".into(), value::Value::Integer(256)); + expected.insert("overflow-checks".into(), value::Value::Boolean(false)); + expected.insert("panic".into(), value::Value::String("unwind".into())); + + let mut manifest_profile = toml::from_str(manifest_toml).unwrap(); + + profile.merge(&mut manifest_profile); + + assert_eq!(expected, manifest_profile) + } +}