1 package TOML::Tiny::Writer;
5 no warnings qw(experimental);
9 use Scalar::Util qw(looks_like_number);
10 use TOML::Tiny::Grammar;
11 use TOML::Tiny::Util qw(is_strict_array);
15 use B qw( svref_2object SVf_IOK SVf_NOK );
25 # Generate simple key/value pairs for scalar data
26 for my $k (grep{ ref($data->{$_}) !~ /HASH|ARRAY/ } sort keys %$data) {
27 my $key = to_toml_key($k);
28 my $val = to_toml($data->{$k}, %param);
29 push @buff_assign, "$key=$val";
32 # For values which are arrays, generate inline arrays for non-table
33 # values, array-of-tables for table values.
34 ARRAY: for my $k (grep{ ref $data->{$_} eq 'ARRAY' } sort keys %$data) {
36 if (!@{$data->{$k}}) {
37 my $key = to_toml_key($k);
38 push @buff_assign, "$key=[]";
45 # Sort table and non-table values into separate containers
46 for my $v (@{$data->{$k}}) {
47 if (ref $v eq 'HASH') {
48 push @table_array, $v;
54 # Non-table values become an inline table
56 my $key = to_toml_key($k);
57 my $val = to_toml(\@inline, %param);
58 push @buff_assign, "$key=$val";
61 # Table values become an array-of-tables
66 push @buff_tables, '', '[[' . join('.', map{ to_toml_key($_) } @KEYS) . ']]';
67 push @buff_tables, to_toml($_);
75 for my $k (grep{ ref $data->{$_} eq 'HASH' } sort keys %$data) {
76 if (!keys(%{$data->{$k}})) {
78 my $key = to_toml_key($k);
79 push @buff_assign, "$key={}";
83 push @buff_tables, '', '[' . join('.', map{ to_toml_key($_) } @KEYS) . ']';
84 push @buff_tables, to_toml($data->{$k}, %param);
91 if (@$data && $param{strict_arrays}) {
92 my ($ok, $err) = is_strict_array($data);
93 die "toml: found heterogenous array, but strict_arrays is set ($err)\n" unless $ok;
96 push @buff_tables, '[' . join(', ', map{ to_toml($_, %param) } @$data) . ']';
102 } elsif ($$_ eq '0') {
105 push @buff_assign, to_toml($$_, %param);
109 when (/JSON::PP::Boolean/) {
110 return $$data ? 'true' : 'false';
114 my $formatter = $param{datetime_formatter};
115 return $formatter ? $formatter->format_datetime($data) : "$data";
118 when ('Math::BigInt') {
122 when ('Math::BigFloat') {
126 when (!! $param{no_string_guessing}) {
127 # Thanks to ikegami on Stack Overflow for the trick!
128 # https://stackoverflow.com/questions/12686335/how-to-tell-apart-numeric-scalars-and-string-scalars-in-perl/12693984#12693984
130 my $sv = svref_2object(\$data);
131 my $svflags = $sv->FLAGS;
133 if ($svflags & (SVf_IOK | SVf_NOK)) {
136 return to_toml_string($data);
142 when (looks_like_number($_)) {
151 return to_toml_string($data);
157 die 'unhandled: '.Dumper($_);
161 join "\n", @buff_assign, @buff_tables;
167 if ($str =~ /^[-_A-Za-z0-9]+$/) {
191 $arg =~ s/([\x22\x5c\n\r\t\f\b])/$escape->{$1}/g;
192 $arg =~ s/([\x00-\x08\x0b\x0e-\x1f])/'\\u00' . unpack('H2', $1)/eg;
194 return '"' . $arg . '"';