typenum_uuid/lib.rs
1//! A crate to generate typesystem-level UUIDs
2//!
3//! By default, all of the macros produce `::typenum::Unsigned`
4//! types.
5//! If `typenum` is located somewhere else, you can append
6//! `| path::to::typenum` to the macro argument, and it will use
7//! that prefix instead:
8//! ```
9//! # use typenum_uuid::uuid_new_v4;
10//! use ::typenum as some_other_name;
11//! type ID = uuid_new_v4!( | some_other_name );
12//! ```
13//! This feature is most useful when exporting macros from a crate
14//! that relies on `typenum`: the UUIDs can be made to use your
15//! crate's re-export of `typenum`, in case your users have an
16//! incompatible version.
17
18use uuid::Uuid;
19use std::iter;
20
21extern crate proc_macro;
22use proc_macro::*;
23
24/// Appends an identifier to a type path
25///
26/// Roughly equivalent to the macro_rules expansion:
27/// ```ignore
28/// $prefix :: $id
29/// ```
30fn prefixed_ident(prefix: &TokenStream, id: &str)->impl Iterator<Item=TokenTree> {
31 prefix.clone().into_iter().chain(
32 vec![
33 Punct::new(':', Spacing::Joint).into(),
34 Punct::new(':', Spacing::Alone).into(),
35 Ident::new(id, Span::call_site()).into()
36 ].into_iter()
37 )
38}
39
40/// A mirror of how `typenum` describes unsigned integers:
41/// recursively with the least significant bit in the outermost
42/// type
43enum TypenumUint {
44 Lsb(Box<TypenumUint>, bool),
45 Term,
46}
47
48impl From<u128> for TypenumUint {
49 fn from(x:u128)->Self {
50 if x == 0 { return Self::Term; }
51 else { Self::Lsb( Box::new(Self::from(x >> 1)), (x & 1) != 0 ) }
52 }
53}
54
55impl TypenumUint {
56 /// Write `self` into `ts`.
57 ///
58 /// `prefix` is the location of the `typenum` crate.
59 fn write_ts(&self, prefix: &TokenStream, ts: &mut TokenStream) {
60 match self {
61 Self::Term => ts.extend(prefixed_ident(prefix, "UTerm")),
62 Self::Lsb(high, bit) => {
63 ts.extend(prefixed_ident(prefix, "UInt"));
64 ts.extend(iter::once::<TokenTree>(
65 Punct::new('<', Spacing::Alone).into()
66 ));
67 high.write_ts(prefix, ts);
68 ts.extend(iter::once::<TokenTree>(
69 Punct::new(',', Spacing::Alone).into()
70 ));
71 ts.extend(prefixed_ident(prefix, if *bit { "B1" } else { "B0" }));
72 ts.extend(iter::once::<TokenTree>(
73 Punct::new('>', Spacing::Alone).into()
74 ));
75 }
76 }
77 }
78}
79
80/// Convert a Uuid object into a TokenStream
81///
82/// The resulting stream contains a type declaration that implements
83/// `typenum::Unsigned`. If the `i128` feature of `typenum` is enabled,
84/// `<result as Unsigned>::to_u128()` will equal `uuid.as_u128()`
85///
86/// `prefix` should be the path to the `typenum` crate at the macro
87/// expansion point.
88fn uuid_to_tokenstream(uuid: Uuid, prefix: TokenStream)->TokenStream {
89 let mut result = TokenStream::new();
90 TypenumUint::from(uuid.as_u128()).write_ts(&prefix, &mut result);
91 result
92}
93
94/// Separate local from global macro arguments.
95///
96/// The macros in this crate all allow `| path::to::typenum` to be
97/// appended to the regular arguments in order to specify where to
98/// find `typenum`. This function is responsible for finding and
99/// interpreting this, and using the default value of `::typenum`
100/// if none is given.
101fn split_off_prefix(args: TokenStream) -> (TokenStream, TokenStream) {
102 let mut args = args.into_iter();
103 let local = (&mut args).take_while(
104 |tt| match tt {
105 TokenTree::Punct(ref p) if p.as_char() == '|' => false,
106 _ => true
107 }
108 ).collect();
109 let mut prefix:TokenStream = args.collect();
110 if prefix.is_empty() {
111 let x:Vec<TokenTree> = vec![
112 Punct::new(':', Spacing::Joint).into(),
113 Punct::new(':', Spacing::Alone).into(),
114 Ident::new("typenum", Span::call_site()).into()
115 ];
116 prefix = x.into_iter().collect();
117 }
118 (local, prefix)
119}
120
121/// Construct a new random UUID
122///
123/// This macro constructs a new random (v4) UUID at compile
124/// time and returns it as a `typenum::Unsigned` type.
125/// Tis can be used to enable the type system to perform
126/// a limited form of negative reasoning on type identities.
127///
128/// For example:
129/// ```
130/// #![recursion_limit = "256"]
131/// use typenum::{Unsigned,IsEqual,True,False};
132/// use typenum_uuid::uuid_new_v4;
133///
134/// trait Id { type ID: typenum::Unsigned; }
135///
136/// trait Different<B:Id> {}
137/// impl<A:Id, B:Id> Different<B> for A
138/// where A::ID: IsEqual<B::ID, Output=False> {}
139///
140/// struct T1;
141/// impl Id for T1 { type ID = uuid_new_v4!(); }
142///
143/// struct T2;
144/// impl Id for T2 { type ID = uuid_new_v4!(); }
145///
146/// fn must_be_different<A:Id, B:Id + Different<A>>(a:A, b:B) {};
147///
148/// must_be_different(T1, T2);
149/// // must_be_different(T1, T1); // Compile Error
150/// ```
151#[proc_macro]
152pub fn uuid_new_v4(args: TokenStream)->TokenStream {
153 let (args, prefix) = split_off_prefix(args);
154 assert!(args.is_empty(), "v4 UUIDs take no arguments");
155 uuid_to_tokenstream(Uuid::new_v4(), prefix)
156}
157
158#[proc_macro]
159pub fn uuid_new_v4_literal(args: TokenStream)->TokenStream {
160 let (args, _prefix) = split_off_prefix(args);
161 assert!(args.is_empty(), "v4 UUIDs take no arguments");
162 TokenTree::Literal(Literal::u128_suffixed(Uuid::new_v4().as_u128())).into()
163}
164
165/// Construct a typenum UUID
166///
167/// This macro parses its argument as a UUID
168/// and returns it as a `typenum::Unsigned` type:
169///
170/// ```edition2018
171/// # use typenum_uuid::uuid;
172/// type Id = uuid!(a65ff38d-b5b2-48d0-b03a-bdf468523d2e);
173/// ```
174#[proc_macro]
175pub fn uuid(args: TokenStream)->TokenStream {
176 let (args, prefix) = split_off_prefix(args);
177 let args:String = args.to_string()
178 .chars().filter(|c| !c.is_whitespace()).collect();
179 uuid_to_tokenstream(Uuid::parse_str(&*args).unwrap(), prefix)
180}