Skip to content

Commit

Permalink
Merge pull request #66 from sunfishcode/main
Browse files Browse the repository at this point in the history
Update the API for I/O safety
  • Loading branch information
eminence authored Sep 18, 2024
2 parents 78e81fa + a29b904 commit 5307747
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 69 deletions.
51 changes: 9 additions & 42 deletions examples/get_size.rs
Original file line number Diff line number Diff line change
@@ -1,52 +1,19 @@
#[cfg(windows)]
fn run() {
use std::os::windows::io::RawHandle;
use windows_sys::Win32::System::Console::{
GetStdHandle, STD_ERROR_HANDLE, STD_INPUT_HANDLE, STD_OUTPUT_HANDLE,
};

let stdout = unsafe { GetStdHandle(STD_OUTPUT_HANDLE) } as RawHandle;
println!(
"Size from terminal_size_using_handle(stdout): {:?}",
terminal_size::terminal_size_using_handle(stdout)
);

let stderr = unsafe { GetStdHandle(STD_ERROR_HANDLE) } as RawHandle;
println!(
"Size from terminal_size_using_handle(stderr): {:?}",
terminal_size::terminal_size_using_handle(stderr)
);

let stdin = unsafe { GetStdHandle(STD_INPUT_HANDLE) } as RawHandle;
fn main() {
println!(
"Size from terminal_size_using_handle(stdin): {:?}",
terminal_size::terminal_size_using_handle(stdin)
"Size from terminal_size(): {:?}",
terminal_size::terminal_size()
);
}

#[cfg(not(windows))]
fn run() {
use std::os::unix::io::AsRawFd;

println!(
"Size from terminal_size_using_fd(stdout): {:?}",
terminal_size::terminal_size_using_fd(std::io::stdout().as_raw_fd())
"Size from terminal_size_of(stdout): {:?}",
terminal_size::terminal_size_of(std::io::stdout())
);
println!(
"Size from terminal_size_using_fd(stderr): {:?}",
terminal_size::terminal_size_using_fd(std::io::stderr().as_raw_fd())
"Size from terminal_size_of(stderr): {:?}",
terminal_size::terminal_size_of(std::io::stderr())
);
println!(
"Size from terminal_size_using_fd(stdin): {:?}",
terminal_size::terminal_size_using_fd(std::io::stdin().as_raw_fd())
);
}

fn main() {
println!(
"Size from terminal_size(): {:?}",
terminal_size::terminal_size()
"Size from terminal_size_of(stdin): {:?}",
terminal_size::terminal_size_of(std::io::stdin())
);

run();
}
6 changes: 4 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,14 @@ pub struct Height(pub u16);
#[cfg(unix)]
mod unix;
#[cfg(unix)]
pub use crate::unix::{terminal_size, terminal_size_using_fd};
#[allow(deprecated)]
pub use crate::unix::{terminal_size, terminal_size_of, terminal_size_using_fd};

#[cfg(windows)]
mod windows;
#[cfg(windows)]
pub use crate::windows::{terminal_size, terminal_size_using_handle};
#[allow(deprecated)]
pub use crate::windows::{terminal_size, terminal_size_of, terminal_size_using_handle};

#[cfg(not(any(unix, windows)))]
pub fn terminal_size() -> Option<(Width, Height)> {
Expand Down
35 changes: 22 additions & 13 deletions src/unix.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
use super::{Height, Width};
use rustix::fd::{BorrowedFd, AsRawFd};
use std::os::unix::io::RawFd;
use std::os::unix::io::{AsFd, BorrowedFd, RawFd};

/// Returns the size of the terminal.
///
/// This function checks the stdout, stderr, and stdin streams (in that order).
/// The size of the first stream that is a TTY will be returned. If nothing
/// is a TTY, then `None` is returned.
pub fn terminal_size() -> Option<(Width, Height)> {
if let Some(size) = terminal_size_using_fd(std::io::stdout().as_raw_fd()) {
if let Some(size) = terminal_size_of(std::io::stdout()) {
Some(size)
} else if let Some(size) = terminal_size_using_fd(std::io::stderr().as_raw_fd()) {
} else if let Some(size) = terminal_size_of(std::io::stderr()) {
Some(size)
} else if let Some(size) = terminal_size_using_fd(std::io::stdin().as_raw_fd()) {
} else if let Some(size) = terminal_size_of(std::io::stdin()) {
Some(size)
} else {
None
Expand All @@ -22,19 +21,14 @@ pub fn terminal_size() -> Option<(Width, Height)> {
/// Returns the size of the terminal using the given file descriptor, if available.
///
/// If the given file descriptor is not a tty, returns `None`
pub fn terminal_size_using_fd(fd: RawFd) -> Option<(Width, Height)> {
pub fn terminal_size_of<Fd: AsFd>(fd: Fd) -> Option<(Width, Height)> {
use rustix::termios::{isatty, tcgetwinsize};

// TODO: Once I/O safety is stabilized, the enlosing function here should
// be unsafe due to taking a `RawFd`. We should then move the main
// logic here into a new function which takes a `BorrowedFd` and is safe.
let fd = unsafe { BorrowedFd::borrow_raw(fd) };

if !isatty(fd) {
if !isatty(&fd) {
return None;
}

let winsize = tcgetwinsize(fd).ok()?;
let winsize = tcgetwinsize(&fd).ok()?;

let rows = winsize.ws_row;
let cols = winsize.ws_col;
Expand All @@ -46,6 +40,21 @@ pub fn terminal_size_using_fd(fd: RawFd) -> Option<(Width, Height)> {
}
}

/// Returns the size of the terminal using the given raw file descriptor, if available.
///
/// The given file descriptor must be an open file descriptor.
///
/// If the given file descriptor is not a tty, returns `None`
///
/// # Safety
///
/// `fd` must be a valid open file descriptor.
#[deprecated(note = "Use `terminal_size_of` instead.
Use `BorrowedFd::borrow_raw` to convert a raw fd into a `BorrowedFd` if needed.")]
pub unsafe fn terminal_size_using_fd(fd: RawFd) -> Option<(Width, Height)> {
terminal_size_of(BorrowedFd::borrow_raw(fd))
}

#[test]
/// Compare with the output of `stty size`
fn compare_with_stty() {
Expand Down
39 changes: 27 additions & 12 deletions src/windows.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use super::{Height, Width};
use std::os::windows::io::RawHandle;
use std::os::windows::io::{AsHandle, AsRawHandle, BorrowedHandle, RawHandle};

/// Returns the size of the terminal.
///
Expand All @@ -14,17 +14,17 @@ pub fn terminal_size() -> Option<(Width, Height)> {
GetStdHandle, STD_ERROR_HANDLE, STD_INPUT_HANDLE, STD_OUTPUT_HANDLE,
};

if let Some(size) =
terminal_size_using_handle(unsafe { GetStdHandle(STD_OUTPUT_HANDLE) as RawHandle })
{
if let Some(size) = terminal_size_of(unsafe {
BorrowedHandle::borrow_raw(GetStdHandle(STD_OUTPUT_HANDLE) as RawHandle)
}) {
Some(size)
} else if let Some(size) =
terminal_size_using_handle(unsafe { GetStdHandle(STD_ERROR_HANDLE) as RawHandle })
{
} else if let Some(size) = terminal_size_of(unsafe {
BorrowedHandle::borrow_raw(GetStdHandle(STD_ERROR_HANDLE) as RawHandle)
}) {
Some(size)
} else if let Some(size) =
terminal_size_using_handle(unsafe { GetStdHandle(STD_INPUT_HANDLE) as RawHandle })
{
} else if let Some(size) = terminal_size_of(unsafe {
BorrowedHandle::borrow_raw(GetStdHandle(STD_INPUT_HANDLE) as RawHandle)
}) {
Some(size)
} else {
None
Expand All @@ -34,14 +34,14 @@ pub fn terminal_size() -> Option<(Width, Height)> {
/// Returns the size of the terminal using the given handle, if available.
///
/// If the given handle is not a tty, returns `None`
pub fn terminal_size_using_handle(handle: RawHandle) -> Option<(Width, Height)> {
pub fn terminal_size_of<Handle: AsHandle>(handle: Handle) -> Option<(Width, Height)> {
use windows_sys::Win32::Foundation::INVALID_HANDLE_VALUE;
use windows_sys::Win32::System::Console::{
GetConsoleScreenBufferInfo, CONSOLE_SCREEN_BUFFER_INFO, COORD, SMALL_RECT,
};

// convert between windows_sys::Win32::Foundation::HANDLE and std::os::windows::raw::HANDLE
let hand = handle as windows_sys::Win32::Foundation::HANDLE;
let hand = handle.as_handle().as_raw_handle() as windows_sys::Win32::Foundation::HANDLE;

if hand == INVALID_HANDLE_VALUE {
return None;
Expand All @@ -68,3 +68,18 @@ pub fn terminal_size_using_handle(handle: RawHandle) -> Option<(Width, Height)>
let h: Height = Height((csbi.srWindow.Bottom - csbi.srWindow.Top + 1) as u16);
Some((w, h))
}

/// Returns the size of the terminal using the given handle, if available.
///
/// The given handle must be an open handle.
///
/// If the given handle is not a tty, returns `None`
///
/// # Safety
///
/// `handle` must be a valid open file handle.
#[deprecated(note = "Use `terminal_size_of` instead.
Use `BorrowedHandle::borrow_raw` to convert a raw handle into a `BorrowedHandle` if needed.")]
pub unsafe fn terminal_size_using_handle(handle: RawHandle) -> Option<(Width, Height)> {
terminal_size_of(BorrowedHandle::borrow_raw(handle))
}

0 comments on commit 5307747

Please sign in to comment.