<commit_before>use byteorder::*;
use std::env;
use std::fs::*;
use std::io::{self, Read, Write, BufReader, BufWriter};
use std::path::*;
use ordermap::OrderMap;

use crusader::*;
use formation::Formation;
use super::Node;

pub struct Cache {
    directory: PathBuf,
}

impl Cache {
    pub fn new(key: u64) -> Self {
        let digest = format!("{:x}", key);
        Cache {
            directory: env::temp_dir().join("cotli_helper").join(&digest),
        }
    }

    pub(super) fn write_to_cache(&self, formation: &Formation, node: &Node) {
        let path = self.path_to_cache_file(formation);
        self.try_write_to_cache(&path, node).unwrap();
    }

    pub(super) fn load_from_cache<'a>(
        &self,
        formation: &Formation<'a>,
        crusaders: &'a [Crusader],
    ) -> Option<Node<'a>> {
        let file_path = self.path_to_cache_file(formation);
        if !file_path.join("search_data.toml").exists() {
            return None;
        }
        match self.try_load_from_cache(&file_path, crusaders) {
            Ok(x) => Some(x),
            Err(e) => {
                println!("Could not load from cache. Deleting directory. Error: {}", e);
                remove_dir_all(file_path).unwrap();
                None
            }
        }
    }

    fn path_to_cache_file(&self, formation: &Formation) -> PathBuf {
        formation.placements().fold(self.directory.clone(), |path, (idx, crusader)| {
            let as_dir = format!("{}{:?}", idx, crusader.name);
            path.join(&as_dir)
        })
    }

    fn try_write_to_cache(&self, path: &Path, node: &Node) -> io::Result<()> {
        create_dir_all(path)?;
        let mut file = BufWriter::new(File::create(path.join("search_data.toml"))?);
        file.write_u8(1)?;
        write_node(node, &mut file)?;
        Ok(())
    }

    fn try_load_from_cache<'a>(
        &self,
        path: &Path,
        crusaders: &'a [Crusader],
    ) -> io::Result<Node<'a>> {
        let mut file = BufReader::new(File::open(path.join("search_data.toml"))?);
        let v = file.read_u8()?;
        if v == 1 {
            read_node(crusaders, &mut file)
        } else {
            Err(io::Error::new(io::ErrorKind::Other, "Invalid cache version"))
        }
    }
}

fn write_node<W: Write>(node: &Node, out: &mut W) -> io::Result<()> {
    out.write_f64::<NativeEndian>(node.highest_score_seen.into())?;
    out.write_f64::<NativeEndian>(node.total_score_seen.into())?;
    out.write_u32::<NativeEndian>(node.times_checked)?;
    out.write_u64::<NativeEndian>(node.children.len() as u64)?;<commit_msg>The cache does not use TOML<commit_after>use byteorder::*;
use std::env;
use std::fs::*;
use std::io::{self, Read, Write, BufReader, BufWriter};
use std::path::*;
use ordermap::OrderMap;

use crusader::*;
use formation::Formation;
use super::Node;

pub struct Cache {
    directory: PathBuf,
}

impl Cache {
    pub fn new(key: u64) -> Self {
        let digest = format!("{:x}", key);
        Cache {
            directory: env::temp_dir().join("cotli_helper").join(&digest),
        }
    }

    pub(super) fn write_to_cache(&self, formation: &Formation, node: &Node) {
        let path = self.path_to_cache_file(formation);
        self.try_write_to_cache(&path, node).unwrap();
    }

    pub(super) fn load_from_cache<'a>(
        &self,
        formation: &Formation<'a>,
        crusaders: &'a [Crusader],
    ) -> Option<Node<'a>> {
        let file_path = self.path_to_cache_file(formation);
        if !file_path.join("search_data.dat").exists() {
            return None;
        }
        match self.try_load_from_cache(&file_path, crusaders) {
            Ok(x) => Some(x),
            Err(e) => {
                println!("Could not load from cache. Deleting directory. Error: {}", e);
                remove_dir_all(file_path).unwrap();
                None
            }
        }
    }

    fn path_to_cache_file(&self, formation: &Formation) -> PathBuf {
        formation.placements().fold(self.directory.clone(), |path, (idx, crusader)| {
            let as_dir = format!("{}{:?}", idx, crusader.name);
            path.join(&as_dir)
        })
    }

    fn try_write_to_cache(&self, path: &Path, node: &Node) -> io::Result<()> {
        create_dir_all(path)?;
        let mut file = BufWriter::new(File::create(path.join("search_data.dat"))?);
        file.write_u8(1)?;
        write_node(node, &mut file)?;
        Ok(())
    }

    fn try_load_from_cache<'a>(
        &self,
        path: &Path,
        crusaders: &'a [Crusader],
    ) -> io::Result<Node<'a>> {
        let mut file = BufReader::new(File::open(path.join("search_data.dat"))?);
        let v = file.read_u8()?;
        if v == 1 {
            read_node(crusaders, &mut file)
        } else {
            Err(io::Error::new(io::ErrorKind::Other, "Invalid cache version"))
        }
    }
}

fn write_node<W: Write>(node: &Node, out: &mut W) -> io::Result<()> {
    out.write_f64::<NativeEndian>(node.highest_score_seen.into())?;
    out.write_f64::<NativeEndian>(node.total_score_seen.into())?;
    out.write_u32::<NativeEndian>(node.times_checked)?;
    out.write_u64::<NativeEndian>(node.children.len() as u64)?;
{'fraction_non_alphanumeric': 0.17195614541257934, 'fraction_numerical': 0.006155029813425659, 'mean_word_length': 2.6161335187760777, 'pattern_counts': {'":': 0, '<': 31, '<?xml version=': 0, '>': 47, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '98400', 'n_tokens_mistral': 1786, 'n_tokens_neox': 1777, 'n_words': 324}