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

Should the API for creating the date components be updated? #93

Open
nekevss opened this issue Aug 11, 2024 · 5 comments
Open

Should the API for creating the date components be updated? #93

nekevss opened this issue Aug 11, 2024 · 5 comments
Labels
question Further information is requested

Comments

@nekevss
Copy link
Member

nekevss commented Aug 11, 2024

Currently, to create one of the structs, our general API is new with an overflow option. It may be better to have a more "rusty" API; i.e., we lack a try_new method.

General Background:

The Temporal specification provides the option to Constrain or Reject the creation of an object with RegulateX abstract methods. To capture this, the current new methods all have an option parameter that allows choosing the overflow option.

While the current new method provides those options, it also means the API doesn't follow typical conventions that may be expected by any Rust users and may also be a little vague.

Initial Proposal:

Essentially, my thinking is that we should add a try_new method and update the API to be a bit more explicit.

  • new(fields..., overflow_option) => new_with_overflow_option(fields, overflow_option)
  • Add try_new(fields ...) that calls new_with_overflow_option(fields..., REJECT)
  • Add new(fields...) that calls new_with_overflow_option(fields..., CONSTRAIN)

That's sort of along the lines of what I was thinking, but I thought I'd bring it up for discussion. I'm not exactly in love with the idea of new being constrain by default, but I think it'd be fine as long as it's documented.

Any thoughts?

@nekevss nekevss added the question Further information is requested label Aug 11, 2024
@nekevss nekevss changed the title Discussion on updating the API for creating the date components Should the API for creating the date components be updated? Aug 11, 2024
@jasonwilliams
Copy link
Member

Are you referring to this?

temporal/src/iso.rs

Lines 308 to 314 in a7fc946

ArithmeticOverflow::Reject => {
if !is_valid_date(year, month, day) {
return Err(TemporalError::range().with_message("not a valid ISO date."));
}
// NOTE: Values have been verified to be in a u8 range.
Self::new_unchecked(year, month as u8, day as u8)
}

Yeah I guess this could work, I don't have any issue with the idea, I'm not sure it's immediately obvious though so it would need some documentation around it. new_with_overflow might be enough (to keep the name shorter). If I just saw try_new and new on their own I probably wouldn't figure out the try_new is "attempt with constrain and fail if it doesn't fit".

That's sort of along the lines of what I was thinking, but I thought I'd bring it up for discussion. I'm not exactly in love with the idea of new being constrain by default, but I think it'd be fine as long as it's documented.

Yep same, it feels odd, with some documentation we may be OK. I actually don't mind how it is right now, its explicit

@nekevss
Copy link
Member Author

nekevss commented Aug 12, 2024

Are you referring to this?

More so around the specific components like Date, Time, and DateTime, so each of these would end up with something akin to the below.

impl Date {
    // Creates a `Date`, constraining to the nearest valid Date if the values are not in a valid range.
    fn new(
        year: i32,
        month: i32,
        day: i32,
        calendar: Calendar,
    ) -> Self {
         Date::new_with_option(feilds, ArithmeticOverflow::Constrain)
             .expect("Constraining cannot return an error.")
    }

    /// Tries to create a new Date, throwing an error if the values are not in a valid Date range.
    fn try_new(
        year: i32,
        month: i32,
        day: i32,
        calendar: Calendar,
    ) -> TemporalResult<Self> {
         Date::new_with_option(feilds, ArithmeticOverflow::Reject)
    }

    /// Create a `Date` with a provided arithmetic overflow option.
    fn new_with_option(
        year: i32,
        month: i32,
        day: i32,
        calendar: Calendar,
        option: ArithmeticOverflow,
    ) -> TemporalResult<Self> {
         todo!()
    }
}

If I just saw try_new and new on their own I probably wouldn't figure out the try_new is "attempt with constrain and fail if it doesn't fit".

try_new wouldn't attempt to constrain first beforehand. It would be basically calling new_with_option with the option automatically set to reject, so it would reject instead of constraining.

@jasonwilliams
Copy link
Member

Sorry yes that’s what I meant (that try_new just calls it with reject).

Would these be convenience methods with new_with_option being public also? Or does the user only have access to try/try_new?

@nekevss
Copy link
Member Author

nekevss commented Aug 13, 2024

I think new_with_option would still be public, and the other methods would mostly be convenience for the native Rust API.

We could also feature flag them with a feature as default that then allows us to remove them when implementing the engine as they'd probably not be needed.

@jasonwilliams
Copy link
Member

I would be fine with this change, if its documented well enough

nekevss added a commit that referenced this issue Oct 22, 2024
In general, this completes some various API housekeeping that was
needed. Updates the built-ins to align with specification names, changes
the exports, implements some of the general API changes to be more
Rust-like.

Below are a full list of changes:

- Updates all component names to align with the Temporal spec (Adds the
`Plain` prefix where needed)
  - Initial implementation of the API suggestions from #93
- Exports each component in the libraries root and adds the partial
records to a `partial` mod.

Ideally, now working with the library should look something like the
below.

```rust
// previously would have been: 
// use temporal_rs::{components::{calendar::Calendar, Date, PartialDate}, options::ArithmeticOverflow};
use temporal_rs::{Date, Calendar, partial::PartialDate, options::ArithmeticOverflow};

let iso_calendar = Calendar::from_str("iso8601")?;
// Previously would have been Date::new(2020, 10, 19, iso_calendar.clone(), ArithmeticOverflow::Reject)?;
let constructed_date = Date::try_new(2020, 10, 19, iso_calendar.clone())?;
let today = PartialDate {
    year: Some(2020),
    month: Some(10),
    day: Some(19),
    ..Default::default()
};
let date_from_partial = iso_calendar.date_from_partial(&today, ArithmeticOverflow::Reject)?;

assert_eq!(constructed_date, from_partial);
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants