use crate::markdown_linter_guard::{GuardFailure, run_guarded};
use crate::markdown_linter_config::one_rule_configuration;
use crate::markdown_linter_output::{LintRun, explanation, interpret_embedded_run};
use crate::git_metadata::path_from_git_bytes;
use crate::policy_markdown::{LintRequest, MarkdownLinter};
use crate::wait_progress::Cadence;
use monochromatic_lint::run_embedded::{EmbeddedFailure, EmbeddedRequest, EmbeddedRun, fix_source};
use std::io::Write;
use std::path::{Path, PathBuf};
use std::time::Duration;
pub const LINT_QUIET_WINDOW: Duration = Duration::from_secs(60);
pub type FixFunction = fn(&EmbeddedRequest<'_>) -> Result<EmbeddedRun, EmbeddedFailure>;
pub struct EmbeddedLinter {
fix: FixFunction,
window: Duration,
cadence: Cadence,
notices: Box<dyn Write>,
abandoned: bool,
}
pub fn embedded_linter() -> EmbeddedLinter {
return EmbeddedLinter {
fix: fix_source,
window: LINT_QUIET_WINDOW,
cadence: Cadence::from_environment(),
notices: Box::new(std::io::stderr()),
abandoned: false,
};
}
pub fn embedded_linter_with(fix: FixFunction, window: Duration) -> EmbeddedLinter {
return EmbeddedLinter {
fix,
window,
cadence: Cadence::from_environment(),
notices: Box::new(std::io::stderr()),
abandoned: false,
};
}
const ABANDONED_REASON: &str = "an earlier Markdown check in this command did not finish and cli-git stopped waiting \
for it, so monochromatic-lint is not run again in this command; run the command again, or set the policy \
markdown/autofix to off to skip the check";
fn guard_failure_reason(failure: &GuardFailure) -> String {
match failure {
GuardFailure::Start(reason) => {
return format!("cli-git could not start the thread that runs monochromatic-lint: {reason}");
}
GuardFailure::Panicked(reason) => {
return format!(
"monochromatic-lint stopped with an internal error ({}); the file was not checked, run the \
command again, run monochromatic-lint by hand on the file to see the error, or set the policy \
markdown/autofix to off to skip the check",
explanation(reason.as_str())
);
}
GuardFailure::Quiet(window) => {
return format!(
"monochromatic-lint showed no sign of work for {} milliseconds and cli-git stopped waiting for it \
(its thread stays in this process until the command ends); run the command again, run \
monochromatic-lint by hand on the file to see why it stalls, or set the policy markdown/autofix \
to off to skip the check",
window.as_millis()
);
}
GuardFailure::Vanished => {
return String::from(
"the thread that runs monochromatic-lint ended without a result; this is a defect in cli-git.",
);
}
}
}
impl MarkdownLinter for EmbeddedLinter {
fn lint(&mut self, request: &LintRequest<'_>) -> Result<LintRun, String> {
if self.abandoned {
return Err(String::from(ABANDONED_REASON));
}
let Some(name) = path_from_git_bytes(request.path) else {
return Err(String::from(
"cli-git cannot spell this candidate's pathname for monochromatic-lint on this platform",
));
};
let waiting_for: String = format!("monochromatic-lint to check {}", name.display());
let top_level: PathBuf = request.top_level.to_path_buf();
let configuration: String = one_rule_configuration(
request.options.rules.as_slice(),
request.options.exclude.as_slice(),
);
let source: Vec<u8> = request.source.to_vec();
let fix: FixFunction = self.fix;
let outcome: Result<Result<EmbeddedRun, EmbeddedFailure>, GuardFailure> = run_guarded(
move || {
return fix(&EmbeddedRequest {
top_level: top_level.as_path(),
configuration: configuration.as_str(),
name: Path::new(name.as_os_str()),
source: source.as_slice(),
});
},
self.window,
self.cadence,
waiting_for.as_str(),
&mut self.notices,
);
match outcome {
Ok(finished) => {
return interpret_embedded_run(request.source, request.options.rules.as_slice(), finished);
}
Err(failure) => {
self.abandoned = matches!(failure, GuardFailure::Quiet(_));
return Err(guard_failure_reason(&failure));
}
}
}
}
impl EmbeddedLinter {
pub fn with_notices(mut self, cadence: Cadence, notices: Box<dyn Write>) -> Self {
self.cadence = cadence;
self.notices = notices;
return self;
}
}
#[cfg(test)]
#[path = "markdown_linter_tests.rs"]
mod tests;