1use crate::manifest::ImplKind;
12use crate::registry::{RegisteredTransform, Registry};
13use crate::types::ColumnSpec;
14use serde::Serialize;
15
16#[derive(Debug, Clone, Serialize, PartialEq)]
18pub struct FieldInfo {
19 pub name: String,
20 #[serde(rename = "type")]
21 pub dtype: String,
22}
23
24impl From<&ColumnSpec> for FieldInfo {
25 fn from(c: &ColumnSpec) -> Self {
26 let dtype = serde_json::to_value(c.dtype)
27 .ok()
28 .and_then(|v| v.as_str().map(str::to_string))
29 .unwrap_or_else(|| "unknown".into());
30 FieldInfo {
31 name: c.name.clone(),
32 dtype,
33 }
34 }
35}
36
37#[derive(Debug, Clone, Serialize, PartialEq)]
39pub struct TransformDescriptor {
40 pub id: String,
41 pub version: String,
42 pub key: String,
44 pub root: String,
46 #[serde(rename = "impl")]
47 pub impl_kind: String,
48 pub inputs: Vec<FieldInfo>,
50 pub outputs: Vec<FieldInfo>,
52 pub hardware: String,
53 pub deterministic: bool,
54 pub io: bool,
55 pub streaming: bool,
56 pub artifact: Option<String>,
58}
59
60fn enum_str<T: Serialize>(v: T) -> Option<String> {
62 serde_json::to_value(v)
63 .ok()
64 .and_then(|v| v.as_str().map(str::to_string))
65}
66
67impl From<&RegisteredTransform> for TransformDescriptor {
68 fn from(r: &RegisteredTransform) -> Self {
69 let m = &r.manifest;
70 let inputs = m
71 .inputs
72 .first()
73 .map(|s| s.columns.iter().map(FieldInfo::from).collect())
74 .unwrap_or_default();
75 let outputs = m.output.columns.iter().map(FieldInfo::from).collect();
76 TransformDescriptor {
77 id: m.id.clone(),
78 version: m.version.clone(),
79 key: r.key(),
80 root: r.root.clone(),
81 impl_kind: enum_str(m.impl_kind).unwrap_or_else(|| "builtin".into()),
82 inputs,
83 outputs,
84 hardware: enum_str(m.capabilities.hardware).unwrap_or_else(|| "cpu".into()),
85 deterministic: m.capabilities.deterministic,
86 io: m.capabilities.io,
87 streaming: m.capabilities.streaming,
88 artifact: m.entry.clone(),
89 }
90 }
91}
92
93pub fn descriptors(registry: &Registry) -> Vec<TransformDescriptor> {
95 let mut out: Vec<TransformDescriptor> = registry.iter().map(TransformDescriptor::from).collect();
96 out.sort_by(|a, b| a.key.cmp(&b.key));
97 out
98}
99
100pub fn model_descriptors(registry: &Registry) -> Vec<TransformDescriptor> {
102 let mut out: Vec<TransformDescriptor> = registry
103 .iter()
104 .filter(|r| r.manifest.impl_kind == ImplKind::Onnx)
105 .map(TransformDescriptor::from)
106 .collect();
107 out.sort_by(|a, b| a.key.cmp(&b.key));
108 out
109}
110
111pub fn catalog_json(registry: &Registry) -> String {
113 serde_json::json!({ "transforms": descriptors(registry) }).to_string()
114}
115
116pub fn models_json(registry: &Registry) -> String {
118 serde_json::json!({ "models": model_descriptors(registry) }).to_string()
119}