Skip to main content

thread_flow/
conversion.rs

1// SPDX-FileCopyrightText: 2025 Knitli Inc. <knitli@knit.li>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4use recoco::base::schema::{
5    BasicValueType, EnrichedValueType, FieldSchema, StructSchema, TableKind, TableSchema, ValueType,
6};
7use recoco::base::value::{BasicValue, FieldValues, ScopeValue, Value};
8
9use std::sync::Arc;
10use thread_services::types::{CallInfo, ImportInfo, ParsedDocument, SymbolInfo};
11
12/// Convert a ParsedDocument to a ReCoco Value
13pub fn serialize_parsed_doc<D: thread_services::types::Doc>(
14    doc: &ParsedDocument<D>,
15) -> Result<Value, recoco::prelude::Error> {
16    // Note: serialize_symbol etc now return ScopeValue.
17    // Value::LTable takes Vec<ScopeValue>.
18
19    // Serialize symbols
20    let symbols = doc
21        .metadata
22        .defined_symbols
23        .values()
24        .map(serialize_symbol)
25        .collect::<Result<Vec<_>, _>>()?;
26
27    // Serialize imports
28    let imports = doc
29        .metadata
30        .imported_symbols
31        .values()
32        .map(serialize_import)
33        .collect::<Result<Vec<_>, _>>()?;
34
35    // Serialize calls
36    let calls = doc
37        .metadata
38        .function_calls
39        .iter()
40        .map(serialize_call)
41        .collect::<Result<Vec<_>, _>>()?;
42
43    // Convert fingerprint to bytes for serialization
44    let fingerprint_bytes = bytes::Bytes::from(doc.content_fingerprint.as_slice().to_vec());
45
46    // Output is a Struct containing LTables and fingerprint.
47    // Value::Struct takes FieldValues. FieldValues takes fields: Vec<Value>.
48    // Value::LTable(symbols) is Value::LTable(Vec<ScopeValue>). This is a Value.
49    // So fields is Vec<Value>. Correct.
50
51    Ok(Value::Struct(FieldValues {
52        fields: vec![
53            Value::LTable(symbols),
54            Value::LTable(imports),
55            Value::LTable(calls),
56            Value::Basic(BasicValue::Bytes(fingerprint_bytes)),
57        ],
58    }))
59}
60
61fn serialize_symbol(info: &SymbolInfo) -> Result<ScopeValue, recoco::prelude::Error> {
62    Ok(ScopeValue(FieldValues {
63        fields: vec![
64            Value::Basic(BasicValue::Str(info.name.clone().into())),
65            Value::Basic(BasicValue::Str(format!("{:?}", info.kind).into())),
66            Value::Basic(BasicValue::Str(info.scope.clone().into())),
67        ],
68    }))
69}
70
71fn serialize_import(info: &ImportInfo) -> Result<ScopeValue, recoco::prelude::Error> {
72    Ok(ScopeValue(FieldValues {
73        fields: vec![
74            Value::Basic(BasicValue::Str(info.symbol_name.clone().into())),
75            Value::Basic(BasicValue::Str(info.source_path.clone().into())),
76            Value::Basic(BasicValue::Str(format!("{:?}", info.import_kind).into())),
77        ],
78    }))
79}
80
81fn serialize_call(info: &CallInfo) -> Result<ScopeValue, recoco::prelude::Error> {
82    Ok(ScopeValue(FieldValues {
83        fields: vec![
84            Value::Basic(BasicValue::Str(info.function_name.clone().into())),
85            Value::Basic(BasicValue::Int64(info.arguments_count as i64)),
86        ],
87    }))
88}
89
90/// Build the schema for the output of ThreadParse
91pub fn get_thread_parse_output_schema() -> EnrichedValueType {
92    EnrichedValueType {
93        typ: ValueType::Struct(StructSchema {
94            fields: Arc::new(vec![
95                FieldSchema::new(
96                    "symbols".to_string(),
97                    EnrichedValueType {
98                        typ: ValueType::Table(TableSchema {
99                            kind: TableKind::LTable,
100                            row: match symbol_type() {
101                                ValueType::Struct(s) => s,
102                                _ => unreachable!(),
103                            },
104                        }),
105                        nullable: false,
106                        attrs: Default::default(),
107                    },
108                ),
109                FieldSchema::new(
110                    "imports".to_string(),
111                    EnrichedValueType {
112                        typ: ValueType::Table(TableSchema {
113                            kind: TableKind::LTable,
114                            row: match import_type() {
115                                ValueType::Struct(s) => s,
116                                _ => unreachable!(),
117                            },
118                        }),
119                        nullable: false,
120                        attrs: Default::default(),
121                    },
122                ),
123                FieldSchema::new(
124                    "calls".to_string(),
125                    EnrichedValueType {
126                        typ: ValueType::Table(TableSchema {
127                            kind: TableKind::LTable,
128                            row: match call_type() {
129                                ValueType::Struct(s) => s,
130                                _ => unreachable!(),
131                            },
132                        }),
133                        nullable: false,
134                        attrs: Default::default(),
135                    },
136                ),
137                FieldSchema::new(
138                    "content_fingerprint".to_string(),
139                    EnrichedValueType {
140                        typ: ValueType::Basic(BasicValueType::Bytes),
141                        nullable: false,
142                        attrs: Default::default(),
143                    },
144                ),
145            ]),
146            description: None,
147        }),
148        nullable: false,
149        attrs: Default::default(),
150    }
151}
152
153pub fn symbol_type() -> ValueType {
154    ValueType::Struct(StructSchema {
155        fields: vec![
156            FieldSchema::new(
157                "name".to_string(),
158                EnrichedValueType {
159                    typ: ValueType::Basic(BasicValueType::Str),
160                    nullable: false,
161                    attrs: Default::default(),
162                },
163            ),
164            FieldSchema::new(
165                "kind".to_string(),
166                EnrichedValueType {
167                    typ: ValueType::Basic(BasicValueType::Str),
168                    nullable: false,
169                    attrs: Default::default(),
170                },
171            ),
172            FieldSchema::new(
173                "scope".to_string(),
174                EnrichedValueType {
175                    typ: ValueType::Basic(BasicValueType::Str),
176                    nullable: false,
177                    attrs: Default::default(),
178                },
179            ),
180        ]
181        .into(),
182        description: None,
183    })
184}
185
186pub fn import_type() -> ValueType {
187    ValueType::Struct(StructSchema {
188        fields: vec![
189            FieldSchema::new(
190                "symbol_name".to_string(),
191                EnrichedValueType {
192                    typ: ValueType::Basic(BasicValueType::Str),
193                    nullable: false,
194                    attrs: Default::default(),
195                },
196            ),
197            FieldSchema::new(
198                "source_path".to_string(),
199                EnrichedValueType {
200                    typ: ValueType::Basic(BasicValueType::Str),
201                    nullable: false,
202                    attrs: Default::default(),
203                },
204            ),
205            FieldSchema::new(
206                "kind".to_string(),
207                EnrichedValueType {
208                    typ: ValueType::Basic(BasicValueType::Str),
209                    nullable: false,
210                    attrs: Default::default(),
211                },
212            ),
213        ]
214        .into(),
215        description: None,
216    })
217}
218
219pub fn call_type() -> ValueType {
220    ValueType::Struct(StructSchema {
221        fields: vec![
222            FieldSchema::new(
223                "function_name".to_string(),
224                EnrichedValueType {
225                    typ: ValueType::Basic(BasicValueType::Str),
226                    nullable: false,
227                    attrs: Default::default(),
228                },
229            ),
230            FieldSchema::new(
231                "arguments_count".to_string(),
232                EnrichedValueType {
233                    typ: ValueType::Basic(BasicValueType::Int64),
234                    nullable: false,
235                    attrs: Default::default(),
236                },
237            ),
238        ]
239        .into(),
240        description: None,
241    })
242}