luadec/
lib.rs

1//! # LuaDec - Lua 5.1 Bytecode Decompiler
2//!
3//! This crate provides Rust bindings for the LuaDec library, which decompiles
4//! Lua 5.1 bytecode back into readable Lua source code.
5//!
6//! ## Example
7//!
8//! ```rust,no_run
9//! use luadec::{decompile, DecompileError};
10//! use std::fs;
11//!
12//! fn main() -> Result<(), DecompileError> {
13//!     // Read compiled Lua bytecode
14//!     let bytecode = fs::read("example.luac")?;
15//!     
16//!     // Decompile to Lua source code
17//!     let source = decompile(&bytecode)?;
18//!     
19//!     println!("Decompiled source:\n{}", source);
20//!     Ok(())
21//! }
22//! ```
23
24pub mod error;
25pub mod ffi;
26
27pub use error::{DecompileError, Result};
28
29use std::fs;
30use std::path::Path;
31
32/// Decompile Lua 5.1 bytecode from a byte slice
33/// 
34/// # Arguments
35/// 
36/// * `bytecode` - The compiled Lua bytecode as a byte slice
37/// 
38/// # Returns
39/// 
40/// Returns the decompiled Lua source code as a String, or a DecompileError if
41/// decompilation fails.
42/// 
43/// # Example
44/// 
45/// ```rust,no_run
46/// use luadec::decompile;
47/// use std::fs;
48/// 
49/// let bytecode = fs::read("hello.luac")?;
50/// let source = decompile(&bytecode)?;
51/// println!("{}", source);
52/// # Ok::<(), luadec::DecompileError>(())
53/// ```
54pub fn decompile(bytecode: &[u8]) -> Result<String> {
55    ffi::decompile_bytecode_raw(bytecode)
56}
57
58/// Decompile Lua 5.1 bytecode from a file
59/// 
60/// # Arguments
61/// 
62/// * `path` - Path to the compiled Lua bytecode file (.luac)
63/// 
64/// # Returns
65/// 
66/// Returns the decompiled Lua source code as a String, or a DecompileError if
67/// reading the file or decompilation fails.
68/// 
69/// # Example
70/// 
71/// ```rust,no_run
72/// use luadec::decompile_file;
73/// 
74/// let source = decompile_file("hello.luac")?;
75/// println!("{}", source);
76/// # Ok::<(), luadec::DecompileError>(())
77/// ```
78pub fn decompile_file<P: AsRef<Path>>(path: P) -> Result<String> {
79    let bytecode = fs::read(path)?;
80    decompile(&bytecode)
81}
82
83/// A high-level decompiler interface for more advanced usage
84pub struct Decompiler {
85    // Future: could add configuration options here
86    // For now, we use the default behavior
87}
88
89impl Decompiler {
90    /// Create a new decompiler instance
91    pub fn new() -> Self {
92        Self {}
93    }
94    
95    /// Decompile bytecode with this decompiler instance
96    pub fn decompile(&self, bytecode: &[u8]) -> Result<String> {
97        decompile(bytecode)
98    }
99    
100    /// Decompile a file with this decompiler instance
101    pub fn decompile_file<P: AsRef<Path>>(&self, path: P) -> Result<String> {
102        decompile_file(path)
103    }
104}
105
106impl Default for Decompiler {
107    fn default() -> Self {
108        Self::new()
109    }
110}
111
112#[cfg(test)]
113mod tests {
114    use super::*;
115    
116    #[test]
117    fn test_empty_bytecode() {
118        let result = decompile(&[]);
119        assert!(result.is_err());
120        match result.unwrap_err() {
121            DecompileError::InvalidBytecode(_) => {},
122            other => panic!("Expected InvalidBytecode error, got: {:?}", other),
123        }
124    }
125    
126    #[test]
127    fn test_invalid_bytecode() {
128        let invalid_bytecode = b"not lua bytecode";
129        let result = decompile(invalid_bytecode);
130        assert!(result.is_err());
131    }
132    
133    #[test]
134    fn test_decompiler_struct() {
135        let decompiler = Decompiler::new();
136        let result = decompiler.decompile(&[]);
137        assert!(result.is_err());
138    }
139
140    #[test]
141    fn test_concurrent_decompile() {
142        use std::thread;
143        use std::sync::Arc;
144        
145        let invalid_bytecode = Arc::new(vec![1, 2, 3, 4, 5]);
146        let mut handles = vec![];
147        
148        // Spawn multiple threads that try to decompile simultaneously
149        for _ in 0..10 {
150            let bytecode = invalid_bytecode.clone();
151            let handle = thread::spawn(move || {
152                let result = decompile(&bytecode);
153                // All should fail with invalid bytecode, but shouldn't crash
154                assert!(result.is_err());
155            });
156            handles.push(handle);
157        }
158        
159        // Wait for all threads to complete
160        for handle in handles {
161            handle.join().expect("Thread panicked");
162        }
163    }
164
165}