tdlib-rs-gen-1.4.0/.cargo_vcs_info.json0000644000000001521046102023000133450ustar { "git": { "sha1": "73f220ae28dfd612e1e6ca6f9adfb4b69d411477" }, "path_in_vcs": "tdlib-rs-gen" }tdlib-rs-gen-1.4.0/Cargo.lock0000644000000006121046102023000113210ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[package]] name = "tdlib-rs-gen" version = "1.4.0" dependencies = [ "tdlib-rs-parser", ] [[package]] name = "tdlib-rs-parser" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d792369bc9984e0a469c0acff4dea61f3bc63c4a6a4a70005e109ba899d7762" tdlib-rs-gen-1.4.0/Cargo.toml0000644000000022711046102023000113470ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2024" name = "tdlib-rs-gen" version = "1.4.0" authors = [ "Federico Bruzzone ", "Andrea Longoni", ] build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "Rust code generator from TDLib's API definitions." homepage = "https://github.com/FedericoBruzzone/tdlib-rs" documentation = "https://docs.rs/tdlib-rs-gen" readme = false keywords = [ "tdlib", "telegram", "codegen", "api", "tdlib-rs", ] license = "MIT OR Apache-2.0" repository = "https://github.com/FedericoBruzzone/tdlib-rs" resolver = "2" [lib] name = "tdlib_rs_gen" path = "src/lib.rs" [dependencies.tdlib-rs-parser] version = "1.4.0" tdlib-rs-gen-1.4.0/Cargo.toml.orig000064400000000000000000000006671046102023000150150ustar 00000000000000[package] name = "tdlib-rs-gen" version.workspace = true edition.workspace = true authors.workspace = true license.workspace = true homepage.workspace = true repository.workspace = true documentation = "https://docs.rs/tdlib-rs-gen" keywords = [ "tdlib", "telegram", "codegen", "api", "tdlib-rs" ] description = "Rust code generator from TDLib's API definitions." [dependencies] tdlib-rs-parser = { workspace = true } tdlib-rs-gen-1.4.0/src/enums.rs000064400000000000000000000057621046102023000144130ustar 00000000000000// Copyright 2020 - developers of the `grammers` project. // Copyright 2021 - developers of the `tdlib-rs` project. // Copyright 2024 - developers of the `tgt` and `tdlib-rs` projects. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! Code to generate Rust's `enum`'s from TL definitions. use crate::ignore_type; use crate::metadata::Metadata; use crate::rustifier; use std::io::{self, Write}; use tdlib_rs_parser::tl::{Category, Definition, Type}; /// Writes an enumeration listing all types such as the following rust code: /// /// ```ignore /// pub enum Name { /// Variant(crate::types::Name), /// } /// ``` fn write_enum( file: &mut W, ty: &Type, metadata: &Metadata, gen_bots_only_api: bool, ) -> io::Result<()> { writeln!( file, " #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]", )?; writeln!(file, " #[serde(tag = \"@type\")]")?; writeln!(file, " pub enum {} {{", rustifier::types::type_name(ty))?; for d in metadata.defs_with_type(ty) { if rustifier::definitions::is_for_bots_only(d) && !gen_bots_only_api { continue; } writeln!( file, "{}", rustifier::definitions::description(d, " ") )?; writeln!( file, " #[serde(rename(serialize = \"{0}\", deserialize = \"{0}\"))]", d.name )?; write!(file, " {}", rustifier::definitions::variant_name(d))?; // Variant with no struct since it has no data and it only adds noise if d.params.is_empty() { writeln!(file, ",")?; continue; } else { write!(file, "(")?; } if metadata.is_recursive_def(d) { write!(file, "Box<")?; } write!(file, "{}", rustifier::definitions::qual_name(d))?; if metadata.is_recursive_def(d) { write!(file, ">")?; } writeln!(file, "),")?; } writeln!(file, " }}")?; Ok(()) } /// Write the entire module dedicated to enums. pub(crate) fn write_enums_mod( mut file: &mut W, definitions: &[Definition], metadata: &Metadata, gen_bots_only_api: bool, ) -> io::Result<()> { // Begin outermost mod writeln!(file, "#[allow(clippy::all)]")?; writeln!(file, "pub mod enums {{")?; writeln!(file, " use serde::{{Deserialize, Serialize}};")?; let mut enums: Vec<&Type> = definitions .iter() .filter(|d| d.category == Category::Types && !ignore_type(&d.ty)) .map(|d| &d.ty) .collect(); enums.dedup(); for ty in enums { write_enum(&mut file, ty, metadata, gen_bots_only_api)?; } // End outermost mod writeln!(file, "}}") } tdlib-rs-gen-1.4.0/src/functions.rs000064400000000000000000000111421046102023000152610ustar 00000000000000// Copyright 2020 - developers of the `grammers` project. // Copyright 2021 - developers of the `tdlib-rs` project. // Copyright 2024 - developers of the `tgt` and `tdlib-rs` projects. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! Code to generate Rust's `fn`'s from TL definitions. use crate::metadata::Metadata; use crate::rustifier; use std::io::{self, Write}; use tdlib_rs_parser::tl::{Category, Definition}; /// Defines the `function` corresponding to the definition: /// /// ```ignore /// pub async fn name(client_id: i32, field: Type) -> Result { /// /// } /// ``` fn write_function( file: &mut W, def: &Definition, _metadata: &Metadata, gen_bots_only_api: bool, ) -> io::Result<()> { if rustifier::definitions::is_for_bots_only(def) && !gen_bots_only_api { return Ok(()); } // Documentation writeln!(file, "{}", rustifier::definitions::description(def, " "))?; writeln!(file, " /// # Arguments")?; for param in def.params.iter() { if rustifier::parameters::is_for_bots_only(param) && !gen_bots_only_api { continue; } writeln!( file, " /// * `{}` - {}", rustifier::parameters::attr_name(param), param.description.replace('\n', "\n /// ") )?; } writeln!( file, " /// * `client_id` - The client id to send the request to" )?; // Function writeln!(file, " #[allow(clippy::too_many_arguments)]")?; write!( file, " pub async fn {}(", rustifier::definitions::function_name(def) )?; for param in def.params.iter() { if rustifier::parameters::is_for_bots_only(param) && !gen_bots_only_api { continue; } write!(file, "{}: ", rustifier::parameters::attr_name(param))?; let is_optional = rustifier::parameters::is_optional(param); if is_optional { write!(file, "Option<")?; } write!(file, "{}", rustifier::parameters::qual_name(param))?; if is_optional { write!(file, ">")?; } write!(file, ", ")?; } writeln!( file, "client_id: i32) -> Result<{}, crate::types::Error> {{", rustifier::types::qual_name(&def.ty, false) )?; // Compose request writeln!(file, " let request = json!({{")?; writeln!(file, " \"@type\": \"{}\",", def.name)?; for param in def.params.iter() { if rustifier::parameters::is_for_bots_only(param) && !gen_bots_only_api { continue; } writeln!( file, " \"{0}\": {1},", param.name, rustifier::parameters::attr_name(param), )?; } writeln!(file, " }});")?; // Send request writeln!( file, " let response = send_request(client_id, request).await;" )?; writeln!(file, " if response[\"@type\"] == \"error\" {{")?; writeln!( file, " return Err(serde_json::from_value(response).unwrap())" )?; writeln!(file, " }}")?; if rustifier::types::is_ok(&def.ty) { writeln!(file, " Ok(())")?; } else { writeln!( file, " Ok(serde_json::from_value(response).unwrap())" )?; } writeln!(file, " }}")?; Ok(()) } /// Writes an entire definition as Rust code (`fn`). fn write_definition( file: &mut W, def: &Definition, metadata: &Metadata, gen_bots_only_api: bool, ) -> io::Result<()> { write_function(file, def, metadata, gen_bots_only_api)?; Ok(()) } /// Write the entire module dedicated to functions. pub(crate) fn write_functions_mod( mut file: &mut W, definitions: &[Definition], metadata: &Metadata, gen_bots_only_api: bool, ) -> io::Result<()> { // Begin outermost mod writeln!(file, "#[allow(clippy::all)]")?; writeln!(file, "pub mod functions {{")?; writeln!(file, " use serde_json::json;")?; writeln!(file, " use crate::send_request;")?; let functions = definitions .iter() .filter(|d| d.category == Category::Functions); for definition in functions { write_definition(&mut file, definition, metadata, gen_bots_only_api)?; } // End outermost mod writeln!(file, "}}") } tdlib-rs-gen-1.4.0/src/lib.rs000064400000000000000000000041441046102023000140230ustar 00000000000000// Copyright 2020 - developers of the `grammers` project. // Copyright 2021 - developers of the `tdlib-rs` project. // Copyright 2024 - developers of the `tgt` and `tdlib-rs` projects. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! This module gathers all the code generation submodules and coordinates //! them, feeding them the right data. mod enums; mod functions; mod metadata; mod rustifier; mod types; use std::io::{self, Write}; use tdlib_rs_parser::tl::{Definition, Type}; /// Don't generate types for definitions of this type, /// since they are "core" types and treated differently. const SPECIAL_CASED_TYPES: [&str; 6] = ["Bool", "Bytes", "Int32", "Int53", "Int64", "Ok"]; fn ignore_type(ty: &Type) -> bool { SPECIAL_CASED_TYPES.iter().any(|&x| x == ty.name) } pub fn generate_rust_code( file: &mut impl Write, definitions: &[Definition], gen_bots_only_api: bool, ) -> io::Result<()> { write!( file, "\ // Copyright 2020 - developers of the `grammers` project.\n\ // Copyright 2021 - developers of the `tdlib-rs` project.\n\ // Copyright 2024 - developers of the `tgt` and `tdlib-rs` projects.\n\ //\n\ // Licensed under the Apache License, Version 2.0 or the MIT license\n\ // , at your\n\ // option. This file may not be copied, modified, or distributed\n\ // except according to those terms.\n\ " )?; let metadata = metadata::Metadata::new(definitions); types::write_types_mod(file, definitions, &metadata, gen_bots_only_api)?; enums::write_enums_mod(file, definitions, &metadata, gen_bots_only_api)?; functions::write_functions_mod(file, definitions, &metadata, gen_bots_only_api)?; Ok(()) } tdlib-rs-gen-1.4.0/src/metadata.rs000064400000000000000000000074241046102023000150410ustar 00000000000000// Copyright 2020 - developers of the `grammers` project. // Copyright 2022 - developers of the `tdlib-rs` project. // Copyright 2024 - developers of the `tgt` and `tdlib-rs` projects. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use crate::rustifier; use std::collections::{HashMap, HashSet}; use tdlib_rs_parser::tl::{Category, Definition, Type}; /// Additional metadata required by several parts of the generation. pub(crate) struct Metadata<'a> { recursing_defs: HashSet<&'a String>, default_impl_defs: HashSet<&'a String>, defs_with_type: HashMap<&'a String, Vec<&'a Definition>>, } impl<'a> Metadata<'a> { pub fn new(definitions: &'a [Definition]) -> Self { let mut metadata = Self { recursing_defs: HashSet::new(), default_impl_defs: HashSet::new(), defs_with_type: HashMap::new(), }; let type_definitions = definitions .iter() .filter(|d| d.category == Category::Types) .collect::>(); let type_definition_map = type_definitions .iter() .map(|d| (&d.name, d)) .collect::>(); type_definitions.iter().for_each(|d| { metadata .defs_with_type .entry(&d.ty.name) .or_default() .push(d); }); type_definitions.iter().for_each(|d| { if def_self_references(d, d, &metadata.defs_with_type, &mut HashSet::new()) { metadata.recursing_defs.insert(&d.name); } }); type_definitions.iter().for_each(|d| { if def_contains_only_bare_types(d, &type_definition_map) { metadata.default_impl_defs.insert(&d.name); } }); metadata } /// Returns `true` if any of the parameters of `Definition` eventually /// contains the same type as the `Definition` itself (meaning it recurses). pub fn is_recursive_def(&self, def: &Definition) -> bool { self.recursing_defs.contains(&def.name) } /// Returns `true` if the `Definition` can implement the trait `Default` pub fn can_def_implement_default(&self, def: &Definition) -> bool { self.default_impl_defs.contains(&def.name) } pub fn defs_with_type(&self, ty: &'a Type) -> &Vec<&Definition> { &self.defs_with_type[&ty.name] } } fn def_self_references<'a>( root: &Definition, check: &'a Definition, defs_with_type: &'a HashMap<&String, Vec<&Definition>>, visited: &mut HashSet<&'a String>, ) -> bool { visited.insert(&check.name); for param in check.params.iter() { if param.ty.name == root.ty.name { return true; } if let Some(defs) = defs_with_type.get(¶m.ty.name) { for def in defs { if visited.contains(&def.name) { continue; } if def_self_references(root, def, defs_with_type, visited) { return true; } } } } false } fn def_contains_only_bare_types<'a>( check: &'a Definition, definition_map: &'a HashMap<&String, &&Definition>, ) -> bool { for param in check.params.iter() { if !rustifier::parameters::is_builtin_type(param) && !param.ty.bare { return false; } if let Some(def) = definition_map.get(¶m.ty.name) && !def_contains_only_bare_types(def, definition_map) { return false; } } true } tdlib-rs-gen-1.4.0/src/rustifier.rs000064400000000000000000000271001046102023000152660ustar 00000000000000// Copyright 2020 - developers of the `grammers` project. // Copyright 2021 - developers of the `tdlib-rs` project. // Copyright 2024 - developers of the `tgt` and `tdlib-rs` projects. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! Several functions to "rustify" names. //! //! Each parsed type can have a corresponding "rusty" name, and //! the method for it can be found in the corresponding submodule: //! //! * `type_name` for use after a type definition (`type FooBar`, `enum FooBar`). //! * `qual_name` for the qualified type name (`crate::foo::BarBaz`). //! * `variant_name` for use inside `enum` variants (`Foo`). //! * `item_path` for use as a qualified item path (`Vec::`). //! * `attr_name` for use as an attribute name (`foo_bar: ()`). use tdlib_rs_parser::tl::{Definition, Parameter, Type}; /// Get the rusty type name for a certain definition, excluding namespace. /// /// For example, transforms `"ns.some_OK_name"` into `"SomeOkName"`. fn rusty_type_name(name: &str) -> String { enum Casing { Upper, Lower, Preserve, } let name = if let Some(pos) = name.rfind('.') { &name[pos + 1..] } else { name }; let mut result = String::with_capacity(name.len()); name.chars().fold(Casing::Upper, |casing, c| { if c == '_' { return Casing::Upper; } match casing { Casing::Upper => { result.push(c.to_ascii_uppercase()); Casing::Lower } Casing::Lower => { result.push(c.to_ascii_lowercase()); if c.is_ascii_uppercase() { Casing::Lower } else { Casing::Preserve } } Casing::Preserve => { result.push(c); if c.is_ascii_uppercase() { Casing::Lower } else { Casing::Preserve } } } }); result } /// Get the rusty documentation from a string. fn rusty_doc(indent: &str, doc: &str) -> String { format!( "{}/// {}", indent, doc.replace('\n', &format!("\n{indent}/// ")) ) } pub mod definitions { use super::*; pub fn type_name(def: &Definition) -> String { rusty_type_name(&def.name) } pub fn function_name(def: &Definition) -> String { let mut result = String::with_capacity(def.name.len()); def.name.chars().for_each(|c| { if c.is_ascii_uppercase() { result.push('_'); result.push(c.to_ascii_lowercase()); } else { result.push(c); } }); result } pub fn qual_name(def: &Definition) -> String { let mut result = String::new(); result.push_str("crate::types::"); result.push_str(&type_name(def)); result } pub fn variant_name(def: &Definition) -> String { let name = type_name(def); let ty_name = types::type_name(&def.ty); let variant = if name.starts_with(&ty_name) && name.len() > ty_name.len() { let variant_name = &name[ty_name.len()..]; if variant_name.chars().next().unwrap().is_ascii_lowercase() { &name } else { variant_name } } else { &name }; match variant { "" => { // Use the name from the last uppercase letter &name[name .as_bytes() .iter() .rposition(|c| c.is_ascii_uppercase()) .unwrap_or(0)..] } "Self" => { // Use the name from the second-to-last uppercase letter &name[name .as_bytes() .iter() .take(name.len() - variant.len()) .rposition(|c| c.is_ascii_uppercase()) .unwrap_or(0)..] } _ => variant, } .to_string() } pub fn description(def: &Definition, indent: &str) -> String { rusty_doc(indent, &def.description) } pub fn is_for_bots_only(def: &Definition) -> bool { def.description.contains("; for bots only") } } pub mod types { use super::*; pub(super) fn builtin_type(ty: &Type) -> Option<&'static str> { Some(match ty.name.as_ref() { "Bool" => "bool", "bytes" => "String", "double" => "f64", "int32" => "i32", "int53" => "i64", "int64" => "i64", "string" => "String", "vector" => "Vec", "Ok" => "()", _ => return None, }) } fn get_base_path(ty: &Type) -> String { if let Some(name) = builtin_type(ty) { name.to_string() } else { let mut result = String::new(); if ty.bare { result.push_str("crate::types::"); } else { result.push_str("crate::enums::"); } result.push_str(&type_name(ty)); result } } fn get_path(ty: &Type, optional_generic_arg: bool) -> String { let mut result = get_base_path(ty); if let Some(generic_ty) = &ty.generic_arg { result.push('<'); if optional_generic_arg { result.push_str("Option<"); } result.push_str(&qual_name(generic_ty, false)); if optional_generic_arg { result.push('>'); } result.push('>'); } result } pub fn type_name(ty: &Type) -> String { rusty_type_name(&ty.name) } pub fn qual_name(ty: &Type, optional_generic_arg: bool) -> String { get_path(ty, optional_generic_arg) } pub fn is_ok(ty: &Type) -> bool { ty.name == "Ok" } pub(super) fn serde_as(ty: &Type) -> Option { if ty.name == "int64" { return Some("DisplayFromStr".into()); } if let Some(generic_arg) = &ty.generic_arg && let Some(serde_as) = serde_as(generic_arg) { let mut result = get_base_path(ty); result.push('<'); result.push_str(&serde_as); result.push('>'); return Some(result); } None } } pub mod parameters { use super::*; pub fn qual_name(param: &Parameter) -> String { // HACK: We're just matching against specific cases because there's not a // documented way for knowing optional generic arguments in the tl scheme let optional_generic_arg = param.description.contains("; messages may be null"); types::qual_name(¶m.ty, optional_generic_arg) } pub fn attr_name(param: &Parameter) -> String { match ¶m.name[..] { "final" => "r#final".into(), "loop" => "r#loop".into(), "self" => "is_self".into(), "static" => "r#static".into(), "type" => "r#type".into(), _ => { let mut result = param.name.clone(); result[..].make_ascii_lowercase(); result } } } pub fn is_builtin_type(param: &Parameter) -> bool { types::builtin_type(¶m.ty).is_some() || is_optional(param) } pub fn is_optional(param: &Parameter) -> bool { param.description.contains("; may be null") || param.description.contains("; pass null") } pub fn is_for_bots_only(param: &Parameter) -> bool { param.description.contains("; for bots only") } pub fn description(param: &Parameter, indent: &str) -> String { rusty_doc(indent, ¶m.description) } pub fn serde_as(param: &Parameter) -> Option { types::serde_as(¶m.ty) } } #[cfg(test)] mod tests { use super::*; // Core methods #[test] fn check_rusty_type_name() { assert_eq!(rusty_type_name("ns.some_OK_name"), "SomeOkName"); } // Definition methods #[test] fn check_def_type_name() { let def = "userEmpty = User".parse().unwrap(); let name = definitions::type_name(&def); assert_eq!(name, "UserEmpty"); } #[test] fn check_def_qual_name() { let def = "userEmpty = User".parse().unwrap(); let name = definitions::qual_name(&def); assert_eq!(name, "crate::types::UserEmpty"); } #[test] fn check_def_variant_name() { let def = "new_session_created = NewSession".parse().unwrap(); let name = definitions::variant_name(&def); assert_eq!(name, "Created"); } #[test] fn check_def_empty_variant_name() { let def = "true = True".parse().unwrap(); let name = definitions::variant_name(&def); assert_eq!(name, "True"); } #[test] fn check_def_self_variant_name() { let def = "inputPeerSelf = InputPeer".parse().unwrap(); let name = definitions::variant_name(&def); assert_eq!(name, "PeerSelf"); } // Type methods #[test] fn check_type_type_name() { let ty = "storage.FileType".parse().unwrap(); let name = types::type_name(&ty); assert_eq!(name, "FileType"); } #[test] fn check_type_qual_name() { let ty = "InputPeer".parse().unwrap(); let name = types::qual_name(&ty, false); assert_eq!(name, "crate::enums::InputPeer"); } #[test] fn check_type_qual_bare_name() { let ty = "ipPort".parse().unwrap(); let name = types::qual_name(&ty, false); assert_eq!(name, "crate::types::IpPort"); } #[test] fn check_type_bytes_qual_name() { let ty = "bytes".parse().unwrap(); let name = types::qual_name(&ty, false); assert_eq!(name, "String"); } #[test] fn check_type_large_int_qual_name() { let ty = "int256".parse().unwrap(); let name = types::qual_name(&ty, false); assert_eq!(name, "crate::types::Int256"); } #[test] fn check_type_raw_vec_qual_name() { let ty = "vector".parse().unwrap(); let name = types::qual_name(&ty, false); assert_eq!(name, "Vec"); } #[test] fn check_type_opt_raw_vec_qual_name() { let ty = "vector".parse().unwrap(); let name = types::qual_name(&ty, true); assert_eq!(name, "Vec>"); } #[test] fn check_type_vec_qual_name() { let ty = "Vector".parse().unwrap(); let name = types::qual_name(&ty, false); assert_eq!(name, "crate::enums::Vector"); } #[test] fn check_type_opt_vec_qual_name() { let ty = "Vector".parse().unwrap(); let name = types::qual_name(&ty, true); assert_eq!(name, "crate::enums::Vector>"); } // Parameter methods #[test] fn check_param_qual_name() { let param = "pts:int".parse().unwrap(); let name = parameters::qual_name(¶m); assert_eq!(name, "crate::types::Int"); } #[test] fn check_param_attr_name() { let param = "access_hash:long".parse().unwrap(); let name = parameters::attr_name(¶m); assert_eq!(name, "access_hash"); } } tdlib-rs-gen-1.4.0/src/types.rs000064400000000000000000000071431046102023000144230ustar 00000000000000// Copyright 2020 - developers of the `grammers` project. // Copyright 2021 - developers of the `tdlib-rs` project. // Copyright 2024 - developers of the `tgt` and `tdlib-rs` projects. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! Code to generate Rust's `struct`'s from TL definitions. use crate::ignore_type; use crate::metadata::Metadata; use crate::rustifier; use std::io::{self, Write}; use tdlib_rs_parser::tl::{Category, Definition}; /// Defines the `struct` corresponding to the definition: /// /// ```ignore /// pub struct Name { /// pub field: Type, /// } /// ``` fn write_struct( file: &mut W, def: &Definition, metadata: &Metadata, gen_bots_only_api: bool, ) -> io::Result<()> { if rustifier::definitions::is_for_bots_only(def) && !gen_bots_only_api { return Ok(()); } writeln!(file, "{}", rustifier::definitions::description(def, " "))?; let serde_as = def .params .iter() .any(|p| rustifier::parameters::serde_as(p).is_some()); if serde_as { writeln!(file, " #[serde_as]",)?; } write!(file, " #[derive(Clone, Debug, ",)?; if metadata.can_def_implement_default(def) { write!(file, "Default, ",)?; } writeln!(file, "PartialEq, Deserialize, Serialize)]",)?; writeln!( file, " pub struct {} {{", rustifier::definitions::type_name(def), )?; for param in def.params.iter() { if rustifier::parameters::is_for_bots_only(param) && !gen_bots_only_api { continue; } writeln!( file, "{}", rustifier::parameters::description(param, " ") )?; if let Some(serde_as) = rustifier::parameters::serde_as(param) { writeln!(file, " #[serde_as(as = \"{serde_as}\")]")?; } write!( file, " pub {}: ", rustifier::parameters::attr_name(param), )?; let is_optional = rustifier::parameters::is_optional(param); if is_optional { write!(file, "Option<")?; } write!(file, "{}", rustifier::parameters::qual_name(param))?; if is_optional { write!(file, ">")?; } writeln!(file, ",")?; } writeln!(file, " }}")?; Ok(()) } /// Writes an entire definition as Rust code (`struct`). fn write_definition( file: &mut W, def: &Definition, metadata: &Metadata, gen_bots_only_api: bool, ) -> io::Result<()> { write_struct(file, def, metadata, gen_bots_only_api)?; Ok(()) } /// Write the entire module dedicated to types. pub(crate) fn write_types_mod( mut file: &mut W, definitions: &[Definition], metadata: &Metadata, gen_bots_only_api: bool, ) -> io::Result<()> { // Begin outermost mod writeln!(file, "#[allow(clippy::all)]")?; writeln!(file, "pub mod types {{")?; writeln!(file, " use serde::{{Deserialize, Serialize}};")?; writeln!(file, " use serde_with::{{serde_as, DisplayFromStr}};")?; let types = definitions .iter() .filter(|d| d.category == Category::Types && !ignore_type(&d.ty) && !d.params.is_empty()); for definition in types { write_definition(&mut file, definition, metadata, gen_bots_only_api)?; } // End outermost mod writeln!(file, "}}") }