Skip to content
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

prepare v1.0.0 release #51

Merged

Conversation

rursprung
Copy link
Collaborator

see the individual commit messages for more details.

the only "violation" of the general guidelines i could see was the (optional) dependency to defmt v0.3 (which many claim is de-facto 1.0). with the dependency renamed to defmt-03 it should be possible to subsequently add a defmt-10 feature to support both at the same time.

the other open point is implementing std::error::Error for the error enums. that can come once rust 1.81.0 has been released which moves this to core::error:Error, so no std feature will be required here (as per our README we can bump the MSRV w/o a major release, thus it's ok to bump it to 1.81.0 once that has been released).

thus, once this PR has been reviewed & merged it should be fine to release v1.0.0 and close #4.

@reviewers: if possible, please give the whole crate a glance again to make sure that we're not missing anything relevant for v1.0.0. thanks!

Copy link
Member

@eldruin eldruin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your work!
This PR seems fine to me but I have not had a look at the whole source code.
I would suggest having a look at the checklist here.
One thing I noticed is that the dependency to embedded-hal-mock can be customized so that this does not depend on embedded-hal 0.2 with:

embedded-hal-mock = {version = "0.11", default-features=false, features=["eh1"]}

CHANGELOG.md Show resolved Hide resolved
README.md Outdated Show resolved Hide resolved
README.md Outdated Show resolved Hide resolved
Copy link
Member

@ryan-summers ryan-summers left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey there, nice work on this! I did a quick pass over the repository as a whole and wanted to provide some general feedback since you requested this yesterday on Matrix.

Feel free to ignore any of these if you disagree:

  • I suspect you could simplify your error definitions by defining a single embedded_hal::digital::ErrorType for all of the pins and assume they all have the same error type. It would be exceptionally weird if they didn't. And even in the edge case where i.e. some pins are on a GPIO expander and others are on the MCU, a user could make their own error enum to wrap them up into a single type expression. This might aid in usability of the driver.
  • Same for the above with PWM errors
  • DriveCommand specifies a speed in percentage, but the stored value is a u8. Are they supposed to use 0 for 0% and u8::MAX for 100%?
  • Beware exposing pub fields in Tb6612fng. Read through https://rust-lang.github.io/api-guidelines/future-proofing.html#structs-have-private-fields-c-struct-private for an analysis of why. It recommends exposing the fields as getter methods.
  • As it stands, your constructor just takes a whole bunch of pins. This could make it easy to get them mixed up. Maybe a builder pattern would be better to ensure the pins are going to their proper definitions? Or have the Tb6612fng take in two Motors` instead.

Cargo.toml Outdated Show resolved Hide resolved
@ripytide
Copy link
Collaborator

I suspect you could simplify your error definitions by defining a single embedded_hal::digital::ErrorType for all of the pins and assume they all have the same error type. It would be exceptionally weird if they didn't. And even in the edge case where i.e. some pins are on a GPIO expander and others are on the MCU, a user could make their own error enum to wrap them up into a single type expression. This might aid in usability of the driver.
Same for the above with PWM errors

I'm not sure I understand this suggestion, are you suggesting we create a catch-all error type? Such as:

struct Error;
impl Tb6612fng {
    fn drive(..) -> Result<(), Error>;
}

This has already been discussed as being less flexible due to the scenario you described as having pins with different error types. How would a user create their own wrapping error types though? Would that not require a generic return type? Such as:

impl Tb6612fng {
    fn drive<E: embedded_hal::digital::ErrorType>(..) -> Result<(), E>;
}

This seems harder to use since it adds trait bounds to the drive() function which can cause hard to diagnose trait bound compile errors.

DriveCommand specifies a speed in percentage, but the stored value is a u8. Are they supposed to use 0 for 0% and u8::MAX for 100%?

This is since we pass the u8 to embedded_hal::SetDutyCycle::set_duty_cycle_percent(speed: u8), this could be better documented on our side though about 0-100 being required. Possibly, we could also switch to a DrivePercent and DriveFraction enum so a user could pick which method they wanted to use (SetDutyCycle::set_duty_cycle_percent() being the other possible method).

Beware exposing pub fields in Tb6612fng. Read through https://rust-lang.github.io/api-guidelines/future-proofing.html#structs-have-private-fields-c-struct-private for an analysis of why. It recommends exposing the fields as getter methods.

Switching to getting mutable motor references via getters also has some drawbacks such as preventing multiple mutable references otherwise allowed:

let driver = Tbb6612fng::new(...);

//no borrow checker issue
takes_mutable_reference(&mut driver.motor_a);
takes_mutable_reference(&mut driver.motor_b);

//borrow checker issue
takes_mutable_reference(driver.motor_a_mut());
takes_mutable_reference(driver.motor_b_mut());

As it stands, your constructor just takes a whole bunch of pins. This could make it easy to get them mixed up. Maybe a builder pattern would be better to ensure the pins are going to their proper definitions? Or have the Tb6612fng take in two Motors` instead.

Good point, Tb6612fng::new(motor_a: Motor, motor_b: Motor, stby: STBY) sounds much nicer to use.

rursprung and others added 7 commits September 9, 2024 10:51
this follows the pattern from other crates (e.g. `heapless`) which
allows to add e.g. an upcoming `defmt` v1.0 support as a non-breaking
feature.
this lets the caller construct the `Motor` and hand it over to
`Tb6612fng` instead of having to pass all the pins. this reduces the
risk fo mixing up the pins as the old API wuold take 7 pins as an input.
@rursprung
Copy link
Collaborator Author

thanks a lot for your review! sorry for not reacting on it earlier, but i was away!

I would suggest having a look at the checklist here.

i had already done that, thanks!

One thing I noticed is that the dependency to embedded-hal-mock can be customized so that this does not depend on embedded-hal 0.2 with:
`embedded-hal-mock = {version = "0.11", default-features=false, features=["eh1"]}

thanks! i've changed it now and also raised dbrgn/embedded-hal-mock#122

  • I suspect you could simplify your error definitions by defining a single embedded_hal::digital::ErrorType for all of the pins and assume they all have the same error type. It would be exceptionally weird if they didn't. And even in the edge case where i.e. some pins are on a GPIO expander and others are on the MCU, a user could make their own error enum to wrap them up into a single type expression. This might aid in usability of the driver.
  • Same for the above with PWM errors

as per @ripytide's comment i don't thinkt hat this simplification would actually work (or simplify anything)?

  • DriveCommand specifies a speed in percentage, but the stored value is a u8. Are they supposed to use 0 for 0% and u8::MAX for 100%?

the comment on the enum says that it's "in percentage" - isn't that enough to show that it's in the 0..100 range? (since it's u8 it can't be 0..1, so that confusion at least is avoided). pulling in something like bounded-integer (or hand-rolling it) seems like an overkill here?

i am aware that it's not recommended to have pub fields, but as pointed out by @ripytide it makes it so much more convenient to use it since you need mut access to the Motor to use it.

  • As it stands, your constructor just takes a whole bunch of pins. This could make it easy to get them mixed up. Maybe a builder pattern would be better to ensure the pins are going to their proper definitions? Or have the Tb6612fng take in two Motors` instead.

thanks, you're right! i've changed this now

based on the discussion on GitHub we don't need this dependency. it's
the only 0.x dependency currently present in the crate and doesn't
provide a lot of value since consuming code can use
`defmt::Debug2Format` when needed. the single `defmt::debug!` statement
in `Motor::drive` was not very helpful anyway if two motors were
connected as it did not differentiate between the motors in the message.

since this was the only optional feature there's no need anymore to test
with `--all-features` in the CI. this has been removed from the build
matrix (but the possibility to add it later has been kept as it doesn't
make the build script unnecessarily complicated).
@rursprung rursprung force-pushed the prepare-1.0-release branch 3 times, most recently from d913b84 to 8224bfb Compare September 9, 2024 10:59
Copy link
Member

@eldruin eldruin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From a quick look the change seems fine to me but I have not checked the whole code. Releasing a 2.0 for this crate should not be a big problem, though.

CHANGELOG.md Outdated Show resolved Hide resolved
this API has been stabilised with Rust 1.81.0. accordingly the MSRV has
been raised.

in order to implement `cause` the underlying error from the HAL must
also implement `core::error::Error`. otherwise the trait bound will not
be satisfied. as the trait has only just been stabilisied it is not yet
implemented by any HAL (except for `embedded-hal-mock` since it's
`std`).

however, for GPIO operations most HALs will anyway just use
`core::convert::Infallible` which in turn already implements
`core::error::Error`, thus this should work out of the box.
by adding a single `build-results` job which depends on all other jobs
we can simplify the setting of required builds in the repository.
currently, all builds - including all variations of the build matrix! -
need to be manually specified.

once this has been merged the settings can be changed to require only
this one job (which will fail if any of the other jobs failed).

this way it's also easier to add/remove jobs or change the build matrix
as it no longer requires changing the settings on the repository.

this is inspired by [this discussion on GH][discussion].

[discussion]: https://github.com/orgs/community/discussions/26822
eldruin
eldruin previously approved these changes Sep 23, 2024
Copy link
Member

@eldruin eldruin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

@rursprung rursprung merged commit 6bb24b3 into rust-embedded-community:master Sep 23, 2024
6 checks passed
@rursprung rursprung deleted the prepare-1.0-release branch September 23, 2024 19:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

release v1.0.0
4 participants