1use crate::host::{with_host, JsObj};
12use fusevm::Value;
13use indexmap::IndexMap;
14
15pub const METHODS: &[&str] = &["isatty"];
16
17pub const READ_STREAM_METHODS: &[&str] = &[
19 "setRawMode",
20 "on",
21 "once",
22 "removeListener",
23 "pause",
24 "resume",
25 "setEncoding",
26 "ref",
27 "unref",
28 "destroy",
29 "read",
30];
31
32pub fn call(method: &str, args: &[Value]) -> Option<Result<Value, String>> {
33 Some(match method {
34 "isatty" => {
35 let fd = super::arg_num(args, 0);
36 let is = if fd.is_finite() {
37 unsafe { libc::isatty(fd as libc::c_int) == 1 }
39 } else {
40 false
41 };
42 Ok(Value::Bool(is))
43 }
44 _ => return None,
45 })
46}
47
48pub fn constant(name: &str) -> Option<Value> {
51 match name {
52 "ReadStream" | "WriteStream" => Some(with_host(|h| h.alloc(JsObj::Builtin(name.into())))),
53 _ => None,
54 }
55}
56
57pub fn construct(name: &str, args: &[Value]) -> Value {
59 let fd = {
60 let n = super::arg_num(args, 0);
61 if n.is_finite() {
62 n as i32
63 } else if name == "WriteStream" {
64 1
65 } else {
66 0
67 }
68 };
69 match name {
70 "ReadStream" => read_stream(fd),
71 _ => write_stream(fd),
72 }
73}
74
75pub fn write_stream(fd: i32) -> Value {
77 let is_tty = unsafe { libc::isatty(fd) == 1 };
79 let size = if is_tty { window_size(fd) } else { None };
80 with_host(|h| {
81 let mut m = IndexMap::new();
82 m.insert("@@native".into(), h.new_str("WriteStream"));
83 m.insert("fd".into(), Value::Float(fd as f64));
84 m.insert("isTTY".into(), Value::Bool(is_tty));
85 m.insert("writable".into(), Value::Bool(true));
86 if let Some((cols, rows)) = size {
87 m.insert("columns".into(), Value::Float(cols as f64));
88 m.insert("rows".into(), Value::Float(rows as f64));
89 }
90 h.new_object(m)
91 })
92}
93
94pub fn read_stream(fd: i32) -> Value {
96 let is_tty = unsafe { libc::isatty(fd) == 1 };
98 with_host(|h| {
99 let mut m = IndexMap::new();
100 m.insert("@@native".into(), h.new_str("ReadStream"));
101 m.insert("fd".into(), Value::Float(fd as f64));
102 m.insert("isTTY".into(), Value::Bool(is_tty));
103 m.insert("isRaw".into(), Value::Bool(false));
104 m.insert("readable".into(), Value::Bool(true));
105 h.new_object(m)
106 })
107}
108
109pub fn instance_call(recv: &Value, method: &str, args: &[Value]) -> Result<Value, String> {
113 match method {
114 "setRawMode" => {
115 let mode = super::arg_num(args, 0) != 0.0;
116 with_host(|h| {
117 if let Some(JsObj::Object(p)) = h.get_mut(recv) {
118 p.insert("isRaw".into(), Value::Bool(mode));
119 }
120 });
121 Ok(recv.clone())
122 }
123 "read" => Ok(Value::Undef),
124 "on" | "once" | "removeListener" | "pause" | "resume" | "setEncoding" | "ref" | "unref"
125 | "destroy" => Ok(recv.clone()),
126 _ => Err(crate::host::type_error(&format!(
127 "{method} is not a function"
128 ))),
129 }
130}
131
132pub fn window_size(fd: i32) -> Option<(u16, u16)> {
135 unsafe {
137 let mut ws: libc::winsize = std::mem::zeroed();
138 if libc::ioctl(fd, libc::TIOCGWINSZ as _, &mut ws as *mut libc::winsize) == 0
139 && ws.ws_col > 0
140 {
141 Some((ws.ws_col, ws.ws_row))
142 } else {
143 None
144 }
145 }
146}