Skip to main content

google_cloud_spanner/
types.rs

1// Copyright 2026 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15// TODO(#4969)
16#![allow(dead_code)]
17
18use crate::generated::gapic_dataplane::model::TypeAnnotationCode;
19use gaxi::prost::ConvertError;
20use std::sync::LazyLock;
21
22/// Spanner type definition.
23#[derive(Clone, Debug, PartialEq, Default)]
24#[repr(transparent)]
25pub struct Type(pub(crate) crate::generated::gapic_dataplane::model::Type);
26
27macro_rules! define_type_code {
28    ($($variant:ident = $val:expr),* $(,)?) => {
29        /// Spanner type code.
30        ///
31        /// Maps directly to the gRPC type codes defined in
32        /// [`crate::generated::gapic_dataplane::model::TypeCode`].
33        #[derive(Clone, Debug, PartialEq, Eq, Copy, Hash, Default)]
34        #[repr(i32)]
35        #[non_exhaustive]
36        pub enum TypeCode {
37            /// Not specified.
38            #[default]
39            Unspecified = 0,
40            $(
41                #[doc = concat!("Spanner `", stringify!($variant), "` data type.")]
42                $variant = $val
43            ),*,
44            /// An unknown or unsupported type code value.
45            Unknown(i32),
46        }
47
48        impl From<i32> for TypeCode {
49            fn from(value: i32) -> Self {
50                match value {
51                    0 => TypeCode::Unspecified,
52                    $($val => TypeCode::$variant),*,
53                    v => TypeCode::Unknown(v),
54                }
55            }
56        }
57
58        impl From<TypeCode> for i32 {
59            fn from(value: TypeCode) -> Self {
60                match value {
61                    TypeCode::Unspecified => 0,
62                    $(TypeCode::$variant => $val),*,
63                    TypeCode::Unknown(v) => v,
64                }
65            }
66        }
67
68        impl From<crate::generated::gapic_dataplane::model::TypeCode> for TypeCode {
69            fn from(value: crate::generated::gapic_dataplane::model::TypeCode) -> Self {
70                 match value.value() {
71                    Some(v) => v.into(),
72                    None => TypeCode::Unspecified,
73                }
74            }
75        }
76
77        impl From<TypeCode> for crate::generated::gapic_dataplane::model::TypeCode {
78            fn from(value: TypeCode) -> Self {
79                let v: i32 = value.into();
80                v.into()
81            }
82        }
83    };
84}
85
86// The values here must match the values in the `google.spanner.v1.TypeCode` enum.
87// We cannot use the generated constants directly because they are not exposed as public constants.
88// See https://github.com/googleapis/googleapis/blob/master/google/spanner/v1/type.proto
89define_type_code!(
90    Bool = 1,
91    Int64 = 2,
92    Float64 = 3,
93    Float32 = 15,
94    Timestamp = 4,
95    Date = 5,
96    String = 6,
97    Bytes = 7,
98    Array = 8,
99    Struct = 9,
100    Numeric = 10,
101    Json = 11,
102    Proto = 13,
103    Enum = 14,
104    Interval = 16,
105    Uuid = 17,
106);
107
108impl From<crate::generated::gapic_dataplane::model::Type> for Type {
109    fn from(value: crate::generated::gapic_dataplane::model::Type) -> Self {
110        Type(value)
111    }
112}
113
114impl From<Type> for crate::generated::gapic_dataplane::model::Type {
115    fn from(value: Type) -> Self {
116        value.0
117    }
118}
119
120impl From<crate::google::spanner::v1::Type> for Type {
121    fn from(value: crate::google::spanner::v1::Type) -> Self {
122        use gaxi::prost::FromProto;
123        value.cnv().unwrap_or_default().into()
124    }
125}
126
127impl Type {
128    /// Returns the type code.
129    pub fn code(&self) -> TypeCode {
130        self.0.code.clone().into()
131    }
132
133    /// Returns the element type of the array, or `None` if this type is not an array.
134    pub fn array_element_type(&self) -> Option<Type> {
135        self.0
136            .array_element_type
137            .as_deref()
138            .map(|t| Type(t.clone()))
139    }
140}
141
142impl gaxi::prost::ToProto<i32> for TypeCode {
143    type Output = i32;
144
145    fn to_proto(self) -> Result<i32, ConvertError> {
146        let internal: crate::generated::gapic_dataplane::model::TypeCode = self.into();
147
148        internal.to_proto()
149    }
150}
151
152impl gaxi::prost::ToProto<crate::generated::gapic_dataplane::model::Type> for Type {
153    type Output = crate::generated::gapic_dataplane::model::Type;
154
155    fn to_proto(self) -> Result<crate::generated::gapic_dataplane::model::Type, ConvertError> {
156        Ok(self.0)
157    }
158}
159
160static TYPE_INT64: LazyLock<Type> = LazyLock::new(|| create_type(TypeCode::Int64));
161static TYPE_STRING: LazyLock<Type> = LazyLock::new(|| create_type(TypeCode::String));
162static TYPE_BOOL: LazyLock<Type> = LazyLock::new(|| create_type(TypeCode::Bool));
163static TYPE_FLOAT32: LazyLock<Type> = LazyLock::new(|| create_type(TypeCode::Float32));
164static TYPE_FLOAT64: LazyLock<Type> = LazyLock::new(|| create_type(TypeCode::Float64));
165static TYPE_JSON: LazyLock<Type> = LazyLock::new(|| create_type(TypeCode::Json));
166static TYPE_BYTES: LazyLock<Type> = LazyLock::new(|| create_type(TypeCode::Bytes));
167static TYPE_TIMESTAMP: LazyLock<Type> = LazyLock::new(|| create_type(TypeCode::Timestamp));
168static TYPE_DATE: LazyLock<Type> = LazyLock::new(|| create_type(TypeCode::Date));
169static TYPE_NUMERIC: LazyLock<Type> = LazyLock::new(|| create_type(TypeCode::Numeric));
170static TYPE_UUID: LazyLock<Type> = LazyLock::new(|| create_type(TypeCode::Uuid));
171static TYPE_INTERVAL: LazyLock<Type> = LazyLock::new(|| create_type(TypeCode::Interval));
172static TYPE_PG_NUMERIC: LazyLock<Type> = LazyLock::new(|| {
173    let mut t = create_type(TypeCode::Numeric);
174    t.0.type_annotation = TypeAnnotationCode::PgNumeric;
175    t
176});
177static TYPE_PG_JSONB: LazyLock<Type> = LazyLock::new(|| {
178    let mut t = create_type(TypeCode::Json);
179    t.0.type_annotation = TypeAnnotationCode::PgJsonb;
180    t
181});
182static TYPE_PG_OID: LazyLock<Type> = LazyLock::new(|| {
183    let mut t = create_type(TypeCode::Int64);
184    t.0.type_annotation = TypeAnnotationCode::PgOid;
185    t
186});
187
188/// Returns a `Type` representing `INT64` (GoogleSQL) or `bigint` (PostgreSQL).
189pub fn int64() -> Type {
190    TYPE_INT64.clone()
191}
192
193/// Returns a `Type` representing `STRING` (GoogleSQL) or `character varying` (PostgreSQL).
194pub fn string() -> Type {
195    TYPE_STRING.clone()
196}
197
198/// Returns a `Type` representing `BOOL` (GoogleSQL) or `boolean` (PostgreSQL).
199pub fn bool() -> Type {
200    TYPE_BOOL.clone()
201}
202
203/// Returns a `Type` representing `FLOAT32` (GoogleSQL) or `real` (PostgreSQL).
204pub fn float32() -> Type {
205    TYPE_FLOAT32.clone()
206}
207
208/// Returns a `Type` representing `FLOAT64` (GoogleSQL) or `double precision` (PostgreSQL).
209pub fn float64() -> Type {
210    TYPE_FLOAT64.clone()
211}
212
213/// Returns a `Type` representing `JSON` (GoogleSQL).
214pub fn json() -> Type {
215    TYPE_JSON.clone()
216}
217
218/// Returns a `Type` representing `BYTES` (GoogleSQL) or `bytea` (PostgreSQL).
219pub fn bytes() -> Type {
220    TYPE_BYTES.clone()
221}
222
223/// Returns a `Type` representing `TIMESTAMP` (GoogleSQL) or `timestamp with time zone` (PostgreSQL).
224pub fn timestamp() -> Type {
225    TYPE_TIMESTAMP.clone()
226}
227
228/// Returns a `Type` representing `DATE` (GoogleSQL) or `date` (PostgreSQL).
229pub fn date() -> Type {
230    TYPE_DATE.clone()
231}
232
233/// Returns a `Type` representing `NUMERIC` (GoogleSQL) or `numeric` (PostgreSQL).
234pub fn numeric() -> Type {
235    TYPE_NUMERIC.clone()
236}
237
238/// Returns a `Type` representing `UUID` (GoogleSQL) or `uuid` (PostgreSQL).
239pub fn uuid() -> Type {
240    TYPE_UUID.clone()
241}
242
243/// Returns a `Type` representing `INTERVAL` (GoogleSQL) or `interval` (PostgreSQL).
244pub fn interval() -> Type {
245    TYPE_INTERVAL.clone()
246}
247
248/// Returns a `Type` representing `numeric` (PostgreSQL).
249pub fn pg_numeric() -> Type {
250    TYPE_PG_NUMERIC.clone()
251}
252
253/// Returns a `Type` representing `jsonb` (PostgreSQL).
254pub fn pg_jsonb() -> Type {
255    TYPE_PG_JSONB.clone()
256}
257
258/// Returns a `Type` representing `oid` (PostgreSQL).
259pub fn pg_oid() -> Type {
260    TYPE_PG_OID.clone()
261}
262
263/// Returns a `Type` representing `ARRAY<t>` (GoogleSQL) or `t[]` (PostgreSQL).
264pub fn array(element_type: Type) -> Type {
265    let mut t = create_type(TypeCode::Array);
266    t.0.array_element_type = Some(Box::new(element_type.0));
267    t
268}
269
270pub(crate) fn create_type(code: TypeCode) -> Type {
271    Type(crate::generated::gapic_dataplane::model::Type {
272        code: code.into(),
273        array_element_type: None,
274        struct_type: None,
275        type_annotation: TypeAnnotationCode::Unspecified,
276        proto_type_fqn: String::new(),
277        _unknown_fields: Default::default(),
278    })
279}
280
281#[cfg(test)]
282mod tests {
283    use super::*;
284    use std::hash::Hash;
285
286    #[test]
287    fn test_type_code_round_trip() {
288        let codes = vec![
289            TypeCode::Unspecified,
290            TypeCode::Bool,
291            TypeCode::Int64,
292            TypeCode::Float64,
293            TypeCode::Float32,
294            TypeCode::Timestamp,
295            TypeCode::Date,
296            TypeCode::String,
297            TypeCode::Bytes,
298            TypeCode::Array,
299            TypeCode::Struct,
300            TypeCode::Numeric,
301            TypeCode::Json,
302            TypeCode::Proto,
303            TypeCode::Enum,
304            TypeCode::Interval,
305            TypeCode::Uuid,
306        ];
307
308        for code in codes {
309            let i: i32 = code.into();
310            let c: TypeCode = i.into();
311            assert_eq!(code, c);
312
313            let generated: crate::generated::gapic_dataplane::model::TypeCode = code.into();
314            let back: TypeCode = generated.into();
315            assert_eq!(code, back);
316        }
317    }
318
319    #[test]
320    fn test_unknown_type_code() {
321        let i = 999;
322        let code: TypeCode = i.into();
323        assert_eq!(code, TypeCode::Unknown(i));
324        assert_eq!(i32::from(code), i);
325    }
326
327    #[test]
328    fn test_unknown_type_code_from_generated() {
329        use crate::generated::gapic_dataplane::model::type_code::UnknownValue;
330        use wkt::internal::UnknownEnumValue;
331
332        let i = 999;
333        let unknown = UnknownValue(UnknownEnumValue::Integer(i));
334        let generated = crate::generated::gapic_dataplane::model::TypeCode::UnknownValue(unknown);
335        let code: TypeCode = generated.into();
336        assert_eq!(code, TypeCode::Unknown(i));
337    }
338
339    #[test]
340    fn test_simple_types() {
341        assert_eq!(int64().code(), TypeCode::Int64);
342        assert_eq!(string().code(), TypeCode::String);
343        assert_eq!(bool().code(), TypeCode::Bool);
344        assert_eq!(float64().code(), TypeCode::Float64);
345        assert_eq!(bytes().code(), TypeCode::Bytes);
346        assert_eq!(timestamp().code(), TypeCode::Timestamp);
347        assert_eq!(date().code(), TypeCode::Date);
348        assert_eq!(numeric().code(), TypeCode::Numeric);
349        assert_eq!(json().code(), TypeCode::Json);
350        assert_eq!(float32().code(), TypeCode::Float32);
351        assert_eq!(uuid().code(), TypeCode::Uuid);
352        assert_eq!(interval().code(), TypeCode::Interval);
353        // PG types are tested in test_pg_types
354    }
355
356    #[test]
357    fn test_default_type() {
358        let t = Type::default();
359        assert_eq!(t.code(), TypeCode::Unspecified);
360        assert_eq!(
361            t.0.code,
362            crate::generated::gapic_dataplane::model::TypeCode::Unspecified
363        );
364    }
365
366    #[test]
367    fn test_to_proto_traits() {
368        use gaxi::prost::ToProto;
369        let t = int64();
370        let proto: crate::generated::gapic_dataplane::model::Type = t.clone().to_proto().unwrap();
371        assert_eq!(
372            proto.code,
373            crate::generated::gapic_dataplane::model::TypeCode::Int64
374        );
375
376        let code = TypeCode::Int64;
377        let proto_code: i32 = code.to_proto().unwrap();
378        assert_eq!(proto_code, 2);
379    }
380
381    #[test]
382    fn test_from_type_traits() {
383        let internal_type = crate::generated::gapic_dataplane::model::Type {
384            code: crate::generated::gapic_dataplane::model::TypeCode::Bool,
385            ..Default::default()
386        };
387        let t: Type = internal_type.clone().into();
388        assert_eq!(t.code(), TypeCode::Bool);
389
390        let back: crate::generated::gapic_dataplane::model::Type = t.into();
391        assert_eq!(back.code, internal_type.code);
392    }
393
394    #[test]
395    fn test_array_type() {
396        let t = array(int64());
397        assert_eq!(t.code(), TypeCode::Array);
398        assert_eq!(
399            t.0.array_element_type.unwrap().code,
400            crate::generated::gapic_dataplane::model::TypeCode::Int64
401        );
402    }
403
404    #[test]
405    fn test_pg_types() {
406        assert_eq!(pg_numeric().code(), TypeCode::Numeric);
407        assert_eq!(
408            pg_numeric().0.type_annotation,
409            TypeAnnotationCode::PgNumeric
410        );
411
412        assert_eq!(pg_jsonb().code(), TypeCode::Json);
413        assert_eq!(pg_jsonb().0.type_annotation, TypeAnnotationCode::PgJsonb);
414
415        assert_eq!(pg_oid().code(), TypeCode::Int64);
416        assert_eq!(pg_oid().0.type_annotation, TypeAnnotationCode::PgOid);
417    }
418
419    #[test]
420    fn test_array_element_type() {
421        let arr = array(int64());
422        assert_eq!(arr.array_element_type(), Some(int64()));
423
424        // Non-array types should return None
425        assert_eq!(int64().array_element_type(), None);
426        assert_eq!(string().array_element_type(), None);
427        assert_eq!(Type::default().array_element_type(), None);
428    }
429
430    #[test]
431    fn test_auto_traits() {
432        static_assertions::assert_impl_all!(Type: Send, Sync, Clone, std::fmt::Debug);
433        static_assertions::assert_impl_all!(
434            TypeCode: Send,
435            Sync,
436            Clone,
437            Copy,
438            std::fmt::Debug,
439            PartialEq,
440            Eq,
441            Hash
442        );
443    }
444}