use serde_json::{Map, Value, json};
use crate::search::{Address, Field, Found, Hit};
pub(super) fn reply(found: &Found) -> Value {
json!({
"ok": true, "kind": "search", "needle": found.needle,
"rows": found.hits.iter().map(hit_row).collect::<Vec<Value>>(),
"unreadable": found.unreadable,
})
}
pub(super) fn hit_row(hit: &Hit) -> Value {
let mut map = Map::new();
map.insert("at".to_owned(), json!(hit.at.token()));
match &hit.at {
Address::Ball { project, id } => {
map.insert("project".to_owned(), json!(project));
map.insert("id".to_owned(), json!(id));
}
Address::Workspace { name } => {
map.insert("workspace".to_owned(), json!(name));
}
Address::Conversation { workspace, agent } => {
map.insert("workspace".to_owned(), json!(workspace));
map.insert("agent".to_owned(), json!(agent));
}
}
map.insert("field".to_owned(), json!(hit.field.token()));
map.insert("offset".to_owned(), json!(hit.offset));
map.insert("excerpt".to_owned(), json!(hit.excerpt));
Value::Object(map)
}
pub(super) fn hit_of(v: &Value) -> Result<Hit, String> {
use crate::boundary::codec::fields::{str_of, usize_of};
let o = v.as_object().ok_or("hit: not an object")?;
let at = match str_of(o, "at")?.as_str() {
"ball" => Address::Ball {
project: str_of(o, "project")?,
id: str_of(o, "id")?,
},
"workspace" => Address::Workspace {
name: str_of(o, "workspace")?,
},
"conversation" => Address::Conversation {
workspace: str_of(o, "workspace")?,
agent: str_of(o, "agent")?,
},
other => return Err(format!("hit: unknown address {other:?}")),
};
let word = str_of(o, "field")?;
let field = [Field::Name, Field::Summary, Field::Text]
.into_iter()
.find(|f| f.token() == word)
.ok_or_else(|| format!("hit: unknown field {word:?}"))?;
Ok(Hit {
at,
field,
offset: usize_of(o, "offset")?,
excerpt: str_of(o, "excerpt")?,
})
}