From 6d924522696c4d09a7d7b6e96b1ba7337ea2e53a Mon Sep 17 00:00:00 2001 From: BrettMayson Date: Thu, 31 Oct 2024 18:55:09 -0600 Subject: [PATCH] localization: handle subaddons (#820) * localization: handle subaddons * handle no optionals --- bin/src/commands/localization/sort.rs | 70 +++++++++++-------- bin/src/modules/stringtables/mod.rs | 60 ++++++++-------- .../src/analyze/lints/01_sorted.rs | 26 +++---- libs/stringtable/src/analyze/mod.rs | 4 +- 4 files changed, 87 insertions(+), 73 deletions(-) diff --git a/bin/src/commands/localization/sort.rs b/bin/src/commands/localization/sort.rs index 627030b3..5edfcb20 100644 --- a/bin/src/commands/localization/sort.rs +++ b/bin/src/commands/localization/sort.rs @@ -1,4 +1,4 @@ -use std::io::BufReader; +use std::{io::BufReader, path::PathBuf}; use clap::ArgMatches; use hemtt_stringtable::Project; @@ -10,39 +10,51 @@ pub fn sort(matches: &ArgMatches) -> Result { let only_lang = matches.get_flag("only-lang"); - for addon in ctx.addons() { - let stringtable_path = ctx - .workspace_path() - .join(addon.folder())? - .join("stringtable.xml")?; - if stringtable_path.exists()? { - match Project::from_reader(BufReader::new(stringtable_path.open_file()?)) { - Ok(mut project) => { - if !only_lang { - project.sort(); + for root in ["addons", "optionals"] { + if !ctx.project_folder().join(root).exists() { + continue; + } + let paths: Vec = walkdir::WalkDir::new(ctx.project_folder().join(root)) + .into_iter() + .filter_map(|p| { + p.map(|p| { + if p.file_name() == "stringtable.xml" { + Some(p.path().to_path_buf()) + } else { + None } - let out_path = ctx - .project_folder() - .join(addon.folder_pathbuf()) - .join("stringtable.xml"); - let mut writer = String::new(); - if let Err(e) = project.to_writer(&mut writer) { - error!("Failed to write stringtable for {}", addon.folder()); - error!("{:?}", e); - return Ok(Report::new()); + }) + .ok() + .flatten() + }) + .collect::>(); + for path in paths { + if path.exists() { + let mut file = std::fs::File::open(&path)?; + match Project::from_reader(BufReader::new(&mut file)) { + Ok(mut project) => { + if !only_lang { + project.sort(); + } + let mut writer = String::new(); + if let Err(e) = project.to_writer(&mut writer) { + error!("Failed to write stringtable for {}", path.display()); + error!("{:?}", e); + return Ok(Report::new()); + } + if let Err(e) = std::fs::write(&path, writer) { + error!("Failed to write stringtable for {}", path.display()); + error!("{:?}", e); + return Ok(Report::new()); + } } - if let Err(e) = std::fs::write(out_path, writer) { - error!("Failed to write stringtable for {}", addon.folder()); + Err(e) => { + error!("Failed to read stringtable for {}", path.display()); error!("{:?}", e); return Ok(Report::new()); } - } - Err(e) => { - error!("Failed to read stringtable for {}", addon.folder()); - error!("{:?}", e); - return Ok(Report::new()); - } - }; + }; + } } } Ok(Report::new()) diff --git a/bin/src/modules/stringtables/mod.rs b/bin/src/modules/stringtables/mod.rs index e2d1252c..6069eaf2 100644 --- a/bin/src/modules/stringtables/mod.rs +++ b/bin/src/modules/stringtables/mod.rs @@ -1,10 +1,13 @@ use std::{io::BufReader, sync::Arc}; use hemtt_stringtable::{ - analyze::{lint_addon, lint_addons, lint_check}, + analyze::{lint_all, lint_check, lint_one}, Project, }; -use hemtt_workspace::reporting::{Code, Diagnostic}; +use hemtt_workspace::{ + reporting::{Code, Diagnostic}, + WorkspacePath, +}; use crate::{context::Context, report::Report, Error}; @@ -33,39 +36,38 @@ impl Module for Stringtables { fn pre_build(&self, ctx: &Context) -> Result { let mut report = Report::new(); - let stringtables = ctx - .addons() - .iter() - .filter_map(|addon| { - let path = ctx - .workspace_path() - .join(addon.folder()) - .expect("vfs issue") - .join("stringtable.xml") - .expect("vfs issue"); + let mut stringtables = Vec::new(); + for root in ["addons", "optionals"] { + if !ctx.workspace_path().join(root)?.exists()? { + continue; + } + let paths = ctx + .workspace_path() + .join(root) + .expect("vfs issue") + .walk_dir() + .expect("vfs issue") + .into_iter() + .filter(|p| p.filename() == "stringtable.xml") + .collect::>(); + for path in paths { if path.exists().expect("vfs issue") { let existing = path.read_to_string().expect("vfs issue"); match Project::from_reader(BufReader::new(existing.as_bytes())) { - Ok(project) => Some((project, addon.clone(), existing)), + Ok(project) => stringtables.push((project, path, existing)), Err(e) => { - debug!("Failed to parse stringtable for {}: {}", addon.folder(), e); - report.push(Arc::new(CodeStringtableInvalid::new( - addon.folder(), - e.to_string(), - ))); - None + debug!("Failed to parse stringtable for {}: {}", path, e); + report.push(Arc::new(CodeStringtableInvalid::new(path, e.to_string()))); } } - } else { - None } - }) - .collect::>(); + } + } - report.extend(lint_addons(&stringtables, Some(ctx.config()))); + report.extend(lint_all(&stringtables, Some(ctx.config()))); for stringtable in stringtables { - report.extend(lint_addon(&stringtable, Some(ctx.config()))); + report.extend(lint_one(&stringtable, Some(ctx.config()))); } Ok(report) @@ -74,7 +76,7 @@ impl Module for Stringtables { #[allow(clippy::module_name_repetitions)] pub struct CodeStringtableInvalid { - addon: String, + path: WorkspacePath, reason: String, diagnostic: Option, } @@ -85,7 +87,7 @@ impl Code for CodeStringtableInvalid { } fn message(&self) -> String { - format!("Stringtable in `{}` is invalid", self.addon) + format!("Stringtable at `{}` is invalid", self.path) } fn note(&self) -> Option { @@ -99,9 +101,9 @@ impl Code for CodeStringtableInvalid { impl CodeStringtableInvalid { #[must_use] - pub fn new(addon: String, reason: String) -> Self { + pub fn new(path: WorkspacePath, reason: String) -> Self { Self { - addon, + path, reason, diagnostic: None, } diff --git a/libs/stringtable/src/analyze/lints/01_sorted.rs b/libs/stringtable/src/analyze/lints/01_sorted.rs index 09815f99..eb94eac1 100644 --- a/libs/stringtable/src/analyze/lints/01_sorted.rs +++ b/libs/stringtable/src/analyze/lints/01_sorted.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use hemtt_common::config::LintConfig; -use hemtt_workspace::{addons::Addon, lint::{AnyLintRunner, Lint, LintRunner}, reporting::{Code, Codes, Diagnostic, Severity}}; +use hemtt_workspace::{lint::{AnyLintRunner, Lint, LintRunner}, reporting::{Code, Codes, Diagnostic, Severity}, WorkspacePath}; use crate::{analyze::SqfLintData, Project}; @@ -33,7 +33,7 @@ impl Lint for LintL01Sorted { } } -pub type StringtableData = (Project, Addon, String); +pub type StringtableData = (Project, WorkspacePath, String); pub struct Runner; impl LintRunner for Runner { @@ -49,30 +49,30 @@ impl LintRunner for Runner { let mut unsorted = Vec::new(); let mut codes: Codes = Vec::new(); let only_lang = matches!(config.option("only-lang"), Some(toml::Value::Boolean(true))); - for (project, addon, existing) in target { + for (project, path, existing) in target { let mut project = project.clone(); if !only_lang { project.sort(); } let mut writer = String::new(); if let Err(e) = project.to_writer(&mut writer) { - panic!("Failed to write stringtable for {}: {}", addon.folder(), e); + panic!("Failed to write stringtable for {path}: {e}"); } if &writer != existing { - unsorted.push(addon.folder().to_string()); + unsorted.push(path.as_str().to_string()); } } if unsorted.len() <= 3 { - for addon in unsorted { + for path in unsorted { codes.push(Arc::new(CodeStringtableNotSorted::new( - Unsorted::Addon(addon), + Unsorted::Path(path), only_lang, config.severity(), ))); } } else { codes.push(Arc::new(CodeStringtableNotSorted::new( - Unsorted::Addons(unsorted), + Unsorted::Paths(unsorted), only_lang, config.severity(), ))); @@ -82,8 +82,8 @@ impl LintRunner for Runner { } pub enum Unsorted { - Addon(String), - Addons(Vec), + Path(String), + Paths(Vec), } #[allow(clippy::module_name_repetitions)] @@ -105,9 +105,9 @@ impl Code for CodeStringtableNotSorted { fn message(&self) -> String { match &self.unsorted { - Unsorted::Addon(addon) => format!("Stringtable in `{addon}` is not sorted"), - Unsorted::Addons(addons) => { - format!("Stringtables in {} addons are not sorted", addons.len()) + Unsorted::Path(path) => format!("Stringtable at `{path}` is not sorted"), + Unsorted::Paths(paths) => { + format!("{} stringtables are not sorted", paths.len()) } } } diff --git a/libs/stringtable/src/analyze/mod.rs b/libs/stringtable/src/analyze/mod.rs index 268b48ba..475495e9 100644 --- a/libs/stringtable/src/analyze/mod.rs +++ b/libs/stringtable/src/analyze/mod.rs @@ -10,7 +10,7 @@ lint_manager!(stringtable, vec![]); pub struct SqfLintData {} -pub fn lint_addon(addon: &StringtableData, project: Option<&ProjectConfig>) -> Codes { +pub fn lint_one(addon: &StringtableData, project: Option<&ProjectConfig>) -> Codes { let mut manager = LintManager::new(project.map_or_else(Default::default, |project| { project.lints().stringtables().clone() })); @@ -26,7 +26,7 @@ pub fn lint_addon(addon: &StringtableData, project: Option<&ProjectConfig>) -> C } #[allow(clippy::ptr_arg)] // Needed for &Vec for &dyn Any -pub fn lint_addons(addons: &Vec, project: Option<&ProjectConfig>) -> Codes { +pub fn lint_all(addons: &Vec, project: Option<&ProjectConfig>) -> Codes { let mut manager = LintManager::new(project.map_or_else(Default::default, |project| { project.lints().stringtables().clone() }));