pjson_rs/domain/services/
priority_service.rs1use crate::domain::{
4 DomainResult,
5 value_objects::{JsonPath, Priority},
6};
7use serde_json::Value as JsonValue;
9use std::collections::HashMap;
10
11#[derive(Debug, Clone)]
13pub struct PriorityService {
14 field_rules: HashMap<String, Priority>,
16 path_rules: HashMap<String, Priority>,
18 type_rules: HashMap<String, Priority>,
20 default_priority: Priority,
22}
23
24impl PriorityService {
25 pub fn new() -> Self {
27 let mut service = Self {
28 field_rules: HashMap::new(),
29 path_rules: HashMap::new(),
30 type_rules: HashMap::new(),
31 default_priority: Priority::MEDIUM,
32 };
33
34 service.add_default_rules();
36 service
37 }
38
39 pub fn with_default_priority(default_priority: Priority) -> Self {
41 let mut service = Self::new();
42 service.default_priority = default_priority;
43 service
44 }
45
46 pub fn add_field_rule(&mut self, field_name: String, priority: Priority) {
48 self.field_rules.insert(field_name, priority);
49 }
50
51 pub fn add_path_rule(&mut self, path_pattern: String, priority: Priority) {
53 self.path_rules.insert(path_pattern, priority);
54 }
55
56 pub fn add_type_rule(&mut self, type_name: String, priority: Priority) {
58 self.type_rules.insert(type_name, priority);
59 }
60
61 pub fn calculate_priority(&self, path: &JsonPath, value: &JsonValue) -> Priority {
63 if let Some(&priority) = self.path_rules.get(path.as_str()) {
65 return priority;
66 }
67
68 for (pattern, &priority) in &self.path_rules {
70 if self.path_matches_pattern(path.as_str(), pattern) {
71 return priority;
72 }
73 }
74
75 if let Some(field_name) = self.extract_field_name(path)
77 && let Some(&priority) = self.field_rules.get(&field_name)
78 {
79 return priority;
80 }
81
82 let type_name = self.get_value_type_name(value);
84 if let Some(&priority) = self.type_rules.get(type_name) {
85 return priority;
86 }
87
88 self.apply_heuristic_priority(path, value)
90 }
91
92 pub fn calculate_priorities(
94 &self,
95 data: &JsonValue,
96 ) -> DomainResult<HashMap<JsonPath, Priority>> {
97 let mut priorities = HashMap::new();
98 self.calculate_priorities_recursive(data, &JsonPath::root(), &mut priorities)?;
99 Ok(priorities)
100 }
101
102 pub fn optimize_rules(&mut self, usage_stats: &UsageStatistics) {
104 for (field, access_count) in &usage_stats.field_access_counts {
106 if *access_count > usage_stats.average_access_count * 2 {
107 let current = self
108 .field_rules
109 .get(field)
110 .copied()
111 .unwrap_or(self.default_priority);
112 let optimized = current.increase_by(10);
113 self.field_rules.insert(field.clone(), optimized);
114 }
115 }
116
117 for (field, access_count) in &usage_stats.field_access_counts {
119 if *access_count < usage_stats.average_access_count / 3 {
120 let current = self
121 .field_rules
122 .get(field)
123 .copied()
124 .unwrap_or(self.default_priority);
125 let optimized = current.decrease_by(5);
126 self.field_rules.insert(field.clone(), optimized);
127 }
128 }
129 }
130
131 pub fn get_rules_summary(&self) -> PriorityRulesSummary {
133 PriorityRulesSummary {
134 field_rule_count: self.field_rules.len(),
135 path_rule_count: self.path_rules.len(),
136 type_rule_count: self.type_rules.len(),
137 default_priority: self.default_priority,
138 }
139 }
140
141 fn add_default_rules(&mut self) {
143 self.add_field_rule("id".to_string(), Priority::CRITICAL);
145 self.add_field_rule("uuid".to_string(), Priority::CRITICAL);
146 self.add_field_rule("status".to_string(), Priority::CRITICAL);
147 self.add_field_rule("state".to_string(), Priority::CRITICAL);
148 self.add_field_rule("error".to_string(), Priority::CRITICAL);
149
150 self.add_field_rule("name".to_string(), Priority::HIGH);
152 self.add_field_rule("title".to_string(), Priority::HIGH);
153 self.add_field_rule("label".to_string(), Priority::HIGH);
154 self.add_field_rule("description".to_string(), Priority::HIGH);
155 self.add_field_rule("message".to_string(), Priority::HIGH);
156
157 self.add_field_rule("content".to_string(), Priority::MEDIUM);
159 self.add_field_rule("body".to_string(), Priority::MEDIUM);
160 self.add_field_rule("value".to_string(), Priority::MEDIUM);
161 self.add_field_rule("data".to_string(), Priority::MEDIUM);
162
163 self.add_field_rule("created_at".to_string(), Priority::LOW);
165 self.add_field_rule("updated_at".to_string(), Priority::LOW);
166 self.add_field_rule("version".to_string(), Priority::LOW);
167 self.add_field_rule("metadata".to_string(), Priority::LOW);
168
169 self.add_field_rule("analytics".to_string(), Priority::BACKGROUND);
171 self.add_field_rule("debug".to_string(), Priority::BACKGROUND);
172 self.add_field_rule("trace".to_string(), Priority::BACKGROUND);
173 self.add_field_rule("logs".to_string(), Priority::BACKGROUND);
174
175 self.add_path_rule("$.error".to_string(), Priority::CRITICAL);
177 self.add_path_rule("$.*.id".to_string(), Priority::CRITICAL);
178 self.add_path_rule("$.users[*].id".to_string(), Priority::CRITICAL);
179 self.add_path_rule("$.*.analytics".to_string(), Priority::BACKGROUND);
180
181 self.add_type_rule("array_large".to_string(), Priority::LOW); self.add_type_rule("string_long".to_string(), Priority::LOW); self.add_type_rule("object_deep".to_string(), Priority::LOW); }
186
187 fn path_matches_pattern(&self, path: &str, pattern: &str) -> bool {
189 if pattern.contains('*') {
191 let parts: Vec<&str> = pattern.split('*').collect();
192 if parts.is_empty() {
193 return true;
194 }
195
196 let mut pos = 0;
197 for (i, part) in parts.iter().enumerate() {
198 if part.is_empty() {
199 continue;
200 }
201
202 if i == 0 {
203 if !path.starts_with(part) {
205 return false;
206 }
207 pos = part.len();
208 } else if i == parts.len() - 1 {
209 if !path[pos..].ends_with(part) {
211 return false;
212 }
213 } else {
214 if let Some(found) = path[pos..].find(part) {
216 pos += found + part.len();
217 } else {
218 return false;
219 }
220 }
221 }
222 true
223 } else {
224 path == pattern
225 }
226 }
227
228 fn extract_field_name(&self, path: &JsonPath) -> Option<String> {
230 if let Some(segment) = path.last_segment() {
231 match segment {
232 crate::domain::value_objects::PathSegment::Key(key) => Some(key),
233 _ => None,
234 }
235 } else {
236 None
237 }
238 }
239
240 fn get_value_type_name(&self, value: &JsonValue) -> &'static str {
242 match value {
243 JsonValue::Null => "null",
244 JsonValue::Bool(_) => "boolean",
245 JsonValue::Number(_) => "number",
246 JsonValue::String(s) => {
247 if s.len() > 1000 {
248 "string_long"
249 } else {
250 "string"
251 }
252 }
253 JsonValue::Array(arr) => {
254 if arr.len() > 100 {
255 "array_large"
256 } else {
257 "array"
258 }
259 }
260 JsonValue::Object(obj) => {
261 if obj.len() > 20 {
262 "object_large"
263 } else {
264 "object"
265 }
266 }
267 }
268 }
269
270 fn apply_heuristic_priority(&self, path: &JsonPath, value: &JsonValue) -> Priority {
272 let mut priority = self.default_priority;
273
274 let depth = path.depth();
276 if depth == 1 {
277 priority = priority.increase_by(20);
278 } else if depth == 2 {
279 priority = priority.increase_by(10);
280 } else if depth > 5 {
281 priority = priority.decrease_by(10);
282 }
283
284 match value {
286 JsonValue::String(s) => {
287 if s.len() < 50 {
288 priority = priority.increase_by(5);
290 }
291 }
292 JsonValue::Array(arr) => {
293 if arr.len() > 10 {
294 priority = priority.decrease_by(15);
296 }
297 }
298 JsonValue::Object(obj) => {
299 if obj.len() > 10 {
300 priority = priority.decrease_by(10);
302 }
303 }
304 _ => {}
305 }
306
307 priority
308 }
309
310 fn calculate_priorities_recursive(
312 &self,
313 value: &JsonValue,
314 current_path: &JsonPath,
315 priorities: &mut HashMap<JsonPath, Priority>,
316 ) -> DomainResult<()> {
317 let priority = self.calculate_priority(current_path, value);
319 priorities.insert(current_path.clone(), priority);
320
321 match value {
323 JsonValue::Object(obj) => {
324 for (key, child_value) in obj.iter() {
325 let child_path = current_path.append_key(key)?;
326 self.calculate_priorities_recursive(child_value, &child_path, priorities)?;
327 }
328 }
329 JsonValue::Array(arr) => {
330 for (index, child_value) in arr.iter().enumerate() {
331 let child_path = current_path.append_index(index);
332 self.calculate_priorities_recursive(child_value, &child_path, priorities)?;
333 }
334 }
335 _ => {
336 }
338 }
339
340 Ok(())
341 }
342}
343
344impl Default for PriorityService {
345 fn default() -> Self {
346 Self::new()
347 }
348}
349
350#[derive(Debug, Clone)]
352pub struct UsageStatistics {
353 pub field_access_counts: HashMap<String, u64>,
354 pub total_accesses: u64,
355 pub average_access_count: u64,
356}
357
358#[derive(Debug, Clone)]
360pub struct PriorityRulesSummary {
361 pub field_rule_count: usize,
362 pub path_rule_count: usize,
363 pub type_rule_count: usize,
364 pub default_priority: Priority,
365}
366
367#[cfg(test)]
368mod tests {
369 use super::*;
370
371 #[test]
372 fn test_default_priority_rules() {
373 let service = PriorityService::new();
374 let path = JsonPath::new("$.id").unwrap();
375 let value = JsonValue::String("123".to_string());
376
377 let priority = service.calculate_priority(&path, &value);
378 assert_eq!(priority, Priority::CRITICAL);
379 }
380
381 #[test]
382 fn test_path_pattern_matching() {
383 let service = PriorityService::new();
384
385 assert!(service.path_matches_pattern("$.users[0].id", "$.*.id"));
387 assert!(service.path_matches_pattern("$.posts[1].id", "$.*.id"));
388 assert!(!service.path_matches_pattern("$.users.name", "$.*.id"));
389 }
390
391 #[test]
392 fn test_custom_rules() {
393 let mut service = PriorityService::new();
394 service.add_field_rule("custom_field".to_string(), Priority::HIGH);
395
396 let path = JsonPath::new("$.custom_field").unwrap();
397 let value = JsonValue::String("test".to_string());
398
399 let priority = service.calculate_priority(&path, &value);
400 assert_eq!(priority, Priority::HIGH);
401 }
402
403 #[test]
404 fn test_heuristic_priority() {
405 let service = PriorityService::new();
406
407 let shallow = JsonPath::new("$.name").unwrap();
409 let deep = JsonPath::new("$.user.profile.settings.theme.color").unwrap();
410 let value = JsonValue::String("test".to_string());
411
412 let shallow_priority = service.calculate_priority(&shallow, &value);
413 let deep_priority = service.calculate_priority(&deep, &value);
414
415 assert!(shallow_priority > deep_priority);
416 }
417
418 #[test]
419 fn test_calculate_all_priorities() {
420 let service = PriorityService::new();
421 let data = serde_json::json!({
422 "id": 123,
423 "name": "Test",
424 "details": {
425 "description": "A test object",
426 "metadata": {
427 "created_at": "2023-01-01"
428 }
429 }
430 });
431
432 let priorities = service.calculate_priorities(&data).unwrap();
433
434 assert!(!priorities.is_empty());
436
437 let id_path = JsonPath::new("$.id").unwrap();
439 assert_eq!(priorities[&id_path], Priority::CRITICAL);
440
441 let name_path = JsonPath::new("$.name").unwrap();
443 assert_eq!(priorities[&name_path], Priority::HIGH);
444 }
445}