use serde_json::{Map, Value};
use super::fields;
pub(crate) const KIND: &str = "search";
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Hit {
pub at: String,
pub field: String,
pub excerpt: String,
pub offset: u64,
pub project: Option<String>,
pub id: Option<String>,
pub workspace: Option<String>,
pub agent: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Found {
pub needle: String,
pub rows: Vec<Hit>,
pub unreadable: Vec<String>,
}
pub(crate) fn row(value: &Value) -> Result<Hit, String> {
let obj: &Map<String, Value> = value.as_object().ok_or("search row: not an object")?;
Ok(Hit {
at: fields::text(obj, "at")?,
field: fields::text(obj, "field")?,
excerpt: fields::text(obj, "excerpt")?,
offset: fields::count(obj, "offset")?,
project: fields::opt_text(obj, "project")?,
id: fields::opt_text(obj, "id")?,
workspace: fields::opt_text(obj, "workspace")?,
agent: fields::opt_text(obj, "agent")?,
})
}
pub(crate) fn found(obj: &Map<String, Value>) -> Result<Found, String> {
Ok(Found {
needle: fields::text(obj, "needle")?,
rows: fields::rows(obj, row)?,
unreadable: fields::list(obj, "unreadable", |value| {
value
.as_str()
.map(str::to_owned)
.ok_or_else(|| "unreadable: not a string".to_owned())
})?,
})
}
impl Hit {
pub fn subject(&self) -> String {
std::iter::once(self.at.clone())
.chain(
[
self.project.clone(),
self.id.clone(),
self.workspace.clone(),
self.agent.clone(),
]
.into_iter()
.flatten(),
)
.collect::<Vec<String>>()
.join(" ")
}
pub fn at_field(&self) -> String {
format!("{} +{}", self.field, self.offset)
}
}
#[cfg(test)]
mod tests;