From 76c19d194d1c5680752f2bef8ae5d960fd883bc0 Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Thu, 6 Feb 2025 20:58:59 +0900 Subject: [PATCH] new: file IO & formatting & update docs with an example --- Cargo.lock | 11 +++++++++++ Cargo.toml | 1 + src/generator.rs | 24 +++++++++++++++++------- src/io.rs | 27 ++++++++++++++++++++++++++ src/lib.rs | 35 +++++++++++++++++++++++++++++----- src/main.rs | 49 +++++++++++++++++------------------------------- 6 files changed, 103 insertions(+), 44 deletions(-) create mode 100644 src/io.rs diff --git a/Cargo.lock b/Cargo.lock index c18c3b5..dff8e1c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -88,6 +88,7 @@ dependencies = [ "heck", "itertools", "log", + "prettyplease", "proc-macro2", "quote", "roxmltree", @@ -163,6 +164,16 @@ version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +[[package]] +name = "prettyplease" +version = "0.2.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6924ced06e1f7dfe3fa48d57b9f74f55d8915f5036121bef647ef4b204895fac" +dependencies = [ + "proc-macro2", + "syn", +] + [[package]] name = "proc-macro2" version = "1.0.93" diff --git a/Cargo.toml b/Cargo.toml index 755bd1c..6bd8bcc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ env_logger = "0.11.6" heck = "0.5" itertools = "0.14" log = "0.4" +prettyplease = "0.2" proc-macro2 = "1.0.93" quote = "1.0" roxmltree = "0.20" diff --git a/src/generator.rs b/src/generator.rs index ba137cc..a4c6d95 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -1,9 +1,9 @@ //! Generate register interface rust code from types in [`crate::types`]. //! -//! root: [`CodeGen`] +//! root: [`Module::generate_code`] //! -//! # TODO -//! add docs (especially fields and registers) +//! # For developers +//! Pass `--document-private-items` to see non-public items. use std::{ collections::HashMap, @@ -15,13 +15,14 @@ use crate::{ types::{Block, Module, ModuleBlockElements, Register}, }; use heck::{ToSnakeCase, ToUpperCamelCase}; +use itertools::Itertools; use quote::quote; use thiserror::Error; #[derive(Debug, Error)] pub enum CodeGenError { - #[error("tokenization error: {0}")] - TokenizeError(#[from] syn::Error), + #[error("tokenization(syn) error: {0}")] + SynError(#[from] syn::Error), #[error("failed to create file: {0}")] FilePathError(String), #[error("parent is required for {module}")] @@ -121,8 +122,17 @@ mod util { } impl Module { - pub fn generate_code(self) -> Result, CodeGenError> { - self.generate_register_interface(None, None, HashMap::new()) + pub fn generate_code(self) -> Result, CodeGenError> { + let files = self.generate_register_interface(None, None, HashMap::new())?; + Ok(files + .into_iter() + .map( + |(path, tokens)| -> Result<(PathBuf, syn::File), syn::Error> { + let file: syn::File = syn::parse2(tokens)?; + Ok((path, file)) + }, + ) + .process_results(|kv| HashMap::from_iter(kv))?) } } diff --git a/src/io.rs b/src/io.rs new file mode 100644 index 0000000..2f345da --- /dev/null +++ b/src/io.rs @@ -0,0 +1,27 @@ +//! File IO for generated codes. + +use std::{collections::HashMap, fs, io, path}; + +/// Write formatted codes generated with [`Module::generate_code`](crate::types::Module::generate_code). +pub fn write_to_files( + files: HashMap, + out_path: &path::Path, +) -> io::Result<()> { + if !out_path.is_dir() { + return Err(io::Error::from(io::ErrorKind::NotADirectory)); + } + if fs::read_dir(out_path)?.next().is_some() { + return Err(io::Error::new( + io::ErrorKind::AlreadyExists, + "out path is not empty", + )); + } + for (file_path, code) in files { + fs::DirBuilder::new() + .recursive(true) + .create(out_path.join(&file_path).parent().unwrap())?; + + fs::write(out_path.join(&file_path), prettyplease::unparse(&code))?; + } + Ok(()) +} diff --git a/src/lib.rs b/src/lib.rs index 49b6578..4129c60 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,27 @@ -//! Root document [`types::Module::from_xml_dom`] +//! Generate register interface software from register map in XML. +//! +//! # Example +//! +//! Here's a typical usage: +//! ```no_run +//! use endcap_sl_software_ri_generator::types; +//! +//! let xmlfile = std::fs::read_to_string("./csr.xml")?; +//! let doc = roxmltree::Document::parse_with_options( +//! &xmlfile, +//! roxmltree::ParsingOptions { +//! allow_dtd: true, +//! nodes_limit: u32::MAX, +//! }, +//! )?; +//! +//! let register_map = types::Module::from_xml_dom(doc.root_element())?; +//! +//! let files = register_map.generate_code()?; +//! endcap_sl_software_ri_generator::write_to_files(files, &std::path::PathBuf::from("out"))?; +//! +//! # Ok::<(), anyhow::Error>(()) +//! ``` //! //! # Overview //! @@ -9,13 +32,15 @@ //! //! # modules //! - [`types`]: type definitions of internal register map representation -//! - [`type_traits`]: supplemental traits for types in [`types`] -//! - [`parser`]: supplemental parser (string to custom types) //! - [`converter`]: DOM to internal representation //! - [`generator`]: internal representation to rust code +//! - [`io`]: formatting and printing pub mod converter; pub mod generator; -pub mod parser; -pub mod type_traits; +pub mod io; +mod parser; +mod type_traits; pub mod types; + +pub use io::write_to_files; diff --git a/src/main.rs b/src/main.rs index 3a43e59..a3a63bf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,15 +1,13 @@ -use std::fs::DirBuilder; -use std::io::Write; -use std::{fs, io::BufWriter}; +use std::fs; -use anyhow::{anyhow, Context, Result}; +use anyhow::Result; use endcap_sl_software_ri_generator::types; use itertools::Itertools; fn main() -> Result<()> { env_logger::init(); + log::debug!("logger enabled"); - println!("Hello, world!"); let xmlfile = fs::read_to_string("./csr.xml")?; let doc = roxmltree::Document::parse_with_options( &xmlfile, @@ -18,39 +16,26 @@ fn main() -> Result<()> { nodes_limit: u32::MAX, }, )?; - // println!("Parsed: {:#?}", doc); - // println!("Root: {:?}", doc.root_element()); + log::debug!("Parsed: {:#?}", doc); let register_map = types::Module::from_xml_dom(doc.root_element())?; - println!("read: {:#?}", register_map); - - println!("==========================================="); + log::info!("read: {:?}", register_map); + log::debug!("read: {:#?}", register_map); let files = register_map.generate_code()?; - // println!("{:#?}", files); - for filepath in files.keys().sorted() { - println!("{}", filepath.display()); + if log::log_enabled!(log::Level::Debug) { + for (path, code) in &files { + log::debug!("path: {:?}", path); + log::debug!("{}", prettyplease::unparse(code)); + } } - if !std::fs::read_dir("register_interface") - .context("register_interface not found")? - .collect_vec() - .is_empty() - { - return Err(anyhow!("dir register_interface is not empty")); - }; - for (file, code) in files { - DirBuilder::new() - .recursive(true) - .create(file.parent().context("no parent")?)?; - let file = fs::OpenOptions::new() - .create(true) - .truncate(true) - .write(true) - .open(file) - .context("opening file")?; - let mut writer = BufWriter::new(file); - write!(writer, "{}", code)?; + if log::log_enabled!(log::Level::Info) { + for filepath in files.keys().sorted() { + log::info!("{}", filepath.display()); + } } + endcap_sl_software_ri_generator::write_to_files(files, &std::path::PathBuf::from("out"))?; + Ok(()) }