mirror of
https://git.pumpin.online/funkpower/catena.git
synced 2026-10-23 03:07:13 +00:00
corpus-core: make the stream contract hold under hostile input
This commit is contained in:
@@ -26,11 +26,23 @@ pub struct Chunk {
|
||||
/// What part of the work the passage belongs to.
|
||||
pub role: Role,
|
||||
/// Seconds from the start of the recording, for timed sources.
|
||||
#[serde(
|
||||
default,
|
||||
serialize_with = "crate::float::serialize_option",
|
||||
deserialize_with = "crate::float::deserialize_option"
|
||||
)]
|
||||
pub start_time: Option<f64>,
|
||||
/// Seconds from the start of the recording, for timed sources.
|
||||
#[serde(
|
||||
default,
|
||||
serialize_with = "crate::float::serialize_option",
|
||||
deserialize_with = "crate::float::deserialize_option"
|
||||
)]
|
||||
pub end_time: Option<f64>,
|
||||
/// Set tags, sorted and deduplicated; see [`flags`].
|
||||
#[serde(default)]
|
||||
/// Set tags; see [`flags`]. Sorted and deduplicated on the way in and on
|
||||
/// the way out, so the same set of flags is always the same bytes
|
||||
/// whatever order the producer set them in.
|
||||
#[serde(default, deserialize_with = "sorted_flags")]
|
||||
pub flags: Vec<String>,
|
||||
}
|
||||
|
||||
@@ -62,12 +74,25 @@ impl Chunk {
|
||||
|
||||
/// Whether a flag is set.
|
||||
pub fn has_flag(&self, flag: &str) -> bool {
|
||||
self.flags
|
||||
.binary_search_by(|f| f.as_str().cmp(flag))
|
||||
.is_ok()
|
||||
self.flags.iter().any(|set| set == flag)
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::Record for Chunk {
|
||||
fn version(&self) -> u32 {
|
||||
self.v
|
||||
}
|
||||
}
|
||||
|
||||
/// Sort and deduplicate flags as they are read, so a record written by
|
||||
/// another implementation gets the same treatment as one built here.
|
||||
fn sorted_flags<'de, D: serde::Deserializer<'de>>(input: D) -> Result<Vec<String>, D::Error> {
|
||||
let mut flags = Vec::<String>::deserialize(input)?;
|
||||
flags.sort_unstable();
|
||||
flags.dedup();
|
||||
Ok(flags)
|
||||
}
|
||||
|
||||
/// The flag names the tools in this workspace set.
|
||||
pub mod flags {
|
||||
/// The passage's footnote markers were resolved against a citation list.
|
||||
@@ -122,6 +147,25 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn flags_read_from_a_stream_are_sorted_and_unique_too() {
|
||||
let chunk: Chunk = serde_json::from_str(
|
||||
r#"{"idx":0,"text":"x","tokens":1,"role":"body","flags":["zebra","alpha","alpha"]}"#,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(chunk.flags, ["alpha", "zebra"]);
|
||||
assert!(chunk.has_flag("zebra"));
|
||||
assert!(chunk.has_flag("alpha"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_timestamp_that_is_not_a_number_is_refused_rather_than_dropped() {
|
||||
let mut chunk = Chunk::new(0, "x", 1);
|
||||
chunk.start_time = Some(f64::NAN);
|
||||
let e = serde_json::to_string(&chunk).unwrap_err();
|
||||
assert!(e.to_string().contains("cannot be written"), "{e}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn flags_stay_sorted_and_unique() {
|
||||
let mut chunk = Chunk::new(0, "x", 1);
|
||||
|
||||
Reference in New Issue
Block a user