pub fn parse_subject(subject: &str) -> Option<SubjectParts>Expand description
Parse a multi-part Usenet/email subject line.
Recognises five marker formats (in priority order):
- Parenthesised fraction:
(03/17)or( 3 / 17 ) - Bracketed fraction:
[2/4](only when both sides are digits) - English “Part N/M” (case-insensitive)
- English “Part N of M” /
part3of17(case-insensitive) - Dash-separated fraction:
- 03/17
Leading Re:, Fwd:, and Fw: prefixes are stripped before matching
(repeatedly, to handle nested re-forwards). The extracted part marker is
removed from the subject to produce base_subject.
§First-match-wins
The five patterns are tried in the priority order listed above. The first
pattern that matches wins; no attempt is made to find a “better” match
further along. If a subject line contains multiple markers
(e.g. "file (1/3) [2/4]"), only the first one matched — the
parenthesised fraction in that example — is used and the rest are left in
base_subject.
§Return value
Returns None only when:
subjectis empty, orsubjectcontains ayEncmarker (those posts use a distinct encoding that is explicitly out of scope for this crate), or- the entire input is consumed by the part-marker pattern, leaving no
base subject (e.g.
"(1/3)"with nothing else). The invariant thatbase_subjectis never empty must hold wheneverSomeis returned.
Otherwise returns Some(SubjectParts). When no part-marker pattern
matches, part_index and part_total are both None and base_subject
is the prefix-stripped, trimmed input.
§Zero totals
A subject like "file.bin (3/0)" produces part_total = Some(0). This is
nonsensical but is passed through verbatim since the crate cannot know
whether the source is malformed or intentional. Callers that pass
part_total directly to PartCollection::with_total
should validate that the total is non-zero before doing so.
§Never panics
This function never panics on any input, including strings containing arbitrary Unicode code points.
§Examples
use uuencoding_multi::parse_subject;
// Parenthesised fraction — the most common Usenet format.
let sp = parse_subject("bigfile.rar (2/5)").unwrap();
assert_eq!(sp.part_index, Some(2));
assert_eq!(sp.part_total, Some(5));
assert_eq!(sp.base_subject, "bigfile.rar");use uuencoding_multi::parse_subject;
// Re: prefix is stripped before matching.
let sp = parse_subject("Re: archive.tar.gz (03/17)").unwrap();
assert_eq!(sp.part_index, Some(3));
assert_eq!(sp.part_total, Some(17));use uuencoding_multi::parse_subject;
// yEnc subject → None (out of scope for this crate).
assert!(parse_subject("\"file.nfo\" yEnc (1/3)").is_none());use uuencoding_multi::parse_subject;
// Empty input → None.
assert!(parse_subject("").is_none());use uuencoding_multi::parse_subject;
// No marker → Some with None fields and subject preserved.
let sp = parse_subject("just a plain subject").unwrap();
assert_eq!(sp.part_index, None);
assert_eq!(sp.part_total, None);
assert_eq!(sp.base_subject, "just a plain subject");