fp_bindgen/types/
cargo_dependency.rs

1use std::{collections::BTreeSet, fmt};
2
3/// Used for defining Cargo dependencies.
4#[non_exhaustive]
5#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
6pub struct CargoDependency {
7    pub branch: Option<&'static str>,
8    pub default_features: Option<bool>,
9    pub features: BTreeSet<&'static str>,
10    pub git: Option<&'static str>,
11    pub path: Option<&'static str>,
12    pub registry: Option<&'static str>,
13    pub version: Option<&'static str>,
14    pub workspace: Option<bool>,
15}
16
17impl CargoDependency {
18    pub fn from_workspace() -> Self {
19        Self::from_workspace_with_features(BTreeSet::new())
20    }
21
22    pub fn from_workspace_with_features(features: BTreeSet<&'static str>) -> Self {
23        Self {
24            features,
25            workspace: Some(true),
26            ..Default::default()
27        }
28    }
29
30    /// Merges or replaces this dependency with another.
31    ///
32    /// The algorithm used attempts to reuse as much of the current
33    /// dependency as possible, but treats the incoming dependency as leading
34    /// in case of conflicts.
35    pub fn merge_or_replace_with(&self, other: &Self) -> Self {
36        if let Some(path) = &other.path {
37            Self {
38                branch: None,
39                default_features: other.default_features.or(self.default_features),
40                features: self.features.union(&other.features).copied().collect(),
41                git: None,
42                path: Some(path),
43                registry: other.registry.or(self.registry),
44                version: other.version.or(self.version),
45                workspace: None,
46            }
47        } else if let Some(git) = &other.git {
48            Self {
49                branch: other.branch,
50                default_features: other.default_features.or(self.default_features),
51                features: self.features.union(&other.features).copied().collect(),
52                git: Some(git),
53                path: None,
54                registry: other.registry.or(self.registry),
55                version: other.version.or(self.version),
56                workspace: None,
57            }
58        } else if let Some(workspace) = &other.workspace {
59            Self {
60                branch: other.branch,
61                default_features: other.default_features.or(self.default_features),
62                features: self.features.union(&other.features).copied().collect(),
63                git: other.git,
64                path: other.path,
65                registry: other.registry,
66                version: other.version,
67                workspace: Some(*workspace),
68            }
69        } else {
70            Self {
71                branch: self.branch,
72                default_features: other.default_features.or(self.default_features),
73                features: self.features.union(&other.features).copied().collect(),
74                git: self.git,
75                path: self.path,
76                registry: other.registry.or(self.registry),
77                workspace: self.workspace,
78                version: other.version.or(self.version),
79            }
80        }
81    }
82
83    pub fn with_path(path: &'static str) -> Self {
84        Self::with_path_and_features(path, BTreeSet::new())
85    }
86
87    pub fn with_path_and_features(path: &'static str, features: BTreeSet<&'static str>) -> Self {
88        Self {
89            features,
90            path: Some(path),
91            ..Default::default()
92        }
93    }
94
95    pub fn with_version(version: &'static str) -> Self {
96        Self::with_version_and_features(version, BTreeSet::new())
97    }
98
99    pub fn with_version_and_features(
100        version: &'static str,
101        features: BTreeSet<&'static str>,
102    ) -> Self {
103        Self {
104            features,
105            version: Some(version),
106            ..Default::default()
107        }
108    }
109
110    pub fn with_version_from_registry(version: &'static str, registry: &'static str) -> Self {
111        Self::with_version_and_features_from_registry(version, registry, BTreeSet::new())
112    }
113
114    pub fn with_version_and_features_from_registry(
115        version: &'static str,
116        registry: &'static str,
117        features: BTreeSet<&'static str>,
118    ) -> Self {
119        Self {
120            features,
121            registry: Some(registry),
122            version: Some(version),
123            ..Default::default()
124        }
125    }
126
127    pub fn with_git(git: &'static str) -> Self {
128        Self::with_git_and_features(git, BTreeSet::new())
129    }
130
131    pub fn with_git_and_features(git: &'static str, features: BTreeSet<&'static str>) -> Self {
132        Self {
133            features,
134            git: Some(git),
135            ..Default::default()
136        }
137    }
138
139    pub fn with_git_and_branch(git: &'static str, branch: &'static str) -> Self {
140        Self::with_git_and_branch_and_features(git, branch, BTreeSet::new())
141    }
142
143    pub fn with_git_and_branch_and_features(
144        git: &'static str,
145        branch: &'static str,
146        features: BTreeSet<&'static str>,
147    ) -> Self {
148        Self {
149            features,
150            git: Some(git),
151            branch: Some(branch),
152            ..Default::default()
153        }
154    }
155}
156
157impl fmt::Display for CargoDependency {
158    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
159        let mut attributes = Vec::new();
160
161        if let Some(path) = self.path {
162            attributes.push(format!("path = {}", quote_value(path)));
163        } else if let Some(git) = self.git {
164            attributes.push(format!("git = {}", quote_value(git)));
165            if let Some(branch) = self.branch {
166                attributes.push(format!("branch = {}", quote_value(branch)));
167            }
168        } else if self.workspace == Some(true) {
169            attributes.push("workspace = true".to_owned());
170        }
171
172        if let Some(version) = self.version {
173            attributes.push(format!("version = {}", quote_value(version)));
174            if let Some(registry) = self.registry {
175                attributes.push(format!("registry = {}", quote_value(registry)));
176            }
177        }
178
179        if let Some(default_features) = self.default_features {
180            attributes.push(format!("default_features = {default_features}"));
181        }
182
183        if !self.features.is_empty() {
184            attributes.push(format!(
185                "features = [{}]",
186                self.features
187                    .iter()
188                    .map(|f| quote_value(f))
189                    .collect::<Vec<_>>()
190                    .join(", ")
191            ));
192        }
193
194        write!(f, "{{ {} }}", attributes.join(", "))
195    }
196}
197
198fn quote_value(val: &str) -> String {
199    format!("\"{}\"", val.replace('\\', "\\\\").replace('\"', "\\\""))
200}