use std::fmt;
use tact_parser::download::DownloadManifest;
use tact_parser::encoding::EncodingFile;
use tact_parser::install::InstallManifest;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ContentKey(pub Vec<u8>);
impl ContentKey {
pub fn new(data: Vec<u8>) -> Self {
Self(data)
}
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
pub fn to_hex(&self) -> String {
hex::encode(&self.0)
}
}
impl fmt::Display for ContentKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "CKey:{}", self.to_hex())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct EncodingKey(pub Vec<u8>);
impl EncodingKey {
pub fn new(data: Vec<u8>) -> Self {
Self(data)
}
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
pub fn to_hex(&self) -> String {
hex::encode(&self.0)
}
}
impl fmt::Display for EncodingKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "EKey:{}", self.to_hex())
}
}
#[derive(Debug, Clone)]
pub struct InstallManifestEntry {
pub path: String,
pub content_key: ContentKey,
pub size: usize,
}
#[derive(Debug, Clone)]
pub struct DownloadManifestEntry {
pub generated_path: String,
pub encoding_key: EncodingKey,
pub compressed_size: usize,
pub priority: i32,
}
#[derive(Debug)]
pub enum TypedManifest {
Install(TypedInstallManifest),
Download(TypedDownloadManifest),
}
#[derive(Debug)]
pub struct TypedInstallManifest {
entries: Vec<InstallManifestEntry>,
}
#[derive(Debug)]
pub struct TypedDownloadManifest {
entries: Vec<DownloadManifestEntry>,
}
impl TypedInstallManifest {
pub fn from_raw(manifest: InstallManifest) -> Self {
let entries = manifest
.entries
.iter()
.map(|entry| InstallManifestEntry {
path: entry.path.clone(),
content_key: ContentKey::new(entry.ckey.clone()),
size: entry.size as usize,
})
.collect();
Self { entries }
}
pub fn entries(&self) -> &[InstallManifestEntry] {
&self.entries
}
pub fn entries_requiring_encoding_lookup(&self) -> Vec<&InstallManifestEntry> {
self.entries.iter().collect()
}
}
impl TypedDownloadManifest {
pub fn from_raw(manifest: DownloadManifest) -> Self {
let entries = manifest
.entries
.iter()
.enumerate()
.map(|(i, (ekey, entry))| DownloadManifestEntry {
generated_path: format!("data/{:08x}", i),
encoding_key: EncodingKey::new(ekey.clone()),
compressed_size: entry.compressed_size as usize,
priority: entry.priority as i32,
})
.collect();
Self { entries }
}
pub fn entries(&self) -> &[DownloadManifestEntry] {
&self.entries
}
pub fn entries_with_encoding_keys(&self) -> Vec<&DownloadManifestEntry> {
self.entries.iter().collect()
}
}
#[derive(Debug, Clone)]
pub struct DownloadableFile {
pub path: String,
pub encoding_key: EncodingKey,
pub content_key: Option<ContentKey>,
pub size: usize,
pub source: ManifestSource,
}
#[derive(Debug, Clone, Copy)]
pub enum ManifestSource {
InstallManifest,
DownloadManifest,
}
pub struct TypedKeyResolver<'a> {
encoding_file: &'a EncodingFile,
}
impl<'a> TypedKeyResolver<'a> {
pub fn new(encoding_file: &'a EncodingFile) -> Self {
Self { encoding_file }
}
pub fn resolve_install_entry(
&self,
entry: &InstallManifestEntry,
) -> Result<DownloadableFile, String> {
let encoding_entry = self
.encoding_file
.lookup_by_ckey(entry.content_key.as_bytes())
.ok_or_else(|| format!("No encoding entry for {}", entry.content_key))?;
let encoding_key = encoding_entry
.encoding_keys
.first()
.ok_or_else(|| format!("No encoding keys for {}", entry.content_key))?;
Ok(DownloadableFile {
path: entry.path.clone(),
encoding_key: EncodingKey::new(encoding_key.clone()),
content_key: Some(entry.content_key.clone()),
size: self
.encoding_file
.get_file_size(entry.content_key.as_bytes())
.map(|s| s as usize)
.unwrap_or(entry.size),
source: ManifestSource::InstallManifest,
})
}
pub fn resolve_download_entry(
&self,
entry: &DownloadManifestEntry,
) -> Result<DownloadableFile, String> {
let content_key = self
.encoding_file
.lookup_by_ekey(entry.encoding_key.as_bytes())
.map(|ckey| ContentKey::new(ckey.clone()));
let size = content_key
.as_ref()
.and_then(|ckey| self.encoding_file.get_file_size(ckey.as_bytes()))
.map(|s| s as usize)
.unwrap_or(entry.compressed_size);
Ok(DownloadableFile {
path: entry.generated_path.clone(),
encoding_key: entry.encoding_key.clone(),
content_key,
size,
source: ManifestSource::DownloadManifest,
})
}
pub fn resolve_manifest(
&self,
manifest: &TypedManifest,
) -> Vec<Result<DownloadableFile, String>> {
match manifest {
TypedManifest::Install(m) => m
.entries()
.iter()
.map(|e| self.resolve_install_entry(e))
.collect(),
TypedManifest::Download(m) => m
.entries()
.iter()
.map(|e| self.resolve_download_entry(e))
.collect(),
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum InstallStrategy {
UseInstallManifest,
UseDownloadManifest,
Auto,
}
impl InstallStrategy {
pub fn select_manifest(self, install_type: InstallType) -> ManifestChoice {
match self {
Self::UseInstallManifest => ManifestChoice::Install,
Self::UseDownloadManifest => ManifestChoice::Download,
Self::Auto => {
match install_type {
InstallType::Minimal => ManifestChoice::Download, InstallType::Full => ManifestChoice::Install, InstallType::Custom(_) => ManifestChoice::Download, }
}
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum ManifestChoice {
Install,
Download,
}
#[derive(Debug, Clone, Copy)]
pub enum InstallType {
Minimal,
Full,
Custom(i32), }