summaryrefslogtreecommitdiffstats
path: root/vendor/gix/src/env.rs
blob: b4973b8d5bb3d5ccee0b8a86288d6ec234bd5afd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
//! 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.
#[cfg(not(target_vendor = "apple"))]
pub fn args_os() -> impl Iterator<Item = OsString> {
    std::env::args_os()
}

/// Equivalent to `std::env::args_os()`, but with precomposed unicode on MacOS and other apple platforms.
///
/// Note that this ignores `core.precomposeUnicode` as git-config isn't available yet. It's default enabled in modern git though.
#[cfg(target_vendor = "apple")]
pub fn args_os() -> impl Iterator<Item = OsString> {
    use unicode_normalization::UnicodeNormalization;
    std::env::args_os().map(|arg| match arg.to_str() {
        Some(arg) => arg.nfc().collect::<String>().into(),
        None => 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 {

    ///
    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,
                }
            }
        }
    }
}