ruma_macros/
api.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
//! Methods and types for generating API endpoints.

use std::{env, fs, path::Path, sync::OnceLock};

use proc_macro2::Span;
use serde::{de::IgnoredAny, Deserialize};

mod attribute;
mod auth_scheme;
pub mod request;
pub mod response;

mod kw {
    syn::custom_keyword!(error);
}

// Returns an error with a helpful error if the crate the request or response macro 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: OnceLock<Result<(), syn::Error>> = OnceLock::new();

    RESULT
        .get_or_init(|| {
            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_to_string(manifest_file)
                .map_err(|_| syn::Error::new(Span::call_site(), "Failed to read Cargo.toml"))?;

            let manifest_parsed: CargoToml = toml::from_str(&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(())
        })
        .as_ref()
        .err()
}