cargo fmt
This commit is contained in:
2
canvas
2
canvas
Submodule canvas updated: a4f0216878...e6c942dd41
2
client
2
client
Submodule client updated: 14a8b4ffbe...25a901ff5e
@@ -1,8 +1,8 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use tantivy::schema::{
|
||||
Field, IndexRecordOption, JsonObjectOptions, Schema, TextFieldIndexing, Term, INDEXED,
|
||||
STORED, STRING,
|
||||
Field, IndexRecordOption, JsonObjectOptions, Schema, Term, TextFieldIndexing, INDEXED, STORED,
|
||||
STRING,
|
||||
};
|
||||
use tantivy::tokenizer::{
|
||||
AsciiFoldingFilter, LowerCaser, NgramTokenizer, RawTokenizer, RemoveLongFilter,
|
||||
@@ -67,11 +67,7 @@ pub fn create_search_schema() -> Schema {
|
||||
schema_builder.build()
|
||||
}
|
||||
|
||||
fn json_options(
|
||||
tokenizer_name: &str,
|
||||
with_positions: bool,
|
||||
stored: bool,
|
||||
) -> JsonObjectOptions {
|
||||
fn json_options(tokenizer_name: &str, with_positions: bool, stored: bool) -> JsonObjectOptions {
|
||||
let index_option = if with_positions {
|
||||
IndexRecordOption::WithFreqsAndPositions
|
||||
} else {
|
||||
|
||||
@@ -5,8 +5,8 @@ use std::path::Path;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use common::proto::komp_ac::search::searcher_server::Searcher;
|
||||
use common::proto::komp_ac::search::{search_response::Hit, SearchRequest, SearchResponse};
|
||||
pub use common::proto::komp_ac::search::searcher_server::SearcherServer;
|
||||
use common::proto::komp_ac::search::{search_response::Hit, SearchRequest, SearchResponse};
|
||||
use common::search::{register_tokenizers, search_index_path, SchemaFields};
|
||||
use query_builder::{build_master_query, ConstraintMode, SearchConstraint};
|
||||
use sqlx::{PgPool, Row};
|
||||
@@ -34,7 +34,10 @@ impl SearcherService {
|
||||
}
|
||||
}
|
||||
|
||||
async fn run_rpc(&self, request: Request<SearchRequest>) -> Result<Response<SearchResponse>, Status> {
|
||||
async fn run_rpc(
|
||||
&self,
|
||||
request: Request<SearchRequest>,
|
||||
) -> Result<Response<SearchResponse>, Status> {
|
||||
let req = request.into_inner();
|
||||
let normalized = normalize_request(req)?;
|
||||
|
||||
@@ -276,7 +279,9 @@ fn normalize_request(req: SearchRequest) -> Result<NormalizedSearchRequest, Stat
|
||||
});
|
||||
}
|
||||
|
||||
let limit = req.limit.map(|value| (value as usize).min(HARD_RESULT_LIMIT));
|
||||
let limit = req
|
||||
.limit
|
||||
.map(|value| (value as usize).min(HARD_RESULT_LIMIT));
|
||||
|
||||
Ok(NormalizedSearchRequest {
|
||||
profile_name: profile_name.to_string(),
|
||||
@@ -335,8 +340,13 @@ async fn run_search(
|
||||
must: &[SearchConstraint],
|
||||
limit: usize,
|
||||
) -> Result<Vec<Hit>, Status> {
|
||||
let master_query =
|
||||
build_master_query(&profile.index, &profile.fields, free_query, must, table_filter)?;
|
||||
let master_query = build_master_query(
|
||||
&profile.index,
|
||||
&profile.fields,
|
||||
free_query,
|
||||
must,
|
||||
table_filter,
|
||||
)?;
|
||||
|
||||
let searcher = profile.reader.searcher();
|
||||
let top_docs = searcher
|
||||
|
||||
@@ -34,7 +34,9 @@ pub fn build_master_query(
|
||||
|
||||
for constraint in must {
|
||||
let predicate = match constraint.mode {
|
||||
ConstraintMode::Exact => exact_predicate(fields, &constraint.column, &constraint.query)?,
|
||||
ConstraintMode::Exact => {
|
||||
exact_predicate(fields, &constraint.column, &constraint.query)?
|
||||
}
|
||||
ConstraintMode::Fuzzy => {
|
||||
fuzzy_predicate_scoped(fields, &constraint.column, &constraint.query)?
|
||||
}
|
||||
@@ -157,7 +159,10 @@ fn fuzzy_predicate_scoped(
|
||||
.collect();
|
||||
layers.push((
|
||||
Occur::Should,
|
||||
Box::new(BoostQuery::new(Box::new(BooleanQuery::new(ngram_clauses)), 1.0)),
|
||||
Box::new(BoostQuery::new(
|
||||
Box::new(BooleanQuery::new(ngram_clauses)),
|
||||
1.0,
|
||||
)),
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
2
server
2
server
Submodule server updated: 99bc97f771...b178fce273
@@ -167,7 +167,11 @@ impl ValidationSettings {
|
||||
&mut self.character_limits,
|
||||
&rule.character_limits,
|
||||
)?;
|
||||
merge_singleton("allowed_values", &mut self.allowed_values, &rule.allowed_values)?;
|
||||
merge_singleton(
|
||||
"allowed_values",
|
||||
&mut self.allowed_values,
|
||||
&rule.allowed_values,
|
||||
)?;
|
||||
merge_singleton("display_mask", &mut self.display_mask, &rule.display_mask)?;
|
||||
merge_singleton("formatter", &mut self.formatter, &rule.formatter)?;
|
||||
|
||||
|
||||
@@ -22,8 +22,7 @@ pub struct CharacterLimits {
|
||||
}
|
||||
|
||||
/// How to count characters for limit checking
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||
#[derive(Default)]
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default)]
|
||||
pub enum CountMode {
|
||||
/// Count actual characters (default)
|
||||
#[default]
|
||||
@@ -36,7 +35,6 @@ pub enum CountMode {
|
||||
Bytes,
|
||||
}
|
||||
|
||||
|
||||
/// Result of a character limit check
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum LimitCheckResult {
|
||||
@@ -217,12 +215,18 @@ impl CharacterLimits {
|
||||
|
||||
if let Some(max) = self.max_length {
|
||||
if count > max {
|
||||
return LimitCheckResult::Exceeded { current: count, max };
|
||||
return LimitCheckResult::Exceeded {
|
||||
current: count,
|
||||
max,
|
||||
};
|
||||
}
|
||||
|
||||
if let Some(warning_threshold) = self.warning_threshold {
|
||||
if count >= warning_threshold {
|
||||
return LimitCheckResult::Warning { current: count, max };
|
||||
return LimitCheckResult::Warning {
|
||||
current: count,
|
||||
max,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -230,7 +234,10 @@ impl CharacterLimits {
|
||||
// Check min length
|
||||
if let Some(min) = self.min_length {
|
||||
if count < min {
|
||||
return LimitCheckResult::TooShort { current: count, min };
|
||||
return LimitCheckResult::TooShort {
|
||||
current: count,
|
||||
min,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,17 +249,16 @@ impl CharacterLimits {
|
||||
match self.check_limits(text) {
|
||||
LimitCheckResult::Ok => {
|
||||
// Show current/max if we have a max limit
|
||||
self.max_length.map(|max| format!("{}/{}", self.count(text), max))
|
||||
},
|
||||
self.max_length
|
||||
.map(|max| format!("{}/{}", self.count(text), max))
|
||||
}
|
||||
LimitCheckResult::Warning { current, max } => {
|
||||
Some(format!("{current}/{max} (approaching limit)"))
|
||||
},
|
||||
}
|
||||
LimitCheckResult::Exceeded { current, max } => {
|
||||
Some(format!("{current}/{max} (exceeded)"))
|
||||
},
|
||||
LimitCheckResult::TooShort { current, min } => {
|
||||
Some(format!("{current}/{min} minimum"))
|
||||
},
|
||||
}
|
||||
LimitCheckResult::TooShort { current, min } => Some(format!("{current}/{min} minimum")),
|
||||
}
|
||||
}
|
||||
pub fn allows_field_switch(&self, text: &str) -> bool {
|
||||
@@ -394,8 +400,14 @@ mod tests {
|
||||
assert_eq!(limits.status_text("hello"), Some("5/10".to_string()));
|
||||
|
||||
let limits = limits.with_warning_threshold(8);
|
||||
assert_eq!(limits.status_text("12345678"), Some("8/10 (approaching limit)".to_string()));
|
||||
assert_eq!(limits.status_text("1234567890x"), Some("11/10 (exceeded)".to_string()));
|
||||
assert_eq!(
|
||||
limits.status_text("12345678"),
|
||||
Some("8/10 (approaching limit)".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
limits.status_text("1234567890x"),
|
||||
Some("11/10 (exceeded)".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -409,7 +421,10 @@ mod tests {
|
||||
// Field with content below minimum: should block switching
|
||||
assert!(!limits.allows_field_switch("hi"));
|
||||
assert!(limits.field_switch_block_reason("hi").is_some());
|
||||
assert!(limits.field_switch_block_reason("hi").unwrap().contains("at least 3 characters"));
|
||||
assert!(limits
|
||||
.field_switch_block_reason("hi")
|
||||
.unwrap()
|
||||
.contains("at least 3 characters"));
|
||||
|
||||
// Field meeting minimum: should allow switching
|
||||
assert!(limits.allows_field_switch("hello"));
|
||||
@@ -417,7 +432,9 @@ mod tests {
|
||||
|
||||
// Field exceeding maximum: should still allow switching (validation shows error but doesn't block)
|
||||
assert!(limits.allows_field_switch("this is way too long"));
|
||||
assert!(limits.field_switch_block_reason("this is way too long").is_none());
|
||||
assert!(limits
|
||||
.field_switch_block_reason("this is way too long")
|
||||
.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[derive(Default)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
|
||||
pub enum MaskDisplayMode {
|
||||
/// Only show separators as user types
|
||||
/// Example: "" → "", "123" → "123", "12345" → "(123) 45"
|
||||
@@ -15,11 +14,10 @@ pub enum MaskDisplayMode {
|
||||
/// Example: "" → "(___) ___-____", "123" → "(123) ___-____"
|
||||
Template {
|
||||
/// Character to use as placeholder for empty input positions
|
||||
placeholder: char
|
||||
placeholder: char,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct DisplayMask {
|
||||
/// Mask pattern like "##-##-####" where # = input position, others are visual separators
|
||||
@@ -95,7 +93,9 @@ impl DisplayMask {
|
||||
pub fn apply_to_display(&self, raw_input: &str) -> String {
|
||||
match &self.display_mode {
|
||||
MaskDisplayMode::Dynamic => self.apply_dynamic(raw_input),
|
||||
MaskDisplayMode::Template { placeholder } => self.apply_template(raw_input, *placeholder),
|
||||
MaskDisplayMode::Template { placeholder } => {
|
||||
self.apply_template(raw_input, *placeholder)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,7 +158,8 @@ impl DisplayMask {
|
||||
|
||||
/// Check if a display position should accept cursor/input
|
||||
pub fn is_input_position(&self, display_position: usize) -> bool {
|
||||
self.pattern.chars()
|
||||
self.pattern
|
||||
.chars()
|
||||
.nth(display_position)
|
||||
.map(|c| c == self.input_char)
|
||||
.unwrap_or(true) // Beyond pattern = accept input
|
||||
@@ -323,7 +324,7 @@ mod tests {
|
||||
|
||||
assert_eq!(underscores.apply_to_display(""), "__-__");
|
||||
assert_eq!(dots.apply_to_display(""), "••-••");
|
||||
assert_eq!(dashes.apply_to_display(""), "-----"); // Note: dashes blend with separator
|
||||
assert_eq!(dashes.apply_to_display(""), "-----"); // Note: dashes blend with separator
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -341,7 +342,7 @@ mod tests {
|
||||
|
||||
assert!(!custom.is_input_position(0)); // A
|
||||
assert!(!custom.is_input_position(3)); // -
|
||||
assert!(custom.is_input_position(4)); // #
|
||||
assert!(custom.is_input_position(4)); // #
|
||||
assert!(!custom.is_input_position(8)); // Y
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,8 +84,12 @@ impl PositionRange {
|
||||
pub fn positions_up_to(&self, max_length: usize) -> Vec<usize> {
|
||||
match self {
|
||||
PositionRange::Single(pos) => {
|
||||
if *pos < max_length { vec![*pos] } else { vec![] }
|
||||
},
|
||||
if *pos < max_length {
|
||||
vec![*pos]
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
PositionRange::Range(start, end) => {
|
||||
let actual_end = (*end).min(max_length.saturating_sub(1));
|
||||
if *start <= actual_end {
|
||||
@@ -93,20 +97,19 @@ impl PositionRange {
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
},
|
||||
}
|
||||
PositionRange::From(start) => {
|
||||
if *start < max_length {
|
||||
(*start..max_length).collect()
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
},
|
||||
PositionRange::Multiple(positions) => {
|
||||
positions.iter()
|
||||
.filter(|&&pos| pos < max_length)
|
||||
.copied()
|
||||
.collect()
|
||||
},
|
||||
}
|
||||
PositionRange::Multiple(positions) => positions
|
||||
.iter()
|
||||
.filter(|&&pos| pos < max_length)
|
||||
.copied()
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -134,7 +137,7 @@ impl CharacterFilter {
|
||||
CharacterFilter::OneOf(chars) => {
|
||||
let char_list: String = chars.iter().collect();
|
||||
format!("one of: {char_list}")
|
||||
},
|
||||
}
|
||||
CharacterFilter::Custom(_) => "custom filter".to_string(),
|
||||
}
|
||||
}
|
||||
@@ -195,7 +198,11 @@ impl PatternFilters {
|
||||
}
|
||||
|
||||
/// Validate a character at a specific position against all applicable filters
|
||||
pub fn validate_char_at_position(&self, position: usize, character: char) -> Result<(), String> {
|
||||
pub fn validate_char_at_position(
|
||||
&self,
|
||||
position: usize,
|
||||
character: char,
|
||||
) -> Result<(), String> {
|
||||
for filter in &self.filters {
|
||||
if let Some(error) = filter.error_message(position, character) {
|
||||
return Err(error);
|
||||
@@ -252,7 +259,10 @@ mod tests {
|
||||
|
||||
assert_eq!(PositionRange::From(2).positions_up_to(5), vec![2, 3, 4]);
|
||||
|
||||
assert_eq!(PositionRange::Multiple(vec![0, 2, 5]).positions_up_to(4), vec![0, 2]);
|
||||
assert_eq!(
|
||||
PositionRange::Multiple(vec![0, 2, 5]).positions_up_to(4),
|
||||
vec![0, 2]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -277,10 +287,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_position_filter_validation() {
|
||||
let filter = PositionFilter::new(
|
||||
PositionRange::Range(0, 1),
|
||||
CharacterFilter::Alphabetic,
|
||||
);
|
||||
let filter = PositionFilter::new(PositionRange::Range(0, 1), CharacterFilter::Alphabetic);
|
||||
|
||||
assert!(filter.validate_position(0, 'A'));
|
||||
assert!(filter.validate_position(1, 'b'));
|
||||
@@ -312,11 +319,10 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_custom_filter() {
|
||||
let pattern = PatternFilters::new()
|
||||
.add_filter(PositionFilter::new(
|
||||
PositionRange::From(0),
|
||||
CharacterFilter::Custom(Arc::new(|c| c.is_lowercase())),
|
||||
));
|
||||
let pattern = PatternFilters::new().add_filter(PositionFilter::new(
|
||||
PositionRange::From(0),
|
||||
CharacterFilter::Custom(Arc::new(|c| c.is_lowercase())),
|
||||
));
|
||||
|
||||
assert!(pattern.validate_text("hello").is_ok());
|
||||
assert!(pattern.validate_text("Hello").is_err()); // Uppercase not allowed
|
||||
|
||||
Reference in New Issue
Block a user