use crate::declare_detector;
use crate::diagnostic::{Action, ActionPlan, Diagnostic, FilesystemAction};
use crate::{FixerError, FixerPreferences, LintianIssue, Visibility};
use debian_workspace::Workspace;
use std::path::{Path, PathBuf};
const TAG_MISSING: char = 'M';
const TAG_OLDER: char = 'O';
fn find_patches_directory(ws: &dyn Workspace) -> Result<Option<PathBuf>, FixerError> {
let makefile = match ws.parsed_rules() {
Ok(m) => m,
Err(debian_workspace::Error::NotFound) => return Ok(None),
Err(e) => return Err(e.into()),
};
Ok(debian_analyzer::patches::rules_find_patches_directory(
&makefile,
))
}
pub fn detect(
ws: &dyn Workspace,
_preferences: &FixerPreferences,
) -> Result<Vec<Diagnostic>, FixerError> {
if ws.read_file(Path::new("debian/debcargo.toml"))?.is_some() {
return Ok(Vec::new());
}
let Some(current_version) = ws.current_version() else {
return Ok(Vec::new());
};
let format_rel = PathBuf::from("debian/source/format");
let orig_format = ws.source_format()?;
if let Some(ref fmt) = orig_format {
if fmt != "1.0" {
return Ok(Vec::new());
}
}
let target_format = if current_version.is_native() {
"3.0 (native)"
} else {
let patches_dir = find_patches_directory(ws)?;
if let Some(ref dir) = patches_dir {
if dir != &PathBuf::from("debian/patches") {
return Ok(Vec::new());
}
}
"3.0 (quilt)"
};
let mut diagnostics: Vec<Diagnostic> = Vec::new();
if orig_format.is_none() {
diagnostics.push(Diagnostic::with_actions(
LintianIssue::source_with_info(
"missing-debian-source-format",
Visibility::Warning,
vec![target_format.to_string()],
),
format!("{}{}", TAG_MISSING, target_format),
"Add missing debian/source/format.",
Vec::new(),
));
}
diagnostics.push(Diagnostic::with_actions(
LintianIssue::source_with_info(
"older-source-format",
Visibility::Info,
vec!["1.0".to_string()],
),
format!("{}{}", TAG_OLDER, target_format),
format!("Upgrade to newer source format {}.", target_format),
Vec::new(),
));
let action = Action::Filesystem(FilesystemAction::Write {
file: format_rel,
content: format!("{}\n", target_format).into_bytes(),
});
diagnostics[0].plans[0].actions.push(action);
Ok(diagnostics)
}
fn describe_aggregate(fixed: &[(Diagnostic, ActionPlan)], _actions: &[Action]) -> String {
let target = fixed
.iter()
.find_map(|(d, _)| {
d.message
.strip_prefix(TAG_OLDER)
.or_else(|| d.message.strip_prefix(TAG_MISSING))
})
.unwrap_or("3.0 (quilt)");
if target == "1.0" {
"Add missing debian/source/format.".to_string()
} else {
format!("Upgrade to newer source format {}.", target)
}
}
declare_detector! {
name: "source-format",
tags: ["missing-debian-source-format", "older-source-format"],
triggers: [
debian_workspace::Trigger::File("debian/debcargo.toml"),
debian_workspace::Trigger::File("debian/source/format"),
debian_workspace::Trigger::File("debian/rules"),
],
detect: |ws, prefs| detect(ws, prefs),
describe: |fixed, actions| describe_aggregate(fixed, actions),
}
#[cfg(test)]
mod tests {
use super::*;
use crate::detector::Detector;
use debversion::Version;
use std::fs;
use tempfile::TempDir;
fn run_apply(base: &Path, version: &Version) -> Result<crate::FixerResult, FixerError> {
let adapter = DetectorImpl;
let ws = debian_workspace::fs_workspace::FsWorkspace::new(
base,
Some("test".into()),
Some(version.clone()),
);
adapter.apply(&ws, &FixerPreferences::default())
}
#[test]
fn test_no_changes_if_already_modern() {
let tmp = TempDir::new().unwrap();
let source_dir = tmp.path().join("debian/source");
fs::create_dir_all(&source_dir).unwrap();
fs::write(source_dir.join("format"), "3.0 (quilt)\n").unwrap();
let v: Version = "1.0-1".parse().unwrap();
assert!(matches!(
run_apply(tmp.path(), &v),
Err(FixerError::NoChanges)
));
}
#[test]
fn test_upgrade_from_1_0_non_native() {
let tmp = TempDir::new().unwrap();
let source_dir = tmp.path().join("debian/source");
fs::create_dir_all(&source_dir).unwrap();
let format_path = source_dir.join("format");
fs::write(&format_path, "1.0\n").unwrap();
let v: Version = "1.0-1".parse().unwrap();
run_apply(tmp.path(), &v).unwrap();
assert_eq!(fs::read_to_string(&format_path).unwrap(), "3.0 (quilt)\n");
}
#[test]
fn test_upgrade_from_1_0_native() {
let tmp = TempDir::new().unwrap();
let source_dir = tmp.path().join("debian/source");
fs::create_dir_all(&source_dir).unwrap();
let format_path = source_dir.join("format");
fs::write(&format_path, "1.0\n").unwrap();
let v: Version = "1.0".parse().unwrap();
run_apply(tmp.path(), &v).unwrap();
assert_eq!(fs::read_to_string(&format_path).unwrap(), "3.0 (native)\n");
}
#[test]
fn test_create_missing_format() {
let tmp = TempDir::new().unwrap();
let debian = tmp.path().join("debian");
fs::create_dir_all(&debian).unwrap();
let v: Version = "1.0-1".parse().unwrap();
let result = run_apply(tmp.path(), &v).unwrap();
let format_path = tmp.path().join("debian/source/format");
assert_eq!(fs::read_to_string(&format_path).unwrap(), "3.0 (quilt)\n");
assert!(result
.description
.contains("Upgrade to newer source format"));
}
#[test]
fn test_skip_debcargo_package() {
let tmp = TempDir::new().unwrap();
let debian = tmp.path().join("debian");
fs::create_dir_all(&debian).unwrap();
fs::write(debian.join("debcargo.toml"), "[package]\n").unwrap();
let v: Version = "1.0-1".parse().unwrap();
assert!(matches!(
run_apply(tmp.path(), &v),
Err(FixerError::NoChanges)
));
}
}