Repo of no-std crates for my personal embedded projects
at main 3.5 kB view raw
1use heck::ToUpperCamelCase; 2use std::{io::BufWriter, path::PathBuf}; 3 4static MESSAGE_TEMPLATE: &str = include_str!("./templates/message.handlebars"); 5 6#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, serde::Serialize)] 7struct ParsedMessageKind { 8 name: String, 9 id: u64, 10} 11 12fn main() -> anyhow::Result<()> { 13 let proto_files = ["protos/api.proto", "protos/api_options.proto"]; 14 let includes = ["protos"]; 15 16 prost_build::Config::new() 17 .btree_map(["."]) 18 .message_attribute( 19 ".", 20 "#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]", 21 ) 22 .default_package_filename("api") 23 .compile_protos(&proto_files, &includes)?; 24 25 let valid_sources = 0..=2; 26 27 let mut message_kinds: Vec<ParsedMessageKind> = protobuf_parse::Parser::new() 28 .inputs(proto_files) 29 .includes(includes) 30 .parse_and_typecheck()? 31 .file_descriptors 32 .into_iter() 33 .flat_map(|fd| fd.message_type) 34 .filter_map(|message| { 35 if let Some(protobuf::UnknownValueRef::Varint(id)) = message 36 .options 37 .get_or_default() 38 .special_fields 39 .unknown_fields() 40 .get(1036) 41 && let Some(protobuf::UnknownValueRef::Varint(source)) = message 42 .options 43 .get_or_default() 44 .special_fields 45 .unknown_fields() 46 .get(1037) 47 && valid_sources.contains(&source) 48 { 49 Some(ParsedMessageKind { 50 name: sanitize_identifier(message.name().to_upper_camel_case()), 51 id, 52 }) 53 } else { 54 None 55 } 56 }) 57 .collect(); 58 59 message_kinds.sort(); 60 61 let out_dir = PathBuf::from(std::env::var("OUT_DIR")?).join("api.rs"); 62 63 let api_constants = BufWriter::new(std::fs::OpenOptions::new().append(true).open(out_dir)?); 64 65 handlebars::Handlebars::new().render_template_to_write( 66 MESSAGE_TEMPLATE, 67 &message_kinds, 68 api_constants, 69 )?; 70 71 Ok(()) 72} 73 74/// From prost-build, as it is in a private module. 75fn sanitize_identifier(ident: String) -> String { 76 // Use a raw identifier if the identifier matches a Rust keyword: 77 // https://doc.rust-lang.org/reference/keywords.html. 78 match ident.as_str() { 79 // 2015 strict keywords. 80 | "as" | "break" | "const" | "continue" | "else" | "enum" | "false" 81 | "fn" | "for" | "if" | "impl" | "in" | "let" | "loop" | "match" | "mod" | "move" | "mut" 82 | "pub" | "ref" | "return" | "static" | "struct" | "trait" | "true" 83 | "type" | "unsafe" | "use" | "where" | "while" 84 // 2018 strict keywords. 85 | "dyn" 86 // 2015 reserved keywords. 87 | "abstract" | "become" | "box" | "do" | "final" | "macro" | "override" | "priv" | "typeof" 88 | "unsized" | "virtual" | "yield" 89 // 2018 reserved keywords. 90 | "async" | "await" | "try" 91 // 2024 reserved keywords. 92 | "gen" => format!("r#{ident}"), 93 // the following keywords are not supported as raw identifiers and are therefore suffixed with an underscore. 94 "_" | "super" | "self" | "Self" | "extern" | "crate" => format!("{ident}_"), 95 // the following keywords begin with a number and are therefore prefixed with an underscore. 96 s if s.starts_with(char::is_numeric) => format!("_{ident}"), 97 _ => ident, 98 } 99}