diff --git a/app/Console/SubCmd/GolangCmd/GenerateStructCmd.php b/app/Console/SubCmd/GolangCmd/GenerateStructCmd.php index 944a2c3..048c65d 100644 --- a/app/Console/SubCmd/GolangCmd/GenerateStructCmd.php +++ b/app/Console/SubCmd/GolangCmd/GenerateStructCmd.php @@ -10,7 +10,7 @@ use Inhere\Kite\Kite; use Inhere\Kite\Lib\Defines\DataField\JsonField; use Inhere\Kite\Lib\Defines\ProgramLang; -use Inhere\Kite\Lib\Generate\GenCodeFactory; +use Inhere\Kite\Lib\Generate\DTOGenerator; use Inhere\Kite\Lib\Parser\Text\Json5ItemParser; use Inhere\Kite\Lib\Parser\Text\TextItemParser; use Inhere\Kite\Lib\Parser\Text\TextParser; @@ -144,6 +144,7 @@ protected function execute(Input $input, Output $output): void } $config = array_merge($config, array_filter([ + 'lang' => $lang, 'tplDir' => $tplDir, 'tplFile' => $tplFile, 'parser' => get_class($itemParser), @@ -151,7 +152,7 @@ protected function execute(Input $input, Output $output): void $output->aList($config); - $gen = GenCodeFactory::create($lang) + $gen = DTOGenerator::new() ->setClassName($fs->getOpt('name')) ->addTplVar('genMark', $input->getFullScript(true)) ->configThis($config) diff --git a/app/Console/SubCmd/JavaCmd/GenerateDTOCmd.php b/app/Console/SubCmd/JavaCmd/GenerateDTOCmd.php index 3b2de24..4da94db 100644 --- a/app/Console/SubCmd/JavaCmd/GenerateDTOCmd.php +++ b/app/Console/SubCmd/JavaCmd/GenerateDTOCmd.php @@ -7,13 +7,8 @@ use Inhere\Console\IO\Output; use Inhere\Kite\Console\Component\ContentsAutoReader; use Inhere\Kite\Console\Component\ContentsAutoWriter; -use Inhere\Kite\Lib\Defines\FieldMeta; -use Inhere\Kite\Lib\Generate\Json5Data; -use Inhere\Kite\Lib\Parser\DBTable; -use Inhere\Kite\Lib\Parser\MySQL\TableField; -use Inhere\Kite\Lib\Parser\Text\TextParser; -use Inhere\Kite\Lib\Stream\ListStream; -use InvalidArgumentException; +use Inhere\Kite\Lib\Defines\ProgramLang; +use Inhere\Kite\Lib\Generate\DTOGenerator; /** * Class GenerateDTOCmd @@ -86,49 +81,11 @@ protected function execute(Input $input, Output $output): void } } - $subObjects = []; - switch ($srcType) { - case 'txt': - $p = TextParser::new($source)->parse(); - $mp = ListStream::new($p->getData()) - ->filter(function (array $item) { - return count($item) >= 3; - }) - ->eachToMap(function (array $item) { - return [ - $item[0], - FieldMeta::new([ - 'name' => $item[0], - 'type' => $item[1], - 'comment' => $item[2], - ]) - ]; - }); - - break; - case 'sql': - $dbt = DBTable::fromSchemeSQL($source); - $mp = $dbt->getObjFields(TableField::class); - - break; - case 'md': - $dbt = DBTable::fromMdTable($source); - $mp = $dbt->getObjFields(TableField::class); - - // $mp = MapStream::new($dbt->getFields()) - // ->eachToMap($this->dbTableInfoHandler()); - break; - case 'json': - case 'json5': - $jd = Json5Data::new()->loadFrom($source); - $mp = $jd->getFields(); - - $subObjects = $jd->getSubObjects(); - $this->config->load($jd->getSettings()); - break; - default: - throw new InvalidArgumentException("unsupported source type: $srcType"); - } + $lang = ProgramLang::JAVA; + $dg = DTOGenerator::new() + ->setLang($lang) + ->setSource($source) + ->setSourceType($srcType); $mainName = $this->config->getString('name'); if (!$mainName) { @@ -141,7 +98,7 @@ protected function execute(Input $input, Output $output): void $this->config->set('className', ucfirst($mainName)); $tplFile = $fs->getOpt('tpl-file'); - $tplFile = $tplFile ?: "@custom/template/java-code-tpl/req_dto.tpl"; + $tplFile = $tplFile ?: "@custom/template/$lang-code-tpl/req_dto.tpl"; $outFile = $fs->getOpt('output'); if ($fs->getOpt('show-info')) { @@ -151,7 +108,7 @@ protected function execute(Input $input, Output $output): void $ctx['outFile'] = $outFile; $output->mList([ 'info' => $ctx, - 'fields' => $mp, + 'fields' => $dg->getFields(), ]); return; } diff --git a/app/Lib/Defines/ClassMeta.php b/app/Lib/Defines/ClassMeta.php index 02af1f6..47d11eb 100644 --- a/app/Lib/Defines/ClassMeta.php +++ b/app/Lib/Defines/ClassMeta.php @@ -79,6 +79,27 @@ class ClassMeta extends BaseObject */ public array $children = []; + /** + * @param array $fields + * + * @return void + */ + public function setFields(array $fields): void + { + if (!$fields) { + return; + } + + $first = reset($fields); + if ($first instanceof FieldMeta) { + $this->fields = $fields; + } else { + foreach ($fields as $field) { + $this->fields[] = FieldMeta::new($field); + } + } + } + /** * @param string $comment * diff --git a/app/Lib/Generate/AbstractGenCode.php b/app/Lib/Generate/AbstractGenCode.php index 99d0b31..9654bee 100644 --- a/app/Lib/Generate/AbstractGenCode.php +++ b/app/Lib/Generate/AbstractGenCode.php @@ -3,9 +3,14 @@ namespace Inhere\Kite\Lib\Generate; use Inhere\Kite\Helper\KiteUtil; +use Inhere\Kite\Lib\Defines\ClassMeta; use Inhere\Kite\Lib\Defines\DataField\JsonField; +use Inhere\Kite\Lib\Defines\FieldMeta; +use Inhere\Kite\Lib\Defines\ProgramLang; use InvalidArgumentException; +use PhpPkg\EasyTpl\EasyTemplate; use Toolkit\FsUtil\File; +use Toolkit\Stdlib\Helper\Assert; use Toolkit\Stdlib\Obj; use Toolkit\Stdlib\OS; use Toolkit\Stdlib\Str; @@ -16,19 +21,31 @@ use function strpos; /** - * class AbstractGenCode + * class AbstractGenCode - abstract code generator * * @author inhere */ abstract class AbstractGenCode { + use Obj\Traits\QuickInitTrait; + + /** + * @var bool + */ + protected bool $prepared = false; + + /** + * @var EasyTemplate|null template engine + */ + private ?EasyTemplate $tplEng = null; + /** * @var string */ public string $tplDir = ''; /** - * @var string + * @var string template file name or path. */ public string $tplFile = ''; @@ -37,6 +54,11 @@ abstract class AbstractGenCode */ protected $pathResolver; + /** + * @var string target language for generate. + */ + protected string $lang = ProgramLang::PHP; + /** * contexts vars for render template * @@ -50,24 +72,77 @@ abstract class AbstractGenCode public string $className = 'YourClass'; /** - * @var array + * fields metadata list + * + * @var array */ protected array $fields = []; /** - * @var bool + * Sub objects/classes. + * + * ### Structure + * + * ```json + * { + * 'className1' => { + * 'fieldName' => FieldMeta, + * }, + * } + * ``` + * + * @var array> */ - protected bool $prepared = false; + protected array $subObjects = []; + + + public static function fromClassMeta(ClassMeta $meta, array $config = []): self + { + $self = new static($config); + + return $self->loadClassMeta($meta); + } /** - * @return string + * @param array $config */ - public function getLang(): string + public function __construct(array $config = []) + { + if ($config) { + $this->configThis($config); + } + } + + /** + * @param ClassMeta $meta + * + * @return $this + */ + public function loadClassMeta(ClassMeta $meta): static + { + Assert::isFalse($this->prepared, 'this object has prepared.'); + $this->prepared = true; + $this->className = $meta->name; + + $this->fields = $meta->fields; + foreach ($meta->children as $child) { + $this->subObjects[$child->name] = $child->fields; + } + + return $this; + } + + /** + * @param array $meta array structure please refer the {@see ClassMeta} + * + * @return $this + */ + public function loadArrayMeta(array $meta): static { - return 'java'; + return $this->loadClassMeta(ClassMeta::new($meta)); } - public function prepareContext(): void + protected function prepareContext(): void { // defaults $this->addContexts([ @@ -76,11 +151,15 @@ public function prepareContext(): void 'modulePkg' => 'MODULE_PKG', 'pkgName' => 'PKG_NAME', 'subPkg' => 'SUB_PKG', + // common info + 'user' => OS::getUserName(), + 'date' => date('Y-m-d'), + 'datetime' => date('Y-m-d H:i:s'), ]); } /** - * @return AbstractJsonToCode + * @return static */ public function prepare(): self { @@ -88,39 +167,65 @@ public function prepare(): self return $this; } - $this->prepared = true; $this->prepareContext(); + $this->prepared = true; return $this; } /** - * @return string + * @return EasyTemplate */ - public function generate(): string + public function getTplEng(): EasyTemplate { - $this->prepare(); + if (!$this->tplEng) { + $this->tplEng = KiteUtil::newTplEngine([ + 'tplDir' => $this->tplDir, + ]); + } - return $this->renderTplText(); + return $this->tplEng; } /** * @return string */ - protected function renderTplText(): string + public function generate(): string { - $tplText = $this->readSourceFromFile(); - $settings = array_merge([ + $this->prepare(); + + $tplFile = $this->findTplFile(); + $contexts = array_merge([ 'lang' => $this->getLang(), 'user' => OS::getUserName(), 'date' => date('Y-m-d'), ], $this->contexts); - $settings['fields'] = $this->fields; + $contexts['fields'] = $this->fields; + + return $this->getTplEng()->renderFile($tplFile, $contexts); + } + + public function renderSubObjects(): array + { + $strMap = []; + $tplFile = $this->findTplFile(); + + if ($this->subObjects) { + $ctx['withHead'] = false; + $ctx['classSfx'] = ''; + $ctx['classMark'] = 'static '; - return KiteUtil::newTplEngine([ - 'tplDir' => $this->tplDir, - ])->renderString($tplText, $settings); + foreach ($this->subObjects as $name => $fields) { + $ctx['mainName'] = $name; + $ctx['className'] = ucfirst($name); + $ctx['fields'] = $fields; + + $strMap[$name] = $this->getTplEng()->renderFile($tplFile, $ctx); + } + } + + return $strMap; } /** @@ -145,18 +250,17 @@ public function generateTo(string $outFile): bool public function configThis(array $config): self { Obj::init($this, $config); - return $this; } /** * @return string */ - protected function readSourceFromFile(): string + protected function findTplFile(): string { $tplFile = $this->tplFile; if (!$tplFile) { - return ''; + throw new InvalidArgumentException('Generate: template file is empty'); } $tplFile = $this->resolvePath($tplFile); @@ -167,13 +271,14 @@ protected function readSourceFromFile(): string $dirFile = File::joinPath($tplDir, $tplFile); if (!is_file($dirFile)) { - throw new InvalidArgumentException("No such file: $tplFile"); + throw new InvalidArgumentException("No such template file: $tplFile"); } $tplFile = $dirFile; } - return File::readAll($tplFile); + $this->tplFile = $tplFile; + return $tplFile; } /** @@ -215,7 +320,7 @@ public function resolvePath(string $filePath): string /** * @param string $name - * @param mixed $value + * @param mixed $value * * @return $this */ @@ -228,7 +333,7 @@ public function addTplVar(string $name, mixed $value): self /** * @param array $contexts * - * @return AbstractJsonToCode + * @return static */ public function addContexts(array $contexts): self { @@ -243,7 +348,7 @@ public function addContexts(array $contexts): self /** * @param array $contexts * - * @return AbstractJsonToCode + * @return static */ public function setContexts(array $contexts): self { @@ -276,7 +381,7 @@ public function setFields(array $fields): self /** * @param callable $pathResolver * - * @return AbstractJsonToCode + * @return static */ public function setPathResolver(callable $pathResolver): self { @@ -287,7 +392,7 @@ public function setPathResolver(callable $pathResolver): self /** * @param string $className * - * @return AbstractJsonToCode + * @return static */ public function setClassName(string $className): self { @@ -313,4 +418,25 @@ public function getContexts(): array { return $this->contexts; } + + public function getLang(): string + { + return $this->lang; + } + + /** + * @param string $lang + * + * @return $this + */ + public function setLang(string $lang): static + { + $this->lang = $lang; + return $this; + } + + public function setSubObjects(array $subObjects): void + { + $this->subObjects = $subObjects; + } } diff --git a/app/Lib/Generate/AbstractJsonToCode.php b/app/Lib/Generate/AbstractJsonToCode.php deleted file mode 100644 index 635276f..0000000 --- a/app/Lib/Generate/AbstractJsonToCode.php +++ /dev/null @@ -1,66 +0,0 @@ - - */ - // protected array $fields = []; - - /** - * @return AbstractJsonToCode - */ - public function prepare(): self - { - if ($this->isPrepared()) { - return $this; - } - - parent::prepare(); - - $json = $this->source; - if (!$json = trim($json)) { - throw new InvalidArgumentException('empty source json(5) data for generate'); - } - - $jd = Json5Data::new()->loadFrom($json); - - $this->fields = $jd->getFields(); - $this->setContexts($jd->getSettings()); - - return $this; - } - - /** - * @param string $source - * - * @return self - */ - public function setSource(string $source): self - { - $this->source = $source; - return $this; - } - -} diff --git a/app/Lib/Generate/DTOGenerator.php b/app/Lib/Generate/DTOGenerator.php new file mode 100644 index 0000000..9b0d3cf --- /dev/null +++ b/app/Lib/Generate/DTOGenerator.php @@ -0,0 +1,163 @@ + $lang]); + } + + public function prepare(): self + { + if ($this->isPrepared()) { + return $this; + } + + parent::prepare(); + + $this->loadAndParseSource(); + + return $this; + } + + protected function loadAndParseSource(): void + { + $source = $this->source; + if (!$source = trim($source)) { + throw new InvalidArgumentException('empty source contents for parse and generate'); + } + + $srcType = $this->sourceType; + if (!$srcType) { + $srcType = 'txt'; + if (stripos($source, 'create table')) { + $srcType = 'sql'; + } elseif (str_contains($source, '--|--')) { + $srcType = 'md'; + } + } + + $subObjects = []; + switch ($srcType) { + case 'txt': + case 'text': + $p = TextParser::new($source)->parse(); + $mp = ListStream::new($p->getData()) + ->filter(function (array $item) { + return count($item) >= 3; + }) + ->eachToMap(function (array $item) { + return [ + $item[0], + FieldMeta::new([ + 'name' => $item[0], + 'type' => $item[1], + 'comment' => $item[2], + ]), + ]; + }); + + break; + case 'sql': + $dbt = DBTable::fromSchemeSQL($source); + $mp = $dbt->getObjFields(TableField::class); + + break; + case 'md': + $dbt = DBTable::fromMdTable($source); + $mp = $dbt->getObjFields(TableField::class); + + // $mp = MapStream::new($dbt->getFields()) + // ->eachToMap($this->dbTableInfoHandler()); + break; + case 'json': + case 'json5': + $jd = Json5Data::new()->loadFrom($source); + $mp = $jd->getFields(); + + $this->subObjects = $jd->getSubObjects(); + $this->setContexts($jd->getSettings()); + break; + case 'yml': + case 'yaml': + $data = ConfigUtil::parseYamlString($source); + Assert::arrayHasKey($data, 'fields'); + $mp = ListStream::new($data['fields'])->eachToMap(function (array $item) { + return [ + $item['name'], + FieldMeta::new($item), + ]; + }); + + break; + default: + throw new InvalidArgumentException("unsupported source type: $srcType"); + } + + // $mp: ['name' => FieldMeta] + $this->fields = $mp; + } + + /** + * @return string + */ + public function getSource(): string + { + return $this->source; + } + + /** + * @param string $source + * + * @return self + */ + public function setSource(string $source): static + { + $this->source = $source; + return $this; + } + + public function getSourceType(): string + { + return $this->sourceType; + } + + public function setSourceType(string $sourceType): static + { + $this->sourceType = $sourceType; + return $this; + } + +} diff --git a/app/Lib/Generate/GenCodeFactory.php b/app/Lib/Generate/GenCodeFactory.php deleted file mode 100644 index 0d16701..0000000 --- a/app/Lib/Generate/GenCodeFactory.php +++ /dev/null @@ -1,42 +0,0 @@ - GenPhpClass::class, - self::LANG_JAVA => GenJavaClass::class, - self::LANG_GO => GenGoStruct::class, - ]; - - /** - * @param string $lang - * - * @return AbstractGenCode - */ - public static function create(string $lang = self::LANG_PHP): AbstractGenCode - { - if (!isset(self::LANG2HANDLER_CLASS[$lang])) { - throw new InvalidArgumentException('invalid lang: ' . $lang); - } - - $class = self::LANG2HANDLER_CLASS[$lang]; - - return new $class; - } -} diff --git a/app/Lib/Generate/Handler/GenGoStruct.php b/app/Lib/Generate/Handler/GenGoStruct.php deleted file mode 100644 index 8c5f409..0000000 --- a/app/Lib/Generate/Handler/GenGoStruct.php +++ /dev/null @@ -1,21 +0,0 @@ -> */ private array $subObjects = []; @@ -46,6 +57,7 @@ class Json5Data extends AbstractObj * @param string $json * * @return $this + * @throws SyntaxError */ public function loadFrom(string $json): self { @@ -155,7 +167,7 @@ public function getFields(): array } /** - * @return Json\JsonField[][] + * @return array> */ public function getSubObjects(): array { diff --git a/app/Lib/Generate/JsonToCode.php b/app/Lib/Generate/JsonToCode.php index 0e316be..9b2af61 100644 --- a/app/Lib/Generate/JsonToCode.php +++ b/app/Lib/Generate/JsonToCode.php @@ -8,20 +8,11 @@ class JsonToCode { /** - * @param string $type + * @param string $lang * - * @return AbstractJsonToCode */ - public static function create(string $type = 'php'): AbstractJsonToCode + public static function create(string $lang): DTOGenerator { - if ($type === JsonToPHPClass::TYPE) { - return new JsonToPHPClass(); - } - - if ($type === JsonToGoStruct::TYPE) { - return new JsonToGoStruct(); - } - - return new JsonToJavaClass(); + return new DTOGenerator(['lang' => $lang]); } } diff --git a/app/Lib/Generate/JsonToGoStruct.php b/app/Lib/Generate/JsonToGoStruct.php deleted file mode 100644 index 124b991..0000000 --- a/app/Lib/Generate/JsonToGoStruct.php +++ /dev/null @@ -1,19 +0,0 @@ - map: {field: object,} + */ + public function getObjFieldList(string $fieldClass): array + { + $map = []; + foreach ($this->fields as $field => $info) { + $map[$field] = new $fieldClass($info); + } + return $map; + } + /** * @return array */ diff --git a/test/unittest/Lib/Parser/TextParserTest.php b/test/unittest/Lib/Parser/TextParserTest.php index d3f5834..e8bf304 100644 --- a/test/unittest/Lib/Parser/TextParserTest.php +++ b/test/unittest/Lib/Parser/TextParserTest.php @@ -4,8 +4,6 @@ use Inhere\Kite\Lib\Parser\Text\TextParser; use Inhere\KiteTest\BaseKiteTestCase; -use function preg_split; -use function vdump; /** * class TextParserTest