1// Copyright 2021 The AccessKit Authors. All rights reserved.
2// Licensed under the Apache License, Version 2.0 (found in
3// the LICENSE-APACHE file) or the MIT license (found in
4// the LICENSE-MIT file), at your option.
5
6use accesskit::{Live, Node as NodeData, NodeId, Tree as TreeData, TreeUpdate};
7use std::collections::{HashMap, HashSet};
8
9use crate::node::{DetachedNode, Node, NodeState, ParentAndIndex};
10
11#[derive(Clone)]
12pub struct State {
13 pub(crate) nodes: HashMap<NodeId, NodeState>,
14 pub(crate) data: TreeData,
15 focus: NodeId,
16 is_host_focused: bool,
17}
18
19struct InternalFocusChange {
20 old_focus: Option<DetachedNode>,
21 new_focus_old_node: Option<DetachedNode>,
22}
23
24#[derive(Default)]
25struct InternalChanges {
26 added_node_ids: HashSet<NodeId>,
27 updated_nodes: HashMap<NodeId, DetachedNode>,
28 focus_change: Option<InternalFocusChange>,
29 removed_nodes: HashMap<NodeId, DetachedNode>,
30}
31
32impl State {
33 fn validate_global(&self) {
34 assert!(self.nodes.contains_key(&self.data.root));
35 assert!(self.nodes.contains_key(&self.focus));
36 }
37
38 fn update(
39 &mut self,
40 update: TreeUpdate,
41 is_host_focused: bool,
42 mut changes: Option<&mut InternalChanges>,
43 ) {
44 // First, if we're collecting changes, get the accurate state
45 // of any updated nodes.
46 if let Some(changes) = &mut changes {
47 for (node_id, _) in &update.nodes {
48 if let Some(old_node) = self.node_by_id(*node_id) {
49 let old_node = old_node.detached();
50 changes.updated_nodes.insert(*node_id, old_node);
51 }
52 }
53 }
54
55 let mut orphans = HashSet::new();
56 let old_focus_id = self.is_host_focused.then_some(self.focus);
57 let old_root_id = self.data.root;
58
59 if let Some(tree) = update.tree {
60 if tree.root != self.data.root {
61 orphans.insert(self.data.root);
62 }
63 self.data = tree;
64 }
65
66 let root = self.data.root;
67 let mut pending_nodes: HashMap<NodeId, _> = HashMap::new();
68 let mut pending_children = HashMap::new();
69
70 fn add_node(
71 nodes: &mut HashMap<NodeId, NodeState>,
72 changes: &mut Option<&mut InternalChanges>,
73 parent_and_index: Option<ParentAndIndex>,
74 id: NodeId,
75 data: NodeData,
76 ) {
77 let state = NodeState {
78 id,
79 parent_and_index,
80 data,
81 };
82 nodes.insert(id, state);
83 if let Some(changes) = changes {
84 changes.added_node_ids.insert(id);
85 }
86 }
87
88 for (node_id, node_data) in update.nodes {
89 orphans.remove(&node_id);
90
91 let mut seen_child_ids = HashSet::new();
92 for (child_index, child_id) in node_data.children().iter().enumerate() {
93 assert!(!seen_child_ids.contains(child_id));
94 orphans.remove(child_id);
95 let parent_and_index = ParentAndIndex(node_id, child_index);
96 if let Some(child_state) = self.nodes.get_mut(child_id) {
97 if child_state.parent_and_index != Some(parent_and_index) {
98 child_state.parent_and_index = Some(parent_and_index);
99 }
100 } else if let Some(child_data) = pending_nodes.remove(child_id) {
101 add_node(
102 &mut self.nodes,
103 &mut changes,
104 Some(parent_and_index),
105 *child_id,
106 child_data,
107 );
108 } else {
109 pending_children.insert(*child_id, parent_and_index);
110 }
111 seen_child_ids.insert(child_id);
112 }
113
114 if let Some(node_state) = self.nodes.get_mut(&node_id) {
115 if node_id == root {
116 node_state.parent_and_index = None;
117 }
118 for child_id in node_state.data.children().iter() {
119 if !seen_child_ids.contains(child_id) {
120 orphans.insert(*child_id);
121 }
122 }
123 node_state.data = node_data;
124 } else if let Some(parent_and_index) = pending_children.remove(&node_id) {
125 add_node(
126 &mut self.nodes,
127 &mut changes,
128 Some(parent_and_index),
129 node_id,
130 node_data,
131 );
132 } else if node_id == root {
133 add_node(&mut self.nodes, &mut changes, None, node_id, node_data);
134 } else {
135 pending_nodes.insert(node_id, node_data);
136 }
137 }
138
139 assert_eq!(pending_nodes.len(), 0);
140 assert_eq!(pending_children.len(), 0);
141
142 if update.focus != self.focus || is_host_focused != self.is_host_focused {
143 let old_focus = old_focus_id.map(|id| self.node_by_id(id).unwrap().detached());
144 let new_focus = is_host_focused.then_some(update.focus);
145 if let Some(changes) = &mut changes {
146 changes.focus_change = Some(InternalFocusChange {
147 old_focus,
148 new_focus_old_node: new_focus
149 .and_then(|id| {
150 (!changes.updated_nodes.contains_key(&id))
151 .then(|| self.node_by_id(id).map(|node| node.detached()))
152 })
153 .flatten(),
154 });
155 }
156 self.focus = update.focus;
157 self.is_host_focused = is_host_focused;
158 }
159
160 if !orphans.is_empty() {
161 let mut to_remove = HashSet::new();
162
163 fn traverse_orphan(
164 nodes: &HashMap<NodeId, NodeState>,
165 to_remove: &mut HashSet<NodeId>,
166 id: NodeId,
167 ) {
168 to_remove.insert(id);
169 let node = nodes.get(&id).unwrap();
170 for child_id in node.data.children().iter() {
171 traverse_orphan(nodes, to_remove, *child_id);
172 }
173 }
174
175 for id in orphans {
176 traverse_orphan(&self.nodes, &mut to_remove, id);
177 }
178
179 for id in to_remove {
180 if let Some(old_node_state) = self.nodes.remove(&id) {
181 if let Some(changes) = &mut changes {
182 let old_node = DetachedNode {
183 state: old_node_state,
184 is_focused: old_focus_id == Some(id),
185 is_root: old_root_id == id,
186 name: None,
187 value: None,
188 live: Live::Off,
189 supports_text_ranges: false,
190 };
191 changes.removed_nodes.insert(id, old_node);
192 }
193 }
194 }
195 }
196
197 self.validate_global();
198 }
199
200 fn update_host_focus_state(
201 &mut self,
202 is_host_focused: bool,
203 changes: Option<&mut InternalChanges>,
204 ) {
205 let update = TreeUpdate {
206 nodes: vec![],
207 tree: None,
208 focus: self.focus,
209 };
210 self.update(update, is_host_focused, changes);
211 }
212
213 pub fn serialize(&self) -> TreeUpdate {
214 let mut nodes = Vec::new();
215
216 fn traverse(state: &State, nodes: &mut Vec<(NodeId, NodeData)>, id: NodeId) {
217 let node = state.nodes.get(&id).unwrap();
218 nodes.push((id, node.data.clone()));
219
220 for child_id in node.data.children().iter() {
221 traverse(state, nodes, *child_id);
222 }
223 }
224
225 traverse(self, &mut nodes, self.data.root);
226 assert_eq!(nodes.len(), self.nodes.len());
227
228 TreeUpdate {
229 nodes,
230 tree: Some(self.data.clone()),
231 focus: self.focus,
232 }
233 }
234
235 pub fn has_node(&self, id: NodeId) -> bool {
236 self.nodes.contains_key(&id)
237 }
238
239 pub fn node_by_id(&self, id: NodeId) -> Option<Node<'_>> {
240 self.nodes.get(&id).map(|node_state| Node {
241 tree_state: self,
242 state: node_state,
243 })
244 }
245
246 pub fn root_id(&self) -> NodeId {
247 self.data.root
248 }
249
250 pub fn root(&self) -> Node<'_> {
251 self.node_by_id(self.root_id()).unwrap()
252 }
253
254 pub fn focus_id(&self) -> Option<NodeId> {
255 self.is_host_focused.then_some(self.focus)
256 }
257
258 pub fn focus(&self) -> Option<Node<'_>> {
259 self.focus_id().map(|id| self.node_by_id(id).unwrap())
260 }
261
262 pub fn app_name(&self) -> Option<String> {
263 self.data.app_name.clone()
264 }
265
266 pub fn toolkit_name(&self) -> Option<String> {
267 self.data.toolkit_name.clone()
268 }
269
270 pub fn toolkit_version(&self) -> Option<String> {
271 self.data.toolkit_version.clone()
272 }
273}
274
275pub trait ChangeHandler {
276 fn node_added(&mut self, node: &Node);
277 fn node_updated(&mut self, old_node: &DetachedNode, new_node: &Node);
278 fn focus_moved(
279 &mut self,
280 old_node: Option<&DetachedNode>,
281 new_node: Option<&Node>,
282 current_state: &State,
283 );
284 /// The tree update process doesn't currently collect all possible information
285 /// about removed nodes. The following methods don't accurately reflect
286 /// the full state of the old node:
287 ///
288 /// * [`DetachedNode::name`]
289 /// * [`DetachedNode::live`]
290 /// * [`DetachedNode::supports_text_ranges`]
291 fn node_removed(&mut self, node: &DetachedNode, current_state: &State);
292}
293
294pub struct Tree {
295 state: State,
296}
297
298impl Tree {
299 pub fn new(mut initial_state: TreeUpdate, is_host_focused: bool) -> Self {
300 let mut state = State {
301 nodes: HashMap::new(),
302 data: initial_state.tree.take().unwrap(),
303 focus: initial_state.focus,
304 is_host_focused,
305 };
306 state.update(initial_state, is_host_focused, None);
307 Self { state }
308 }
309
310 pub fn update(&mut self, update: TreeUpdate) {
311 self.state.update(update, self.state.is_host_focused, None);
312 }
313
314 pub fn update_and_process_changes(
315 &mut self,
316 update: TreeUpdate,
317 handler: &mut impl ChangeHandler,
318 ) {
319 let mut changes = InternalChanges::default();
320 self.state
321 .update(update, self.state.is_host_focused, Some(&mut changes));
322 self.process_changes(changes, handler);
323 }
324
325 pub fn update_host_focus_state(&mut self, is_host_focused: bool) {
326 self.state.update_host_focus_state(is_host_focused, None);
327 }
328
329 pub fn update_host_focus_state_and_process_changes(
330 &mut self,
331 is_host_focused: bool,
332 handler: &mut impl ChangeHandler,
333 ) {
334 let mut changes = InternalChanges::default();
335 self.state
336 .update_host_focus_state(is_host_focused, Some(&mut changes));
337 self.process_changes(changes, handler);
338 }
339
340 fn process_changes(&self, changes: InternalChanges, handler: &mut impl ChangeHandler) {
341 for id in &changes.added_node_ids {
342 let node = self.state.node_by_id(*id).unwrap();
343 handler.node_added(&node);
344 }
345 for (id, old_node) in &changes.updated_nodes {
346 let new_node = self.state.node_by_id(*id).unwrap();
347 handler.node_updated(old_node, &new_node);
348 }
349 if let Some(focus_change) = changes.focus_change {
350 if let Some(old_node) = &focus_change.old_focus {
351 let id = old_node.id();
352 if !changes.updated_nodes.contains_key(&id)
353 && !changes.removed_nodes.contains_key(&id)
354 {
355 if let Some(old_node_new_version) = self.state.node_by_id(id) {
356 handler.node_updated(old_node, &old_node_new_version);
357 }
358 }
359 }
360 let new_node = self.state.focus();
361 if let Some(new_node) = new_node {
362 let id = new_node.id();
363 if !changes.added_node_ids.contains(&id) && !changes.updated_nodes.contains_key(&id)
364 {
365 if let Some(new_node_old_version) = focus_change.new_focus_old_node {
366 handler.node_updated(&new_node_old_version, &new_node);
367 }
368 }
369 }
370 handler.focus_moved(
371 focus_change.old_focus.as_ref(),
372 new_node.as_ref(),
373 &self.state,
374 );
375 }
376 for node in changes.removed_nodes.values() {
377 handler.node_removed(node, &self.state);
378 }
379 }
380
381 pub fn state(&self) -> &State {
382 &self.state
383 }
384}
385
386#[cfg(test)]
387mod tests {
388 use accesskit::{NodeBuilder, NodeClassSet, NodeId, Role, Tree, TreeUpdate};
389
390 #[test]
391 fn init_tree_with_root_node() {
392 let mut classes = NodeClassSet::new();
393 let update = TreeUpdate {
394 nodes: vec![(
395 NodeId(0),
396 NodeBuilder::new(Role::Window).build(&mut classes),
397 )],
398 tree: Some(Tree::new(NodeId(0))),
399 focus: NodeId(0),
400 };
401 let tree = super::Tree::new(update, false);
402 assert_eq!(NodeId(0), tree.state().root().id());
403 assert_eq!(Role::Window, tree.state().root().role());
404 assert!(tree.state().root().parent().is_none());
405 }
406
407 #[test]
408 fn root_node_has_children() {
409 let mut classes = NodeClassSet::new();
410 let update = TreeUpdate {
411 nodes: vec![
412 (NodeId(0), {
413 let mut builder = NodeBuilder::new(Role::Window);
414 builder.set_children(vec![NodeId(1), NodeId(2)]);
415 builder.build(&mut classes)
416 }),
417 (
418 NodeId(1),
419 NodeBuilder::new(Role::Button).build(&mut classes),
420 ),
421 (
422 NodeId(2),
423 NodeBuilder::new(Role::Button).build(&mut classes),
424 ),
425 ],
426 tree: Some(Tree::new(NodeId(0))),
427 focus: NodeId(0),
428 };
429 let tree = super::Tree::new(update, false);
430 let state = tree.state();
431 assert_eq!(
432 NodeId(0),
433 state.node_by_id(NodeId(1)).unwrap().parent().unwrap().id()
434 );
435 assert_eq!(
436 NodeId(0),
437 state.node_by_id(NodeId(2)).unwrap().parent().unwrap().id()
438 );
439 assert_eq!(2, state.root().children().count());
440 }
441
442 #[test]
443 fn add_child_to_root_node() {
444 let mut classes = NodeClassSet::new();
445 let root_builder = NodeBuilder::new(Role::Window);
446 let first_update = TreeUpdate {
447 nodes: vec![(NodeId(0), root_builder.clone().build(&mut classes))],
448 tree: Some(Tree::new(NodeId(0))),
449 focus: NodeId(0),
450 };
451 let mut tree = super::Tree::new(first_update, false);
452 assert_eq!(0, tree.state().root().children().count());
453 let second_update = TreeUpdate {
454 nodes: vec![
455 (NodeId(0), {
456 let mut builder = root_builder;
457 builder.push_child(NodeId(1));
458 builder.build(&mut classes)
459 }),
460 (
461 NodeId(1),
462 NodeBuilder::new(Role::RootWebArea).build(&mut classes),
463 ),
464 ],
465 tree: None,
466 focus: NodeId(0),
467 };
468 struct Handler {
469 got_new_child_node: bool,
470 got_updated_root_node: bool,
471 }
472 fn unexpected_change() {
473 panic!("expected only new child node and updated root node");
474 }
475 impl super::ChangeHandler for Handler {
476 fn node_added(&mut self, node: &crate::Node) {
477 if node.id() == NodeId(1) {
478 self.got_new_child_node = true;
479 return;
480 }
481 unexpected_change();
482 }
483 fn node_updated(&mut self, old_node: &crate::DetachedNode, new_node: &crate::Node) {
484 if new_node.id() == NodeId(0)
485 && old_node.data().children().is_empty()
486 && new_node.data().children() == [NodeId(1)]
487 {
488 self.got_updated_root_node = true;
489 return;
490 }
491 unexpected_change();
492 }
493 fn focus_moved(
494 &mut self,
495 _old_node: Option<&crate::DetachedNode>,
496 _new_node: Option<&crate::Node>,
497 _current_state: &crate::TreeState,
498 ) {
499 unexpected_change();
500 }
501 fn node_removed(
502 &mut self,
503 _node: &crate::DetachedNode,
504 _current_state: &crate::TreeState,
505 ) {
506 unexpected_change();
507 }
508 }
509 let mut handler = Handler {
510 got_new_child_node: false,
511 got_updated_root_node: false,
512 };
513 tree.update_and_process_changes(second_update, &mut handler);
514 assert!(handler.got_new_child_node);
515 assert!(handler.got_updated_root_node);
516 let state = tree.state();
517 assert_eq!(1, state.root().children().count());
518 assert_eq!(NodeId(1), state.root().children().next().unwrap().id());
519 assert_eq!(
520 NodeId(0),
521 state.node_by_id(NodeId(1)).unwrap().parent().unwrap().id()
522 );
523 }
524
525 #[test]
526 fn remove_child_from_root_node() {
527 let mut classes = NodeClassSet::new();
528 let root_builder = NodeBuilder::new(Role::Window);
529 let first_update = TreeUpdate {
530 nodes: vec![
531 (NodeId(0), {
532 let mut builder = root_builder.clone();
533 builder.push_child(NodeId(1));
534 builder.build(&mut classes)
535 }),
536 (
537 NodeId(1),
538 NodeBuilder::new(Role::RootWebArea).build(&mut classes),
539 ),
540 ],
541 tree: Some(Tree::new(NodeId(0))),
542 focus: NodeId(0),
543 };
544 let mut tree = super::Tree::new(first_update, false);
545 assert_eq!(1, tree.state().root().children().count());
546 let second_update = TreeUpdate {
547 nodes: vec![(NodeId(0), root_builder.build(&mut classes))],
548 tree: None,
549 focus: NodeId(0),
550 };
551 struct Handler {
552 got_updated_root_node: bool,
553 got_removed_child_node: bool,
554 }
555 fn unexpected_change() {
556 panic!("expected only removed child node and updated root node");
557 }
558 impl super::ChangeHandler for Handler {
559 fn node_added(&mut self, _node: &crate::Node) {
560 unexpected_change();
561 }
562 fn node_updated(&mut self, old_node: &crate::DetachedNode, new_node: &crate::Node) {
563 if new_node.id() == NodeId(0)
564 && old_node.data().children() == [NodeId(1)]
565 && new_node.data().children().is_empty()
566 {
567 self.got_updated_root_node = true;
568 return;
569 }
570 unexpected_change();
571 }
572 fn focus_moved(
573 &mut self,
574 _old_node: Option<&crate::DetachedNode>,
575 _new_node: Option<&crate::Node>,
576 _current_state: &crate::TreeState,
577 ) {
578 unexpected_change();
579 }
580 fn node_removed(
581 &mut self,
582 node: &crate::DetachedNode,
583 _current_state: &crate::TreeState,
584 ) {
585 if node.id() == NodeId(1) {
586 self.got_removed_child_node = true;
587 return;
588 }
589 unexpected_change();
590 }
591 }
592 let mut handler = Handler {
593 got_updated_root_node: false,
594 got_removed_child_node: false,
595 };
596 tree.update_and_process_changes(second_update, &mut handler);
597 assert!(handler.got_updated_root_node);
598 assert!(handler.got_removed_child_node);
599 assert_eq!(0, tree.state().root().children().count());
600 assert!(tree.state().node_by_id(NodeId(1)).is_none());
601 }
602
603 #[test]
604 fn move_focus_between_siblings() {
605 let mut classes = NodeClassSet::new();
606 let first_update = TreeUpdate {
607 nodes: vec![
608 (NodeId(0), {
609 let mut builder = NodeBuilder::new(Role::Window);
610 builder.set_children(vec![NodeId(1), NodeId(2)]);
611 builder.build(&mut classes)
612 }),
613 (
614 NodeId(1),
615 NodeBuilder::new(Role::Button).build(&mut classes),
616 ),
617 (
618 NodeId(2),
619 NodeBuilder::new(Role::Button).build(&mut classes),
620 ),
621 ],
622 tree: Some(Tree::new(NodeId(0))),
623 focus: NodeId(1),
624 };
625 let mut tree = super::Tree::new(first_update, true);
626 assert!(tree.state().node_by_id(NodeId(1)).unwrap().is_focused());
627 let second_update = TreeUpdate {
628 nodes: vec![],
629 tree: None,
630 focus: NodeId(2),
631 };
632 struct Handler {
633 got_old_focus_node_update: bool,
634 got_new_focus_node_update: bool,
635 got_focus_change: bool,
636 }
637 fn unexpected_change() {
638 panic!("expected only focus change");
639 }
640 impl super::ChangeHandler for Handler {
641 fn node_added(&mut self, _node: &crate::Node) {
642 unexpected_change();
643 }
644 fn node_updated(&mut self, old_node: &crate::DetachedNode, new_node: &crate::Node) {
645 if old_node.id() == NodeId(1)
646 && new_node.id() == NodeId(1)
647 && old_node.is_focused()
648 && !new_node.is_focused()
649 {
650 self.got_old_focus_node_update = true;
651 return;
652 }
653 if old_node.id() == NodeId(2)
654 && new_node.id() == NodeId(2)
655 && !old_node.is_focused()
656 && new_node.is_focused()
657 {
658 self.got_new_focus_node_update = true;
659 return;
660 }
661 unexpected_change();
662 }
663 fn focus_moved(
664 &mut self,
665 old_node: Option<&crate::DetachedNode>,
666 new_node: Option<&crate::Node>,
667 _current_state: &crate::TreeState,
668 ) {
669 if let (Some(old_node), Some(new_node)) = (old_node, new_node) {
670 if old_node.id() == NodeId(1) && new_node.id() == NodeId(2) {
671 self.got_focus_change = true;
672 return;
673 }
674 }
675 unexpected_change();
676 }
677 fn node_removed(
678 &mut self,
679 _node: &crate::DetachedNode,
680 _current_state: &crate::TreeState,
681 ) {
682 unexpected_change();
683 }
684 }
685 let mut handler = Handler {
686 got_old_focus_node_update: false,
687 got_new_focus_node_update: false,
688 got_focus_change: false,
689 };
690 tree.update_and_process_changes(second_update, &mut handler);
691 assert!(handler.got_old_focus_node_update);
692 assert!(handler.got_new_focus_node_update);
693 assert!(handler.got_focus_change);
694 assert!(tree.state().node_by_id(NodeId(2)).unwrap().is_focused());
695 assert!(!tree.state().node_by_id(NodeId(1)).unwrap().is_focused());
696 }
697
698 #[test]
699 fn update_node() {
700 let mut classes = NodeClassSet::new();
701 let child_builder = NodeBuilder::new(Role::Button);
702 let first_update = TreeUpdate {
703 nodes: vec![
704 (NodeId(0), {
705 let mut builder = NodeBuilder::new(Role::Window);
706 builder.set_children(vec![NodeId(1)]);
707 builder.build(&mut classes)
708 }),
709 (NodeId(1), {
710 let mut builder = child_builder.clone();
711 builder.set_name("foo");
712 builder.build(&mut classes)
713 }),
714 ],
715 tree: Some(Tree::new(NodeId(0))),
716 focus: NodeId(0),
717 };
718 let mut tree = super::Tree::new(first_update, false);
719 assert_eq!(
720 Some("foo".into()),
721 tree.state().node_by_id(NodeId(1)).unwrap().name()
722 );
723 let second_update = TreeUpdate {
724 nodes: vec![(NodeId(1), {
725 let mut builder = child_builder;
726 builder.set_name("bar");
727 builder.build(&mut classes)
728 })],
729 tree: None,
730 focus: NodeId(0),
731 };
732 struct Handler {
733 got_updated_child_node: bool,
734 }
735 fn unexpected_change() {
736 panic!("expected only updated child node");
737 }
738 impl super::ChangeHandler for Handler {
739 fn node_added(&mut self, _node: &crate::Node) {
740 unexpected_change();
741 }
742 fn node_updated(&mut self, old_node: &crate::DetachedNode, new_node: &crate::Node) {
743 if new_node.id() == NodeId(1)
744 && old_node.name() == Some("foo".into())
745 && new_node.name() == Some("bar".into())
746 {
747 self.got_updated_child_node = true;
748 return;
749 }
750 unexpected_change();
751 }
752 fn focus_moved(
753 &mut self,
754 _old_node: Option<&crate::DetachedNode>,
755 _new_node: Option<&crate::Node>,
756 _current_state: &crate::TreeState,
757 ) {
758 unexpected_change();
759 }
760 fn node_removed(
761 &mut self,
762 _node: &crate::DetachedNode,
763 _current_state: &crate::TreeState,
764 ) {
765 unexpected_change();
766 }
767 }
768 let mut handler = Handler {
769 got_updated_child_node: false,
770 };
771 tree.update_and_process_changes(second_update, &mut handler);
772 assert!(handler.got_updated_child_node);
773 assert_eq!(
774 Some("bar".into()),
775 tree.state().node_by_id(NodeId(1)).unwrap().name()
776 );
777 }
778}
779