1use flams_backend_types::search::{QueryFilter, SearchResult, SearchResultKind};
2
3#[must_use]
4pub fn build_query(
5 query: &str,
6 index: &tantivy::Index,
7 filter: QueryFilter,
8) -> Option<Box<dyn tantivy::query::Query>> {
9 use std::fmt::Write;
10 let QueryFilter {
11 allow_documents,
12 allow_paragraphs,
13 allow_definitions,
14 allow_examples,
15 allow_assertions,
16 allow_problems,
17 definition_like_only,
18 } = filter;
19 let mut s = String::new();
20 if !allow_documents
21 || !allow_paragraphs
22 || !allow_definitions
23 || !allow_examples
24 || !allow_assertions
25 || !allow_problems
26 {
27 let mut had_first = false;
29 if allow_documents {
30 had_first = true;
31 s.push_str("(kind:0");
32 }
33 if allow_paragraphs {
34 s.push_str(if had_first { " OR kind:1" } else { "(kind:1" });
35 had_first = true;
36 }
37 if allow_definitions {
38 s.push_str(if had_first { " OR kind:2" } else { "(kind:2" });
39 had_first = true;
40 }
41 if allow_examples {
42 s.push_str(if had_first { " OR kind:3" } else { "(kind:3" });
43 had_first = true;
44 }
45 if allow_assertions {
46 s.push_str(if had_first { " OR kind:4" } else { "(kind:4" });
47 had_first = true;
48 }
49 if allow_problems {
50 s.push_str(if had_first { " OR kind:5" } else { "(kind:5" });
51 }
52 if had_first {
53 s.push_str(") AND ");
54 }
55 }
56 if definition_like_only {
57 s.push_str("deflike:true AND ");
58 }
59 write!(s, "({query})").ok()?;
60 let schema = crate::schema::SearchSchema::get();
61 let mut parser = tantivy::query::QueryParser::for_index(index, vec![schema.fors,schema.uri, schema.title, schema.body]);
62 parser.set_conjunction_by_default();
64 parser.parse_query(&s).ok()
65}
66
67#[derive(Debug)]
68pub(crate) struct Wrapper<T>(pub T);
69
70impl tantivy::schema::document::ValueDeserialize for Wrapper<bool> {
71 fn deserialize<'de, D>(
72 deserializer: D,
73 ) -> Result<Self, tantivy::schema::document::DeserializeError>
74 where
75 D: tantivy::schema::document::ValueDeserializer<'de>,
76 {
77 Ok(Self(deserializer.deserialize_bool()?))
78 }
79}
80
81impl tantivy::schema::document::ValueDeserialize for Wrapper<SearchResultKind> {
82 fn deserialize<'de, D>(
83 deserializer: D,
84 ) -> Result<Self, tantivy::schema::document::DeserializeError>
85 where
86 D: tantivy::schema::document::ValueDeserializer<'de>,
87 {
88 deserializer
89 .deserialize_u64()?
90 .try_into()
91 .map(Wrapper)
92 .map_err(|()| tantivy::schema::document::DeserializeError::custom(""))
93 }
94}
95
96impl tantivy::schema::document::DocumentDeserialize for Wrapper<SearchResult> {
97 fn deserialize<'de, D>(
98 mut deserializer: D,
99 ) -> Result<Self, tantivy::schema::document::DeserializeError>
100 where
101 D: tantivy::schema::document::DocumentDeserializer<'de>,
102 {
103 macro_rules! next {
104 () => {{
105 let Some((_, r)) = deserializer.next_field()? else {
106 return Err(tantivy::schema::document::DeserializeError::custom(
107 "Missing value",
108 ));
109 };
110 r
111 }};
112 (!) => {{
113 let Some((_, Wrapper(r))) = deserializer.next_field()? else {
114 return Err(tantivy::schema::document::DeserializeError::custom(
115 "Missing value",
116 ));
117 };
118 r
119 }};
120 }
121 let Wrapper(kind) = next!();
122 match kind {
123 SearchResultKind::Document => Ok(Self(SearchResult::Document(next!()))),
124 kind => {
125 let uri = next!();
126 let def_like = next!(!);
127 let mut fors = Vec::new();
128 while let Some((_, s)) = deserializer.next_field()? {
129 fors.push(s);
130 }
131 Ok(Self(SearchResult::Paragraph {
132 uri,
133 def_like,
134 kind,
135 fors,
136 }))
137 }
138 }
139 }
140}