Skip to main content

cache_demo/
cache_demo.rs

1/// Demonstration of ParsedSchemaCache for schema reuse
2///
3/// This example shows how to:
4/// 1. Create a cache instance (or use the global one)
5/// 2. Parse and cache schemas with custom keys
6/// 3. Retrieve and reuse cached schemas
7/// 4. Manage cache lifecycle (clear, remove)
8/// 5. Use cache for high-performance multi-evaluation scenarios
9use json_eval_rs::{JSONEval, ParsedSchema, ParsedSchemaCache, PARSED_SCHEMA_CACHE};
10use std::sync::Arc;
11use std::time::Instant;
12
13fn main() -> Result<(), Box<dyn std::error::Error>> {
14    println!("๐Ÿš€ ParsedSchemaCache Demo\n");
15
16    // Example 1: Using a local cache instance
17    demo_local_cache()?;
18
19    println!("\n{}\n", "=".repeat(60));
20
21    // Example 2: Using the global cache instance
22    demo_global_cache()?;
23
24    println!("\n{}\n", "=".repeat(60));
25
26    // Example 3: Performance comparison
27    demo_performance_comparison()?;
28
29    println!("\n{}\n", "=".repeat(60));
30
31    // Example 4: get_or_insert_with pattern
32    demo_lazy_insertion()?;
33
34    Ok(())
35}
36
37fn demo_local_cache() -> Result<(), Box<dyn std::error::Error>> {
38    println!("๐Ÿ“ฆ Example 1: Local Cache Instance");
39    println!("Creating a dedicated cache for this application...\n");
40
41    let cache = ParsedSchemaCache::new();
42
43    // Simple schema
44    let schema_json = r#"{
45        "$params": {
46            "rate": { "type": "number" }
47        },
48        "result": {
49            "type": "number",
50            "title": "Calculated Result",
51            "$evaluation": {
52                "logic": { "*": [{"var": "$rate"}, 100] }
53            }
54        }
55    }"#;
56
57    // Parse and cache with a custom key
58    println!("๐Ÿ“ Parsing schema and caching with key 'calculation-v1'...");
59    let parsed = ParsedSchema::parse(schema_json)?;
60    cache.insert("calculation-v1".to_string(), Arc::new(parsed));
61
62    println!("โœ… Schema cached successfully");
63    println!("   Cache size: {} entries", cache.len());
64    println!("   Keys: {:?}\n", cache.keys());
65
66    // Retrieve and use cached schema
67    println!("๐Ÿ” Retrieving cached schema...");
68    if let Some(cached_schema) = cache.get("calculation-v1") {
69        println!("โœ… Retrieved from cache");
70
71        // Create JSONEval from cached ParsedSchema
72        let mut eval = JSONEval::with_parsed_schema(cached_schema, Some(r#"{"rate": 1.5}"#), None)?;
73        eval.evaluate("{}", None, None, None)?;
74
75        let evaluated = eval.get_evaluated_schema(false);
76        let result = evaluated
77            .pointer("/result")
78            .and_then(|v| v.as_f64())
79            .unwrap_or(0.0);
80        println!("   Evaluation result: {}\n", result);
81    }
82
83    // Check cache stats
84    let stats = cache.stats();
85    println!("๐Ÿ“Š Cache Statistics: {}", stats);
86
87    // Remove entry
88    println!("\n๐Ÿ—‘๏ธ  Removing 'calculation-v1' from cache...");
89    cache.remove("calculation-v1");
90    println!("   Cache size after removal: {}", cache.len());
91
92    Ok(())
93}
94
95fn demo_global_cache() -> Result<(), Box<dyn std::error::Error>> {
96    println!("๐ŸŒ Example 2: Global Cache Instance");
97    println!("Using the built-in PARSED_SCHEMA_CACHE...\n");
98
99    let schema_json = r#"{
100        "$params": {
101            "x": { "type": "number" },
102            "y": { "type": "number" }
103        },
104        "sum": {
105            "type": "number",
106            "$evaluation": { "+": [{"var": "$x"}, {"var": "$y"}] }
107        }
108    }"#;
109
110    // Use global cache
111    println!("๐Ÿ“ Caching schema globally with key 'math-operations'...");
112    let parsed = ParsedSchema::parse(schema_json)?;
113    PARSED_SCHEMA_CACHE.insert("math-operations".to_string(), Arc::new(parsed));
114
115    println!("โœ… Schema cached globally");
116    println!("   Global cache size: {}\n", PARSED_SCHEMA_CACHE.len());
117
118    // Access from anywhere in the application
119    simulate_another_function()?;
120
121    // Clean up
122    println!("\n๐Ÿงน Clearing global cache...");
123    PARSED_SCHEMA_CACHE.clear();
124    println!("   Global cache size: {}", PARSED_SCHEMA_CACHE.len());
125
126    Ok(())
127}
128
129fn simulate_another_function() -> Result<(), Box<dyn std::error::Error>> {
130    println!("๐Ÿ”„ In another function, accessing global cache...");
131
132    if let Some(cached) = PARSED_SCHEMA_CACHE.get("math-operations") {
133        println!("โœ… Retrieved schema from global cache");
134
135        let mut eval = JSONEval::with_parsed_schema(cached, Some(r#"{"x": 10, "y": 20}"#), None)?;
136        eval.evaluate("{}", None, None, None)?;
137
138        let evaluated = eval.get_evaluated_schema(false);
139        let sum = evaluated
140            .pointer("/sum")
141            .and_then(|v| v.as_f64())
142            .unwrap_or(0.0);
143        println!("   Result: {}", sum);
144    }
145
146    Ok(())
147}
148
149fn demo_performance_comparison() -> Result<(), Box<dyn std::error::Error>> {
150    println!("โšก Example 3: Performance Comparison");
151    println!("Comparing cached vs non-cached schema usage...\n");
152
153    let schema_json = r#"{
154        "$params": {
155            "value": { "type": "number" }
156        },
157        "doubled": {
158            "type": "number",
159            "$evaluation": { "*": [{"var": "$value"}, 2] }
160        },
161        "tripled": {
162            "type": "number",
163            "$evaluation": { "*": [{"var": "$value"}, 3] }
164        }
165    }"#;
166
167    let iterations = 100;
168
169    // WITHOUT CACHE: Parse schema every time
170    println!("๐ŸŒ Without cache (parse + evaluate each time):");
171    let start = Instant::now();
172    for i in 0..iterations {
173        let context = format!(r#"{{"value": {}}}"#, i);
174        let mut eval = JSONEval::new(schema_json, Some(&context), None)?;
175        eval.evaluate("{}", None, None, None)?;
176    }
177    let without_cache = start.elapsed();
178    println!("   Time: {:?}", without_cache);
179    println!("   Avg per iteration: {:?}\n", without_cache / iterations);
180
181    // WITH CACHE: Parse once, evaluate many times
182    println!("๐Ÿš€ With cache (parse once, reuse for all evaluations):");
183    let cache = ParsedSchemaCache::new();
184
185    // Parse once
186    let parse_start = Instant::now();
187    let parsed = ParsedSchema::parse(schema_json)?;
188    cache.insert("perf-test".to_string(), Arc::new(parsed));
189    let parse_time = parse_start.elapsed();
190
191    // Evaluate many times
192    let eval_start = Instant::now();
193    for i in 0..iterations {
194        if let Some(cached) = cache.get("perf-test") {
195            let context = format!(r#"{{"value": {}}}"#, i);
196            let mut eval = JSONEval::with_parsed_schema(cached.clone(), Some(&context), None)?;
197            eval.evaluate("{}", None, None, None)?;
198        }
199    }
200    let eval_time = eval_start.elapsed();
201    let with_cache = parse_time + eval_time;
202
203    println!("   Parse time: {:?}", parse_time);
204    println!("   Eval time: {:?}", eval_time);
205    println!("   Total time: {:?}", with_cache);
206    println!("   Avg per iteration: {:?}\n", eval_time / iterations);
207
208    let speedup = without_cache.as_secs_f64() / with_cache.as_secs_f64();
209    println!("๐Ÿ“ˆ Speedup: {:.2}x faster", speedup);
210
211    Ok(())
212}
213
214fn demo_lazy_insertion() -> Result<(), Box<dyn std::error::Error>> {
215    println!("๐Ÿ”„ Example 4: Lazy Insertion with get_or_insert_with");
216    println!("Parse only if not already cached...\n");
217
218    let cache = ParsedSchemaCache::new();
219
220    let schema_json = r#"{
221        "$params": {
222            "name": { "type": "string" }
223        },
224        "greeting": {
225            "type": "string",
226            "$evaluation": {
227                "cat": ["Hello, ", {"var": "$name"}, "!"]
228            }
229        }
230    }"#;
231
232    // First access: will parse
233    println!("๐Ÿ“ First access (will parse)...");
234    let start = Instant::now();
235    let schema1 = cache.get_or_insert_with("greeting-schema", || {
236        println!("   โš™๏ธ  Parsing schema...");
237        Arc::new(ParsedSchema::parse(schema_json).unwrap())
238    });
239    println!("   Time: {:?}\n", start.elapsed());
240
241    // Second access: will use cached
242    println!("๐Ÿ” Second access (will use cache)...");
243    let start = Instant::now();
244    let schema2 = cache.get_or_insert_with("greeting-schema", || {
245        println!("   โš™๏ธ  Parsing schema...");
246        Arc::new(ParsedSchema::parse(schema_json).unwrap())
247    });
248    println!("   Time: {:?}", start.elapsed());
249
250    // Verify they're the same Arc (pointer equality)
251    println!("\nโœ… Both accesses returned the same cached instance");
252    println!("   Same pointer: {}", Arc::ptr_eq(&schema1, &schema2));
253
254    Ok(())
255}