Skip to content

Commit

Permalink
wip: TOML support
Browse files Browse the repository at this point in the history
  • Loading branch information
vanodevium authored and Crell committed Sep 6, 2024
1 parent 228cea4 commit c63cc7c
Show file tree
Hide file tree
Showing 5 changed files with 151 additions and 2 deletions.
2 changes: 2 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@
"crell/fp": "~1.0"
},
"require-dev": {
"devium/toml": "^1.0",
"phpbench/phpbench": "^1.3.0",
"phpstan/phpstan": "^1.11",
"phpunit/phpunit": "~10.5",
"symfony/yaml": "^5.4"
},
"suggest": {
"devium/toml": "Enables serializing to/from TOML files.",
"symfony/yaml": "Enables serializing to/from YAML files."
},
"autoload": {
Expand Down
68 changes: 68 additions & 0 deletions src/Formatter/TomlFormatter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php

declare(strict_types=1);

namespace Crell\Serde\Formatter;

use Crell\Serde\Attributes\ClassSettings;
use Crell\Serde\Attributes\Field;
use Crell\Serde\Deserializer;
use Devium\Toml\Toml;
use Devium\Toml\TomlError;

class TomlFormatter implements Formatter, Deformatter, SupportsCollecting
{
use ArrayBasedFormatter;
use ArrayBasedDeformatter;

/**
* Constructor parameters map directly to the devium/toml component's encode() and decode() methods.
*
* @see Toml::encode()
* @see Toml::decode()
*/
public function __construct() {}

public function format(): string
{
return 'toml';
}

/**
* @param ClassSettings $classDef
* @param Field $rootField
* @return array<string, mixed>
*/
public function serializeInitialize(ClassSettings $classDef, Field $rootField): array
{
return ['root' => []];
}

public function serializeFinalize(mixed $runningValue, ClassSettings $classDef): string
{
return Toml::encode($runningValue['root']);
}

/**
* @param mixed $serialized
* @param ClassSettings $classDef
* @param Field $rootField
* @param Deserializer $deserializer
* @return array<string, mixed>
* @throws TomlError
*/
public function deserializeInitialize(
mixed $serialized,
ClassSettings $classDef,
Field $rootField,
Deserializer $deserializer
): array
{
return ['root' => Toml::decode($serialized ?: '', true)];
}

public function deserializeFinalize(mixed $decoded): void
{

}
}
4 changes: 2 additions & 2 deletions src/PropertyHandler/MixedExporter.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
*
* This class makes a good-faith attempt to detect the type of a given field by its value.
* It currently does not work for objects, and on import it works only on array-based
* formats (JSON, YAML, etc.)
* formats (JSON, YAML, TOML, etc.)
*/
class MixedExporter implements Importer, Exporter
{
Expand Down Expand Up @@ -55,6 +55,6 @@ public function canImport(Field $field, string $format): bool
// We can only import if we know that the $source will be an array so that it
// can be introspected. If it's not, then this class has no way to tell what
// type to tell the Deformatter to read.
return $field->typeCategory === TypeCategory::Mixed && in_array($format, ['json', 'yaml', 'array']);
return $field->typeCategory === TypeCategory::Mixed && in_array($format, ['json', 'yaml', 'array', 'toml']);
}
}
5 changes: 5 additions & 0 deletions src/SerdeCommon.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use Crell\Serde\Formatter\Deformatter;
use Crell\Serde\Formatter\Formatter;
use Crell\Serde\Formatter\JsonFormatter;
use Crell\Serde\Formatter\TomlFormatter;
use Crell\Serde\Formatter\YamlFormatter;
use Crell\Serde\PropertyHandler\DateTimeExporter;
use Crell\Serde\PropertyHandler\DateTimeZoneExporter;
Expand All @@ -26,6 +27,7 @@
use Crell\Serde\PropertyHandler\ScalarExporter;
use Crell\Serde\PropertyHandler\SequenceExporter;
use Crell\Serde\PropertyHandler\UnixTimeExporter;
use Devium\Toml\Toml;
use Symfony\Component\Yaml\Yaml;
use function Crell\fp\afilter;
use function Crell\fp\indexBy;
Expand Down Expand Up @@ -94,6 +96,9 @@ public function __construct(
if (class_exists(Yaml::class)) {
$formatters[] = new YamlFormatter();
}
if (class_exists(Toml::class)) {
$formatters[] = new TomlFormatter();
}

// These lines by definition filter the array to the correct type, but
// PHPStan doesn't know that.
Expand Down
74 changes: 74 additions & 0 deletions tests/TomlFormatterTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?php

declare(strict_types=1);

namespace Crell\Serde;

use Devium\Toml\Toml;
use Crell\Serde\Formatter\TomlFormatter;

class TomlFormatterTest extends ArrayBasedFormatterTestCases
{
public function setUp(): void
{
parent::setUp();
$this->formatters = [new TomlFormatter()];
$this->format = 'toml';
$this->emptyData = '';

$this->aliasedData = Toml::encode([
'un' => 1,
'dos' => 'dos',
'dot' => [
'x' => 1,
'y' => 2,
'z' => 3,
]
]);

$this->invalidDictStringKey = Toml::encode([
'stringKey' => ['a' => 'A', 2 => 'B'],
// The 'd' key here is invalid and won't deserialize.
'intKey' => [5 => 'C', 'd' => 'D'],
]);

$this->invalidDictIntKey = Toml::encode([
// The 2 key here is invalid and won't deserialize.
'stringKey' => ['a' => 'A', 2 => 'B'],
'intKey' => [5 => 'C', 10 => 'D'],
]);

$this->missingOptionalData = Toml::encode(['a' => 'A']);

$this->dictsInSequenceShouldFail = Toml::encode([
'strict' => ['a' => 'A', 'b' => 'B'],
'nonstrict' => ['a' => 'A', 'b' => 'B'],
]);

$this->dictsInSequenceShouldPass = Toml::encode([
'strict' => ['A', 'B'],
'nonstrict' => ['a' => 'A', 'b' => 'B'],
]);
}

protected function arrayify(mixed $serialized): array
{
return Toml::decode($serialized, true);
}

public static function non_strict_properties_examples(): iterable
{
foreach (self::non_strict_properties_examples_data() as $k => $v) {
$v['serialized'] = Toml::encode($v['serialized']);
yield $k => $v;
}
}

public static function strict_mode_throws_examples(): iterable
{
foreach (self::strict_mode_throws_examples_data() as $k => $v) {
$v['serialized'] = Toml::encode($v['serialized']);
yield $k => $v;
}
}
}

0 comments on commit c63cc7c

Please sign in to comment.