Skip to content

Commit

Permalink
feat: Support parse string as git/gitoxide features from ENV and Config
Browse files Browse the repository at this point in the history
- pass 'all' or true to enable all pre-definded git/gitoxide features
- support parse git/gitoxide as table in Config, if the field is tagged with #[serde(default)], then it can be skipped
  • Loading branch information
linyihai committed May 21, 2024
1 parent 1d19e76 commit 76421f7
Show file tree
Hide file tree
Showing 3 changed files with 400 additions and 31 deletions.
140 changes: 132 additions & 8 deletions src/cargo/core/features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -759,7 +759,9 @@ unstable_cli_options!(
dual_proc_macros: bool = ("Build proc-macros for both the host and the target"),
features: Option<Vec<String>>,
gc: bool = ("Track cache usage and \"garbage collect\" unused files"),
#[serde(deserialize_with = "deserialize_git_features")]
git: Option<GitFeatures> = ("Enable support for shallow git fetch operations"),
#[serde(deserialize_with = "deserialize_gitoxide_features")]
gitoxide: Option<GitoxideFeatures> = ("Use gitoxide for the given git interactions, or all of them if no argument is given"),
host_config: bool = ("Enable the `[host]` section in the .cargo/config.toml file"),
minimal_versions: bool = ("Resolve minimal dependency versions instead of maximum"),
Expand Down Expand Up @@ -866,7 +868,8 @@ where
))
}

#[derive(Debug, Copy, Clone, Default, Deserialize)]
#[derive(Debug, Copy, Clone, Default, Deserialize, Ord, PartialOrd, Eq, PartialEq)]
#[serde(default)]
pub struct GitFeatures {
/// When cloning the index, perform a shallow clone. Maintain shallowness upon subsequent fetches.
pub shallow_index: bool,
Expand All @@ -875,12 +878,71 @@ pub struct GitFeatures {
}

impl GitFeatures {
fn all() -> Self {
pub fn all() -> Self {
GitFeatures {
shallow_index: true,
shallow_deps: true,
}
}

fn expecting() -> String {
let fields = vec!["'all'", "'shallow-index'", "'shallow-deps'"];
format!(
"unstable 'git' only takes {} as valid inputs, your can use 'all' to turn out all git features",
fields.join(" and ")
)
}
}

fn deserialize_git_features<'de, D>(deserializer: D) -> Result<Option<GitFeatures>, D::Error>
where
D: serde::de::Deserializer<'de>,
{
struct GitFeaturesVisitor;

impl<'de> serde::de::Visitor<'de> for GitFeaturesVisitor {
type Value = Option<GitFeatures>;

fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(&GitFeatures::expecting())
}

fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
if v {
Ok(Some(GitFeatures::all()))
} else {
Ok(None)
}
}

fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(parse_git(s.split(",")).map_err(serde::de::Error::custom)?)
}

fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
where
D: serde::de::Deserializer<'de>,
{
let git = GitFeatures::deserialize(deserializer)?;
Ok(Some(git))
}

fn visit_map<V>(self, map: V) -> Result<Self::Value, V::Error>
where
V: serde::de::MapAccess<'de>,
{
let mvd = serde::de::value::MapAccessDeserializer::new(map);
Ok(Some(GitFeatures::deserialize(mvd)?))
}
}

deserializer.deserialize_any(GitFeaturesVisitor)
}

fn parse_git(it: impl Iterator<Item = impl AsRef<str>>) -> CargoResult<Option<GitFeatures>> {
Expand All @@ -892,19 +954,19 @@ fn parse_git(it: impl Iterator<Item = impl AsRef<str>>) -> CargoResult<Option<Gi

for e in it {
match e.as_ref() {
"all" => return Ok(Some(GitFeatures::all())),
"shallow-index" => *shallow_index = true,
"shallow-deps" => *shallow_deps = true,
_ => {
bail!(
"unstable 'git' only takes 'shallow-index' and 'shallow-deps' as valid inputs"
)
bail!(GitFeatures::expecting())
}
}
}
Ok(Some(out))
}

#[derive(Debug, Copy, Clone, Default, Deserialize)]
#[derive(Debug, Copy, Clone, Default, Deserialize, Ord, PartialOrd, Eq, PartialEq)]
#[serde(default)]
pub struct GitoxideFeatures {
/// All fetches are done with `gitoxide`, which includes git dependencies as well as the crates index.
pub fetch: bool,
Expand All @@ -918,7 +980,7 @@ pub struct GitoxideFeatures {
}

impl GitoxideFeatures {
fn all() -> Self {
pub fn all() -> Self {
GitoxideFeatures {
fetch: true,
checkout: true,
Expand All @@ -935,6 +997,67 @@ impl GitoxideFeatures {
internal_use_git2: false,
}
}

fn expecting() -> String {
let fields = vec!["'all'", "'fetch'", "'checkout'", "'internal-use-git2'"];
format!(
"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`",
fields.join(" and ")
)
}
}

fn deserialize_gitoxide_features<'de, D>(
deserializer: D,
) -> Result<Option<GitoxideFeatures>, D::Error>
where
D: serde::de::Deserializer<'de>,
{
struct GitoxideFeaturesVisitor;

impl<'de> serde::de::Visitor<'de> for GitoxideFeaturesVisitor {
type Value = Option<GitoxideFeatures>;

fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(&GitoxideFeatures::expecting())
}

fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(parse_gitoxide(s.split(",")).map_err(serde::de::Error::custom)?)
}

fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
if v {
Ok(Some(GitoxideFeatures::all()))
} else {
Ok(None)
}
}

fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
where
D: serde::de::Deserializer<'de>,
{
let gitoxide = GitoxideFeatures::deserialize(deserializer)?;
Ok(Some(gitoxide))
}

fn visit_map<V>(self, map: V) -> Result<Self::Value, V::Error>
where
V: serde::de::MapAccess<'de>,
{
let mvd = serde::de::value::MapAccessDeserializer::new(map);
Ok(Some(GitoxideFeatures::deserialize(mvd)?))
}
}

deserializer.deserialize_any(GitoxideFeaturesVisitor)
}

fn parse_gitoxide(
Expand All @@ -949,11 +1072,12 @@ fn parse_gitoxide(

for e in it {
match e.as_ref() {
"all" => return Ok(Some(GitoxideFeatures::all())),
"fetch" => *fetch = true,
"checkout" => *checkout = true,
"internal-use-git2" => *internal_use_git2 = true,
_ => {
bail!("unstable 'gitoxide' only takes `fetch` and 'checkout' as valid input, for shallow fetches see `-Zgit=shallow-index,shallow-deps`")
bail!(GitoxideFeatures::expecting())
}
}
}
Expand Down
14 changes: 13 additions & 1 deletion src/cargo/util/context/de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@ impl<'de, 'gctx> de::Deserializer<'de> for Deserializer<'gctx> {
let (res, def) = res;
return res.map_err(|e| e.with_key_context(&self.key, def));
}

// The effect here is the same as in [`deserialize_option`].
if self.gctx.has_key(&self.key, self.env_prefix_ok)? {
return visitor.visit_some(self);
}

Err(ConfigError::missing(&self.key))
}

Expand Down Expand Up @@ -265,8 +271,14 @@ impl<'gctx> ConfigMapAccess<'gctx> {
let mut field_key = de.key.clone();
field_key.push(field);
for env_key in de.gctx.env_keys() {
if env_key.starts_with(field_key.as_env_key()) {
if env_key == field_key.as_env_key() {
fields.insert(KeyKind::Normal(field.to_string()));
} else {
let mut env_key_prefix = field_key.as_env_key().to_string();
env_key_prefix.push('_');
if env_key.starts_with(&env_key_prefix) {
fields.insert(KeyKind::Normal(field.to_string()));
}
}
}
}
Expand Down
Loading

0 comments on commit 76421f7

Please sign in to comment.