Radish alpha
h
Radicle Heartwood Protocol & Stack
Radicle
Git (anonymous pull)
Log in to clone via SSH
term: Remove dependency on libc
Lorenz Leutgeb committed 11 months ago
commit fcbe0635763f5bc7d1a833ecd0183630803f3016
parent 3dd602b6f813bfc5c293409ead9da918eadce303
7 files changed +67 -19
modified Cargo.lock
@@ -2746,7 +2746,6 @@ dependencies = [
 "crossterm 0.29.0",
 "git2",
 "inquire",
-
 "libc",
 "pretty_assertions",
 "radicle-signals",
 "shlex",
modified crates/radicle-remote-helper/src/lib.rs
@@ -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<_>>();
modified crates/radicle-term/Cargo.toml
@@ -18,7 +18,6 @@ anstyle-query = "1.0.0"
crossbeam-channel = { workspace = true }
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"
modified crates/radicle-term/src/ansi.rs
@@ -14,4 +14,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;
modified crates/radicle-term/src/ansi/paint.rs
@@ -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.
modified crates/radicle-term/src/editor.rs
@@ -1,7 +1,6 @@
use std::ffi::OsString;
use std::io::IsTerminal;
use std::io::Write;
-
use std::os::fd::{AsRawFd, FromRawFd};
use std::path::{Path, PathBuf};
use std::process;
use std::{env, fs, io};
@@ -126,11 +125,6 @@ impl Editor {
            ));
        };

-
        // We duplicate the stderr file descriptor to pass it to the child process, otherwise, if
-
        // we simply pass the `RawFd` of our stderr, `Command` will close our stderr when the
-
        // child exits.
-
        let stderr = io::stderr().as_raw_fd();
-
        let stderr = unsafe { libc::dup(stderr) };
        let stdin = if io::stdin().is_terminal() {
            process::Stdio::inherit()
        } else if cfg!(unix) {
@@ -148,9 +142,9 @@ impl Editor {
            ));
        };

-
        process::Command::new(program)
-
            .stdout(unsafe { process::Stdio::from_raw_fd(stderr) })
-
            .stderr(process::Stdio::inherit())
+
        let output = process::Command::new(program)
+
            .stdout(process::Stdio::piped())
+
            .stderr(process::Stdio::piped())
            .stdin(stdin)
            .args(args)
            .arg(&self.path)
@@ -161,7 +155,7 @@ impl Editor {
                    format!("failed to spawn editor command {cmd:?}: {e}"),
                )
            })?
-
            .wait()
+
            .wait_with_output()
            .map_err(|e| {
                io::Error::new(
                    e.kind(),
@@ -169,6 +163,34 @@ impl Editor {
                )
            })?;

+
        if !output.status.success() {
+
            let code: String = match output.status.code() {
+
                Some(code) => format!("exit code {code}"),
+
                None => {
+
                    #[cfg(unix)]
+
                    {
+
                        use std::os::unix::process::ExitStatusExt as _;
+
                        match output.status.signal() {
+
                            Some(signal) => format!("signal {signal}"),
+
                            None => "unknown signal".to_string(),
+
                        }
+
                    }
+
                    #[cfg(not(unix))]
+
                    {
+
                        "unknown exit code".to_string()
+
                    }
+
                }
+
            };
+

+
            return Err(io::Error::new(
+
                io::ErrorKind::Other,
+
                format!("editor command {cmd:?} failed with {code}",),
+
            ));
+
        }
+

+
        io::stderr().write_all(&output.stdout)?;
+
        io::stderr().write_all(&output.stderr)?;
+

        let text = fs::read_to_string(&self.path)?;
        if text.trim().is_empty() {
            return Ok(None);
modified crates/radicle-term/src/lib.rs
@@ -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;