summaryrefslogtreecommitdiff
path: root/cli/src/world.rs
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2023-07-02 19:59:52 +0200
committerLaurenz <laurmaedje@gmail.com>2023-07-02 20:07:43 +0200
commitebfdb1dafa430786db10dad2ef7d5467c1bdbed1 (patch)
tree2bbc24ddb4124c4bb14dec0e536129d4de37b056 /cli/src/world.rs
parent3ab19185093d7709f824b95b979060ce125389d8 (diff)
Move everything into `crates/` directory
Diffstat (limited to 'cli/src/world.rs')
-rw-r--r--cli/src/world.rs256
1 files changed, 0 insertions, 256 deletions
diff --git a/cli/src/world.rs b/cli/src/world.rs
deleted file mode 100644
index f09a3f6c..00000000
--- a/cli/src/world.rs
+++ /dev/null
@@ -1,256 +0,0 @@
-use std::cell::{OnceCell, RefCell, RefMut};
-use std::collections::HashMap;
-use std::fs;
-use std::hash::Hash;
-use std::path::{Path, PathBuf};
-
-use chrono::Datelike;
-use comemo::Prehashed;
-use same_file::Handle;
-use siphasher::sip128::{Hasher128, SipHasher13};
-use typst::diag::{FileError, FileResult, StrResult};
-use typst::eval::{eco_format, Datetime, Library};
-use typst::file::FileId;
-use typst::font::{Font, FontBook};
-use typst::syntax::Source;
-use typst::util::{Bytes, PathExt};
-use typst::World;
-
-use crate::args::CompileCommand;
-use crate::fonts::{FontSearcher, FontSlot};
-use crate::package::prepare_package;
-
-/// A world that provides access to the operating system.
-pub struct SystemWorld {
- /// The root relative to which absolute paths are resolved.
- root: PathBuf,
- /// The input path.
- main: FileId,
- /// Typst's standard library.
- library: Prehashed<Library>,
- /// Metadata about discovered fonts.
- book: Prehashed<FontBook>,
- /// Locations of and storage for lazily loaded fonts.
- fonts: Vec<FontSlot>,
- /// Maps package-path combinations to canonical hashes. All package-path
- /// combinations that point to the same file are mapped to the same hash. To
- /// be used in conjunction with `paths`.
- hashes: RefCell<HashMap<FileId, FileResult<PathHash>>>,
- /// Maps canonical path hashes to source files and buffers.
- paths: RefCell<HashMap<PathHash, PathSlot>>,
- /// The current date if requested. This is stored here to ensure it is
- /// always the same within one compilation. Reset between compilations.
- today: OnceCell<Option<Datetime>>,
-}
-
-impl SystemWorld {
- /// Create a new system world.
- pub fn new(command: &CompileCommand) -> StrResult<Self> {
- let mut searcher = FontSearcher::new();
- searcher.search(&command.font_paths);
-
- // Resolve the system-global input path.
- let system_input = command.input.canonicalize().map_err(|_| {
- eco_format!("input file not found (searched at {})", command.input.display())
- })?;
-
- // Resolve the system-global root directory.
- let root = {
- let path = command
- .root
- .as_deref()
- .or_else(|| system_input.parent())
- .unwrap_or(Path::new("."));
- path.canonicalize().map_err(|_| {
- eco_format!("root directory not found (searched at {})", path.display())
- })?
- };
-
- // Resolve the input path within the project.
- let project_input = system_input
- .strip_prefix(&root)
- .map(|path| Path::new("/").join(path))
- .map_err(|_| "input file must be contained in project root")?;
-
- Ok(Self {
- root,
- main: FileId::new(None, &project_input),
- library: Prehashed::new(typst_library::build()),
- book: Prehashed::new(searcher.book),
- fonts: searcher.fonts,
- hashes: RefCell::default(),
- paths: RefCell::default(),
- today: OnceCell::new(),
- })
- }
-
- /// The id of the main source file.
- pub fn main(&self) -> FileId {
- self.main
- }
-
- /// Return all paths the last compilation depended on.
- pub fn dependencies(&mut self) -> impl Iterator<Item = &Path> {
- self.paths.get_mut().values().map(|slot| slot.system_path.as_path())
- }
-
- /// Reset the compilation state in preparation of a new compilation.
- pub fn reset(&mut self) {
- self.hashes.borrow_mut().clear();
- self.paths.borrow_mut().clear();
- self.today.take();
- }
-
- /// Lookup a source file by id.
- #[track_caller]
- pub fn lookup(&self, id: FileId) -> Source {
- self.source(id).expect("file id does not point to any source file")
- }
-}
-
-impl World for SystemWorld {
- fn library(&self) -> &Prehashed<Library> {
- &self.library
- }
-
- fn book(&self) -> &Prehashed<FontBook> {
- &self.book
- }
-
- fn main(&self) -> Source {
- self.source(self.main).unwrap()
- }
-
- fn source(&self, id: FileId) -> FileResult<Source> {
- self.slot(id)?.source()
- }
-
- fn file(&self, id: FileId) -> FileResult<Bytes> {
- self.slot(id)?.file()
- }
-
- fn font(&self, index: usize) -> Option<Font> {
- self.fonts[index].get()
- }
-
- fn today(&self, offset: Option<i64>) -> Option<Datetime> {
- *self.today.get_or_init(|| {
- let naive = match offset {
- None => chrono::Local::now().naive_local(),
- Some(o) => (chrono::Utc::now() + chrono::Duration::hours(o)).naive_utc(),
- };
-
- Datetime::from_ymd(
- naive.year(),
- naive.month().try_into().ok()?,
- naive.day().try_into().ok()?,
- )
- })
- }
-}
-
-impl SystemWorld {
- /// Access the canonical slot for the given file id.
- #[tracing::instrument(skip_all)]
- fn slot(&self, id: FileId) -> FileResult<RefMut<PathSlot>> {
- let mut system_path = PathBuf::new();
- let hash = self
- .hashes
- .borrow_mut()
- .entry(id)
- .or_insert_with(|| {
- // Determine the root path relative to which the file path
- // will be resolved.
- let root = match id.package() {
- Some(spec) => prepare_package(spec)?,
- None => self.root.clone(),
- };
-
- // Join the path to the root. If it tries to escape, deny
- // access. Note: It can still escape via symlinks.
- system_path =
- root.join_rooted(id.path()).ok_or(FileError::AccessDenied)?;
-
- PathHash::new(&system_path)
- })
- .clone()?;
-
- Ok(RefMut::map(self.paths.borrow_mut(), |paths| {
- paths.entry(hash).or_insert_with(|| PathSlot {
- id,
- // This will only trigger if the `or_insert_with` above also
- // triggered.
- system_path,
- source: OnceCell::new(),
- buffer: OnceCell::new(),
- })
- }))
- }
-}
-
-/// Holds canonical data for all paths pointing to the same entity.
-///
-/// Both fields can be populated if the file is both imported and read().
-struct PathSlot {
- /// The slot's canonical file id.
- id: FileId,
- /// The slot's path on the system.
- system_path: PathBuf,
- /// The lazily loaded source file for a path hash.
- source: OnceCell<FileResult<Source>>,
- /// The lazily loaded buffer for a path hash.
- buffer: OnceCell<FileResult<Bytes>>,
-}
-
-impl PathSlot {
- fn source(&self) -> FileResult<Source> {
- self.source
- .get_or_init(|| {
- let buf = read(&self.system_path)?;
- let text = decode_utf8(buf)?;
- Ok(Source::new(self.id, text))
- })
- .clone()
- }
-
- fn file(&self) -> FileResult<Bytes> {
- self.buffer
- .get_or_init(|| read(&self.system_path).map(Bytes::from))
- .clone()
- }
-}
-
-/// A hash that is the same for all paths pointing to the same entity.
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-struct PathHash(u128);
-
-impl PathHash {
- fn new(path: &Path) -> FileResult<Self> {
- let f = |e| FileError::from_io(e, path);
- let handle = Handle::from_path(path).map_err(f)?;
- let mut state = SipHasher13::new();
- handle.hash(&mut state);
- Ok(Self(state.finish128().as_u128()))
- }
-}
-
-/// Read a file.
-fn read(path: &Path) -> FileResult<Vec<u8>> {
- let f = |e| FileError::from_io(e, path);
- if fs::metadata(path).map_err(f)?.is_dir() {
- Err(FileError::IsDirectory)
- } else {
- fs::read(path).map_err(f)
- }
-}
-
-/// Decode UTF-8 with an optional BOM.
-fn decode_utf8(buf: Vec<u8>) -> FileResult<String> {
- Ok(if buf.starts_with(b"\xef\xbb\xbf") {
- // Remove UTF-8 BOM.
- std::str::from_utf8(&buf[3..])?.into()
- } else {
- // Assume UTF-8.
- String::from_utf8(buf)?
- })
-}