File Coverage

lib/MM/Table.pm
Criterion Covered Total %
statement 267 294 90.8
branch 62 100 62.0
condition 28 66 42.4
subroutine 40 42 95.2
pod 14 14 100.0
total 411 516 79.6


line stmt bran cond sub pod time code
1             ##----------------------------------------------------------------------------
2             ## MIME Email Builder - ~/lib/MM/Table.pm
3             ## Version v0.5.0
4             ## Copyright(c) 2026 DEGUEST Pte. Ltd.
5             ## Author: Jacques Deguest <jack@deguest.jp>
6             ## Created 2026/03/03
7             ## Modified 2026/03/04
8             ## All rights reserved
9             ##
10             ##
11             ## This program is free software; you can redistribute it and/or modify it
12             ## under the same terms as Perl itself.
13             ##----------------------------------------------------------------------------
14             package MM::Table;
15             BEGIN
16             {
17 10     10   123028 use strict;
  10         53  
  10         374  
18 10     10   66 use warnings;
  10         19  
  10         497  
19 10     10   45 use vars qw( $VERSION );
  10         16  
  10         533  
20 10     10   67 use Scalar::Util qw( blessed );
  10         18  
  10         563  
21 10     10   979 use Mail::Make::Exception;
  10         19  
  10         87  
22             use overload (
23 10         74 '%{}' => '_as_hashref',
24             fallback => 1
25 10     10   5110 );
  10         22  
26 10     10   1114 our $VERSION = 'v0.5.0';
27             };
28              
29 10     10   61 use strict;
  10         31  
  10         240  
30 10     10   42 use warnings;
  10         15  
  10         8745  
31              
32             sub make
33             {
34 199     199 1 297860 my( $class ) = @_;
35              
36 199         2345 my $state =
37             {
38             _entries => [], # array of { key => original, lkey => lc, val => string }
39             _error => undef,
40             _tied_href => undef,
41             };
42              
43 199         836 my $self = bless( \$state, $class );
44 199         1128 return( $self );
45             }
46              
47             sub add
48             {
49 779     779 1 1870 my( $self, $key, $val ) = @_;
50              
51 779 50       1712 $self->_validate_key( $key ) || return( $self->pass_error );
52 779         1767 $val = $self->_stringify_value( $val );
53 779         1747 my $lkey = lc( $key );
54              
55 779         1506 my $st = $self->_state();
56 779         1040 push( @{$st->{_entries}}, { key => $key, lkey => $lkey, val => $val } );
  779         5187  
57              
58 779         2000 $self->_invalidate_tie_cache();
59 779         4318 return( $self );
60             }
61              
62             sub clear
63             {
64 5     5 1 1052 my( $self ) = @_;
65 5         13 my $st = $self->_state();
66 5         18 $st->{_entries} = [];
67 5         12 $self->_invalidate_tie_cache();
68 5         6 return( $self );
69             }
70              
71             sub compress
72             {
73 2     2 1 10 my( $self, $flags ) = @_;
74              
75 2 50       7 $self->_validate_overlap_flags( $flags, 'compress' ) || return( $self->pass_error );
76 2   50     5 $flags //= 0;
77              
78 2         4 my $st = $self->_state();
79 2   50     6 my $src = $st->{_entries} || [];
80 2         3 my @out;
81              
82             my %seen_idx;
83              
84 2         6 for( my $i = 0; $i < scalar( @$src ); $i++ )
85             {
86 8         7 my $e = $src->[ $i ];
87 8         9 my $lkey = $e->{lkey};
88              
89 8 100       13 if( !exists( $seen_idx{ $lkey } ) )
90             {
91             push( @out,
92             {
93             key => $e->{key},
94             lkey => $lkey,
95             val => $e->{val},
96 4         13 });
97 4         10 $seen_idx{ $lkey } = scalar( @out ) - 1;
98 4         8 next;
99             }
100              
101 4         3 my $idx = $seen_idx{ $lkey };
102              
103 4 100       7 if( $flags )
104             {
105 2         5 $out[ $idx ]->{val} = $out[$idx]->{val} . ', ' . $e->{val};
106             }
107             else
108             {
109 2         3 $out[ $idx ]->{key} = $e->{key};
110 2         4 $out[ $idx ]->{val} = $e->{val};
111             }
112             }
113              
114 2         5 $st->{_entries} = \@out;
115 2         4 $self->_invalidate_tie_cache();
116 2         9 return( $self );
117             }
118              
119             sub copy
120             {
121 6     6 1 3143 my( $self ) = @_;
122              
123 6   33     18 my $class = ref( $self ) || $self;
124              
125 6         20 my $state =
126             {
127             _entries => [],
128             _error => undef,
129             _tied_href => undef,
130             };
131              
132 6         12 my $new = bless( \$state, $class );
133              
134 6   50     12 my $src = $self->_state()->{_entries} || [];
135 6         10 my $dst = $new->_state()->{_entries};
136              
137 6         18 for( my $i = 0; $i < scalar( @$src ); $i++ )
138             {
139 16         20 my $e = $src->[ $i ];
140             push( @$dst,
141             {
142             key => $e->{key},
143             lkey => $e->{lkey},
144             val => $e->{val},
145 16         54 });
146             }
147              
148 6         13 return( $new );
149             }
150              
151             sub do
152             {
153 130     130 1 19950 my( $self, $sub, @filter ) = @_;
154              
155 130 50       360 return( $self->error( "MM::Table->do: missing callback." ) ) if( !defined( $sub ) );
156              
157 130         208 my $cb;
158 130 50       381 if( ref( $sub ) eq 'CODE' )
    0          
159             {
160 130         187 $cb = $sub;
161             }
162             elsif( !ref( $sub ) )
163             {
164 0         0 my $name = $sub;
165 0 0       0 if( $name !~ /::/ )
166             {
167 0         0 my $pkg = (caller())[0];
168 0         0 $name = $pkg . '::' . $name;
169             }
170              
171 10     10   74 no strict 'refs';
  10         19  
  10         22930  
172 0         0 my $code = *{$name}{CODE};
  0         0  
173 0 0       0 return( $self->error( "MM::Table->do: could not resolve callback '$sub'." ) ) if( !$code );
174 0         0 $cb = $code;
175             }
176             else
177             {
178 0         0 return( $self->error( "MM::Table->do: callback must be a CODE reference or a sub name." ) );
179             }
180              
181 130         198 my %filter;
182 130 100       283 if( scalar( @filter ) )
183             {
184 1         4 for( my $i = 0; $i < scalar( @filter ); $i++ )
185             {
186 2         3 my $k = $filter[ $i ];
187 2 50       4 next if( !defined( $k ) );
188 2         8 $filter{ lc( "$k" ) } = 1;
189             }
190             }
191              
192 130   50     340 my $src = $self->_state()->{_entries} || [];
193              
194 130         469 for( my $i = 0; $i < scalar( @$src ); $i++ )
195             {
196 636         905 my $e = $src->[ $i ];
197              
198 636 100       1130 if( scalar( %filter ) )
199             {
200 4 100       9 next if( !$filter{ $e->{lkey} } );
201             }
202              
203 635         1591 my $ok = $cb->( $e->{key}, $e->{val} );
204 635 100       1818 last if( !$ok );
205             }
206              
207 130         389 return( $self );
208             }
209              
210             # error( [$message] )
211             # With an argument : creates a Mail::Make::Exception, stores it in the
212             # instance, and returns undef (or empty list).
213             # Without argument : returns the stored exception object, or undef.
214             sub error
215             {
216 0     0 1 0 my $self = shift( @_ );
217 0         0 my $st = $self->_state();
218 0 0       0 if( @_ )
219             {
220 0         0 my $msg = join( '', @_ );
221 0         0 my $o = Mail::Make::Exception->new( $msg );
222 0         0 $st->{_error} = $o;
223 0 0       0 warn( $o->as_string ) if( warnings::enabled( scalar( caller ) ) );
224 0         0 return;
225             }
226 0         0 return( $st->{_error} );
227             }
228              
229             sub get
230             {
231 743     743 1 2473 my( $self, $key ) = @_;
232              
233 743 50       1656 $self->_validate_key( $key ) || return( $self->pass_error );
234 743         1456 my $lkey = lc( $key );
235              
236 743   50     1430 my $src = $self->_state()->{_entries} || [];
237              
238 743 100       1709 if( wantarray )
239             {
240 679         888 my @vals;
241 679         1640 for( my $i = 0; $i < scalar( @$src ); $i++ )
242             {
243 2811         3974 my $e = $src->[ $i ];
244 2811 100       7884 next if( $e->{lkey} ne $lkey );
245 233         738 push( @vals, $e->{val} );
246             }
247 679         2147 return( @vals );
248             }
249              
250 64         415 for( my $i = 0; $i < scalar( @$src ); $i++ )
251             {
252 231         316 my $e = $src->[ $i ];
253 231 100       716 next if( $e->{lkey} ne $lkey );
254 14         64 return( $e->{val} );
255             }
256              
257 50         154 return;
258             }
259              
260             sub merge
261             {
262 5     5 1 18 my( $self, $key, $val ) = @_;
263              
264 5 50       10 $self->_validate_key( $key ) || return( $self->pass_error );
265 5         9 $val = $self->_stringify_value( $val );
266 5         9 my $lkey = lc( $key );
267              
268 5         5 my $st = $self->_state();
269 5   50     12 my $src = $st->{_entries} || [];
270              
271 5         12 for( my $i = 0; $i < scalar( @$src ); $i++ )
272             {
273 5         9 my $e = $src->[ $i ];
274 5 100       14 next if( $e->{lkey} ne $lkey );
275              
276 3         7 $e->{val} = $e->{val} . ', ' . $val;
277 3         7 $self->_invalidate_tie_cache();
278 3         8 return( $self );
279             }
280              
281 2         9 push( @$src, { key => $key, lkey => $lkey, val => $val } );
282 2         5 $self->_invalidate_tie_cache();
283 2         4 return( $self );
284             }
285              
286             sub overlap
287             {
288 2     2 1 11 my( $self, $other, $flags ) = @_;
289              
290 2 50       7 $self->_validate_table( $other ) || return( $self->pass_error );
291 2 50       7 $self->_validate_overlap_flags( $flags ) || return( $self->pass_error );
292 2   50     5 $flags //= 0;
293              
294 2   50     4 my $o = $other->_state()->{_entries} || [];
295 2         7 for( my $i = 0; $i < scalar( @$o ); $i++ )
296             {
297 3         5 my $e = $o->[ $i ];
298 3 100       7 if( $flags )
299             {
300 2 50       6 $self->merge( $e->{key}, $e->{val} ) || return( $self->pass_error );
301             }
302             else
303             {
304 1 50       5 $self->set( $e->{key}, $e->{val} ) || return( $self->pass_error );
305             }
306             }
307 2         5 return( $self );
308             }
309              
310             sub overlay
311             {
312 1     1 1 6 my( $self, $other ) = @_;
313              
314 1 50       5 $self->_validate_table( $other ) || return( $self->pass_error );
315              
316 1   33     4 my $class = ref( $self ) || $self;
317              
318 1         4 my $state =
319             {
320             _entries => [],
321             _error => undef,
322             _tied_href => undef,
323             };
324              
325 1         3 my $new = bless( \$state, $class );
326              
327 1         3 my $dst = $new->_state()->{_entries};
328              
329 1   50     2 my $o = $other->_state()->{_entries} || [];
330 1         4 for( my $i = 0; $i < scalar( @$o ); $i++ )
331             {
332 1         1 my $e = $o->[ $i ];
333 1         5 push( @$dst, { key => $e->{key}, lkey => $e->{lkey}, val => $e->{val} } );
334             }
335              
336 1   50     2 my $s = $self->_state()->{_entries} || [];
337 1         4 for( my $i = 0; $i < scalar( @$s ); $i++ )
338             {
339 3         3 my $e = $s->[$i];
340 3         8 push( @$dst, { key => $e->{key}, lkey => $e->{lkey}, val => $e->{val} } );
341             }
342              
343 1         3 return( $new );
344             }
345              
346             # pass_error()
347             # Propagates the error stored in this instance: sets it as the current
348             # error and returns undef (or empty list), exactly as error() does, but
349             # without creating a new exception object.
350             sub pass_error
351             {
352 0     0 1 0 my $self = shift( @_ );
353             # If called with arguments, delegate to error() to create a new exception.
354 0 0       0 return( $self->error( @_ ) ) if( @_ );
355             # No arguments: the error is already stored in the instance by the method
356             # that failed. We simply return, letting Perl resolve the context: undef
357             # in scalar context, empty list in list context.
358 0         0 return;
359             }
360              
361             sub set
362             {
363 21     21 1 2672 my( $self, $key, $val ) = @_;
364              
365 21 50       44 $self->_validate_key( $key ) || return( $self->pass_error );
366 21         47 $val = $self->_stringify_value( $val );
367 21         31 my $lkey = lc( $key );
368              
369 21         42 my $st = $self->_state();
370 21   50     44 my $src = $st->{_entries} || [];
371 21         23 my @kept;
372              
373 21         88 for( my $i = 0; $i < scalar( @$src ); $i++ )
374             {
375 11         18 my $e = $src->[ $i ];
376 11 100       27 next if( $e->{lkey} eq $lkey );
377 9         20 push( @kept, $e );
378             }
379              
380 21         110 push( @kept, { key => $key, lkey => $lkey, val => $val } );
381              
382 21         44 $st->{_entries} = \@kept;
383 21         50 $self->_invalidate_tie_cache();
384 21         43 return( $self );
385             }
386              
387             sub unset
388             {
389 443     443 1 1676 my( $self, $key ) = @_;
390              
391 443 50       1670 $self->_validate_key( $key ) || return( $self->pass_error );
392 443         1149 my $lkey = lc( $key );
393              
394 443         1224 my $st = $self->_state();
395 443   50     1432 my $src = $st->{_entries} || [];
396 443         715 my @kept;
397              
398 443         1551 for( my $i = 0; $i < scalar( @$src ); $i++ )
399             {
400 567         873 my $e = $src->[ $i ];
401 567 100       1521 next if( $e->{lkey} eq $lkey );
402 545         1531 push( @kept, $e );
403             }
404              
405 443         923 $st->{_entries} = \@kept;
406 443         1390 $self->_invalidate_tie_cache();
407 443         1749 return( $self );
408             }
409              
410             # NOTE: tied-hash like APR::Table
411             sub _as_hashref
412             {
413 16     16   5303 my( $self ) = @_;
414              
415 16         24 my $st = $self->_state();
416              
417 16 100       34 if( defined( $st->{_tied_href} ) )
418             {
419 12         62 return( $st->{_tied_href} );
420             }
421              
422 4         4 my %h;
423 4         21 tie( %h, 'MM::Table::Tie', $self );
424              
425 4         8 $st->{_tied_href} = \%h;
426 4         18 return( $st->{_tied_href} );
427             }
428              
429             sub _invalidate_tie_cache
430             {
431 1255     1255   2054 my( $self ) = @_;
432              
433 1255         2046 my $st = $self->_state();
434             # Drop our own reference to the tied hash. If the caller holds a copy of
435             # the \%{} deref, Perl will keep the tie alive until that reference is
436             # released - untie() would warn in that case, so we leave it to Perl's
437             # normal reference counting and simply clear our cached pointer so that
438             # the next %{} deref creates a fresh, correctly-initialised tie.
439 1255         2454 $st->{_tied_href} = undef;
440              
441 1255         1969 return;
442             }
443              
444             sub _state
445             {
446 3431     3431   4796 my( $self ) = @_;
447 3431         3793 return( ${$self} );
  3431         8862  
448             }
449              
450             sub _stringify_value
451             {
452 805     805   1425 my( $self, $val ) = @_;
453 805 50       2586 return( defined( $val ) ? "$val" : '' );
454             }
455              
456             sub _validate_key
457             {
458 1991     1991   3359 my( $self, $key ) = @_;
459 1991 50 33     7756 if( !defined( $key ) || ref( $key ) )
460             {
461 0         0 return( $self->error( "MM::Table: key must be a defined non-reference scalar." ) );
462             }
463 1991         4579 return(1);
464             }
465              
466             sub _validate_overlap_flags
467             {
468 4     4   8 my( $self, $flags, $method ) = @_;
469 4   100     10 $method ||= 'method';
470 4 50       10 return(1) if( !defined( $flags ) );
471              
472 4 50       24 if( $flags !~ /^\d+\z/ )
473             {
474 0         0 return( $self->error( "MM::Table->$method: invalid flags '$flags' (expected 0 or 1)." ) );
475             }
476              
477 4         8 $flags = int( $flags );
478 4 50 66     37 if( $flags != 0 && $flags != 1 )
479             {
480 0         0 return( $self->error( "MM::Table->$method: invalid flags '$flags' (expected 0 or 1)." ) );
481             }
482              
483 4         6 return(1);
484             }
485              
486             sub _validate_table
487             {
488 3     3   6 my( $self, $other ) = @_;
489 3 50 33     22 if( !blessed( $other ) || !$other->isa( 'MM::Table' ) )
490             {
491 0   0     0 return( $self->error( "MM::Table: expected an MM::Table object, got '" . ( ref( $other ) || 'undef' ) . "'." ) );
492             }
493 3         7 return(1);
494             }
495              
496             1;
497             # NOTE: package MM::Table::Tie
498             package MM::Table::Tie;
499              
500 10     10   81 use strict;
  10         17  
  10         375  
501 10     10   44 use warnings;
  10         19  
  10         8593  
502              
503             sub TIEHASH
504             {
505 4     4   6 my( $class, $table ) = @_;
506             # This is a programming error, not a runtime one - die is appropriate here.
507 4 50 33     18 unless( defined( $table ) && ref( $table ) eq 'MM::Table' )
508             {
509 0   0     0 die( "MM::Table::Tie: expected an MM::Table instance, got '" . ( ref( $table ) || 'undef' ) . "'.\n" );
510             }
511              
512 4         16 return( bless(
513             {
514             _table => $table,
515             _iter => 0,
516             _curr => undef,
517             }, $class ) );
518             }
519              
520             sub CLEAR
521             {
522 1     1   3 my( $self ) = @_;
523 1         6 $self->{_table}->clear();
524 1         1 return;
525             }
526              
527             sub DELETE
528             {
529 1     1   3 my( $self, $key ) = @_;
530 1         8 $self->{_table}->unset( $key );
531 1         2 return;
532             }
533              
534             sub DESTROY
535             {
536 4     4   17 my( $self ) = @_;
537 4         17 return;
538             }
539              
540             sub EXISTS
541             {
542 3     3   7 my( $self, $key ) = @_;
543              
544 3 50 33     10 return(0) unless( defined( $key ) && !ref( $key ) );
545 3         5 my $lkey = lc( $key );
546              
547 3   50     6 my $src = $self->{_table}->_state()->{_entries} || [];
548 3         9 for( my $i = 0; $i < scalar( @$src ); $i++ )
549             {
550 4         3 my $e = $src->[$i];
551 4 100       19 return(1) if( $e->{lkey} eq $lkey );
552             }
553              
554 1         5 return(0);
555             }
556              
557             sub FETCH
558             {
559 8     8   78 my( $self, $key ) = @_;
560              
561 8 50 33     32 return( undef ) unless( defined( $key ) && !ref( $key ) );
562 8         15 my $lkey = lc( $key );
563              
564 8         9 my $t = $self->{_table};
565 8   50     13 my $src = $t->_state()->{_entries} || [];
566              
567             # Fast path: if the iterator is currently positioned on this key, we can
568             # return its value without a full scan. This is safe because _curr is only
569             # set by NEXTKEY, which always runs against the live _entries array, and
570             # FIRSTKEY resets it to undef before any iteration begins.
571 8 100       20 if( defined( $self->{_curr} ) )
572             {
573 6         8 my $idx = $self->{_curr};
574 6 50 33     23 if( $idx >= 0 && $idx < scalar( @$src ) )
575             {
576 6         7 my $e = $src->[$idx];
577 6 100       30 return( $e->{val} ) if( $e->{lkey} eq $lkey );
578             }
579             }
580              
581 3         11 for( my $i = 0; $i < scalar( @$src ); $i++ )
582             {
583 3         4 my $e = $src->[$i];
584 3 50       7 next if( $e->{lkey} ne $lkey );
585 3         12 return( $e->{val} );
586             }
587              
588 0         0 return( undef );
589             }
590              
591             sub FIRSTKEY
592             {
593 1     1   3 my( $self ) = @_;
594 1         3 $self->{_iter} = 0;
595 1         2 $self->{_curr} = undef;
596 1         4 return( $self->NEXTKEY( undef ) );
597             }
598              
599             sub NEXTKEY
600             {
601 4     4   7 my( $self, $lastkey ) = @_;
602              
603 4   50     7 my $src = $self->{_table}->_state()->{_entries} || [];
604              
605 4 100       10 if( $self->{_iter} >= scalar( @$src ) )
606             {
607 1         3 $self->{_curr} = undef;
608 1         2 return( undef );
609             }
610              
611 3         3 my $idx = $self->{_iter};
612 3         4 $self->{_iter}++;
613 3         3 $self->{_curr} = $idx;
614              
615 3         60 return( $src->[$idx]->{key} );
616             }
617              
618             sub STORE
619             {
620 2     2   5 my( $self, $key, $val ) = @_;
621 2 50 33     11 return unless( defined( $key ) && !ref( $key ) );
622 2         14 $self->{_table}->set( $key, $val );
623 2         4 return;
624             }
625              
626             1;
627             # NOTE: POD
628             __END__
629              
630             =encoding utf8
631              
632             =head1 NAME
633              
634             MM::Table - Pure-Perl mimic of APR::Table (multi-valued, case-insensitive table)
635              
636             =head1 SYNOPSIS
637              
638             use MM::Table ();
639             use MM::Const qw( :table );
640              
641             my $t = MM::Table->make;
642              
643             $t->set( Foo => "one" ) || die( $t->error );
644             $t->add( foo => "two" ) || die( $t->error );
645              
646             my $v = $t->get( 'FOO' ); # "one" (scalar ctx: oldest)
647             my @vs = $t->get( 'foo' ); # ("one","two")
648              
649             $t->merge( foo => "three" ); # first "foo" becomes "one, three"
650              
651             my $copy = $t->copy;
652              
653             my $o = $t->overlay( $copy );
654              
655             $t->compress( OVERLAP_TABLES_SET ); # flattens to last value per key
656             $t->compress( OVERLAP_TABLES_MERGE ); # flattens to "a, b, c"
657              
658             $t->do( sub{ print "$_[0] => $_[1]\n"; 1 } ) || die( $t->error );
659              
660             # APR-like deref:
661             $t->{foo} = "bar"; # calls set()
662             print $t->{foo}; # calls get()
663             print "yes\n" if( exists( $t->{foo} ) );
664              
665             while( my( $k, $v ) = each( %$t ) )
666             {
667             print "$k => $v\n"; # duplicates preserved in insertion order
668             }
669              
670             =head1 VERSION
671              
672             v0.5.0
673              
674             =head1 DESCRIPTION
675              
676             A pure-Perl, ordered, multi-valued, case-insensitive key-value table, modelled on L<APR::Table>. Used internally by L<Mail::Make::Headers> to store mail header fields in insertion order while allowing case-insensitive lookup and multiple values per field name.
677              
678             =head1 ERROR HANDLING
679              
680             C<MM::Table> does not inherit from L<Module::Generic>, but follows the same error convention used throughout the C<Mail::Make> ecosystem:
681              
682             =over 4
683              
684             =item * On error, a method stores a L<Mail::Make::Exception> object via L</error> and returns C<undef> in scalar context or an empty list in list context.
685              
686             =item * The caller retrieves the exception with C<< $t->error >>.
687              
688             =item * L</pass_error> is provided for propagating an error set earlier in the same object.
689              
690             =back
691              
692             Because C<MM::Table> is never instantiated by untrusted input and construction cannot fail, there is no class-level C<< MM::Table->error >> - errors are always per-instance.
693              
694             =head1 CONSTRUCTOR
695              
696             =head2 make
697              
698             my $t = MM::Table->make;
699              
700             Creates and returns a new, empty C<MM::Table> instance.
701              
702             =head1 METHODS
703              
704             =head2 add( $key, $value )
705              
706             Appends a new entry without removing any existing entries for C<$key>.
707             Returns C<$self>, or C<undef> on error.
708              
709             =head2 clear
710              
711             Removes all entries. Returns C<$self>.
712              
713             =head2 compress( $flags )
714              
715             Flattens duplicate keys. C<$flags> must be C<OVERLAP_TABLES_SET> (C<0>) to keep only the last value, or C<OVERLAP_TABLES_MERGE> (C<1>) to join all values with C<", ">. Returns C<$self>, or C<undef> on error.
716              
717             =head2 copy
718              
719             Returns a deep copy of the table as a new C<MM::Table> instance.
720              
721             =head2 do( $callback [, @filter_keys] )
722              
723             Iterates over all entries in insertion order, calling C<< $callback->( $key, $value ) >> for each. Iteration stops if the callback returns a false value.
724             If C<@filter_keys> is provided, only entries whose lowercased key matches one of the filter keys are visited. Returns C<$self>, or C<undef> on error.
725              
726             =head2 error( [$message] )
727              
728             Without argument: returns the stored L<Mail::Make::Exception> object, or C<undef> if no error has occurred.
729              
730             With one or more arguments: joins them into a message, creates a L<Mail::Make::Exception>, stores it, and returns C<undef>.
731              
732             =head2 get( $key )
733              
734             In scalar context: returns the value of the first matching entry, or C<undef>. In list context: returns all values for C<$key>, in insertion order. Returns C<undef>/empty list on error.
735              
736             =head2 merge( $key, $value )
737              
738             If an entry for C<$key> already exists, appends C<", $value"> to its value.
739             Otherwise behaves like L</add>. Returns C<$self>, or C<undef> on error.
740              
741             =head2 overlap( $other_table, $flags )
742              
743             Copies all entries from C<$other_table> into C<$self>. With C<OVERLAP_TABLES_SET> each key is replaced; with C<OVERLAP_TABLES_MERGE> values are appended. Returns C<$self>, or C<undef> on error.
744              
745             =head2 overlay( $other_table )
746              
747             Returns a new C<MM::Table> containing all entries from C<$other_table> followed by all entries from C<$self> (C<$other_table> entries come first).
748             Returns the new table, or C<undef> on error.
749              
750             =head2 pass_error
751              
752             Propagates the error currently stored in this instance by returning C<undef>. If called with arguments, delegates to L</error> to create a new exception first.
753              
754             =head2 set( $key, $value )
755              
756             Removes all existing entries for C<$key> and adds a single new one.
757             Returns C<$self>, or C<undef> on error.
758              
759             =head2 unset( $key )
760              
761             Removes all entries for C<$key>. Returns C<$self>, or C<undef> on error.
762              
763             =head1 TIED-HASH INTERFACE
764              
765             C<MM::Table> overloads C<%{}> to expose a tied hash interface compatible with APR::Table's C<< $t->{key} >> syntax. Assignment calls L</set>, deletion calls L</unset>, and C<each>/C<keys>/C<values> iterate in insertion order. Multiple values for the same key are all visited during iteration.
766              
767             =head1 NOTES / LIMITATIONS
768              
769             =over 4
770              
771             =item * Performance
772              
773             All lookups are linear scans. C<MM::Table> is designed for the small, bounded sets of headers found in email messages, not for large tables.
774              
775             =item * C<copy> and the C<$pool> argument
776              
777             The C<copy> method accepts no arguments. The C<$pool> parameter present in the original C<APR::Table> API has no equivalent here.
778              
779             =back
780              
781             =head1 AUTHOR
782              
783             Jacques Deguest E<lt>F<jack@deguest.jp>E<gt>
784              
785             =head1 SEE ALSO
786              
787             L<APR::Table>, L<Mail::Make::Headers>, L<Mail::Make::Exception>
788              
789             =head1 COPYRIGHT & LICENSE
790              
791             Copyright(c) 2026 DEGUEST Pte. Ltd.
792              
793             All rights reserved.
794              
795             This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
796              
797             =cut