urna_format/sections/
contract.rs1use super::codec::Cursor;
8use crate::error::UrnaError;
9use crate::layout::{
10 SECTION_PAYLOAD_PREFIX_SIZE, SECTION_PAYLOAD_VERSION, SECTION_SEARCH_CONTRACT,
11};
12
13#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
14pub struct SearchContract {
15 pub metric: String,
16 pub score_type: String,
17 pub normalize: String,
18 pub index_type: String,
19 pub rerank_policy: String,
20}
21
22pub fn encode_search_contract(contract: &SearchContract) -> crate::Result<Vec<u8>> {
23 let json = serde_json::to_vec(contract)?;
24 let json_len = u32::try_from(json.len())
25 .map_err(|_| UrnaError::InvalidInput("search_contract JSON too large".into()))?;
26 let mut buf = Vec::with_capacity(SECTION_PAYLOAD_PREFIX_SIZE + json.len());
27 buf.extend_from_slice(&SECTION_PAYLOAD_VERSION.to_le_bytes());
28 buf.extend_from_slice(&(json_len as u64).to_le_bytes());
29 buf.extend_from_slice(&json);
30 Ok(buf)
31}
32
33pub fn decode_search_contract(data: &[u8]) -> crate::Result<SearchContract> {
34 let mut c = Cursor::new(data, SECTION_SEARCH_CONTRACT);
35 if c.data.len() < SECTION_PAYLOAD_PREFIX_SIZE {
36 return Err(c.malformed("payload shorter than prefix"));
37 }
38 let version = c.read_u32()?;
39 if version != SECTION_PAYLOAD_VERSION {
40 return Err(UrnaError::UnsupportedSectionVersion {
41 section_id: SECTION_SEARCH_CONTRACT,
42 version,
43 });
44 }
45 let json_len = c.read_u64()? as usize;
46 let json = c.read_bytes(json_len)?;
47 let contract: SearchContract = serde_json::from_slice(json).map_err(UrnaError::Json)?;
48 c.finish()?;
49 Ok(contract)
50}
51
52#[cfg(test)]
53mod tests {
54 use super::*;
55
56 #[test]
57 fn roundtrip() {
58 let c = SearchContract {
59 metric: "ip".into(),
60 score_type: "cosine".into(),
61 normalize: "l2".into(),
62 index_type: "exact".into(),
63 rerank_policy: "none".into(),
64 };
65 let bytes = encode_search_contract(&c).unwrap();
66 let back = decode_search_contract(&bytes).unwrap();
67 assert_eq!(c, back);
68 }
69}