1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
require "pro_tacts/birthday"
require "pro_tacts/vcard/parser"
module ProTacts
# What one BDAY line in a submitted card is, for EditedContact to act on
# (docs/plans/2026-09-11-every-birthday-in-the-model.md). Match with
# case/when, not case/in: Steep does not check the bodies of case/in
# branches, and these branches are the ones worth checking.
#
# The variants are signed in sig/pro_tacts/birthday_line.rbs, for
# Birthday's reason.
module BirthdayLine
# A birthday the model takes: the line leaves the card.
# @rbs skip
class Modeled < Data.define(:birthday)
end
# A line that reads as a BDAY the model does not take. It stays in
# the card verbatim and is reported.
# @rbs skip
class Unrecognized < Data.define
end
# A line the parser could not read as a BDAY at all. It stays in the
# card, and Web#report_unreadable_lines has already reported it.
# @rbs skip
class Unreadable < Data.define
end
# The line's property is checked by name as well as read, because a
# line matched as BDAY by its bytes can still fail to parse into one.
#: (VCard::Parser::Line line) -> (Modeled | Unrecognized | Unreadable)
def self.read(line)
property = line.property
return Unreadable.new unless property&.name&.casecmp?("BDAY")
birthday = Birthday.from_property(property)
birthday ? Modeled.new(birthday:) : Unrecognized.new
end
end
end