bspc_rs/
query.rs

1use std::string::ToString;
2
3use crate::errors::{QueryError, ReplyError};
4use crate::parser::utils::from_hex_to_id;
5use crate::selectors::{
6    DesktopSelector, MonitorSelector, NodeSelector, Selector,
7};
8use crate::socket::{self, BspcCommunication};
9use crate::tree::Tree;
10use crate::Id;
11
12fn query(
13    query_type: &str,
14    names_flag: bool,
15    selector: Option<&str>,
16    monitor_selector: Option<&str>,
17    desktop_selector: Option<&str>,
18    node_selector: Option<&str>,
19) -> Result<Vec<Id>, ReplyError> {
20    let mut conn = socket::connect()?;
21    let mut request = format!("query\x00--{query_type}\x00");
22
23    if names_flag {
24        if query_type == "nodes" {
25            return Err(ReplyError::QueryError(QueryError::InvalidRequest(
26                "You can't apply --names for nodes query request".to_string(),
27            )));
28        } else {
29            request = format!("{request}--names\x00");
30        }
31    }
32
33    if let Some(sel) = selector {
34        request = format!("{request}{sel}\x00");
35    }
36
37    if let Some(sel) = monitor_selector {
38        request = format!("{request}--monitor\x00{sel}\x00");
39    }
40
41    if let Some(sel) = desktop_selector {
42        request = format!("{request}--desktop\x00{sel}\x00");
43    }
44
45    if let Some(sel) = node_selector {
46        request = format!("{request}--node\x00{sel}\x00");
47    }
48
49    conn.send_message(&request)?;
50
51    let reply = conn.receive_message()?;
52
53    let mut ids = Vec::new();
54
55    for reply_id in reply.iter() {
56        let id = from_hex_to_id(reply_id)?;
57        ids.push(id);
58    }
59
60    Ok(ids)
61}
62
63/// Returns ids of the nodes, that match certain criteria
64pub fn query_nodes(
65    selector: Option<NodeSelector>,
66    monitor_selector: Option<MonitorSelector>,
67    desktop_selector: Option<DesktopSelector>,
68    node_selector: Option<NodeSelector>,
69) -> Result<Vec<Id>, ReplyError> {
70    query(
71        "nodes",
72        false,
73        extract(&selector)?,
74        extract(&monitor_selector)?,
75        extract(&desktop_selector)?,
76        extract(&node_selector)?,
77    )
78}
79
80/// Returns ids of the desktops, that match certain criteria
81pub fn query_desktops(
82    names_flag: bool,
83    selector: Option<DesktopSelector>,
84    monitor_selector: Option<MonitorSelector>,
85    desktop_selector: Option<DesktopSelector>,
86    node_selector: Option<NodeSelector>,
87) -> Result<Vec<Id>, ReplyError> {
88    query(
89        "desktops",
90        names_flag,
91        extract(&selector)?,
92        extract(&monitor_selector)?,
93        extract(&desktop_selector)?,
94        extract(&node_selector)?,
95    )
96}
97
98fn extract<S>(selector: &Option<S>) -> Result<Option<&str>, ReplyError>
99where
100    S: Selector,
101{
102    if let Some(sel) = selector {
103        if !sel.is_valid() {
104            return Err(ReplyError::InvalidSelector(format!(
105                "This {} selector is invalid: '{}'",
106                sel.kind(),
107                sel.extract(),
108            )));
109        }
110
111        return Ok(Some(sel.extract()));
112    }
113
114    Ok(None)
115}
116
117/// Returns ids of monitors, that match certain criteria
118pub fn query_monitors(
119    names_flag: bool,
120    selector: Option<MonitorSelector>,
121    monitor_selector: Option<MonitorSelector>,
122    desktop_selector: Option<DesktopSelector>,
123    node_selector: Option<NodeSelector>,
124) -> Result<Vec<Id>, ReplyError> {
125    query(
126        "monitors",
127        names_flag,
128        extract(&selector)?,
129        extract(&monitor_selector)?,
130        extract(&desktop_selector)?,
131        extract(&node_selector)?,
132    )
133}
134
135/// Returnes tree representation of the matching item
136///
137/// Note: when more then one of the arguments are not `None`, then the
138/// matching will give the result in this priority: Node, Desktop, Monitor.
139/// For example, if Desktop and Node are both not `None`, than this will give the
140/// output for Node.
141pub fn query_tree(
142    monitor_selector: Option<MonitorSelector>,
143    desktop_selector: Option<DesktopSelector>,
144    node_selector: Option<NodeSelector>,
145) -> Result<Tree, ReplyError> {
146    let mut conn = socket::connect()?;
147    let mut request = "query\x00--tree\x00".to_string();
148
149    let monitor_selector = extract(&monitor_selector)?;
150    let desktop_selector = extract(&desktop_selector)?;
151    let node_selector = extract(&node_selector)?;
152
153    if let Some(sel) = monitor_selector {
154        request = format!("{request}--monitor\x00{sel}\x00");
155    }
156
157    if let Some(sel) = desktop_selector {
158        request = format!("{request}--desktop\x00{sel}\x00");
159    }
160
161    if let Some(sel) = node_selector {
162        request = format!("{request}--node\x00{sel}\x00");
163    }
164
165    conn.send_message(&request)?;
166
167    let reply = conn.receive_message()?;
168
169    if reply.len() > 1 {
170        // TODO: Test if this can happen
171        panic!("{}", format!("Something is weird, reply has more than one element, this is debug log: {:#?}", reply));
172    }
173
174    let reply = &reply[0];
175
176    if node_selector.is_some() {
177        return Ok(Tree::Node(serde_json::from_str(reply)?));
178    }
179
180    if desktop_selector.is_some() {
181        return Ok(Tree::Desktop(serde_json::from_str(reply)?));
182    }
183
184    if monitor_selector.is_some() {
185        return Ok(Tree::Monitor(serde_json::from_str(reply)?));
186    }
187
188    Err(ReplyError::QueryError(QueryError::InvalidRequest(
189        "No options were given".to_string(),
190    )))
191
192    // match option {
193    //     QueryOptions::Monitor => {
194    //         Ok(Tree::Monitor(serde_json::from_str(reply)?))
195    //     }
196
197    //     QueryOptions::Desktop => {
198    //         Ok(Tree::Desktop(serde_json::from_str(reply)?))
199    //     }
200
201    //     QueryOptions::Node => Ok(Tree::Node(serde_json::from_str(reply)?)),
202    // }
203}
204
205#[cfg(test)]
206mod test {
207    use super::*;
208    use crate::events::*;
209    // use std::error::Error;
210    // use std::io::{self, Read, Write};
211    // use std::os::unix::net::UnixStream;
212
213    // #[test]
214    // fn test_query_nodes() {
215    //     println!(
216    //         "{:#?}",
217    //         Bspc::query_nodes(None, None, None, Some(".!hidden"))
218    //             .unwrap()
219    //     );
220    // }
221
222    #[test]
223    fn test_fullscreen_node() {
224        let node_request = format!(".fullscreen.window");
225        let query_result =
226            query_nodes(None, None, None, Some(NodeSelector(&node_request)));
227
228        println!("{query_result:#?}");
229    }
230
231    #[test]
232    fn test_query_tree() {
233        let tree =
234            query_tree(Some(MonitorSelector("focused")), None, None).unwrap();
235
236        println!("{tree:#?}");
237    }
238}