Skip to content

Commit 4219b66

Browse files
committed
Auto merge of #13687 - linyihai:git-features-env, r=weihanglo
Allows the default git/gitoxide configuration to be obtained from the ENV and config ### What does this PR try to resolve? The default git/gitoxide feautures config can be obtained througt `-Zgit` and `-Zgitoxide`. However, it cannot be obtained from environment variables or configurations. It's not very ergonomic. ### How should we test and review this PR? The previous commit explained the staus quo, the next commit addressed the problem. ### Additional information Inspired by #11813 (comment) See also #13285 (comment) ### Change of usage 1. Mirror Zgit/Zgitoxide when they parsed as string Specify the feature you like ``` CARGO_UNSABLE_GIT='shallow-deps' cargo fetch cargo fetch --config "unstable.git='shallow-deps'" cargo fetch -Zgit="shallow-deps" ``` 2. Specify partial fields when parsed as table ``` CARGO_UNSTABLE_GITOXIDE_FETCH=true cargo fetch ``` The rest fields will use Rust default value. That said, checkout and internal_use_git2 will be false. Besides, you can pass true to the whole feature to specify the pre-defined features. ``` CARGO_UNSTABLE_GITOXIDE=true cargo fetch ```
2 parents 4b681c7 + ee87c91 commit 4219b66

File tree

3 files changed

+345
-8
lines changed

3 files changed

+345
-8
lines changed

src/cargo/core/features.rs

+130-8
Original file line numberDiff line numberDiff line change
@@ -764,7 +764,9 @@ unstable_cli_options!(
764764
dual_proc_macros: bool = ("Build proc-macros for both the host and the target"),
765765
features: Option<Vec<String>>,
766766
gc: bool = ("Track cache usage and \"garbage collect\" unused files"),
767+
#[serde(deserialize_with = "deserialize_git_features")]
767768
git: Option<GitFeatures> = ("Enable support for shallow git fetch operations"),
769+
#[serde(deserialize_with = "deserialize_gitoxide_features")]
768770
gitoxide: Option<GitoxideFeatures> = ("Use gitoxide for the given git interactions, or all of them if no argument is given"),
769771
host_config: bool = ("Enable the `[host]` section in the .cargo/config.toml file"),
770772
minimal_versions: bool = ("Resolve minimal dependency versions instead of maximum"),
@@ -874,7 +876,8 @@ where
874876
))
875877
}
876878

877-
#[derive(Debug, Copy, Clone, Default, Deserialize)]
879+
#[derive(Debug, Copy, Clone, Default, Deserialize, Ord, PartialOrd, Eq, PartialEq)]
880+
#[serde(default)]
878881
pub struct GitFeatures {
879882
/// When cloning the index, perform a shallow clone. Maintain shallowness upon subsequent fetches.
880883
pub shallow_index: bool,
@@ -883,12 +886,71 @@ pub struct GitFeatures {
883886
}
884887

885888
impl GitFeatures {
886-
fn all() -> Self {
889+
pub fn all() -> Self {
887890
GitFeatures {
888891
shallow_index: true,
889892
shallow_deps: true,
890893
}
891894
}
895+
896+
fn expecting() -> String {
897+
let fields = vec!["`shallow-index`", "`shallow-deps`"];
898+
format!(
899+
"unstable 'git' only takes {} as valid inputs",
900+
fields.join(" and ")
901+
)
902+
}
903+
}
904+
905+
fn deserialize_git_features<'de, D>(deserializer: D) -> Result<Option<GitFeatures>, D::Error>
906+
where
907+
D: serde::de::Deserializer<'de>,
908+
{
909+
struct GitFeaturesVisitor;
910+
911+
impl<'de> serde::de::Visitor<'de> for GitFeaturesVisitor {
912+
type Value = Option<GitFeatures>;
913+
914+
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
915+
formatter.write_str(&GitFeatures::expecting())
916+
}
917+
918+
fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
919+
where
920+
E: serde::de::Error,
921+
{
922+
if v {
923+
Ok(Some(GitFeatures::all()))
924+
} else {
925+
Ok(None)
926+
}
927+
}
928+
929+
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
930+
where
931+
E: serde::de::Error,
932+
{
933+
Ok(parse_git(s.split(",")).map_err(serde::de::Error::custom)?)
934+
}
935+
936+
fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
937+
where
938+
D: serde::de::Deserializer<'de>,
939+
{
940+
let git = GitFeatures::deserialize(deserializer)?;
941+
Ok(Some(git))
942+
}
943+
944+
fn visit_map<V>(self, map: V) -> Result<Self::Value, V::Error>
945+
where
946+
V: serde::de::MapAccess<'de>,
947+
{
948+
let mvd = serde::de::value::MapAccessDeserializer::new(map);
949+
Ok(Some(GitFeatures::deserialize(mvd)?))
950+
}
951+
}
952+
953+
deserializer.deserialize_any(GitFeaturesVisitor)
892954
}
893955

894956
fn parse_git(it: impl Iterator<Item = impl AsRef<str>>) -> CargoResult<Option<GitFeatures>> {
@@ -903,16 +965,15 @@ fn parse_git(it: impl Iterator<Item = impl AsRef<str>>) -> CargoResult<Option<Gi
903965
"shallow-index" => *shallow_index = true,
904966
"shallow-deps" => *shallow_deps = true,
905967
_ => {
906-
bail!(
907-
"unstable 'git' only takes 'shallow-index' and 'shallow-deps' as valid inputs"
908-
)
968+
bail!(GitFeatures::expecting())
909969
}
910970
}
911971
}
912972
Ok(Some(out))
913973
}
914974

915-
#[derive(Debug, Copy, Clone, Default, Deserialize)]
975+
#[derive(Debug, Copy, Clone, Default, Deserialize, Ord, PartialOrd, Eq, PartialEq)]
976+
#[serde(default)]
916977
pub struct GitoxideFeatures {
917978
/// All fetches are done with `gitoxide`, which includes git dependencies as well as the crates index.
918979
pub fetch: bool,
@@ -926,7 +987,7 @@ pub struct GitoxideFeatures {
926987
}
927988

928989
impl GitoxideFeatures {
929-
fn all() -> Self {
990+
pub fn all() -> Self {
930991
GitoxideFeatures {
931992
fetch: true,
932993
checkout: true,
@@ -943,6 +1004,67 @@ impl GitoxideFeatures {
9431004
internal_use_git2: false,
9441005
}
9451006
}
1007+
1008+
fn expecting() -> String {
1009+
let fields = vec!["`fetch`", "`checkout`", "`internal-use-git2`"];
1010+
format!(
1011+
"unstable 'gitoxide' only takes {} as valid inputs, for shallow fetches see `-Zgit=shallow-index,shallow-deps`",
1012+
fields.join(" and ")
1013+
)
1014+
}
1015+
}
1016+
1017+
fn deserialize_gitoxide_features<'de, D>(
1018+
deserializer: D,
1019+
) -> Result<Option<GitoxideFeatures>, D::Error>
1020+
where
1021+
D: serde::de::Deserializer<'de>,
1022+
{
1023+
struct GitoxideFeaturesVisitor;
1024+
1025+
impl<'de> serde::de::Visitor<'de> for GitoxideFeaturesVisitor {
1026+
type Value = Option<GitoxideFeatures>;
1027+
1028+
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1029+
formatter.write_str(&GitoxideFeatures::expecting())
1030+
}
1031+
1032+
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
1033+
where
1034+
E: serde::de::Error,
1035+
{
1036+
Ok(parse_gitoxide(s.split(",")).map_err(serde::de::Error::custom)?)
1037+
}
1038+
1039+
fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
1040+
where
1041+
E: serde::de::Error,
1042+
{
1043+
if v {
1044+
Ok(Some(GitoxideFeatures::all()))
1045+
} else {
1046+
Ok(None)
1047+
}
1048+
}
1049+
1050+
fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
1051+
where
1052+
D: serde::de::Deserializer<'de>,
1053+
{
1054+
let gitoxide = GitoxideFeatures::deserialize(deserializer)?;
1055+
Ok(Some(gitoxide))
1056+
}
1057+
1058+
fn visit_map<V>(self, map: V) -> Result<Self::Value, V::Error>
1059+
where
1060+
V: serde::de::MapAccess<'de>,
1061+
{
1062+
let mvd = serde::de::value::MapAccessDeserializer::new(map);
1063+
Ok(Some(GitoxideFeatures::deserialize(mvd)?))
1064+
}
1065+
}
1066+
1067+
deserializer.deserialize_any(GitoxideFeaturesVisitor)
9461068
}
9471069

9481070
fn parse_gitoxide(
@@ -961,7 +1083,7 @@ fn parse_gitoxide(
9611083
"checkout" => *checkout = true,
9621084
"internal-use-git2" => *internal_use_git2 = true,
9631085
_ => {
964-
bail!("unstable 'gitoxide' only takes `fetch` and 'checkout' as valid input, for shallow fetches see `-Zgit=shallow-index,shallow-deps`")
1086+
bail!(GitoxideFeatures::expecting())
9651087
}
9661088
}
9671089
}

src/cargo/util/context/de.rs

+6
Original file line numberDiff line numberDiff line change
@@ -62,6 +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, Some(def)));
6464
}
65+
66+
// The effect here is the same as in `deserialize_option`.
67+
if self.gctx.has_key(&self.key, self.env_prefix_ok)? {
68+
return visitor.visit_some(self);
69+
}
70+
6571
Err(ConfigError::missing(&self.key))
6672
}
6773

0 commit comments

Comments
 (0)