Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,9 @@ version = "0.2"
[dev-dependencies.wasm-bindgen-test]
version = "0.3"

[dev-dependencies.trybuild]
version = "1.0.52"

[target.'cfg(windows)'.dev-dependencies.winapi]
version = "0.3"
features = ["combaseapi"]
Expand Down
29 changes: 29 additions & 0 deletions benches/macros/invalid_parse.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use uuid::{uuid, Uuid};

const _: Uuid = uuid!("");
const _: Uuid = uuid!("!");
const _: Uuid = uuid!("F9168C5E-CEB2-4faa-B6BF-329BF39FA1E45");
const _: Uuid = uuid!("F9168C5E-CEB2-4faa-BBF-329BF39FA1E4");
const _: Uuid = uuid!("F9168C5E-CEB2-4faa-BGBF-329BF39FA1E4");
const _: Uuid = uuid!("F9168C5E-CEB2-4faa-B6BFF329BF39FA1E4");
const _: Uuid = uuid!("F9168C5E-CEB2-4faa");
const _: Uuid = uuid!("F9168C5E-CEB2-4faaXB6BFF329BF39FA1E4");
const _: Uuid = uuid!("F9168C5E-CEB-24fa-eB6BFF32-BF39FA1E4");
const _: Uuid = uuid!("01020304-1112-2122-3132-41424344");
const _: Uuid = uuid!("67e5504410b1426f9247bb680e5fe0c88");
const _: Uuid = uuid!("67e5504410b1426f9247bb680e5fe0cg8");
const _: Uuid = uuid!("67e5504410b1426%9247bb680e5fe0c8");

// Test error reporting
const _: Uuid = uuid!("67e5504410b1426f9247bb680e5fe0c");
const _: Uuid = uuid!("67e550X410b1426f9247bb680e5fe0cd");
const _: Uuid = uuid!("67e550-4105b1426f9247bb680e5fe0c");
const _: Uuid = uuid!("F9168C5E-CEB2-4faa-B6BF1-02BF39FA1E4");


const _: Uuid = uuid!("F9168C5E-CEB2-4faa-BBF-329BF39FA1E4");
const _: Uuid = uuid!("F9168C5E-CEB2-4faa-BGBF-329BF39FA1E4");
const _: Uuid = uuid!("01020304-1112-2122-3132-41424344");
const _: Uuid = uuid!("F9168C5E-CEB2-4faa-B6BFF329BF39FA1E4");

fn main() {}
125 changes: 125 additions & 0 deletions benches/macros/invalid_parse.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
error: invalid length: expected one of [36, 32], found 0
--> benches/macros/invalid_parse.rs:3:23
|
3 | const _: Uuid = uuid!("");
| ^^

error: invalid length: expected one of [36, 32], found 1
--> benches/macros/invalid_parse.rs:4:23
|
4 | const _: Uuid = uuid!("!");
| ^^^

error: invalid length: expected one of [36, 32], found 37
--> benches/macros/invalid_parse.rs:5:23
|
5 | const _: Uuid = uuid!("F9168C5E-CEB2-4faa-B6BF-329BF39FA1E45");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: invalid length: expected one of [36, 32], found 35
--> benches/macros/invalid_parse.rs:6:23
|
6 | const _: Uuid = uuid!("F9168C5E-CEB2-4faa-BBF-329BF39FA1E4");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: invalid character: expected an optional prefix of `urn:uuid:` followed by 0123456789abcdefABCDEF-, found G at 20
--> benches/macros/invalid_parse.rs:7:44
|
7 | const _: Uuid = uuid!("F9168C5E-CEB2-4faa-BGBF-329BF39FA1E4");
| ^

error: invalid number of groups: expected one of [1, 5], found 4
--> benches/macros/invalid_parse.rs:8:23
|
8 | const _: Uuid = uuid!("F9168C5E-CEB2-4faa-B6BFF329BF39FA1E4");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: invalid length: expected one of [36, 32], found 18
--> benches/macros/invalid_parse.rs:9:23
|
9 | const _: Uuid = uuid!("F9168C5E-CEB2-4faa");
| ^^^^^^^^^^^^^^^^^^^^

error: invalid character: expected an optional prefix of `urn:uuid:` followed by 0123456789abcdefABCDEF-, found X at 18
--> benches/macros/invalid_parse.rs:10:42
|
10 | const _: Uuid = uuid!("F9168C5E-CEB2-4faaXB6BFF329BF39FA1E4");
| ^

error: invalid group length: expected 4, found 3 in group 1
--> benches/macros/invalid_parse.rs:11:33
|
11 | const _: Uuid = uuid!("F9168C5E-CEB-24fa-eB6BFF32-BF39FA1E4");
| ^^^

error: invalid group length: expected 12, found 8 in group 4
--> benches/macros/invalid_parse.rs:12:48
|
12 | const _: Uuid = uuid!("01020304-1112-2122-3132-41424344");
| ^^^^^^^^

error: invalid length: expected one of [36, 32], found 33
--> benches/macros/invalid_parse.rs:13:23
|
13 | const _: Uuid = uuid!("67e5504410b1426f9247bb680e5fe0c88");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: invalid length: expected one of [36, 32], found 33
--> benches/macros/invalid_parse.rs:14:23
|
14 | const _: Uuid = uuid!("67e5504410b1426f9247bb680e5fe0cg8");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: invalid character: expected an optional prefix of `urn:uuid:` followed by 0123456789abcdefABCDEF-, found % at 15
--> benches/macros/invalid_parse.rs:15:39
|
15 | const _: Uuid = uuid!("67e5504410b1426%9247bb680e5fe0c8");
| ^

error: invalid length: expected one of [36, 32], found 31
--> benches/macros/invalid_parse.rs:18:23
|
18 | const _: Uuid = uuid!("67e5504410b1426f9247bb680e5fe0c");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: invalid character: expected an optional prefix of `urn:uuid:` followed by 0123456789abcdefABCDEF-, found X at 6
--> benches/macros/invalid_parse.rs:19:30
|
19 | const _: Uuid = uuid!("67e550X410b1426f9247bb680e5fe0cd");
| ^

error: invalid group length: expected 8, found 6 in group 0
--> benches/macros/invalid_parse.rs:20:24
|
20 | const _: Uuid = uuid!("67e550-4105b1426f9247bb680e5fe0c");
| ^^^^^^

error: invalid group length: expected 4, found 5 in group 3
--> benches/macros/invalid_parse.rs:21:43
|
21 | const _: Uuid = uuid!("F9168C5E-CEB2-4faa-B6BF1-02BF39FA1E4");
| ^^^^^

error: invalid length: expected one of [36, 32], found 35
--> benches/macros/invalid_parse.rs:24:23
|
24 | const _: Uuid = uuid!("F9168C5E-CEB2-4faa-BBF-329BF39FA1E4");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: invalid character: expected an optional prefix of `urn:uuid:` followed by 0123456789abcdefABCDEF-, found G at 20
--> benches/macros/invalid_parse.rs:25:44
|
25 | const _: Uuid = uuid!("F9168C5E-CEB2-4faa-BGBF-329BF39FA1E4");
| ^

error: invalid group length: expected 12, found 8 in group 4
--> benches/macros/invalid_parse.rs:26:48
|
26 | const _: Uuid = uuid!("01020304-1112-2122-3132-41424344");
| ^^^^^^^^

error: invalid number of groups: expected one of [1, 5], found 4
--> benches/macros/invalid_parse.rs:27:23
|
27 | const _: Uuid = uuid!("F9168C5E-CEB2-4faa-B6BFF329BF39FA1E4");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
11 changes: 11 additions & 0 deletions benches/macros/renamed.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
use ::uuid::{uuid as id, Uuid as Id};

mod uuid {
struct MyType;
}

struct Uuid;

const _: Id = id!("67e55044-10b1-426f-9247-bb680e5fe0c8");

fn main() {}
20 changes: 20 additions & 0 deletions benches/macros/valid_parse.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use uuid::{uuid, Uuid};

const _: Uuid = uuid!("00000000000000000000000000000000");
const _: Uuid = uuid!("67e55044-10b1-426f-9247-bb680e5fe0c8");
const _: Uuid = uuid!("67e55044-10b1-426f-9247-bb680e5fe0c8");
const _: Uuid = uuid!("F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4");
const _: Uuid = uuid!("67e5504410b1426f9247bb680e5fe0c8");
const _: Uuid = uuid!("01020304-1112-2122-3132-414243444546");
const _: Uuid = uuid!("urn:uuid:67e55044-10b1-426f-9247-bb680e5fe0c8");

// Nil
const _: Uuid = uuid!("00000000000000000000000000000000");
const _: Uuid = uuid!("00000000-0000-0000-0000-000000000000");

// valid hyphenated
const _: Uuid = uuid!("67e55044-10b1-426f-9247-bb680e5fe0c8");
// valid short
const _: Uuid = uuid!("67e5504410b1426f9247bb680e5fe0c8");

fn main() {}
9 changes: 9 additions & 0 deletions benches/parse_str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,12 @@ fn parse_invalid_group_len(b: &mut Bencher) {
fn parse_invalid_groups(b: &mut Bencher) {
b.iter(|| Uuid::parse_str("F9168C5E-CEB2-4faa-B6BFF329BF39FA1E4"));
}

#[cfg(feature = "macros")]
#[test]
fn test_valid_macro() {
let t = trybuild::TestCases::new();
t.pass("benches/macros/valid_parse.rs");
t.pass("benches/macros/renamed.rs");
t.compile_fail("benches/macros/invalid_parse.rs");
}
3 changes: 3 additions & 0 deletions macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@ edition = "2018"
proc-macro = true

[dependencies]
syn = "1.0.80"
quote = "1.0.10"
proc-macro2 = "1.0.29"
80 changes: 72 additions & 8 deletions macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,81 @@
use std;
use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use quote::{quote, quote_spanned};
use std::fmt;
use syn::spanned::Spanned;

#[cfg(any(feature = "std", test))]
#[macro_use]
extern crate std;

#[cfg(all(not(feature = "std"), not(test)))]
#[macro_use]
extern crate core as std;
Comment on lines +7 to +13
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Required to stop shared/error.rs and shared/parser.rs from complaining. I just copy-pasted it from uuid.


#[path = "../../shared/error.rs"]
#[allow(dead_code)]
mod error;

#[path = "../../shared/parser.rs"]
#[allow(dead_code)]
mod parser;

#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
#[proc_macro]
pub fn parse_lit(input: TokenStream) -> TokenStream {
build_uuid(input.clone()).unwrap_or_else(|e| {
let msg = e.to_string();
let ts = TokenStream2::from(input);
let span = match e {
Error::UuidParse(error::Error(
error::ErrorKind::InvalidCharacter { index, .. },
)) => {
let mut s = proc_macro2::Literal::string("");
s.set_span(ts.span());
s.subspan(index + 1..=index + 1).unwrap()
}
Error::UuidParse(error::Error(
error::ErrorKind::InvalidGroupLength { found, group, .. },
)) => {
let start =
parser::GROUP_LENS.iter().take(group).sum::<usize>()
+ group
+ 1;
let mut s = proc_macro2::Literal::string("");
s.set_span(ts.span());
s.subspan(start..start + found).unwrap()
}
_ => ts.span(),
};
TokenStream::from(quote_spanned! {span=>
compile_error!(#msg)
})
})
}

enum Error {
NonStringLiteral,
UuidParse(error::Error),
}

impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Error::NonStringLiteral => f.write_str("expected string literal"),
Error::UuidParse(ref e) => write!(f, "{}", e),
}
}
}

fn build_uuid(input: TokenStream) -> Result<TokenStream, Error> {
let string = match syn::parse::<syn::Lit>(input) {
Ok(syn::Lit::Str(literal)) => literal.value(),
_ => return Err(Error::NonStringLiteral),
};

let bytes = parser::parse_str(&string).map_err(Error::UuidParse)?;

let tokens = bytes
.iter()
.map(|byte| quote! { #byte, })
.collect::<TokenStream2>();

Ok(quote! {[#tokens]}.into())
}
2 changes: 1 addition & 1 deletion shared/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ fn len_matches_any(len: usize, crits: &[usize]) -> bool {
const ACC_GROUP_LENS: [usize; 5] = [8, 12, 16, 20, 32];

// Length of each hyphenated group in hex digits.
const GROUP_LENS: [usize; 5] = [8, 4, 4, 4, 12];
pub(super) const GROUP_LENS: [usize; 5] = [8, 4, 4, 4, 12];

pub fn parse_str(mut input: &str) -> Result<[u8; 16], Error> {
// Ensure length is valid for any of the supported formats
Expand Down
6 changes: 4 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,8 +207,10 @@ mod v5;
mod winapi_support;

#[cfg(feature = "macros")]
#[doc(inline)]
pub use uuid_macros::*;
#[macro_use]
mod macros;
#[cfg(feature = "macros")]
pub extern crate uuid_macros;

use crate::std::convert;

Expand Down
53 changes: 53 additions & 0 deletions src/macros.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/// Parse [`Uuid`][uuid::Uuid]s from string literals at compile time.
/// ## Usage
/// This macro transforms the string literal representation of a
/// [`Uuid`][uuid::Uuid] into the bytes representation, raising a compilation
/// error if it cannot properly be parsed.
///
/// ## Examples
/// Setting a global constant:
/// ```
/// # use uuid::{uuid, Uuid};
/// pub const SCHEMA_ATTR_CLASS: Uuid = uuid!("00000000-0000-0000-0000-ffff00000000");
/// pub const SCHEMA_ATTR_UUID: Uuid = uuid!("00000000-0000-0000-0000-ffff00000001");
/// pub const SCHEMA_ATTR_NAME: Uuid = uuid!("00000000-0000-0000-0000-ffff00000002");
/// ```
/// Defining a local variable:
/// ```
/// # use uuid::{uuid, Uuid};
/// let uuid: Uuid = uuid!("urn:uuid:F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4");
/// ```
/// ## Compilation Failures
/// Invalid UUIDs are rejected:
/// ```ignore
/// # use uuid::{uuid, Uuid};
/// let uuid: Uuid = uuid!("F9168C5E-ZEB2-4FAA-B6BF-329BF39FA1E4");
/// ```
/// Provides the following compilation error:
/// ```txt
/// error: invalid character: expected an optional prefix of `urn:uuid:` followed by 0123456789abcdefABCDEF-, found Z at 9
/// |
/// | let id: Uuid = uuid!("F9168C5E-ZEB2-4FAA-B6BF-329BF39FA1E4");
/// | ^
/// ```
/// Tokens that aren't string literals are also rejected:
/// ```ignore
/// # use uuid::{uuid, Uuid};
/// let uuid_str: &str = "550e8400e29b41d4a716446655440000";
/// let uuid: Uuid = uuid!(uuid_str);
/// ```
/// Provides the following compilation error:
/// ```txt
/// error: expected string literal
/// |
/// | let uuid: Uuid = uuid!(uuid_str);
/// | ^^^^^^^^
/// ```
///
/// [uuid::Uuid]: https://docs.rs/uuid/*/uuid/struct.Uuid.html
#[macro_export]
macro_rules! uuid {
($uuid:tt) => {{
$crate::Uuid::from_bytes($crate::uuid_macros::parse_lit!($uuid))
}};
}