Browse Source

Add a new enum variant for already redirected

Also add libc errno error helper and an errno function in ctru_sys.
pull/61/head
Ian Chamberlain 3 years ago
parent
commit
042d5602ce
No known key found for this signature in database
GPG Key ID: AE5484D09405AA60
  1. 2
      ctru-rs/examples/output-3dslink.rs
  2. 39
      ctru-rs/src/error.rs
  3. 18
      ctru-rs/src/services/soc.rs
  4. 7
      ctru-sys/src/lib.rs

2
ctru-rs/examples/output-3dslink.rs

@ -24,8 +24,6 @@ fn main() { @@ -24,8 +24,6 @@ fn main() {
soc.redirect_to_3dslink(true, true)
.expect("unable to redirect stdout/err to 3dslink server");
print!("\x1b[2J\x1b[0;0H"); // Clear screen + move to 0,0
println!("Hello 3dslink!");
eprintln!("Press Start on the device to disconnect and exit.");

39
ctru-rs/src/error.rs

@ -1,4 +1,5 @@ @@ -1,4 +1,5 @@
use std::error;
use std::ffi::CStr;
use std::fmt;
use ctru_sys::result::{R_DESCRIPTION, R_LEVEL, R_MODULE, R_SUMMARY};
@ -9,7 +10,27 @@ pub type Result<T> = ::std::result::Result<T, Error>; @@ -9,7 +10,27 @@ pub type Result<T> = ::std::result::Result<T, Error>;
#[non_exhaustive]
pub enum Error {
Os(ctru_sys::Result),
Libc(String),
ServiceAlreadyActive,
OutputAlreadyRedirected,
}
impl Error {
/// Create an [`Error`] out of the last set value in `errno`. This can be used
/// to get a human-readable error string from calls to `libc` functions.
pub(crate) fn from_errno() -> Self {
let error_str = unsafe {
let errno = ctru_sys::errno();
let str_ptr = libc::strerror(errno);
// Safety: strerror should always return a valid string,
// even if the error number is unknown
CStr::from_ptr(str_ptr)
};
// Copy out of the error string, since it may be changed by other libc calls later
Self::Libc(error_str.to_string_lossy().into())
}
}
impl From<ctru_sys::Result> for Error {
@ -20,8 +41,8 @@ impl From<ctru_sys::Result> for Error { @@ -20,8 +41,8 @@ impl From<ctru_sys::Result> for Error {
impl fmt::Debug for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Error::Os(err) => f
match self {
&Self::Os(err) => f
.debug_struct("Error")
.field("raw", &format_args!("{:#08X}", err))
.field("description", &R_DESCRIPTION(err))
@ -29,7 +50,9 @@ impl fmt::Debug for Error { @@ -29,7 +50,9 @@ impl fmt::Debug for Error {
.field("summary", &R_SUMMARY(err))
.field("level", &R_LEVEL(err))
.finish(),
Error::ServiceAlreadyActive => f.debug_tuple("ServiceAlreadyActive").finish(),
Self::Libc(err) => f.debug_tuple("Libc").field(err).finish(),
Self::ServiceAlreadyActive => f.debug_tuple("ServiceAlreadyActive").finish(),
Self::OutputAlreadyRedirected => f.debug_tuple("OutputAlreadyRedirected").finish(),
}
}
}
@ -39,9 +62,13 @@ impl fmt::Debug for Error { @@ -39,9 +62,13 @@ impl fmt::Debug for Error {
// https://github.com/devkitPro/libctru/blob/master/libctru/include/3ds/result.h
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Error::Os(err) => write!(f, "libctru result code: 0x{:08X}", err),
Error::ServiceAlreadyActive => write!(f, "Service already active"),
match self {
&Self::Os(err) => write!(f, "libctru result code: 0x{:08X}", err),
Self::Libc(err) => write!(f, "{}", err),
Self::ServiceAlreadyActive => write!(f, "Service already active"),
Self::OutputAlreadyRedirected => {
write!(f, "output streams are already redirected to 3dslink")
}
}
}
}

18
ctru-rs/src/services/soc.rs

@ -4,6 +4,7 @@ use std::net::Ipv4Addr; @@ -4,6 +4,7 @@ use std::net::Ipv4Addr;
use std::sync::Mutex;
use crate::services::ServiceReference;
use crate::Error;
/// Soc service. Initializing this service will enable the use of network sockets and utilities
/// such as those found in `std::net`. The service will be closed when this struct is is dropped.
@ -74,15 +75,17 @@ impl Soc { @@ -74,15 +75,17 @@ impl Soc {
/// output was already previously redirected.
pub fn redirect_to_3dslink(&mut self, stdout: bool, stderr: bool) -> crate::Result<()> {
if self.sock_3dslink >= 0 {
// TODO AlreadyRedirected or something
return Err(crate::Error::ServiceAlreadyActive);
return Err(Error::OutputAlreadyRedirected);
}
let sock = unsafe { ctru_sys::link3dsConnectToHost(stdout, stderr) };
if sock < 0 {
Err(sock.into())
if !stdout && !stderr {
return Ok(());
}
self.sock_3dslink = unsafe { ctru_sys::link3dsConnectToHost(stdout, stderr) };
if self.sock_3dslink < 0 {
Err(Error::from_errno())
} else {
self.sock_3dslink = sock;
Ok(())
}
}
@ -92,7 +95,7 @@ impl Drop for Soc { @@ -92,7 +95,7 @@ impl Drop for Soc {
fn drop(&mut self) {
if self.sock_3dslink >= 0 {
unsafe {
libc::closesocket(self.sock_3dslink);
libc::close(self.sock_3dslink);
}
}
}
@ -101,7 +104,6 @@ impl Drop for Soc { @@ -101,7 +104,6 @@ impl Drop for Soc {
#[cfg(test)]
mod tests {
use super::*;
use crate::Error;
#[test]
fn soc_duplicate() {

7
ctru-sys/src/lib.rs

@ -10,3 +10,10 @@ mod bindings; @@ -10,3 +10,10 @@ mod bindings;
pub use bindings::*;
pub use result::*;
/// In lieu of a proper errno function exposed by libc
/// (<https://github.com/rust-lang/libc/issues/1995>), this will retrieve the
/// last error set in the global reentrancy struct.
pub unsafe fn errno() -> s32 {
(*__getreent())._errno
}

Loading…
Cancel
Save