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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
//! A handle to an opened file.
use crate::{
    alloc::CBox,
    core::{
        result::NSTDResult,
        slice::{NSTDSlice, NSTDSliceMut},
        str::NSTDStr,
    },
    io::{NSTDIOError, NSTDIOResult},
    string::NSTDString,
    vec::NSTDVec,
    NSTDUInt8,
};
use nstdapi::nstdapi;
use std::fs::File;
#[cfg(unix)]
use std::os::unix::io::AsRawFd;

/// Creates the file upon opening if it does not already exist.
///
/// Either of the `NSTD_FILE_WRITE` or `NSTD_FILE_APPEND` options must also be toggled for the file
/// to be created.
pub const NSTD_FILE_CREATE: NSTDUInt8 = 1;

/// Open a file in read mode.
pub const NSTD_FILE_READ: NSTDUInt8 = 1 << 1;

/// Open a file in write mode.
pub const NSTD_FILE_WRITE: NSTDUInt8 = 1 << 2;

/// Open a file in writing mode without overwriting saved data.
pub const NSTD_FILE_APPEND: NSTDUInt8 = 1 << 3;

/// Open a file in truncate mode, this will set the file's length to 0 upon opening.
pub const NSTD_FILE_TRUNC: NSTDUInt8 = 1 << 4;

/// A handle to an opened file.
#[nstdapi]
pub struct NSTDFile {
    /// The inner [File].
    f: CBox<File>,
}

/// A result type yielding an `NSTDFile` on success.
pub type NSTDFileResult = NSTDResult<NSTDFile, NSTDIOError>;

/// Opens file on the filesystem and returns a handle to it.
///
/// # Parameters:
///
/// - `const NSTDStr *name` - The name of the file to create.
///
/// - `NSTDUInt8 mask` - A bit mask for toggling the file's different open options.
///
/// # Returns
///
/// `NSTDFileResult file` - A handle to the opened file, or the IO error on failure.
///
/// # Safety
///
/// This operation can cause undefined behavior if `name`'s data is invalid.
#[nstdapi]
pub unsafe fn nstd_fs_file_open(name: &NSTDStr, mask: NSTDUInt8) -> NSTDFileResult {
    // Attempt to create/open the file in write mode.
    match File::options()
        .create((mask & NSTD_FILE_CREATE) != 0)
        .read((mask & NSTD_FILE_READ) != 0)
        .write((mask & NSTD_FILE_WRITE) != 0)
        .append((mask & NSTD_FILE_APPEND) != 0)
        .truncate((mask & NSTD_FILE_TRUNC) != 0)
        .open(name.as_str())
    {
        Ok(f) => match CBox::new(f) {
            Some(f) => NSTDResult::Ok(NSTDFile { f }),
            _ => NSTDResult::Err(NSTDIOError::NSTD_IO_ERROR_OUT_OF_MEMORY),
        },
        Err(err) => NSTDResult::Err(NSTDIOError::from_err(err.kind())),
    }
}

/// Writes some data to a file & returns how many bytes were written.
///
/// # Parameters:
///
/// - `NSTDFile *file` - A handle to an open file.
///
/// - `const NSTDSlice *bytes` - The data to write to the file.
///
/// # Returns
///
/// `NSTDIOResult written` - The number of bytes written to `handle` on success, or the I/O
/// operation error code on failure.
///
/// # Safety
///
/// This function's caller must guarantee validity of `bytes`.
#[inline]
#[nstdapi]
pub unsafe fn nstd_fs_file_write(file: &mut NSTDFile, bytes: &NSTDSlice) -> NSTDIOResult {
    #[cfg(not(unix))]
    return crate::io::stdio::write(&mut *file.f, bytes);
    #[cfg(unix)]
    return crate::os::unix::io::stdio::write(file.f.as_raw_fd(), bytes).into();
}

/// Writes a whole buffer to a file.
///
/// # Parameters:
///
/// - `NSTDFile *file` - A handle to an open file.
///
/// - `const NSTDSlice *bytes` - The data to write to the file.
///
/// # Returns
///
/// `NSTDIOError errc` - The I/O operation error code.
///
/// # Safety
///
/// This function's caller must guarantee validity of `bytes`.
#[inline]
#[nstdapi]
pub unsafe fn nstd_fs_file_write_all(file: &mut NSTDFile, bytes: &NSTDSlice) -> NSTDIOError {
    #[cfg(not(unix))]
    return crate::io::stdio::write_all(&mut *file.f, bytes);
    #[cfg(unix)]
    return crate::os::unix::io::stdio::write_all(file.f.as_raw_fd(), bytes).into();
}

/// Flushes a file stream.
///
/// # Parameters:
///
/// - `NSTDFile *file` - The file stream.
///
/// # Returns
///
/// `NSTDIOError errc` - The I/O operation error code.
#[inline]
#[nstdapi]
pub fn nstd_fs_file_flush(file: &mut NSTDFile) -> NSTDIOError {
    crate::io::stdio::flush(&mut *file.f)
}

/// Reads some data from an open file into a buffer.
///
/// # Parameters:
///
/// - `NSTDFile *file` - A handle to the opened file.
///
/// - `NSTDSliceMut *buffer` - The buffer to start filling with data from the file.
///
/// # Returns
///
/// `NSTDIOResult read` - The number of bytes read from `handle` on success, or the I/O operation
/// error code on failure.
///
/// # Safety
///
/// `buffer`'s data must be valid for writes.
#[inline]
#[nstdapi]
pub unsafe fn nstd_fs_file_read(file: &mut NSTDFile, buffer: &mut NSTDSliceMut) -> NSTDIOResult {
    #[cfg(not(unix))]
    return crate::io::stdio::read(&mut *file.f, buffer);
    #[cfg(unix)]
    return crate::os::unix::io::stdio::read(file.f.as_raw_fd(), buffer).into();
}

/// Continuously reads data from `file` into a buffer until EOF is reached.
///
/// # Note
///
/// If extending the buffer fails, an error code of `NSTD_IO_ERROR_OUT_OF_MEMORY` will be returned.
/// This does not mean there were no bytes read from `file` in this case.
///
/// # Parameters:
///
/// - `NSTDFile *file` - A handle to the file.
///
/// - `NSTDVec *buffer` - The buffer to be extended with data from the file.
///
/// # Returns
///
/// `NSTDIOResult read` - The number of bytes read from `handle` on success, or the I/O operation
/// error code on failure.
#[inline]
#[nstdapi]
pub fn nstd_fs_file_read_all(file: &mut NSTDFile, buffer: &mut NSTDVec) -> NSTDIOResult {
    #[cfg(not(unix))]
    return crate::io::stdio::read_all(&mut *file.f, buffer);
    #[cfg(unix)]
    // SAFETY: `file` owns the file descriptor.
    return unsafe { crate::os::unix::io::stdio::read_all(file.f.as_raw_fd(), buffer).into() };
}

/// Continuously reads UTF-8 data from `file` into a string buffer until EOF is reached.
///
/// # Note
///
/// If extending the buffer fails, an error code of `NSTD_IO_ERROR_OUT_OF_MEMORY` will be returned.
/// This does not mean there were no bytes read from `file` in this case.
///
/// # Parameters:
///
/// - `NSTDFile *file` - A handle to the file.
///
/// - `NSTDString *buffer` - The buffer to be extended with data from the file.
///
/// # Returns
///
/// `NSTDIOResult read` - The number of bytes read from `handle` on success, or the I/O operation
/// error code on failure.
#[inline]
#[nstdapi]
pub fn nstd_fs_file_read_to_string(file: &mut NSTDFile, buffer: &mut NSTDString) -> NSTDIOResult {
    #[cfg(not(unix))]
    return crate::io::stdio::read_to_string(&mut *file.f, buffer);
    #[cfg(unix)]
    // SAFETY: `file` owns the file descriptor.
    return unsafe {
        crate::os::unix::io::stdio::read_to_string(file.f.as_raw_fd(), buffer).into()
    };
}

/// Reads enough data from `file` to fill the entirety of `buffer`.
///
/// # Note
///
/// This function will return an error code of `NSTD_IO_ERROR_INVALID_INPUT` if the buffer's
/// element size is not 1.
///
/// # Parameters:
///
/// - `NSTDFile *file` - A handle to the file.
///
/// - `NSTDSliceMut *buffer` - The buffer to fill with data from the file.
///
/// # Returns
///
/// `NSTDIOError errc` - The I/O operation error code.
///
/// # Safety
///
/// `buffer` must be valid for writes.
#[inline]
#[nstdapi]
pub unsafe fn nstd_fs_file_read_exact(
    file: &mut NSTDFile,
    buffer: &mut NSTDSliceMut,
) -> NSTDIOError {
    #[cfg(not(unix))]
    return crate::io::stdio::read_exact(&mut *file.f, buffer);
    #[cfg(unix)]
    return crate::os::unix::io::stdio::read_exact(file.f.as_raw_fd(), buffer).into();
}

/// Closes a file handle.
///
/// # Parameters:
///
/// - `NSTDFile file` - The file handle to close.
#[inline]
#[nstdapi]
#[allow(unused_variables)]
pub fn nstd_fs_file_close(file: NSTDFile) {}