api: Add metadata! macro for easy Metadata construction
Co-authored-by: Jonathan de Jong <jonathan@automatia.nl>
This commit is contained in:
parent
ec67fcbd6f
commit
b9ec4db8f0
@ -428,3 +428,80 @@ pub enum AuthScheme {
|
|||||||
/// as defined in the federation API.
|
/// as defined in the federation API.
|
||||||
ServerSignatures,
|
ServerSignatures,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convenient constructor for [`Metadata`] constants.
|
||||||
|
///
|
||||||
|
/// Usage:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use ruma_common::{metadata, api::Metadata};
|
||||||
|
/// const _: Metadata = metadata! {
|
||||||
|
/// description: "Endpoint description.",
|
||||||
|
/// method: GET, // one of the associated constants of http::Method
|
||||||
|
/// name: "enpdoint_name",
|
||||||
|
/// rate_limited: true,
|
||||||
|
/// authentication: AccessToken, // one of the variants of api::AuthScheme
|
||||||
|
///
|
||||||
|
/// // history of endpoint paths
|
||||||
|
/// // there must be at least one path but otherwise everything is optional
|
||||||
|
/// history: {
|
||||||
|
/// unstable => "/_matrix/foo/org.bar.msc9000/baz",
|
||||||
|
/// unstable => "/_matrix/foo/org.bar.msc9000/qux",
|
||||||
|
/// 1.0 => "/_matrix/media/r0/qux",
|
||||||
|
/// 1.1 => "/_matrix/media/v3/qux",
|
||||||
|
/// 1.2 => deprecated,
|
||||||
|
/// 1.3 => removed,
|
||||||
|
/// }
|
||||||
|
/// };
|
||||||
|
/// ```
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! metadata {
|
||||||
|
( $( $field:ident: $rhs:tt ),+ $(,)? ) => {
|
||||||
|
$crate::api::Metadata {
|
||||||
|
$( $field: $crate::metadata!(@field $field: $rhs) ),+
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
( @field method: $method:ident ) => { $crate::exports::http::Method::$method };
|
||||||
|
|
||||||
|
( @field authentication: $scheme:ident ) => { $crate::api::AuthScheme::$scheme };
|
||||||
|
|
||||||
|
( @field history: {
|
||||||
|
$( unstable => $unstable_path:literal ),*
|
||||||
|
$(, $( $version:literal => $rhs:tt ),+ )?
|
||||||
|
$(,)?
|
||||||
|
} ) => {
|
||||||
|
$crate::metadata! {
|
||||||
|
@history_impl
|
||||||
|
[ $($unstable_path),* ]
|
||||||
|
// Flip left and right to avoid macro parsing ambiguities
|
||||||
|
$( $( $rhs = $version ),+ )?
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Simple literal case: used for description, name, rate_limited
|
||||||
|
( @field $_field:ident: $rhs:literal ) => { $rhs };
|
||||||
|
|
||||||
|
( @history_impl
|
||||||
|
[ $($unstable_path:literal),* ]
|
||||||
|
$(
|
||||||
|
$( $stable_path:literal = $version:literal ),+
|
||||||
|
$(,
|
||||||
|
deprecated = $deprecated_version:literal
|
||||||
|
$(, removed = $removed_version:literal )?
|
||||||
|
)?
|
||||||
|
)?
|
||||||
|
) => {
|
||||||
|
$crate::api::VersionHistory::new(
|
||||||
|
&[ $( $unstable_path ),+ ],
|
||||||
|
&[ $($(
|
||||||
|
($crate::api::MatrixVersion::from_lit(stringify!($version)), $stable_path)
|
||||||
|
),+)? ],
|
||||||
|
$crate::metadata!(@optional_version $($( $deprecated_version )?)?),
|
||||||
|
$crate::metadata!(@optional_version $($($( $removed_version )?)?)?),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
( @optional_version ) => { None };
|
||||||
|
( @optional_version $version:literal ) => { Some($crate::api::MatrixVersion::from_lit(stringify!($version))) }
|
||||||
|
}
|
||||||
|
@ -559,7 +559,7 @@ impl MatrixVersion {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Try to turn a pair of (major, minor) version components back into a `MatrixVersion`.
|
/// Try to turn a pair of (major, minor) version components back into a `MatrixVersion`.
|
||||||
pub fn from_parts(major: u8, minor: u8) -> Result<Self, UnknownVersionError> {
|
pub const fn from_parts(major: u8, minor: u8) -> Result<Self, UnknownVersionError> {
|
||||||
match (major, minor) {
|
match (major, minor) {
|
||||||
(1, 0) => Ok(MatrixVersion::V1_0),
|
(1, 0) => Ok(MatrixVersion::V1_0),
|
||||||
(1, 1) => Ok(MatrixVersion::V1_1),
|
(1, 1) => Ok(MatrixVersion::V1_1),
|
||||||
@ -570,6 +570,48 @@ impl MatrixVersion {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Constructor for use by the `metadata!` macro.
|
||||||
|
///
|
||||||
|
/// Accepts string literals and parses them.
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub const fn from_lit(lit: &'static str) -> Self {
|
||||||
|
use konst::{option, primitive::parse_u8, result, string};
|
||||||
|
|
||||||
|
let major: u8;
|
||||||
|
let minor: u8;
|
||||||
|
|
||||||
|
let mut lit_iter = string::split(lit, ".").next();
|
||||||
|
|
||||||
|
{
|
||||||
|
let (checked_first, checked_split) = option::unwrap!(lit_iter); // First iteration always succeeds
|
||||||
|
|
||||||
|
major = result::unwrap_or_else!(parse_u8(checked_first), |_| panic!(
|
||||||
|
"major version is not a valid number"
|
||||||
|
));
|
||||||
|
|
||||||
|
lit_iter = checked_split.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
match lit_iter {
|
||||||
|
Some((checked_second, checked_split)) => {
|
||||||
|
minor = result::unwrap_or_else!(parse_u8(checked_second), |_| panic!(
|
||||||
|
"minor version is not a valid number"
|
||||||
|
));
|
||||||
|
|
||||||
|
lit_iter = checked_split.next();
|
||||||
|
}
|
||||||
|
None => panic!("could not find dot to denote second number"),
|
||||||
|
}
|
||||||
|
|
||||||
|
if lit_iter.is_some() {
|
||||||
|
panic!("version literal contains more than one dot")
|
||||||
|
}
|
||||||
|
|
||||||
|
result::unwrap_or_else!(Self::from_parts(major, minor), |_| panic!(
|
||||||
|
"not a valid version literal"
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
// Internal function to do ordering in const-fn contexts
|
// Internal function to do ordering in const-fn contexts
|
||||||
const fn const_ord(&self, other: &Self) -> Ordering {
|
const fn const_ord(&self, other: &Self) -> Ordering {
|
||||||
let self_parts = self.into_parts();
|
let self_parts = self.into_parts();
|
||||||
@ -722,4 +764,11 @@ mod tests {
|
|||||||
Err(IntoHttpError::NoUnstablePath)
|
Err(IntoHttpError::NoUnstablePath)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn version_literal() {
|
||||||
|
const LIT: MatrixVersion = MatrixVersion::from_lit("1.0");
|
||||||
|
|
||||||
|
assert_eq!(LIT, V1_0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -95,7 +95,10 @@ pub use ruma_state_res as state_res;
|
|||||||
/// [apis]: https://spec.matrix.org/v1.4/#matrix-apis
|
/// [apis]: https://spec.matrix.org/v1.4/#matrix-apis
|
||||||
#[cfg(feature = "api")]
|
#[cfg(feature = "api")]
|
||||||
pub mod api {
|
pub mod api {
|
||||||
pub use ruma_common::api::*;
|
// The metadata macro is also exported at the crate root because `#[macro_export]` always
|
||||||
|
// places things at the crate root of the defining crate and we do a glob re-export of
|
||||||
|
// `ruma_common`, but here is the more logical (preferred) location.
|
||||||
|
pub use ruma_common::{api::*, metadata};
|
||||||
|
|
||||||
#[cfg(any(feature = "appservice-api-c", feature = "appservice-api-s"))]
|
#[cfg(any(feature = "appservice-api-c", feature = "appservice-api-s"))]
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user