scratch_test_model/scopes/
target_blocks.rs1use 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#[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 || 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 pub fn iter_blocks(&self) -> impl Iterator<Item = &ARc<BlockWrapper>> {
67 self.blocks.values()
68 }
69 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 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}