provenant/models/
package_uid.rs1use std::borrow::Borrow;
5use std::fmt;
6use std::ops::Deref;
7
8use serde::{Deserialize, Serialize};
9use uuid::Uuid;
10
11#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
12#[serde(transparent)]
13pub struct PackageUid(String);
14
15impl PackageUid {
16 pub fn new(purl: &str) -> Self {
18 let uuid = Uuid::new_v4();
19 Self::with_uuid_suffix(purl, uuid)
20 }
21
22 pub fn new_opaque(base: &str) -> Self {
24 let uuid = Uuid::new_v4();
25 Self::with_uuid_suffix(base, uuid)
26 }
27
28 fn with_uuid_suffix(base: &str, uuid: Uuid) -> Self {
29 if base.contains('?') {
30 PackageUid(format!("{}&uuid={}", base, uuid))
31 } else {
32 PackageUid(format!("{}?uuid={}", base, uuid))
33 }
34 }
35
36 pub fn from_raw(s: String) -> Self {
41 PackageUid(s)
42 }
43
44 pub fn empty() -> Self {
46 PackageUid(String::new())
47 }
48
49 pub fn stable_key(&self) -> &str {
51 self.0
52 .split_once("?uuid=")
53 .map(|(prefix, _)| prefix)
54 .or_else(|| self.0.split_once("&uuid=").map(|(prefix, _)| prefix))
55 .unwrap_or(&self.0)
56 }
57
58 pub fn replace_base(&self, new_purl: &str) -> Self {
60 if let Some((_, suffix)) = self.0.split_once("?uuid=") {
61 return PackageUid(format!("{}?uuid={}", new_purl, suffix));
62 }
63 if let Some((_, suffix)) = self.0.split_once("&uuid=") {
64 let separator = if new_purl.contains('?') { '&' } else { '?' };
65 return PackageUid(format!("{}{separator}uuid={suffix}", new_purl));
66 }
67 PackageUid(self.0.clone())
68 }
69
70 pub fn as_str(&self) -> &str {
72 &self.0
73 }
74
75 pub fn is_empty(&self) -> bool {
77 self.0.is_empty()
78 }
79}
80
81impl fmt::Display for PackageUid {
82 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
83 self.0.fmt(f)
84 }
85}
86
87impl AsRef<str> for PackageUid {
88 fn as_ref(&self) -> &str {
89 &self.0
90 }
91}
92
93impl Borrow<str> for PackageUid {
94 fn borrow(&self) -> &str {
95 &self.0
96 }
97}
98
99impl Deref for PackageUid {
100 type Target = str;
101
102 fn deref(&self) -> &Self::Target {
103 &self.0
104 }
105}