1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
//! Provides an error type used for higher level calls.

use core::fmt::{Display, Formatter};

/// The errors that a component call can return.
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum Error {
	/// The call was addressed to a component that does not exist, is not accessible, or is of the
	/// wrong type.
	///
	/// The underlying syscall error is encapsulated.
	BadComponent(oc_wasm_safe::error::Error),

	/// An unsupported screen resolution, viewport size, or screen coordinate was requested.
	BadCoordinate,

	/// An unsupported colour depth was requested.
	BadDepth,

	/// The filename passed to a function is structurally invalid to be a filename.
	BadFilename,

	/// The character to use to fill the screen lies outside the basic multilingual plane and
	/// therefore cannot be used for filling.
	BadFillCharacter,

	/// An index number is outside the range of slots for an inventory or tanks for a
	/// fluid-containing block.
	///
	/// In the case of item inventories, this is only returned if the block is in fact an
	/// inventory; [`NoInventory`](Self::NoInventory) is returned for non-inventories. In the case
	/// of fluid containers, this is also returned if the block is not a fluid container at all (it
	/// is treated as a fluid container with zero tanks), but not if the block is in an unloaded
	/// chunk.
	BadInventorySlot,

	/// An item is not usable for the requested purpose.
	///
	/// For example, an item that does not hold fluid cannot have its fluid level or capacity
	/// queried.
	BadItem,

	/// A set-colour request tried to set a colour by palette index, and the palette index was
	/// unacceptable. This could be because the palette index was outside the range of the palette
	/// for the current bit depth, or it could be because the current bit depth does not support a
	/// palette at all.
	BadPaletteIndex,

	/// The address specified when binding a GPU does not exist, is inaccessible, or is not a
	/// screen; or, a GPU operation which requires a screen was executed when the GPU is unbound or
	/// the screen to which it was bound has been disconnected.
	BadScreen,

	/// The robot cannot move because there is something in the way.
	Blocked(super::robot::BlockContent),

	/// The checksum passed when trying to make an EEPROM read-only does not match the checksum of
	/// the contents of the EEPROM.
	ChecksumMismatch,

	/// The data (e.g. to write to a file or EEPROM or send over a communication interface) is too
	/// large.
	DataTooLarge,

	/// The named file or directory does not exist.
	FileNotFound,

	/// The robot tried to move to a position that is too far away from support (and does not have
	/// a suitable hover upgrade to allow such movement), tried to move into an unloaded chunk, or
	/// the destination is otherwise unsuitable for a reason other than there being something in
	/// the way.
	ImpossibleMove,

	/// An attempt was made to move an item or fluid into an inventory slot or tank, but it could
	/// not be inserted because the slot or tank is full, or the existing contents are not
	/// compatible with the new addition.
	InventoryFull,

	/// A seek in a file would place the file pointer at a negative position.
	NegativeSeek,

	/// There is no item in an inventory slot when one is required.
	NoItem,

	/// There is no inventory in the specified location. This includes if the inventory is in an
	/// unloaded chunk.
	///
	/// This is also returned when attempting to access a fluid tank in an unloaded chunk, but not
	/// when attempting to access a fluid tank in a block that does not contain any tanks; in the
	/// latter case, [`BadInventorySlot`](Self::BadInventorySlot) is used instead.
	NoInventory,

	/// The computer does not have enough energy to perform the operation.
	NotEnoughEnergy,

	/// The storage (e.g. EEPROM or filesystem) cannot be written to because it is read-only.
	StorageReadOnly,

	/// There are too many open descriptors.
	TooManyDescriptors,

	/// There are too many listening network ports.
	TooManyOpenPorts,

	/// There are too many parts in a packet.
	TooManyParts,

	/// The operation is not supported for this component.
	///
	/// For example:
	/// * An attempt was made to set a filesystem’s label, but this particular filesystem does not
	///   support labels.
	/// * An attempt was made to access item stack information in an inventory, but detailed item
	///   information access is disabled in the configuration file.
	Unsupported,

	/// The operation failed but no more detailed information is available at the type level. The
	/// individual method may document more specific reasons for this error to appear.
	Failed,
}

impl Error {
	/// Returns a string describing the error.
	#[must_use = "This function is only useful for its return value"]
	pub fn as_str(&self) -> &'static str {
		match self {
			Self::BadComponent(_) => "nonexistent or incorrect component",
			Self::BadCoordinate => "bad coordinate",
			Self::BadDepth => "bad depth",
			Self::BadFilename => "bad filename",
			Self::BadFillCharacter => "bad fill character",
			Self::BadInventorySlot => "bad inventory slot",
			Self::BadItem => "bad item",
			Self::BadPaletteIndex => "bad palette index",
			Self::BadScreen => "bad screen",
			Self::Blocked(super::robot::BlockContent::Entity) => "blocked by entity",
			Self::Blocked(super::robot::BlockContent::Air) => "blocked by air",
			Self::Blocked(super::robot::BlockContent::Liquid) => "blocked by liquid",
			Self::Blocked(super::robot::BlockContent::Replaceable) => "blocked by replaceable",
			Self::Blocked(super::robot::BlockContent::Passable) => "blocked by passable",
			Self::Blocked(super::robot::BlockContent::Solid) => "blocked by solid",
			Self::ChecksumMismatch => "checksum mismatch",
			Self::DataTooLarge => "data too large",
			Self::FileNotFound => "file not found",
			Self::ImpossibleMove => "impossible move",
			Self::InventoryFull => "inventory full",
			Self::NegativeSeek => "seek to negative position",
			Self::NoInventory => "no inventory",
			Self::NoItem => "no item",
			Self::NotEnoughEnergy => "not enough energy",
			Self::StorageReadOnly => "storage is read-only",
			Self::TooManyDescriptors => "too many descriptors",
			Self::TooManyOpenPorts => "too many open ports",
			Self::TooManyParts => "too many parts",
			Self::Unsupported => "unsupported",
			Self::Failed => "failed",
		}
	}
}

impl Display for Error {
	fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), core::fmt::Error> {
		match self {
			Self::BadComponent(e) => write!(f, "nonexistent or incorrect component: {e}"),
			_ => self.as_str().fmt(f),
		}
	}
}

#[cfg(feature = "std")]
impl std::error::Error for Error {
	fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
		match self {
			Self::BadComponent(underlying) => Some(underlying),
			Self::BadCoordinate
			| Self::BadDepth
			| Self::BadFilename
			| Self::BadFillCharacter
			| Self::BadInventorySlot
			| Self::BadItem
			| Self::BadPaletteIndex
			| Self::BadScreen
			| Self::Blocked(_)
			| Self::ChecksumMismatch
			| Self::DataTooLarge
			| Self::FileNotFound
			| Self::ImpossibleMove
			| Self::InventoryFull
			| Self::NegativeSeek
			| Self::NoInventory
			| Self::NoItem
			| Self::NotEnoughEnergy
			| Self::StorageReadOnly
			| Self::TooManyDescriptors
			| Self::TooManyOpenPorts
			| Self::TooManyParts
			| Self::Unsupported
			| Self::Failed => None,
		}
	}
}

impl From<oc_wasm_safe::error::Error> for Error {
	fn from(source: oc_wasm_safe::error::Error) -> Self {
		match source {
			oc_wasm_safe::error::Error::BadParameters | oc_wasm_safe::error::Error::Other => {
				Self::Failed
			}
			oc_wasm_safe::error::Error::TooManyDescriptors => Self::TooManyDescriptors,
			_ => Self::BadComponent(source),
		}
	}
}

impl From<oc_wasm_safe::component::MethodCallError<'_>> for Error {
	fn from(source: oc_wasm_safe::component::MethodCallError<'_>) -> Self {
		source.simplify().into()
	}
}