sqlite_graphrag/
output.rs1use crate::errors::AppError;
2use serde::Serialize;
3
4#[derive(Debug, Clone, Copy, clap::ValueEnum, Default)]
5pub enum OutputFormat {
6 #[default]
7 Json,
8 Text,
9 Markdown,
10}
11
12pub fn emit_json<T: Serialize>(value: &T) -> Result<(), AppError> {
13 let json = serde_json::to_string_pretty(value)?;
14 println!("{json}");
15 Ok(())
16}
17
18pub fn emit_json_compact<T: Serialize>(value: &T) -> Result<(), AppError> {
19 let json = serde_json::to_string(value)?;
20 println!("{json}");
21 Ok(())
22}
23
24pub fn emit_text(msg: &str) {
25 println!("{msg}");
26}
27
28pub fn emit_progress(msg: &str) {
29 tracing::info!(message = msg);
30}
31
32pub fn emit_progress_i18n(en: &str, pt: &str) {
35 use crate::i18n::{current, Language};
36 match current() {
37 Language::English => tracing::info!(message = en),
38 Language::Portugues => tracing::info!(message = pt),
39 }
40}
41
42#[derive(Serialize)]
75pub struct RememberResponse {
76 pub memory_id: i64,
77 pub name: String,
78 pub namespace: String,
79 pub action: String,
80 pub operation: String,
82 pub version: i64,
83 pub entities_persisted: usize,
84 pub relationships_persisted: usize,
85 pub chunks_created: usize,
86 pub merged_into_memory_id: Option<i64>,
87 pub warnings: Vec<String>,
88 pub created_at: i64,
90 pub created_at_iso: String,
92 pub elapsed_ms: u64,
94}
95
96#[derive(Serialize, Clone)]
125pub struct RecallItem {
126 pub memory_id: i64,
127 pub name: String,
128 pub namespace: String,
129 #[serde(rename = "type")]
130 pub memory_type: String,
131 pub description: String,
132 pub snippet: String,
133 pub distance: f32,
134 pub source: String,
135}
136
137#[derive(Serialize)]
138pub struct RecallResponse {
139 pub query: String,
140 pub k: usize,
141 pub direct_matches: Vec<RecallItem>,
142 pub graph_matches: Vec<RecallItem>,
143 pub results: Vec<RecallItem>,
145 pub elapsed_ms: u64,
147}
148
149#[cfg(test)]
150mod tests {
151 use super::*;
152 use serde::Serialize;
153
154 #[derive(Serialize)]
155 struct Dummy {
156 val: u32,
157 }
158
159 struct NotSerializable;
161 impl Serialize for NotSerializable {
162 fn serialize<S: serde::Serializer>(&self, _: S) -> Result<S::Ok, S::Error> {
163 Err(serde::ser::Error::custom(
164 "falha intencional de serialização",
165 ))
166 }
167 }
168
169 #[test]
170 fn emit_json_retorna_ok_para_valor_valido() {
171 let v = Dummy { val: 42 };
172 assert!(emit_json(&v).is_ok());
173 }
174
175 #[test]
176 fn emit_json_retorna_erro_para_valor_nao_serializavel() {
177 let v = NotSerializable;
178 assert!(emit_json(&v).is_err());
179 }
180
181 #[test]
182 fn emit_json_compact_retorna_ok_para_valor_valido() {
183 let v = Dummy { val: 7 };
184 assert!(emit_json_compact(&v).is_ok());
185 }
186
187 #[test]
188 fn emit_json_compact_retorna_erro_para_valor_nao_serializavel() {
189 let v = NotSerializable;
190 assert!(emit_json_compact(&v).is_err());
191 }
192
193 #[test]
194 fn emit_text_nao_entra_em_panico() {
195 emit_text("mensagem de teste");
196 }
197
198 #[test]
199 fn emit_progress_nao_entra_em_panico() {
200 emit_progress("progresso de teste");
201 }
202
203 #[test]
204 fn remember_response_serializa_corretamente() {
205 let r = RememberResponse {
206 memory_id: 1,
207 name: "teste".to_string(),
208 namespace: "ns".to_string(),
209 action: "created".to_string(),
210 operation: "created".to_string(),
211 version: 1,
212 entities_persisted: 2,
213 relationships_persisted: 3,
214 chunks_created: 4,
215 merged_into_memory_id: None,
216 warnings: vec!["aviso".to_string()],
217 created_at: 1776569715,
218 created_at_iso: "2026-04-19T03:34:15Z".to_string(),
219 elapsed_ms: 123,
220 };
221 let json = serde_json::to_string(&r).unwrap();
222 assert!(json.contains("memory_id"));
223 assert!(json.contains("aviso"));
224 assert!(json.contains("\"namespace\""));
225 assert!(json.contains("\"merged_into_memory_id\""));
226 assert!(json.contains("\"operation\""));
227 assert!(json.contains("\"created_at\""));
228 assert!(json.contains("\"created_at_iso\""));
229 assert!(json.contains("\"elapsed_ms\""));
230 }
231
232 #[test]
233 fn recall_item_serializa_campo_type_renomeado() {
234 let item = RecallItem {
235 memory_id: 10,
236 name: "entidade".to_string(),
237 namespace: "ns".to_string(),
238 memory_type: "entity".to_string(),
239 description: "desc".to_string(),
240 snippet: "trecho".to_string(),
241 distance: 0.5,
242 source: "db".to_string(),
243 };
244 let json = serde_json::to_string(&item).unwrap();
245 assert!(json.contains("\"type\""));
246 assert!(!json.contains("memory_type"));
247 }
248
249 #[test]
250 fn recall_response_serializa_com_listas() {
251 let resp = RecallResponse {
252 query: "busca".to_string(),
253 k: 10,
254 direct_matches: vec![],
255 graph_matches: vec![],
256 results: vec![],
257 elapsed_ms: 42,
258 };
259 let json = serde_json::to_string(&resp).unwrap();
260 assert!(json.contains("direct_matches"));
261 assert!(json.contains("graph_matches"));
262 assert!(json.contains("\"k\":"));
263 assert!(json.contains("\"results\""));
264 assert!(json.contains("\"elapsed_ms\""));
265 }
266
267 #[test]
268 fn output_format_default_eh_json() {
269 let fmt = OutputFormat::default();
270 assert!(matches!(fmt, OutputFormat::Json));
271 }
272
273 #[test]
274 fn output_format_variantes_existem() {
275 let _text = OutputFormat::Text;
276 let _md = OutputFormat::Markdown;
277 let _json = OutputFormat::Json;
278 }
279
280 #[test]
281 fn recall_item_clone_produz_valor_igual() {
282 let item = RecallItem {
283 memory_id: 99,
284 name: "clone".to_string(),
285 namespace: "ns".to_string(),
286 memory_type: "relation".to_string(),
287 description: "d".to_string(),
288 snippet: "s".to_string(),
289 distance: 0.1,
290 source: "src".to_string(),
291 };
292 let cloned = item.clone();
293 assert_eq!(cloned.memory_id, item.memory_id);
294 assert_eq!(cloned.name, item.name);
295 }
296}