pub struct CommandInfo {
pub name: &'static str,
pub usage: &'static str,
pub description: &'static str,
}
pub const COMMANDS: &[CommandInfo] = &[
CommandInfo {
name: "filter",
usage: "filter [--field] [-l] [--fg <color>] [--bg <color>] <pattern>",
description: "Add an include filter. With --field, pattern is key=value scoped to a parsed field. e.g. filter --fg Red error, filter --field level=error, filter --field component=auth",
},
CommandInfo {
name: "exclude",
usage: "exclude [--field] <pattern>",
description: "Add an exclude filter. With --field, pattern is key=value scoped to a parsed field. e.g. exclude debug, exclude --field level=debug",
},
CommandInfo {
name: "set-color",
usage: "set-color [-l] [--fg <color>] [--bg <color>]",
description: "Set color for the selected filter. -l colors the whole line. e.g. set-color --fg Green, set-color --fg [0,255,0]",
},
CommandInfo {
name: "export-marked",
usage: "export-marked <path>",
description: "Export marked logs to a file. e.g. export-marked /tmp/marked.log",
},
CommandInfo {
name: "save",
usage: "save <path>",
description: "Save visible (filtered) lines to a file in raw format. e.g. save /tmp/visible.log",
},
CommandInfo {
name: "save-filters",
usage: "save-filters <path>",
description: "Save current filters to a file. e.g. save-filters filters.json",
},
CommandInfo {
name: "load-filters",
usage: "load-filters <path>",
description: "Load filters from a file. e.g. load-filters filters.json",
},
CommandInfo {
name: "wrap",
usage: "wrap",
description: "Toggle line wrapping on/off",
},
CommandInfo {
name: "set-theme",
usage: "set-theme <name>",
description: "Change the color theme. e.g. set-theme dracula",
},
CommandInfo {
name: "level-colors",
usage: "level-colors",
description: "Toggle ERROR/WARN log level color highlighting on/off",
},
CommandInfo {
name: "open",
usage: "open <path>",
description: "Open a file in a new tab. e.g. open /var/log/syslog",
},
CommandInfo {
name: "close-tab",
usage: "close-tab",
description: "Close the current tab (quits if last tab)",
},
CommandInfo {
name: "line-numbers",
usage: "line-numbers",
description: "Toggle line numbers on/off",
},
CommandInfo {
name: "clear-filters",
usage: "clear-filters",
description: "Remove all filter definitions",
},
CommandInfo {
name: "disable-filters",
usage: "disable-filters",
description: "Disable all filters without removing them",
},
CommandInfo {
name: "enable-filters",
usage: "enable-filters",
description: "Enable all disabled filters",
},
CommandInfo {
name: "filtering",
usage: "filtering",
description: "Toggle global filtering on/off (bypass all filters)",
},
CommandInfo {
name: "hide-field",
usage: "hide-field <name|index>",
description: "Hide a field by name or 0-based index. e.g. hide-field level or hide-field 0",
},
CommandInfo {
name: "show-field",
usage: "show-field <name>",
description: "Show a previously hidden field. e.g. show-field level",
},
CommandInfo {
name: "show-all-fields",
usage: "show-all-fields",
description: "Clear all hidden fields and show all fields",
},
CommandInfo {
name: "select-fields",
usage: "select-fields",
description: "Open a modal to select which fields to display and their order",
},
CommandInfo {
name: "docker",
usage: "docker",
description: "List running Docker containers and stream logs from the selected one",
},
CommandInfo {
name: "value-colors",
usage: "value-colors",
description: "Toggle value-based color coding (HTTP methods, status codes, IPs, UUIDs)",
},
CommandInfo {
name: "export",
usage: "export [-t <template>] <path>",
description: "Export analysis (comments + marked lines) to a file. -t sets the template (default: markdown). e.g. export /tmp/report.md",
},
CommandInfo {
name: "date-filter",
usage: "date-filter <expression>",
description: "Filter lines by timestamp. e.g. date-filter 01:00 .. 02:00, date-filter > 2024-02-22, date-filter >= Feb 21",
},
CommandInfo {
name: "tail",
usage: "tail",
description: "Toggle tail mode — when on, always scrolls to the last line as new content arrives",
},
CommandInfo {
name: "show-keys",
usage: "show-keys",
description: "Show field keys alongside values in structured log display (e.g. method=GET instead of GET)",
},
CommandInfo {
name: "hide-keys",
usage: "hide-keys",
description: "Show only values in structured log display, hiding field keys (default)",
},
CommandInfo {
name: "raw",
usage: "raw",
description: "Toggle raw mode — disables the format parser and shows unformatted log lines",
},
CommandInfo {
name: "stop",
usage: "stop",
description: "Stop all incoming data for the current tab (file watcher and/or stream)",
},
CommandInfo {
name: "pause",
usage: "pause",
description: "Pause applying incoming data to the view (watcher/stream keeps running in the background)",
},
CommandInfo {
name: "resume",
usage: "resume",
description: "Resume applying incoming data after a pause",
},
CommandInfo {
name: "reset",
usage: "reset",
description: "Restore all settings to defaults and clear all persisted state",
},
CommandInfo {
name: "dlt",
usage: "dlt",
description: "Show configured DLT devices and connect to one",
},
CommandInfo {
name: "otel",
usage: "otel [--http] [port]",
description: "Start an OTel collector receiver. Default: gRPC on port 4317. Use --http for HTTP/JSON on port 4318. e.g. otel, otel 4317, otel --http, otel --http 4318",
},
CommandInfo {
name: "enable-mcp",
usage: "enable-mcp [--port <port>]",
description: "Start the embedded MCP server (default port 9876). e.g. enable-mcp --port 8080",
},
CommandInfo {
name: "disable-mcp",
usage: "disable-mcp",
description: "Stop the embedded MCP server",
},
CommandInfo {
name: "run",
usage: "run <program> [args...]",
description: "Execute a command and stream its output to a new tab. Stderr lines are shown as errors. e.g. run docker logs -f mycontainer, run tail -f /var/log/syslog",
},
CommandInfo {
name: "sidebar-position",
usage: "sidebar-position <left|right>",
description: "Move the filter sidebar to the left or right of the log panel. e.g. sidebar-position left",
},
];
pub const FILE_PATH_COMMANDS: &[&str] = &[
"open",
"load-filters",
"save-filters",
"export-marked",
"export",
"save",
];
pub fn command_names() -> Vec<&'static str> {
COMMANDS.iter().map(|c| c.name).collect()
}
pub fn find_matching_command(input: &str) -> Option<&'static CommandInfo> {
let trimmed = input.trim();
if trimmed.is_empty() {
return None;
}
let cmd_word = trimmed.split_whitespace().next().unwrap_or("");
COMMANDS.iter().find(|c| c.name == cmd_word)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_command_names_returns_all_commands() {
let names = command_names();
assert_eq!(names.len(), COMMANDS.len());
}
#[test]
fn test_command_names_contains_known_commands() {
let names = command_names();
for expected in &[
"filter",
"exclude",
"set-color",
"wrap",
"set-theme",
"level-colors",
"open",
"close-tab",
"line-numbers",
"export-marked",
"save-filters",
"load-filters",
"reset",
] {
assert!(names.contains(expected), "missing command: {expected}");
}
}
#[test]
fn test_find_matching_command_exact() {
let cmd = find_matching_command("filter").unwrap();
assert_eq!(cmd.name, "filter");
}
#[test]
fn test_find_matching_command_with_args() {
let cmd = find_matching_command("filter --fg Red error").unwrap();
assert_eq!(cmd.name, "filter");
}
#[test]
fn test_find_matching_command_with_leading_spaces() {
let cmd = find_matching_command(" wrap ").unwrap();
assert_eq!(cmd.name, "wrap");
}
#[test]
fn test_find_matching_command_empty_returns_none() {
assert!(find_matching_command("").is_none());
assert!(find_matching_command(" ").is_none());
}
#[test]
fn test_find_matching_command_unknown_returns_none() {
assert!(find_matching_command("unknown-cmd").is_none());
}
#[test]
fn test_find_matching_command_partial_prefix_returns_none() {
assert!(find_matching_command("fil").is_none());
}
#[test]
fn test_find_matching_command_usage_and_description_populated() {
let cmd = find_matching_command("filter").unwrap();
assert!(!cmd.usage.is_empty());
assert!(!cmd.description.is_empty());
}
}