1#![deny(
2 clippy::all,
3 clippy::pedantic,
4 clippy::cargo,
5 clippy::map_unwrap_or,
6 clippy::unwrap_used,
7 unsafe_code
8)]
9
10use std::{
11 collections::HashMap,
12 sync::{Arc, RwLock, Weak},
13};
14
15use async_trait::async_trait;
16use atspi::{
17 accessible::{Accessible, AccessibleProxy, RelationType, Role},
18 accessible_id::{AccessibleId, HasAccessibleId},
19 convertable::Convertable,
20 events::GenericEvent,
21 signify::Signified,
22 text::{ClipType, Granularity, Text, TextProxy},
23 text_ext::TextExt,
24 CoordType, InterfaceSet, StateSet,
25};
26use dashmap::DashMap;
27use fxhash::FxBuildHasher;
28use odilia_common::{
29 errors::{AccessiblePrimitiveConversionError, CacheError, OdiliaError},
30 result::OdiliaResult,
31};
32use serde::{Deserialize, Serialize};
33use zbus::{
34 names::OwnedUniqueName,
35 zvariant::{ObjectPath, OwnedObjectPath},
36 CacheProperties, ProxyBuilder,
37};
38
39type CacheKey = AccessiblePrimitive;
40type InnerCache = DashMap<CacheKey, Arc<RwLock<CacheItem>>, FxBuildHasher>;
41type ThreadSafeCache = Arc<InnerCache>;
42
43#[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)]
44pub struct AccessiblePrimitive {
47 pub id: AccessibleId,
49 pub sender: smartstring::alias::String,
51}
52impl AccessiblePrimitive {
53 pub async fn into_accessible<'a>(
57 self,
58 conn: &zbus::Connection,
59 ) -> zbus::Result<AccessibleProxy<'a>> {
60 let id = self.id;
61 let sender = self.sender.clone();
62 let path: ObjectPath<'a> = id.try_into()?;
63 ProxyBuilder::new(conn)
64 .path(path)?
65 .destination(sender.as_str().to_owned())?
66 .cache_properties(CacheProperties::No)
67 .build()
68 .await
69 }
70 pub async fn into_text<'a>(self, conn: &zbus::Connection) -> zbus::Result<TextProxy<'a>> {
74 let id = self.id;
75 let sender = self.sender.clone();
76 let path: ObjectPath<'a> = id.try_into()?;
77 ProxyBuilder::new(conn)
78 .path(path)?
79 .destination(sender.as_str().to_owned())?
80 .cache_properties(CacheProperties::No)
81 .build()
82 .await
83 }
84 pub fn from_event<T: GenericEvent>(
88 event: &T,
89 ) -> Result<Self, AccessiblePrimitiveConversionError> {
90 let sender = event
91 .sender()
92 .map_err(|_| AccessiblePrimitiveConversionError::ErrSender)?
93 .ok_or(AccessiblePrimitiveConversionError::NoSender)?;
94 let path = event.path().ok_or(AccessiblePrimitiveConversionError::NoPathId)?;
95 let id: AccessibleId = path
96 .try_into()
97 .map_err(|_| AccessiblePrimitiveConversionError::InvalidPath)?;
98 Ok(Self { id, sender: sender.as_str().into() })
99 }
100}
101impl TryFrom<atspi::events::Accessible> for AccessiblePrimitive {
102 type Error = AccessiblePrimitiveConversionError;
103
104 fn try_from(
105 atspi_accessible: atspi::events::Accessible,
106 ) -> Result<AccessiblePrimitive, Self::Error> {
107 let tuple_converter = (atspi_accessible.name, atspi_accessible.path);
108 tuple_converter.try_into()
109 }
110}
111impl TryFrom<(OwnedUniqueName, OwnedObjectPath)> for AccessiblePrimitive {
112 type Error = AccessiblePrimitiveConversionError;
113
114 fn try_from(
115 so: (OwnedUniqueName, OwnedObjectPath),
116 ) -> Result<AccessiblePrimitive, Self::Error> {
117 let accessible_id: AccessibleId = so.1.try_into()?;
118 Ok(AccessiblePrimitive { id: accessible_id, sender: so.0.as_str().into() })
119 }
120}
121impl TryFrom<(String, OwnedObjectPath)> for AccessiblePrimitive {
122 type Error = AccessiblePrimitiveConversionError;
123
124 fn try_from(so: (String, OwnedObjectPath)) -> Result<AccessiblePrimitive, Self::Error> {
125 let accessible_id: AccessibleId = so.1.try_into()?;
126 Ok(AccessiblePrimitive { id: accessible_id, sender: so.0.into() })
127 }
128}
129impl TryFrom<(String, AccessibleId)> for AccessiblePrimitive {
130 type Error = AccessiblePrimitiveConversionError;
131
132 fn try_from(so: (String, AccessibleId)) -> Result<AccessiblePrimitive, Self::Error> {
133 Ok(AccessiblePrimitive { id: so.1, sender: so.0.into() })
134 }
135}
136impl<'a> TryFrom<(String, ObjectPath<'a>)> for AccessiblePrimitive {
137 type Error = OdiliaError;
138
139 fn try_from(so: (String, ObjectPath<'a>)) -> Result<AccessiblePrimitive, Self::Error> {
140 let accessible_id: AccessibleId = so.1.try_into()?;
141 Ok(AccessiblePrimitive { id: accessible_id, sender: so.0.into() })
142 }
143}
144impl<'a> TryFrom<&AccessibleProxy<'a>> for AccessiblePrimitive {
145 type Error = AccessiblePrimitiveConversionError;
146
147 fn try_from(accessible: &AccessibleProxy<'_>) -> Result<AccessiblePrimitive, Self::Error> {
148 let sender = accessible.destination().as_str().into();
149 let Ok(id) = accessible.id() else {
150 return Err(AccessiblePrimitiveConversionError::NoPathId);
151 };
152 Ok(AccessiblePrimitive { id, sender })
153 }
154}
155impl<'a> TryFrom<AccessibleProxy<'a>> for AccessiblePrimitive {
156 type Error = AccessiblePrimitiveConversionError;
157
158 fn try_from(accessible: AccessibleProxy<'_>) -> Result<AccessiblePrimitive, Self::Error> {
159 let sender = accessible.destination().as_str().into();
160 let Ok(id) = accessible.id() else {
161 return Err(AccessiblePrimitiveConversionError::NoPathId);
162 };
163 Ok(AccessiblePrimitive { id, sender })
164 }
165}
166
167#[derive(Clone, Debug, Deserialize, Serialize)]
168pub struct CacheItem {
170 pub object: AccessiblePrimitive,
172 pub app: AccessiblePrimitive,
174 pub parent: CacheRef,
176 pub index: i32,
178 pub children_num: i32,
180 pub interfaces: InterfaceSet,
182 pub role: Role,
184 pub states: StateSet,
186 pub text: String,
188 pub children: Vec<CacheRef>,
190
191 #[serde(skip)]
192 pub cache: Weak<Cache>,
193}
194impl CacheItem {
195 pub fn parent_ref(&mut self) -> OdiliaResult<Arc<std::sync::RwLock<CacheItem>>> {
199 let parent_ref = Weak::upgrade(&self.parent.item);
200 if let Some(p) = parent_ref {
201 Ok(p)
202 } else {
203 let cache = strong_cache(&self.cache)?;
204 let arc_mut_parent = cache
205 .get_ref(&self.parent.key.clone())
206 .ok_or(CacheError::NoItem)?;
207 self.parent.item = Arc::downgrade(&arc_mut_parent);
208 Ok(arc_mut_parent)
209 }
210 }
211 pub async fn from_atspi_event<T: Signified>(
219 event: &T,
220 cache: Weak<Cache>,
221 connection: &zbus::Connection,
222 ) -> OdiliaResult<Self> {
223 let a11y_prim = AccessiblePrimitive::from_event(event)?;
224 accessible_to_cache_item(&a11y_prim.into_accessible(connection).await?, cache).await
225 }
226 pub async fn from_atspi_cache_item(
237 atspi_cache_item: atspi::cache::CacheItem,
238 cache: Weak<Cache>,
239 connection: &zbus::Connection,
240 ) -> OdiliaResult<Self> {
241 let children: Vec<CacheRef> =
242 AccessiblePrimitive::try_from(atspi_cache_item.object.clone())?
243 .into_accessible(connection)
244 .await?
245 .get_children()
246 .await?
247 .into_iter()
248 .map(|child_object_pair| {
249 Ok(CacheRef::new(child_object_pair.try_into()?))
250 })
251 .collect::<Result<_, AccessiblePrimitiveConversionError>>()?;
252 Ok(Self {
253 object: atspi_cache_item.object.try_into()?,
254 app: atspi_cache_item.app.try_into()?,
255 parent: CacheRef::new(atspi_cache_item.parent.try_into()?),
256 index: atspi_cache_item.index,
257 children_num: atspi_cache_item.children,
258 interfaces: atspi_cache_item.ifaces,
259 role: atspi_cache_item.role,
260 states: atspi_cache_item.states,
261 text: atspi_cache_item.name,
262 cache,
263 children,
264 })
265 }
266 pub fn get_children(&self) -> OdiliaResult<Vec<Self>> {
272 let derefed_cache: Arc<Cache> = strong_cache(&self.cache)?;
273 let children = self
274 .children
275 .iter()
276 .map(|child_ref| {
277 child_ref
278 .clone_inner()
279 .or_else(|| derefed_cache.get(&child_ref.key))
280 .ok_or(CacheError::NoItem)
281 })
282 .collect::<Result<Vec<_>, _>>()?;
283 Ok(children)
284 }
285}
286
287#[derive(Debug, Clone, Deserialize, Serialize)]
294pub struct CacheRef {
295 pub key: CacheKey,
296 #[serde(skip)]
297 item: Weak<RwLock<CacheItem>>,
298}
299
300impl CacheRef {
301 #[must_use]
302 pub fn new(key: AccessiblePrimitive) -> Self {
303 Self { key, item: Weak::new() }
304 }
305
306 #[must_use]
307 pub fn clone_inner(&self) -> Option<CacheItem> {
308 Some(self.item.upgrade().as_ref()?.read().ok()?.clone())
309 }
310}
311
312impl From<AccessiblePrimitive> for CacheRef {
313 fn from(value: AccessiblePrimitive) -> Self {
314 Self::new(value)
315 }
316}
317
318#[inline]
319async fn as_accessible(cache_item: &CacheItem) -> OdiliaResult<AccessibleProxy<'_>> {
320 let cache = strong_cache(&cache_item.cache)?;
321 Ok(cache_item.object.clone().into_accessible(&cache.connection).await?)
322}
323#[inline]
324async fn as_text(cache_item: &CacheItem) -> OdiliaResult<TextProxy<'_>> {
325 let cache = strong_cache(&cache_item.cache)?;
326 Ok(cache_item.object.clone().into_text(&cache.connection).await?)
327}
328
329#[inline]
330fn strong_cache(weak_cache: &Weak<Cache>) -> OdiliaResult<Arc<Cache>> {
331 Weak::upgrade(weak_cache).ok_or(OdiliaError::Cache(CacheError::NotAvailable))
332}
333
334#[async_trait]
335impl Accessible for CacheItem {
336 type Error = OdiliaError;
337
338 async fn get_application(&self) -> Result<Self, Self::Error> {
339 let derefed_cache: Arc<Cache> = strong_cache(&self.cache)?;
340 derefed_cache.get(&self.app).ok_or(CacheError::NoItem.into())
341 }
342 async fn parent(&self) -> Result<Self, Self::Error> {
343 let parent_item = self
344 .parent
345 .clone_inner()
346 .or_else(|| self.cache.upgrade()?.get(&self.parent.key));
347 parent_item.ok_or(CacheError::NoItem.into())
348 }
349 async fn get_children(&self) -> Result<Vec<Self>, Self::Error> {
350 self.get_children()
351 }
352 async fn child_count(&self) -> Result<i32, Self::Error> {
353 Ok(self.children_num)
354 }
355 async fn get_index_in_parent(&self) -> Result<i32, Self::Error> {
356 Ok(self.index)
357 }
358 async fn get_role(&self) -> Result<Role, Self::Error> {
359 Ok(self.role)
360 }
361 async fn get_interfaces(&self) -> Result<InterfaceSet, Self::Error> {
362 Ok(self.interfaces)
363 }
364 async fn get_attributes(&self) -> Result<HashMap<String, String>, Self::Error> {
365 Ok(as_accessible(self).await?.get_attributes().await?)
366 }
367 async fn name(&self) -> Result<String, Self::Error> {
368 Ok(as_accessible(self).await?.name().await?)
369 }
370 async fn locale(&self) -> Result<String, Self::Error> {
371 Ok(as_accessible(self).await?.locale().await?)
372 }
373 async fn description(&self) -> Result<String, Self::Error> {
374 Ok(as_accessible(self).await?.description().await?)
375 }
376 async fn get_relation_set(&self) -> Result<Vec<(RelationType, Vec<Self>)>, Self::Error> {
377 let cache = strong_cache(&self.cache)?;
378 as_accessible(self)
379 .await?
380 .get_relation_set()
381 .await?
382 .into_iter()
383 .map(|(relation, object_pairs)| {
384 (
385 relation,
386 object_pairs
387 .into_iter()
388 .map(|object_pair| {
389 cache.get(&object_pair.try_into()?).ok_or(
390 OdiliaError::Cache(
391 CacheError::NoItem,
392 ),
393 )
394 })
395 .collect::<Result<Vec<Self>, OdiliaError>>(),
396 )
397 })
398 .map(|(relation, result_selfs)| Ok((relation, result_selfs?)))
399 .collect::<Result<Vec<(RelationType, Vec<Self>)>, OdiliaError>>()
400 }
401 async fn get_role_name(&self) -> Result<String, Self::Error> {
402 Ok(as_accessible(self).await?.get_role_name().await?)
403 }
404 async fn get_state(&self) -> Result<StateSet, Self::Error> {
405 Ok(self.states)
406 }
407 async fn get_child_at_index(&self, idx: i32) -> Result<Self, Self::Error> {
408 <Self as Accessible>::get_children(self)
409 .await?
410 .get(usize::try_from(idx)?)
411 .ok_or(CacheError::NoItem.into())
412 .cloned()
413 }
414 async fn get_localized_role_name(&self) -> Result<String, Self::Error> {
415 Ok(as_accessible(self).await?.get_localized_role_name().await?)
416 }
417 async fn accessible_id(&self) -> Result<AccessibleId, Self::Error> {
418 Ok(self.object.id)
419 }
420}
421#[async_trait]
422impl Text for CacheItem {
423 type Error = OdiliaError;
424
425 async fn add_selection(
426 &self,
427 start_offset: i32,
428 end_offset: i32,
429 ) -> Result<bool, Self::Error> {
430 Ok(as_text(self).await?.add_selection(start_offset, end_offset).await?)
431 }
432 async fn get_attribute_run(
433 &self,
434 offset: i32,
435 include_defaults: bool,
436 ) -> Result<(std::collections::HashMap<String, String>, i32, i32), Self::Error> {
437 Ok(as_text(self)
438 .await?
439 .get_attribute_run(offset, include_defaults)
440 .await?)
441 }
442 async fn get_attribute_value(
443 &self,
444 offset: i32,
445 attribute_name: &str,
446 ) -> Result<String, Self::Error> {
447 Ok(as_text(self)
448 .await?
449 .get_attribute_value(offset, attribute_name)
450 .await?)
451 }
452 async fn get_attributes(
453 &self,
454 offset: i32,
455 ) -> Result<(std::collections::HashMap<String, String>, i32, i32), Self::Error> {
456 Ok(as_text(self).await?.get_attributes(offset).await?)
457 }
458 async fn get_bounded_ranges(
459 &self,
460 x: i32,
461 y: i32,
462 width: i32,
463 height: i32,
464 coord_type: CoordType,
465 x_clip_type: ClipType,
466 y_clip_type: ClipType,
467 ) -> Result<Vec<(i32, i32, String, zbus::zvariant::OwnedValue)>, Self::Error> {
468 Ok(as_text(self)
469 .await?
470 .get_bounded_ranges(
471 x,
472 y,
473 width,
474 height,
475 coord_type,
476 x_clip_type,
477 y_clip_type,
478 )
479 .await?)
480 }
481 async fn get_character_at_offset(&self, offset: i32) -> Result<i32, Self::Error> {
482 Ok(as_text(self).await?.get_character_at_offset(offset).await?)
483 }
484 async fn get_character_extents(
485 &self,
486 offset: i32,
487 coord_type: CoordType,
488 ) -> Result<(i32, i32, i32, i32), Self::Error> {
489 Ok(as_text(self).await?.get_character_extents(offset, coord_type).await?)
490 }
491 async fn get_default_attribute_set(
492 &self,
493 ) -> Result<std::collections::HashMap<String, String>, Self::Error> {
494 Ok(as_text(self).await?.get_default_attribute_set().await?)
495 }
496 async fn get_default_attributes(
497 &self,
498 ) -> Result<std::collections::HashMap<String, String>, Self::Error> {
499 Ok(as_text(self).await?.get_default_attributes().await?)
500 }
501 async fn get_nselections(&self) -> Result<i32, Self::Error> {
502 Ok(as_text(self).await?.get_nselections().await?)
503 }
504 async fn get_offset_at_point(
505 &self,
506 x: i32,
507 y: i32,
508 coord_type: CoordType,
509 ) -> Result<i32, Self::Error> {
510 Ok(as_text(self).await?.get_offset_at_point(x, y, coord_type).await?)
511 }
512 async fn get_range_extents(
513 &self,
514 start_offset: i32,
515 end_offset: i32,
516 coord_type: CoordType,
517 ) -> Result<(i32, i32, i32, i32), Self::Error> {
518 Ok(as_text(self)
519 .await?
520 .get_range_extents(start_offset, end_offset, coord_type)
521 .await?)
522 }
523 async fn get_selection(&self, selection_num: i32) -> Result<(i32, i32), Self::Error> {
524 Ok(as_text(self).await?.get_selection(selection_num).await?)
525 }
526 async fn get_string_at_offset(
527 &self,
528 offset: i32,
529 granularity: Granularity,
530 ) -> Result<(String, i32, i32), Self::Error> {
531 let uoffset = usize::try_from(offset)?;
532 if granularity == Granularity::Paragraph {
534 return Ok((self.text.clone(), 0, self.text.len().try_into()?));
535 } else if granularity == Granularity::Char {
536 let range = uoffset..=uoffset;
537 return Ok((
538 self.text
539 .get(range)
540 .ok_or(CacheError::TextBoundsError)?
541 .to_string(),
542 offset,
543 offset + 1,
544 ));
545 } else if granularity == Granularity::Word {
546 return Ok(self
547 .text
548 .split_whitespace()
550 .enumerate()
552 .filter_map(|(_, word)| {
554 let start = self
555 .text
556 .char_indices()
558 .find(|&(idx, _)| {
560 idx == word.as_ptr() as usize
561 - self.text.as_ptr() as usize
562 })
563 .map(|(idx, _)| idx)?;
565 let end = start + word.len();
567 let i_start = i32::try_from(start).ok()?;
568 let i_end = i32::try_from(end).ok()?;
569 if uoffset >= start && uoffset <= end {
571 Some((word.to_string(), i_start, i_end))
572 } else {
573 None
574 }
575 })
576 .collect::<Vec<_>>()
578 .get(0)
580 .ok_or_else(|| OdiliaError::Generic("Out of bounds".to_string()))?
582 .clone());
584 }
585 Ok(as_text(self).await?.get_string_at_offset(offset, granularity).await?)
588 }
589 async fn get_text(
590 &self,
591 start_offset: i32,
592 end_offset: i32,
593 ) -> Result<String, Self::Error> {
594 self.text
595 .get(usize::try_from(start_offset)?..usize::try_from(end_offset)?)
596 .map(std::borrow::ToOwned::to_owned)
597 .ok_or(OdiliaError::Generic("Type is None, not Some".to_string()))
598 }
599 async fn get_text_after_offset(
600 &self,
601 offset: i32,
602 type_: u32,
603 ) -> Result<(String, i32, i32), Self::Error> {
604 Ok(as_text(self).await?.get_text_after_offset(offset, type_).await?)
605 }
606 async fn get_text_at_offset(
607 &self,
608 offset: i32,
609 type_: u32,
610 ) -> Result<(String, i32, i32), Self::Error> {
611 Ok(as_text(self).await?.get_text_at_offset(offset, type_).await?)
612 }
613 async fn get_text_before_offset(
614 &self,
615 offset: i32,
616 type_: u32,
617 ) -> Result<(String, i32, i32), Self::Error> {
618 Ok(as_text(self).await?.get_text_before_offset(offset, type_).await?)
619 }
620 async fn remove_selection(&self, selection_num: i32) -> Result<bool, Self::Error> {
621 Ok(as_text(self).await?.remove_selection(selection_num).await?)
622 }
623 async fn scroll_substring_to(
624 &self,
625 start_offset: i32,
626 end_offset: i32,
627 type_: u32,
628 ) -> Result<bool, Self::Error> {
629 Ok(as_text(self)
630 .await?
631 .scroll_substring_to(start_offset, end_offset, type_)
632 .await?)
633 }
634 async fn scroll_substring_to_point(
635 &self,
636 start_offset: i32,
637 end_offset: i32,
638 type_: u32,
639 x: i32,
640 y: i32,
641 ) -> Result<bool, Self::Error> {
642 Ok(as_text(self)
643 .await?
644 .scroll_substring_to_point(start_offset, end_offset, type_, x, y)
645 .await?)
646 }
647 async fn set_caret_offset(&self, offset: i32) -> Result<bool, Self::Error> {
648 Ok(as_text(self).await?.set_caret_offset(offset).await?)
649 }
650 async fn set_selection(
651 &self,
652 selection_num: i32,
653 start_offset: i32,
654 end_offset: i32,
655 ) -> Result<bool, Self::Error> {
656 Ok(as_text(self)
657 .await?
658 .set_selection(selection_num, start_offset, end_offset)
659 .await?)
660 }
661 async fn caret_offset(&self) -> Result<i32, Self::Error> {
662 Ok(as_text(self).await?.caret_offset().await?)
663 }
664 async fn character_count(&self) -> Result<i32, Self::Error> {
665 Ok(i32::try_from(self.text.len())?)
666 }
667}
668
669#[derive(Clone, Debug)]
675pub struct Cache {
676 pub by_id: ThreadSafeCache,
677 pub connection: zbus::Connection,
678}
679
680impl Cache {
684 #[must_use]
686 pub fn new(conn: zbus::Connection) -> Self {
687 Self {
688 by_id: Arc::new(DashMap::with_capacity_and_hasher(
689 10_000,
690 FxBuildHasher::default(),
691 )),
692 connection: conn,
693 }
694 }
695 pub fn add(&self, cache_item: CacheItem) -> OdiliaResult<()> {
701 let id = cache_item.object.clone();
702 self.add_ref(id, &Arc::new(RwLock::new(cache_item)))
703 }
704
705 pub fn add_ref(
709 &self,
710 id: CacheKey,
711 cache_item: &Arc<RwLock<CacheItem>>,
712 ) -> OdiliaResult<()> {
713 self.by_id.insert(id, Arc::clone(cache_item));
714 Self::populate_references(&self.by_id, cache_item)
715 }
716
717 pub fn remove(&self, id: &CacheKey) {
719 self.by_id.remove(id);
720 }
721
722 #[must_use]
726 pub fn get_ref(&self, id: &CacheKey) -> Option<Arc<RwLock<CacheItem>>> {
727 self.by_id.get(id).as_deref().cloned()
728 }
729
730 #[must_use]
735 pub fn get(&self, id: &CacheKey) -> Option<CacheItem> {
736 Some(self.by_id.get(id).as_deref()?.read().ok()?.clone())
737 }
738
739 #[must_use]
741 pub fn get_all(&self, ids: &[CacheKey]) -> Vec<Option<CacheItem>> {
742 ids.iter().map(|id| self.get(id)).collect()
743 }
744
745 pub fn add_all(&self, cache_items: Vec<CacheItem>) -> OdiliaResult<()> {
750 cache_items
751 .into_iter()
752 .map(|cache_item| {
753 let id = cache_item.object.clone();
754 let arc = Arc::new(RwLock::new(cache_item));
755 self.by_id.insert(id, Arc::clone(&arc));
756 arc
757 })
758 .collect::<Vec<_>>() .into_iter()
760 .try_for_each(|item| Self::populate_references(&self.by_id, &item))
761 }
762 pub fn remove_all(&self, ids: &Vec<CacheKey>) {
764 for id in ids {
765 self.by_id.remove(id);
766 }
767 }
768
769 pub fn modify_item<F>(&self, id: &CacheKey, modify: F) -> OdiliaResult<bool>
778 where
779 F: FnOnce(&mut CacheItem),
780 {
781 let entry = if let Some(i) = self.by_id.get(id) {
785 (*i).clone()
787 } else {
788 tracing::trace!("The cache does not contain the requested item: {:?}", id);
789 return Ok(false);
790 };
791 let mut cache_item = entry.write()?;
792 modify(&mut cache_item);
793 Ok(true)
794 }
795
796 pub async fn get_or_create(
804 &self,
805 accessible: &AccessibleProxy<'_>,
806 cache: Weak<Self>,
807 ) -> OdiliaResult<CacheItem> {
808 let primitive = accessible.try_into()?;
810 if let Some(cache_item) = self.get(&primitive) {
811 return Ok(cache_item);
812 }
813 let start = std::time::Instant::now();
815 let cache_item = accessible_to_cache_item(accessible, cache).await?;
816 let end = std::time::Instant::now();
817 let diff = end - start;
818 tracing::debug!("Time to create cache item: {:?}", diff);
819 self.add(cache_item.clone())?;
821 Ok(cache_item)
823 }
824
825 pub fn populate_references(
831 cache: &ThreadSafeCache,
832 item_ref: &Arc<RwLock<CacheItem>>,
833 ) -> Result<(), OdiliaError> {
834 let item_wk_ref = Arc::downgrade(item_ref);
835
836 let mut item = item_ref.write()?;
837 let item_key = item.object.clone();
838
839 let parent_key = item.parent.key.clone();
840 let parent_ref_opt = cache.get(&parent_key);
841
842 let ix_opt = usize::try_from(item.index).ok();
844
845 for child_ref in &mut item.children {
847 if let Some(child_arc) = cache.get(&child_ref.key).as_ref() {
848 child_ref.item = Arc::downgrade(child_arc);
849 child_arc.write()?.parent.item = Weak::clone(&item_wk_ref);
850 }
851 }
852
853 if let Some(parent_ref) = parent_ref_opt {
855 item.parent.item = Arc::downgrade(&parent_ref);
856 if let Some(ix) = ix_opt {
857 if let Some(cache_ref) = parent_ref
858 .write()?
859 .children
860 .get_mut(ix)
861 .filter(|i| i.key == item_key)
862 {
863 cache_ref.item = Weak::clone(&item_wk_ref);
864 }
865 }
866 }
867 Ok(())
868 }
869}
870
871pub async fn accessible_to_cache_item(
883 accessible: &AccessibleProxy<'_>,
884 cache: Weak<Cache>,
885) -> OdiliaResult<CacheItem> {
886 let (app, parent, index, children_num, interfaces, role, states, children) = tokio::try_join!(
887 accessible.get_application(),
888 accessible.parent(),
889 accessible.get_index_in_parent(),
890 accessible.child_count(),
891 accessible.get_interfaces(),
892 accessible.get_role(),
893 accessible.get_state(),
894 accessible.get_children(),
895 )?;
896 let text = match accessible.to_text().await {
898 Ok(text_iface) => text_iface.get_all_text().await,
900 Err(_) => Ok(accessible.name().await?),
902 }?;
903 Ok(CacheItem {
904 object: accessible.try_into()?,
905 app: app.try_into()?,
906 parent: CacheRef::new(parent.try_into()?),
907 index,
908 children_num,
909 interfaces,
910 role,
911 states,
912 text,
913 children: children
914 .into_iter()
915 .map(|k| Ok(CacheRef::new(k.try_into()?)))
916 .collect::<Result<_, AccessiblePrimitiveConversionError>>()?,
917 cache,
918 })
919}