git_ref_format_core/
lit.rs

1use crate::{name, Qualified, RefStr};
2
3/// A literal [`RefStr`].
4///
5/// Types implementing [`Lit`] must be [`name::Component`]s, and provide a
6/// conversion from a component _iff_ the component's [`RefStr`] representation
7/// is equal to [`Lit::NAME`]. Because these morphisms can only be guaranteed
8/// axiomatically, the trait can not currently be implemented by types outside
9/// of this crate.
10///
11/// [`Lit`] types are useful for efficiently creating known-valid [`Qualified`]
12/// refs, and sometimes for pattern matching.
13pub trait Lit: Sized + sealed::Sealed {
14    const SELF: Self;
15    const NAME: &'static RefStr;
16
17    #[inline]
18    fn from_component(c: &name::Component) -> Option<Self> {
19        (c.as_ref() == Self::NAME).then_some(Self::SELF)
20    }
21}
22
23impl<T: Lit> From<T> for &'static RefStr {
24    #[inline]
25    fn from(_: T) -> Self {
26        T::NAME
27    }
28}
29
30mod sealed {
31    pub trait Sealed {}
32}
33
34/// All known literal [`RefStr`]s.
35#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)]
36pub enum KnownLit {
37    Refs,
38    Heads,
39    Namespaces,
40    Remotes,
41    Tags,
42    Notes,
43}
44
45impl KnownLit {
46    #[inline]
47    pub fn from_component(c: &name::Component) -> Option<Self> {
48        let c: &RefStr = c.as_ref();
49        if c == Refs::NAME {
50            Some(Self::Refs)
51        } else if c == Heads::NAME {
52            Some(Self::Heads)
53        } else if c == Namespaces::NAME {
54            Some(Self::Namespaces)
55        } else if c == Remotes::NAME {
56            Some(Self::Remotes)
57        } else if c == Tags::NAME {
58            Some(Self::Tags)
59        } else if c == Notes::NAME {
60            Some(Self::Notes)
61        } else {
62            None
63        }
64    }
65}
66
67impl From<KnownLit> for name::Component<'_> {
68    #[inline]
69    fn from(k: KnownLit) -> Self {
70        match k {
71            KnownLit::Refs => Refs.into(),
72            KnownLit::Heads => Heads.into(),
73            KnownLit::Namespaces => Namespaces.into(),
74            KnownLit::Remotes => Remotes.into(),
75            KnownLit::Tags => Tags.into(),
76            KnownLit::Notes => Notes.into(),
77        }
78    }
79}
80
81/// Either a [`KnownLit`] or a [`name::Component`]
82pub enum SomeLit<'a> {
83    Known(KnownLit),
84    Any(name::Component<'a>),
85}
86
87impl SomeLit<'_> {
88    pub fn known(self) -> Option<KnownLit> {
89        match self {
90            Self::Known(k) => Some(k),
91            _ => None,
92        }
93    }
94}
95
96impl<'a> From<name::Component<'a>> for SomeLit<'a> {
97    #[inline]
98    fn from(c: name::Component<'a>) -> Self {
99        match KnownLit::from_component(&c) {
100            Some(k) => Self::Known(k),
101            None => Self::Any(c),
102        }
103    }
104}
105
106pub type RefsHeads<T> = (Refs, Heads, T);
107pub type RefsTags<T> = (Refs, Tags, T);
108pub type RefsNotes<T> = (Refs, Notes, T);
109pub type RefsRemotes<T> = (Refs, Remotes, T);
110pub type RefsNamespaces<'a, T> = (Refs, Namespaces, T, Qualified<'a>);
111
112#[inline]
113pub fn refs_heads<T: AsRef<RefStr>>(name: T) -> RefsHeads<T> {
114    (Refs, Heads, name)
115}
116
117#[inline]
118pub fn refs_tags<T: AsRef<RefStr>>(name: T) -> RefsTags<T> {
119    (Refs, Tags, name)
120}
121
122#[inline]
123pub fn refs_notes<T: AsRef<RefStr>>(name: T) -> RefsNotes<T> {
124    (Refs, Notes, name)
125}
126
127#[inline]
128pub fn refs_remotes<T: AsRef<RefStr>>(name: T) -> RefsRemotes<T> {
129    (Refs, Remotes, name)
130}
131
132#[inline]
133pub fn refs_namespaces<'a, 'b, T>(namespace: T, name: Qualified<'b>) -> RefsNamespaces<'b, T>
134where
135    T: Into<name::Component<'a>>,
136{
137    (Refs, Namespaces, namespace, name)
138}
139
140#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)]
141pub struct Refs;
142
143impl Lit for Refs {
144    const SELF: Self = Self;
145    const NAME: &'static RefStr = name::REFS;
146}
147impl sealed::Sealed for Refs {}
148
149#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)]
150pub struct Heads;
151
152impl Lit for Heads {
153    const SELF: Self = Self;
154    const NAME: &'static RefStr = name::HEADS;
155}
156impl sealed::Sealed for Heads {}
157
158#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)]
159pub struct Namespaces;
160
161impl Lit for Namespaces {
162    const SELF: Self = Self;
163    const NAME: &'static RefStr = name::NAMESPACES;
164}
165impl sealed::Sealed for Namespaces {}
166
167#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)]
168pub struct Remotes;
169
170impl Lit for Remotes {
171    const SELF: Self = Self;
172    const NAME: &'static RefStr = name::REMOTES;
173}
174impl sealed::Sealed for Remotes {}
175
176#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)]
177pub struct Tags;
178
179impl Lit for Tags {
180    const SELF: Self = Self;
181    const NAME: &'static RefStr = name::TAGS;
182}
183impl sealed::Sealed for Tags {}
184
185#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)]
186pub struct Notes;
187
188impl Lit for Notes {
189    const SELF: Self = Self;
190    const NAME: &'static RefStr = name::NOTES;
191}
192impl sealed::Sealed for Notes {}