Draft: alternate ManagedCS trait#350
Conversation
Co-authored-by: Grant Miller <GrantM11235@gmail.com>
|
r? @therealprof (rust-highfive has picked a reviewer for you, use r? to override) |
|
There was a problem hiding this comment.
Nice work! Looks pretty interesting and simple to use.
I think it is not too bad to omit the default implementations. Also in light of the downstream problems caused by these elsewhere.
I see the "unnecessary" boilerplate increase, though.
Is the inner() method in ManagedCs necessary? It seems like a method that should rather be part of SpiWithCs.
One thing I did not understand is why ManagedCsAlt requires Transactional (but not ManagedCs?). Is this just not part of this PR yet? I do not see any related trait bounds.
spi.with_cs(|d| {
spi.with_cs(|d| {
let _ = d.write(&[0xaa, 0xbb, 0xcc]);
Ok(())
});
Ok(())
});
/// blah
pub trait ManagedCs: ErrorType {
/// Inner SPI type
type Inner: ErrorType;
/// Future returned by the `with_cs` method.
type WithCsFuture<'a, R, F, Fut>: Future<Output = Result<R, Self::Error>> + 'a
where
Self: 'a,
R: 'a,
F: FnMut(&'a mut Self::Inner) -> Fut + 'a,
Fut: Future<Output = R> + 'a;
/// Execute the provided closure within a CS assertion
fn with_cs<'a, R, F, Fut>(&'a mut self, f: F) -> Self::WithCsFuture<'a, R, F, Fut>
where
F: FnMut(&'a mut Self::Inner) -> Fut + 'a,
Fut: Future<Output = R> + 'a;
/// Reference the inner SPI object without manipulating CS
fn inner(&mut self) -> &mut Self::Inner;
}
/// [`ManagedCs`] implementation for [`SpiWithCs`] wrapper.
/// Provides `with_cs` function that asserts and deasserts CS
impl<Spi, Pin> ManagedCs for SpiWithCs<Spi, Pin>
where
Spi: ErrorType,
Pin: OutputPin + DigitalErrorType,
{
type Inner = Spi;
type WithCsFuture<'a, R, F, Fut>
where
Self: 'a,
R: 'a,
F: FnMut(&'a mut Self::Inner) -> Fut + 'a,
Fut: Future<Output = R> + 'a,
= impl Future<Output = Result<R, Self::Error>> + 'a;
/// Execute the provided closure within a CS assertion
fn with_cs<'a, R, F, Fut>(&'a mut self, mut f: F) -> Self::WithCsFuture<'a, R, F, Fut>
where
F: FnMut(&'a mut Self::Inner) -> Fut + 'a,
Fut: Future<Output = R> + 'a,
{
async move {
self.cs.set_low().map_err(SpiWithCsError::Pin)?;
let r = f(&mut self.spi).await;
self.cs.set_high().map_err(SpiWithCsError::Pin)?;
Ok(r)
}
}
/// Reference the inner SPI object without manipulating CS
fn inner(&mut self) -> &mut Self::Inner {
&mut self.spi
}
} |
are we talking eliding the default
yep, definitely strange. i think the second has to be a noop, because if you call
yep agreed, i just hadn't decided how to bound the error yet. updated now.
yep, the advantage here is being able to have user code within CS though eh. i think there's no harm in having separate |
| /// Executes the provided closure within a CS assertion | ||
| fn with_cs<R>( | ||
| &mut self, | ||
| mut f: impl FnMut(&mut Self::Inner) -> Result<R, Self::Error>, |
There was a problem hiding this comment.
These should be FnOnce, so it can consume captured stuff.
|
Closing in favour of #351 |
Based on work by @GrantM11235 as an alternative to #245.
There appears to be a choice between a magic / automatic impl that will deal with CS assertion itself, but conflicts with
&mut Timpls, and a more manual option where users will call.with_cs(|p| { ... })to perform operations which seems rather less complex.(Note that for
ManagedCsAltSpiWithCsmust implementTransactionaletc. which it does not yet in this PR, and&mut Timpls are disabled toManagedCscan compile)Co-authored-by: Grant Miller GrantM11235@gmail.com