nstd_sys/
io.rs

1//! Provides functionality for interacting with the standard I/O streams.
2pub mod stderr;
3pub mod stdin;
4pub(crate) mod stdio;
5pub mod stdout;
6#[cfg(unix)]
7use crate::os::unix::io::{
8    NSTDUnixIOError::{
9        self, NSTD_UNIX_IO_ERROR_BLOCKING, NSTD_UNIX_IO_ERROR_BROKEN_PIPE,
10        NSTD_UNIX_IO_ERROR_CONNECTION_RESET, NSTD_UNIX_IO_ERROR_INTERRUPTED,
11        NSTD_UNIX_IO_ERROR_INVALID_DATA, NSTD_UNIX_IO_ERROR_INVALID_INPUT, NSTD_UNIX_IO_ERROR_NONE,
12        NSTD_UNIX_IO_ERROR_NOT_FOUND, NSTD_UNIX_IO_ERROR_NO_CONNECTION,
13        NSTD_UNIX_IO_ERROR_OUT_OF_MEMORY, NSTD_UNIX_IO_ERROR_PERMISSION_DENIED,
14        NSTD_UNIX_IO_ERROR_TIMED_OUT, NSTD_UNIX_IO_ERROR_UNEXPECTED_EOF,
15    },
16    NSTDUnixIOResult,
17};
18use crate::{
19    core::{result::NSTDResult, str::NSTDStr},
20    string::{nstd_string_pop, NSTDString},
21    vec::NSTDVec,
22    NSTDUInt,
23};
24use nstdapi::nstdapi;
25use std::io::{ErrorKind, Write};
26
27/// An error type for I/O operations.
28#[nstdapi]
29#[derive(Clone, Copy, PartialEq, Eq)]
30#[allow(non_camel_case_types)]
31pub enum NSTDIOError {
32    /// No error occurred.
33    NSTD_IO_ERROR_NONE,
34    /// An unknown/other error occurred.
35    NSTD_IO_ERROR_UNKNOWN,
36    /// An entity, such as a file, was not found.
37    NSTD_IO_ERROR_NOT_FOUND,
38    /// Permission was denied.
39    NSTD_IO_ERROR_PERMISSION_DENIED,
40    /// The connection was refused by a remote server.
41    NSTD_IO_ERROR_CONNECTION_REFUSED,
42    /// The connection was reset by a remote server.
43    NSTD_IO_ERROR_CONNECTION_RESET,
44    /// The connection was terminated by a remote server.
45    NSTD_IO_ERROR_CONNECTION_TERMINATED,
46    /// There is no connection.
47    NSTD_IO_ERROR_NO_CONNECTION,
48    /// A socket address could not be used.
49    NSTD_IO_ERROR_SOCKET_IN_USE,
50    /// An address could not be found.
51    NSTD_IO_ERROR_ADDRESS_NOT_FOUND,
52    /// The operation failed because a pipe was closed.
53    NSTD_IO_ERROR_BROKEN_PIPE,
54    /// An entity, such as a file, already exists.
55    NSTD_IO_ERROR_ALREADY_EXISTS,
56    /// The operation needs to block to complete.
57    NSTD_IO_ERROR_BLOCKING,
58    /// Some input parameter was incorrect.
59    NSTD_IO_ERROR_INVALID_INPUT,
60    /// Some input data was incorrect.
61    NSTD_IO_ERROR_INVALID_DATA,
62    /// The I/O operation's timeout expired, causing it to be canceled.
63    NSTD_IO_ERROR_TIMED_OUT,
64    /// Zero bytes were written to an output stream.
65    NSTD_IO_ERROR_WRITE_ZERO,
66    /// The operation was interrupted.
67    NSTD_IO_ERROR_INTERRUPTED,
68    /// The operation is unsupported on the current platform.
69    NSTD_IO_ERROR_UNSUPPORTED,
70    /// A reader unexpectedly reached the end of a file.
71    NSTD_IO_ERROR_UNEXPECTED_EOF,
72    /// An operation could not be completed, because it failed to allocate enough memory.
73    NSTD_IO_ERROR_OUT_OF_MEMORY,
74}
75impl NSTDIOError {
76    /// Creates a new instance of [`NSTDIOError`] from a Rust [`ErrorKind`].
77    pub(crate) const fn from_err(err: ErrorKind) -> Self {
78        match err {
79            ErrorKind::NotFound => Self::NSTD_IO_ERROR_NOT_FOUND,
80            ErrorKind::PermissionDenied => Self::NSTD_IO_ERROR_PERMISSION_DENIED,
81            ErrorKind::ConnectionRefused => Self::NSTD_IO_ERROR_CONNECTION_REFUSED,
82            ErrorKind::ConnectionReset => Self::NSTD_IO_ERROR_CONNECTION_RESET,
83            ErrorKind::ConnectionAborted => Self::NSTD_IO_ERROR_CONNECTION_TERMINATED,
84            ErrorKind::NotConnected => Self::NSTD_IO_ERROR_NO_CONNECTION,
85            ErrorKind::AddrInUse => Self::NSTD_IO_ERROR_SOCKET_IN_USE,
86            ErrorKind::AddrNotAvailable => Self::NSTD_IO_ERROR_ADDRESS_NOT_FOUND,
87            ErrorKind::BrokenPipe => Self::NSTD_IO_ERROR_BROKEN_PIPE,
88            ErrorKind::AlreadyExists => Self::NSTD_IO_ERROR_ALREADY_EXISTS,
89            ErrorKind::WouldBlock => Self::NSTD_IO_ERROR_BLOCKING,
90            ErrorKind::InvalidInput => Self::NSTD_IO_ERROR_INVALID_INPUT,
91            ErrorKind::InvalidData => Self::NSTD_IO_ERROR_INVALID_DATA,
92            ErrorKind::TimedOut => Self::NSTD_IO_ERROR_TIMED_OUT,
93            ErrorKind::WriteZero => Self::NSTD_IO_ERROR_WRITE_ZERO,
94            ErrorKind::Interrupted => Self::NSTD_IO_ERROR_INTERRUPTED,
95            ErrorKind::Unsupported => Self::NSTD_IO_ERROR_UNSUPPORTED,
96            ErrorKind::UnexpectedEof => Self::NSTD_IO_ERROR_UNEXPECTED_EOF,
97            ErrorKind::OutOfMemory => Self::NSTD_IO_ERROR_OUT_OF_MEMORY,
98            _ => Self::NSTD_IO_ERROR_UNKNOWN,
99        }
100    }
101}
102#[cfg(unix)]
103impl From<NSTDUnixIOError> for NSTDIOError {
104    /// Converts an [`NSTDUnixIOError`] into an [`NSTDIOError`].
105    fn from(err: NSTDUnixIOError) -> Self {
106        match err {
107            NSTD_UNIX_IO_ERROR_NONE => Self::NSTD_IO_ERROR_NONE,
108            NSTD_UNIX_IO_ERROR_NOT_FOUND => Self::NSTD_IO_ERROR_NOT_FOUND,
109            NSTD_UNIX_IO_ERROR_PERMISSION_DENIED => Self::NSTD_IO_ERROR_PERMISSION_DENIED,
110            NSTD_UNIX_IO_ERROR_CONNECTION_RESET => Self::NSTD_IO_ERROR_CONNECTION_RESET,
111            NSTD_UNIX_IO_ERROR_NO_CONNECTION => Self::NSTD_IO_ERROR_NO_CONNECTION,
112            NSTD_UNIX_IO_ERROR_BROKEN_PIPE => Self::NSTD_IO_ERROR_BROKEN_PIPE,
113            NSTD_UNIX_IO_ERROR_BLOCKING => Self::NSTD_IO_ERROR_BLOCKING,
114            NSTD_UNIX_IO_ERROR_INVALID_INPUT => Self::NSTD_IO_ERROR_INVALID_INPUT,
115            NSTD_UNIX_IO_ERROR_INVALID_DATA => Self::NSTD_IO_ERROR_INVALID_DATA,
116            NSTD_UNIX_IO_ERROR_TIMED_OUT => Self::NSTD_IO_ERROR_TIMED_OUT,
117            NSTD_UNIX_IO_ERROR_INTERRUPTED => Self::NSTD_IO_ERROR_INTERRUPTED,
118            NSTD_UNIX_IO_ERROR_UNEXPECTED_EOF => Self::NSTD_IO_ERROR_UNEXPECTED_EOF,
119            NSTD_UNIX_IO_ERROR_OUT_OF_MEMORY => Self::NSTD_IO_ERROR_OUT_OF_MEMORY,
120            _ => Self::NSTD_IO_ERROR_UNKNOWN,
121        }
122    }
123}
124
125/// A result type that yields an [`NSTDUInt`] representing the number of bytes read or written by an
126/// I/O operation on success and an I/O operation error code on failure.
127pub type NSTDIOResult = NSTDResult<NSTDUInt, NSTDIOError>;
128#[cfg(unix)]
129impl From<NSTDUnixIOResult> for NSTDIOResult {
130    /// Converts an [`NSTDUnixIOResult`] into an [`NSTDIOResult`].
131    #[inline]
132    fn from(value: NSTDUnixIOResult) -> Self {
133        match value {
134            NSTDResult::Ok(value) => Self::Ok(value),
135            NSTDResult::Err(err) => Self::Err(err.into()),
136        }
137    }
138}
139
140/// A result type that yields an [`NSTDVec`] on success and an I/O operation error code on failure.
141pub type NSTDIOBufferResult<'a> = NSTDResult<NSTDVec<'a>, NSTDIOError>;
142
143/// A result type that yields a UTF-8 string on success and an I/O operation error code on failure.
144pub type NSTDIOStringResult<'a> = NSTDResult<NSTDString<'a>, NSTDIOError>;
145
146/// Writes a string slice to stdout.
147///
148/// # Parameters:
149///
150/// - `const NSTDStr *output` - The string slice to write to stdout.
151///
152/// # Returns
153///
154/// `NSTDIOError errc` - The I/O operation error code.
155///
156/// # Safety
157///
158/// The provided string slice's data must be valid, else this function can cause garbage bytes to
159/// be written to stdout.
160#[nstdapi]
161pub unsafe fn nstd_io_print(output: &NSTDStr) -> NSTDIOError {
162    let mut stdout = std::io::stdout();
163    if let Err(err) = stdout.write_all(output.as_str().as_bytes()) {
164        return NSTDIOError::from_err(err.kind());
165    } else if let Err(err) = stdout.flush() {
166        return NSTDIOError::from_err(err.kind());
167    }
168    NSTDIOError::NSTD_IO_ERROR_NONE
169}
170
171/// Writes a string slice to stdout followed by a new line.
172///
173/// # Parameters:
174///
175/// - `const NSTDStr *output` - The string slice to write to stdout.
176///
177/// # Returns
178///
179/// `NSTDIOError errc` - The I/O operation error code.
180///
181/// # Safety
182///
183/// The provided string slice's data must be valid, else this function can cause garbage bytes to
184/// be written to stdout.
185#[nstdapi]
186pub unsafe fn nstd_io_print_line(output: &NSTDStr) -> NSTDIOError {
187    let mut stdout = std::io::stdout();
188    if let Err(err) = stdout.write_all(output.as_str().as_bytes()) {
189        return NSTDIOError::from_err(err.kind());
190    } else if let Err(err) = stdout.write_all(b"\n") {
191        return NSTDIOError::from_err(err.kind());
192    } else if let Err(err) = stdout.flush() {
193        return NSTDIOError::from_err(err.kind());
194    }
195    NSTDIOError::NSTD_IO_ERROR_NONE
196}
197
198/// Reads a line of UTF-8 input from stdin, discarding the newline character.
199///
200/// # Returns
201///
202/// `NSTDIOStringResult input` - The UTF-8 input from stdin on success and the I/O operation error
203/// code on failure.
204#[nstdapi]
205pub fn nstd_io_read() -> NSTDIOStringResult<'static> {
206    let mut res = nstd_io_read_line();
207    if let NSTDResult::Ok(input) = &mut res {
208        nstd_string_pop(input);
209    }
210    res
211}
212
213/// Reads a line of UTF-8 input from stdin.
214///
215/// # Returns
216///
217/// `NSTDIOStringResult input` - The UTF-8 input from stdin on success and the I/O operation error
218/// code on failure.
219#[nstdapi]
220pub fn nstd_io_read_line() -> NSTDIOStringResult<'static> {
221    // Attempt to read a line from stdin.
222    let mut input = String::new();
223    match std::io::stdin().read_line(&mut input) {
224        Ok(_) => NSTDResult::Ok(NSTDString::from_string(input)),
225        Err(err) => NSTDResult::Err(NSTDIOError::from_err(err.kind())),
226    }
227}