diff --git a/ruma-api-macros/src/lib.rs b/ruma-api-macros/src/lib.rs index 051e1d09..af90cd9b 100644 --- a/ruma-api-macros/src/lib.rs +++ b/ruma-api-macros/src/lib.rs @@ -3,11 +3,7 @@ //! //! See the documentation for the `ruma_api!` macro for usage details. -#![deny( - missing_copy_implementations, - missing_debug_implementations, - // missing_docs, # Uncomment when https://github.com/rust-lang/rust/pull/60562 is released. -)] +#![deny(missing_copy_implementations, missing_debug_implementations)] #![allow(clippy::cognitive_complexity)] // Since we support Rust 1.36.0, we can't apply this suggestion yet #![allow(clippy::use_self)] @@ -24,175 +20,6 @@ use crate::api::{Api, RawApi}; mod api; -/// Generates a `ruma_api::Endpoint` from a concise definition. -/// -/// The macro expects the following structure as input: -/// -/// ```ignore -/// ruma_api! { -/// metadata { -/// description: &'static str -/// method: http::Method, -/// name: &'static str, -/// path: &'static str, -/// rate_limited: bool, -/// requires_authentication: bool, -/// } -/// -/// request { -/// // Struct fields for each piece of data required -/// // to make a request to this API endpoint. -/// } -/// -/// response { -/// // Struct fields for each piece of data expected -/// // in the response from this API endpoint. -/// } -/// } -/// ``` -/// -/// This will generate a `ruma_api::Metadata` value to be used for the `ruma_api::Endpoint`'s -/// associated constant, single `Request` and `Response` structs, and the necessary trait -/// implementations to convert the request into a `http::Request` and to create a response from a -/// `http::Response` and vice versa. -/// -/// The details of each of the three sections of the macros are documented below. -/// -/// ## Metadata -/// -/// * `description`: A short description of what the endpoint does. -/// * `method`: The HTTP method used for requests to the endpoint. -/// It's not necessary to import `http::Method`'s associated constants. Just write -/// the value as if it was imported, e.g. `GET`. -/// * `name`: A unique name for the endpoint. -/// Generally this will be the same as the containing module. -/// * `path`: The path component of the URL for the endpoint, e.g. "/foo/bar". -/// Components of the path that are parameterized can indicate a varible by using a Rust -/// identifier prefixed with a colon, e.g. `/foo/:some_parameter`. -/// A corresponding query string parameter will be expected in the request struct (see below -/// for details). -/// * `rate_limited`: Whether or not the endpoint enforces rate limiting on requests. -/// * `requires_authentication`: Whether or not the endpoint requires a valid access token. -/// -/// ## Request -/// -/// The request block contains normal struct field definitions. -/// Doc comments and attributes are allowed as normal. -/// There are also a few special attributes available to control how the struct is converted into a -/// `http::Request`: -/// -/// * `#[ruma_api(header = HEADER_NAME)]`: Fields with this attribute will be treated as HTTP -/// headers on the request. -/// The value must implement `AsRef`. -/// Generally this is a `String`. -/// The attribute value shown above as `HEADER_NAME` must be a header name constant from -/// `http::header`, e.g. `CONTENT_TYPE`. -/// * `#[ruma_api(path)]`: Fields with this attribute will be inserted into the matching path -/// component of the request URL. -/// * `#[ruma_api(query)]`: Fields with this attribute will be inserting into the URL's query -/// string. -/// * `#[ruma_api(query_map)]`: Instead of individual query fields, one query_map field, of any -/// type that implements `IntoIterator` (e.g. -/// `HashMap`, can be used for cases where an endpoint supports arbitrary query -/// parameters. -/// -/// Any field that does not include one of these attributes will be part of the request's JSON -/// body. -/// -/// ## Response -/// -/// Like the request block, the response block consists of normal struct field definitions. -/// Doc comments and attributes are allowed as normal. -/// There is also a special attribute available to control how the struct is created from a -/// `http::Request`: -/// -/// * `#[ruma_api(header = HEADER_NAME)]`: Fields with this attribute will be treated as HTTP -/// headers on the response. -/// The value must implement `AsRef`. -/// Generally this is a `String`. -/// The attribute value shown above as `HEADER_NAME` must be a header name constant from -/// `http::header`, e.g. `CONTENT_TYPE`. -/// -/// Any field that does not include the above attribute will be expected in the response's JSON -/// body. -/// -/// ## Newtype bodies -/// -/// Both the request and response block also support "newtype bodies" by using the -/// `#[ruma_api(body)]` attribute on a field. If present on a field, the entire request or response -/// body will be treated as the value of the field. This allows you to treat the entire request or -/// response body as a specific type, rather than a JSON object with named fields. Only one field in -/// each struct can be marked with this attribute. It is an error to have a newtype body field and -/// normal body fields within the same struct. -/// -/// # Examples -/// -/// ```rust,ignore -/// pub mod some_endpoint { -/// use ruma_api_macros::ruma_api; -/// -/// ruma_api! { -/// metadata { -/// description: "Does something.", -/// method: GET, -/// name: "some_endpoint", -/// path: "/_matrix/some/endpoint/:baz", -/// rate_limited: false, -/// requires_authentication: false, -/// } -/// -/// request { -/// pub foo: String, -/// -/// #[ruma_api(header = CONTENT_TYPE)] -/// pub content_type: String, -/// -/// #[ruma_api(query)] -/// pub bar: String, -/// -/// #[ruma_api(path)] -/// pub baz: String, -/// } -/// -/// response { -/// #[ruma_api(header = CONTENT_TYPE)] -/// pub content_type: String, -/// -/// pub value: String, -/// } -/// } -/// } -/// -/// pub mod newtype_body_endpoint { -/// use ruma_api_macros::ruma_api; -/// -/// #[derive(Clone, Debug, Deserialize, Serialize)] -/// pub struct MyCustomType { -/// pub foo: String, -/// } -/// -/// ruma_api! { -/// metadata { -/// description: "Does something.", -/// method: GET, -/// name: "newtype_body_endpoint", -/// path: "/_matrix/some/newtype/body/endpoint", -/// rate_limited: false, -/// requires_authentication: false, -/// } -/// -/// request { -/// #[ruma_api(body)] -/// pub file: Vec, -/// } -/// -/// response { -/// #[ruma_api(body)] -/// pub my_custom_type: MyCustomType, -/// } -/// } -/// } -/// ``` #[proc_macro] pub fn ruma_api(input: TokenStream) -> TokenStream { let raw_api = syn::parse_macro_input!(input as RawApi); diff --git a/src/lib.rs b/src/lib.rs index 355e4dee..3f6867e1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,6 +27,176 @@ use ruma_identifiers; use serde_json; use serde_urlencoded; +/// Generates a `ruma_api::Endpoint` from a concise definition. +/// +/// The macro expects the following structure as input: +/// +/// ```text +/// ruma_api! { +/// metadata { +/// description: &'static str, +/// method: http::Method, +/// name: &'static str, +/// path: &'static str, +/// rate_limited: bool, +/// requires_authentication: bool, +/// } +/// +/// request { +/// // Struct fields for each piece of data required +/// // to make a request to this API endpoint. +/// } +/// +/// response { +/// // Struct fields for each piece of data expected +/// // in the response from this API endpoint. +/// } +/// } +/// ``` +/// +/// This will generate a `ruma_api::Metadata` value to be used for the `ruma_api::Endpoint`'s +/// associated constant, single `Request` and `Response` structs, and the necessary trait +/// implementations to convert the request into a `http::Request` and to create a response from a +/// `http::Response` and vice versa. +/// +/// The details of each of the three sections of the macros are documented below. +/// +/// ## Metadata +/// +/// * `description`: A short description of what the endpoint does. +/// * `method`: The HTTP method used for requests to the endpoint. +/// It's not necessary to import `http::Method`'s associated constants. Just write +/// the value as if it was imported, e.g. `GET`. +/// * `name`: A unique name for the endpoint. +/// Generally this will be the same as the containing module. +/// * `path`: The path component of the URL for the endpoint, e.g. "/foo/bar". +/// Components of the path that are parameterized can indicate a varible by using a Rust +/// identifier prefixed with a colon, e.g. `/foo/:some_parameter`. +/// A corresponding query string parameter will be expected in the request struct (see below +/// for details). +/// * `rate_limited`: Whether or not the endpoint enforces rate limiting on requests. +/// * `requires_authentication`: Whether or not the endpoint requires a valid access token. +/// +/// ## Request +/// +/// The request block contains normal struct field definitions. +/// Doc comments and attributes are allowed as normal. +/// There are also a few special attributes available to control how the struct is converted into a +/// `http::Request`: +/// +/// * `#[ruma_api(header = HEADER_NAME)]`: Fields with this attribute will be treated as HTTP +/// headers on the request. +/// The value must implement `AsRef`. +/// Generally this is a `String`. +/// The attribute value shown above as `HEADER_NAME` must be a header name constant from +/// `http::header`, e.g. `CONTENT_TYPE`. +/// * `#[ruma_api(path)]`: Fields with this attribute will be inserted into the matching path +/// component of the request URL. +/// * `#[ruma_api(query)]`: Fields with this attribute will be inserting into the URL's query +/// string. +/// * `#[ruma_api(query_map)]`: Instead of individual query fields, one query_map field, of any +/// type that implements `IntoIterator` (e.g. +/// `HashMap`, can be used for cases where an endpoint supports arbitrary query +/// parameters. +/// +/// Any field that does not include one of these attributes will be part of the request's JSON +/// body. +/// +/// ## Response +/// +/// Like the request block, the response block consists of normal struct field definitions. +/// Doc comments and attributes are allowed as normal. +/// There is also a special attribute available to control how the struct is created from a +/// `http::Request`: +/// +/// * `#[ruma_api(header = HEADER_NAME)]`: Fields with this attribute will be treated as HTTP +/// headers on the response. +/// The value must implement `AsRef`. +/// Generally this is a `String`. +/// The attribute value shown above as `HEADER_NAME` must be a header name constant from +/// `http::header`, e.g. `CONTENT_TYPE`. +/// +/// Any field that does not include the above attribute will be expected in the response's JSON +/// body. +/// +/// ## Newtype bodies +/// +/// Both the request and response block also support "newtype bodies" by using the +/// `#[ruma_api(body)]` attribute on a field. If present on a field, the entire request or response +/// body will be treated as the value of the field. This allows you to treat the entire request or +/// response body as a specific type, rather than a JSON object with named fields. Only one field in +/// each struct can be marked with this attribute. It is an error to have a newtype body field and +/// normal body fields within the same struct. +/// +/// # Examples +/// +/// ``` +/// pub mod some_endpoint { +/// use ruma_api_macros::ruma_api; +/// +/// ruma_api! { +/// metadata { +/// description: "Does something.", +/// method: POST, +/// name: "some_endpoint", +/// path: "/_matrix/some/endpoint/:baz", +/// rate_limited: false, +/// requires_authentication: false, +/// } +/// +/// request { +/// pub foo: String, +/// +/// #[ruma_api(header = CONTENT_TYPE)] +/// pub content_type: String, +/// +/// #[ruma_api(query)] +/// pub bar: String, +/// +/// #[ruma_api(path)] +/// pub baz: String, +/// } +/// +/// response { +/// #[ruma_api(header = CONTENT_TYPE)] +/// pub content_type: String, +/// +/// pub value: String, +/// } +/// } +/// } +/// +/// pub mod newtype_body_endpoint { +/// use ruma_api_macros::ruma_api; +/// use serde::{Deserialize, Serialize}; +/// +/// #[derive(Clone, Debug, Deserialize, Serialize)] +/// pub struct MyCustomType { +/// pub foo: String, +/// } +/// +/// ruma_api! { +/// metadata { +/// description: "Does something.", +/// method: PUT, +/// name: "newtype_body_endpoint", +/// path: "/_matrix/some/newtype/body/endpoint", +/// rate_limited: false, +/// requires_authentication: false, +/// } +/// +/// request { +/// #[ruma_api(body)] +/// pub file: Vec, +/// } +/// +/// response { +/// #[ruma_api(body)] +/// pub my_custom_type: MyCustomType, +/// } +/// } +/// } +/// ``` #[cfg(feature = "with-ruma-api-macros")] pub use ruma_api_macros::ruma_api;