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
change qkzrsnnqltkkymsyzssvlzyzqstsqlur
commit 166bf820f345af92c24b9ebc050bce4ab8fc48f9
author Alpha Chen <alpha@kejadlen.dev>
date
parent syxtkxus
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)