feat(core): define core types with tests
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -1 +1,193 @@
|
||||
// Task 2 will implement types
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
use std::str::FromStr;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
|
||||
/// 日志级别
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub enum LogLevel {
|
||||
Error,
|
||||
Warn,
|
||||
Info,
|
||||
Debug,
|
||||
Trace,
|
||||
Unknown(String),
|
||||
}
|
||||
|
||||
impl FromStr for LogLevel {
|
||||
type Err = std::convert::Infallible;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s.to_uppercase().as_str() {
|
||||
"ERROR" | "ERR" | "SEVERE" | "FATAL" => Ok(LogLevel::Error),
|
||||
"WARN" | "WARNING" | "WRN" => Ok(LogLevel::Warn),
|
||||
"INFO" | "INFORMATION" => Ok(LogLevel::Info),
|
||||
"DEBUG" | "DBG" => Ok(LogLevel::Debug),
|
||||
"TRACE" | "TRC" => Ok(LogLevel::Trace),
|
||||
_ => Ok(LogLevel::Unknown(s.to_string())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for LogLevel {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
LogLevel::Error => write!(f, "ERROR"),
|
||||
LogLevel::Warn => write!(f, "WARN"),
|
||||
LogLevel::Info => write!(f, "INFO"),
|
||||
LogLevel::Debug => write!(f, "DEBUG"),
|
||||
LogLevel::Trace => write!(f, "TRACE"),
|
||||
LogLevel::Unknown(s) => write!(f, "UNKNOWN({s})"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 一行解析后的日志
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct LogEntry {
|
||||
pub line_number: u64,
|
||||
pub raw_line: String,
|
||||
pub timestamp: Option<String>,
|
||||
pub level: Option<LogLevel>,
|
||||
pub fields: HashMap<String, Value>,
|
||||
}
|
||||
|
||||
/// 搜索匹配结果
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct SearchResult {
|
||||
pub line_number: u64,
|
||||
pub match_start: usize,
|
||||
pub match_end: usize,
|
||||
}
|
||||
|
||||
/// 搜索查询
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum SearchQuery {
|
||||
Regex(String),
|
||||
Keyword(String),
|
||||
}
|
||||
|
||||
/// 搜索过滤器
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct SearchFilter {
|
||||
pub levels: Option<Vec<LogLevel>>,
|
||||
pub time_range: Option<(String, String)>,
|
||||
}
|
||||
|
||||
/// 书签
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Bookmark {
|
||||
pub line_number: u64,
|
||||
pub label: Option<String>,
|
||||
pub created_at: String,
|
||||
}
|
||||
|
||||
/// 打开的文件会话信息
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct FileSession {
|
||||
pub file_path: std::path::PathBuf,
|
||||
pub display_name: String,
|
||||
pub total_lines: u64,
|
||||
pub file_size: u64,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_from_str_error_variants() {
|
||||
assert_eq!("ERROR".parse::<LogLevel>(), Ok(LogLevel::Error));
|
||||
assert_eq!("error".parse::<LogLevel>(), Ok(LogLevel::Error));
|
||||
assert_eq!("ERR".parse::<LogLevel>(), Ok(LogLevel::Error));
|
||||
assert_eq!("SEVERE".parse::<LogLevel>(), Ok(LogLevel::Error));
|
||||
assert_eq!("FATAL".parse::<LogLevel>(), Ok(LogLevel::Error));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_str_warn_variants() {
|
||||
assert_eq!("WARN".parse::<LogLevel>(), Ok(LogLevel::Warn));
|
||||
assert_eq!("warn".parse::<LogLevel>(), Ok(LogLevel::Warn));
|
||||
assert_eq!("WARNING".parse::<LogLevel>(), Ok(LogLevel::Warn));
|
||||
assert_eq!("WRN".parse::<LogLevel>(), Ok(LogLevel::Warn));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_str_info_variants() {
|
||||
assert_eq!("INFO".parse::<LogLevel>(), Ok(LogLevel::Info));
|
||||
assert_eq!("info".parse::<LogLevel>(), Ok(LogLevel::Info));
|
||||
assert_eq!("INFORMATION".parse::<LogLevel>(), Ok(LogLevel::Info));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_str_debug_variants() {
|
||||
assert_eq!("DEBUG".parse::<LogLevel>(), Ok(LogLevel::Debug));
|
||||
assert_eq!("debug".parse::<LogLevel>(), Ok(LogLevel::Debug));
|
||||
assert_eq!("DBG".parse::<LogLevel>(), Ok(LogLevel::Debug));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_str_trace_variants() {
|
||||
assert_eq!("TRACE".parse::<LogLevel>(), Ok(LogLevel::Trace));
|
||||
assert_eq!("trace".parse::<LogLevel>(), Ok(LogLevel::Trace));
|
||||
assert_eq!("TRC".parse::<LogLevel>(), Ok(LogLevel::Trace));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_str_unknown() {
|
||||
assert_eq!(
|
||||
"FOOBAR".parse::<LogLevel>(),
|
||||
Ok(LogLevel::Unknown("FOOBAR".into()))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_display_output() {
|
||||
assert_eq!(LogLevel::Error.to_string(), "ERROR");
|
||||
assert_eq!(LogLevel::Warn.to_string(), "WARN");
|
||||
assert_eq!(LogLevel::Info.to_string(), "INFO");
|
||||
assert_eq!(LogLevel::Debug.to_string(), "DEBUG");
|
||||
assert_eq!(LogLevel::Trace.to_string(), "TRACE");
|
||||
assert_eq!(
|
||||
LogLevel::Unknown("custom".into()).to_string(),
|
||||
"UNKNOWN(custom)"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_log_entry_construction() {
|
||||
let mut fields = HashMap::new();
|
||||
fields.insert("key".to_string(), Value::String("value".to_string()));
|
||||
|
||||
let entry = LogEntry {
|
||||
line_number: 42,
|
||||
raw_line: "test line".to_string(),
|
||||
timestamp: Some("2024-01-01T00:00:00".to_string()),
|
||||
level: Some(LogLevel::Info),
|
||||
fields,
|
||||
};
|
||||
|
||||
assert_eq!(entry.line_number, 42);
|
||||
assert_eq!(entry.raw_line, "test line");
|
||||
assert_eq!(entry.timestamp, Some("2024-01-01T00:00:00".to_string()));
|
||||
assert_eq!(entry.level, Some(LogLevel::Info));
|
||||
assert_eq!(
|
||||
entry.fields.get("key"),
|
||||
Some(&Value::String("value".to_string()))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_search_result_construction() {
|
||||
let result = SearchResult {
|
||||
line_number: 10,
|
||||
match_start: 5,
|
||||
match_end: 9,
|
||||
};
|
||||
assert_eq!(result.line_number, 10);
|
||||
assert_eq!(result.match_start, 5);
|
||||
assert_eq!(result.match_end, 9);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user