use std::collections::HashMap;
use std::fmt::Debug;
use std::iter;
use std::marker::PhantomData;
use std::mem;
use either::Either;
use log::{trace, warn};
use super::{Fragment, Transformation};
use crate::AnyError;
#[derive(Debug)]
pub struct IdGen(u128);
impl IdGen {
fn new() -> Self {
IdGen(1)
}
}
impl Default for IdGen {
fn default() -> Self {
Self::new()
}
}
impl Iterator for IdGen {
type Item = CacheId;
fn next(&mut self) -> Option<CacheId> {
let id = self.0;
self.0 = self
.0
.checked_add(1)
.expect("WTF? Run out of 128bit cache IDs!?");
Some(CacheId(id))
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct CacheId(u128);
impl CacheId {
pub fn dummy() -> Self {
CacheId(0)
}
}
pub enum Instruction<Resource> {
DropAll,
DropSpecific(CacheId),
Install {
id: CacheId,
resource: Resource,
},
}
impl<Resource> Instruction<Resource> {
fn replace(resource: Resource) -> Vec<Self> {
vec![
Instruction::DropAll,
Instruction::Install {
id: CacheId::dummy(),
resource,
},
]
}
}
pub trait Driver<F: Fragment> {
type SubFragment: Fragment;
fn instructions<T, I>(
&mut self,
fragment: &F,
transform: &mut T,
name: &'static str,
) -> Result<Vec<Instruction<T::OutputResource>>, Vec<AnyError>>
where
T: Transformation<<Self::SubFragment as Fragment>::Resource, I, Self::SubFragment>;
fn confirm(&mut self, name: &'static str);
fn abort(&mut self, name: &'static str);
fn maybe_cached(&self, frament: &F, name: &'static str) -> bool;
}
#[derive(Clone, Copy, Debug, Default)]
pub struct Trivial;
impl<F: Fragment> Driver<F> for Trivial {
type SubFragment = F;
fn instructions<T, I>(
&mut self,
fragment: &F,
transform: &mut T,
name: &'static str,
) -> Result<Vec<Instruction<T::OutputResource>>, Vec<AnyError>>
where
T: Transformation<F::Resource, I, F>,
{
trace!(
"Creating resource {}, generating a replace instruction for any possible previous",
name,
);
let resource = fragment
.create(name)
.and_then(|r| transform.transform(r, fragment, name))
.map_err(|e| vec![e])?;
Ok(Instruction::replace(resource))
}
fn confirm(&mut self, _name: &'static str) {}
fn abort(&mut self, _name: &'static str) {}
fn maybe_cached(&self, _: &F, _name: &'static str) -> bool {
false
}
}
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum Comparison {
Dissimilar,
Similar,
Same,
}
pub trait Comparable<RHS = Self> {
fn compare(&self, other: &RHS) -> Comparison;
}
#[derive(Debug)]
enum Proposition<F: Fragment + ToOwned> {
Nothing,
ReplaceFragment(F::Owned),
ReplaceBoth { fragment: F::Owned, seed: F::Seed },
}
impl<F: Fragment + ToOwned> Proposition<F> {
fn active(&self) -> bool {
!matches!(self, Proposition::Nothing)
}
}
#[derive(Debug)]
pub struct CacheSimilar<F: Fragment + ToOwned> {
previous: Option<F::Owned>,
seed: Option<F::Seed>,
proposition: Proposition<F>,
}
impl<F: Fragment + ToOwned> Default for CacheSimilar<F> {
fn default() -> Self {
CacheSimilar {
previous: None,
seed: None,
proposition: Proposition::Nothing,
}
}
}
impl<F> CacheSimilar<F>
where
F: Debug + Fragment + ToOwned + Comparable<<F as ToOwned>::Owned>,
{
fn compare(&self, fragment: &F) -> Comparison {
self.previous
.as_ref()
.map(|prev| fragment.compare(prev))
.unwrap_or(Comparison::Dissimilar)
}
}
impl<F> Driver<F> for CacheSimilar<F>
where
F: Debug + Fragment + ToOwned + Comparable<<F as ToOwned>::Owned>,
{
type SubFragment = F;
fn instructions<T, I>(
&mut self,
fragment: &F,
transform: &mut T,
name: &'static str,
) -> Result<Vec<Instruction<T::OutputResource>>, Vec<AnyError>>
where
T: Transformation<F::Resource, I, F>,
{
assert!(!self.proposition.active(), "Unclosed transaction");
match self.compare(fragment) {
Comparison::Dissimilar => {
trace!(
"Completely new config {:?} for {}, recreating from scratch",
fragment,
name
);
let mut new_seed = fragment.make_seed(name).map_err(|e| vec![e])?;
let resource = fragment
.make_resource(&mut new_seed, name)
.and_then(|r| transform.transform(r, fragment, name))
.map_err(|e| vec![e])?;
self.proposition = Proposition::ReplaceBoth {
seed: new_seed,
fragment: fragment.to_owned(),
};
Ok(Instruction::replace(resource))
}
Comparison::Similar => {
trace!(
"A similar config {:?} for {}, recreating from previous seed",
fragment,
name
);
let resource = fragment
.make_resource(self.seed.as_mut().expect("Missing previous seed"), name)
.and_then(|r| transform.transform(r, fragment, name))
.map_err(|e| vec![e])?;
self.proposition = Proposition::ReplaceFragment(fragment.to_owned());
Ok(Instruction::replace(resource))
}
Comparison::Same => {
trace!(
"The {} stays the same on {:?}, keeping previous resource",
name,
fragment
);
Ok(Vec::new())
}
}
}
fn abort(&mut self, name: &'static str) {
trace!("Aborting {}", name);
self.proposition = Proposition::Nothing;
}
fn confirm(&mut self, name: &'static str) {
trace!("Confirming {}", name);
let mut proposition = Proposition::Nothing;
mem::swap(&mut proposition, &mut self.proposition);
match proposition {
Proposition::Nothing => (),
Proposition::ReplaceFragment(f) => self.previous = Some(f),
Proposition::ReplaceBoth { fragment, seed } => {
self.seed = Some(seed);
self.previous = Some(fragment);
}
}
}
fn maybe_cached(&self, fragment: &F, _name: &'static str) -> bool {
self.compare(fragment) != Comparison::Dissimilar
}
}
#[derive(Debug)]
pub struct CacheEq<Fragment: ToOwned> {
previous: Option<Fragment::Owned>,
proposition: Option<Fragment::Owned>,
}
impl<F: ToOwned> Default for CacheEq<F> {
fn default() -> Self {
CacheEq {
previous: None,
proposition: None,
}
}
}
impl<F> Driver<F> for CacheEq<F>
where
F: Debug + Fragment + ToOwned + PartialEq<<F as ToOwned>::Owned>,
{
type SubFragment = F;
fn instructions<T, I>(
&mut self,
fragment: &F,
transform: &mut T,
name: &'static str,
) -> Result<Vec<Instruction<T::OutputResource>>, Vec<AnyError>>
where
T: Transformation<F::Resource, I, F>,
{
assert!(self.proposition.is_none(), "Unclosed transaction");
if self.maybe_cached(fragment, name) {
trace!(
"The {} stays the same on {:?}, keeping previous",
name,
fragment
);
Ok(Vec::new())
} else {
trace!("New config {:?} for {}, recreating", fragment, name);
self.proposition = Some(fragment.to_owned());
<Trivial as Driver<F>>::instructions(&mut Trivial, fragment, transform, name)
}
}
fn abort(&mut self, name: &'static str) {
trace!("Aborting {}", name);
self.proposition.take();
}
fn confirm(&mut self, name: &'static str) {
trace!("Confirming {}", name);
if let Some(proposition) = self.proposition.take() {
self.previous = Some(proposition);
}
}
fn maybe_cached(&self, fragment: &F, _name: &'static str) -> bool {
if let Some(prev) = self.previous.as_ref() {
fragment == prev
} else {
false
}
}
}
#[derive(Clone, Debug, Default)]
pub struct IdMapping {
mapping: HashMap<CacheId, CacheId>,
}
impl IdMapping {
pub fn translate<'a, R, I>(
&'a mut self,
id_gen: &'a mut IdGen,
instructions: I,
) -> impl Iterator<Item = Instruction<R>> + 'a
where
R: 'a,
I: IntoIterator<Item = Instruction<R>> + 'a,
{
instructions
.into_iter()
.flat_map(move |i| match i {
Instruction::DropAll => {
let mut mapping = HashMap::new();
mem::swap(&mut mapping, &mut self.mapping);
Either::Left(
mapping
.into_iter()
.map(|(_, outer_id)| Instruction::DropSpecific(outer_id)),
)
}
Instruction::DropSpecific(id) => {
let id = self
.mapping
.remove(&id)
.expect("Inconsistent use of cache: missing ID to remove");
Either::Right(iter::once(Instruction::DropSpecific(id)))
}
Instruction::Install { id, resource } => {
let new_id = id_gen.next().expect("Run out of cache IDs? Impossible");
assert!(
self.mapping.insert(id, new_id).is_none(),
"Duplicate ID created"
);
Either::Right(iter::once(Instruction::Install {
id: new_id,
resource,
}))
}
})
}
pub fn active_target_ids(&self) -> impl Iterator<Item = &CacheId> {
self.mapping.values()
}
}
#[derive(Debug, Default)]
struct ItemDriver<Driver> {
driver: Driver,
id_mapping: IdMapping,
proposed_mapping: Option<IdMapping>,
used: bool,
new: bool,
}
#[derive(Debug)]
pub struct SeqDriver<Item, SlaveDriver> {
id_gen: IdGen,
sub_drivers: Vec<ItemDriver<SlaveDriver>>,
transaction_open: bool,
_item: PhantomData<fn(&Item)>,
}
impl<Item, SlaveDriver> Default for SeqDriver<Item, SlaveDriver> {
fn default() -> Self {
Self {
id_gen: IdGen::new(),
sub_drivers: Vec::new(),
transaction_open: false,
_item: PhantomData,
}
}
}
impl<F, I, SlaveDriver> Driver<F> for SeqDriver<I, SlaveDriver>
where
F: Fragment,
I: Fragment,
for<'a> &'a F: IntoIterator<Item = &'a I>,
SlaveDriver: Driver<I> + Default,
{
type SubFragment = SlaveDriver::SubFragment;
fn instructions<T, Ins>(
&mut self,
fragment: &F,
transform: &mut T,
name: &'static str,
) -> Result<Vec<Instruction<T::OutputResource>>, Vec<AnyError>>
where
T: Transformation<<Self::SubFragment as Fragment>::Resource, Ins, Self::SubFragment>,
{
assert!(!self.transaction_open);
trace!("Updating sequence {}", name);
self.transaction_open = true;
let mut instructions = Vec::new();
let mut errors = Vec::new();
for sub in fragment {
let existing = self
.sub_drivers
.iter_mut()
.find(|d| !d.used && d.driver.maybe_cached(sub, name));
let slot = if let Some(existing) = existing {
trace!("Found existing version of instance in {}", name);
existing
} else {
trace!(
"Previous version of instance in {} not found, creating a new one",
name
);
self.sub_drivers.push(ItemDriver::default());
let slot = self.sub_drivers.last_mut().unwrap();
slot.new = true;
slot
};
slot.used = true;
match slot.driver.instructions(sub, transform, name) {
Ok(new_instructions) => {
let mapping = if slot.new {
&mut slot.id_mapping
} else {
slot.proposed_mapping = Some(slot.id_mapping.clone());
slot.proposed_mapping.as_mut().unwrap()
};
instructions.extend(mapping.translate(&mut self.id_gen, new_instructions));
}
Err(errs) => errors.extend(errs),
}
}
for slot in &self.sub_drivers {
if !slot.used {
instructions.extend(
slot.id_mapping
.active_target_ids()
.cloned()
.map(Instruction::DropSpecific),
);
}
}
if errors.is_empty() {
Ok(instructions)
} else {
self.abort(name);
Err(errors)
}
}
fn confirm(&mut self, name: &'static str) {
trace!("Confirming the whole sequence {}", name);
assert!(self.transaction_open);
self.transaction_open = false;
self.sub_drivers.retain(|s| s.used);
for sub in &mut self.sub_drivers {
sub.driver.confirm(name);
if let Some(mapping) = sub.proposed_mapping.take() {
sub.id_mapping = mapping;
}
sub.new = false;
sub.used = false;
}
}
fn abort(&mut self, name: &'static str) {
trace!("Aborting the whole sequence of {}", name);
assert!(self.transaction_open);
self.transaction_open = false;
self.sub_drivers.retain(|s| !s.new);
for sub in &mut self.sub_drivers {
if sub.used {
sub.driver.abort(name);
sub.proposed_mapping.take();
sub.used = false;
}
assert!(
sub.proposed_mapping.is_none(),
"Proposed mapping for something not used"
);
}
}
fn maybe_cached(&self, fragment: &F, name: &'static str) -> bool {
fragment.into_iter().any(|s| {
self.sub_drivers
.iter()
.any(|slave| slave.driver.maybe_cached(s, name))
})
}
}
pub struct OnceDriver<F: ToOwned> {
loaded: Option<F::Owned>,
initial: bool,
}
impl<F> Default for OnceDriver<F>
where
F: ToOwned,
{
fn default() -> Self {
OnceDriver {
loaded: None,
initial: true,
}
}
}
impl<F> Driver<F> for OnceDriver<F>
where
F: Fragment + PartialEq<<F as ToOwned>::Owned> + ToOwned + 'static,
{
type SubFragment = F;
fn instructions<T, I>(
&mut self,
fragment: &F,
transform: &mut T,
name: &'static str,
) -> Result<Vec<Instruction<T::OutputResource>>, Vec<AnyError>>
where
T: Transformation<<Self::SubFragment as Fragment>::Resource, I, Self::SubFragment>,
{
if let Some(loaded) = self.loaded.as_ref() {
if fragment == loaded {
warn!(
"{} changed in configuration, can't change at runtime, keeping previous",
name,
);
}
Ok(Vec::new())
} else {
assert!(self.initial);
trace!("Building {} for the first time", name);
self.loaded = Some(fragment.to_owned());
Trivial.instructions(fragment, transform, name)
}
}
fn confirm(&mut self, _name: &'static str) {
assert!(self.loaded.is_some(), "Confirm called before instructions");
self.initial = false;
}
fn abort(&mut self, _name: &'static str) {
if self.initial {
assert!(
self.loaded.take().is_some(),
"Abort called before instructions"
);
}
}
fn maybe_cached(&self, fragment: &F, _name: &'static str) -> bool {
self.loaded
.as_ref()
.map(|l| fragment == l)
.unwrap_or_default()
}
}
pub struct SilentOnceDriver {
attempted: bool,
initial: bool,
}
impl Default for SilentOnceDriver {
fn default() -> Self {
Self {
attempted: false,
initial: true,
}
}
}
impl<F> Driver<F> for SilentOnceDriver
where
F: Fragment,
{
type SubFragment = F;
fn instructions<T, I>(
&mut self,
fragment: &F,
transform: &mut T,
name: &'static str,
) -> Result<Vec<Instruction<T::OutputResource>>, Vec<AnyError>>
where
T: Transformation<<Self::SubFragment as Fragment>::Resource, I, Self::SubFragment>,
{
if self.initial {
trace!("Building {} for the first time", name);
assert!(!self.attempted);
self.attempted = true;
Trivial.instructions(fragment, transform, name)
} else {
Ok(Vec::new())
}
}
fn confirm(&mut self, _name: &'static str) {
assert!(self.attempted, "Confirm called before instructions");
self.initial = false;
}
fn abort(&mut self, _name: &'static str) {
}
fn maybe_cached(&self, _: &F, _name: &'static str) -> bool {
false
}
}
#[derive(Debug, Default)]
pub struct RefDriver<Inner>(Inner);
impl<Inner> RefDriver<Inner> {
pub fn new(inner: Inner) -> Self {
RefDriver(inner)
}
}
impl<'a, F: Fragment, Inner: Driver<F>> Driver<&'a F> for RefDriver<Inner> {
type SubFragment = Inner::SubFragment;
fn instructions<T, I>(
&mut self,
fragment: &&F,
transform: &mut T,
name: &'static str,
) -> Result<Vec<Instruction<T::OutputResource>>, Vec<AnyError>>
where
T: Transformation<<Self::SubFragment as Fragment>::Resource, I, Self::SubFragment>,
{
self.0.instructions(*fragment, transform, name)
}
fn confirm(&mut self, name: &'static str) {
self.0.confirm(name);
}
fn abort(&mut self, name: &'static str) {
self.0.abort(name);
}
fn maybe_cached(&self, fragment: &&F, name: &'static str) -> bool {
self.0.maybe_cached(*fragment, name)
}
}