macros: Ensure that crates using ruma_api macro have client and server features
This commit is contained in:
parent
b11f09030a
commit
909e80841f
@ -18,8 +18,11 @@ proc-macro = true
|
|||||||
compat = []
|
compat = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
once_cell = "1.13.0"
|
||||||
proc-macro-crate = "1.0.0"
|
proc-macro-crate = "1.0.0"
|
||||||
proc-macro2 = "1.0.24"
|
proc-macro2 = "1.0.24"
|
||||||
quote = "1.0.8"
|
quote = "1.0.8"
|
||||||
ruma-identifiers-validation = { version = "0.8.1", path = "../ruma-identifiers-validation", default-features = false }
|
ruma-identifiers-validation = { version = "0.8.1", path = "../ruma-identifiers-validation", default-features = false }
|
||||||
|
serde = { version = "1.0.139", features = ["derive"] }
|
||||||
syn = { version = "1.0.57", features = ["extra-traits", "full", "visit"] }
|
syn = { version = "1.0.57", features = ["extra-traits", "full", "visit"] }
|
||||||
|
toml = "0.5.9"
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
//! Methods and types for generating API endpoints.
|
//! Methods and types for generating API endpoints.
|
||||||
|
|
||||||
use proc_macro2::TokenStream;
|
use std::{env, fs, path::Path};
|
||||||
|
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
use proc_macro2::{Span, TokenStream};
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
|
use serde::{de::IgnoredAny, Deserialize};
|
||||||
use syn::{
|
use syn::{
|
||||||
braced,
|
braced,
|
||||||
parse::{Parse, ParseStream},
|
parse::{Parse, ParseStream},
|
||||||
@ -46,6 +50,8 @@ pub struct Api {
|
|||||||
|
|
||||||
impl Api {
|
impl Api {
|
||||||
pub fn expand_all(self) -> TokenStream {
|
pub fn expand_all(self) -> TokenStream {
|
||||||
|
let maybe_error = ensure_feature_presence().map(syn::Error::to_compile_error);
|
||||||
|
|
||||||
let ruma_common = import_ruma_common();
|
let ruma_common = import_ruma_common();
|
||||||
let http = quote! { #ruma_common::exports::http };
|
let http = quote! { #ruma_common::exports::http };
|
||||||
|
|
||||||
@ -73,6 +79,8 @@ impl Api {
|
|||||||
let metadata_doc = format!("Metadata for the `{}` API endpoint.", name.value());
|
let metadata_doc = format!("Metadata for the `{}` API endpoint.", name.value());
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
|
#maybe_error
|
||||||
|
|
||||||
#[doc = #metadata_doc]
|
#[doc = #metadata_doc]
|
||||||
pub const METADATA: #ruma_common::api::Metadata = #ruma_common::api::Metadata {
|
pub const METADATA: #ruma_common::api::Metadata = #ruma_common::api::Metadata {
|
||||||
description: #description,
|
description: #description,
|
||||||
@ -158,3 +166,52 @@ fn parse_response(input: ParseStream<'_>, attributes: Vec<Attribute>) -> syn::Re
|
|||||||
|
|
||||||
Ok(Response { attributes, fields, response_kw })
|
Ok(Response { attributes, fields, response_kw })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns an error with a helpful error if the crate `ruma_api!` is used from doesn't declare both
|
||||||
|
// a `client` and a `server` feature.
|
||||||
|
fn ensure_feature_presence() -> Option<&'static syn::Error> {
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct CargoToml {
|
||||||
|
features: Features,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct Features {
|
||||||
|
client: Option<IgnoredAny>,
|
||||||
|
server: Option<IgnoredAny>,
|
||||||
|
}
|
||||||
|
|
||||||
|
static RESULT: Lazy<Result<(), syn::Error>> = Lazy::new(|| {
|
||||||
|
let manifest_dir = env::var("CARGO_MANIFEST_DIR")
|
||||||
|
.map_err(|_| syn::Error::new(Span::call_site(), "Failed to read CARGO_MANIFEST_DIR"))?;
|
||||||
|
|
||||||
|
let manifest_file = Path::new(&manifest_dir).join("Cargo.toml");
|
||||||
|
let manifest_bytes = fs::read(manifest_file)
|
||||||
|
.map_err(|_| syn::Error::new(Span::call_site(), "Failed to read Cargo.toml"))?;
|
||||||
|
|
||||||
|
let manifest_parsed: CargoToml = toml::from_slice(&manifest_bytes)
|
||||||
|
.map_err(|_| syn::Error::new(Span::call_site(), "Failed to parse Cargo.toml"))?;
|
||||||
|
|
||||||
|
if manifest_parsed.features.client.is_none() {
|
||||||
|
return Err(syn::Error::new(
|
||||||
|
Span::call_site(),
|
||||||
|
"This crate doesn't define a `client` feature in its `Cargo.toml`.\n\
|
||||||
|
Please add a `client` feature such that generated `OutgoingRequest` and \
|
||||||
|
`IncomingResponse` implementations can be enabled.",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if manifest_parsed.features.server.is_none() {
|
||||||
|
return Err(syn::Error::new(
|
||||||
|
Span::call_site(),
|
||||||
|
"This crate doesn't define a `server` feature in its `Cargo.toml`.\n\
|
||||||
|
Please add a `server` feature such that generated `IncomingRequest` and \
|
||||||
|
`OutgoingResponse` implementations can be enabled.",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
|
||||||
|
RESULT.as_ref().err()
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user