meilisearch/meilisearch-lib/src/index/mod.rs

339 lines
10 KiB
Rust
Raw Normal View History

2021-06-23 20:48:33 +08:00
pub use search::{default_crop_length, SearchQuery, SearchResult, DEFAULT_SEARCH_LIMIT};
2021-09-29 04:22:59 +08:00
pub use updates::{apply_settings_to_builder, Checked, Facets, Settings, Unchecked};
pub mod error;
pub mod update_handler;
2021-05-27 04:52:06 +08:00
mod dump;
2021-05-31 22:03:39 +08:00
mod search;
mod updates;
2021-10-04 18:15:21 +08:00
mod index;
2021-04-01 22:44:42 +08:00
2021-10-04 18:15:21 +08:00
pub use index::{Document, IndexMeta, IndexStats};
2021-03-04 21:20:19 +08:00
2021-10-04 18:15:21 +08:00
#[cfg(not(test))]
pub use index::Index;
2021-09-24 17:53:11 +08:00
2021-10-04 18:15:21 +08:00
#[cfg(test)]
pub use test::MockIndex as Index;
2021-09-24 17:53:11 +08:00
2021-10-04 18:15:21 +08:00
#[cfg(test)]
2021-10-05 00:27:42 +08:00
pub mod test {
2021-10-04 18:15:21 +08:00
use std::any::Any;
use std::collections::HashMap;
2021-10-05 00:27:42 +08:00
use std::panic::{RefUnwindSafe, UnwindSafe};
2021-10-04 18:15:21 +08:00
use std::path::PathBuf;
use std::sync::Mutex;
use std::{path::Path, sync::Arc};
2021-09-24 17:53:11 +08:00
2021-10-04 18:15:21 +08:00
use serde_json::{Map, Value};
use uuid::Uuid;
2021-09-24 17:53:11 +08:00
2021-10-04 18:15:21 +08:00
use crate::index_controller::update_file_store::UpdateFileStore;
use crate::index_controller::updates::status::{Failed, Processed, Processing};
2021-03-04 18:56:32 +08:00
2021-10-04 18:15:21 +08:00
use super::{Checked, IndexMeta, IndexStats, SearchQuery, SearchResult, Settings};
use super::index::Index;
use super::error::Result;
use super::update_handler::UpdateHandler;
2021-03-04 18:56:32 +08:00
2021-10-04 18:15:21 +08:00
pub struct Stub<A, R> {
name: String,
times: Option<usize>,
stub: Box<dyn Fn(A) -> R + Sync + Send>,
2021-10-05 00:27:42 +08:00
invalidated: bool,
2021-03-04 18:56:32 +08:00
}
2021-03-04 19:38:55 +08:00
2021-10-04 18:15:21 +08:00
impl<A, R> Drop for Stub<A, R> {
fn drop(&mut self) {
2021-10-05 00:27:42 +08:00
if !self.invalidated {
if let Some(n) = self.times {
assert_eq!(n, 0, "{} not called enough times", self.name);
2021-10-04 18:15:21 +08:00
}
}
}
2021-09-24 17:53:11 +08:00
}
2021-10-05 00:27:42 +08:00
impl<A: UnwindSafe, R> Stub<A, R> {
2021-10-04 18:15:21 +08:00
fn call(&mut self, args: A) -> R {
match self.times {
Some(0) => panic!("{} called to many times", self.name),
Some(ref mut times) => { *times -= 1; },
None => (),
}
2021-10-05 00:27:42 +08:00
// Since we add assertions in drop implementation for Stub, an panic can occur in a
// panic, cause a hard abort of the program. To handle that, we catch the panic, and
// set the stub as invalidated so the assertions are not run during the drop.
impl<'a, A, R> RefUnwindSafe for StubHolder<'a, A, R> {}
struct StubHolder<'a, A, R>(&'a (dyn Fn(A) -> R + Sync + Send));
let stub = StubHolder(self.stub.as_ref());
match std::panic::catch_unwind(|| (stub.0)(args)) {
Ok(r) => r,
Err(panic) => {
self.invalidated = true;
std::panic::resume_unwind(panic);
}
}
2021-10-04 18:15:21 +08:00
}
}
2021-09-24 17:53:11 +08:00
2021-10-04 18:15:21 +08:00
#[derive(Debug, Default)]
struct StubStore {
inner: Arc<Mutex<HashMap<String, Box<dyn Any + Sync + Send>>>>
2021-05-27 04:52:06 +08:00
}
2021-10-04 18:15:21 +08:00
impl StubStore {
pub fn insert<A: 'static, R: 'static>(&self, name: String, stub: Stub<A, R>) {
let mut lock = self.inner.lock().unwrap();
lock.insert(name, Box::new(stub));
}
pub fn get_mut<A, B>(&self, name: &str) -> Option<&mut Stub<A, B>> {
let mut lock = self.inner.lock().unwrap();
match lock.get_mut(name) {
Some(s) => {
let s = s.as_mut() as *mut dyn Any as *mut Stub<A, B>;
Some(unsafe { &mut *s })
}
None => None,
}
}
2021-05-25 00:16:35 +08:00
}
2021-03-04 19:38:55 +08:00
2021-10-04 18:15:21 +08:00
pub struct StubBuilder<'a> {
name: String,
store: &'a StubStore,
times: Option<usize>,
2021-03-04 19:38:55 +08:00
}
2021-03-04 21:20:19 +08:00
2021-10-04 18:15:21 +08:00
impl<'a> StubBuilder<'a> {
#[must_use]
pub fn times(mut self, times: usize) -> Self {
self.times = Some(times);
self
}
2021-03-04 21:20:19 +08:00
2021-10-04 18:15:21 +08:00
pub fn then<A: 'static, R: 'static>(self, f: impl Fn(A) -> R + Sync + Send + 'static) {
let stub = Stub {
stub: Box::new(f),
times: self.times,
name: self.name.clone(),
2021-10-05 00:27:42 +08:00
invalidated: false,
2021-10-04 18:15:21 +08:00
};
2021-03-04 21:20:19 +08:00
2021-10-04 18:15:21 +08:00
self.store.insert(self.name, stub);
}
}
2021-03-04 21:20:19 +08:00
2021-10-05 00:27:42 +08:00
/// Mocker allows to stub metod call on any struct. you can register stubs by calling
/// `Mocker::when` and retrieve it in the proxy implementation when with `Mocker::get`.
///
/// Mocker uses unsafe code to erase function types, because `Any` is too restrictive with it's
/// requirement for all stub arguments to be static. Because of that panic inside a stub is UB,
/// and it has been observed to crash with an illegal hardware instruction. Use with caution.
#[derive(Debug, Default)]
pub struct Mocker {
store: StubStore,
}
impl Mocker {
2021-10-04 18:15:21 +08:00
pub fn when(&self, name: &str) -> StubBuilder {
StubBuilder {
name: name.to_string(),
store: &self.store,
times: None,
}
2021-03-04 21:20:19 +08:00
}
2021-10-04 18:15:21 +08:00
pub fn get<'a, A, R>(&'a self, name: &str) -> &'a mut Stub<A, R> {
match self.store.get_mut(name) {
Some(stub) => stub,
None => panic!("unexpected call to {}", name),
}
}
2021-03-04 21:20:19 +08:00
}
2021-03-04 22:09:00 +08:00
2021-10-05 00:27:42 +08:00
#[derive(Debug, Clone)]
pub enum MockIndex {
Vrai(Index),
Faux(Arc<Mocker>),
}
2021-10-04 18:15:21 +08:00
impl MockIndex {
2021-10-05 00:27:42 +08:00
pub fn faux(faux: Mocker) -> Self {
2021-10-04 18:15:21 +08:00
Self::Faux(Arc::new(faux))
}
2021-03-04 22:09:00 +08:00
2021-10-04 18:15:21 +08:00
pub fn open(
path: impl AsRef<Path>,
size: usize,
update_file_store: Arc<UpdateFileStore>,
uuid: Uuid,
update_handler: Arc<UpdateHandler>,
) -> Result<Self> {
let index = Index::open(path, size, update_file_store, uuid, update_handler)?;
Ok(Self::Vrai(index))
}
2021-03-04 22:09:00 +08:00
2021-10-04 18:15:21 +08:00
pub fn load_dump(
src: impl AsRef<Path>,
dst: impl AsRef<Path>,
size: usize,
update_handler: &UpdateHandler,
) -> anyhow::Result<()> {
Index::load_dump(src, dst, size, update_handler)?;
Ok(())
}
2021-10-04 18:15:21 +08:00
pub fn handle_update(&self, update: Processing) -> std::result::Result<Processed, Failed> {
match self {
MockIndex::Vrai(index) => index.handle_update(update),
MockIndex::Faux(_) => todo!(),
}
}
2021-10-04 18:15:21 +08:00
pub fn uuid(&self) -> Uuid {
match self {
MockIndex::Vrai(index) => index.uuid(),
2021-10-05 00:27:42 +08:00
MockIndex::Faux(faux) => faux.get("uuid").call(()),
2021-10-04 18:15:21 +08:00
}
}
2021-06-17 20:36:32 +08:00
2021-10-04 18:15:21 +08:00
pub fn stats(&self) -> Result<IndexStats> {
match self {
MockIndex::Vrai(index) => index.stats(),
MockIndex::Faux(_) => todo!(),
}
}
2021-06-17 20:36:32 +08:00
2021-10-04 18:15:21 +08:00
pub fn meta(&self) -> Result<IndexMeta> {
match self {
MockIndex::Vrai(index) => index.meta(),
MockIndex::Faux(_) => todo!(),
}
}
pub fn settings(&self) -> Result<Settings<Checked>> {
match self {
MockIndex::Vrai(index) => index.settings(),
MockIndex::Faux(_) => todo!(),
}
}
2021-10-04 18:15:21 +08:00
pub fn retrieve_documents<S: AsRef<str>>(
&self,
offset: usize,
limit: usize,
attributes_to_retrieve: Option<Vec<S>>,
) -> Result<Vec<Map<String, Value>>> {
match self {
MockIndex::Vrai(index) => index.retrieve_documents(offset, limit, attributes_to_retrieve),
MockIndex::Faux(_) => todo!(),
}
}
2021-04-01 22:44:42 +08:00
2021-10-04 18:15:21 +08:00
pub fn retrieve_document<S: AsRef<str>>(
&self,
doc_id: String,
attributes_to_retrieve: Option<Vec<S>>,
) -> Result<Map<String, Value>> {
match self {
MockIndex::Vrai(index) => index.retrieve_document(doc_id, attributes_to_retrieve),
MockIndex::Faux(_) => todo!(),
}
}
pub fn size(&self) -> u64 {
match self {
MockIndex::Vrai(index) => index.size(),
MockIndex::Faux(_) => todo!(),
}
}
pub fn snapshot(&self, path: impl AsRef<Path>) -> Result<()> {
match self {
MockIndex::Vrai(index) => index.snapshot(path),
2021-10-05 00:27:42 +08:00
MockIndex::Faux(faux) => {
faux.get("snapshot").call(path.as_ref())
}
2021-10-04 18:15:21 +08:00
}
}
pub fn inner(&self) -> &milli::Index {
match self {
MockIndex::Vrai(index) => index.inner(),
MockIndex::Faux(_) => todo!(),
}
}
pub fn update_primary_key(&self, primary_key: Option<String>) -> Result<IndexMeta> {
match self {
MockIndex::Vrai(index) => index.update_primary_key(primary_key),
MockIndex::Faux(_) => todo!(),
}
}
pub fn perform_search(&self, query: SearchQuery) -> Result<SearchResult> {
match self {
MockIndex::Vrai(index) => index.perform_search(query),
MockIndex::Faux(_) => todo!(),
}
}
pub fn dump(&self, path: impl AsRef<Path>) -> Result<()> {
match self {
MockIndex::Vrai(index) => index.dump(path),
MockIndex::Faux(_) => todo!(),
}
}
}
2021-09-27 22:48:03 +08:00
2021-10-04 18:15:21 +08:00
#[test]
fn test_faux_index() {
2021-10-05 00:27:42 +08:00
let faux = Mocker::default();
2021-10-04 18:15:21 +08:00
faux
.when("snapshot")
2021-10-05 00:27:42 +08:00
.times(2)
.then(|_: &Path| -> Result<()> {
2021-10-04 18:15:21 +08:00
Ok(())
});
let index = MockIndex::faux(faux);
let path = PathBuf::from("hello");
index.snapshot(&path).unwrap();
index.snapshot(&path).unwrap();
2021-09-27 22:48:03 +08:00
}
2021-10-05 00:27:42 +08:00
#[test]
#[should_panic]
fn test_faux_unexisting_method_stub() {
let faux = Mocker::default();
let index = MockIndex::faux(faux);
let path = PathBuf::from("hello");
index.snapshot(&path).unwrap();
index.snapshot(&path).unwrap();
}
#[test]
#[should_panic]
fn test_faux_panic() {
let faux = Mocker::default();
faux
.when("snapshot")
.times(2)
.then(|_: &Path| -> Result<()> {
panic!();
});
let index = MockIndex::faux(faux);
let path = PathBuf::from("hello");
index.snapshot(&path).unwrap();
index.snapshot(&path).unwrap();
}
2021-03-04 19:38:55 +08:00
}