Skip to content

Commit

Permalink
First attempt at basic axum-style state management (#138)
Browse files Browse the repository at this point in the history
* First attempt at basic axum-style state management

Empty service/handler impl

I'm starting to understand the basis of Tower's service model.
However, that does not make this attempt good.
This only covers the case of a function that takes no arguments (and that
is on purpose).

In order to take more arguments, I will need to create a trait which can
handle them, think something like Axum's `FromRequest`. We will need an
adapted form of this, since we actually will want it for `FromEvent`.
You might be wondering why we don't just use `From<Event>`, but that is
annoyingly problematic, since you will get conflicting implementations
because "downstream crates may implement this for ..."; as far as I can
tell, this will require a custom trait.

I have not yet found a way around this.

Add Handler impl for up to 3 args

Ah ha! I found the secret! So it's true that you can not just take in
any argument for the first one. That's essentially special-cased.

But, assuming that most functions' first argument would be the event
itself (atspi::Event = Request), then we can make that the first
argument, followed by N arguments that implement `From<State>`
(roughly).

This will allow us to implement `From<State>` for various types, which
will be wrappers of more widely available types, for example:

- Current/LastFocusedItem(CacheItem)
- Current/LastCursorPos(CacheItem, u32)
- EventItem(CacheItem)
- etc.

Add generic first parameter for Handler

This allows the first parameter to be any type that implements
`TryFrom<atspi::Event>`. It currently requires an unwrap due to the
possibility of it being the wrong event type. This should be converted
into its own Layer type, which can easily wrap the inner service,
instead of manually implementing the conversion within the `Service`
impl for `Handler<_>` types.

* Implement generic first parameters for Handler

Combined with the new atspi_event_handler function, automatically
convert a HandlerService for a specific event type, into a generic event
handler, which checks if the event type matches before passing it into
the inner event handler.

It will either: transform the event into its specific type, or abort
the calling of the service with an
`OdiliaError::AtspiError(AtspiError::Conversion(...))`

* [WIP] attempt service-based event extractors

* [WIP] Use layers to compose services instead of chaining services with combinators

* You can now add boxed event handlers to the handler list

* [WIP] remove Filter functions

* [WIP] tower reader

* [WIP] run all events handlers bound to a particular event

* [WIP] first use of tower-based Odilia

* [WIP] update SSIP, add speech to certain events

* [WIP] return set of commands, instead of unit

* [WIP] await all futures for an ev type in serial

This is done to help with ordering caching handlers.
It is important that caching is done first.

* [WIP] Add commands enum

* Make Handler trait generic over response type

* Add command handler implementations

* [WIP] finished and implemented serial futures, MultiService

* [WIP] remove multiservice

* [WIP] SerialHandlers use concrete type

* [WIP] handle sequential futures better

* [WIP] using boxes, get further ahead

* Update interfaces in accordance with new atspi version

* Use event introspection

Some cleanup

* Fix loop that never terminates; clone for now

* Expand command handler impls to 3 params

* Add new command introspection path

* Add command_handlers function

* Use discriminants instead of random strings

* Add SendError type from tokio; you need the 'tokio' feature enabled to get the conversions

* Change trait bounds to allow any Command: TryInto<C>

* Remove unused deps that are now in common

* Use the tokio feature for common so that we can access extra error types.

* Create one and exactly one handler function for the command speak

* Pass commands from handlers to command handlers with error handling; it currently works!

* Remove Speech var from doc_loaded func

* Add IntoCommands trait, and implement it for a tuple up to three

* Use IntoCommands trait to bound response types

* Add IntoCommands impl for Results which contain other Into<Command> items

* Use R instead of Response alias

* Add From<&str> for OdiliaCommand

* Add change_priority to command handlers

* Add TryIntoCommands

* Rework trait bounds for additional flexibility

* Use IntoCommands recursively

* Use 'impl TryIntoCommand' for return type of event processor :)

* Add more specific timing to logs

* Add microsecond logtime

* Add Speech(SsipClientAsync) AsyncTryFrom impl for state

- One, we set up a few new traits:
	- AsyncTryFrom/AsyncTryInto
	- FromState
- Second, we use those traits instead of `From<S>` to get arguments to
  handler functions. This allows us (in theory) to get cached items of
  various kinds.
- Third, this removes some old test code.
- Fourth, this adds new trait bounds using feature flags to make this
  work.
  	- `try_trait_v2` for `FromResidual`
	- `impl_trait_in_assoc_type` (ITIAT), which is at least already
	  has its stabalization PR opened

* Remove failure case for AccessiblePrimitive::from_event

* Remove use of ? in now infallible from_event and state

* Update benchmarks to work on latest atspi

* Fix structure of caching fns

* Adjust to new cache fn style

* Add caching, AsyncTryInto layer

- Add new CacheLayer that injects Arc<Cache> as a second parameter of
  the inner Service.
- Add new AsyncTryInto layer to generically convert from one type to
  another, then call the inner service once that has been done.
  	- Makes sure to follow advice to use `std::mem::replace` instead
	  of just plain copying.
- Drop some trait bounds when unneeded.
- Wrap all end-user events in EventItem<E> where E is a specific event
  type from `atspi` that implements `EventProperties`.
- The complexity is overwhelming :(, hoping to crate this out, use less
  `impl Future` in trait (since it isn't stable, maybe just Box<dyn> for
  now?)
- Also uses unstable trait `FromResidual`

* Use dbus if no cache available for relation

* Macroify the complex trait definitions

- impl_handler
- impl_from_state

Both are generic over a variable number of parameters and error types,
assuming the errors implement `Into<OdiliaError>` and the types
implement various underlying traits.

* Use more flexible join! macro instead of join*()

* Update tower sub-module and split into various files

* Bring TryIntoLayer/Service into its own file

* Further split Handler trait and impl from Handlers struct

* [WIP] rethinking the handler trait

* [WIP] add desired layer layout

* Rework Handler function to be much more generic

* More generic-aware AsyncTry, auto impl for TryFromState

* Genericise from_state, clone all over; remove macro for now

* Genericize state service

* Use new, less strict traits: require more trait bounds on *_listener functions

* Add TryFromState impls, Command<C> type

* Use new types in handlers, import new traits

* Remove unbounded channels

* Remove source of possible deadlock?

* Remove .await where it is no longer needed

* Add futures-concurrency crate

* Remove cache layer

* Remove CacheProvider trait

* Remove FromState in favour of TryFromState

* Add IterService and ServiceSet

- IterService provides a way to stack two
  services together, where one take the iterated output of the other
- ServiceSet provides a way to run sets of identically-typed services in
  series, much like SerialFutures did for Futures, but for Services.
- Both take advantage of `type Future = impl Future<...>`, relying on a
  full Rust 2024 compiler which is not here yet.

* Remove serial_fut module

* DO NOT PRINT THE CACHE!

* Instead of not printing the cache, just make the debug output not so long

* Do not debug param of function that uses zbus::Connection

* Modify ServiceSet in accordance with real-world usage

* Remove unused function, use new ServiceSet for event handlers

* User ServiceSet

* Add ChoiceService

* Add ServiceNotFound error type

* fmt

* Use more convenience functions

* Update deps to latest zbus, seems to fix a deadlock issue

* Add tokio-console capability, add script for conveneicne

* Add tokio-console feature to main odilia binary

* Cleanups and new inner services

- Remove unused deps
- Use if let ... on loop { ... } instead of while let Some(...)
- Switch to using the ChoiceService model
- Remove attempt at "generic handlers"; handled by ChoiceService

* Move to mutli-threaded runtime; add channel indirection on zbus messages

* Add comments explaining channel redirection

* Create CacheEvent from state + E (event)

* Add clone for choice service

* public new function in IterService

* Use command sub-service to layer atspi responses

* Use futures-concurrency as workspace package, add rt-multi-thread feature to tokio

Move futures-concurrency to workspace

* Stop using channels to communicate commands

Stop using channels to communicate commands

* Add instrumenting on try_from_state

* Add caret_moved handler (which does not work due to no focus being recorded)

* Various improvements to common

- Move `AccessiblePrimitive` to common from cache
- Add `CaretPos` and `Focus` variants to `Command`

* Remove AccessiblePrimitive from cache

* Add tracing option to common, make atspi-proxies a dep

* Update AccessiblePrimitive location

* Add AccessibleHistory and CurrentCaretPos state structs

* Add focus changed and caret pos updater handlers

* Update cargo lock

* Speak entire item upon focus

* Add ability to unwrap Infallible values, then map them

* Add new ServiceExt API for DX

* Use new Odilia-specific ServiceExt

* Remove return type generic on HandlerService

* Add iter_into combinator

* Add static version of Chooser trait

* Use ChooserStatic trait, iter_into combinator

* Use boxed_clone combinator

* Fix docs

* Add nightly to CI temporarily

* Use dtolnay instead of actions-rs actions

* Adjust clippy lints

* Fix some other CI issues with old versions of actions-rs

* Fix notify private documentation

* Fix type in CI

* Add clippy component for clippy job

* Make sure llvm-tools are installed for the coverage components

* Setup nightly MSRV

* Fix yaml error

* Add StateChanged newtype with predicate trait implementations

* Add new error for predicate failure

* Add the refinement crate for additional types, and derived-deref for convenience

* Add state_changed crate to tower subcrate

* Add TODO on error variant

* Add focused/unfocused handlers using new StateChanged<S, E> type

* Update lock file

* Use latest atspi branch in object handlers

* Fix bench imports

* Update dashmap and use inlining for about a 10% speed improvement; tested by running benchmarks

* Add more type aliases for simplicity

* Use enabled field as bool

* Use bool as predicate for state changed instead of i32

* Rename predicates

* Use deref on CacheEvent

* Use specific Focused/Unfocused types

* Move CacheEvent into its own file; move Choice impls into choice

* Add CacheEvent<E> types

* Remove TryFromState for CacheEvent<E> from state

* Use main atspi

* Only speak 'doc loaded' on focused applications

* Add specific nightly toolchain (for now)
  • Loading branch information
TTWNO committed Sep 1, 2024
1 parent 04fbf9e commit 6c50f0c
Show file tree
Hide file tree
Showing 38 changed files with 2,794 additions and 451 deletions.
39 changes: 17 additions & 22 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ jobs:
${{ runner.os }}-${{ matrix.target }}-build-${{ env.cache-name }}-
- name: Install Rust
uses: actions-rs/toolchain@v1
uses: dtolnay/rust-toolchain@master
with:
toolchain: stable
toolchain: nightly
target: ${{ matrix.target }}

- name: Install Dependencies
Expand Down Expand Up @@ -68,16 +68,13 @@ jobs:
${{ runner.os }}-x86_64-unknown-linux-gnu-build-${{ env.cache-name }}-
- name: Install Rust
uses: actions-rs/toolchain@v1
uses: dtolnay/rust-toolchain@master
with:
toolchain: stable
toolchain: nightly
components: clippy

- name: Run tests
uses: actions-rs/cargo@v1
with:
command: clippy
# "-- -D warnings" will make the job fail if their are clippy warnings
args: --workspace --no-deps -- -D warnings -W clippy::print_stdout
run: cargo clippy --tests --workspace --no-deps -- -D warnings

rustfmt:
runs-on: ubuntu-latest
Expand All @@ -86,17 +83,14 @@ jobs:
uses: actions/checkout@v3

- name: Install Rust
uses: actions-rs/toolchain@v1
uses: dtolnay/rust-toolchain@master
with:
profile: minimal
toolchain: stable
toolchain: nightly
components: rustfmt

- name: Run formatter
uses: actions-rs/cargo@v1
with:
command: fmt
args: --all --check
run: cargo fmt --all --check

rustdoc:
runs-on: ubuntu-latest
Expand All @@ -119,14 +113,12 @@ jobs:
${{ runner.os }}-x86_64-unknown-linux-gnu-build-${{ env.cache-name }}-
- name: Install Rust
uses: actions-rs/toolchain@v1
uses: dtolnay/rust-toolchain@master
with:
toolchain: stable
toolchain: nightly

- name: Generate Documentation
uses: actions-rs/cargo@v1
with:
command: doc
run: cargo doc --workspace --document-private-items

- name: Deploy Documentation
uses: peaceiris/actions-gh-pages@v3
Expand Down Expand Up @@ -159,7 +151,10 @@ jobs:
restore-keys: |
${{ runner.os }}-${{ matrix.target }}-build-${{ env.cache-name }}-
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
uses: dtolnay/rust-toolchain@master
with:
toolchain: nightly
components: llvm-tools
- name: cargo install llvm-cov
uses: taiki-e/install-action@cargo-llvm-cov
- name: cargo generate lockfile
Expand Down Expand Up @@ -199,7 +194,7 @@ jobs:
- name: Install Rust
uses: dtolnay/rust-toolchain@master
with:
toolchain: stable
toolchain: nightly
- name: Install Cargo MSRV Verifier
run: cargo install cargo-msrv --force
- name: Check MSRV Compliance
Expand Down
Loading

0 comments on commit 6c50f0c

Please sign in to comment.