use crate::core::config::CompressionLevel;
use super::rules_canonical::{END_MARK, RULES_VERSION, START_MARK, Wrapper, render};
#[derive(Debug)]
pub struct RulesFile<'a> {
content: &'a str,
start: Option<usize>,
end: Option<usize>,
version: usize,
}
fn parse_version_number(s: &str) -> Option<usize> {
let prefix = "<!-- version: ";
let vs = s.find(prefix)?;
let num_start = vs + prefix.len();
let end = s[num_start..].find(" -->")?;
s[num_start..num_start + end].parse().ok()
}
impl<'a> RulesFile<'a> {
pub fn parse(content: &'a str) -> Self {
let start = content.find(START_MARK);
let version = start
.and_then(|s| parse_version_number(&content[s + START_MARK.len()..]))
.unwrap_or(0);
let end = content.find(END_MARK);
RulesFile {
content,
start,
end,
version,
}
}
pub fn has_content(&self) -> bool {
self.start.is_some()
}
pub fn version(&self) -> usize {
self.version
}
pub fn is_current(&self) -> bool {
self.version >= RULES_VERSION
}
pub fn prefix(&self) -> &'a str {
self.start.map_or("", |s| self.content[..s].trim())
}
pub fn suffix(&self) -> &'a str {
self.end
.map_or("", |e| self.content[e + END_MARK.len()..].trim())
}
fn block(&self) -> Option<&'a str> {
match (self.start, self.end) {
(Some(s), Some(e)) if e >= s => Some(&self.content[s..e + END_MARK.len()]),
_ => None,
}
}
pub fn block_matches_render(
&self,
shadow: bool,
wrapper: Wrapper,
level: CompressionLevel,
tool_profile: &super::tool_profiles::ToolProfile,
) -> bool {
match self.block() {
Some(block) => block.trim() == render(shadow, wrapper, level, tool_profile).trim(),
None => false,
}
}
pub fn merged(
&self,
shadow: bool,
wrapper: Wrapper,
level: CompressionLevel,
tool_profile: &super::tool_profiles::ToolProfile,
) -> String {
let fresh = render(shadow, wrapper, level, tool_profile);
if self.start.is_some() {
let before = self.prefix();
let after = self.suffix();
let mut out = String::new();
if !before.is_empty() {
out.push_str(before);
out.push('\n');
out.push('\n');
}
out.push_str(&fresh);
if !after.is_empty() {
out.push('\n');
out.push('\n');
out.push_str(after);
}
if !out.ends_with('\n') {
out.push('\n');
}
out
} else {
let trimmed = self.content.trim_end();
let mut out = trimmed.to_string();
if !out.is_empty() {
out.push('\n');
out.push('\n');
}
out.push_str(&fresh);
out
}
}
pub fn initial(
shadow: bool,
wrapper: Wrapper,
level: CompressionLevel,
tool_profile: &super::tool_profiles::ToolProfile,
) -> String {
render(shadow, wrapper, level, tool_profile)
}
pub fn without_section(&self) -> String {
if let Some(start_pos) = self.start {
let before = self.content[..start_pos].trim();
let after = self.suffix();
let mut out = String::new();
if !before.is_empty() {
out.push_str(before);
out.push('\n');
}
if !after.is_empty() {
out.push('\n');
out.push_str(after);
}
out
} else {
self.content.to_string()
}
}
}