api-ws
Source: crates/lvcs-api/src/ws.rs.
/// `ws.fork`: fork a workspace named `name` from `from` (`main`, or a
/// workspace name or id), optionally only the subtree under the path
/// `subtree`, mirrored to `dir` (by default `.lvcs/ws/<name>/`, or
/// `.lvcs/ws/<name>-<first 6 characters of its id>/` when another workspace
/// in the registry has the name; design §8). The caller authors the new
/// workspace's mirror (decision 137 B): its disk saves are checked against
/// the caller's grants, metered at its signing rate, scanned for secrets,
/// and signed as it, the hook ops attribute through it, and its stale
/// saves reach the caller. A connection from another OS user than the
/// daemon's forks with no mirror, so with no author, and one naming `dir`
/// is refused `REJECTED` (decision 134).
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct WsForkParams {
pub name: String,
pub from: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub subtree: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub dir: Option<String>,
}
/// `ws.fork` result: the new workspace's id (`ID_ENCODING`) and its mirror
/// directory, relative to the project root unless `dir` was absolute;
/// empty for a workspace a connection from another OS user than the
/// daemon's forked, which has no mirror (decision 134).
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct WsForkResult {
pub workspace: String,
pub dir: String,
}
/// `ws.merge`: merge `workspace` (a name or id) back into `into` (`main`,
/// or a workspace name or id; the workspace's base when omitted, and it
/// must be the workspace's base, else invalid params, -32602) as one
/// `WorkspaceMerged` bundle (design §8). A workspace forked from another
/// workspace merges into it, which absorbs the bundle copy-on-write into
/// its own overrides and writes no registry record (decision 107); it
/// reaches `main` only through that workspace's own merge-back. With
/// `require_validated` (false when omitted) the merge would wait until
/// checks pass on a preview of the merged state; that is deferred past the
/// prototype demo (decisions 101 and 126), so such a merge is refused
/// `REJECTED` and nothing is merged.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct WsMergeParams {
pub workspace: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub into: Option<String>,
#[serde(default)]
pub require_validated: bool,
}
/// `ws.merge` result: the bundle id and the visible frontier id of `into`
/// after the bundle (both `ID_ENCODING`), and every conflict the merge-back
/// left, each with the thread it opened. A merge-back a base document
/// refused writes no record and opens no thread, and `ws.merge` answers an
/// error instead (decision 88).
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct WsMergeResult {
pub bundle: String,
pub frontier: String,
#[serde(default)]
pub conflicts: Vec<WsConflict>,
}
/// One merge-back conflict.
///
/// It names the base document, why it conflicts, the conflicting range as code points at the merged heads (`start == end`
/// when that range is empty; both absent for an `anchor_lost` or
/// `whiteout` conflict, whose override or whiteout has no range in the
/// merged text), the participants whose edits overlap (`ID_ENCODING`,
/// sorted; authors the daemon can't map to a participant are omitted), and
/// the conflict thread's address: a thread anchored on `address` and
/// addressed to `authors`. `lines` names the range's lines in the merged
/// text, as the merge plan named them for this conflict alone (decisions
/// 160, 165, and 197); it is absent for an empty range, a document that
/// holds no text after the merge, and an `anchor_lost` or `whiteout`
/// conflict.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct WsConflict {
pub address: String,
pub cause: WsConflictCause,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub start: Option<usize>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub end: Option<usize>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub lines: Option<LineName>,
#[serde(default)]
pub authors: Vec<String>,
pub thread: String,
}
/// Why a merge-back conflict opened (decision 92), a JSON string: the
/// workspace's edits and the base's genuinely overlap and the base's text
/// was kept (`overlap`), the base deleted an override's whole range
/// (`anchor_lost`), or the base changed a file the workspace deleted, which
/// was kept (`whiteout`).
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum WsConflictCause {
Overlap,
AnchorLost,
Whiteout,
}
/// `ws.list` (decision 303): every workspace in the registry, sorted by
/// name, then id.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct WsListParams {}
/// `ws.list` result.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct WsListResult {
#[serde(default)]
pub workspaces: Vec<WsEntry>,
}
/// One workspace as the registry and this daemon know it.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct WsEntry {
/// Its id, in `ID_ENCODING`.
pub id: String,
pub name: String,
/// `main`, or the id of the workspace it was forked from.
pub base: String,
/// The base's visible frontier id when it was forked (`ID_ENCODING`).
pub base_frontier: String,
/// The subtree it was forked with, if any.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub subtree: Option<String>,
/// When it was forked, in Unix milliseconds (the fork's signed time).
pub created_ms: i64,
/// Its mirror's author, by name (`Named`), when this daemon mirrors it.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub author: Option<String>,
/// Whether this daemon runs it, so `ws.detail` lists its files.
#[serde(default)]
pub here: bool,
/// How many file paths its index names (overrides, workspace-local
/// files, whiteouts), when this daemon runs it.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub files: Option<u32>,
}
/// `ws.detail` (decision 303): one workspace, by name or id, with every
/// file path its index names. A name no workspace has is invalid params.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct WsDetailParams {
pub name: String,
}
/// `ws.detail` result. `files` is empty when this daemon doesn't run the
/// workspace (`workspace.here` false).
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct WsDetailResult {
pub workspace: WsEntry,
/// Sorted by address.
#[serde(default)]
pub files: Vec<WsFile>,
}
/// One file path a workspace's index names, and how. Its line ranges and
/// their authors are `blame`'s.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct WsFile {
pub address: String,
pub change: WsChange,
}
/// How a workspace changes a path.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum WsChange {
/// It overrides the base's document, in a range or whole.
Override,
/// It is a file only this workspace has.
Local,
/// It deletes the base's file.
Whiteout,
}