dllx/
lib.rs

1// SPDX-License-Identifier: AGPL-3.0
2
3use std::{env, fs::{self, File}, io::{self, Read}, path::{Path, PathBuf}};
4use zip::read::ZipArchive;
5use serde::{Deserialize};
6use libloading::{Library, Symbol};
7
8// Manifest structure
9#[derive(Deserialize)]
10struct Manifest {
11    name: String,
12    platforms: std::collections::HashMap<String, String>,
13}
14
15// Extracts the .dllx (zip file) into a temporary directory
16fn 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
37// Read the manifest.json inside the .dllx and determine the platform-specific file to extract
38fn 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    // Look for manifest.json in the archive
43    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
69// Determine the appropriate file based on the platform
70fn 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
83// Load the appropriate shared library for the platform
84fn 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    // Extract the .dllx file into the target directory
89    let target_dir = "./extracted";
90    extract_dllx(dllx_file, target_dir)?;
91    
92    // The platform-specific shared library file
93    let plugin_path = Path::new(target_dir).join(platform_file);
94    
95    // Load the shared library
96    let lib = unsafe { Library::new(plugin_path)? };
97    Ok(lib)
98}
99
100// Function lookup in the shared library
101fn 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    // Read manifest from the .dllx file
110    let manifest = read_manifest_from_dllx(dllx_file)?;
111    
112    // Load the library based on the platform
113    let lib = load_library(dllx_file, &manifest)?;
114    
115    // Lookup and call the function 'Foo'
116    let func: Symbol<unsafe fn()> = lookup_function(&lib, function_name)?;
117    
118    // Call the function (this assumes it's a function with no arguments and no return value)
119    unsafe { func() };
120    
121    Ok(())
122}
123