playdate_fs/
options.rs

1use alloc::borrow::ToOwned;
2use sys::ffi::FileOptions;
3
4use crate::api;
5use crate::Path;
6use crate::error::ApiError;
7
8
9/// Extension for [`sys::ffi::FileOptions`] make it looks like [`std::fs::OpenOptions`].
10// TODO: FileOptionsExt should be const_trait
11pub trait FileOptionsExt: Into<FileOptions> {
12	/// Creates new empty file options.
13	fn new() -> Self;
14
15	fn read(self, read: bool) -> Self;
16	fn read_data(self, read: bool) -> Self;
17
18	fn write(self, write: bool) -> Self;
19	fn append(self, append: bool) -> Self;
20
21	fn is_empty(&self) -> bool;
22	fn is_read(&self) -> bool;
23	fn is_read_data(&self) -> bool;
24	fn is_write(&self) -> bool;
25	fn is_append(&self) -> bool;
26
27	fn is_read_any(&self) -> bool;
28	fn is_write_any(&self) -> bool;
29}
30
31pub trait OpenOptions: Into<FileOptions> {
32	/// Open file with this options.
33	fn open<P: AsRef<Path>>(&self, path: P) -> Result<crate::file::File<api::Cache>, ApiError>;
34
35	/// Open file with this options, using given `api`.
36	fn open_using<Api: api::Api, P: AsRef<Path>>(&self,
37	                                             api: Api,
38	                                             path: P)
39	                                             -> Result<crate::file::File<Api>, ApiError>;
40}
41
42impl OpenOptions for FileOptions {
43	#[inline(always)]
44	fn open<P: AsRef<Path>>(&self, path: P) -> Result<crate::file::File<api::Cache>, ApiError> {
45		crate::ops::open(api::Cache::default(), path, self.to_owned())
46	}
47
48	#[inline(always)]
49	fn open_using<Api: api::Api, P: AsRef<Path>>(&self,
50	                                             api: Api,
51	                                             path: P)
52	                                             -> Result<crate::file::File<Api>, ApiError> {
53		crate::ops::open(api, path, self.to_owned())
54	}
55}
56
57
58impl FileOptionsExt for FileOptions {
59	fn new() -> Self { FileOptions(0) }
60
61	/// Read access to Game’s package (bundle) directory.
62	fn read(mut self, read: bool) -> Self {
63		if read {
64			self.0 |= FileOptions::kFileRead.0;
65		} else {
66			self.0 &= 255 - FileOptions::kFileRead.0;
67		}
68		self
69	}
70
71	/// Read access to Game’s data directory.
72	fn read_data(mut self, read: bool) -> Self {
73		if read {
74			self.0 |= FileOptions::kFileReadData.0;
75		} else {
76			self.0 &= 255 - FileOptions::kFileReadData.0;
77		}
78		self
79	}
80
81	/// Write access to Game’s data directory.
82	fn write(mut self, write: bool) -> Self {
83		if write {
84			self.0 |= FileOptions::kFileWrite.0;
85		} else {
86			self.0 &= 255 - FileOptions::kFileWrite.0;
87		}
88		self
89	}
90
91	/// Write access to Game’s data directory.
92	fn append(mut self, append: bool) -> Self {
93		if append {
94			self.0 |= FileOptions::kFileAppend.0;
95		} else {
96			self.0 &= 255 - FileOptions::kFileAppend.0;
97		}
98		self
99	}
100
101
102	fn is_empty(&self) -> bool { self.0 == 0 }
103	fn is_read(&self) -> bool { (self.0 & FileOptions::kFileRead.0) != 0 }
104	fn is_read_data(&self) -> bool { (self.0 & FileOptions::kFileReadData.0) != 0 }
105	fn is_write(&self) -> bool { (self.0 & FileOptions::kFileWrite.0) != 0 }
106	fn is_append(&self) -> bool { (self.0 & FileOptions::kFileAppend.0) != 0 }
107
108	fn is_read_any(&self) -> bool { self.is_read() || self.is_read_data() }
109	fn is_write_any(&self) -> bool { self.is_write() || self.is_append() }
110}
111
112#[cfg(test)]
113mod tests {
114	use super::{FileOptionsExt, FileOptions};
115	use FileOptions as FO;
116
117	#[test]
118	fn fo_empty() {
119		let fo = FO::new();
120		assert!(fo.is_empty());
121		assert_eq!(fo.0, 0);
122		assert!(!fo.is_read());
123		assert!(!fo.is_read_data());
124		assert!(!fo.is_write());
125		assert!(!fo.is_append());
126	}
127
128	#[test]
129	fn fo_read() {
130		let fo = FO::new().read(true);
131		assert_eq!(fo.0, FO::kFileRead.0);
132		assert!(fo.is_read());
133		assert!(!fo.is_read_data());
134		assert!(!fo.is_write());
135		assert!(!fo.is_append());
136		assert!(!fo.is_empty());
137	}
138
139	#[test]
140	fn fo_read_data() {
141		let fo = FO::new().read_data(true);
142		assert_ne!(fo.0, FO::kFileRead.0);
143		assert_eq!(fo.0, FO::kFileReadData.0);
144		assert!(!fo.is_read());
145		assert!(fo.is_read_data());
146		assert!(!fo.is_write());
147		assert!(!fo.is_append());
148		assert!(!fo.is_empty());
149
150		let fo = FO::new().read(true).read_data(true);
151		assert_ne!(fo.0, FO::kFileRead.0);
152		assert_ne!(fo.0, FO::kFileReadData.0);
153		assert!(fo.is_read());
154		assert!(fo.is_read_data());
155		assert!(!fo.is_write());
156		assert!(!fo.is_append());
157		assert!(!fo.is_empty());
158	}
159
160	#[test]
161	fn fo_many() {
162		let fo = FO::new().read(true)
163		                  .write(true)
164		                  .read_data(true)
165		                  .append(true)
166		                  .read(false)
167		                  .append(false);
168		assert!(!fo.is_read());
169		assert!(fo.is_read_data());
170		assert!(fo.is_write());
171		assert!(!fo.is_append());
172		assert!(!fo.is_empty());
173	}
174}