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}