fix: auto-install embedded rulesets for installed binary
This commit is contained in:
@@ -96,6 +96,8 @@ This section is for contributors and anyone building on the project.
|
|||||||
## Rulesets 🧩
|
## Rulesets 🧩
|
||||||
|
|
||||||
Rules are data‑driven and split by domain in `rulesets/*.toml`.
|
Rules are data‑driven and split by domain in `rulesets/*.toml`.
|
||||||
|
When running an installed binary, the first run will auto‑install the default rulesets
|
||||||
|
into the XDG data directory (for example `~/.local/share/vid-repair/rulesets` on Linux).
|
||||||
Before shipping changes:
|
Before shipping changes:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
45
vid-repair-core/src/rules/embedded.rs
Normal file
45
vid-repair-core/src/rules/embedded.rs
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
pub const FILES: &[(&str, &str)] = &[
|
||||||
|
(
|
||||||
|
"containers.toml",
|
||||||
|
include_str!(concat!(
|
||||||
|
env!("CARGO_MANIFEST_DIR"),
|
||||||
|
"/../rulesets/containers.toml"
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"decode.toml",
|
||||||
|
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/../rulesets/decode.toml")),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"probe.toml",
|
||||||
|
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/../rulesets/probe.toml")),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"codecs-video.toml",
|
||||||
|
include_str!(concat!(
|
||||||
|
env!("CARGO_MANIFEST_DIR"),
|
||||||
|
"/../rulesets/codecs-video.toml"
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"codecs-audio.toml",
|
||||||
|
include_str!(concat!(
|
||||||
|
env!("CARGO_MANIFEST_DIR"),
|
||||||
|
"/../rulesets/codecs-audio.toml"
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"transport.toml",
|
||||||
|
include_str!(concat!(
|
||||||
|
env!("CARGO_MANIFEST_DIR"),
|
||||||
|
"/../rulesets/transport.toml"
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"timestamps.toml",
|
||||||
|
include_str!(concat!(
|
||||||
|
env!("CARGO_MANIFEST_DIR"),
|
||||||
|
"/../rulesets/timestamps.toml"
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
];
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use directories::ProjectDirs;
|
||||||
|
use fs_err as fs;
|
||||||
|
|
||||||
use crate::scan::ProbeData;
|
use crate::scan::ProbeData;
|
||||||
|
|
||||||
@@ -8,6 +10,7 @@ mod loader;
|
|||||||
mod lint;
|
mod lint;
|
||||||
mod matcher;
|
mod matcher;
|
||||||
mod model;
|
mod model;
|
||||||
|
mod embedded;
|
||||||
|
|
||||||
use model::Rule;
|
use model::Rule;
|
||||||
|
|
||||||
@@ -22,23 +25,14 @@ pub struct RuleSet {
|
|||||||
|
|
||||||
impl RuleSet {
|
impl RuleSet {
|
||||||
pub fn load() -> Result<Self> {
|
pub fn load() -> Result<Self> {
|
||||||
let mut candidates = Vec::new();
|
if let Some(ruleset) = load_compiled_from_candidates()? {
|
||||||
|
return Ok(ruleset);
|
||||||
if let Ok(current) = std::env::current_dir() {
|
|
||||||
candidates.push(current.join("rulesets"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(exe) = std::env::current_exe() {
|
if let Some(dir) = data_ruleset_dir() {
|
||||||
if let Some(parent) = exe.parent() {
|
install_embedded_rulesets(&dir)?;
|
||||||
candidates.push(parent.join("rulesets"));
|
if let Some(ruleset) = load_compiled_from_candidates()? {
|
||||||
}
|
return Ok(ruleset);
|
||||||
}
|
|
||||||
|
|
||||||
for dir in candidates {
|
|
||||||
let rules = loader::load_rules_from_dir(&dir)?;
|
|
||||||
if !rules.is_empty() {
|
|
||||||
let compiled = loader::compile_rules(rules)?;
|
|
||||||
return Ok(Self { rules: compiled });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,21 +66,13 @@ impl RuleSet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_raw_rules() -> Result<Vec<Rule>> {
|
pub fn load_raw_rules() -> Result<Vec<Rule>> {
|
||||||
let mut candidates = Vec::new();
|
if let Some(rules) = load_raw_from_candidates()? {
|
||||||
|
return Ok(rules);
|
||||||
if let Ok(current) = std::env::current_dir() {
|
|
||||||
candidates.push(current.join("rulesets"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(exe) = std::env::current_exe() {
|
if let Some(dir) = data_ruleset_dir() {
|
||||||
if let Some(parent) = exe.parent() {
|
install_embedded_rulesets(&dir)?;
|
||||||
candidates.push(parent.join("rulesets"));
|
if let Some(rules) = load_raw_from_candidates()? {
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for dir in candidates {
|
|
||||||
let rules = loader::load_rules_from_dir(&dir)?;
|
|
||||||
if !rules.is_empty() {
|
|
||||||
return Ok(rules);
|
return Ok(rules);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -114,17 +100,14 @@ pub fn build_context(probe: &ProbeData) -> RuleContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn ruleset_dir_for_display() -> Result<PathBuf> {
|
pub fn ruleset_dir_for_display() -> Result<PathBuf> {
|
||||||
if let Ok(current) = std::env::current_dir() {
|
for dir in candidate_ruleset_dirs() {
|
||||||
let dir = current.join("rulesets");
|
|
||||||
if dir.exists() {
|
if dir.exists() {
|
||||||
return Ok(dir);
|
return Ok(dir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(exe) = std::env::current_exe() {
|
if let Some(dir) = data_ruleset_dir() {
|
||||||
if let Some(parent) = exe.parent() {
|
return Ok(dir);
|
||||||
return Ok(parent.join("rulesets"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(anyhow::anyhow!("No ruleset directory found"))
|
Err(anyhow::anyhow!("No ruleset directory found"))
|
||||||
@@ -140,3 +123,74 @@ pub fn ensure_ruleset_loaded(ruleset: &RuleSet) -> Result<()> {
|
|||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn candidate_ruleset_dirs() -> Vec<PathBuf> {
|
||||||
|
let mut candidates = Vec::new();
|
||||||
|
|
||||||
|
if let Ok(current) = std::env::current_dir() {
|
||||||
|
candidates.push(current.join("rulesets"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(exe) = std::env::current_exe() {
|
||||||
|
if let Some(parent) = exe.parent() {
|
||||||
|
candidates.push(parent.join("rulesets"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(dir) = data_ruleset_dir() {
|
||||||
|
candidates.push(dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(dir) = config_ruleset_dir() {
|
||||||
|
candidates.push(dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
candidates
|
||||||
|
}
|
||||||
|
|
||||||
|
fn data_ruleset_dir() -> Option<PathBuf> {
|
||||||
|
ProjectDirs::from("cc", "44r0n", "vid-repair").map(|dirs| dirs.data_dir().join("rulesets"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn config_ruleset_dir() -> Option<PathBuf> {
|
||||||
|
ProjectDirs::from("cc", "44r0n", "vid-repair").map(|dirs| dirs.config_dir().join("rulesets"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_compiled_from_candidates() -> Result<Option<RuleSet>> {
|
||||||
|
for dir in candidate_ruleset_dirs() {
|
||||||
|
let rules = loader::load_rules_from_dir(&dir)?;
|
||||||
|
if !rules.is_empty() {
|
||||||
|
let compiled = loader::compile_rules(rules)?;
|
||||||
|
return Ok(Some(RuleSet { rules: compiled }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_raw_from_candidates() -> Result<Option<Vec<Rule>>> {
|
||||||
|
for dir in candidate_ruleset_dirs() {
|
||||||
|
let rules = loader::load_rules_from_dir(&dir)?;
|
||||||
|
if !rules.is_empty() {
|
||||||
|
return Ok(Some(rules));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn install_embedded_rulesets(dir: &std::path::Path) -> Result<()> {
|
||||||
|
if dir.exists() && dir.is_file() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
fs::create_dir_all(dir)?;
|
||||||
|
|
||||||
|
for (name, contents) in embedded::FILES {
|
||||||
|
let path = dir.join(name);
|
||||||
|
if path.exists() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
fs::write(&path, contents)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user