#![no_std] //! [`git_ref_format`]: https://crates.io/crates/git-ref-format //! [`radicle-git-ext`]: https://crates.io/crates/radicle-git-ext //! //! This crate depends on and re-exports from [`git_ref_format_core`]. //! //! ## Macros //! //! Instead of providing procedural macros, like [`git_ref_format`] //! it just provides much simpler declarative macros, guarded by the feature //! flag `macro`. //! //! ### Benefits //! //! - Does not depend on [`radicle-git-ext`]. //! - Does not pull in procedural macro dependencies. //! - Has much smaller compile-time overhead than [`git_ref_format`]. //! //! ### Drawback //! //! The main drawback is that the macros in this crate cannot provide compile //! time validation of the argument. Thus, these macros must be used in //! conjunction with testing: If all generated objects are used in tests, and //! these tests are run, then the guarantees are equally strong. Consumers that //! do not or cannot test their code should not use the macros then. pub use git_ref_format_core::*; /// Create a [`git_ref_format_core::RefString`] from a string literal. /// /// Similar to [`core::debug_assert`], an optimized build will not validate /// (but rather perform an unsafe conversion) unless `-C debug-assertions` is /// passed to the compiler. #[cfg(any(feature = "macro", test))] #[macro_export] macro_rules! refname { ($arg:literal) => {{ use $crate::RefString; #[cfg(debug_assertions)] { RefString::try_from($arg).expect(core::concat!( "literal `", $arg, "` must be a valid reference name" )) } #[cfg(not(debug_assertions))] { extern crate alloc; use alloc::string::String; let s: String = $arg.to_owned(); unsafe { core::mem::transmute::<_, RefString>(s) } } }}; } /// Create a [`git_ref_format_core::Qualified`] from a string literal. /// /// Similar to [`core::debug_assert`], an optimized build will not validate /// (but rather perform an unsafe conversion) unless `-C debug-assertions` is /// passed to the compiler. #[cfg(any(feature = "macro", test))] #[macro_export] macro_rules! qualified { ($arg:literal) => {{ use $crate::Qualified; #[cfg(debug_assertions)] { Qualified::from_refstr($crate::refname!($arg)).expect(core::concat!( "literal `", $arg, "` must be of the form 'refs//'" )) } #[cfg(not(debug_assertions))] { extern crate alloc; use core::mem::transmute; use alloc::borrow::Cow; use alloc::string::String; use $crate::{RefStr, RefString}; let s: String = $arg.to_owned(); let refstring: RefString = unsafe { transmute(s) }; let cow: Cow<'_, RefStr> = Cow::Owned(refstring); let qualified: Qualified = unsafe { transmute(cow) }; qualified } }}; } /// Create a [`git_ref_format_core::Component`] from a string literal. /// /// Similar to [`core::debug_assert`], an optimized build will not validate /// (but rather perform an unsafe conversion) unless `-C debug-assertions` is /// passed to the compiler. #[cfg(any(feature = "macro", test))] #[macro_export] macro_rules! component { ($arg:literal) => {{ use $crate::Component; #[cfg(debug_assertions)] { Component::from_refstr($crate::refname!($arg)).expect(core::concat!( "literal `", $arg, "` must be a valid component (cannot contain '/')" )) } #[cfg(not(debug_assertions))] { extern crate alloc; use core::mem::transmute; use alloc::borrow::Cow; use alloc::string::String; use $crate::{RefStr, RefString}; let s: String = $arg.to_owned(); let refstring: RefString = unsafe { transmute(s) }; let cow: Cow<'_, RefStr> = Cow::Owned(refstring); let component: Component = unsafe { transmute(cow) }; component } }}; } /// Create a [`git_ref_format_core::refspec::PatternString`] from a string literal. /// /// Similar to [`core::debug_assert`], an optimized build will not validate /// (but rather perform an unsafe conversion) unless `-C debug-assertions` is /// passed to the compiler. #[cfg(any(feature = "macro", test))] #[macro_export] macro_rules! pattern { ($arg:literal) => {{ use $crate::refspec::PatternString; #[cfg(debug_assertions)] { PatternString::try_from($arg).expect(core::concat!( "literal `", $arg, "` must be a valid refspec pattern" )) } #[cfg(not(debug_assertions))] { extern crate alloc; use alloc::string::String; let s: String = $arg.to_owned(); unsafe { core::mem::transmute::<_, PatternString>(s) } } }}; } /// Create a [`git_ref_format_core::refspec::QualifiedPattern`] from a string literal. /// /// Similar to [`core::debug_assert`], an optimized build will not validate /// (but rather perform an unsafe conversion) unless `-C debug-assertions` is /// passed to the compiler. #[cfg(any(feature = "macro", test))] #[macro_export] macro_rules! qualified_pattern { ($arg:literal) => {{ use $crate::refspec::QualifiedPattern; #[cfg(debug_assertions)] { use core::concat; use $crate::refspec::PatternStr; let pattern = PatternStr::try_from_str($arg).expect(concat!( "literal `", $arg, "` must be a valid refspec pattern" )); QualifiedPattern::from_patternstr(pattern).expect(concat!( "literal `", $arg, "` must be a valid qualified refspec pattern" )) } #[cfg(not(debug_assertions))] { extern crate alloc; use core::mem::transmute; use alloc::borrow::Cow; use alloc::string::String; use $crate::refspec::{PatternStr, PatternString}; let s: String = $arg.to_owned(); let pattern: PatternString = unsafe { transmute(s) }; let cow: Cow<'_, PatternStr> = Cow::Owned(pattern); let qualified: QualifiedPattern = unsafe { transmute(cow) }; qualified } }}; } #[cfg(test)] mod test { #[test] fn refname() { let _ = crate::refname!("refs/heads/main"); let _ = crate::refname!("refs/tags/v1.0.0"); let _ = crate::refname!("refs/remotes/origin/main"); let _ = crate::refname!("a"); } #[test] #[should_panic] fn refname_invalid() { let _ = crate::refname!("a~b"); } #[test] fn qualified() { let _ = crate::qualified!("refs/heads/main"); let _ = crate::qualified!("refs/tags/v1.0.0"); let _ = crate::qualified!("refs/remotes/origin/main"); } #[test] #[should_panic] fn qualified_invalid() { let _ = crate::qualified!("a"); } #[test] fn component() { let _ = crate::component!("a"); } #[test] #[should_panic] fn component_invalid() { let _ = crate::component!("a/b"); } #[test] fn pattern() { let _ = crate::pattern!("refs/heads/main"); let _ = crate::pattern!("refs/tags/v1.0.0"); let _ = crate::pattern!("refs/remotes/origin/main"); let _ = crate::pattern!("a"); let _ = crate::pattern!("a/*"); let _ = crate::pattern!("*"); let _ = crate::pattern!("a/b*"); let _ = crate::pattern!("a/b*/c"); let _ = crate::pattern!("a/*/c"); } #[test] fn qualified_pattern() { let _ = crate::qualified_pattern!("refs/heads/main"); let _ = crate::qualified_pattern!("refs/tags/v1.0.0"); let _ = crate::qualified_pattern!("refs/remotes/origin/main"); let _ = crate::qualified_pattern!("refs/heads/main/*"); let _ = crate::qualified_pattern!("refs/tags/v*"); let _ = crate::qualified_pattern!("refs/remotes/origin/main"); let _ = crate::qualified_pattern!("refs/remotes/origin/department/*/person"); } #[test] #[should_panic] fn qualified_pattern_invalid() { let _ = crate::qualified_pattern!("a/*/b"); } }