Skip to main content

endpoint_libs/model/
endpoint.rs

1use crate::model::{Field, Type};
2use convert_case::{Case, Casing};
3use eyre::{ContextCompat, Result};
4use serde::*;
5use std::fmt::Write;
6
7/// `EndpointSchema` is a struct that represents a single endpoint in the API.
8#[derive(Debug, Serialize, Deserialize, Default, Clone)]
9pub struct EndpointSchema {
10    /// The name of the endpoint (e.g. `UserListSymbols`)
11    pub name: String,
12
13    /// The method code of the endpoint (e.g. `10020`)
14    pub code: u32,
15
16    /// A list of parameters that the endpoint accepts (e.g. "symbol" of type `String`)
17    pub parameters: Vec<Field>,
18
19    /// A list of fields that the endpoint returns
20    pub returns: Vec<Field>,
21
22    /// The type of the stream response (if any)
23    #[serde(default)]
24    pub stream_response: Option<Type>,
25
26    /// A description of the endpoint added by `with_description` method
27    #[serde(default)]
28    pub description: String,
29
30    /// The JSON schema of the endpoint (`Default::default()`)
31    #[serde(default)]
32    pub json_schema: serde_json::Value,
33
34    // Allowed roles for this endpoint ["EnumRole::EnumVariant"]
35    pub roles: Vec<String>,
36}
37
38impl EndpointSchema {
39    /// Creates a new `EndpointSchema` with the given name, method code, parameters and returns.
40    pub fn new(
41        name: impl Into<String>,
42        code: u32,
43        parameters: Vec<Field>,
44        returns: Vec<Field>,
45    ) -> Self {
46        Self {
47            name: name.into(),
48            code,
49            parameters,
50            returns,
51            stream_response: None,
52            description: "".to_string(),
53            json_schema: Default::default(),
54            roles: Vec::new(),
55        }
56    }
57
58    /// Adds a stream response type field to the endpoint.
59    pub fn with_stream_response_type(mut self, stream_response: Type) -> Self {
60        self.stream_response = Some(stream_response);
61        self
62    }
63
64    /// Adds a description field to the endpoint.
65    pub fn with_description(mut self, desc: impl Into<String>) -> Self {
66        self.description = desc.into();
67        self
68    }
69
70    /// Adds allowed roles to the endpoint.
71    pub fn with_roles(mut self, roles: Vec<String>) -> Self {
72        self.roles = roles;
73        self
74    }
75}
76
77pub fn encode_header<T: Serialize>(v: T, schema: EndpointSchema) -> Result<String> {
78    let mut s = String::new();
79    write!(s, "0{}", schema.name.to_ascii_lowercase())?;
80    let v = serde_json::to_value(&v)?;
81
82    for (i, f) in schema.parameters.iter().enumerate() {
83        let key = f.name.to_case(Case::Camel);
84        let value = v.get(&key).with_context(|| format!("key: {key}"))?;
85        if value.is_null() {
86            continue;
87        }
88        write!(
89            s,
90            ", {}{}",
91            i + 1,
92            urlencoding::encode(&value.to_string().replace('\"', ""))
93        )?;
94    }
95    Ok(s)
96}