refactor(g3-core): reduce lib.rs complexity by extracting utilities

- Extract truncate_to_word_boundary() to utils.rs with tests
- Consolidate duplicate detection: use streaming::are_tool_calls_duplicate()
  instead of inline closures (eliminates code-path aliasing)
- Remove unused regex import
- Remove wrapper methods format_duration/format_timing_footer that just
  delegated to streaming module - call streaming::* directly

Reduces lib.rs from 2945 to 2897 lines (-48 lines, -1.6%)
All 159+ g3-core tests pass.

Agent: fowler
This commit is contained in:
Dhanji R. Prasanna
2026-01-12 09:47:47 +05:30
parent 6c17f269d7
commit 8df044ac13
2 changed files with 71 additions and 55 deletions

View File

@@ -1,6 +1,7 @@
//! Utility functions for diff parsing, shell escaping, and JSON fixing.
//!
//! This module contains helper functions used by the agent for:
//! - String truncation utilities
//! - Applying unified diffs to strings
//! - Shell command escaping
//! - JSON quote fixing
@@ -8,6 +9,42 @@
use anyhow::Result;
use tracing::debug;
/// Truncate a string to approximately max_len characters, ending at a word boundary.
///
/// This function attempts to break at a space character for cleaner display.
/// If no suitable word boundary is found (or it would result in too short a string),
/// it falls back to character-based truncation.
///
/// # Arguments
/// * `s` - The string to truncate
/// * `max_len` - Maximum number of characters (approximate)
///
/// # Returns
/// The truncated string with "..." appended if truncation occurred
pub fn truncate_to_word_boundary(s: &str, max_len: usize) -> String {
let char_count = s.chars().count();
if char_count <= max_len {
return s.to_string();
}
// Get the byte index of the max_len-th character
let byte_index: usize = s.char_indices()
.nth(max_len)
.map(|(i, _)| i)
.unwrap_or(s.len());
// Find the last space before the character limit
let truncated = &s[..byte_index];
if let Some(last_space_byte) = truncated.rfind(' ') {
if truncated[..last_space_byte].chars().count() > max_len / 2 {
// Only use word boundary if it's not too short (in characters)
return format!("{}...", &s[..last_space_byte]);
}
}
// Fall back to truncation at character boundary
format!("{}...", truncated)
}
/// Normalize Unicode space characters in a file path to regular ASCII spaces.
///
/// macOS uses special Unicode space characters in certain filenames:
@@ -604,4 +641,31 @@ mod tests {
let cmd = "cat \"/etc/hosts\"";
assert_eq!(resolve_paths_in_shell_command(cmd), cmd);
}
#[test]
fn truncate_to_word_boundary_short_string_unchanged() {
assert_eq!(truncate_to_word_boundary("hello", 10), "hello");
assert_eq!(truncate_to_word_boundary("hello world", 20), "hello world");
}
#[test]
fn truncate_to_word_boundary_breaks_at_space() {
// Should break at word boundary
let result = truncate_to_word_boundary("hello world this is a long string", 15);
assert_eq!(result, "hello world...");
}
#[test]
fn truncate_to_word_boundary_falls_back_to_char_limit() {
// When word boundary would be too short, fall back to char limit
let result = truncate_to_word_boundary("a verylongwordwithoutspaces", 10);
assert_eq!(result, "a verylong...");
}
#[test]
fn truncate_to_word_boundary_handles_unicode() {
// Should handle unicode characters correctly
let result = truncate_to_word_boundary("héllo wörld this is long", 12);
assert!(result.ends_with("..."));
}
}