memlink_runtime/resolver/
local.rs1use std::fs::File;
4use std::io::Read;
5use std::path::Path;
6
7use crate::error::{Error, Result};
8use crate::resolver::{ArtifactHandle, ModuleRef, ModuleResolver};
9
10pub struct LocalResolver;
11
12impl LocalResolver {
13 pub fn new() -> Self {
14 LocalResolver
15 }
16}
17
18impl Default for LocalResolver {
19 fn default() -> Self {
20 Self::new()
21 }
22}
23
24impl ModuleResolver for LocalResolver {
25 fn resolve(&self, reference: ModuleRef) -> Result<ArtifactHandle> {
26 let ModuleRef::LocalPath(path) = reference;
27
28 if !path.exists() {
29 return Err(Error::FileNotFound(path));
30 }
31
32 validate_extension(&path)?;
33 validate_shared_library(&path)?;
34
35 Ok(ArtifactHandle::Local(path))
36 }
37}
38
39fn validate_extension(path: &Path) -> Result<()> {
40 let extension = path
41 .extension()
42 .and_then(|ext| ext.to_str())
43 .unwrap_or("");
44
45 match extension.to_lowercase().as_str() {
46 "so" | "dll" | "dylib" => Ok(()),
47 _ => Err(Error::InvalidModuleFormat(format!(
48 "Invalid extension '.{}': expected .so, .dll, or .dylib",
49 extension
50 ))),
51 }
52}
53
54pub fn validate_shared_library(path: &Path) -> Result<()> {
55 let mut file = File::open(path).map_err(|e| {
56 Error::InvalidModuleFormat(format!("Failed to open file: {}", e))
57 })?;
58
59 let mut buffer = [0u8; 16];
60 file.read_exact(&mut buffer).map_err(|e| {
61 Error::InvalidModuleFormat(format!("Failed to read file header: {}", e))
62 })?;
63
64 if buffer[0..4] == [0x7f, 0x45, 0x4c, 0x46] {
65 return Ok(());
66 }
67
68 if buffer[0..4] == [0xce, 0xfa, 0xed, 0xfe]
69 || buffer[0..4] == [0xfe, 0xed, 0xfa, 0xce]
70 || buffer[0..4] == [0xcf, 0xfa, 0xed, 0xfe]
71 || buffer[0..4] == [0xfe, 0xed, 0xfa, 0xcf]
72 {
73 return Ok(());
74 }
75
76 if buffer[0..2] == [0x4d, 0x5a] {
77 return Ok(());
78 }
79
80 Err(Error::InvalidModuleFormat(format!(
81 "File '{}' does not have valid shared library magic bytes. Found: {:02x?}",
82 path.display(),
83 &buffer[0..4]
84 )))
85}