Skip to content

Commit 5aca9af

Browse files
committed
Auto merge of #12581 - epage:untagged, r=Muscraft
refactor: Use more serde_untagged ### What does this PR try to resolve? This is a follow up to #12574 that replaces hand-implemented Visitors with `serde_untagged` because I felt it is cleaner code Due to an error reporting limitation in `serde_untagged`, I'm not using this for some `MaybeWorkspace` types because it makes the errors worse. See dtolnay/serde-untagged#2 I also held off on some config visitors because they seemed more complicated and I didn't want to risk that code. ### How should we test and review this PR? The main caveat is we might not have tests for every single error case. ### Additional information
2 parents 77a9b2d + 4da5fa5 commit 5aca9af

File tree

3 files changed

+72
-208
lines changed

3 files changed

+72
-208
lines changed

src/cargo/util/interning.rs

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use serde::{Serialize, Serializer};
2+
use serde_untagged::UntaggedEnumVisitor;
23
use std::borrow::Borrow;
34
use std::cmp::Ordering;
45
use std::collections::HashSet;
@@ -150,28 +151,14 @@ impl Serialize for InternedString {
150151
}
151152
}
152153

153-
struct InternedStringVisitor;
154-
155154
impl<'de> serde::Deserialize<'de> for InternedString {
156155
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
157156
where
158157
D: serde::Deserializer<'de>,
159158
{
160-
deserializer.deserialize_str(InternedStringVisitor)
161-
}
162-
}
163-
164-
impl<'de> serde::de::Visitor<'de> for InternedStringVisitor {
165-
type Value = InternedString;
166-
167-
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
168-
formatter.write_str("an String like thing")
169-
}
170-
171-
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
172-
where
173-
E: serde::de::Error,
174-
{
175-
Ok(InternedString::new(v))
159+
UntaggedEnumVisitor::new()
160+
.expecting("an String like thing")
161+
.string(|value| Ok(InternedString::new(value)))
162+
.deserialize(deserializer)
176163
}
177164
}

src/cargo/util/semver_ext.rs

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use semver::{Comparator, Op, Version, VersionReq};
2+
use serde_untagged::UntaggedEnumVisitor;
23
use std::fmt::{self, Display};
34

45
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
@@ -198,25 +199,10 @@ impl<'de> serde::Deserialize<'de> for PartialVersion {
198199
where
199200
D: serde::Deserializer<'de>,
200201
{
201-
struct VersionVisitor;
202-
203-
impl<'de> serde::de::Visitor<'de> for VersionVisitor {
204-
type Value = PartialVersion;
205-
206-
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
207-
formatter.write_str("SemVer version")
208-
}
209-
210-
fn visit_str<E>(self, string: &str) -> Result<Self::Value, E>
211-
where
212-
E: serde::de::Error,
213-
{
214-
string.parse().map_err(serde::de::Error::custom)
215-
}
216-
}
217-
218-
let s = String::deserialize(deserializer)?;
219-
s.parse().map_err(serde::de::Error::custom)
202+
UntaggedEnumVisitor::new()
203+
.expecting("SemVer version")
204+
.string(|value| value.parse().map_err(serde::de::Error::custom))
205+
.deserialize(deserializer)
220206
}
221207
}
222208

src/cargo/util/toml/mod.rs

Lines changed: 62 additions & 171 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
use std::collections::{BTreeMap, BTreeSet, HashMap};
22
use std::ffi::OsStr;
33
use std::fmt::{self, Display, Write};
4-
use std::marker::PhantomData;
54
use std::path::{Path, PathBuf};
65
use std::rc::Rc;
76
use std::str::{self, FromStr};
@@ -213,34 +212,14 @@ impl<'de, P: Deserialize<'de> + Clone> de::Deserialize<'de> for TomlDependency<P
213212
where
214213
D: de::Deserializer<'de>,
215214
{
216-
struct TomlDependencyVisitor<P>(PhantomData<P>);
217-
218-
impl<'de, P: Deserialize<'de> + Clone> de::Visitor<'de> for TomlDependencyVisitor<P> {
219-
type Value = TomlDependency<P>;
220-
221-
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
222-
formatter.write_str(
223-
"a version string like \"0.9.8\" or a \
215+
UntaggedEnumVisitor::new()
216+
.expecting(
217+
"a version string like \"0.9.8\" or a \
224218
detailed dependency like { version = \"0.9.8\" }",
225-
)
226-
}
227-
228-
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
229-
where
230-
E: de::Error,
231-
{
232-
Ok(TomlDependency::Simple(s.to_owned()))
233-
}
234-
235-
fn visit_map<V>(self, map: V) -> Result<Self::Value, V::Error>
236-
where
237-
V: de::MapAccess<'de>,
238-
{
239-
let mvd = de::value::MapAccessDeserializer::new(map);
240-
DetailedTomlDependency::deserialize(mvd).map(TomlDependency::Detailed)
241-
}
242-
}
243-
deserializer.deserialize_any(TomlDependencyVisitor(PhantomData))
219+
)
220+
.string(|value| Ok(TomlDependency::Simple(value.to_owned())))
221+
.map(|value| value.deserialize().map(TomlDependency::Detailed))
222+
.deserialize(deserializer)
244223
}
245224
}
246225

@@ -400,39 +379,22 @@ impl<'de> de::Deserialize<'de> for TomlOptLevel {
400379
where
401380
D: de::Deserializer<'de>,
402381
{
403-
struct Visitor;
404-
405-
impl<'de> de::Visitor<'de> for Visitor {
406-
type Value = TomlOptLevel;
407-
408-
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
409-
formatter.write_str("an optimization level")
410-
}
411-
412-
fn visit_i64<E>(self, value: i64) -> Result<TomlOptLevel, E>
413-
where
414-
E: de::Error,
415-
{
416-
Ok(TomlOptLevel(value.to_string()))
417-
}
418-
419-
fn visit_str<E>(self, value: &str) -> Result<TomlOptLevel, E>
420-
where
421-
E: de::Error,
422-
{
382+
use serde::de::Error as _;
383+
UntaggedEnumVisitor::new()
384+
.expecting("an optimization level")
385+
.i64(|value| Ok(TomlOptLevel(value.to_string())))
386+
.string(|value| {
423387
if value == "s" || value == "z" {
424388
Ok(TomlOptLevel(value.to_string()))
425389
} else {
426-
Err(E::custom(format!(
390+
Err(serde_untagged::de::Error::custom(format!(
427391
"must be `0`, `1`, `2`, `3`, `s` or `z`, \
428392
but found the string: \"{}\"",
429393
value
430394
)))
431395
}
432-
}
433-
}
434-
435-
d.deserialize_any(Visitor)
396+
})
397+
.deserialize(d)
436398
}
437399
}
438400

@@ -477,58 +439,48 @@ impl<'de> de::Deserialize<'de> for TomlDebugInfo {
477439
where
478440
D: de::Deserializer<'de>,
479441
{
480-
struct Visitor;
481-
482-
impl<'de> de::Visitor<'de> for Visitor {
483-
type Value = TomlDebugInfo;
484-
485-
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
486-
formatter.write_str(
487-
"a boolean, 0, 1, 2, \"line-tables-only\", or \"line-directives-only\"",
488-
)
489-
}
490-
491-
fn visit_i64<E>(self, value: i64) -> Result<TomlDebugInfo, E>
492-
where
493-
E: de::Error,
494-
{
442+
use serde::de::Error as _;
443+
let expecting = "a boolean, 0, 1, 2, \"line-tables-only\", or \"line-directives-only\"";
444+
UntaggedEnumVisitor::new()
445+
.expecting(expecting)
446+
.bool(|value| {
447+
Ok(if value {
448+
TomlDebugInfo::Full
449+
} else {
450+
TomlDebugInfo::None
451+
})
452+
})
453+
.i64(|value| {
495454
let debuginfo = match value {
496455
0 => TomlDebugInfo::None,
497456
1 => TomlDebugInfo::Limited,
498457
2 => TomlDebugInfo::Full,
499-
_ => return Err(de::Error::invalid_value(Unexpected::Signed(value), &self)),
458+
_ => {
459+
return Err(serde_untagged::de::Error::invalid_value(
460+
Unexpected::Signed(value),
461+
&expecting,
462+
))
463+
}
500464
};
501465
Ok(debuginfo)
502-
}
503-
504-
fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
505-
where
506-
E: de::Error,
507-
{
508-
Ok(if v {
509-
TomlDebugInfo::Full
510-
} else {
511-
TomlDebugInfo::None
512-
})
513-
}
514-
515-
fn visit_str<E>(self, value: &str) -> Result<TomlDebugInfo, E>
516-
where
517-
E: de::Error,
518-
{
466+
})
467+
.string(|value| {
519468
let debuginfo = match value {
520469
"none" => TomlDebugInfo::None,
521470
"limited" => TomlDebugInfo::Limited,
522471
"full" => TomlDebugInfo::Full,
523472
"line-directives-only" => TomlDebugInfo::LineDirectivesOnly,
524473
"line-tables-only" => TomlDebugInfo::LineTablesOnly,
525-
_ => return Err(de::Error::invalid_value(Unexpected::Str(value), &self)),
474+
_ => {
475+
return Err(serde_untagged::de::Error::invalid_value(
476+
Unexpected::Str(value),
477+
&expecting,
478+
))
479+
}
526480
};
527481
Ok(debuginfo)
528-
}
529-
}
530-
531-
d.deserialize_any(Visitor)
482+
})
483+
.deserialize(d)
532484
}
533485
}
534486

@@ -927,32 +879,11 @@ impl<'de> de::Deserialize<'de> for StringOrVec {
927879
where
928880
D: de::Deserializer<'de>,
929881
{
930-
struct Visitor;
931-
932-
impl<'de> de::Visitor<'de> for Visitor {
933-
type Value = StringOrVec;
934-
935-
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
936-
formatter.write_str("string or list of strings")
937-
}
938-
939-
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
940-
where
941-
E: de::Error,
942-
{
943-
Ok(StringOrVec(vec![s.to_string()]))
944-
}
945-
946-
fn visit_seq<V>(self, v: V) -> Result<Self::Value, V::Error>
947-
where
948-
V: de::SeqAccess<'de>,
949-
{
950-
let seq = de::value::SeqAccessDeserializer::new(v);
951-
Vec::deserialize(seq).map(StringOrVec)
952-
}
953-
}
954-
955-
deserializer.deserialize_any(Visitor)
882+
UntaggedEnumVisitor::new()
883+
.expecting("string or list of strings")
884+
.string(|value| Ok(StringOrVec(vec![value.to_owned()])))
885+
.seq(|value| value.deserialize().map(StringOrVec))
886+
.deserialize(deserializer)
956887
}
957888
}
958889

@@ -975,8 +906,8 @@ impl<'de> Deserialize<'de> for StringOrBool {
975906
D: de::Deserializer<'de>,
976907
{
977908
UntaggedEnumVisitor::new()
978-
.string(|s| Ok(StringOrBool::String(s.to_owned())))
979909
.bool(|b| Ok(StringOrBool::Bool(b)))
910+
.string(|s| Ok(StringOrBool::String(s.to_owned())))
980911
.deserialize(deserializer)
981912
}
982913
}
@@ -993,68 +924,28 @@ impl<'de> de::Deserialize<'de> for VecStringOrBool {
993924
where
994925
D: de::Deserializer<'de>,
995926
{
996-
struct Visitor;
997-
998-
impl<'de> de::Visitor<'de> for Visitor {
999-
type Value = VecStringOrBool;
1000-
1001-
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1002-
formatter.write_str("a boolean or vector of strings")
1003-
}
1004-
1005-
fn visit_seq<V>(self, v: V) -> Result<Self::Value, V::Error>
1006-
where
1007-
V: de::SeqAccess<'de>,
1008-
{
1009-
let seq = de::value::SeqAccessDeserializer::new(v);
1010-
Vec::deserialize(seq).map(VecStringOrBool::VecString)
1011-
}
1012-
1013-
fn visit_bool<E>(self, b: bool) -> Result<Self::Value, E>
1014-
where
1015-
E: de::Error,
1016-
{
1017-
Ok(VecStringOrBool::Bool(b))
1018-
}
1019-
}
1020-
1021-
deserializer.deserialize_any(Visitor)
927+
UntaggedEnumVisitor::new()
928+
.expecting("a boolean or vector of strings")
929+
.bool(|value| Ok(VecStringOrBool::Bool(value)))
930+
.seq(|value| value.deserialize().map(VecStringOrBool::VecString))
931+
.deserialize(deserializer)
1022932
}
1023933
}
1024934

1025935
fn version_trim_whitespace<'de, D>(deserializer: D) -> Result<MaybeWorkspaceSemverVersion, D::Error>
1026936
where
1027937
D: de::Deserializer<'de>,
1028938
{
1029-
struct Visitor;
1030-
1031-
impl<'de> de::Visitor<'de> for Visitor {
1032-
type Value = MaybeWorkspaceSemverVersion;
1033-
1034-
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1035-
formatter.write_str("SemVer version")
1036-
}
1037-
1038-
fn visit_str<E>(self, string: &str) -> Result<Self::Value, E>
1039-
where
1040-
E: de::Error,
1041-
{
1042-
match string.trim().parse().map_err(de::Error::custom) {
939+
UntaggedEnumVisitor::new()
940+
.expecting("SemVer version")
941+
.string(
942+
|value| match value.trim().parse().map_err(de::Error::custom) {
1043943
Ok(parsed) => Ok(MaybeWorkspace::Defined(parsed)),
1044944
Err(e) => Err(e),
1045-
}
1046-
}
1047-
1048-
fn visit_map<V>(self, map: V) -> Result<Self::Value, V::Error>
1049-
where
1050-
V: de::MapAccess<'de>,
1051-
{
1052-
let mvd = de::value::MapAccessDeserializer::new(map);
1053-
TomlWorkspaceField::deserialize(mvd).map(MaybeWorkspace::Workspace)
1054-
}
1055-
}
1056-
1057-
deserializer.deserialize_any(Visitor)
945+
},
946+
)
947+
.map(|value| value.deserialize().map(MaybeWorkspace::Workspace))
948+
.deserialize(deserializer)
1058949
}
1059950

1060951
/// This Trait exists to make [`MaybeWorkspace::Workspace`] generic. It makes deserialization of

0 commit comments

Comments
 (0)