//! The purpose of this crate is to abstract the user interface of `gix` (the command-line interface) from the actual implementation.//! That way, one day it's possible to provide alternative frontends, including user interfaces.//!//! ### What is `gix`?//!//! `gix` is a tool to aid developers of `gitoxide` run their code in real-world situations and to validate the `gix` API.//! This makes it more of a test-bed than a tool that could ever rival `git` in terms of feature-set.//!//! That said, `gix` may actively carve out a niche for itself where it sees the greatest benefits for users of `git`.//!//! ### This crate is internal - use `gix` instead.//!//! It's important to understand that this crate consider itself an implementation detail of the `gix` CLI and is not meant to be//! used for external consumption by means of `cargo` dependency. This is emphasized by there being no other documentation.//! There is also no intention of ever stabilizing this crate.//!//! If you want to get started with what powers `gix`, please take a look at the `gix` crate which provides all the building//! blocks to create any application, including a carbon-copy of `git` itself (at least aspirationally as not all capabilities are//! available in `gix` yet).//!//! For users of `gix`, this codebase might serve as elaborate example as most of not all of its APIs are used here.//!//! ## Feature Flags#![cfg_attr( all(doc, feature = "document-features"), doc = ::document_features::document_features!())]#![cfg_attr(all(doc, feature = "document-features"), feature(doc_cfg))]#![cfg_attr(feature = "async-client", allow(unused))]#![forbid(unsafe_code)] use std::str::FromStr; use anyhow::bail; #[derive(Debug, Eq, PartialEq, Hash, Clone, Copy)]pub enum OutputFormat { Human, #[cfg(feature = "serde")] Json,} impl OutputFormat { pub fn variants() -> &'static [&'static str] { &[ "human", #[cfg(feature = "serde")] "json", ] }} impl FromStr for OutputFormat { type Err = String; fn from_str(s: &str) -> Result<Self, Self::Err> { let s_lc = s.to_ascii_lowercase(); Ok(match s_lc.as_str() { "human" => OutputFormat::Human, #[cfg(feature = "serde")] "json" => OutputFormat::Json, _ => return Err(format!("Invalid output format: '{s}'")), }) }} pub mod commitgraph;#[cfg(feature = "corpus")]pub mod corpus;pub mod net; #[cfg(feature = "estimate-hours")]pub mod hours;pub mod index;pub mod mailmap;#[cfg(feature = "organize")]pub mod organize;pub mod pack;#[cfg(feature = "query")]pub mod query;#[cfg(feature = "blocking-client")]pub mod remote;pub mod repository; mod output; pub(crate) fn report_ignored_filter_errors( records: impl IntoIterator<Item = gix::worktree::state::checkout::ErrorRecord>, mut err: impl std::io::Write,) { for record in records { writeln!( err, "warning: {}: {}; continued without encoding conversion", record.path, record.error ) .ok(); }} mod discover;pub use discover::discover; pub fn trust(paths: &[std::path::PathBuf], mut out: impl std::io::Write) -> anyhow::Result<()> { let trust_width = "Reduced".len(); for path in paths { let trust = gix::sec::Trust::from_path_ownership(path)?; let trust = format!("{trust:?}"); writeln!(out, "{trust:<trust_width$} {}", path.display())?; } Ok(())} pub fn env(mut out: impl std::io::Write, format: OutputFormat) -> anyhow::Result<()> { if format != OutputFormat::Human { bail!("JSON output isn't supported"); } let width = 15; writeln!( out, "{field:>width$}: {}", std::path::Path::new(gix::path::env::shell()).display(), field = "shell", )?; writeln!( out, "{field:>width$}: {:?}", gix::path::env::installation_config_prefix(), field = "config prefix", )?; writeln!( out, "{field:>width$}: {:?}", gix::path::env::installation_config(), field = "config", )?; writeln!( out, "{field:>width$}: {}", gix::path::env::exe_invocation().display(), field = "git exe", )?; writeln!( out, "{field:>width$}: {:?}", gix::path::env::system_prefix(), field = "system prefix", )?; writeln!( out, "{field:>width$}: {:?}", gix::path::env::core_dir(), field = "core dir", )?; Ok(())} #[cfg(all(feature = "async-client", feature = "blocking-client"))]compile_error!("Cannot set both 'blocking-client' and 'async-client' features as they are mutually exclusive"); fn is_dir_to_mode(is_dir: bool) -> gix::index::entry::Mode { if is_dir { gix::index::entry::Mode::DIR } else { gix::index::entry::Mode::FILE }} #[cfg(test)]mod tests { #[test] fn ignored_filter_errors_are_warnings() { let mut actual = Vec::new(); super::report_ignored_filter_errors( [gix::worktree::state::checkout::ErrorRecord { path: "file.txt".into(), error: Box::new( gix::filter::plumbing::pipeline::convert::configuration::Error::UnknownEncoding { name: "UTF-32".into(), }, ), }], &mut actual, ); assert_eq!( actual, b"warning: file.txt: The encoding named 'UTF-32' isn't available; continued without encoding conversion\n" ); }}
//! The purpose of this crate is to abstract the user interface of `gix` (the command-line interface) from the actual implementation.
//! That way, one day it's possible to provide alternative frontends, including user interfaces.
//!
//! ### What is `gix`?
//!
//! `gix` is a tool to aid developers of `gitoxide` run their code in real-world situations and to validate the `gix` API.
//! This makes it more of a test-bed than a tool that could ever rival `git` in terms of feature-set.
//!
//! That said, `gix` may actively carve out a niche for itself where it sees the greatest benefits for users of `git`.
//!
//! ### This crate is internal - use `gix` instead.
//!
//! It's important to understand that this crate consider itself an implementation detail of the `gix` CLI and is not meant to be
//! used for external consumption by means of `cargo` dependency. This is emphasized by there being no other documentation.
//! There is also no intention of ever stabilizing this crate.
//!
//! If you want to get started with what powers `gix`, please take a look at the `gix` crate which provides all the building
//! blocks to create any application, including a carbon-copy of `git` itself (at least aspirationally as not all capabilities are
//! available in `gix` yet).
//!
//! For users of `gix`, this codebase might serve as elaborate example as most of not all of its APIs are used here.
//!
//! ## Feature Flags
#![cfg_attr(
all(doc, feature = "document-features"),
doc = ::document_features::document_features!()
)]
#![cfg_attr(all(doc, feature = "document-features"), feature(doc_cfg))]
#![cfg_attr(feature = "async-client", allow(unused))]
#![forbid(unsafe_code)]
use std::str::FromStr;
use anyhow::bail;
#[derive(Debug, Eq, PartialEq, Hash, Clone, Copy)]
pub enum OutputFormat {
Human,
#[cfg(feature = "serde")]
Json,
}
impl OutputFormat {
pub fn variants() -> &'static [&'static str] {
&[
"human",
#[cfg(feature = "serde")]
"json",
]
}
}
impl FromStr for OutputFormat {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s_lc = s.to_ascii_lowercase();
Ok(match s_lc.as_str() {
"human" => OutputFormat::Human,
#[cfg(feature = "serde")]
"json" => OutputFormat::Json,
_ => return Err(format!("Invalid output format: '{s}'")),
})
}
}
pub mod commitgraph;
#[cfg(feature = "corpus")]
pub mod corpus;
pub mod net;
#[cfg(feature = "estimate-hours")]
pub mod hours;
pub mod index;
pub mod mailmap;
#[cfg(feature = "organize")]
pub mod organize;
pub mod pack;
#[cfg(feature = "query")]
pub mod query;
#[cfg(feature = "blocking-client")]
pub mod remote;
pub mod repository;
mod output;
pub(crate) fn report_ignored_filter_errors(
records: impl IntoIterator<Item = gix::worktree::state::checkout::ErrorRecord>,
mut err: impl std::io::Write,
) {
for record in records {
writeln!(
err,
"warning: {}: {}; continued without encoding conversion",
record.path, record.error
)
.ok();
}
}
mod discover;
pub use discover::discover;
pub fn trust(paths: &[std::path::PathBuf], mut out: impl std::io::Write) -> anyhow::Result<()> {
let trust_width = "Reduced".len();
for path in paths {
let trust = gix::sec::Trust::from_path_ownership(path)?;
let trust = format!("{trust:?}");
writeln!(out, "{trust:<trust_width$} {}", path.display())?;
}
Ok(())
}
pub fn env(mut out: impl std::io::Write, format: OutputFormat) -> anyhow::Result<()> {
if format != OutputFormat::Human {
bail!("JSON output isn't supported");
}
let width = 15;
writeln!(
out,
"{field:>width$}: {}",
std::path::Path::new(gix::path::env::shell()).display(),
field = "shell",
)?;
writeln!(
out,
"{field:>width$}: {:?}",
gix::path::env::installation_config_prefix(),
field = "config prefix",
)?;
writeln!(
out,
"{field:>width$}: {:?}",
gix::path::env::installation_config(),
field = "config",
)?;
writeln!(
out,
"{field:>width$}: {}",
gix::path::env::exe_invocation().display(),
field = "git exe",
)?;
writeln!(
out,
"{field:>width$}: {:?}",
gix::path::env::system_prefix(),
field = "system prefix",
)?;
writeln!(
out,
"{field:>width$}: {:?}",
gix::path::env::core_dir(),
field = "core dir",
)?;
Ok(())
}
#[cfg(all(feature = "async-client", feature = "blocking-client"))]
compile_error!("Cannot set both 'blocking-client' and 'async-client' features as they are mutually exclusive");
fn is_dir_to_mode(is_dir: bool) -> gix::index::entry::Mode {
if is_dir {
gix::index::entry::Mode::DIR
} else {
gix::index::entry::Mode::FILE
}
}
#[cfg(test)]
mod tests {
#[test]
fn ignored_filter_errors_are_warnings() {
let mut actual = Vec::new();
super::report_ignored_filter_errors(
[gix::worktree::state::checkout::ErrorRecord {
path: "file.txt".into(),
error: Box::new(
gix::filter::plumbing::pipeline::convert::configuration::Error::UnknownEncoding {
name: "UTF-32".into(),
},
),
}],
&mut actual,
);
assert_eq!(
actual,
b"warning: file.txt: The encoding named 'UTF-32' isn't available; continued without encoding conversion\n"
);
}
}