Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update the API for I/O safety #66

Merged
merged 3 commits into from
Sep 18, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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`
#[deprecated(note = "Use `terminal_size_of` instead.
Use `BorrowedFd::borrow_raw` to convert a raw fd into a `BorrowedFd` if needed.")]
pub fn terminal_size_using_fd(fd: RawFd) -> Option<(Width, Height)> {
// SAFETY: Under I/O safety, this function should be `unsafe`, but we can't
// remove it without breaking compatibility, so we instead deprecate it.
// This unsafe block has the same precondition that the function implicitly
// does: `fd` must be an open handle.
unsafe { terminal_size_of(BorrowedFd::borrow_raw(fd)) }
}
eminence marked this conversation as resolved.
Show resolved Hide resolved

#[test]
/// Compare with the output of `stty size`
fn compare_with_stty() {
Expand Down
19 changes: 16 additions & 3 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 Down Expand Up @@ -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,16 @@ 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`
pub fn terminal_size_using_handle(handle: RawHandle) -> Option<(Width, Height)> {
// SAFETY: Under I/O safety, this function should be `unsafe`, but we can't
// remove it without breaking compatibility, so we instead deprecate it.
// This unsafe block has the same precondition that the function implicitly
// does: `handle` must be a valid open file descriptor.
unsafe { terminal_size_of(BorrowedHandle::borrow_raw(handle)) }
}
Loading