From 1e900ab58cf9bdf50e5d48213bd9601e9364c67a Mon Sep 17 00:00:00 2001 From: Jonathan de Jong Date: Tue, 8 Feb 2022 14:29:21 +0100 Subject: [PATCH] api: Add `stable_path`, `r0_path` and `unstable_path` metadata fields --- crates/ruma-api-macros/src/api.rs | 6 ++ crates/ruma-api-macros/src/api/metadata.rs | 97 ++++++++++++++++--- crates/ruma-api/src/lib.rs | 12 ++- crates/ruma-api/tests/manual_endpoint_impl.rs | 3 + .../ruma-api/tests/ui/01-api-sanity-check.rs | 7 ++ 5 files changed, 109 insertions(+), 16 deletions(-) diff --git a/crates/ruma-api-macros/src/api.rs b/crates/ruma-api-macros/src/api.rs index a27e8caf..dbabf9f1 100644 --- a/crates/ruma-api-macros/src/api.rs +++ b/crates/ruma-api-macros/src/api.rs @@ -48,6 +48,9 @@ impl Api { let method = &metadata.method; let name = &metadata.name; let path = &metadata.path; + let unstable_path = util::map_option_literal(&metadata.unstable_path); + let r0_path = util::map_option_literal(&metadata.r0_path); + let stable_path = util::map_option_literal(&metadata.stable_path); let rate_limited: TokenStream = metadata .rate_limited .iter() @@ -92,6 +95,9 @@ impl Api { method: #http::Method::#method, name: #name, path: #path, + unstable_path: #unstable_path, + r0_path: #r0_path, + stable_path: #stable_path, added: #added, deprecated: #deprecated, removed: #removed, diff --git a/crates/ruma-api-macros/src/api/metadata.rs b/crates/ruma-api-macros/src/api/metadata.rs index 443252cd..b80179e8 100644 --- a/crates/ruma-api-macros/src/api/metadata.rs +++ b/crates/ruma-api-macros/src/api/metadata.rs @@ -15,6 +15,9 @@ mod kw { syn::custom_keyword!(method); syn::custom_keyword!(name); syn::custom_keyword!(path); + syn::custom_keyword!(unstable); + syn::custom_keyword!(r0); + syn::custom_keyword!(stable); syn::custom_keyword!(rate_limited); syn::custom_keyword!(authentication); syn::custom_keyword!(added); @@ -42,8 +45,17 @@ pub struct Metadata { /// The name field. pub name: LitStr, - /// The path field. - pub path: LitStr, + /// The path field. (deprecated) + pub path: EndpointPath, + + /// The unstable path field. + pub unstable_path: Option, + + /// The pre-v1.1 path field. + pub r0_path: Option, + + /// The stable path field. + pub stable_path: Option, /// The rate_limited field. pub rate_limited: Vec>, @@ -90,6 +102,9 @@ impl Parse for Metadata { let mut method = None; let mut name = None; let mut path = None; + let mut unstable_path = None; + let mut r0_path = None; + let mut stable_path = None; let mut rate_limited = vec![]; let mut authentication = vec![]; let mut added = None; @@ -102,6 +117,9 @@ impl Parse for Metadata { FieldValue::Method(m) => set_field(&mut method, m)?, FieldValue::Name(n) => set_field(&mut name, n)?, FieldValue::Path(p) => set_field(&mut path, p)?, + FieldValue::Unstable(p) => set_field(&mut unstable_path, p)?, + FieldValue::R0(p) => set_field(&mut r0_path, p)?, + FieldValue::Stable(p) => set_field(&mut stable_path, p)?, FieldValue::RateLimited(value, attrs) => { rate_limited.push(MetadataField { attrs, value }); } @@ -141,11 +159,29 @@ impl Parse for Metadata { } } + if let Some(added) = &added { + if stable_path.is_none() { + return Err(syn::Error::new_spanned( + added, + "added version is defined, but no stable path exists", + )); + } + } + + if unstable_path.is_none() && r0_path.is_none() && stable_path.is_none() { + // TODO replace with error + // return Err(syn::Error::new_spanned(metadata_kw, "no path is defined")); + r0_path = path.clone(); + } + Ok(Self { description: description.ok_or_else(|| missing_field("description"))?, method: method.ok_or_else(|| missing_field("method"))?, name: name.ok_or_else(|| missing_field("name"))?, path: path.ok_or_else(|| missing_field("path"))?, + unstable_path, + r0_path, + stable_path, rate_limited: if rate_limited.is_empty() { return Err(missing_field("rate_limited")); } else { @@ -168,6 +204,9 @@ enum Field { Method, Name, Path, + Unstable, + R0, + Stable, RateLimited, Authentication, Added, @@ -191,6 +230,15 @@ impl Parse for Field { } else if lookahead.peek(kw::path) { let _: kw::path = input.parse()?; Ok(Self::Path) + } else if lookahead.peek(kw::unstable) { + let _: kw::unstable = input.parse()?; + Ok(Self::Unstable) + } else if lookahead.peek(kw::r0) { + let _: kw::r0 = input.parse()?; + Ok(Self::R0) + } else if lookahead.peek(kw::stable) { + let _: kw::stable = input.parse()?; + Ok(Self::Stable) } else if lookahead.peek(kw::rate_limited) { let _: kw::rate_limited = input.parse()?; Ok(Self::RateLimited) @@ -216,7 +264,10 @@ enum FieldValue { Description(LitStr), Method(Ident), Name(LitStr), - Path(LitStr), + Path(EndpointPath), + Unstable(EndpointPath), + R0(EndpointPath), + Stable(EndpointPath), RateLimited(LitBool, Vec), Authentication(AuthScheme, Vec), Added(MatrixVersionLiteral), @@ -242,18 +293,10 @@ impl Parse for FieldValue { Field::Description => Self::Description(input.parse()?), Field::Method => Self::Method(input.parse()?), Field::Name => Self::Name(input.parse()?), - Field::Path => { - let path: LitStr = input.parse()?; - - if !util::is_valid_endpoint_path(&path.value()) { - return Err(syn::Error::new_spanned( - &path, - "path may only contain printable ASCII characters with no spaces", - )); - } - - Self::Path(path) - } + Field::Path => Self::Path(input.parse()?), + Field::Unstable => Self::Unstable(input.parse()?), + Field::R0 => Self::R0(input.parse()?), + Field::Stable => Self::Stable(input.parse()?), Field::RateLimited => Self::RateLimited(input.parse()?, attrs), Field::Authentication => Self::Authentication(input.parse()?, attrs), Field::Added => Self::Added(input.parse()?), @@ -262,3 +305,27 @@ impl Parse for FieldValue { }) } } + +#[derive(Clone)] +pub struct EndpointPath(LitStr); + +impl Parse for EndpointPath { + fn parse(input: ParseStream<'_>) -> syn::Result { + let path: LitStr = input.parse()?; + + if util::is_valid_endpoint_path(&path.value()) { + Ok(Self(path)) + } else { + Err(syn::Error::new_spanned( + &path, + "path may only contain printable ASCII characters with no spaces", + )) + } + } +} + +impl ToTokens for EndpointPath { + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + self.0.to_tokens(tokens) + } +} diff --git a/crates/ruma-api/src/lib.rs b/crates/ruma-api/src/lib.rs index 012f2e26..5003a192 100644 --- a/crates/ruma-api/src/lib.rs +++ b/crates/ruma-api/src/lib.rs @@ -415,9 +415,19 @@ pub struct Metadata { /// A unique identifier for this endpoint. pub name: &'static str, + /// (DEPRECATED) + pub path: &'static str, + + /// The unstable path of this endpoint's URL, often `None`, used for developmental purposes. + pub unstable_path: Option<&'static str>, + + /// The pre-v1.1 version of this endpoint's URL, `None` for post-v1.1 endpoints, supplemental + /// to `stable_path`. + pub r0_path: Option<&'static str>, + /// The path of this endpoint's URL, with variable names where path parameters should be filled /// in during a request. - pub path: &'static str, + pub stable_path: Option<&'static str>, /// Whether or not this endpoint is rate limited by the server. pub rate_limited: bool, diff --git a/crates/ruma-api/tests/manual_endpoint_impl.rs b/crates/ruma-api/tests/manual_endpoint_impl.rs index cc876aec..a00137a9 100644 --- a/crates/ruma-api/tests/manual_endpoint_impl.rs +++ b/crates/ruma-api/tests/manual_endpoint_impl.rs @@ -29,6 +29,9 @@ const METADATA: Metadata = Metadata { method: Method::PUT, name: "create_alias", path: "/_matrix/client/r0/directory/room/:room_alias", + unstable_path: None, + r0_path: None, + stable_path: None, rate_limited: false, authentication: AuthScheme::None, added: None, diff --git a/crates/ruma-api/tests/ui/01-api-sanity-check.rs b/crates/ruma-api/tests/ui/01-api-sanity-check.rs index 2d4253f8..88e46fb0 100644 --- a/crates/ruma-api/tests/ui/01-api-sanity-check.rs +++ b/crates/ruma-api/tests/ui/01-api-sanity-check.rs @@ -8,6 +8,9 @@ ruma_api! { method: POST, // An `http::Method` constant. No imports required. name: "some_endpoint", path: "/_matrix/some/endpoint/:baz", + unstable: "/_matrix/some/msc1234/endpoint/:baz", + r0: "/_matrix/some/r0/endpoint/:baz", + stable: "/_matrix/some/v1/endpoint/:baz", rate_limited: false, authentication: None, added: 1.0, @@ -56,6 +59,10 @@ ruma_api! { fn main() { use ruma_api::MatrixVersion; + assert_eq!(METADATA.unstable_path, Some("/_matrix/some/msc1234/endpoint/:baz")); + assert_eq!(METADATA.r0_path, Some("/_matrix/some/r0/endpoint/:baz")); + assert_eq!(METADATA.stable_path, Some("/_matrix/some/v1/endpoint/:baz")); + assert_eq!(METADATA.added, Some(MatrixVersion::V1_0)); assert_eq!(METADATA.deprecated, Some(MatrixVersion::V1_1)); assert_eq!(METADATA.removed, Some(MatrixVersion::V1_2));