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() {
soc.redirect_to_3dslink(true, true) soc.redirect_to_3dslink(true, true)
.expect("unable to redirect stdout/err to 3dslink server"); .expect("unable to redirect stdout/err to 3dslink server");
print!("\x1b[2J\x1b[0;0H"); // Clear screen + move to 0,0
println!("Hello 3dslink!"); println!("Hello 3dslink!");
eprintln!("Press Start on the device to disconnect and exit."); eprintln!("Press Start on the device to disconnect and exit.");

39
ctru-rs/src/error.rs

@ -1,4 +1,5 @@
use std::error; use std::error;
use std::ffi::CStr;
use std::fmt; use std::fmt;
use ctru_sys::result::{R_DESCRIPTION, R_LEVEL, R_MODULE, R_SUMMARY}; 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>;
#[non_exhaustive] #[non_exhaustive]
pub enum Error { pub enum Error {
Os(ctru_sys::Result), Os(ctru_sys::Result),
Libc(String),
ServiceAlreadyActive, 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 { impl From<ctru_sys::Result> for Error {
@ -20,8 +41,8 @@ impl From<ctru_sys::Result> for Error {
impl fmt::Debug for Error { impl fmt::Debug for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self { match self {
Error::Os(err) => f &Self::Os(err) => f
.debug_struct("Error") .debug_struct("Error")
.field("raw", &format_args!("{:#08X}", err)) .field("raw", &format_args!("{:#08X}", err))
.field("description", &R_DESCRIPTION(err)) .field("description", &R_DESCRIPTION(err))
@ -29,7 +50,9 @@ impl fmt::Debug for Error {
.field("summary", &R_SUMMARY(err)) .field("summary", &R_SUMMARY(err))
.field("level", &R_LEVEL(err)) .field("level", &R_LEVEL(err))
.finish(), .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 {
// https://github.com/devkitPro/libctru/blob/master/libctru/include/3ds/result.h // https://github.com/devkitPro/libctru/blob/master/libctru/include/3ds/result.h
impl fmt::Display for Error { impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self { match self {
Error::Os(err) => write!(f, "libctru result code: 0x{:08X}", err), &Self::Os(err) => write!(f, "libctru result code: 0x{:08X}", err),
Error::ServiceAlreadyActive => write!(f, "Service already active"), 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;
use std::sync::Mutex; use std::sync::Mutex;
use crate::services::ServiceReference; use crate::services::ServiceReference;
use crate::Error;
/// Soc service. Initializing this service will enable the use of network sockets and utilities /// 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. /// such as those found in `std::net`. The service will be closed when this struct is is dropped.
@ -74,15 +75,17 @@ impl Soc {
/// output was already previously redirected. /// output was already previously redirected.
pub fn redirect_to_3dslink(&mut self, stdout: bool, stderr: bool) -> crate::Result<()> { pub fn redirect_to_3dslink(&mut self, stdout: bool, stderr: bool) -> crate::Result<()> {
if self.sock_3dslink >= 0 { if self.sock_3dslink >= 0 {
// TODO AlreadyRedirected or something return Err(Error::OutputAlreadyRedirected);
return Err(crate::Error::ServiceAlreadyActive);
} }
let sock = unsafe { ctru_sys::link3dsConnectToHost(stdout, stderr) }; if !stdout && !stderr {
if sock < 0 { return Ok(());
Err(sock.into()) }
self.sock_3dslink = unsafe { ctru_sys::link3dsConnectToHost(stdout, stderr) };
if self.sock_3dslink < 0 {
Err(Error::from_errno())
} else { } else {
self.sock_3dslink = sock;
Ok(()) Ok(())
} }
} }
@ -92,7 +95,7 @@ impl Drop for Soc {
fn drop(&mut self) { fn drop(&mut self) {
if self.sock_3dslink >= 0 { if self.sock_3dslink >= 0 {
unsafe { unsafe {
libc::closesocket(self.sock_3dslink); libc::close(self.sock_3dslink);
} }
} }
} }
@ -101,7 +104,6 @@ impl Drop for Soc {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::Error;
#[test] #[test]
fn soc_duplicate() { fn soc_duplicate() {

7
ctru-sys/src/lib.rs

@ -10,3 +10,10 @@ mod bindings;
pub use bindings::*; pub use bindings::*;
pub use result::*; 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