1use std::{
2 error::Error as StdError,
3 fmt, fs,
4 path::{Path, PathBuf},
5};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum Lang {
10 C,
12 Rust,
14}
15
16impl Lang {
17 pub fn extension(self) -> &'static str {
19 match self {
20 Lang::C => "h",
21 Lang::Rust => "rs",
22 }
23 }
24}
25
26#[derive(Debug)]
28pub enum Error {
29 Io(std::io::Error),
30 Parse(Box<pest::error::Error<synapse_parser::synapse::Rule>>),
31}
32
33impl fmt::Display for Error {
34 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35 match self {
36 Error::Io(e) => write!(f, "{e}"),
37 Error::Parse(e) => write!(f, "{e}"),
38 }
39 }
40}
41
42impl StdError for Error {
43 fn source(&self) -> Option<&(dyn StdError + 'static)> {
44 match self {
45 Error::Io(e) => Some(e),
46 Error::Parse(e) => Some(e),
47 }
48 }
49}
50
51impl From<std::io::Error> for Error {
52 fn from(value: std::io::Error) -> Self {
53 Error::Io(value)
54 }
55}
56
57impl From<pest::error::Error<synapse_parser::synapse::Rule>> for Error {
58 fn from(value: pest::error::Error<synapse_parser::synapse::Rule>) -> Self {
59 Error::Parse(Box::new(value))
60 }
61}
62
63pub fn generate_str(source: &str, lang: Lang) -> Result<String, Error> {
65 let file = synapse_parser::ast::parse(source)?;
66 let output = match lang {
67 Lang::C => synapse_codegen_cfs::generate_c(&file),
68 Lang::Rust => synapse_codegen_cfs::generate_rust(&file, &Default::default()),
69 };
70 Ok(output)
71}
72
73pub fn generate_file(
78 input: impl AsRef<Path>,
79 out_dir: impl AsRef<Path>,
80 lang: Lang,
81) -> Result<PathBuf, Error> {
82 let input = input.as_ref();
83 let source = fs::read_to_string(input)?;
84 let output = generate_str(&source, lang)?;
85
86 let out_dir = out_dir.as_ref();
87 fs::create_dir_all(out_dir)?;
88
89 let stem = input.file_stem().ok_or_else(|| {
90 std::io::Error::new(
91 std::io::ErrorKind::InvalidInput,
92 format!("input file has no stem: {}", input.display()),
93 )
94 })?;
95 let out_path = out_dir.join(format!("{}.{}", stem.to_string_lossy(), lang.extension()));
96 fs::write(&out_path, output)?;
97 Ok(out_path)
98}
99
100pub fn generate_c_file(
102 input: impl AsRef<Path>,
103 out_dir: impl AsRef<Path>,
104) -> Result<PathBuf, Error> {
105 generate_file(input, out_dir, Lang::C)
106}
107
108pub fn generate_rust_file(
110 input: impl AsRef<Path>,
111 out_dir: impl AsRef<Path>,
112) -> Result<PathBuf, Error> {
113 generate_file(input, out_dir, Lang::Rust)
114}
115
116#[cfg(test)]
117mod tests {
118 use super::*;
119
120 #[test]
121 fn generate_c_from_string() {
122 let out = generate_str("@mid(0x1880)\ncommand SetMode { mode: u8 }", Lang::C).unwrap();
123 assert!(out.contains("#define SET_MODE_MID 0x1880U"));
124 assert!(out.contains("CFE_MSG_CommandHeader_t Header;"));
125 }
126
127 #[test]
128 fn generate_rust_from_string() {
129 let out = generate_str("@mid(0x0801)\ntelemetry NavState { x: f64 }", Lang::Rust).unwrap();
130 assert!(out.contains("pub const NAV_STATE_MID: u16 = 0x0801;"));
131 assert!(out.contains("pub cfs_header: cfs_sys::CFE_MSG_TelemetryHeader_t,"));
132 }
133}