Skip to content

Commit 2547237

Browse files
committed
Allow the edition specifier to be an integer
This helps save the number of characters needed to specify an edition. So far, each edition was fully described by its year number, 2015, 2018, 2021. Also for the future, the intent is that editions will always just be year numbers. Therefore, we don't have to use a toml string for them in Cargo.toml, an integer is enough. This saves two characters in each Cargo.toml.
1 parent 05cd14e commit 2547237

File tree

2 files changed

+133
-5
lines changed

2 files changed

+133
-5
lines changed

src/cargo/util/toml/mod.rs

Lines changed: 68 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -979,6 +979,22 @@ impl StringOrVec {
979979
}
980980
}
981981

982+
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
983+
#[serde(untagged, expecting = "expected a string or an integer")]
984+
pub enum StringOrI64 {
985+
String(String),
986+
I64(i64),
987+
}
988+
989+
impl Display for StringOrI64 {
990+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
991+
match self {
992+
StringOrI64::String(s) => f.write_str(s),
993+
StringOrI64::I64(v) => write!(f, "{v}"),
994+
}
995+
}
996+
}
997+
982998
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
983999
#[serde(untagged, expecting = "expected a boolean or a string")]
9841000
pub enum StringOrBool {
@@ -1262,6 +1278,50 @@ impl TomlWorkspaceDependency {
12621278
//. This already has a `Deserialize` impl from version_trim_whitespace
12631279
type MaybeWorkspaceSemverVersion = MaybeWorkspace<semver::Version, TomlWorkspaceField>;
12641280

1281+
type MaybeWorkspaceStringOrI64 = MaybeWorkspace<StringOrI64, TomlWorkspaceField>;
1282+
impl<'de> de::Deserialize<'de> for MaybeWorkspaceStringOrI64 {
1283+
fn deserialize<D>(d: D) -> Result<Self, D::Error>
1284+
where
1285+
D: de::Deserializer<'de>,
1286+
{
1287+
struct Visitor;
1288+
1289+
impl<'de> de::Visitor<'de> for Visitor {
1290+
type Value = MaybeWorkspaceStringOrI64;
1291+
1292+
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
1293+
f.write_str("a string, integer or workspace")
1294+
}
1295+
1296+
fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
1297+
where
1298+
E: de::Error,
1299+
{
1300+
let int = de::value::I64Deserializer::new(value);
1301+
StringOrI64::deserialize(int).map(MaybeWorkspace::Defined)
1302+
}
1303+
1304+
fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
1305+
where
1306+
E: de::Error,
1307+
{
1308+
let string = de::value::StringDeserializer::new(value);
1309+
StringOrI64::deserialize(string).map(MaybeWorkspace::Defined)
1310+
}
1311+
1312+
fn visit_map<V>(self, map: V) -> Result<Self::Value, V::Error>
1313+
where
1314+
V: de::MapAccess<'de>,
1315+
{
1316+
let mvd = de::value::MapAccessDeserializer::new(map);
1317+
TomlWorkspaceField::deserialize(mvd).map(MaybeWorkspace::Workspace)
1318+
}
1319+
}
1320+
1321+
d.deserialize_any(Visitor)
1322+
}
1323+
}
1324+
12651325
type MaybeWorkspaceString = MaybeWorkspace<String, TomlWorkspaceField>;
12661326
impl<'de> de::Deserialize<'de> for MaybeWorkspaceString {
12671327
fn deserialize<D>(d: D) -> Result<Self, D::Error>
@@ -1501,7 +1561,7 @@ impl WorkspaceInherit for TomlWorkspaceField {
15011561
#[derive(Deserialize, Serialize, Clone, Debug)]
15021562
#[serde(rename_all = "kebab-case")]
15031563
pub struct TomlPackage {
1504-
edition: Option<MaybeWorkspaceString>,
1564+
edition: Option<MaybeWorkspaceStringOrI64>,
15051565
rust_version: Option<MaybeWorkspaceString>,
15061566
name: InternedString,
15071567
#[serde(deserialize_with = "version_trim_whitespace")]
@@ -1583,7 +1643,7 @@ pub struct InheritableFields {
15831643
license_file: Option<String>,
15841644
repository: Option<String>,
15851645
publish: Option<VecStringOrBool>,
1586-
edition: Option<String>,
1646+
edition: Option<StringOrI64>,
15871647
badges: Option<BTreeMap<String, BTreeMap<String, String>>>,
15881648
exclude: Option<Vec<String>>,
15891649
include: Option<Vec<String>>,
@@ -1730,7 +1790,7 @@ impl InheritableFields {
17301790
)
17311791
}
17321792

1733-
pub fn edition(&self) -> CargoResult<String> {
1793+
pub fn edition(&self) -> CargoResult<StringOrI64> {
17341794
self.edition.clone().map_or(
17351795
Err(anyhow!("`workspace.package.edition` was not defined")),
17361796
|d| Ok(d),
@@ -1817,7 +1877,7 @@ impl TomlManifest {
18171877
.edition
18181878
.as_ref()
18191879
.and_then(|e| e.as_defined())
1820-
.map(|e| Edition::from_str(e))
1880+
.map(|e| Edition::from_str(&e.to_string()))
18211881
.unwrap_or(Ok(Edition::Edition2015))
18221882
.map(|e| e.default_resolve_behavior())
18231883
})?;
@@ -2129,9 +2189,12 @@ impl TomlManifest {
21292189
let edition = if let Some(edition) = package.edition.clone() {
21302190
let edition: Edition = edition
21312191
.resolve("edition", || inherit()?.edition())?
2192+
.to_string()
21322193
.parse()
21332194
.with_context(|| "failed to parse the `edition` key")?;
2134-
package.edition = Some(MaybeWorkspace::Defined(edition.to_string()));
2195+
package.edition = Some(MaybeWorkspace::Defined(StringOrI64::String(
2196+
edition.to_string(),
2197+
)));
21352198
edition
21362199
} else {
21372200
Edition::Edition2015

tests/testsuite/edition.rs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,71 @@ fn edition_works_for_build_script() {
3434
p.cargo("check -v").run();
3535
}
3636

37+
#[cargo_test]
38+
fn edition_works_as_integer() {
39+
const NEEDS_2021: &str = "pub fn foo() { let hi: u8 = 0i32.try_into().unwrap(); }";
40+
let p_2021 = project()
41+
.file(
42+
"Cargo.toml",
43+
r#"
44+
[package]
45+
name = "foo"
46+
version = "0.1.0"
47+
edition = 2021
48+
"#,
49+
)
50+
.file("src/lib.rs", NEEDS_2021)
51+
.build();
52+
p_2021.cargo("check").run();
53+
54+
let p_2018 = project()
55+
.file(
56+
"Cargo.toml",
57+
r#"
58+
[package]
59+
name = "foo"
60+
version = "0.1.0"
61+
edition = 2018
62+
"#,
63+
)
64+
.file("src/lib.rs", NEEDS_2021)
65+
.build();
66+
p_2018
67+
.cargo("check")
68+
.with_status(101)
69+
.with_stderr_contains("[..] is included in the prelude starting in Edition 2021")
70+
.run();
71+
}
72+
73+
#[cargo_test]
74+
fn edition_breaks_as_float() {
75+
let p = project()
76+
.file(
77+
"Cargo.toml",
78+
r#"
79+
[package]
80+
name = "foo"
81+
version = "0.1.0"
82+
edition = 2021.0
83+
"#,
84+
)
85+
.file("src/lib.rs", "")
86+
.build();
87+
88+
p.cargo("check -v")
89+
.with_status(101)
90+
.with_stderr(
91+
"\
92+
[ERROR] failed to parse manifest at `[..]/foo/Cargo.toml`
93+
94+
Caused by:
95+
invalid type: floating point `2021`, expected a string, integer or workspace
96+
in `package.edition`
97+
",
98+
)
99+
.run();
100+
}
101+
37102
#[cargo_test]
38103
fn edition_unstable_gated() {
39104
// During the period where a new edition is coming up, but not yet stable,

0 commit comments

Comments
 (0)