driver_tools/subcommands/node/
common.rs1use crate::common;
6use ansi_term::Colour;
7use anyhow::{Result, anyhow, bail, format_err};
8use fidl_fuchsia_driver_development as fdd;
9use itertools::Itertools;
10use std::collections::{BTreeMap, VecDeque};
11use std::str::FromStr;
12
13#[derive(Debug, PartialEq, Clone)]
15pub enum NodeFilter {
16 Bound,
19 Unbound,
21 Ancestor(String, bool),
24 Descendant(String),
27 Relative(String, bool),
30 Sibling(String, bool),
32}
33
34impl FromStr for NodeFilter {
35 type Err = &'static str;
36
37 fn from_str(s: &str) -> Result<Self, Self::Err> {
38 match s.to_lowercase().as_str() {
39 "bound" => Ok(NodeFilter::Bound),
40 "unbound" => Ok(NodeFilter::Unbound),
41 filter => match filter.split_once(":") {
42 Some((function, arg)) => match function {
43 "ancestor" | "ancestors" => Ok(NodeFilter::Ancestor(arg.to_string(), false)),
44 "primary_ancestor" | "primary_ancestors" => {
45 Ok(NodeFilter::Ancestor(arg.to_string(), true))
46 }
47 "descendant" | "descendants" => Ok(NodeFilter::Descendant(arg.to_string())),
48 "relative" | "relatives" => Ok(NodeFilter::Relative(arg.to_string(), false)),
49 "primary_relative" | "primary_relatives" => {
50 Ok(NodeFilter::Relative(arg.to_string(), true))
51 }
52 "sibling" | "siblings" => Ok(NodeFilter::Sibling(arg.to_string(), false)),
53 "primary_sibling" | "primary_siblings" => {
54 Ok(NodeFilter::Sibling(arg.to_string(), true))
55 }
56 _ => Err("unknown function for node filter."),
57 },
58 None => Err(
59 "node filter should be 'bound', 'unbound', 'ancestors:<node_name>', 'primary_ancestors:<node_name>', 'descendants:<node_name>', 'relatives:<node_name>', 'primary_relatives:<node_name>', 'siblings:<node_name>', or 'primary_siblings:<node_name>'.",
60 ),
61 },
62 }
63 }
64}
65
66pub fn get_state_and_owner(
67 quarantined: Option<bool>,
68 url: &Option<String>,
69 with_style: bool,
70) -> (String, String) {
71 let not_found = "Driver not found".to_string();
72 let url = url.as_ref().unwrap_or(¬_found);
73 let state = if url == "unbound" {
74 common::colorized("Unbound", Colour::Yellow, with_style)
75 } else if quarantined == Some(true) {
76 common::colorized("Quarantined", Colour::Red, with_style)
77 } else {
78 common::colorized("Bound", Colour::Green, with_style)
79 };
80
81 let owner = if url == "unbound" {
82 "none".to_string()
83 } else if url == "owned by parent" {
84 "parent".to_string()
85 } else if url == "owned by composite(s)" {
86 "composite(s)".to_string()
87 } else {
88 url.clone()
89 };
90 (state, owner)
91}
92
93pub async fn get_nodes_from_query(
94 query: &str,
95 nodes: Vec<fdd::NodeInfo>,
96) -> Result<Vec<fdd::NodeInfo>> {
97 let mut filtered_instances: Vec<fdd::NodeInfo> = nodes
100 .into_iter()
101 .filter(|node| {
102 let empty = "".to_string();
103 let url_match = node.bound_driver_url.as_ref().unwrap_or(&empty).contains(&query);
104 let moniker_match = node.moniker.as_ref().unwrap_or(&empty).contains(&query);
105 url_match || moniker_match
106 })
107 .collect();
108
109 filtered_instances.sort_unstable_by(|a, b| a.moniker.as_ref().cmp(&b.moniker.as_ref()));
111
112 let (exact_matches, others): (Vec<_>, Vec<_>) = filtered_instances
113 .into_iter()
114 .partition(|i| i.moniker.as_ref().map(|s| s.as_str()) == Some(query));
115
116 if !exact_matches.is_empty() { Ok(exact_matches) } else { Ok(others) }
117}
118
119pub async fn get_single_node_from_query(
120 query: &str,
121 nodes: Vec<fdd::NodeInfo>,
122) -> Result<fdd::NodeInfo> {
123 let mut instances = get_nodes_from_query(&query, nodes).await?;
125 if instances.len() > 1 {
126 let monikers: Vec<String> = instances
127 .into_iter()
128 .map(|i| i.moniker.unwrap_or_else(|| "No moniker.".to_string()))
129 .collect();
130 let monikers = monikers.join("\n");
131 bail!(
132 "The query {:?} matches more than one node:\n{}\n\nTo avoid ambiguity, use one of the above monikers instead.",
133 query,
134 monikers
135 );
136 }
137 if instances.is_empty() {
138 bail!("No matching node found for query {:?}.", query);
139 }
140 let instance = instances.remove(0);
141 Ok(instance)
142}
143
144pub fn filter_nodes(
145 nodes: Vec<fdd::NodeInfo>,
146 filter: Option<NodeFilter>,
147) -> Result<Vec<fdd::NodeInfo>> {
148 if let Some(filter) = filter {
149 match filter {
150 NodeFilter::Bound => Ok(nodes
151 .into_iter()
152 .filter(|n| {
153 let (state, _) = get_state_and_owner(n.quarantined, &n.bound_driver_url, false);
154 state != "Unbound"
156 })
157 .collect()),
158 NodeFilter::Unbound => Ok(nodes
159 .into_iter()
160 .filter(|n| {
161 let (state, _) = get_state_and_owner(n.quarantined, &n.bound_driver_url, false);
162 state == "Unbound"
163 })
164 .collect()),
165 NodeFilter::Ancestor(filter, primary_only) => {
166 filter_ancestor(&nodes, filter, primary_only)
167 }
168 NodeFilter::Descendant(filter) => filter_descendant(&nodes, filter),
169 NodeFilter::Relative(filter, primary_only) => {
170 filter_relative(&nodes, filter, primary_only)
171 }
172 NodeFilter::Sibling(filter, primary_only) => {
173 let node_map = create_node_map(&nodes)?;
174 let target = match nodes
175 .iter()
176 .find(|n| n.moniker.as_ref().unwrap().eq_ignore_ascii_case(&filter))
177 {
178 Some(node) => node,
179 None => return Err(anyhow!("Node with moniker {} not found", filter)),
180 };
181
182 let mut results: BTreeMap<u64, fdd::NodeInfo> = BTreeMap::new();
183
184 if let Some(parent_ids) = &target.parent_ids {
187 for parent_id in parent_ids {
188 if let Some(parent) = node_map.get(parent_id) {
189 if !primary_only
190 || target
191 .moniker
192 .as_ref()
193 .unwrap()
194 .starts_with(parent.moniker.as_ref().unwrap())
195 {
196 if let Some(child_ids) = &parent.child_ids {
197 for child_id in child_ids {
198 if let Some(child) = node_map.get(child_id) {
199 results.insert(*child_id, child.clone());
200 }
201 }
202 }
203 }
204 }
205 }
206 }
207
208 Ok(results.into_values().sorted_by_key(|a| a.moniker.clone()).collect())
209 }
210 }
211 } else {
212 Ok(nodes)
213 }
214}
215
216pub fn create_node_map(nodes: &Vec<fdd::NodeInfo>) -> Result<BTreeMap<u64, fdd::NodeInfo>> {
217 nodes
218 .iter()
219 .map(|node| {
220 if let Some(id) = node.id {
221 Ok((id, node.clone()))
222 } else {
223 Err(format_err!("Missing node id"))
224 }
225 })
226 .collect::<Result<BTreeMap<_, _>>>()
227}
228
229fn filter_ancestor(
230 nodes: &Vec<fdd::NodeInfo>,
231 filter: String,
232 primary_only: bool,
233) -> Result<Vec<fdd::NodeInfo>> {
234 let node_map = create_node_map(nodes)?;
235 let root =
236 match nodes.iter().find(|n| n.moniker.as_ref().unwrap().eq_ignore_ascii_case(&filter)) {
237 Some(node) => node,
238 None => return Err(anyhow!("Node with moniker {} not found", filter)),
239 };
240
241 let mut results: BTreeMap<u64, fdd::NodeInfo> = BTreeMap::new();
242 let mut visit_q: VecDeque<&fdd::NodeInfo> = VecDeque::new();
243 results.insert(root.id.ok_or_else(|| format_err!("Node missing id"))?, root.clone());
244 visit_q.push_back(root);
245
246 while let Some(node) = visit_q.pop_front() {
249 if let Some(parent_ids) = &node.parent_ids {
250 for parent_id in parent_ids {
251 if let Some(parent) = node_map.get(parent_id) {
252 if !primary_only
253 || node
254 .moniker
255 .as_ref()
256 .unwrap()
257 .starts_with(parent.moniker.as_ref().unwrap())
258 {
259 if !results.contains_key(parent_id) {
260 results.insert(*parent_id, parent.clone());
261 visit_q.push_back(parent);
262 }
263 }
264 }
265 }
266 }
267 }
268
269 Ok(results.into_values().sorted_by_key(|a| a.moniker.clone()).collect())
270}
271
272fn filter_descendant(nodes: &Vec<fdd::NodeInfo>, filter: String) -> Result<Vec<fdd::NodeInfo>> {
273 let node_map = create_node_map(&nodes)?;
274 let root =
275 match nodes.iter().find(|n| n.moniker.as_ref().unwrap().eq_ignore_ascii_case(&filter)) {
276 Some(node) => node,
277 None => return Err(anyhow!("Node with moniker {} not found", filter)),
278 };
279
280 let mut results: BTreeMap<u64, fdd::NodeInfo> = BTreeMap::new();
281 let mut visit_q: VecDeque<&fdd::NodeInfo> = VecDeque::new();
282 results.insert(root.id.ok_or_else(|| format_err!("Node missing id"))?, root.clone());
283 visit_q.push_back(root);
284
285 while let Some(node) = visit_q.pop_front() {
288 if let Some(child_ids) = &node.child_ids {
289 for child_id in child_ids {
290 if let Some(child) = node_map.get(child_id) {
291 if !results.contains_key(child_id) {
292 results.insert(*child_id, child.clone());
293 visit_q.push_back(child);
294 }
295 }
296 }
297 }
298 }
299
300 Ok(results.into_values().sorted_by_key(|a| a.moniker.clone()).collect())
301}
302
303fn filter_relative(
304 nodes: &Vec<fdd::NodeInfo>,
305 filter: String,
306 primary_only: bool,
307) -> Result<Vec<fdd::NodeInfo>> {
308 let node_map = create_node_map(&nodes)?;
309 let root =
310 match nodes.iter().find(|n| n.moniker.as_ref().unwrap().eq_ignore_ascii_case(&filter)) {
311 Some(node) => node,
312 None => return Err(anyhow!("Node with moniker {} not found", filter)),
313 };
314
315 let mut results: BTreeMap<u64, fdd::NodeInfo> = BTreeMap::new();
316 let mut visit_q: VecDeque<&fdd::NodeInfo> = VecDeque::new();
317 results.insert(root.id.ok_or_else(|| format_err!("Node missing id"))?, root.clone());
318 visit_q.push_back(root);
319
320 while let Some(node) = visit_q.pop_front() {
324 if let Some(parent_ids) = &node.parent_ids {
325 for parent_id in parent_ids {
326 if let Some(parent) = node_map.get(parent_id) {
327 if !primary_only
328 || node
329 .moniker
330 .as_ref()
331 .unwrap()
332 .starts_with(parent.moniker.as_ref().unwrap())
333 {
334 if !results.contains_key(parent_id) {
335 results.insert(*parent_id, parent.clone());
336 visit_q.push_back(parent);
337 }
338 }
339 }
340 }
341 }
342 }
343
344 visit_q = VecDeque::new();
346 visit_q.push_back(root);
347
348 while let Some(node) = visit_q.pop_front() {
352 if let Some(child_ids) = &node.child_ids {
353 for child_id in child_ids {
354 if let Some(child) = node_map.get(child_id) {
355 if !results.contains_key(child_id) {
356 results.insert(*child_id, child.clone());
357 visit_q.push_back(child);
358 }
359 }
360 }
361 }
362 }
363
364 Ok(results.into_values().sorted_by_key(|a| a.moniker.clone()).collect())
365}