term: Remove dependency on libc

Introducing the new enum `TerminalFile` to model which standard stream
stream the terminal uses allows us to remove the dependency on `libc`
which does not build on Windows.
This commit is contained in:
Lorenz Leutgeb 2025-07-14 18:14:59 +02:00
parent 70fb0d3fef
commit e7fb5647ac
6 changed files with 35 additions and 9 deletions

1
Cargo.lock generated
View File

@ -2787,7 +2787,6 @@ dependencies = [
"crossterm 0.29.0",
"git2",
"inquire",
"libc",
"pretty_assertions",
"radicle-signals",
"shlex",

View File

@ -90,7 +90,7 @@ pub struct Options {
pub fn run(profile: radicle::Profile) -> Result<(), Error> {
// Since we're going to be writing user output to `stderr`, make sure the paint
// module is aware of that.
cli::Paint::set_terminal(io::stderr());
cli::Paint::set_terminal(cli::TerminalFile::Stderr);
let (remote, url): (Option<git::RefString>, Url) = {
let args = env::args().skip(1).take(2).collect::<Vec<_>>();

View File

@ -17,7 +17,6 @@ anyhow = { workspace = true }
anstyle-query = "1.0.0"
crossterm = "0.29.0"
inquire = { version = "0.7.4", default-features = false, features = ["crossterm", "editor"] }
libc = { workspace = true }
shlex = { workspace = true }
thiserror = { workspace = true }
unicode-display-width = "0.3.0"

View File

@ -13,4 +13,5 @@ pub use color::Color;
pub use paint::paint;
pub use paint::Filled;
pub use paint::Paint;
pub use paint::TerminalFile;
pub use style::Style;

View File

@ -1,5 +1,4 @@
use std::io::IsTerminal as _;
use std::os::fd::{AsRawFd, BorrowedFd};
use std::sync::atomic::{AtomicBool, AtomicI32};
use std::sync::LazyLock;
use std::{fmt, sync};
@ -7,8 +6,36 @@ use std::{fmt, sync};
use super::color::Color;
use super::style::{Property, Style};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(i32)]
pub enum TerminalFile {
Stdout = 1,
Stderr = 2,
}
impl TryFrom<i32> for TerminalFile {
type Error = ();
fn try_from(value: i32) -> Result<Self, Self::Error> {
match value {
1 => Ok(TerminalFile::Stdout),
2 => Ok(TerminalFile::Stderr),
_ => Err(()),
}
}
}
impl TerminalFile {
fn is_terminal(&self) -> bool {
match self {
TerminalFile::Stdout => std::io::stdout().is_terminal(),
TerminalFile::Stderr => std::io::stderr().is_terminal(),
}
}
}
/// What file is used for text output.
static TERMINAL: AtomicI32 = AtomicI32::new(libc::STDOUT_FILENO);
static TERMINAL: AtomicI32 = AtomicI32::new(TerminalFile::Stdout as i32);
/// Whether paint styling is enabled or not.
static ENABLED: AtomicBool = AtomicBool::new(true);
/// Whether paint styling should be forced.
@ -263,7 +290,7 @@ impl Paint<()> {
let clicolor_enabled = clicolor.unwrap_or(false);
let clicolor_disabled = !clicolor.unwrap_or(true);
let terminal = TERMINAL.load(sync::atomic::Ordering::SeqCst);
let is_terminal = unsafe { BorrowedFd::borrow_raw(terminal).is_terminal() };
let is_terminal = TerminalFile::try_from(terminal).is_ok_and(|tf| tf.is_terminal());
let is_enabled = ENABLED.load(sync::atomic::Ordering::SeqCst);
is_terminal
@ -287,8 +314,8 @@ impl Paint<()> {
/// Set the terminal we are writing to. This influences the logic that checks whether or not to
/// include colors.
pub fn set_terminal(fd: impl AsRawFd) {
TERMINAL.store(fd.as_raw_fd(), sync::atomic::Ordering::SeqCst);
pub fn set_terminal(tf: TerminalFile) {
TERMINAL.store(tf as i32, sync::atomic::Ordering::SeqCst);
}
/// Force paint styling.

View File

@ -17,7 +17,7 @@ use std::fmt;
use std::io::IsTerminal;
pub use ansi::Color;
pub use ansi::{paint, Filled, Paint, Style};
pub use ansi::{paint, Filled, Paint, Style, TerminalFile};
pub use editor::Editor;
pub use element::{Constraint, Element, Line, Size};
pub use hstack::HStack;