use serde_json::Value;
#[derive(Debug, Clone)]
pub enum RespOp {
UpdateTypingIndicator {
thread_id: String,
user_id: String,
is_typing: bool,
},
InsertMessage {
thread_id: String,
sender_id: String,
message_id: String,
text: Option<String>,
timestamp_ms: Option<i64>,
},
TaskExists {
task_id: String,
},
UpsertSyncGroupThreadsRange,
UpsertInboxThreadsRange,
UpdateThreadsRangesV2,
RemoveTask {
task_id: String,
},
ExecuteFirstBlockForSyncTransaction,
UpsertSequenceId,
VerifyContactRowExists,
ExecuteFinallyBlockForSyncTransaction,
Unknown {
name: String,
args: Vec<Value>,
raw: Value,
},
}
pub fn parse_operations(step: &Value) -> Vec<RespOp> {
let mut output = Vec::new();
collect_step_operations(step, &mut output);
output
}
fn collect_step_operations(value: &Value, output: &mut Vec<RespOp>) {
if let Some(operation) = parse_operation(value) {
output.push(operation);
}
match value {
Value::Array(items) => {
for item in items {
collect_step_operations(item, output);
}
}
Value::Object(map) => {
for child in map.values() {
collect_step_operations(child, output);
}
}
_ => {}
}
}
fn parse_operation(value: &Value) -> Option<RespOp> {
let items = value.as_array()?;
if items.len() < 2 {
return None;
}
if items[0].as_i64()? != 5 {
return None;
}
let name = items[1].as_str()?.to_string();
let args = items[2..].to_vec();
match name.as_str() {
"updateTypingIndicator" => {
let thread_id = decode_ls_token(items.get(2)?)?;
let user_id = decode_ls_token(items.get(3)?)?;
let is_typing = items.get(4)?.as_bool()?;
Some(RespOp::UpdateTypingIndicator {
thread_id,
user_id,
is_typing,
})
}
"insertMessage" => parse_insert_message(items),
"taskExists" => Some(RespOp::TaskExists {
task_id: decode_ls_token(items.get(2)?)?,
}),
"upsertSyncGroupThreadsRange" => Some(RespOp::UpsertSyncGroupThreadsRange),
"upsertInboxThreadsRange" => Some(RespOp::UpsertInboxThreadsRange),
"updateThreadsRangesV2" => Some(RespOp::UpdateThreadsRangesV2),
"removeTask" => Some(RespOp::RemoveTask {
task_id: decode_ls_token(items.get(2)?)?,
}),
"executeFirstBlockForSyncTransaction" => Some(RespOp::ExecuteFirstBlockForSyncTransaction),
"upsertSequenceId" => Some(RespOp::UpsertSequenceId),
"verifyContactRowExists" => Some(RespOp::VerifyContactRowExists),
"executeFinallyBlockForSyncTransaction" => {
Some(RespOp::ExecuteFinallyBlockForSyncTransaction)
}
_ => Some(RespOp::Unknown {
name,
args,
raw: value.clone(),
}),
}
}
fn parse_insert_message(items: &[Value]) -> Option<RespOp> {
if items.len() < 13 {
return None;
}
let text = items.get(2).and_then(Value::as_str).map(str::to_owned);
let thread_id = decode_ls_token(items.get(5)?)?;
let timestamp_ms = items.get(7).and_then(decode_ls_token_as_i64);
let message_id = items.get(10).and_then(Value::as_str)?.to_owned();
let sender_id = decode_ls_token(items.get(12)?)?;
Some(RespOp::InsertMessage {
thread_id,
sender_id,
message_id,
text,
timestamp_ms,
})
}
fn decode_ls_token(value: &Value) -> Option<String> {
match value {
Value::String(inner) => Some(inner.clone()),
Value::Number(inner) => Some(inner.to_string()),
Value::Array(items) => {
if items.len() >= 2 && items[0].as_i64() == Some(19) {
return decode_ls_token(&items[1]);
}
None
}
_ => None,
}
}
fn decode_ls_token_as_i64(value: &Value) -> Option<i64> {
decode_ls_token(value)?.parse::<i64>().ok()
}