Skip to main content

linkrs_wire/
schema.rs

1//! Schema contract DTOs (HTTP `/schema` endpoints).
2//!
3//! `PropertyDef` is the unified property definition: the server-side
4//! `api-core PropertyDef` (which carries `default_value`/`comment`) and the
5//! CLI's serialization-only mirror are both represented by this single wire
6//! type. `data_type` is the core `DataType` `Display` output
7//! (`BOOL`/`INT`/`FIXEDSTRING(8)`/`VECTOR_DENSE(3)` ...), which the server
8//! parses back through the same `FromStr` source of truth.
9
10use serde::{Deserialize, Serialize};
11
12/// Space information
13#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
14pub struct SpaceInfo {
15    pub id: u64,
16    pub name: String,
17    pub vid_type: String,
18    #[serde(default)]
19    pub comment: Option<String>,
20}
21
22/// Tag information
23#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
24pub struct TagInfo {
25    pub name: String,
26    #[serde(default)]
27    pub fields: Vec<FieldInfo>,
28}
29
30/// Edge type information
31#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
32pub struct EdgeTypeInfo {
33    pub name: String,
34    #[serde(default)]
35    pub fields: Vec<FieldInfo>,
36}
37
38/// Field information
39#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
40pub struct FieldInfo {
41    pub name: String,
42    pub data_type: String,
43    pub nullable: bool,
44    #[serde(default)]
45    pub default_value: Option<String>,
46}
47
48/// Property definition (create tag / edge type request payload).
49#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
50pub struct PropertyDef {
51    pub name: String,
52    /// Core `DataType` wire name (see module docs).
53    pub data_type: String,
54    #[serde(default)]
55    pub nullable: bool,
56    #[serde(default)]
57    pub default_value: Option<serde_json::Value>,
58    #[serde(default)]
59    pub comment: Option<String>,
60}
61
62impl PropertyDef {
63    /// Create a property definition from a core data type name.
64    pub fn new(name: impl Into<String>, data_type: impl Into<String>) -> Self {
65        Self {
66            name: name.into(),
67            data_type: data_type.into(),
68            nullable: true,
69            default_value: None,
70            comment: None,
71        }
72    }
73
74    /// Mark the property as NOT NULL.
75    pub fn not_null(mut self) -> Self {
76        self.nullable = false;
77        self
78    }
79}
80
81/// Create space request
82#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
83pub struct CreateSpaceRequest {
84    pub name: String,
85    #[serde(default)]
86    pub vid_type: Option<String>,
87    #[serde(default)]
88    pub comment: Option<String>,
89}
90
91/// Create tag request
92#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
93pub struct CreateTagRequest {
94    pub name: String,
95    #[serde(default)]
96    pub properties: Vec<PropertyDef>,
97}
98
99/// Create edge type request
100#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
101pub struct CreateEdgeTypeRequest {
102    pub name: String,
103    #[serde(default)]
104    pub properties: Vec<PropertyDef>,
105}
106
107#[cfg(test)]
108mod tests {
109    use super::*;
110
111    #[test]
112    fn property_def_roundtrip() {
113        let def = PropertyDef {
114            name: "age".to_string(),
115            data_type: "INT".to_string(),
116            nullable: false,
117            default_value: Some(serde_json::json!(18)),
118            comment: Some("years".to_string()),
119        };
120        let json = serde_json::to_string(&def).unwrap();
121        let back: PropertyDef = serde_json::from_str(&json).unwrap();
122        assert_eq!(back.name, "age");
123        assert_eq!(back.data_type, "INT");
124        assert!(!back.nullable);
125        assert_eq!(back.default_value, Some(serde_json::json!(18)));
126        assert_eq!(back.comment.as_deref(), Some("years"));
127    }
128
129    #[test]
130    fn property_def_defaults_absent_fields() {
131        let back: PropertyDef =
132            serde_json::from_str(r#"{"name": "name", "data_type": "STRING"}"#).unwrap();
133        assert!(!back.nullable);
134        assert!(back.default_value.is_none());
135        assert!(back.comment.is_none());
136    }
137
138    #[test]
139    fn space_info_roundtrip() {
140        let info = SpaceInfo {
141            id: 1,
142            name: "space1".to_string(),
143            vid_type: "INT64".to_string(),
144            comment: None,
145        };
146        let json = serde_json::to_string(&info).unwrap();
147        let back: SpaceInfo = serde_json::from_str(&json).unwrap();
148        assert_eq!(back.id, 1);
149        assert_eq!(back.name, "space1");
150    }
151
152    #[test]
153    fn create_space_request_roundtrip() {
154        let request = CreateSpaceRequest {
155            name: "s".to_string(),
156            vid_type: Some("STRING".to_string()),
157            comment: None,
158        };
159        let json = serde_json::to_string(&request).unwrap();
160        let back: CreateSpaceRequest = serde_json::from_str(&json).unwrap();
161        assert_eq!(back.vid_type.as_deref(), Some("STRING"));
162    }
163}