@lix-js/sdk 0.6.0-preview.3 → 0.6.0-preview.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/SKILL.md +105 -65
- package/dist/engine-wasm/index.js +4 -4
- package/dist/engine-wasm/wasm/lix_engine.d.ts +30 -6
- package/dist/engine-wasm/wasm/lix_engine.js +187 -117
- package/dist/engine-wasm/wasm/lix_engine.wasm +0 -0
- package/dist/engine-wasm/wasm/lix_engine.wasm.d.ts +14 -8
- package/dist/generated/builtin-schemas.d.ts +69 -69
- package/dist/generated/builtin-schemas.js +94 -94
- package/dist/open-lix.d.ts +42 -28
- package/dist/open-lix.js +49 -10
- package/dist/sqlite/index.js +86 -30
- package/dist-engine-src/README.md +3 -3
- package/dist-engine-src/src/backend/capabilities.rs +67 -0
- package/dist-engine-src/src/backend/conformance/baseline.rs +1127 -0
- package/dist-engine-src/src/backend/conformance/factory.rs +93 -0
- package/dist-engine-src/src/backend/conformance/failure_tests.rs +608 -0
- package/dist-engine-src/src/backend/conformance/fixtures.rs +26 -0
- package/dist-engine-src/src/backend/conformance/mod.rs +75 -0
- package/dist-engine-src/src/backend/conformance/model.rs +28 -0
- package/dist-engine-src/src/backend/conformance/model_based.rs +257 -0
- package/dist-engine-src/src/backend/conformance/persistence.rs +204 -0
- package/dist-engine-src/src/backend/conformance/projection.rs +21 -0
- package/dist-engine-src/src/backend/conformance/pushdown.rs +24 -0
- package/dist-engine-src/src/backend/conformance/runner.rs +90 -0
- package/dist-engine-src/src/backend/conformance/scan.rs +24 -0
- package/dist-engine-src/src/backend/conformance/write.rs +16 -0
- package/dist-engine-src/src/backend/error.rs +94 -0
- package/dist-engine-src/src/backend/in_memory.rs +670 -0
- package/dist-engine-src/src/backend/mod.rs +36 -9
- package/dist-engine-src/src/backend/predicate.rs +80 -0
- package/dist-engine-src/src/backend/traits.rs +260 -0
- package/dist-engine-src/src/backend/types.rs +224 -81
- package/dist-engine-src/src/binary_cas/context.rs +8 -8
- package/dist-engine-src/src/binary_cas/kv.rs +234 -259
- package/dist-engine-src/src/{version → branch}/context.rs +12 -12
- package/dist-engine-src/src/branch/lifecycle.rs +221 -0
- package/dist-engine-src/src/branch/mod.rs +13 -0
- package/dist-engine-src/src/branch/refs.rs +321 -0
- package/dist-engine-src/src/branch/stage_rows.rs +67 -0
- package/dist-engine-src/src/branch/types.rs +21 -0
- package/dist-engine-src/src/catalog/context.rs +18 -18
- package/dist-engine-src/src/catalog/snapshot.rs +8 -8
- package/dist-engine-src/src/changelog/bench_support.rs +785 -0
- package/dist-engine-src/src/changelog/change.rs +1 -0
- package/dist-engine-src/src/changelog/codec.rs +497 -0
- package/dist-engine-src/src/changelog/commit.rs +1 -0
- package/dist-engine-src/src/changelog/context.rs +1614 -0
- package/dist-engine-src/src/changelog/mod.rs +29 -0
- package/dist-engine-src/src/changelog/store.rs +163 -0
- package/dist-engine-src/src/changelog/test_support.rs +54 -0
- package/dist-engine-src/src/changelog/types.rs +213 -0
- package/dist-engine-src/src/commit_graph/context.rs +317 -274
- package/dist-engine-src/src/commit_graph/mod.rs +2 -4
- package/dist-engine-src/src/commit_graph/types.rs +22 -42
- package/dist-engine-src/src/commit_graph/walker.rs +133 -103
- package/dist-engine-src/src/common/error.rs +52 -18
- package/dist-engine-src/src/common/identity.rs +2 -2
- package/dist-engine-src/src/common/mod.rs +1 -1
- package/dist-engine-src/src/domain.rs +42 -46
- package/dist-engine-src/src/engine.rs +74 -96
- package/dist-engine-src/src/{entity_identity.rs → entity_pk.rs} +89 -92
- package/dist-engine-src/src/functions/context.rs +56 -52
- package/dist-engine-src/src/functions/state.rs +51 -52
- package/dist-engine-src/src/init.rs +288 -154
- package/dist-engine-src/src/json_store/context.rs +15 -266
- package/dist-engine-src/src/json_store/mod.rs +26 -0
- package/dist-engine-src/src/json_store/store.rs +103 -718
- package/dist-engine-src/src/json_store/types.rs +4 -9
- package/dist-engine-src/src/lib.rs +49 -19
- package/dist-engine-src/src/live_state/context.rs +654 -790
- package/dist-engine-src/src/live_state/mod.rs +9 -3
- package/dist-engine-src/src/live_state/overlay.rs +4 -4
- package/dist-engine-src/src/live_state/types.rs +30 -21
- package/dist-engine-src/src/live_state/visibility.rs +514 -71
- package/dist-engine-src/src/plugin/install.rs +48 -48
- package/dist-engine-src/src/plugin/manifest.rs +7 -7
- package/dist-engine-src/src/plugin/materializer.rs +0 -275
- package/dist-engine-src/src/plugin/plugin_manifest.json +4 -3
- package/dist-engine-src/src/schema/builtin/lix_binary_blob_ref.json +2 -2
- package/dist-engine-src/src/schema/builtin/lix_branch_descriptor.json +34 -0
- package/dist-engine-src/src/schema/builtin/lix_branch_ref.json +48 -0
- package/dist-engine-src/src/schema/builtin/lix_change.json +3 -3
- package/dist-engine-src/src/schema/builtin/lix_commit.json +1 -1
- package/dist-engine-src/src/schema/builtin/lix_label_assignment.json +6 -6
- package/dist-engine-src/src/schema/builtin/mod.rs +18 -20
- package/dist-engine-src/src/schema/compatibility.rs +11 -11
- package/dist-engine-src/src/schema/definition.json +2 -2
- package/dist-engine-src/src/schema/definition.rs +5 -5
- package/dist-engine-src/src/schema/key.rs +3 -3
- package/dist-engine-src/src/schema/mod.rs +1 -1
- package/dist-engine-src/src/schema/tests.rs +18 -18
- package/dist-engine-src/src/session/context.rs +819 -124
- package/dist-engine-src/src/session/create_branch.rs +94 -0
- package/dist-engine-src/src/session/execute.rs +260 -57
- package/dist-engine-src/src/session/merge/analysis.rs +9 -3
- package/dist-engine-src/src/session/merge/{version.rs → branch.rs} +119 -129
- package/dist-engine-src/src/session/merge/conflicts.rs +2 -2
- package/dist-engine-src/src/session/merge/mod.rs +5 -6
- package/dist-engine-src/src/session/merge/stats.rs +7 -11
- package/dist-engine-src/src/session/mod.rs +19 -16
- package/dist-engine-src/src/session/switch_branch.rs +113 -0
- package/dist-engine-src/src/session/transaction.rs +557 -0
- package/dist-engine-src/src/sql2/bind/classify.rs +102 -0
- package/dist-engine-src/src/sql2/bind/error.rs +5 -0
- package/dist-engine-src/src/sql2/bind/expr.rs +29 -0
- package/dist-engine-src/src/sql2/bind/mod.rs +12 -0
- package/dist-engine-src/src/sql2/{udfs/public_call.rs → bind/public_udf.rs} +98 -3
- package/dist-engine-src/src/sql2/bind/read.rs +65 -0
- package/dist-engine-src/src/sql2/bind/statement.rs +2236 -0
- package/dist-engine-src/src/sql2/bind/table.rs +273 -0
- package/dist-engine-src/src/sql2/bind/write.rs +86 -0
- package/dist-engine-src/src/sql2/branch_scope.rs +436 -0
- package/dist-engine-src/src/sql2/catalog/capability.rs +20 -0
- package/dist-engine-src/src/sql2/catalog/entity_surface.rs +296 -0
- package/dist-engine-src/src/sql2/catalog/mod.rs +15 -0
- package/dist-engine-src/src/sql2/catalog/registry.rs +556 -0
- package/dist-engine-src/src/sql2/catalog/schema.rs +88 -0
- package/dist-engine-src/src/sql2/catalog/surface.rs +41 -0
- package/dist-engine-src/src/sql2/change_materialization.rs +122 -0
- package/dist-engine-src/src/sql2/context.rs +36 -30
- package/dist-engine-src/src/sql2/error.rs +4 -5
- package/dist-engine-src/src/sql2/exec/bound_public_write.rs +1593 -0
- package/dist-engine-src/src/sql2/exec/datafusion.rs +5266 -0
- package/dist-engine-src/src/sql2/exec/fast_write.rs +82 -0
- package/dist-engine-src/src/sql2/exec/mod.rs +24 -0
- package/dist-engine-src/src/sql2/exec/write.rs +661 -0
- package/dist-engine-src/src/sql2/filesystem_planner.rs +72 -77
- package/dist-engine-src/src/sql2/filesystem_visibility.rs +21 -21
- package/dist-engine-src/src/sql2/history_projection.rs +8 -8
- package/dist-engine-src/src/sql2/history_route.rs +35 -31
- package/dist-engine-src/src/sql2/mod.rs +30 -24
- package/dist-engine-src/src/sql2/optimize/datafusion.rs +1 -0
- package/dist-engine-src/src/sql2/optimize/mod.rs +2 -0
- package/dist-engine-src/src/sql2/optimize/simple_write.rs +116 -0
- package/dist-engine-src/src/sql2/parse/mod.rs +69 -0
- package/dist-engine-src/src/sql2/parse/normalize.rs +1 -0
- package/dist-engine-src/src/sql2/plan/branch_scope.rs +24 -0
- package/dist-engine-src/src/sql2/plan/mod.rs +5 -0
- package/dist-engine-src/src/sql2/plan/predicate.rs +22 -0
- package/dist-engine-src/src/sql2/plan/write.rs +147 -0
- package/dist-engine-src/src/sql2/predicate_typecheck.rs +258 -0
- package/dist-engine-src/src/sql2/{version_provider.rs → providers/branch.rs} +218 -214
- package/dist-engine-src/src/sql2/{change_provider.rs → providers/change.rs} +156 -42
- package/dist-engine-src/src/sql2/{directory_provider.rs → providers/directory.rs} +291 -322
- package/dist-engine-src/src/sql2/{directory_history_provider.rs → providers/directory_history.rs} +56 -42
- package/dist-engine-src/src/sql2/providers/entity.rs +1484 -0
- package/dist-engine-src/src/sql2/{entity_history_provider.rs → providers/entity_history.rs} +43 -31
- package/dist-engine-src/src/sql2/{file_provider.rs → providers/file.rs} +323 -316
- package/dist-engine-src/src/sql2/{file_history_provider.rs → providers/file_history.rs} +60 -46
- package/dist-engine-src/src/sql2/{history_provider.rs → providers/history.rs} +46 -32
- package/dist-engine-src/src/sql2/{lix_state_provider.rs → providers/lix_state.rs} +359 -329
- package/dist-engine-src/src/sql2/providers/mod.rs +508 -0
- package/dist-engine-src/src/sql2/read_only.rs +2 -2
- package/dist-engine-src/src/sql2/session.rs +47 -96
- package/dist-engine-src/src/sql2/storage/constraints.rs +1 -0
- package/dist-engine-src/src/sql2/storage/mod.rs +1 -0
- package/dist-engine-src/src/sql2/test_support/differential.rs +712 -0
- package/dist-engine-src/src/sql2/test_support/generators.rs +354 -0
- package/dist-engine-src/src/sql2/test_support/mod.rs +2 -0
- package/dist-engine-src/src/sql2/udfs/{lix_active_version_commit_id.rs → lix_active_branch_commit_id.rs} +7 -7
- package/dist-engine-src/src/sql2/udfs/mod.rs +3 -6
- package/dist-engine-src/src/sql2/write_normalization.rs +45 -22
- package/dist-engine-src/src/storage/conformance.rs +399 -0
- package/dist-engine-src/src/storage/context.rs +552 -288
- package/dist-engine-src/src/storage/mod.rs +48 -10
- package/dist-engine-src/src/storage/point.rs +440 -0
- package/dist-engine-src/src/storage/read_scope.rs +43 -64
- package/dist-engine-src/src/storage/reader.rs +867 -0
- package/dist-engine-src/src/storage/scan.rs +784 -0
- package/dist-engine-src/src/storage/spaces.rs +236 -0
- package/dist-engine-src/src/storage/stats.rs +80 -0
- package/dist-engine-src/src/storage/write_set.rs +962 -0
- package/dist-engine-src/src/storage_bench.rs +136 -4828
- package/dist-engine-src/src/test_support.rs +360 -138
- package/dist-engine-src/src/tracked_state/bench_support.rs +394 -0
- package/dist-engine-src/src/tracked_state/codec.rs +155 -1057
- package/dist-engine-src/src/tracked_state/commit_root_rebuild.rs +358 -0
- package/dist-engine-src/src/tracked_state/context.rs +1927 -993
- package/dist-engine-src/src/tracked_state/diff.rs +1715 -261
- package/dist-engine-src/src/tracked_state/merge.rs +74 -88
- package/dist-engine-src/src/tracked_state/mod.rs +19 -16
- package/dist-engine-src/src/tracked_state/{materialization.rs → row_materialization.rs} +50 -178
- package/dist-engine-src/src/tracked_state/storage.rs +243 -191
- package/dist-engine-src/src/tracked_state/tree.rs +247 -371
- package/dist-engine-src/src/tracked_state/types.rs +49 -42
- package/dist-engine-src/src/transaction/bench_support.rs +407 -0
- package/dist-engine-src/src/transaction/commit.rs +821 -713
- package/dist-engine-src/src/transaction/context.rs +705 -600
- package/dist-engine-src/src/transaction/mod.rs +13 -2
- package/dist-engine-src/src/transaction/normalization.rs +63 -76
- package/dist-engine-src/src/transaction/prep.rs +13 -13
- package/dist-engine-src/src/transaction/schema_resolver.rs +19 -5
- package/dist-engine-src/src/transaction/staging.rs +228 -434
- package/dist-engine-src/src/transaction/types.rs +41 -98
- package/dist-engine-src/src/transaction/validation.rs +382 -446
- package/dist-engine-src/src/untracked_state/codec.rs +337 -29
- package/dist-engine-src/src/untracked_state/context.rs +7 -7
- package/dist-engine-src/src/untracked_state/materialization.rs +2 -2
- package/dist-engine-src/src/untracked_state/mod.rs +1 -1
- package/dist-engine-src/src/untracked_state/storage.rs +659 -157
- package/dist-engine-src/src/untracked_state/types.rs +21 -21
- package/package.json +71 -68
- package/dist-engine-src/src/backend/kv.rs +0 -358
- package/dist-engine-src/src/backend/testing.rs +0 -658
- package/dist-engine-src/src/commit_store/codec.rs +0 -887
- package/dist-engine-src/src/commit_store/context.rs +0 -944
- package/dist-engine-src/src/commit_store/materialization.rs +0 -84
- package/dist-engine-src/src/commit_store/mod.rs +0 -16
- package/dist-engine-src/src/commit_store/storage.rs +0 -600
- package/dist-engine-src/src/commit_store/types.rs +0 -215
- package/dist-engine-src/src/schema/builtin/lix_version_descriptor.json +0 -34
- package/dist-engine-src/src/schema/builtin/lix_version_ref.json +0 -48
- package/dist-engine-src/src/session/create_version.rs +0 -88
- package/dist-engine-src/src/session/merge/apply.rs +0 -23
- package/dist-engine-src/src/session/optimization9_sql2_bench.rs +0 -100
- package/dist-engine-src/src/session/switch_version.rs +0 -109
- package/dist-engine-src/src/sql2/classify.rs +0 -182
- package/dist-engine-src/src/sql2/entity_provider.rs +0 -3211
- package/dist-engine-src/src/sql2/execute.rs +0 -3440
- package/dist-engine-src/src/sql2/public_bind/assignment.rs +0 -46
- package/dist-engine-src/src/sql2/public_bind/capability.rs +0 -41
- package/dist-engine-src/src/sql2/public_bind/dml.rs +0 -166
- package/dist-engine-src/src/sql2/public_bind/mod.rs +0 -25
- package/dist-engine-src/src/sql2/public_bind/table.rs +0 -168
- package/dist-engine-src/src/sql2/version_scope.rs +0 -394
- package/dist-engine-src/src/storage/types.rs +0 -501
- package/dist-engine-src/src/tracked_state/by_file_index.rs +0 -98
- package/dist-engine-src/src/tracked_state/materializer.rs +0 -488
- package/dist-engine-src/src/transaction/live_state_overlay.rs +0 -35
- package/dist-engine-src/src/version/lifecycle.rs +0 -221
- package/dist-engine-src/src/version/mod.rs +0 -13
- package/dist-engine-src/src/version/refs.rs +0 -330
- package/dist-engine-src/src/version/stage_rows.rs +0 -67
- package/dist-engine-src/src/version/types.rs +0 -21
|
@@ -1,31 +1,31 @@
|
|
|
1
1
|
use std::sync::Arc;
|
|
2
2
|
|
|
3
|
-
use crate::storage::{
|
|
3
|
+
use crate::storage::{StorageRead, StorageWriteSet};
|
|
4
4
|
use crate::untracked_state::{UntrackedStateContext, UntrackedStateRow};
|
|
5
5
|
|
|
6
|
-
use super::refs::
|
|
7
|
-
use super::
|
|
6
|
+
use super::refs::BranchRefContext;
|
|
7
|
+
use super::BranchRefReader;
|
|
8
8
|
|
|
9
|
-
/// Aggregate entrypoint for
|
|
9
|
+
/// Aggregate entrypoint for branch-domain services.
|
|
10
10
|
///
|
|
11
11
|
/// Today this owns the moving-ref subsystem. Descriptor helpers are re-exported
|
|
12
|
-
/// by `
|
|
12
|
+
/// by `branch`; future branch APIs can grow here without making session or
|
|
13
13
|
/// SQL code depend directly on ref storage details.
|
|
14
|
-
pub(crate) struct
|
|
15
|
-
refs: Arc<
|
|
14
|
+
pub(crate) struct BranchContext {
|
|
15
|
+
refs: Arc<BranchRefContext>,
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
impl
|
|
18
|
+
impl BranchContext {
|
|
19
19
|
pub(crate) fn new(untracked_state: Arc<UntrackedStateContext>) -> Self {
|
|
20
20
|
Self {
|
|
21
|
-
refs: Arc::new(
|
|
21
|
+
refs: Arc::new(BranchRefContext::new(untracked_state)),
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
/// Creates a
|
|
26
|
-
pub(crate) fn ref_reader<S>(&self, store: S) -> impl
|
|
25
|
+
/// Creates a branch-ref reader over a caller-provided KV store.
|
|
26
|
+
pub(crate) fn ref_reader<S>(&self, store: S) -> impl BranchRefReader
|
|
27
27
|
where
|
|
28
|
-
S:
|
|
28
|
+
S: StorageRead + Send + Sync,
|
|
29
29
|
{
|
|
30
30
|
self.refs.reader(store)
|
|
31
31
|
}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
use crate::commit_graph::{CommitGraphCommit, CommitGraphReader};
|
|
2
|
+
use crate::common::validate_non_empty_identity_value;
|
|
3
|
+
use crate::LixError;
|
|
4
|
+
|
|
5
|
+
use super::{BranchHead, BranchRefReader};
|
|
6
|
+
|
|
7
|
+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
8
|
+
pub(crate) enum BranchOperation {
|
|
9
|
+
CreateBranch,
|
|
10
|
+
SwitchBranch,
|
|
11
|
+
MergeBranch,
|
|
12
|
+
MergeBranchPreview,
|
|
13
|
+
LoadWorkspaceSelector,
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
impl BranchOperation {
|
|
17
|
+
pub(crate) fn label(self) -> &'static str {
|
|
18
|
+
match self {
|
|
19
|
+
Self::CreateBranch => "create_branch",
|
|
20
|
+
Self::SwitchBranch => "switch_branch",
|
|
21
|
+
Self::MergeBranch => "merge_branch",
|
|
22
|
+
Self::MergeBranchPreview => "merge_branch_preview",
|
|
23
|
+
Self::LoadWorkspaceSelector => "load_workspace_branch_id",
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
29
|
+
pub(crate) enum BranchReferenceRole {
|
|
30
|
+
Source,
|
|
31
|
+
Target,
|
|
32
|
+
WorkspaceSelector,
|
|
33
|
+
CommitSource,
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
impl BranchReferenceRole {
|
|
37
|
+
pub(crate) fn label(self) -> &'static str {
|
|
38
|
+
match self {
|
|
39
|
+
Self::Source => "source",
|
|
40
|
+
Self::Target => "target",
|
|
41
|
+
Self::WorkspaceSelector => "workspace_selector",
|
|
42
|
+
Self::CommitSource => "commit_source",
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/// Shared domain service for resolving public branch references.
|
|
48
|
+
///
|
|
49
|
+
/// Built-in branch schemas describe row shape. This service owns semantic
|
|
50
|
+
/// ref validation: non-empty ids, global sentinel handling, and missing refs.
|
|
51
|
+
pub(crate) struct BranchLifecycle<'a> {
|
|
52
|
+
refs: &'a dyn BranchRefReader,
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
impl<'a> BranchLifecycle<'a> {
|
|
56
|
+
pub(crate) fn new(refs: &'a dyn BranchRefReader) -> Self {
|
|
57
|
+
Self { refs }
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
pub(crate) fn require_non_empty_id(
|
|
61
|
+
branch_id: &str,
|
|
62
|
+
operation: BranchOperation,
|
|
63
|
+
role: BranchReferenceRole,
|
|
64
|
+
) -> Result<(), LixError> {
|
|
65
|
+
require_non_empty_public_id("branch_id", branch_id, operation, role)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
pub(crate) async fn require_existing_commit(
|
|
69
|
+
commit_graph: &mut dyn CommitGraphReader,
|
|
70
|
+
commit_id: &str,
|
|
71
|
+
operation: BranchOperation,
|
|
72
|
+
role: BranchReferenceRole,
|
|
73
|
+
) -> Result<CommitGraphCommit, LixError> {
|
|
74
|
+
require_non_empty_public_id("commit_id", commit_id, operation, role)?;
|
|
75
|
+
commit_graph
|
|
76
|
+
.load_commit(commit_id)
|
|
77
|
+
.await?
|
|
78
|
+
.ok_or_else(|| LixError::commit_not_found(commit_id, operation.label(), role.label()))
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
pub(crate) async fn require_existing_ref(
|
|
82
|
+
&self,
|
|
83
|
+
branch_id: &str,
|
|
84
|
+
operation: BranchOperation,
|
|
85
|
+
role: BranchReferenceRole,
|
|
86
|
+
) -> Result<BranchHead, LixError> {
|
|
87
|
+
Self::require_non_empty_id(branch_id, operation, role)?;
|
|
88
|
+
self.require_existing_stored_ref(branch_id, operation, role)
|
|
89
|
+
.await
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
pub(crate) async fn require_existing_commit_id(
|
|
93
|
+
&self,
|
|
94
|
+
branch_id: &str,
|
|
95
|
+
operation: BranchOperation,
|
|
96
|
+
role: BranchReferenceRole,
|
|
97
|
+
) -> Result<String, LixError> {
|
|
98
|
+
Ok(self
|
|
99
|
+
.require_existing_ref(branch_id, operation, role)
|
|
100
|
+
.await?
|
|
101
|
+
.commit_id)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async fn require_existing_stored_ref(
|
|
105
|
+
&self,
|
|
106
|
+
branch_id: &str,
|
|
107
|
+
operation: BranchOperation,
|
|
108
|
+
role: BranchReferenceRole,
|
|
109
|
+
) -> Result<BranchHead, LixError> {
|
|
110
|
+
self.refs
|
|
111
|
+
.load_head(branch_id)
|
|
112
|
+
.await?
|
|
113
|
+
.ok_or_else(|| LixError::branch_not_found(branch_id, operation.label(), role.label()))
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
fn require_non_empty_public_id(
|
|
118
|
+
label: &str,
|
|
119
|
+
value: &str,
|
|
120
|
+
operation: BranchOperation,
|
|
121
|
+
role: BranchReferenceRole,
|
|
122
|
+
) -> Result<(), LixError> {
|
|
123
|
+
validate_non_empty_identity_value(label, value)
|
|
124
|
+
.map(|_| ())
|
|
125
|
+
.map_err(|_| {
|
|
126
|
+
LixError::new(
|
|
127
|
+
LixError::CODE_INVALID_PARAM,
|
|
128
|
+
format!(
|
|
129
|
+
"{} {} {label} must be non-empty",
|
|
130
|
+
operation.label(),
|
|
131
|
+
role.label()
|
|
132
|
+
),
|
|
133
|
+
)
|
|
134
|
+
})
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
#[cfg(test)]
|
|
138
|
+
mod tests {
|
|
139
|
+
use async_trait::async_trait;
|
|
140
|
+
|
|
141
|
+
use super::*;
|
|
142
|
+
|
|
143
|
+
#[tokio::test]
|
|
144
|
+
async fn require_existing_ref_returns_head() {
|
|
145
|
+
let reader = RowsBranchRefReader::new(vec![BranchHead {
|
|
146
|
+
branch_id: "branch-a".to_string(),
|
|
147
|
+
commit_id: "commit-a".to_string(),
|
|
148
|
+
}]);
|
|
149
|
+
let lifecycle = BranchLifecycle::new(&reader);
|
|
150
|
+
|
|
151
|
+
let head = lifecycle
|
|
152
|
+
.require_existing_ref(
|
|
153
|
+
"branch-a",
|
|
154
|
+
BranchOperation::SwitchBranch,
|
|
155
|
+
BranchReferenceRole::Target,
|
|
156
|
+
)
|
|
157
|
+
.await
|
|
158
|
+
.expect("branch should resolve");
|
|
159
|
+
|
|
160
|
+
assert_eq!(head.commit_id, "commit-a");
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
#[tokio::test]
|
|
164
|
+
async fn require_existing_ref_rejects_empty_id_as_invalid_param() {
|
|
165
|
+
let reader = RowsBranchRefReader::new(Vec::new());
|
|
166
|
+
let lifecycle = BranchLifecycle::new(&reader);
|
|
167
|
+
|
|
168
|
+
let error = lifecycle
|
|
169
|
+
.require_existing_ref(
|
|
170
|
+
"",
|
|
171
|
+
BranchOperation::SwitchBranch,
|
|
172
|
+
BranchReferenceRole::Target,
|
|
173
|
+
)
|
|
174
|
+
.await
|
|
175
|
+
.expect_err("empty branch id should be rejected before lookup");
|
|
176
|
+
|
|
177
|
+
assert_eq!(error.code, LixError::CODE_INVALID_PARAM);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
#[tokio::test]
|
|
181
|
+
async fn require_existing_ref_reports_missing_branch() {
|
|
182
|
+
let reader = RowsBranchRefReader::new(Vec::new());
|
|
183
|
+
let lifecycle = BranchLifecycle::new(&reader);
|
|
184
|
+
|
|
185
|
+
let error = lifecycle
|
|
186
|
+
.require_existing_ref(
|
|
187
|
+
"missing",
|
|
188
|
+
BranchOperation::SwitchBranch,
|
|
189
|
+
BranchReferenceRole::Target,
|
|
190
|
+
)
|
|
191
|
+
.await
|
|
192
|
+
.expect_err("missing branch should be rejected");
|
|
193
|
+
|
|
194
|
+
assert_eq!(error.code, LixError::CODE_BRANCH_NOT_FOUND);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
struct RowsBranchRefReader {
|
|
198
|
+
heads: Vec<BranchHead>,
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
impl RowsBranchRefReader {
|
|
202
|
+
fn new(heads: Vec<BranchHead>) -> Self {
|
|
203
|
+
Self { heads }
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
#[async_trait]
|
|
208
|
+
impl BranchRefReader for RowsBranchRefReader {
|
|
209
|
+
async fn load_head(&self, branch_id: &str) -> Result<Option<BranchHead>, LixError> {
|
|
210
|
+
Ok(self
|
|
211
|
+
.heads
|
|
212
|
+
.iter()
|
|
213
|
+
.find(|head| head.branch_id == branch_id)
|
|
214
|
+
.cloned())
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
async fn scan_heads(&self) -> Result<Vec<BranchHead>, LixError> {
|
|
218
|
+
Ok(self.heads.clone())
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
mod context;
|
|
2
|
+
mod lifecycle;
|
|
3
|
+
mod refs;
|
|
4
|
+
mod stage_rows;
|
|
5
|
+
mod types;
|
|
6
|
+
|
|
7
|
+
pub(crate) use context::BranchContext;
|
|
8
|
+
pub(crate) use lifecycle::{BranchLifecycle, BranchOperation, BranchReferenceRole};
|
|
9
|
+
pub(crate) use stage_rows::{
|
|
10
|
+
branch_descriptor_stage_row, branch_descriptor_tombstone_row, branch_ref_stage_row,
|
|
11
|
+
branch_ref_tombstone_row, BRANCH_DESCRIPTOR_SCHEMA_KEY, BRANCH_REF_SCHEMA_KEY,
|
|
12
|
+
};
|
|
13
|
+
pub(crate) use types::{BranchHead, BranchRefReader};
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
use std::sync::Arc;
|
|
2
|
+
|
|
3
|
+
use tokio::sync::Mutex;
|
|
4
|
+
|
|
5
|
+
use crate::branch::BRANCH_REF_SCHEMA_KEY;
|
|
6
|
+
use crate::branch::{BranchHead, BranchRefReader};
|
|
7
|
+
use crate::entity_pk::EntityPk;
|
|
8
|
+
use crate::storage::{StorageRead, StorageWriteSet};
|
|
9
|
+
use crate::untracked_state::{
|
|
10
|
+
MaterializedUntrackedStateRow, UntrackedStateContext, UntrackedStateFilter, UntrackedStateRow,
|
|
11
|
+
UntrackedStateRowRequest, UntrackedStateScanRequest,
|
|
12
|
+
};
|
|
13
|
+
use crate::GLOBAL_BRANCH_ID;
|
|
14
|
+
use crate::{LixError, NullableKeyFilter};
|
|
15
|
+
|
|
16
|
+
/// Typed access to moving branch heads stored in untracked state.
|
|
17
|
+
///
|
|
18
|
+
/// Branch refs are one of the inputs used by live_state visibility, so this
|
|
19
|
+
/// context deliberately bypasses live_state and reads the underlying untracked
|
|
20
|
+
/// rows directly. That keeps the dependency acyclic:
|
|
21
|
+
/// untracked_state -> branch_ref -> live_state.
|
|
22
|
+
pub(super) struct BranchRefContext {
|
|
23
|
+
untracked_state: Arc<UntrackedStateContext>,
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
impl BranchRefContext {
|
|
27
|
+
pub(super) fn new(untracked_state: Arc<UntrackedStateContext>) -> Self {
|
|
28
|
+
Self { untracked_state }
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/// Creates a branch-ref reader over a caller-provided KV store.
|
|
32
|
+
pub(super) fn reader<S>(&self, store: S) -> BranchRefStoreReader<S>
|
|
33
|
+
where
|
|
34
|
+
S: StorageRead + Send + Sync,
|
|
35
|
+
{
|
|
36
|
+
BranchRefStoreReader {
|
|
37
|
+
untracked_state: Arc::clone(&self.untracked_state),
|
|
38
|
+
store: Mutex::new(store),
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/// Creates a branch-ref writer over a transaction-local storage write set.
|
|
43
|
+
pub(super) fn writer<'a>(&self, writes: &'a mut StorageWriteSet) -> BranchRefWriter<'a> {
|
|
44
|
+
BranchRefWriter {
|
|
45
|
+
untracked_state: Arc::clone(&self.untracked_state),
|
|
46
|
+
writes,
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/// Read side for branch heads.
|
|
52
|
+
pub(super) struct BranchRefStoreReader<S>
|
|
53
|
+
where
|
|
54
|
+
S: StorageRead + Send + Sync,
|
|
55
|
+
{
|
|
56
|
+
untracked_state: Arc<UntrackedStateContext>,
|
|
57
|
+
store: Mutex<S>,
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
impl<S> BranchRefStoreReader<S>
|
|
61
|
+
where
|
|
62
|
+
S: StorageRead + Send + Sync,
|
|
63
|
+
{
|
|
64
|
+
pub(crate) async fn load_head(&self, branch_id: &str) -> Result<Option<BranchHead>, LixError> {
|
|
65
|
+
let store = self.store.lock().await;
|
|
66
|
+
let Some(row) = self
|
|
67
|
+
.untracked_state
|
|
68
|
+
.reader(&*store)
|
|
69
|
+
.load_row(&UntrackedStateRowRequest {
|
|
70
|
+
schema_key: BRANCH_REF_SCHEMA_KEY.to_string(),
|
|
71
|
+
branch_id: GLOBAL_BRANCH_ID.to_string(),
|
|
72
|
+
entity_pk: EntityPk::single(branch_id),
|
|
73
|
+
file_id: NullableKeyFilter::Null,
|
|
74
|
+
})
|
|
75
|
+
.await?
|
|
76
|
+
else {
|
|
77
|
+
return Ok(None);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
decode_branch_head(branch_id, &row)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
pub(crate) async fn load_head_commit_id(
|
|
84
|
+
&self,
|
|
85
|
+
branch_id: &str,
|
|
86
|
+
) -> Result<Option<String>, LixError> {
|
|
87
|
+
Ok(self.load_head(branch_id).await?.map(|head| head.commit_id))
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
pub(crate) async fn scan_heads(&self) -> Result<Vec<BranchHead>, LixError> {
|
|
91
|
+
let store = self.store.lock().await;
|
|
92
|
+
let rows = self
|
|
93
|
+
.untracked_state
|
|
94
|
+
.reader(&*store)
|
|
95
|
+
.scan_rows(&UntrackedStateScanRequest {
|
|
96
|
+
filter: UntrackedStateFilter {
|
|
97
|
+
schema_keys: vec![BRANCH_REF_SCHEMA_KEY.to_string()],
|
|
98
|
+
branch_ids: vec![GLOBAL_BRANCH_ID.to_string()],
|
|
99
|
+
..UntrackedStateFilter::default()
|
|
100
|
+
},
|
|
101
|
+
..UntrackedStateScanRequest::default()
|
|
102
|
+
})
|
|
103
|
+
.await?;
|
|
104
|
+
let mut heads = rows
|
|
105
|
+
.iter()
|
|
106
|
+
.map(|row| {
|
|
107
|
+
let branch_id = row.entity_pk.as_single_string_owned()?;
|
|
108
|
+
decode_branch_head(&branch_id, row)
|
|
109
|
+
})
|
|
110
|
+
.collect::<Result<Vec<_>, _>>()?
|
|
111
|
+
.into_iter()
|
|
112
|
+
.flatten()
|
|
113
|
+
.collect::<Vec<_>>();
|
|
114
|
+
heads.sort_by(|left, right| left.branch_id.cmp(&right.branch_id));
|
|
115
|
+
Ok(heads)
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
#[async_trait::async_trait]
|
|
120
|
+
impl<S> BranchRefReader for BranchRefStoreReader<S>
|
|
121
|
+
where
|
|
122
|
+
S: StorageRead + Send + Sync,
|
|
123
|
+
{
|
|
124
|
+
async fn load_head(&self, branch_id: &str) -> Result<Option<BranchHead>, LixError> {
|
|
125
|
+
BranchRefStoreReader::load_head(self, branch_id).await
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async fn load_head_commit_id(&self, branch_id: &str) -> Result<Option<String>, LixError> {
|
|
129
|
+
BranchRefStoreReader::load_head_commit_id(self, branch_id).await
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async fn scan_heads(&self) -> Result<Vec<BranchHead>, LixError> {
|
|
133
|
+
BranchRefStoreReader::scan_heads(self).await
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/// Write side for moving branch heads.
|
|
138
|
+
pub(super) struct BranchRefWriter<'a> {
|
|
139
|
+
untracked_state: Arc<UntrackedStateContext>,
|
|
140
|
+
writes: &'a mut StorageWriteSet,
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
impl BranchRefWriter<'_> {
|
|
144
|
+
pub(crate) fn stage_rows(&mut self, rows: &[UntrackedStateRow]) -> Result<(), LixError> {
|
|
145
|
+
self.untracked_state
|
|
146
|
+
.writer(self.writes)
|
|
147
|
+
.stage_rows(rows.iter().map(|row| row.as_ref()))
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
fn decode_branch_head(
|
|
152
|
+
requested_branch_id: &str,
|
|
153
|
+
row: &MaterializedUntrackedStateRow,
|
|
154
|
+
) -> Result<Option<BranchHead>, LixError> {
|
|
155
|
+
let Some(snapshot_content) = row.snapshot_content.as_deref() else {
|
|
156
|
+
return Ok(None);
|
|
157
|
+
};
|
|
158
|
+
let snapshot =
|
|
159
|
+
serde_json::from_str::<serde_json::Value>(snapshot_content).map_err(|error| {
|
|
160
|
+
LixError::new(
|
|
161
|
+
"LIX_ERROR_UNKNOWN",
|
|
162
|
+
format!("engine branch-ref snapshot parse failed: {error}"),
|
|
163
|
+
)
|
|
164
|
+
})?;
|
|
165
|
+
let commit_id = snapshot
|
|
166
|
+
.get("commit_id")
|
|
167
|
+
.and_then(serde_json::Value::as_str)
|
|
168
|
+
.ok_or_else(|| {
|
|
169
|
+
LixError::new(
|
|
170
|
+
"LIX_ERROR_UNKNOWN",
|
|
171
|
+
format!("branch ref for branch '{requested_branch_id}' is missing commit_id"),
|
|
172
|
+
)
|
|
173
|
+
})?;
|
|
174
|
+
Ok(Some(BranchHead {
|
|
175
|
+
branch_id: requested_branch_id.to_string(),
|
|
176
|
+
commit_id: commit_id.to_string(),
|
|
177
|
+
}))
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
#[cfg(test)]
|
|
181
|
+
mod tests {
|
|
182
|
+
use std::sync::Arc;
|
|
183
|
+
|
|
184
|
+
use crate::storage::{InMemoryStorageBackend, StorageReadOptions, StorageWriteOptions};
|
|
185
|
+
use crate::storage::{StorageContext, StorageWriteSet};
|
|
186
|
+
use crate::transaction::prepare_branch_ref_row;
|
|
187
|
+
use crate::untracked_state::{UntrackedStateContext, UntrackedStateRowRequest};
|
|
188
|
+
|
|
189
|
+
use super::*;
|
|
190
|
+
|
|
191
|
+
#[tokio::test]
|
|
192
|
+
async fn load_head_returns_none_when_missing() {
|
|
193
|
+
let storage = StorageContext::new(InMemoryStorageBackend::new());
|
|
194
|
+
let branch_ref = test_branch_ref();
|
|
195
|
+
let read = storage
|
|
196
|
+
.begin_read(StorageReadOptions::default())
|
|
197
|
+
.expect("read should open");
|
|
198
|
+
|
|
199
|
+
let head = branch_ref
|
|
200
|
+
.reader(read)
|
|
201
|
+
.load_head("missing-branch")
|
|
202
|
+
.await
|
|
203
|
+
.expect("missing branch ref should load cleanly");
|
|
204
|
+
|
|
205
|
+
assert_eq!(head, None);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
#[tokio::test]
|
|
209
|
+
async fn advance_head_writes_untracked_global_ref() {
|
|
210
|
+
let storage = StorageContext::new(InMemoryStorageBackend::new());
|
|
211
|
+
let branch_ref = BranchRefContext::new(Arc::new(UntrackedStateContext::new()));
|
|
212
|
+
|
|
213
|
+
let mut writes = storage.new_write_set();
|
|
214
|
+
stage_branch_head(
|
|
215
|
+
&branch_ref,
|
|
216
|
+
&mut writes,
|
|
217
|
+
"branch-a",
|
|
218
|
+
"commit-a",
|
|
219
|
+
"2026-01-01T00:00:00Z",
|
|
220
|
+
)
|
|
221
|
+
.expect("branch head should advance");
|
|
222
|
+
storage
|
|
223
|
+
.commit_write_set(writes, StorageWriteOptions::default())
|
|
224
|
+
.expect("branch head should commit");
|
|
225
|
+
|
|
226
|
+
let read = storage
|
|
227
|
+
.begin_read(StorageReadOptions::default())
|
|
228
|
+
.expect("read should open");
|
|
229
|
+
let head = branch_ref
|
|
230
|
+
.reader(read)
|
|
231
|
+
.load_head("branch-a")
|
|
232
|
+
.await
|
|
233
|
+
.expect("branch head should load")
|
|
234
|
+
.expect("branch head should exist");
|
|
235
|
+
assert_eq!(head.branch_id, "branch-a");
|
|
236
|
+
assert_eq!(head.commit_id, "commit-a");
|
|
237
|
+
|
|
238
|
+
let read = storage
|
|
239
|
+
.begin_read(StorageReadOptions::default())
|
|
240
|
+
.expect("read should open");
|
|
241
|
+
let mut reader = UntrackedStateContext::new().reader(read);
|
|
242
|
+
let row = reader
|
|
243
|
+
.load_row(&UntrackedStateRowRequest {
|
|
244
|
+
schema_key: BRANCH_REF_SCHEMA_KEY.to_string(),
|
|
245
|
+
branch_id: GLOBAL_BRANCH_ID.to_string(),
|
|
246
|
+
entity_pk: crate::entity_pk::EntityPk::single("branch-a"),
|
|
247
|
+
file_id: NullableKeyFilter::Null,
|
|
248
|
+
})
|
|
249
|
+
.await
|
|
250
|
+
.expect("branch-ref row should load")
|
|
251
|
+
.expect("branch-ref row should exist");
|
|
252
|
+
assert!(row.global);
|
|
253
|
+
assert_eq!(row.created_at, "2026-01-01T00:00:00Z");
|
|
254
|
+
assert_eq!(row.updated_at, "2026-01-01T00:00:00Z");
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
#[tokio::test]
|
|
258
|
+
async fn scan_heads_returns_sorted_branch_heads() {
|
|
259
|
+
let storage = StorageContext::new(InMemoryStorageBackend::new());
|
|
260
|
+
let branch_ref = test_branch_ref();
|
|
261
|
+
|
|
262
|
+
let mut writes = storage.new_write_set();
|
|
263
|
+
stage_branch_head(
|
|
264
|
+
&branch_ref,
|
|
265
|
+
&mut writes,
|
|
266
|
+
"branch-b",
|
|
267
|
+
"commit-b",
|
|
268
|
+
"2026-01-01T00:00:00Z",
|
|
269
|
+
)
|
|
270
|
+
.expect("branch-b should advance");
|
|
271
|
+
stage_branch_head(
|
|
272
|
+
&branch_ref,
|
|
273
|
+
&mut writes,
|
|
274
|
+
"branch-a",
|
|
275
|
+
"commit-a",
|
|
276
|
+
"2026-01-01T00:00:00Z",
|
|
277
|
+
)
|
|
278
|
+
.expect("branch-a should advance");
|
|
279
|
+
storage
|
|
280
|
+
.commit_write_set(writes, StorageWriteOptions::default())
|
|
281
|
+
.expect("branch heads should commit");
|
|
282
|
+
|
|
283
|
+
let read = storage
|
|
284
|
+
.begin_read(StorageReadOptions::default())
|
|
285
|
+
.expect("read should open");
|
|
286
|
+
let heads = branch_ref
|
|
287
|
+
.reader(read)
|
|
288
|
+
.scan_heads()
|
|
289
|
+
.await
|
|
290
|
+
.expect("heads should scan");
|
|
291
|
+
|
|
292
|
+
assert_eq!(
|
|
293
|
+
heads,
|
|
294
|
+
vec![
|
|
295
|
+
BranchHead {
|
|
296
|
+
branch_id: "branch-a".to_string(),
|
|
297
|
+
commit_id: "commit-a".to_string(),
|
|
298
|
+
},
|
|
299
|
+
BranchHead {
|
|
300
|
+
branch_id: "branch-b".to_string(),
|
|
301
|
+
commit_id: "commit-b".to_string(),
|
|
302
|
+
},
|
|
303
|
+
]
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
fn test_branch_ref() -> BranchRefContext {
|
|
308
|
+
BranchRefContext::new(Arc::new(UntrackedStateContext::new()))
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
fn stage_branch_head(
|
|
312
|
+
branch_ref: &BranchRefContext,
|
|
313
|
+
writes: &mut StorageWriteSet,
|
|
314
|
+
branch_id: &str,
|
|
315
|
+
commit_id: &str,
|
|
316
|
+
timestamp: &str,
|
|
317
|
+
) -> Result<(), LixError> {
|
|
318
|
+
let canonical_row = prepare_branch_ref_row(branch_id, commit_id, timestamp)?;
|
|
319
|
+
branch_ref.writer(writes).stage_rows(&[canonical_row.row])
|
|
320
|
+
}
|
|
321
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
use serde_json::json;
|
|
2
|
+
|
|
3
|
+
use crate::entity_pk::EntityPk;
|
|
4
|
+
use crate::transaction::types::{TransactionJson, TransactionWriteRow};
|
|
5
|
+
use crate::GLOBAL_BRANCH_ID;
|
|
6
|
+
|
|
7
|
+
pub(crate) const BRANCH_DESCRIPTOR_SCHEMA_KEY: &str = "lix_branch_descriptor";
|
|
8
|
+
pub(crate) const BRANCH_REF_SCHEMA_KEY: &str = "lix_branch_ref";
|
|
9
|
+
|
|
10
|
+
pub(crate) fn branch_descriptor_stage_row(
|
|
11
|
+
branch_id: &str,
|
|
12
|
+
name: &str,
|
|
13
|
+
hidden: bool,
|
|
14
|
+
) -> TransactionWriteRow {
|
|
15
|
+
TransactionWriteRow {
|
|
16
|
+
entity_pk: Some(EntityPk::single(branch_id)),
|
|
17
|
+
schema_key: BRANCH_DESCRIPTOR_SCHEMA_KEY.to_string(),
|
|
18
|
+
file_id: None,
|
|
19
|
+
snapshot: Some(TransactionJson::from_value_unchecked(json!({
|
|
20
|
+
"id": branch_id,
|
|
21
|
+
"name": name,
|
|
22
|
+
"hidden": hidden,
|
|
23
|
+
}))),
|
|
24
|
+
metadata: None,
|
|
25
|
+
origin: None,
|
|
26
|
+
created_at: None,
|
|
27
|
+
updated_at: None,
|
|
28
|
+
global: true,
|
|
29
|
+
change_id: None,
|
|
30
|
+
commit_id: None,
|
|
31
|
+
untracked: false,
|
|
32
|
+
branch_id: GLOBAL_BRANCH_ID.to_string(),
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
pub(crate) fn branch_ref_stage_row(branch_id: &str, commit_id: &str) -> TransactionWriteRow {
|
|
37
|
+
TransactionWriteRow {
|
|
38
|
+
entity_pk: Some(EntityPk::single(branch_id)),
|
|
39
|
+
schema_key: BRANCH_REF_SCHEMA_KEY.to_string(),
|
|
40
|
+
file_id: None,
|
|
41
|
+
snapshot: Some(TransactionJson::from_value_unchecked(json!({
|
|
42
|
+
"id": branch_id,
|
|
43
|
+
"commit_id": commit_id,
|
|
44
|
+
}))),
|
|
45
|
+
metadata: None,
|
|
46
|
+
origin: None,
|
|
47
|
+
created_at: None,
|
|
48
|
+
updated_at: None,
|
|
49
|
+
global: true,
|
|
50
|
+
change_id: None,
|
|
51
|
+
commit_id: None,
|
|
52
|
+
untracked: true,
|
|
53
|
+
branch_id: GLOBAL_BRANCH_ID.to_string(),
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
pub(crate) fn branch_descriptor_tombstone_row(branch_id: &str) -> TransactionWriteRow {
|
|
58
|
+
let mut row = branch_descriptor_stage_row(branch_id, "", false);
|
|
59
|
+
row.snapshot = None;
|
|
60
|
+
row
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
pub(crate) fn branch_ref_tombstone_row(branch_id: &str) -> TransactionWriteRow {
|
|
64
|
+
let mut row = branch_ref_stage_row(branch_id, "");
|
|
65
|
+
row.snapshot = None;
|
|
66
|
+
row
|
|
67
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/// Current changelog head for a branch.
|
|
2
|
+
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
3
|
+
pub(crate) struct BranchHead {
|
|
4
|
+
pub(crate) branch_id: String,
|
|
5
|
+
pub(crate) commit_id: String,
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/// Typed reader for moving branch heads.
|
|
9
|
+
#[async_trait::async_trait]
|
|
10
|
+
pub(crate) trait BranchRefReader: Send + Sync {
|
|
11
|
+
async fn load_head(&self, branch_id: &str) -> Result<Option<BranchHead>, crate::LixError>;
|
|
12
|
+
|
|
13
|
+
async fn load_head_commit_id(
|
|
14
|
+
&self,
|
|
15
|
+
branch_id: &str,
|
|
16
|
+
) -> Result<Option<String>, crate::LixError> {
|
|
17
|
+
Ok(self.load_head(branch_id).await?.map(|head| head.commit_id))
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async fn scan_heads(&self) -> Result<Vec<BranchHead>, crate::LixError>;
|
|
21
|
+
}
|