1use super::{control::*, reader::*, response::*, utils::*, writer::*};
2
3use std::{
4 io::{self, Write},
5 path::*,
6};
7
8const MAX_PAYLOAD_CHUNK_SIZE: usize = 4096;
9const MAX_PAYLOAD_CHUNK_SIZE_U64: u64 = MAX_PAYLOAD_CHUNK_SIZE as u64;
10
11const VT100_QUERY_DA1: &str = "\x1b[c";
13
14#[derive(Clone, Debug, Default)]
22pub struct Command {
23 pub(crate) controls: Controls,
24 expect_response: bool,
25}
26
27impl Command {
28 pub fn add_control<ControlT>(&mut self, code: char, value: ControlT)
30 where
31 ControlT: Into<ControlValue>,
32 {
33 self.controls.add(code, value);
34 }
35
36 pub fn with_control<ControlT>(mut self, code: char, value: ControlT) -> Self
40 where
41 ControlT: Into<ControlValue>,
42 {
43 self.add_control(code, value);
44 self
45 }
46
47 pub fn set_expect_response(&mut self) {
49 self.expect_response = true;
50 }
51
52 pub fn with_expect_response(mut self) -> Self {
56 self.set_expect_response();
57 self
58 }
59
60 pub fn execute(&self) -> io::Result<Option<Response>> {
64 let reader = self.reader();
65
66 let mut writer = io::stdout();
67 writer.write_start()?;
68 self.controls.write(&mut writer)?;
69 writer.write_end()?;
70
71 reader.and_then(|mut reader| in_raw_mode(|| reader.read_response()).transpose()).transpose()
72 }
73
74 pub fn execute_with_payload(&self, payload: &[u8]) -> io::Result<Option<Response>> {
78 if payload.len() > MAX_PAYLOAD_CHUNK_SIZE {
79 self.execute_with_payload_from(payload)
80 } else {
81 let reader = self.reader();
82 let mut writer = io::stdout();
83
84 writer.write_start()?;
85 self.controls.write(&mut writer)?;
86 write!(writer, ";")?;
87 writer = writer.write_base64(payload)?;
88 writer.write_end()?;
89
90 reader.and_then(|mut reader| in_raw_mode(|| reader.read_response()).transpose()).transpose()
91 }
92 }
93
94 pub fn execute_with_payload_from<ReadT>(&self, mut payload: ReadT) -> io::Result<Option<Response>>
98 where
99 ReadT: io::Read,
100 {
101 let reader = self.reader();
102 let mut writer = io::stdout();
103
104 writer.write_start()?;
105 if !self.controls.is_empty() {
106 self.controls.write(&mut writer)?;
107 write!(writer, ",")?;
108 }
109 write!(writer, "m=1;")?;
110
111 let mut chunk = Vec::with_capacity(MAX_PAYLOAD_CHUNK_SIZE);
112
113 loop {
114 chunk.clear();
116 payload = payload.read_chunk(MAX_PAYLOAD_CHUNK_SIZE_U64, &mut chunk)?;
117
118 if chunk.is_empty() {
119 writer.write_end()?;
121
122 writer.write_start()?;
124 write!(writer, "m=0;")?;
125 writer.write_end()?;
126 break;
127 }
128
129 writer = writer.write_base64(&chunk)?;
131 writer.write_end()?;
132
133 writer.write_start()?;
135 write!(writer, "m=1;")?;
136 }
137
138 reader.and_then(|mut reader| in_raw_mode(|| reader.read_response()).transpose()).transpose()
139 }
140
141 pub fn execute_with_path_payload<PathT>(&self, path: PathT) -> io::Result<Option<Response>>
145 where
146 PathT: AsRef<Path>,
147 {
148 self.execute_with_payload(absolute(path)?.as_os_str().as_encoded_bytes())
152 }
153
154 pub fn is_supported() -> io::Result<bool> {
156 let mut reader = io::stdin().lock();
159
160 let mut writer = io::stdout();
161 writer.write_start()?;
162 Controls::default().with('a', 'q').with('i', 1).write(&mut writer)?;
164 write!(writer, "{}{}", COMMAND_END, VT100_QUERY_DA1)?;
165 writer.flush()?;
166
167 in_raw_mode(|| reader.has_kitty_and_vt100_da1_responses())
168 }
169
170 pub(crate) fn reader(&self) -> Option<io::StdinLock<'_>> {
172 if self.expect_response { Some(io::stdin().lock()) } else { None }
173 }
174}