vcard: keep the reading a card was split from
Import::Vcf.cards parsed the whole staged file and then made each card
from the joined bytes, so the envelope check and every later read
parsed each card again; a file with photos is megabytes, read on every
screen of the walk. VCard.of keeps the lines it was given, and
#extract and Vcf.read hand theirs on.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GNVLyiMrZJ7sZctEYq6WCM
diff --git a/lib/pro_tacts/import/vcf.rb b/lib/pro_tacts/import/vcf.rb
index 957efd5..02d73e6 100644
--- a/lib/pro_tacts/import/vcf.rb
+++ b/lib/pro_tacts/import/vcf.rb
@@ -87,7 +87,7 @@ module ProTacts
open << line
next unless name&.casecmp?("END")
- cards << VCard.new(open.map(&:verbatim).join)
+ cards << VCard.of(open)
open = nil
end
end
@@ -176,7 +176,7 @@ module ProTacts
property = line.property
property.nil? || (known?(property.name) && !orphaned.include?(property.group))
}
- Reading.new(card: VCard.new(kept.map(&:verbatim).join), dropped:)
+ Reading.new(card: VCard.of(kept), dropped:)
end
# The group prefixes (`item1.`) with nothing left in them but a
diff --git a/lib/pro_tacts/vcard.rb b/lib/pro_tacts/vcard.rb
index b74691c..d8ef090 100644
--- a/lib/pro_tacts/vcard.rb
+++ b/lib/pro_tacts/vcard.rb
@@ -104,11 +104,21 @@ module ProTacts
# anything here sees them (ProTacts::Web's write_card). This is the
# assertion under those callers, not a check any of them makes; the
# store's bind holds the same line a third time below the card.
- #: (String bytes) -> void
- def initialize(bytes)
+ #: (String bytes, ?lines: Array[Parser::Line]?) -> void
+ def initialize(bytes, lines: nil)
raise ArgumentError, "not valid UTF-8: a card is text" unless bytes.valid_encoding?
@bytes = bytes
+ @lines = lines if lines
+ end
+
+ # A card made of lines already read, keeping that reading: a file
+ # split into cards (Import::Vcf.cards) or a card split by #extract
+ # has walked every one of them, and the bytes they join back into
+ # read as the same lines, so a second walk would find nothing new.
+ #: (Array[Parser::Line] lines) -> VCard
+ def self.of(lines)
+ new(lines.map(&:verbatim).join, lines:)
end
# The card's properties, folded off the same single walk #lines is
@@ -164,7 +174,7 @@ module ProTacts
#: (String name) -> [Array[Parser::Line], VCard]
def extract(name)
taken, rest = lines.partition { it.names?(name) }
- [taken, VCard.new(rest.map(&:verbatim).join)]
+ [taken, VCard.of(rest)]
end
#: () -> String