refactor(vm) helpful codegen round 1~
This commit is contained in:
28
xtask/src/enums.rs
Normal file
28
xtask/src/enums.rs
Normal file
@@ -0,0 +1,28 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
|
||||
#[serde(rename = "Enums")]
|
||||
pub struct Enums {
|
||||
#[serde(rename = "scriptEnums")]
|
||||
pub script_enums: BTreeMap<String, EnumListing>,
|
||||
#[serde(rename = "basicEnums")]
|
||||
pub basic_enums: BTreeMap<String, EnumListing>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
|
||||
#[serde(rename = "EnumListing")]
|
||||
pub struct EnumListing {
|
||||
#[serde(rename = "enumName")]
|
||||
pub enum_name: String,
|
||||
pub values: BTreeMap<String, EnumEntry>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
|
||||
#[serde(rename = "EnumEntry")]
|
||||
pub struct EnumEntry {
|
||||
pub value: i64,
|
||||
pub deprecated: bool,
|
||||
pub description: String,
|
||||
}
|
||||
101
xtask/src/generate.rs
Normal file
101
xtask/src/generate.rs
Normal file
@@ -0,0 +1,101 @@
|
||||
use color_eyre::eyre;
|
||||
|
||||
use std::{collections::BTreeMap, process::Command};
|
||||
|
||||
use crate::{enums::Enums, stationpedia::Stationpedia};
|
||||
|
||||
mod database;
|
||||
mod enums;
|
||||
mod instructions;
|
||||
mod utils;
|
||||
|
||||
pub fn generate(
|
||||
stationpedia_path: &std::path::Path,
|
||||
workspace: &std::path::Path,
|
||||
) -> color_eyre::Result<()> {
|
||||
let mut pedia: Stationpedia = parse_json(&mut serde_json::Deserializer::from_reader(
|
||||
std::io::BufReader::new(std::fs::File::open(
|
||||
stationpedia_path.join("Stationpedia.json"),
|
||||
)?),
|
||||
))?;
|
||||
|
||||
let instruction_help_patches: BTreeMap<String, String> = parse_json(
|
||||
&mut serde_json::Deserializer::from_reader(std::io::BufReader::new(std::fs::File::open(
|
||||
workspace.join("data").join("instruction_help_patches.json"),
|
||||
)?)),
|
||||
)?;
|
||||
|
||||
for (inst, patch) in instruction_help_patches {
|
||||
if let Some(cmd) = pedia.script_commands.get_mut(&inst) {
|
||||
cmd.desc = patch;
|
||||
} else {
|
||||
eprintln!("Warning: can find instruction '{inst}' to patch help!");
|
||||
}
|
||||
}
|
||||
|
||||
let enums: Enums = parse_json(&mut serde_json::Deserializer::from_reader(
|
||||
std::io::BufReader::new(std::fs::File::open(stationpedia_path.join("Enums.json"))?),
|
||||
))?;
|
||||
|
||||
database::generate_database(&pedia, &enums, workspace)?;
|
||||
let enums_files = enums::generate_enums(&pedia, &enums, workspace)?;
|
||||
let inst_files = instructions::generate_instructions(&pedia, workspace)?;
|
||||
|
||||
let generated_files = [enums_files.as_slice(), inst_files.as_slice()].concat();
|
||||
|
||||
eprintln!("Formatting generated files...");
|
||||
for file in &generated_files {
|
||||
prepend_genereated_comment(file)?;
|
||||
}
|
||||
let mut cmd = Command::new("cargo");
|
||||
cmd.current_dir(workspace);
|
||||
cmd.arg("fmt").arg("--");
|
||||
cmd.args(&generated_files);
|
||||
cmd.status()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn parse_json<'a, T: serde::Deserialize<'a>>(
|
||||
jd: impl serde::Deserializer<'a>,
|
||||
) -> Result<T, color_eyre::Report> {
|
||||
let mut track = serde_path_to_error::Track::new();
|
||||
let path = serde_path_to_error::Deserializer::new(jd, &mut track);
|
||||
let mut fun = |path: serde_ignored::Path| {
|
||||
tracing::warn!(key=%path,"Found ignored key");
|
||||
};
|
||||
serde_ignored::deserialize(path, &mut fun).map_err(|e| {
|
||||
eyre::eyre!(
|
||||
"path: {track} | error = {e}",
|
||||
track = track.path().to_string(),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn prepend_genereated_comment(file_path: &std::path::Path) -> color_eyre::Result<()> {
|
||||
use std::io::Write;
|
||||
let tmp_path = file_path.with_extension("rs.tmp");
|
||||
{
|
||||
let mut tmp = std::fs::File::create(&tmp_path)?;
|
||||
let mut src = std::fs::File::open(file_path)?;
|
||||
write!(
|
||||
&mut tmp,
|
||||
"// ================================================= \n\
|
||||
// !! <-----> DO NOT MODIFY <-----> !! \n\
|
||||
// \n\
|
||||
// This module was automatically generated by an
|
||||
// xtask \n\
|
||||
// \n\
|
||||
// run `cargo xtask generate` from the workspace \n\
|
||||
// to regenerate \n\
|
||||
// \n\
|
||||
// ================================================= \n\
|
||||
\n\
|
||||
\n\
|
||||
"
|
||||
)?;
|
||||
std::io::copy(&mut src, &mut tmp)?;
|
||||
}
|
||||
std::fs::remove_file(file_path)?;
|
||||
std::fs::rename(&tmp_path, file_path)?;
|
||||
Ok(())
|
||||
}
|
||||
674
xtask/src/generate/database.rs
Normal file
674
xtask/src/generate/database.rs
Normal file
@@ -0,0 +1,674 @@
|
||||
use std::{collections::BTreeMap, io::Write};
|
||||
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
enums,
|
||||
stationpedia::{self, Page, Stationpedia},
|
||||
};
|
||||
|
||||
pub fn generate_database(
|
||||
stationpedia: &stationpedia::Stationpedia,
|
||||
enums: &enums::Enums,
|
||||
workspace: &std::path::Path,
|
||||
) -> color_eyre::Result<()> {
|
||||
let templates = generate_templates(stationpedia)?;
|
||||
|
||||
println!("Writing prefab database ...");
|
||||
|
||||
let prefabs: BTreeMap<String, ObjectTemplate> = templates
|
||||
.into_iter()
|
||||
.map(|obj| (obj.prefab().prefab_name.clone(), obj))
|
||||
.collect();
|
||||
let prefabs_by_hash: BTreeMap<i32, String> = 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(_)
|
||||
| StructureLogicDeviceMemory(_) => Some(val.prefab().prefab_name.clone()),
|
||||
Item(_) | ItemSlots(_) | ItemLogic(_) | ItemLogicMemory(_) => None,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
let items = prefabs
|
||||
.iter()
|
||||
.filter_map(|(_, val)| {
|
||||
use ObjectTemplate::*;
|
||||
match val {
|
||||
Structure(_)
|
||||
| StructureSlots(_)
|
||||
| StructureLogic(_)
|
||||
| StructureLogicDevice(_)
|
||||
| StructureLogicDeviceMemory(_) => None,
|
||||
Item(_) | ItemSlots(_) | ItemLogic(_) | ItemLogicMemory(_) => {
|
||||
Some(val.prefab().prefab_name.clone())
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
let logicable_items = prefabs
|
||||
.iter()
|
||||
.filter_map(|(_, val)| {
|
||||
use ObjectTemplate::*;
|
||||
match val {
|
||||
Structure(_)
|
||||
| StructureSlots(_)
|
||||
| StructureLogic(_)
|
||||
| StructureLogicDevice(_)
|
||||
| StructureLogicDeviceMemory(_)
|
||||
| Item(_)
|
||||
| ItemSlots(_) => None,
|
||||
ItemLogic(_) | ItemLogicMemory(_) => Some(val.prefab().prefab_name.clone()),
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
let devices = prefabs
|
||||
.iter()
|
||||
.filter_map(|(_, val)| {
|
||||
use ObjectTemplate::*;
|
||||
match val {
|
||||
Structure(_) | StructureSlots(_) | StructureLogic(_) | Item(_) | ItemSlots(_)
|
||||
| ItemLogic(_) | ItemLogicMemory(_) => None,
|
||||
StructureLogicDevice(_) | StructureLogicDeviceMemory(_) => {
|
||||
Some(val.prefab().prefab_name.clone())
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
let db: ObjectDatabase = ObjectDatabase {
|
||||
prefabs,
|
||||
reagents: stationpedia.reagents.clone(),
|
||||
enums: enums.clone(),
|
||||
prefabs_by_hash,
|
||||
structures,
|
||||
devices,
|
||||
items,
|
||||
logicable_items,
|
||||
};
|
||||
|
||||
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(&mut database_file, &db)?;
|
||||
database_file.flush()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn generate_templates(pedia: &Stationpedia) -> color_eyre::Result<Vec<ObjectTemplate>> {
|
||||
println!("Generating templates ...");
|
||||
let mut templates: Vec<ObjectTemplate> = Vec::new();
|
||||
for page in pedia.pages.iter() {
|
||||
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: None,
|
||||
wireless_logic: None,
|
||||
circuit_holder: None,
|
||||
..
|
||||
} if slot_inserts.is_empty() => {
|
||||
templates.push(ObjectTemplate::Item(ItemTemplate {
|
||||
prefab,
|
||||
item: item.into(),
|
||||
}));
|
||||
}
|
||||
Page {
|
||||
item: Some(item),
|
||||
structure: None,
|
||||
logic_info: None,
|
||||
slot_inserts,
|
||||
memory: None,
|
||||
device: None,
|
||||
transmission_receiver: None,
|
||||
wireless_logic: None,
|
||||
circuit_holder: None,
|
||||
..
|
||||
} => {
|
||||
templates.push(ObjectTemplate::ItemSlots(ItemSlotsTemplate {
|
||||
prefab,
|
||||
item: item.into(),
|
||||
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,
|
||||
..
|
||||
} => {
|
||||
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.unwrap_or(false);
|
||||
logic.wireless_logic = wireless_logic.unwrap_or(false);
|
||||
logic.circuit_holder = circuit_holder.unwrap_or(false);
|
||||
|
||||
templates.push(ObjectTemplate::ItemLogic(ItemLogicTemplate {
|
||||
prefab,
|
||||
item: item.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,
|
||||
..
|
||||
} => {
|
||||
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.unwrap_or(false);
|
||||
logic.wireless_logic = wireless_logic.unwrap_or(false);
|
||||
logic.circuit_holder = circuit_holder.unwrap_or(false);
|
||||
|
||||
templates.push(ObjectTemplate::ItemLogicMemory(ItemLogicMemoryTemplate {
|
||||
prefab,
|
||||
item: item.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: None,
|
||||
wireless_logic: None,
|
||||
circuit_holder: None,
|
||||
..
|
||||
} if slot_inserts.is_empty() => {
|
||||
templates.push(ObjectTemplate::Structure(StructureTemplate {
|
||||
prefab,
|
||||
structure: structure.into(),
|
||||
}));
|
||||
// println!("Structure")
|
||||
}
|
||||
Page {
|
||||
item: None,
|
||||
structure: Some(structure),
|
||||
slot_inserts,
|
||||
logic_info: None,
|
||||
memory: None,
|
||||
device: None,
|
||||
transmission_receiver: None,
|
||||
wireless_logic: None,
|
||||
circuit_holder: None,
|
||||
..
|
||||
} => {
|
||||
templates.push(ObjectTemplate::StructureSlots(StructureSlotsTemplate {
|
||||
prefab,
|
||||
structure: structure.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,
|
||||
..
|
||||
} => {
|
||||
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.unwrap_or(false);
|
||||
logic.wireless_logic = wireless_logic.unwrap_or(false);
|
||||
logic.circuit_holder = circuit_holder.unwrap_or(false);
|
||||
|
||||
templates.push(ObjectTemplate::StructureLogic(StructureLogicTemplate {
|
||||
prefab,
|
||||
structure: structure.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,
|
||||
..
|
||||
} => {
|
||||
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.unwrap_or(false);
|
||||
logic.wireless_logic = wireless_logic.unwrap_or(false);
|
||||
logic.circuit_holder = circuit_holder.unwrap_or(false);
|
||||
|
||||
templates.push(ObjectTemplate::StructureLogicDevice(
|
||||
StructureLogicDeviceTemplate {
|
||||
prefab,
|
||||
structure: structure.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: Some(memory),
|
||||
device: Some(device),
|
||||
transmission_receiver,
|
||||
wireless_logic,
|
||||
circuit_holder,
|
||||
..
|
||||
} => {
|
||||
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.unwrap_or(false);
|
||||
logic.wireless_logic = wireless_logic.unwrap_or(false);
|
||||
logic.circuit_holder = circuit_holder.unwrap_or(false);
|
||||
templates.push(ObjectTemplate::StructureLogicDeviceMemory(
|
||||
StructureLogicDeviceMemoryTemplate {
|
||||
prefab,
|
||||
structure: structure.into(),
|
||||
logic,
|
||||
slots: slot_inserts_to_info(slot_inserts),
|
||||
device: device.into(),
|
||||
memory: memory.into(),
|
||||
},
|
||||
));
|
||||
// println!("Structure")
|
||||
}
|
||||
_ => panic!(
|
||||
"Non conforming: {:?} \n\titem: {:?}\n\tstructure: {:?}\n\tlogic_info: {:?}\n\tslot_inserts: {:?}\n\tslot_logic: {:?}\n\tmemory: {:?}\n\tdevice: {:?}",
|
||||
page.key,
|
||||
page.item,
|
||||
page.structure,
|
||||
page.logic_info,
|
||||
page.slot_inserts,
|
||||
page.logic_slot_insert,
|
||||
page.memory,
|
||||
page.device,
|
||||
),
|
||||
}
|
||||
}
|
||||
Ok(templates)
|
||||
}
|
||||
|
||||
fn slot_inserts_to_info(slots: &[stationpedia::SlotInsert]) -> Vec<SlotInfo> {
|
||||
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.clone(),
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn mode_inserts_to_info(modes: &[stationpedia::ModeInsert]) -> BTreeMap<u32, String> {
|
||||
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<String, ObjectTemplate>,
|
||||
pub reagents: BTreeMap<String, stationpedia::Reagent>,
|
||||
pub enums: enums::Enums,
|
||||
pub prefabs_by_hash: BTreeMap<i32, String>,
|
||||
pub structures: Vec<String>,
|
||||
pub devices: Vec<String>,
|
||||
pub items: Vec<String>,
|
||||
pub logicable_items: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum ObjectTemplate {
|
||||
Structure(StructureTemplate),
|
||||
StructureSlots(StructureSlotsTemplate),
|
||||
StructureLogic(StructureLogicTemplate),
|
||||
StructureLogicDevice(StructureLogicDeviceTemplate),
|
||||
StructureLogicDeviceMemory(StructureLogicDeviceMemoryTemplate),
|
||||
Item(ItemTemplate),
|
||||
ItemSlots(ItemSlotsTemplate),
|
||||
ItemLogic(ItemLogicTemplate),
|
||||
ItemLogicMemory(ItemLogicMemoryTemplate),
|
||||
}
|
||||
|
||||
impl ObjectTemplate {
|
||||
fn prefab(&self) -> &PrefabInfo {
|
||||
use ObjectTemplate::*;
|
||||
match self {
|
||||
Structure(s) => &s.prefab,
|
||||
StructureSlots(s) => &s.prefab,
|
||||
StructureLogic(s) => &s.prefab,
|
||||
StructureLogicDevice(s) => &s.prefab,
|
||||
StructureLogicDeviceMemory(s) => &s.prefab,
|
||||
Item(i) => &i.prefab,
|
||||
ItemSlots(i) => &i.prefab,
|
||||
ItemLogic(i) => &i.prefab,
|
||||
ItemLogicMemory(i) => &i.prefab,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PrefabInfo {
|
||||
pub prefab_name: String,
|
||||
pub prefab_hash: i32,
|
||||
pub desc: String,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct SlotInfo {
|
||||
pub name: String,
|
||||
pub typ: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct LogicInfo {
|
||||
pub logic_slot_types: BTreeMap<u32, stationpedia::LogicSlotTypes>,
|
||||
pub logic_types: stationpedia::LogicTypes,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub modes: Option<BTreeMap<u32, String>>,
|
||||
pub transmission_receiver: bool,
|
||||
pub wireless_logic: bool,
|
||||
pub circuit_holder: bool,
|
||||
}
|
||||
|
||||
impl From<&stationpedia::LogicInfo> for LogicInfo {
|
||||
fn from(value: &stationpedia::LogicInfo) -> Self {
|
||||
LogicInfo {
|
||||
logic_slot_types: value.logic_slot_types.clone(),
|
||||
logic_types: value.logic_types.clone(),
|
||||
modes: None,
|
||||
transmission_receiver: false,
|
||||
wireless_logic: false,
|
||||
circuit_holder: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ItemInfo {
|
||||
pub consumable: bool,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub filter_type: Option<String>,
|
||||
pub ingredient: bool,
|
||||
pub max_quantity: f64,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub reagents: Option<BTreeMap<String, f64>>,
|
||||
pub slot_class: String,
|
||||
pub sorting_class: String,
|
||||
}
|
||||
|
||||
impl From<&stationpedia::Item> for ItemInfo {
|
||||
fn from(item: &stationpedia::Item) -> Self {
|
||||
ItemInfo {
|
||||
consumable: item.consumable.unwrap_or(false),
|
||||
filter_type: item.filter_type.clone(),
|
||||
ingredient: item.ingredient.unwrap_or(false),
|
||||
max_quantity: item.max_quantity.unwrap_or(1.0),
|
||||
reagents: item
|
||||
.reagents
|
||||
.as_ref()
|
||||
.map(|map| map.iter().map(|(key, val)| (key.clone(), *val)).collect()),
|
||||
slot_class: item.slot_class.clone(),
|
||||
sorting_class: item.sorting_class.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct DeviceInfo {
|
||||
pub connection_list: Vec<(String, String)>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub device_pins_length: Option<i64>,
|
||||
pub has_activate_state: bool,
|
||||
pub has_atmosphere: bool,
|
||||
pub has_color_state: bool,
|
||||
pub has_lock_state: bool,
|
||||
pub has_mode_state: bool,
|
||||
pub has_on_off_state: bool,
|
||||
pub has_open_state: bool,
|
||||
pub has_reagents: bool,
|
||||
}
|
||||
|
||||
impl From<&stationpedia::Device> for DeviceInfo {
|
||||
fn from(value: &stationpedia::Device) -> Self {
|
||||
DeviceInfo {
|
||||
connection_list: value.connection_list.clone(),
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct StructureInfo {
|
||||
pub small_grid: bool,
|
||||
}
|
||||
|
||||
impl From<&stationpedia::Structure> for StructureInfo {
|
||||
fn from(value: &stationpedia::Structure) -> Self {
|
||||
StructureInfo {
|
||||
small_grid: value.small_grid,
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Instruction {
|
||||
pub description: String,
|
||||
pub typ: String,
|
||||
pub value: i64,
|
||||
}
|
||||
|
||||
impl From<&stationpedia::Instruction> for Instruction {
|
||||
fn from(value: &stationpedia::Instruction) -> Self {
|
||||
Instruction {
|
||||
description: value.description.clone(),
|
||||
typ: value.type_.clone(),
|
||||
value: value.value,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct MemoryInfo {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub instructions: Option<BTreeMap<String, Instruction>>,
|
||||
pub memory_access: String,
|
||||
pub memory_size: i64,
|
||||
}
|
||||
|
||||
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.clone(),
|
||||
memory_size: value.memory_size,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct StructureTemplate {
|
||||
pub prefab: PrefabInfo,
|
||||
pub structure: StructureInfo,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct StructureSlotsTemplate {
|
||||
pub prefab: PrefabInfo,
|
||||
pub structure: StructureInfo,
|
||||
pub slots: Vec<SlotInfo>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct StructureLogicTemplate {
|
||||
pub prefab: PrefabInfo,
|
||||
pub structure: StructureInfo,
|
||||
pub logic: LogicInfo,
|
||||
pub slots: Vec<SlotInfo>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct StructureLogicDeviceTemplate {
|
||||
pub prefab: PrefabInfo,
|
||||
pub structure: StructureInfo,
|
||||
pub logic: LogicInfo,
|
||||
pub slots: Vec<SlotInfo>,
|
||||
pub device: DeviceInfo,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct StructureLogicDeviceMemoryTemplate {
|
||||
pub prefab: PrefabInfo,
|
||||
pub structure: StructureInfo,
|
||||
pub logic: LogicInfo,
|
||||
pub slots: Vec<SlotInfo>,
|
||||
pub device: DeviceInfo,
|
||||
pub memory: MemoryInfo,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ItemTemplate {
|
||||
pub prefab: PrefabInfo,
|
||||
pub item: ItemInfo,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ItemSlotsTemplate {
|
||||
pub prefab: PrefabInfo,
|
||||
pub item: ItemInfo,
|
||||
pub slots: Vec<SlotInfo>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ItemLogicTemplate {
|
||||
pub prefab: PrefabInfo,
|
||||
pub item: ItemInfo,
|
||||
pub logic: LogicInfo,
|
||||
pub slots: Vec<SlotInfo>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ItemLogicMemoryTemplate {
|
||||
pub prefab: PrefabInfo,
|
||||
pub item: ItemInfo,
|
||||
pub logic: LogicInfo,
|
||||
pub slots: Vec<SlotInfo>,
|
||||
pub memory: MemoryInfo,
|
||||
}
|
||||
397
xtask/src/generate/enums.rs
Normal file
397
xtask/src/generate/enums.rs
Normal file
@@ -0,0 +1,397 @@
|
||||
use convert_case::{Case, Casing};
|
||||
use std::collections::BTreeMap;
|
||||
use std::{
|
||||
fmt::Display,
|
||||
io::{BufWriter, Write},
|
||||
path::PathBuf,
|
||||
str::FromStr,
|
||||
};
|
||||
pub fn generate_enums(
|
||||
stationpedia: &crate::stationpedia::Stationpedia,
|
||||
enums: &crate::enums::Enums,
|
||||
workspace: &std::path::Path,
|
||||
) -> color_eyre::Result<Vec<PathBuf>> {
|
||||
println!("Writing Enum Listings ...");
|
||||
let enums_path = workspace
|
||||
.join("ic10emu")
|
||||
.join("src")
|
||||
.join("vm")
|
||||
.join("enums");
|
||||
if !enums_path.exists() {
|
||||
std::fs::create_dir(&enums_path)?;
|
||||
}
|
||||
|
||||
let mut writer =
|
||||
std::io::BufWriter::new(std::fs::File::create(enums_path.join("script_enums.rs"))?);
|
||||
write_repr_enum_use_header(&mut writer)?;
|
||||
for enm in enums.script_enums.values() {
|
||||
write_enum_listing(&mut writer, enm)?;
|
||||
}
|
||||
|
||||
let mut writer =
|
||||
std::io::BufWriter::new(std::fs::File::create(enums_path.join("basic_enums.rs"))?);
|
||||
write_repr_enum_use_header(&mut writer)?;
|
||||
for enm in enums.basic_enums.values() {
|
||||
write_enum_listing(&mut writer, enm)?;
|
||||
}
|
||||
write_enum_aggragate_mod(&mut writer, &enums.basic_enums)?;
|
||||
|
||||
let mut writer = std::io::BufWriter::new(std::fs::File::create(enums_path.join("prefabs.rs"))?);
|
||||
write_repr_enum_use_header(&mut writer)?;
|
||||
let prefabs = stationpedia
|
||||
.pages
|
||||
.iter()
|
||||
.map(|page| {
|
||||
let variant = ReprEnumVariant {
|
||||
value: page.prefab_hash,
|
||||
deprecated: false,
|
||||
props: vec![
|
||||
("name".to_owned(), page.title.clone()),
|
||||
("desc".to_owned(), page.description.clone()),
|
||||
],
|
||||
};
|
||||
(page.prefab_name.clone(), variant)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
write_repr_enum(&mut writer, "StationpediaPrefab", &prefabs, true)?;
|
||||
|
||||
Ok(vec![
|
||||
enums_path.join("script_enums.rs"),
|
||||
enums_path.join("basic_enums.rs"),
|
||||
enums_path.join("prefabs.rs"),
|
||||
])
|
||||
}
|
||||
|
||||
fn write_enum_aggragate_mod<T: std::io::Write>(
|
||||
writer: &mut BufWriter<T>,
|
||||
enums: &BTreeMap<String, crate::enums::EnumListing>,
|
||||
) -> color_eyre::Result<()> {
|
||||
let variant_lines = enums
|
||||
.iter()
|
||||
.map(|(name, listing)| {
|
||||
let name = if name.is_empty() || name == "_unnamed" {
|
||||
"Unnamed"
|
||||
} else {
|
||||
name
|
||||
};
|
||||
format!(
|
||||
" {}({}),",
|
||||
name.to_case(Case::Pascal),
|
||||
listing.enum_name.to_case(Case::Pascal)
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
let value_arms = enums
|
||||
.keys()
|
||||
.map(|name| {
|
||||
let variant_name = (if name.is_empty() || name == "_unnamed" {
|
||||
"Unnamed"
|
||||
} else {
|
||||
name
|
||||
})
|
||||
.to_case(Case::Pascal);
|
||||
format!(" Self::{variant_name}(enm) => *enm as u32,",)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
|
||||
let get_str_arms = enums
|
||||
.keys()
|
||||
.map(|name| {
|
||||
let variant_name = (if name.is_empty() || name == "_unnamed" {
|
||||
"Unnamed"
|
||||
} else {
|
||||
name
|
||||
})
|
||||
.to_case(Case::Pascal);
|
||||
format!(" Self::{variant_name}(enm) => enm.get_str(prop),",)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
let iter_chain = enums
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, (name, listing))| {
|
||||
let variant_name = (if name.is_empty() || name == "_unnamed" {
|
||||
"Unnamed"
|
||||
} else {
|
||||
name
|
||||
})
|
||||
.to_case(Case::Pascal);
|
||||
let enum_name = listing.enum_name.to_case(Case::Pascal);
|
||||
if index == 0 {
|
||||
format!("{enum_name}::iter().map(|enm| Self::{variant_name}(enm))")
|
||||
} else {
|
||||
format!(".chain({enum_name}::iter().map(|enm| Self::{variant_name}(enm)))")
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
write!(
|
||||
writer,
|
||||
"pub enum BasicEnum {{\n\
|
||||
{variant_lines}
|
||||
}}\n\
|
||||
impl BasicEnum {{\n \
|
||||
pub fn get_value(&self) -> u32 {{\n \
|
||||
match self {{\n \
|
||||
{value_arms}\n \
|
||||
}}\n \
|
||||
}}\n\
|
||||
pub fn get_str(&self, prop: &str) -> Option<&'static str> {{\n \
|
||||
match self {{\n \
|
||||
{get_str_arms}\n \
|
||||
}}\n \
|
||||
}}\n\
|
||||
pub fn iter() -> impl std::iter::Iterator<Item = Self> {{\n \
|
||||
use strum::IntoEnumIterator;\n \
|
||||
{iter_chain}\n \
|
||||
}}
|
||||
}}\n\
|
||||
"
|
||||
)?;
|
||||
let arms = enums
|
||||
.iter()
|
||||
.flat_map(|(name, listing)| {
|
||||
let variant_name = (if name.is_empty() || name == "_unnamed" {
|
||||
"Unnamed"
|
||||
} else {
|
||||
name
|
||||
})
|
||||
.to_case(Case::Pascal);
|
||||
let name = if name == "_unnamed" {
|
||||
"".to_string()
|
||||
} else {
|
||||
name.clone()
|
||||
};
|
||||
let enum_name = listing.enum_name.to_case(Case::Pascal);
|
||||
listing.values.keys().map(move |variant| {
|
||||
let sep = if name.is_empty() { "" } else { "." };
|
||||
let pat = format!("{name}{sep}{variant}").to_lowercase();
|
||||
let variant = variant.to_case(Case::Pascal);
|
||||
format!("\"{pat}\" => Ok(Self::{variant_name}({enum_name}::{variant})),")
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n ");
|
||||
write!(
|
||||
writer,
|
||||
"\
|
||||
impl std::str::FromStr for BasicEnum {{\n \
|
||||
type Err = crate::errors::ParseError;\n \
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {{\n \
|
||||
let end = s.len();\n \
|
||||
match s {{\n \
|
||||
{arms}\n \
|
||||
_ => Err(crate::errors::ParseError{{ line: 0, start: 0, end, msg: format!(\"Unknown enum '{{}}'\", s) }})\n \
|
||||
}}\n \
|
||||
}}\n\
|
||||
}}\
|
||||
"
|
||||
)?;
|
||||
let display_arms = enums
|
||||
.keys()
|
||||
.map(|name| {
|
||||
let variant_name = (if name.is_empty() || name == "_unnamed" {
|
||||
"Unnamed"
|
||||
} else {
|
||||
name
|
||||
})
|
||||
.to_case(Case::Pascal);
|
||||
let name = if name == "_unnamed" {
|
||||
"".to_string()
|
||||
} else {
|
||||
name.clone()
|
||||
};
|
||||
let sep = if name.is_empty() || name == "_unnamed" {
|
||||
""
|
||||
} else {
|
||||
"."
|
||||
};
|
||||
let pat = format!("{name}{sep}{{}}");
|
||||
format!(" Self::{variant_name}(enm) => write!(f, \"{pat}\", enm),",)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n ");
|
||||
write!(
|
||||
writer,
|
||||
"\
|
||||
impl std::fmt::Display for BasicEnum {{\n \
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {{\n \
|
||||
match self {{\n \
|
||||
{display_arms}\n \
|
||||
}}\n \
|
||||
}}\n\
|
||||
}}\
|
||||
"
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
pub fn write_enum_listing<T: std::io::Write>(
|
||||
writer: &mut BufWriter<T>,
|
||||
enm: &crate::enums::EnumListing,
|
||||
) -> color_eyre::Result<()> {
|
||||
let max = enm
|
||||
.values
|
||||
.values()
|
||||
.map(|var| var.value)
|
||||
.max()
|
||||
.expect("enum should have max value");
|
||||
let min = enm
|
||||
.values
|
||||
.values()
|
||||
.map(|var| var.value)
|
||||
.min()
|
||||
.expect("enum should have min value");
|
||||
|
||||
if max < u8::MAX as i64 && min >= u8::MIN as i64 {
|
||||
let variants: Vec<_> = enm
|
||||
.values
|
||||
.iter()
|
||||
.map(|(n, var)| {
|
||||
let variant = ReprEnumVariant {
|
||||
value: var.value as u8,
|
||||
deprecated: var.deprecated,
|
||||
props: vec![("docs".to_owned(), var.description.to_owned())],
|
||||
};
|
||||
(n.clone(), variant)
|
||||
})
|
||||
.collect();
|
||||
write_repr_enum(
|
||||
writer,
|
||||
&enm.enum_name.to_case(Case::Pascal),
|
||||
&variants,
|
||||
true,
|
||||
)?;
|
||||
} else if max < u16::MAX as i64 && min >= u16::MIN as i64 {
|
||||
let variants: Vec<_> = enm
|
||||
.values
|
||||
.iter()
|
||||
.map(|(n, var)| {
|
||||
let variant = ReprEnumVariant {
|
||||
value: var.value as u16,
|
||||
deprecated: var.deprecated,
|
||||
props: vec![("docs".to_owned(), var.description.to_owned())],
|
||||
};
|
||||
(n.clone(), variant)
|
||||
})
|
||||
.collect();
|
||||
write_repr_enum(writer, &enm.enum_name, &variants, true)?;
|
||||
} else if max < u32::MAX as i64 && min >= u32::MIN as i64 {
|
||||
let variants: Vec<_> = enm
|
||||
.values
|
||||
.iter()
|
||||
.map(|(n, var)| {
|
||||
let variant = ReprEnumVariant {
|
||||
value: var.value as u32,
|
||||
deprecated: var.deprecated,
|
||||
props: vec![("docs".to_owned(), var.description.to_owned())],
|
||||
};
|
||||
(n.clone(), variant)
|
||||
})
|
||||
.collect();
|
||||
write_repr_enum(writer, &enm.enum_name, &variants, true)?;
|
||||
} else if max < i32::MAX as i64 && min >= i32::MIN as i64 {
|
||||
let variants: Vec<_> = enm
|
||||
.values
|
||||
.iter()
|
||||
.map(|(n, var)| {
|
||||
let variant = ReprEnumVariant {
|
||||
value: var.value as i32,
|
||||
deprecated: var.deprecated,
|
||||
props: vec![("docs".to_owned(), var.description.to_owned())],
|
||||
};
|
||||
(n.clone(), variant)
|
||||
})
|
||||
.collect();
|
||||
write_repr_enum(writer, &enm.enum_name, &variants, true)?;
|
||||
} else {
|
||||
let variants: Vec<_> = enm
|
||||
.values
|
||||
.iter()
|
||||
.map(|(n, var)| {
|
||||
let variant = ReprEnumVariant {
|
||||
value: var.value as i32,
|
||||
deprecated: var.deprecated,
|
||||
props: vec![("docs".to_owned(), var.description.to_owned())],
|
||||
};
|
||||
(n.clone(), variant)
|
||||
})
|
||||
.collect();
|
||||
write_repr_enum(writer, &enm.enum_name, &variants, true)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
struct ReprEnumVariant<P>
|
||||
where
|
||||
P: Display + FromStr,
|
||||
{
|
||||
pub value: P,
|
||||
pub deprecated: bool,
|
||||
pub props: Vec<(String, String)>,
|
||||
}
|
||||
|
||||
fn write_repr_enum_use_header<T: std::io::Write>(
|
||||
writer: &mut BufWriter<T>,
|
||||
) -> color_eyre::Result<()> {
|
||||
write!(
|
||||
writer,
|
||||
"use serde::{{Deserialize, Serialize}};\n\
|
||||
use strum::{{\n \
|
||||
AsRefStr, Display, EnumIter, EnumProperty, EnumString, FromRepr,\n\
|
||||
}};\n"
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_repr_enum<'a, T: std::io::Write, I, P>(
|
||||
writer: &mut BufWriter<T>,
|
||||
name: &str,
|
||||
variants: I,
|
||||
use_phf: bool,
|
||||
) -> color_eyre::Result<()>
|
||||
where
|
||||
P: Display + FromStr + 'a,
|
||||
I: IntoIterator<Item = &'a (String, ReprEnumVariant<P>)>,
|
||||
{
|
||||
let additional_strum = if use_phf { "#[strum(use_phf)]\n" } else { "" };
|
||||
let repr = std::any::type_name::<P>();
|
||||
write!(
|
||||
writer,
|
||||
"#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, EnumString, AsRefStr, EnumProperty, EnumIter, FromRepr, Serialize, Deserialize)]\n\
|
||||
{additional_strum}\
|
||||
#[repr({repr})]\n\
|
||||
pub enum {name} {{\n"
|
||||
)?;
|
||||
for (name, variant) in variants {
|
||||
let variant_name = name.replace('.', "").to_case(Case::Pascal);
|
||||
let serialize = vec![name.clone()];
|
||||
let serialize_str = serialize
|
||||
.into_iter()
|
||||
.map(|s| format!("serialize = \"{s}\""))
|
||||
.collect::<Vec<String>>()
|
||||
.join(", ");
|
||||
let mut props = Vec::new();
|
||||
if variant.deprecated {
|
||||
props.push("deprecated = \"true\"".to_owned());
|
||||
}
|
||||
for (prop_name, prop_val) in &variant.props {
|
||||
props.push(format!("{prop_name} = r#\"{prop_val}\"#"));
|
||||
}
|
||||
let val = &variant.value;
|
||||
props.push(format!("value = \"{val}\""));
|
||||
let props_str = if !props.is_empty() {
|
||||
format!(", props( {} )", props.join(", "))
|
||||
} else {
|
||||
"".to_owned()
|
||||
};
|
||||
writeln!(
|
||||
writer,
|
||||
" #[strum({serialize_str}{props_str})] {variant_name} = {val}{repr},"
|
||||
)?;
|
||||
}
|
||||
writeln!(writer, "}}")?;
|
||||
Ok(())
|
||||
}
|
||||
216
xtask/src/generate/instructions.rs
Normal file
216
xtask/src/generate/instructions.rs
Normal file
@@ -0,0 +1,216 @@
|
||||
use convert_case::{Case, Casing};
|
||||
use std::{collections::BTreeMap, path::PathBuf};
|
||||
|
||||
use crate::{generate::utils, stationpedia};
|
||||
|
||||
pub fn generate_instructions(
|
||||
stationpedia: &stationpedia::Stationpedia,
|
||||
workspace: &std::path::Path,
|
||||
) -> color_eyre::Result<Vec<PathBuf>> {
|
||||
let instructions_path = workspace
|
||||
.join("ic10emu")
|
||||
.join("src")
|
||||
.join("vm")
|
||||
.join("instructions");
|
||||
if !instructions_path.exists() {
|
||||
std::fs::create_dir(&instructions_path)?;
|
||||
}
|
||||
let mut writer =
|
||||
std::io::BufWriter::new(std::fs::File::create(instructions_path.join("enums.rs"))?);
|
||||
write_instructions_enum(&mut writer, &stationpedia.script_commands)?;
|
||||
|
||||
let mut writer =
|
||||
std::io::BufWriter::new(std::fs::File::create(instructions_path.join("traits.rs"))?);
|
||||
|
||||
write_instruction_interface_trait(&mut writer)?;
|
||||
for (typ, info) in &stationpedia.script_commands {
|
||||
write_instruction_trait(&mut writer, (typ, info))?;
|
||||
}
|
||||
write_instruction_super_trait(&mut writer, &stationpedia.script_commands)?;
|
||||
|
||||
Ok(vec![
|
||||
instructions_path.join("enums.rs"),
|
||||
instructions_path.join("traits.rs"),
|
||||
])
|
||||
}
|
||||
|
||||
fn write_instructions_enum<T: std::io::Write>(
|
||||
writer: &mut T,
|
||||
instructions: &BTreeMap<String, stationpedia::Command>,
|
||||
) -> color_eyre::Result<()> {
|
||||
println!("Writing instruction Listings ...");
|
||||
|
||||
let mut instructions = instructions.clone();
|
||||
for (_, ref mut info) in instructions.iter_mut() {
|
||||
info.example = utils::strip_color(&info.example);
|
||||
}
|
||||
|
||||
write!(
|
||||
writer,
|
||||
"use serde::{{Deserialize, Serialize}};\n\
|
||||
use strum::{{\n \
|
||||
Display, EnumIter, EnumProperty, EnumString, FromRepr,\n\
|
||||
}};\n
|
||||
use crate::vm::object::traits::Programmable;\n\
|
||||
use crate::vm::instructions::traits::*;\n\
|
||||
"
|
||||
)?;
|
||||
|
||||
write!(
|
||||
writer,
|
||||
"#[derive(Debug, Display, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Serialize, Deserialize)]\n\
|
||||
#[derive(EnumIter, EnumString, EnumProperty, FromRepr)]\n\
|
||||
#[strum(use_phf, serialize_all = \"lowercase\")]\n\
|
||||
#[serde(rename_all = \"lowercase\")]\n\
|
||||
pub enum InstructionOp {{\n\
|
||||
"
|
||||
)?;
|
||||
writeln!(writer, " Nop,")?;
|
||||
for (name, info) in &instructions {
|
||||
let props_str = format!(
|
||||
"props( example = \"{}\", desc = \"{}\", operands = \"{}\" )",
|
||||
&info.example,
|
||||
&info.desc,
|
||||
count_operands(&info.example)
|
||||
);
|
||||
writeln!(
|
||||
writer,
|
||||
" #[strum({props_str})] {},",
|
||||
name.to_case(Case::Pascal)
|
||||
)?;
|
||||
}
|
||||
writeln!(writer, "}}")?;
|
||||
|
||||
write!(
|
||||
writer,
|
||||
"impl InstructionOp {{\n \
|
||||
pub fn num_operands(&self) -> usize {{\n \
|
||||
self.get_str(\"operands\").expect(\"instruction without operand property\").parse::<usize>().expect(\"invalid instruction operand property\")\n \
|
||||
}}\n\
|
||||
\n \
|
||||
pub fn execute<T>(\n \
|
||||
&self,\n \
|
||||
ic: &mut T,\n \
|
||||
vm: &crate::vm::VM,\n \
|
||||
operands: &[crate::vm::instructions::operands::Operand],\n \
|
||||
) -> Result<(), crate::errors::ICError>\n \
|
||||
where\n \
|
||||
T: Programmable,\n\
|
||||
{{\n \
|
||||
let num_operands = self.num_operands();\n \
|
||||
if operands.len() != num_operands {{\n \
|
||||
return Err(crate::errors::ICError::mismatch_operands(operands.len(), num_operands as u32));\n \
|
||||
}}\n \
|
||||
match self {{\n \
|
||||
Self::Nop => Ok(()),\n \
|
||||
"
|
||||
)?;
|
||||
|
||||
for (name, info) in instructions {
|
||||
let num_operands = count_operands(&info.example);
|
||||
let operands = (0..num_operands)
|
||||
.map(|i| format!("&operands[{}]", i))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
let trait_name = name.to_case(Case::Pascal);
|
||||
writeln!(
|
||||
writer,
|
||||
" Self::{trait_name} => ic.execute_{name}(vm, {operands}),",
|
||||
)?;
|
||||
}
|
||||
|
||||
write!(
|
||||
writer,
|
||||
" }}\
|
||||
}}\n\
|
||||
}}
|
||||
"
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_instruction_trait<T: std::io::Write>(
|
||||
writer: &mut T,
|
||||
instruction: (&str, &stationpedia::Command),
|
||||
) -> color_eyre::Result<()> {
|
||||
let (name, info) = instruction;
|
||||
let trait_name = format!("{}Instruction", name.to_case(Case::Pascal));
|
||||
let operands = operand_names(&info.example)
|
||||
.iter()
|
||||
.map(|name| {
|
||||
format!(
|
||||
"{}: &crate::vm::instructions::operands::Operand",
|
||||
name.to_case(Case::Snake)
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
let example = utils::strip_color(&info.example);
|
||||
write!(
|
||||
writer,
|
||||
"pub trait {trait_name}: IntegratedCircuit {{\n \
|
||||
/// {example} \n \
|
||||
fn execute_{name}(&mut self, vm: &crate::vm::VM, {operands}) -> Result<(), crate::errors::ICError>;\n\
|
||||
}}"
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn count_operands(example: &str) -> usize {
|
||||
example.split(' ').count() - 1
|
||||
}
|
||||
|
||||
fn operand_names(example: &str) -> Vec<String> {
|
||||
utils::strip_color(example)
|
||||
.split(' ')
|
||||
.skip(1)
|
||||
.map(|name| name.split(['?', '(']).next().unwrap().to_string())
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn write_instruction_interface_trait<T: std::io::Write>(writer: &mut T) -> color_eyre::Result<()> {
|
||||
write!(
|
||||
writer,
|
||||
"\
|
||||
use std::collections::BTreeMap;\n\
|
||||
use crate::vm::object::traits::{{Logicable, MemoryWritable, SourceCode}};\n\
|
||||
use crate::errors::ICError; \n\
|
||||
pub trait IntegratedCircuit: Logicable + MemoryWritable + SourceCode {{\n \
|
||||
fn get_instruciton_pointer(&self) -> usize;\n \
|
||||
fn set_next_instruction(&mut self, next_instruction: usize);\n \
|
||||
fn reset(&mut self);\n \
|
||||
fn get_real_target(&self, indirection: u32, target: u32) -> Result<f64, ICError>;\n \
|
||||
fn get_register(&self, indirection: u32, target: u32) -> Result<f64, ICError>;\n \
|
||||
fn set_register(&mut self, indirection: u32, target: u32, val: f64) -> Result<f64, ICError>;\n \
|
||||
fn set_return_address(&mut self, addr: f64);\n \
|
||||
fn push_stack(&mut self, val: f64) -> Result<f64, ICError>;\n \
|
||||
fn pop_stack(&mut self) -> Result<f64, ICError>;\n \
|
||||
fn peek_stack(&self) -> Result<f64, ICError>;\n \
|
||||
fn get_aliases(&self) -> &BTreeMap<String, crate::vm::instructions::operands::Operand>;\n \
|
||||
fn get_defines(&self) -> &BTreeMap<String, f64>;\n \
|
||||
fn get_lables(&self) -> &BTreeMap<String, u32>;\n\
|
||||
}}\n\
|
||||
"
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_instruction_super_trait<T: std::io::Write>(
|
||||
writer: &mut T,
|
||||
instructions: &BTreeMap<String, stationpedia::Command>,
|
||||
) -> color_eyre::Result<()> {
|
||||
let traits = instructions
|
||||
.keys()
|
||||
.map(|name| format!("{}Instruction", name.to_case(Case::Pascal)))
|
||||
.collect::<Vec<_>>()
|
||||
.join(" + ");
|
||||
write!(
|
||||
writer,
|
||||
"\
|
||||
pub trait ICInstructable: {traits} {{}}\n\
|
||||
impl <T> ICInstructable for T where T: {traits} {{}}
|
||||
"
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
60
xtask/src/generate/utils.rs
Normal file
60
xtask/src/generate/utils.rs
Normal file
@@ -0,0 +1,60 @@
|
||||
use onig::{Captures, Regex, RegexOptions, Syntax};
|
||||
|
||||
pub fn strip_color(s: &str) -> String {
|
||||
let color_regex = Regex::with_options(
|
||||
r#"<color=(#?\w+)>((:?(?!<color=(?:#?\w+)>).)+?)</color>"#,
|
||||
RegexOptions::REGEX_OPTION_MULTILINE | RegexOptions::REGEX_OPTION_CAPTURE_GROUP,
|
||||
Syntax::default(),
|
||||
)
|
||||
.unwrap();
|
||||
let mut new = s.to_owned();
|
||||
loop {
|
||||
new = color_regex.replace_all(&new, |caps: &Captures| caps.at(2).unwrap_or("").to_string());
|
||||
if !color_regex.is_match(&new) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
new
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn color_to_heml(s: &str) -> String {
|
||||
let color_regex = Regex::with_options(
|
||||
r#"<color=(#?\w+)>((:?(?!<color=(?:#?\w+)>).)+?)</color>"#,
|
||||
RegexOptions::REGEX_OPTION_MULTILINE | RegexOptions::REGEX_OPTION_CAPTURE_GROUP,
|
||||
Syntax::default(),
|
||||
)
|
||||
.unwrap();
|
||||
let mut new = s.to_owned();
|
||||
loop {
|
||||
new = color_regex.replace_all(&new, |caps: &Captures| {
|
||||
format!(
|
||||
r#"<div style="color: {};">{}</div>"#,
|
||||
caps.at(1).unwrap_or(""),
|
||||
caps.at(2).unwrap_or("")
|
||||
)
|
||||
});
|
||||
if !color_regex.is_match(&new) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
new
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn strip_link(s: &str) -> String {
|
||||
let link_regex = Regex::with_options(
|
||||
r#"<link=(\w+)>(.+?)</link>"#,
|
||||
RegexOptions::REGEX_OPTION_MULTILINE | RegexOptions::REGEX_OPTION_CAPTURE_GROUP,
|
||||
Syntax::default(),
|
||||
)
|
||||
.unwrap();
|
||||
let mut new = s.to_owned();
|
||||
loop {
|
||||
new = link_regex.replace_all(&new, |caps: &Captures| caps.at(2).unwrap_or("").to_string());
|
||||
if !link_regex.is_match(&new) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
new
|
||||
}
|
||||
@@ -2,6 +2,10 @@ use std::process::{Command, ExitStatus};
|
||||
|
||||
use clap::{Parser, Subcommand};
|
||||
|
||||
mod enums;
|
||||
mod generate;
|
||||
mod stationpedia;
|
||||
|
||||
/// Helper program to start ic10emu and website.
|
||||
///
|
||||
/// Can be invoked as `cargo xtask <command>`
|
||||
@@ -44,11 +48,19 @@ enum Task {
|
||||
Deploy {},
|
||||
/// bump the cargo.toml and package,json versions
|
||||
Version {
|
||||
#[arg(last = true, default_value = "patch", value_parser = clap::builder::PossibleValuesParser::new(VALID_VERSION_TYPE))]
|
||||
#[arg(default_value = "patch", value_parser = clap::builder::PossibleValuesParser::new(VALID_VERSION_TYPE))]
|
||||
version: String,
|
||||
},
|
||||
/// update changelog
|
||||
Changelog {},
|
||||
Generate {
|
||||
#[arg()]
|
||||
/// Path to Stationeers installation. Used to locate "Stationpedia.json" and "Enums.json"
|
||||
/// generated by https://github.com/Ryex/StationeersStationpediaExtractor
|
||||
/// Otherwise looks for both files in `<workspace>` or `<workspace>/data`.
|
||||
/// Can also point directly at a folder containing the two files.
|
||||
path: Option<std::ffi::OsString>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(thiserror::Error)]
|
||||
@@ -57,6 +69,8 @@ enum Error {
|
||||
BuildFailed(String, String, std::process::ExitStatus),
|
||||
#[error("failed to run command `{0}`")]
|
||||
Command(String, #[source] std::io::Error),
|
||||
#[error("can not find `Stationpedia.json` and/or `Enums.json` at `{0}`")]
|
||||
BadStationeresPath(std::path::PathBuf),
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Error {
|
||||
@@ -75,7 +89,8 @@ impl std::fmt::Debug for Error {
|
||||
}
|
||||
|
||||
const VERSION: Option<&str> = option_env!("CARGO_PKG_VERSION");
|
||||
fn main() -> Result<(), Error> {
|
||||
fn main() -> color_eyre::Result<()> {
|
||||
color_eyre::install()?;
|
||||
let args = Args::parse();
|
||||
let workspace = {
|
||||
let out = Command::new("cargo")
|
||||
@@ -132,21 +147,78 @@ fn main() -> Result<(), Error> {
|
||||
cmd.args(["version", &version]).status().map_err(|e| {
|
||||
Error::Command(format!("{}", cmd.get_program().to_string_lossy()), e)
|
||||
})?;
|
||||
},
|
||||
Task::Changelog { } => {
|
||||
}
|
||||
Task::Changelog {} => {
|
||||
let mut cmd = Command::new("git-changelog");
|
||||
cmd.current_dir(&workspace);
|
||||
cmd.args([
|
||||
"-io", "CHANGELOG.md",
|
||||
"-t", "path:CHANGELOG.md.jinja",
|
||||
"-c", "conventional",
|
||||
"--bump", VERSION.unwrap_or("auto"),
|
||||
"-io",
|
||||
"CHANGELOG.md",
|
||||
"-t",
|
||||
"path:CHANGELOG.md.jinja",
|
||||
"-c",
|
||||
"conventional",
|
||||
"--bump",
|
||||
VERSION.unwrap_or("auto"),
|
||||
"--parse-refs",
|
||||
"--trailers"
|
||||
]).status().map_err(|e| {
|
||||
Error::Command(format!("{}", cmd.get_program().to_string_lossy()), e)
|
||||
})?;
|
||||
},
|
||||
"--trailers",
|
||||
])
|
||||
.status()
|
||||
.map_err(|e| Error::Command(format!("{}", cmd.get_program().to_string_lossy()), e))?;
|
||||
}
|
||||
Task::Generate { path } => {
|
||||
let path = match path {
|
||||
Some(path) => {
|
||||
let mut path = std::path::PathBuf::from(path);
|
||||
if path.exists()
|
||||
&& path
|
||||
.parent()
|
||||
.and_then(|p| p.file_name())
|
||||
.is_some_and(|p| p == "Stationeers")
|
||||
&& path.file_name().is_some_and(|name| {
|
||||
(std::env::consts::OS == "windows" && name == "rocketstation.exe")
|
||||
|| (name == "rocketstation")
|
||||
|| (name == "rocketstation_Data")
|
||||
})
|
||||
{
|
||||
path = path.parent().unwrap().to_path_buf();
|
||||
}
|
||||
if path.is_dir()
|
||||
&& path.file_name().is_some_and(|name| name == "Stationeers")
|
||||
&& path.join("Stationpedia").join("Stationpedia.json").exists()
|
||||
{
|
||||
path = path.join("Stationpedia");
|
||||
}
|
||||
if path.is_file()
|
||||
&& path
|
||||
.file_name()
|
||||
.is_some_and(|name| name == "Stationpedia.json")
|
||||
{
|
||||
path = path.parent().unwrap().to_path_buf();
|
||||
}
|
||||
path
|
||||
}
|
||||
None => {
|
||||
let mut path = workspace.clone();
|
||||
if path.join("data").join("Stationpedia.json").exists()
|
||||
&& path.join("data").join("Enums.json").exists()
|
||||
{
|
||||
path = path.join("data")
|
||||
}
|
||||
|
||||
path
|
||||
}
|
||||
};
|
||||
|
||||
if path.is_dir()
|
||||
&& path.join("Stationpedia.json").exists()
|
||||
&& path.join("Enums.json").exists()
|
||||
{
|
||||
generate::generate(&path, &workspace)?;
|
||||
} else {
|
||||
return Err(Error::BadStationeresPath(path).into());
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
346
xtask/src/stationpedia.rs
Normal file
346
xtask/src/stationpedia.rs
Normal file
@@ -0,0 +1,346 @@
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use serde_with::{serde_as, DisplayFromStr};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename = "Stationpedia", deny_unknown_fields)]
|
||||
pub struct Stationpedia {
|
||||
pub pages: Vec<Page>,
|
||||
pub reagents: BTreeMap<String, Reagent>,
|
||||
#[serde(rename = "scriptCommands")]
|
||||
pub script_commands: BTreeMap<String, Command>,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl Stationpedia {
|
||||
pub fn lookup_prefab_name(&self, prefab_name: &'_ str) -> Option<&Page> {
|
||||
self.pages.iter().find(|p| p.prefab_name == prefab_name)
|
||||
}
|
||||
|
||||
pub fn lookup_key(&self, key: &str) -> Option<&Page> {
|
||||
self.pages.iter().find(|p| p.key == key)
|
||||
}
|
||||
|
||||
pub fn lookup_hash(&self, hash: i32) -> Option<&Page> {
|
||||
self.pages.iter().find(|p| p.prefab_hash == hash)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Reagent {
|
||||
#[serde(rename = "Hash")]
|
||||
pub hash: i64,
|
||||
#[serde(rename = "Unit")]
|
||||
pub unit: String,
|
||||
#[serde(rename = "Sources")]
|
||||
pub sources: Option<BTreeMap<String, f64>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Command {
|
||||
pub desc: String,
|
||||
pub example: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Page {
|
||||
#[serde(rename = "ConnectionInsert")]
|
||||
pub connection_insert: Vec<ConnectionInsert>,
|
||||
#[serde(rename = "ConstructedByKits")]
|
||||
pub constructs: Vec<Constructs>,
|
||||
#[serde(rename = "Description")]
|
||||
pub description: String,
|
||||
#[serde(rename = "Device")]
|
||||
pub device: Option<Device>,
|
||||
/// the item , if none then deprecated
|
||||
#[serde(rename = "Item")]
|
||||
pub item: Option<Item>,
|
||||
#[serde(rename = "Structure")]
|
||||
pub structure: Option<Structure>,
|
||||
#[serde(rename = "Key")]
|
||||
pub key: String,
|
||||
#[serde(rename = "LogicInfo")]
|
||||
pub logic_info: Option<LogicInfo>,
|
||||
#[serde(rename = "LogicInsert")]
|
||||
pub logic_insert: Vec<LogicInsert>,
|
||||
#[serde(rename = "LogicSlotInsert")]
|
||||
pub logic_slot_insert: Vec<LogicSlotInsert>,
|
||||
#[serde(rename = "Memory")]
|
||||
pub memory: Option<Memory>,
|
||||
#[serde(rename = "ModeInsert")]
|
||||
pub mode_insert: Vec<ModeInsert>,
|
||||
#[serde(rename = "PrefabHash")]
|
||||
pub prefab_hash: i32,
|
||||
#[serde(rename = "PrefabName")]
|
||||
pub prefab_name: String,
|
||||
#[serde(rename = "SlotInserts")]
|
||||
pub slot_inserts: Vec<SlotInsert>,
|
||||
#[serde(rename = "Title")]
|
||||
pub title: String,
|
||||
#[serde(rename = "TransmissionReceiver")]
|
||||
pub transmission_receiver: Option<bool>,
|
||||
#[serde(rename = "WirelessLogic")]
|
||||
pub wireless_logic: Option<bool>,
|
||||
#[serde(rename = "CircuitHolder")]
|
||||
pub circuit_holder: Option<bool>,
|
||||
#[serde(rename = "BasePowerDraw")]
|
||||
pub base_power_draw: Option<String>,
|
||||
#[serde(rename = "MaxPressure")]
|
||||
pub max_pressure: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Constructs {
|
||||
#[serde(rename = "NameOfThing")]
|
||||
pub name_of_thing: String,
|
||||
#[serde(rename = "PageLink")]
|
||||
pub page_link: String,
|
||||
#[serde(rename = "PrefabHash")]
|
||||
pub prefab_hash: i32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Structure {
|
||||
#[serde(rename = "SmallGrid")]
|
||||
pub small_grid: bool,
|
||||
#[serde(rename = "BuildStates")]
|
||||
pub build_states: BuildStates,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
|
||||
pub struct BuildStates(pub Vec<BuildState>);
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct BuildState {
|
||||
#[serde(rename = "Tool")]
|
||||
pub tool: Option<Vec<Tool>>,
|
||||
#[serde(rename = "ToolExit")]
|
||||
pub tool_exit: Option<Vec<Tool>>,
|
||||
#[serde(rename = "CanManufacture", default)]
|
||||
pub can_manufacture: bool,
|
||||
#[serde(rename = "MachineTier")]
|
||||
pub machine_tier: Option<MachineTier>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
|
||||
pub enum MachineTier {
|
||||
Undefined,
|
||||
TierOne,
|
||||
TierTwo,
|
||||
TierThree,
|
||||
Max,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Tool {
|
||||
#[serde(rename = "IsTool", default)]
|
||||
pub is_tool: bool,
|
||||
#[serde(rename = "PrefabName")]
|
||||
pub prefab_name: String,
|
||||
#[serde(rename = "Quantity")]
|
||||
pub quantity: Option<i64>,
|
||||
}
|
||||
|
||||
#[serde_as]
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct SlotInsert {
|
||||
#[serde(rename = "SlotIndex")]
|
||||
#[serde_as(as = "DisplayFromStr")]
|
||||
pub slot_index: u32,
|
||||
#[serde(rename = "SlotName")]
|
||||
pub slot_name: String,
|
||||
#[serde(rename = "SlotType")]
|
||||
pub slot_type: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct LogicInsert {
|
||||
#[serde(rename = "LogicAccessTypes")]
|
||||
pub logic_access_types: String,
|
||||
#[serde(rename = "LogicName")]
|
||||
pub logic_name: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct LogicSlotInsert {
|
||||
#[serde(rename = "LogicAccessTypes")]
|
||||
pub logic_access_types: String,
|
||||
#[serde(rename = "LogicName")]
|
||||
pub logic_name: String,
|
||||
}
|
||||
|
||||
#[serde_as]
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct ModeInsert {
|
||||
#[serde(rename = "LogicAccessTypes")]
|
||||
#[serde_as(as = "DisplayFromStr")]
|
||||
pub logic_access_types: u32,
|
||||
#[serde(rename = "LogicName")]
|
||||
pub logic_name: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct ConnectionInsert {
|
||||
#[serde(rename = "LogicAccessTypes")]
|
||||
pub logic_access_types: String,
|
||||
#[serde(rename = "LogicName")]
|
||||
pub logic_name: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct LogicInfo {
|
||||
#[serde(rename = "LogicSlotTypes")]
|
||||
pub logic_slot_types: BTreeMap<u32, LogicSlotTypes>,
|
||||
#[serde(rename = "LogicTypes")]
|
||||
pub logic_types: LogicTypes,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
|
||||
pub struct LogicSlotTypes {
|
||||
#[serde(flatten)]
|
||||
pub slot_types: BTreeMap<String, String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
|
||||
pub struct LogicTypes {
|
||||
#[serde(flatten)]
|
||||
pub types: BTreeMap<String, String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Memory {
|
||||
#[serde(rename = "Instructions")]
|
||||
pub instructions: Option<BTreeMap<String, Instruction>>,
|
||||
#[serde(rename = "MemoryAccess")]
|
||||
pub memory_access: String,
|
||||
#[serde(rename = "MemorySize")]
|
||||
pub memory_size: i64,
|
||||
#[serde(rename = "MemorySizeReadable")]
|
||||
pub memory_size_readable: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Instruction {
|
||||
#[serde(rename = "Description")]
|
||||
pub description: String,
|
||||
#[serde(rename = "Type")]
|
||||
pub type_: String,
|
||||
#[serde(rename = "Value")]
|
||||
pub value: i64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Item {
|
||||
#[serde(rename = "Consumable")]
|
||||
pub consumable: Option<bool>,
|
||||
#[serde(rename = "FilterType")]
|
||||
pub filter_type: Option<String>,
|
||||
#[serde(rename = "Ingredient")]
|
||||
pub ingredient: Option<bool>,
|
||||
#[serde(rename = "MaxQuantity")]
|
||||
pub max_quantity: Option<f64>,
|
||||
#[serde(rename = "Reagents")]
|
||||
pub reagents: Option<indexmap::IndexMap<String, f64>>,
|
||||
#[serde(rename = "SlotClass")]
|
||||
pub slot_class: String,
|
||||
#[serde(rename = "SortingClass")]
|
||||
pub sorting_class: String,
|
||||
#[serde(rename = "Recipes", default)]
|
||||
pub recipes: Vec<Recipe>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Recipe {
|
||||
#[serde(rename = "CreatorPrefabName")]
|
||||
pub creator_prefab_name: String,
|
||||
#[serde(rename = "TierName")]
|
||||
pub tier_name: String,
|
||||
#[serde(rename = "Time")]
|
||||
pub time: f64,
|
||||
#[serde(rename = "Energy")]
|
||||
pub energy: f64,
|
||||
#[serde(rename = "Temperature")]
|
||||
pub temperature: RecipeTemperature,
|
||||
#[serde(rename = "Pressure")]
|
||||
pub pressure: RecipePressure,
|
||||
#[serde(rename = "RequiredMix")]
|
||||
pub required_mix: RecipeGasMix,
|
||||
#[serde(rename = "CountTypes")]
|
||||
pub count_types: i64,
|
||||
#[serde(flatten)]
|
||||
pub reagents: indexmap::IndexMap<String, f64>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct RecipeTemperature {
|
||||
#[serde(rename = "Start")]
|
||||
pub start: f64,
|
||||
#[serde(rename = "Stop")]
|
||||
pub stop: f64,
|
||||
#[serde(rename = "IsValid")]
|
||||
pub is_valid: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct RecipePressure {
|
||||
#[serde(rename = "Start")]
|
||||
pub start: f64,
|
||||
#[serde(rename = "Stop")]
|
||||
pub stop: f64,
|
||||
#[serde(rename = "IsValid")]
|
||||
pub is_valid: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||
pub struct RecipeGasMix {
|
||||
#[serde(rename = "Rule")]
|
||||
pub rule: i64,
|
||||
#[serde(rename = "IsAny")]
|
||||
pub is_any: bool,
|
||||
#[serde(rename = "IsAnyToRemove")]
|
||||
pub is_any_to_remove: bool,
|
||||
#[serde(flatten)]
|
||||
pub reagents: BTreeMap<String, f64>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
|
||||
pub struct Device {
|
||||
#[serde(rename = "ConnectionList")]
|
||||
pub connection_list: Vec<(String, String)>,
|
||||
#[serde(rename = "DevicesLength")]
|
||||
pub devices_length: Option<i64>,
|
||||
#[serde(rename = "HasActivateState")]
|
||||
pub has_activate_state: bool,
|
||||
#[serde(rename = "HasAtmosphere")]
|
||||
pub has_atmosphere: bool,
|
||||
#[serde(rename = "HasColorState")]
|
||||
pub has_color_state: bool,
|
||||
#[serde(rename = "HasLockState")]
|
||||
pub has_lock_state: bool,
|
||||
#[serde(rename = "HasModeState")]
|
||||
pub has_mode_state: bool,
|
||||
#[serde(rename = "HasOnOffState")]
|
||||
pub has_on_off_state: bool,
|
||||
#[serde(rename = "HasOpenState")]
|
||||
pub has_open_state: bool,
|
||||
#[serde(rename = "HasReagents")]
|
||||
pub has_reagents: bool,
|
||||
}
|
||||
Reference in New Issue
Block a user