From cfef1c2de878ef45c8d48314ea273125219b031e Mon Sep 17 00:00:00 2001
From: Shane Madden <shanemadden@users.noreply.github.com>
Date: Wed, 6 Mar 2024 19:22:13 -0700
Subject: [PATCH 1/2] Fix unusable keys in game::structures/construction_sites
 (#510)

---
 CHANGELOG.md           |  4 +++-
 src/game.rs            | 17 ++++++++++-------
 src/local/object_id.rs | 13 +++++++++++--
 3 files changed, 24 insertions(+), 10 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 337c1712..a7364e26 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,8 @@ Unreleased
 - Remove features `generate-pixel` and `inter-shard-memory`, use the `mmo` feature instead
 - Place `game::cpu::{shard_limits, unlocked, unlocked_time, set_shard_limits, unlock}` functions
   behind the `mmo` feature
+- Change return type of `game::{construction_sites, structures}` to `JsHashMap<ObjectId<_>, _>`
+  instead of `JsHashMap<RawObjectId, _>`
 
 ### Additions:
 
@@ -33,7 +35,7 @@ Unreleased
 
 ### Bugfixes:
 
-- Implement `JsCollectionFromValue` for `Direction`
+- Implement `JsCollectionFromValue` for `Direction`, `ObjectId<_>`
 - Implement `Debug` for `RouteStep`
 
 ### Misc:
diff --git a/src/game.rs b/src/game.rs
index a6e97d52..2b797ba1 100644
--- a/src/game.rs
+++ b/src/game.rs
@@ -18,7 +18,10 @@ use crate::{
     enums::StructureObject,
     js_collections::{JsHashMap, JsObjectId},
     local::{ObjectId, RawObjectId, RoomName},
-    objects::{AccountPowerCreep, ConstructionSite, Creep, Flag, Room, RoomObject, StructureSpawn},
+    objects::{
+        AccountPowerCreep, ConstructionSite, Creep, Flag, Room, RoomObject, Structure,
+        StructureSpawn,
+    },
     traits::MaybeHasId,
 };
 
@@ -75,11 +78,11 @@ extern "C" {
     fn notify(message: &JsString, group_interval: Option<u32>);
 }
 
-/// Get a [`JsHashMap<RawObjectId, ConstructionSite>`] with all of your
-/// construction sites.
+/// Get a [`JsHashMap<ObjectId<ConstructionSite>, ConstructionSite>`] with all
+/// of your construction sites.
 ///
 /// [Screeps documentation](https://docs.screeps.com/api/#Game.constructionSites)
-pub fn construction_sites() -> JsHashMap<RawObjectId, ConstructionSite> {
+pub fn construction_sites() -> JsHashMap<ObjectId<ConstructionSite>, ConstructionSite> {
     Game::construction_sites().into()
 }
 
@@ -169,11 +172,11 @@ pub fn spawns_jsstring() -> JsHashMap<JsString, StructureSpawn> {
     Game::spawns().into()
 }
 
-/// Get a [`JsHashMap<RawObjectId, StructureObject>`] with all of your owned
-/// structures.
+/// Get a [`JsHashMap<ObjectId<Structure>, StructureObject>`] with all of your
+/// owned structures.
 ///
 /// [Screeps documentation](https://docs.screeps.com/api/#Game.spawns)
-pub fn structures() -> JsHashMap<RawObjectId, StructureObject> {
+pub fn structures() -> JsHashMap<ObjectId<Structure>, StructureObject> {
     Game::structures().into()
 }
 
diff --git a/src/local/object_id.rs b/src/local/object_id.rs
index 157bfe75..ffc851df 100644
--- a/src/local/object_id.rs
+++ b/src/local/object_id.rs
@@ -7,10 +7,11 @@ use std::{
 };
 
 use arrayvec::ArrayString;
+use js_sys::JsString;
 use serde::{Deserialize, Serialize};
-use wasm_bindgen::JsCast;
+use wasm_bindgen::{JsCast, JsValue};
 
-use crate::{game, objects::RoomObject, traits::MaybeHasId};
+use crate::{game, js_collections::JsCollectionFromValue, objects::RoomObject, traits::MaybeHasId};
 
 mod errors;
 mod raw;
@@ -262,3 +263,11 @@ impl<T> From<u128> for ObjectId<T> {
         Self::from_packed(packed)
     }
 }
+
+impl<T> JsCollectionFromValue for ObjectId<T> {
+    fn from_value(val: JsValue) -> Self {
+        let val: JsString = val.unchecked_into();
+        let val: String = val.into();
+        val.parse().expect("valid id string")
+    }
+}

From 2b49b646735b31be3262553552031c79679731cb Mon Sep 17 00:00:00 2001
From: scottbot95 <scott.techau@gmail.com>
Date: Wed, 13 Mar 2024 18:27:05 -0700
Subject: [PATCH 2/2] Add `JsHashMap::entries` method (#511)

---
 CHANGELOG.md          |  1 +
 src/js_collections.rs | 25 +++++++++++++++++++++++++
 2 files changed, 26 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index a7364e26..5dc79580 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -32,6 +32,7 @@ Unreleased
       which returns a `Result<&'static [ResourceType], StoreObjectConversionError>`
 - Add missing `StoreObject::Reactor` to the `seasonal-season-5` feature
 - Implement `Serialize` and `Deserialize` for `RoomStatus`
+- Add function `JsHashMap::entries`
 
 ### Bugfixes:
 
diff --git a/src/js_collections.rs b/src/js_collections.rs
index 8be65c58..c1d5ecab 100644
--- a/src/js_collections.rs
+++ b/src/js_collections.rs
@@ -59,6 +59,18 @@ where
     }
 }
 
+impl<K, V> JsHashMap<K, V>
+where
+    K: JsCollectionFromValue,
+    V: JsCollectionFromValue,
+{
+    pub fn entries(&self) -> impl Iterator<Item = (K, V)> {
+        let array = Object::entries(self.map.unchecked_ref());
+
+        OwnedArrayIter::new(array)
+    }
+}
+
 impl<K, V> JsHashMap<K, V>
 where
     K: JsCollectionIntoValue,
@@ -307,3 +319,16 @@ impl JsCollectionFromValue for u8 {
         }
     }
 }
+
+impl<K, V> JsCollectionFromValue for (K, V)
+where
+    K: JsCollectionFromValue,
+    V: JsCollectionFromValue,
+{
+    fn from_value(val: JsValue) -> Self {
+        let val: &Array = val.dyn_ref().expect("expected tuple of length 2");
+        let k = K::from_value(val.get(0));
+        let v = V::from_value(val.get(1));
+        (k, v)
+    }
+}