use std::{
fs::File,
io::{Cursor, Read, Write},
path::{Path, PathBuf},
};
use ron::ser::PrettyConfig;
use serde::{Deserialize, Serialize};
use crate::{
error::{AddContext, InstallerError, InstallerErrorKind},
os::AccessScope,
path::AppPathPrefix,
};
use super::AppId;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum FileType {
Executable,
#[doc(hidden)]
Library,
#[doc(hidden)]
Configuration,
#[doc(hidden)]
Documentation,
Data,
}
impl Default for FileType {
fn default() -> Self {
Self::Data
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[non_exhaustive]
pub struct DiskFileEntry {
pub path: PathBuf,
pub len: u64,
pub crc32c: u32,
pub file_type: FileType,
pub is_main_executable: bool,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[non_exhaustive]
pub struct DiskDirEntry {
pub path: PathBuf,
pub preserve: bool,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[non_exhaustive]
pub struct DiskManifest {
#[serde(skip)]
pub manifest_path: PathBuf,
pub manifest_version: u64,
pub app_id: AppId,
pub app_name: String,
pub app_version: String,
pub access_scope: AccessScope,
pub app_paths: DiskPaths,
pub dirs: Vec<DiskDirEntry>,
pub files: Vec<DiskFileEntry>,
pub search_path: Option<PathBuf>,
#[cfg(any(windows, doc))]
pub app_path_exe_name: Option<String>,
#[cfg(any(unix, doc))]
pub shell_profile_path: Option<PathBuf>,
}
impl DiskManifest {
pub fn load<P: AsRef<Path>>(path: P) -> Result<Self, InstallerError> {
let path = path.as_ref();
let buf =
std::fs::read(path).with_contextc(|_error| format!("could not open file {path:?}"))?;
let mut manifest = Self::from_reader(Cursor::new(buf))?;
manifest.manifest_path = path.to_path_buf();
Ok(manifest)
}
pub fn from_reader<R: Read>(reader: R) -> Result<Self, InstallerError> {
let manifest = ron::de::from_reader::<R, Self>(reader).map_err(|error| {
InstallerError::new(InstallerErrorKind::MalformedDiskManifest).with_source(error)
})?;
Ok(manifest)
}
pub fn save<P: AsRef<Path>>(&self, path: P) -> Result<(), InstallerError> {
let file = File::options()
.write(true)
.create_new(true)
.truncate(true)
.open(path)?;
self.to_writer(file)?;
Ok(())
}
pub fn to_writer<W: Write>(&self, output: W) -> Result<(), InstallerError> {
let options = ron::Options::default();
options
.to_io_writer_pretty(output, &self, PrettyConfig::default())
.map_err(|error| InstallerError::new(InstallerErrorKind::Other).with_source(error))?;
Ok(())
}
pub fn total_file_size(&self) -> u64 {
self.files.iter().map(|entry| entry.len).sum()
}
pub fn main_executable(&self) -> Option<&DiskFileEntry> {
self.files.iter().find(|entry| entry.is_main_executable)
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[non_exhaustive]
pub struct DiskPaths {
pub prefix: AppPathPrefix,
pub executable: PathBuf,
#[doc(hidden)]
pub library: PathBuf,
#[doc(hidden)]
pub configuration: PathBuf,
#[doc(hidden)]
pub documentation: PathBuf,
pub data: PathBuf,
}