git_ref_format_macro/
lib.rs

1#[macro_use]
2extern crate proc_macro_error2;
3
4use std::convert::TryInto;
5
6use proc_macro::TokenStream;
7use proc_macro_error2::abort;
8use quote::quote;
9use syn::{parse_macro_input, LitStr};
10
11use git_ref_format_core::{refspec::PatternStr, Component, Error, Qualified, RefStr};
12
13/// Create a [`git_ref_format_core::RefString`] from a string literal.
14///
15/// The string is validated at compile time, and an unsafe conversion is
16/// emitted.
17#[proc_macro_error]
18#[proc_macro]
19pub fn refname(input: TokenStream) -> TokenStream {
20    let lit = parse_macro_input!(input as LitStr);
21    let val = lit.value();
22
23    let parsed: Result<&RefStr, Error> = val.as_str().try_into();
24    match parsed {
25        Ok(safe) => {
26            let safe: &str = safe.as_str();
27            let expand = quote! {
28                unsafe {
29                    use ::std::mem::transmute;
30                    use ::radicle_git_ext::ref_format::RefString;
31
32                    transmute::<_, RefString>(#safe.to_owned())
33                }
34            };
35            TokenStream::from(expand)
36        }
37
38        Err(e) => {
39            abort!(lit.span(), "invalid refname literal: {}", e);
40        }
41    }
42}
43
44/// Create a [`git_ref_format_core::Qualified`] from a string literal.
45///
46/// The string is validated at compile time, and an unsafe conversion is
47/// emitted.
48#[proc_macro_error]
49#[proc_macro]
50pub fn qualified(input: TokenStream) -> TokenStream {
51    let lit = parse_macro_input!(input as LitStr);
52    let val = lit.value();
53
54    let parsed: Result<&RefStr, Error> = val.as_str().try_into();
55    match parsed {
56        Ok(name) => {
57            let qualified: Option<Qualified> = Qualified::from_refstr(name);
58            match qualified {
59                Some(safe) => {
60                    let safe: &str = safe.as_str();
61                    let expand = quote! {
62                        unsafe {
63                            use ::std::{borrow::Cow, mem::transmute};
64                            use ::radicle_git_ext::ref_format::{Component, RefStr, RefString, Qualified};
65
66                            let inner: RefString = transmute(#safe.to_owned());
67                            let cow: Cow<'static, RefStr> = Cow::Owned(inner);
68                            transmute::<_, Qualified>(cow)
69                        }
70                    };
71
72                    TokenStream::from(expand)
73                }
74
75                None => {
76                    abort!(
77                        lit.span(),
78                        "refname is not of the form 'refs/<category>/<name>'"
79                    );
80                }
81            }
82        }
83
84        Err(e) => {
85            abort!(lit.span(), "invalid refname literal: {}", e);
86        }
87    }
88}
89
90/// Create a [`git_ref_format_core::Component`] from a string literal.
91///
92/// The string is validated at compile time, and an unsafe conversion is
93/// emitted.
94#[proc_macro_error]
95#[proc_macro]
96pub fn component(input: TokenStream) -> TokenStream {
97    let lit = parse_macro_input!(input as LitStr);
98    let val = lit.value();
99
100    let name: Result<&RefStr, Error> = val.as_str().try_into();
101    match name {
102        Ok(name) => {
103            let comp: Option<Component> = name.into();
104            match comp {
105                Some(safe) => {
106                    let safe: &str = safe.as_ref().as_str();
107                    let expand = quote! {
108                        unsafe {
109                            use ::std::{borrow::Cow, mem::transmute};
110                            use ::radicle_git_ext::ref_format::{Component, RefStr, RefString};
111
112                            let inner: RefString = transmute(#safe.to_owned());
113                            let cow: Cow<'static, RefStr> = Cow::Owned(inner);
114                            transmute::<_, Component>(cow)
115                        }
116                    };
117
118                    TokenStream::from(expand)
119                }
120
121                None => {
122                    abort!(lit.span(), "component contains a '/'");
123                }
124            }
125        }
126
127        Err(e) => {
128            abort!(lit.span(), "invalid refname literal: {}", e);
129        }
130    }
131}
132
133/// Create a [`git_ref_format_core::refspec::PatternString`] from a string
134/// literal.
135///
136/// The string is validated at compile time, and an unsafe conversion is
137/// emitted.
138#[proc_macro_error]
139#[proc_macro]
140pub fn pattern(input: TokenStream) -> TokenStream {
141    let lit = parse_macro_input!(input as LitStr);
142    let val = lit.value();
143
144    let parsed: Result<&PatternStr, Error> = val.as_str().try_into();
145    match parsed {
146        Ok(safe) => {
147            let safe: &str = safe.as_str();
148            let expand = quote! {
149                unsafe {
150                    use ::std::mem::transmute;
151                    use ::radicle_git_ext::ref_format::refspec::PatternString;
152
153                    transmute::<_, PatternString>(#safe.to_owned())
154                }
155            };
156            TokenStream::from(expand)
157        }
158
159        Err(e) => {
160            abort!(lit.span(), "invalid refspec pattern literal: {}", e);
161        }
162    }
163}
164
165/// Create a [`git_ref_format_core::refspec::QualifiedPattern`] from a string
166/// literal.
167///
168/// The string is validated at compile time, and an unsafe conversion is
169/// emitted.
170#[proc_macro_error]
171#[proc_macro]
172pub fn qualified_pattern(input: TokenStream) -> TokenStream {
173    let lit = parse_macro_input!(input as LitStr);
174    let val = lit.value();
175
176    let parsed: Result<&PatternStr, Error> = val.as_str().try_into();
177    match parsed {
178        Ok(safe) => {
179            let safe: &str = safe.as_str();
180            let expand = quote! {
181                unsafe {
182                    use ::std::mem::transmute;
183                    use ::radicle_git_ext::ref_format::refspec::QualifiedPattern;
184
185                    transmute::<_, QualifiedPattern>(#safe.to_owned())
186                }
187            };
188            TokenStream::from(expand)
189        }
190
191        Err(e) => {
192            abort!(lit.span(), "invalid refspec pattern literal: {}", e);
193        }
194    }
195}