shopify_client/common/
gid.rs1use std::fmt;
2
3pub const GID_PREFIX: &str = "gid://shopify/";
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub struct Gid<'a> {
7 kind: &'a str,
8 id: &'a str,
9}
10
11impl<'a> Gid<'a> {
12 pub const ORDER: &'static str = "Order";
13
14 pub fn of(kind: &'a str, id: &'a str) -> Self {
15 Self {
16 kind,
17 id: Self::id_of(id),
18 }
19 }
20
21 pub fn id_of(value: &str) -> &str {
22 match value.strip_prefix(GID_PREFIX) {
23 Some(rest) => rest
24 .split('?')
25 .next()
26 .unwrap_or(rest)
27 .rsplit('/')
28 .next()
29 .unwrap_or(rest),
30 None => value,
31 }
32 }
33
34 pub fn kind(&self) -> &'a str {
35 self.kind
36 }
37
38 pub fn id(&self) -> &'a str {
39 self.id
40 }
41}
42
43impl fmt::Display for Gid<'_> {
44 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45 write!(f, "{GID_PREFIX}{}/{}", self.kind, self.id)
46 }
47}
48
49#[cfg(test)]
50mod tests {
51 use super::*;
52
53 #[test]
54 fn renders_a_gid_from_a_bare_id() {
55 assert_eq!(
56 Gid::of(Gid::ORDER, "1001").to_string(),
57 "gid://shopify/Order/1001"
58 );
59 }
60
61 #[test]
62 fn building_from_a_gid_is_idempotent() {
63 let once = Gid::of(Gid::ORDER, "1001").to_string();
64 assert_eq!(Gid::of(Gid::ORDER, &once).to_string(), once);
65 }
66
67 #[test]
68 fn id_of_strips_the_prefix_and_the_type() {
69 assert_eq!(Gid::id_of("gid://shopify/Order/1001"), "1001");
70 assert_eq!(Gid::id_of("gid://shopify/ProductVariant/11"), "11");
71 }
72
73 #[test]
74 fn id_of_drops_query_parameters() {
75 assert_eq!(
76 Gid::id_of("gid://shopify/Order/1001?namespace=custom"),
77 "1001"
78 );
79 }
80
81 #[test]
82 fn id_of_leaves_a_value_that_is_not_a_gid_alone() {
83 assert_eq!(Gid::id_of("1001"), "1001");
84 assert_eq!(Gid::id_of(""), "");
85 assert_eq!(Gid::id_of("shopify/Order/1001"), "shopify/Order/1001");
86 }
87
88 #[test]
89 fn parts_are_readable_back() {
90 let gid = Gid::of(Gid::ORDER, "gid://shopify/Order/1001");
91 assert_eq!(gid.kind(), "Order");
92 assert_eq!(gid.id(), "1001");
93 }
94}