Radish alpha
h
Radicle Heartwood Protocol & Stack
Radicle
Git (anonymous pull)
Log in to clone via SSH
git-ref-format: New crate
Lorenz Leutgeb committed 6 months ago
commit 3fcf2a86da18fc958d79fd8f435e7c7edcbd2c58
parent 292befdb36b97e48c5112dbdd6565dd6316969ed
3 files changed +323 -0
modified Cargo.lock
@@ -2915,6 +2915,13 @@ dependencies = [
]

[[package]]
+
name = "radicle-git-ref-format"
+
version = "0.1.0"
+
dependencies = [
+
 "git-ref-format-core",
+
]
+

+
[[package]]
name = "radicle-node"
version = "0.16.0"
dependencies = [
added crates/radicle-git-ref-format/Cargo.toml
@@ -0,0 +1,18 @@
+
[package]
+
name = "radicle-git-ref-format"
+
description = "Radicle re-exports and macros for `git-ref-format-core`"
+
homepage.workspace = true
+
repository.workspace = true
+
version = "0.1.0"
+
edition.workspace = true
+
license.workspace = true
+
keywords = ["radicle", "git", "refname", "ref", "references"]
+
rust-version.workspace = true
+

+
[features]
+
macro = []
+
bstr = ["git-ref-format-core/bstr"]
+
serde = ["git-ref-format-core/serde"]
+

+
[dependencies]
+
git-ref-format-core.workspace = true
added crates/radicle-git-ref-format/src/lib.rs
@@ -0,0 +1,298 @@
+
#![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");
+
    }
+
}