dynpatch_core/
validator.rs1use crate::error::{Error, Result};
4use crate::loader::Library;
5use dynpatch_interface::{PatchMetadata, TypeLayout, Version};
6use tracing::{debug, warn};
7
8#[derive(Debug, Clone)]
10pub struct ValidationConfig {
11 pub check_version: bool,
13 pub check_layouts: bool,
15 pub check_hash: bool,
17 pub strict: bool,
19}
20
21impl Default for ValidationConfig {
22 fn default() -> Self {
23 Self {
24 check_version: true,
25 check_layouts: true,
26 check_hash: true,
27 strict: true,
28 }
29 }
30}
31
32impl ValidationConfig {
33 pub fn permissive() -> Self {
34 Self {
35 check_version: false,
36 check_layouts: false,
37 check_hash: false,
38 strict: false,
39 }
40 }
41
42 pub fn strict() -> Self {
43 Self {
44 check_version: true,
45 check_layouts: true,
46 check_hash: true,
47 strict: true,
48 }
49 }
50}
51
52pub struct Validator {
54 config: ValidationConfig,
55 expected_interface_version: Version,
56 expected_type_hash: u64,
57}
58
59impl Validator {
60 pub fn new(expected_interface_version: Version, expected_type_hash: u64) -> Self {
61 Self {
62 config: ValidationConfig::default(),
63 expected_interface_version,
64 expected_type_hash,
65 }
66 }
67
68 pub fn with_config(mut self, config: ValidationConfig) -> Self {
69 self.config = config;
70 self
71 }
72
73 pub fn validate(&self, library: &Library) -> Result<()> {
75 let metadata = library.metadata();
76 debug!("Validating patch: {}", metadata.name);
77
78 let mut errors = Vec::new();
79
80 if self.config.check_version {
82 if let Err(e) = self.validate_version(metadata) {
83 if self.config.strict {
84 return Err(e);
85 }
86 warn!("Version validation failed: {}", e);
87 errors.push(e);
88 }
89 }
90
91 if self.config.check_hash {
93 if let Err(e) = self.validate_hash(metadata) {
94 if self.config.strict {
95 return Err(e);
96 }
97 warn!("Hash validation failed: {}", e);
98 errors.push(e);
99 }
100 }
101
102 if !errors.is_empty() && self.config.strict {
103 return Err(Error::AbiValidationFailed(format!(
104 "Multiple validation errors: {:?}",
105 errors
106 )));
107 }
108
109 debug!("Validation passed for: {}", metadata.name);
110 Ok(())
111 }
112
113 fn validate_version(&self, metadata: &PatchMetadata) -> Result<()> {
114 if !self
115 .expected_interface_version
116 .is_compatible(&metadata.interface_version)
117 {
118 return Err(Error::VersionMismatch {
119 expected: self.expected_interface_version.to_string(),
120 found: metadata.interface_version.to_string(),
121 });
122 }
123 Ok(())
124 }
125
126 fn validate_hash(&self, metadata: &PatchMetadata) -> Result<()> {
127 if self.expected_type_hash != metadata.type_hash {
128 return Err(Error::AbiValidationFailed(format!(
129 "Type hash mismatch: expected {:x}, found {:x}",
130 self.expected_type_hash, metadata.type_hash
131 )));
132 }
133 Ok(())
134 }
135
136 pub fn validate_layout(&self, expected: &TypeLayout, found: &TypeLayout) -> Result<()> {
138 if !expected.matches(found) {
139 return Err(Error::TypeLayoutMismatch {
140 type_name: "unknown".to_string(),
141 expected_size: expected.size,
142 expected_align: expected.alignment,
143 found_size: found.size,
144 found_align: found.alignment,
145 });
146 }
147 Ok(())
148 }
149}
150
151#[cfg(test)]
152mod tests {
153 use super::*;
154 use dynpatch_interface::Version;
155
156 #[test]
157 fn test_validation_config() {
158 let config = ValidationConfig::default();
159 assert!(config.strict);
160
161 let config = ValidationConfig::permissive();
162 assert!(!config.strict);
163 }
164
165 #[test]
166 fn test_validator_creation() {
167 let version = Version::new(1, 0, 0);
168 let validator = Validator::new(version, 0x1234);
169 assert!(validator.config.strict);
170 }
171
172 #[test]
173 fn test_type_layout_validation() {
174 let validator = Validator::new(Version::new(1, 0, 0), 0);
175
176 let layout1 = TypeLayout {
177 size: 8,
178 alignment: 8,
179 type_id: 0x1234,
180 };
181 let layout2 = TypeLayout {
182 size: 8,
183 alignment: 8,
184 type_id: 0x1234,
185 };
186 let layout3 = TypeLayout {
187 size: 16,
188 alignment: 8,
189 type_id: 0x1234,
190 };
191
192 assert!(validator.validate_layout(&layout1, &layout2).is_ok());
193 assert!(validator.validate_layout(&layout1, &layout3).is_err());
194 }
195}