1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
use core::ffi::c_uint;
use alloc::vec::Vec;

use sys::error::OkOrNullFnErr;
use sys::ffi::FileOptions;
use sys::ffi::SDFile;

use crate::FileSystem;
use crate::Fs;
use crate::Path;
use crate::ApiError;
use crate::error::Error;
use crate::options::FileOptionsExt;
use crate::options::OpenOptions;
use crate::seek::SeekFrom;


#[must_use = "File will closed on drop"]
#[cfg_attr(feature = "bindings-derive-debug", derive(Debug))]
pub struct File(pub(crate) *mut SDFile);

impl File {
	// Ensure the inner pointer is valid (not null).
	pub fn valid(&self) -> bool { !self.0.is_null() }

	/// Attempts to open a file in read-only mode.
	///
	/// See the [`OpenOptions::open`] method for more details.
	///
	/// https://sdk.play.date/2.0.1/Inside%20Playdate%20with%20C.html#f-file.open
	#[must_use]
	pub fn open<P: AsRef<Path>>(path: P, data_dir: bool) -> Result<File, ApiError> {
		Fs::new()?.open(path, FileOptions::new().read(true).read_data(data_dir))
	}

	/// Reads up to `len` bytes from the file into the buffer `buf`.
	/// Returns the number of bytes read (0 indicating end of file).
	pub fn read(&mut self, to: &mut Vec<u8>, len: c_uint) -> Result<c_uint, ApiError> {
		Fs::new()?.read(self, to, len)
	}

	/// Writes the buffer of bytes buf to the file. Returns the number of bytes written.
	pub fn write(&mut self, from: &[u8]) -> Result<c_uint, ApiError> { Fs::new()?.write(self, from) }

	/// Flushes the output buffer of file immediately. Returns the number of bytes written.
	pub fn flush(&mut self) -> Result<c_uint, ApiError> { Fs::new()?.flush(self) }

	/// Returns the current read/write offset in the given file handle.
	#[must_use]
	pub fn tell(&mut self) -> Result<c_uint, ApiError> { Fs::new()?.tell(self) }

	/// Sets the read/write offset in the given file handle to pos, relative to the `whence`.
	/// `Whence::Set` is relative to the beginning of the file,
	/// `Whence::Cur` is relative to the current position of the file pointer, and
	/// `Whence::End` is relative to the end of the file.
	/// Returns 0 on success.
	pub fn seek(&mut self, pos: SeekFrom) -> Result<c_uint, ApiError> { Fs::new()?.seek(self, pos) }

	/// Closes the this file handle.
	pub fn close(self) -> Result<(), ApiError> { Fs::new()?.close(self) }


	/// Creates a blank new set of options ready for configuration.
	/// All options are initially set to false.
	///
	/// It is equivalent to `FileOptions::new()`.
	#[must_use]
	pub fn options() -> impl OpenOptions + FileOptionsExt { FileOptions::new() }
}

impl Drop for File {
	fn drop(&mut self) {
		if self.valid() {
			fn print_err<E: core::fmt::Display>(err: E) { println!("Err on file-drop: {err}") }
			let _ = Fs::new().map(|fs| {
				                 let _ = fs.0
				                           .close
				                           .ok_or_null()
				                           .map(|f| {
					                           let result = unsafe { f(self.0) };
					                           self.0 = core::ptr::null_mut();
					                           match Error::ok_from_code(result) {
						                           Ok(_) => (),
					                              Err(err) => print_err(err),
					                           }
				                           })
				                           .map_err(|err| print_err(err))
				                           .ok();
			                 })
			                 .map_err(|err| print_err(err))
			                 .ok();
		}
	}
}