Skip to main content

scratch_test_model/
error.rs

1use std::path::PathBuf;
2
3pub use crate::aux::JsonCtx;
4use crate::{
5    Id,
6    attrs::data::{ListKind, VariableKind},
7};
8
9pub use crate::aux::errors::{
10    AttributeContentError, AttributeParseError, BlockJsonStructureError, BlockKindError,
11    MandatoryAttrMissingError,
12};
13pub use crate::scopes::project_doc::NoValidBlockForId;
14pub use crate::scopes::target_blocks::BlockReferenceInvalid;
15
16#[allow(unused)]
17use crate::attrs::{List, Variable};
18
19/// A [target](`crate::scopes::Target`)'s collection of [`Variable`]s or [`List`]s failed to parse.
20///
21/// The `Kind` is either `VariableKind` or `ListKind` and isn't
22/// present at runtime. It only gives type-level information about
23/// the place where the error occured.
24///
25/// See [`TargetData`](crate::scopes::TargetData)
26#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, thiserror::Error)]
27pub enum TargetDataError<Kind> {
28    /// The data for variables / lists should always be stored
29    /// – for each target – in a json object (`{ ... }`) per kind.
30    /// This variant indicates that this wasn't the case.
31    #[error("expected object {{...}} for data entities of target")]
32    ExpectedObject,
33    /// At least one stored variable / list has invalid structure
34    #[error("at least one target data entry (id={0:?}) has unknown structure")]
35    AtLeastOneInvalid(Id),
36    /// This variant can never occur (see [`std::convert::Infallible`])
37    /// It is only present for placing a type variable
38    #[error("SHOULD NEVER OCCUR")]
39    Phantom {
40        phantom: std::marker::PhantomData<Kind>,
41        delete: std::convert::Infallible,
42    },
43}
44
45/// Failed to parse [`BlockWrapper`](crate::blocks::BlockWrapper).
46#[derive(Debug, PartialEq, thiserror::Error)]
47pub enum BlockError<'a> {
48    /// A property of [`BlockWrapper`](crate::blocks::BlockWrapper) that is not directly
49    /// related to the blocks [kind](crate::blocks::BlockKind) is missing.
50    /// This includes ["next"](crate::blocks::BlockWrapper::next),
51    /// ["parent"](crate::blocks::BlockWrapper::parent),
52    /// ["shadow"](crate::blocks::BlockWrapper::shadow), "topLevel", "x" and "y".
53    /// "x" and "y" are only present/needed if "topLevel" is `true`, see
54    /// [`top_level_pos`](crate::blocks::BlockWrapper::top_level_pos)
55    #[error("missing block attr: {0}")]
56    MissingMandatoryAttr(&'static str),
57
58    /// A boolean attribute ("shadow", "topLevel") was not stored as json boolean or
59    /// an integer attribute ("x", "y") was not stored as json integer
60    #[error("block attr '{attr_name}' should have type '{expected_type}'")]
61    AttrType {
62        attr_name: &'static str,
63        expected_type: &'static str,
64    },
65
66    /// Some error occured that is directly related to the blocks [kind](crate::blocks::BlockKind),
67    /// e. g. its opcode
68    #[error("{0}")]
69    Kind(BlockKindError<'a>),
70}
71
72/// A [target](`crate::scopes::Target`)'s collection of [blocks](`crate::scopes::TargetBlocks`)
73/// is invalid.
74///
75/// Typically, this means that at least one of the blocks was invalid.
76/// Only very invalid files wouldn't have an object as the value of the blocks.
77#[derive(Debug, thiserror::Error)]
78pub enum TargetBlocksError<'a> {
79    /// Is unlikely as files triggering this are so wrong that they shouldn't be considered normal.
80    #[error("expected object {{...}} for blocks of target")]
81    ExpectedObject,
82    /// One of the stored blocks failed to parse
83    #[error("at least one target block (id={id:?}) has unknown structure (block-error={error})")]
84    AtLeastOneInvalid {
85        id: Id,
86        error: JsonCtx<'a, BlockError<'a>>,
87    },
88}
89
90/// Failure while parsing a [`Target`](crate::scopes::Target).
91#[derive(Debug, thiserror::Error, derive_more::From)]
92pub enum TargetError<'a> {
93    /// Every target should say if it is _the_ stage or _a_ sprite.
94    #[error("the attribute \"isStage\" of the target is missing")]
95    MissingIsStage,
96    /// Every target should have a name.
97    #[error("the attribute \"name\" of the target is missing")]
98    MissingName,
99
100    /// The saved variable values are provided in an invalid way
101    #[error("error in target variables: {0}")]
102    TargetVar(JsonCtx<'a, TargetDataError<VariableKind>>),
103    /// The saved variable values are provided in an invalid way
104    #[error("error in target lists: {0}")]
105    TargetLis(JsonCtx<'a, TargetDataError<ListKind>>),
106    /// The target blocks (the most important compononet) is invalid
107    #[error("error in target blocks: {0}")]
108    TargetBlocks(TargetBlocksError<'a>),
109}
110
111/// The provided json document doesn't represent a valid model.
112///
113/// Unless the document is very abnormal there should at least be
114/// an array of targets, so the variant [`ModelError::Target`]
115/// should occur more often.
116#[derive(Debug, thiserror::Error, derive_more::From)]
117pub enum ModelError<'a> {
118    /// One of the `target`-entries produced an error
119    #[error("target error: {0}")]
120    Target(JsonCtx<'a, TargetError<'a>>),
121    /// There was no json array (`[...]`) with targets
122    #[error("The document doesn't contain a \"targets\" array: {0}")]
123    #[from(skip)]
124    NoTargetsArray(&'a serde_json::Value),
125}
126
127/// _Not actively used_, combines errors from file reading and model parsing.
128#[derive(Debug, thiserror::Error, derive_more::From)]
129pub enum ParseError<'a> {
130    #[error("model: {0}")]
131    Model(ModelError<'a>),
132    #[error("document: {0}")]
133    Doc(DocError),
134}
135
136/// Failure while parsing (ZIP-) file and extracting json document.
137#[derive(Debug, thiserror::Error, derive_more::From)]
138pub enum DocError {
139    #[error("stream or file was used that doesn't contain a document")]
140    NoDocument,
141    #[error("io error: {0}")]
142    #[from(skip)]
143    Io(#[from] Box<dyn std::error::Error>),
144    #[error("invalid json document: {0}")]
145    #[from(skip)]
146    Json(#[from] serde_json::Error),
147    #[error("file {0:?} read error: {1}")]
148    #[from(skip)]
149    FileRead(PathBuf, std::io::Error),
150}
151
152#[cfg(feature = "sb3")]
153impl From<zip::result::ZipError> for DocError {
154    fn from(value: zip::result::ZipError) -> Self {
155        Self::Io(value.into())
156    }
157}