aws_smithy_schema/schema/
shape_id.rs

1/*
2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6use std::fmt;
7
8/// Creates a [`ShapeId`] from a namespace and shape name at compile time.
9///
10/// The fully qualified name (`namespace#ShapeName`) is computed via `concat!`,
11/// eliminating the risk of the FQN getting out of sync with the parts.
12///
13/// # Examples
14/// ```
15/// use aws_smithy_schema::{shape_id, ShapeId};
16///
17/// const ID: ShapeId = shape_id!("smithy.api", "String");
18/// assert_eq!(ID.as_str(), "smithy.api#String");
19/// ```
20#[macro_export]
21macro_rules! shape_id {
22    ($ns:literal, $name:literal) => {
23        $crate::ShapeId::from_static(concat!($ns, "#", $name), $ns, $name)
24    };
25    ($ns:literal, $name:literal, $member:literal) => {
26        $crate::ShapeId::from_static_with_member(
27            concat!($ns, "#", $name, "$", $member),
28            $ns,
29            $name,
30            $member,
31        )
32    };
33}
34
35/// A Smithy Shape ID.
36///
37/// Shape IDs uniquely identify shapes in a Smithy model.
38/// Use the [`shape_id!`] macro to construct instances — it computes the
39/// fully qualified name at compile time from the namespace and shape name,
40/// preventing the parts from getting out of sync.
41///
42/// # Examples
43/// ```
44/// use aws_smithy_schema::{shape_id, ShapeId};
45///
46/// const ID: ShapeId = shape_id!("smithy.api", "String");
47/// assert_eq!(ID.namespace(), "smithy.api");
48/// assert_eq!(ID.shape_name(), "String");
49/// assert_eq!(ID.as_str(), "smithy.api#String");
50/// ```
51#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
52pub struct ShapeId {
53    fqn: &'static str,
54    namespace: &'static str,
55    shape_name: &'static str,
56    member_name: Option<&'static str>,
57}
58
59impl ShapeId {
60    /// Creates a ShapeId from pre-computed static strings.
61    ///
62    /// Prefer the [`shape_id!`] macro which computes `fqn` via `concat!`
63    /// to prevent the parts from getting out of sync.
64    #[doc(hidden)]
65    pub const fn from_static(
66        fqn: &'static str,
67        namespace: &'static str,
68        shape_name: &'static str,
69    ) -> Self {
70        Self {
71            fqn,
72            namespace,
73            shape_name,
74            member_name: None,
75        }
76    }
77
78    /// Creates a ShapeId with a member name from pre-computed static strings.
79    ///
80    /// Prefer the [`shape_id!`] macro which computes `fqn` via `concat!`
81    /// to prevent the parts from getting out of sync.
82    #[doc(hidden)]
83    pub const fn from_static_with_member(
84        fqn: &'static str,
85        namespace: &'static str,
86        shape_name: &'static str,
87        member_name: &'static str,
88    ) -> Self {
89        Self {
90            fqn,
91            namespace,
92            shape_name,
93            member_name: Some(member_name),
94        }
95    }
96
97    /// Returns the fully qualified string representation (e.g. `"smithy.api#String"`).
98    pub fn as_str(&self) -> &str {
99        self.fqn
100    }
101
102    /// Returns the namespace portion of the ShapeId.
103    pub fn namespace(&self) -> &str {
104        self.namespace
105    }
106
107    /// Returns the shape name portion of the ShapeId.
108    pub fn shape_name(&self) -> &str {
109        self.shape_name
110    }
111
112    /// Returns the member name if this is a member shape ID.
113    pub fn member_name(&self) -> Option<&str> {
114        self.member_name
115    }
116}
117
118impl fmt::Display for ShapeId {
119    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120        f.write_str(self.fqn)
121    }
122}
123
124#[cfg(test)]
125mod tests {
126    use super::*;
127
128    #[test]
129    fn test_shape_id_macro() {
130        const ID: ShapeId = shape_id!("smithy.api", "String");
131        assert_eq!(ID.as_str(), "smithy.api#String");
132        assert_eq!(ID.namespace(), "smithy.api");
133        assert_eq!(ID.shape_name(), "String");
134        assert_eq!(ID.member_name(), None);
135    }
136
137    #[test]
138    fn test_shape_id_macro_with_member() {
139        const ID: ShapeId = shape_id!("com.example", "MyStruct", "field");
140        assert_eq!(ID.as_str(), "com.example#MyStruct$field");
141        assert_eq!(ID.namespace(), "com.example");
142        assert_eq!(ID.shape_name(), "MyStruct");
143        assert_eq!(ID.member_name(), Some("field"));
144    }
145
146    #[test]
147    fn test_display() {
148        let id = shape_id!("smithy.api", "String");
149        assert_eq!(format!("{id}"), "smithy.api#String");
150    }
151
152    #[test]
153    fn test_equality() {
154        let a = shape_id!("smithy.api", "String");
155        let b = shape_id!("smithy.api", "String");
156        assert_eq!(a, b);
157
158        let c = shape_id!("smithy.api", "String", "foo");
159        let d = shape_id!("smithy.api", "String", "foo");
160        assert_eq!(c, d);
161    }
162}