#![allow(clippy::module_name_repetitions, clippy::enum_glob_use)] use std::{ collections::BTreeMap, io::{BufWriter, Write}, path::PathBuf, }; use quote::quote; use serde_derive::{Deserialize, Serialize}; use crate::{ enums, stationpedia::{self, Memory, Page, Stationpedia}, }; use stationeers_data::{ enums::MemoryAccess, templates::{ ConnectionInfo, ConsumerInfo, DeviceInfo, Instruction, InternalAtmoInfo, ItemCircuitHolderTemplate, ItemConsumerTemplate, ItemInfo, ItemLogicMemoryTemplate, ItemLogicTemplate, ItemSlotsTemplate, ItemSuitCircuitHolderTemplate, ItemSuitLogicTemplate, ItemSuitTemplate, ItemTemplate, LogicInfo, MemoryInfo, ObjectTemplate, PrefabInfo, SlotInfo, StructureCircuitHolderTemplate, StructureInfo, StructureLogicDeviceConsumerMemoryTemplate, StructureLogicDeviceConsumerTemplate, StructureLogicDeviceMemoryTemplate, StructureLogicDeviceTemplate, StructureLogicTemplate, StructureSlotsTemplate, StructureTemplate, SuitInfo, ThermalInfo, }, }; #[allow(clippy::too_many_lines)] pub fn generate_database( stationpedia: &stationpedia::Stationpedia, enums: &enums::Enums, workspace: &std::path::Path, ) -> color_eyre::Result> { let templates = generate_templates(stationpedia); eprintln!("Writing prefab database ..."); let prefabs: BTreeMap = templates .into_iter() .map(|obj| (obj.prefab().prefab_name.clone(), obj)) .collect(); let prefabs_by_hash: BTreeMap = prefabs .iter() .map(|(key, val)| (val.prefab().prefab_hash, key.clone())) .collect(); let structures = prefabs .iter() .filter_map(|(_, val)| { use ObjectTemplate::*; match val { Structure(_) | StructureSlots(_) | StructureLogic(_) | StructureLogicDevice(_) | StructureCircuitHolder(_) | StructureLogicDeviceConsumer(_) | StructureLogicDeviceMemory(_) | StructureLogicDeviceConsumerMemory(_) => Some(val.prefab().prefab_name.clone()), Item(_) | ItemSlots(_) | ItemConsumer(_) | ItemLogic(_) | ItemCircuitHolder(_) | ItemLogicMemory(_) | ItemSuit(_) | ItemSuitLogic(_) | ItemSuitCircuitHolder(_) => None, } }) .collect(); let items = prefabs .iter() .filter_map(|(_, val)| { use ObjectTemplate::*; match val { Structure(_) | StructureSlots(_) | StructureLogic(_) | StructureLogicDevice(_) | StructureCircuitHolder(_) | StructureLogicDeviceConsumer(_) | StructureLogicDeviceMemory(_) | StructureLogicDeviceConsumerMemory(_) => None, Item(_) | ItemSlots(_) | ItemConsumer(_) | ItemLogic(_) | ItemCircuitHolder(_) | ItemLogicMemory(_) | ItemSuit(_) | ItemSuitLogic(_) | ItemSuitCircuitHolder(_) => Some(val.prefab().prefab_name.clone()), } }) .collect(); let logicable_items = prefabs .iter() .filter_map(|(_, val)| { use ObjectTemplate::*; match val { Structure(_) | StructureSlots(_) | StructureLogic(_) | StructureLogicDevice(_) | StructureCircuitHolder(_) | StructureLogicDeviceConsumer(_) | StructureLogicDeviceMemory(_) | StructureLogicDeviceConsumerMemory(_) | Item(_) | ItemSlots(_) | ItemSuit(_) | ItemConsumer(_) => None, ItemLogic(_) | ItemCircuitHolder(_) | ItemLogicMemory(_) | ItemSuitLogic(_) | ItemSuitCircuitHolder(_) => Some(val.prefab().prefab_name.clone()), } }) .collect(); let devices = prefabs .iter() .filter_map(|(_, val)| { use ObjectTemplate::*; match val { Structure(_) | StructureSlots(_) | StructureLogic(_) | Item(_) | ItemSlots(_) | ItemConsumer(_) | ItemLogic(_) | ItemCircuitHolder(_) | ItemLogicMemory(_) | ItemSuit(_) | ItemSuitLogic(_) | ItemSuitCircuitHolder(_) => None, StructureLogicDevice(_) | StructureCircuitHolder(_) | StructureLogicDeviceMemory(_) | StructureLogicDeviceConsumer(_) | StructureLogicDeviceConsumerMemory(_) => Some(val.prefab().prefab_name.clone()), } }) .collect(); let suits = prefabs .iter() .filter_map(|(_, val)| { use ObjectTemplate::*; match val { ItemSuitCircuitHolder(_) | ItemSuitLogic(_) | ItemSuit(_) => { Some(val.prefab().prefab_name.clone()) } _ => None, } }) .collect(); let circuit_holders = prefabs .iter() .filter_map(|(_, val)| { use ObjectTemplate::*; match val { ItemSuitCircuitHolder(_) | ItemCircuitHolder(_) | StructureCircuitHolder(_) => { Some(val.prefab().prefab_name.clone()) } _ => None, } }) .collect(); let db: ObjectDatabase = ObjectDatabase { prefabs, reagents: stationpedia.reagents.clone(), enums: enums.clone(), prefabs_by_hash, structures, devices, items, logicable_items, suits, circuit_holders, }; let data_path = workspace.join("data"); if !data_path.exists() { std::fs::create_dir(&data_path)?; } let database_path = data_path.join("database.json"); let mut database_file = std::io::BufWriter::new(std::fs::File::create(database_path)?); serde_json::to_writer_pretty(&mut database_file, &db)?; database_file.flush()?; let prefab_map_path = workspace .join("stationeers_data") .join("src") .join("database") .join("prefab_map.rs"); let mut prefab_map_file = std::io::BufWriter::new(std::fs::File::create(&prefab_map_path)?); write_prefab_map(&mut prefab_map_file, &db.prefabs)?; Ok(vec![prefab_map_path]) } fn write_prefab_map( writer: &mut BufWriter, prefabs: &BTreeMap, ) -> color_eyre::Result<()> { write!( writer, "{}", quote! { use crate::enums::script_enums::*; use crate::enums::basic_enums::*; use crate::enums::{MemoryAccess, ConnectionType, ConnectionRole}; use crate::templates::*; } )?; let entries = prefabs .values() .map(|prefab| { let hash = prefab.prefab().prefab_hash; let obj = syn::parse_str::(&uneval::to_string(prefab)?)?; let entry = quote! { ( #hash, #obj.into(), ) }; Ok(entry) }) .collect::, color_eyre::Report>>()?; write!( writer, "{}", quote! { pub fn build_prefab_database() -> std::collections::BTreeMap { #[allow(clippy::unreadable_literal)] std::collections::BTreeMap::from([ #(#entries),* ]) } }, )?; Ok(()) } #[allow(clippy::too_many_lines)] fn generate_templates(pedia: &Stationpedia) -> Vec { println!("Generating templates ..."); let mut templates: Vec = Vec::new(); for page in &pedia.pages { let prefab = PrefabInfo { prefab_name: page.prefab_name.clone(), prefab_hash: page.prefab_hash, desc: page.description.clone(), name: page.title.clone(), }; // every page should either by a item or a structure // in theory every device is logicable // in theory everything with memory is logicable match page { Page { item: Some(item), structure: None, logic_info: None, slot_inserts, memory: None, device: None, transmission_receiver: false, wireless_logic: false, circuit_holder: false, resource_consumer: None, internal_atmosphere, thermal, .. } if slot_inserts.is_empty() && item.suit.is_none() => { templates.push(ObjectTemplate::Item(ItemTemplate { prefab, item: item.into(), thermal_info: thermal.as_ref().map(Into::into), internal_atmo_info: internal_atmosphere.as_ref().map(Into::into), })); } Page { item: Some(item), structure: None, logic_info: None, slot_inserts, memory: None, device: None, transmission_receiver: false, wireless_logic: false, circuit_holder: false, resource_consumer: None, internal_atmosphere, thermal, .. } if item.suit.is_none() => { templates.push(ObjectTemplate::ItemSlots(ItemSlotsTemplate { prefab, item: item.into(), thermal_info: thermal.as_ref().map(Into::into), internal_atmo_info: internal_atmosphere.as_ref().map(Into::into), slots: slot_inserts_to_info(slot_inserts), })); } Page { item: Some(item), structure: None, logic_info: None, slot_inserts, memory: None, device: None, transmission_receiver: false, wireless_logic: false, circuit_holder: false, resource_consumer: Some(consumer), internal_atmosphere, thermal, .. } if item.suit.is_none() => { templates.push(ObjectTemplate::ItemConsumer(ItemConsumerTemplate { prefab, item: item.into(), thermal_info: thermal.as_ref().map(Into::into), internal_atmo_info: internal_atmosphere.as_ref().map(Into::into), slots: slot_inserts_to_info(slot_inserts), consumer_info: consumer.into(), })); } Page { item: Some(item), structure: None, logic_info: None, slot_inserts, memory: None, device: None, transmission_receiver: false, wireless_logic: false, circuit_holder: false, resource_consumer: None, internal_atmosphere, thermal, .. } if item.suit.is_some() => { templates.push(ObjectTemplate::ItemSuit(ItemSuitTemplate { prefab, item: item.into(), thermal_info: thermal.as_ref().map(Into::into), internal_atmo_info: internal_atmosphere.as_ref().map(Into::into), slots: slot_inserts_to_info(slot_inserts), suit_info: item.suit.as_ref().unwrap().into(), })); } Page { item: Some(item), structure: None, logic_info: Some(logic), slot_inserts, memory: None, device: None, transmission_receiver, wireless_logic, circuit_holder: false, resource_consumer: None, internal_atmosphere, thermal, .. } if item.suit.is_some() => { let mut logic: LogicInfo = logic.into(); if !page.mode_insert.is_empty() { logic.modes = Some(mode_inserts_to_info(&page.mode_insert)); } logic.transmission_receiver = *transmission_receiver; logic.wireless_logic = *wireless_logic; logic.circuit_holder = false; templates.push(ObjectTemplate::ItemSuitLogic(ItemSuitLogicTemplate { prefab, item: item.into(), thermal_info: thermal.as_ref().map(Into::into), internal_atmo_info: internal_atmosphere.as_ref().map(Into::into), logic, slots: slot_inserts_to_info(slot_inserts), suit_info: item.suit.as_ref().unwrap().into(), })); } Page { item: Some(item), structure: None, logic_info: Some(logic), slot_inserts, memory: None, device: None, transmission_receiver, wireless_logic, circuit_holder: false, resource_consumer: None, internal_atmosphere, thermal, .. } if item.suit.is_none() => { let mut logic: LogicInfo = logic.into(); if !page.mode_insert.is_empty() { logic.modes = Some(mode_inserts_to_info(&page.mode_insert)); } logic.transmission_receiver = *transmission_receiver; logic.wireless_logic = *wireless_logic; logic.circuit_holder = false; templates.push(ObjectTemplate::ItemLogic(ItemLogicTemplate { prefab, item: item.into(), thermal_info: thermal.as_ref().map(Into::into), internal_atmo_info: internal_atmosphere.as_ref().map(Into::into), logic, slots: slot_inserts_to_info(slot_inserts), })); } Page { item: Some(item), structure: None, logic_info: Some(logic), slot_inserts, memory: None, device: None, transmission_receiver, wireless_logic, circuit_holder: true, resource_consumer: None, internal_atmosphere, thermal, .. } if item.suit.is_none() => { let mut logic: LogicInfo = logic.into(); if !page.mode_insert.is_empty() { logic.modes = Some(mode_inserts_to_info(&page.mode_insert)); } logic.transmission_receiver = *transmission_receiver; logic.wireless_logic = *wireless_logic; logic.circuit_holder = true; templates.push(ObjectTemplate::ItemCircuitHolder( ItemCircuitHolderTemplate { prefab, item: item.into(), thermal_info: thermal.as_ref().map(Into::into), internal_atmo_info: internal_atmosphere.as_ref().map(Into::into), logic, slots: slot_inserts_to_info(slot_inserts), }, )); } Page { item: Some(item), structure: None, logic_info: Some(logic), slot_inserts, memory: Some(memory), device: None, transmission_receiver, wireless_logic, circuit_holder: true, resource_consumer: None, internal_atmosphere, thermal, .. } if item.suit.is_some() => { let mut logic: LogicInfo = logic.into(); if !page.mode_insert.is_empty() { logic.modes = Some(mode_inserts_to_info(&page.mode_insert)); } logic.transmission_receiver = *transmission_receiver; logic.wireless_logic = *wireless_logic; logic.circuit_holder = true; templates.push(ObjectTemplate::ItemSuitCircuitHolder( ItemSuitCircuitHolderTemplate { prefab, item: item.into(), thermal_info: thermal.as_ref().map(Into::into), internal_atmo_info: internal_atmosphere.as_ref().map(Into::into), logic, slots: slot_inserts_to_info(slot_inserts), suit_info: item.suit.as_ref().unwrap().into(), memory: memory.into(), }, )); } Page { item: Some(item), structure: None, logic_info: Some(logic), slot_inserts, memory: Some(memory), device: None, transmission_receiver, wireless_logic, circuit_holder: false, resource_consumer: None, internal_atmosphere, thermal, .. } if item.suit.is_none() => { let mut logic: LogicInfo = logic.into(); if !page.mode_insert.is_empty() { logic.modes = Some(mode_inserts_to_info(&page.mode_insert)); } logic.transmission_receiver = *transmission_receiver; logic.wireless_logic = *wireless_logic; logic.circuit_holder = false; templates.push(ObjectTemplate::ItemLogicMemory(ItemLogicMemoryTemplate { prefab, item: item.into(), thermal_info: thermal.as_ref().map(Into::into), internal_atmo_info: internal_atmosphere.as_ref().map(Into::into), logic, slots: slot_inserts_to_info(slot_inserts), memory: memory.into(), })); } Page { item: None, structure: Some(structure), slot_inserts, logic_info: None, memory: None, device: None, transmission_receiver: false, wireless_logic: false, circuit_holder: false, resource_consumer: None, internal_atmosphere, thermal, .. } if slot_inserts.is_empty() => { templates.push(ObjectTemplate::Structure(StructureTemplate { prefab, structure: structure.into(), thermal_info: thermal.as_ref().map(Into::into), internal_atmo_info: internal_atmosphere.as_ref().map(Into::into), })); // println!("Structure") } Page { item: None, structure: Some(structure), slot_inserts, logic_info: None, memory: None, device: None, transmission_receiver: false, wireless_logic: false, circuit_holder: false, resource_consumer: None, internal_atmosphere, thermal, .. } => { templates.push(ObjectTemplate::StructureSlots(StructureSlotsTemplate { prefab, structure: structure.into(), thermal_info: thermal.as_ref().map(Into::into), internal_atmo_info: internal_atmosphere.as_ref().map(Into::into), slots: slot_inserts_to_info(slot_inserts), })); // println!("Structure") } Page { item: None, structure: Some(structure), logic_info: Some(logic), slot_inserts, memory: None, device: None, transmission_receiver, wireless_logic, circuit_holder: false, resource_consumer: None, internal_atmosphere, thermal, .. } => { let mut logic: LogicInfo = logic.into(); if !page.mode_insert.is_empty() { logic.modes = Some(mode_inserts_to_info(&page.mode_insert)); } logic.transmission_receiver = *transmission_receiver; logic.wireless_logic = *wireless_logic; logic.circuit_holder = false; templates.push(ObjectTemplate::StructureLogic(StructureLogicTemplate { prefab, structure: structure.into(), thermal_info: thermal.as_ref().map(Into::into), internal_atmo_info: internal_atmosphere.as_ref().map(Into::into), logic, slots: slot_inserts_to_info(slot_inserts), })); // println!("Structure") } Page { item: None, structure: Some(structure), logic_info: Some(logic), slot_inserts, memory: None, device: Some(device), transmission_receiver, wireless_logic, circuit_holder: false, resource_consumer: None, internal_atmosphere, thermal, .. } => { let mut logic: LogicInfo = logic.into(); if !page.mode_insert.is_empty() { logic.modes = Some(mode_inserts_to_info(&page.mode_insert)); } logic.transmission_receiver = *transmission_receiver; logic.wireless_logic = *wireless_logic; logic.circuit_holder = false; templates.push(ObjectTemplate::StructureLogicDevice( StructureLogicDeviceTemplate { prefab, structure: structure.into(), thermal_info: thermal.as_ref().map(Into::into), internal_atmo_info: internal_atmosphere.as_ref().map(Into::into), logic, slots: slot_inserts_to_info(slot_inserts), device: device.into(), }, )); // println!("Structure") } Page { item: None, structure: Some(structure), logic_info: Some(logic), slot_inserts, // NOTE: at the time of writing StructureCircuitHolder structure has a read write 0b memory, useless // other holders have no memory memory: Some(Memory { instructions: None, memory_size: 0, .. }) | None, device: Some(device), transmission_receiver, wireless_logic, circuit_holder: true, resource_consumer: None, internal_atmosphere, thermal, .. } => { let mut logic: LogicInfo = logic.into(); if !page.mode_insert.is_empty() { logic.modes = Some(mode_inserts_to_info(&page.mode_insert)); } logic.transmission_receiver = *transmission_receiver; logic.wireless_logic = *wireless_logic; logic.circuit_holder = true; templates.push(ObjectTemplate::StructureCircuitHolder( StructureCircuitHolderTemplate { prefab, structure: structure.into(), thermal_info: thermal.as_ref().map(Into::into), internal_atmo_info: internal_atmosphere.as_ref().map(Into::into), logic, slots: slot_inserts_to_info(slot_inserts), device: device.into(), }, )); // println!("Structure") } Page { item: None, structure: Some(structure), logic_info: Some(logic), slot_inserts, memory: None, device: Some(device), transmission_receiver, wireless_logic, circuit_holder: false, resource_consumer: Some(consumer), internal_atmosphere, thermal, .. } => { let mut logic: LogicInfo = logic.into(); if !page.mode_insert.is_empty() { logic.modes = Some(mode_inserts_to_info(&page.mode_insert)); } logic.transmission_receiver = *transmission_receiver; logic.wireless_logic = *wireless_logic; logic.circuit_holder = false; templates.push(ObjectTemplate::StructureLogicDeviceConsumer( StructureLogicDeviceConsumerTemplate { prefab, structure: structure.into(), thermal_info: thermal.as_ref().map(Into::into), internal_atmo_info: internal_atmosphere.as_ref().map(Into::into), logic, slots: slot_inserts_to_info(slot_inserts), device: device.into(), consumer_info: consumer.into(), }, )); // println!("Structure") } Page { item: None, structure: Some(structure), logic_info: Some(logic), slot_inserts, memory: Some(memory), device: Some(device), transmission_receiver, wireless_logic, circuit_holder: false, resource_consumer: None, internal_atmosphere, thermal, .. } => { let mut logic: LogicInfo = logic.into(); if !page.mode_insert.is_empty() { logic.modes = Some(mode_inserts_to_info(&page.mode_insert)); } logic.transmission_receiver = *transmission_receiver; logic.wireless_logic = *wireless_logic; logic.circuit_holder = false; templates.push(ObjectTemplate::StructureLogicDeviceMemory( StructureLogicDeviceMemoryTemplate { prefab, structure: structure.into(), thermal_info: thermal.as_ref().map(Into::into), internal_atmo_info: internal_atmosphere.as_ref().map(Into::into), logic, slots: slot_inserts_to_info(slot_inserts), device: device.into(), memory: memory.into(), }, )); // println!("Structure") } Page { item: None, structure: Some(structure), logic_info: Some(logic), slot_inserts, memory: Some(memory), device: Some(device), transmission_receiver, wireless_logic, circuit_holder: false, resource_consumer: Some(consumer), internal_atmosphere, thermal, .. } => { let mut logic: LogicInfo = logic.into(); if !page.mode_insert.is_empty() { logic.modes = Some(mode_inserts_to_info(&page.mode_insert)); } logic.transmission_receiver = *transmission_receiver; logic.wireless_logic = *wireless_logic; logic.circuit_holder = false; templates.push(ObjectTemplate::StructureLogicDeviceConsumerMemory( StructureLogicDeviceConsumerMemoryTemplate { prefab, structure: structure.into(), thermal_info: thermal.as_ref().map(Into::into), internal_atmo_info: internal_atmosphere.as_ref().map(Into::into), logic, slots: slot_inserts_to_info(slot_inserts), device: device.into(), consumer_info: consumer.into(), memory: memory.into(), }, )); // println!("Structure") } _ => panic!( "\ Non conforming: {:?} \n\t\ item: {:?}\n\t\ structure: {:?}\n\t\ logic_info: {:?}\n\t\ slot_inserts: {:?}\n\t\ slot_logic: {:?}\n\t\ memory: {:?}\n\t\ circuit_holder: {:?}\n\t\ device: {:?}\n\t\ resource_consumer: {:?}\n\t\ internal_atmosphere: {:?}\n\t\ thermal: {:?}\n\t\ ", page.key, page.item, page.structure, page.logic_info, page.slot_inserts, page.logic_slot_insert, page.memory, page.circuit_holder, page.device, page.resource_consumer, page.internal_atmosphere, page.thermal, ), } } templates } fn slot_inserts_to_info(slots: &[stationpedia::SlotInsert]) -> Vec { let mut tmp: Vec<_> = slots.into(); tmp.sort_by(|a, b| a.slot_index.cmp(&b.slot_index)); tmp.iter() .map(|slot| SlotInfo { name: slot.slot_name.clone(), typ: slot .slot_type .parse() .unwrap_or_else(|err| panic!("faild to parse slot class: {err}")), }) .collect() } fn mode_inserts_to_info(modes: &[stationpedia::ModeInsert]) -> BTreeMap { modes .iter() .map(|mode| (mode.logic_access_types, mode.logic_name.clone())) .collect() } #[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)] #[serde(deny_unknown_fields)] #[serde(rename_all = "camelCase")] pub struct ObjectDatabase { pub prefabs: BTreeMap, pub reagents: BTreeMap, pub enums: enums::Enums, pub prefabs_by_hash: BTreeMap, pub structures: Vec, pub devices: Vec, pub items: Vec, pub logicable_items: Vec, pub suits: Vec, pub circuit_holders: Vec, } impl From<&stationpedia::SuitInfo> for SuitInfo { fn from(value: &stationpedia::SuitInfo) -> Self { SuitInfo { hygine_reduction_multiplier: value.hygine_reduction_multiplier, waste_max_pressure: value.waste_max_pressure, } } } impl From<&stationpedia::ThermalInfo> for ThermalInfo { fn from(value: &stationpedia::ThermalInfo) -> Self { ThermalInfo { convection_factor: value.convection, radiation_factor: value.radiation, } } } impl From<&stationpedia::InternalAtmosphereInfo> for InternalAtmoInfo { fn from(value: &stationpedia::InternalAtmosphereInfo) -> Self { InternalAtmoInfo { volume: value.volume, } } } impl From<&stationpedia::LogicInfo> for LogicInfo { fn from(value: &stationpedia::LogicInfo) -> Self { LogicInfo { logic_slot_types: value .logic_slot_types .iter() .map(|(index, slt_map)| { ( *index, slt_map .slot_types .iter() .map(|(key, val)| { ( key.parse().unwrap_or_else(|err| { panic!("failed to parse logic slot type: {err}") }), val.parse().unwrap_or_else(|err| { panic!("failed to parse memory access: {err}") }), ) }) .collect(), ) }) .collect(), logic_types: value .logic_types .types .iter() .map(|(key, val)| { ( key.parse() .unwrap_or_else(|err| panic!("failed to parse logic type: {err}")), val.parse() .unwrap_or_else(|err| panic!("failed to parse memory access: {err}")), ) }) .collect(), modes: None, transmission_receiver: false, wireless_logic: false, circuit_holder: false, } } } impl From<&stationpedia::Item> for ItemInfo { fn from(item: &stationpedia::Item) -> Self { ItemInfo { consumable: item.consumable, filter_type: item.filter_type.as_ref().map(|typ| { typ.parse() .unwrap_or_else(|err| panic!("failed to parse filter type: {err}")) }), ingredient: item.ingredient, #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] max_quantity: item.max_quantity.unwrap_or(1.0) as u32, reagents: item .reagents .as_ref() .map(|map| map.iter().map(|(key, val)| (key.clone(), *val)).collect()), slot_class: item .slot_class .parse() .unwrap_or_else(|err| panic!("failed to parse slot class: {err}")), sorting_class: item .sorting_class .parse() .unwrap_or_else(|err| panic!("failed to parse sorting class: {err}")), } } } impl From<&stationpedia::Device> for DeviceInfo { fn from(value: &stationpedia::Device) -> Self { DeviceInfo { connection_list: value .connection_list .iter() .map(|(typ, role)| ConnectionInfo { typ: typ .parse() .unwrap_or_else(|err| panic!("failed to parse connection type: {err}")), role: role .parse() .unwrap_or_else(|err| panic!("failed to parse connection role: {err}")), }) .collect(), device_pins_length: value.devices_length, has_activate_state: value.has_activate_state, has_atmosphere: value.has_atmosphere, has_color_state: value.has_color_state, has_lock_state: value.has_lock_state, has_mode_state: value.has_mode_state, has_on_off_state: value.has_on_off_state, has_open_state: value.has_open_state, has_reagents: value.has_reagents, } } } impl From<&stationpedia::Structure> for StructureInfo { fn from(value: &stationpedia::Structure) -> Self { StructureInfo { small_grid: value.small_grid, } } } impl From<&stationpedia::Instruction> for Instruction { fn from(value: &stationpedia::Instruction) -> Self { Instruction { description: value.description.clone(), typ: value.type_.clone(), value: value.value, } } } impl From<&stationpedia::Memory> for MemoryInfo { fn from(value: &stationpedia::Memory) -> Self { MemoryInfo { instructions: value.instructions.as_ref().map(|insts| { insts .iter() .map(|(key, value)| (key.clone(), value.into())) .collect() }), memory_access: value .memory_access .parse() .unwrap_or_else(|err| panic!("failed to parse memory access: {err}")), memory_size: value.memory_size, } } } impl From<&stationpedia::ResourceConsumer> for ConsumerInfo { fn from(value: &stationpedia::ResourceConsumer) -> Self { ConsumerInfo { consumed_resouces: value.consumed_resources.clone(), processed_reagents: value.processed_reagents.clone(), } } }