Skip to main content

scratch_test_model/scopes/
project_doc.rs

1use crate::{
2    Id,
3    attrs::RefBlock,
4    blocks::BlockKindUnit,
5    error::BlockReferenceInvalid,
6    scopes::{block_wrapper::BlockWrapper, target_blocks::WrappableKind},
7};
8
9use super::target::Target;
10use itertools::Itertools;
11use svalue::ARc;
12
13/// Represents an entire sb3 program file with all [`Target`]s,
14/// blocks ([`TargetBlocks`](crate::TargetBlocks)) and
15/// procedures ([`TargetProcedures`](crate::TargetProcedures))
16///
17/// See the [`crate`] level docs for a general explaination
18#[derive(Debug, derive_getters::Getters, Clone, PartialEq)]
19pub struct ProjectDoc {
20    /// Targets of the document, see [`Target`] for details
21    pub(crate) targets: ARc<[Target]>,
22    /// The version number stored as metadata in the document
23    ///
24    /// This field is currently unused by the implementation
25    /// but may be used in the future to detect compatible files
26    pub(crate) semver: Option<ARc<str>>,
27}
28
29/// No block with the provided id was found.
30#[derive(Debug, thiserror::Error, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
31#[error("The requested block id isn't associated with a valid block")]
32pub struct NoValidBlockForId(());
33
34impl ProjectDoc {
35    pub fn ids_with_opcodes(&self) -> impl Iterator<Item = (&Id, BlockKindUnit)> {
36        self.targets()
37            .iter()
38            .flat_map(|t| t.blocks().ids_with_opcodes())
39    }
40
41    pub fn su_ids_with_blocks(&self) -> impl Iterator<Item = (&Id, BlockKindUnit)> {
42        self.ids_with_opcodes().sorted().unique()
43    }
44    /*
45    /// Returns the block with the given [`Id`] regardless in which target it's stored.
46    pub fn get_block_owned(&self, id: &crate::Id) -> Result<ARc<BlockWrapper>, NoValidBlockForId> {
47        self.get_block(id).cloned()
48    }
49    */
50    /// Returns the block with the given [`Id`] regardless in which target it's stored.
51    pub fn get_block<'a>(
52        &'a self,
53        id: &crate::Id,
54    ) -> Result<&'a ARc<BlockWrapper>, NoValidBlockForId> {
55        self.targets()
56            .iter()
57            .flat_map(|trgt| trgt.blocks().iter_blocks())
58            .find(|blk| blk.id() == id)
59            .ok_or(NoValidBlockForId(()))
60    }
61
62    pub fn get_specific_kind<T: WrappableKind + PartialEq>(
63        &self,
64        reference: &RefBlock<T>,
65    ) -> Result<&T, BlockReferenceInvalid> {
66        self.targets()
67            .iter()
68            .map(|trgt| trgt.blocks().get_specific_kind(reference))
69            .find(|blk| !matches!(blk, Err(BlockReferenceInvalid::IdNotFound)))
70            .ok_or(BlockReferenceInvalid::IdNotFound)?
71    }
72    pub fn get_specific_with_wrapper<T: WrappableKind>(
73        &self,
74        reference: &RefBlock<T>,
75    ) -> Result<(&T, &svalue::ARc<BlockWrapper>), BlockReferenceInvalid> {
76        self.targets()
77            .iter()
78            .map(|trgt| trgt.blocks().get_specific_with_wrapper(reference))
79            .find(|blk| !matches!(blk, Err(BlockReferenceInvalid::IdNotFound)))
80            .ok_or(BlockReferenceInvalid::IdNotFound)?
81    }
82}