tagged_bytes 0.0.1

proc-macro for tagging your bytes
Documentation
extern crate proc_macro;
use proc_macro::{TokenStream, TokenTree};

/// Makes a "tagged bytes" struct with the given name you input.
///
/// * The struct is `repr(transparent)` over `[u8]`, making it one of those
///   nasty *dynamically sized* types.
/// * The inner field is `pub(crate)`. You can keep it private to your crate, or
///   if you want users to keep using the bytes as raw bytes you can impl
///   [Deref](core::ops::Deref) or make a method maybe.
/// * The macro creates some `pub(crate)` constructors with funny names:
///   `pub_crate_from_ref` and `pub_crate_from_mut`. The "secret" here is that
///   they must use [transmute](core::mem::transmute) to do their thing, but
///   since the code that does it is proc-macro generated then it won't fall
///   afoul of `forbid(unsafe_code)`. The strange names are selected so that
///   when necessary you can make `pub` methods using the common, or you can
///   make `try_` named methods that enforce any invariants of the type.
///   * If the `alloc` feature of this proc-macro crate is enabled, the output
///     will have an additional method named `pub_crate_from_box` which is keyed
///     to an `alloc` feature in your own crate, and which will require that
///     your crate have `extern crate alloc;` somewhere in the crate when its
///     `alloc` feature is enabled.
/// * The macro will also derive `PartialEq`, `Eq`, `PartialOrd`, `Ord`, and
///   `Hash`. The first two so that you get the `StructuralEq` impl, which a
///   type can only have when `PartialEq` and `Eq` are derived. Having
///   `StructuralEq` allows you to potentially use the type in a `match`
///   expression. The others are provided just in case you need them, and so
///   that the ordering and hash impls are sure to match the equality impls.
/// * Note that `Debug` is **not** derived. You'll have to decide how you want
///   to present the type for yourself.
#[proc_macro]
pub fn tagged_bytes(tokens: TokenStream) -> TokenStream {
  let trees: Vec<TokenTree> = tokens.into_iter().collect();
  assert_eq!(trees.len(), 1, "Only one identifier please.");
  let ident = match &trees[0] {
    TokenTree::Ident(ident) => ident,
    _ => panic!("You must provide an identifier."),
  };
  let mut output = format!(
    "#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
    #[repr(transparent)]
    pub struct {ident}(pub(crate) [u8]);
    impl {ident} {{
      pub(crate) const fn pub_crate_from_ref(r: &[u8]) -> &Self {{
        unsafe {{ core::mem::transmute(r) }}
      }}
      pub(crate) fn pub_crate_from_mut(r: &mut [u8]) -> &mut Self {{
        unsafe {{ core::mem::transmute(r) }}
      }}
    }}",
    ident = ident
  );
  if cfg!(feature = "alloc") {
    output.push_str(&format!(
      "#[cfg(feature = \"alloc\")]
      impl {ident} {{
        pub(crate) fn pub_crate_from_box(b: alloc::boxed::Box<[u8]>) -> Box<Self> {{
          unsafe {{ core::mem::transmute(b) }}
        }}
      }}
      ",
      ident = ident
    ));
  }

  output.parse().unwrap()
}