huskies: merge 836
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
//! Read/write helpers for the `test_jobs` LWW-map collection.
|
||||
//!
|
||||
//! Test-job entries are keyed by `story_id` and track the status, timing,
|
||||
//! and captured output for each test run.
|
||||
|
||||
use bft_json_crdt::json_crdt::{JsonValue, *};
|
||||
use bft_json_crdt::op::ROOT_ID;
|
||||
use serde_json::json;
|
||||
|
||||
use super::super::state::{apply_and_persist, get_crdt, rebuild_test_job_index};
|
||||
use super::super::types::{TestJobCrdt, TestJobView};
|
||||
use super::list_id_at;
|
||||
|
||||
/// Write or update a test-job entry keyed by `story_id`.
|
||||
pub fn write_test_job(
|
||||
story_id: &str,
|
||||
status: &str,
|
||||
started_at: f64,
|
||||
finished_at: Option<f64>,
|
||||
output: Option<&str>,
|
||||
) {
|
||||
let Some(state_mutex) = get_crdt() else {
|
||||
return;
|
||||
};
|
||||
let Ok(mut state) = state_mutex.lock() else {
|
||||
return;
|
||||
};
|
||||
|
||||
if let Some(&idx) = state.test_job_index.get(story_id) {
|
||||
apply_and_persist(&mut state, |s| {
|
||||
s.crdt.doc.test_jobs[idx].status.set(status.to_string())
|
||||
});
|
||||
apply_and_persist(&mut state, |s| {
|
||||
s.crdt.doc.test_jobs[idx].started_at.set(started_at)
|
||||
});
|
||||
if let Some(fa) = finished_at {
|
||||
apply_and_persist(&mut state, |s| {
|
||||
s.crdt.doc.test_jobs[idx].finished_at.set(fa)
|
||||
});
|
||||
}
|
||||
if let Some(o) = output {
|
||||
apply_and_persist(&mut state, |s| {
|
||||
s.crdt.doc.test_jobs[idx].output.set(o.to_string())
|
||||
});
|
||||
}
|
||||
} else {
|
||||
let entry: JsonValue = json!({
|
||||
"story_id": story_id,
|
||||
"status": status,
|
||||
"started_at": started_at,
|
||||
"finished_at": finished_at.unwrap_or(0.0),
|
||||
"output": output.unwrap_or(""),
|
||||
})
|
||||
.into();
|
||||
apply_and_persist(&mut state, |s| s.crdt.doc.test_jobs.insert(ROOT_ID, entry));
|
||||
state.test_job_index = rebuild_test_job_index(&state.crdt);
|
||||
}
|
||||
}
|
||||
|
||||
/// Read all test-job entries.
|
||||
pub fn read_all_test_jobs() -> Option<Vec<TestJobView>> {
|
||||
let state_mutex = get_crdt()?;
|
||||
let state = state_mutex.lock().ok()?;
|
||||
let mut out = Vec::new();
|
||||
for entry in state.crdt.doc.test_jobs.iter() {
|
||||
if let Some(v) = extract_test_job_view(entry) {
|
||||
out.push(v);
|
||||
}
|
||||
}
|
||||
Some(out)
|
||||
}
|
||||
|
||||
/// Read a single test-job entry by `story_id`.
|
||||
pub fn read_test_job(story_id: &str) -> Option<TestJobView> {
|
||||
let state_mutex = get_crdt()?;
|
||||
let state = state_mutex.lock().ok()?;
|
||||
let &idx = state.test_job_index.get(story_id)?;
|
||||
extract_test_job_view(&state.crdt.doc.test_jobs[idx])
|
||||
}
|
||||
|
||||
/// Tombstone a test-job entry by `story_id`.
|
||||
pub fn delete_test_job(story_id: &str) -> bool {
|
||||
let Some(state_mutex) = get_crdt() else {
|
||||
return false;
|
||||
};
|
||||
let Ok(mut state) = state_mutex.lock() else {
|
||||
return false;
|
||||
};
|
||||
let Some(&idx) = state.test_job_index.get(story_id) else {
|
||||
return false;
|
||||
};
|
||||
let Some(op_id) = list_id_at(&state.crdt.doc.test_jobs, idx) else {
|
||||
return false;
|
||||
};
|
||||
apply_and_persist(&mut state, |s| s.crdt.doc.test_jobs.delete(op_id));
|
||||
state.test_job_index = rebuild_test_job_index(&state.crdt);
|
||||
true
|
||||
}
|
||||
|
||||
/// Convert a CRDT test-job entry into its read-only view representation.
|
||||
pub(super) fn extract_test_job_view(entry: &TestJobCrdt) -> Option<TestJobView> {
|
||||
let story_id = match entry.story_id.view() {
|
||||
JsonValue::String(s) if !s.is_empty() => s,
|
||||
_ => return None,
|
||||
};
|
||||
let status = match entry.status.view() {
|
||||
JsonValue::String(s) => s,
|
||||
_ => String::new(),
|
||||
};
|
||||
let started_at = match entry.started_at.view() {
|
||||
JsonValue::Number(n) => n,
|
||||
_ => 0.0,
|
||||
};
|
||||
let finished_at = match entry.finished_at.view() {
|
||||
JsonValue::Number(n) if n > 0.0 => Some(n),
|
||||
_ => None,
|
||||
};
|
||||
let output = match entry.output.view() {
|
||||
JsonValue::String(s) if !s.is_empty() => Some(s),
|
||||
_ => None,
|
||||
};
|
||||
Some(TestJobView {
|
||||
story_id,
|
||||
status,
|
||||
started_at,
|
||||
finished_at,
|
||||
output,
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user