use std::io::Cursor;
use tiny_http::Response;
use crate::handlers::{error_response, json_response, State};
use lex_ast::{stage_id, Stage};
use lex_vcs::{Intent, IntentLog, Issue, IssueLog};
pub(crate) fn stages_batch_handler(state: &State, body: &str) -> Response<Cursor<Vec<u8>>> {
let stages: Vec<Stage> = match serde_json::from_str(body) {
Ok(s) => s,
Err(e) => return error_response(400, format!("body must be a JSON array of Stage: {e}")),
};
let store = state.store.lock().unwrap();
let (mut added, mut skipped) = (0usize, 0usize);
for stage in &stages {
let id = match stage_id(stage) {
Some(id) => id,
None => { skipped += 1; continue } };
let existed = store.get_ast(&id).is_ok();
if let Err(e) = store.publish(stage) {
return error_response(500, format!("publish stage {id}: {e}"));
}
if existed { skipped += 1 } else { added += 1 }
}
json_response(200, &serde_json::json!({
"received": stages.len(), "added": added, "skipped": skipped,
}))
}
pub(crate) fn stages_fetch_handler(state: &State, body: &str) -> Response<Cursor<Vec<u8>>> {
let ids = match parse_ids(body) {
Ok(ids) => ids,
Err(resp) => return resp,
};
let store = state.store.lock().unwrap();
let stages: Vec<Stage> = ids.iter().filter_map(|id| store.get_ast(id).ok()).collect();
json_response(200, &serde_json::json!({ "stages": stages }))
}
pub(crate) fn intents_batch_handler(state: &State, body: &str) -> Response<Cursor<Vec<u8>>> {
let intents: Vec<Intent> = match serde_json::from_str(body) {
Ok(i) => i,
Err(e) => return error_response(400, format!("body must be a JSON array of Intent: {e}")),
};
let store = state.store.lock().unwrap();
let log = match IntentLog::open(store.root()) {
Ok(l) => l,
Err(e) => return error_response(500, format!("opening intent log: {e}")),
};
let mut added = 0usize;
for intent in &intents {
let existed = matches!(log.get(&intent.intent_id), Ok(Some(_)));
if let Err(e) = log.put(intent) {
return error_response(500, format!("put intent {}: {e}", intent.intent_id));
}
if !existed { added += 1 }
}
json_response(200, &serde_json::json!({
"received": intents.len(), "added": added,
}))
}
pub(crate) fn intents_fetch_handler(state: &State, body: &str) -> Response<Cursor<Vec<u8>>> {
let ids = match parse_ids(body) {
Ok(ids) => ids,
Err(resp) => return resp,
};
let store = state.store.lock().unwrap();
let log = match IntentLog::open(store.root()) {
Ok(l) => l,
Err(e) => return error_response(500, format!("opening intent log: {e}")),
};
let intents: Vec<Intent> = ids.iter().filter_map(|id| log.get(id).ok().flatten()).collect();
json_response(200, &serde_json::json!({ "intents": intents }))
}
pub(crate) fn locks_batch_handler(state: &State, body: &str) -> Response<Cursor<Vec<u8>>> {
let v: serde_json::Value = match serde_json::from_str(body) {
Ok(v) => v,
Err(e) => return error_response(400, format!("body must be JSON: {e}")),
};
let entries = match v.as_array() {
Some(a) => a,
None => return error_response(400, "body must be a JSON array of {head_op, lock}"),
};
let store = state.store.lock().unwrap();
let mut added = 0usize;
for e in entries {
let head_op = match e.get("head_op").and_then(|x| x.as_str()) {
Some(h) => h,
None => return error_response(400, "each entry needs a string `head_op`"),
};
let lock = match e.get("lock").and_then(|x| x.as_str()) {
Some(l) => l,
None => return error_response(400, "each entry needs a string `lock`"),
};
if let Err(err) = store.set_committed_lock(head_op, lock) {
return error_response(500, format!("store lock for {head_op}: {err}"));
}
added += 1;
}
json_response(200, &serde_json::json!({ "received": entries.len(), "added": added }))
}
pub(crate) fn locks_fetch_handler(state: &State, body: &str) -> Response<Cursor<Vec<u8>>> {
let v: serde_json::Value = match serde_json::from_str(body) {
Ok(v) => v,
Err(e) => return error_response(400, format!("body must be JSON: {e}")),
};
let ids = match v.get("head_ops").and_then(|i| i.as_array()) {
Some(a) => a,
None => return error_response(400, "missing array field `head_ops`"),
};
let store = state.store.lock().unwrap();
let mut locks = serde_json::Map::new();
for id in ids.iter().filter_map(|x| x.as_str()) {
match store.committed_lock(id) {
Ok(Some(toml)) => {
locks.insert(id.to_string(), serde_json::Value::String(toml));
}
Ok(None) => {}
Err(e) => return error_response(500, format!("read lock {id}: {e}")),
}
}
json_response(200, &serde_json::json!({ "locks": locks }))
}
pub(crate) fn issues_batch_handler(state: &State, body: &str) -> Response<Cursor<Vec<u8>>> {
let issues: Vec<Issue> = match serde_json::from_str(body) {
Ok(i) => i,
Err(e) => return error_response(400, format!("body must be a JSON array of Issue: {e}")),
};
let store = state.store.lock().unwrap();
let log = match IssueLog::open(store.root()) {
Ok(l) => l,
Err(e) => return error_response(500, format!("opening issue log: {e}")),
};
let mut added = 0usize;
for issue in &issues {
let existed = matches!(log.get(&issue.issue_id), Ok(Some(_)));
if let Err(e) = log.put(issue) {
return error_response(500, format!("put issue {}: {e}", issue.issue_id));
}
if !existed { added += 1 }
}
json_response(200, &serde_json::json!({
"received": issues.len(), "added": added,
}))
}
pub(crate) fn issues_fetch_handler(state: &State, body: &str) -> Response<Cursor<Vec<u8>>> {
let ids = match parse_ids(body) {
Ok(ids) => ids,
Err(resp) => return resp,
};
let store = state.store.lock().unwrap();
let log = match IssueLog::open(store.root()) {
Ok(l) => l,
Err(e) => return error_response(500, format!("opening issue log: {e}")),
};
let issues: Vec<Issue> = ids.iter().filter_map(|id| log.get(id).ok().flatten()).collect();
json_response(200, &serde_json::json!({ "issues": issues }))
}
pub(crate) fn issues_list_handler(state: &State) -> Response<Cursor<Vec<u8>>> {
let store = state.store.lock().unwrap();
let log = match IssueLog::open(store.root()) {
Ok(l) => l,
Err(e) => return error_response(500, format!("opening issue log: {e}")),
};
match log.list_ids() {
Ok(ids) => json_response(200, &serde_json::json!({ "ids": ids })),
Err(e) => error_response(500, format!("listing issues: {e}")),
}
}
fn parse_ids(body: &str) -> Result<Vec<String>, Response<Cursor<Vec<u8>>>> {
let v: serde_json::Value = serde_json::from_str(body)
.map_err(|e| error_response(400, format!("body must be JSON: {e}")))?;
let ids = v.get("ids").and_then(|i| i.as_array())
.ok_or_else(|| error_response(400, "missing array field `ids`"))?;
Ok(ids.iter().filter_map(|x| x.as_str().map(String::from)).collect())
}