oci_spec/image/oci_layout.rs
1use crate::{
2 error::{OciSpecError, Result},
3 from_file, from_reader, to_file, to_string, to_writer,
4};
5use derive_builder::Builder;
6use getset::{Getters, Setters};
7use serde::{Deserialize, Serialize};
8use std::{
9 io::{Read, Write},
10 path::Path,
11};
12
13#[derive(Builder, Clone, Debug, Deserialize, Eq, Getters, Setters, PartialEq, Serialize)]
14#[serde(rename_all = "camelCase")]
15#[builder(
16 pattern = "owned",
17 setter(into, strip_option),
18 build_fn(error = "OciSpecError")
19)]
20/// The oci layout JSON object serves as a marker for the base of an Open Container Image Layout
21/// and to provide the version of the image-layout in use. The imageLayoutVersion value will align
22/// with the OCI Image Specification version at the time changes to the layout are made, and will
23/// pin a given version until changes to the image layout are required.
24pub struct OciLayout {
25 /// This REQUIRED property specifies the image layout version.
26 #[getset(get = "pub", set = "pub")]
27 image_layout_version: String,
28}
29
30impl OciLayout {
31 /// Attempts to load an oci layout from a file.
32 /// # Errors
33 /// This function will return an [OciSpecError::Io](crate::OciSpecError::Io)
34 /// if the file does not exist or an
35 /// [OciSpecError::SerDe](crate::OciSpecError::SerDe) if the oci layout
36 /// cannot be deserialized.
37 /// # Example
38 /// ``` no_run
39 /// use oci_spec::image::OciLayout;
40 ///
41 /// let oci_layout = OciLayout::from_file("oci-layout").unwrap();
42 /// ```
43 pub fn from_file<P: AsRef<Path>>(path: P) -> Result<OciLayout> {
44 from_file(path)
45 }
46
47 /// Attempts to load an oci layout from a stream.
48 /// # Errors
49 /// This function will return an [OciSpecError::SerDe](crate::OciSpecError::SerDe)
50 /// if the oci layout cannot be deserialized.
51 /// # Example
52 /// ``` no_run
53 /// use oci_spec::image::OciLayout;
54 /// use std::fs::File;
55 ///
56 /// let reader = File::open("oci-layout").unwrap();
57 /// let oci_layout = OciLayout::from_reader(reader).unwrap();
58 /// ```
59 pub fn from_reader<R: Read>(reader: R) -> Result<OciLayout> {
60 from_reader(reader)
61 }
62
63 /// Attempts to write an oci layout to a file as JSON. If the file already exists, it
64 /// will be overwritten.
65 /// # Errors
66 /// This function will return an [OciSpecError::SerDe](crate::OciSpecError::SerDe) if
67 /// the oci layout cannot be serialized.
68 /// # Example
69 /// ``` no_run
70 /// use oci_spec::image::OciLayout;
71 ///
72 /// let oci_layout = OciLayout::from_file("oci-layout").unwrap();
73 /// oci_layout.to_file("oci-layout").unwrap();
74 /// ```
75 pub fn to_file<P: AsRef<Path>>(&self, path: P) -> Result<()> {
76 to_file(&self, path, false)
77 }
78
79 /// Attempts to write an oci layout to a file as pretty printed JSON. If the file
80 /// already exists, it will be overwritten.
81 /// # Errors
82 /// This function will return an [OciSpecError::SerDe](crate::OciSpecError::SerDe) if
83 /// the oci layout cannot be serialized.
84 /// # Example
85 /// ``` no_run
86 /// use oci_spec::image::OciLayout;
87 ///
88 /// let oci_layout = OciLayout::from_file("oci-layout").unwrap();
89 /// oci_layout.to_file_pretty("my-oci-layout").unwrap();
90 /// ```
91 pub fn to_file_pretty<P: AsRef<Path>>(&self, path: P) -> Result<()> {
92 to_file(&self, path, true)
93 }
94
95 /// Attempts to write an oci layout to a stream as JSON.
96 /// # Errors
97 /// This function will return an [OciSpecError::SerDe](crate::OciSpecError::SerDe) if
98 /// the oci layout cannot be serialized.
99 /// # Example
100 /// ``` no_run
101 /// use oci_spec::image::OciLayout;
102 ///
103 /// let oci_layout = OciLayout::from_file("oci-layout").unwrap();
104 /// let mut writer = Vec::new();
105 /// oci_layout.to_writer(&mut writer);
106 /// ```
107 pub fn to_writer<W: Write>(&self, writer: &mut W) -> Result<()> {
108 to_writer(&self, writer, false)
109 }
110
111 /// Attempts to write an oci layout to a stream as pretty printed JSON.
112 /// # Errors
113 /// This function will return an [OciSpecError::SerDe](crate::OciSpecError::SerDe) if
114 /// the oci layout cannot be serialized.
115 /// # Example
116 /// ``` no_run
117 /// use oci_spec::image::OciLayout;
118 ///
119 /// let oci_layout = OciLayout::from_file("oci-layout").unwrap();
120 /// let mut writer = Vec::new();
121 /// oci_layout.to_writer_pretty(&mut writer);
122 /// ```
123 pub fn to_writer_pretty<W: Write>(&self, writer: &mut W) -> Result<()> {
124 to_writer(&self, writer, true)
125 }
126
127 /// Attempts to write an oci layout to a string as JSON.
128 /// # Errors
129 /// This function will return an [OciSpecError::SerDe](crate::OciSpecError::SerDe) if
130 /// the oci layout configuration cannot be serialized.
131 /// # Example
132 /// ``` no_run
133 /// use oci_spec::image::OciLayout;
134 ///
135 /// let oci_layout = OciLayout::from_file("oci-layout").unwrap();
136 /// let json_str = oci_layout.to_string().unwrap();
137 /// ```
138 pub fn to_string(&self) -> Result<String> {
139 to_string(&self, false)
140 }
141
142 /// Attempts to write an oci layout to a string as pretty printed JSON.
143 /// # Errors
144 /// This function will return an [OciSpecError::SerDe](crate::OciSpecError::SerDe) if
145 /// the oci layout configuration cannot be serialized.
146 /// # Example
147 /// ``` no_run
148 /// use oci_spec::image::OciLayout;
149 ///
150 /// let oci_layout = OciLayout::from_file("oci-layout").unwrap();
151 /// let json_str = oci_layout.to_string_pretty().unwrap();
152 /// ```
153 pub fn to_string_pretty(&self) -> Result<String> {
154 to_string(&self, true)
155 }
156}
157
158#[cfg(test)]
159mod tests {
160 use std::{fs, path::PathBuf};
161
162 use super::*;
163
164 fn create_oci_layout() -> OciLayout {
165 OciLayoutBuilder::default()
166 .image_layout_version("lorem ipsum")
167 .build()
168 .expect("build oci layout")
169 }
170
171 fn get_oci_layout_path() -> PathBuf {
172 PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("test/data/oci-layout")
173 }
174
175 #[test]
176 fn load_oci_layout_from_file() {
177 // arrange
178 let oci_layout_path = get_oci_layout_path();
179
180 // act
181 let actual = OciLayout::from_file(oci_layout_path).expect("from file");
182
183 // assert
184 let expected = create_oci_layout();
185 assert_eq!(actual, expected);
186 }
187
188 #[test]
189 fn load_oci_layout_from_reader() {
190 // arrange
191 let reader = fs::read(get_oci_layout_path()).expect("read oci-layout");
192
193 // act
194 let actual = OciLayout::from_reader(&*reader).expect("from reader");
195
196 // assert
197 let expected = create_oci_layout();
198 assert_eq!(actual, expected);
199 }
200
201 #[test]
202 fn save_oci_layout_to_file() {
203 // arrange
204 let tmp = std::env::temp_dir().join("save_oci_layout_to_file");
205 fs::create_dir_all(&tmp).expect("create test directory");
206 let oci_layout = create_oci_layout();
207 let oci_layout_path = tmp.join("oci-layout");
208
209 // act
210 oci_layout
211 .to_file_pretty(&oci_layout_path)
212 .expect("write oci-layout to file");
213
214 // assert
215 let actual = fs::read_to_string(oci_layout_path).expect("read actual");
216 let expected = fs::read_to_string(get_oci_layout_path()).expect("read expected");
217 assert_eq!(actual, expected);
218 }
219
220 #[test]
221 fn save_oci_layout_to_writer() {
222 // arrange
223 let mut actual = Vec::new();
224 let oci_layout = create_oci_layout();
225
226 // act
227 oci_layout.to_writer_pretty(&mut actual).expect("to writer");
228
229 // assert
230 let expected = fs::read(get_oci_layout_path()).expect("read expected");
231 assert_eq!(actual, expected);
232 }
233
234 #[test]
235 fn save_oci_layout_to_string() {
236 // arrange
237 let oci_layout = create_oci_layout();
238
239 // act
240 let actual = oci_layout.to_string_pretty().expect("to string");
241
242 // assert
243 let expected = fs::read_to_string(get_oci_layout_path()).expect("read expected");
244 assert_eq!(actual, expected);
245 }
246}