1use crate::error::{Result, SearchError};
2use serde_json::Value;
3use std::fs;
4use std::path::{Path, PathBuf};
5
6use super::translation::TranslationEntry;
7
8pub struct JsonParser;
10
11impl JsonParser {
12 pub fn parse_file(path: &Path) -> Result<Vec<TranslationEntry>> {
13 let content = fs::read_to_string(path).map_err(|e| {
14 SearchError::json_parse_error(path, format!("Failed to read file: {}", e))
15 })?;
16
17 let root: Value = serde_json::from_str(&content).map_err(|e| {
18 SearchError::json_parse_error(path, format!("Invalid JSON syntax: {}", e))
19 })?;
20
21 let mut entries = Vec::new();
22 Self::flatten_json(&root, String::new(), path, &mut entries);
23
24 Ok(entries)
25 }
26
27 fn flatten_json(
28 value: &Value,
29 prefix: String,
30 file_path: &Path,
31 entries: &mut Vec<TranslationEntry>,
32 ) {
33 match value {
34 Value::Object(map) => {
35 for (key, val) in map {
36 let new_prefix = if prefix.is_empty() {
37 key.clone()
38 } else {
39 format!("{}.{}", prefix, key)
40 };
41
42 Self::flatten_json(val, new_prefix, file_path, entries);
43 }
44 }
45 Value::String(s) => {
46 entries.push(TranslationEntry {
54 key: prefix,
55 value: s.clone(),
56 line: 0, file: PathBuf::from(file_path),
58 });
59 }
60 Value::Number(n) => {
61 entries.push(TranslationEntry {
62 key: prefix,
63 value: n.to_string(),
64 line: 0,
65 file: PathBuf::from(file_path),
66 });
67 }
68 Value::Bool(b) => {
69 entries.push(TranslationEntry {
70 key: prefix,
71 value: b.to_string(),
72 line: 0,
73 file: PathBuf::from(file_path),
74 });
75 }
76 Value::Array(arr) => {
77 for (index, val) in arr.iter().enumerate() {
78 let new_prefix = if prefix.is_empty() {
79 index.to_string()
80 } else {
81 format!("{}.{}", prefix, index)
82 };
83 Self::flatten_json(val, new_prefix, file_path, entries);
84 }
85 }
86 _ => {
87 }
89 }
90 }
91}
92
93#[cfg(test)]
94mod tests {
95 use super::*;
96 use std::io::Write;
97 use tempfile::NamedTempFile;
98
99 #[test]
100 fn test_parse_simple_json() {
101 let mut file = NamedTempFile::new().unwrap();
102 write!(file, r#"{{"key": "value"}}"#).unwrap();
103
104 let entries = JsonParser::parse_file(file.path()).unwrap();
105 assert_eq!(entries.len(), 1);
106 assert_eq!(entries[0].key, "key");
107 assert_eq!(entries[0].value, "value");
108 }
109
110 #[test]
111 fn test_parse_nested_json() {
112 let mut file = NamedTempFile::new().unwrap();
113 write!(file, r#"{{"parent": {{"child": "value"}}}}"#).unwrap();
114
115 let entries = JsonParser::parse_file(file.path()).unwrap();
116 assert_eq!(entries.len(), 1);
117 assert_eq!(entries[0].key, "parent.child");
118 assert_eq!(entries[0].value, "value");
119 }
120
121 #[test]
122 fn test_parse_json_array() {
123 let mut file = NamedTempFile::new().unwrap();
124 write!(file, r#"{{"list": ["item1", "item2"]}}"#).unwrap();
125
126 let entries = JsonParser::parse_file(file.path()).unwrap();
127 assert_eq!(entries.len(), 2);
128
129 let item1 = entries.iter().find(|e| e.value == "item1").unwrap();
131 assert_eq!(item1.key, "list.0");
132
133 let item2 = entries.iter().find(|e| e.value == "item2").unwrap();
135 assert_eq!(item2.key, "list.1");
136 }
137}