Skip to main content

cargo_toml_builder/types/
dependency.rs

1use std::fmt;
2
3use toml_edit::{self, InlineTable, Table, Value};
4
5/// Adds some methods to create `Dependency` objects from objects that impl `Display`
6pub trait DependencyExt: fmt::Display {
7    /// Constructs a `Dependency` using `self.to_string()` as the name, and `version` as the
8    /// semver version
9    ///
10    /// # Example
11    ///
12    /// ```rust
13    /// extern crate cargo_toml_builder;
14    /// 
15    /// # use std::error::Error;
16    /// use cargo_toml_builder::prelude::*;
17    /// # fn main() -> Result<(), Box<dyn Error>> {
18    /// let dep = "foo".version("0.1.0");
19    /// let expected = Dependency::version("foo", "0.1.0");
20    /// assert_eq!(dep, expected);
21    /// #   Ok(())
22    /// # }
23    /// ```
24    fn version(&self, version: &str) -> Dependency {
25        Dependency::version(&self.to_string(), version)
26    }
27
28    /// Constructs a `Dependency` using `self.to_string()` as the name, and `repo` as the git
29    /// repository
30    ///
31    /// # Example
32    ///
33    /// ```rust
34    /// extern crate cargo_toml_builder;
35    ///
36    /// # use std::error::Error;
37    /// use cargo_toml_builder::prelude::*;
38    /// # fn main() -> Result<(), Box<dyn Error>> {
39    /// let dep = "foo".repo("https://github.com/bar/foo");
40    /// let expected = Dependency::repo("foo", "https://github.com/bar/foo");
41    /// assert_eq!(dep, expected);
42    /// #   Ok(())
43    /// # }
44    /// ```
45    fn repo(&self, repo: &str) -> Dependency {
46        Dependency::repo(&self.to_string(), repo)
47    }
48
49    /// Constructs an optional dependency using `self.to_string()` as the name, and `version` as
50    /// the semver version
51    ///
52    /// # Example
53    ///
54    /// ```rust
55    /// extern crate cargo_toml_builder;
56    ///
57    /// # use std::error::Error;
58    /// use cargo_toml_builder::prelude::*;
59    /// # fn main() -> Result<(), Box<dyn Error>> {
60    /// let dep = "foo".optional("0.1.0");
61    /// let expected = Dependency::version("foo", "0.1.0")
62    ///                           .optional(true)
63    ///                           .build();
64    /// assert_eq!(dep, expected);
65    /// #   Ok(())
66    /// # }
67    /// ```
68    fn optional(&self, version: &str) -> Dependency {
69        Dependency::new(&self.to_string(), DependencyType::Crate(Crate { version: Some(version.to_string()) }))
70                   .optional(true)
71                   .build()
72    }
73
74    /// Constructs a `Dependency` using `self.to_string()` as the name and `path` as the path
75    ///
76    /// # Example
77    ///
78    /// ```rust
79    /// extern crate cargo_toml_builder;
80    ///
81    /// # use std::error::Error;
82    /// use cargo_toml_builder::prelude::*;
83    /// # fn main() -> Result<(), Box<dyn Error>> {
84    /// let dep = "foo".path("/path/to/foo");
85    /// let expected = Dependency::path("foo", "/path/to/foo");
86    /// assert_eq!(dep, expected);
87    /// #   Ok(())
88    /// # }
89    /// ```
90    fn path(&self, path: &str) -> Dependency {
91        Dependency::new(&self.to_string(), DependencyType::Path(path.to_string()))
92    }
93}
94
95impl<T: fmt::Display> DependencyExt for T {}
96
97/// Represents a single dependency
98#[derive(Debug, Clone, PartialEq)]
99pub struct Dependency {
100    pub(crate) label: String,
101    pub(crate) dep_type: DependencyType,
102    pub(crate) optional: Option<bool>,
103    pub(crate) default_features: Option<bool>,
104}
105
106impl Dependency {
107    fn new(label: &str, dep_type: DependencyType) -> Dependency {
108        Dependency {
109            label: label.to_string(),
110            dep_type: dep_type,
111            optional: None,
112            default_features: None, 
113        }
114    }
115
116    pub(crate) fn is_inline(&self) -> bool {
117        // types of dependencies that look ok when rendered inline
118        //
119        // e.g.
120        //
121        // foo = "1.0.0"
122        // bar = { path = "baz" }
123        // qux = { git = "https://github.com/foo/bar" }
124        let inline_type = match self.dep_type {
125            DependencyType::Crate(..) => true,
126            DependencyType::Repo(Repo { url: _, repo_type: None }) => true,
127            DependencyType::Path(..) => true,
128            _ => false
129        };
130        // adding `optional = true` inline looks ok, but not `default_features` (imo)
131        match (inline_type, self.default_features) {
132            (true, None) => return true,
133            _ => return false,
134        }
135    }
136    /* Constructors */
137    /// Constructs a `Depedency` that points to a git repo
138    ///
139    /// # Example
140    ///
141    /// ```rust
142    /// extern crate cargo_toml_builder;
143    /// # use std::error::Error;
144    /// use cargo_toml_builder::prelude::*;
145    /// # fn main() -> Result<(), Box<dyn Error>> {
146    /// let dep = Dependency::repo("foo", "https://github.com/foo/bar");
147    /// // translates to:
148    /// // [dependencies]
149    /// // foo = { git = "https://github.com/foo/bar" }
150    /// #   Ok(())
151    /// # }
152    /// ```
153    pub fn repo(label: &str, repo: &str) -> Dependency {
154        Dependency::new(label, DependencyType::Repo(Repo { url: repo.into(), repo_type: None}))
155    }
156
157    /// Constructs a `Depedency` that specifies a semver version
158    ///
159    /// # Example
160    ///
161    /// ```rust
162    /// extern crate cargo_toml_builder;
163    /// # use std::error::Error;
164    /// use cargo_toml_builder::prelude::*;
165    /// # fn main() -> Result<(), Box<dyn Error>> {
166    /// let dep = Dependency::version("foo", "0.1.0");
167    /// // translates to:
168    /// // [dependencies]
169    /// // foo = "0.1.0"
170    /// #   Ok(())
171    /// # }
172    /// ```
173    pub fn version(label: &str, version: &str) -> Dependency {
174        Dependency::new(label, DependencyType::Crate(Crate { version: Some(version.into())}))
175    }
176
177    /// Constructs a `Depedency` that points to a path
178    ///
179    /// # Example
180    ///
181    /// ```rust
182    /// extern crate cargo_toml_builder;
183    /// # use std::error::Error;
184    /// use cargo_toml_builder::prelude::*;
185    /// # fn main() -> Result<(), Box<dyn Error>> {
186    /// let dep = Dependency::path("foo", "/path/to/foo");
187    /// // translates to:
188    /// // [dependencies]
189    /// // foo = { path = "/path/to/foo" }
190    /// #   Ok(())
191    /// # }
192    /// ```
193    pub fn path(label: &str, path: &str) -> Dependency {
194        Dependency::new(label, DependencyType::Path(path.into()))
195    }
196
197    /// Constructs a `Depedency` that points to a specific revision of a repository
198    ///
199    /// # Example
200    ///
201    /// ```rust
202    /// extern crate cargo_toml_builder;
203    /// # use std::error::Error;
204    /// use cargo_toml_builder::prelude::*;
205    /// # fn main() -> Result<(), Box<dyn Error>> {
206    /// let dep = Dependency::rev("foo", "https://github.com/foo/bar", "0a14cbe3928");
207    /// // translates to:
208    /// // [dependencies]
209    /// // foo = { git = "https://github.com/foo/bar", rev = "0a14cbe3928" }
210    /// #   Ok(())
211    /// # }
212    /// ```
213    pub fn rev(label: &str, repo: &str, rev: &str) -> Dependency {
214        Dependency::new(label, DependencyType::Repo(Repo { url: repo.into(), repo_type: Some(RepoType::Rev(rev.into()))}))
215    }
216
217    /// Constructs a `Depedency` that points to a specific tag of a repository
218    ///
219    /// # Example
220    ///
221    /// ```rust
222    /// extern crate cargo_toml_builder;
223    /// # use std::error::Error;
224    /// use cargo_toml_builder::prelude::*;
225    /// # fn main() -> Result<(), Box<dyn Error>> {
226    /// let dep = Dependency::tag("foo", "https://github.com/foo/bar", "v1.0.0");
227    /// // translates to:
228    /// // [dependencies]
229    /// // foo = { git = "https://github.com/foo/bar", tag = "v1.0.0" }
230    /// #   Ok(())
231    /// # }
232    /// ```
233    pub fn tag(label: &str, repo: &str, tag: &str) -> Dependency {
234        Dependency::new(label, DependencyType::Repo(Repo { url: repo.into(), repo_type: Some(RepoType::Tag(tag.into()))}))
235    }
236
237    /// Constructs a `Depedency` that points to a specific branch of a repository
238    ///
239    /// # Example
240    ///
241    /// ```rust
242    /// extern crate cargo_toml_builder;
243    /// # use std::error::Error;
244    /// use cargo_toml_builder::prelude::*;
245    /// # fn main() -> Result<(), Box<dyn Error>> {
246    /// let dep = Dependency::branch("foo", "https://github.com/foo/bar", "some-branch");
247    /// // translates to:
248    /// // [dependencies]
249    /// // foo = { git = "https://github.com/foo/bar", branch = "some-branch" }
250    /// #   Ok(())
251    /// # }
252    /// ```
253    pub fn branch(label: &str, repo: &str, branch: &str) -> Dependency {
254        Dependency::new(label, DependencyType::Repo(Repo { url: repo.into(), repo_type: Some(RepoType::Branch(branch.into()))}))
255    }
256
257    /// If the builder pattern has been used to construct this `Dependency`, this takes ownership
258    pub fn build(&self) -> Self {
259        self.clone()
260    }
261
262    /// Mark this dependency as "optional = true"
263    ///
264    /// # Example
265    ///
266    /// ```rust
267    /// extern crate cargo_toml_builder;
268    /// # use std::error::Error;
269    /// use cargo_toml_builder::prelude::*;
270    /// # fn main() -> Result<(), Box<dyn Error>> {
271    /// let dep = Dependency::version("foo", "0.1.0")
272    ///                      .optional(true)
273    ///                      .build();
274    /// // translates to:
275    /// // [dependencies]
276    /// // foo = { version = "0.1.0", optional = true }
277    /// #   Ok(())
278    /// # }
279    /// ```
280    pub fn optional(&mut self, optional: bool) -> &mut Self {
281        self.optional = Some(optional);
282        self
283    }
284
285    /// Specify whether this dependency is using the default features or not
286    ///
287    /// # Example
288    ///
289    /// ```rust
290    /// extern crate cargo_toml_builder;
291    /// # use std::error::Error;
292    /// use cargo_toml_builder::prelude::*;
293    /// # fn main() -> Result<(), Box<dyn Error>> {
294    /// let dep = Dependency::version("foo", "0.1.0")
295    ///                      .default_features(false)
296    ///                      .build();
297    /// // translates to:
298    /// // [dependencies]
299    /// // foo = { version = "0.1.0", default-features = false }
300    /// #   Ok(())
301    /// # }
302    /// ```
303    pub fn default_features(&mut self, default_features: bool) -> &mut Self {
304        self.default_features = Some(default_features);
305        self
306    }
307
308    // Non-builder methods
309
310    pub(crate) fn only_version(&self) -> Option<&str> {
311        if let DependencyType::Crate(Crate { ref version }) = self.dep_type {
312            if self.optional.is_none() && self.default_features.is_none() {
313                if let Some(v) = version {
314                    return Some(&v);
315                } else {
316                    return Some("*");
317                }
318            }
319        }
320        None
321    }
322
323    pub(crate) fn render_into(&self, table: &mut Table) {
324        if let Some(version) = self.only_version() {
325            // inserts `label = "version"`
326            *table.entry(&self.label) = toml_edit::value(version);
327        } else {
328            let inline_table = InlineTable::from(self);
329            // inserts `label = { ... }`
330            *table.entry(&self.label) = toml_edit::value(Value::InlineTable(inline_table));
331        }
332    }
333}
334
335impl<'a> From<&'a str> for Dependency {
336    fn from(s: &'a str) -> Dependency {
337        Dependency::new(s, DependencyType::Crate(Crate { version: None}))
338    }
339}
340
341impl From<String> for Dependency {
342    fn from(s: String) -> Dependency {
343        Dependency::from(s.as_str())
344    }
345}
346
347impl<'a> From<&'a Dependency> for Dependency {
348    fn from(d: &Dependency) -> Dependency {
349        d.clone()
350    }
351}
352
353impl<'a> From<&'a mut Dependency> for Dependency {
354    fn from(d: &'a mut Dependency) -> Dependency {
355        d.clone()
356    }
357}
358
359/// Represents the different types of dependencies
360#[derive(Debug, Clone, PartialEq)]
361pub enum DependencyType {
362    /// This is a crate retrieved from a crate registry like crates.io using it's name and it's semver version number
363    Crate(Crate),
364    /// This is a crate retrieved from a git repository
365    Repo(Repo),
366    /// This is a crate retrieved from a filesystem path
367    Path(String),
368}
369
370/// Represents a dependency that has a semver verison number
371#[derive(Debug, Clone, PartialEq)]
372pub struct Crate {
373    pub(crate) version: Option<String>,
374}
375
376/// Represents a dependency that is retrieved from a git repository
377#[derive(Debug, Clone, PartialEq)]
378pub struct Repo {
379    pub(crate) url: String,
380    pub(crate) repo_type: Option<RepoType>,
381}
382
383/// Sets a specific commit of a repository to use
384#[derive(Debug, Clone, PartialEq)]
385pub enum RepoType {
386    /// Sets the specific commit to the given hash
387    Rev(String),
388    /// Sets the specific commit to the commit pointed to by the given tag
389    Tag(String),
390    /// Sets the specific commit to the commit pointed to by the given branch name
391    Branch(String),
392}
393
394impl From<Dependency> for InlineTable {
395    fn from(d: Dependency) -> InlineTable {
396        From::from(&d)
397    }
398}
399
400impl<'a> From<&'a Dependency> for InlineTable {
401    fn from(dep: &'a Dependency) -> InlineTable {
402        let mut table = InlineTable::default();
403        match dep.dep_type {
404            DependencyType::Crate(Crate { version: None }) => {
405                table.get_or_insert("version", "*");
406            },
407            DependencyType::Crate(Crate { version: Some(ref version) }) => {
408                table.get_or_insert("version", version.as_str());
409            },
410            DependencyType::Repo(Repo { ref url, repo_type: None }) => {
411                table.get_or_insert("git", url.as_str());
412            },
413            DependencyType::Repo(Repo { ref url, repo_type: Some(RepoType::Rev(ref rev)) }) => {
414                table.get_or_insert("git", url.as_str());
415                table.get_or_insert("rev", rev.as_str());
416            },
417            DependencyType::Repo(Repo { ref url, repo_type: Some(RepoType::Tag(ref tag)) }) => {
418                table.get_or_insert("git", url.as_str());
419                table.get_or_insert("tag", tag.as_str());
420            },
421            DependencyType::Repo(Repo { ref url, repo_type: Some(RepoType::Branch(ref branch)) }) => {
422                table.get_or_insert("git", url.as_str());
423                table.get_or_insert("branch", branch.as_str());
424            },
425            DependencyType::Path(ref path) => {
426                table.get_or_insert("path", path.as_str());
427            },
428        }
429        if let Some(optional) = dep.optional {
430            table.get_or_insert("optional", Value::from(optional));
431        }
432        if let Some(default_features) = dep.default_features {
433            table.get_or_insert("default-features", Value::from(default_features));
434        }
435        table
436    }
437}
438
439impl<'a> From<&'a Dependency> for Table {
440    fn from(dep: &'a Dependency) -> Table {
441        let mut table = Table::new();
442        match dep.dep_type {
443            DependencyType::Crate(Crate { version: None }) => {
444                *table.entry("version") = toml_edit::value("*");
445            },
446            DependencyType::Crate(Crate { version: Some(ref version) }) => {
447                *table.entry("version") = toml_edit::value(version.as_str());
448            },
449            DependencyType::Repo(Repo { ref url, repo_type: None }) => {
450                *table.entry("git") = toml_edit::value(url.as_str());
451            },
452            DependencyType::Repo(Repo { ref url, repo_type: Some(RepoType::Rev(ref rev)) }) => {
453                *table.entry("git") = toml_edit::value(url.as_str());
454                *table.entry("rev") = toml_edit::value(rev.as_str());
455            },
456            DependencyType::Repo(Repo { ref url, repo_type: Some(RepoType::Tag(ref tag)) }) => {
457                *table.entry("git") = toml_edit::value(url.as_str());
458                *table.entry("tag") = toml_edit::value(tag.as_str());
459            },
460            DependencyType::Repo(Repo { ref url, repo_type: Some(RepoType::Branch(ref branch)) }) => {
461                *table.entry("git") = toml_edit::value(url.as_str());
462                *table.entry("branch") = toml_edit::value(branch.as_str());
463            },
464            DependencyType::Path(ref path) => {
465                *table.entry("path") = toml_edit::value(path.as_str());
466            },
467        }
468        if let Some(optional) = dep.optional {
469            *table.entry("optional") = toml_edit::value(optional);
470        }
471        if let Some(default_features) = dep.default_features {
472            *table.entry("default-features") = toml_edit::value(default_features);
473        }
474        table
475    }
476}