1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
//! Serialization and deserialization of VMs
//!
//! Texlang VMs can be serialized using the standard serde infrastructure
//! because they always satisfy the [`::serde::Serialize`] trait.
//!
//! In the case when the VM's state type implements [`super::HasDefaultBuiltInCommands`],
//! the VM satisfies the [`::serde::Deserialize`] trait too.
//!
//! If the state type doesn't implement this trait, deserialization is slightly more complicated
//! because the set of built-in commands needs to be provided at deserialization time.
//! This is because the built-in commands which are regular Rust functions,
//! and it is not possible to fully serialize and deserialize Rust functions.
//! Deserialization of VMs is thus a two-step process:
//!
//! 1. Deserialize the bytes to a [`DeserializedVM`] type.
//!
//! 2. Invoke [`finish_deserialization`] with the deserialized VM
//! and a map of built-in commands in order to recover the regular Texlang VM.
//!
//! The Texlang VM has a [`deserialize_with_built_in_commands` convenience method](super::VM::deserialize_with_built_in_commands)
//! which performs both of these steps at once.
//! In the case when VM's state type implements [`super::HasDefaultBuiltInCommands`],
//! the VM's implementation of [`::serde::Deserialize`] handles all this automatically.
use crate::*;
use serde::{Deserialize, Deserializer, Serialize};
use std::collections::HashMap;
#[derive(Serialize)]
struct SerializableVM<'a, S> {
state: &'a S,
commands_map: &'a command::Map<S>,
internal: &'a vm::Internal<S>,
save_stack: Vec<variable::SerializableSaveStackElement<'a>>,
}
impl<'a, S> SerializableVM<'a, S> {
fn new(vm: &'a vm::VM<S>) -> Self {
let variable_key_to_built_in = vm.commands_map.getters_key_to_built_in();
Self {
state: &vm.state,
commands_map: &vm.commands_map,
internal: &vm.internal,
save_stack: vm
.internal
.save_stack
.iter()
.map(|element| element.serializable(&variable_key_to_built_in))
.collect(),
}
}
}
impl<State: serde::Serialize> Serialize for vm::VM<State> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let serializable_vm = SerializableVM::new(self);
serializable_vm.serialize(serializer)
}
}
/// A VM that has been deserialized.
///
/// In order to recover a regular [Texlang `VM`](super::VM) it is necessary to
/// call the [`finish_deserialization`] function with the relevant built-in commands.
#[derive(Deserialize)]
pub struct DeserializedVM<'a, S> {
state: S,
commands_map: command::map::SerializableMap<'a>,
internal: vm::Internal<S>,
save_stack: Vec<variable::SerializableSaveStackElement<'a>>,
}
/// Finish the deserialization of a VM.
///
/// This function accepts a [`DeserializedVM`] and a collection of built-in commands
/// and returns a regular [Texlang `VM`](super::VM).
pub fn finish_deserialization<S>(
#[allow(clippy::boxed_local)] mut deserialized: Box<DeserializedVM<'_, S>>,
built_in_commands: HashMap<&str, command::BuiltIn<S>>,
) -> vm::VM<S> {
let built_in_commands = built_in_commands
.into_iter()
.map(|(key, value)| {
let cs_name = deserialized.internal.cs_name_interner.get_or_intern(key);
(cs_name, value)
})
.collect();
deserialized.internal.save_stack = deserialized
.save_stack
.into_iter()
.map(|element| element.finish_deserialization(&built_in_commands))
.collect();
let commands_map = deserialized
.commands_map
.finish_deserialization(built_in_commands, &deserialized.internal.cs_name_interner);
vm::VM {
state: deserialized.state,
commands_map,
working_directory: match std::env::current_dir() {
Ok(path_buf) => Some(path_buf),
Err(err) => {
println!("failed to determine the working directory: {err}");
None
}
},
internal: deserialized.internal,
}
}
pub(super) fn deserialize<'de, D: Deserializer<'de>, S: serde::Deserialize<'de>>(
deserializer: D,
built_in_commands: HashMap<&str, command::BuiltIn<S>>,
) -> Result<vm::VM<S>, D::Error> {
let deserialized_vm: Box<DeserializedVM<S>> = Deserialize::deserialize(deserializer)?;
Ok(finish_deserialization(deserialized_vm, built_in_commands))
}