use anyhow::{Context, Result};
use std::path::{Path, PathBuf};
use typst::diag::{FileError, FileResult};
use typst::foundations::{Bytes, Datetime};
use typst::syntax::{FileId, Source, VirtualPath};
use typst::text::{Font, FontBook};
use typst::utils::LazyHash;
use typst::{Library, LibraryExt, World};
use typst_kit::download::{Downloader, ProgressSink};
use typst_kit::fonts::{FontSlot, Fonts};
use typst_kit::package::PackageStorage;
pub struct SimpleWorld {
library: LazyHash<Library>,
book: LazyHash<FontBook>,
fonts: Vec<FontSlot>,
main: FileId,
root: PathBuf,
package_storage: PackageStorage,
}
impl SimpleWorld {
pub fn new(main_path: &Path) -> Result<Self> {
let main_path = main_path
.canonicalize()
.context("Failed to find input file")?;
let root = main_path
.parent()
.context("Input file has no parent directory")?
.to_path_buf();
let vpath = VirtualPath::new(
main_path
.file_name()
.context("Input file has no filename")?,
);
let main = FileId::new_fake(vpath);
let downloader = Downloader::new("typst-count");
let package_storage = PackageStorage::new(None, None, downloader);
let mut font_searcher = Fonts::searcher();
font_searcher.include_system_fonts(true);
#[cfg(feature = "embed-fonts")]
font_searcher.include_embedded_fonts(true);
let fonts = font_searcher.search();
Ok(Self {
library: LazyHash::new(Library::builder().build()),
book: LazyHash::new(fonts.book),
fonts: fonts.fonts,
main,
root,
package_storage,
})
}
fn resolve_path(&self, id: FileId) -> FileResult<PathBuf> {
if let Some(spec) = id.package() {
let package_dir = self
.package_storage
.prepare_package(spec, &mut ProgressSink)
.map_err(|e| FileError::Other(Some(e.to_string().into())))?;
Ok(package_dir.join(id.vpath().as_rootless_path()))
} else {
let path = if id.vpath().as_rootless_path().is_absolute() {
id.vpath().as_rootless_path().to_path_buf()
} else {
self.root.join(id.vpath().as_rootless_path())
};
Ok(path)
}
}
}
impl World for SimpleWorld {
fn library(&self) -> &LazyHash<Library> {
&self.library
}
fn book(&self) -> &LazyHash<FontBook> {
&self.book
}
fn main(&self) -> FileId {
self.main
}
fn source(&self, id: FileId) -> FileResult<Source> {
let path = self.resolve_path(id)?;
let content = std::fs::read_to_string(&path).map_err(|e| FileError::from_io(e, &path))?;
Ok(Source::new(id, content))
}
fn file(&self, id: FileId) -> FileResult<Bytes> {
let path = self.resolve_path(id)?;
let content = std::fs::read(&path).map_err(|e| FileError::from_io(e, &path))?;
Ok(Bytes::new(content))
}
fn font(&self, index: usize) -> Option<Font> {
self.fonts.get(index)?.get()
}
fn today(&self, _offset: Option<i64>) -> Option<Datetime> {
Some(Datetime::from_ymd(2024, 1, 1).unwrap())
}
}