Skip to main content

cargo_toml_builder/
toml.rs

1//! Methods for turning data structure into a toml document
2
3use crate::CargoToml;
4use crate::types::{Dependency, LibTarget, NonLibTarget, PatchSet, Profile};
5use crate::error::Error;
6
7use toml_edit::{self, Array, ArrayOfTables, Document, InlineTable, Item, Table, Value};
8
9const DEFAULT_VERSION: &'static str = "0.1.0";
10
11pub(crate) struct TomlOutput(CargoToml);
12
13impl TomlOutput {
14    pub(crate) fn new(c: &CargoToml) -> TomlOutput {
15        TomlOutput(c.clone())
16    }
17
18
19    pub(crate) fn document(&self) -> Result<Document, Error> {
20        let mut doc = Document::new();
21        let mut root_table = Table::new();
22
23        self.package(&mut root_table)?;
24        self.badges(&mut root_table)?;
25        self.features(&mut root_table)?;
26        self.targets(&mut root_table)?;
27        self.dependencies(&mut root_table)?;
28        self.build_dependencies(&mut root_table)?;
29        self.dev_dependencies(&mut root_table)?;
30        self.target_dependencies(&mut root_table)?;
31        self.profiles(&mut root_table)?;
32        self.workspace(&mut root_table)?;
33        self.patches(&mut root_table)?;
34        self.replace(&mut root_table)?;
35
36        doc.root = Item::Table(root_table);
37        Ok(doc)
38    }
39
40    // [package]
41    fn package(&self, root: &mut Table) -> Result<(), Error> {
42        let mut table = Table::new();
43
44        // name
45        if let Some(ref name) = self.0.name {
46            insert_str(&mut table, "name", name);
47        } else {
48            return Err("must have a name".into());
49        }
50
51        // version
52        let version = itemize(if let Some(ref version) = self.0.version {
53            version.as_str()
54        } else {
55            DEFAULT_VERSION
56        });
57        insert(&mut table, "version", version);
58
59        // authors
60        if let Some(ref authors) = self.0.authors {
61            let arr = slice_to_arr(&authors);
62            insert(&mut table, "authors", arr);
63        } else {
64            return Err("must have an author".into());
65        }
66
67        // build
68        maybe_insert_str(&mut table, "build", &self.0.build_script);
69
70        // documentation
71        maybe_insert_str(&mut table, "documentation", &self.0.documentation);
72
73        // exclude
74        maybe_insert_arr(&mut table, "exclude", &self.0.exclude);
75
76        // include
77        maybe_insert_arr(&mut table, "include", &self.0.include);
78
79        // publish
80        if let Some(publish) = self.0.publish {
81            insert(&mut table, "publish", itemize(publish));
82        }
83
84        // workspace
85        maybe_insert_str(&mut table, "workspace", &self.0.workspace_root);
86
87        // description
88        maybe_insert_str(&mut table, "description", &self.0.description);
89
90        // homepage
91        maybe_insert_str(&mut table, "homepage", &self.0.homepage);
92
93        // repository
94        maybe_insert_str(&mut table, "repository", &self.0.repository);
95
96        // readme
97        maybe_insert_str(&mut table, "readme", &self.0.readme);
98
99        // keywords
100        maybe_insert_arr(&mut table, "keywords", &self.0.keywords);
101
102        // categories
103        maybe_insert_arr(&mut table, "categories", &self.0.categories);
104
105        // license
106        if let Some(ref licenses) = self.0.licenses {
107            let s = licenses.iter().map(|l| l.to_string()).collect::<Vec<_>>().join("/");
108            insert_str(&mut table, "license", &s);
109        }
110
111        // license-file
112        maybe_insert_str(&mut table, "license-file", &self.0.license_file);
113
114        self.metadata(&mut table)?;
115        insert(root, "package", Item::Table(table));
116        Ok(())
117    }
118
119    fn metadata(&self, root: &mut Table) -> Result<(), Error> {
120        if let Some(ref tables) = self.0.metadata {
121            let mut package_metadata_table = Table::new();
122            package_metadata_table.set_implicit(true);
123
124            for metadata_table in tables.iter() {
125                let mut table = Table::new();
126                for (key, value) in metadata_table.data.iter() {
127                    insert(&mut table, key, itemize(value.as_str()));
128                }
129                insert(&mut package_metadata_table, &metadata_table.label, Item::Table(table));
130            }
131
132            insert(root, "metadata", Item::Table(package_metadata_table));
133        }
134        Ok(())
135    }
136
137    /*
138     *  [badges]
139     *
140     *  appveyor: Option<Appveyor>,
141     *  circle_ci: Option<CircleCi>,
142     *  gitlab: Option<Gitlab>,
143     *  travis_ci: Option<TravisCi>,
144     *  codecov: Option<Codecov>,
145     *  coveralls: Option<Coveralls>,
146     *  is_it_maintained_time: Option<String>,
147     *  is_it_maintained_issues: Option<String>,
148     *  maintenance: Option<Maintenance>,
149     */
150    fn badges(&self, root: &mut Table) -> Result<(), Error> {
151        let mut table = Table::new();
152
153        // appveyor = { ... }
154        if let Some(ref appveyor) = self.0.appveyor {
155            let mut t = InlineTable::default();
156            insert_inline(&mut t, "repository", appveyor.repository.as_str());
157            if let Some(ref branch) = appveyor.branch {
158                insert_inline(&mut t, "branch", branch.as_str());
159            }
160            if let Some(ref service) = appveyor.service {
161                insert_inline(&mut t, "service", service.to_string());
162            }
163            insert(&mut table, "appveyor", itemize(Value::InlineTable(t)));
164        }
165
166        // circe-ci = { ... }
167        if let Some(ref circle_ci) = self.0.circle_ci {
168            let mut t = InlineTable::default();
169            insert_inline(&mut t, "repository", circle_ci.repository.as_str());
170            if let Some(ref branch) = circle_ci.branch {
171                insert_inline(&mut t, "branch", branch.as_str());
172            }
173            insert(&mut table, "circle-ci", itemize(Value::InlineTable(t)));
174        }
175
176        // gitlab = { ... }
177        if let Some(ref gitlab) = self.0.gitlab {
178            let mut t = InlineTable::default();
179            insert_inline(&mut t, "repository", gitlab.repository.as_str());
180            if let Some(ref branch) = gitlab.branch {
181                insert_inline(&mut t, "branch", branch.as_str());
182            }
183            insert(&mut table, "gitlab", itemize(Value::InlineTable(t)));
184        }
185
186        // travis-ci = { ... }
187        if let Some(ref travis_ci) = self.0.travis_ci {
188            let mut t = InlineTable::default();
189            insert_inline(&mut t, "repository", travis_ci.repository.as_str());
190            if let Some(ref branch) = travis_ci.branch {
191                insert_inline(&mut t, "branch", branch.as_str());
192            }
193            insert(&mut table, "travis-ci", itemize(Value::InlineTable(t)));
194        }
195
196        // codecov = { ... }
197        if let Some(ref codecov) = self.0.codecov {
198            let mut t = InlineTable::default();
199            insert_inline(&mut t, "repository", codecov.repository.as_str());
200            if let Some(ref branch) = codecov.branch {
201                insert_inline(&mut t, "branch", branch.as_str());
202            }
203            if let Some(ref service) = codecov.service {
204                insert_inline(&mut t, "service", service.to_string());
205            }
206            insert(&mut table, "codecov", itemize(Value::InlineTable(t)));
207        }
208
209        // coveralls = { ... }
210        if let Some(ref coveralls) = self.0.coveralls {
211            let mut t = InlineTable::default();
212            insert_inline(&mut t, "repository", coveralls.repository.as_str());
213            if let Some(ref branch) = coveralls.branch {
214                insert_inline(&mut t, "branch", branch.as_str());
215            }
216            if let Some(ref service) = coveralls.service {
217                insert_inline(&mut t, "service", service.to_string());
218            }
219            insert(&mut table, "coveralls", itemize(Value::InlineTable(t)));
220        }
221
222        // is-it-maintained-issue-resolution = { ... }
223        if let Some(ref is_it_maintained_time) = self.0.is_it_maintained_time {
224            let mut t = InlineTable::default();
225            insert_inline(&mut t, "repository", is_it_maintained_time.as_str());
226            insert(&mut table, "is-it-maintained-issue-resolution", itemize(Value::InlineTable(t)));
227        }
228
229        // is-it-maintained-open-issues = { ... }
230        if let Some(ref is_it_maintained_issues) = self.0.is_it_maintained_issues {
231            let mut t = InlineTable::default();
232            insert_inline(&mut t, "repository", is_it_maintained_issues.as_str());
233            insert(&mut table, "is-it-maintained-open-issues", itemize(Value::InlineTable(t)));
234        }
235
236        // maintenance = { ... }
237        if let Some(ref maintenance) = self.0.maintenance {
238            let mut t = InlineTable::default();
239            insert_inline(&mut t, "status", maintenance.status.to_string());
240            insert(&mut table, "maintenance", itemize(Value::InlineTable(t)));
241        }
242
243        if !table.is_empty() {
244            insert(root, "badges", Item::Table(table));
245        }
246        Ok(())
247    }
248
249    // [dependencies]
250    fn dependencies(&self, root: &mut Table) -> Result<(), Error> {
251        let mut iters = Vec::new();
252        if let Some(ref dependencies) = self.0.dependencies {
253            let clone = dependencies.clone();
254            iters.push(clone.into_iter());
255        };
256
257        if let Some(ref features) = self.0.features {
258            for feature in features {
259                let clone = feature.deps.clone().into_iter().map(|mut d| d.optional(true).build()).collect::<Vec<_>>();
260                iters.push(clone.into_iter());
261            }
262        }
263
264        let iters = iters.into_iter().flat_map(|i| i.clone());
265        let deps: Vec<Dependency> = iters.collect();
266        if let Err(..) = self.dependency_table(root, "dependencies", &deps) {
267            insert(root, "dependencies", Item::Table(Table::new()));
268        }
269        Ok(())
270    }
271
272    // [build-dependencies]
273    fn build_dependencies(&self, root: &mut Table) -> Result<(), Error> {
274        if let Some(ref build_dependencies) = self.0.build_dependencies {
275            let _ = self.dependency_table(root, "build-dependencies", build_dependencies);
276        }
277        Ok(())
278    }
279
280    // [dev-dependencies]
281    fn dev_dependencies(&self, root: &mut Table) -> Result<(), Error> {
282        if let Some(ref dev_dependencies) = self.0.dev_dependencies {
283            let _ = self.dependency_table(root, "dev-dependencies", dev_dependencies);
284        }
285        Ok(())
286    }
287
288    // [target.*.dependencies]
289    fn target_dependencies(&self, root: &mut Table) -> Result<(), Error> {
290        if let Some(ref target_dependencies) = self.0.target_dependencies {
291            let mut target_table = Table::new();
292            target_table.set_implicit(true);
293            for (label, deps) in target_dependencies {
294                let mut label_table = Table::new();
295                label_table.set_implicit(true);
296
297                self.dependency_table(&mut label_table, "dependencies", deps)?;
298
299                insert(&mut target_table, label, Item::Table(label_table));
300            }
301
302            insert(root, "target", Item::Table(target_table));
303        }
304        Ok(())
305    }
306
307    fn dependency_table(&self, root: &mut Table, label: &str, dependencies: &[Dependency]) -> Result<(), Error> {
308        let no_deps = dependencies.is_empty();
309        if no_deps {
310            return Err("dependencies is empty".into());
311        }
312
313        // partitions into a set of dependencies that can be displayed in one line, e.g.
314        //
315        // ```toml,ignore
316        // [dependencies]
317        // nom = "4.0.0"
318        // bytes = {version = "0.4", optional = true}
319        // ```
320        //
321        // and a set of dependencies that need their own table, e.g.
322        //
323        // ```toml,ignore
324        // [dependencies.num]
325        // version = "0.5"
326        // optional = true
327        // default-features = false
328        // ```
329        let iter = dependencies.iter();
330        let (inline, noninline): (Vec<_>, Vec<_>) = iter.partition(|d| d.is_inline());
331
332        let mut dep_table = self.dependencies_table(&inline);
333        dep_table.set_implicit(true);
334
335        for dep in noninline.into_iter() {
336            let item = Table::from(dep);
337            insert(&mut dep_table, &dep.label, Item::Table(item));
338        }
339
340        insert(root, label, Item::Table(dep_table));
341        Ok(())
342    }
343
344    fn dependencies_table(&self, dependencies: &[&Dependency]) -> Table {
345        let mut table = Table::new();
346        for dep in dependencies {
347            dep.render_into(&mut table);
348        }
349        table
350    }
351
352    fn profiles(&self, root_table: &mut Table) -> Result<(), Error> {
353        let mut profile_table = Table::new();
354        profile_table.set_implicit(true);
355        self.profile_dev(&mut profile_table)?;
356        self.profile_release(&mut profile_table)?;
357        self.profile_test(&mut profile_table)?;
358        self.profile_bench(&mut profile_table)?;
359        self.profile_doc(&mut profile_table)?;
360        insert(root_table, "profile", Item::Table(profile_table));
361        Ok(())
362    }
363
364    fn profile_dev(&self, profile_table: &mut Table) -> Result<(), Error> {
365        if let Some(ref profile_dev) = self.0.profile_dev {
366            let table = profile_to_table(profile_dev);
367            insert(profile_table, "dev", Item::Table(table));
368        }
369        Ok(())
370    }
371
372    fn profile_release(&self, profile_table: &mut Table) -> Result<(), Error> {
373        if let Some(ref profile_release) = self.0.profile_release {
374            let table = profile_to_table(profile_release);
375            insert(profile_table, "release", Item::Table(table));
376        }
377        Ok(())
378    }
379
380    fn profile_test(&self, profile_table: &mut Table) -> Result<(), Error> {
381        if let Some(ref profile_test) = self.0.profile_test {
382            let table = profile_to_table(profile_test);
383            insert(profile_table, "test", Item::Table(table));
384        }
385        Ok(())
386    }
387
388    fn profile_bench(&self, profile_table: &mut Table) -> Result<(), Error> {
389        if let Some(ref profile_bench) = self.0.profile_bench {
390            let table = profile_to_table(profile_bench);
391            insert(profile_table, "bench", Item::Table(table));
392        }
393        Ok(())
394    }
395
396    fn profile_doc(&self, profile_table: &mut Table) -> Result<(), Error> {
397        if let Some(ref profile_doc) = self.0.profile_doc {
398            let table = profile_to_table(profile_doc);
399            insert(profile_table, "doc", Item::Table(table));
400        }
401        Ok(())
402    }
403
404    fn features(&self, root_table: &mut Table) -> Result<(), Error> {
405        if let Some(ref features) = self.0.features {
406            let mut feature_table = Table::new();
407            for feature in features {
408                let mut labels = Vec::new();
409                for dep in &feature.deps {
410                    labels.push(dep.label.to_string())
411                }
412                labels.extend(feature.features.iter().map(|s| s.to_string()));
413                let arr = slice_to_arr(&labels);
414                insert(&mut feature_table, &feature.label, arr);
415            }
416            insert(root_table, "features", Item::Table(feature_table));
417        }
418        Ok(())
419    }
420
421    // [workspace]
422    fn workspace(&self, root_table: &mut Table) -> Result<(), Error> {
423        if let Some(ref workspace) = self.0.workspace {
424            let mut table = Table::new();
425            if let Some(ref members) = workspace.members {
426                let arr = slice_to_arr(members);
427                insert(&mut table, "members", arr);
428            }
429            if let Some(ref exclude) = workspace.exclude {
430                let arr = slice_to_arr(exclude);
431                insert(&mut table, "exclude", arr);
432            }
433            insert(root_table, "workspace", Item::Table(table));
434        }
435        Ok(())
436    }
437
438    // [[bin]], [[bench]], [[example]], [[test]], [lib]
439    fn targets(&self, root_table: &mut Table) -> Result<(), Error> {
440        if let Some(ref bins) = self.0.bins {
441            let item = nonlib_targets_to_item(bins);
442            insert(root_table, "bin", item);
443        }
444        if let Some(ref benches) = self.0.benches {
445            let item = nonlib_targets_to_item(benches);
446            insert(root_table, "bench", item);
447        }
448        if let Some(ref examples) = self.0.examples {
449            let item = nonlib_targets_to_item(examples);
450            insert(root_table, "example", item);
451        }
452        if let Some(ref tests) = self.0.tests {
453            let item = nonlib_targets_to_item(tests);
454            insert(root_table, "test", item);
455        }
456        if let Some(ref lib) = self.0.lib {
457            let table = Table::from(lib);
458            insert(root_table, "lib", Item::Table(table));
459        }
460        Ok(())
461    }
462
463    // [patch.___]
464    fn patches(&self, root_table: &mut Table) -> Result<(), Error> {
465        let mut patch_table = Table::new();
466        patch_table.set_implicit(true);
467
468        if let Some(ref patches) = self.0.patches {
469            for patchset in patches {
470                let table = self.patchset_to_table(&patchset);
471                insert(&mut patch_table, &patchset.label, table);
472            }
473        }
474
475        insert(root_table, "patch", Item::Table(patch_table));
476        Ok(())
477    }
478
479    fn patchset_to_table(&self, patchset: &PatchSet) -> Item {
480        let mut table = Table::new();
481        for dep in patchset.patches.iter() {
482            dep.render_into(&mut table);
483        }
484        Item::Table(table)
485    }
486
487    // [replace]
488    fn replace(&self, root_table: &mut Table) -> Result<(), Error> {
489        if let Some(ref replacements) = self.0.replacements {
490            let mut table = Table::new();
491            for rep in replacements.iter() {
492                rep.0.render_into(&mut table);
493            }
494            insert(root_table, "replace", Item::Table(table));
495        }
496        Ok(())
497    }
498}
499
500fn nonlib_targets_to_item(targets: &[NonLibTarget]) -> Item {
501    let mut array = ArrayOfTables::new();
502    for target in targets.iter() {
503        let t = Table::from(target);
504        array.append(t);
505    }
506    Item::ArrayOfTables(array)
507}
508
509impl<'a> From<&'a LibTarget> for Table {
510    fn from(t: &'a LibTarget) -> Table {
511        let mut table = Table::new();
512        if let Some(ref name) = t.name() {
513            insert(&mut table, "name", itemize(name.as_str()));
514        }
515        if let Some(ref path) = t.path() {
516            insert(&mut table, "path", itemize(path.as_str()));
517        }
518        if let Some(test) = t.test() {
519            insert(&mut table, "test", itemize(test));
520        }
521        if let Some(doctest) = t.doctest() {
522            insert(&mut table, "doctest", itemize(doctest));
523        }
524        if let Some(bench) = t.bench() {
525            insert(&mut table, "bench", itemize(bench));
526        }
527        if let Some(doc) = t.doc() {
528            insert(&mut table, "doc", itemize(doc));
529        }
530        if let Some(plugin) = t.plugin() {
531            insert(&mut table, "plugin", itemize(plugin));
532        }
533        if let Some(proc_macro) = t.proc_macro() {
534            insert(&mut table, "proc-macro", itemize(proc_macro));
535        }
536        if let Some(harness) = t.harness() {
537            insert(&mut table, "harness", itemize(harness));
538        }
539        if let Some(ref crate_type) = t.crate_type() {
540            insert(&mut table, "crate-type", itemize(crate_type.to_string()));
541        }
542        table
543    }
544}
545
546impl<'a> From<&'a NonLibTarget> for Table {
547    fn from(t: &'a NonLibTarget) -> Table {
548        let mut table = Table::new();
549        if let Some(ref name) = t.name() {
550            insert(&mut table, "name", itemize(name.as_str()));
551        }
552        if let Some(ref path) = t.path() {
553            insert(&mut table, "path", itemize(path.as_str()));
554        }
555        if let Some(test) = t.test() {
556            insert(&mut table, "test", itemize(test));
557        }
558        if let Some(doctest) = t.doctest() {
559            insert(&mut table, "doctest", itemize(doctest));
560        }
561        if let Some(bench) = t.bench() {
562            insert(&mut table, "bench", itemize(bench));
563        }
564        if let Some(doc) = t.doc() {
565            insert(&mut table, "doc", itemize(doc));
566        }
567        if let Some(plugin) = t.plugin() {
568            insert(&mut table, "plugin", itemize(plugin));
569        }
570        if let Some(proc_macro) = t.proc_macro() {
571            insert(&mut table, "proc-macro", itemize(proc_macro));
572        }
573        if let Some(harness) = t.harness() {
574            insert(&mut table, "harness", itemize(harness));
575        }
576        if let Some(ref required_features) = t.required_features() {
577            let arr = slice_to_arr(required_features);
578            insert(&mut table, "required-features", arr);
579        }
580        table
581    }
582}
583
584fn profile_to_table(profile: &Profile) -> Table {
585    /*
586    opt_level: Option<u8>,
587    debug: Option<bool>,
588    rpath: Option<bool>,
589    lto: Option<bool>,
590    debug_assertions: Option<bool>,
591    codegen_units: Option<u64>,
592    panic: Option<PanicStrategy>,
593    */
594    let mut table = Table::new();
595    if let Some(opt_level) = profile.opt_level {
596        insert(&mut table, "opt-level", itemize(opt_level as i64));
597    }
598    if let Some(debug) = profile.debug {
599        insert(&mut table, "debug", itemize(debug));
600    }
601    if let Some(rpath) = profile.rpath {
602        insert(&mut table, "rpath", itemize(rpath));
603    }
604    if let Some(lto) = profile.lto {
605        insert(&mut table, "lto", itemize(lto));
606    }
607    if let Some(debug_assertions) = profile.debug_assertions {
608        insert(&mut table, "debug-assertions", itemize(debug_assertions));
609    }
610    if let Some(codegen_units) = profile.codegen_units {
611        insert(&mut table, "codegen-units", itemize(codegen_units as i64));
612    }
613    if let Some(ref panic) = profile.panic {
614        insert(&mut table, "panic", itemize(panic.to_string()));
615    }
616    table
617}
618
619fn itemize<T: Into<Value>>(ty: T) -> Item {
620    toml_edit::value(ty.into())
621}
622
623fn insert_inline<V: Into<Value>>(table: &mut InlineTable, key: &str, val: V) {
624    table.get_or_insert(key, val);
625}
626
627fn insert(table: &mut Table, key: &str, item: Item) {
628    *table.entry(key) = item;
629}
630
631fn insert_str(table: &mut Table, key: &str, s: &str) {
632    let item = itemize(s);
633    insert(table, key, item);
634}
635
636fn maybe_insert_str(table: &mut Table, key: &str, s: &Option<String>) {
637    if let &Some(ref s) = s {
638        insert_str(table, key, s);
639    }
640}
641
642fn maybe_insert_arr(table: &mut Table, key: &str, maybe_vec: &Option<Vec<String>>) {
643    if let &Some(ref slice) = maybe_vec {
644        let arr = slice_to_arr(&slice);
645        insert(table, key, arr);
646    }
647}
648
649fn slice_to_arr(v: &[String]) -> Item {
650    let mut arr = Array::default();
651    for el in v.iter() {
652        arr.push(el.as_str());
653    }
654    itemize(arr)
655}
656
657#[cfg(test)]
658mod tests {
659    use crate::CargoToml;
660
661    #[test]
662    fn test_basics() {
663        let c = CargoToml::builder()
664                            .name("my-project")
665                            .version("0.1.1")
666                            .author("Foo <foo@bar>")
667                            .build().expect("Couldn't build toml");
668        let d = c.to_string();
669        assert_eq!(d, r#"
670[package]
671name = "my-project"
672version = "0.1.1"
673authors = ["Foo <foo@bar>"]
674
675[dependencies]
676"#);
677    }
678}