sb3_decoder/structs/
project.rs

1//! A project that contains all the decoded data from a Scratch 3.0 project file (.sb3).
2
3use std::collections::HashMap;
4
5use zip::ZipArchive;
6
7use crate::{
8    decoder::RawProject,
9    error::DecodeError,
10    structs::{Broadcast, Sprite, Stage, Target, Variable},
11};
12
13/// The [`Project`] struct represents a Scratch 3.0 project.
14///
15/// It contains all the decoded data from a `.sb3` file, including sprites and the stage.
16#[derive(Debug, Clone)]
17pub struct Project {
18    /// Contains all the sprites in the project.
19    pub sprites: Vec<Sprite>,
20
21    /// Contains the stage of the project.
22    pub stage: Stage,
23}
24
25impl Project {
26    /// Returns a new project from a [`RawProject`] and the zip archive.
27    ///
28    /// # Errors
29    /// Returns [`DecodeError::Zip`] if there is an error reading the ZIP archive.
30    /// Returns [`DecodeError::NotFound`] if something expected is not found in the archive or in
31    /// the JSON data.
32    pub fn new(
33        raw: RawProject,
34        zip: &mut ZipArchive<std::io::Cursor<Vec<u8>>>,
35    ) -> Result<Self, DecodeError> {
36        let mut sprites = Vec::new();
37        let mut stage = None;
38
39        for raw_target in raw.targets {
40            match Target::new(raw_target, zip)? {
41                Target::Sprite(sprite) => sprites.push(sprite),
42                Target::Stage(s) => stage = Some(s),
43            }
44        }
45
46        Ok(Self {
47            sprites,
48            stage: stage.ok_or(DecodeError::NotFound("Stage".to_string()))?,
49        })
50    }
51
52    /// Returns a reference to the global variables from the stage.
53    ///
54    /// # Example
55    ///
56    /// ```no_run
57    /// # use sb3_decoder::prelude::*;
58    /// # let mut decoder = DecoderBuilder::new()
59    /// #    .use_file("path/to/project.sb3")
60    /// #    .build()
61    /// #    .unwrap();
62    /// let project = decoder.decode().unwrap();
63    /// let global_vars = project.global_variables();
64    /// ```
65    pub fn global_variables(&self) -> &HashMap<String, Variable> {
66        &self.stage.variables
67    }
68
69    /// Returns a mutable reference to the global variables from the stage.
70    ///
71    /// # Example
72    ///
73    /// ```no_run
74    /// # use sb3_decoder::prelude::*;
75    /// # let mut decoder = DecoderBuilder::new()
76    /// #    .use_file("path/to/project.sb3")
77    /// #    .build()
78    /// #    .unwrap();
79    /// let mut project = decoder.decode().unwrap();
80    /// let mut global_vars = project.global_variables_mut();
81    /// ```
82    pub fn global_variables_mut(&mut self) -> &mut HashMap<String, Variable> {
83        &mut self.stage.variables
84    }
85
86    /// Returns all the broadcast messages in the project.
87    pub fn broadcasts(&self) -> Vec<Broadcast> {
88        self.stage.broadcasts.clone()
89    }
90
91    /// Returns a reference to a sprite by its name, if it exists.
92    ///
93    /// # Example
94    ///
95    /// ```no_run
96    /// # use sb3_decoder::prelude::*;
97    /// # let mut decoder = DecoderBuilder::new()
98    /// #    .use_file("path/to/project.sb3")
99    /// #    .build()
100    /// #    .unwrap();
101    /// let project = decoder.decode().unwrap();
102    /// let sprite = project.get("Cat");
103    /// if let Some(sprite) = sprite {
104    ///   // Do something with the sprite
105    /// }
106    /// ```
107    pub fn get(&self, name: &str) -> Option<&Sprite> {
108        self.sprites.iter().find(|sprite| sprite.name == name)
109    }
110
111    /// Returns a mutable reference to a sprite by its name, if it exists.
112    ///
113    /// # Example
114    ///
115    /// ```no_run
116    /// # use sb3_decoder::prelude::*;
117    /// # let mut decoder = DecoderBuilder::new()
118    /// #    .use_file("path/to/project.sb3")
119    /// #    .build()
120    /// #    .unwrap();
121    /// let mut project = decoder.decode().unwrap();
122    /// let sprite = project.get_mut("Cat");
123    /// if let Some(sprite) = sprite {
124    ///   // Do something with the sprite
125    /// }
126    /// ```
127    pub fn get_mut(&mut self, name: &str) -> Option<&mut Sprite> {
128        self.sprites.iter_mut().find(|sprite| sprite.name == name)
129    }
130}