1use nms_core::player::PlayerBase;
4use nms_core::system::System;
5use nms_graph::spatial::SystemId;
6use nms_graph::{GalaxyModel, GraphError};
7
8#[derive(Debug, Clone)]
10pub enum ShowQuery {
11 System(String),
13 Base(String),
15}
16
17#[derive(Debug, Clone)]
19pub enum ShowResult {
20 System(ShowSystemResult),
21 Base(ShowBaseResult),
22}
23
24#[derive(Debug, Clone)]
25pub struct ShowSystemResult {
26 pub system: System,
27 pub portal_hex: String,
28 pub galaxy_name: String,
29 pub distance_from_player: Option<f64>,
30}
31
32#[derive(Debug, Clone)]
33pub struct ShowBaseResult {
34 pub base: PlayerBase,
35 pub portal_hex: String,
36 pub galaxy_name: String,
37 pub system: Option<System>,
38 pub distance_from_player: Option<f64>,
39}
40
41pub fn execute_show(model: &GalaxyModel, query: &ShowQuery) -> Result<ShowResult, GraphError> {
43 match query {
44 ShowQuery::System(name_or_id) => show_system(model, name_or_id),
45 ShowQuery::Base(name) => show_base(model, name),
46 }
47}
48
49fn show_system(model: &GalaxyModel, name_or_id: &str) -> Result<ShowResult, GraphError> {
50 let system = if let Some((_id, sys)) = model.system_by_name(name_or_id) {
52 sys
53 } else {
54 let hex = name_or_id
55 .strip_prefix("0x")
56 .or_else(|| name_or_id.strip_prefix("0X"))
57 .unwrap_or(name_or_id);
58 let packed = u64::from_str_radix(hex, 16)
59 .map_err(|_| GraphError::SystemNotFound(name_or_id.to_string()))?;
60 let id = SystemId(packed & 0x0FFF_FFFF_FFFF);
61 model
62 .system(&id)
63 .ok_or_else(|| GraphError::SystemNotFound(name_or_id.to_string()))?
64 };
65
66 let portal_hex = format!("{:012X}", system.address.packed());
67 let galaxy = nms_core::galaxy::Galaxy::by_index(system.address.reality_index);
68
69 let distance_from_player = model
70 .player_position()
71 .map(|pos| pos.distance_ly(&system.address));
72
73 Ok(ShowResult::System(ShowSystemResult {
74 system: system.clone(),
75 portal_hex,
76 galaxy_name: galaxy.name.to_string(),
77 distance_from_player,
78 }))
79}
80
81fn show_base(model: &GalaxyModel, name: &str) -> Result<ShowResult, GraphError> {
82 let base = model
83 .base(name)
84 .ok_or_else(|| GraphError::BaseNotFound(name.to_string()))?;
85
86 let portal_hex = format!("{:012X}", base.address.packed());
87 let galaxy = nms_core::galaxy::Galaxy::by_index(base.address.reality_index);
88
89 let sys_id = SystemId::from_address(&base.address);
91 let system = model.system(&sys_id).cloned();
92
93 let distance_from_player = model
94 .player_position()
95 .map(|pos| pos.distance_ly(&base.address));
96
97 Ok(ShowResult::Base(ShowBaseResult {
98 base: base.clone(),
99 portal_hex,
100 galaxy_name: galaxy.name.to_string(),
101 system,
102 distance_from_player,
103 }))
104}
105
106#[cfg(test)]
107mod tests {
108 use super::*;
109
110 fn test_model() -> GalaxyModel {
111 let json = r#"{
112 "Version": 4720, "Platform": "Mac|Final", "ActiveContext": "Main",
113 "CommonStateData": {"SaveName": "Test", "TotalPlayTime": 100},
114 "BaseContext": {"GameMode": 1, "PlayerStateData": {"UniverseAddress": {"RealityIndex": 0, "GalacticAddress": {"VoxelX": 0, "VoxelY": 0, "VoxelZ": 0, "SolarSystemIndex": 1, "PlanetIndex": 0}}, "Units": 0, "Nanites": 0, "Specials": 0, "PersistentPlayerBases": [{"BaseVersion": 8, "GalacticAddress": "0x001000000064", "Position": [0.0,0.0,0.0], "Forward": [1.0,0.0,0.0], "LastUpdateTimestamp": 0, "Objects": [], "RID": "", "Owner": {"LID":"","UID":"1","USN":"","PTK":"ST","TS":0}, "Name": "Alpha Base", "BaseType": {"PersistentBaseTypes": "HomePlanetBase"}, "LastEditedById": "", "LastEditedByUsername": ""}]}},
115 "ExpeditionContext": {"GameMode": 6, "PlayerStateData": {"UniverseAddress": {"RealityIndex": 0, "GalacticAddress": {"VoxelX": 0, "VoxelY": 0, "VoxelZ": 0, "SolarSystemIndex": 0, "PlanetIndex": 0}}, "Units": 0, "Nanites": 0, "Specials": 0, "PersistentPlayerBases": []}},
116 "DiscoveryManagerData": {"DiscoveryData-v1": {"ReserveStore": 0, "ReserveManaged": 0, "Store": {"Record": [
117 {"DD": {"UA": "0x001000000064", "DT": "SolarSystem", "VP": []}, "DM": {}, "OWS": {"LID": "", "UID": "1", "USN": "Explorer", "PTK": "ST", "TS": 1700000000}, "FL": {"U": 1}}
118 ]}}}
119 }"#;
120 nms_save::parse_save(json.as_bytes())
121 .map(|save| GalaxyModel::from_save(&save))
122 .unwrap()
123 }
124
125 #[test]
126 fn test_show_base_by_name() {
127 let model = test_model();
128 let result = execute_show(&model, &ShowQuery::Base("Alpha Base".into())).unwrap();
129 match result {
130 ShowResult::Base(b) => {
131 assert_eq!(b.base.name, "Alpha Base");
132 assert_eq!(b.galaxy_name, "Euclid");
133 assert_eq!(b.portal_hex.len(), 12);
134 }
135 _ => panic!("Expected Base result"),
136 }
137 }
138
139 #[test]
140 fn test_show_base_case_insensitive() {
141 let model = test_model();
142 assert!(execute_show(&model, &ShowQuery::Base("alpha base".into())).is_ok());
143 }
144
145 #[test]
146 fn test_show_base_not_found() {
147 let model = test_model();
148 assert!(execute_show(&model, &ShowQuery::Base("No Base".into())).is_err());
149 }
150
151 #[test]
152 fn test_show_system_not_found() {
153 let model = test_model();
154 assert!(execute_show(&model, &ShowQuery::System("No System".into())).is_err());
155 }
156}