contact: an email's label skips internet and pref
INTERNET is the default, so it labels nothing new;
pref's a preference, not a place — the first real TYPE comes through.
Assisted-by: Claude Opus 5 via Claude Code
diff --git a/lib/pro_tacts/contact.rb b/lib/pro_tacts/contact.rb
index 30de471..b5065bb 100644
--- a/lib/pro_tacts/contact.rb
+++ b/lib/pro_tacts/contact.rb
@@ -285,7 +285,9 @@ module ProTacts
def emails
rows("EMAIL") { |property, line|
value = text_of(property)
- Email.new(value:, type: type_of(property), line:) if value
+ # RFC 2426 section 3.3.2: `internet` is the format every address
+ # has by default and `pref` a preference, so neither names one.
+ Email.new(value:, type: type_of(property, except: %w[internet pref]), line:) if value
}
end
@@ -392,11 +394,15 @@ module ProTacts
Address.new(po_box:, extended:, street:, locality:, region:, postal_code:, country:, type: type_of(property), line:)
end
- # RFC 2426 section 3.3.1. Downcased: the spelling is the card's,
- # and a screen shows one.
- #: (VCard::Parser::Property property) -> String?
- def type_of(property)
- property.parameter("TYPE")&.downcase
+ # RFC 2426 section 3.3.1: the first TYPE not in `except`, which
+ # holds the values a property uses for something other than a
+ # label. Downcased: the spelling is the card's, and a screen shows
+ # one.
+ #: (VCard::Parser::Property property, ?except: Array[String]) -> String?
+ def type_of(property, except: [])
+ property.parameters
+ .filter_map { |name, value| value.downcase if name.casecmp?("TYPE") }
+ .find { !except.include?(it) }
end
end
end
diff --git a/test/pro_tacts/test_contact.rb b/test/pro_tacts/test_contact.rb
index 0cccafd..5f1dd6b 100644
--- a/test/pro_tacts/test_contact.rb
+++ b/test/pro_tacts/test_contact.rb
@@ -147,6 +147,18 @@ class ContactTest < Minitest::Test
assert_equal "home", emails.fetch(0).type
end
+ def test_an_emails_type_skips_internet_and_pref
+ apple = STRUCTURED.sub("EMAIL;TYPE=home:", "EMAIL;type=INTERNET;type=pref;type=HOME:")
+
+ assert_equal "home", contact(apple).emails.fetch(0).type
+ end
+
+ def test_an_email_typed_only_internet_and_pref_has_no_type
+ apple = STRUCTURED.sub("EMAIL;TYPE=home:", "EMAIL;type=INTERNET;type=pref:")
+
+ assert_nil contact(apple).emails.fetch(0).type
+ end
+
def test_every_email_carries_the_line_it_was_read_from
email = contact(STRUCTURED).emails.fetch(0)