tinymist_package/
pack.rs

1//! A bundle that is modifiable
2
3use core::fmt;
4use std::fmt::Display;
5use std::io::{self, Read};
6use std::path::Path;
7use std::sync::Arc;
8
9use ecow::{EcoVec, eco_format};
10use tinymist_std::{ImmutBytes, ImmutPath};
11use typst::diag::{PackageError, PackageResult};
12use typst::syntax::package::{PackageSpec, VersionlessPackageSpec};
13
14#[cfg(feature = "fs-pack")]
15mod fs;
16#[cfg(feature = "gitcl-pack")]
17mod gitcl;
18#[cfg(feature = "http-pack")]
19mod http;
20mod memory;
21mod ops;
22#[cfg(feature = "release-pack")]
23mod release;
24mod tarball;
25#[cfg(feature = "universe-pack")]
26mod universe;
27
28#[cfg(feature = "fs-pack")]
29pub use fs::*;
30#[cfg(feature = "gitcl-pack")]
31pub use gitcl::*;
32#[cfg(feature = "http-pack")]
33pub use http::*;
34pub use memory::*;
35pub use ops::*;
36#[cfg(feature = "release-pack")]
37pub use release::*;
38pub use tarball::*;
39#[cfg(feature = "universe-pack")]
40pub use universe::*;
41
42/// The pack file is the knownn file type in the package.
43pub enum PackFile<'a> {
44    /// A single file in the memory.
45    Data(io::Cursor<ImmutBytes>),
46    /// A file in the package.
47    Read(Box<dyn Read + 'a>),
48}
49
50impl io::Read for PackFile<'_> {
51    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
52        match self {
53            PackFile::Data(data) => data.read(buf),
54            PackFile::Read(reader) => reader.read(buf),
55        }
56    }
57}
58
59/// The pack file is the knownn file type in the package.
60pub enum PackEntries<'a> {
61    /// A single file in the memory.
62    Data(EcoVec<ImmutPath>),
63    /// A file in the package.
64    Read(Box<dyn Iterator<Item = Path> + 'a>),
65}
66
67/// The pack trait is used for read/write files in a package.
68pub trait PackFs: fmt::Debug {
69    /// Read files from the package.
70    fn read_all(
71        &mut self,
72        f: &mut (dyn FnMut(&str, PackFile) -> PackageResult<()> + Send + Sync),
73    ) -> PackageResult<()>;
74    /// Read a file from the package.
75    fn read(&self, _path: &str) -> io::Result<PackFile<'_>> {
76        Err(unsupported())
77    }
78    /// Read entries from the package.
79    fn entries(&self) -> io::Result<PackEntries<'_>> {
80        Err(unsupported())
81    }
82}
83
84/// The specifier is used to identify a package.
85pub enum PackSpecifier {
86    /// A package with a version.
87    Versioned(PackageSpec),
88    /// A package without a version.
89    Versionless(VersionlessPackageSpec),
90}
91
92/// The pack trait is used to hold a package.
93pub trait Pack: PackFs {}
94
95/// The pack trait extension.
96pub trait PackExt: Pack {
97    /// Filter the package files to read by a function.
98    fn filter(&mut self, f: impl Fn(&str) -> bool + Send + Sync) -> impl Pack
99    where
100        Self: std::marker::Sized,
101    {
102        FilterPack { src: self, f }
103    }
104}
105
106/// The pack trait is used to hold a package.
107pub trait CloneIntoPack: fmt::Debug {
108    /// Clones the pack into a new pack.
109    fn clone_into_pack(&mut self, pack: &mut impl PackFs) -> std::io::Result<()>;
110}
111
112/// The package is a trait that can be used to create a package.
113#[derive(Debug, Clone)]
114pub struct Package {
115    /// The underlying pack.
116    pub pack: Arc<dyn Pack + Send + Sync>,
117}
118
119fn unsupported() -> io::Error {
120    io::Error::new(io::ErrorKind::Unsupported, "unsupported operation")
121}
122
123fn malform(e: io::Error) -> PackageError {
124    PackageError::MalformedArchive(Some(eco_format!("{e:?}")))
125}
126
127fn other_io(e: impl Display) -> io::Error {
128    io::Error::other(e.to_string())
129}
130
131fn other(e: impl Display) -> PackageError {
132    PackageError::Other(Some(eco_format!("{e}")))
133}