git_ref_format_core/
lit.rs

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