#![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/<category>/<name>'"
))
}
#[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");
}
}