Skip to main content

provenant/models/
package_uid.rs

1// SPDX-FileCopyrightText: Provenant contributors
2// SPDX-License-Identifier: Apache-2.0
3
4use 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    /// Creates a new `PackageUid` by appending a UUID to the given purl.
17    pub fn new(purl: &str) -> Self {
18        let uuid = Uuid::new_v4();
19        Self::with_uuid_suffix(purl, uuid)
20    }
21
22    /// Creates a new `PackageUid` from a non-purl base string.
23    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        PackageUid(crate::models::purl::append_uuid_qualifier(
30            base,
31            &uuid.to_string(),
32        ))
33    }
34
35    /// Wraps an existing UID string without validation or UUID generation.
36    ///
37    /// Use this for deserialization boundaries and round-trip conversions
38    /// where the UID string is already well-formed.
39    pub fn from_raw(s: String) -> Self {
40        PackageUid(s)
41    }
42
43    /// Returns the empty-string sentinel representing "no purl".
44    pub fn empty() -> Self {
45        PackageUid(String::new())
46    }
47
48    /// Returns the purl portion by stripping the UUID qualifier. Borrows unless
49    /// the purl carries a subpath, which has to be rejoined to what precedes the
50    /// qualifier.
51    pub fn stable_key(&self) -> std::borrow::Cow<'_, str> {
52        crate::models::purl::strip_uuid_qualifier(&self.0)
53    }
54
55    /// Returns a new `PackageUid` with the purl base replaced, preserving the UUID.
56    pub fn replace_base(&self, new_purl: &str) -> Self {
57        let Some(uuid) = crate::models::purl::uuid_qualifier_value(&self.0) else {
58            return PackageUid(self.0.clone());
59        };
60        PackageUid(crate::models::purl::append_uuid_qualifier(new_purl, uuid))
61    }
62
63    /// Returns the inner string slice.
64    pub fn as_str(&self) -> &str {
65        &self.0
66    }
67
68    /// Returns `true` if this is the empty-string sentinel.
69    pub fn is_empty(&self) -> bool {
70        self.0.is_empty()
71    }
72}
73
74impl fmt::Display for PackageUid {
75    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76        self.0.fmt(f)
77    }
78}
79
80impl AsRef<str> for PackageUid {
81    fn as_ref(&self) -> &str {
82        &self.0
83    }
84}
85
86impl Borrow<str> for PackageUid {
87    fn borrow(&self) -> &str {
88        &self.0
89    }
90}
91
92impl Deref for PackageUid {
93    type Target = str;
94
95    fn deref(&self) -> &Self::Target {
96        &self.0
97    }
98}