1use std::{fs::File, io::BufReader, path::Path};
10
11use anyhow::Context as _;
12
13pub mod backend;
14pub mod common;
15pub mod disagg_router;
16pub mod discovery;
17pub mod engines;
18pub mod gguf;
19pub mod http;
20pub mod hub;
21pub mod kv_router;
23pub mod local_model;
24pub mod mocker;
25pub mod model_card;
26pub mod model_type;
27pub mod preprocessor;
28pub mod protocols;
29pub mod recorder;
30pub mod request_template;
31pub mod tokenizers;
32pub mod tokens;
33pub mod types;
34
35#[cfg(feature = "block-manager")]
36pub mod block_manager;
37
38pub fn file_json_field<T: serde::de::DeserializeOwned>(
55 json_file_path: &Path,
56 field_name: &str,
57) -> anyhow::Result<T> {
58 let file = File::open(json_file_path)
60 .with_context(|| format!("Failed to open file: {:?}", json_file_path))?;
61 let reader = BufReader::new(file);
62
63 let json_data: serde_json::Value = serde_json::from_reader(reader)
67 .with_context(|| format!("Failed to parse JSON from file: {:?}", json_file_path))?;
68
69 let map = json_data.as_object().ok_or_else(|| {
71 anyhow::anyhow!("JSON root is not an object in file: {:?}", json_file_path)
72 })?;
73
74 let field_value = map.get(field_name).ok_or_else(|| {
76 anyhow::anyhow!(
77 "Field '{}' not found in JSON file: {:?}",
78 field_name,
79 json_file_path
80 )
81 })?;
82
83 serde_json::from_value(field_value.clone()).with_context(|| {
86 format!(
87 "Failed to deserialize field '{}' (value: {:?}) to the expected type from file: {:?}",
88 field_name, field_value, json_file_path
89 )
90 })
91}
92
93#[cfg(test)]
94mod file_json_field_tests {
95 use super::file_json_field;
96 use serde::Deserialize;
97 use std::fs::File;
98 use std::io::Write;
99 use std::path::{Path, PathBuf};
100 use tempfile::tempdir;
101
102 fn create_temp_json_file(dir: &Path, file_name: &str, content: &str) -> PathBuf {
104 let file_path = dir.join(file_name);
105 let mut file = File::create(&file_path)
106 .unwrap_or_else(|_| panic!("Failed to create test file: {:?}", file_path));
107 file.write_all(content.as_bytes())
108 .unwrap_or_else(|_| panic!("Failed to write to test file: {:?}", file_path));
109 file_path
110 }
111
112 #[derive(Debug, PartialEq, Deserialize)]
114 struct MyConfig {
115 version: String,
116 enabled: bool,
117 count: u32,
118 }
119
120 #[test]
121 fn test_success_basic() {
122 let tmp_dir = tempdir().unwrap();
123 let file_path = create_temp_json_file(
124 tmp_dir.path(),
125 "test_basic.json",
126 r#"{ "name": "Rust", "age": 30, "is_active": true }"#,
127 );
128
129 let name: String = file_json_field(&file_path, "name").unwrap();
130 assert_eq!(name, "Rust");
131
132 let age: i32 = file_json_field(&file_path, "age").unwrap();
133 assert_eq!(age, 30);
134
135 let is_active: bool = file_json_field(&file_path, "is_active").unwrap();
136 assert!(is_active);
137 }
138
139 #[test]
140 fn test_success_custom_struct_field() {
141 let tmp_dir = tempdir().unwrap();
142 let file_path = create_temp_json_file(
143 tmp_dir.path(),
144 "test_struct.json",
145 r#"{
146 "config": {
147 "version": "1.0.0",
148 "enabled": true,
149 "count": 123
150 },
151 "other_field": "value"
152 }"#,
153 );
154
155 let config: MyConfig = file_json_field(&file_path, "config").unwrap();
156 assert_eq!(
157 config,
158 MyConfig {
159 version: "1.0.0".to_string(),
160 enabled: true,
161 count: 123,
162 }
163 );
164 }
165
166 #[test]
167 fn test_file_not_found() {
168 let tmp_dir = tempdir().unwrap();
169 let non_existent_path = tmp_dir.path().join("non_existent.json");
170
171 let result: anyhow::Result<String> = file_json_field(&non_existent_path, "field");
172 assert!(result.is_err());
173 let err = result.unwrap_err();
174 assert!(err.to_string().contains("Failed to open file"));
175 }
176
177 #[test]
178 fn test_invalid_json_syntax() {
179 let tmp_dir = tempdir().unwrap();
180 let file_path = create_temp_json_file(
181 tmp_dir.path(),
182 "invalid.json",
183 r#"{ "key": "value", "bad_syntax": }"#, );
185
186 let result: anyhow::Result<String> = file_json_field(&file_path, "key");
187 assert!(result.is_err());
188 let err = result.unwrap_err();
189 assert!(err.to_string().contains("Failed to parse JSON from file"));
190 }
191
192 #[test]
193 fn test_json_root_not_object_array() {
194 let tmp_dir = tempdir().unwrap();
195 let file_path = create_temp_json_file(
196 tmp_dir.path(),
197 "root_array.json",
198 r#"[ { "item": 1 }, { "item": 2 } ]"#, );
200
201 let result: anyhow::Result<String> = file_json_field(&file_path, "item");
202 assert!(result.is_err());
203 let err = result.unwrap_err();
204 assert!(err.to_string().contains("JSON root is not an object"));
205 }
206
207 #[test]
208 fn test_json_root_not_object_primitive() {
209 let tmp_dir = tempdir().unwrap();
210 let file_path = create_temp_json_file(
211 tmp_dir.path(),
212 "root_primitive.json",
213 r#""just_a_string""#, );
215
216 let result: anyhow::Result<String> = file_json_field(&file_path, "field");
217 assert!(result.is_err());
218 let err = result.unwrap_err();
219 assert!(err.to_string().contains("JSON root is not an object"));
220 }
221
222 #[test]
223 fn test_field_not_found() {
224 let tmp_dir = tempdir().unwrap();
225 let file_path = create_temp_json_file(
226 tmp_dir.path(),
227 "missing_field.json",
228 r#"{ "existing_field": "hello" }"#,
229 );
230
231 let result: anyhow::Result<String> = file_json_field(&file_path, "non_existent_field");
232 assert!(result.is_err());
233 let err = result.unwrap_err();
234 assert!(err
235 .to_string()
236 .contains("Field 'non_existent_field' not found"));
237 }
238
239 #[test]
240 fn test_field_type_mismatch() {
241 let tmp_dir = tempdir().unwrap();
242 let file_path = create_temp_json_file(
243 tmp_dir.path(),
244 "type_mismatch.json",
245 r#"{ "count": "not_an_integer" }"#,
246 );
247
248 let result: anyhow::Result<u32> = file_json_field(&file_path, "count");
249 assert!(result.is_err());
250 let err = result.unwrap_err();
251 assert!(err
252 .to_string()
253 .contains("Failed to deserialize field 'count'"));
254 }
255
256 #[test]
257 fn test_empty_file() {
258 let tmp_dir = tempdir().unwrap();
259 let file_path = create_temp_json_file(tmp_dir.path(), "empty.json", "");
260
261 let result: anyhow::Result<String> = file_json_field(&file_path, "field");
262 assert!(result.is_err());
263 let err = result.unwrap_err();
264 assert!(err.to_string().contains("Failed to parse JSON from file"));
265 }
266}