//! File IO for generated codes. use std::{collections::HashMap, fs, io, path, process}; use thiserror::Error; use crate::types::XmlGitInfo; #[derive(Debug, Error)] pub enum XmlGitInfoError { #[error("io error while getting xml git info: {msg} {source}")] IOError { msg: &'static str, #[source] source: io::Error, }, #[error("git failed: {msg}: {stderr}")] CommandFailed { msg: &'static str, stderr: String }, } impl XmlGitInfoError { fn io_error(e: io::Error, msg: &'static str) -> Self { XmlGitInfoError::IOError { msg, source: e } } } pub(crate) fn get_xml_gitinfo(xml_path: &path::Path) -> Result { let describe = process::Command::new("git") .args(["describe", "--always", "--dirty"]) .current_dir(xml_path.parent().unwrap()) .output() .map_err(|e| XmlGitInfoError::io_error(e, "git describe"))?; let describe = if describe.status.success() { String::from_utf8_lossy(&describe.stdout).trim().to_owned() } else { return Err(XmlGitInfoError::CommandFailed { msg: "git describe", stderr: String::from_utf8_lossy(&describe.stderr).into_owned(), }); }; let timestamp = process::Command::new("git") .args(["log", "-1", "--pretty=format:'%cI'"]) .current_dir(xml_path.parent().unwrap()) .output() .map_err(|e| XmlGitInfoError::io_error(e, "git log (timestamp)"))?; let timestamp = if timestamp.status.success() { String::from_utf8_lossy(×tamp.stdout).trim().to_owned() } else { return Err(XmlGitInfoError::CommandFailed { msg: "git log (timestamp)", stderr: String::from_utf8_lossy(×tamp.stderr).into_owned(), }); }; let sha = process::Command::new("git") .args(["rev-parse", "HEAD"]) .current_dir(xml_path.parent().unwrap()) .output() .map_err(|e| XmlGitInfoError::io_error(e, "git rev-parse (sha)"))?; let git_sha = if sha.status.success() { String::from_utf8_lossy(&sha.stdout).trim().to_owned() } else { return Err(XmlGitInfoError::CommandFailed { msg: "git rev-parse (sha)", stderr: String::from_utf8_lossy(&sha.stderr).into_owned(), }); }; Ok(XmlGitInfo { describe, commit_timestamp: timestamp, sha: git_sha, }) } /// 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, format!("out path `{}` is not empty", out_path.display()), )); } 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(()) }