Skip to main content

ppt_rs/parts/
vba_macro.rs

1//! VBA macro part
2//!
3//! Represents VBA macros embedded in the presentation (.pptm files).
4//! Note: VBA macros require the presentation to be saved as .pptm format.
5
6use super::base::{Part, PartType, ContentType};
7use crate::exc::PptxError;
8
9/// VBA project part (ppt/vbaProject.bin)
10#[derive(Debug, Clone)]
11pub struct VbaProjectPart {
12    path: String,
13    data: Vec<u8>,
14    modules: Vec<VbaModule>,
15}
16
17/// VBA module
18#[derive(Debug, Clone)]
19pub struct VbaModule {
20    pub name: String,
21    pub code: String,
22    pub module_type: VbaModuleType,
23}
24
25/// VBA module type
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub enum VbaModuleType {
28    Standard,
29    Class,
30    Form,
31    Document,
32}
33
34impl VbaModule {
35    /// Create a new standard module
36    pub fn new(name: impl Into<String>, code: impl Into<String>) -> Self {
37        VbaModule {
38            name: name.into(),
39            code: code.into(),
40            module_type: VbaModuleType::Standard,
41        }
42    }
43
44    /// Create a class module
45    pub fn class(name: impl Into<String>, code: impl Into<String>) -> Self {
46        VbaModule {
47            name: name.into(),
48            code: code.into(),
49            module_type: VbaModuleType::Class,
50        }
51    }
52
53    /// Set module type
54    pub fn module_type(mut self, module_type: VbaModuleType) -> Self {
55        self.module_type = module_type;
56        self
57    }
58}
59
60impl VbaProjectPart {
61    /// Create a new VBA project part
62    pub fn new() -> Self {
63        VbaProjectPart {
64            path: "ppt/vbaProject.bin".to_string(),
65            data: vec![],
66            modules: vec![],
67        }
68    }
69
70    /// Create from binary data (existing vbaProject.bin)
71    pub fn from_data(data: Vec<u8>) -> Self {
72        VbaProjectPart {
73            path: "ppt/vbaProject.bin".to_string(),
74            data,
75            modules: vec![],
76        }
77    }
78
79    /// Add a module
80    pub fn add_module(mut self, module: VbaModule) -> Self {
81        self.modules.push(module);
82        self
83    }
84
85    /// Get modules
86    pub fn modules(&self) -> &[VbaModule] {
87        &self.modules
88    }
89
90    /// Get binary data
91    pub fn data(&self) -> &[u8] {
92        &self.data
93    }
94
95    /// Check if this is a macro-enabled presentation
96    pub fn is_macro_enabled(&self) -> bool {
97        !self.data.is_empty() || !self.modules.is_empty()
98    }
99
100    /// Get the content type for macro-enabled presentations
101    pub fn macro_content_type() -> &'static str {
102        "application/vnd.ms-office.vbaProject"
103    }
104
105    /// Get the file extension for macro-enabled presentations
106    pub fn macro_extension() -> &'static str {
107        "pptm"
108    }
109}
110
111impl Default for VbaProjectPart {
112    fn default() -> Self {
113        Self::new()
114    }
115}
116
117impl Part for VbaProjectPart {
118    fn path(&self) -> &str {
119        &self.path
120    }
121
122    fn part_type(&self) -> PartType {
123        PartType::Relationships // Custom handling for binary
124    }
125
126    fn content_type(&self) -> ContentType {
127        ContentType::Xml // Actually binary, but handled specially
128    }
129
130    fn to_xml(&self) -> Result<String, PptxError> {
131        // VBA projects are binary, not XML
132        Err(PptxError::InvalidOperation("VBA projects are binary, not XML".to_string()))
133    }
134
135    fn from_xml(_xml: &str) -> Result<Self, PptxError> {
136        Err(PptxError::InvalidOperation("VBA projects cannot be created from XML".to_string()))
137    }
138}
139
140/// VBA macro security settings
141#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
142pub enum MacroSecurity {
143    #[default]
144    DisableAll,
145    DisableWithNotification,
146    DisableExceptDigitallySigned,
147    EnableAll,
148}
149
150impl MacroSecurity {
151    pub fn as_str(&self) -> &'static str {
152        match self {
153            MacroSecurity::DisableAll => "DisableAll",
154            MacroSecurity::DisableWithNotification => "DisableWithNotification",
155            MacroSecurity::DisableExceptDigitallySigned => "DisableExceptDigitallySigned",
156            MacroSecurity::EnableAll => "EnableAll",
157        }
158    }
159}
160
161#[cfg(test)]
162mod tests {
163    use super::*;
164
165    #[test]
166    fn test_vba_project_new() {
167        let project = VbaProjectPart::new();
168        assert_eq!(project.path(), "ppt/vbaProject.bin");
169        assert!(!project.is_macro_enabled());
170    }
171
172    #[test]
173    fn test_vba_module_new() {
174        let module = VbaModule::new("Module1", "Sub Test()\nEnd Sub");
175        assert_eq!(module.name, "Module1");
176        assert_eq!(module.module_type, VbaModuleType::Standard);
177    }
178
179    #[test]
180    fn test_vba_module_class() {
181        let module = VbaModule::class("MyClass", "Private x As Integer");
182        assert_eq!(module.module_type, VbaModuleType::Class);
183    }
184
185    #[test]
186    fn test_vba_project_add_module() {
187        let project = VbaProjectPart::new()
188            .add_module(VbaModule::new("Module1", "Sub Test()\nEnd Sub"))
189            .add_module(VbaModule::class("Class1", ""));
190        assert_eq!(project.modules().len(), 2);
191        assert!(project.is_macro_enabled());
192    }
193
194    #[test]
195    fn test_vba_from_data() {
196        let project = VbaProjectPart::from_data(vec![0x00, 0x01, 0x02]);
197        assert!(project.is_macro_enabled());
198        assert_eq!(project.data().len(), 3);
199    }
200
201    #[test]
202    fn test_macro_extension() {
203        assert_eq!(VbaProjectPart::macro_extension(), "pptm");
204    }
205}