This commit is contained in:
nora 2022-06-17 22:35:48 +02:00
parent dd85598b5c
commit 8d11f678c1
4 changed files with 133 additions and 7 deletions

View file

@ -9,5 +9,6 @@ proc-macro = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
fslock = "0.2.1"
quote = "1.0.18"
syn = "1.0.96"

View file

@ -1,21 +1,74 @@
use std::{env, fs, fs::OpenOptions, io::Write, path::PathBuf};
use fslock::LockFile;
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput};
const DEFAULT_LOCK_FILE_NAME: &str =
"accursed-unutterable-type-id-global-store-oh-god-is-this-cursed-dont-touch-it-LOCK";
const DEFAULT_FILE_NAME: &str =
"accursed-unutterable-type-id-global-store-oh-god-is-this-cursed-dont-touch-it";
#[proc_macro_derive(AccursedUnutterablyTypeIdentified)]
pub fn my_macro(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let mut logfile = OpenOptions::new()
.append(true)
.create(true)
.open("logfile.log")
.unwrap();
let dir_path = match env::var("ACCURSED_UNUTTERABLE_TYPE_ID_DIR") {
Ok(string) => PathBuf::from(string),
Err(_) => {
let manifest_dir = env::var("CARGO_MANIFEST_DIR")
.unwrap_or_else(|_| fail("`CARGO_MANIFEST_DIR` environment variable not found"));
let manifest_dir = PathBuf::from(manifest_dir);
manifest_dir.join("target")
}
};
let lock_path = dir_path.join(DEFAULT_LOCK_FILE_NAME);
let file_path = dir_path.join(DEFAULT_FILE_NAME);
let mut file = LockFile::open(&dir_path.join(lock_path))
.unwrap_or_else(|_| fail("failed to open global lock file"));
file.lock().unwrap_or_else(|_| fail("failed to lock file"));
let old = fs::read_to_string(&file_path).unwrap_or_else(|_| "0".to_string()); // lmao
writeln!(logfile, "read only value from file: {old}").ok();
let old: u64 = old.trim().parse().unwrap_or(0); // highly dangerous indeed
writeln!(logfile, "using {old} as the old value").ok();
let new_value = old
.checked_add(1)
.unwrap_or_else(|| fail("integer overflow. you have too many derives"));
fs::write(&file_path, new_value.to_string())
.unwrap_or_else(|_| fail("failed to write new number"));
writeln!(logfile, "wrote new number to file {new_value}").ok();
let _ = file.unlock();
let name = input.ident;
let generics1 = input.generics.params.iter();
let generics2 = input.generics.params.iter();
// SAFETY: We literally are the proc macro. and we have made sure that no duplicate type ids
// will ever happen, right? :ferrisClueless:
let expanded = quote! {
unsafe impl ::accursed_unutterable_type_id::AccursedUnutterablyTypeIdentified for #name {
unsafe impl<#(#generics1),*> ::accursed_unutterable_type_id::AccursedUnutterablyTypeIdentified for #name<#(#generics2),*> {
fn type_id() -> ::accursed_unutterable_type_id::AccursedUnutterableTypeId {
::accursed_unutterable_type_id::AccursedUnutterableTypeId(
::accursed_unutterable_type_id::InternalAccursedUnutterableTypeId::new(
0
::accursed_unutterable_type_id::AccursedUnutterableTypeId::__internal_new(
::accursed_unutterable_type_id::InternalAccursedUnutterableTypeId::__internal_new(
#new_value
)
)
}
@ -25,3 +78,11 @@ pub fn my_macro(input: TokenStream) -> TokenStream {
// Hand the output tokens back to the compiler
TokenStream::from(expanded)
}
fn fail(msg: impl Into<String>) -> ! {
let msg = msg.into();
panic!("Failed to run accursed-unutterable-type-id proc macro with error '{msg}'. \
Set the `ACCURSED_UNUTTERABLE_TYPE_ID_DIR` environment variable to a file path of your choice to fix this issue. \
cargo clean could help as well.");
}

View file

@ -6,9 +6,19 @@ struct Uwu;
#[derive(AccursedUnutterablyTypeIdentified)]
struct Owo;
#[derive(AccursedUnutterablyTypeIdentified)]
struct Hi;
#[derive(AccursedUnutterablyTypeIdentified)]
struct OhLord;
fn main() {
let uwu_id = AccursedUnutterableTypeId::of::<Uwu>();
let owo_id = AccursedUnutterableTypeId::of::<Owo>();
let hi_id = AccursedUnutterableTypeId::of::<Hi>();
let oh_lord_id = AccursedUnutterableTypeId::of::<OhLord>();
assert_ne!(uwu_id, owo_id);
assert_ne!(owo_id, hi_id);
assert_ne!(hi_id, oh_lord_id);
}

View file

@ -1,3 +1,8 @@
//! An accursed, unutterable type id.
//!
//! Once upon a time, back when time may not have been a human concept but only a vague idea among the
//! wise, there was [`std::any::TypeId`]
use std::fmt::{Debug, Formatter};
pub use accursed_unutterable_type_id_derive::AccursedUnutterablyTypeIdentified;
@ -7,11 +12,31 @@ pub use accursed_unutterable_type_id_derive::AccursedUnutterablyTypeIdentified;
/// # Safety
/// This trait is only allowed to be implemented by the derive macro.
pub unsafe trait AccursedUnutterablyTypeIdentified: 'static {
/// Returns the accursed unutterable type id for the type.
///
/// It's suggested to use [`AccursedUnutterableTypeId::of`] instead.
fn type_id() -> AccursedUnutterableTypeId;
}
/// A unique type id for a type. A fancier (less fancy) [`std::any::TypeId]`] without any
/// internal compiler magic!
///
/// It can easily be derived for your type. The derive is the only way to implement this trait.
/// ```
/// use accursed_unutterable_type_id::{AccursedUnutterableTypeId, AccursedUnutterablyTypeIdentified};
///
/// #[derive(AccursedUnutterablyTypeIdentified)]
/// struct Uwu;
/// ```
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct AccursedUnutterableTypeId(#[doc(hidden)] pub InternalAccursedUnutterableTypeId);
pub struct AccursedUnutterableTypeId(#[doc(hidden)] InternalAccursedUnutterableTypeId);
impl AccursedUnutterableTypeId {
#[doc(hidden)]
pub fn __internal_new(inner: InternalAccursedUnutterableTypeId) -> Self {
Self(inner)
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[doc(hidden)]
@ -22,11 +47,11 @@ pub struct InternalAccursedUnutterableTypeId(u128, u64);
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[doc(hidden)]
#[cfg(not(debug_assertions))]
pub struct InternalAccursedUnutterableTypeId(pub u64);
pub struct InternalAccursedUnutterableTypeId(u64);
#[cfg(debug_assertions)]
impl InternalAccursedUnutterableTypeId {
pub fn new(n: u64) -> Self {
pub fn __internal_new(n: u64) -> Self {
Self(0, n)
}
fn inner(self) -> u64 {
@ -51,6 +76,22 @@ impl Debug for InternalAccursedUnutterableTypeId {
}
impl AccursedUnutterableTypeId {
/// Returns the accursed unutterable type id for the type.
///
/// ```
/// use accursed_unutterable_type_id::{AccursedUnutterableTypeId, AccursedUnutterablyTypeIdentified};
///
/// #[derive(AccursedUnutterablyTypeIdentified)]
/// struct Uwu;
///
/// #[derive(AccursedUnutterablyTypeIdentified)]
/// struct Owo;
///
/// let uwu_id = AccursedUnutterableTypeId::of::<Uwu>();
/// let owo_id = AccursedUnutterableTypeId::of::<Owo>();
///
/// assert_ne!(uwu_id, owo_id);
/// ```
pub fn of<T>() -> Self
where
T: AccursedUnutterablyTypeIdentified,
@ -58,3 +99,16 @@ impl AccursedUnutterableTypeId {
T::type_id()
}
}
mod __doctest {
/// ```
/// use accursed_unutterable_type_id::AccursedUnutterablyTypeIdentified;
///
/// #[derive(AccursedUnutterablyTypeIdentified)]
/// struct Uwu<'a, T, const N: usize> {
/// _x: &'a [T; N],
/// }
/// ```
mod complex {}
}