Plan cloud upload decisions in app core

This commit is contained in:
2026-06-02 23:34:58 +02:00
parent 6e3296469a
commit 3a78361aea
10 changed files with 217 additions and 8 deletions

View File

@@ -211,6 +211,7 @@ target_link_libraries(pp_ui_core
pp_project_warnings) pp_project_warnings)
add_library(pp_app_core STATIC add_library(pp_app_core STATIC
src/app_core/document_cloud.h
src/app_core/document_export.cpp src/app_core/document_export.cpp
src/app_core/document_route.cpp src/app_core/document_route.cpp
src/app_core/document_session.cpp) src/app_core/document_session.cpp)

View File

@@ -418,6 +418,10 @@ Known local toolchain state:
the live image, layer, animation-frame, depth, and cube-face export dialogs the live image, layer, animation-frame, depth, and cube-face export dialogs
plus MP4 animation and timelapse export dialogs consume the same start plus MP4 animation and timelapse export dialogs consume the same start
contract before reaching legacy canvas/recording export execution. contract before reaching legacy canvas/recording export execution.
- `pano_cli plan-cloud-upload` exposes `pp_app_core` cloud upload availability,
new-document warning, publish prompt, and save-before-upload planning as JSON;
the live cloud upload command consumes the same start contract before
reaching legacy UI, canvas save, and network upload execution.
- `pano_cli simulate-app-session` exposes `pp_app_core` project-open, - `pano_cli simulate-app-session` exposes `pp_app_core` project-open,
app-close, save, save-as, save-version, and save-before-workflow decisions app-close, save, save-as, save-version, and save-before-workflow decisions
as JSON and is covered for clean, dirty, already-prompting, missing-canvas, as JSON and is covered for clean, dirty, already-prompting, missing-canvas,
@@ -430,6 +434,9 @@ Known local toolchain state:
directory/stem targets, picked-directory stems, MP4 suggested names, and directory/stem targets, picked-directory stems, MP4 suggested names, and
invalid export naming inputs, plus export-start license/canvas availability invalid export naming inputs, plus export-start license/canvas availability
decisions. decisions.
- `pp_app_core_document_cloud_tests` covers cloud upload no-canvas,
new-document warning, clean publish prompt, and dirty save-before-upload
decisions.
- `pp_app_core_document_session_tests` covers clean and dirty app session, - `pp_app_core_document_session_tests` covers clean and dirty app session,
document-open action planning, save-request, save-before-workflow, document-open action planning, save-request, save-before-workflow,
new-document target/resolution/overwrite planning, document file target, new-document target/resolution/overwrite planning, document file target,

View File

@@ -77,7 +77,7 @@ and validation command.
| Capability | Current Area | Target Owner | Required Tests | | Capability | Current Area | Target Owner | Required Tests |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| Upload/download/browse | `app_cloud`, CURL helpers | app service, `pp_platform_*` | Mocked HTTP and timeout tests | | Upload/download/browse | `app_cloud`, CURL helpers | `pp_app_core`, app service, `pp_platform_*` | Upload prompt/new-doc/no-canvas decision tests, mocked HTTP and timeout tests |
| License/check flows | app/cloud code | app service | Mocked response tests | | License/check flows | app/cloud code | app service | Mocked response tests |
| Logging/crash reporting | `log`, BugTrap/AppCenter | `pp_foundation`, platform wrappers | Log formatting and platform compile | | Logging/crash reporting | `log`, BugTrap/AppCenter | `pp_foundation`, platform wrappers | Log formatting and platform compile |
| Headless automation | none yet | `tools/pano_cli` | JSON command fixtures | | Headless automation | none yet | `tools/pano_cli` | JSON command fixtures |

View File

@@ -22,7 +22,7 @@ agent or engineer to remove them without reconstructing context from chat.
| --- | --- | --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- | --- | --- |
| DEBT-0001 | Open | Modernization | Existing platform build files remain alongside new CMake | Required for incremental migration without losing platform coverage | Existing platform builds plus new CMake configure | Remove after all platform builds consume shared CMake targets | | DEBT-0001 | Open | Modernization | Existing platform build files remain alongside new CMake | Required for incremental migration without losing platform coverage | Existing platform builds plus new CMake configure | Remove after all platform builds consume shared CMake targets |
| DEBT-0002 | Open | Modernization | Vendored SDK and patched libraries retained initially | Some dependencies are SDK-only, patched, or have platform-specific binaries | Dependency inventory and platform build smoke tests | Replace with vcpkg packages or document permanent vendored status after triplet evaluation | | DEBT-0002 | Open | Modernization | Vendored SDK and patched libraries retained initially | Some dependencies are SDK-only, patched, or have platform-specific binaries | Dependency inventory and platform build smoke tests | Replace with vcpkg packages or document permanent vendored status after triplet evaluation |
| DEBT-0003 | Open | Modernization | Existing singletons remain during initial split; `App::open_document`, `App::request_close`, file-menu save actions, `NodeCanvas` save hotkeys, new/open/browse dirty-document workflow prompts, new-document target/resolution/overwrite decisions, save-as document file naming and overwrite decisions, save-version target decisions, export start/target naming/path decisions, `pano_cli classify-open`, `pano_cli plan-open-route`, `pano_cli plan-new-document`, `pano_cli plan-document-file`, `pano_cli plan-document-version`, `pano_cli plan-export-start`, `pano_cli plan-export-target`, and `pano_cli simulate-app-session` now consume pure `pp_app_core` route/session/export contracts, but document creation/loading, brush import execution, saving, and export execution still reach legacy `Canvas::I`/UI singletons | Avoid behavior changes while introducing component boundaries | App launch and component tests; `pp_app_core_document_route_tests`; `pp_app_core_document_export_tests`; `pp_app_core_document_session_tests`; `pano_cli classify-open --path D:/Paint/demo.ppi`; `pano_cli plan-open-route --path D:/Paint/demo.ppi --unsaved`; `pano_cli plan-new-document --work-dir D:/Paint --name demo --resolution-index 3 --target-exists`; `pano_cli plan-document-file --work-dir D:/Paint --name demo --target-exists`; `pano_cli plan-document-version --directory D:/Paint --doc-name demo.01 --existing-path D:/Paint/demo.02.ppi`; `pano_cli plan-export-start --requires-license --demo`; `pano_cli plan-export-target --kind file --work-dir D:/Paint --doc-name demo --extension .png`; `pano_cli simulate-app-session --unsaved --save-intent save-dirty-version`; `pano_cli simulate-app-session --no-canvas`; `ctest --preset desktop-fast --build-config Debug` | Replace singleton reaches with context/service injection at component boundaries | | DEBT-0003 | Open | Modernization | Existing singletons remain during initial split; `App::open_document`, `App::request_close`, `App::cloud_upload`, file-menu save actions, `NodeCanvas` save hotkeys, new/open/browse dirty-document workflow prompts, new-document target/resolution/overwrite decisions, save-as document file naming and overwrite decisions, save-version target decisions, export start/target naming/path decisions, cloud-upload prompt/save-before-upload decisions, `pano_cli classify-open`, `pano_cli plan-open-route`, `pano_cli plan-new-document`, `pano_cli plan-document-file`, `pano_cli plan-document-version`, `pano_cli plan-export-start`, `pano_cli plan-export-target`, `pano_cli plan-cloud-upload`, and `pano_cli simulate-app-session` now consume pure `pp_app_core` route/session/export/cloud contracts, but document creation/loading, brush import execution, saving, export execution, cloud upload execution, and cloud browse/download still reach legacy `Canvas::I`/UI/network singletons | Avoid behavior changes while introducing component boundaries | App launch and component tests; `pp_app_core_document_route_tests`; `pp_app_core_document_export_tests`; `pp_app_core_document_cloud_tests`; `pp_app_core_document_session_tests`; `pano_cli classify-open --path D:/Paint/demo.ppi`; `pano_cli plan-open-route --path D:/Paint/demo.ppi --unsaved`; `pano_cli plan-new-document --work-dir D:/Paint --name demo --resolution-index 3 --target-exists`; `pano_cli plan-document-file --work-dir D:/Paint --name demo --target-exists`; `pano_cli plan-document-version --directory D:/Paint --doc-name demo.01 --existing-path D:/Paint/demo.02.ppi`; `pano_cli plan-export-start --requires-license --demo`; `pano_cli plan-export-target --kind file --work-dir D:/Paint --doc-name demo --extension .png`; `pano_cli plan-cloud-upload --new-document --unsaved`; `pano_cli simulate-app-session --unsaved --save-intent save-dirty-version`; `pano_cli simulate-app-session --no-canvas`; `ctest --preset desktop-fast --build-config Debug` | Replace singleton reaches with context/service injection at component boundaries |
| DEBT-0004 | Open | Modernization | Android, Linux, WebGL, Apple, and AppX build files remain platform-specific until root CMake alignment reaches them | Prevent platform regressions during incremental migration; raw Windows `.sln/.vcxproj` files were removed on 2026-05-31 by user decision | `cmake --preset windows-msvc-default`; platform-specific configure/build smoke checks as each platform is migrated | Root CMake owns every platform source list and package path | | DEBT-0004 | Open | Modernization | Android, Linux, WebGL, Apple, and AppX build files remain platform-specific until root CMake alignment reaches them | Prevent platform regressions during incremental migration; raw Windows `.sln/.vcxproj` files were removed on 2026-05-31 by user decision | `cmake --preset windows-msvc-default`; platform-specific configure/build smoke checks as each platform is migrated | Root CMake owns every platform source list and package path |
| DEBT-0005 | Open | Modernization | Temporary local CTest harness is used before Catch2 is wired through vcpkg | `vcpkg` is not currently on PATH, but headless tests need to run now | `ctest --preset desktop-fast --build-config Debug` | Replace `tests/test_harness.h` tests with Catch2 tests once vcpkg toolchain/presets are validated | | DEBT-0005 | Open | Modernization | Temporary local CTest harness is used before Catch2 is wired through vcpkg | `vcpkg` is not currently on PATH, but headless tests need to run now | `ctest --preset desktop-fast --build-config Debug` | Replace `tests/test_harness.h` tests with Catch2 tests once vcpkg toolchain/presets are validated |
| DEBT-0007 | Open | Modernization | `vcpkg.json` and `windows-msvc-vcpkg-headless` are validated for the headless Windows component matrix, but app targets still use vendored libraries and Android/Apple triplets are not proven | Dependency migration must stay incremental while SDK/patched/vendor dependencies remain in use | `$env:VCPKG_ROOT="C:\Program Files\Microsoft Visual Studio\2022\Community\VC\vcpkg"; cmake --preset windows-msvc-vcpkg-headless`; `ctest --preset desktop-fast-vcpkg --build-config Debug` | Component targets consume vcpkg packages where reliable and desktop app, Android, and Apple triplets are validated or explicitly documented as permanent vendor exceptions | | DEBT-0007 | Open | Modernization | `vcpkg.json` and `windows-msvc-vcpkg-headless` are validated for the headless Windows component matrix, but app targets still use vendored libraries and Android/Apple triplets are not proven | Dependency migration must stay incremental while SDK/patched/vendor dependencies remain in use | `$env:VCPKG_ROOT="C:\Program Files\Microsoft Visual Studio\2022\Community\VC\vcpkg"; cmake --preset windows-msvc-vcpkg-headless`; `ctest --preset desktop-fast-vcpkg --build-config Debug` | Component targets consume vcpkg packages where reliable and desktop app, Android, and Apple triplets are validated or explicitly documented as permanent vendor exceptions |

View File

@@ -444,6 +444,10 @@ stems, and MP4 suggested names used by the live export dialogs.
used by live image, layer, animation-frame, depth, and cube-face export dialogs used by live image, layer, animation-frame, depth, and cube-face export dialogs
plus MP4 animation and timelapse export dialogs before they call legacy plus MP4 animation and timelapse export dialogs before they call legacy
canvas/recording export execution. canvas/recording export execution.
`pano_cli plan-cloud-upload` exposes the app-core cloud upload decision used by
the live cloud upload command for missing-canvas, new-document warning, publish
prompt, and dirty-document save-before-upload states before legacy UI, canvas,
and network execution continue.
`pano_cli parse-layout` exercises the XML layout path. Continue expanding `pano_cli parse-layout` exercises the XML layout path. Continue expanding
document behavior toward legacy Canvas parity and then port OpenGL classes document behavior toward legacy Canvas parity and then port OpenGL classes
behind the renderer boundary. behind the renderer boundary.
@@ -889,6 +893,14 @@ Results:
sample counts/distances. sample counts/distances.
- `pano_cli_simulate_stroke_script_smoke` passed and reports deterministic - `pano_cli_simulate_stroke_script_smoke` passed and reports deterministic
aggregate stroke-script counts/distances. aggregate stroke-script counts/distances.
- `pp_app_core_document_cloud_tests` passed, covering cloud upload no-canvas,
new-document warning, clean publish prompt, and dirty save-before-upload
decisions.
- `pano_cli_plan_cloud_upload_clean_smoke`,
`pano_cli_plan_cloud_upload_unsaved_smoke`,
`pano_cli_plan_cloud_upload_new_document_smoke`, and
`pano_cli_plan_cloud_upload_no_canvas_smoke` passed and expose those app-core
cloud upload decisions as JSON.
- `panopainter_validate_shaders` passed, validating 25 shader programs and 7 - `panopainter_validate_shaders` passed, validating 25 shader programs and 7
shader includes for stage markers and include graph integrity. shader includes for stage markers and include graph integrity.
- `pp_renderer_gl_capabilities_tests` passed on default MSVC, vcpkg-headless, - `pp_renderer_gl_capabilities_tests` passed on default MSVC, vcpkg-headless,

View File

@@ -1,19 +1,29 @@
#include "pch.h" #include "pch.h"
#include "app.h" #include "app.h"
#include "app_core/document_cloud.h"
#include "util.h" #include "util.h"
#include "node_progress_bar.h" #include "node_progress_bar.h"
#include "node_dialog_cloud.h" #include "node_dialog_cloud.h"
void App::cloud_upload() void App::cloud_upload()
{ {
if (!canvas) const bool has_canvas = canvas != nullptr;
const auto plan = pp::app::plan_cloud_upload(
has_canvas,
has_canvas && Canvas::I->m_newdoc,
has_canvas && Canvas::I->m_unsaved);
switch (plan.action)
{
case pp::app::CloudUploadAction::unavailable_no_canvas:
return; return;
if (Canvas::I->m_newdoc) case pp::app::CloudUploadAction::show_save_required_warning:
{
message_box("Warning", "This document needs to be saved before upload."); message_box("Warning", "This document needs to be saved before upload.");
return;
case pp::app::CloudUploadAction::prompt_publish:
break;
} }
else
{
auto upload_thread = [this] { auto upload_thread = [this] {
BT_SetTerminate(); BT_SetTerminate();
@@ -43,7 +53,6 @@ void App::cloud_upload()
m->destroy(); m->destroy();
}; };
} }
}
void App::cloud_upload_all() void App::cloud_upload_all()
{ {

View File

@@ -0,0 +1,32 @@
#pragma once
namespace pp::app {
enum class CloudUploadAction {
unavailable_no_canvas,
show_save_required_warning,
prompt_publish,
};
struct CloudUploadPlan {
CloudUploadAction action = CloudUploadAction::unavailable_no_canvas;
bool save_before_upload = false;
};
[[nodiscard]] constexpr CloudUploadPlan plan_cloud_upload(
bool has_canvas,
bool is_new_document,
bool has_unsaved_changes) noexcept
{
if (!has_canvas) {
return { CloudUploadAction::unavailable_no_canvas, false };
}
if (is_new_document) {
return { CloudUploadAction::show_save_required_warning, false };
}
return { CloudUploadAction::prompt_publish, has_unsaved_changes };
}
}

View File

@@ -278,6 +278,16 @@ add_test(NAME pp_app_core_document_export_tests COMMAND pp_app_core_document_exp
set_tests_properties(pp_app_core_document_export_tests PROPERTIES set_tests_properties(pp_app_core_document_export_tests PROPERTIES
LABELS "app;desktop-fast;fuzz") LABELS "app;desktop-fast;fuzz")
add_executable(pp_app_core_document_cloud_tests
app_core/document_cloud_tests.cpp)
target_link_libraries(pp_app_core_document_cloud_tests PRIVATE
pp_app_core
pp_test_harness)
add_test(NAME pp_app_core_document_cloud_tests COMMAND pp_app_core_document_cloud_tests)
set_tests_properties(pp_app_core_document_cloud_tests PROPERTIES
LABELS "app;desktop-fast;fuzz")
add_executable(pp_app_core_document_session_tests add_executable(pp_app_core_document_session_tests
app_core/document_session_tests.cpp) app_core/document_session_tests.cpp)
target_link_libraries(pp_app_core_document_session_tests PRIVATE target_link_libraries(pp_app_core_document_session_tests PRIVATE
@@ -479,6 +489,30 @@ if(TARGET pano_cli)
LABELS "app;integration;desktop-fast" LABELS "app;integration;desktop-fast"
PASS_REGULAR_EXPRESSION "\"command\":\"plan-export-target\".*\"kind\":\"name\".*\"suggestedName\":\"demo-timelapse\"") PASS_REGULAR_EXPRESSION "\"command\":\"plan-export-target\".*\"kind\":\"name\".*\"suggestedName\":\"demo-timelapse\"")
add_test(NAME pano_cli_plan_cloud_upload_clean_smoke
COMMAND pano_cli plan-cloud-upload)
set_tests_properties(pano_cli_plan_cloud_upload_clean_smoke PROPERTIES
LABELS "app;integration;desktop-fast"
PASS_REGULAR_EXPRESSION "\"command\":\"plan-cloud-upload\".*\"hasCanvas\":true.*\"newDocument\":false.*\"unsaved\":false.*\"decision\":\"prompt-publish\".*\"saveBeforeUpload\":false")
add_test(NAME pano_cli_plan_cloud_upload_unsaved_smoke
COMMAND pano_cli plan-cloud-upload --unsaved)
set_tests_properties(pano_cli_plan_cloud_upload_unsaved_smoke PROPERTIES
LABELS "app;integration;desktop-fast"
PASS_REGULAR_EXPRESSION "\"command\":\"plan-cloud-upload\".*\"unsaved\":true.*\"decision\":\"prompt-publish\".*\"saveBeforeUpload\":true")
add_test(NAME pano_cli_plan_cloud_upload_new_document_smoke
COMMAND pano_cli plan-cloud-upload --new-document --unsaved)
set_tests_properties(pano_cli_plan_cloud_upload_new_document_smoke PROPERTIES
LABELS "app;integration;desktop-fast;fuzz"
PASS_REGULAR_EXPRESSION "\"command\":\"plan-cloud-upload\".*\"newDocument\":true.*\"decision\":\"show-save-required-warning\".*\"saveBeforeUpload\":false")
add_test(NAME pano_cli_plan_cloud_upload_no_canvas_smoke
COMMAND pano_cli plan-cloud-upload --no-canvas --new-document --unsaved)
set_tests_properties(pano_cli_plan_cloud_upload_no_canvas_smoke PROPERTIES
LABELS "app;integration;desktop-fast;fuzz"
PASS_REGULAR_EXPRESSION "\"command\":\"plan-cloud-upload\".*\"hasCanvas\":false.*\"decision\":\"unavailable-no-canvas\".*\"saveBeforeUpload\":false")
add_test(NAME pano_cli_simulate_app_session_clean_smoke add_test(NAME pano_cli_simulate_app_session_clean_smoke
COMMAND pano_cli simulate-app-session) COMMAND pano_cli simulate-app-session)
set_tests_properties(pano_cli_simulate_app_session_clean_smoke PROPERTIES set_tests_properties(pano_cli_simulate_app_session_clean_smoke PROPERTIES

View File

@@ -0,0 +1,44 @@
#include "app_core/document_cloud.h"
#include "test_harness.h"
namespace {
void cloud_upload_is_unavailable_without_canvas(pp::tests::Harness& harness)
{
const auto plan = pp::app::plan_cloud_upload(false, false, false);
PP_EXPECT(harness, plan.action == pp::app::CloudUploadAction::unavailable_no_canvas);
PP_EXPECT(harness, !plan.save_before_upload);
}
void cloud_upload_warns_for_new_documents(pp::tests::Harness& harness)
{
const auto plan = pp::app::plan_cloud_upload(true, true, true);
PP_EXPECT(harness, plan.action == pp::app::CloudUploadAction::show_save_required_warning);
PP_EXPECT(harness, !plan.save_before_upload);
}
void cloud_upload_prompts_for_clean_existing_documents(pp::tests::Harness& harness)
{
const auto plan = pp::app::plan_cloud_upload(true, false, false);
PP_EXPECT(harness, plan.action == pp::app::CloudUploadAction::prompt_publish);
PP_EXPECT(harness, !plan.save_before_upload);
}
void cloud_upload_records_save_before_upload_for_dirty_existing_documents(pp::tests::Harness& harness)
{
const auto plan = pp::app::plan_cloud_upload(true, false, true);
PP_EXPECT(harness, plan.action == pp::app::CloudUploadAction::prompt_publish);
PP_EXPECT(harness, plan.save_before_upload);
}
}
int main()
{
pp::tests::Harness harness;
harness.run("cloud upload is unavailable without canvas", cloud_upload_is_unavailable_without_canvas);
harness.run("cloud upload warns for new documents", cloud_upload_warns_for_new_documents);
harness.run("cloud upload prompts for clean existing documents", cloud_upload_prompts_for_clean_existing_documents);
harness.run("cloud upload records save before upload for dirty existing documents", cloud_upload_records_save_before_upload_for_dirty_existing_documents);
return harness.finish();
}

View File

@@ -1,4 +1,5 @@
#include "app_core/document_export.h" #include "app_core/document_export.h"
#include "app_core/document_cloud.h"
#include "app_core/document_route.h" #include "app_core/document_route.h"
#include "app_core/document_session.h" #include "app_core/document_session.h"
#include "assets/image_format.h" #include "assets/image_format.h"
@@ -136,6 +137,12 @@ struct PlanExportStartArgs {
bool has_canvas = true; bool has_canvas = true;
}; };
struct PlanCloudUploadArgs {
bool has_canvas = true;
bool new_document = false;
bool unsaved = false;
};
struct SimulateAppSessionArgs { struct SimulateAppSessionArgs {
bool has_canvas = true; bool has_canvas = true;
bool new_document = false; bool new_document = false;
@@ -395,6 +402,20 @@ const char* document_export_start_decision_name(pp::app::DocumentExportStartDeci
return "unavailable-no-canvas"; return "unavailable-no-canvas";
} }
const char* cloud_upload_action_name(pp::app::CloudUploadAction action) noexcept
{
switch (action) {
case pp::app::CloudUploadAction::unavailable_no_canvas:
return "unavailable-no-canvas";
case pp::app::CloudUploadAction::show_save_required_warning:
return "show-save-required-warning";
case pp::app::CloudUploadAction::prompt_publish:
return "prompt-publish";
}
return "unavailable-no-canvas";
}
pp::foundation::Result<float> parse_float_arg(std::string_view text) pp::foundation::Result<float> parse_float_arg(std::string_view text)
{ {
float value = 0.0F; float value = 0.0F;
@@ -431,6 +452,7 @@ void print_help()
<< " plan-document-version --directory DIR --doc-name NAME [--existing-path FILE]\n" << " plan-document-version --directory DIR --doc-name NAME [--existing-path FILE]\n"
<< " plan-export-start [--requires-license] [--demo] [--no-canvas]\n" << " plan-export-start [--requires-license] [--demo] [--no-canvas]\n"
<< " plan-export-target --kind file|collection|stem|name --doc-name NAME [--work-dir DIR] [--directory DIR] [--extension EXT] [--suffix SUFFIX]\n" << " plan-export-target --kind file|collection|stem|name --doc-name NAME [--work-dir DIR] [--directory DIR] [--extension EXT] [--suffix SUFFIX]\n"
<< " plan-cloud-upload [--no-canvas] [--new-document] [--unsaved]\n"
<< " load-project --path FILE\n" << " load-project --path FILE\n"
<< " parse-layout --path FILE\n" << " parse-layout --path FILE\n"
<< " record-render [--width N] [--height N] [--exercise-clear]\n" << " record-render [--width N] [--height N] [--exercise-clear]\n"
@@ -1574,6 +1596,50 @@ int plan_export_start(int argc, char** argv)
return 0; return 0;
} }
pp::foundation::Status parse_plan_cloud_upload_args(
int argc,
char** argv,
PlanCloudUploadArgs& args)
{
for (int i = 2; i < argc; ++i) {
const std::string_view key(argv[i]);
if (key == "--no-canvas") {
args.has_canvas = false;
} else if (key == "--new-document") {
args.new_document = true;
} else if (key == "--unsaved") {
args.unsaved = true;
} else {
return pp::foundation::Status::invalid_argument("unknown option");
}
}
return pp::foundation::Status::success();
}
int plan_cloud_upload(int argc, char** argv)
{
PlanCloudUploadArgs args;
const auto status = parse_plan_cloud_upload_args(argc, argv, args);
if (!status.ok()) {
print_error("plan-cloud-upload", status.message);
return 2;
}
const auto plan = pp::app::plan_cloud_upload(
args.has_canvas,
args.new_document,
args.unsaved);
std::cout << "{\"ok\":true,\"command\":\"plan-cloud-upload\""
<< ",\"state\":{\"hasCanvas\":" << json_bool(args.has_canvas)
<< ",\"newDocument\":" << json_bool(args.new_document)
<< ",\"unsaved\":" << json_bool(args.unsaved)
<< "},\"decision\":\"" << cloud_upload_action_name(plan.action)
<< "\",\"saveBeforeUpload\":" << json_bool(plan.save_before_upload)
<< "}\n";
return 0;
}
pp::foundation::Status parse_plan_export_target_args( pp::foundation::Status parse_plan_export_target_args(
int argc, int argc,
char** argv, char** argv,
@@ -3725,6 +3791,10 @@ int main(int argc, char** argv)
return plan_export_target(argc, argv); return plan_export_target(argc, argv);
} }
if (command == "plan-cloud-upload") {
return plan_cloud_upload(argc, argv);
}
if (command == "load-project") { if (command == "load-project") {
return load_project(argc, argv); return load_project(argc, argv);
} }