bevy_mod_scripting_bindings/path/
mod.rs1use std::{borrow::Cow, fmt::Display};
4
5use bevy_mod_scripting_asset::Language;
6use bevy_mod_scripting_derive::DebugWithTypeInfo;
7use bevy_mod_scripting_display::{DisplayWithTypeInfo, GetTypeInfo};
8use bevy_reflect::{PartialReflect, ReflectMut, ReflectRef, TypeInfo, TypeRegistry};
9
10use crate::{ScriptValue, WorldGuard, convert};
11
12#[derive(DebugWithTypeInfo)]
14#[debug_with_type_info(bms_display_path = "bevy_mod_scripting_display")]
15pub enum ReferencePart {
16 StringAccess(Cow<'static, str>),
18 IntegerAccess(i64, bool),
20 MapAccess(Box<dyn PartialReflect>),
22}
23
24impl Clone for ReferencePart {
25 fn clone(&self) -> Self {
26 match self {
27 Self::StringAccess(arg0) => Self::StringAccess(arg0.clone()),
28 Self::IntegerAccess(arg0, arg1) => Self::IntegerAccess(*arg0, *arg1),
29 Self::MapAccess(arg0) => Self::MapAccess(match arg0.reflect_clone() {
30 Ok(c) => c,
31 Err(_) => arg0.to_dynamic(), }),
33 }
34 }
35}
36
37impl PartialEq for ReferencePart {
38 fn eq(&self, other: &Self) -> bool {
39 match (self, other) {
40 (Self::StringAccess(l0), Self::StringAccess(r0)) => l0 == r0,
41 (Self::IntegerAccess(l0, l1), Self::IntegerAccess(r0, r1)) => l0 == r0 && l1 == r1,
42 (Self::MapAccess(l0), Self::MapAccess(r0)) => {
43 l0.reflect_partial_eq(r0.as_ref()).unwrap_or(false)
44 }
45 _ => false,
46 }
47 }
48}
49
50impl Display for ReferencePart {
51 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
52 match self {
53 ReferencePart::StringAccess(cow) => {
54 f.write_str("[\"")?;
55 f.write_str(cow)?;
56 f.write_str("\"]")
57 }
58 ReferencePart::IntegerAccess(int, correction) => {
59 f.write_str("[")?;
60 f.write_str(&if *correction { *int - 1 } else { *int }.to_string())?;
61 f.write_str("]")
62 }
63 ReferencePart::MapAccess(partial_reflect) => {
64 f.write_str("[")?;
65 partial_reflect.debug(f)?;
66 f.write_str("]")
67 }
68 }
69 }
70}
71
72#[allow(clippy::result_unit_err, reason = "it works better")]
73impl ReferencePart {
74 pub fn expect_string(&self) -> Result<&str, ()> {
76 match self {
77 ReferencePart::StringAccess(cow) => Ok(cow.as_ref()),
78 _ => Err(()),
79 }
80 }
81
82 pub fn expect_integer(&self, correct_indexing: bool) -> Result<i64, ()> {
84 match self {
85 ReferencePart::IntegerAccess(index, correction) => {
86 Ok(if *correction && correct_indexing {
87 *index - 1
88 } else {
89 *index
90 })
91 }
92 _ => Err(()),
93 }
94 }
95
96 pub fn with_any<O, F: FnOnce(&dyn PartialReflect) -> O>(
98 &self,
99 correct_indexing: bool,
100 f: F,
101 ) -> O {
102 match self {
103 ReferencePart::StringAccess(cow) => f(cow),
104 ReferencePart::IntegerAccess(index, correction) => {
105 f(&(if *correction && correct_indexing {
106 *index - 1
107 } else {
108 *index
109 }))
110 }
111 ReferencePart::MapAccess(partial_reflect) => f(partial_reflect.as_ref()),
112 }
113 }
114
115 pub fn new_from_script_val(
119 value: ScriptValue,
120 language: Language,
121 world: Option<WorldGuard>,
122 ) -> Result<Self, ScriptValue> {
123 Ok(match value {
124 ScriptValue::Integer(v) => Self::IntegerAccess(v, language.one_indexed()),
125 ScriptValue::Float(v) => Self::IntegerAccess(
126 v.max(i64::MIN as f64).min(i64::MAX as f64).round() as i64,
127 language.one_indexed(),
128 ),
129 ScriptValue::String(cow) => Self::StringAccess(cow),
130 ScriptValue::Reference(_ref) => world
131 .and_then(|world| Some(Self::MapAccess(_ref.to_owned_value(world).ok()?)))
132 .ok_or_else(|| ScriptValue::Reference(_ref))?,
133 _ => return Err(value),
134 })
135 }
136
137 pub fn reflect_element<'a>(
139 &self,
140 elem: &'a dyn PartialReflect,
141 _type_registry: &TypeRegistry,
142 one_indexed: bool,
143 ) -> Result<Option<&'a dyn PartialReflect>, ()> {
144 Ok(match elem.reflect_ref() {
145 ReflectRef::Struct(x) => x.field(self.expect_string()?),
146 ReflectRef::TupleStruct(x) => x.field(self.expect_integer(one_indexed)? as usize),
147 ReflectRef::Tuple(x) => x.field(self.expect_integer(one_indexed)? as usize),
148 ReflectRef::List(x) => x.get(self.expect_integer(one_indexed)? as usize),
149 ReflectRef::Array(x) => x.get(self.expect_integer(one_indexed)? as usize),
150 ReflectRef::Enum(x) => match x.variant_type() {
151 bevy_reflect::VariantType::Struct => x.field(self.expect_string()?),
152 bevy_reflect::VariantType::Tuple => {
153 x.field_at(self.expect_integer(one_indexed)? as usize)
154 }
155 bevy_reflect::VariantType::Unit => return Err(()),
156 },
157 ReflectRef::Map(x) => {
158 let id = x.get_represented_map_info().ok_or(())?.key_ty().id();
159 self.with_any(one_indexed, |key| {
160 let coerced = convert(key, id).ok_or(())?;
161 Ok(x.get(coerced.as_ref()))
162 })?
163 }
164 ReflectRef::Set(x) => {
165 let id = match x.get_represented_type_info().ok_or(())? {
166 TypeInfo::Set(set_info) => set_info.value_ty().id(),
167 _ => unreachable!("impossible"),
168 };
169 self.with_any(one_indexed, |key| {
170 let coerced = convert(key, id).ok_or(())?;
171 Ok(x.get(coerced.as_ref()))
172 })?
173 }
174 _ => return Err(()),
175 })
176 }
177
178 pub fn reflect_element_mut<'a>(
180 &self,
181 elem: &'a mut dyn PartialReflect,
182 _type_registry: &TypeRegistry,
183 one_indexed: bool,
184 ) -> Result<Option<&'a mut dyn PartialReflect>, ()> {
185 Ok(match elem.reflect_mut() {
186 ReflectMut::Struct(x) => x.field_mut(self.expect_string()?),
187 ReflectMut::TupleStruct(x) => x.field_mut(self.expect_integer(one_indexed)? as usize),
188 ReflectMut::Tuple(x) => x.field_mut(self.expect_integer(one_indexed)? as usize),
189 ReflectMut::List(x) => x.get_mut(self.expect_integer(one_indexed)? as usize),
190 ReflectMut::Array(x) => x.get_mut(self.expect_integer(one_indexed)? as usize),
191 ReflectMut::Enum(x) => match x.variant_type() {
192 bevy_reflect::VariantType::Struct => x.field_mut(self.expect_string()?),
193 bevy_reflect::VariantType::Tuple => {
194 x.field_at_mut(self.expect_integer(one_indexed)? as usize)
195 }
196 bevy_reflect::VariantType::Unit => return Err(()),
197 },
198 ReflectMut::Map(x) => {
199 let id = x.get_represented_map_info().ok_or(())?.key_ty().id();
200 self.with_any(one_indexed, |key| {
201 let coerced = convert(key, id).ok_or(())?;
202 Ok(x.get_mut(coerced.as_ref()))
203 })?
204 }
205 _ => return Err(()),
207 })
208 }
209}
210
211#[derive(DebugWithTypeInfo, Clone, PartialEq, Default)]
213#[debug_with_type_info(bms_display_path = "bevy_mod_scripting_display")]
214pub struct ReferencePath {
215 one_indexed: bool,
216 path: Vec<ReferencePart>,
217}
218
219impl ReferencePath {
220 pub fn set_is_one_indexed(&mut self, is_one_indexed: bool) {
222 self.one_indexed = is_one_indexed;
223 }
224
225 pub fn reflect_element<'a>(
227 &self,
228 val: &'a dyn PartialReflect,
229 type_registry: &TypeRegistry,
230 ) -> Result<Option<&'a dyn PartialReflect>, ReferencePathError> {
231 let mut next: &'a dyn PartialReflect = val;
232 for i in &self.path {
233 next = match i.reflect_element(next, type_registry, self.one_indexed) {
234 Ok(None) => return Ok(None),
235 Ok(Some(v)) => v,
236 Err(_) => {
237 return Err(ReferencePathError {
238 val: next.get_represented_type_info(),
239 part: i.clone(),
240 });
241 }
242 };
243 }
244 Ok(Some(next))
245 }
246
247 pub fn reflect_element_mut<'a>(
249 &self,
250 val: &'a mut dyn PartialReflect,
251 type_registry: &TypeRegistry,
252 ) -> Result<Option<&'a mut dyn PartialReflect>, ReferencePathError> {
253 let mut next: &'a mut dyn PartialReflect = val;
254 for i in &self.path {
255 let type_info_current = next.get_represented_type_info();
256 next = match i.reflect_element_mut(next, type_registry, self.one_indexed) {
257 Ok(None) => return Ok(None),
258 Ok(Some(v)) => v,
259 Err(_) => {
260 return Err(ReferencePathError {
261 val: type_info_current,
262 part: i.clone(),
263 });
264 }
265 };
266 }
267 Ok(Some(next))
268 }
269
270 pub fn is_empty(&self) -> bool {
272 self.path.is_empty()
273 }
274
275 pub fn push(&mut self, part: ReferencePart) {
277 self.path.push(part)
278 }
279
280 pub fn extend(&mut self, part: impl Iterator<Item = ReferencePart>) {
282 self.path.extend(part)
283 }
284}
285
286impl Display for ReferencePath {
287 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
288 for i in &self.path {
289 std::fmt::Display::fmt(i, f)?
290 }
291 Ok(())
292 }
293}
294
295impl DisplayWithTypeInfo for ReferencePath {
296 fn display_with_type_info(
297 &self,
298 f: &mut std::fmt::Formatter<'_>,
299 _type_info_provider: Option<&dyn GetTypeInfo>,
300 ) -> std::fmt::Result {
301 std::fmt::Display::fmt(self, f)
302 }
303}
304
305pub struct ReferencePathError {
307 val: Option<&'static TypeInfo>,
308 part: ReferencePart,
309}
310
311impl Display for ReferencePathError {
312 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
313 f.write_str("Cannot reflect into ")?;
314 f.write_str(match self.val {
315 Some(TypeInfo::Struct(_)) => "struct",
316 Some(TypeInfo::TupleStruct(_)) => "tuple truct",
317 Some(TypeInfo::Tuple(_)) => "tuple",
318 Some(TypeInfo::List(_)) => "list",
319 Some(TypeInfo::Array(_)) => "array",
320 Some(TypeInfo::Map(_)) => "map",
321 Some(TypeInfo::Set(_)) => "set",
322 Some(TypeInfo::Enum(_)) => "enum",
323 Some(TypeInfo::Opaque(_)) => "opaque type",
324 None => "unknown type",
325 })?;
326
327 f.write_str(" of type: ")?;
328 f.write_str(
329 self.val
330 .map(|t| t.type_path_table().path())
331 .unwrap_or("unknown type"),
332 )?;
333
334 f.write_str(" with ")?;
335
336 f.write_str(match &self.part {
337 ReferencePart::StringAccess(_) => "string key",
338 ReferencePart::IntegerAccess(_, _) => "integer key",
339 ReferencePart::MapAccess(_) => "map key",
340 })?;
341
342 f.write_str(": `")?;
343 std::fmt::Display::fmt(&self.part, f)?;
344 f.write_str("`")?;
345
346 Ok(())
347 }
348}