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

Default and/or Insert, onboarding. #18

Open
cheako opened this issue Dec 18, 2022 · 3 comments
Open

Default and/or Insert, onboarding. #18

cheako opened this issue Dec 18, 2022 · 3 comments

Comments

@cheako
Copy link

cheako commented Dec 18, 2022

The first time an application is run, using a default value appears clunky. Catching the NotFound Error is natural enough to have specific helpers for that case. There is also an argument that this is not an error and get should return an Result<Option<_>... sounds interesting.

Notice the examples are bad(delete those b4 someone copies them), they just roll through any errors by treating any error as the NotFound condition.

Similar to: hwchen/keyring-rs#103

It would be nice if there were rust conventions surrounding these APIs, if you look at std they almost always returns an Option... noting the difference that IO errors are not a thing for HashMap.

    let h: PkvStore;

    let _limits: Option<Limits> =
        h.get("limits")
            .or_else(|x| -> Result<_, Box<dyn std::error::Error>> {
                if matches!(x, bevy_pkv::GetError::NotFound) {
                    h.set("limits", &Option::<Limits>::None)?;
                    Ok(None) /// Indicating the user has not seen the wizard for this.
                } else {
                    Err(x)?
                }
            })?;

    let _old_boot_id: Option<u64> = h.get("boot_id").map(Option::Some).or_else(
        |x| -> Result<_, Box<dyn std::error::Error>> {
            if matches!(x, bevy_pkv::GetError::NotFound) {
                Ok(None) /// Value set in next block.
            } else {
                Err(x)?
            }
        },
    )?;

    let boot_id: u64 = rand::thread_rng().gen();
    h.set("boot_id", &boot_id)?;

I think there are better ways to write this, suggestions welcome.

@landhb
Copy link

landhb commented Dec 28, 2022

@cheako Similar to what's discussed here: hwchen/keyring-rs#103

You can create a helper function:

/// Helper to obtain the value, returning None when not
/// yet set.
pub fn get_optional<T: DeserializeOwned>(
    store: &PkvStore,
    value: &str,
) -> Result<Option<T>, Box<dyn Error>> {
    match store.get(value) {
        Ok(s) => Ok(Some(s)),
        Err(bevy_pkv::GetError::NotFound) => Ok(None),
        Err(e) => Err(e),
    }
}

let h: PkvStore;
let _limits: Option<Limits> = get_optional(&h, "limits")?;
let _old_boot_id: Option<u64> = get_optional(&h, "boot_id")?;

@cheako
Copy link
Author

cheako commented Dec 28, 2022

Looks good, what if that was a pub function in the crate? That would prevent almost every client from having something similar.

@johanhelsing
Copy link
Owner

I'd like to try to keep simple usage as ergonomic as possible, but that what's simple may depend on your intention/expectation when using the store. Sometimes you really do expect the thing you query for to be there, so having a Result<Option<T>> then is a bit annoying.

Adding a such get_optional method in addition would be fine, though.

For reference, in bevy APIs, the approach to this is having some methods often come in two flavors, for instance:

  • World::entity - panics if there is no matching entity
  • World::get_entity - returns None if there is no entity
  • Query::single - panics if there is not exactly one matching entity
  • Query::get_single - returns an Err if there is not exactly one matching entity

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

No branches or pull requests

3 participants