pub use yog_ids::yog::{MACHINE_PREFIX, PLAYER_INVENTORY, SLOT_PREFIX};
const ACCEPTS: char = '=';
const ACCEPTS_SEP: &str = "|";
const PART_SEP: char = ';';
const PLACE_SEP: &str = ",";
const PLACE_INDEX: char = '/';
const PLACE_INV: char = '=';
const MACHINE_PARTS: usize = 3;
pub fn slot_id(index: usize, accepts: &[&str]) -> String {
match accepts {
[] => format!("{SLOT_PREFIX}{index}"),
_ => format!("{SLOT_PREFIX}{index}{ACCEPTS}{}", accepts.join(ACCEPTS_SEP)),
}
}
pub fn read_slot(id: &str) -> Option<(usize, Vec<String>)> {
let rest = id.strip_prefix(SLOT_PREFIX)?;
let (index, accepts) = rest.split_once(ACCEPTS).unwrap_or((rest, ""));
let accepts = accepts
.split(ACCEPTS_SEP)
.filter(|a| !a.is_empty())
.map(str::to_owned)
.collect();
Some((index.parse().ok()?, accepts))
}
#[derive(Debug, Clone, PartialEq)]
pub struct MachineId {
pub kind: String,
pub places: Vec<(String, usize, usize)>,
pub result: usize,
}
impl MachineId {
pub fn write(kind: &str, places: &[(&str, usize, usize)], result: usize) -> String {
let places: Vec<String> = places
.iter()
.map(|(s, i, inv)| format!("{s}{PLACE_INDEX}{i}{PLACE_INV}{inv}"))
.collect();
format!(
"{MACHINE_PREFIX}{kind}{PART_SEP}{result}{PART_SEP}{}",
places.join(PLACE_SEP)
)
}
pub fn read(id: &str) -> Option<MachineId> {
let mut parts = id
.strip_prefix(MACHINE_PREFIX)?
.splitn(MACHINE_PARTS, PART_SEP);
let kind = parts.next()?.to_string();
let result = parts.next()?.parse().ok()?;
let places = parts
.next()
.unwrap_or_default()
.split(PLACE_SEP)
.filter(|p| !p.is_empty())
.filter_map(|p| {
let (slot, rest) = p.split_once(PLACE_INDEX)?;
let (index, inv) = rest.split_once(PLACE_INV)?;
Some((slot.to_string(), index.parse().ok()?, inv.parse().ok()?))
})
.collect();
Some(MachineId {
kind,
places,
result,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ids_read_back_as_written() {
assert_eq!(read_slot(&slot_id(3, &[])), Some((3, vec![])));
assert_eq!(
read_slot(&slot_id(4, &["a:b", "#c:d"])),
Some((4, vec!["a:b".into(), "#c:d".into()]))
);
let id = MachineId::write("m:k", &[("in", 0, 1), ("in", 1, 2)], 5);
assert_eq!(
MachineId::read(&id),
Some(MachineId {
kind: "m:k".into(),
places: vec![("in".into(), 0, 1), ("in".into(), 1, 2)],
result: 5
})
);
assert_eq!(read_slot("other"), None);
}
}