use crate::CargoToml;
use crate::types::{Dependency, LibTarget, NonLibTarget, PatchSet, Profile};
use crate::error::Error;
use toml_edit::{self, Array, ArrayOfTables, Document, InlineTable, Item, Table, Value};
const DEFAULT_VERSION: &'static str = "0.1.0";
pub(crate) struct TomlOutput(CargoToml);
impl TomlOutput {
pub(crate) fn new(c: &CargoToml) -> TomlOutput {
TomlOutput(c.clone())
}
pub(crate) fn document(&self) -> Result<Document, Error> {
let mut doc = Document::new();
let mut root_table = Table::new();
self.package(&mut root_table)?;
self.badges(&mut root_table)?;
self.features(&mut root_table)?;
self.targets(&mut root_table)?;
self.dependencies(&mut root_table)?;
self.build_dependencies(&mut root_table)?;
self.dev_dependencies(&mut root_table)?;
self.target_dependencies(&mut root_table)?;
self.profiles(&mut root_table)?;
self.workspace(&mut root_table)?;
self.patches(&mut root_table)?;
self.replace(&mut root_table)?;
doc.root = Item::Table(root_table);
Ok(doc)
}
fn package(&self, root: &mut Table) -> Result<(), Error> {
let mut table = Table::new();
if let Some(ref name) = self.0.name {
insert_str(&mut table, "name", name);
} else {
return Err("must have a name".into());
}
let version = itemize(if let Some(ref version) = self.0.version {
version.as_str()
} else {
DEFAULT_VERSION
});
insert(&mut table, "version", version);
if let Some(ref authors) = self.0.authors {
let arr = slice_to_arr(&authors);
insert(&mut table, "authors", arr);
} else {
return Err("must have an author".into());
}
maybe_insert_str(&mut table, "build", &self.0.build_script);
maybe_insert_str(&mut table, "documentation", &self.0.documentation);
maybe_insert_arr(&mut table, "exclude", &self.0.exclude);
maybe_insert_arr(&mut table, "include", &self.0.include);
if let Some(publish) = self.0.publish {
insert(&mut table, "publish", itemize(publish));
}
maybe_insert_str(&mut table, "workspace", &self.0.workspace_root);
maybe_insert_str(&mut table, "description", &self.0.description);
maybe_insert_str(&mut table, "homepage", &self.0.homepage);
maybe_insert_str(&mut table, "repository", &self.0.repository);
maybe_insert_str(&mut table, "readme", &self.0.readme);
maybe_insert_arr(&mut table, "keywords", &self.0.keywords);
maybe_insert_arr(&mut table, "categories", &self.0.categories);
if let Some(ref licenses) = self.0.licenses {
let s = licenses.iter().map(|l| l.to_string()).collect::<Vec<_>>().join("/");
insert_str(&mut table, "license", &s);
}
maybe_insert_str(&mut table, "license-file", &self.0.license_file);
self.metadata(&mut table)?;
insert(root, "package", Item::Table(table));
Ok(())
}
fn metadata(&self, root: &mut Table) -> Result<(), Error> {
if let Some(ref tables) = self.0.metadata {
let mut package_metadata_table = Table::new();
package_metadata_table.set_implicit(true);
for metadata_table in tables.iter() {
let mut table = Table::new();
for (key, value) in metadata_table.data.iter() {
insert(&mut table, key, itemize(value.as_str()));
}
insert(&mut package_metadata_table, &metadata_table.label, Item::Table(table));
}
insert(root, "metadata", Item::Table(package_metadata_table));
}
Ok(())
}
fn badges(&self, root: &mut Table) -> Result<(), Error> {
let mut table = Table::new();
if let Some(ref appveyor) = self.0.appveyor {
let mut t = InlineTable::default();
insert_inline(&mut t, "repository", appveyor.repository.as_str());
if let Some(ref branch) = appveyor.branch {
insert_inline(&mut t, "branch", branch.as_str());
}
if let Some(ref service) = appveyor.service {
insert_inline(&mut t, "service", service.to_string());
}
insert(&mut table, "appveyor", itemize(Value::InlineTable(t)));
}
if let Some(ref circle_ci) = self.0.circle_ci {
let mut t = InlineTable::default();
insert_inline(&mut t, "repository", circle_ci.repository.as_str());
if let Some(ref branch) = circle_ci.branch {
insert_inline(&mut t, "branch", branch.as_str());
}
insert(&mut table, "circle-ci", itemize(Value::InlineTable(t)));
}
if let Some(ref gitlab) = self.0.gitlab {
let mut t = InlineTable::default();
insert_inline(&mut t, "repository", gitlab.repository.as_str());
if let Some(ref branch) = gitlab.branch {
insert_inline(&mut t, "branch", branch.as_str());
}
insert(&mut table, "gitlab", itemize(Value::InlineTable(t)));
}
if let Some(ref travis_ci) = self.0.travis_ci {
let mut t = InlineTable::default();
insert_inline(&mut t, "repository", travis_ci.repository.as_str());
if let Some(ref branch) = travis_ci.branch {
insert_inline(&mut t, "branch", branch.as_str());
}
insert(&mut table, "travis-ci", itemize(Value::InlineTable(t)));
}
if let Some(ref codecov) = self.0.codecov {
let mut t = InlineTable::default();
insert_inline(&mut t, "repository", codecov.repository.as_str());
if let Some(ref branch) = codecov.branch {
insert_inline(&mut t, "branch", branch.as_str());
}
if let Some(ref service) = codecov.service {
insert_inline(&mut t, "service", service.to_string());
}
insert(&mut table, "codecov", itemize(Value::InlineTable(t)));
}
if let Some(ref coveralls) = self.0.coveralls {
let mut t = InlineTable::default();
insert_inline(&mut t, "repository", coveralls.repository.as_str());
if let Some(ref branch) = coveralls.branch {
insert_inline(&mut t, "branch", branch.as_str());
}
if let Some(ref service) = coveralls.service {
insert_inline(&mut t, "service", service.to_string());
}
insert(&mut table, "coveralls", itemize(Value::InlineTable(t)));
}
if let Some(ref is_it_maintained_time) = self.0.is_it_maintained_time {
let mut t = InlineTable::default();
insert_inline(&mut t, "repository", is_it_maintained_time.as_str());
insert(&mut table, "is-it-maintained-issue-resolution", itemize(Value::InlineTable(t)));
}
if let Some(ref is_it_maintained_issues) = self.0.is_it_maintained_issues {
let mut t = InlineTable::default();
insert_inline(&mut t, "repository", is_it_maintained_issues.as_str());
insert(&mut table, "is-it-maintained-open-issues", itemize(Value::InlineTable(t)));
}
if let Some(ref maintenance) = self.0.maintenance {
let mut t = InlineTable::default();
insert_inline(&mut t, "status", maintenance.status.to_string());
insert(&mut table, "maintenance", itemize(Value::InlineTable(t)));
}
if !table.is_empty() {
insert(root, "badges", Item::Table(table));
}
Ok(())
}
fn dependencies(&self, root: &mut Table) -> Result<(), Error> {
let mut iters = Vec::new();
if let Some(ref dependencies) = self.0.dependencies {
let clone = dependencies.clone();
iters.push(clone.into_iter());
};
if let Some(ref features) = self.0.features {
for feature in features {
let clone = feature.deps.clone().into_iter().map(|mut d| d.optional(true).build()).collect::<Vec<_>>();
iters.push(clone.into_iter());
}
}
let iters = iters.into_iter().flat_map(|i| i.clone());
let deps: Vec<Dependency> = iters.collect();
if let Err(..) = self.dependency_table(root, "dependencies", &deps) {
insert(root, "dependencies", Item::Table(Table::new()));
}
Ok(())
}
fn build_dependencies(&self, root: &mut Table) -> Result<(), Error> {
if let Some(ref build_dependencies) = self.0.build_dependencies {
let _ = self.dependency_table(root, "build-dependencies", build_dependencies);
}
Ok(())
}
fn dev_dependencies(&self, root: &mut Table) -> Result<(), Error> {
if let Some(ref dev_dependencies) = self.0.dev_dependencies {
let _ = self.dependency_table(root, "dev-dependencies", dev_dependencies);
}
Ok(())
}
fn target_dependencies(&self, root: &mut Table) -> Result<(), Error> {
if let Some(ref target_dependencies) = self.0.target_dependencies {
let mut target_table = Table::new();
target_table.set_implicit(true);
for (label, deps) in target_dependencies {
let mut label_table = Table::new();
label_table.set_implicit(true);
self.dependency_table(&mut label_table, "dependencies", deps)?;
insert(&mut target_table, label, Item::Table(label_table));
}
insert(root, "target", Item::Table(target_table));
}
Ok(())
}
fn dependency_table(&self, root: &mut Table, label: &str, dependencies: &[Dependency]) -> Result<(), Error> {
let no_deps = dependencies.is_empty();
if no_deps {
return Err("dependencies is empty".into());
}
let iter = dependencies.iter();
let (inline, noninline): (Vec<_>, Vec<_>) = iter.partition(|d| d.is_inline());
let mut dep_table = self.dependencies_table(&inline);
dep_table.set_implicit(true);
for dep in noninline.into_iter() {
let item = Table::from(dep);
insert(&mut dep_table, &dep.label, Item::Table(item));
}
insert(root, label, Item::Table(dep_table));
Ok(())
}
fn dependencies_table(&self, dependencies: &[&Dependency]) -> Table {
let mut table = Table::new();
for dep in dependencies {
dep.render_into(&mut table);
}
table
}
fn profiles(&self, root_table: &mut Table) -> Result<(), Error> {
let mut profile_table = Table::new();
profile_table.set_implicit(true);
self.profile_dev(&mut profile_table)?;
self.profile_release(&mut profile_table)?;
self.profile_test(&mut profile_table)?;
self.profile_bench(&mut profile_table)?;
self.profile_doc(&mut profile_table)?;
insert(root_table, "profile", Item::Table(profile_table));
Ok(())
}
fn profile_dev(&self, profile_table: &mut Table) -> Result<(), Error> {
if let Some(ref profile_dev) = self.0.profile_dev {
let table = profile_to_table(profile_dev);
insert(profile_table, "dev", Item::Table(table));
}
Ok(())
}
fn profile_release(&self, profile_table: &mut Table) -> Result<(), Error> {
if let Some(ref profile_release) = self.0.profile_release {
let table = profile_to_table(profile_release);
insert(profile_table, "release", Item::Table(table));
}
Ok(())
}
fn profile_test(&self, profile_table: &mut Table) -> Result<(), Error> {
if let Some(ref profile_test) = self.0.profile_test {
let table = profile_to_table(profile_test);
insert(profile_table, "test", Item::Table(table));
}
Ok(())
}
fn profile_bench(&self, profile_table: &mut Table) -> Result<(), Error> {
if let Some(ref profile_bench) = self.0.profile_bench {
let table = profile_to_table(profile_bench);
insert(profile_table, "bench", Item::Table(table));
}
Ok(())
}
fn profile_doc(&self, profile_table: &mut Table) -> Result<(), Error> {
if let Some(ref profile_doc) = self.0.profile_doc {
let table = profile_to_table(profile_doc);
insert(profile_table, "doc", Item::Table(table));
}
Ok(())
}
fn features(&self, root_table: &mut Table) -> Result<(), Error> {
if let Some(ref features) = self.0.features {
let mut feature_table = Table::new();
for feature in features {
let mut labels = Vec::new();
for dep in &feature.deps {
labels.push(dep.label.to_string())
}
labels.extend(feature.features.iter().map(|s| s.to_string()));
let arr = slice_to_arr(&labels);
insert(&mut feature_table, &feature.label, arr);
}
insert(root_table, "features", Item::Table(feature_table));
}
Ok(())
}
fn workspace(&self, root_table: &mut Table) -> Result<(), Error> {
if let Some(ref workspace) = self.0.workspace {
let mut table = Table::new();
if let Some(ref members) = workspace.members {
let arr = slice_to_arr(members);
insert(&mut table, "members", arr);
}
if let Some(ref exclude) = workspace.exclude {
let arr = slice_to_arr(exclude);
insert(&mut table, "exclude", arr);
}
insert(root_table, "workspace", Item::Table(table));
}
Ok(())
}
fn targets(&self, root_table: &mut Table) -> Result<(), Error> {
if let Some(ref bins) = self.0.bins {
let item = nonlib_targets_to_item(bins);
insert(root_table, "bin", item);
}
if let Some(ref benches) = self.0.benches {
let item = nonlib_targets_to_item(benches);
insert(root_table, "bench", item);
}
if let Some(ref examples) = self.0.examples {
let item = nonlib_targets_to_item(examples);
insert(root_table, "example", item);
}
if let Some(ref tests) = self.0.tests {
let item = nonlib_targets_to_item(tests);
insert(root_table, "test", item);
}
if let Some(ref lib) = self.0.lib {
let table = Table::from(lib);
insert(root_table, "lib", Item::Table(table));
}
Ok(())
}
fn patches(&self, root_table: &mut Table) -> Result<(), Error> {
let mut patch_table = Table::new();
patch_table.set_implicit(true);
if let Some(ref patches) = self.0.patches {
for patchset in patches {
let table = self.patchset_to_table(&patchset);
insert(&mut patch_table, &patchset.label, table);
}
}
insert(root_table, "patch", Item::Table(patch_table));
Ok(())
}
fn patchset_to_table(&self, patchset: &PatchSet) -> Item {
let mut table = Table::new();
for dep in patchset.patches.iter() {
dep.render_into(&mut table);
}
Item::Table(table)
}
fn replace(&self, root_table: &mut Table) -> Result<(), Error> {
if let Some(ref replacements) = self.0.replacements {
let mut table = Table::new();
for rep in replacements.iter() {
rep.0.render_into(&mut table);
}
insert(root_table, "replace", Item::Table(table));
}
Ok(())
}
}
fn nonlib_targets_to_item(targets: &[NonLibTarget]) -> Item {
let mut array = ArrayOfTables::new();
for target in targets.iter() {
let t = Table::from(target);
array.append(t);
}
Item::ArrayOfTables(array)
}
impl<'a> From<&'a LibTarget> for Table {
fn from(t: &'a LibTarget) -> Table {
let mut table = Table::new();
if let Some(ref name) = t.name() {
insert(&mut table, "name", itemize(name.as_str()));
}
if let Some(ref path) = t.path() {
insert(&mut table, "path", itemize(path.as_str()));
}
if let Some(test) = t.test() {
insert(&mut table, "test", itemize(test));
}
if let Some(doctest) = t.doctest() {
insert(&mut table, "doctest", itemize(doctest));
}
if let Some(bench) = t.bench() {
insert(&mut table, "bench", itemize(bench));
}
if let Some(doc) = t.doc() {
insert(&mut table, "doc", itemize(doc));
}
if let Some(plugin) = t.plugin() {
insert(&mut table, "plugin", itemize(plugin));
}
if let Some(proc_macro) = t.proc_macro() {
insert(&mut table, "proc-macro", itemize(proc_macro));
}
if let Some(harness) = t.harness() {
insert(&mut table, "harness", itemize(harness));
}
if let Some(ref crate_type) = t.crate_type() {
insert(&mut table, "crate-type", itemize(crate_type.to_string()));
}
table
}
}
impl<'a> From<&'a NonLibTarget> for Table {
fn from(t: &'a NonLibTarget) -> Table {
let mut table = Table::new();
if let Some(ref name) = t.name() {
insert(&mut table, "name", itemize(name.as_str()));
}
if let Some(ref path) = t.path() {
insert(&mut table, "path", itemize(path.as_str()));
}
if let Some(test) = t.test() {
insert(&mut table, "test", itemize(test));
}
if let Some(doctest) = t.doctest() {
insert(&mut table, "doctest", itemize(doctest));
}
if let Some(bench) = t.bench() {
insert(&mut table, "bench", itemize(bench));
}
if let Some(doc) = t.doc() {
insert(&mut table, "doc", itemize(doc));
}
if let Some(plugin) = t.plugin() {
insert(&mut table, "plugin", itemize(plugin));
}
if let Some(proc_macro) = t.proc_macro() {
insert(&mut table, "proc-macro", itemize(proc_macro));
}
if let Some(harness) = t.harness() {
insert(&mut table, "harness", itemize(harness));
}
if let Some(ref required_features) = t.required_features() {
let arr = slice_to_arr(required_features);
insert(&mut table, "required-features", arr);
}
table
}
}
fn profile_to_table(profile: &Profile) -> Table {
let mut table = Table::new();
if let Some(opt_level) = profile.opt_level {
insert(&mut table, "opt-level", itemize(opt_level as i64));
}
if let Some(debug) = profile.debug {
insert(&mut table, "debug", itemize(debug));
}
if let Some(rpath) = profile.rpath {
insert(&mut table, "rpath", itemize(rpath));
}
if let Some(lto) = profile.lto {
insert(&mut table, "lto", itemize(lto));
}
if let Some(debug_assertions) = profile.debug_assertions {
insert(&mut table, "debug-assertions", itemize(debug_assertions));
}
if let Some(codegen_units) = profile.codegen_units {
insert(&mut table, "codegen-units", itemize(codegen_units as i64));
}
if let Some(ref panic) = profile.panic {
insert(&mut table, "panic", itemize(panic.to_string()));
}
table
}
fn itemize<T: Into<Value>>(ty: T) -> Item {
toml_edit::value(ty.into())
}
fn insert_inline<V: Into<Value>>(table: &mut InlineTable, key: &str, val: V) {
table.get_or_insert(key, val);
}
fn insert(table: &mut Table, key: &str, item: Item) {
*table.entry(key) = item;
}
fn insert_str(table: &mut Table, key: &str, s: &str) {
let item = itemize(s);
insert(table, key, item);
}
fn maybe_insert_str(table: &mut Table, key: &str, s: &Option<String>) {
if let &Some(ref s) = s {
insert_str(table, key, s);
}
}
fn maybe_insert_arr(table: &mut Table, key: &str, maybe_vec: &Option<Vec<String>>) {
if let &Some(ref slice) = maybe_vec {
let arr = slice_to_arr(&slice);
insert(table, key, arr);
}
}
fn slice_to_arr(v: &[String]) -> Item {
let mut arr = Array::default();
for el in v.iter() {
arr.push(el.as_str());
}
itemize(arr)
}
#[cfg(test)]
mod tests {
use crate::CargoToml;
#[test]
fn test_basics() {
let c = CargoToml::builder()
.name("my-project")
.version("0.1.1")
.author("Foo <foo@bar>")
.build().expect("Couldn't build toml");
let d = c.to_string();
assert_eq!(d, r#"
[package]
name = "my-project"
version = "0.1.1"
authors = ["Foo <foo@bar>"]
[dependencies]
"#);
}
}