mz_repr/
catalog_item_id.rs

1// Copyright Materialize, Inc. and contributors. All rights reserved.
2//
3// Use of this software is governed by the Business Source License
4// included in the LICENSE file.
5//
6// As of the Change Date specified in that file, in accordance with
7// the Business Source License, use of this software will be governed
8// by the Apache License, Version 2.0.
9
10use std::fmt;
11use std::str::FromStr;
12
13use anyhow::{Error, anyhow};
14use mz_lowertest::MzReflect;
15use mz_proto::{RustType, TryFromProtoError};
16use proptest_derive::Arbitrary;
17use serde::{Deserialize, Serialize};
18
19include!(concat!(env!("OUT_DIR"), "/mz_repr.catalog_item_id.rs"));
20
21/// The identifier for an item within the Catalog.
22#[derive(
23    Arbitrary,
24    Clone,
25    Copy,
26    Debug,
27    Eq,
28    PartialEq,
29    Ord,
30    PartialOrd,
31    Hash,
32    Serialize,
33    Deserialize,
34    MzReflect,
35)]
36pub enum CatalogItemId {
37    /// System namespace.
38    System(u64),
39    /// Introspection Source Index namespace.
40    IntrospectionSourceIndex(u64),
41    /// User namespace.
42    User(u64),
43    /// Transient item.
44    Transient(u64),
45}
46
47impl CatalogItemId {
48    /// Reports whether this ID is in the system namespace.
49    pub fn is_system(&self) -> bool {
50        matches!(
51            self,
52            CatalogItemId::System(_) | CatalogItemId::IntrospectionSourceIndex(_)
53        )
54    }
55
56    /// Reports whether this ID is in the user namespace.
57    pub fn is_user(&self) -> bool {
58        matches!(self, CatalogItemId::User(_))
59    }
60
61    /// Reports whether this ID is for a transient item.
62    pub fn is_transient(&self) -> bool {
63        matches!(self, CatalogItemId::Transient(_))
64    }
65}
66
67impl FromStr for CatalogItemId {
68    type Err = Error;
69
70    fn from_str(mut s: &str) -> Result<Self, Self::Err> {
71        if s.len() < 2 {
72            return Err(anyhow!("couldn't parse id {}", s));
73        }
74        let tag = s.chars().next().unwrap();
75        s = &s[1..];
76        let variant = match tag {
77            's' => {
78                if Some('i') == s.chars().next() {
79                    s = &s[1..];
80                    CatalogItemId::IntrospectionSourceIndex
81                } else {
82                    CatalogItemId::System
83                }
84            }
85            'u' => CatalogItemId::User,
86            't' => CatalogItemId::Transient,
87            _ => return Err(anyhow!("couldn't parse id {}", s)),
88        };
89        let val: u64 = s.parse()?;
90        Ok(variant(val))
91    }
92}
93
94impl fmt::Display for CatalogItemId {
95    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
96        match self {
97            CatalogItemId::System(id) => write!(f, "s{}", id),
98            CatalogItemId::IntrospectionSourceIndex(id) => write!(f, "si{}", id),
99            CatalogItemId::User(id) => write!(f, "u{}", id),
100            CatalogItemId::Transient(id) => write!(f, "t{}", id),
101        }
102    }
103}
104
105impl RustType<ProtoCatalogItemId> for CatalogItemId {
106    fn into_proto(&self) -> ProtoCatalogItemId {
107        use proto_catalog_item_id::Kind::*;
108        ProtoCatalogItemId {
109            kind: Some(match self {
110                CatalogItemId::System(x) => System(*x),
111                CatalogItemId::IntrospectionSourceIndex(x) => IntrospectionSourceIndex(*x),
112                CatalogItemId::User(x) => User(*x),
113                CatalogItemId::Transient(x) => Transient(*x),
114            }),
115        }
116    }
117
118    fn from_proto(proto: ProtoCatalogItemId) -> Result<Self, TryFromProtoError> {
119        use proto_catalog_item_id::Kind::*;
120        match proto.kind {
121            Some(System(x)) => Ok(CatalogItemId::System(x)),
122            Some(IntrospectionSourceIndex(x)) => Ok(CatalogItemId::IntrospectionSourceIndex(x)),
123            Some(User(x)) => Ok(CatalogItemId::User(x)),
124            Some(Transient(x)) => Ok(CatalogItemId::Transient(x)),
125            None => Err(TryFromProtoError::missing_field("ProtoCatalogItemId::kind")),
126        }
127    }
128}
129
130#[cfg(test)]
131mod tests {
132    use proptest::prelude::*;
133
134    use super::*;
135
136    #[mz_ore::test]
137    fn proptest_catalog_item_id_roundtrips() {
138        fn testcase(og: CatalogItemId) {
139            let s = og.to_string();
140            let rnd: CatalogItemId = s.parse().unwrap();
141            assert_eq!(og, rnd);
142        }
143
144        proptest!(|(id in any::<CatalogItemId>())| {
145            testcase(id);
146        })
147    }
148}