Skip to main content

scratch_test_model/scopes/
target_blocks.rs

1use std::collections::HashMap;
2
3use itertools::Itertools;
4use svalue::ARc;
5
6use crate::{
7    _exports::AsOpcodeUnit,
8    Id,
9    attrs::RefBlock,
10    aux::JsonBlocks,
11    blocks::{BlockKind, BlockKindUnit},
12    scopes::{TargetBlocksError, block_wrapper::BlockWrapper},
13};
14
15#[derive(Debug, PartialEq)]
16pub struct TargetBlocks {
17    blocks: HashMap<Id, ARc<BlockWrapper>>,
18}
19
20/// Produced by for example [`ProjectDoc::get_specific_kind`](crate::scopes::ProjectDoc::get_specific_kind).
21#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, thiserror::Error)]
22#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
23#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
24#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
25#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
26pub enum BlockReferenceInvalid {
27    #[error("the tried id doesn't belong to a valid block")]
28    IdNotFound,
29    #[error("the wanted block kind differs from the actual kind")]
30    WrongKind,
31}
32
33impl TargetBlocks {
34    pub(crate) fn from_json(value: &serde_json::Value) -> Result<Self, TargetBlocksError<'_>> {
35        let dict = value.as_object().ok_or(TargetBlocksError::ExpectedObject)?;
36        let all_blocks = JsonBlocks::new(value);
37
38        let valid: Result<HashMap<_, _>, _> = dict
39            .into_iter()
40            .map(|(id, obj): (&String, &serde_json::Value)| {
41                let id: Id = id.clone().into();
42                if obj
43                    .get("opcode")
44                    .and_then(|o| o.as_str())
45                    .map(|o| crate::attrs::dropdowns::META_DROPDOWN_MENUES.contains(&o))
46                    == Some(true)
47                    // sometimes there are array blocks that belong to
48                    // displays in the ui for variables
49                    || obj.is_array()
50                {
51                    return Ok(None);
52                }
53
54                match BlockWrapper::from_json(all_blocks, id.clone(), obj) {
55                    Ok(b) => Ok(Some((id, b.into()))),
56                    Err(error) => Err(TargetBlocksError::AtLeastOneInvalid { id, error }),
57                }
58            })
59            .flatten_ok()
60            .collect();
61
62        Ok(Self { blocks: valid? })
63    }
64
65    /// All blocks
66    pub fn iter_blocks(&self) -> impl Iterator<Item = &ARc<BlockWrapper>> {
67        self.blocks.values()
68    }
69    /// Get block by [`Id`]
70    pub fn get_by_id(&self, id: &Id) -> Option<&ARc<BlockWrapper>> {
71        self.blocks.get(id)
72    }
73    pub fn iter(&self) -> impl Iterator<Item = (&Id, &ARc<BlockWrapper>)> {
74        self.blocks.iter()
75    }
76    pub fn iter_ids(&self) -> impl Iterator<Item = &Id> {
77        self.blocks.keys()
78    }
79
80    /// Iterator of [`Id`]s and [opcode names](https://en.scratch-wiki.info/wiki/List_of_Block_Opcodes)
81    /// of valid and invalid blocks
82    pub fn ids_with_opcodes(&self) -> impl Iterator<Item = (&Id, BlockKindUnit)> {
83        self.blocks.iter().map(|(id, bw)| (id, bw.inner().opcode()))
84    }
85
86    pub fn get_specific_kind<T: WrappableKind>(
87        &self,
88        reference: &RefBlock<T>,
89    ) -> Result<&T, BlockReferenceInvalid> {
90        let any = self
91            .get_by_id(reference.id())
92            .ok_or(BlockReferenceInvalid::IdNotFound)?
93            .inner();
94        T::ensure(any).ok_or(BlockReferenceInvalid::WrongKind)
95    }
96    pub fn get_specific_with_wrapper<T: WrappableKind>(
97        &self,
98        reference: &RefBlock<T>,
99    ) -> Result<(&T, &svalue::ARc<BlockWrapper>), BlockReferenceInvalid> {
100        let wrapped = self
101            .get_by_id(reference.id())
102            .ok_or(BlockReferenceInvalid::IdNotFound)?;
103        let kind = T::ensure(wrapped.inner()).ok_or(BlockReferenceInvalid::WrongKind)?;
104        Ok((kind, wrapped))
105    }
106}
107
108pub trait WrappableKind {
109    fn ensure(any: &BlockKind) -> Option<&Self>;
110}
111
112impl WrappableKind for crate::blocks::CmpBlockKind {
113    fn ensure(any: &BlockKind) -> Option<&Self> {
114        if let BlockKind::ExprCmp(crate::blocks::ExprOrCmpBlockKind::Cmp(v)) = any {
115            Some(v)
116        } else {
117            None
118        }
119    }
120}
121
122impl WrappableKind for crate::blocks::ExprBlockKind {
123    fn ensure(any: &BlockKind) -> Option<&Self> {
124        if let BlockKind::ExprCmp(crate::blocks::ExprOrCmpBlockKind::Expr(v)) = any {
125            Some(v)
126        } else {
127            None
128        }
129    }
130}
131
132impl WrappableKind for crate::blocks::BlockKind {
133    fn ensure(any: &BlockKind) -> Option<&Self> {
134        Some(any)
135    }
136}
137impl WrappableKind for crate::blocks::ProceduresDefinition {
138    fn ensure(any: &BlockKind) -> Option<&Self> {
139        if let BlockKind::ProceduresDefinition(v) = any {
140            Some(v)
141        } else {
142            None
143        }
144    }
145}
146impl WrappableKind for crate::blocks::ProceduresPrototype {
147    fn ensure(any: &BlockKind) -> Option<&Self> {
148        if let BlockKind::ProceduresPrototype(v) = any {
149            Some(v)
150        } else {
151            None
152        }
153    }
154}
155impl WrappableKind for crate::blocks::EventBlockKind {
156    fn ensure(any: &BlockKind) -> Option<&Self> {
157        if let BlockKind::Event(v) = any {
158            Some(v)
159        } else {
160            None
161        }
162    }
163}
164impl WrappableKind for crate::blocks::StmtBlockKind {
165    fn ensure(any: &BlockKind) -> Option<&Self> {
166        if let BlockKind::Stmt(v) = any {
167            Some(v)
168        } else {
169            None
170        }
171    }
172}
173impl WrappableKind for crate::blocks::ExprOrCmpBlockKind {
174    fn ensure(any: &BlockKind) -> Option<&Self> {
175        if let BlockKind::ExprCmp(v) = any {
176            Some(v)
177        } else {
178            None
179        }
180    }
181}