blob: 307138b8fb346613c26ff824d5a432de87ced926 [file] [log] [blame] [edit]
//! Utilities to handle program arguments and other values of interest.
use std::ffi::{OsStr, OsString};
use crate::bstr::{BString, ByteVec};
/// Returns the name of the agent for identification towards a remote server as statically known when compiling the crate.
/// Suitable for both `git` servers and HTTP servers, and used unless configured otherwise.
///
/// Note that it's meant to be used in conjunction with [`protocol::agent()`][crate::protocol::agent()] which
/// prepends `git/`.
pub fn agent() -> &'static str {
concat!("oxide-", env!("CARGO_PKG_VERSION"))
}
/// Equivalent to `std::env::args_os()`, but with precomposed unicode on MacOS and other apple platforms.
/// It does not change the input arguments on any other platform.
///
/// Note that this ignores `core.precomposeUnicode` as git-config isn't available yet. It's default enabled in modern git though,
/// and generally decomposed unicode is nothing one would want in a git repository.
pub fn args_os() -> impl Iterator<Item = OsString> {
args_os_opt(cfg!(target_vendor = "apple"))
}
/// Like [`args_os()`], but with the `precompose_unicode` parameter akin to `core.precomposeUnicode` in the Git configuration.
pub fn args_os_opt(precompose_unicode: bool) -> impl Iterator<Item = OsString> {
std::env::args_os().map(move |arg| {
if precompose_unicode {
gix_utils::str::precompose_os_string(arg.into()).into_owned()
} else {
arg
}
})
}
/// Convert the given `input` into a `BString`, useful for usage in `clap`.
pub fn os_str_to_bstring(input: &OsStr) -> Option<BString> {
Vec::from_os_string(input.into()).map(Into::into).ok()
}
/// Utilities to collate errors of common operations into one error type.
///
/// This is useful as this type can present an API to answer common questions, like whether a network request seems to have failed
/// spuriously or if the underlying repository seems to be corrupted.
/// Error collation supports all operations, including opening the repository.
///
/// ### Usage
///
/// The caller may define a function that specifies the result type as `Result<T, gix::env::collate::{operation}::Error>` to collect
/// errors into a well-known error type which provides an API for simple queries.
pub mod collate {
///
#[allow(clippy::empty_docs)]
pub mod fetch {
/// An error which combines all possible errors when opening a repository, finding remotes and using them to fetch.
///
/// It can be used to detect if the repository is likely be corrupted in some way, or if the fetch failed spuriously
/// and thus can be retried.
#[derive(Debug, thiserror::Error)]
#[allow(missing_docs)]
pub enum Error<E: std::error::Error + Send + Sync + 'static = std::convert::Infallible> {
#[error(transparent)]
Open(#[from] crate::open::Error),
#[error(transparent)]
FindExistingReference(#[from] crate::reference::find::existing::Error),
#[error(transparent)]
RemoteInit(#[from] crate::remote::init::Error),
#[error(transparent)]
FindExistingRemote(#[from] crate::remote::find::existing::Error),
#[error(transparent)]
#[cfg(feature = "credentials")]
CredentialHelperConfig(#[from] crate::config::credential_helpers::Error),
#[cfg(any(feature = "blocking-network-client", feature = "async-network-client"))]
#[error(transparent)]
Connect(#[from] crate::remote::connect::Error),
#[cfg(any(feature = "blocking-network-client", feature = "async-network-client"))]
#[error(transparent)]
PrepareFetch(#[from] crate::remote::fetch::prepare::Error),
#[cfg(any(feature = "blocking-network-client", feature = "async-network-client"))]
#[error(transparent)]
Fetch(#[from] crate::remote::fetch::Error),
#[error(transparent)]
Other(E),
}
#[cfg(any(feature = "async-network-client", feature = "blocking-network-client"))]
impl<E> crate::protocol::transport::IsSpuriousError for Error<E>
where
E: std::error::Error + Send + Sync + 'static,
{
fn is_spurious(&self) -> bool {
match self {
Error::Open(_)
| Error::CredentialHelperConfig(_)
| Error::RemoteInit(_)
| Error::FindExistingReference(_)
| Error::FindExistingRemote(_)
| Error::Other(_) => false,
Error::Connect(err) => err.is_spurious(),
Error::PrepareFetch(err) => err.is_spurious(),
Error::Fetch(err) => err.is_spurious(),
}
}
}
/// Queries
impl<E> Error<E>
where
E: std::error::Error + Send + Sync + 'static,
{
/// Return true if repository corruption caused the failure.
pub fn is_corrupted(&self) -> bool {
match self {
Error::Open(crate::open::Error::NotARepository { .. } | crate::open::Error::Config(_)) => true,
#[cfg(any(feature = "async-network-client", feature = "blocking-network-client"))]
Error::PrepareFetch(crate::remote::fetch::prepare::Error::RefMap(
// Configuration couldn't be accessed or was incomplete.
crate::remote::ref_map::Error::GatherTransportConfig { .. }
| crate::remote::ref_map::Error::ConfigureCredentials(_),
)) => true,
// Maybe the value of the configuration was corrupted, or a file couldn't be removed.
#[cfg(any(feature = "async-network-client", feature = "blocking-network-client"))]
Error::Fetch(
crate::remote::fetch::Error::PackThreads(_)
| crate::remote::fetch::Error::PackIndexVersion(_)
| crate::remote::fetch::Error::RemovePackKeepFile { .. }
| crate::remote::fetch::Error::Negotiate(_),
) => true,
_ => false,
}
}
}
}
}