use crate::{GdtfError, GdtfResult};
use std::fmt::{Debug, Formatter};
use std::io::{Read, Seek};
use zip::read::ZipFile;
use zip::result::{ZipError, ZipResult};
use zip::ZipArchive;
pub struct ResourceMap {
archive: Box<dyn AnyZipArchive>,
}
impl ResourceMap {
pub(crate) fn new<A>(archive: A) -> Self
where
A: AnyZipArchive,
{
ResourceMap {
archive: Box::new(archive),
}
}
pub fn read_resource(&mut self, path: &str) -> GdtfResult<Resource> {
match self.archive.by_name(path) {
Ok(file) => Ok(Resource::new(file)),
Err(ZipError::FileNotFound) => Err(GdtfError::ResourceNotFound),
Err(err) => Err(err.into()),
}
}
pub fn read_ft_thumbnail(
&mut self,
name: &str,
format: FtThumbnailFormat,
) -> GdtfResult<Resource> {
self.read_resource(&format!("{name}.{}", format.extension()))
}
pub fn read_wheel_media(&mut self, name: &str) -> GdtfResult<Resource> {
self.read_resource(&format!("wheels/{name}.png"))
}
pub fn read_model_symbol(&mut self, name: &str, view: Model2View) -> GdtfResult<Resource> {
self.read_resource(&format!("models/{}/{name}.svg", view.folder()))
}
pub fn read_model_mesh(
&mut self,
name: &str,
format: Model3Format,
detail: Model3Detail,
) -> GdtfResult<Resource> {
self.read_resource(&format!(
"models/{}{}/{name}.{}",
format.folder(),
detail.folder_postfix(),
format.extension()
))
}
}
impl Debug for ResourceMap {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ResourceMap").finish()
}
}
pub(crate) trait AnyZipArchive: 'static {
fn by_name(&mut self, path: &str) -> ZipResult<ZipFile>;
}
impl<R> AnyZipArchive for ZipArchive<R>
where
R: Read + Seek + 'static,
{
fn by_name(&mut self, path: &str) -> ZipResult<ZipFile> {
self.by_name(path)
}
}
pub struct Resource<'a> {
archive_file: ZipFile<'a>,
}
impl<'a> Resource<'a> {
pub(crate) fn new(archive_file: ZipFile<'a>) -> Self {
Resource { archive_file }
}
pub fn size(&self) -> u64 {
self.archive_file.size()
}
}
impl<'a> Debug for Resource<'a> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Resource")
.field("archive_file", &self.archive_file.name())
.finish()
}
}
impl<'a> Read for Resource<'a> {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
self.archive_file.read(buf)
}
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> std::io::Result<usize> {
self.archive_file.read_to_end(buf)
}
fn read_to_string(&mut self, buf: &mut String) -> std::io::Result<usize> {
self.archive_file.read_to_string(buf)
}
fn read_exact(&mut self, buf: &mut [u8]) -> std::io::Result<()> {
self.archive_file.read_exact(buf)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FtThumbnailFormat {
Png,
Svg,
}
impl FtThumbnailFormat {
pub const fn extension(self) -> &'static str {
match self {
FtThumbnailFormat::Png => "png",
FtThumbnailFormat::Svg => "svg",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Model2View {
Top,
Front,
Side,
}
impl Model2View {
pub const fn folder(self) -> &'static str {
match self {
Model2View::Top => "svg",
Model2View::Front => "svg_front",
Model2View::Side => "svg_side",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Model3Format {
Gltf,
Max3ds,
}
impl Model3Format {
pub const fn extension(self) -> &'static str {
match self {
Model3Format::Gltf => "glb",
Model3Format::Max3ds => "3ds",
}
}
pub const fn folder(self) -> &'static str {
match self {
Model3Format::Gltf => "gltf",
Model3Format::Max3ds => "3ds",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Model3Detail {
Low,
Default,
High,
}
impl Model3Detail {
pub const fn folder_postfix(self) -> &'static str {
match self {
Model3Detail::Low => "_low",
Model3Detail::Default => "",
Model3Detail::High => "_high",
}
}
}