Data-TOON

 view release on metacpan or  search on metacpan

README.md  view on Meta::CPAN


    # Basic usage
    my $data = { name => 'Alice', age => 30, active => 1 };
    my $toon = Data::TOON->encode($data);
    print $toon;
    # Output:
    #   active: true
    #   age: 30
    #   name: Alice

    my $decoded = Data::TOON->decode($toon);
    
    # Tabular arrays
    my $users = {
        users => [
            { id => 1, name => 'Alice', role => 'admin' },
            { id => 2, name => 'Bob', role => 'user' }
        ]
    };
    
    print Data::TOON->encode($users);

lib/Data/TOON.pm  view on Meta::CPAN


    # Basic usage
    my $data = { name => 'Alice', age => 30, active => 1 };
    my $toon = Data::TOON->encode($data);
    print $toon;
    # Output:
    #   active: true
    #   age: 30
    #   name: Alice

    my $decoded = Data::TOON->decode($toon);
    
    # Tabular arrays
    my $users = {
        users => [
            { id => 1, name => 'Alice', role => 'admin' },
            { id => 2, name => 'Bob', role => 'user' }
        ]
    };
    
    print Data::TOON->encode($users);

t/01_encode_decode.t  view on Meta::CPAN

# Test 3: Simple object decoding
{
    my $toon_text = <<'TOON';
id: 123
name: Alice
active: true
TOON
    
    my $data = Data::TOON->decode($toon_text);
    ok($data, 'decode() returns data structure');
    is($data->{id}, 123, 'decoded id is correct');
    is($data->{name}, 'Alice', 'decoded name is correct');
}

# Test 4: Array decoding
{
    my $toon_text = <<'TOON';
users[2]{id,name,role}:
  1,Alice,admin
  2,Bob,user
TOON
    

t/03_nested_objects.t  view on Meta::CPAN

        id => 1,
        name => 'Alice',
        profile => {
            age => 30,
            email => 'alice@example.com'
        },
        active => 1
    };
    
    my $encoded = Data::TOON->encode($data);
    my $decoded = Data::TOON->decode($encoded);
    
    is($decoded->{id}, 1, 'simple field preserved');
    is(ref $decoded->{profile}, 'HASH', 'nested profile is hash');
    is($decoded->{profile}->{age}, 30, 'nested age preserved');
    is($decoded->{active}, 1, 'field after nested object preserved');
}

done_testing;

t/05_security.t  view on Meta::CPAN

}

# Test 10: NaN and Infinity are strings, not numbers
{
    my $toon_text = <<'TOON';
value1: NaN
value2: Infinity
TOON
    
    my $data = Data::TOON->decode($toon_text);
    is($data->{value1}, 'NaN', 'NaN decoded as string');
    is($data->{value2}, 'Infinity', 'Infinity decoded as string');
}

# Test 11: Shell injection-like patterns (data escaping)
{
    my $toon_text = 'cmd: "rm -rf /; echo pwned"';
    
    my $data = Data::TOON->decode($toon_text);
    is($data->{cmd}, "rm -rf /; echo pwned", 'dangerous string preserved literally');
}

t/05_security.t  view on Meta::CPAN

    # First row should try to split by comma, not pipe
}

# Test 17: Empty nested objects
{
    my $toon_text = <<'TOON';
user:
TOON
    
    my $data = Data::TOON->decode($toon_text);
    is(ref $data->{user}, 'HASH', 'empty nested object decoded as hash');
}

# Test 18: Newlines in values
{
    my $toon_text = "text: \"line1\\nline2\"";
    
    my $data = Data::TOON->decode($toon_text);
    is($data->{text}, "line1\nline2", 'escaped newline in value preserved');
}

t/06_delimiters.t  view on Meta::CPAN

{
    my $data = {
        records => [
            { field1 => 'value1', field2 => 'value2' },
            { field1 => 'value3', field2 => 'value4' }
        ]
    };
    
    my $encoder = Data::TOON::Encoder->new(delimiter => "\t");
    my $encoded = $encoder->encode($data);
    my $decoded = Data::TOON->decode($encoded);
    
    is_deeply($decoded, $data, 'tab round-trip preserves data');
}

# Test 6: Pipe delimiter round-trip
{
    my $data = {
        records => [
            { field1 => 'value1', field2 => 'value2' },
            { field1 => 'value3', field2 => 'value4' }
        ]
    };
    
    my $encoder = Data::TOON::Encoder->new(delimiter => '|');
    my $encoded = $encoder->encode($data);
    my $decoded = Data::TOON->decode($encoded);
    
    is_deeply($decoded, $data, 'pipe round-trip preserves data');
}

# Test 7: Delimiter with comma in values (should be quoted)
{
    my $data = {
        items => [
            { desc => 'hello,world', id => 1 },
            { desc => 'foo,bar', id => 2 }
        ]
    };
    
    my $encoder = Data::TOON::Encoder->new(delimiter => '|');
    my $encoded = $encoder->encode($data);
    my $decoded = Data::TOON->decode($encoded);
    
    is($decoded->{items}->[0]->{desc}, 'hello,world', 'comma in value with pipe delimiter');
}

done_testing;

t/07_root_forms.t  view on Meta::CPAN

{
    my $toon_text = 'data: 1e3';
    my $data = Data::TOON->decode($toon_text);
    is($data->{data}, 1000, 'canonical: scientific notation');
}

# Test 16: Round-trip root primitive
{
    for my $value (42, 3.14, 'hello', 1, 0) {
        my $encoded = Data::TOON->encode($value);
        my $decoded = Data::TOON->decode($encoded);
        is($decoded, $value, "round-trip: $value");
    }
}

# Test 17: Round-trip root array
{
    my $data = [1, 2, 3];
    my $encoded = Data::TOON->encode($data);
    my $decoded = Data::TOON->decode($encoded);
    is_deeply($decoded, $data, 'round-trip root array');
}

done_testing;

t/09_nested_hash_from_json.t  view on Meta::CPAN

    my $json = '{"items":[{"id":1,"meta":{"tags":["a","b"],"info":"test"}}]}';
    my $data = JSON::PP::decode_json($json);
    my $toon = Data::TOON->encode($data);
    
    ok($toon, 'encode() handles arrays with nested hashes from JSON');
    unlike($toon, qr/HASH\(0x/, 'nested hashes in array elements are expanded');
    like($toon, qr/meta/, 'meta key is present');
    like($toon, qr/info/, 'info key is present');
}

# Test 4: Compare with manually created structure vs JSON decoded
{
    # Manually created structure
    my $manual = {
        result => {
            tools => [
                {
                    name => 'test',
                    inputSchema => {
                        type => 'object',
                        properties => {}
                    }
                }
            ]
        }
    };
    
    # JSON decoded structure (should behave the same)
    my $json = '{"result":{"tools":[{"name":"test","inputSchema":{"type":"object","properties":{}}}]}}';
    my $from_json = JSON::PP::decode_json($json);
    
    my $toon_manual = Data::TOON->encode($manual);
    my $toon_json = Data::TOON->encode($from_json);
    
    # Both should not contain HASH references
    unlike($toon_manual, qr/HASH\(0x/, 'manually created structure is properly expanded');
    unlike($toon_json, qr/HASH\(0x/, 'JSON decoded structure is properly expanded');
    
    # Both should produce similar output (order may differ due to hash randomization)
    like($toon_manual, qr/inputSchema/, 'manual: contains inputSchema');
    like($toon_json, qr/inputSchema/, 'json: contains inputSchema');
}

done_testing;



( run in 0.457 second using v1.01-cache-2.11-cpan-9383018d099 )