1use crate::generator::{SlideContent, SlideLayout, create_pptx_with_content};
28use crate::exc::Result;
29
30#[derive(Debug, Clone)]
32pub struct ProposalContent<'a> {
33 pub executive_summary: Vec<&'a str>,
34 pub problem: Vec<&'a str>,
35 pub solution: Vec<&'a str>,
36 pub timeline: Vec<(&'a str, &'a str)>,
37 pub budget: Vec<(&'a str, &'a str)>,
38 pub next_steps: Vec<&'a str>,
39}
40
41impl<'a> Default for ProposalContent<'a> {
42 fn default() -> Self {
43 Self {
44 executive_summary: vec!["Add executive summary points"],
45 problem: vec!["Define the problem"],
46 solution: vec!["Present your solution"],
47 timeline: vec![("Phase 1", "Description")],
48 budget: vec![("Item", "Amount")],
49 next_steps: vec!["Define next steps"],
50 }
51 }
52}
53
54#[derive(Debug, Clone)]
56pub struct TrainingContent<'a> {
57 pub objectives: Vec<&'a str>,
58 pub modules: Vec<(&'a str, Vec<&'a str>)>,
59 pub exercises: Vec<&'a str>,
60 pub summary: Vec<&'a str>,
61}
62
63impl<'a> Default for TrainingContent<'a> {
64 fn default() -> Self {
65 Self {
66 objectives: vec!["Learning objective 1"],
67 modules: vec![("Module 1", vec!["Topic 1", "Topic 2"])],
68 exercises: vec!["Practice exercise"],
69 summary: vec!["Key takeaway"],
70 }
71 }
72}
73
74#[derive(Debug, Clone)]
76pub struct StatusContent<'a> {
77 pub summary: Vec<&'a str>,
78 pub completed: Vec<&'a str>,
79 pub in_progress: Vec<&'a str>,
80 pub blocked: Vec<&'a str>,
81 pub next_week: Vec<&'a str>,
82 pub metrics: Vec<(&'a str, &'a str)>,
83}
84
85impl<'a> Default for StatusContent<'a> {
86 fn default() -> Self {
87 Self {
88 summary: vec!["High-level status"],
89 completed: vec!["Completed item"],
90 in_progress: vec!["In progress item"],
91 blocked: vec!["Blocked item"],
92 next_week: vec!["Planned item"],
93 metrics: vec![("Metric", "Value")],
94 }
95 }
96}
97
98#[derive(Debug, Clone)]
100pub struct TechnicalContent<'a> {
101 pub overview: Vec<&'a str>,
102 pub architecture: Vec<&'a str>,
103 pub components: Vec<(&'a str, Vec<&'a str>)>,
104 pub api_examples: Vec<(&'a str, &'a str)>,
105 pub best_practices: Vec<&'a str>,
106}
107
108impl<'a> Default for TechnicalContent<'a> {
109 fn default() -> Self {
110 Self {
111 overview: vec!["System overview"],
112 architecture: vec!["Architecture description"],
113 components: vec![("Component", vec!["Feature 1"])],
114 api_examples: vec![("Method", "Description")],
115 best_practices: vec!["Best practice"],
116 }
117 }
118}
119
120pub fn business_proposal<'a>(
133 title: &str,
134 author: &str,
135 content: ProposalContent<'a>,
136) -> Result<Vec<u8>> {
137 let mut slides = Vec::new();
138
139 slides.push(
141 SlideContent::new(title)
142 .add_bullet(author)
143 .layout(SlideLayout::CenteredTitle)
144 );
145
146 slides.push(
148 SlideContent::new("Agenda")
149 .add_bullet("Executive Summary")
150 .add_bullet("Problem Statement")
151 .add_bullet("Proposed Solution")
152 .add_bullet("Timeline & Budget")
153 .add_bullet("Next Steps")
154 );
155
156 let mut summary = SlideContent::new("Executive Summary");
158 for point in &content.executive_summary {
159 summary = summary.add_bullet(*point);
160 }
161 slides.push(summary);
162
163 let mut problem = SlideContent::new("Problem Statement");
165 for point in &content.problem {
166 problem = problem.add_bullet(*point);
167 }
168 slides.push(problem);
169
170 let mut solution = SlideContent::new("Proposed Solution");
172 for point in &content.solution {
173 solution = solution.add_bullet(*point);
174 }
175 slides.push(solution);
176
177 let mut timeline = SlideContent::new("Timeline");
179 for (phase, desc) in &content.timeline {
180 timeline = timeline.add_bullet(&format!("{}: {}", phase, desc));
181 }
182 slides.push(timeline);
183
184 let mut budget = SlideContent::new("Budget");
186 for (item, amount) in &content.budget {
187 budget = budget.add_bullet(&format!("{}: {}", item, amount));
188 }
189 slides.push(budget);
190
191 let mut next = SlideContent::new("Next Steps");
193 for step in &content.next_steps {
194 next = next.add_bullet(*step);
195 }
196 slides.push(next);
197
198 slides.push(
200 SlideContent::new("Questions?")
201 .add_bullet("Discussion and Q&A")
202 .layout(SlideLayout::CenteredTitle)
203 );
204
205 create_pptx_with_content(title, slides).map_err(|e| crate::exc::PptxError::InvalidOperation(e.to_string()))
206}
207
208pub fn training_material<'a>(
218 title: &str,
219 author: &str,
220 content: TrainingContent<'a>,
221) -> Result<Vec<u8>> {
222 let mut slides = Vec::new();
223
224 slides.push(
226 SlideContent::new(title)
227 .add_bullet(author)
228 .layout(SlideLayout::CenteredTitle)
229 );
230
231 let mut objectives = SlideContent::new("Learning Objectives");
233 for obj in &content.objectives {
234 objectives = objectives.add_bullet(*obj);
235 }
236 slides.push(objectives);
237
238 for (module_name, topics) in &content.modules {
240 let mut module = SlideContent::new(*module_name);
241 for topic in topics {
242 module = module.add_bullet(*topic);
243 }
244 slides.push(module);
245 }
246
247 let mut exercises = SlideContent::new("Exercises");
249 for ex in &content.exercises {
250 exercises = exercises.add_bullet(*ex);
251 }
252 slides.push(exercises);
253
254 let mut summary = SlideContent::new("Summary");
256 for point in &content.summary {
257 summary = summary.add_bullet(*point);
258 }
259 slides.push(summary);
260
261 slides.push(
263 SlideContent::new("Questions?")
264 .layout(SlideLayout::CenteredTitle)
265 );
266
267 create_pptx_with_content(title, slides).map_err(|e| crate::exc::PptxError::InvalidOperation(e.to_string()))
268}
269
270pub fn status_report<'a>(
281 title: &str,
282 date: &str,
283 content: StatusContent<'a>,
284) -> Result<Vec<u8>> {
285 let mut slides = Vec::new();
286
287 slides.push(
289 SlideContent::new(title)
290 .add_bullet(date)
291 .layout(SlideLayout::CenteredTitle)
292 );
293
294 let mut summary = SlideContent::new("Executive Summary");
296 for point in &content.summary {
297 summary = summary.add_bullet(*point);
298 }
299 slides.push(summary);
300
301 let mut completed = SlideContent::new("Completed ✓");
303 for item in &content.completed {
304 completed = completed.add_bullet(*item);
305 }
306 slides.push(completed);
307
308 let mut progress = SlideContent::new("In Progress");
310 for item in &content.in_progress {
311 progress = progress.add_bullet(*item);
312 }
313 slides.push(progress);
314
315 if !content.blocked.is_empty() {
317 let mut blocked = SlideContent::new("Blocked / Risks");
318 for item in &content.blocked {
319 blocked = blocked.add_bullet(*item);
320 }
321 slides.push(blocked);
322 }
323
324 let mut next = SlideContent::new("Next Week");
326 for item in &content.next_week {
327 next = next.add_bullet(*item);
328 }
329 slides.push(next);
330
331 if !content.metrics.is_empty() {
333 let mut metrics = SlideContent::new("Key Metrics");
334 for (metric, value) in &content.metrics {
335 metrics = metrics.add_bullet(&format!("{}: {}", metric, value));
336 }
337 slides.push(metrics);
338 }
339
340 create_pptx_with_content(title, slides).map_err(|e| crate::exc::PptxError::InvalidOperation(e.to_string()))
341}
342
343pub fn technical_doc<'a>(
353 title: &str,
354 version: &str,
355 content: TechnicalContent<'a>,
356) -> Result<Vec<u8>> {
357 let mut slides = Vec::new();
358
359 slides.push(
361 SlideContent::new(title)
362 .add_bullet(&format!("Version {}", version))
363 .layout(SlideLayout::CenteredTitle)
364 );
365
366 let mut overview = SlideContent::new("Overview");
368 for point in &content.overview {
369 overview = overview.add_bullet(*point);
370 }
371 slides.push(overview);
372
373 let mut arch = SlideContent::new("Architecture");
375 for point in &content.architecture {
376 arch = arch.add_bullet(*point);
377 }
378 slides.push(arch);
379
380 for (name, features) in &content.components {
382 let mut comp = SlideContent::new(*name);
383 for feature in features {
384 comp = comp.add_bullet(*feature);
385 }
386 slides.push(comp);
387 }
388
389 if !content.api_examples.is_empty() {
391 let mut api = SlideContent::new("API Reference");
392 for (method, desc) in &content.api_examples {
393 api = api.add_bullet(&format!("{} - {}", method, desc));
394 }
395 slides.push(api);
396 }
397
398 let mut practices = SlideContent::new("Best Practices");
400 for practice in &content.best_practices {
401 practices = practices.add_bullet(*practice);
402 }
403 slides.push(practices);
404
405 create_pptx_with_content(title, slides).map_err(|e| crate::exc::PptxError::InvalidOperation(e.to_string()))
406}
407
408pub fn simple(title: &str, slides: &[(&str, &[&str])]) -> Result<Vec<u8>> {
410 let slide_contents: Vec<SlideContent> = slides.iter().map(|(slide_title, bullets)| {
411 let mut slide = SlideContent::new(*slide_title);
412 for bullet in *bullets {
413 slide = slide.add_bullet(*bullet);
414 }
415 slide
416 }).collect();
417
418 create_pptx_with_content(title, slide_contents).map_err(|e| crate::exc::PptxError::InvalidOperation(e.to_string()))
419}
420
421#[cfg(test)]
422mod tests {
423 use super::*;
424
425 #[test]
426 fn test_business_proposal() {
427 let content = ProposalContent::default();
428 let result = business_proposal("Test Proposal", "Author", content);
429 assert!(result.is_ok());
430 let data = result.unwrap();
431 assert!(data.len() > 1000);
432 }
433
434 #[test]
435 fn test_training_material() {
436 let content = TrainingContent::default();
437 let result = training_material("Test Training", "Trainer", content);
438 assert!(result.is_ok());
439 }
440
441 #[test]
442 fn test_status_report() {
443 let content = StatusContent::default();
444 let result = status_report("Weekly Status", "2025-01-01", content);
445 assert!(result.is_ok());
446 }
447
448 #[test]
449 fn test_technical_doc() {
450 let content = TechnicalContent::default();
451 let result = technical_doc("API Documentation", "1.0.0", content);
452 assert!(result.is_ok());
453 }
454
455 #[test]
456 fn test_simple() {
457 let slides = [
458 ("Introduction", &["Point 1", "Point 2"][..]),
459 ("Conclusion", &["Summary"][..]),
460 ];
461 let result = simple("Simple Presentation", &slides);
462 assert!(result.is_ok());
463 }
464
465 #[test]
466 fn test_custom_proposal_content() {
467 let content = ProposalContent {
468 executive_summary: vec!["We need more budget", "Market opportunity exists"],
469 problem: vec!["Current system is outdated", "Losing customers"],
470 solution: vec!["New platform", "Modern technology"],
471 timeline: vec![("Q1", "Design"), ("Q2", "Build"), ("Q3", "Launch")],
472 budget: vec![("Development", "$100,000"), ("Marketing", "$50,000")],
473 next_steps: vec!["Approve budget", "Hire team", "Start development"],
474 };
475 let result = business_proposal("Q1 Budget Proposal", "Finance", content);
476 assert!(result.is_ok());
477 }
478}
479