1use crate::connection::Connection;
4use crate::error::{Error, Result};
5use crate::proto;
6use crate::request;
7use crate::session::Session;
8use crate::tab::Tab;
9use crate::window::Window;
10use std::sync::Arc;
11use tokio::io::{AsyncRead, AsyncWrite};
12
13pub struct App<S> {
18 conn: Arc<Connection<S>>,
19}
20
21impl<S: AsyncRead + AsyncWrite + Unpin + Send + 'static> App<S> {
22 pub fn new(conn: Connection<S>) -> Self {
24 Self {
25 conn: Arc::new(conn),
26 }
27 }
28
29 pub fn from_arc(conn: Arc<Connection<S>>) -> Self {
31 Self { conn }
32 }
33
34 pub async fn list_sessions(&self) -> Result<ListSessionsResult<S>> {
36 let resp = self.conn.call(request::list_sessions()).await?;
37 match resp.submessage {
38 Some(proto::server_originated_message::Submessage::ListSessionsResponse(r)) => {
39 Ok(self.parse_list_sessions(r))
40 }
41 _ => Err(Error::UnexpectedResponse {
42 expected: "ListSessionsResponse",
43 }),
44 }
45 }
46
47 fn parse_list_sessions(&self, resp: proto::ListSessionsResponse) -> ListSessionsResult<S> {
48 let mut windows = Vec::new();
49 for w in resp.windows {
50 let window_id = w.window_id.unwrap_or_default();
51 let mut tabs = Vec::new();
52 for t in w.tabs {
53 let tab_id = t.tab_id.unwrap_or_default();
54 let sessions = collect_sessions_from_tree(t.root.as_ref(), &self.conn);
55 tabs.push(TabInfo {
56 tab: Tab::new_unchecked(tab_id, Arc::clone(&self.conn)),
57 sessions,
58 });
59 }
60 windows.push(WindowInfo {
61 window: Window::new_unchecked(window_id, Arc::clone(&self.conn)),
62 tabs,
63 });
64 }
65
66 let buried_sessions = resp
67 .buried_sessions
68 .into_iter()
69 .map(|s| {
70 Session::new_unchecked(
71 s.unique_identifier.unwrap_or_default(),
72 s.title,
73 Arc::clone(&self.conn),
74 )
75 })
76 .collect();
77
78 ListSessionsResult {
79 windows,
80 buried_sessions,
81 }
82 }
83
84 pub async fn create_tab(
86 &self,
87 profile_name: Option<&str>,
88 window_id: Option<&str>,
89 ) -> Result<CreateTabResult<S>> {
90 let resp = self
91 .conn
92 .call(request::create_tab(profile_name, window_id))
93 .await?;
94 match resp.submessage {
95 Some(proto::server_originated_message::Submessage::CreateTabResponse(r)) => {
96 check_status_i32(r.status, "CreateTab")?;
97 let session_id = r.session_id.unwrap_or_default();
98 let tab_id = r.tab_id.map(|id| id.to_string()).unwrap_or_default();
99 let window_id = r.window_id.unwrap_or_default();
100 Ok(CreateTabResult {
101 window: Window::new_unchecked(window_id, Arc::clone(&self.conn)),
102 tab: Tab::new_unchecked(tab_id, Arc::clone(&self.conn)),
103 session: Session::new_unchecked(session_id, None, Arc::clone(&self.conn)),
104 })
105 }
106 _ => Err(Error::UnexpectedResponse {
107 expected: "CreateTabResponse",
108 }),
109 }
110 }
111
112 pub async fn focus(&self) -> Result<Vec<proto::FocusChangedNotification>> {
114 let resp = self.conn.call(request::focus()).await?;
115 match resp.submessage {
116 Some(proto::server_originated_message::Submessage::FocusResponse(r)) => {
117 Ok(r.notifications)
118 }
119 _ => Err(Error::UnexpectedResponse {
120 expected: "FocusResponse",
121 }),
122 }
123 }
124
125 pub async fn activate(&self, raise_all: bool, ignoring_other_apps: bool) -> Result<()> {
127 let resp = self
128 .conn
129 .call(request::activate_app(raise_all, ignoring_other_apps))
130 .await?;
131 match resp.submessage {
132 Some(proto::server_originated_message::Submessage::ActivateResponse(r)) => {
133 check_status_i32(r.status, "Activate")
134 }
135 _ => Err(Error::UnexpectedResponse {
136 expected: "ActivateResponse",
137 }),
138 }
139 }
140
141 pub async fn list_profiles(
143 &self,
144 properties: Vec<String>,
145 guids: Vec<String>,
146 ) -> Result<proto::ListProfilesResponse> {
147 let resp = self
148 .conn
149 .call(request::list_profiles(properties, guids))
150 .await?;
151 match resp.submessage {
152 Some(proto::server_originated_message::Submessage::ListProfilesResponse(r)) => Ok(r),
153 _ => Err(Error::UnexpectedResponse {
154 expected: "ListProfilesResponse",
155 }),
156 }
157 }
158
159 pub async fn begin_transaction(&self) -> Result<()> {
161 let resp = self.conn.call(request::begin_transaction()).await?;
162 match resp.submessage {
163 Some(proto::server_originated_message::Submessage::TransactionResponse(r)) => {
164 check_status_i32(r.status, "Transaction")
165 }
166 _ => Err(Error::UnexpectedResponse {
167 expected: "TransactionResponse",
168 }),
169 }
170 }
171
172 pub async fn end_transaction(&self) -> Result<()> {
174 let resp = self.conn.call(request::end_transaction()).await?;
175 match resp.submessage {
176 Some(proto::server_originated_message::Submessage::TransactionResponse(r)) => {
177 check_status_i32(r.status, "Transaction")
178 }
179 _ => Err(Error::UnexpectedResponse {
180 expected: "TransactionResponse",
181 }),
182 }
183 }
184
185 pub async fn list_color_presets(&self) -> Result<Vec<String>> {
187 let resp = self.conn.call(request::list_color_presets()).await?;
188 match resp.submessage {
189 Some(proto::server_originated_message::Submessage::ColorPresetResponse(r)) => {
190 check_status_i32(r.status, "ColorPreset")?;
191 match r.response {
192 Some(proto::color_preset_response::Response::ListPresets(lp)) => Ok(lp.name),
193 _ => Ok(vec![]),
194 }
195 }
196 _ => Err(Error::UnexpectedResponse {
197 expected: "ColorPresetResponse",
198 }),
199 }
200 }
201
202 pub async fn list_arrangements(&self) -> Result<Vec<String>> {
204 let resp = self.conn.call(request::list_arrangements()).await?;
205 match resp.submessage {
206 Some(proto::server_originated_message::Submessage::SavedArrangementResponse(r)) => {
207 check_status_i32(r.status, "SavedArrangement")?;
208 Ok(r.names)
209 }
210 _ => Err(Error::UnexpectedResponse {
211 expected: "SavedArrangementResponse",
212 }),
213 }
214 }
215
216 pub async fn get_broadcast_domains(&self) -> Result<Vec<proto::BroadcastDomain>> {
218 let resp = self.conn.call(request::get_broadcast_domains()).await?;
219 match resp.submessage {
220 Some(proto::server_originated_message::Submessage::GetBroadcastDomainsResponse(r)) => {
221 Ok(r.broadcast_domains)
222 }
223 _ => Err(Error::UnexpectedResponse {
224 expected: "GetBroadcastDomainsResponse",
225 }),
226 }
227 }
228
229 pub fn subscribe_notifications(&self) -> tokio::sync::broadcast::Receiver<proto::Notification> {
231 self.conn.subscribe_notifications()
232 }
233
234 pub fn connection(&self) -> &Connection<S> {
236 &self.conn
237 }
238
239 pub fn connection_arc(&self) -> Arc<Connection<S>> {
241 Arc::clone(&self.conn)
242 }
243}
244
245fn collect_sessions_from_tree<S: AsyncRead + AsyncWrite + Unpin + Send + 'static>(
246 node: Option<&proto::SplitTreeNode>,
247 conn: &Arc<Connection<S>>,
248) -> Vec<Session<S>> {
249 let mut sessions = Vec::new();
250 if let Some(node) = node {
251 for link in &node.links {
252 if let Some(child) = &link.child {
253 match child {
254 proto::split_tree_node::split_tree_link::Child::Session(s) => {
255 sessions.push(Session::new_unchecked(
256 s.unique_identifier.clone().unwrap_or_default(),
257 s.title.clone(),
258 Arc::clone(conn),
259 ));
260 }
261 proto::split_tree_node::split_tree_link::Child::Node(n) => {
262 sessions.extend(collect_sessions_from_tree(Some(n), conn));
263 }
264 }
265 }
266 }
267 }
268 sessions
269}
270
271pub struct ListSessionsResult<S> {
273 pub windows: Vec<WindowInfo<S>>,
274 pub buried_sessions: Vec<Session<S>>,
275}
276
277pub struct WindowInfo<S> {
279 pub window: Window<S>,
280 pub tabs: Vec<TabInfo<S>>,
281}
282
283pub struct TabInfo<S> {
285 pub tab: Tab<S>,
286 pub sessions: Vec<Session<S>>,
287}
288
289pub struct CreateTabResult<S> {
291 pub window: Window<S>,
292 pub tab: Tab<S>,
293 pub session: Session<S>,
294}
295
296fn check_status_i32(status: Option<i32>, op: &str) -> Result<()> {
297 match status {
298 Some(0) | None => Ok(()),
299 Some(code) => Err(Error::Status(format!("{op} returned status {code}"))),
300 }
301}