1use super::*;
2
3#[derive(Debug, Clone, PartialEq, Serialize, Eq, PartialOrd, Ord, Deserialize)]
5#[cfg_attr(feature = "schemars", derive(JsonSchema))]
6#[serde(try_from = "RawLto", into = "RawLto")]
7#[cfg_attr(feature = "schemars", schemars(with = "RawLto"))]
8#[serde(rename_all = "kebab-case")]
9pub enum LtoSetting {
10 None,
11 ThinLocal,
12 Thin,
13 Fat,
14}
15
16#[derive(Debug, Serialize, Deserialize)]
17#[cfg_attr(feature = "schemars", derive(JsonSchema))]
18#[serde(untagged)]
19enum RawLto {
20 Bool(bool),
21 String(String),
22}
23
24impl TryFrom<RawLto> for LtoSetting {
25 type Error = String;
26
27 fn try_from(value: RawLto) -> Result<Self, Self::Error> {
28 match value {
29 RawLto::Bool(false) => Ok(Self::ThinLocal),
30 RawLto::Bool(true) => Ok(Self::Fat),
31
32 RawLto::String(s) => match s.as_str() {
33 "off" => Ok(Self::None),
34 "thin-local" => Ok(Self::ThinLocal),
35 "thin" => Ok(Self::Thin),
36 "fat" => Ok(Self::Fat),
37 _ => Err(format!("Unknown LTO setting: {s}")),
38 },
39 }
40 }
41}
42
43impl From<LtoSetting> for RawLto {
44 fn from(setting: LtoSetting) -> Self {
45 match setting {
46 LtoSetting::None => Self::String("off".to_string()),
47 LtoSetting::ThinLocal => Self::Bool(false),
48 LtoSetting::Thin => Self::String("thin".to_string()),
49 LtoSetting::Fat => Self::Bool(true),
50 }
51 }
52}
53
54impl AsTomlValue for LtoSetting {
55 fn as_toml_value(&self) -> Item {
56 match self {
57 Self::None => "off".into(),
58 Self::ThinLocal => false.into(),
59 Self::Thin => "thin".into(),
60 Self::Fat => true.into(),
61 }
62 }
63}
64
65#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
67#[serde(try_from = "RawDebugSetting", into = "RawDebugSetting")]
68#[cfg_attr(feature = "schemars", derive(JsonSchema))]
69#[cfg_attr(feature = "schemars", schemars(with = "RawDebugSetting"))]
70pub enum DebugSetting {
71 None, LineDirectivesOnly, LineTablesOnly, Limited, Full, }
77
78#[derive(Debug, Serialize, Deserialize)]
79#[cfg_attr(feature = "schemars", derive(JsonSchema))]
80#[serde(untagged)]
81enum RawDebugSetting {
82 Bool(bool),
83 Integer(u8),
84 String(String),
85}
86
87impl TryFrom<RawDebugSetting> for DebugSetting {
88 type Error = String;
89
90 fn try_from(value: RawDebugSetting) -> Result<Self, Self::Error> {
91 match value {
92 RawDebugSetting::Bool(false) | RawDebugSetting::Integer(0) => Ok(Self::None),
93 RawDebugSetting::Integer(1) => Ok(Self::Limited),
94 RawDebugSetting::Bool(true) | RawDebugSetting::Integer(2) => Ok(Self::Full),
95 RawDebugSetting::Integer(n) => {
96 Err(format!("Invalid debug level: {n}. Use 0, 1, or 2."))
97 }
98
99 RawDebugSetting::String(s) => match s.as_str() {
100 "none" | "false" | "0" => Ok(Self::None),
101
102 "line-directives-only" => Ok(Self::LineDirectivesOnly),
103 "line-tables-only" => Ok(Self::LineTablesOnly),
104
105 "limited" | "1" => Ok(Self::Limited),
106 "full" | "true" | "2" => Ok(Self::Full),
107 _ => Err(format!("Unknown debug setting: '{s}'")),
108 },
109 }
110 }
111}
112
113impl From<DebugSetting> for RawDebugSetting {
114 fn from(setting: DebugSetting) -> Self {
115 match setting {
116 DebugSetting::None => Self::Bool(false),
117 DebugSetting::Full => Self::Bool(true),
118
119 DebugSetting::Limited => Self::Integer(1),
120
121 DebugSetting::LineDirectivesOnly => Self::String("line-directives-only".to_string()),
122 DebugSetting::LineTablesOnly => Self::String("line-tables-only".to_string()),
123 }
124 }
125}
126
127impl AsTomlValue for DebugSetting {
128 fn as_toml_value(&self) -> Item {
129 let raw: RawDebugSetting = (*self).into();
130
131 match raw {
132 RawDebugSetting::Bool(bool) => bool.into(),
133 RawDebugSetting::Integer(int) => (i64::from(int)).into(),
134 RawDebugSetting::String(str) => str.into(),
135 }
136 }
137}
138
139#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
141#[serde(try_from = "RawStripSetting", into = "RawStripSetting")]
142#[cfg_attr(feature = "schemars", derive(JsonSchema))]
143#[cfg_attr(feature = "schemars", schemars(with = "RawStripSetting"))]
144pub enum StripSetting {
145 None,
146 Debuginfo,
147 Symbols,
148}
149
150#[derive(Debug, Serialize, Deserialize)]
151#[cfg_attr(feature = "schemars", derive(JsonSchema))]
152#[serde(untagged)]
153enum RawStripSetting {
154 Bool(bool),
155 String(String),
156}
157
158impl TryFrom<RawStripSetting> for StripSetting {
159 type Error = String;
160
161 fn try_from(value: RawStripSetting) -> Result<Self, Self::Error> {
162 match value {
163 RawStripSetting::Bool(false) => Ok(Self::None),
164 RawStripSetting::Bool(true) => Ok(Self::Symbols),
165
166 RawStripSetting::String(s) => match s.as_str() {
167 "debuginfo" => Ok(Self::Debuginfo),
168 "symbols" | "true" => Ok(Self::Symbols),
169
170 "none" | "false" => Ok(Self::None),
171 _ => Err(format!(
172 "Invalid strip setting: '{s}'. Use 'none', 'debuginfo', 'symbols', true, or false."
173 )),
174 },
175 }
176 }
177}
178
179impl From<StripSetting> for RawStripSetting {
180 fn from(setting: StripSetting) -> Self {
181 match setting {
182 StripSetting::None => Self::String("none".to_string()),
183 StripSetting::Debuginfo => Self::String("debuginfo".to_string()),
184 StripSetting::Symbols => Self::String("symbols".to_string()),
185 }
186 }
187}
188
189impl AsTomlValue for StripSetting {
190 fn as_toml_value(&self) -> Item {
191 let raw: RawStripSetting = (*self).into();
192
193 match raw {
194 RawStripSetting::Bool(bool) => bool.into(),
195 RawStripSetting::String(str) => str.into(),
196 }
197 }
198}
199
200#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
201#[cfg_attr(feature = "schemars", derive(JsonSchema))]
202#[serde(try_from = "RawOptLevel", into = "RawOptLevel")]
203pub enum OptLevel {
204 Zero,
205 One,
206 Two,
207 Three,
208 S,
209 Z,
210}
211
212impl AsTomlValue for OptLevel {
213 fn as_toml_value(&self) -> Item {
214 match self {
215 Self::Zero => Item::from(0),
216 Self::One => Item::from(1),
217 Self::Two => Item::from(2),
218 Self::Three => Item::from(3),
219 Self::S => Item::from("s"),
220 Self::Z => Item::from("z"),
221 }
222 }
223}
224
225#[derive(Debug, Deserialize, Serialize)]
226#[cfg_attr(feature = "schemars", derive(JsonSchema))]
227#[serde(untagged)]
228enum RawOptLevel {
229 Integer(u8),
230 String(String),
231}
232
233impl TryFrom<RawOptLevel> for OptLevel {
234 type Error = String;
235
236 fn try_from(value: RawOptLevel) -> Result<Self, Self::Error> {
237 match value {
238 RawOptLevel::Integer(0) => Ok(Self::Zero),
239 RawOptLevel::Integer(1) => Ok(Self::One),
240 RawOptLevel::Integer(2) => Ok(Self::Two),
241 RawOptLevel::Integer(3) => Ok(Self::Three),
242
243 RawOptLevel::String(s) => match s.as_str() {
244 "0" => Ok(Self::Zero),
245 "1" => Ok(Self::One),
246 "2" => Ok(Self::Two),
247 "3" => Ok(Self::Three),
248 "s" => Ok(Self::S),
249 "z" => Ok(Self::Z),
250 _ => Err(format!("Invalid opt-level: {s}")),
251 },
252
253 _ => Err("opt-level must be 0-3, 's', or 'z'".to_string()),
254 }
255 }
256}
257
258impl From<OptLevel> for RawOptLevel {
259 fn from(val: OptLevel) -> Self {
260 match val {
261 OptLevel::Zero => Self::Integer(0),
262 OptLevel::One => Self::Integer(1),
263 OptLevel::Two => Self::Integer(2),
264 OptLevel::Three => Self::Integer(3),
265 OptLevel::S => Self::String("s".to_string()),
266 OptLevel::Z => Self::String("z".to_string()),
267 }
268 }
269}
270
271#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize, Merge)]
273#[cfg_attr(feature = "schemars", derive(JsonSchema))]
274#[serde(rename_all = "kebab-case")]
275#[serde(deny_unknown_fields)]
276pub struct Profile {
277 #[serde(default, skip_serializing_if = "Option::is_none")]
279 pub opt_level: Option<OptLevel>,
280
281 #[serde(default, skip_serializing_if = "Option::is_none")]
283 pub debug: Option<DebugSetting>,
284
285 #[serde(default, skip_serializing_if = "Option::is_none")]
287 pub split_debuginfo: Option<String>,
288
289 #[serde(default, skip_serializing_if = "Option::is_none")]
291 pub rpath: Option<bool>,
292
293 #[serde(default, skip_serializing_if = "Option::is_none")]
297 pub lto: Option<LtoSetting>,
298
299 #[serde(default, skip_serializing_if = "Option::is_none")]
301 pub debug_assertions: Option<bool>,
302
303 #[serde(default, skip_serializing_if = "Option::is_none")]
307 pub codegen_units: Option<u16>,
308
309 #[serde(default, skip_serializing_if = "Option::is_none")]
315 pub panic: Option<String>,
316
317 #[serde(default, skip_serializing_if = "Option::is_none")]
319 pub incremental: Option<bool>,
320
321 #[serde(default, skip_serializing_if = "Option::is_none")]
323 pub overflow_checks: Option<bool>,
324
325 #[serde(default, skip_serializing_if = "Option::is_none")]
327 pub strip: Option<StripSetting>,
328
329 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
338 pub package: BTreeMap<String, Self>,
339
340 #[serde(default, skip_serializing_if = "Option::is_none")]
347 pub build_override: Option<Box<Self>>,
348
349 #[serde(default, skip_serializing_if = "Option::is_none")]
351 pub inherits: Option<String>,
352}
353
354impl AsTomlValue for Profile {
355 fn as_toml_value(&self) -> Item {
356 let mut table = Table::new();
357
358 table.set_implicit(true);
359
360 add_value!(self, table => debug, lto, strip, opt_level, build_override);
361 add_string!(self, table => split_debuginfo, panic, inherits);
362
363 add_optional_bool!(self, table => rpath, debug_assertions, incremental, overflow_checks);
364
365 if let Some(codegen_units) = self.codegen_units {
366 table["codegen-units"] = i64::from(codegen_units).into();
367 }
368
369 if !self.package.is_empty() {
370 let mut pkg_table = Table::from_iter(
371 self.package
372 .iter()
373 .map(|(k, v)| (toml_edit::Key::from(k), v.as_toml_value())),
374 );
375
376 pkg_table.set_implicit(true);
377
378 table.insert("package", pkg_table.into());
379 };
380
381 table.into()
382 }
383}
384
385#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize, Merge)]
387#[cfg_attr(feature = "schemars", derive(JsonSchema))]
388#[merge(with = merge_options)]
389pub struct Profiles {
390 #[serde(skip_serializing_if = "Option::is_none")]
392 pub release: Option<Profile>,
393
394 #[serde(skip_serializing_if = "Option::is_none")]
396 pub dev: Option<Profile>,
397
398 #[serde(skip_serializing_if = "Option::is_none")]
400 pub test: Option<Profile>,
401
402 #[serde(skip_serializing_if = "Option::is_none")]
404 pub bench: Option<Profile>,
405
406 #[serde(flatten)]
408 #[merge(with = BTreeMap::extend)]
409 pub custom: BTreeMap<String, Profile>,
410}
411
412impl AsTomlValue for Profiles {
413 fn as_toml_value(&self) -> Item {
414 let mut table = Table::new();
415
416 table.set_implicit(true);
417
418 add_value!(self, table => dev, test, bench, release);
419 add_table!(self, table => custom);
420
421 table.into()
422 }
423}