chiark / gitweb /
TOML::Tiny: Provide no_string_guessing
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Mon, 4 May 2020 00:13:41 +0000 (01:13 +0100)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Tue, 5 May 2020 20:10:56 +0000 (21:10 +0100)
Ideally, if we read a TOML file and write it back out again, we get a
semantically equivalent TOML file.  A necessary part of this is to
avoid the string-contents-based type-guessing.

Here, we provide a new `no_string_guessing` parameter which disables
this format-guessing.  Instead, it uses internal Perl flags to
distinguish integers from strings.

Right now this new feature is not likely to work for things that came
from TOML::Tiny::Parser without inflate_* parameters, since those are
all strings.  But it now allows Perl code to generate the TOML it
wants.

README.pod
lib/TOML/Tiny.pm
lib/TOML/Tiny/Writer.pm

index 911f6db41c8d6e3da2b495df82b43ebf65936c96..4b0d40cfa684dba1fe81ee864ab972fd10bd6df5 100644 (file)
@@ -168,6 +168,21 @@ wish to require strictly typed arrays (for C<TOML>'s definition of "type",
 anyway), C<strict_arrays> will produce an error when encountering arrays with
 heterogenous types.
 
+=item no_string_guessing
+
+When encoding a Perl scalar it is not always clear what the TOML type
+of the value is supposed to be.  By default, C<TOML::Tiny> will, for
+unblessed scalars, guess based on the scalar's appearance.  Strings
+that look like numbers, or like datetimes, will be encoded as such.
+
+With no_string_guessing, C<TOML::Tiny> will look at the perl innards
+to find the currently stored value type.  If it is a number, the
+scalar will be encoded as a number.  If it's a string, as a string.
+Dates and times which weren't built with DateTime come out as strings.
+
+Specifying C<inflate_float>, C<inflate_integer>, and
+C<inflate_datetime> is likely to be helpful with this option.
+
 =back
 
 =head2 decode
index 6cd88be24cf5019375e1a1c7362eac76f9b6111f..4597fe8f93fb4e60ba0cfa8362f1ed751fee5935 100644 (file)
@@ -52,6 +52,7 @@ sub encode {
   my ($self, $data) = @_;
   TOML::Tiny::Writer::to_toml($data,
     strict_arrays => $self->{strict_arrays},
+    no_string_guessing => $self->{no_string_guessing},
   );
 }
 
@@ -224,6 +225,21 @@ wish to require strictly typed arrays (for C<TOML>'s definition of "type",
 anyway), C<strict_arrays> will produce an error when encountering arrays with
 heterogenous types.
 
+=item no_string_guessing
+
+When encoding a Perl scalar it is not always clear what the TOML type
+of the value is supposed to be.  By default, C<TOML::Tiny> will, for
+unblessed scalars, guess based on the scalar's appearance.  Strings
+that look like numbers, or like datetimes, will be encoded as such.
+
+With no_string_guessing, C<TOML::Tiny> will look at the perl innards
+to find the currently stored value type.  If it is a number, the
+scalar will be encoded as a number.  If it's a string, as a string.
+Dates and times which weren't built with DateTime come out as strings.
+
+Specifying C<inflate_float>, C<inflate_integer>, and
+C<inflate_datetime> is likely to be helpful with this option.
+
 =back
 
 =head2 decode
index 66249a855f8b19359e6a4647aa698829549f8be9..831d2e5395583320954e8437346d9e3984e790d9 100644 (file)
@@ -12,6 +12,8 @@ use TOML::Tiny::Util qw(is_strict_array);
 
 my @KEYS;
 
+use B qw( svref_2object SVf_IOK SVf_NOK );
+
 sub to_toml {
   my $data = shift;
   my %param = @_;
@@ -119,6 +121,20 @@ sub to_toml {
       return $data->bnstr;
     }
 
+    when (!! $param{no_string_guessing}) {
+      # Thanks to ikegami on Stack Overflow for the trick!
+      # https://stackoverflow.com/questions/12686335/how-to-tell-apart-numeric-scalars-and-string-scalars-in-perl/12693984#12693984
+
+      my $sv = svref_2object(\$data);
+      my $svflags = $sv->FLAGS;
+
+      if ($svflags & (SVf_IOK | SVf_NOK)) {
+       return $data;
+      } else {
+       return to_toml_string($data);
+      }
+    }
+
     when ('') {
       for ($data) {
         when (looks_like_number($_)) {