Skip to content

Commit 9ab493b

Browse files
authored
Merge pull request #22 from ASalvail/lodashMem
Switch to lodash for memory lookup
2 parents 8c9e1b2 + 6bfab5d commit 9ab493b

File tree

1 file changed

+128
-49
lines changed

1 file changed

+128
-49
lines changed

screeps-game-api/src/memory.rs

Lines changed: 128 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,62 @@
1-
use std::fmt;
1+
//! Interface with Screeps' `Memory` global variable
2+
//!
3+
//! Screeps' memory lives in the javascript `Memory` global variable and is
4+
//! encoded as a javascript object. This object's reference is tracked within
5+
//! rust as a `MemoryReference`. The [`root`] function gives access to a
6+
//! reference to the `Memory` global object.
7+
//!
8+
//! # Typing
9+
//! Contrary to accessing the memory in javascript, rust's strong type system,
10+
//! requires that read values be assigned a type. To facilitate this, the
11+
//! `MemoryReference` provides methods to read a part of the memory as a
12+
//! certain type. If the value read cannot be transformed to the requested
13+
//! type, the method return `None`.
14+
//!
15+
//! # Accessing the memory
16+
//! Memory can be accessed in two ways:
17+
//! - via _keys_
18+
//! - via _paths_ (methods prefixed with `path_`)
19+
//!
20+
//! In both cases, if the value requested is `undefined`, `null`, or even just
21+
//! of the wrong type, the method returns `None`.
22+
//!
23+
//! ## Accessing memory with a _key_
24+
//! Since a `MemoryReference` represents a javascript object, its children can
25+
//! be accessed using the `object["key"]` javascript syntax using type methods.
26+
//! ```no_run
27+
//! let mem = screeps::memory::root();
28+
//! let cpu_used_last_tick = mem.int("cpu_used_last_tick").unwrap();
29+
//! ```
30+
//!
31+
//! ## Accessing memory with a _path_
32+
//! A quality of life improvement upon the key access is through full path. In
33+
//! javascript, it is possible to query a value with a full path:
34+
//! ```javascript
35+
//! var creep_time = Memory.creeps.John.time;
36+
//! ```
37+
//!
38+
//! To emulate this behavior in rust, you can write such a path to a string and
39+
//! it will fetch the javascript object using
40+
//! [lodash](https://lodash.com/docs/4.17.10#get) and convert the result
41+
//! depending on the method used. For example,
42+
//! ```no_run
43+
//! let mem = screeps::memory::root();
44+
//! let creep_time = mem.path_num("creeps.John.time").unwrap();
45+
//! ```
46+
//!
47+
//! # Other methods that provide `MemoryReference`s
48+
//! In addition to accessing the memory from the root, it is possible to
49+
//! access the memory via creeps, spawns, rooms and flags. Accessing the memory
50+
//! from those objects will also result in a `MemoryReference` which instead
51+
//! points at the root of this object's memory.
52+
//!
253
54+
use std::fmt;
355
use stdweb::unstable::{TryFrom, TryInto};
456
use stdweb::{Array, JsSerialize, Reference, Value};
557

58+
use ConversionError;
59+
660
#[derive(Clone, Debug)]
761
pub struct UnexpectedTypeError;
862

@@ -13,7 +67,8 @@ impl fmt::Display for UnexpectedTypeError {
1367
}
1468
}
1569

16-
/// TODO: do we even need this over just a raw 'Reference'?
70+
// TODO: do we even need this over just a raw 'Reference'?
71+
/// A [`Reference`] to a screeps memory object
1772
pub struct MemoryReference(Reference);
1873

1974
impl AsRef<Reference> for MemoryReference {
@@ -42,46 +97,60 @@ impl MemoryReference {
4297
MemoryReference(reference)
4398
}
4499

45-
pub fn bool(&self, path: &str) -> bool {
46-
js_unwrap!(Boolean(@{self.as_ref()}[@{path}]))
100+
pub fn bool(&self, key: &str) -> bool {
101+
js_unwrap!(Boolean(@{self.as_ref()}[@{key}]))
47102
}
48103

49-
pub fn num(&self, path: &str) -> Option<f64> {
104+
pub fn path_bool(&self, path: &str) -> bool {
105+
js_unwrap!(Boolean(_.get(@{self.as_ref()}, @{path})))
106+
}
107+
108+
pub fn f64(&self, key: &str) -> Result<Option<f64>, ConversionError> {
50109
(js! {
51-
return (@{self.as_ref()})[@{path}];
110+
return (@{self.as_ref()})[@{key}];
52111
}).try_into()
53-
.map(Some)
54-
.unwrap_or_default()
55112
}
56113

57-
pub fn int(&self, path: &str) -> Option<i32> {
114+
pub fn path_f64(&self, path: &str) -> Result<Option<f64>, ConversionError> {
58115
(js! {
59-
return (@{self.as_ref()})[@{path}];
116+
return _.get(@{self.as_ref()}, @{path});
60117
}).try_into()
61-
.map(Some)
62-
.unwrap_or_default()
63118
}
64119

65-
pub fn string(&self, path: &str) -> Option<String> {
120+
pub fn i32(&self, key: &str) -> Result<Option<i32>, ConversionError> {
66121
(js! {
67-
return (@{self.as_ref()})[@{path}];
122+
return (@{self.as_ref()})[@{key}];
68123
}).try_into()
69-
.map(Some)
70-
.unwrap_or_default()
71124
}
72125

73-
pub fn dict(&self, path: &str) -> Option<MemoryReference> {
126+
pub fn path_i32(&self, path: &str) -> Result<Option<i32>, ConversionError> {
74127
(js! {
75-
var v = (@{self.as_ref()})[@{path}];
76-
if (_.isArray(v)) {
77-
return null;
78-
} else {
79-
return v || null;
80-
}
128+
return _.get(@{self.as_ref()}, @{path});
129+
}).try_into()
130+
}
131+
132+
pub fn string(&self, key: &str) -> Result<Option<String>, ConversionError> {
133+
(js! {
134+
return (@{self.as_ref()})[@{key}];
135+
}).try_into()
136+
}
137+
138+
pub fn path_string(&self, path: &str) -> Result<Option<String>, ConversionError> {
139+
(js! {
140+
return _.get(@{self.as_ref()}, @{path});
141+
}).try_into()
142+
}
143+
144+
pub fn dict(&self, key: &str) -> Result<Option<MemoryReference>, ConversionError> {
145+
(js! {
146+
return (@{self.as_ref()})[@{key}];
147+
}).try_into()
148+
}
149+
150+
pub fn path_dict(&self, path: &str) -> Result<Option<MemoryReference>, ConversionError> {
151+
(js! {
152+
return _.get(@{self.as_ref()}, @{path});
81153
}).try_into()
82-
.map(Some)
83-
.unwrap_or_default()
84-
.map(MemoryReference)
85154
}
86155

87156
/// Get a dictionary value or create it if it does not exist.
@@ -110,48 +179,57 @@ impl MemoryReference {
110179
js_unwrap!(Object.keys(@{self.as_ref()}))
111180
}
112181

113-
pub fn del(&self, path: &str) {
182+
pub fn del(&self, key: &str) {
183+
js! {
184+
(@{self.as_ref()})[@{key}] = undefined;
185+
}
186+
}
187+
188+
pub fn path_del(&self, path: &str) {
114189
js! {
115-
(@{self.as_ref()})[@{path}] = undefined;
190+
_.set(@{self.as_ref()}, @{path}, undefined);
116191
}
117192
}
118193

119-
pub fn set<T>(&self, path: &str, value: T)
194+
pub fn set<T>(&self, key: &str, value: T)
120195
where
121196
T: JsSerialize,
122197
{
123198
js! {
124-
(@{self.as_ref()})[@{path}] = @{value};
199+
(@{self.as_ref()})[@{key}] = @{value};
125200
}
126201
}
127202

128-
pub fn arr<T>(&self, path: &str) -> Option<Vec<T>>
203+
pub fn path_set<T>(&self, path: &str, value: T)
129204
where
130-
T: TryFrom<Value, Error = <Reference as TryFrom<Value>>::Error>,
205+
T: JsSerialize,
131206
{
132-
let x: Reference = (js! {
133-
var v = (@{self.as_ref()})[@{path}];
134-
if (!_.isArray(v)) {
135-
return null;
136-
} else {
137-
return v || null;
138-
}
139-
}).try_into()
140-
.ok()?;
207+
js! {
208+
_.set(@{self.as_ref()}, @{path}, @{value});
209+
}
210+
}
141211

142-
// Memory arrays don't have the regular Array as their prototype - they
143-
// have the 'outside' type.
144-
let as_arr: Array = unsafe {
145-
use stdweb::ReferenceType;
146-
Array::from_reference_unchecked(x)
147-
};
212+
pub fn arr<T>(&self, key: &str) -> Result<Option<Vec<T>>, ConversionError>
213+
where
214+
T: TryFrom<Value, Error = ConversionError>,
215+
{
216+
(js! {
217+
return (@{self.as_ref()})[@{key}];
218+
}).try_into()
219+
}
148220

149-
as_arr.try_into().ok()
221+
pub fn path_arr<T>(&self, path: &str) -> Result<Option<Vec<T>>, ConversionError>
222+
where
223+
T: TryFrom<Value, Error = ConversionError>,
224+
{
225+
(js! {
226+
return _.get(@{self.as_ref()}, @{path});
227+
}).try_into()
150228
}
151229
}
152230

153231
impl TryFrom<Value> for MemoryReference {
154-
type Error = <Reference as TryFrom<Value>>::Error;
232+
type Error = ConversionError;
155233

156234
fn try_from(v: Value) -> Result<Self, Self::Error> {
157235
let r: Reference = v.try_into()?; // fail early.
@@ -168,6 +246,7 @@ impl TryFrom<Value> for MemoryReference {
168246
}
169247
}
170248

249+
/// Get a reference to the `Memory` global object
171250
pub fn root() -> MemoryReference {
172251
js_unwrap!(Memory)
173252
}

0 commit comments

Comments
 (0)