diff --git a/src/Command/Pf2eGenerateCommand.php b/src/Command/Pf2eGenerateCommand.php new file mode 100644 index 0000000..0179296 --- /dev/null +++ b/src/Command/Pf2eGenerateCommand.php @@ -0,0 +1,136 @@ +addOption("revision", null, InputOption::VALUE_REQUIRED, "Load index of specific revision. Default value - latest revision.") + ->addOption('type', null, InputOption::VALUE_REQUIRED, 'Specify types to import schema') + ->addOption('all', null, InputOption::VALUE_NONE, 'Generate schema for all types') + ; + } + + private int $revision; + + /** + * @var string[] + */ + private array $types; + + /** + * @var array + */ + private array $schemas = []; + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $io = new SymfonyStyle($input, $output); + + $this->revision = $input->getOption('revision') ?? $this->connectionService->getRevision(); + + if ($input->getOption('all')) { + $this->types = []; + } + else if (is_null($input->getOption('type'))) { +// $io->error("You must specify --type=[TYPE] or --all"); +// return Command::INVALID; + $this->types = ["class"]; + } + else { + $this->types = explode(",", $input->getOption('type')); + } + + $typePages = $this->loadTypePages(); + + if ($input->getOption('all')) { + $this->types = array_keys($typePages); + } + + $pages = []; + + foreach ($this->types as $type) { + $pages = [...$pages, ...$typePages[$type]]; + $this->schemas[$type] = new Schema(); + } + + $pages = array_unique($pages); + + foreach ($pages as $page) { + $this->loadPage($page); + } + + foreach ($this->schemas as $type => $schema) { + $io->write("{$type}"); + $io->writeln(str_replace(PHP_EOL, PHP_EOL.str_repeat(" ", 4), PHP_EOL.$schema)); + $io->writeln(""); + } + + $io->success('You have a new command! Now make it your own! Pass --help to see your options.'); + + return Command::SUCCESS; + } + + protected function loadTypePages(): array + { + $typePages = []; + + $index = $this->connectionService->loadCurrentIndex($this->revision); + + foreach ($index as $page => $elements) { + foreach ($elements as $element) { + preg_match("#^([a-z\\-]+?)-(\\d.*)$#", $element, $matches); + $type = $matches[1]; + + if (!empty($targetType) && !in_array($type, $this->types)) { + continue; + } + + if (!isset($typePages[$type])) { + $typePages[$type] = []; + } + + if (!in_array($page, $typePages[$type])) { + $typePages[$type][] = $page; + } + } + } + + return $typePages; + } + + protected function loadPage(string $page): void { + $data = $this->connectionService->load("/json-data/{$page}.json"); + + foreach ($data as $element) { + $type = $element["category"]; + + if (!in_array($type, $this->types)) { + continue; + } + + $this->schemas[$type]->process($element); + } + } +} \ No newline at end of file diff --git a/src/Command/Pf2eIndexCommand.php b/src/Command/Pf2eIndexCommand.php new file mode 100644 index 0000000..56855a7 --- /dev/null +++ b/src/Command/Pf2eIndexCommand.php @@ -0,0 +1,34 @@ +connectionService->getRevision(); + $io->success("Current Archives of Nethys' revision number is {$revision}"); + + return Command::SUCCESS; + } +} diff --git a/src/Command/Pf2eSchemaGenerator/Schema.php b/src/Command/Pf2eSchemaGenerator/Schema.php new file mode 100644 index 0000000..5aaffe2 --- /dev/null +++ b/src/Command/Pf2eSchemaGenerator/Schema.php @@ -0,0 +1,97 @@ +items as $key => $item) { + if (!in_array($key, $keys)) { + $item->disableRequired(); + } + } + + foreach ($keys as $key) { + if (!array_key_exists($key, $this->items)) { + $this->items[$key] = new SchemaItem($key); + } + + $this->items[$key]->adapt($element[$key]); + } + } + + + public static function resolveType(mixed $value): string + { + $type = gettype($value); + + if ($type === "array") { + if (empty($value)) { + return "empty[]"; + } + + if (array_is_list($value)) { + $elementType = ""; + foreach ($value as $element) { + $elementType = self::mergeTypes($elementType, self::resolveType($element)); + } + + if (str_contains("|", $elementType)) { + return "mixed[]"; + } + + return $elementType . "[]"; + } + + return "object"; + } + + return $type; + } + + public static function mergeTypes(string $typeList, string $newType): string { + if (empty($typeList)) { + return $newType; + } + + else if (str_contains("|", $typeList)) { + $types = explode("|", $typeList); + + if (!in_array($newType, $types, true)) { + $types[] = $newType; + } + + return implode("|", $types); + } + else if ($newType !== $typeList) { + return $typeList . "|" . $newType; + } + + return $typeList; + } + + public function __toString(): string + { + $rows = []; + + foreach ($this->items as $item) { + $rows = [...$rows, ...$item->strings()]; + } + + return implode(PHP_EOL, $rows); + } + +} \ No newline at end of file diff --git a/src/Command/Pf2eSchemaGenerator/SchemaItem.php b/src/Command/Pf2eSchemaGenerator/SchemaItem.php new file mode 100644 index 0000000..c4fde13 --- /dev/null +++ b/src/Command/Pf2eSchemaGenerator/SchemaItem.php @@ -0,0 +1,68 @@ +name = $name; + } + + public function disableRequired(): void + { + $this->required = false; + } + + public function adapt(mixed $value): void + { + $type = Schema::resolveType($value); + + $this->type = Schema::mergeTypes($this->type, $type); + + if (str_contains($this->type, "[]") && !is_null($this->elements)) { + foreach ($value as $element) { + if (!in_array($element, $this->elements)) { + $this->elements[] = $element; + } + } + + if (count($this->elements) > 20) { + $this->elements = null; + } + } + } + + public function strings(): array { + $name = $this->name; + + if (!$this->required) { + $name = "[{$name}]"; + } + + $rows = [ + sprintf("%s: %s", $name, $this->type), + ]; + + if (!empty($this->elements)) { + sort($this->elements); + $elements = []; + foreach ($this->elements as $element) { + $elements[] = is_string($element) ? "\"{$element}\"" : strval($element); + } + $rows[] = " Elements: [ " . implode(", ", $elements) . " ]"; + } + + return $rows; + } + + +} \ No newline at end of file diff --git a/src/Command/Pf2eTypesCommand.php b/src/Command/Pf2eTypesCommand.php new file mode 100644 index 0000000..d3381fc --- /dev/null +++ b/src/Command/Pf2eTypesCommand.php @@ -0,0 +1,95 @@ +addOption("revision", null, InputOption::VALUE_REQUIRED, "Load index of specific revision. Default value - latest revision.") + ->addOption("full", null, InputOption::VALUE_NONE, "Display all columns") + ; + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $io = new SymfonyStyle($input, $output); + + $revision = $input->getOption("revision"); + $full = $input->getOption("full") ?? false; + + $index = $this->connectionService->loadCurrentIndex($revision); + + $types = []; + + foreach ($index as $page) { + foreach ($page as $element) { + preg_match("#^([a-z\\-]+?)-(\\d.*)$#", $element, $matches); + $type = $matches[1]; + $id = $matches[2]; + + if (!isset($types[$type])) { + $types[$type] = [ + "name" => $type, + "first_id" => $element, + "count" => 0, + "templates" => [] + ]; + } + + $template = preg_replace("#\\d+#", "%d", $id); + + if (!in_array($template, $types[$type]['templates'])) { + $types[$type]['templates'][] = $template; + } + + $types[$type]['count']++; + } + } + + if ($full) { + $table = []; + + foreach ($types as $key => $type) { + $table[] = [ + $key, + sprintf("%s", + $type["first_id"], + $type["first_id"] + ), + $type['count'], + implode(", ", $type['templates']), + ]; + } + + $io->table(["Type name", "First Id", "Amount", "ID templates"], $table); + } + else { + foreach ($types as $type) { + $io->writeln($type['name']); + } + } + + return Command::SUCCESS; + } +} diff --git a/src/Entity/Pf2e/Item.php b/src/Entity/Item.php similarity index 94% rename from src/Entity/Pf2e/Item.php rename to src/Entity/Item.php index 6cef8eb..b86ec84 100644 --- a/src/Entity/Pf2e/Item.php +++ b/src/Entity/Item.php @@ -1,8 +1,8 @@ client->request('GET', $url); + + return json_decode($response->getContent(false), true); + } + + public function getRevision(): int + { + if (null === $this->revision) { + $data = $this->load("/aon/_doc/feat-1"); + $this->revision = intval(str_replace("aon", "", $data["_index"])); + } + + return $this->revision; + } + + public function loadCurrentIndex(int $revision = null): array + { + if ($revision === null) { + $revision = $this->getRevision(); + } + + $data = $this->load("/json-data/aon{$revision}-index.json"); + + return $data; + } +} \ No newline at end of file