#[derive(Debug, thiserror::Error, serde::Serialize, serde::Deserialize)] pub enum Error { #[error("Can't create index")] CantCreate, #[error("Failed to find records in bucket")] QueryFailed, } pub type Result = std::result::Result; #[allow(clippy::module_inception)] pub mod search { use crate::search::create_index::Lang; use crate::search::Error; #[derive(Debug, serde::Serialize, serde::Deserialize)] pub struct Input { pub query: String, pub bucket: String, pub collection: String, pub lang: Lang, } impl Input { pub fn new, B: Into, C: Into>( query: Q, collection: C, bucket: B, lang: whatlang::Lang, ) -> Self { Self { query: query.into(), bucket: bucket.into(), collection: collection.into(), lang: Lang(lang), } } } #[derive(Debug, Default, serde::Serialize, serde::Deserialize)] pub struct Output { pub found: Option>, pub error: Option, } impl Output { pub fn found(found: Vec) -> Self { Self { found: Some(found), ..Default::default() } } pub fn error(error: Error) -> Self { Self { error: Some(error), ..Default::default() } } } } pub mod suggest { use crate::search::Error; #[derive(Debug, serde::Serialize, serde::Deserialize)] pub struct Input { pub query: String, pub bucket: String, pub collection: String, } impl Input { pub fn new, B: Into, C: Into>( query: Q, collection: C, bucket: B, ) -> Self { Self { query: query.into(), bucket: bucket.into(), collection: collection.into(), } } } #[derive(Debug, Default, serde::Serialize, serde::Deserialize)] pub struct Output { pub found: Option>, pub error: Option, } impl Output { pub fn found(found: Vec) -> Self { Self { found: Some(found), ..Default::default() } } pub fn error(error: Error) -> Self { Self { error: Some(error), ..Default::default() } } } } pub mod create_index { use std::fmt::Formatter; use serde::de::Visitor; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use crate::search::Error; #[derive(Debug)] pub struct Lang(pub whatlang::Lang); impl Serialize for Lang { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_str(self.0.code()) } } impl<'de> Deserialize<'de> for Lang { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { struct SV; impl Visitor<'_> for SV { type Value = whatlang::Lang; fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result { formatter.write_str("must be valid whatlang::Lang") } fn visit_string(self, v: String) -> Result where E: serde::de::Error, { match v.parse() { Ok(l) => Ok(l), Err(e) => Err(E::custom(format!("{}", e))), } } } Ok(Self(deserializer.deserialize_string(SV)?)) } } #[derive(Debug, serde::Serialize, serde::Deserialize)] pub struct Input { pub key: String, pub value: String, pub bucket: String, pub collection: String, pub lang: Lang, } impl Input { pub fn new, V: Into, B: Into, C: Into>( key: K, value: V, collection: C, bucket: B, lang: whatlang::Lang, ) -> Self { Self { key: key.into(), value: value.into(), bucket: bucket.into(), collection: collection.into(), lang: Lang(lang), } } } #[derive(Debug, Default, serde::Serialize, serde::Deserialize)] pub struct Output { pub found: Option<()>, pub error: Option, } impl Output { pub fn ok() -> Self { Self { found: Some(()), ..Default::default() } } pub fn error(error: Error) -> Self { Self { error: Some(error), ..Default::default() } } } } pub mod rpc { use config::SharedAppConfig; use crate::search::{create_index, search, suggest}; #[tarpc::service] pub trait Search { /// Search all matching indices. async fn search(input: search::Input) -> search::Output; /// Suggest all matching indices. async fn suggest(input: suggest::Input) -> suggest::Output; /// Create new search index. async fn create_index(input: create_index::Input) -> create_index::Output; } pub async fn create_client(config: SharedAppConfig) -> SearchClient { use tarpc::client; use tarpc::tokio_serde::formats::Bincode; let l = config.lock(); let transport = tarpc::serde_transport::tcp::connect(l.search().rpc_addr(), Bincode::default); let client = SearchClient::new( client::Config::default(), transport.await.expect("Failed to connect to search server"), ) .spawn(); client } }