use std::fs::File;
use std::io::{BufRead, Write};
use std::path::{Path, PathBuf};
use std::{fs, io};
use serde::Deserialize;
use wax::walk::Entry as _;
use super::{Error, Pipe, Result};
#[derive(Debug, Deserialize)]
pub struct Action {
path: PathBuf,
show_warnings: Option<bool>,
clean: Option<bool>,
}
impl Action {
pub async fn execute(&self, pipe: &mut Pipe) -> Result<()> {
super::validate_contained_path(&self.path, "document path")?;
let input = pipe.input().ok_or(Error::NoInputInPipe)?;
if self.path.exists() && self.clean.unwrap_or(true) {
tokio::fs::remove_dir_all(&self.path).await.map_err(|e| {
Error::CouldNotDeleteDirectory {
path: self.path.clone(),
err: e,
}
})?;
}
let glob = wax::Glob::new("**/*.yml").unwrap();
for entry in glob.walk(&input) {
let entry = entry?;
let mut destination =
compute_out_path(input.as_path(), self.path.as_path(), entry.path())?;
build_doc_file(entry.path(), &mut destination).await?;
}
let glob = wax::Glob::new("**/*.md").unwrap();
for entry in glob.walk(&input) {
let entry = entry?;
let destination = compute_out_path(input.as_path(), self.path.as_path(), entry.path())?;
if let Some(dest) = destination.parent() {
fs::create_dir_all(dest).map_err(Error::IntermediateDirectory)?;
}
fs::copy(entry.path(), &destination).map_err(|e| Error::CouldNotCopyFile {
source: entry.into_path(),
destination,
err: e,
})?;
}
if self.show_warnings.unwrap_or(true) {
check_read_me_files(&self.path).map_err(|err| {
Error::io(
err,
format!("Error while browsing {} file", self.path.display()),
)
})?;
}
Ok(())
}
}
fn check_read_me_files(dir: &Path) -> io::Result<()> {
if dir.is_dir() {
for entry in fs::read_dir(dir)? {
let entry = entry?;
let path = entry.path();
if path.is_dir() {
if !path.join("README.md").exists() {
tracing::warn!("path: {} : The README file is missing.", path.display());
}
check_read_me_files(&path)?;
}
}
}
Ok(())
}
async fn build_doc_file(file: &Path, destination: &mut PathBuf) -> Result<()> {
let mut buffer = String::new();
if let Ok(lines) = read_lines(file) {
for line in lines.map_while(Result::ok) {
let line = line.trim_start();
if line.starts_with("##") {
buffer.push('\n');
buffer.push_str(line.strip_prefix("##").unwrap().trim_start());
}
}
}
if !buffer.is_empty() {
let buffer: String = format!("###### Generated Documentation from adnv ######\n{buffer}");
destination.set_extension("md");
create_file(destination.as_path(), &buffer)?;
}
Ok(())
}
fn create_file(path: &Path, content: &str) -> Result<()> {
if let Some(dest) = path.parent() {
fs::create_dir_all(dest).map_err(Error::IntermediateDirectory)?;
}
let mut file = File::create(path).map_err(|e| Error::CouldNotOpenFile {
path: PathBuf::from(path),
err: e,
})?;
file.write_all(content.as_bytes())
.map_err(|e| Error::CouldNotWriteFile {
path: PathBuf::from(path),
err: e,
})?;
Ok(())
}
fn compute_out_path(parent: &Path, root: &Path, file: &Path) -> Result<PathBuf> {
let path = file
.strip_prefix(parent)
.map_err(|e| Error::CouldNotStripPrefix {
path: PathBuf::from(file),
to_strip: PathBuf::from(parent),
err: e,
})?;
Ok(Path::new(root).join(path))
}
fn read_lines(filename: &Path) -> io::Result<io::Lines<io::BufReader<File>>> {
let file = File::open(filename)?;
Ok(io::BufReader::new(file).lines())
}