tinymist_package/
pack.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
//! A bundle that is modifiable

use core::fmt;
use std::fmt::Display;
use std::io::{self, Read};
use std::path::Path;
use std::sync::Arc;

use ecow::{eco_format, EcoVec};
use tinymist_std::{ImmutBytes, ImmutPath};
use typst::diag::{PackageError, PackageResult};
use typst::syntax::package::{PackageSpec, VersionlessPackageSpec};

#[cfg(feature = "fs-pack")]
mod fs;
#[cfg(feature = "gitcl-pack")]
mod gitcl;
#[cfg(feature = "http-pack")]
mod http;
mod memory;
mod ops;
#[cfg(feature = "release-pack")]
mod release;
mod tarball;
#[cfg(feature = "universe-pack")]
mod universe;

#[cfg(feature = "fs-pack")]
pub use fs::*;
#[cfg(feature = "gitcl-pack")]
pub use gitcl::*;
#[cfg(feature = "http-pack")]
pub use http::*;
pub use memory::*;
pub use ops::*;
#[cfg(feature = "release-pack")]
pub use release::*;
pub use tarball::*;
#[cfg(feature = "universe-pack")]
pub use universe::*;

/// The pack file is the knownn file type in the package.
pub enum PackFile<'a> {
    /// A single file in the memory.
    Data(io::Cursor<ImmutBytes>),
    /// A file in the package.
    Read(Box<dyn Read + 'a>),
}

impl io::Read for PackFile<'_> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        match self {
            PackFile::Data(data) => data.read(buf),
            PackFile::Read(reader) => reader.read(buf),
        }
    }
}

/// The pack file is the knownn file type in the package.
pub enum PackEntries<'a> {
    /// A single file in the memory.
    Data(EcoVec<ImmutPath>),
    /// A file in the package.
    Read(Box<dyn Iterator<Item = Path> + 'a>),
}

/// The pack trait is used for read/write files in a package.
pub trait PackFs: fmt::Debug {
    /// Read files from the package.
    fn read_all(
        &mut self,
        f: &mut (dyn FnMut(&str, PackFile) -> PackageResult<()> + Send + Sync),
    ) -> PackageResult<()>;
    /// Read a file from the package.
    fn read(&self, _path: &str) -> io::Result<PackFile> {
        Err(unsupported())
    }
    /// Read entries from the package.
    fn entries(&self) -> io::Result<PackEntries> {
        Err(unsupported())
    }
}

/// The specifier is used to identify a package.
pub enum PackSpecifier {
    /// A package with a version.
    Versioned(PackageSpec),
    /// A package without a version.
    Versionless(VersionlessPackageSpec),
}

/// The pack trait is used to hold a package.
pub trait Pack: PackFs {}

/// The pack trait extension.
pub trait PackExt: Pack {
    /// Filter the package files to read by a function.
    fn filter(&mut self, f: impl Fn(&str) -> bool + Send + Sync) -> impl Pack
    where
        Self: std::marker::Sized,
    {
        FilterPack { src: self, f }
    }
}

/// The pack trait is used to hold a package.
pub trait CloneIntoPack: fmt::Debug {
    /// Clones the pack into a new pack.
    fn clone_into_pack(&mut self, pack: &mut impl PackFs) -> std::io::Result<()>;
}

/// The package is a trait that can be used to create a package.
#[derive(Debug, Clone)]
pub struct Package {
    /// The underlying pack.
    pub pack: Arc<dyn Pack + Send + Sync>,
}

fn unsupported() -> io::Error {
    io::Error::new(io::ErrorKind::Unsupported, "unsupported operation")
}

fn malform(e: io::Error) -> PackageError {
    PackageError::MalformedArchive(Some(eco_format!("{e:?}")))
}

fn other_io(e: impl Display) -> io::Error {
    io::Error::new(io::ErrorKind::Other, e.to_string())
}

fn other(e: impl Display) -> PackageError {
    PackageError::Other(Some(eco_format!("{e}")))
}