bradfordboyle / fitbit-grabber-rs

Rust utility for exporting data from Fitbit API
2 stars 3 forks source link

WIP: Demonstrate deserializing String -> NaiveDate #6

Closed anxiousmodernman closed 6 years ago

anxiousmodernman commented 6 years ago

The weight series json object returned by fitbit contains two strings, for example

{ "dateTime": "2018-01-01", "value": "70" }

Computation in Rust itself isn't possible unless we convert these to a NaiveDate and a float, respectively.

Here is an example of using a custom module "shim" to deserialize an Option<NaiveDate> from a string. A float deserialization would look similar. An Option seems more polite than either 1) panicking internally or 2) returning some weird "0-value" for the date. I've included the code under a potential library module serializers. I imagine we could nest a few mods in that file.

What do you think?

bradfordboyle commented 6 years ago

Looking over this, I have two thoughts/questions:

  1. Do we want to expose chrono in the API for this? If we do, then users are forced to pull in another dependency to work with the library. Hiding behind our own type gives a degree of freedom. On the other hand, chrono is the de-facto standard library for date/times---why take on the burden of wrapping it?

  2. If one of the dates fails to parse, should we trust the data at all? Suppose we have let x = Vec<A> and f: A -> Option<B>. Doing x.map(f) results in Vec<Option<B>> but a lot of the time, I find myself wanting Option<Vec<B>>

Overall, I like this change. Having structured types opens up a wider range of integrations (I think)

anxiousmodernman commented 6 years ago

If another time library became very popular, it would be possible to feature-gate the definition of a struct. So we'd have another escape hatch besides our own type, in theory.

Regarding no. 2, you raise an good point. There is the filter_map combinator in the standard library that only yield the Some(x) case. Although in practice, you'd get a "shorter" iterator than you might expect.

But if, for some reason, Fitbit was consistently returning an unparsable date (or other type, because we'll need to parse the float here, too), I think it would be better for the consumer of the api to deal with a None (or maybe an Err?) in a Vec, than to fail the whole operation. If Fitibit had this bad data hiding in a user's account, the user would have to construct range queries to "dance around" the bad data, e.g. if C is bad here

[ A B C(bad) D E F]

I'd only figure it out by querying repeatedly if the whole vec came back as None.