1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4use core::fmt;
5use std::error::Error;
6
7use use_oci_annotation::Annotation;
8use use_oci_descriptor::OciDescriptor;
9
10#[derive(Clone, Copy, Debug, Eq, PartialEq)]
12pub enum ManifestError {
13 UnsupportedSchemaVersion,
14}
15
16impl fmt::Display for ManifestError {
17 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
18 match self {
19 Self::UnsupportedSchemaVersion => {
20 formatter.write_str("unsupported OCI manifest schema version")
21 },
22 }
23 }
24}
25
26impl Error for ManifestError {}
27
28#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
30pub struct SchemaVersion(u8);
31
32impl SchemaVersion {
33 pub const fn new(value: u8) -> Result<Self, ManifestError> {
35 if value == 2 {
36 Ok(Self(value))
37 } else {
38 Err(ManifestError::UnsupportedSchemaVersion)
39 }
40 }
41
42 #[must_use]
44 pub const fn as_u8(self) -> u8 {
45 self.0
46 }
47}
48
49impl Default for SchemaVersion {
50 fn default() -> Self {
51 Self(2)
52 }
53}
54
55#[derive(Clone, Debug, Eq, PartialEq)]
57pub struct OciManifest {
58 schema_version: SchemaVersion,
59 config: OciDescriptor,
60 layers: Vec<OciDescriptor>,
61 subject: Option<OciDescriptor>,
62 annotations: Vec<Annotation>,
63}
64
65impl OciManifest {
66 #[must_use]
68 pub fn new(config: OciDescriptor) -> Self {
69 Self {
70 schema_version: SchemaVersion::default(),
71 config,
72 layers: Vec::new(),
73 subject: None,
74 annotations: Vec::new(),
75 }
76 }
77
78 #[must_use]
80 pub fn with_layer(mut self, layer: OciDescriptor) -> Self {
81 self.layers.push(layer);
82 self
83 }
84
85 #[must_use]
87 pub fn with_subject(mut self, subject: OciDescriptor) -> Self {
88 self.subject = Some(subject);
89 self
90 }
91
92 #[must_use]
94 pub fn with_annotation(mut self, annotation: Annotation) -> Self {
95 self.annotations.push(annotation);
96 self
97 }
98
99 #[must_use]
101 pub const fn schema_version(&self) -> SchemaVersion {
102 self.schema_version
103 }
104
105 #[must_use]
107 pub const fn config(&self) -> &OciDescriptor {
108 &self.config
109 }
110
111 #[must_use]
113 pub fn layers(&self) -> &[OciDescriptor] {
114 &self.layers
115 }
116
117 #[must_use]
119 pub const fn subject(&self) -> Option<&OciDescriptor> {
120 self.subject.as_ref()
121 }
122
123 #[must_use]
125 pub fn annotations(&self) -> &[Annotation] {
126 &self.annotations
127 }
128}
129
130#[derive(Clone, Debug, Default, Eq, PartialEq)]
132pub struct ManifestAnnotations(Vec<Annotation>);
133
134impl ManifestAnnotations {
135 #[must_use]
137 pub const fn new() -> Self {
138 Self(Vec::new())
139 }
140
141 #[must_use]
143 pub fn with_annotation(mut self, annotation: Annotation) -> Self {
144 self.0.push(annotation);
145 self
146 }
147
148 #[must_use]
150 pub fn as_slice(&self) -> &[Annotation] {
151 &self.0
152 }
153}
154
155#[cfg(test)]
156mod tests {
157 use super::{ManifestAnnotations, ManifestError, OciManifest, SchemaVersion};
158 use use_oci_annotation::Annotation;
159 use use_oci_descriptor::{DescriptorSize, OciDescriptor};
160 use use_oci_digest::OciDigest;
161 use use_oci_media_type::OciMediaType;
162
163 const SHA: &str = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
164
165 fn descriptor() -> Result<OciDescriptor, Box<dyn std::error::Error>> {
166 let digest: OciDigest = format!("sha256:{SHA}").parse()?;
167 Ok(OciDescriptor::new(
168 OciMediaType::image_config(),
169 digest,
170 DescriptorSize::new(1),
171 ))
172 }
173
174 #[test]
175 fn models_manifest_layer_lists() -> Result<(), Box<dyn std::error::Error>> {
176 let manifest = OciManifest::new(descriptor()?)
177 .with_layer(descriptor()?)
178 .with_annotation(Annotation::title("Example")?);
179 let annotations = ManifestAnnotations::new().with_annotation(Annotation::title("Example")?);
180
181 assert_eq!(manifest.schema_version().as_u8(), 2);
182 assert_eq!(manifest.layers().len(), 1);
183 assert_eq!(manifest.annotations().len(), 1);
184 assert_eq!(annotations.as_slice().len(), 1);
185 assert_eq!(
186 SchemaVersion::new(1),
187 Err(ManifestError::UnsupportedSchemaVersion)
188 );
189 Ok(())
190 }
191}