Skip to content

Conversation

@soywod
Copy link
Contributor

@soywod soywod commented Jan 23, 2026

Closes #689.

@soywod soywod force-pushed the quirk-dedup-sort-seq-encoding branch from bb9e0f5 to d69ebe4 Compare January 23, 2026 15:34
@soywod
Copy link
Contributor Author

soywod commented Jan 23, 2026

Looks like the feature breaks things, I will need more time to adjust. I would like to know first if you agree with the proposition.

@duesee
Copy link
Owner

duesee commented Jan 23, 2026

This is going in the right direction! I think that our structured fuzzer will break due to this, but I propose we first don't care about that here. We should maybe add an issue to also account for this quirk during fuzzing but I can take care of that later.

Thanks to your unit tests, I think that we can make the best of this quirk by making it a nice generic feature. Curious what you think.

By the way, I'll be on vacation for a fee weeks. But I'll try to have a look here now and then.

@soywod soywod force-pushed the quirk-dedup-sort-seq-encoding branch from 4351bcb to 71f5ba0 Compare January 26, 2026 20:48
@soywod
Copy link
Contributor Author

soywod commented Jan 26, 2026

I pushed a new version in another commit so you can see the difference. I will squash before merging. I moved the logic into normalize functions inside imap-types (for Sequence and SequenceSet). The quirk remains in imap-codec and just call the normalize function before encoding. Few questions at the top of my head:

  • There is so many ways to declare such function, I opted for the pub fn normalize(&mut self) -> &mut Self on both Sequence and SequenceSet, but I don't have solid arguments about this choice.
  • With these new functions, should the normalization should remains at encoding level or at declaration?

@soywod
Copy link
Contributor Author

soywod commented Jan 26, 2026

I also added more cases to the normalization, for exemple range a:a to single a, single asterisk to range 1:asterisk (because not all IMAP server like the * only, 1:* seems to be the safest as most understood in general) etc. See tests.

match self {
Sequence::Single(SeqOrUid::Asterisk) => {
let begin = NonZeroU32::new(1).unwrap();
let range = Sequence::Range(begin.into(), SeqOrUid::Asterisk);
Copy link
Owner

@duesee duesee Jan 26, 2026

Choose a reason for hiding this comment

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

I think this is not right. * doesn't mean "all messages". * resolves to "the largest uid/seq" (see below). If we have, say 1337 messages, fetch * should only fetch the single message with seq 1337. This code would change it to 1:* which means 1:1337 and would fetch 1337 messages.

Reference:

seq-number = nz-number / "*"
                    ; message sequence number (COPY, FETCH, STORE
                    ; commands) or unique identifier (UID COPY,
                    ; UID FETCH, UID STORE commands).
                    ; * represents the largest number in use.  In
                    ; the case of message sequence numbers, it is
                    ; the number of messages in a non-empty mailbox.
                    ; In the case of unique identifiers, it is the
                    ; unique identifier of the last message in the
                    ; mailbox or, if the mailbox is empty, the
                    ; mailbox's current UIDNEXT value.
                    ; The server should respond with a tagged BAD
                    ; response to a command that uses a message
                    ; sequence number greater than the number of
                    ; messages in the selected mailbox.  This
                    ; includes "*" if the selected mailbox is empty.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's been so long I did not put my nose in the IMAP RFC that I mistook the meaning of *. I was convinced it had the same meaning as a range bound ..2 = *:2. Sorry for that. It makes things easier tho, let me fix that.

let tests = vec![
("*", "1:*"),
("1:*", "1:*"),
("*:1", "*:1"),
Copy link
Owner

@duesee duesee Jan 26, 2026

Choose a reason for hiding this comment

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

Hm... since we don't know what * would resolve to, it's unclear if it should be *:x or x:*. Havn't thought about this case to be honest...

#[test]
fn normalize_sequence_set() {
let tests = [
("1,2,3,4", "1,2,3,4"),
Copy link
Owner

@duesee duesee Jan 26, 2026

Choose a reason for hiding this comment

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

Arguably, this could be 1:4? But: I think currently it is not too important to have a precise (and "stable") normalization. When these changes help you already, let's get them in! But we could open an issue to improve the normalization. Maybe I'll come back to it at some point. Sounds like a fun algorithm to implement.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Definitely, and I have an idea on how implement it. Let me give it a shot in another commit.

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.

feat: add quirk to sort sequence sets

2 participants