use csv::ReaderBuilder;
use getset::*;
use serde_derive::Deserialize;
use std::fs::canonicalize;
use std::path::{Path, PathBuf};
use crate::error::{RLibError, Result};
use super::GameInfo;
const MANIFEST_FILE_NAME: &str = "manifest.txt";
#[derive(Deserialize)]
pub struct Manifest(pub Vec<ManifestEntry>);
#[derive(Default, Getters, Deserialize)]
#[getset(get = "pub")]
pub struct ManifestEntry {
relative_path: String,
size: u64,
belongs_to_base_game: Option<u8>,
}
impl Manifest {
pub fn read_from_game_path(game: &GameInfo, game_path: &Path) -> Result<Self> {
let manifest_path = game.data_path(game_path)?.join(MANIFEST_FILE_NAME);
Self::read(&manifest_path)
}
pub fn read(manifest_path: &Path) -> Result<Self> {
let mut reader = ReaderBuilder::new()
.delimiter(b'\t')
.quoting(false)
.has_headers(false)
.flexible(true)
.from_path(manifest_path)?;
let mut entries = vec![];
for record in reader.records() {
let record = record?;
if record.len() != 2 && record.len() != 3 {
return Err(RLibError::ManifestFileParseError("Mismatch column count".to_owned()));
} else {
let mut manifest_entry = ManifestEntry {
relative_path: record.get(0).ok_or_else(|| RLibError::ManifestFileParseError("Error reading relative path".to_owned()))?.to_owned(),
size: record.get(1).ok_or_else(|| RLibError::ManifestFileParseError("Error reading size".to_owned()))?.parse()?,
..Default::default()
};
if record.len() == 3 {
manifest_entry.belongs_to_base_game = record.get(2).ok_or_else(|| RLibError::ManifestFileParseError("Error reading if file belongs to the base game".to_owned()))?.parse().ok();
}
else {
manifest_entry.belongs_to_base_game = None;
}
entries.push(manifest_entry);
}
}
let manifest = Self(entries);
Ok(manifest)
}
pub fn is_path_in_manifest(&self, path: &Path) -> bool {
let insensitivized_path = path.to_str().unwrap().to_lowercase().replace('\\', "/");
self.0.iter().any(|x| insensitivized_path.ends_with(&x.relative_path.to_lowercase()))
}
}
impl ManifestEntry {
pub fn path_from_manifest_entry(&self, path: PathBuf) -> Option<PathBuf> {
match self.belongs_to_base_game() {
Some(value) => {
if *value == 1 || path.is_file() {
canonicalize(path).ok()
} else {
None
}
},
None => canonicalize(path).ok(),
}
}
}