Skip to main content

xa11y_core/
tree.rs

1use std::fmt;
2
3use serde::{Deserialize, Serialize};
4
5use crate::error::Result;
6use crate::node::{NodeData, NodeIndex};
7use crate::selector::Selector;
8
9/// A snapshot of an application's accessibility tree.
10///
11/// The tree is a flattened snapshot — nodes are stored in DFS order and
12/// reference each other by internal indices.
13///
14/// **This type is internal to provider implementations.** End users should
15/// use [`Node`](crate::Node) (returned by `xa11y::app()`, etc.) which wraps
16/// a `Tree` with navigation methods.
17#[derive(Debug, Clone, Serialize, Deserialize)]
18pub struct Tree {
19    /// Application name
20    pub app_name: String,
21
22    /// Process ID. `None` for multi-app queries.
23    pub pid: Option<u32>,
24
25    /// Screen dimensions at capture time (width, height)
26    pub screen_size: (u32, u32),
27
28    /// All nodes in DFS order (access through methods)
29    nodes: Vec<NodeData>,
30}
31
32impl Tree {
33    /// Create a new Tree from a list of nodes.
34    pub fn new(
35        app_name: String,
36        pid: Option<u32>,
37        screen_size: (u32, u32),
38        nodes: Vec<NodeData>,
39    ) -> Self {
40        Self {
41            app_name,
42            pid,
43            screen_size,
44            nodes,
45        }
46    }
47
48    /// Get a node's data by its internal index.
49    pub fn get_data(&self, index: u32) -> Option<&NodeData> {
50        self.nodes.get(index as usize)
51    }
52
53    /// Get the root node data.
54    pub fn root_data(&self) -> &NodeData {
55        &self.nodes[0]
56    }
57
58    /// Get the parent of a node.
59    pub fn parent_data(&self, node: &NodeData) -> Option<&NodeData> {
60        node.parent_index
61            .and_then(|idx| self.nodes.get(idx as usize))
62    }
63
64    /// Get direct children of a node.
65    pub fn children_data(&self, node: &NodeData) -> Vec<&NodeData> {
66        node.children_indices
67            .iter()
68            .filter_map(|&idx| self.nodes.get(idx as usize))
69            .collect()
70    }
71
72    /// Get indices of the subtree rooted at a node (including the node itself).
73    pub fn subtree_indices(&self, index: u32) -> Vec<u32> {
74        let mut result = Vec::new();
75        self.collect_subtree_indices(index, &mut result);
76        result
77    }
78
79    fn collect_subtree_indices(&self, index: NodeIndex, result: &mut Vec<u32>) {
80        if let Some(node) = self.nodes.get(index as usize) {
81            result.push(index);
82            for &child_idx in &node.children_indices {
83                self.collect_subtree_indices(child_idx, result);
84            }
85        }
86    }
87
88    /// Iterate all node data.
89    pub fn iter(&self) -> impl Iterator<Item = &NodeData> {
90        self.nodes.iter()
91    }
92
93    /// Query node indices matching a CSS-like selector string.
94    pub fn query_indices(&self, selector_str: &str) -> Result<Vec<u32>> {
95        let selector = Selector::parse(selector_str)?;
96        Ok(selector.match_nodes(self).iter().map(|n| n.index).collect())
97    }
98
99    /// Query nodes matching a CSS-like selector string (returns NodeData refs).
100    pub fn query(&self, selector_str: &str) -> Result<Vec<&NodeData>> {
101        let selector = Selector::parse(selector_str)?;
102        Ok(selector.match_nodes(self))
103    }
104
105    /// Get the number of nodes in the tree.
106    pub fn len(&self) -> usize {
107        self.nodes.len()
108    }
109
110    /// Check if the tree is empty.
111    pub fn is_empty(&self) -> bool {
112        self.nodes.is_empty()
113    }
114
115    /// Get the internal node index for a node. Used by platform providers
116    /// to look up cached element handles.
117    #[doc(hidden)]
118    pub fn node_index(&self, node: &NodeData) -> NodeIndex {
119        node.index
120    }
121}
122
123impl fmt::Display for Tree {
124    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
125        // Compute depth from parent_index so NodeData doesn't need a depth field.
126        let mut depths = vec![0u32; self.nodes.len()];
127        for node in &self.nodes {
128            let d = depths[node.index as usize];
129            for &child_idx in &node.children_indices {
130                if let Some(cd) = depths.get_mut(child_idx as usize) {
131                    *cd = d + 1;
132                }
133            }
134        }
135
136        for node in &self.nodes {
137            let depth = depths.get(node.index as usize).copied().unwrap_or(0);
138            let indent = "  ".repeat(depth as usize);
139            let name_part = node
140                .name
141                .as_ref()
142                .map(|n| format!(" \"{}\"", n))
143                .unwrap_or_default();
144            let value_part = node
145                .value
146                .as_ref()
147                .map(|v| format!(" value=\"{}\"", v))
148                .unwrap_or_default();
149            writeln!(
150                f,
151                "{}[{}] {}{}{}",
152                indent,
153                node.index,
154                node.role.to_snake_case(),
155                name_part,
156                value_part,
157            )?;
158        }
159        Ok(())
160    }
161}