From c8b1aad189d9a34f537bba24e155977ba5756b97 Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Fri, 24 Jul 2020 22:22:25 +0200 Subject: [PATCH] Add macros to create identifiers from string slices --- ruma-identifiers-macros/Cargo.toml | 19 ++++ ruma-identifiers-macros/LICENSE | 19 ++++ ruma-identifiers-macros/src/lib.rs | 117 ++++++++++++++++++++++ ruma/Cargo.toml | 4 + ruma/src/lib.rs | 1 + ruma/tests/id-macros.rs | 6 ++ ruma/tests/ui/01-valid-id-macros.rs | 12 +++ ruma/tests/ui/02-invalid-id-macros.rs | 11 ++ ruma/tests/ui/02-invalid-id-macros.stderr | 71 +++++++++++++ 9 files changed, 260 insertions(+) create mode 100644 ruma-identifiers-macros/Cargo.toml create mode 100644 ruma-identifiers-macros/LICENSE create mode 100644 ruma-identifiers-macros/src/lib.rs create mode 100644 ruma/tests/id-macros.rs create mode 100644 ruma/tests/ui/01-valid-id-macros.rs create mode 100644 ruma/tests/ui/02-invalid-id-macros.rs create mode 100644 ruma/tests/ui/02-invalid-id-macros.stderr diff --git a/ruma-identifiers-macros/Cargo.toml b/ruma-identifiers-macros/Cargo.toml new file mode 100644 index 00000000..cdab8472 --- /dev/null +++ b/ruma-identifiers-macros/Cargo.toml @@ -0,0 +1,19 @@ +[package] +authors = ["Jonas Platte "] +description = "Procedural macros for creating Matrix identifiers." +homepage = "https://www.ruma.io/" +keywords = ["matrix", "chat", "messaging", "ruma"] +license = "MIT" +name = "ruma-identifiers-macros" +repository = "https://github.com/ruma/ruma" +version = "0.17.0" +edition = "2018" + +[dependencies] +ruma-identifiers = "=0.17.0" +quote = "1.0.7" +proc-macro2 = "1.0.19" +syn = "1.0.35" + +[lib] +proc-macro = true diff --git a/ruma-identifiers-macros/LICENSE b/ruma-identifiers-macros/LICENSE new file mode 100644 index 00000000..f0d48ccb --- /dev/null +++ b/ruma-identifiers-macros/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2020 Jonas Platte + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/ruma-identifiers-macros/src/lib.rs b/ruma-identifiers-macros/src/lib.rs new file mode 100644 index 00000000..244cf104 --- /dev/null +++ b/ruma-identifiers-macros/src/lib.rs @@ -0,0 +1,117 @@ +use proc_macro::TokenStream; +use std::convert::TryFrom; + +use quote::quote; +use ruma_identifiers::{ + DeviceKeyId, EventId, RoomAliasId, RoomId, RoomVersionId, ServerKeyId, ServerName, UserId, +}; +use syn::{parse_macro_input, LitStr}; + +#[proc_macro] +pub fn device_id(input: TokenStream) -> TokenStream { + let id = parse_macro_input!(input as LitStr); + let output = quote! { + ::std::boxed::Box<::ruma::identifiers::DeviceId>::from(#id) + }; + + output.into() +} + +// w/o macro: UserId::try_from("@user:example.org").unwrap() +// w/ macro: user_id!("@user:example.org") + +#[proc_macro] +pub fn device_key_id(input: TokenStream) -> TokenStream { + let id = parse_macro_input!(input as LitStr); + assert!(DeviceKeyId::try_from(id.value()).is_ok(), "Invalid device key id"); + + let output = quote! { + <::ruma::identifiers::DeviceKeyId as ::std::convert::TryFrom<&str>>::try_from(#id).unwrap() + }; + + output.into() +} + +#[proc_macro] +pub fn event_id(input: TokenStream) -> TokenStream { + let id = parse_macro_input!(input as LitStr); + assert!(EventId::try_from(id.value()).is_ok(), "Invalid event id"); + + let output = quote! { + <::ruma::identifiers::EventId as ::std::convert::TryFrom<&str>>::try_from(#id).unwrap() + }; + + output.into() +} + +#[proc_macro] +pub fn room_alias_id(input: TokenStream) -> TokenStream { + let id = parse_macro_input!(input as LitStr); + assert!(RoomAliasId::try_from(id.value()).is_ok(), "Invalid room_alias_id"); + + let output = quote! { + <::ruma::identifiers::RoomAliasId as ::std::convert::TryFrom<&str>>::try_from(#id).unwrap() + }; + + output.into() +} + +#[proc_macro] +pub fn room_id(input: TokenStream) -> TokenStream { + let id = parse_macro_input!(input as LitStr); + assert!(RoomId::try_from(id.value()).is_ok(), "Invalid room_id"); + + let output = quote! { + <::ruma::identifiers::RoomId as ::std::convert::TryFrom<&str>>::try_from(#id).unwrap() + }; + + output.into() +} + +#[proc_macro] +pub fn room_version_id(input: TokenStream) -> TokenStream { + let id = parse_macro_input!(input as LitStr); + assert!(RoomVersionId::try_from(id.value()).is_ok(), "Invalid room_version_id"); + + let output = quote! { + <::ruma::identifiers::RoomVersionId as ::std::convert::TryFrom<&str>>::try_from(#id).unwrap() + }; + + output.into() +} + +#[proc_macro] +pub fn server_key_id(input: TokenStream) -> TokenStream { + let id = parse_macro_input!(input as LitStr); + assert!(ServerKeyId::try_from(id.value()).is_ok(), "Invalid server_key_id"); + + let output = quote! { + <::ruma::identifiers::ServerKeyId as ::std::convert::TryFrom<&str>>::try_from(#id).unwrap() + }; + + output.into() +} + +#[proc_macro] +pub fn server_name(input: TokenStream) -> TokenStream { + let id = parse_macro_input!(input as LitStr); + assert!(<&ServerName>::try_from(id.value().as_str()).is_ok(), "Invalid server_name"); + + let output = quote! { + <::std::boxed::Box::<::ruma::identifiers::ServerName> as ::std::convert::TryFrom<&str>>::try_from(#id).unwrap() + }; + + output.into() +} + +#[proc_macro] +pub fn user_id(input: TokenStream) -> TokenStream { + let id = parse_macro_input!(input as LitStr); + assert!(UserId::try_from(id.value()).is_ok(), "Invalid user_id"); + + let output = quote! { + <::ruma::identifiers::UserId as ::std::convert::TryFrom<&str>>::try_from(#id).unwrap() + }; + + output.into() +} diff --git a/ruma/Cargo.toml b/ruma/Cargo.toml index d2872f77..a2de538b 100644 --- a/ruma/Cargo.toml +++ b/ruma/Cargo.toml @@ -25,6 +25,7 @@ federation-api = ["ruma-api", "ruma-federation-api", "ruma-signatures"] [dependencies] ruma-common = { version = "0.2.0", path = "../ruma-common" } ruma-identifiers = { version = "0.17.0", path = "../ruma-identifiers", features = ["serde"] } +ruma-identifiers-macros = { version = "0.17.0", path = "../ruma-identifiers-macros" } ruma-events = { version = "0.21.3", path = "../ruma-events", optional = true } ruma-signatures = { version = "0.6.0-dev.1", path = "../ruma-signatures", optional = true } @@ -33,3 +34,6 @@ ruma-api = { version = "0.16.1", path = "../ruma-api", optional = true } ruma-appservice-api = { version = "0.1.0", path = "../ruma-appservice-api", optional = true } ruma-client-api = { version = "0.9.0", path = "../ruma-client-api", optional = true } ruma-federation-api = { version = "0.0.2", path = "../ruma-federation-api", optional = true } + +[dev-dependencies] +trybuild = "1.0.30" diff --git a/ruma/src/lib.rs b/ruma/src/lib.rs index 0e474086..da36aab9 100644 --- a/ruma/src/lib.rs +++ b/ruma/src/lib.rs @@ -16,6 +16,7 @@ pub use ruma_common::*; #[doc(inline)] pub use ruma_identifiers as identifiers; +pub use ruma_identifiers_macros::*; #[cfg(feature = "ruma-events")] #[doc(inline)] diff --git a/ruma/tests/id-macros.rs b/ruma/tests/id-macros.rs new file mode 100644 index 00000000..a7da5793 --- /dev/null +++ b/ruma/tests/id-macros.rs @@ -0,0 +1,6 @@ +#[test] +fn ui() { + let t = trybuild::TestCases::new(); + t.pass("tests/ui/01-valid-id-macros.rs"); + t.compile_fail("tests/ui/02-invalid-id-macros.rs"); +} diff --git a/ruma/tests/ui/01-valid-id-macros.rs b/ruma/tests/ui/01-valid-id-macros.rs new file mode 100644 index 00000000..2e63aa5b --- /dev/null +++ b/ruma/tests/ui/01-valid-id-macros.rs @@ -0,0 +1,12 @@ +fn main() { + let _ = ruma::device_key_id!("ed25519:JLAFKJWSCS"); + let _ = ruma::event_id!("$39hvsi03hlne:example.com"); + let _ = ruma::event_id!("$acR1l0raoZnm60CBwAVgqbZqoO/mYU81xysh1u7XcJk"); + let _ = ruma::room_alias_id!("#alias:server.tld"); + let _ = ruma::room_id!("!1234567890:matrix.org"); + let _ = ruma::room_version_id!("1"); + let _ = ruma::room_version_id!("1-custom"); + let _ = ruma::server_key_id!("ed25519:Abc_1"); + let _ = ruma::server_name!("myserver.fish"); + let _ = ruma::user_id!("@user:ruma.io"); +} diff --git a/ruma/tests/ui/02-invalid-id-macros.rs b/ruma/tests/ui/02-invalid-id-macros.rs new file mode 100644 index 00000000..5a81b2f7 --- /dev/null +++ b/ruma/tests/ui/02-invalid-id-macros.rs @@ -0,0 +1,11 @@ +fn main() { + let _ = ruma::device_key_id!("ed2519:JLAFKJWSCS"); + let _ = ruma::event_id!("39hvsi03hlne:example.com"); + let _ = ruma::event_id!("acR1l0raoZnm60CBwAVgqbZqoO/mYU81xysh1u7XcJk"); + let _ = ruma::room_alias_id!("alias:server.tld"); + let _ = ruma::room_id!("1234567890:matrix.org"); + let _ = ruma::room_version_id!(""); + let _ = ruma::server_key_id!("ed219:Abc_1"); + let _ = ruma::server_name!(""); + let _ = ruma::user_id!("user:ruma.io"); +} diff --git a/ruma/tests/ui/02-invalid-id-macros.stderr b/ruma/tests/ui/02-invalid-id-macros.stderr new file mode 100644 index 00000000..0ba14b28 --- /dev/null +++ b/ruma/tests/ui/02-invalid-id-macros.stderr @@ -0,0 +1,71 @@ +error: proc macro panicked + --> $DIR/02-invalid-id-macros.rs:2:13 + | +2 | let _ = ruma::device_key_id!("ed2519:JLAFKJWSCS"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: message: Invalid device key id + +error: proc macro panicked + --> $DIR/02-invalid-id-macros.rs:3:13 + | +3 | let _ = ruma::event_id!("39hvsi03hlne:example.com"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: message: Invalid event id + +error: proc macro panicked + --> $DIR/02-invalid-id-macros.rs:4:13 + | +4 | let _ = ruma::event_id!("acR1l0raoZnm60CBwAVgqbZqoO/mYU81xysh1u7XcJk"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: message: Invalid event id + +error: proc macro panicked + --> $DIR/02-invalid-id-macros.rs:5:13 + | +5 | let _ = ruma::room_alias_id!("alias:server.tld"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: message: Invalid room_alias_id + +error: proc macro panicked + --> $DIR/02-invalid-id-macros.rs:6:13 + | +6 | let _ = ruma::room_id!("1234567890:matrix.org"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: message: Invalid room_id + +error: proc macro panicked + --> $DIR/02-invalid-id-macros.rs:7:13 + | +7 | let _ = ruma::room_version_id!(""); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: message: Invalid room_version_id + +error: proc macro panicked + --> $DIR/02-invalid-id-macros.rs:8:13 + | +8 | let _ = ruma::server_key_id!("ed219:Abc_1"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: message: Invalid server_key_id + +error: proc macro panicked + --> $DIR/02-invalid-id-macros.rs:9:13 + | +9 | let _ = ruma::server_name!(""); + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: message: Invalid server_name + +error: proc macro panicked + --> $DIR/02-invalid-id-macros.rs:10:13 + | +10 | let _ = ruma::user_id!("user:ruma.io"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: message: Invalid user_id