1#![cfg_attr(all(not(feature = "std"), not(test)), no_std)]
15
16extern crate alloc;
17
18use alloc::string::String;
19use core::fmt;
20use serde::{Deserialize, Serialize};
21
22#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
24pub struct Version {
25 pub major: u32,
26 pub minor: u32,
27 pub patch: u32,
28}
29
30impl Version {
31 pub const fn new(major: u32, minor: u32, patch: u32) -> Self {
32 Self {
33 major,
34 minor,
35 patch,
36 }
37 }
38
39 pub fn is_compatible(&self, other: &Version) -> bool {
41 self.major == other.major
42 }
43}
44
45impl fmt::Display for Version {
46 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47 write!(f, "{}.{}.{}", self.major, self.minor, self.patch)
48 }
49}
50
51#[derive(Debug, Clone, Serialize, Deserialize)]
53pub struct PatchMetadata {
54 pub name: String,
56 pub version: Version,
58 pub interface_version: Version,
60 pub type_hash: u64,
62 pub description: Option<String>,
64 pub author: Option<String>,
66}
67
68impl PatchMetadata {
69 pub fn new(name: String, version: Version, interface_version: Version, type_hash: u64) -> Self {
70 Self {
71 name,
72 version,
73 interface_version,
74 type_hash,
75 description: None,
76 author: None,
77 }
78 }
79
80 pub fn with_description(mut self, description: String) -> Self {
81 self.description = Some(description);
82 self
83 }
84
85 pub fn with_author(mut self, author: String) -> Self {
86 self.author = Some(author);
87 self
88 }
89}
90
91#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
93pub struct TypeLayout {
94 pub size: usize,
95 pub alignment: usize,
96 pub type_id: u64, }
98
99impl TypeLayout {
100 pub fn of<T: 'static>() -> Self {
101 Self {
102 size: core::mem::size_of::<T>(),
103 alignment: core::mem::align_of::<T>(),
104 type_id: type_id_as_u64::<T>(),
105 }
106 }
107
108 pub fn matches(&self, other: &TypeLayout) -> bool {
109 self.size == other.size && self.alignment == other.alignment && self.type_id == other.type_id
110 }
111}
112
113fn type_id_as_u64<T: 'static>() -> u64 {
115 compute_type_hash(
118 core::any::type_name::<T>(),
119 core::mem::size_of::<T>(),
120 core::mem::align_of::<T>(),
121 )
122}
123
124pub trait PatchInterface: Send + Sync {
126 fn metadata(&self) -> &PatchMetadata;
128
129 fn type_layout(&self) -> TypeLayout;
131}
132
133pub trait PatchEntry: Send + Sync {
135 fn init(&mut self) -> Result<(), PatchError>;
137
138 fn teardown(&mut self);
140}
141
142#[derive(Debug, Clone, Serialize, Deserialize)]
144pub enum PatchError {
145 IncompatibleAbi {
147 expected: String,
148 found: String,
149 },
150 IncompatibleVersion {
152 expected: String,
153 found: String,
154 },
155 SymbolNotFound {
157 symbol: String,
158 },
159 InitializationFailed {
161 message: String,
162 },
163 InvalidFormat {
165 message: String,
166 },
167 Other {
169 message: String,
170 },
171}
172
173impl fmt::Display for PatchError {
174 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
175 match self {
176 PatchError::IncompatibleAbi { expected, found } => {
177 write!(f, "ABI mismatch: expected {}, found {}", expected, found)
178 }
179 PatchError::IncompatibleVersion { expected, found } => {
180 write!(f, "Version mismatch: expected {}, found {}", expected, found)
181 }
182 PatchError::SymbolNotFound { symbol } => {
183 write!(f, "Symbol not found: {}", symbol)
184 }
185 PatchError::InitializationFailed { message } => {
186 write!(f, "Initialization failed: {}", message)
187 }
188 PatchError::InvalidFormat { message } => {
189 write!(f, "Invalid format: {}", message)
190 }
191 PatchError::Other { message } => {
192 write!(f, "Error: {}", message)
193 }
194 }
195 }
196}
197
198#[cfg(feature = "std")]
199impl std::error::Error for PatchError {}
200
201pub fn compute_type_hash(type_name: &str, size: usize, align: usize) -> u64 {
203 let mut hash: u64 = 0xcbf29ce484222325;
205
206 for byte in type_name.as_bytes() {
207 hash ^= *byte as u64;
208 hash = hash.wrapping_mul(0x100000001b3);
209 }
210
211 hash ^= size as u64;
212 hash = hash.wrapping_mul(0x100000001b3);
213 hash ^= align as u64;
214 hash = hash.wrapping_mul(0x100000001b3);
215
216 hash
217}
218
219pub trait StateMigration<Old, New> {
221 fn migrate(old: Old) -> Result<New, PatchError>;
223}
224
225#[cfg(test)]
226mod tests {
227 use super::*;
228
229 #[test]
230 fn test_version_compatibility() {
231 let v1 = Version::new(1, 0, 0);
232 let v2 = Version::new(1, 1, 0);
233 let v3 = Version::new(2, 0, 0);
234
235 assert!(v1.is_compatible(&v2));
236 assert!(!v1.is_compatible(&v3));
237 }
238
239 #[test]
240 fn test_type_layout() {
241 let layout1 = TypeLayout::of::<u32>();
242 let layout2 = TypeLayout::of::<u32>();
243 let layout3 = TypeLayout::of::<u64>();
244
245 assert!(layout1.matches(&layout2));
246 assert!(!layout1.matches(&layout3));
247 }
248
249 #[test]
250 fn test_compute_type_hash() {
251 let hash1 = compute_type_hash("MyType", 8, 8);
252 let hash2 = compute_type_hash("MyType", 8, 8);
253 let hash3 = compute_type_hash("MyType", 16, 8);
254
255 assert_eq!(hash1, hash2);
256 assert_ne!(hash1, hash3);
257 }
258}