Skip to main content

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