warg_protocol/package/
model.rs

1use crate::registry::RecordId;
2use core::fmt;
3use indexmap::IndexSet;
4use semver::Version;
5use serde::{Deserialize, Serialize};
6use std::{str::FromStr, time::SystemTime};
7use warg_crypto::hash::{AnyHash, HashAlgorithm};
8use warg_crypto::signing;
9
10/// A package record is a collection of entries published together by the same author
11#[derive(Debug, Clone, PartialEq, Eq)]
12pub struct PackageRecord {
13    /// The hash of the previous package record envelope
14    pub prev: Option<RecordId>,
15    /// The version of the registry protocol used
16    pub version: u32,
17    /// When this record was published
18    pub timestamp: SystemTime,
19    /// The entries being published in this record
20    pub entries: Vec<PackageEntry>,
21}
22
23impl crate::Record for PackageRecord {
24    fn contents(&self) -> IndexSet<&AnyHash> {
25        self.entries
26            .iter()
27            .filter_map(PackageEntry::content)
28            .collect()
29    }
30}
31
32/// Each permission represents the ability to use the specified entry
33#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize)]
34#[serde(rename_all = "camelCase")]
35#[non_exhaustive]
36pub enum Permission {
37    Release,
38    Yank,
39}
40
41impl Permission {
42    /// Gets an array of all permissions.
43    pub const fn all() -> [Permission; 2] {
44        [Permission::Release, Permission::Yank]
45    }
46}
47
48impl fmt::Display for Permission {
49    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50        match self {
51            Permission::Release => write!(f, "release"),
52            Permission::Yank => write!(f, "yank"),
53        }
54    }
55}
56
57impl FromStr for Permission {
58    type Err = String;
59
60    fn from_str(s: &str) -> Result<Self, Self::Err> {
61        match s {
62            "release" => Ok(Permission::Release),
63            "yank" => Ok(Permission::Yank),
64            _ => Err(format!("invalid permission {s:?}")),
65        }
66    }
67}
68
69#[derive(Debug, Clone, PartialEq, Eq)]
70#[non_exhaustive]
71pub enum PackageEntry {
72    /// Initializes a package log.
73    /// Must be the first entry of every log and not appear elsewhere.
74    Init {
75        /// The hash algorithm this log will use for linking
76        hash_algorithm: HashAlgorithm,
77        /// The key of the original package maintainer
78        key: signing::PublicKey,
79    },
80    /// Grant the specified key a permission.
81    /// The author of this entry must have the permission.
82    GrantFlat {
83        key: signing::PublicKey,
84        permissions: Vec<Permission>,
85    },
86    /// Remove a permission from a key.
87    /// The author of this entry must have the permission.
88    RevokeFlat {
89        key_id: signing::KeyID,
90        permissions: Vec<Permission>,
91    },
92    /// Release a version of a package.
93    /// The version must not have been released yet.
94    Release { version: Version, content: AnyHash },
95    /// Yank a version of a package.
96    /// The version must have been released and not yanked.
97    Yank { version: Version },
98}
99
100impl PackageEntry {
101    /// Check permission is required to submit this entry
102    pub fn required_permission(&self) -> Option<Permission> {
103        match self {
104            Self::Init { .. } | Self::GrantFlat { .. } | Self::RevokeFlat { .. } => None,
105            Self::Release { .. } => Some(Permission::Release),
106            Self::Yank { .. } => Some(Permission::Yank),
107        }
108    }
109
110    /// Gets the content associated with the entry.
111    ///
112    /// Returns `None` if the entry does not have content.
113    pub fn content(&self) -> Option<&AnyHash> {
114        match self {
115            Self::Release { content, .. } => Some(content),
116            _ => None,
117        }
118    }
119}