mirror of
https://gitlab.cern.ch/wotsubo/endcap-sl-software-ri-generator.git
synced 2025-02-23 09:07:07 +09:00
new: file IO & formatting & update docs with an example
This commit is contained in:
parent
a6c56ef9d6
commit
76c19d194d
6 changed files with 103 additions and 44 deletions
11
Cargo.lock
generated
11
Cargo.lock
generated
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
27
src/io.rs
Normal 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(())
|
||||
}
|
35
src/lib.rs
35
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;
|
||||
|
|
49
src/main.rs
49
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(())
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue