///|
/// Serializes a LogEntry to a JSON string.
pub fn entry_to_json_string(entry : LogEntry) -> String {
json_formatter()(entry)
}
///|
/// Deserializes a JSON string into a LogEntry, returning None on failure.
pub fn entry_from_json(json : String) -> LogEntry? {
let ts = extract_json_field_int64(json, "timestamp")
let level_str = extract_json_field_str(json, "level")
let msg = extract_json_field_str(json, "message")
let mod_name = extract_json_field_str(json, "module")
let file = extract_json_field_str(json, "file")
let line = extract_json_field_int(json, "line")
let thread = extract_json_field_int(json, "thread_id")
let seq = extract_json_field_int64(json, "sequence")
match (ts, level_str, msg) {
(Some(t), Some(l), Some(m)) => {
let level = Level::from_string(l)
let mut builder = LogEntryBuilder::new(level, m).with_timestamp(t)
match mod_name {
Some(mn) => if mn.length() > 0 { builder = builder.with_module(mn) }
None => ()
}
match file {
Some(f) => {
let ln = match line {
Some(v) => v
None => 0
}
builder = builder.with_location(f, ln)
}
None => ()
}
let tid = match thread {
Some(v) => v
None => 0
}
builder = builder.with_thread(tid)
let sq = match seq {
Some(v) => v
None => t
}
builder = builder.with_sequence(sq)
Some(builder.build())
}
_ => None
}
}
// Extracts an Int64 field from a JSON string by field name.
///|
fn extract_json_field_int64(json : String, name : String) -> Int64? {
let pattern = "\"" + name + "\":"
let pos = json.find(pattern[:])
match pos {
Some(p) => {
let start = p + pattern.length()
let mut end = start
let chars = json.iter()
let mut idx = 0
for c in chars {
if idx >= start {
if (c >= '0' && c <= '9') || c == '-' {
end = idx + 1
} else if idx > start {
break
}
}
idx = idx + 1
}
if end > start {
let num_str = json[start:end].to_owned()
Some(parse_int64(num_str))
} else {
None
}
}
None => None
}
}
// Extracts an Int field from a JSON string by field name.
///|
fn extract_json_field_int(json : String, name : String) -> Int? {
let pattern = "\"" + name + "\":"
let pos = json.find(pattern[:])
match pos {
Some(p) => {
let start = p + pattern.length()
let mut end = start
let chars = json.iter()
let mut idx = 0
for c in chars {
if idx >= start {
if (c >= '0' && c <= '9') || c == '-' {
end = idx + 1
} else if idx > start {
break
}
}
idx = idx + 1
}
if end > start {
let num_str = json[start:end].to_owned()
Some(parse_int(num_str))
} else {
None
}
}
None => None
}
}
// Extracts a string field from a JSON string by field name.
///|
fn extract_json_field_str(json : String, name : String) -> String? {
let pattern = "\"" + name + "\":\""
let pos = json.find(pattern[:])
match pos {
Some(p) => {
let start = p + pattern.length()
let chars = json.iter()
let mut end = start
let mut escaped = false
let mut idx = 0
for c in chars {
if idx >= start {
if escaped {
escaped = false
} else if c == '\\' {
escaped = true
} else if c == '"' {
end = idx
break
}
}
idx = idx + 1
}
if end > start {
Some(json[start:end].to_owned())
} else {
None
}
}
None => None
}
}
// Parses a decimal string to an Int.
///|
fn parse_int(s : String) -> Int {
let mut val = 0
let mut neg = false
let chars = s.iter()
let mut idx = 0
for c in chars {
if idx == 0 && c == '-' {
neg = true
idx = idx + 1
continue
}
val = val * 10 + (c.to_int() - 48)
idx = idx + 1
}
if neg {
-val
} else {
val
}
}
// Parses a decimal string to an Int64.
///|
fn parse_int64(s : String) -> Int64 {
let mut val = 0L
let mut neg = false
let chars = s.iter()
let mut idx = 0
for c in chars {
if idx == 0 && c == '-' {
neg = true
idx = idx + 1
continue
}
val = val * 10L + (c.to_int() - 48).to_int64()
idx = idx + 1
}
if neg {
-val
} else {
val
}
}
///|
/// Returns true if the entry contains a field with the given key.
pub fn entry_has_field(entry : LogEntry, key : String) -> Bool {
let n = entry.fields.length()
let mut i = 0
while i < n {
let (k, _) = entry.fields[i]
if k == key {
return true
}
i = i + 1
}
false
}
///|
/// Returns the value of a field by key, or None if not found.
pub fn entry_get_field(entry : LogEntry, key : String) -> String? {
let n = entry.fields.length()
let mut i = 0
while i < n {
let (k, v) = entry.fields[i]
if k == key {
return Some(v)
}
i = i + 1
}
None
}
///|
/// Merges two log entries, combining their fields (base fields first, then other).
pub fn entry_merge(base : LogEntry, other : LogEntry) -> LogEntry {
let all_fields = base.fields + other.fields
{ ..base, fields: all_fields }
}
///|
/// Returns a copy of the entry with additional fields appended.
pub fn entry_with_fields(
entry : LogEntry,
extras : Array[(String, String)],
) -> LogEntry {
{ ..entry, fields: entry.fields + extras }
}
///|
/// Serializes an array of LogEntries to a JSON array string.
pub fn entries_to_json_array(entries : Array[LogEntry]) -> String {
let sb = StringBuilder()
sb.write_string("[")
let n = entries.length()
let mut i = 0
while i < n {
if i > 0 {
sb.write_string(",")
}
sb.write_string(entry_to_json_string(entries[i]))
i = i + 1
}
sb.write_string("]")
sb.to_string()
}
///|
/// Sorts an array of LogEntries by timestamp in ascending order.
pub fn entries_sort_by_timestamp(entries : Array[LogEntry]) -> Array[LogEntry] {
let n = entries.length()
let result = entries
let mut i = 0
while i < n {
let mut j = i + 1
while j < n {
if result[j].timestamp < result[i].timestamp {
let tmp = result[i]
result[i] = result[j]
result[j] = tmp
}
j = j + 1
}
i = i + 1
}
result
}
///|
/// Filters entries using a predicate function, returning matching entries.
pub fn entries_filter(
entries : Array[LogEntry],
predicate : (LogEntry) -> Bool,
) -> Array[LogEntry] {
let result : Array[LogEntry] = []
for e in entries {
if predicate(e) {
result.push(e)
}
}
result
}
///|
/// Returns the number of entries in the array.
pub fn entries_count(entries : Array[LogEntry]) -> Int {
entries.length()
}