1use crate::connection::Connection;
4use crate::error::{Error, Result};
5use crate::proto;
6use crate::request;
7use crate::validate;
8use std::sync::Arc;
9use tokio::io::{AsyncRead, AsyncWrite};
10
11pub struct Session<S> {
16 pub id: String,
18 pub title: Option<String>,
20 conn: Arc<Connection<S>>,
21}
22
23impl<S: AsyncRead + AsyncWrite + Unpin + Send + 'static> Session<S> {
24 pub fn new(id: String, title: Option<String>, conn: Arc<Connection<S>>) -> Result<Self> {
26 validate::identifier(&id, "session")?;
27 Ok(Self { id, title, conn })
28 }
29
30 pub(crate) fn new_unchecked(id: String, title: Option<String>, conn: Arc<Connection<S>>) -> Self {
32 Self { id, title, conn }
33 }
34
35 pub async fn send_text(&self, text: &str) -> Result<()> {
37 validate::text_len(text)?;
38 let resp = self.conn.call(request::send_text(&self.id, text)).await?;
39 match resp.submessage {
40 Some(proto::server_originated_message::Submessage::SendTextResponse(r)) => {
41 check_status_i32(r.status, "SendText")
42 }
43 _ => Err(Error::UnexpectedResponse {
44 expected: "SendTextResponse",
45 }),
46 }
47 }
48
49 pub async fn get_screen_contents(&self) -> Result<Vec<String>> {
51 let resp = self
52 .conn
53 .call(request::get_buffer_screen(&self.id))
54 .await?;
55 match resp.submessage {
56 Some(proto::server_originated_message::Submessage::GetBufferResponse(r)) => {
57 check_buffer_status(r.status)?;
58 Ok(r.contents
59 .into_iter()
60 .map(|line| line.text.unwrap_or_default())
61 .collect())
62 }
63 _ => Err(Error::UnexpectedResponse {
64 expected: "GetBufferResponse",
65 }),
66 }
67 }
68
69 pub async fn get_buffer_lines(&self, trailing_lines: i32) -> Result<Vec<String>> {
71 let resp = self
72 .conn
73 .call(request::get_buffer_trailing(&self.id, trailing_lines))
74 .await?;
75 match resp.submessage {
76 Some(proto::server_originated_message::Submessage::GetBufferResponse(r)) => {
77 check_buffer_status(r.status)?;
78 Ok(r.contents
79 .into_iter()
80 .map(|line| line.text.unwrap_or_default())
81 .collect())
82 }
83 _ => Err(Error::UnexpectedResponse {
84 expected: "GetBufferResponse",
85 }),
86 }
87 }
88
89 pub async fn split(
91 &self,
92 direction: proto::split_pane_request::SplitDirection,
93 before: bool,
94 profile_name: Option<&str>,
95 ) -> Result<Vec<String>> {
96 let resp = self
97 .conn
98 .call(request::split_pane(&self.id, direction, before, profile_name))
99 .await?;
100 match resp.submessage {
101 Some(proto::server_originated_message::Submessage::SplitPaneResponse(r)) => {
102 check_split_status(r.status)?;
103 Ok(r.session_id)
104 }
105 _ => Err(Error::UnexpectedResponse {
106 expected: "SplitPaneResponse",
107 }),
108 }
109 }
110
111 pub async fn get_variable(&self, name: &str) -> Result<Option<String>> {
113 let resp = self
114 .conn
115 .call(request::get_variable_session(
116 &self.id,
117 vec![name.to_string()],
118 ))
119 .await?;
120 match resp.submessage {
121 Some(proto::server_originated_message::Submessage::VariableResponse(r)) => {
122 check_variable_status(r.status)?;
123 Ok(r.values.into_iter().next())
124 }
125 _ => Err(Error::UnexpectedResponse {
126 expected: "VariableResponse",
127 }),
128 }
129 }
130
131 pub async fn set_variable(&self, name: &str, json_value: &str) -> Result<()> {
133 validate::json_value(json_value)?;
134 let resp = self
135 .conn
136 .call(request::set_variable_session(
137 &self.id,
138 vec![(name.to_string(), json_value.to_string())],
139 ))
140 .await?;
141 match resp.submessage {
142 Some(proto::server_originated_message::Submessage::VariableResponse(r)) => {
143 check_variable_status(r.status)
144 }
145 _ => Err(Error::UnexpectedResponse {
146 expected: "VariableResponse",
147 }),
148 }
149 }
150
151 pub async fn get_profile_property(&self, keys: Vec<String>) -> Result<Vec<proto::ProfileProperty>> {
153 let resp = self
154 .conn
155 .call(request::get_profile_property(&self.id, keys))
156 .await?;
157 match resp.submessage {
158 Some(proto::server_originated_message::Submessage::GetProfilePropertyResponse(r)) => {
159 check_status_i32(r.status, "GetProfileProperty")?;
160 Ok(r.properties)
161 }
162 _ => Err(Error::UnexpectedResponse {
163 expected: "GetProfilePropertyResponse",
164 }),
165 }
166 }
167
168 pub async fn set_profile_property(&self, key: &str, json_value: &str) -> Result<()> {
170 validate::json_value(json_value)?;
171 let resp = self
172 .conn
173 .call(request::set_profile_property_session(
174 &self.id, key, json_value,
175 ))
176 .await?;
177 match resp.submessage {
178 Some(proto::server_originated_message::Submessage::SetProfilePropertyResponse(r)) => {
179 check_status_i32(r.status, "SetProfileProperty")
180 }
181 _ => Err(Error::UnexpectedResponse {
182 expected: "SetProfilePropertyResponse",
183 }),
184 }
185 }
186
187 pub async fn inject(&self, data: Vec<u8>) -> Result<()> {
189 let resp = self
190 .conn
191 .call(request::inject(vec![self.id.clone()], data))
192 .await?;
193 match resp.submessage {
194 Some(proto::server_originated_message::Submessage::InjectResponse(r)) => {
195 for status in &r.status {
196 if *status != proto::inject_response::Status::Ok as i32 {
197 return Err(Error::Status(format!(
198 "Inject failed with status: {status}"
199 )));
200 }
201 }
202 Ok(())
203 }
204 _ => Err(Error::UnexpectedResponse {
205 expected: "InjectResponse",
206 }),
207 }
208 }
209
210 pub async fn restart(&self, only_if_exited: bool) -> Result<()> {
212 let resp = self
213 .conn
214 .call(request::restart_session(&self.id, only_if_exited))
215 .await?;
216 match resp.submessage {
217 Some(proto::server_originated_message::Submessage::RestartSessionResponse(r)) => {
218 check_status_i32(r.status, "RestartSession")
219 }
220 _ => Err(Error::UnexpectedResponse {
221 expected: "RestartSessionResponse",
222 }),
223 }
224 }
225
226 pub async fn close(&self, force: bool) -> Result<()> {
228 let resp = self
229 .conn
230 .call(request::close_sessions(vec![self.id.clone()], force))
231 .await?;
232 match resp.submessage {
233 Some(proto::server_originated_message::Submessage::CloseResponse(_r)) => Ok(()),
234 _ => Err(Error::UnexpectedResponse {
235 expected: "CloseResponse",
236 }),
237 }
238 }
239
240 pub async fn activate(&self) -> Result<()> {
242 let resp = self
243 .conn
244 .call(request::activate_session(&self.id))
245 .await?;
246 match resp.submessage {
247 Some(proto::server_originated_message::Submessage::ActivateResponse(r)) => {
248 check_status_i32(r.status, "Activate")
249 }
250 _ => Err(Error::UnexpectedResponse {
251 expected: "ActivateResponse",
252 }),
253 }
254 }
255
256 pub async fn get_prompt(&self) -> Result<proto::GetPromptResponse> {
258 let resp = self.conn.call(request::get_prompt(&self.id)).await?;
259 match resp.submessage {
260 Some(proto::server_originated_message::Submessage::GetPromptResponse(r)) => Ok(r),
261 _ => Err(Error::UnexpectedResponse {
262 expected: "GetPromptResponse",
263 }),
264 }
265 }
266
267 pub fn connection(&self) -> &Connection<S> {
269 &self.conn
270 }
271}
272
273fn check_status_i32(status: Option<i32>, op: &str) -> Result<()> {
274 match status {
275 Some(0) | None => Ok(()),
276 Some(code) => Err(Error::Status(format!("{op} returned status {code}"))),
277 }
278}
279
280fn check_buffer_status(status: Option<i32>) -> Result<()> {
281 check_status_i32(status, "GetBuffer")
282}
283
284fn check_split_status(status: Option<i32>) -> Result<()> {
285 check_status_i32(status, "SplitPane")
286}
287
288fn check_variable_status(status: Option<i32>) -> Result<()> {
289 check_status_i32(status, "Variable")
290}