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

introduce generate_overflowing and increment_overflowing methods #75

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions src/generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,35 @@ impl Generator {
self.generate_from_datetime(crate::time_utils::now())
}

/// Generate a new Ulid. Each call is guaranteed to provide a Ulid with a larger value than the
/// last call. The time part of the returned [`Ulid`] can be up to one millisecond in the future,
/// under reasonable assumptions about processor speeds.
///
/// # Panics
///
/// This function panics if the previously returned value was already the highest possible ulid.
/// This should not happen before year 91000 of the gregorian calendar at least.
///
/// ```rust
/// use ulid::Generator;
/// let mut gen = Generator::new();
///
/// let ulid1 = gen.generate_overflowing();
/// let ulid2 = gen.generate_overflowing();
///
/// assert!(ulid1 < ulid2);
/// ```
pub fn generate_overflowing(&mut self) -> Ulid {
Copy link
Owner

@dylanhart dylanhart Feb 3, 2024

Choose a reason for hiding this comment

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

If a single version of generate_overflowing is going to be added I'd prefer generate_from_datetime_with_source_overflowing. This would give the user the most choice.

However, looking at this code again, it may make sense to make the overflow value part of MonotonicError. If you're up for that change it should provide the best overall API here.

Copy link
Owner

Choose a reason for hiding this comment

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

Nevermind this doesn't work since it wouldn't store into previous.

Copy link
Author

Choose a reason for hiding this comment

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

Considering the reason to be of generate_overflowing is for people who just want to say "I don't care", I think only having generate_from_datetime_with_source_overflowing would be counter-productive: after all, it's reasonably easy to replace it:

gen.generate_from_datetime_with_source_overflowing(time, source);
// is equivalent to
gen.generate_from_datetime_with_source(time, source)
    .unwrap_or_else(|| gen.generate_from_datetime_with_source(time + Duration::from_millis(1), source).unwrap());

So IMO this is the place where there's literally the least to gain, compared to generate_overflowing that does gain a lot compared to trying to reimplement it manually. Now, if you'd rather have all the variants, I'm all good with adding them; I was just trying to avoid combinatorial explosion of the API :)

I'll try to handle your other review comments soon! In the meantime please let me know your thoughts about the API you'd prefer so I can adjust this PR accordingly :)

Copy link
Author

Choose a reason for hiding this comment

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

@dylanhart WDYT about this question?

let next = Ulid::new();
if next > self.previous {
Copy link
Owner

Choose a reason for hiding this comment

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

This behavior differs from Generator::generate(). In the same-millisecond case generate will always increment while this will first try a random value. I'd like to preserve the existing behavior because it matches the reference implementation.

Copy link
Author

Choose a reason for hiding this comment

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

I'm going to handle this one once #75 (comment) is decided, as it'll impact the things to do :)

self.previous = next;
next
} else {
self.previous = self.previous.increment_overflowing();
self.previous
}
}

/// Generate a new Ulid matching the given DateTime.
/// Each call is guaranteed to provide a Ulid with a larger value than the last call.
/// If the random bits would overflow, this method will return an error.
Expand Down
31 changes: 31 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,37 @@ impl Ulid {
}
}

/// Increment the random number, increasing the timestamp if the random number overflows
///
/// # Panics
Ekleog marked this conversation as resolved.
Show resolved Hide resolved
///
/// This function panics if the previous value was already the highest possible ulid.
/// This should not happen before year 91000 of the gregorian calendar at least.
///
/// # Example
///
/// ```rust
/// use ulid::Ulid;
///
/// let ulid_0 = Ulid::from_parts(0, (1 << Ulid::TIME_BITS) - 1);
Ekleog marked this conversation as resolved.
Show resolved Hide resolved
/// assert_eq!(
/// ulid_0.to_string(),
/// "00000000000000007ZZZZZZZZZ"
/// );
///
/// let ulid_1 = ulid_0.increment_overflowing();
/// assert_eq!(
/// ulid_1.to_string(),
/// "00000000000000008000000000"
/// );
/// ```
pub const fn increment_overflowing(&self) -> Ulid {
Ulid(match self.0.checked_add(1) {
Some(val) => val,
None => panic!("ULID overflow"),
})
}

/// Creates a Ulid using the provided bytes array.
///
/// # Example
Expand Down
Loading