sb3_decoder/structs/block/
block.rs

1//! This module provides the [`Block`] struct. It represents a Scratch block inside a target.
2
3use std::collections::HashMap;
4
5use crate::{decoder::RawBlock, error::DecodeError, structs::{BlockInput, Opcode}};
6
7/// The [`Block`] struct represents a Scratch block inside a target. It has an opcode, some inputs,
8/// some fields, and thats about it.
9#[derive(Debug, Clone, PartialEq)]
10pub struct Block {
11    /// The opcode of the block.
12    pub opcode: Opcode,
13
14    /// The inputs of the block, represented as a map from input name to a [`BlockInput`].
15    pub inputs: HashMap<String, BlockInput>,
16
17    /// The fields of the block, represented as a map from field name to a variable name.
18    pub fields: HashMap<String, String>,
19}
20
21impl Block {
22    /// Creates a new empty block with the given block ID and the entire [`crate::decoder::RawSprite::blocks`]
23    /// map to un-flatten the structure.
24    pub fn new(id: &str, blocks: &HashMap<String, RawBlock>) -> Result<Self, DecodeError> {
25        let raw = blocks
26            .get(id)
27            .ok_or_else(|| DecodeError::NotFound(format!("Block with ID {}", id)))?;
28        let inputs = raw
29            .inputs
30            .iter()
31            .map(|(k, v)| {
32                Ok((
33                    k.clone(),
34                    BlockInput::new(v.clone(), blocks)?,
35                ))
36            })
37            .collect::<Result<HashMap<String, BlockInput>, DecodeError>>()?;
38        let fields = raw
39            .fields
40            .iter()
41            .map(|(f, (name, _))| (f.clone(), name.clone()))
42            .collect();
43        Ok(Self {
44            opcode: raw.opcode.as_str().parse()?,
45            inputs,
46            fields,
47        })
48    }
49}
50
51pub type Script = Vec<Block>;
52
53/// Decodes scripts from a map with [`String`] and [`RawBlock`]s as key and value, respectively.
54pub fn decode_scripts(blocks: &HashMap<String, RawBlock>) -> Result<Vec<Script>, DecodeError> {
55    let mut scripts = Vec::new();
56
57    for (id, raw) in blocks {
58        if raw.top_level && raw.parent.is_none() {
59            let mut script = Vec::new();
60            let mut current_id = Some(id.clone());
61
62            while let Some(cid) = current_id {
63                let block = Block::new(&cid, blocks)?;
64                current_id = blocks.get(&cid).and_then(|b| b.next.clone());
65                script.push(block);
66            }
67
68            scripts.push(script);
69        }
70    }
71
72    Ok(scripts)
73}