pub mod advanced;
use crate::error::repair::{RepairAction, RepairType};
use rustc_hash::FxHashMap;
pub use advanced::{
AdvancedJsonRepairer, RepairConfidence, RepairHistory, RepairHistoryEntry, RepairPreview,
RepairStrategy, TypeCoercionRules,
};
pub struct JsonRepairer {
max_repairs: usize,
repair_cache: FxHashMap<String, (String, Vec<RepairAction>)>,
cache_enabled: bool,
}
impl JsonRepairer {
pub fn new(max_repairs: usize) -> Self {
Self {
max_repairs,
repair_cache: FxHashMap::default(),
cache_enabled: true,
}
}
pub fn new_without_cache(max_repairs: usize) -> Self {
Self {
max_repairs,
repair_cache: FxHashMap::default(),
cache_enabled: false,
}
}
pub fn clear_cache(&mut self) {
self.repair_cache.clear();
}
pub fn cache_size(&self) -> usize {
self.repair_cache.len()
}
pub fn repair(&mut self, input: &str) -> Result<(String, Vec<RepairAction>), String> {
if self.cache_enabled {
if let Some((cached_result, cached_repairs)) = self.repair_cache.get(input) {
return Ok((cached_result.clone(), cached_repairs.clone()));
}
}
let mut repairs = Vec::new();
let mut repaired = input.to_string();
let balance = self.analyze_bracket_balance(&repaired);
if balance.is_balanced() {
let result = (repaired.clone(), repairs.clone());
if self.cache_enabled {
self.repair_cache.insert(input.to_string(), result.clone());
}
return Ok(result);
}
if let Some(fixed) = self.fix_bracket_balance(&repaired, &balance) {
repairs.push(RepairAction {
action_type: RepairType::InsertBracket,
position: repaired.len(),
original: String::new(),
replacement: fixed.chars().skip(repaired.len()).collect(),
description: "Added missing closing brackets".to_string(),
});
repaired = fixed;
}
if repairs.len() > self.max_repairs {
return Err("Maximum repair attempts exceeded".to_string());
}
let result = (repaired, repairs);
if self.cache_enabled && self.repair_cache.len() < 1000 {
self.repair_cache.insert(input.to_string(), result.clone());
}
Ok(result)
}
pub fn repair_with_detailed_tracking(
&mut self,
input: &str,
) -> Result<(String, Vec<RepairAction>), String> {
self.repair(input)
}
fn analyze_bracket_balance(&self, input: &str) -> BracketBalance {
let mut stack = Vec::new();
let mut in_string = false;
let mut escape_next = false;
let mut quote_char = '"';
for ch in input.chars() {
if escape_next {
escape_next = false;
continue;
}
if ch == '\\' {
escape_next = true;
continue;
}
if in_string {
if ch == quote_char {
in_string = false;
}
continue;
}
match ch {
'"' | '\'' => {
in_string = true;
quote_char = ch;
}
'{' => stack.push(BracketType::Brace),
'}' => {
if let Some(BracketType::Brace) = stack.last() {
stack.pop();
}
}
'[' => stack.push(BracketType::Bracket),
']' => {
if let Some(BracketType::Bracket) = stack.last() {
stack.pop();
}
}
_ => {}
}
}
BracketBalance {
unmatched_stack: stack,
}
}
fn fix_bracket_balance(&self, input: &str, balance: &BracketBalance) -> Option<String> {
if balance.unmatched_stack.is_empty() {
return None;
}
let mut result = input.to_string();
for bracket_type in balance.unmatched_stack.iter().rev() {
match bracket_type {
BracketType::Brace => result.push('}'),
BracketType::Bracket => result.push(']'),
}
}
Some(result)
}
}
#[derive(Debug, Clone, PartialEq)]
enum BracketType {
Brace, Bracket, }
#[derive(Debug, Clone)]
struct BracketBalance {
unmatched_stack: Vec<BracketType>,
}
impl BracketBalance {
fn is_balanced(&self) -> bool {
self.unmatched_stack.is_empty()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_balanced_json() {
let mut repairer = JsonRepairer::new(10);
let input = r#"{"key": "value"}"#;
let (repaired, repairs) = repairer.repair(input).unwrap();
assert_eq!(repaired, input);
assert!(repairs.is_empty());
}
#[test]
fn test_missing_closing_brace() {
let mut repairer = JsonRepairer::new(10);
let input = r#"{"key": "value""#;
let (repaired, repairs) = repairer.repair(input).unwrap();
assert_eq!(repaired, r#"{"key": "value"}"#);
assert_eq!(repairs.len(), 1);
assert_eq!(repairs[0].action_type, RepairType::InsertBracket);
}
#[test]
fn test_missing_closing_bracket() {
let mut repairer = JsonRepairer::new(10);
let input = r#"[1, 2, 3"#;
let (repaired, repairs) = repairer.repair(input).unwrap();
assert_eq!(repaired, r#"[1, 2, 3]"#);
assert_eq!(repairs.len(), 1);
assert_eq!(repairs[0].action_type, RepairType::InsertBracket);
}
#[test]
fn test_string_with_brackets() {
let mut repairer = JsonRepairer::new(10);
let input = r#"{"key": "value with ] bracket"}"#;
let (repaired, repairs) = repairer.repair(input).unwrap();
assert_eq!(repaired, input);
assert!(repairs.is_empty());
}
#[test]
fn test_nested_structures() {
let mut repairer = JsonRepairer::new(10);
let input = r#"{"array": [1, 2, {"nested": "value""#;
let (repaired, repairs) = repairer.repair(input).unwrap();
assert_eq!(repaired, r#"{"array": [1, 2, {"nested": "value"}]}"#);
assert_eq!(repairs.len(), 1);
}
#[test]
fn test_repair_caching() {
let mut repairer = JsonRepairer::new(10);
let input = r#"{"key": "value""#;
let (repaired1, repairs1) = repairer.repair(input).unwrap();
assert_eq!(repairer.cache_size(), 1);
let (repaired2, repairs2) = repairer.repair(input).unwrap();
assert_eq!(repaired1, repaired2);
assert_eq!(repairs1.len(), repairs2.len());
assert_eq!(repairer.cache_size(), 1);
}
#[test]
fn test_detailed_repair_tracking() {
let mut repairer = JsonRepairer::new(10);
let input = r#"{"key": "value", "array": [1, 2, 3"#;
let (repaired, repairs) = repairer.repair_with_detailed_tracking(input).unwrap();
assert_eq!(repaired, r#"{"key": "value", "array": [1, 2, 3]}"#);
assert_eq!(repairs.len(), 1);
assert_eq!(repairs[0].action_type, RepairType::InsertBracket);
}
#[test]
fn test_debug_bracket_balance() {
let repairer = JsonRepairer::new(10);
let input1 = r#"{"array": [1, 2, {"nested": "value""#;
let balance1 = repairer.analyze_bracket_balance(input1);
println!("Input1: {input1}");
println!(
"Balance1: braces={}, brackets={}",
balance1
.unmatched_stack
.iter()
.filter(|&x| x == &BracketType::Brace)
.count(),
balance1
.unmatched_stack
.iter()
.filter(|&x| x == &BracketType::Bracket)
.count()
);
let input2 = r#"{"key": "value", "array": [1, 2, 3"#;
let balance2 = repairer.analyze_bracket_balance(input2);
println!("Input2: {input2}");
println!(
"Balance2: braces={}, brackets={}",
balance2
.unmatched_stack
.iter()
.filter(|&x| x == &BracketType::Brace)
.count(),
balance2
.unmatched_stack
.iter()
.filter(|&x| x == &BracketType::Bracket)
.count()
);
assert_eq!(balance1.unmatched_stack.len(), 3);
assert_eq!(
balance1.unmatched_stack,
vec![BracketType::Brace, BracketType::Bracket, BracketType::Brace]
);
assert_eq!(balance2.unmatched_stack.len(), 2);
assert_eq!(
balance2.unmatched_stack,
vec![BracketType::Brace, BracketType::Bracket]
);
}
}