1use std::fmt;
7use std::str::FromStr;
8
9use heck::*;
10use serde::Deserialize;
11
12type Result<T> = std::result::Result<T, fmt::Error>;
13
14pub mod book;
15pub mod book_to_messages;
16pub mod enums;
17pub mod errors;
18pub mod messages;
19pub mod messages_to_book;
20pub mod permissions;
21pub mod versions;
22
23#[derive(Debug, Deserialize)]
24pub struct EnumValue {
25 pub name: String,
26 pub doc: String,
27 pub num: String,
28}
29
30#[derive(Clone, Debug)]
31pub enum InnerRustType {
32 Primitive(String),
33 Struct(String),
34 Ref(Box<InnerRustType>),
35 Option(Box<InnerRustType>),
36 Map(Box<InnerRustType>, Box<InnerRustType>),
38 Set(Box<InnerRustType>),
39 Vec(Box<InnerRustType>),
40 Cow(Box<InnerRustType>),
41}
42
43#[derive(Clone, Debug)]
44pub struct RustType {
45 pub inner: InnerRustType,
46 pub lifetime: bool,
48}
49
50impl InnerRustType {
51 pub fn fmt(&self, f: &mut fmt::Formatter, lifetime: bool, is_ref: bool) -> fmt::Result {
54 let lifetime_str = if lifetime { "'a " } else { "" };
55 match self {
56 Self::Struct(s) if s == "str" => {
57 if is_ref {
58 write!(f, "str")?;
59 } else {
60 write!(f, "String")?;
61 }
62 }
63 Self::Struct(s) if s == "Uid" => {
64 write!(f, "{}", s)?;
65 }
66 Self::Primitive(s) | Self::Struct(s) => write!(f, "{}", s)?,
67 Self::Ref(i) => {
68 write!(f, "&{}", lifetime_str)?;
69 i.fmt(f, lifetime, true)?;
70 }
71 Self::Option(i) => {
72 write!(f, "Option<")?;
73 i.fmt(f, lifetime, false)?;
74 write!(f, ">")?;
75 }
76 Self::Map(k, v) => {
77 write!(f, "HashMap<")?;
78 k.fmt(f, lifetime, false)?;
79 write!(f, ", ")?;
80 v.fmt(f, lifetime, false)?;
81 write!(f, ">")?;
82 }
83 Self::Set(i) => {
84 write!(f, "HashSet<")?;
85 i.fmt(f, lifetime, false)?;
86 write!(f, ">")?;
87 }
88 Self::Vec(i) => {
89 if is_ref {
90 write!(f, "[")?;
91 i.fmt(f, lifetime, false)?;
92 write!(f, "]")?;
93 } else {
94 write!(f, "Vec<")?;
95 i.fmt(f, lifetime, false)?;
96 write!(f, ">")?;
97 }
98 }
99 Self::Cow(i) => {
100 write!(f, "Cow<{}, ", lifetime_str.trim())?;
101 i.fmt(f, false, true)?;
102 write!(f, ">")?;
103 }
104 }
105 Ok(())
106 }
107
108 pub fn to_ref(&self) -> Self {
110 match self {
111 Self::Struct(s) if s == "UidBuf" => Self::Ref(Box::new(Self::Struct("Uid".into()))),
112 Self::Struct(s) if s == "String" => Self::Ref(Box::new(Self::Struct("str".into()))),
113 Self::Struct(_) | Self::Map(_, _) | Self::Set(_) | Self::Vec(_) => {
114 Self::Ref(Box::new(self.clone()))
115 }
116 Self::Primitive(_) | Self::Ref(_) | Self::Cow(_) => self.clone(),
117 Self::Option(i) => Self::Option(Box::new(i.to_ref())),
118 }
119 }
120
121 pub fn to_cow(&self) -> Self {
123 match self {
124 Self::Struct(s) if s == "UidBuf" => Self::Cow(Box::new(Self::Struct("Uid".into()))),
125 Self::Struct(s) if s == "String" => Self::Cow(Box::new(Self::Struct("str".into()))),
126 Self::Struct(_) | Self::Map(_, _) | Self::Set(_) | Self::Vec(_) => {
127 Self::Cow(Box::new(self.clone()))
128 }
129 Self::Primitive(_) | Self::Ref(_) | Self::Cow(_) => self.clone(),
130 Self::Option(i) => Self::Option(Box::new(i.to_cow())),
131 }
132 }
133
134 pub fn code_as_ref(&self, name: &str) -> String {
136 match self {
137 Self::Struct(s) if s == "UidBuf" || s == "str" => format!("{}.as_ref()", name),
138 Self::Struct(s) if s == "String" => format!("{}.as_str()", name),
139 Self::Struct(s) if s == "str" => name.into(),
140 Self::Struct(_) => format!("&{}", name),
141 Self::Map(_, _) | Self::Set(_) | Self::Vec(_) | Self::Cow(_) => {
142 format!("{}.as_ref()", name)
143 }
144 Self::Primitive(_) => name.into(),
145 Self::Ref(i) => {
146 let inner = i.code_as_ref(name);
147 if inner == name {
148 format!("*{}", name)
149 } else if inner.starts_with('&') && &inner[1..] == name {
150 name.into()
151 } else {
152 inner
153 }
154 }
155 Self::Option(i) => {
156 match &**i {
157 Self::Struct(s) if s == "String" => format!("{}.as_deref()", name),
159 _ => {
160 let inner = i.code_as_ref(name);
161 if inner == name {
162 inner
163 } else if inner.starts_with('&') && &inner[1..] == name {
164 format!("{0}.as_ref()", name)
165 } else {
166 format!("{0}.as_ref().map(|{0}| {1})", name, inner)
167 }
168 }
169 }
170 }
171 }
172 }
173
174 pub fn uses_lifetime(&self) -> bool {
175 match self {
176 Self::Struct(s) if s == "Uid" => true,
177 Self::Ref(_) | Self::Cow(_) => true,
178 Self::Map(_, i) | Self::Set(i) | Self::Vec(i) | Self::Option(i) => i.uses_lifetime(),
179 _ => false,
180 }
181 }
182}
183
184impl FromStr for InnerRustType {
185 type Err = fmt::Error;
186 fn from_str(s: &str) -> Result<Self> {
187 if s == "DateTime" {
188 Ok(Self::Primitive("OffsetDateTime".into()))
189 } else if s.starts_with("Duration") {
190 Ok(Self::Primitive("Duration".into()))
191 } else if s == "PermissionId" {
192 Ok(Self::Primitive("Permission".into()))
193 } else if s == "Ts3ErrorCode" {
194 Ok(Self::Primitive("Error".into()))
195 } else if s == "bool"
196 || s.starts_with('i')
197 || s.starts_with('u')
198 || s.starts_with('f')
199 || s.ends_with("Id")
200 || s.ends_with("Type")
201 || s.ends_with("Mode")
202 || s == "ChannelPermissionHint"
203 || s == "ClientPermissionHint"
204 || s == "Codec"
205 || s == "LogLevel"
206 || s == "MaxClients"
207 || s == "IpAddr"
208 || s == "Reason"
209 || s == "SocketAddr"
210 {
211 Ok(Self::Primitive(s.into()))
212 } else if s == "Uid" {
213 Ok(Self::Struct("UidBuf".into()))
214 } else if s == "str" || s == "String" {
215 Ok(Self::Struct("String".into()))
216 } else if let Some(rest) = s.strip_prefix('&') {
217 let rest = if rest.starts_with('\'') {
218 let i = rest.find(' ').ok_or_else(|| {
219 eprintln!("Reference type with lifetime has no inner type: {:?}", s);
220 fmt::Error
221 })?;
222 &rest[i + 1..]
223 } else {
224 rest
225 };
226 Ok(Self::Ref(Box::new(rest.parse()?)))
227 } else if let Some(rest) = s.strip_suffix('?') {
228 Ok(Self::Option(Box::new(rest.parse()?)))
229 } else if s.starts_with("Option<") {
230 let rest = &s[7..s.len() - 1];
231 Ok(Self::Option(Box::new(rest.parse()?)))
232 } else if let Some(rest) = s.strip_suffix("[]") {
233 Ok(Self::Vec(Box::new(rest.parse()?)))
234 } else if s.starts_with("HashMap<") {
235 let rest = &s[8..s.len() - 1];
236 let i = rest.find(',').ok_or_else(|| {
237 eprintln!("HashMap without key: {:?}", s);
238 fmt::Error
239 })?;
240 Ok(Self::Map(Box::new((&rest[..i]).parse()?), Box::new(rest[i..].trim().parse()?)))
241 } else if s.starts_with("HashSet<") {
242 let rest = &s[8..s.len() - 1];
243 Ok(Self::Set(Box::new(rest.parse()?)))
244 } else if s.starts_with("Vec<") {
245 let rest = &s[4..s.len() - 1];
246 Ok(Self::Vec(Box::new(rest.parse()?)))
247 } else if s.starts_with('[') {
248 let rest = &s[1..s.len() - 1];
249 if rest.contains(';') {
250 Ok(Self::Struct(s.into()))
252 } else {
253 Ok(Self::Vec(Box::new(rest.parse()?)))
254 }
255 } else if let Some(rest) = s.strip_suffix('T') {
256 rest.parse()
257 } else {
258 Ok(Self::Struct(s.into()))
259 }
260 }
261}
262
263impl FromStr for RustType {
264 type Err = fmt::Error;
265 fn from_str(s: &str) -> Result<Self> {
266 Ok(Self { inner: s.parse()?, lifetime: s.contains("&'") })
267 }
268}
269
270impl RustType {
271 pub fn with_opt(s: &str, opt: bool) -> Result<Self> {
272 let inner = s.parse()?;
273 let inner = if opt { InnerRustType::Option(Box::new(inner)) } else { inner };
274 Ok(Self { inner, lifetime: s.contains("&'") })
275 }
276
277 pub fn with(s: &str, opt: bool, map: Option<&str>, set: bool, vec: bool) -> Result<Self> {
279 assert!(
280 [map.is_some(), set, vec].iter().filter(|b| **b).count() <= 1,
281 "Too many modifiers active (map: {:?}, set: {:?}, vec: {:?})",
282 map,
283 set,
284 vec
285 );
286 let mut inner = s.parse()?;
287 if let Some(key) = map {
288 inner = InnerRustType::Map(Box::new(key.parse()?), Box::new(inner));
289 }
290 if set {
291 inner = InnerRustType::Set(Box::new(inner));
292 }
293 if vec {
294 inner = InnerRustType::Vec(Box::new(inner));
295 }
296
297 inner = if opt { InnerRustType::Option(Box::new(inner)) } else { inner };
298 Ok(Self { inner, lifetime: s.contains("&'") })
299 }
300
301 pub fn to_opt(&self, opt: bool) -> Self {
302 if opt {
303 Self {
304 inner: InnerRustType::Option(Box::new(self.inner.clone())),
305 lifetime: self.lifetime,
306 }
307 } else {
308 self.clone()
309 }
310 }
311
312 pub fn to_ref(&self, as_ref: bool) -> Self {
313 if as_ref {
314 Self { inner: self.inner.to_ref(), lifetime: self.lifetime }
315 } else {
316 self.clone()
317 }
318 }
319
320 pub fn to_cow(&self) -> Self { Self { inner: self.inner.to_cow(), lifetime: self.lifetime } }
321
322 pub fn lifetime(&self, lifetime: bool) -> Self {
323 let mut r = self.clone();
324 r.lifetime = lifetime;
325 r
326 }
327
328 pub fn wrap_ref(&self) -> Self {
329 Self { inner: InnerRustType::Ref(Box::new(self.inner.clone())), lifetime: self.lifetime }
330 }
331
332 pub fn wrap_opt(&self) -> Self {
333 Self { inner: InnerRustType::Option(Box::new(self.inner.clone())), lifetime: self.lifetime }
334 }
335
336 pub fn is_opt(&self) -> bool { matches!(self.inner, InnerRustType::Option(_)) }
337
338 pub fn is_primitive(&self) -> bool { matches!(self.inner, InnerRustType::Primitive(_)) }
339
340 pub fn is_vec(&self) -> bool { matches!(self.inner, InnerRustType::Vec(_)) }
341
342 pub fn is_cow(&self) -> bool {
343 let inner = if let InnerRustType::Option(i) = &self.inner { &*i } else { &self.inner };
344 matches!(inner, InnerRustType::Cow(_))
345 }
346
347 pub fn uses_lifetime(&self) -> bool { self.inner.uses_lifetime() }
348
349 pub fn to_name(&self) -> String {
351 self.to_string().replace('<', "_").replace('>', "").to_camel_case()
352 }
353
354 pub fn code_as_ref(&self, name: &str) -> String { self.inner.code_as_ref(name) }
356}
357
358impl From<InnerRustType> for RustType {
359 fn from(inner: InnerRustType) -> Self { Self { inner, lifetime: false } }
360}
361
362impl fmt::Display for RustType {
363 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.inner.fmt(f, self.lifetime, false) }
364}
365
366fn get_false() -> bool { false }
367
368pub fn doc_comment(s: &str) -> String { s.lines().map(|l| format!("/// {}\n", l)).collect() }
370
371pub fn indent<S: AsRef<str>>(s: S, count: usize) -> String {
373 let sref = s.as_ref();
374 let line_count = sref.lines().count();
375 let mut result = String::with_capacity(sref.len() + line_count * count * 4);
376 for l in sref.lines() {
377 if !l.is_empty() {
378 result.push_str(std::iter::repeat("\t").take(count).collect::<String>().as_str());
379 }
380 result.push_str(l);
381 result.push('\n');
382 }
383 result
384}
385
386pub fn unindent(mut s: &mut String) {
388 std::mem::swap(&mut s.replace("\n\t", "\n"), &mut s);
389 if s.get(0..1) == Some("\t") {
390 s.remove(0);
391 }
392}
393
394pub fn embrace(s: &str) -> String { if s.is_empty() { String::new() } else { format!("({})", s) } }