mirror of
https://github.com/facebook/sapling.git
synced 2024-10-10 08:47:12 +03:00
io: add IsTty API
Summary: Add a new API to test if a stream is a tty. This is needed to replace the Python `fin`, `fout` etc. to Rust objects, because the Python land requires the `istty` API. It is also useful for properly implement color detection in the pure Rust land. Reviewed By: sfilipco Differential Revision: D26612480 fbshipit-source-id: 5cf79447b1d74e0031a954788db342afd48dc288
This commit is contained in:
parent
deb50bdef8
commit
f3a5686a15
@ -12,6 +12,7 @@ python3 = ["python3-sys", "cpython/python3-sys"]
|
||||
anyhow = "1.0.20"
|
||||
cpython = { version = "0.5", default-features = false }
|
||||
encoding = { path = "../encoding" }
|
||||
io = { path = "../io" }
|
||||
lazy_static = "1"
|
||||
libc = "0.2"
|
||||
parking_lot = "0.9"
|
||||
|
@ -61,6 +61,18 @@ impl io::Write for WrappedIO {
|
||||
}
|
||||
}
|
||||
|
||||
impl ::io::IsTty for WrappedIO {
|
||||
fn is_tty(&self) -> bool {
|
||||
(|| -> PyResult<bool> {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let result = self.0.call_method(py, "isatty", NoArgs, None)?;
|
||||
result.extract(py)
|
||||
})()
|
||||
.unwrap_or(false)
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a Python `IOError` to Rust `io::Error`.
|
||||
fn convert_ioerr(mut pyerr: PyErr) -> io::Error {
|
||||
let gil = Python::acquire_gil();
|
||||
|
@ -5,6 +5,7 @@ authors = ["Facebook Source Control Team <sourcecontrol-dev@fb.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
atty = "0.2"
|
||||
configparser = { path = "../configparser" }
|
||||
once_cell = "1"
|
||||
parking_lot = "0.10"
|
||||
|
99
eden/scm/lib/io/src/impls.rs
Normal file
99
eden/scm/lib/io/src/impls.rs
Normal file
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This software may be used and distributed according to the terms of the
|
||||
* GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
use crate::IsTty;
|
||||
use std::io::Cursor;
|
||||
use std::sync::Weak;
|
||||
|
||||
impl IsTty for std::io::Stdin {
|
||||
fn is_tty(&self) -> bool {
|
||||
atty::is(atty::Stream::Stdin)
|
||||
}
|
||||
}
|
||||
|
||||
impl IsTty for std::io::Stdout {
|
||||
fn is_tty(&self) -> bool {
|
||||
atty::is(atty::Stream::Stdout)
|
||||
}
|
||||
}
|
||||
|
||||
impl IsTty for std::io::Stderr {
|
||||
fn is_tty(&self) -> bool {
|
||||
atty::is(atty::Stream::Stderr)
|
||||
}
|
||||
}
|
||||
|
||||
impl IsTty for Vec<u8> {
|
||||
fn is_tty(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IsTty for &'a [u8] {
|
||||
fn is_tty(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IsTty for Cursor<T> {
|
||||
fn is_tty(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl IsTty for crate::IOOutput {
|
||||
fn is_tty(&self) -> bool {
|
||||
let inner = match Weak::upgrade(&self.0) {
|
||||
Some(inner) => inner,
|
||||
None => return false,
|
||||
};
|
||||
let inner = inner.lock();
|
||||
inner.output.is_tty()
|
||||
}
|
||||
}
|
||||
|
||||
impl IsTty for crate::IOError {
|
||||
fn is_tty(&self) -> bool {
|
||||
let inner = match Weak::upgrade(&self.0) {
|
||||
Some(inner) => inner,
|
||||
None => return false,
|
||||
};
|
||||
let inner = inner.lock();
|
||||
if let Some(error) = inner.error.as_ref() {
|
||||
error.is_tty()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct PipeWriterWithTty {
|
||||
inner: pipe::PipeWriter,
|
||||
is_tty: bool,
|
||||
}
|
||||
|
||||
impl std::io::Write for PipeWriterWithTty {
|
||||
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||
self.inner.write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> std::io::Result<()> {
|
||||
self.inner.flush()
|
||||
}
|
||||
}
|
||||
|
||||
impl IsTty for PipeWriterWithTty {
|
||||
fn is_tty(&self) -> bool {
|
||||
self.is_tty
|
||||
}
|
||||
}
|
||||
|
||||
impl PipeWriterWithTty {
|
||||
pub fn new(inner: pipe::PipeWriter, is_tty: bool) -> Self {
|
||||
Self { inner, is_tty }
|
||||
}
|
||||
}
|
@ -56,7 +56,11 @@ struct Inner {
|
||||
/// to obtain the "main" `IO`.
|
||||
static MAIN_IO_REF: Lazy<RwLock<Option<Weak<Mutex<Inner>>>>> = Lazy::new(Default::default);
|
||||
|
||||
pub trait Read: io::Read + Any + Send + Sync {
|
||||
pub trait IsTty {
|
||||
fn is_tty(&self) -> bool;
|
||||
}
|
||||
|
||||
pub trait Read: io::Read + IsTty + Any + Send + Sync {
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
|
||||
fn type_name(&self) -> &'static str {
|
||||
@ -64,7 +68,7 @@ pub trait Read: io::Read + Any + Send + Sync {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Write: io::Write + Any + Send + Sync {
|
||||
pub trait Write: io::Write + IsTty + Any + Send + Sync {
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
|
||||
fn type_name(&self) -> &'static str {
|
||||
@ -72,18 +76,20 @@ pub trait Write: io::Write + Any + Send + Sync {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: io::Read + Any + Send + Sync> Read for T {
|
||||
impl<T: io::Read + IsTty + Any + Send + Sync> Read for T {
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: io::Write + Any + Send + Sync> Write for T {
|
||||
impl<T: io::Write + IsTty + Any + Send + Sync> Write for T {
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
mod impls;
|
||||
|
||||
// Write to error.
|
||||
impl io::Write for IOError {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
@ -336,10 +342,18 @@ impl IO {
|
||||
let (err_read, err_write) = pipe();
|
||||
let (prg_read, prg_write) = pipe();
|
||||
|
||||
use impls::PipeWriterWithTty;
|
||||
let out_is_tty = inner.output.is_tty();
|
||||
let err_is_tty = inner
|
||||
.error
|
||||
.as_ref()
|
||||
.map(|e| e.is_tty())
|
||||
.unwrap_or_else(|| out_is_tty);
|
||||
|
||||
inner.flush()?;
|
||||
inner.output = Box::new(out_write);
|
||||
inner.error = Some(Box::new(err_write));
|
||||
inner.progress = Some(Box::new(prg_write));
|
||||
inner.output = Box::new(PipeWriterWithTty::new(out_write, out_is_tty));
|
||||
inner.error = Some(Box::new(PipeWriterWithTty::new(err_write, err_is_tty)));
|
||||
inner.progress = Some(Box::new(PipeWriterWithTty::new(prg_write, false)));
|
||||
|
||||
inner.pager_handle = Some(spawn(|| {
|
||||
pager
|
||||
|
Loading…
Reference in New Issue
Block a user