From 339d7fc097d2c3c256345e551479b1817bed7821 Mon Sep 17 00:00:00 2001 From: Fintan Halpenny Date: Wed, 12 Oct 2022 16:56:40 +0100 Subject: [PATCH] git-ref-format: qualified macro Add a `qualified!` macro to the set of git-ref-format macros. This allows the construction of a `refs//` reference from a string literal. Signed-off-by: Fintan Halpenny --- git-ref-format/macro/src/lib.rs | 48 ++++++++++++++++++++++++++++++++- git-ref-format/src/lib.rs | 2 ++ git-ref-format/t/src/tests.rs | 17 +++++++++++- 3 files changed, 65 insertions(+), 2 deletions(-) diff --git a/git-ref-format/macro/src/lib.rs b/git-ref-format/macro/src/lib.rs index cffbdbd..4b617df 100644 --- a/git-ref-format/macro/src/lib.rs +++ b/git-ref-format/macro/src/lib.rs @@ -13,7 +13,7 @@ use proc_macro_error::abort; use quote::quote; use syn::{parse_macro_input, LitStr}; -use git_ref_format_core::{refspec::PatternStr, Component, Error, RefStr}; +use git_ref_format_core::{refspec::PatternStr, Component, Error, Qualified, RefStr}; /// Create a [`git_ref_format_core::RefString`] from a string literal. /// @@ -46,6 +46,52 @@ pub fn refname(input: TokenStream) -> TokenStream { } } +/// Create a [`git_ref_format_core::Qualified`] from a string literal. +/// +/// The string is validated at compile time, and an unsafe conversion is +/// emitted. +#[proc_macro_error] +#[proc_macro] +pub fn qualified(input: TokenStream) -> TokenStream { + let lit = parse_macro_input!(input as LitStr); + let val = lit.value(); + + let parsed: Result<&RefStr, Error> = val.as_str().try_into(); + match parsed { + Ok(name) => { + let qualified: Option = Qualified::from_refstr(name); + match qualified { + Some(safe) => { + let safe: &str = safe.as_str(); + let expand = quote! { + unsafe { + use ::std::{borrow::Cow, mem::transmute}; + use ::git_ref_format::{Component, RefStr, RefString}; + + let inner: RefString = transmute(#safe.to_owned()); + let cow: Cow<'static, RefStr> = Cow::Owned(inner); + transmute::<_, Qualified>(cow) + } + }; + + TokenStream::from(expand) + }, + + None => { + abort!( + lit.span(), + "refname is not of the form 'refs//'" + ); + }, + } + }, + + Err(e) => { + abort!(lit.span(), "invalid refname literal: {}", e); + }, + } +} + /// Create a [`git_ref_format_core::Component`] from a string literal. /// /// The string is validated at compile time, and an unsafe conversion is diff --git a/git-ref-format/src/lib.rs b/git-ref-format/src/lib.rs index 78a536f..a0dad0f 100644 --- a/git-ref-format/src/lib.rs +++ b/git-ref-format/src/lib.rs @@ -144,6 +144,8 @@ pub mod name { pub use git_ref_format_macro::component; } +#[cfg(any(feature = "macro", feature = "git-ref-format-macro"))] +pub use git_ref_format_macro::qualified; #[cfg(any(feature = "macro", feature = "git-ref-format-macro"))] pub use git_ref_format_macro::refname; diff --git a/git-ref-format/t/src/tests.rs b/git-ref-format/t/src/tests.rs index 48e4b7b..d021305 100644 --- a/git-ref-format/t/src/tests.rs +++ b/git-ref-format/t/src/tests.rs @@ -1,13 +1,28 @@ // Copyright © 2022 The Radicle Link Contributors // SPDX-License-Identifier: GPL-3.0-or-later -use git_ref_format::{component, name, refname, refspec, Error, Qualified, RefStr, RefString}; +use git_ref_format::{ + component, + name, + qualified, + refname, + refspec, + Error, + Qualified, + RefStr, + RefString, +}; #[test] fn refname_macro_works() { assert_eq!("refs/heads/main", refname!("refs/heads/main").as_str()) } +#[test] +fn qualified_macro_works() { + assert_eq!("refs/heads/main", qualified!("refs/heads/main").as_str()) +} + #[test] fn component_macro_works() { assert_eq!("self", name::component!("self").as_str())