Initial skeleton copying typst-cli
This commit is contained in:
parent
0c2a5f6482
commit
abe5140de0
4
.gitignore
vendored
4
.gitignore
vendored
@ -4,10 +4,6 @@
|
||||
debug/
|
||||
target/
|
||||
|
||||
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
||||
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
|
||||
Cargo.lock
|
||||
|
||||
# These are backup files generated by rustfmt
|
||||
**/*.rs.bk
|
||||
|
||||
|
@ -6,3 +6,12 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
comemo = "0.2.2"
|
||||
elsa = "1.8.1"
|
||||
html = "0.5.2"
|
||||
notify = "5.1.0"
|
||||
same-file = "1.0.6"
|
||||
siphasher = "0.3.10"
|
||||
typst = { git = "https://github.com/typst/typst.git" }
|
||||
typst-library = { git = "https://github.com/typst/typst.git" }
|
||||
|
||||
|
177
src/main.rs
177
src/main.rs
@ -1,3 +1,180 @@
|
||||
#![feature(hashmap_internals)]
|
||||
|
||||
use std::cell::{OnceCell, RefCell, RefMut};
|
||||
use std::collections::HashMap;
|
||||
use std::fs::{self};
|
||||
use std::hash::Hash;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use comemo::Prehashed;
|
||||
use elsa::FrozenVec;
|
||||
use same_file::Handle;
|
||||
use siphasher::sip128::{Hasher128, SipHasher13};
|
||||
use typst::diag::{FileError, FileResult};
|
||||
use typst::eval::Library;
|
||||
use typst::font::{Font, FontBook};
|
||||
use typst::syntax::{Source, SourceId};
|
||||
use typst::util::{Buffer, PathExt};
|
||||
use typst::World;
|
||||
|
||||
|
||||
/// Holds canonical data for all paths pointing to the same entity.
|
||||
#[derive(Default)]
|
||||
struct PathSlot {
|
||||
source: OnceCell<FileResult<SourceId>>,
|
||||
buffer: OnceCell<FileResult<Buffer>>,
|
||||
}
|
||||
|
||||
/// 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)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WebWorld {
|
||||
root: PathBuf,
|
||||
library: Prehashed<Library>,
|
||||
book: Prehashed<FontBook>,
|
||||
// unused
|
||||
hashes: RefCell<HashMap<PathBuf, FileResult<PathHash>>>,
|
||||
paths: RefCell<HashMap<PathHash, PathSlot>>,
|
||||
sources: FrozenVec<Box<Source>>,
|
||||
main: SourceId,
|
||||
}
|
||||
|
||||
impl WebWorld {
|
||||
fn new(root: PathBuf) -> Self {
|
||||
WebWorld {
|
||||
root,
|
||||
library: Prehashed::new(typst_library::build()),
|
||||
book: Prehashed::new(Default::default()),
|
||||
hashes: RefCell::default(),
|
||||
paths: RefCell::default(),
|
||||
sources: FrozenVec::new(),
|
||||
main: SourceId::detached(),
|
||||
}
|
||||
}
|
||||
|
||||
fn slot(&self, path: &Path) -> FileResult<RefMut<PathSlot>> {
|
||||
let mut hashes = self.hashes.borrow_mut();
|
||||
let hash = match hashes.get(path).cloned() {
|
||||
Some(hash) => hash,
|
||||
None => {
|
||||
let hash = PathHash::new(path);
|
||||
if let Ok(canon) = path.canonicalize() {
|
||||
hashes.insert(canon.normalize(), hash.clone());
|
||||
}
|
||||
hashes.insert(path.into(), hash.clone());
|
||||
hash
|
||||
}
|
||||
}?;
|
||||
|
||||
Ok(RefMut::map(self.paths.borrow_mut(), |paths| {
|
||||
paths.entry(hash).or_default()
|
||||
}))
|
||||
}
|
||||
|
||||
/// Add a source file to the world and return its ID
|
||||
fn insert(&self, path: &Path, text: String) -> SourceId {
|
||||
let id = SourceId::from_u16(self.sources.len() as u16);
|
||||
let source = Source::new(id, path, text);
|
||||
self.sources.push(Box::new(source));
|
||||
id
|
||||
}
|
||||
|
||||
fn relevant(&mut self, event: ¬ify::Event) -> bool {
|
||||
match &event.kind {
|
||||
notify::EventKind::Any => {}
|
||||
notify::EventKind::Access(_) => return false,
|
||||
notify::EventKind::Create(_) => return true,
|
||||
notify::EventKind::Modify(kind) => match kind {
|
||||
notify::event::ModifyKind::Any => {}
|
||||
notify::event::ModifyKind::Data(_) => {}
|
||||
notify::event::ModifyKind::Metadata(_) => return false,
|
||||
notify::event::ModifyKind::Name(_) => return true,
|
||||
notify::event::ModifyKind::Other => return false,
|
||||
},
|
||||
notify::EventKind::Remove(_) => {}
|
||||
notify::EventKind::Other => return false,
|
||||
}
|
||||
|
||||
event.paths.iter().any(|path| self.dependant(path))
|
||||
}
|
||||
|
||||
fn dependant(&self, path: &Path) -> bool {
|
||||
self.hashes.borrow().contains_key(&path.normalize())
|
||||
|| PathHash::new(path)
|
||||
.map_or(false, |hash| self.paths.borrow().contains_key(&hash))
|
||||
}
|
||||
|
||||
fn reset(&mut self) {
|
||||
self.sources.as_mut().clear();
|
||||
self.hashes.borrow_mut().clear();
|
||||
self.paths.borrow_mut().clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl World for WebWorld {
|
||||
fn root(&self) -> &Path {
|
||||
&self.root
|
||||
}
|
||||
|
||||
fn library(&self) -> &Prehashed<Library> {
|
||||
&self.library
|
||||
}
|
||||
|
||||
fn main(&self) -> &Source {
|
||||
self.source(self.main)
|
||||
}
|
||||
|
||||
fn resolve(&self, path: &Path) -> FileResult<SourceId> {
|
||||
self.slot(path)?
|
||||
.source
|
||||
.get_or_init(|| {
|
||||
let buf = read(path)?;
|
||||
let text = String::from_utf8(buf)?;
|
||||
Ok(self.insert(path, text))
|
||||
})
|
||||
.clone()
|
||||
}
|
||||
|
||||
fn source(&self, id: SourceId) -> &Source {
|
||||
&self.sources[id.into_u16() as usize]
|
||||
}
|
||||
|
||||
fn book(&self) -> &Prehashed<FontBook> {
|
||||
&self.book // always empty
|
||||
}
|
||||
|
||||
fn font(&self, _: usize) -> Option<Font> { None }
|
||||
|
||||
fn file(&self, path: &Path) -> FileResult<Buffer> {
|
||||
self.slot(path)?
|
||||
.buffer
|
||||
.get_or_init(|| read(path).map(Buffer::from))
|
||||
.clone()
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user