#![no_std] extern crate alloc; use core::fmt; use alloc::{string::FromUtf8Error, string::String, vec::Vec, rc::Rc, str}; use core_io::{self as io, BufRead, BufReader, Read, Write, Seek, ErrorKind, SeekFrom}; use libboard_zynq::sdio; pub mod sd_reader; pub mod net_settings; pub mod bootgen; #[derive(Debug)] pub enum Error<'a> { SdError(sdio::sd_card::CardInitializationError), IoError(io::Error), Utf8Error(FromUtf8Error), KeyNotFoundError(&'a str), NoConfig, } pub type Result<'a, T> = core::result::Result>; impl<'a> fmt::Display for Error<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Error::SdError(error) => write!(f, "SD error: {}", error), Error::IoError(error) => write!(f, "I/O error: {}", error), Error::Utf8Error(error) => write!(f, "UTF-8 error: {}", error), Error::KeyNotFoundError(name) => write!(f, "Configuration key `{}` not found", name), Error::NoConfig => write!(f, "Configuration not present"), } } } impl<'a> From for Error<'a> { fn from(error: sdio::sd_card::CardInitializationError) -> Self { Error::SdError(error) } } impl<'a> From for Error<'a> { fn from(error: io::Error) -> Self { Error::IoError(error) } } impl<'a> From for Error<'a> { fn from(error: FromUtf8Error) -> Self { Error::Utf8Error(error) } } fn parse_config<'a>( key: &'a str, buffer: &mut Vec, file: fatfs::File, ) -> Result<'a, ()> { let prefix = [key, "="].concat(); for line in BufReader::new(file).lines() { let line = line?; if line.starts_with(&prefix) { buffer.extend(line[prefix.len()..].as_bytes()); return Ok(()); } } Err(Error::KeyNotFoundError(key)) } fn delete_old_entry<'a>( key: &str, file: fatfs::File, mut file_tmp: fatfs::File, ) -> Result<'a, ()> { let prefix = [key, "="].concat(); let buf_reader = BufReader::new(file); for line in buf_reader.lines() { let line = line?; if !line.starts_with(&prefix) { file_tmp.write_all(&[line.as_str(), "\n"].concat().as_bytes())?; } } Ok(()) } fn rename_file<'a>(dir: &fatfs::Dir, old_file_name: &str, new_file_name: &str) -> Result<'a, ()>{ { let old_file = dir.open_file(old_file_name)?; let mut new_file = dir.create_file(new_file_name)?; new_file.truncate()?; for line in BufReader::new(old_file).lines() { let line = line?; new_file.write_all(&[line.as_str(), "\n"].concat().as_bytes())?; } } dir.remove(old_file_name)?; Ok(()) } pub struct Config { fs: Option>>, } impl Config { pub fn new() -> Result<'static, Self> { let sdio = sdio::Sdio::sdio0(true); if !sdio.is_card_inserted() { Err(sdio::sd_card::CardInitializationError::NoCardInserted)?; } let sd = sdio::sd_card::SdCard::from_sdio(sdio)?; let reader = sd_reader::SdReader::new(sd); let fs = reader.mount_fatfs(sd_reader::PartitionEntry::Entry1)?; Ok(Config { fs: Some(Rc::new(fs)) }) } pub fn from_fs(fs: Option>>) -> Self { Config { fs } } pub fn new_dummy() -> Self { Config { fs: None } } pub fn read<'b>(&self, key: &'b str) -> Result<'b, Vec> { if let Some(fs) = &self.fs { let root_dir = fs.root_dir(); let mut buffer: Vec = Vec::new(); match root_dir.open_file(&["/CONFIG/", key, ".BIN"].concat()) { Ok(mut f) => f.read_to_end(&mut buffer).map(|_| ())?, Err(_) => match root_dir.open_file("/CONFIG.TXT") { Ok(f) => parse_config(key, &mut buffer, f)?, Err(_) => return Err(Error::KeyNotFoundError(key)), }, }; Ok(buffer) } else { Err(Error::NoConfig) } } pub fn read_str<'b>(&self, key: &'b str) -> Result<'b, String> { Ok(String::from_utf8(self.read(key)?)?) } pub fn erase<'b>(&mut self) -> Result<'b, ()>{ if let Some(fs) = &self.fs { let root_dir = fs.root_dir(); match root_dir.create_file("/CONFIG.TXT") { Ok(mut file) => { file.truncate()?; }, Err(e) => { return Err(Error::IoError(e)); } }; let dir = root_dir.create_dir("/CONFIG")?; for r in dir.iter() { let entry = r?; if entry.is_file() { dir.remove(str::from_utf8(entry.short_file_name_as_bytes()).unwrap())?; } } Ok(()) } else { Err(Error::NoConfig) } } pub fn remove_config_txt<'b>(&mut self, key: &str) -> Result<'b, ()>{ if let Some(fs) = &self.fs { let root_dir = fs.root_dir(); let config_txt_tmp = "/CONFIG.TMP"; let mut need_to_rename = false; match root_dir.open_file("/CONFIG.TXT") { Ok(file) => { need_to_rename = true; let mut file_tmp = root_dir.create_file(config_txt_tmp)?; file_tmp.truncate()?; delete_old_entry(key, file, file_tmp)?; }, Err(e) => match e.kind() { ErrorKind::NotFound => {}, _ => { return Err(Error::IoError(e)); } } }; if need_to_rename { rename_file(&root_dir, config_txt_tmp, "/CONFIG.TXT")?; } Ok(()) } else { Err(Error::NoConfig) } } pub fn remove_config_key_bin<'b>(&mut self, key: &str) -> Result<'b, ()>{ if let Some(fs) = &self.fs { let root_dir = fs.root_dir(); let config_key_bin = &["/CONFIG/", key, ".BIN"].concat(); match root_dir.remove(config_key_bin) { Ok(_) => {}, Err(e) => match e.kind() { ErrorKind::NotFound => {}, _ => { return Err(Error::IoError(e)); } } }; Ok(()) } else { Err(Error::NoConfig) } } pub fn remove<'b>(&mut self, key: &str) -> Result<'b, ()>{ self.remove_config_txt(key)?; self.remove_config_key_bin(key)?; Ok(()) } pub fn write_str<'b>(&mut self, key: &str, data: &str) -> Result<'b, ()>{ let mut call_remove_config_key_bin = false; let mut call_remove_config_txt = false; if let Some(fs) = &self.fs { let root_dir = fs.root_dir(); let config_key_bin = &["/CONFIG/", key, ".BIN"].concat(); let config_txt_tmp = "/CONFIG.TMP"; if data.is_ascii() & (data.len() <= 100) { match root_dir.create_file("/CONFIG.TXT") { Ok(file) => { let mut file_tmp = root_dir.create_file(config_txt_tmp)?; file_tmp.truncate()?; delete_old_entry(key, file, file_tmp)?; }, Err(e) => { return Err(Error::IoError(e)); } }; rename_file(&root_dir, config_txt_tmp, "/CONFIG.TXT")?; let mut file = root_dir.open_file("/CONFIG.TXT")?; file.seek(SeekFrom::End(0))?; file.write_all(&["\n", key, "=", data, "\n"].concat().as_bytes())?; call_remove_config_key_bin = true; } else { root_dir.create_dir("/CONFIG")?; match root_dir.create_file(config_key_bin) { Ok(mut file) => { file.truncate()?; file.write_all(&[data, "\n"].concat().as_bytes())?; }, Err(e) => { return Err(Error::IoError(e)); } }; call_remove_config_txt = true; } } else { return Err(Error::NoConfig); } if call_remove_config_key_bin { self.remove_config_key_bin(key)?; } if call_remove_config_txt { self.remove_config_txt(key)?; } Ok(()) } }