Skip to content

Commit 52474e4

Browse files
authored
feat(cli): enhance Cargo features injection, add tests (#7141)
1 parent b41b57e commit 52474e4

File tree

6 files changed

+542
-225
lines changed

6 files changed

+542
-225
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"tauri-build": patch
3+
---
4+
5+
Enhance Cargo features check.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"tauri-cli": patch
3+
"@tauri-apps/cli": patch
4+
---
5+
6+
Enhance injection of Cargo features.

core/tauri-build/src/allowlist.rs

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
2+
// SPDX-License-Identifier: Apache-2.0
3+
// SPDX-License-Identifier: MIT
4+
5+
use anyhow::{anyhow, Result};
6+
use cargo_toml::{Dependency, Manifest};
7+
use tauri_utils::config::{Config, PatternKind, TauriConfig};
8+
9+
#[derive(Debug, Default, PartialEq, Eq)]
10+
struct Diff {
11+
remove: Vec<String>,
12+
add: Vec<String>,
13+
}
14+
15+
#[derive(Debug, Clone, Copy)]
16+
enum DependencyKind {
17+
Build,
18+
Normal,
19+
}
20+
21+
#[derive(Debug)]
22+
struct AllowlistedDependency {
23+
name: String,
24+
alias: Option<String>,
25+
kind: DependencyKind,
26+
all_cli_managed_features: Option<Vec<&'static str>>,
27+
expected_features: Vec<String>,
28+
}
29+
30+
pub fn check(config: &Config, manifest: &mut Manifest) -> Result<()> {
31+
let dependencies = vec![
32+
AllowlistedDependency {
33+
name: "tauri-build".into(),
34+
alias: None,
35+
kind: DependencyKind::Build,
36+
all_cli_managed_features: Some(vec!["isolation"]),
37+
expected_features: match config.tauri.pattern {
38+
PatternKind::Isolation { .. } => vec!["isolation".to_string()],
39+
_ => vec![],
40+
},
41+
},
42+
AllowlistedDependency {
43+
name: "tauri".into(),
44+
alias: None,
45+
kind: DependencyKind::Normal,
46+
all_cli_managed_features: Some(TauriConfig::all_features()),
47+
expected_features: config
48+
.tauri
49+
.features()
50+
.into_iter()
51+
.map(|f| f.to_string())
52+
.collect::<Vec<String>>(),
53+
},
54+
];
55+
56+
for metadata in dependencies {
57+
let mut name = metadata.name.clone();
58+
let mut deps = find_dependency(manifest, &metadata.name, metadata.kind);
59+
if deps.is_empty() {
60+
if let Some(alias) = &metadata.alias {
61+
deps = find_dependency(manifest, alias, metadata.kind);
62+
name = alias.clone();
63+
}
64+
}
65+
66+
for dep in deps {
67+
if let Err(error) = check_features(dep, &metadata) {
68+
return Err(anyhow!("
69+
The `{}` dependency features on the `Cargo.toml` file does not match the allowlist defined under `tauri.conf.json`.
70+
Please run `tauri dev` or `tauri build` or {}.
71+
", name, error));
72+
}
73+
}
74+
}
75+
76+
Ok(())
77+
}
78+
79+
fn find_dependency(manifest: &mut Manifest, name: &str, kind: DependencyKind) -> Vec<Dependency> {
80+
let dep = match kind {
81+
DependencyKind::Build => manifest.build_dependencies.remove(name),
82+
DependencyKind::Normal => manifest.dependencies.remove(name),
83+
};
84+
85+
if let Some(dep) = dep {
86+
vec![dep]
87+
} else {
88+
let mut deps = Vec::new();
89+
for target in manifest.target.values_mut() {
90+
if let Some(dep) = match kind {
91+
DependencyKind::Build => target.build_dependencies.remove(name),
92+
DependencyKind::Normal => target.dependencies.remove(name),
93+
} {
94+
deps.push(dep);
95+
}
96+
}
97+
deps
98+
}
99+
}
100+
101+
fn features_diff(current: &[String], expected: &[String]) -> Diff {
102+
let mut remove = Vec::new();
103+
let mut add = Vec::new();
104+
for feature in current {
105+
if !expected.contains(feature) {
106+
remove.push(feature.clone());
107+
}
108+
}
109+
110+
for feature in expected {
111+
if !current.contains(feature) {
112+
add.push(feature.clone());
113+
}
114+
}
115+
116+
Diff { remove, add }
117+
}
118+
119+
fn check_features(dependency: Dependency, metadata: &AllowlistedDependency) -> Result<(), String> {
120+
let features = match dependency {
121+
Dependency::Simple(_) => Vec::new(),
122+
Dependency::Detailed(dep) => dep.features,
123+
Dependency::Inherited(dep) => dep.features,
124+
};
125+
126+
let diff = if let Some(all_cli_managed_features) = &metadata.all_cli_managed_features {
127+
features_diff(
128+
&features
129+
.into_iter()
130+
.filter(|f| all_cli_managed_features.contains(&f.as_str()))
131+
.collect::<Vec<String>>(),
132+
&metadata.expected_features,
133+
)
134+
} else {
135+
features_diff(
136+
&features
137+
.into_iter()
138+
.filter(|f| f.starts_with("allow-"))
139+
.collect::<Vec<String>>(),
140+
&metadata.expected_features,
141+
)
142+
};
143+
144+
let mut error_message = String::new();
145+
if !diff.remove.is_empty() {
146+
error_message.push_str("remove the `");
147+
error_message.push_str(&diff.remove.join(", "));
148+
error_message.push_str(if diff.remove.len() == 1 {
149+
"` feature"
150+
} else {
151+
"` features"
152+
});
153+
if !diff.add.is_empty() {
154+
error_message.push_str(" and ");
155+
}
156+
}
157+
if !diff.add.is_empty() {
158+
error_message.push_str("add the `");
159+
error_message.push_str(&diff.add.join(", "));
160+
error_message.push_str(if diff.add.len() == 1 {
161+
"` feature"
162+
} else {
163+
"` features"
164+
});
165+
}
166+
167+
if error_message.is_empty() {
168+
Ok(())
169+
} else {
170+
Err(error_message)
171+
}
172+
}
173+
174+
#[cfg(test)]
175+
mod tests {
176+
use super::Diff;
177+
178+
#[test]
179+
fn array_diff() {
180+
for (current, expected, result) in [
181+
(vec![], vec![], Default::default()),
182+
(
183+
vec!["a".into()],
184+
vec![],
185+
Diff {
186+
remove: vec!["a".into()],
187+
add: vec![],
188+
},
189+
),
190+
(vec!["a".into()], vec!["a".into()], Default::default()),
191+
(
192+
vec!["a".into(), "b".into()],
193+
vec!["a".into()],
194+
Diff {
195+
remove: vec!["b".into()],
196+
add: vec![],
197+
},
198+
),
199+
(
200+
vec!["a".into(), "b".into()],
201+
vec!["a".into(), "c".into()],
202+
Diff {
203+
remove: vec!["b".into()],
204+
add: vec!["c".into()],
205+
},
206+
),
207+
] {
208+
assert_eq!(super::features_diff(&current, &expected), result);
209+
}
210+
}
211+
}

0 commit comments

Comments
 (0)