1use crate::types::{Header, HEADER_SIZE, MAGIC};
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum StructureCheck {
19 Pass,
21 Fail,
23}
24
25impl StructureCheck {
26 #[must_use]
28 pub fn is_pass(self) -> bool {
29 matches!(self, StructureCheck::Pass)
30 }
31}
32
33#[must_use]
35pub fn check_magic(data: &[u8]) -> StructureCheck {
36 if data.len() >= 4 && data[0..4] == MAGIC {
37 StructureCheck::Pass
38 } else {
39 StructureCheck::Fail
40 }
41}
42
43#[must_use]
45pub fn check_header_size(data: &[u8]) -> StructureCheck {
46 if data.len() >= HEADER_SIZE {
47 StructureCheck::Pass
48 } else {
49 StructureCheck::Fail
50 }
51}
52
53#[must_use]
55pub fn check_version(header: &Header) -> StructureCheck {
56 if header.version.0 <= crate::types::FORMAT_VERSION.0 {
57 StructureCheck::Pass
58 } else {
59 StructureCheck::Fail
60 }
61}
62
63#[must_use]
65pub fn check_flags(header: &Header) -> StructureCheck {
66 if header.flags.bits() & 0b1000_0000 == 0 {
69 StructureCheck::Pass
70 } else {
71 StructureCheck::Fail
72 }
73}
74
75#[must_use]
80pub fn validate_structure(data: &[u8]) -> bool {
81 if !check_magic(data).is_pass() || !check_header_size(data).is_pass() {
82 return false;
83 }
84 match Header::from_bytes(&data[..HEADER_SIZE]) {
85 Ok(header) => check_version(&header).is_pass() && check_flags(&header).is_pass(),
86 Err(_) => false,
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93 use crate::types::{Header, ModelType};
94
95 fn good_header_bytes() -> Vec<u8> {
96 let mut v = Header::new(ModelType::LinearRegression).to_bytes().to_vec();
97 v.resize(HEADER_SIZE, 0);
98 v
99 }
100
101 #[test]
102 fn test_check_magic_pass_and_fail() {
103 assert!(check_magic(&good_header_bytes()).is_pass());
104 assert!(!check_magic(b"GGUF").is_pass());
105 }
106
107 #[test]
108 fn test_validate_structure_round_trip() {
109 assert!(validate_structure(&good_header_bytes()));
110 let mut bad = good_header_bytes();
111 bad[0] = 0x00; assert!(!validate_structure(&bad));
113 }
114}