yutani_codegen/
proto.rs

1use std::{
2    fs::File,
3    io::Read,
4    path::Path
5};
6use heck::ToSnakeCase;
7use proc_macro2::{TokenStream, Ident, Span};
8use quote::quote;
9use serde::Deserialize;
10
11use crate::Result;
12
13#[derive(Debug, Deserialize)]
14pub struct Protocol {
15    pub name: String,
16    pub summary: Option<String>,
17    pub description: Option<String>,
18    pub copyright: Option<String>,
19    #[serde(rename = "interface", default)]
20    pub interfaces: Vec<Interface>
21}
22impl Protocol {
23    pub fn from_str(string: &str) -> Result<Self> {
24        Ok(toml::from_str(string)?)
25    }
26    pub fn load<P: AsRef<Path>>(path: P) -> Result<Self> {
27        let path = path.as_ref();
28        let mut protocol = String::new();
29        let mut file = File::open(path)?;
30        file.read_to_string(&mut protocol)?;
31        Ok(Self::from_str(&protocol)?)
32    }
33}
34
35#[derive(Clone, Debug, Deserialize)]
36pub struct Interface {
37    pub name: String,
38    pub summary: Option<String>,
39    pub description: Option<String>,
40    pub version: u32,
41    #[serde(rename = "enum", default)]
42    pub enums: Vec<Enum>,
43    #[serde(rename = "request", default)]
44    pub requests: Vec<Request>,
45    #[serde(rename = "event", default)]
46    pub events: Vec<Event>
47}
48
49#[derive(Clone, Debug, Deserialize)]
50pub struct Enum {
51    pub name: String,
52    pub summary: Option<String>,
53    pub description: Option<String>,
54    pub since: Option<u32>,
55    #[serde(rename = "entry", default)]
56    pub entries: Vec<Entry>
57}
58#[derive(Clone, Debug, Deserialize)]
59pub struct Request {
60    pub name: String,
61    pub since: Option<u32>,
62    #[serde(default)]
63    pub destructor: bool,
64    pub summary: Option<String>,
65    pub description: Option<String>,
66    #[serde(rename = "arg", default)]
67    pub args: Vec<Arg>
68}
69#[derive(Clone, Debug, Deserialize)]
70pub struct Event {
71    pub name: String,
72    pub since: Option<u32>,
73    pub summary: Option<String>,
74    pub description: Option<String>,
75    #[serde(rename = "arg", default)]
76    pub args: Vec<Arg>
77}
78
79#[derive(Clone, Debug, Deserialize)]
80pub struct Entry {
81    pub name: String,
82    pub since: Option<u32>,
83    pub summary: Option<String>,
84    pub description: Option<String>,
85    pub value: u32
86}
87
88#[derive(Clone, Debug, Deserialize)]
89#[serde(rename_all = "lowercase")]
90pub enum RequestType {
91    Destructor
92}
93
94#[derive(Clone, Debug, Deserialize)]
95pub struct Arg {
96    pub name: String,
97    #[serde(rename = "allow-null", default)]
98    pub nullable: bool,
99    #[serde(rename = "type")]
100    pub ty: DataType,
101    pub interface: Option<String>,
102    #[serde(rename = "enum")]
103    pub enumeration: Option<String>,
104    pub summary: Option<String>
105}
106impl Arg {
107    pub fn getter(&self, stream: &Ident) -> TokenStream {
108        match self.ty {
109            DataType::Int => quote!{#stream.i32()?},
110            DataType::Uint => quote!{#stream.u32()?},
111            DataType::Fixed => quote!{#stream.fixed()?},
112            DataType::String => if self.nullable {
113                quote!{#stream.string()?}
114            } else {
115                quote!{#stream.string()?.ok_or(::yutani::wire::WlError::NON_NULLABLE)?}
116            },
117            DataType::Array => quote!{#stream.bytes()?},
118            DataType::Fd => quote!{#stream.file()?},
119            DataType::Object => if self.nullable {
120                quote!{#stream.object()?}
121            } else {
122                quote!{#stream.object()?.ok_or(::yutani::wire::WlError::NON_NULLABLE)?}
123            },
124            DataType::NewId => if let Some(_) = self.interface.as_ref() {
125                quote!{#stream.object()?.ok_or(::yutani::wire::WlError::NON_NULLABLE)?}
126            } else {
127                quote!{#stream.new_id()?}
128            }
129        }
130    }
131    pub fn sender(&self, stream: &Ident) -> TokenStream {
132        let ident = Ident::new_raw(&self.name.to_snake_case(), Span::call_site());
133        match self.ty {
134            DataType::Int => quote!{#stream.send_i32(#ident)?},
135            DataType::Uint => quote!{#stream.send_u32(#ident)?},
136            DataType::Fixed => quote!{#stream.send_fixed(#ident)?},
137            DataType::String => if self.nullable {
138                quote!{#stream.send_string(#ident)?}
139            } else {
140                quote!{#stream.send_string(::core::option::Option::Some(#ident))?}
141            },
142            DataType::Array => quote!{#stream.send_bytes(#ident)?},
143            DataType::Fd => quote!{#stream.send_file(#ident)?},
144            DataType::Object => if self.nullable {
145                quote!{#stream.send_object(#ident)?}
146            } else {
147                quote!{#stream.send_object(Some(#ident))?}
148            },
149            DataType::NewId => if let Some(_) = self.interface.as_ref() {
150                quote!{#stream.send_object(Some(#ident))?}
151            } else {
152                quote!{#stream.send_new_id(#ident)?}
153            }
154        }
155    }
156    pub fn ty(&self) -> TokenStream {
157        match self.ty {
158            DataType::Int => quote!{::core::primitive::i32},
159            DataType::Uint => quote!{::core::primitive::u32},
160            DataType::Fixed => quote!{::yutani::Fixed},
161            DataType::String => if self.nullable {
162                quote!{::core::option::Option<::std::string::String>}
163            } else {
164                quote!{::std::string::String}
165            },
166            DataType::Array => quote!{::std::vec::Vec<u8>},
167            DataType::Fd => quote!{::yutani::File},
168            DataType::Object => if self.nullable {
169                quote!{::core::option::Option<::yutani::Id>}
170            } else {
171                quote!{::yutani::Id}
172            },
173            DataType::NewId => if let Some(_) = self.interface.as_ref() {
174                quote!{::yutani::Id}
175            } else {
176                quote!{::yutani::NewId}
177            }
178        }
179    }
180    pub fn send_ty(&self) -> TokenStream {
181        match self.ty {
182            DataType::Int => quote!{::core::primitive::i32},
183            DataType::Uint => quote!{::core::primitive::u32},
184            DataType::Fixed => quote!{::yutani::Fixed},
185            DataType::String => if self.nullable {
186                quote!{::core::option::Option<&'_ ::core::primitive::str>}
187            } else {
188                quote!{&'_ ::core::primitive::str}
189            },
190            DataType::Array => quote!{&'_ [::core::primitive::u8]},
191            DataType::Fd => quote!{::yutani::Fd<'static>},
192            DataType::Object => if self.nullable {
193                quote!{::core::option::Option<::yutani::Id>}
194            } else {
195                quote!{::yutani::Id}
196            },
197            DataType::NewId => if let Some(_) = self.interface.as_ref() {
198                quote!{::yutani::Id}
199            } else {
200                quote!{&'_ ::yutani::NewId}
201            }
202        }
203    }
204}
205
206#[derive(Copy, Clone, Debug, Deserialize)]
207#[serde(rename_all = "snake_case")]
208pub enum DataType {
209    Int,
210    Uint,
211    Fixed,
212    String,
213    Array,
214    Fd,
215    Object,
216    NewId
217}