Add UI layout XML automation
This commit is contained in:
@@ -46,6 +46,15 @@ target_compile_features(pp_project_options INTERFACE cxx_std_23)
|
|||||||
add_library(pp_project_warnings INTERFACE)
|
add_library(pp_project_warnings INTERFACE)
|
||||||
pp_configure_project_warnings(pp_project_warnings)
|
pp_configure_project_warnings(pp_project_warnings)
|
||||||
|
|
||||||
|
add_library(pp_vendor_tinyxml2 STATIC
|
||||||
|
libs/tinyxml2/tinyxml2.cpp)
|
||||||
|
target_include_directories(pp_vendor_tinyxml2
|
||||||
|
SYSTEM PUBLIC
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/libs/tinyxml2")
|
||||||
|
target_link_libraries(pp_vendor_tinyxml2
|
||||||
|
PUBLIC
|
||||||
|
pp_project_options)
|
||||||
|
|
||||||
add_custom_target(panopainter_modernization_status
|
add_custom_target(panopainter_modernization_status
|
||||||
COMMAND "${CMAKE_COMMAND}" -E echo "PanoPainter modernization scaffold configured."
|
COMMAND "${CMAKE_COMMAND}" -E echo "PanoPainter modernization scaffold configured."
|
||||||
COMMAND "${CMAKE_COMMAND}" -E echo "Roadmap: docs/modernization/roadmap.md"
|
COMMAND "${CMAKE_COMMAND}" -E echo "Roadmap: docs/modernization/roadmap.md"
|
||||||
@@ -129,7 +138,8 @@ target_link_libraries(pp_paint_renderer
|
|||||||
pp_project_warnings)
|
pp_project_warnings)
|
||||||
|
|
||||||
add_library(pp_ui_core STATIC
|
add_library(pp_ui_core STATIC
|
||||||
src/ui_core/layout_value.cpp)
|
src/ui_core/layout_value.cpp
|
||||||
|
src/ui_core/layout_xml.cpp)
|
||||||
target_include_directories(pp_ui_core
|
target_include_directories(pp_ui_core
|
||||||
PUBLIC
|
PUBLIC
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/src")
|
"${CMAKE_CURRENT_SOURCE_DIR}/src")
|
||||||
@@ -138,6 +148,7 @@ target_link_libraries(pp_ui_core
|
|||||||
pp_foundation
|
pp_foundation
|
||||||
pp_project_options
|
pp_project_options
|
||||||
PRIVATE
|
PRIVATE
|
||||||
|
pp_vendor_tinyxml2
|
||||||
pp_project_warnings)
|
pp_project_warnings)
|
||||||
|
|
||||||
if(PP_BUILD_TOOLS)
|
if(PP_BUILD_TOOLS)
|
||||||
|
|||||||
@@ -77,7 +77,8 @@ Known local toolchain state:
|
|||||||
- Android arm64 headless configure/build passes through root CMake and the
|
- Android arm64 headless configure/build passes through root CMake and the
|
||||||
`platform-build` automation wrapper for `pp_foundation`, `pp_assets`,
|
`platform-build` automation wrapper for `pp_foundation`, `pp_assets`,
|
||||||
`pp_paint`, `pp_document`, `pp_renderer_api`, `pp_paint_renderer`,
|
`pp_paint`, `pp_document`, `pp_renderer_api`, `pp_paint_renderer`,
|
||||||
`pp_ui_core`, `pano_cli`, and their current headless test binaries.
|
`pp_ui_core`, `pano_cli`, and their current headless test binaries,
|
||||||
|
including layout XML parse coverage.
|
||||||
- `vcpkg` is not on PATH yet; see DEBT-0007.
|
- `vcpkg` is not on PATH yet; see DEBT-0007.
|
||||||
|
|
||||||
Known warnings after the current CMake app build:
|
Known warnings after the current CMake app build:
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ agent or engineer to remove them without reconstructing context from chat.
|
|||||||
| DEBT-0009 | Open | Modernization | Android root CMake validation currently builds headless targets only, not APK/package variants | Platform app entrypoints still live in legacy Gradle/CMake projects and need Phase 6 alignment | `powershell -ExecutionPolicy Bypass -File scripts\automation\platform-build.ps1 -Presets android-arm64` | Android standard, Quest, and Focus/Wave package targets consume shared component targets and have package smoke commands |
|
| DEBT-0009 | Open | Modernization | Android root CMake validation currently builds headless targets only, not APK/package variants | Platform app entrypoints still live in legacy Gradle/CMake projects and need Phase 6 alignment | `powershell -ExecutionPolicy Bypass -File scripts\automation\platform-build.ps1 -Presets android-arm64` | Android standard, Quest, and Focus/Wave package targets consume shared component targets and have package smoke commands |
|
||||||
| DEBT-0010 | Open | Modernization | `pp_document` is a pure layer/frame/document model but is not yet wired to legacy `Canvas`, PPI load/save, selection masks, or undo/redo | Keep extraction incremental while preserving app behavior | `ctest --preset desktop-fast --build-config Debug`; `pano_cli create-document --width 64 --height 32 --layers 2` | Legacy document behavior is represented by `pp_document` tests and the app consumes it through a boundary/facade |
|
| DEBT-0010 | Open | Modernization | `pp_document` is a pure layer/frame/document model but is not yet wired to legacy `Canvas`, PPI load/save, selection masks, or undo/redo | Keep extraction incremental while preserving app behavior | `ctest --preset desktop-fast --build-config Debug`; `pano_cli create-document --width 64 --height 32 --layers 2` | Legacy document behavior is represented by `pp_document` tests and the app consumes it through a boundary/facade |
|
||||||
| DEBT-0011 | Open | Modernization | `package-smoke` validates the Windows CMake app artifact only, not AppX/APK/Apple/WebGL package outputs | Platform package targets are not migrated to root CMake yet | `powershell -ExecutionPolicy Bypass -File scripts\automation\package-smoke.ps1 -Preset windows-msvc-default -Configuration Debug` | Package-smoke covers Windows AppX, Android APK variants, Apple bundles, and WebGL output where local toolchains are present |
|
| DEBT-0011 | Open | Modernization | `package-smoke` validates the Windows CMake app artifact only, not AppX/APK/Apple/WebGL package outputs | Platform package targets are not migrated to root CMake yet | `powershell -ExecutionPolicy Bypass -File scripts\automation\package-smoke.ps1 -Preset windows-msvc-default -Configuration Debug` | Package-smoke covers Windows AppX, Android APK variants, Apple bundles, and WebGL output where local toolchains are present |
|
||||||
|
| DEBT-0012 | Open | Modernization | `pp_vendor_tinyxml2` compiles the retained vendored tinyxml2 copy for `pp_ui_core` layout parsing | vcpkg is not validated yet, but layout parsing needs a structured XML parser now | `ctest --preset desktop-fast --build-config Debug`; `powershell -ExecutionPolicy Bypass -File scripts\automation\platform-build.ps1 -Presets android-arm64` | Replace with vcpkg tinyxml2 target once desktop and mobile triplets are validated |
|
||||||
|
|
||||||
## Closed Debt
|
## Closed Debt
|
||||||
|
|
||||||
|
|||||||
@@ -311,9 +311,10 @@ and layer/frame invariant tests. `pp_renderer_api` has started with renderer-neu
|
|||||||
texture/readback descriptors and validation tests. `pp_paint_renderer` has
|
texture/readback descriptors and validation tests. `pp_paint_renderer` has
|
||||||
started with deterministic CPU layer compositing over renderer extents using
|
started with deterministic CPU layer compositing over renderer extents using
|
||||||
the paint blend reference. `pp_ui_core` has started with XML-layout-facing
|
the paint blend reference. `pp_ui_core` has started with XML-layout-facing
|
||||||
length parsing and invalid input tests. Continue expanding document behavior
|
length parsing, tinyxml-backed layout XML parsing, and invalid input tests.
|
||||||
toward legacy Canvas parity and then port OpenGL classes behind the renderer
|
`pano_cli parse-layout` now exercises that path. Continue expanding document
|
||||||
boundary.
|
behavior toward legacy Canvas parity and then port OpenGL classes behind the
|
||||||
|
renderer boundary.
|
||||||
|
|
||||||
Implementation tasks:
|
Implementation tasks:
|
||||||
|
|
||||||
@@ -517,7 +518,7 @@ Last verified on 2026-06-01:
|
|||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
cmake --preset windows-msvc-default
|
cmake --preset windows-msvc-default
|
||||||
cmake --build --preset windows-msvc-default --config Debug --target pp_foundation_binary_stream_tests pp_foundation_parse_tests pp_foundation_trace_tests pp_assets_image_format_tests pp_paint_blend_tests pp_document_tests pp_renderer_api_tests pp_paint_renderer_compositor_tests pp_ui_core_layout_value_tests pano_cli PanoPainter
|
cmake --build --preset windows-msvc-default --config Debug --target pp_foundation_binary_stream_tests pp_foundation_parse_tests pp_foundation_trace_tests pp_assets_image_format_tests pp_paint_blend_tests pp_document_tests pp_renderer_api_tests pp_paint_renderer_compositor_tests pp_ui_core_layout_value_tests pp_ui_core_layout_xml_tests pano_cli PanoPainter
|
||||||
ctest --preset desktop-fast --build-config Debug
|
ctest --preset desktop-fast --build-config Debug
|
||||||
powershell -ExecutionPolicy Bypass -File scripts\automation\test.ps1 -Preset desktop-fast -Configuration Debug
|
powershell -ExecutionPolicy Bypass -File scripts\automation\test.ps1 -Preset desktop-fast -Configuration Debug
|
||||||
powershell -ExecutionPolicy Bypass -File scripts\automation\build.ps1 -Preset windows-msvc-default -Configuration Debug -Target pano_cli
|
powershell -ExecutionPolicy Bypass -File scripts\automation\build.ps1 -Preset windows-msvc-default -Configuration Debug -Target pano_cli
|
||||||
@@ -537,9 +538,11 @@ Results:
|
|||||||
- `pp_renderer_api_tests` passed.
|
- `pp_renderer_api_tests` passed.
|
||||||
- `pp_paint_renderer_compositor_tests` passed.
|
- `pp_paint_renderer_compositor_tests` passed.
|
||||||
- `pp_ui_core_layout_value_tests` passed.
|
- `pp_ui_core_layout_value_tests` passed.
|
||||||
|
- `pp_ui_core_layout_xml_tests` passed.
|
||||||
- `pano_cli_create_document_smoke` passed.
|
- `pano_cli_create_document_smoke` passed.
|
||||||
- `pano_cli_inspect_image_rejects_unsupported` passed as an expected failure
|
- `pano_cli_inspect_image_rejects_unsupported` passed as an expected failure
|
||||||
test.
|
test.
|
||||||
|
- `pano_cli_parse_layout_smoke` passed.
|
||||||
- `PanoPainter.exe` built through CMake at
|
- `PanoPainter.exe` built through CMake at
|
||||||
`out/build/windows-msvc-default/Debug/PanoPainter.exe`.
|
`out/build/windows-msvc-default/Debug/PanoPainter.exe`.
|
||||||
- PowerShell build/test automation wrappers return JSON summaries and passed
|
- PowerShell build/test automation wrappers return JSON summaries and passed
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
param(
|
param(
|
||||||
[string[]]$Presets = @("android-arm64"),
|
[string[]]$Presets = @("android-arm64"),
|
||||||
[string[]]$Targets = @("pp_foundation", "pp_assets", "pp_paint", "pp_document", "pp_renderer_api", "pp_paint_renderer", "pp_ui_core", "pano_cli", "pp_foundation_binary_stream_tests", "pp_foundation_parse_tests", "pp_foundation_trace_tests", "pp_assets_image_format_tests", "pp_paint_blend_tests", "pp_document_tests", "pp_renderer_api_tests", "pp_paint_renderer_compositor_tests", "pp_ui_core_layout_value_tests")
|
[string[]]$Targets = @("pp_foundation", "pp_assets", "pp_paint", "pp_document", "pp_renderer_api", "pp_paint_renderer", "pp_ui_core", "pano_cli", "pp_foundation_binary_stream_tests", "pp_foundation_parse_tests", "pp_foundation_trace_tests", "pp_assets_image_format_tests", "pp_paint_blend_tests", "pp_document_tests", "pp_renderer_api_tests", "pp_paint_renderer_compositor_tests", "pp_ui_core_layout_value_tests", "pp_ui_core_layout_xml_tests")
|
||||||
)
|
)
|
||||||
|
|
||||||
$ErrorActionPreference = "Stop"
|
$ErrorActionPreference = "Stop"
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ set -u
|
|||||||
|
|
||||||
preset="${1:-android-arm64}"
|
preset="${1:-android-arm64}"
|
||||||
shift || true
|
shift || true
|
||||||
targets="${*:-pp_foundation pp_assets pp_paint pp_document pp_renderer_api pp_paint_renderer pp_ui_core pano_cli pp_foundation_binary_stream_tests pp_foundation_parse_tests pp_foundation_trace_tests pp_assets_image_format_tests pp_paint_blend_tests pp_document_tests pp_renderer_api_tests pp_paint_renderer_compositor_tests pp_ui_core_layout_value_tests}"
|
targets="${*:-pp_foundation pp_assets pp_paint pp_document pp_renderer_api pp_paint_renderer pp_ui_core pano_cli pp_foundation_binary_stream_tests pp_foundation_parse_tests pp_foundation_trace_tests pp_assets_image_format_tests pp_paint_blend_tests pp_document_tests pp_renderer_api_tests pp_paint_renderer_compositor_tests pp_ui_core_layout_value_tests pp_ui_core_layout_xml_tests}"
|
||||||
start="$(date +%s)"
|
start="$(date +%s)"
|
||||||
|
|
||||||
cmake --preset "$preset"
|
cmake --preset "$preset"
|
||||||
|
|||||||
71
src/ui_core/layout_xml.cpp
Normal file
71
src/ui_core/layout_xml.cpp
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
#include "ui_core/layout_xml.h"
|
||||||
|
|
||||||
|
#include "ui_core/layout_value.h"
|
||||||
|
|
||||||
|
#include <tinyxml2.h>
|
||||||
|
|
||||||
|
namespace pp::ui {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
[[nodiscard]] pp::foundation::Status visit_element(const tinyxml2::XMLElement& element, LayoutParseSummary& summary)
|
||||||
|
{
|
||||||
|
++summary.node_count;
|
||||||
|
|
||||||
|
for (const char* name : { "width", "height" }) {
|
||||||
|
const char* value = element.Attribute(name);
|
||||||
|
if (value == nullptr) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto length = parse_layout_length(value);
|
||||||
|
if (!length) {
|
||||||
|
return length.status();
|
||||||
|
}
|
||||||
|
++summary.length_attribute_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const tinyxml2::XMLElement* child = element.FirstChildElement();
|
||||||
|
child != nullptr;
|
||||||
|
child = child->NextSiblingElement()) {
|
||||||
|
const auto status = visit_element(*child, summary);
|
||||||
|
if (!status.ok()) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pp::foundation::Status::success();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pp::foundation::Result<LayoutParseSummary> parse_layout_xml(std::string_view xml)
|
||||||
|
{
|
||||||
|
if (xml.empty()) {
|
||||||
|
return pp::foundation::Result<LayoutParseSummary>::failure(
|
||||||
|
pp::foundation::Status::invalid_argument("layout XML must not be empty"));
|
||||||
|
}
|
||||||
|
|
||||||
|
tinyxml2::XMLDocument document;
|
||||||
|
const auto error = document.Parse(xml.data(), xml.size());
|
||||||
|
if (error != tinyxml2::XML_SUCCESS) {
|
||||||
|
return pp::foundation::Result<LayoutParseSummary>::failure(
|
||||||
|
pp::foundation::Status::invalid_argument("layout XML could not be parsed"));
|
||||||
|
}
|
||||||
|
|
||||||
|
const tinyxml2::XMLElement* root = document.RootElement();
|
||||||
|
if (root == nullptr) {
|
||||||
|
return pp::foundation::Result<LayoutParseSummary>::failure(
|
||||||
|
pp::foundation::Status::invalid_argument("layout XML has no root element"));
|
||||||
|
}
|
||||||
|
|
||||||
|
LayoutParseSummary summary;
|
||||||
|
const auto status = visit_element(*root, summary);
|
||||||
|
if (!status.ok()) {
|
||||||
|
return pp::foundation::Result<LayoutParseSummary>::failure(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
return pp::foundation::Result<LayoutParseSummary>::success(summary);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
17
src/ui_core/layout_xml.h
Normal file
17
src/ui_core/layout_xml.h
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "foundation/result.h"
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
namespace pp::ui {
|
||||||
|
|
||||||
|
struct LayoutParseSummary {
|
||||||
|
std::size_t node_count = 0;
|
||||||
|
std::size_t length_attribute_count = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] pp::foundation::Result<LayoutParseSummary> parse_layout_xml(std::string_view xml);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -96,6 +96,16 @@ add_test(NAME pp_ui_core_layout_value_tests COMMAND pp_ui_core_layout_value_test
|
|||||||
set_tests_properties(pp_ui_core_layout_value_tests PROPERTIES
|
set_tests_properties(pp_ui_core_layout_value_tests PROPERTIES
|
||||||
LABELS "ui;desktop-fast")
|
LABELS "ui;desktop-fast")
|
||||||
|
|
||||||
|
add_executable(pp_ui_core_layout_xml_tests
|
||||||
|
ui_core/layout_xml_tests.cpp)
|
||||||
|
target_link_libraries(pp_ui_core_layout_xml_tests PRIVATE
|
||||||
|
pp_ui_core
|
||||||
|
pp_test_harness)
|
||||||
|
|
||||||
|
add_test(NAME pp_ui_core_layout_xml_tests COMMAND pp_ui_core_layout_xml_tests)
|
||||||
|
set_tests_properties(pp_ui_core_layout_xml_tests PROPERTIES
|
||||||
|
LABELS "ui;desktop-fast")
|
||||||
|
|
||||||
if(TARGET pano_cli)
|
if(TARGET pano_cli)
|
||||||
add_test(NAME pano_cli_create_document_smoke
|
add_test(NAME pano_cli_create_document_smoke
|
||||||
COMMAND pano_cli create-document --width 64 --height 32 --layers 2)
|
COMMAND pano_cli create-document --width 64 --height 32 --layers 2)
|
||||||
@@ -107,4 +117,9 @@ if(TARGET pano_cli)
|
|||||||
set_tests_properties(pano_cli_inspect_image_rejects_unsupported PROPERTIES
|
set_tests_properties(pano_cli_inspect_image_rejects_unsupported PROPERTIES
|
||||||
LABELS "assets;integration;desktop-fast"
|
LABELS "assets;integration;desktop-fast"
|
||||||
WILL_FAIL TRUE)
|
WILL_FAIL TRUE)
|
||||||
|
|
||||||
|
add_test(NAME pano_cli_parse_layout_smoke
|
||||||
|
COMMAND pano_cli parse-layout --path "${CMAKE_CURRENT_SOURCE_DIR}/data/layouts/simple-layout.xml")
|
||||||
|
set_tests_properties(pano_cli_parse_layout_smoke PROPERTIES
|
||||||
|
LABELS "ui;integration;desktop-fast")
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
5
tests/data/layouts/simple-layout.xml
Normal file
5
tests/data/layouts/simple-layout.xml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<layout width="100%" height="auto">
|
||||||
|
<panel width="320" height="200">
|
||||||
|
<button width="64" height="28"/>
|
||||||
|
</panel>
|
||||||
|
</layout>
|
||||||
47
tests/ui_core/layout_xml_tests.cpp
Normal file
47
tests/ui_core/layout_xml_tests.cpp
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
#include "ui_core/layout_xml.h"
|
||||||
|
#include "test_harness.h"
|
||||||
|
|
||||||
|
using pp::foundation::StatusCode;
|
||||||
|
using pp::ui::parse_layout_xml;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
void parses_nested_layout_xml_and_lengths(pp::tests::Harness& h)
|
||||||
|
{
|
||||||
|
constexpr auto xml =
|
||||||
|
"<layout width=\"100%\" height=\"auto\">"
|
||||||
|
" <panel width=\"320\" height=\"200\">"
|
||||||
|
" <button width=\"64\" height=\"28\"/>"
|
||||||
|
" </panel>"
|
||||||
|
"</layout>";
|
||||||
|
|
||||||
|
const auto summary = parse_layout_xml(xml);
|
||||||
|
|
||||||
|
PP_EXPECT(h, summary.ok());
|
||||||
|
PP_EXPECT(h, summary.value().node_count == 3U);
|
||||||
|
PP_EXPECT(h, summary.value().length_attribute_count == 6U);
|
||||||
|
}
|
||||||
|
|
||||||
|
void rejects_malformed_empty_and_invalid_lengths(pp::tests::Harness& h)
|
||||||
|
{
|
||||||
|
const auto empty = parse_layout_xml("");
|
||||||
|
const auto malformed = parse_layout_xml("<layout><panel></layout>");
|
||||||
|
const auto invalid_length = parse_layout_xml("<layout width=\"101%\"/>");
|
||||||
|
|
||||||
|
PP_EXPECT(h, !empty.ok());
|
||||||
|
PP_EXPECT(h, empty.status().code == StatusCode::invalid_argument);
|
||||||
|
PP_EXPECT(h, !malformed.ok());
|
||||||
|
PP_EXPECT(h, malformed.status().code == StatusCode::invalid_argument);
|
||||||
|
PP_EXPECT(h, !invalid_length.ok());
|
||||||
|
PP_EXPECT(h, invalid_length.status().code == StatusCode::out_of_range);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
pp::tests::Harness harness;
|
||||||
|
harness.run("parses_nested_layout_xml_and_lengths", parses_nested_layout_xml_and_lengths);
|
||||||
|
harness.run("rejects_malformed_empty_and_invalid_lengths", rejects_malformed_empty_and_invalid_lengths);
|
||||||
|
return harness.finish();
|
||||||
|
}
|
||||||
@@ -5,4 +5,5 @@ target_link_libraries(pano_cli PRIVATE
|
|||||||
pp_project_warnings
|
pp_project_warnings
|
||||||
pp_foundation
|
pp_foundation
|
||||||
pp_assets
|
pp_assets
|
||||||
pp_document)
|
pp_document
|
||||||
|
pp_ui_core)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#include "document/document.h"
|
#include "document/document.h"
|
||||||
#include "foundation/parse.h"
|
#include "foundation/parse.h"
|
||||||
#include "foundation/result.h"
|
#include "foundation/result.h"
|
||||||
|
#include "ui_core/layout_xml.h"
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
@@ -23,6 +24,10 @@ struct InspectImageArgs {
|
|||||||
std::string path;
|
std::string path;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ParseLayoutArgs {
|
||||||
|
std::string path;
|
||||||
|
};
|
||||||
|
|
||||||
void print_error(std::string_view command, std::string_view message)
|
void print_error(std::string_view command, std::string_view message)
|
||||||
{
|
{
|
||||||
std::cout << "{\"ok\":false,\"command\":\"" << command
|
std::cout << "{\"ok\":false,\"command\":\"" << command
|
||||||
@@ -35,6 +40,7 @@ void print_help()
|
|||||||
<< "pano_cli commands:\n"
|
<< "pano_cli commands:\n"
|
||||||
<< " create-document --width N --height N [--layers N]\n"
|
<< " create-document --width N --height N [--layers N]\n"
|
||||||
<< " inspect-image --path FILE\n"
|
<< " inspect-image --path FILE\n"
|
||||||
|
<< " parse-layout --path FILE\n"
|
||||||
<< " --help\n";
|
<< " --help\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,6 +166,60 @@ int inspect_image(int argc, char** argv)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pp::foundation::Status parse_layout_args(int argc, char** argv, ParseLayoutArgs& args)
|
||||||
|
{
|
||||||
|
for (int i = 2; i < argc; ++i) {
|
||||||
|
const std::string_view key(argv[i]);
|
||||||
|
if (key == "--path") {
|
||||||
|
if (i + 1 >= argc) {
|
||||||
|
return pp::foundation::Status::invalid_argument("missing value for option");
|
||||||
|
}
|
||||||
|
|
||||||
|
args.path = argv[++i];
|
||||||
|
} else {
|
||||||
|
return pp::foundation::Status::invalid_argument("unknown option");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.path.empty()) {
|
||||||
|
return pp::foundation::Status::invalid_argument("path must not be empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
return pp::foundation::Status::success();
|
||||||
|
}
|
||||||
|
|
||||||
|
int parse_layout(int argc, char** argv)
|
||||||
|
{
|
||||||
|
ParseLayoutArgs args;
|
||||||
|
const auto status = parse_layout_args(argc, argv, args);
|
||||||
|
if (!status.ok()) {
|
||||||
|
print_error("parse-layout", status.message);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ifstream stream(args.path, std::ios::binary);
|
||||||
|
if (!stream) {
|
||||||
|
print_error("parse-layout", "layout file could not be opened");
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string xml {
|
||||||
|
std::istreambuf_iterator<char>(stream),
|
||||||
|
std::istreambuf_iterator<char>()
|
||||||
|
};
|
||||||
|
const auto summary = pp::ui::parse_layout_xml(xml);
|
||||||
|
if (!summary) {
|
||||||
|
print_error("parse-layout", summary.status().message);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "{\"ok\":true,\"command\":\"parse-layout\""
|
||||||
|
<< ",\"nodes\":" << summary.value().node_count
|
||||||
|
<< ",\"lengthAttributes\":" << summary.value().length_attribute_count
|
||||||
|
<< "}\n";
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
@@ -183,6 +243,10 @@ int main(int argc, char** argv)
|
|||||||
return inspect_image(argc, argv);
|
return inspect_image(argc, argv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (command == "parse-layout") {
|
||||||
|
return parse_layout(argc, argv);
|
||||||
|
}
|
||||||
|
|
||||||
print_error(command, "unknown command");
|
print_error(command, "unknown command");
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user