Skip to content

Commit

Permalink
added-go-support
Browse files Browse the repository at this point in the history
  • Loading branch information
KoNekoD committed Apr 12, 2024
1 parent 8823846 commit c0863f0
Show file tree
Hide file tree
Showing 15 changed files with 1,384 additions and 0 deletions.
10 changes: 10 additions & 0 deletions src/OutputGenerator/Go/GoGeneratorOptions.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

declare(strict_types=1);

namespace Riverwaysoft\PhpConverter\OutputGenerator\Go;

class GoGeneratorOptions
{
public function __construct() {}
}
189 changes: 189 additions & 0 deletions src/OutputGenerator/Go/GoOutputGenerator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
<?php

declare(strict_types=1);

namespace Riverwaysoft\PhpConverter\OutputGenerator\Go;

use Exception;
use Riverwaysoft\PhpConverter\Ast\ConverterResult;
use Riverwaysoft\PhpConverter\Dto\DtoClassProperty;
use Riverwaysoft\PhpConverter\Dto\DtoEnumProperty;
use Riverwaysoft\PhpConverter\Dto\DtoList;
use Riverwaysoft\PhpConverter\Dto\DtoType;
use Riverwaysoft\PhpConverter\Dto\ExpressionType;
use Riverwaysoft\PhpConverter\OutputGenerator\OutputGeneratorInterface;
use Riverwaysoft\PhpConverter\OutputGenerator\PropertyNameGeneratorInterface;
use Riverwaysoft\PhpConverter\OutputWriter\OutputFile;
use Riverwaysoft\PhpConverter\OutputWriter\OutputProcessor\OutputFilesProcessor;
use Riverwaysoft\PhpConverter\OutputWriter\OutputProcessor\PrependAutogeneratedNoticeFileProcessor;
use Riverwaysoft\PhpConverter\OutputWriter\OutputWriterInterface;
use function sprintf;

class GoOutputGenerator
implements OutputGeneratorInterface
{
private GoGeneratorOptions $options;

Check failure on line 25 in src/OutputGenerator/Go/GoOutputGenerator.php

View workflow job for this annotation

GitHub Actions / PHP 8.0 Test on ubuntu latest

Property Riverwaysoft\PhpConverter\OutputGenerator\Go\GoOutputGenerator::$options is never read, only written.

Check failure on line 25 in src/OutputGenerator/Go/GoOutputGenerator.php

View workflow job for this annotation

GitHub Actions / PHP 8.1 Test on ubuntu latest

Property Riverwaysoft\PhpConverter\OutputGenerator\Go\GoOutputGenerator::$options is never read, only written.

/** @var PropertyNameGeneratorInterface[] */
private array $propertyNameGenerators;

/** @param PropertyNameGeneratorInterface[] $propertyNameGenerators */
public function __construct(
private OutputWriterInterface $outputWriter,
private GoTypeResolver $typeResolver,
private ?OutputFilesProcessor $outputFilesProcessor = null,
array $propertyNameGenerators = [],
?GoGeneratorOptions $options = null,
) {
$this->options = $options ?? new GoGeneratorOptions();
$this->outputFilesProcessor = $this->outputFilesProcessor ?? new OutputFilesProcessor(
[
new PrependAutogeneratedNoticeFileProcessor(
text: <<<TEXT
// THE FILE WAS AUTOGENERATED USING PHP-CONVERTER. PLEASE DO NOT EDIT IT!
// THE FILE WAS AUTOGENERATED USING PHP-CONVERTER. PLEASE DO NOT EDIT IT!
// THE FILE WAS AUTOGENERATED USING PHP-CONVERTER. PLEASE DO NOT EDIT IT!
package gen
TEXT
),
]
);
$this->propertyNameGenerators = $propertyNameGenerators ?: [
new GoPropertyNameGenerator(),
];
}

/** @return OutputFile[] */
public function generate(ConverterResult $converterResult): array
{
$this->outputWriter->reset();

$dtoList = $converterResult->dtoList;
foreach ($dtoList->getList() as $dto) {
$result = $this->convertToGoType($dto, $dtoList);
$this->outputWriter->writeType($result, $dto);
}

$files = $this->outputWriter->getTypes();

return $this->outputFilesProcessor->process($files);
}

/** @var string[] */
private array $usedConstantsStore = [];

private function convertToGoType(DtoType $dto, DtoList $dtoList): string
{
if ($dto->getExpressionType()->equals(ExpressionType::class())) {
$typeName = $dto->getName();
$convertedProperties = '';

/** @param DtoClassProperty[] $properties */
$properties = $dto->getProperties();
foreach ($properties as $property) {
$convertedProperties .= sprintf(
"\n %s %s",
$this
->getPropertyNameGenerator($dto)
->generate($property),
$this->typeResolver->getTypeFromPhp(
$property->getType(),
$dto,
$dtoList
)
);
}

return sprintf(
"type %s struct {%s\n}",
$typeName,
$convertedProperties
);
}
if ($dto->getExpressionType()->isAnyEnum()) {
$firstEnumProperty = $dto->getProperties()[0] ?? null;
if ($firstEnumProperty === null) {
throw new Exception('Enum must have at least one property');
}

$firstEnumPropertyValue = $firstEnumProperty->getValue();
if (is_int($firstEnumPropertyValue)) {
$type = 'int';
} elseif (is_string($firstEnumPropertyValue)) {
$type = 'string';
} else {
$type = 'any';
}

return sprintf(
"type %s %s \n\nconst(%s\n)",
$dto->getName(),
$type,
$this->convertEnumToTypeScriptEnumProperties(
$dto->getProperties(),
$dto->getName(),
)
);
}
throw new Exception(
'Unknown expression type '.
$dto->getExpressionType()->jsonSerialize()
);
}

private function getPropertyNameGenerator(
DtoType $dto
): PropertyNameGeneratorInterface {
if (count($this->propertyNameGenerators) === 1) {
return $this->propertyNameGenerators[0];
}

foreach ($this->propertyNameGenerators as $propertyNameGenerator) {
if ($propertyNameGenerator->supports($dto)) {
return $propertyNameGenerator;
}
}

throw new Exception(
'Property name generator not found for type '.$dto->getName()
);
}

/** @param DtoEnumProperty[] $properties */
private function convertEnumToTypeScriptEnumProperties(
array $properties,
string $enumName
): string {
$string = '';

foreach ($properties as $property) {
$propertyValue = $property->isNumeric()
? $property->getValue()
: sprintf("\"%s\"", $property->getValue());

$constName = $property->getName();
if (in_array($constName, $this->usedConstantsStore)) {
$constName .= $enumName;
}

if (array_key_exists($constName, $this->usedConstantsStore)) {
throw new Exception(
'Please rename constant '.$constName
);
}
$this->usedConstantsStore[] = $constName;

$string .= sprintf(
"\n %s %s = %s",
$constName,
$enumName,
$propertyValue
);
}

return $string;
}
}
22 changes: 22 additions & 0 deletions src/OutputGenerator/Go/GoPropertyNameGenerator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

namespace Riverwaysoft\PhpConverter\OutputGenerator\Go;

use Riverwaysoft\PhpConverter\Dto\DtoClassProperty;
use Riverwaysoft\PhpConverter\Dto\DtoType;
use Riverwaysoft\PhpConverter\OutputGenerator\PropertyNameGeneratorInterface;

class GoPropertyNameGenerator implements PropertyNameGeneratorInterface
{
public function supports(DtoType $dto): bool
{
return true;
}

public function generate(DtoClassProperty $property): string
{
return ucfirst($property->getName());
}
}
130 changes: 130 additions & 0 deletions src/OutputGenerator/Go/GoTypeResolver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
<?php

declare(strict_types=1);

namespace Riverwaysoft\PhpConverter\OutputGenerator\Go;

use Exception;
use Riverwaysoft\PhpConverter\Dto\DtoList;
use Riverwaysoft\PhpConverter\Dto\DtoType;
use Riverwaysoft\PhpConverter\Dto\PhpType\PhpBaseType;
use Riverwaysoft\PhpConverter\Dto\PhpType\PhpListType;
use Riverwaysoft\PhpConverter\Dto\PhpType\PhpOptionalType;
use Riverwaysoft\PhpConverter\Dto\PhpType\PhpTypeInterface;
use Riverwaysoft\PhpConverter\Dto\PhpType\PhpUnionType;
use Riverwaysoft\PhpConverter\Dto\PhpType\PhpUnknownType;
use Riverwaysoft\PhpConverter\OutputGenerator\UnknownTypeResolver\UnknownTypeResolverInterface;
use Riverwaysoft\PhpConverter\OutputGenerator\UnsupportedTypeException;

class GoTypeResolver
{
/** @param UnknownTypeResolverInterface[] $unknownTypeResolvers */
public function __construct(private array $unknownTypeResolvers = []) {}

public function getTypeFromPhp(
PhpTypeInterface $type,
?DtoType $dto,
DtoList $dtoList
): string {
if ($type instanceof PhpUnionType) {
$fn = fn(PhpTypeInterface $type) => $this->getTypeFromPhp(
$type,
$dto,
$dtoList
);
$types = array_map($fn, $type->getTypes());

// Two args, one of them is null
if (count($types) === 2 && in_array('null', $types)) {
$types = array_diff($types, ['null']);

return "*$types[0]";
}

return 'any';
}

if ($type instanceof PhpListType) {
$listType = $this->getTypeFromPhp($type->getType(), $dto, $dtoList);

if ($type->getType() instanceof PhpUnionType) {
return sprintf('[](%s)', $listType);
}

return sprintf('[]%s', $listType);
}

if ($type instanceof PhpOptionalType) {
return $this->getTypeFromPhp($type->getType(), $dto, $dtoList);
}

if ($type instanceof PhpBaseType) {
return match (true) {
$type->equalsTo(PhpBaseType::int()) => 'int',
$type->equalsTo(PhpBaseType::float()) => 'float64',
$type->equalsTo(PhpBaseType::string()) => 'string',
$type->equalsTo(PhpBaseType::bool()) => 'bool',
$type->equalsTo(PhpBaseType::mixed()),
$type->equalsTo(PhpBaseType::object()) => 'any',
$type->equalsTo(PhpBaseType::array()),
$type->equalsTo(PhpBaseType::iterable()) => '[]any',
$type->equalsTo(PhpBaseType::null()) => 'null',
$type->equalsTo(PhpBaseType::self()) => $dto->getName(),
default => throw new Exception(
sprintf("Unknown base PHP type: %s", $type->jsonSerialize())
)
};
}

if ($type instanceof PhpUnknownType && $dto?->hasGeneric($type)) {
return 'any';
}

if (
$type instanceof PhpUnknownType &&
$type->hasGenerics() && (
$dtoList->hasDtoWithType($type->getName()) ||
!empty(
$type->getContext()[PhpUnknownType::GENERIC_IGNORE_NO_RESOLVER]
)
)
) {
$result = $type->getName();

$generics = array_map(
fn(PhpTypeInterface $innerGeneric) => $this->getTypeFromPhp(
$innerGeneric,
$dto,
$dtoList,
),
$type->getGenerics()
);

$result .= sprintf("<%s>", join(', ', $generics));

return $result;
}

/** @var PhpUnknownType $type */
$result = $this->handleUnknownType($type, $dto, $dtoList);
if ($result instanceof PhpTypeInterface) {
return $this->getTypeFromPhp($result, $dto, $dtoList);
}

return $result;
}

private function handleUnknownType(
PhpUnknownType $type,
?DtoType $dto,
DtoList $dtoList
): string|PhpTypeInterface {
foreach ($this->unknownTypeResolvers as $unknownTypeResolver) {
if ($unknownTypeResolver->supports($type, $dto, $dtoList)) {
return $unknownTypeResolver->resolve($type, $dto, $dtoList);
}
}

throw UnsupportedTypeException::forType($type, $dto?->getName() ?? '');
}
}
Loading

0 comments on commit c0863f0

Please sign in to comment.