summaryrefslogtreecommitdiffstats
path: root/vendor/gix/src/revision/spec/parse/error.rs
blob: 3af4697b0da44e8db779c0c712e698def2910038 (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
use std::collections::HashSet;

use gix_hash::ObjectId;

use super::Error;
use crate::{bstr, bstr::BString, ext::ObjectIdExt, Repository};

/// Additional information about candidates that caused ambiguity.
#[derive(Debug)]
pub enum CandidateInfo {
    /// An error occurred when looking up the object in the database.
    FindError {
        /// The reported error.
        source: crate::object::find::existing::Error,
    },
    /// The candidate is an object of the given `kind`.
    Object {
        /// The kind of the object.
        kind: gix_object::Kind,
    },
    /// The candidate is a tag.
    Tag {
        /// The name of the tag.
        name: BString,
    },
    /// The candidate is a commit.
    Commit {
        /// The date of the commit.
        date: gix_date::Time,
        /// The subject line.
        title: BString,
    },
}

impl std::fmt::Display for CandidateInfo {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            CandidateInfo::FindError { source } => write!(f, "lookup error: {source}"),
            CandidateInfo::Tag { name } => write!(f, "tag {name:?}"),
            CandidateInfo::Object { kind } => std::fmt::Display::fmt(kind, f),
            CandidateInfo::Commit { date, title } => {
                write!(f, "commit {} {title:?}", date.format(gix_date::time::format::SHORT))
            }
        }
    }
}

impl Error {
    pub(crate) fn ambiguous(candidates: HashSet<ObjectId>, prefix: gix_hash::Prefix, repo: &Repository) -> Self {
        #[derive(PartialOrd, Ord, Eq, PartialEq, Copy, Clone)]
        enum Order {
            Tag,
            Commit,
            Tree,
            Blob,
            Invalid,
        }
        let candidates = {
            let mut c: Vec<_> = candidates
                .into_iter()
                .map(|oid| {
                    let obj = repo.find_object(oid);
                    let order = match &obj {
                        Err(_) => Order::Invalid,
                        Ok(obj) => match obj.kind {
                            gix_object::Kind::Tag => Order::Tag,
                            gix_object::Kind::Commit => Order::Commit,
                            gix_object::Kind::Tree => Order::Tree,
                            gix_object::Kind::Blob => Order::Blob,
                        },
                    };
                    (oid, obj, order)
                })
                .collect();
            c.sort_by(|lhs, rhs| lhs.2.cmp(&rhs.2).then_with(|| lhs.0.cmp(&rhs.0)));
            c
        };
        Error::AmbiguousPrefix {
            prefix,
            info: candidates
                .into_iter()
                .map(|(oid, find_result, _)| {
                    let info = match find_result {
                        Ok(obj) => match obj.kind {
                            gix_object::Kind::Tree | gix_object::Kind::Blob => CandidateInfo::Object { kind: obj.kind },
                            gix_object::Kind::Tag => {
                                let tag = obj.to_tag_ref();
                                CandidateInfo::Tag { name: tag.name.into() }
                            }
                            gix_object::Kind::Commit => {
                                use bstr::ByteSlice;
                                let commit = obj.to_commit_ref();
                                CandidateInfo::Commit {
                                    date: commit.committer().time,
                                    title: commit.message().title.trim().into(),
                                }
                            }
                        },
                        Err(err) => CandidateInfo::FindError { source: err },
                    };
                    (oid.attach(repo).shorten().unwrap_or_else(|_| oid.into()), info)
                })
                .collect(),
        }
    }

    pub(crate) fn from_errors(errors: Vec<Self>) -> Self {
        assert!(!errors.is_empty());
        match errors.len() {
            0 => unreachable!(
                "BUG: cannot create something from nothing, must have recorded some errors to call from_errors()"
            ),
            1 => errors.into_iter().next().expect("one"),
            _ => {
                let mut it = errors.into_iter().rev();
                let mut recent = Error::Multi {
                    current: Box::new(it.next().expect("at least one error")),
                    next: None,
                };
                for err in it {
                    recent = Error::Multi {
                        current: Box::new(err),
                        next: Some(Box::new(recent)),
                    }
                }
                recent
            }
        }
    }
}