1use crate::utils::module::{export_default, ModuleInfo};
4use rquickjs::{
5 module::{Declarations, Exports, ModuleDef},
6 prelude::Func,
7 Ctx, Result,
8};
9
10fn isatty(fd: i32) -> bool {
17 use std::io::IsTerminal;
18
19 match fd {
20 0 => std::io::stdin().is_terminal(),
21 1 => std::io::stdout().is_terminal(),
22 2 => std::io::stderr().is_terminal(),
23 #[cfg(unix)]
24 other => unsafe { libc::isatty(other) != 0 },
25 #[cfg(not(unix))]
26 _ => false,
27 }
28}
29
30pub struct TtyModule;
31
32impl ModuleDef for TtyModule {
33 fn declare(declare: &Declarations<'_>) -> Result<()> {
34 declare.declare("isatty")?;
35 declare.declare("default")?;
36 Ok(())
37 }
38
39 fn evaluate<'js>(ctx: &Ctx<'js>, exports: &Exports<'js>) -> Result<()> {
40 export_default(ctx, exports, |default| {
41 default.set("isatty", Func::from(isatty))?;
42 Ok(())
43 })
44 }
45}
46
47impl From<TtyModule> for ModuleInfo<TtyModule> {
48 fn from(val: TtyModule) -> Self {
49 ModuleInfo {
50 name: "tty",
51 module: val,
52 }
53 }
54}
55
56#[cfg(test)]
57mod tests {
58 use crate::tty::TtyModule;
59 use crate::test::{call_test, test_async_with, ModuleEvaluator};
60 use std::io::{stderr, stdin, stdout, IsTerminal};
61
62 #[tokio::test]
63 async fn test_isatty() {
64 test_async_with(|ctx| {
65 Box::pin(async move {
66 ModuleEvaluator::eval_rust::<TtyModule>(ctx.clone(), "tty")
67 .await
68 .unwrap();
69
70 let module = ModuleEvaluator::eval_js(
71 ctx.clone(),
72 "test",
73 r#"
74 import { isatty } from 'tty';
75
76 export async function test() {
77 return new Array(3).fill(0).map((_, i) => +isatty(i)).join('')
78 }
79 "#,
80 )
81 .await
82 .unwrap();
83 let expect = [
84 stdin().is_terminal(),
85 stdout().is_terminal(),
86 stderr().is_terminal(),
87 ]
88 .map(|i| (i as u8).to_string())
89 .join("");
90 let result = call_test::<String, _>(&ctx, &module, ()).await;
91 assert_eq!(result, expect);
92 })
93 })
94 .await;
95 }
96}