mod render;
mod walk;
use std::path::{Path, PathBuf};
use anyhow::{Context, Result};
use crate::{Classification, classify_source, eject_tests};
pub use render::{render_apply, render_check};
#[derive(Debug, Clone, Copy)]
pub enum OutputFormat {
Text,
Json,
}
pub struct FileResult {
pub path: PathBuf,
pub classification: Classification,
pub test_file: Option<String>,
pub applied: bool,
}
pub struct Report {
pub results: Vec<FileResult>,
}
impl Report {
#[must_use]
pub fn has_inline(&self) -> bool {
self.results
.iter()
.any(|res| res.classification == Classification::Inline)
}
}
pub fn check_path(path: &Path) -> Result<Report> {
let files = walk::collect_rust_files(path)?;
let mut results = Vec::with_capacity(files.len());
for file in files {
let source = std::fs::read_to_string(&file)
.with_context(|| format!("failed to read {}", file.display()))?;
results.push(FileResult {
path: file,
classification: classify_source(&source),
test_file: None,
applied: false,
});
}
Ok(Report { results })
}
pub fn apply_path(path: &Path, dry_run: bool) -> Result<Report> {
if path.is_dir() {
apply_dir(path, dry_run)
} else {
apply_file(path, dry_run)
}
}
fn apply_dir(path: &Path, dry_run: bool) -> Result<Report> {
let files = walk::collect_rust_files(path)?;
let mut results = Vec::with_capacity(files.len());
for file in files {
results.push(eject_one(&file, dry_run)?);
}
Ok(Report { results })
}
fn apply_file(path: &Path, dry_run: bool) -> Result<Report> {
let source = std::fs::read_to_string(path)
.with_context(|| format!("failed to read {}", path.display()))?;
let test_file = write_eject(path, &source, dry_run)?;
Ok(Report {
results: vec![FileResult {
path: path.to_path_buf(),
classification: Classification::Inline,
test_file: Some(test_file),
applied: !dry_run,
}],
})
}
fn eject_one(path: &Path, dry_run: bool) -> Result<FileResult> {
let source = std::fs::read_to_string(path)
.with_context(|| format!("failed to read {}", path.display()))?;
match classify_source(&source) {
Classification::Inline => Ok(FileResult {
path: path.to_path_buf(),
classification: Classification::Inline,
test_file: Some(write_eject(path, &source, dry_run)?),
applied: !dry_run,
}),
classification => Ok(FileResult {
path: path.to_path_buf(),
classification,
test_file: None,
applied: false,
}),
}
}
fn write_eject(path: &Path, source: &str, dry_run: bool) -> Result<String> {
let file_stem = path
.file_stem()
.and_then(|os| os.to_str())
.context("invalid file name")?;
let result = eject_tests(source, file_stem)?;
if !dry_run {
let parent = path.parent().unwrap_or_else(|| Path::new("."));
let test_path = parent.join(&result.test_file_name);
std::fs::write(&test_path, &result.test_content)
.with_context(|| format!("failed to write {}", test_path.display()))?;
std::fs::write(path, &result.modified_source)
.with_context(|| format!("failed to write {}", path.display()))?;
}
Ok(result.test_file_name)
}