1#![warn(rust_2018_idioms)]
29
30#[macro_use]
31extern crate fstrings;
32
33#[macro_use]
34mod utils;
35mod db;
36mod generate;
37mod gpio;
38mod i2c;
39mod rcc;
40mod spi;
41mod usart;
42
43use std::collections::HashMap;
44use std::fs;
45use std::fs::OpenOptions;
46use std::io::Write;
47use std::path::Path;
48use std::process::Command;
49
50use anyhow::{anyhow, bail, ensure, Context};
51
52use crate::gpio::GpioPin;
53use crate::i2c::I2C;
54use crate::rcc::RCC;
55use crate::spi::SPI;
56use crate::usart::USART;
57use crate::utils::*;
58
59type ConfigParams<'a> = HashMap<&'a str, HashMap<&'a str, &'a str>>;
60
61#[derive(Debug)]
63pub struct Config {
64 pub version: String,
65 pub mcu_family: MCUFamily,
66 pub mcu_name: String,
67 pub rcc: RCC,
68 pub gpios: Vec<GpioPin>,
69 pub ports: Vec<char>,
70 pub spis: Vec<SPI>,
71 pub usarts: Vec<USART>,
72 pub i2cs: Vec<I2C>,
73}
74
75pub fn load_ioc(file_content: &str) -> anyhow::Result<Config> {
77 let config_params = parse_ioc(file_content);
78
79 let version = String::from(
80 *config_params
81 .get("File")
82 .ok_or_else(|| anyhow!("Couldn't check ioc version"))?
83 .get("Version")
84 .ok_or_else(|| anyhow!("Couldn't check ioc version"))?,
85 );
86
87 let mcu = config_params
88 .get("Mcu")
89 .ok_or_else(|| anyhow!("Couldn't check MCU information"))?;
90
91 let mcu_family = parse_mandatory_param(mcu, "Family")?;
92
93 let mcu_name = mcu
94 .get("UserName")
95 .ok_or_else(|| anyhow!("Couldn't check MCU name"))?
96 .to_string();
97
98 let rcc = rcc::get_rcc(&config_params).context("Parsing of RCC")?;
99
100 let (ports, gpios) = gpio::get_gpios(&config_params).context("Parsing of GPIOs")?;
101
102 let spis = spi::get_spis(&config_params).context("Parsing of SPIs")?;
103
104 let usarts = usart::get_usarts(&config_params).context("Parsing of USARTs")?;
105
106 let i2cs = i2c::get_i2cs(&config_params).context("Parsing of I2Cs")?;
107
108 Ok(Config {
109 version,
110 mcu_family,
111 mcu_name,
112 rcc,
113 gpios,
114 ports,
115 spis,
116 usarts,
117 i2cs,
118 })
119}
120
121pub fn parse_ioc(file_content: &str) -> ConfigParams<'_> {
123 let mut config_params = HashMap::new();
124
125 for line in file_content.lines() {
126 let name_and_value: Vec<&str> = line.split('=').collect();
127
128 if let [name, value] = name_and_value[..] {
129 let object_and_parameter: Vec<&str> = name.split('.').collect();
130 if let [object_name, parameter_name] = object_and_parameter[..] {
131 config_params
132 .entry(object_name)
133 .or_insert_with(HashMap::new)
134 .insert(parameter_name, value);
135 }
136 }
137 }
138
139 config_params
140}
141
142fn cargo_init(project_dir: &Path) -> anyhow::Result<bool> {
143 let output = if project_dir.eq(Path::new("")) {
144 Command::new("cargo").arg("init").output()
146 } else {
147 Command::new("cargo")
148 .arg("init")
149 .current_dir(project_dir)
150 .output()
151 }
152 .context("cargo init")?;
153
154 let output = String::from_utf8(output.stderr).unwrap();
155 Ok(output.contains("Created binary (application) package"))
156}
157
158pub fn generate(project_dir: &Path, config: Config) -> anyhow::Result<()> {
160 ensure!(
161 config.version == "6",
162 "only File.Version=6 supported in ioc file"
163 );
164
165 let package_created = cargo_init(project_dir)?;
167
168 if package_created {
171 println!("Ran cargo init");
172 let cargo_toml = project_dir.join("Cargo.toml");
173 let mut file = OpenOptions::new().append(true).open(cargo_toml)?;
174
175 let dependencies = generate::generate_dependencies(&config)?;
176 write!(file, "{}", dependencies)?;
177 println!("Added dependencies to Cargo.toml");
178 } else {
179 println!("Detected existing project");
180 }
181
182 let main_rs = generate::generate_main(&config)?;
184 println!("Generated src/main.rs");
185
186 let path_to_main = project_dir.join("src/main.rs");
187 fs::write(path_to_main, main_rs).context("write to main.rs")?;
188
189 let cargo_config = generate::generate_cargo_config(&config);
191
192 let path_to_cargo_cofig = project_dir.join(".cargo/config");
193 fs::create_dir_all(path_to_cargo_cofig.parent().unwrap()).unwrap();
194 fs::write(path_to_cargo_cofig, cargo_config).context("write to config")?;
195 println!("Generated .cargo/config");
196
197 let memory_config = generate::generate_memory_x(&config)?;
199
200 let path_to_memory_x = project_dir.join("memory.x");
201 fs::write(path_to_memory_x, memory_config).context("write to memory.x")?;
202 println!("Generated memory.x");
203
204 Ok(())
205}