Skip to content

Commit

Permalink
DateTime support in structures
Browse files Browse the repository at this point in the history
  • Loading branch information
ddebowczyk committed May 19, 2024
1 parent ddc67c3 commit c61e52b
Show file tree
Hide file tree
Showing 13 changed files with 375 additions and 101 deletions.
9 changes: 8 additions & 1 deletion src/Deserialization/Symfony/Deserializer.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,14 @@ public function fromArray(array $data, string $dataType): mixed {
}

public function toArray(object $object): array {
return $this->serializer->normalize($object, 'array');
$normalized = $this->serializer->normalize($object, 'array');
return match(true) {
($normalized === null) => [],
is_array($normalized) => $normalized,
is_object($normalized) => (array) $normalized,
is_string($normalized) => dd($normalized),
default => $normalized
};
}

protected function defaultTypeExtractor() : PropertyInfoExtractor {
Expand Down
5 changes: 4 additions & 1 deletion src/Extras/Structure/Field.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,22 @@

class Field {
use Traits\HandlesFieldDefinitions;
use Traits\HandlesFieldDescription;
use Traits\HandlesFieldInfo;
use Traits\HandlesFieldValidation;
use Traits\HandlesFieldSchemas;
use Traits\HandlesFieldValue;
use Traits\HandlesFieldExamples;
use Traits\HandlesOptionality;

public function __construct(
string $name = '',
string $description = '',
string $instructions = '',
TypeDetails $typeDetails = null,
) {
$this->name = $name;
$this->description = $description;
$this->instructions = $instructions;
$this->typeDetails = $typeDetails;
}
}
15 changes: 9 additions & 6 deletions src/Extras/Structure/Structure.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,26 @@ class Structure implements CanProvideSchema, CanDeserializeSelf, CanValidateSelf
use Traits\ProvidesSchema;
use Traits\HandlesDeserialization;
use Traits\HandlesSerialization;
use Traits\HandlesTransformation;
use Traits\HandlesFieldAccess;
use Traits\HandlesDescription;
use Traits\HandlesStructureInfo;

public function __construct() {
$this->schemaFactory = new SchemaFactory(false);
$this->typeDetailsFactory = new TypeDetailsFactory();
$this->deserializer = new Deserializer();
}

static public function define(string $name, array|callable $fields, string $description = '') : self {
static public function define(
string $name,
array|callable $fields,
string $description = '',
string $instructions = '',
) : self {
$structure = new Structure();
$structure->name = $name;
$structure->description = $description;
$structure->instructions = $instructions;

if (is_callable($fields)) {
$fields = $fields($structure);
Expand All @@ -44,8 +51,4 @@ static public function define(string $name, array|callable $fields, string $desc
}
return $structure;
}

public function transform(): mixed {
return $this;
}
}
28 changes: 23 additions & 5 deletions src/Extras/Structure/Traits/HandlesDeserialization.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
use Cognesy\Instructor\Extras\Structure\Structure;
use Cognesy\Instructor\Schema\Data\TypeDetails;
use Cognesy\Instructor\Utils\Json;
use DateTime;
use DateTimeImmutable;
use Exception;

trait HandlesDeserialization
{
Expand Down Expand Up @@ -40,19 +43,34 @@ private function deserializeField(Structure $structure, Field $field, string $na
$type = $field->typeDetails();
$value = match(true) {
($type === null) => throw new \Exception("Undefined field `$name` found in JSON data."),
($type->type === TypeDetails::PHP_ENUM) => $fieldData,
($type->type === TypeDetails::PHP_ARRAY) => $this->deserializeArray($structure->get($name), $field, $fieldData),
($type->type === TypeDetails::PHP_ENUM) => ($type->class)::from($fieldData),
($type->type === TypeDetails::PHP_ARRAY) => $this->deserializeArray($field, $fieldData),
($type->class === null) => $fieldData,
($type->class === Structure::class) => $structure->get($name)->fromArray($fieldData),
($type->class === DateTime::class) => new DateTime($fieldData),
($type->class === DateTimeImmutable::class) => new DateTimeImmutable($fieldData),
default => $this->deserializer->fromArray($fieldData, $type->class),
};
return $value;
}

private function deserializeArray(Structure $structure, Field $field, mixed $fieldData) : mixed {
private function deserializeArray(Field $field, mixed $fieldData) : mixed {
$values = [];
foreach($fieldData as $index => $itemData) {
$values[] = $this->deserializeField($structure, $field, $index, $itemData);
$typeDetails = $field->nestedTypeDetails();
foreach($fieldData as $itemData) {
// if $type is scalar, just assign the value
// if $type is object, then deserialize each value as object
// if $type is enum, then deserialize each value as enum
// if $type is array, then deserialize each value as array
$values[] = match(true) {
($typeDetails->type === TypeDetails::PHP_ENUM) => ($typeDetails->class)::from($itemData),
($typeDetails->type === TypeDetails::PHP_ARRAY) => throw new Exception('Nested arrays are not supported.'),
($typeDetails->class === null) => $itemData,
($typeDetails->class === Structure::class) => $field->value->fromArray($itemData),
($typeDetails->class === DateTime::class) => new DateTime($itemData),
($typeDetails->class === DateTimeImmutable::class) => new DateTimeImmutable($itemData),
default => $this->deserializer->fromArray($itemData, $typeDetails->class),
};
}
return $values;
}
Expand Down
41 changes: 24 additions & 17 deletions src/Extras/Structure/Traits/HandlesFieldDefinitions.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,58 +6,65 @@
use Cognesy\Instructor\Extras\Structure\Structure;
use Cognesy\Instructor\Schema\Data\TypeDetails;
use Cognesy\Instructor\Schema\Factories\TypeDetailsFactory;
use DateTime;

trait HandlesFieldDefinitions
{
private TypeDetailsFactory $typeDetailsFactory;

static public function int(string $name, string $description = '') : self {
static public function int(string $name, string $description = '', string $instructions = '') : self {
$factory = new TypeDetailsFactory();
$type = $factory->scalarType(TypeDetails::PHP_INT);
return new Field($name, $description, $type);
return new Field($name, $description, $instructions, $type);
}

static public function string(string $name, string $description = '') : self {
static public function string(string $name, string $description = '', string $instructions = '') : self {
$factory = new TypeDetailsFactory();
$type = $factory->scalarType(TypeDetails::PHP_STRING);
return new Field($name, $description, $type);
return new Field($name, $description, $instructions, $type);
}

static public function float(string $name, string $description = '') : self {
static public function float(string $name, string $description = '', string $instructions = '') : self {
$factory = new TypeDetailsFactory();
$type = $factory->scalarType(TypeDetails::PHP_FLOAT);
return new Field($name, $description, $type);
return new Field($name, $description, $instructions, $type);
}

static public function bool(string $name, string $description = '') : self {
static public function bool(string $name, string $description = '', string $instructions = '') : self {
$factory = new TypeDetailsFactory();
$type = $factory->scalarType(TypeDetails::PHP_BOOL);
return new Field($name, $description, $type);
return new Field($name, $description, $instructions, $type);
}

static public function enum(string $name, string $enumClass, string $description = '') : self {
static public function enum(string $name, string $enumClass, string $description = '', string $instructions = '') : self {
$factory = new TypeDetailsFactory();
$type = $factory->enumType($enumClass);
return new Field($name, $description, $type);
return new Field($name, $description, $instructions, $type);
}

static public function object(string $name, string $class, string $description = '') : self {
static public function object(string $name, string $class, string $description = '', string $instructions = '') : self {
$factory = new TypeDetailsFactory();
$type = $factory->objectType($class);
return new Field($name, $description, $type);
return new Field($name, $description, $instructions, $type);
}

static public function structure(string $name, array|callable $fields, string $description = '') : self {
static public function datetime(string $name, string $description = '', string $instructions = '') : self {
$factory = new TypeDetailsFactory();
$type = $factory->objectType(DateTime::class);
return new Field($name, $description, $instructions, $type);
}

static public function structure(string $name, array|callable $fields, string $description = '', string $instructions = '') : self {
$factory = new TypeDetailsFactory();
$type = $factory->objectType(Structure::class);
$result = new Field($name, $description, $type);
$result->value = Structure::define($name, $fields, $description);
$result = new Field($name, $description, $instructions, $type);
$result->value = Structure::define($name, $fields, $description, $instructions);
return $result;
}

static public function array(string $name, string $itemType, string $description = '') : self {
static public function array(string $name, string $itemType, string $description = '', string $instructions = '') : self {
$factory = new TypeDetailsFactory();
$type = $factory->arrayType($itemType);
return new Field($name, $description, $type);
return new Field($name, $description, $instructions, $type);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@

use Cognesy\Instructor\Extras\Structure\Structure;

trait HandlesFieldDescription
trait HandlesFieldInfo
{
private string $name;
private string $description;
private bool $required = true;
private string $name = '';
private string $description = '';
private string $instructions = '';

public function withName(string $name) : self {
$this->name = $name;
Expand All @@ -18,6 +18,10 @@ public function withName(string $name) : self {
return $this;
}

public function name() : string {
return $this->name ?? '';
}

public function withDescription(string $description) : self {
$this->description = $description;
if ($this->typeDetails->class === Structure::class) {
Expand All @@ -26,21 +30,26 @@ public function withDescription(string $description) : self {
return $this;
}

public function name() : string {
return $this->name ?? '';
}

public function description() : string {
return $this->description ?? '';
}

public function required(bool $isRequired = false) : self {
$this->required = $isRequired;
public function withInstructions(string $instructions) : self {
$this->instructions = $instructions;
if ($this->typeDetails->class === Structure::class) {
$this->value->withInstructions($instructions);
}
return $this;
}

public function optional(bool $isOptional = true) : self {
$this->required = !$isOptional;
return $this;
public function instructions() : string {
return $this->instructions ?? '';
}

public function info() : string {
return implode('; ', array_filter([
$this->description(),
$this->instructions(),
]));
}
}
84 changes: 43 additions & 41 deletions src/Extras/Structure/Traits/HandlesFieldSchemas.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

namespace Cognesy\Instructor\Extras\Structure\Traits;

use Cognesy\Instructor\Extras\Structure\Field;
use Cognesy\Instructor\Schema\Data\Schema\ArraySchema;
use Cognesy\Instructor\Schema\Data\Schema\EnumSchema;
use Cognesy\Instructor\Schema\Data\Schema\ObjectSchema;
Expand All @@ -13,51 +12,54 @@
trait HandlesFieldSchemas
{
private TypeDetails $typeDetails;
private Field $nestedField;

public function typeDetails() : TypeDetails {
return $this->typeDetails;
}

private function makeSchema() : Schema {
return match($this->typeDetails->type) {
TypeDetails::PHP_OBJECT => $this->objectSchema(),
TypeDetails::PHP_ENUM => $this->enumSchema(),
TypeDetails::PHP_ARRAY => $this->arraySchema(),
default => $this->scalarSchema(),
};
public function nestedTypeDetails() : TypeDetails {
return $this->typeDetails()->nestedType;
}

private function objectSchema() : ObjectSchema {
return new ObjectSchema(
type: $this->typeDetails,
name: $this->name,
description: $this->description,
);
}

private function enumSchema() : Schema {
return new EnumSchema(
type: $this->typeDetails,
name: $this->name,
description: $this->description,
);
}

private function arraySchema() : Schema {
return new ArraySchema(
type: $this->typeDetails,
name: $this->name,
description: $this->description,
nestedItemSchema: $this->nestedField->schema(), // TODO: ERROR!
);
}

private function scalarSchema() : Schema {
return new ScalarSchema(
type: $this->typeDetails,
name: $this->name,
description: $this->description,
);
}
// private function makeSchema() : Schema {
// return match($this->typeDetails->type) {
// TypeDetails::PHP_OBJECT => $this->objectSchema(),
// TypeDetails::PHP_ENUM => $this->enumSchema(),
// TypeDetails::PHP_ARRAY => $this->arraySchema(),
// default => $this->scalarSchema(),
// };
// }
//
// private function objectSchema() : ObjectSchema {
// return new ObjectSchema(
// type: $this->typeDetails,
// name: $this->name,
// description: $this->description,
// );
// }
//
// private function enumSchema() : Schema {
// return new EnumSchema(
// type: $this->typeDetails,
// name: $this->name,
// description: $this->description,
// );
// }
//
// private function arraySchema() : Schema {
// return new ArraySchema(
// type: $this->typeDetails,
// name: $this->name,
// description: $this->description,
// nestedItemSchema: null, // $this->nestedTypeDetails(),
// );
// }
//
// private function scalarSchema() : Schema {
// return new ScalarSchema(
// type: $this->typeDetails,
// name: $this->name,
// description: $this->description,
// );
// }
}
18 changes: 18 additions & 0 deletions src/Extras/Structure/Traits/HandlesOptionality.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

namespace Cognesy\Instructor\Extras\Structure\Traits;

trait HandlesOptionality
{
private bool $required = true;

public function required(bool $isRequired = false) : self {
$this->required = $isRequired;
return $this;
}

public function optional(bool $isOptional = true) : self {
$this->required = !$isOptional;
return $this;
}
}
Loading

0 comments on commit c61e52b

Please sign in to comment.