1use std::{env, fs::{self, File}, io::{self, Read}, path::{Path, PathBuf}};
4use zip::read::ZipArchive;
5use serde::{Deserialize};
6use libloading::{Library, Symbol};
7
8#[derive(Deserialize)]
10struct Manifest {
11 name: String,
12 platforms: std::collections::HashMap<String, String>,
13}
14
15fn extract_dllx(dllx_file: &str, target_dir: &str) -> io::Result<()> {
17 let file = File::open(dllx_file)?;
18 let mut archive = ZipArchive::new(file)?;
19
20 fs::create_dir_all(target_dir)?;
21
22 for i in 0..archive.len() {
23 let mut file = archive.by_index(i)?;
24 let outpath = Path::new(target_dir).join(file.name());
25
26 if (&*file.name()).ends_with('/') {
27 fs::create_dir_all(&outpath)?;
28 } else {
29 let mut outfile = File::create(&outpath)?;
30 io::copy(&mut file, &mut outfile)?;
31 }
32 }
33
34 Ok(())
35}
36
37fn read_manifest_from_dllx(dllx_file: &str) -> Result<Manifest, io::Error> {
39 let file = File::open(dllx_file)?;
40 let mut archive = ZipArchive::new(file)?;
41
42 let mut manifest_file = None;
44 let mut file_names = Vec::new();
45 for i in 0..archive.len() {
46 file_names.push(archive.by_index(i)?.name().to_string());
47 }
48
49 for name in file_names {
50 if name == "manifest.json" {
51 let file = archive.by_name(&name)?;
52 manifest_file = Some(file);
53 break;
54 }
55 }
56
57 match manifest_file {
58 Some(file) => {
59 let mut file = file;
60 let mut content = String::new();
61 file.read_to_string(&mut content)?;
62 let manifest: Manifest = serde_json::from_str(&content)?;
63 Ok(manifest)
64 }
65 None => Err(io::Error::new(io::ErrorKind::NotFound, "manifest.json not found")),
66 }
67}
68
69fn get_platform_file(manifest: &Manifest) -> Option<String> {
71 let current_platform = env::consts::OS;
72
73 match current_platform {
74 "windows" => manifest.platforms.get("windows").cloned(),
75 "macos" => manifest.platforms.get("macos").cloned(),
76 "linux" => manifest.platforms.get("linux").cloned(),
77 "ios" => manifest.platforms.get("ios").cloned(),
78 "android" => manifest.platforms.get("android").cloned(),
79 _ => None,
80 }
81}
82
83fn load_library(dllx_file: &str, manifest: &Manifest) -> Result<Library, Box<dyn std::error::Error>> {
85 let platform_file = get_platform_file(manifest)
86 .ok_or("No platform-specific file found")?;
87
88 let target_dir = "./extracted";
90 extract_dllx(dllx_file, target_dir)?;
91
92 let plugin_path = Path::new(target_dir).join(platform_file);
94
95 let lib = unsafe { Library::new(plugin_path)? };
97 Ok(lib)
98}
99
100fn lookup_function<'a>(lib: &'a Library, func_name: &str) -> Result<Symbol<'a, unsafe fn()>, Box<dyn std::error::Error>> {
102 unsafe {
103 let func: Symbol<unsafe fn()> = lib.get(func_name.as_bytes())?;
104 Ok(func)
105 }
106}
107
108pub fn load_and_call(dllx_file: &str, function_name: &str) -> Result<(), Box<dyn std::error::Error>> {
109 let manifest = read_manifest_from_dllx(dllx_file)?;
111
112 let lib = load_library(dllx_file, &manifest)?;
114
115 let func: Symbol<unsafe fn()> = lookup_function(&lib, function_name)?;
117
118 unsafe { func() };
120
121 Ok(())
122}
123