pub mod cache;
pub mod cohort;
pub mod source;
pub mod task;
pub mod view;
use serde_json::{json, Value};
pub use cache::{validate_cache, validate_cache_in, CacheCheck, CacheReport, CacheWriter, FeatureCache};
pub use cohort::{CohortTask, Partitions};
pub use source::{SourceAccess, SourceRef, SourceState};
pub use task::{RepinReport, Repinned, TaskManifest};
pub use view::{
preflight, preflight_each, preflight_each_in, preflight_in, write_preflight, write_preflight_in,
write_preflight_noted, Admitted, Preflight, PreflightDone, RowView, SubjectHistory,
};
pub const CODES: [(&str, &str); 20] = [
("T101", "the manifest is not JSON, or fails its JSON Schema"),
("T102", "the manifest's policy, slot or target definition is inconsistent"),
("T103", "a declared fingerprint does not match the manifest"),
("T201", "a subject is declared twice, or a row names an undeclared subject"),
("T202", "a subject's partition is not one of the split's partitions"),
("T203", "one source belongs to two subjects, or two subjects pin one sample"),
("T204", "a row id or a source id is not unique"),
("T301", "a source cannot be opened, or names a collection member that does not exist"),
("T302", "a source's content no longer matches its pinned content_id"),
("T303", "a source's identity.subject_id is not the subject the manifest maps it to"),
("T304", "a subject's fragments disagree about their clock, or it is not the subject's"),
("T305", "an event or document present in several fragments differs, or is not reconciled"),
("T306", "a source is a version or profile this engine cannot certify, or its clinical tables are invalid"),
("T401", "a cache's manifest is corrupt, or fails its schema"),
("T402", "a cache entry's payload is corrupt, or has the wrong dtype or shape"),
("T403", "a cache entry's source changed or cannot be reached: the entry is stale"),
("T404", "a cache or preflight is of another task, manifest, row or row version, or a validated cache changed"),
("T405", "learned preprocessing was fitted on a split other than the task's training partition"),
("T406", "a cache entry is not admissible as input at the row's cutoff"),
("T407", "an event-level cache entry names an event its source lacks, or a document that event does not own"),
];
pub fn code_summary(code: &str) -> &'static str {
CODES.iter().find(|(c, _)| *c == code).map(|(_, s)| *s).unwrap_or("")
}
#[derive(Debug, Clone, PartialEq)]
pub struct Finding {
pub code: String,
pub location: String,
pub message: String,
}
impl Finding {
pub fn new(code: &str, location: impl Into<String>, message: impl Into<String>) -> Finding {
Finding { code: code.into(), location: location.into(), message: message.into() }
}
pub fn to_json(&self) -> Value {
json!({
"code": self.code,
"location": self.location,
"message": self.message,
"summary": code_summary(&self.code),
})
}
pub fn line(&self) -> String {
format!("{} {}: {}", self.code, self.location, self.message)
}
}
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub struct Note {
pub location: String,
pub message: String,
}
impl Note {
pub fn new(location: impl Into<String>, message: impl Into<String>) -> Note {
Note { location: location.into(), message: message.into() }
}
pub fn to_json(&self) -> Value {
json!({"location": self.location, "message": self.message})
}
pub fn line(&self) -> String {
format!("note {}: {}", self.location, self.message)
}
}
pub fn sha256(bytes: &[u8]) -> String {
use sha2::{Digest, Sha256};
format!("sha256:{}", hex::encode(Sha256::digest(bytes)))
}
pub fn fingerprint(value: &Value) -> String {
sha256(crate::json::canonical(value).as_bytes())
}