1use quote::ToTokens;
2use serde::{Deserialize, Serialize};
3use std::collections::BTreeMap;
4use syn::parse::{Parse, ParseBuffer};
5use syn::spanned::Spanned;
6use syn::{
7 Attribute, Error, Field, Fields, GenericArgument, Ident, ItemStruct, LitStr, Path,
8 PathArguments, Token, Type, TypePath,
9};
10
11#[derive(Clone, Debug, Serialize, Deserialize)]
12pub enum StatsKind {
13 #[serde(rename = "i64")]
14 I64,
15 #[serde(rename = "u64")]
16 U64,
17 #[serde(rename = "float")]
18 Float,
19 #[serde(rename = "string")]
20 String,
21 #[serde(rename = "struct")]
22 Struct(String),
23}
24
25impl StatsKind {
26 pub fn new(ty: &Type, paths: &mut BTreeMap<String, Path>) -> syn::Result<Self> {
27 match ty {
28 Type::Reference(reference) => return Self::new(&reference.elem, paths),
29 Type::Path(TypePath { qself: _, path }) => {
30 if let Some(ident) = path.get_ident() {
31 match ident.to_string().as_str() {
32 "String" | "str" => return Ok(Self::String),
33 "i8" | "i16" | "i32" | "i64" | "isize" => return Ok(Self::I64),
34 "u8" | "u16" | "u32" | "u64" | "usize" => return Ok(Self::U64),
35 "f32" | "f64" => return Ok(Self::Float),
36 _ => {}
37 }
38 }
39 let name = path.to_token_stream().to_string();
40 paths.insert(name.to_string(), path.clone());
41 return Ok(Self::Struct(name));
42 }
43 _ => {}
44 }
45 Err(Error::new(ty.span(), "scx_stats: Unsupported element type"))
46 }
47
48 pub fn can_be_dict_key(&self) -> bool {
49 matches!(self, Self::I64 | Self::U64 | Self::String)
50 }
51}
52
53impl std::fmt::Display for StatsKind {
54 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
55 match self {
56 Self::I64 => write!(f, "i64"),
57 Self::U64 => write!(f, "u64"),
58 Self::Float => write!(f, "float"),
59 Self::String => write!(f, "string"),
60 Self::Struct(name) => write!(f, "{}", name),
61 }
62 }
63}
64
65#[derive(Clone, Debug, Serialize, Deserialize)]
66pub enum StatsData {
67 #[serde(rename = "datum")]
68 Datum(StatsKind),
69 #[serde(rename = "array")]
70 Array(StatsKind),
71 #[serde(rename = "dict")]
72 Dict { key: StatsKind, datum: StatsKind },
73}
74
75impl StatsData {
76 fn new_array(path: &Path, paths: &mut BTreeMap<String, Path>) -> syn::Result<Option<Self>> {
77 if path.leading_colon.is_some() {
78 return Ok(None);
79 }
80
81 let is_vec = match path.segments.len() {
82 1 => path.segments[0].ident == "Vec",
83 3 => {
84 path.segments[0].ident == "std"
85 && path.segments[1].ident == "vec"
86 && path.segments[2].ident == "Vec"
87 }
88 _ => false,
89 };
90
91 if !is_vec {
92 return Ok(None);
93 }
94
95 if let PathArguments::AngleBracketed(ab) = &path.segments.last().unwrap().arguments {
96 let args = &ab.args;
97 if args.is_empty() {
98 return Err(Error::new(
99 args.span(),
100 "scx_stats: T generic argument missing",
101 ));
102 }
103
104 match &args[0] {
105 GenericArgument::Type(ty) => Ok(Some(Self::Array(StatsKind::new(ty, paths)?))),
106 _ => Ok(None),
107 }
108 } else {
109 Ok(None)
110 }
111 }
112
113 fn new_dict(path: &Path, paths: &mut BTreeMap<String, Path>) -> syn::Result<Option<Self>> {
114 if path.leading_colon.is_some() {
115 return Ok(None);
116 }
117
118 let is_btree_map = match path.segments.len() {
119 1 => path.segments[0].ident == "BTreeMap",
120 3 => {
121 path.segments[0].ident == "std"
122 && path.segments[1].ident == "collections"
123 && path.segments[2].ident == "BTreeMap"
124 }
125 _ => false,
126 };
127
128 if !is_btree_map {
129 return Ok(None);
130 }
131
132 if let PathArguments::AngleBracketed(ab) = &path.segments.last().unwrap().arguments {
133 let args = &ab.args;
134 if args.len() < 2 {
135 return Err(Error::new(
136 args.span(),
137 "scx_stats: K, V generic arguments missing",
138 ));
139 }
140
141 match (&args[0], &args[1]) {
142 (GenericArgument::Type(ty0), GenericArgument::Type(ty1)) => {
143 let kind0 = StatsKind::new(ty0, paths)?;
144 let kind1 = StatsKind::new(ty1, paths)?;
145
146 if kind0.can_be_dict_key() {
147 Ok(Some(Self::Dict {
148 key: kind0,
149 datum: kind1,
150 }))
151 } else {
152 Err(Error::new(
153 ty0.span(),
154 "scx_stats: K must be an integer or String",
155 ))
156 }
157 }
158 _ => Ok(None),
159 }
160 } else {
161 Ok(None)
162 }
163 }
164
165 pub fn new(ty: &Type, paths: &mut BTreeMap<String, Path>) -> syn::Result<Self> {
166 let kind = StatsKind::new(ty, paths)?;
167 if let StatsKind::Struct(_) = &kind {
168 if let Type::Path(path) = ty {
169 if let Some(ar) = Self::new_array(&path.path, paths)? {
170 return Ok(ar);
171 }
172 if let Some(dict) = Self::new_dict(&path.path, paths)? {
173 return Ok(dict);
174 }
175 }
176 }
177 Ok(Self::Datum(kind))
178 }
179}
180
181impl std::fmt::Display for StatsData {
182 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
183 match self {
184 Self::Datum(kind) => write!(f, "{}", kind),
185 Self::Array(kind) => write!(f, "[{}]", kind),
186 Self::Dict { key, datum } => write!(f, "{{{}:{}}}", key, datum),
187 }
188 }
189}
190
191#[derive(Clone, Debug, Serialize, Deserialize)]
192pub enum StatsAttr {
193 Top,
194 Desc(String),
195 User(String, String),
196}
197
198struct StatsAttrVec {
199 attrs: Vec<StatsAttr>,
200}
201
202impl Parse for StatsAttrVec {
203 fn parse(input: &ParseBuffer) -> syn::Result<Self> {
204 let mut attrs = vec![];
205 loop {
206 let ident = input.parse::<Ident>()?;
207 match ident.to_string().as_str() {
208 "top" => attrs.push(StatsAttr::Top),
209 "desc" => {
210 input.parse::<Token!(=)>()?;
211 attrs.push(StatsAttr::Desc(input.parse::<LitStr>()?.value()))
212 }
213 key if key.starts_with("_") => {
214 let val = match input.peek(Token!(=)) {
215 true => {
216 input.parse::<Token!(=)>()?;
217 input.parse::<LitStr>()?.value()
218 }
219 false => "true".to_string(),
220 };
221 attrs.push(StatsAttr::User(key.to_string(), val));
222 }
223 _ => Err(Error::new(ident.span(), "scx_stats: Unknown attribute"))?,
224 }
225 if !input.is_empty() {
226 input.parse::<Token!(,)>()?;
227 }
228 if input.is_empty() {
229 break;
230 }
231 }
232 Ok(Self { attrs })
233 }
234}
235
236#[derive(Clone, Debug, Default, Serialize, Deserialize)]
237pub struct StatsFieldAttrs {
238 #[serde(default, skip_serializing_if = "Option::is_none")]
239 pub desc: Option<String>,
240 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
241 pub user: BTreeMap<String, String>,
242}
243
244impl StatsFieldAttrs {
245 pub fn new(attrs: &[Attribute]) -> syn::Result<Self> {
246 let mut fattrs: Self = Self::default();
247
248 for attr in attrs {
249 if attr.path().is_ident("stat") {
250 let vec = attr.parse_args::<StatsAttrVec>()?;
251 for elem in vec.attrs.into_iter() {
252 match elem {
253 StatsAttr::Desc(v) => fattrs.desc = Some(v),
254 StatsAttr::User(k, v) => {
255 fattrs.user.insert(k, v);
256 }
257 v => Err(Error::new(
258 attr.span(),
259 format!("Not a field attribute: {:?}", &v),
260 ))?,
261 }
262 }
263 }
264 }
265
266 Ok(fattrs)
267 }
268}
269
270#[derive(Clone, Debug, Serialize, Deserialize)]
271pub struct StatsField {
272 #[serde(flatten)]
273 pub data: StatsData,
274 #[serde(flatten)]
275 pub attrs: StatsFieldAttrs,
276}
277
278impl StatsField {
279 pub fn new(field: &Field, paths: &mut BTreeMap<String, Path>) -> syn::Result<(String, Self)> {
280 Ok((
281 field.ident.as_ref().unwrap().to_string(),
282 Self {
283 data: StatsData::new(&field.ty, paths)?,
284 attrs: StatsFieldAttrs::new(&field.attrs)?,
285 },
286 ))
287 }
288}
289
290#[derive(Clone, Debug, Default, Serialize, Deserialize)]
291pub struct StatsStructAttrs {
292 #[serde(default, skip_serializing_if = "Option::is_none")]
293 pub top: Option<String>,
294 #[serde(default, skip_serializing_if = "Option::is_none")]
295 pub desc: Option<String>,
296 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
297 pub user: BTreeMap<String, String>,
298}
299
300impl StatsStructAttrs {
301 pub fn new(attrs: &[Attribute]) -> syn::Result<Self> {
302 let mut sattrs: Self = Self::default();
303
304 for attr in attrs {
305 if attr.path().is_ident("stat") {
306 let vec = attr.parse_args::<StatsAttrVec>()?;
307 for elem in vec.attrs.into_iter() {
308 match elem {
309 StatsAttr::Top => sattrs.top = Some("true".into()),
310 StatsAttr::Desc(v) => sattrs.desc = Some(v),
311 StatsAttr::User(k, v) => {
312 sattrs.user.insert(k, v);
313 }
314 }
315 }
316 }
317 }
318
319 Ok(sattrs)
320 }
321}
322
323#[derive(Clone, Debug, Serialize, Deserialize)]
324pub struct StatsMeta {
325 pub name: String,
326 #[serde(flatten)]
327 pub attrs: StatsStructAttrs,
328 pub fields: BTreeMap<String, StatsField>,
329}
330
331#[derive(Clone, Debug)]
332pub struct StatsMetaAux {
333 pub meta: StatsMeta,
334 pub ident: Ident,
335 pub paths: BTreeMap<String, Path>,
336}
337
338impl Parse for StatsMetaAux {
339 fn parse(input: &ParseBuffer) -> syn::Result<Self> {
340 let mut paths = BTreeMap::new();
341 let mut fields = BTreeMap::new();
342
343 let item_struct: ItemStruct = input.parse()?;
344 let attrs = StatsStructAttrs::new(&item_struct.attrs)?;
345
346 if let Fields::Named(named_fields) = &item_struct.fields {
347 for field in named_fields.named.iter() {
348 let (name, sf) = StatsField::new(field, &mut paths)?;
349 fields.insert(name, sf);
350 }
351 }
352
353 Ok(Self {
354 meta: StatsMeta {
355 name: item_struct.ident.to_string(),
356 attrs,
357 fields,
358 },
359 ident: item_struct.ident,
360 paths,
361 })
362 }
363}
364
365pub trait Meta {
366 fn meta() -> StatsMeta;
367}