Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
mod utils;
pub mod toml_filter;
24 changes: 24 additions & 0 deletions src/toml_filter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,14 @@ pub struct TomlFilterRegistry {
}

impl TomlFilterRegistry {
/// Load only the embedded built-in registry. This is deterministic and does
/// not consult the filesystem or host configuration.
pub fn builtin() -> Result<Self, String> {
Ok(Self {
filters: Self::parse_and_compile(BUILTIN_TOML, "builtin")?,
})
}

/// Load registry from disk + built-in. Emits warnings to stderr on parse
/// errors but never panics — bad files are silently ignored.
fn load() -> Self {
Expand Down Expand Up @@ -210,6 +218,13 @@ impl TomlFilterRegistry {
TomlFilterRegistry { filters }
}

/// Build a registry from TOML content without touching the filesystem.
fn from_toml_str(content: &str, source: &str) -> Result<Self, String> {
Ok(Self {
filters: Self::parse_and_compile(content, source)?,
})
}

fn parse_and_compile(content: &str, source: &str) -> Result<Vec<CompiledFilter>, String> {
let file: TomlFilterFile = toml::from_str(content)
.map_err(|e| format!("TOML parse error in {}: {}", source, e))?;
Expand Down Expand Up @@ -1586,6 +1601,15 @@ match_command = "^make\\b"
);
}

#[test]
fn test_builtin_registry_uses_embedded_filters_only() {
let registry = TomlFilterRegistry::builtin().expect("builtin registry should compile");
let filter = find_filter_in("mix format", &registry.filters).expect("mix format filter");
let output = apply_filter(filter, "");

assert_eq!(output, "mix format: ok");
}

/// Verify that every built-in filter has at least one inline test.
/// Prevents shipping filters with zero test coverage.
#[test]
Expand Down
22 changes: 11 additions & 11 deletions src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use std::process::Command;
/// use rtk::utils::truncate;
/// assert_eq!(truncate("hello world", 8), "hello...");
/// assert_eq!(truncate("hi", 10), "hi");
/// ```
/// ```ignore
pub fn truncate(s: &str, max_len: usize) -> String {
let char_count = s.chars().count();
if char_count <= max_len {
Expand All @@ -39,7 +39,7 @@ pub fn truncate(s: &str, max_len: usize) -> String {
/// * `text` - Text potentially containing ANSI escape codes
///
/// # Examples
/// ```
/// ```ignore
/// use rtk::utils::strip_ansi;
/// let colored = "\x1b[31mError\x1b[0m";
/// assert_eq!(strip_ansi(colored), "Error");
Expand All @@ -61,11 +61,11 @@ pub fn strip_ansi(text: &str) -> String {
/// `(stdout: String, stderr: String, exit_code: i32)`
///
/// # Examples
/// ```no_run
/// ```
/// use rtk::utils::execute_command;
/// let (stdout, stderr, code) = execute_command("echo", &["test"]).unwrap();
/// assert_eq!(code, 0);
/// ```
/// ```ignore
#[allow(dead_code)]
pub fn execute_command(cmd: &str, args: &[&str]) -> Result<(String, String, i32)> {
let output = Command::new(cmd)
Expand All @@ -89,12 +89,12 @@ pub fn execute_command(cmd: &str, args: &[&str]) -> Result<(String, String, i32)
/// Formatted string (e.g. "1.2M", "59.2K", "694")
///
/// # Examples
/// ```
/// ```ignore
/// use rtk::utils::format_tokens;
/// assert_eq!(format_tokens(1_234_567), "1.2M");
/// assert_eq!(format_tokens(59_234), "59.2K");
/// assert_eq!(format_tokens(694), "694");
/// ```
/// ```ignore
pub fn format_tokens(n: usize) -> String {
if n >= 1_000_000 {
format!("{:.1}M", n as f64 / 1_000_000.0)
Expand All @@ -114,13 +114,13 @@ pub fn format_tokens(n: usize) -> String {
/// Formatted string with $ prefix
///
/// # Examples
/// ```
/// ```ignore
/// use rtk::utils::format_usd;
/// assert_eq!(format_usd(1234.567), "$1234.57");
/// assert_eq!(format_usd(12.345), "$12.35");
/// assert_eq!(format_usd(0.123), "$0.12");
/// assert_eq!(format_usd(0.0096), "$0.0096");
/// ```
/// ```ignore
pub fn format_usd(amount: f64) -> String {
if !amount.is_finite() {
return "$0.00".to_string();
Expand All @@ -141,12 +141,12 @@ pub fn format_usd(amount: f64) -> String {
/// Formatted string like "$3.86/MTok"
///
/// # Examples
/// ```
/// ```ignore
/// use rtk::utils::format_cpt;
/// assert_eq!(format_cpt(0.000003), "$3.00/MTok");
/// assert_eq!(format_cpt(0.0000038), "$3.80/MTok");
/// assert_eq!(format_cpt(0.00000386), "$3.86/MTok");
/// ```
/// ```ignore
pub fn format_cpt(cpt: f64) -> String {
if !cpt.is_finite() || cpt <= 0.0 {
return "$0.00/MTok".to_string();
Expand Down Expand Up @@ -210,7 +210,7 @@ pub fn ok_confirmation(action: &str, detail: &str) -> String {
/// Returns "pnpm", "yarn", or "npm" based on lockfile presence.
///
/// # Examples
/// ```no_run
/// ```ignore
/// use rtk::utils::detect_package_manager;
/// let pm = detect_package_manager();
/// // Returns "pnpm" if pnpm-lock.yaml exists, "yarn" if yarn.lock, else "npm"
Expand Down