new: file IO & formatting & update docs with an example

This commit is contained in:
Wataru Otsubo 2025-02-06 20:58:59 +09:00
parent a6c56ef9d6
commit 76c19d194d
6 changed files with 103 additions and 44 deletions

11
Cargo.lock generated
View file

@ -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"

View file

@ -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"

View file

@ -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<HashMap<path::PathBuf, proc_macro2::TokenStream>, CodeGenError> {
self.generate_register_interface(None, None, HashMap::new())
pub fn generate_code(self) -> Result<HashMap<path::PathBuf, syn::File>, 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))?)
}
}

27
src/io.rs Normal file
View file

@ -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<path::PathBuf, syn::File>,
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(())
}

View file

@ -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;

View file

@ -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);
if log::log_enabled!(log::Level::Debug) {
for (path, code) in &files {
log::debug!("path: {:?}", path);
log::debug!("{}", prettyplease::unparse(code));
}
}
if log::log_enabled!(log::Level::Info) {
for filepath in files.keys().sorted() {
println!("{}", filepath.display());
log::info!("{}", filepath.display());
}
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)?;
}
endcap_sl_software_ri_generator::write_to_files(files, &std::path::PathBuf::from("out"))?;
Ok(())
}