Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improvement converted json type #468

Open
filippeb opened this issue Apr 18, 2024 · 5 comments
Open

Improvement converted json type #468

filippeb opened this issue Apr 18, 2024 · 5 comments
Assignees
Labels
Type: Enhancement Most issues will probably ask for additions or changes.

Comments

@filippeb
Copy link

filippeb commented Apr 18, 2024

Is your feature request related to a problem?
Not a problem, a proposal for a convenience addition to \Psl\Json\typed conversion.

Describe the solution you'd like
A converted type function for json data (see below snippet) usable on convert shapes.

Describe alternatives you've considered
Can be achieved with current Psl but is a bit more verbose

Snippet

/**
 * @template D
 * @template O
 *
 * @param TypeInterface<D>     $decoded   decoded json type
 * @param TypeInterface<O>     $into
 * @param null|(Closure(D): O) $converter argument is the decoded json value of type $from
 *
 * @return TypeInterface<O>
 */
function convertedJson(TypeInterface $decoded, TypeInterface $into, ?\Closure $converter = null): TypeInterface
{
    return new Internal\ConvertedType(
        string(),
        $into,
        null === $converter
            ? static fn(string $json): mixed => $into->assert(typed($json, $decoded)) // if the decoded json type is the same as the 'into' type
            : static fn(string $json): mixed => $converter(typed($json, $decoded))
    );
}
@filippeb filippeb added the Type: Enhancement Most issues will probably ask for additions or changes. label Apr 18, 2024
@filippeb
Copy link
Author

filippeb commented Apr 18, 2024

Some examples:

In short: Converting a list of string id's to a collection of id's

<?php
final readonly class Id
{
    public function __construct(public string $id)
    {
    }

    public static function type(): TypeInterface
    {
        return converted(
            string(),
            instance_of(__CLASS__),
            static fn(string $id) => new self($id)
        );
    }
}

final readonly class IdCollection
{
    public function __construct(
        public array $ids
    ) {
    }

    public static function type(): TypeInterface
    {
        return converted(
            vec(
                Id::type()
            ),
            instance_of(__CLASS__),
            static fn(array $ids) => new self($ids)
        );
    }
}

/** shape containing the full convert type */
shape([
    'key' => converted(
        string(),
        instance_of(IdCollection::class),
        static fn(string $json) => typed(
            $json,
            converted(
                vec(
                    string()
                ),
                instance_of(IdCollection::class),
                static fn(array $ids): IdCollection => new IdCollection(
                    map(
                        $ids,
                        static fn(string $id): Id => new Id($id)
                    )
                )
            )
        )
    ),
])->coerce(
    ['key' => '["id-one","id-two"]']
);

/** same with abstracted types (already possible with current Psl) */
shape([
    'key' => converted(
        string(),
        instance_of(IdCollection::class),
        static fn(string $json): IdCollection => typed(
            $json,
            IdCollection::type()
        )
    ),
])->coerce(
    ['key' => '["id-one","id-two"]']
);

/** convertedJson with converter Closure */
shape([
    'key' => convertedJson(
        IdCollection::type(),
        instance_of(IdCollection::class),
        static fn(IdCollection $collection): IdCollection => $collection
    ),
])->coerce(
    ['key' => '["id-one","id-two"]']
);

/** convertedJson without converter */
$shape = shape([
    'key' => convertedJson(
        IdCollection::type(),
        instance_of(IdCollection::class),
    ),
])->coerce(
    ['key' => '["id-one","id-two"]']
);

@filippeb
Copy link
Author

Penny for your thoughts on this

@veewee
Copy link
Collaborator

veewee commented Apr 19, 2024

Something like this could work:

namespace Psl\Type;

use Psl\Json;

/**
 * @psalm-pure
 *
 * @template T
 *
 * @param TypeInterface<T> $value_type
 *
 * @return TypeInterface<T>
 */
function json(TypeInterface $value_type): TypeInterface
{
    return converted(
        non_empty_string(),
        $value_type,
        static fn (string $json): mixed => Json\typed($json, $value_type)
    );
}

Where you could do:

Type\Json(IdCollection::type())

Having an additional callback would be a bridge too far for me. You could always do that in your IdCollection::type() which already contains a converted type.

@azjezz WDYT?
In the future we could introduce similar converted shortcuts like these, e.g. for Type\date_time($format, $timezone) without having to specifying a dedicated internal type for them?

@filippeb
Copy link
Author

@veewee that would indeed do the job.

@azjezz
Copy link
Owner

azjezz commented Apr 22, 2024

@azjezz WDYT?

I like the idea, but maybe a different name than Type\json? something like Type\from_json() that indicate that the value would come from json?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Type: Enhancement Most issues will probably ask for additions or changes.
Projects
None yet
Development

No branches or pull requests

3 participants