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}