Skip to content

Commit b5fd826

Browse files
committed
feat: Support parse string as git/gitoxide features from ENV and Config
- pass 'all' to enable all predefind git/gitoxide features - stil support parse git/gitoxide as table in Config, but not support parser table in ENV
1 parent 1d19e76 commit b5fd826

File tree

3 files changed

+343
-31
lines changed

3 files changed

+343
-31
lines changed

src/cargo/core/features.rs

Lines changed: 106 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -759,7 +759,9 @@ unstable_cli_options!(
759759
dual_proc_macros: bool = ("Build proc-macros for both the host and the target"),
760760
features: Option<Vec<String>>,
761761
gc: bool = ("Track cache usage and \"garbage collect\" unused files"),
762+
#[serde(deserialize_with = "deserialize_git_features")]
762763
git: Option<GitFeatures> = ("Enable support for shallow git fetch operations"),
764+
#[serde(deserialize_with = "deserialize_gitoxide_features")]
763765
gitoxide: Option<GitoxideFeatures> = ("Use gitoxide for the given git interactions, or all of them if no argument is given"),
764766
host_config: bool = ("Enable the `[host]` section in the .cargo/config.toml file"),
765767
minimal_versions: bool = ("Resolve minimal dependency versions instead of maximum"),
@@ -866,7 +868,7 @@ where
866868
))
867869
}
868870

869-
#[derive(Debug, Copy, Clone, Default, Deserialize)]
871+
#[derive(Debug, Copy, Clone, Default, Deserialize, Ord, PartialOrd, Eq, PartialEq)]
870872
pub struct GitFeatures {
871873
/// When cloning the index, perform a shallow clone. Maintain shallowness upon subsequent fetches.
872874
pub shallow_index: bool,
@@ -875,12 +877,59 @@ pub struct GitFeatures {
875877
}
876878

877879
impl GitFeatures {
878-
fn all() -> Self {
880+
pub fn all() -> Self {
879881
GitFeatures {
880882
shallow_index: true,
881883
shallow_deps: true,
882884
}
883885
}
886+
887+
fn expecting() -> String {
888+
let fields = vec!["'all'", "'shallow-index'", "'shallow-deps'"];
889+
format!(
890+
"unstable 'git' only takes {} as valid inputs, your can use 'all' to turn out all git features",
891+
fields.join(" and ")
892+
)
893+
}
894+
}
895+
896+
fn deserialize_git_features<'de, D>(deserializer: D) -> Result<Option<GitFeatures>, D::Error>
897+
where
898+
D: serde::de::Deserializer<'de>,
899+
{
900+
struct GitFeaturesVisitor;
901+
902+
impl<'de> serde::de::Visitor<'de> for GitFeaturesVisitor {
903+
type Value = Option<GitFeatures>;
904+
905+
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
906+
formatter.write_str(&GitFeatures::expecting())
907+
}
908+
909+
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
910+
where
911+
E: serde::de::Error,
912+
{
913+
Ok(parse_git(s.split(",")).map_err(serde::de::Error::custom)?)
914+
}
915+
916+
fn visit_none<E>(self) -> Result<Self::Value, E>
917+
where
918+
E: serde::de::Error,
919+
{
920+
Ok(None)
921+
}
922+
923+
fn visit_map<V>(self, map: V) -> Result<Self::Value, V::Error>
924+
where
925+
V: serde::de::MapAccess<'de>,
926+
{
927+
let mvd = serde::de::value::MapAccessDeserializer::new(map);
928+
Ok(Some(GitFeatures::deserialize(mvd)?))
929+
}
930+
}
931+
932+
deserializer.deserialize_any(GitFeaturesVisitor)
884933
}
885934

886935
fn parse_git(it: impl Iterator<Item = impl AsRef<str>>) -> CargoResult<Option<GitFeatures>> {
@@ -892,19 +941,18 @@ fn parse_git(it: impl Iterator<Item = impl AsRef<str>>) -> CargoResult<Option<Gi
892941

893942
for e in it {
894943
match e.as_ref() {
944+
"all" => return Ok(Some(GitFeatures::all())),
895945
"shallow-index" => *shallow_index = true,
896946
"shallow-deps" => *shallow_deps = true,
897947
_ => {
898-
bail!(
899-
"unstable 'git' only takes 'shallow-index' and 'shallow-deps' as valid inputs"
900-
)
948+
bail!(GitFeatures::expecting())
901949
}
902950
}
903951
}
904952
Ok(Some(out))
905953
}
906954

907-
#[derive(Debug, Copy, Clone, Default, Deserialize)]
955+
#[derive(Debug, Copy, Clone, Default, Deserialize, Ord, PartialOrd, Eq, PartialEq)]
908956
pub struct GitoxideFeatures {
909957
/// All fetches are done with `gitoxide`, which includes git dependencies as well as the crates index.
910958
pub fetch: bool,
@@ -918,7 +966,7 @@ pub struct GitoxideFeatures {
918966
}
919967

920968
impl GitoxideFeatures {
921-
fn all() -> Self {
969+
pub fn all() -> Self {
922970
GitoxideFeatures {
923971
fetch: true,
924972
checkout: true,
@@ -935,6 +983,55 @@ impl GitoxideFeatures {
935983
internal_use_git2: false,
936984
}
937985
}
986+
987+
fn expecting() -> String {
988+
let fields = vec!["'all'", "'fetch'", "'checkout'", "'internal-use-git2'"];
989+
format!(
990+
"unstable 'gitoxide' only takes {} as valid inputs, your can use 'all' to turn out all gitoxide features, for shallow fetches see `shallow-index,shallow-deps`",
991+
fields.join(" and ")
992+
)
993+
}
994+
}
995+
996+
fn deserialize_gitoxide_features<'de, D>(
997+
deserializer: D,
998+
) -> Result<Option<GitoxideFeatures>, D::Error>
999+
where
1000+
D: serde::de::Deserializer<'de>,
1001+
{
1002+
struct GitoxideFeaturesVisitor;
1003+
1004+
impl<'de> serde::de::Visitor<'de> for GitoxideFeaturesVisitor {
1005+
type Value = Option<GitoxideFeatures>;
1006+
1007+
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1008+
formatter.write_str(&GitoxideFeatures::expecting())
1009+
}
1010+
1011+
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
1012+
where
1013+
E: serde::de::Error,
1014+
{
1015+
Ok(parse_gitoxide(s.split(",")).map_err(serde::de::Error::custom)?)
1016+
}
1017+
1018+
fn visit_none<E>(self) -> Result<Self::Value, E>
1019+
where
1020+
E: serde::de::Error,
1021+
{
1022+
Ok(None)
1023+
}
1024+
1025+
fn visit_map<V>(self, map: V) -> Result<Self::Value, V::Error>
1026+
where
1027+
V: serde::de::MapAccess<'de>,
1028+
{
1029+
let mvd = serde::de::value::MapAccessDeserializer::new(map);
1030+
Ok(Some(GitoxideFeatures::deserialize(mvd)?))
1031+
}
1032+
}
1033+
1034+
deserializer.deserialize_any(GitoxideFeaturesVisitor)
9381035
}
9391036

9401037
fn parse_gitoxide(
@@ -949,11 +1046,12 @@ fn parse_gitoxide(
9491046

9501047
for e in it {
9511048
match e.as_ref() {
1049+
"all" => return Ok(Some(GitoxideFeatures::all())),
9521050
"fetch" => *fetch = true,
9531051
"checkout" => *checkout = true,
9541052
"internal-use-git2" => *internal_use_git2 = true,
9551053
_ => {
956-
bail!("unstable 'gitoxide' only takes `fetch` and 'checkout' as valid input, for shallow fetches see `-Zgit=shallow-index,shallow-deps`")
1054+
bail!(GitoxideFeatures::expecting())
9571055
}
9581056
}
9591057
}

src/cargo/util/context/de.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,12 @@ impl<'de, 'gctx> de::Deserializer<'de> for Deserializer<'gctx> {
6262
let (res, def) = res;
6363
return res.map_err(|e| e.with_key_context(&self.key, def));
6464
}
65-
Err(ConfigError::missing(&self.key))
65+
66+
// Let's assume that `unstable.git` had defined its `deserialize_any`` method,
67+
// then run `CARGO_UNSTABLE_GIT_SHALLOW_INDEX cargo fetch`, here will return `missing config key unstable.git`.
68+
// It seems that anything that starts with CARGO_UNSTABLE_GIT, even like CARGO_UNSTABLE_GITOXIDE can trigger this.
69+
// This is a workaround for now, but should be fixed in the future.
70+
visitor.visit_none()
6671
}
6772

6873
deserialize_method!(deserialize_bool, visit_bool, get_bool);

0 commit comments

Comments
 (0)