faucet_common_spanner/
types.rs1use serde_json::{Value, json};
5
6#[derive(Debug, Clone, PartialEq, Eq)]
8pub enum SpannerType {
9 Bool,
10 Int64,
11 Float32,
12 Float64,
13 Timestamp,
14 Date,
15 String,
17 Bytes,
19 Numeric,
22 Json,
23 Array(Box<SpannerType>),
24 Other,
27}
28
29pub fn parse_spanner_type(raw: &str) -> SpannerType {
33 let s = raw.trim();
34 let upper = s.to_ascii_uppercase();
35 if let Some(inner) = upper
36 .strip_prefix("ARRAY<")
37 .and_then(|rest| rest.strip_suffix('>'))
38 {
39 return SpannerType::Array(Box::new(parse_spanner_type(inner)));
40 }
41 let base = upper.split('(').next().unwrap_or(&upper).trim();
43 match base {
44 "BOOL" => SpannerType::Bool,
45 "INT64" => SpannerType::Int64,
46 "FLOAT32" => SpannerType::Float32,
47 "FLOAT64" => SpannerType::Float64,
48 "TIMESTAMP" => SpannerType::Timestamp,
49 "DATE" => SpannerType::Date,
50 "STRING" => SpannerType::String,
51 "BYTES" => SpannerType::Bytes,
52 "NUMERIC" => SpannerType::Numeric,
53 "JSON" => SpannerType::Json,
54 _ => SpannerType::Other,
55 }
56}
57
58pub fn spanner_type_to_json_schema(ty: &SpannerType, nullable: bool) -> Value {
65 let base: Value = match ty {
66 SpannerType::Bool => json!({"type": "boolean"}),
67 SpannerType::Int64 => json!({"type": "integer"}),
68 SpannerType::Float32 | SpannerType::Float64 => json!({"type": "number"}),
69 SpannerType::Json => json!({"type": "object"}),
70 SpannerType::Array(inner) => {
71 json!({"type": "array", "items": spanner_type_to_json_schema(inner, false)})
72 }
73 SpannerType::Timestamp
74 | SpannerType::Date
75 | SpannerType::String
76 | SpannerType::Bytes
77 | SpannerType::Numeric => json!({"type": "string"}),
78 SpannerType::Other => json!({"type": "object"}),
79 };
80 if nullable {
81 let mut obj = base;
82 if let Some(t) = obj.get("type").cloned() {
83 obj["type"] = json!([t, "null"]);
84 }
85 obj
86 } else {
87 base
88 }
89}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94
95 #[test]
96 fn parses_scalar_types_case_insensitively_and_strips_lengths() {
97 assert_eq!(parse_spanner_type("INT64"), SpannerType::Int64);
98 assert_eq!(parse_spanner_type("string(MAX)"), SpannerType::String);
99 assert_eq!(parse_spanner_type("BYTES(256)"), SpannerType::Bytes);
100 assert_eq!(parse_spanner_type("Float64"), SpannerType::Float64);
101 assert_eq!(parse_spanner_type("FLOAT32"), SpannerType::Float32);
102 assert_eq!(parse_spanner_type("NUMERIC"), SpannerType::Numeric);
103 assert_eq!(parse_spanner_type("JSON"), SpannerType::Json);
104 assert_eq!(parse_spanner_type("TIMESTAMP"), SpannerType::Timestamp);
105 assert_eq!(parse_spanner_type("DATE"), SpannerType::Date);
106 assert_eq!(parse_spanner_type("BOOL"), SpannerType::Bool);
107 }
108
109 #[test]
110 fn parses_arrays_recursively() {
111 assert_eq!(
112 parse_spanner_type("ARRAY<INT64>"),
113 SpannerType::Array(Box::new(SpannerType::Int64))
114 );
115 assert_eq!(
116 parse_spanner_type("ARRAY<STRING(MAX)>"),
117 SpannerType::Array(Box::new(SpannerType::String))
118 );
119 }
120
121 #[test]
122 fn unknown_types_map_to_other_not_error() {
123 assert_eq!(
124 parse_spanner_type("STRUCT<a INT64, b STRING(MAX)>"),
125 SpannerType::Other
126 );
127 assert_eq!(parse_spanner_type("PROTO<x.Y>"), SpannerType::Other);
128 assert_eq!(parse_spanner_type(""), SpannerType::Other);
129 }
130
131 #[test]
132 fn json_schema_mapping_and_nullability() {
133 assert_eq!(
134 spanner_type_to_json_schema(&SpannerType::Int64, false),
135 serde_json::json!({"type": "integer"})
136 );
137 assert_eq!(
138 spanner_type_to_json_schema(&SpannerType::Numeric, false),
139 serde_json::json!({"type": "string"})
140 );
141 assert_eq!(
142 spanner_type_to_json_schema(&SpannerType::Bool, true),
143 serde_json::json!({"type": ["boolean", "null"]})
144 );
145 assert_eq!(
146 spanner_type_to_json_schema(&SpannerType::Array(Box::new(SpannerType::Float64)), false),
147 serde_json::json!({"type": "array", "items": {"type": "number"}})
148 );
149 }
150}