Crafter.Debug/interfaces/Crafter.Debug.Cli-Client.cppm

79 lines
3.3 KiB
Text
Raw Normal View History

// SPDX-License-Identifier: LGPL-3.0-only
// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
// crafter-debug, the client agents drive an app with. Run is the whole
// program but for the transport: main hands it a Connector that reaches an
// instance over QUIC, and tests hand it one that reaches a Session in the
// same process.
//
// Exit codes:
// 0 done
// 2 a usage error, or no debug build yet (the launcher's stub)
// 3 no instance to talk to
// 4 the app and this client speak different protocols
// 5 timed out
// 6 the app answered with an error
// 1 is left to the launcher and `flatpak run` failing.
export module Crafter.Debug.Cli:Client;
import std;
import Crafter.Debug.Core;
export namespace Crafter::Debug::Cli {
inline constexpr std::int32_t exitOk = 0;
inline constexpr std::int32_t exitUsage = 2;
inline constexpr std::int32_t exitNoInstance = 3;
inline constexpr std::int32_t exitVersion = 4;
inline constexpr std::int32_t exitTimeout = 5;
inline constexpr std::int32_t exitError = 6;
struct CallError {
enum class Kind : std::uint8_t {
// No connection: the app is gone, or its port does not answer.
Unreachable,
// Connected, but no reply within the timeout.
TimedOut,
// The reply was not what the protocol says.
Broken,
};
Kind kind = Kind::Unreachable;
std::string message;
};
// One request to an instance. The reply is the last object it sends;
// objects before it are stream lines (`log --follow`), handed to
// `onLine`, which returns false to stop.
class Connection {
public:
virtual ~Connection() = default;
virtual std::expected<Value, CallError> Call(const Value& request, std::chrono::milliseconds timeout,
const std::function<bool(const Value& line)>& onLine) = 0;
};
using Connector = std::function<std::unique_ptr<Connection>(const InstanceRecord& instance)>;
struct Environment {
Connector connect;
std::function<std::vector<Instance>()> list = ListInstances;
std::ostream* out = &std::cout;
std::ostream* err = &std::cerr;
// Where a screenshot without -o goes, relative paths included.
std::filesystem::path cwd = std::filesystem::current_path();
// The project the launcher that started this client was written for
// (CRAFTER_DEBUG_PROJECT). Its apps, the ones whose executable lies
// under it, are picked without -i when other apps run too, and no
// other app is. Empty: whichever app runs, as long as only one does.
std::filesystem::path project;
// Where lldb is here, for the attach command `debugger` prints: its
// absolute path, since `flatpak enter` runs a command without the
// sandbox's PATH, which /app/bin is on. Empty for none: gdb then.
std::function<std::string()> lldb;
};
std::int32_t Run(std::span<const std::string_view> args, Environment& environment);
// The full reference an agent reads with `crafter-debug guide`.
std::string_view Guide();
// "30s", "500ms", "2m", "1h"; a bare number is seconds.
std::optional<std::chrono::milliseconds> ParseDuration(std::string_view text);
}