Skip to main content

rust_manifest/
workspace.rs

1use super::*;
2
3/// Workspace settings.
4#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default, Merge)]
5#[cfg_attr(feature = "schemars", derive(JsonSchema))]
6#[serde(default, rename_all = "kebab-case")]
7#[serde(deny_unknown_fields)]
8pub struct Workspace {
9	/// Relative paths of crates in this workspace.
10	pub members: BTreeSet<String>,
11
12	/// Members to operate on when in the workspace root.
13	///
14	/// When specified, `default-members` must expand to a subset of `members`.
15	#[serde(skip_serializing_if = "BTreeSet::is_empty")]
16	pub default_members: BTreeSet<String>,
17
18	/// Settings that can be inherited by packages in this workspace.
19	#[serde(skip_serializing_if = "Option::is_none")]
20	#[merge(with = merge_options)]
21	pub package: Option<PackageTemplate>,
22
23	/// Ignore these dirs
24	#[serde(skip_serializing_if = "BTreeSet::is_empty")]
25	pub exclude: BTreeSet<String>,
26
27	/// Custom settings for the workspace.
28	#[serde(skip_serializing_if = "BTreeMap::is_empty")]
29	pub metadata: BTreeMap<String, Value>,
30
31	/// The resolver to use for the workspace.
32	#[serde(skip_serializing_if = "Option::is_none")]
33	pub resolver: Option<Resolver>,
34
35	/// Dependencies that can be inherited by packages in the workspace.
36	#[serde(skip_serializing_if = "BTreeMap::is_empty")]
37	#[merge(with = merge_btree_maps)]
38	pub dependencies: BTreeMap<String, Dependency>,
39
40	/// Workspace-level lint groups, which can be inherited by packages.
41	#[serde(skip_serializing_if = "Option::is_none")]
42	pub lints: Option<Lints>,
43}
44
45impl AsTomlValue for Workspace {
46	fn as_toml_value(&self) -> Item {
47		let mut table = Table::new();
48
49		add_value!(self, table => resolver);
50
51		add_string_list!(self, table => members, default_members, exclude);
52
53		add_value!(self, table => package, lints);
54
55		if !self.metadata.is_empty() {
56			let mut metadata = Table::from_iter(self.metadata.iter().filter_map(|(k, v)| {
57				json_to_standard_table(v).map(|v| (toml_edit::Key::from(k), v))
58			}));
59
60			metadata.set_implicit(true);
61
62			table["metadata"] = metadata.into();
63		}
64
65		if !self.dependencies.is_empty() {
66			table["dependencies"] = Table::from_iter(
67				self.dependencies
68					.iter()
69					.map(|(name, dep)| (toml_edit::Key::from(name), dep.as_toml_value())),
70			)
71			.into();
72		}
73
74		table.into()
75	}
76}
77
78/// Settings for lint groups in a workspace/package.
79#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, Merge)]
80#[cfg_attr(feature = "schemars", derive(JsonSchema))]
81#[serde(default, deny_unknown_fields)]
82pub struct Lints {
83	/// rustc lints
84	#[serde(skip_serializing_if = "BTreeMap::is_empty")]
85	pub rust: BTreeMap<String, LintKind>,
86
87	/// Clippy lints
88	#[serde(skip_serializing_if = "BTreeMap::is_empty")]
89	pub clippy: BTreeMap<String, LintKind>,
90}
91
92impl AsTomlValue for Lints {
93	fn as_toml_value(&self) -> Item {
94		let mut table = Table::new();
95
96		table.set_implicit(true);
97
98		add_table!(self, table => rust, clippy);
99
100		table.into()
101	}
102}
103
104/// Lint group entry
105#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
106#[cfg_attr(feature = "schemars", derive(JsonSchema))]
107#[serde(untagged)]
108pub enum LintKind {
109	/// Simple definition, with lint level (i.e. "allow")
110	Simple(LintLevel),
111	/// Detailed definintion
112	Detailed(Lint),
113}
114
115impl AsTomlValue for LintKind {
116	fn as_toml_value(&self) -> Item {
117		match self {
118			Self::Simple(lev) => lev.as_toml_value(),
119			Self::Detailed(det) => det.as_toml_value(),
120		}
121	}
122}
123
124/// Properties that can be inherited via `{ workspace = true }` by member packages.
125#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default, Merge)]
126#[cfg_attr(feature = "schemars", derive(JsonSchema))]
127#[serde(default, rename_all = "kebab-case")]
128#[serde(deny_unknown_fields)]
129pub struct PackageTemplate {
130	/// Deprecated
131	#[serde(skip_serializing_if = "Vec::is_empty")]
132	pub authors: Vec<String>,
133
134	/// See <https://crates.io/category_slugs>
135	#[serde(skip_serializing_if = "BTreeSet::is_empty")]
136	pub categories: BTreeSet<String>,
137
138	/// Description for a package.
139	#[serde(skip_serializing_if = "Option::is_none")]
140	pub description: Option<String>,
141
142	/// Link to the documentation
143	#[serde(skip_serializing_if = "Option::is_none")]
144	pub documentation: Option<String>,
145
146	/// Opt-in to new Rust behaviors
147	#[serde(skip_serializing_if = "Option::is_none")]
148	pub edition: Option<Edition>,
149
150	/// Don't publish these files, relative to workspace
151	#[serde(skip_serializing_if = "BTreeSet::is_empty")]
152	pub exclude: BTreeSet<String>,
153
154	/// Homepage URL
155	#[serde(skip_serializing_if = "Option::is_none")]
156	pub homepage: Option<String>,
157
158	/// Publish these files, relative to workspace
159	#[serde(skip_serializing_if = "BTreeSet::is_empty")]
160	pub include: BTreeSet<String>,
161
162	/// Keywords to use for a package
163	#[serde(skip_serializing_if = "BTreeSet::is_empty")]
164	pub keywords: BTreeSet<String>,
165
166	/// License
167	#[serde(skip_serializing_if = "Option::is_none")]
168	pub license: Option<String>,
169
170	/// If `license` is not standard
171	#[serde(skip_serializing_if = "Option::is_none")]
172	pub license_file: Option<PathBuf>,
173
174	/// Block publishing or choose custom registries
175	#[serde(skip_serializing_if = "Option::is_none")]
176	pub publish: Option<Publish>,
177
178	/// Opt-out or custom path, relative to workspace
179	#[serde(skip_serializing_if = "Option::is_none")]
180	pub readme: Option<OptionalFile>,
181
182	/// (HTTPS) repository URL
183	#[serde(skip_serializing_if = "Option::is_none")]
184	pub repository: Option<String>,
185
186	/// Minimum required rustc version in format `1.99`
187	#[serde(skip_serializing_if = "Option::is_none")]
188	pub rust_version: Option<String>,
189
190	/// Package version semver
191	#[serde(skip_serializing_if = "Option::is_none")]
192	pub version: Option<String>,
193}
194
195impl AsTomlValue for PackageTemplate {
196	fn as_toml_value(&self) -> Item {
197		let mut table = Table::new();
198
199		add_value!(self, table => publish, edition, readme);
200
201		add_string_list!(self, table => categories, exclude, include, keywords);
202
203		add_string!(self, table =>
204			description,
205			documentation,
206			homepage,
207			license,
208			repository,
209			rust_version,
210			version
211		);
212
213		if let Some(license_file) = &self.license_file {
214			table["license-file"] = license_file.to_string_lossy().to_string().into();
215		}
216
217		table.into()
218	}
219}