Skip to content

Commit

Permalink
add keys required to deal with worktree conversions and filters.
Browse files Browse the repository at this point in the history
  • Loading branch information
Byron committed Jul 8, 2023
1 parent 94df6e0 commit 3fbd7b0
Show file tree
Hide file tree
Showing 9 changed files with 305 additions and 14 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions gix/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ gix-validate = { version = "^0.7.6", path = "../gix-validate" }
gix-sec = { version = "^0.8.3", path = "../gix-sec" }
gix-date = { version = "^0.7.0", path = "../gix-date" }
gix-refspec = { version = "^0.13.0", path = "../gix-refspec" }
gix-filter = { version = "^0.0.0", path = "../gix-filter" }

gix-config = { version = "^0.25.1", path = "../gix-config" }
gix-odb = { version = "^0.49.1", path = "../gix-odb" }
Expand Down
17 changes: 17 additions & 0 deletions gix/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,23 @@ pub mod key {
pub type GenericErrorWithValue<E = gix_config::value::Error> = Error<E, 'v', 'i'>;
}

///
pub mod encoding {
use crate::bstr::BString;

/// The error produced when failing to parse the `core.checkRoundTripEncoding` key.
#[derive(Debug, thiserror::Error)]
#[error("The encoding named '{encoding}' seen in key '{key}={value}' is unsupported")]
pub struct Error {
/// The configuration key that contained the value.
pub key: BString,
/// The value that was assigned to `key`.
pub value: BString,
/// The encoding that failed.
pub encoding: BString,
}
}

///
pub mod checkout {
///
Expand Down
182 changes: 182 additions & 0 deletions gix/src/config/tree/sections/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,18 @@ impl Core {
.with_environment_override("GIT_NO_REPLACE_OBJECTS");
/// The `core.commitGraph` key.
pub const COMMIT_GRAPH: keys::Boolean = keys::Boolean::new_boolean("commitGraph", &config::Tree::CORE);
/// The `core.safecrlf` key.
pub const SAFE_CRLF: SafeCrlf = SafeCrlf::new_with_validate("safecrlf", &config::Tree::CORE, validate::SafeCrlf);
/// The `core.autocrlf` key.
pub const AUTO_CRLF: AutoCrlf = AutoCrlf::new_with_validate("autocrlf", &config::Tree::CORE, validate::AutoCrlf);
/// The `core.eol` key.
pub const EOL: Eol = Eol::new_with_validate("eol", &config::Tree::CORE, validate::Eol);
/// The `core.checkRoundTripEncoding` key.
pub const CHECK_ROUND_TRIP_ENCODING: CheckRoundTripEncoding = CheckRoundTripEncoding::new_with_validate(
"checkRoundTripEncoding",
&config::Tree::CORE,
validate::CheckRoundTripEncoding,
);
}

impl Section for Core {
Expand Down Expand Up @@ -99,6 +111,10 @@ impl Section for Core {
&Self::SSH_COMMAND,
&Self::USE_REPLACE_REFS,
&Self::COMMIT_GRAPH,
&Self::SAFE_CRLF,
&Self::AUTO_CRLF,
&Self::EOL,
&Self::CHECK_ROUND_TRIP_ENCODING,
]
}
}
Expand All @@ -115,6 +131,140 @@ pub type LogAllRefUpdates = keys::Any<validate::LogAllRefUpdates>;
/// The `core.disambiguate` key.
pub type Disambiguate = keys::Any<validate::Disambiguate>;

/// The `core.safecrlf` key.
pub type SafeCrlf = keys::Any<validate::SafeCrlf>;

/// The `core.autocrlf` key.
pub type AutoCrlf = keys::Any<validate::AutoCrlf>;

/// The `core.eol` key.
pub type Eol = keys::Any<validate::Eol>;

/// The `core.checkRoundTripEncoding` key.
pub type CheckRoundTripEncoding = keys::Any<validate::CheckRoundTripEncoding>;

mod check_round_trip_encoding {
use crate::bstr::{BStr, ByteSlice};
use crate::config;
use crate::config::tree::core::CheckRoundTripEncoding;
use crate::config::tree::Key;
use std::borrow::Cow;

impl CheckRoundTripEncoding {
/// Convert `value` into a list of encodings, which are either space or coma separated. Fail if an encoding is unknown.
/// If `None`, the default is returned.
pub fn try_into_encodings(
&'static self,
value: Option<Cow<'_, BStr>>,
) -> Result<Vec<&'static gix_filter::encoding::Encoding>, config::encoding::Error> {
Ok(match value {
None => vec![gix_filter::encoding::SHIFT_JIS],
Some(value) => {
let mut out = Vec::new();
for encoding in value
.as_ref()
.split(|b| *b == b',' || *b == b' ')
.filter(|e| !e.trim().is_empty())
{
out.push(
gix_filter::encoding::Encoding::for_label(encoding.trim()).ok_or_else(|| {
config::encoding::Error {
key: self.logical_name().into(),
value: value.as_ref().to_owned(),
encoding: encoding.into(),
}
})?,
);
}
out
}
})
}
}
}

mod eol {
use crate::bstr::{BStr, ByteSlice};
use crate::config;
use crate::config::tree::core::Eol;
use std::borrow::Cow;

impl Eol {
/// Convert `value` into the default end-of-line mode.
///
/// ### Deviation
///
/// git will allow any value and silently leaves it unset, we will fail if the value is not known.
pub fn try_into_eol(
&'static self,
value: Cow<'_, BStr>,
) -> Result<gix_filter::eol::Mode, config::key::GenericErrorWithValue> {
Ok(match value.to_str_lossy().as_ref() {
"lf" => gix_filter::eol::Mode::Lf,
"crlf" => gix_filter::eol::Mode::CrLf,
"native" => gix_filter::eol::Mode::default(),
_ => return Err(config::key::GenericErrorWithValue::from_value(self, value.into_owned())),
})
}
}
}

mod safecrlf {
use crate::bstr::BStr;
use crate::config;
use crate::config::tree::core::SafeCrlf;
use gix_filter::pipeline::CrlfRoundTripCheck;
use std::borrow::Cow;

impl SafeCrlf {
/// Convert `value` into the safe-crlf enumeration, if possible.
pub fn try_into_safecrlf(
&'static self,
value: Cow<'_, BStr>,
) -> Result<CrlfRoundTripCheck, config::key::GenericErrorWithValue> {
if value.as_ref() == "warn" {
return Ok(CrlfRoundTripCheck::Warn);
}
let value = gix_config::Boolean::try_from(value.as_ref()).map_err(|err| {
config::key::GenericErrorWithValue::from_value(self, value.into_owned()).with_source(err)
})?;
Ok(if value.into() {
CrlfRoundTripCheck::Fail
} else {
CrlfRoundTripCheck::Skip
})
}
}
}

mod autocrlf {
use crate::bstr::BStr;
use crate::config;
use crate::config::tree::core::AutoCrlf;
use gix_filter::eol;
use std::borrow::Cow;

impl AutoCrlf {
/// Convert `value` into the safe-crlf enumeration, if possible.
pub fn try_into_autocrlf(
&'static self,
value: Cow<'_, BStr>,
) -> Result<eol::AutoCrlf, config::key::GenericErrorWithValue> {
if value.as_ref() == "input" {
return Ok(eol::AutoCrlf::Input);
}
let value = gix_config::Boolean::try_from(value.as_ref()).map_err(|err| {
config::key::GenericErrorWithValue::from_value(self, value.into_owned()).with_source(err)
})?;
Ok(if value.into() {
eol::AutoCrlf::Enabled
} else {
eol::AutoCrlf::Disabled
})
}
}
}

mod disambiguate {
use std::borrow::Cow;

Expand Down Expand Up @@ -306,4 +456,36 @@ mod validate {
Ok(())
}
}

pub struct SafeCrlf;
impl keys::Validate for SafeCrlf {
fn validate(&self, value: &BStr) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
super::Core::SAFE_CRLF.try_into_safecrlf(value.into())?;
Ok(())
}
}

pub struct AutoCrlf;
impl keys::Validate for AutoCrlf {
fn validate(&self, value: &BStr) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
super::Core::AUTO_CRLF.try_into_autocrlf(value.into())?;
Ok(())
}
}

pub struct Eol;
impl keys::Validate for Eol {
fn validate(&self, value: &BStr) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
super::Core::EOL.try_into_eol(value.into())?;
Ok(())
}
}

pub struct CheckRoundTripEncoding;
impl keys::Validate for CheckRoundTripEncoding {
fn validate(&self, value: &BStr) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
super::Core::CHECK_ROUND_TRIP_ENCODING.try_into_encodings(Some(value.into()))?;
Ok(())
}
}
}
7 changes: 7 additions & 0 deletions gix/src/config/tree/sections/gitoxide.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,12 @@ mod subsections {
.with_deviation(
"relative file paths will always be made relative to the git-common-dir, whereas `git` keeps them as is.",
);

/// The `gitoxide.core.filterProcessDelay` key (default `true`).
///
/// It controls whether or not long running filter driver processes can use the 'delay' capability.
pub const FILTER_PROCESS_DELAY: keys::Boolean =
keys::Boolean::new_boolean("filterProcessDelay", &Gitoxide::CORE);
}

impl Section for Core {
Expand All @@ -99,6 +105,7 @@ mod subsections {
&Self::USE_NSEC,
&Self::USE_STDEV,
&Self::SHALLOW_FILE,
&Self::FILTER_PROCESS_DELAY,
]
}

Expand Down
2 changes: 2 additions & 0 deletions gix/src/filter.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
//! lower-level access to filters which are applied to create working tree checkouts or to 'clean' working tree contents for storage in git.
pub use gix_filter as plumbing;
2 changes: 2 additions & 0 deletions gix/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,8 @@ pub mod worktree;

pub mod revision;

pub mod filter;

///
pub mod remote;

Expand Down
91 changes: 91 additions & 0 deletions gix/tests/config/tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,97 @@ mod core {
assert!(Core::CHECK_STAT.validate("foo".into()).is_err());
Ok(())
}

#[test]
fn safecrlf() -> crate::Result {
for (value, expected) in [
("false", gix_filter::pipeline::CrlfRoundTripCheck::Skip),
("true", gix_filter::pipeline::CrlfRoundTripCheck::Fail),
("warn", gix_filter::pipeline::CrlfRoundTripCheck::Warn),
] {
assert_eq!(Core::SAFE_CRLF.try_into_safecrlf(bcow(value)).unwrap(), expected);
assert!(Core::SAFE_CRLF.validate(value.into()).is_ok());
}
assert_eq!(
Core::SAFE_CRLF.try_into_safecrlf(bcow("WARN")).unwrap_err().to_string(),
"The key \"core.safecrlf=WARN\" was invalid"
);
Ok(())
}

#[test]
fn autocrlf() -> crate::Result {
for (value, expected) in [
("false", gix_filter::eol::AutoCrlf::Disabled),
("true", gix_filter::eol::AutoCrlf::Enabled),
("input", gix_filter::eol::AutoCrlf::Input),
] {
assert_eq!(Core::AUTO_CRLF.try_into_autocrlf(bcow(value)).unwrap(), expected);
assert!(Core::AUTO_CRLF.validate(value.into()).is_ok());
}
assert_eq!(
Core::AUTO_CRLF
.try_into_autocrlf(bcow("Input"))
.unwrap_err()
.to_string(),
"The key \"core.autocrlf=Input\" was invalid"
);
Ok(())
}

#[test]
fn eol() -> crate::Result {
for (value, expected) in [
("lf", gix_filter::eol::Mode::Lf),
("crlf", gix_filter::eol::Mode::CrLf),
("native", gix_filter::eol::Mode::default()),
] {
assert_eq!(Core::EOL.try_into_eol(bcow(value)).unwrap(), expected);
assert!(Core::EOL.validate(value.into()).is_ok());
}
assert_eq!(
Core::EOL.try_into_eol(bcow("LF")).unwrap_err().to_string(),
"The key \"core.eol=LF\" was invalid"
);
Ok(())
}

#[test]
fn check_round_trip_encoding() -> crate::Result {
for (value, expected) in [
(
Some("UTF-8 utf-16BE"),
&[gix_filter::encoding::UTF_8, gix_filter::encoding::UTF_16BE][..],
),
(
Some("SHIFT-JIS,UTF-8"),
&[gix_filter::encoding::SHIFT_JIS, gix_filter::encoding::UTF_8],
),
(
Some("UTF-16LE, SHIFT-JIS"),
&[gix_filter::encoding::UTF_16LE, gix_filter::encoding::SHIFT_JIS],
),
(None, &[gix_filter::encoding::SHIFT_JIS]),
] {
assert_eq!(
Core::CHECK_ROUND_TRIP_ENCODING
.try_into_encodings(value.map(bcow))
.unwrap(),
expected
);
if let Some(value) = value {
assert!(Core::CHECK_ROUND_TRIP_ENCODING.validate(value.into()).is_ok());
}
}
assert_eq!(
Core::CHECK_ROUND_TRIP_ENCODING
.try_into_encodings(Some(bcow("SOMETHING ELSE")))
.unwrap_err()
.to_string(),
"The encoding named 'SOMETHING' seen in key 'core.checkRoundTripEncoding=SOMETHING ELSE' is unsupported"
);
Ok(())
}
}

mod index {
Expand Down
Loading

0 comments on commit 3fbd7b0

Please sign in to comment.