steeldb/projectors/
json_proj.rs1use crate::projector::{slug, CorpusKind, Projector, Situation};
9use serde_json::Value;
10use std::path::PathBuf;
11
12pub struct JsonProjector {
13 path: PathBuf,
14}
15
16impl JsonProjector {
17 pub fn open(path: impl Into<PathBuf>) -> JsonProjector {
18 JsonProjector { path: path.into() }
19 }
20}
21
22fn flatten(prefix: &str, v: &Value, out: &mut Vec<String>) {
24 match v {
25 Value::Object(map) => {
26 for (k, child) in map {
27 let key = slug(k);
28 let path = if prefix.is_empty() { key } else { format!("{prefix}/{key}") };
29 flatten(&path, child, out);
30 }
31 }
32 Value::Array(items) => {
33 for item in items {
34 flatten(prefix, item, out);
35 }
36 }
37 Value::String(s) => {
38 let sv = slug(s);
39 if !sv.is_empty() && !prefix.is_empty() {
40 out.push(format!("{prefix}/{sv}"));
41 }
42 }
43 Value::Number(n) => {
44 if !prefix.is_empty() {
45 out.push(format!("{prefix}/{}", slug(&n.to_string())));
46 }
47 }
48 Value::Bool(b) => {
49 if !prefix.is_empty() {
50 out.push(format!("{prefix}/{b}"));
51 }
52 }
53 Value::Null => {}
54 }
55}
56
57fn situation_of(v: &Value) -> Situation {
58 let mut tokens = Vec::new();
59 flatten("", v, &mut tokens);
60 tokens.sort();
61 tokens.dedup();
62 Situation::new(tokens, vec![v.to_string()])
63}
64
65impl Projector for JsonProjector {
66 fn columns(&self) -> Vec<String> {
67 vec!["json".to_string()]
68 }
69 fn kind(&self) -> CorpusKind {
70 CorpusKind::Csv
71 }
72 fn source(&self) -> String {
73 self.path.display().to_string()
74 }
75 fn project(self: Box<Self>, sink: &mut dyn FnMut(Situation)) -> std::io::Result<()> {
76 let text = std::fs::read_to_string(&self.path)?;
77 match serde_json::from_str::<Value>(&text) {
79 Ok(Value::Array(items)) => {
80 for item in &items {
81 sink(situation_of(item));
82 }
83 }
84 Ok(other) => sink(situation_of(&other)),
85 Err(_) => {
86 for line in text.lines() {
87 let line = line.trim();
88 if line.is_empty() {
89 continue;
90 }
91 if let Ok(v) = serde_json::from_str::<Value>(line) {
92 sink(situation_of(&v));
93 }
94 }
95 }
96 }
97 Ok(())
98 }
99}