Compare commits

..

1 commit

Author SHA1 Message Date
220f294149 Merge branch 'generate-code' into 'main'
[Feat] implement code generation

See merge request wotsubo/endcap-sl-software-ri-generator!3
2025-02-03 11:55:34 +01:00
6 changed files with 46 additions and 111 deletions

11
Cargo.lock generated
View file

@ -88,7 +88,6 @@ dependencies = [
"heck",
"itertools",
"log",
"prettyplease",
"proc-macro2",
"quote",
"roxmltree",
@ -164,16 +163,6 @@ 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,7 +17,6 @@ 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: [`Module::generate_code`]
//! root: [`CodeGen`]
//!
//! # For developers
//! Pass `--document-private-items` to see non-public items.
//! # TODO
//! add docs (especially fields and registers)
use std::{
collections::HashMap,
@ -15,14 +15,13 @@ 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(syn) error: {0}")]
SynError(#[from] syn::Error),
#[error("tokenization error: {0}")]
TokenizeError(#[from] syn::Error),
#[error("failed to create file: {0}")]
FilePathError(String),
#[error("parent is required for {module}")]
@ -121,22 +120,7 @@ mod util {
}
}
impl Module {
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))?)
}
}
trait CodeGen {
pub trait CodeGen {
/// `parent_name` in UpperCamelCase.
fn generate_register_interface(
self,

View file

@ -1,27 +0,0 @@
//! 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,27 +1,4 @@
//! 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>(())
//! ```
//! Root document [`types::Module::from_xml_dom`]
//!
//! # Overview
//!
@ -32,15 +9,13 @@
//!
//! # 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 io;
mod parser;
mod type_traits;
pub mod parser;
pub mod type_traits;
pub mod types;
pub use io::write_to_files;

View file

@ -1,13 +1,15 @@
use std::fs;
use std::fs::DirBuilder;
use std::io::Write;
use std::{collections::HashMap, fs, io::BufWriter};
use anyhow::Result;
use endcap_sl_software_ri_generator::types;
use anyhow::{anyhow, Context, Result};
use endcap_sl_software_ri_generator::{generator::CodeGen, 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,
@ -16,26 +18,39 @@ fn main() -> Result<()> {
nodes_limit: u32::MAX,
},
)?;
log::debug!("Parsed: {:#?}", doc);
// println!("Parsed: {:#?}", doc);
// println!("Root: {:?}", doc.root_element());
let register_map = types::Module::from_xml_dom(doc.root_element())?;
log::info!("read: {:?}", register_map);
log::debug!("read: {:#?}", register_map);
println!("read: {:#?}", register_map);
let files = register_map.generate_code()?;
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) {
println!("===========================================");
let files = register_map.generate_register_interface(None, None, HashMap::new())?;
// println!("{:#?}", files);
for filepath in files.keys().sorted() {
log::info!("{}", filepath.display());
println!("{}", 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(())
}