use std::{
cell::RefCell,
collections::BTreeMap,
io::{Read, Seek, Write},
rc::Rc,
};
use serde::{Deserialize, Serialize};
use crate::{Header, PropertyTagPartial, Result, StructType};
#[derive(Debug, Default, Clone)]
pub struct Types {
types: std::collections::HashMap<String, StructType>,
}
impl Types {
pub fn new() -> Self {
Self::default()
}
pub fn add(&mut self, path: String, t: StructType) {
self.types.insert(path, t);
}
}
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
pub struct PropertySchemas {
schemas: BTreeMap<String, PropertyTagPartial>,
}
impl PropertySchemas {
pub fn new() -> Self {
Self::default()
}
pub fn record(&mut self, path: String, tag: PropertyTagPartial) {
self.schemas.insert(path, tag);
}
pub fn get(&self, path: &str) -> Option<&PropertyTagPartial> {
self.schemas.get(path)
}
pub fn schemas(&self) -> &BTreeMap<String, PropertyTagPartial> {
&self.schemas
}
}
#[derive(Debug, Clone, Default)]
pub struct Scope {
components: Vec<String>,
}
impl Scope {
pub fn root() -> Self {
Self::default()
}
pub fn path(&self) -> String {
self.components.join(".")
}
pub fn push(&mut self, name: &str) {
self.components.push(name.to_string());
}
pub fn pop(&mut self) {
self.components.pop();
}
}
#[derive(Debug)]
pub(crate) struct SaveGameArchive<S> {
pub(crate) stream: S,
pub(crate) version: Option<Header>,
pub(crate) types: Rc<Types>,
pub(crate) scope: Scope,
pub(crate) log: bool,
pub(crate) error_to_raw: bool,
pub(crate) schemas: Rc<RefCell<PropertySchemas>>,
}
impl<R: Read> Read for SaveGameArchive<R> {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
self.stream.read(buf)
}
}
impl<S: Seek> Seek for SaveGameArchive<S> {
fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
self.stream.seek(pos)
}
}
impl<W: Write + Seek> Write for SaveGameArchive<W> {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.stream.write(buf)
}
fn flush(&mut self) -> std::io::Result<()> {
self.stream.flush()
}
}
impl<S> SaveGameArchive<S> {
pub(crate) fn run<F, T>(stream: S, f: F) -> T
where
F: FnOnce(&mut SaveGameArchive<S>) -> T,
{
f(&mut SaveGameArchive {
stream,
version: None,
types: Rc::new(Types::new()),
scope: Scope::root(),
log: false,
error_to_raw: false,
schemas: Rc::new(RefCell::new(PropertySchemas::new())),
})
}
fn path(&self) -> String {
self.scope.path()
}
fn get_type(&self) -> Option<&StructType> {
self.types.types.get(&self.path())
}
pub(crate) fn set_version(&mut self, version: Header) {
self.version = Some(version);
}
pub(crate) fn version(&self) -> &Header {
self.version.as_ref().expect("version info not set")
}
pub(crate) fn log(&self) -> bool {
self.log
}
pub(crate) fn error_to_raw(&self) -> bool {
self.error_to_raw
}
}
impl<R: Read + Seek> SaveGameArchive<R> {
pub(crate) fn get_type_or(&mut self, t: &StructType) -> Result<StructType> {
let offset = self.stream.stream_position()?;
Ok(self.get_type().cloned().unwrap_or_else(|| {
if self.log() {
eprintln!(
"offset {}: StructType for \"{}\" unspecified, assuming {:?}",
offset,
self.path(),
t
);
}
t.clone()
}))
}
}