Skip to content

Commit

Permalink
Add require_footer option
Browse files Browse the repository at this point in the history
  • Loading branch information
perlpunk committed Jan 25, 2025
1 parent 4282b1d commit 5aa8c69
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 0 deletions.
3 changes: 3 additions & 0 deletions bin/yamlpp-load
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ GetOptions(
'duplicate-keys' => \my $duplicate_keys,
'merge' => \my $merge,
'catchall' => \my $catchall,
'require-footer' => \my $require_footer,
'perl' => \my $perl,
'module|M=s' => \my $module,
'yaml-version=s' => \my $yaml_version,
Expand Down Expand Up @@ -78,6 +79,7 @@ sub _yamlpp {
duplicate_keys => $duplicate_keys ? 1 : 0,
preserve => PRESERVE_ORDER,
yaml_version => \@yaml_versions,
require_footer => $require_footer,
);
my @docs = $ypp->load_string($yaml);
return @docs;
Expand Down Expand Up @@ -142,6 +144,7 @@ Options:
--duplicate-keys Allow duplicate keys
--merge Enable loading merge keys '<<'
--catchall Ignore any unknown tags
--require-footer Require '...' and the end of each document
--perl Enable loading perl types and objects (use only
on trusted input!)
--module -M YAML::PP (default), YAML, YAML::PP::LibYAML,
Expand Down
3 changes: 3 additions & 0 deletions bin/yamlpp-load-dump
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ GetOptions(
'footer!' => \my $footer,
'merge' => \my $merge,
'catchall' => \my $catchall,
'require-footer' => \my $require_footer,
'perl' => \my $perl,
'preserve|P=s' => \my $preserve,
'module|M=s' => \my $module,
Expand Down Expand Up @@ -144,6 +145,7 @@ sub _yamlpp {
footer => $footer ? 1 : 0,
yaml_version => \@yaml_versions,
version_directive => $version_directive || 0,
require_footer => $require_footer,
);
if ($inc) {
$inc->yp($ypp);
Expand Down Expand Up @@ -292,6 +294,7 @@ Options:
--[no-]footer Print '...'
--merge Enable loading merge keys '<<'
--catchall Ignore any unknown tags
--require-footer Require '...' and the end of each document
--perl Enable loading perl types and objects (use only
on trusted input!)
--preserve, -P Comma separated: 'order', 'scalar', 'flow', 'alias'.
Expand Down
25 changes: 25 additions & 0 deletions lib/YAML/PP.pm
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ sub new {
my $writer = delete $args{writer};
my $header = delete $args{header};
my $footer = delete $args{footer};
my $require_footer = delete $args{require_footer};
my $duplicate_keys = delete $args{duplicate_keys};
my $yaml_version = $class->_arg_yaml_version(delete $args{yaml_version});
my $default_yaml_version = $yaml_version->[0];
Expand Down Expand Up @@ -69,6 +70,7 @@ sub new {
default_yaml_version => $default_yaml_version,
preserve => $preserve,
duplicate_keys => $duplicate_keys,
require_footer => $require_footer,
);
my $dumper = YAML::PP::Dumper->new(
schema => $default_schema,
Expand Down Expand Up @@ -667,6 +669,29 @@ This option is for dumping.
Print document footer C<...>
=item require_footer
Default: 0
Will require a C<...> at the end of each document.
This can be useful in a context where you want to make sure you received
the complete content, for example over network.
# Good
---
a: 1
...
---
a: 2
...
# Bad
---
a: 1
---
a: 2
=item yaml_version
Since version 0.020
Expand Down
6 changes: 6 additions & 0 deletions lib/YAML/PP/Constructor.pm
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ sub new {
unless (defined $duplicate_keys) {
$duplicate_keys = 0;
}
my $require_footer = delete $args{require_footer};
my $preserve = delete $args{preserve} || 0;
if ($preserve == 1) {
$preserve = PRESERVE_ORDER | PRESERVE_SCALAR_STYLE | PRESERVE_FLOW_STYLE | PRESERVE_ALIAS;
Expand All @@ -44,6 +45,7 @@ sub new {
cyclic_refs => $cyclic_refs,
preserve => $preserve,
duplicate_keys => $duplicate_keys,
require_footer => $require_footer,
}, $class;
$self->init;
return $self;
Expand Down Expand Up @@ -89,6 +91,7 @@ sub preserve_scalar_style { return $_[0]->{preserve} & PRESERVE_SCALAR_STYLE }
sub preserve_flow_style { return $_[0]->{preserve} & PRESERVE_FLOW_STYLE }
sub preserve_alias { return $_[0]->{preserve} & PRESERVE_ALIAS }
sub duplicate_keys { return $_[0]->{duplicate_keys} }
sub require_footer { return $_[0]->{require_footer} }

sub document_start_event {
my ($self, $event) = @_;
Expand Down Expand Up @@ -118,6 +121,9 @@ sub document_end_event {
die "Got unexpected end of document";
}
my $docs = $self->docs;
if ($event->{implicit} and $self->require_footer) {
die sprintf "load: Document (%d) did not end with '...' (require_footer=1)", 1 + scalar @$docs;
}
push @$docs, $last->{ref}->[0];
$self->set_anchors({});
$self->set_stack([]);
Expand Down
2 changes: 2 additions & 0 deletions lib/YAML/PP/Loader.pm
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ sub new {
my $default_yaml_version = delete $args{default_yaml_version} || '1.2';
my $preserve = delete $args{preserve};
my $duplicate_keys = delete $args{duplicate_keys};
my $require_footer = delete $args{require_footer};
my $schemas = delete $args{schemas};
$schemas ||= {
'1.2' => YAML::PP->default_schema(
Expand All @@ -29,6 +30,7 @@ sub new {
default_yaml_version => $default_yaml_version,
preserve => $preserve,
duplicate_keys => $duplicate_keys,
require_footer => $require_footer,
);
my $parser = delete $args{parser};
unless ($parser) {
Expand Down
46 changes: 46 additions & 0 deletions t/47.header-footer.t
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,52 @@ EOM
cmp_ok($out, 'eq', $out_expected, "Dumping with indent");
};

subtest require_footer => sub {
my $good1 = <<'EOM';
a: 1
...
EOM
my $good2 = <<'EOM';
a: 1
...
---
a: 2
...
EOM
my $bad1 = <<'EOM';
a: 1
---
a: 2
...
EOM
my $bad2 = <<'EOM';
a: 1
...
---
a: 2
EOM
my $bad3 = <<'EOM';
a: 1
---
a: 2
EOM
my $yp = YAML::PP->new( require_footer => 1 );
my $data;
local $@;

$data = eval { $yp->load_string($good1) };
is $@, '', "good 1";
$data = eval { $yp->load_string($good2) };
is $@, '', "good 2";

my $re = qr{Document .\d+. did not end with '...' .require_footer=1.};
$data = eval { $yp->load_string($bad1) };
like $@, $re, "bad 1";
$data = eval { $yp->load_string($bad1) };
like $@, $re, "bad 2";
$data = eval { $yp->load_string($bad1) };
like $@, $re, "bad 3";
};

done_testing;

0 comments on commit 5aa8c69

Please sign in to comment.