Skip to content

Commit f06cfc9

Browse files
author
Robert Gasch
committed
initial commit
0 parents  commit f06cfc9

File tree

15 files changed

+1295
-0
lines changed

15 files changed

+1295
-0
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/vendor/
2+
composer.lock
3+
.phpunit.result.cache
4+
.pest

.phpunit.cache/test-results

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"version":"pest_4.1.1","defects":{"P\\Tests\\CommandTest::__pest_evaluable__Artisan_toon_convert_Command__\u2192_it_is_registered_in_artisan":8,"P\\Tests\\CommandTest::__pest_evaluable__Artisan_toon_convert_Command__\u2192_it_shows_statistics_when___stats_flag_is_used":8,"P\\Tests\\ConfigTest::__pest_evaluable__Configuration__\u2192_it_supports_environment_variable_configuration":7,"P\\Tests\\ConfigTest::__pest_evaluable__Toon_Service_with_Config__\u2192_it_allows_overriding_config_with_explicit_options":8,"P\\Tests\\IntegrationTest::__pest_evaluable__Laravel_Integration__\u2192_it_registers_Toon_service_in_container":8,"P\\Tests\\IntegrationTest::__pest_evaluable__Laravel_Integration__\u2192_it_provides_Toon_facade":8,"P\\Tests\\IntegrationTest::__pest_evaluable__Laravel_Integration__\u2192_it_facade_can_decode_TOON_format":8,"P\\Tests\\IntegrationTest::__pest_evaluable__Configuration_Integration__\u2192_it_allows_per_call_option_overrides":8,"P\\Tests\\IntegrationTest::__pest_evaluable__Facade_Functionality__\u2192_it_encodes_StdClass_objects":8,"P\\Tests\\IntegrationTest::__pest_evaluable__Facade_Functionality__\u2192_it_decodes_with_StdClass_when_configured":8,"P\\Tests\\IntegrationTest::__pest_evaluable__Facade_Functionality__\u2192_it_supports_perfect_round_trips_with_StdClass":8},"times":{"P\\Tests\\CommandTest::__pest_evaluable__Artisan_toon_convert_Command__\u2192_it_is_registered_in_artisan":0.049,"P\\Tests\\CommandTest::__pest_evaluable__Artisan_toon_convert_Command__\u2192_it_encodes_JSON_to_TOON_by_file_extension":0.012,"P\\Tests\\CommandTest::__pest_evaluable__Artisan_toon_convert_Command__\u2192_it_decodes_TOON_to_JSON_by_file_extension":0.003,"P\\Tests\\CommandTest::__pest_evaluable__Artisan_toon_convert_Command__\u2192_it_respects___encode_flag":0.002,"P\\Tests\\CommandTest::__pest_evaluable__Artisan_toon_convert_Command__\u2192_it_respects___decode_flag":0.002,"P\\Tests\\CommandTest::__pest_evaluable__Artisan_toon_convert_Command__\u2192_it_uses_custom_delimiter":0.002,"P\\Tests\\CommandTest::__pest_evaluable__Artisan_toon_convert_Command__\u2192_it_uses_custom_indentation":0.002,"P\\Tests\\CommandTest::__pest_evaluable__Artisan_toon_convert_Command__\u2192_it_uses_length_marker_when_specified":0.003,"P\\Tests\\CommandTest::__pest_evaluable__Artisan_toon_convert_Command__\u2192_it_outputs_to_stdout_when_no_output_file_specified":0.003,"P\\Tests\\CommandTest::__pest_evaluable__Artisan_toon_convert_Command__\u2192_it_fails_with_invalid_input_file":0.002,"P\\Tests\\CommandTest::__pest_evaluable__Artisan_toon_convert_Command__\u2192_it_fails_with_invalid_delimiter":0.002,"P\\Tests\\CommandTest::__pest_evaluable__Artisan_toon_convert_Command__\u2192_it_handles___no_strict_flag_for_decoding":0.002,"P\\Tests\\CommandTest::__pest_evaluable__Artisan_toon_convert_Command__\u2192_it_shows_statistics_when___stats_flag_is_used":0.002,"P\\Tests\\CommandTest::__pest_evaluable__Artisan_toon_convert_Command__\u2192_it_handles_complex_nested_structures":0.002,"P\\Tests\\CommandTest::__pest_evaluable__Artisan_toon_convert_Command__\u2192_it_handles_round_trip_conversion":0.003,"P\\Tests\\ConfigTest::__pest_evaluable__Configuration__\u2192_it_has_default_configuration_values":0.001,"P\\Tests\\ConfigTest::__pest_evaluable__Configuration__\u2192_it_can_be_overridden_via_config":0.001,"P\\Tests\\ConfigTest::__pest_evaluable__Configuration__\u2192_it_supports_environment_variable_configuration":0.001,"P\\Tests\\ConfigTest::__pest_evaluable__Configuration_Publishing__\u2192_it_can_publish_configuration_file":0.006,"P\\Tests\\ConfigTest::__pest_evaluable__Service_Provider_Features__\u2192_it_registers_Toon_service_as_singleton":0.001,"P\\Tests\\ConfigTest::__pest_evaluable__Service_Provider_Features__\u2192_it_registers_ToonCommand_when_in_console":0.008,"P\\Tests\\ConfigTest::__pest_evaluable__Service_Provider_Features__\u2192_it_merges_package_configuration":0.002,"P\\Tests\\ConfigTest::__pest_evaluable__Toon_Service_with_Config__\u2192_it_applies_config_defaults_when_encoding":0.001,"P\\Tests\\ConfigTest::__pest_evaluable__Toon_Service_with_Config__\u2192_it_applies_config_defaults_when_decoding":0.001,"P\\Tests\\ConfigTest::__pest_evaluable__Toon_Service_with_Config__\u2192_it_allows_overriding_config_with_explicit_options":0.001,"P\\Tests\\IntegrationTest::__pest_evaluable__Laravel_Integration__\u2192_it_registers_Toon_service_in_container":0,"P\\Tests\\IntegrationTest::__pest_evaluable__Laravel_Integration__\u2192_it_provides_Toon_facade":0.001,"P\\Tests\\IntegrationTest::__pest_evaluable__Laravel_Integration__\u2192_it_facade_can_decode_TOON_format":0.001,"P\\Tests\\IntegrationTest::__pest_evaluable__Configuration_Integration__\u2192_it_uses_configured_encode_defaults":0.001,"P\\Tests\\IntegrationTest::__pest_evaluable__Configuration_Integration__\u2192_it_uses_configured_decode_defaults":0.001,"P\\Tests\\IntegrationTest::__pest_evaluable__Configuration_Integration__\u2192_it_allows_per_call_option_overrides":0.001,"P\\Tests\\IntegrationTest::__pest_evaluable__Configuration_Integration__\u2192_it_handles_length_marker_configuration":0.001,"P\\Tests\\IntegrationTest::__pest_evaluable__Configuration_Integration__\u2192_it_handles_false_length_marker_configuration":0.001,"P\\Tests\\IntegrationTest::__pest_evaluable__Service_Provider__\u2192_it_merges_default_configuration":0.001,"P\\Tests\\IntegrationTest::__pest_evaluable__Service_Provider__\u2192_it_registers_console_command_when_running_in_console":0.009,"P\\Tests\\IntegrationTest::__pest_evaluable__Facade_Functionality__\u2192_it_encodes_StdClass_objects":0.001,"P\\Tests\\IntegrationTest::__pest_evaluable__Facade_Functionality__\u2192_it_decodes_with_StdClass_when_configured":0.001,"P\\Tests\\IntegrationTest::__pest_evaluable__Facade_Functionality__\u2192_it_supports_perfect_round_trips_with_StdClass":0.001}}

README.md

Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
# Laravel TOON
2+
3+
Laravel integration for TOON - A human-readable data serialization format.
4+
5+
This package provides Laravel-specific features on top of the [robertgdev/php-toon](https://github.com/robertgdev/php-toon) core library.
6+
7+
## Installation
8+
9+
```bash
10+
composer require robertgdev/laravel-toon
11+
```
12+
13+
This will automatically install the `robertgdev/php-toon` core library as a dependency.
14+
15+
## Configuration
16+
17+
### Publishing Configuration
18+
19+
Publish the configuration file to customize default encoding/decoding options:
20+
21+
```bash
22+
php artisan vendor:publish --tag=toon-config
23+
```
24+
25+
This creates `config/toon.php` where you can set default options.
26+
27+
### Configuration Options
28+
29+
The config file supports both file-based configuration and environment variables:
30+
31+
```php
32+
// config/toon.php
33+
return [
34+
'encode' => [
35+
'indent' => env('TOON_ENCODE_INDENT', 2),
36+
'delimiter' => env('TOON_ENCODE_DELIMITER', ','),
37+
'lengthMarker' => env('TOON_ENCODE_LENGTH_MARKER', false),
38+
],
39+
'decode' => [
40+
'indent' => env('TOON_DECODE_INDENT', 2),
41+
'strict' => env('TOON_DECODE_STRICT', true),
42+
'objectsAsStdClass' => env('TOON_DECODE_OBJECTS_AS_STDCLASS', false),
43+
],
44+
];
45+
```
46+
47+
### Environment Variables
48+
49+
Add these to your `.env` file to configure TOON globally:
50+
51+
```env
52+
# Encoding options
53+
TOON_ENCODE_INDENT=2
54+
TOON_ENCODE_DELIMITER=,
55+
TOON_ENCODE_LENGTH_MARKER=false
56+
57+
# Decoding options
58+
TOON_DECODE_INDENT=2
59+
TOON_DECODE_STRICT=true
60+
TOON_DECODE_OBJECTS_AS_STDCLASS=false
61+
```
62+
63+
**Available Delimiters:**
64+
- `,` (comma, default)
65+
- `\t` (tab - use `"\t"` in config or `\t` in .env)
66+
- `|` (pipe)
67+
68+
**Length Marker:**
69+
- `false` (default) - no marker
70+
- `true` or `#` - adds `#` prefix to array lengths
71+
72+
**Objects as StdClass:**
73+
- `false` (default) - objects decode to associative arrays
74+
- `true` - objects decode to `StdClass` instances (enables perfect round-trips)
75+
76+
### Using Configured Defaults
77+
78+
When you use the facade without options, it automatically uses your configured defaults:
79+
80+
```php
81+
use RobertGDev\LaravelToon\Facades\Toon;
82+
83+
// Uses config defaults
84+
$encoded = Toon::encode($data);
85+
$decoded = Toon::decode($encoded);
86+
87+
// Override with custom options
88+
use RobertGDev\Toon\Types\EncodeOptions;
89+
$encoded = Toon::encode($data, new EncodeOptions(indent: 4));
90+
```
91+
92+
## Programmatic Usage
93+
94+
### Using the Facade (Laravel-style)
95+
96+
The package provides a Laravel facade for easy access:
97+
98+
```php
99+
use RobertGDev\LaravelToon\Facades\Toon;
100+
use RobertGDev\Toon\Types\EncodeOptions;
101+
102+
// Simple encoding
103+
$data = ['name' => 'Ada', 'age' => 30, 'active' => true];
104+
$encoded = Toon::encode($data);
105+
106+
// With options
107+
$options = new EncodeOptions(
108+
indent: 4,
109+
delimiter: "\t",
110+
lengthMarker: '#'
111+
);
112+
$encoded = Toon::encode($data, $options);
113+
114+
// Decoding
115+
$decoded = Toon::decode($encoded);
116+
```
117+
118+
The facade is automatically registered via package discovery as `Toon`, so you can also use it without importing:
119+
120+
```php
121+
$encoded = \Toon::encode(['key' => 'value']);
122+
$decoded = \Toon::decode($encoded);
123+
```
124+
125+
### Using the Core Library Directly
126+
127+
You can also use the core library directly:
128+
129+
```php
130+
use RobertGDev\Toon\Toon;
131+
132+
$encoded = Toon::encode(['name' => 'Ada']);
133+
$decoded = Toon::decode($encoded);
134+
```
135+
136+
For detailed API documentation, see the [robertgdev/php-toon](https://github.com/robertgdev/php-toon) package.
137+
138+
## Artisan Command
139+
140+
The package includes an Artisan command for converting between JSON and TOON formats:
141+
142+
```bash
143+
# Encode JSON to TOON
144+
php artisan toon:convert input.json --output=output.toon
145+
146+
# Decode TOON to JSON
147+
php artisan toon:convert input.toon --output=output.json
148+
149+
# Auto-detect mode based on file extension
150+
php artisan toon:convert data.json # Encodes to TOON
151+
php artisan toon:convert data.toon # Decodes to JSON
152+
153+
# Print to stdout instead of file
154+
php artisan toon:convert input.json
155+
156+
# Use custom delimiter (tab or pipe)
157+
php artisan toon:convert input.json --delimiter="\t"
158+
php artisan toon:convert input.json --delimiter="|"
159+
160+
# Use length marker
161+
php artisan toon:convert input.json --length-marker
162+
163+
# Show token statistics
164+
php artisan toon:convert input.json --stats
165+
166+
# Custom indentation
167+
php artisan toon:convert input.json --indent=4
168+
169+
# Disable strict mode for decoding
170+
php artisan toon:convert input.toon --no-strict
171+
```
172+
173+
### Command Options
174+
175+
- `input` - Input file path (required)
176+
- `--o|output` - Output file path (prints to stdout if not specified)
177+
- `--e|encode` - Force encode mode (auto-detected by default)
178+
- `--d|decode` - Force decode mode (auto-detected by default)
179+
- `--delimiter` - Delimiter for arrays: comma (,), tab (\t), or pipe (|)
180+
- `--indent` - Indentation size (default: 2)
181+
- `--length-marker` - Use length marker (#) for arrays
182+
- `--strict` - Enable strict mode for decoding (default: true)
183+
- `--no-strict` - Disable strict mode for decoding
184+
- `--stats` - Show token statistics
185+
186+
## Features
187+
188+
- **Laravel Facade**: Use `Toon::encode()` and `Toon::decode()` anywhere in your Laravel app
189+
- **Artisan Command**: Convert files between JSON and TOON formats via CLI
190+
- **Auto-Registration**: Service provider and facade automatically registered via package discovery
191+
- **Service Container**: Toon class registered as a singleton in Laravel's container
192+
- **File Operations**: Read and write TOON files with ease
193+
- **Token Statistics**: Estimate token savings when converting to TOON
194+
195+
## Package Structure
196+
197+
This package is a thin Laravel integration layer. The core TOON functionality is provided by the `robertgdev/php-toon` package, which is a standalone PHP library.
198+
199+
### What's in this package:
200+
- [`ToonServiceProvider`](src/ToonServiceProvider.php) - Registers the service and command
201+
- [`Toon` Facade](src/Facades/Toon.php) - Laravel facade for easy access
202+
- [`ToonCommand`](src/Console/ToonCommand.php) - Artisan command for file conversion
203+
- Configuration file with Laravel integration
204+
- Comprehensive integration test suite (38 tests covering all features)
205+
206+
### What's in the core package:
207+
- All encoding/decoding logic
208+
- Type definitions and options
209+
- Core TOON parser and serializer
210+
211+
See [robertgdev/php-toon](https://github.com/robertgdev/php-toon) for the core library documentation.
212+
213+
## Requirements
214+
215+
- PHP 8.2+
216+
- Laravel 10.x or 11.x or 12.x
217+
- robertgdev/php-toon (automatically installed)
218+
219+
## Testing
220+
221+
Run the test suite with:
222+
223+
```bash
224+
vendor/bin/pest
225+
```
226+
227+
The package includes 38 comprehensive tests covering:
228+
- Artisan command functionality (15 tests)
229+
- Configuration and service provider features (10 tests)
230+
- Laravel integration and facade functionality (13 tests)
231+
232+
## License
233+
234+
MIT License

composer.json

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
{
2+
"name": "robertgdev/laravel-toon",
3+
"description": "Laravel integration for TOON - A human-readable data serialization format",
4+
"type": "library",
5+
"license": "MIT",
6+
"authors": [
7+
{
8+
"name": "TOON Contributors"
9+
}
10+
],
11+
"require": {
12+
"php": "^8.2",
13+
"illuminate/console": ">10",
14+
"illuminate/support": ">10",
15+
"robertgdev/php-toon": "^1.0"
16+
},
17+
"require-dev": {
18+
"pestphp/pest": "^4.0",
19+
"phpunit/phpunit": "^12.0",
20+
"orchestra/testbench": "^9.0"
21+
},
22+
"autoload": {
23+
"psr-4": {
24+
"RobertGDev\\LaravelToon\\": "src/"
25+
}
26+
},
27+
"autoload-dev": {
28+
"psr-4": {
29+
"RobertGDev\\LaravelToon\\Tests\\": "tests/"
30+
}
31+
},
32+
"scripts": {
33+
"test": "pest"
34+
},
35+
"config": {
36+
"sort-packages": true,
37+
"allow-plugins": {
38+
"pestphp/pest-plugin": true
39+
}
40+
},
41+
"extra": {
42+
"laravel": {
43+
"providers": [
44+
"RobertGDev\\LaravelToon\\ToonServiceProvider"
45+
],
46+
"aliases": {
47+
"Toon": "RobertGDev\\LaravelToon\\Facades\\Toon"
48+
}
49+
}
50+
},
51+
"minimum-stability": "stable",
52+
"prefer-stable": true
53+
}

config/toon.php

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
return [
4+
5+
/*
6+
|--------------------------------------------------------------------------
7+
| Encoding Options
8+
|--------------------------------------------------------------------------
9+
|
10+
| These options control how data is encoded to TOON format.
11+
|
12+
*/
13+
14+
'encode' => [
15+
// Number of spaces per indentation level
16+
'indent' => env('TOON_ENCODE_INDENT', 2),
17+
18+
// Delimiter character for arrays and tabular data
19+
'delimiter' => env('TOON_ENCODE_DELIMITER', ','),
20+
21+
// Length marker prefix (false or '#')
22+
'lengthMarker' => env('TOON_ENCODE_LENGTH_MARKER', false),
23+
],
24+
25+
/*
26+
|--------------------------------------------------------------------------
27+
| Decoding Options
28+
|--------------------------------------------------------------------------
29+
|
30+
| These options control how TOON format is decoded back to PHP data.
31+
|
32+
*/
33+
34+
'decode' => [
35+
// Number of spaces per indentation level
36+
'indent' => env('TOON_DECODE_INDENT', 2),
37+
38+
// Enforce strict validation of array lengths and indentation
39+
'strict' => env('TOON_DECODE_STRICT', true),
40+
41+
// Decode objects as StdClass instances instead of arrays
42+
'objectsAsStdClass' => env('TOON_DECODE_OBJECTS_AS_STDCLASS', false),
43+
],
44+
45+
];

phpunit.xml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/11.0/phpunit.xsd"
4+
bootstrap="vendor/autoload.php"
5+
colors="true"
6+
cacheDirectory=".phpunit.cache"
7+
>
8+
<testsuites>
9+
<testsuite name="Package Test Suite">
10+
<directory>./tests</directory>
11+
</testsuite>
12+
</testsuites>
13+
<source>
14+
<include>
15+
<directory>./src</directory>
16+
</include>
17+
</source>
18+
</phpunit>

0 commit comments

Comments
 (0)