device_driver_codegen/
rust.rs1use std::ops::Range;
2
3use askama::Template;
4use clap::Parser;
5use convert_case::Case;
6use device_driver_common::{
7 identifier::{Identifier, IdentifierType, Type},
8 specifiers::{Access, AddressMode},
9};
10use device_driver_lir::model::{
11 BlockMethod, BlockMethodType, Driver, Field, FieldConversionMethod, Repeat,
12};
13
14#[derive(Parser, Debug, Clone, Default)]
15#[command(no_binary_name = true, bin_name = "")]
16pub struct RustCodegenOptions {
17 #[arg(
19 long = "rust-defmt-feature",
20 value_name = "FEATURE",
21 require_equals = true
22 )]
23 pub defmt_feature: Option<String>,
24}
25
26#[derive(Template)]
27#[template(path = "rust/driver.rs.j2", escape = "none", whitespace = "minimize")]
28pub struct DriverTemplateRust<'a> {
29 driver: &'a Driver,
30 source: &'a str,
31 codegen_options: &'a RustCodegenOptions,
32}
33
34impl<'a> DriverTemplateRust<'a> {
35 pub fn new(
36 device: &'a Driver,
37 source: &'a str,
38 codegen_options: &'a RustCodegenOptions,
39 ) -> Self {
40 Self {
41 driver: device,
42 source,
43 codegen_options,
44 }
45 }
46
47 fn defmt_feature(&self) -> Option<&str> {
48 self.codegen_options.defmt_feature.as_deref()
49 }
50
51 fn get_block_method_docs(&self, method: &BlockMethod) -> String {
52 use std::fmt::Write;
53
54 let mut docs = String::new();
55
56 let operation_type = match method.method_type {
57 BlockMethodType::Block { .. } => "Block",
58 BlockMethodType::Register { .. } => "Register",
59 BlockMethodType::Command { .. } => "Command",
60 BlockMethodType::Buffer { .. } => "Buffer",
61 };
62
63 if !method.description.is_empty() {
64 writeln!(&mut docs, "///").unwrap();
65 }
66
67 writeln!(&mut docs, "/// {} operation:", operation_type).unwrap();
68 writeln!(&mut docs, "/// - Address: `{}`", method.address).unwrap();
69
70 let reset_value_text = match &method.method_type {
71 BlockMethodType::Register { reset_value, .. } => Some(
72 reset_value
73 .as_ref()
74 .map(|reset_value| {
75 self.source
76 .get(Range::from(reset_value.span))
77 .unwrap_or("error: invalid span")
78 })
79 .unwrap_or("0"),
80 ),
81 _ => None,
82 };
83
84 if let Some(reset_value_text) = reset_value_text {
85 writeln!(&mut docs, "/// - Reset value: `{reset_value_text}`").unwrap();
86 }
87
88 if let Repeat::Count { count, .. } = method.repeat {
89 writeln!(&mut docs, "/// - Index range: `0..{count}`").unwrap();
90 };
91
92 docs
93 }
94}
95
96fn description_to_docstring(description: &str) -> String {
97 use std::fmt::Write;
98
99 let mut docstring = String::new();
100
101 for line in description.lines() {
102 writeln!(
103 &mut docstring,
104 "///{}{line}",
105 if line.starts_with(' ') { "" } else { " " }
106 )
107 .unwrap();
108 }
109
110 docstring
111}
112
113fn get_defmt_fmt_string(field: &Field) -> String {
114 let defmt_type_hint = match field.conversion_method {
115 FieldConversionMethod::None => {
116 let base_type = &field.base_type;
117 format!("={base_type}")
118 }
119 FieldConversionMethod::Bool => "=bool".into(),
120 _ => String::new(),
121 };
122
123 format!(
124 "{}: {{{}}}, ",
125 field.name.to_case(Case::Snake),
126 defmt_type_hint
127 )
128}
129
130fn get_command_fieldset_name(fieldset: &Option<Identifier<Type>>) -> String {
131 match fieldset {
132 Some(fs) => fs.to_case(Case::Pascal),
133 None => "()".into(),
134 }
135}
136
137fn get_enum_base_type<'d>(driver: &'d Driver, enum_name: &Identifier<Type>) -> &'d str {
138 &driver
139 .enums
140 .iter()
141 .find(|e| e.name == *enum_name)
142 .expect("This enum reference is checked in a mir pass")
143 .base_type
144}
145
146fn get_address_mode_const_value(value: &Option<AddressMode>) -> &'static str {
147 match value {
148 Some(AddressMode::Mapped) => "::device_driver::MappedAddressMode",
149 Some(AddressMode::Indexed) => "::device_driver::IndexedAddressMode",
150 None => "()",
151 }
152}
153
154fn maybe_doc_alias<T: IdentifierType>(identifier: &Identifier<T>, case: Case) -> String {
155 if identifier.to_case(case) == identifier.original() {
156 return String::new();
157 }
158
159 format!("#[doc(alias = \"{}\")]", identifier.original())
160}