-
Notifications
You must be signed in to change notification settings - Fork 10
refactor: Add NonNullObjectRef as part of ObjectRef
#301
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
We'd like to encode in our APIs that some functions should not be
called with a null-reference. This required us to rethink how we
organize our references.
We landed on:
```rust
enum ObjectRef<'o> {
Null,
NonNull(NonNullObjectRef<'o>),
}
enum NonNullObjectRef<'o> {
Borrowed { name: UniqueName<'o>, path: ObjectPath<'o> },
Owned { name: OwnedUniqueName, path: OwnedObjectPath }
}
struct ObjectRefOwned(ObjectRef<'static>)
```
Which offers us an `ObjectRef` and `ObjectRefOwned` that can be
deserialized from any reference, including null-referenes. Good for
receiving references.
`NonNullObjectRef` is particularly well suited where we want to
accept object references.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #301 +/- ##
==========================================
- Coverage 75.17% 74.79% -0.38%
==========================================
Files 44 44
Lines 4374 4586 +212
==========================================
+ Hits 3288 3430 +142
- Misses 1086 1156 +70 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
e2b64ec to
3720c34
Compare
|
The fixes to the Fixing it involves a bit of "forensics". I need to recreate the flow as it was when the example ran when we still had null references that could be made into proxies without problems. Performing calls would error, very different from what we have now. On the bright side the macros I had before are now gone, in favor of mostly this pattern: // Skip null reference and convert to NonNullObjectRef.
let Ok(non_null_ref) = NonNullObjectRef::try_from(app.clone()) else { continue }; |
186fc6b to
0d82a2e
Compare
TTWNO
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good work so far. Sorry about my limited free time.
| /// In AT-SPI2, objects in the applications' UI object tree are uniquely identified | ||
| /// using an application's bus name and object path. "(so)" | ||
| /// | ||
| /// Emitted by `RemoveAccessible` and `Available` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Link the events!
|
|
||
| /// Returns the name of the object reference. | ||
| #[must_use] | ||
| #[allow(clippy::match_same_arms)] // Arms differ by lifetime |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Name the two arms' name variable differently in the two cases, and then you don't need the clippy allow.
|
|
||
| /// Returns the path of the object reference. | ||
| #[must_use] | ||
| #[allow(clippy::match_same_arms)] // Arms differ by lifetime |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Likewise as above.
| // because PartialEq does not consider it either. | ||
| // | ||
| // This to uphold the contract that if a == b, then a.hash() == b.hash() must hold true. | ||
| impl Hash for NonNullObjectRef<'_> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good observation that derive-ing would have missed!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you haven't already, please add tests to ensure that this remains the case.
| } | ||
|
|
||
| #[test] | ||
| fn non_null_hash_and_object_coherence() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh good! There is a test after all :D
atspi-connection/src/p2p.rs
Outdated
| let mut peers = Vec::with_capacity(accessible_applications.len()); | ||
|
|
||
| for app in accessible_applications { | ||
| if app.is_null() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should probably be:
let app = match app.is_null() {
true => continue,
// etc.
};IMO easier to reason about.
atspi/examples/accessible-counts.rs
Outdated
| for child in root.get_children().await?.iter() { | ||
| let proxy = child.clone().into_accessible_proxy(conn).await?; | ||
| for child in root.get_children().await?.into_iter() { | ||
| let Ok(child) = NonNullObjectRef::try_from(child) else { continue }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be the same as the other case; show a unified way to deal with null-refs.
let x = match child.is_null() {
false => continue,
_ => // etc.
};|
|
||
| for app in apps.iter() { | ||
| let proxy = app.clone().into_accessible_proxy(conn).await?; | ||
| for app in apps.into_iter().flat_map(|or| NonNullObjectRef::try_from(or).ok()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On second thought.... this pattern is very common: continue if null, otherwise try to convert to proxy. That should probably just be a method?
|
If you are looking at other ways to do the "if null continue, else convert to proxy" thing., you could also try |
Example watched mouse events but it doesn't actually use them anywhere
This commit adds local git hooks based on those distributed with [zbus](https://github.com/dbus2/zbus). This adds a "cargo machete" pass to check for unused dependencies to the pre-commit script. Considering `.git/hooks` is not part of the distribution, contributors are advised to configure git to enable these hooks for this repository: ```sh git config core.hooksPath .githooks ``` Thank you @C-Loftus for bringing this up and @jonathanhood for the tidy helper script. Addresses: #298
The event enum had a mistake in the `DBusMatchRule` impl. Fixes: #306
- `GetUri` -> `GetURI` - `Nanchors` -> `NAnchors`
Make `event_stream` to parse signals with prefix "org.a11y.atspi." only. Otherwise it will error on non-atspi signals this connection expects. (`NameOwnerChanged` of "org.freedesktop.DBus") Fixes: #307
and.. remove bad lifetime requirements on return in methods `name()` / `path()`. We no more need to opt out of clippy.
Rustc can only guarantee the static arm if the impl is static too. But this comes with the disadvantage that a second 'static impl cannot have same name methods. I had to reinstate the opt-out for match-on-same-arm pedantic clippy warning. fixes doctests
0d82a2e to
841f994
Compare
To encode in the APIs that functions should not be called with a null-reference and to prevent this from happening by accident, we introduce
NonNullObjectRef.To prevent repeating ourselves, this refactor changes the
ObjectReftypes to:Which offers an
ObjectRefandObjectRefOwnedthat can be deserialized from any reference, including null-referenes. Good for receiving references 'bus-side'.NonNullObjectRefis particularly well suited where we want to accept object references.Addresses: #296