1use crate::model::{Field, Type};
2use convert_case::{Case, Casing};
3use eyre::{ContextCompat, Result};
4use serde::de::{Error, Unexpected};
5use serde::ser::SerializeStruct;
6use serde::*;
7use std::fmt::Write;
8
9#[derive(Debug, Serialize, Deserialize, Default, Clone)]
11pub struct EndpointSchema {
12 pub name: String,
14
15 pub code: u32,
17
18 pub parameters: Vec<Field>,
20
21 pub returns: Vec<Field>,
23
24 #[serde(default)]
26 pub stream_response: Option<Type>,
27
28 #[serde(default)]
30 pub description: String,
31
32 #[serde(default)]
34 pub json_schema: serde_json::Value,
35
36 pub roles: Vec<String>,
38
39 #[serde(default)]
41 pub errors: Vec<EndpointErrorSchema>,
42}
43
44impl EndpointSchema {
45 pub fn new(
47 name: impl Into<String>,
48 code: u32,
49 parameters: Vec<Field>,
50 returns: Vec<Field>,
51 ) -> Self {
52 Self {
53 name: name.into(),
54 code,
55 parameters,
56 returns,
57 stream_response: None,
58 description: "".to_string(),
59 json_schema: Default::default(),
60 roles: Vec::new(),
61 errors: Vec::new(),
62 }
63 }
64
65 pub fn with_stream_response_type(mut self, stream_response: Type) -> Self {
67 self.stream_response = Some(stream_response);
68 self
69 }
70
71 pub fn with_description(mut self, desc: impl Into<String>) -> Self {
73 self.description = desc.into();
74 self
75 }
76
77 pub fn with_roles(mut self, roles: Vec<String>) -> Self {
79 self.roles = roles;
80 self
81 }
82
83 pub fn with_errors(mut self, errors: Vec<EndpointErrorSchema>) -> Self {
85 self.errors = errors;
86 self
87 }
88}
89
90#[derive(Clone, Debug, Serialize, Deserialize, Hash, PartialEq, PartialOrd, Eq, Ord)]
91pub struct EndpointErrorSchema {
92 pub name: String,
93 pub code: EndpointErrorCodeRef,
94 #[serde(default)]
95 pub message: String,
96 #[serde(default)]
97 pub fields: Vec<Field>,
98}
99
100#[derive(Clone, Debug, Hash, PartialEq, PartialOrd, Eq, Ord)]
101pub struct EndpointErrorCodeRef {
102 pub ty: Type,
103 pub variant: String,
104}
105
106impl EndpointErrorCodeRef {
107 pub const ENUM_NAME: &'static str = "ErrorCode";
108
109 pub fn new(variant: impl Into<String>) -> Self {
110 Self {
111 ty: Type::enum_ref(Self::ENUM_NAME, true),
112 variant: variant.into(),
113 }
114 }
115
116 pub fn variant(&self) -> &str {
117 &self.variant
118 }
119
120 pub fn path(&self) -> String {
121 format!("{}::{}", Self::ENUM_NAME, self.variant)
122 }
123
124 fn validate_ty(ty: Type) -> std::result::Result<Type, String> {
125 match &ty {
126 Type::EnumRef { name, .. } if name == Self::ENUM_NAME => Ok(ty),
127 Type::EnumRef { name, .. } => {
128 Err(format!("expected {} enum ref, got {name}", Self::ENUM_NAME))
129 }
130 _ => Err(format!("expected {} enum ref", Self::ENUM_NAME)),
131 }
132 }
133
134 fn parse_path(path: &str) -> std::result::Result<Self, String> {
135 let (enum_name, variant) = path
136 .split_once("::")
137 .ok_or_else(|| format!("expected {}::Variant", Self::ENUM_NAME))?;
138
139 if enum_name != Self::ENUM_NAME {
140 return Err(format!(
141 "expected {} enum path, got {enum_name}",
142 Self::ENUM_NAME
143 ));
144 }
145
146 if variant.is_empty() || variant.contains("::") {
147 return Err(format!("expected {}::Variant", Self::ENUM_NAME));
148 }
149
150 Ok(Self::new(variant))
151 }
152}
153
154impl std::fmt::Display for EndpointErrorCodeRef {
155 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
156 f.write_str(&self.path())
157 }
158}
159
160impl Serialize for EndpointErrorCodeRef {
161 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
162 where
163 S: Serializer,
164 {
165 let mut state = serializer.serialize_struct("EndpointErrorCodeRef", 2)?;
166 state.serialize_field("ty", &self.ty)?;
167 state.serialize_field("variant", &self.variant)?;
168 state.end()
169 }
170}
171
172impl<'de> Deserialize<'de> for EndpointErrorCodeRef {
173 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
174 where
175 D: Deserializer<'de>,
176 {
177 #[derive(Deserialize)]
178 #[serde(untagged)]
179 enum Helper {
180 Path(String),
181 Structured { ty: Type, variant: String },
182 }
183
184 match Helper::deserialize(deserializer)? {
185 Helper::Path(path) => Self::parse_path(&path)
186 .map_err(|err| D::Error::invalid_value(Unexpected::Str(&path), &err.as_str())),
187 Helper::Structured { ty, variant } => {
188 let ty = Self::validate_ty(ty).map_err(D::Error::custom)?;
189 Ok(Self { ty, variant })
190 }
191 }
192 }
193}
194
195pub fn encode_header<T: Serialize>(v: T, schema: EndpointSchema) -> Result<String> {
196 let mut s = String::new();
197 write!(s, "0{}", schema.name.to_ascii_lowercase())?;
198 let v = serde_json::to_value(&v)?;
199
200 for (i, f) in schema.parameters.iter().enumerate() {
201 let key = f.name.to_case(Case::Camel);
202 let value = v.get(&key).with_context(|| format!("key: {key}"))?;
203 if value.is_null() {
204 continue;
205 }
206 write!(
207 s,
208 ", {}{}",
209 i + 1,
210 urlencoding::encode(&value.to_string().replace('\"', ""))
211 )?;
212 }
213 Ok(s)
214}