dpp_digital_link/linktype/
vocabulary.rs1use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
10#[serde(rename_all = "camelCase")]
11pub enum Gs1LinkType {
12 ProductInformationPage,
14 ElectronicLeaflet,
16 SustainabilityInfo,
18 RecyclingInfo,
20 MasterData,
22 CertificationInfo,
24 Instructions,
26 SafetyInfo,
28 Traceability,
30 DigitalProductPassport,
33 Predecessor,
37 Successor,
40 HasComponent,
43 IsComponentOf,
46 Custom(String),
48}
49
50impl Gs1LinkType {
51 pub fn parse(s: &str) -> Self {
53 match s {
54 "gs1:pip" | "https://ref.gs1.org/voc/pip" => Self::ProductInformationPage,
55 "gs1:epil" | "https://ref.gs1.org/voc/epil" => Self::ElectronicLeaflet,
56 "gs1:sustainabilityInfo" | "https://ref.gs1.org/voc/sustainabilityInfo" => {
57 Self::SustainabilityInfo
58 }
59 "gs1:recyclingInfo" | "https://ref.gs1.org/voc/recyclingInfo" => Self::RecyclingInfo,
60 "gs1:masterData" | "https://ref.gs1.org/voc/masterData" => Self::MasterData,
61 "gs1:certificationInfo" | "https://ref.gs1.org/voc/certificationInfo" => {
62 Self::CertificationInfo
63 }
64 "gs1:instructions" | "https://ref.gs1.org/voc/instructions" => Self::Instructions,
65 "gs1:safetyInfo" | "https://ref.gs1.org/voc/safetyInfo" => Self::SafetyInfo,
66 "gs1:traceability" | "https://ref.gs1.org/voc/traceability" => Self::Traceability,
67 "gs1:dpp" | "https://ref.gs1.org/voc/dpp" => Self::DigitalProductPassport,
68 "odal:predecessor" | "https://ref.odal-node.io/voc/predecessor" => Self::Predecessor,
69 "odal:successor" | "https://ref.odal-node.io/voc/successor" => Self::Successor,
70 "odal:hasComponent" | "https://ref.odal-node.io/voc/hasComponent" => Self::HasComponent,
71 "odal:isComponentOf" | "https://ref.odal-node.io/voc/isComponentOf" => {
72 Self::IsComponentOf
73 }
74 other => Self::Custom(other.to_owned()),
75 }
76 }
77
78 pub fn as_gs1_uri(&self) -> &str {
80 match self {
81 Self::ProductInformationPage => "https://ref.gs1.org/voc/pip",
82 Self::ElectronicLeaflet => "https://ref.gs1.org/voc/epil",
83 Self::SustainabilityInfo => "https://ref.gs1.org/voc/sustainabilityInfo",
84 Self::RecyclingInfo => "https://ref.gs1.org/voc/recyclingInfo",
85 Self::MasterData => "https://ref.gs1.org/voc/masterData",
86 Self::CertificationInfo => "https://ref.gs1.org/voc/certificationInfo",
87 Self::Instructions => "https://ref.gs1.org/voc/instructions",
88 Self::SafetyInfo => "https://ref.gs1.org/voc/safetyInfo",
89 Self::Traceability => "https://ref.gs1.org/voc/traceability",
90 Self::DigitalProductPassport => "https://ref.gs1.org/voc/dpp",
91 Self::Predecessor => "https://ref.odal-node.io/voc/predecessor",
92 Self::Successor => "https://ref.odal-node.io/voc/successor",
93 Self::HasComponent => "https://ref.odal-node.io/voc/hasComponent",
94 Self::IsComponentOf => "https://ref.odal-node.io/voc/isComponentOf",
95 Self::Custom(uri) => uri.as_str(),
96 }
97 }
98}
99
100#[cfg(test)]
101mod tests {
102 use super::*;
103
104 #[test]
105 fn every_known_link_type_round_trips_via_canonical_uri() {
106 let all = [
107 Gs1LinkType::ProductInformationPage,
108 Gs1LinkType::ElectronicLeaflet,
109 Gs1LinkType::SustainabilityInfo,
110 Gs1LinkType::RecyclingInfo,
111 Gs1LinkType::MasterData,
112 Gs1LinkType::CertificationInfo,
113 Gs1LinkType::Instructions,
114 Gs1LinkType::SafetyInfo,
115 Gs1LinkType::Traceability,
116 Gs1LinkType::DigitalProductPassport,
117 Gs1LinkType::Predecessor,
118 Gs1LinkType::Successor,
119 Gs1LinkType::HasComponent,
120 Gs1LinkType::IsComponentOf,
121 ];
122 for lt in all {
123 let uri = lt.as_gs1_uri();
124 assert_eq!(Gs1LinkType::parse(uri), lt, "canonical URI must round-trip");
125 }
126 }
127
128 #[test]
129 fn shorthand_and_full_uris_parse_equivalently() {
130 assert_eq!(
131 Gs1LinkType::parse("gs1:dpp"),
132 Gs1LinkType::DigitalProductPassport
133 );
134 assert_eq!(
135 Gs1LinkType::parse("gs1:pip"),
136 Gs1LinkType::ProductInformationPage
137 );
138 assert_eq!(
139 Gs1LinkType::parse("gs1:safetyInfo"),
140 Gs1LinkType::SafetyInfo
141 );
142 }
143
144 #[test]
145 fn unknown_link_type_becomes_custom() {
146 let custom = Gs1LinkType::parse("https://example.com/voc/warranty");
147 assert_eq!(
148 custom,
149 Gs1LinkType::Custom("https://example.com/voc/warranty".to_owned())
150 );
151 assert_eq!(custom.as_gs1_uri(), "https://example.com/voc/warranty");
152 }
153}