sift_query_core/codecs/
list.rs1use crate::{Codec, ConfigError, FieldMeta, RowValue, ScalarValue, ValueType};
2use sift_query_algorithm::StableHasher64;
3
4#[derive(Debug, Clone)]
7pub struct ListResultCodec {
8 name: &'static str,
9 field: &'static str,
10 separator: &'static str,
11 item_type: ValueType,
12 max_items: usize,
13}
14impl ListResultCodec {
15 pub fn new(
16 name: &'static str,
17 field: &'static str,
18 separator: &'static str,
19 item_type: ValueType,
20 max_items: usize,
21 ) -> Result<Self, ConfigError> {
22 if separator.is_empty() || separator.len() > 32 || max_items == 0 || max_items > 16384 {
23 return Err(ConfigError::Invalid(
24 "invalid list separator or item budget".to_owned(),
25 ));
26 }
27 #[cfg(feature = "json")]
28 if item_type == ValueType::Json {
29 return Err(ConfigError::Invalid(
30 "list items must be scalar values".to_owned(),
31 ));
32 }
33 Ok(Self {
34 name,
35 field,
36 separator,
37 item_type,
38 max_items,
39 })
40 }
41}
42impl Codec for ListResultCodec {
43 fn name(&self) -> &'static str {
44 self.name
45 }
46 fn supports(&self, field: &FieldMeta) -> bool {
47 field.public_name == self.field && field.value_type == ValueType::Text
48 }
49 fn parse(&self, _: &FieldMeta, input: &str) -> Result<ScalarValue, String> {
50 Ok(ScalarValue::Text(input.to_owned()))
51 }
52 fn format_input(&self, _: &FieldMeta, value: &ScalarValue) -> Result<String, String> {
53 Ok(value.to_external_text())
54 }
55 fn format_result(&self, _: &FieldMeta, value: RowValue) -> Result<RowValue, String> {
56 let text = match value {
57 RowValue::Null => return Ok(RowValue::Null),
58 RowValue::Text(text) => text,
59 _ => return Err("list result needs a text field".to_owned()),
60 };
61 let mut items = Vec::new();
62 if !text.trim().is_empty() {
63 for part in text.split(self.separator) {
64 if items.len() >= self.max_items {
65 return Err("list item budget exceeded".to_owned());
66 }
67 items.push(
68 ScalarValue::parse(self.item_type, part, self.field)
69 .map(RowValue::from)
70 .map_err(|_| "invalid list item".to_owned())?,
71 );
72 }
73 }
74 Ok(RowValue::List(items))
75 }
76 fn fingerprint(&self) -> u64 {
77 let mut hash = StableHasher64::default();
78 for part in [self.name, self.field, self.separator] {
79 hash.write_delimited(part);
80 }
81 hash.write_delimited(format!("{:?}", self.item_type));
82 hash.write_delimited(self.max_items.to_string());
83 hash.finish()
84 }
85}