#![allow(dead_code)]
use std::path::PathBuf;
use typst::Library;
use typst::World;
use typst::diag::FileResult;
use typst::foundations::Bytes;
use typst::foundations::Datetime;
use typst::foundations::Dict;
use typst::syntax::FileId;
use typst::text::Font;
use typst::text::FontBook;
use typst::utils::LazyHash;
use typst_kit::download::Downloader;
use typst_kit::fonts::FontSearcher;
use typst_kit::package::PackageStorage;
use typst_syntax::Source;
use typst_syntax::VirtualPath;
use typst_syntax::package::PackageSpec;
use tytanic_core::Project;
use tytanic_core::TemplateTest;
use tytanic_core::UnitTest;
use tytanic_core::library::augmented_default_library;
use tytanic_core::library::augmented_library;
use tytanic_core::world_builder::ComposedWorld;
use tytanic_core::world_builder::ProvideDatetime;
use tytanic_core::world_builder::ProvideFile;
use tytanic_core::world_builder::ProvideFont;
use tytanic_core::world_builder::ProvideLibrary;
use tytanic_core::world_builder::datetime::FixedDateProvider;
use tytanic_core::world_builder::file::FilesystemFileProvider;
use tytanic_core::world_builder::font::FilesystemFontProvider;
use tytanic_core::world_builder::library::LibraryProvider;
use crate::cli::commands::CompileOptions;
use crate::cli::commands::FontOptions;
use crate::cli::commands::PackageOptions;
use crate::cli::commands::Switch;
#[tracing::instrument]
fn package_storage(package_opts: &PackageOptions) -> PackageStorage {
let agent = format!("{}/{}", tytanic_core::TOOL_NAME, env!("CARGO_PKG_VERSION"));
let downloader = match package_opts.certificate.clone() {
Some(path) => Downloader::with_path(agent, path),
None => Downloader::new(agent),
};
PackageStorage::new(
package_opts.package_cache_path.clone(),
package_opts.package_path.clone(),
downloader,
)
}
#[tracing::instrument(skip(project))]
pub fn project_file_provider(
project: &Project,
package_opts: &PackageOptions,
) -> Box<dyn ProvideFile> {
Box::new(FilesystemFileProvider::new(
project.root(),
Some(package_storage(package_opts)),
)) as _
}
#[tracing::instrument(skip(project))]
pub fn template_file_provider(
project: &Project,
package_opts: &PackageOptions,
) -> Box<dyn ProvideFile> {
let manifest = project.manifest().unwrap();
let spec = PackageSpec {
namespace: "preview".into(),
name: manifest.package.name.clone(),
version: manifest.package.version,
};
Box::new(FilesystemFileProvider::with_overrides(
project.template_root().unwrap(),
[(spec, project.root().to_path_buf())],
Some(package_storage(package_opts)),
))
}
#[tracing::instrument]
pub fn font_provider(font_opts: &FontOptions) -> Box<dyn ProvideFont> {
let mut searcher = FontSearcher::new();
#[cfg(feature = "embed-fonts")]
searcher.include_embedded_fonts(font_opts.use_embedded_fonts.get_or_default());
searcher.include_system_fonts(font_opts.use_system_fonts.get_or_default());
let fonts = searcher.search_with(font_opts.font_paths.iter().map(PathBuf::as_path));
tracing::debug!(fonts = ?fonts.fonts.len(), "collected fonts");
Box::new(FilesystemFontProvider::from_searcher(fonts))
}
#[tracing::instrument]
pub fn datetime_provider(compile_opts: &CompileOptions) -> Box<dyn ProvideDatetime> {
Box::new(FixedDateProvider::new(compile_opts.timestamp)) as _
}
#[tracing::instrument]
pub fn augmented_library_provider() -> Box<dyn ProvideLibrary> {
Box::new(LibraryProvider::with_library(augmented_default_library())) as _
}
#[tracing::instrument]
pub fn augmented_library_provider_with_inputs(inputs: Dict) -> Box<dyn ProvideLibrary> {
Box::new(LibraryProvider::with_library(augmented_library(
|builder| builder.with_inputs(inputs),
))) as _
}
#[tracing::instrument]
pub fn default_library_provider() -> Box<dyn ProvideLibrary> {
Box::new(LibraryProvider::new()) as _
}
pub struct Providers {
augmented_library: Box<dyn ProvideLibrary>,
default_library: Box<dyn ProvideLibrary>,
project_files: Box<dyn ProvideFile>,
template_files: Option<Box<dyn ProvideFile>>,
fonts: Box<dyn ProvideFont>,
datetime: Box<dyn ProvideDatetime>,
}
impl Providers {
pub fn new(
project: &Project,
package_opts: &PackageOptions,
font_opts: &FontOptions,
compile_opts: &CompileOptions,
) -> Self {
Self {
augmented_library: augmented_library_provider(),
default_library: default_library_provider(),
project_files: project_file_provider(project, package_opts),
template_files: project.manifest().and_then(|m| {
m.template
.is_some()
.then(|| template_file_provider(project, package_opts))
}),
fonts: font_provider(font_opts),
datetime: datetime_provider(compile_opts),
}
}
}
impl Providers {
pub fn system_world(&self, source: Source) -> NewTestWorld<'_> {
NewTestWorld(
ComposedWorld::builder()
.library_provider(&*self.augmented_library)
.file_provider(&*self.project_files)
.font_provider(&*self.fonts)
.datetime_provider(&*self.datetime)
.build(source.id()),
source,
)
}
pub fn unit_world<'w>(
&'w self,
project: &Project,
test: &'w UnitTest,
is_ref: bool,
alternative_library: Option<&'w dyn ProvideLibrary>,
) -> ComposedWorld<'w> {
let path = if is_ref {
project.unit_test_ref_script(test.id())
} else {
project.unit_test_script(test.id())
};
let prefix = project.root();
let id = FileId::new(
None,
VirtualPath::new(
path.strip_prefix(prefix)
.expect("tests are in project root"),
),
);
let library = if let Some(library) = alternative_library {
library
} else {
&*self.augmented_library
};
ComposedWorld::builder()
.library_provider(library)
.file_provider(&*self.project_files)
.font_provider(&*self.fonts)
.datetime_provider(&*self.datetime)
.build(id)
}
pub fn template_world<'w>(
&'w self,
project: &Project,
_test: &'w TemplateTest,
) -> ComposedWorld<'w> {
let prefix = project.template_root().unwrap();
let entrypoint = project.template_entrypoint().unwrap();
let id = FileId::new(
None,
VirtualPath::new(
entrypoint
.strip_prefix(prefix)
.expect("entrypoint is created with template root"),
),
);
ComposedWorld::builder()
.library_provider(&*self.default_library)
.file_provider(&**self.template_files.as_ref().unwrap())
.font_provider(&*self.fonts)
.datetime_provider(&*self.datetime)
.build(id)
}
}
pub struct NewTestWorld<'w>(ComposedWorld<'w>, Source);
impl World for NewTestWorld<'_> {
fn library(&self) -> &LazyHash<Library> {
self.0.library()
}
fn book(&self) -> &LazyHash<FontBook> {
self.0.book()
}
fn main(&self) -> FileId {
self.1.id()
}
fn source(&self, id: FileId) -> FileResult<Source> {
if id == self.1.id() {
Ok(self.1.clone())
} else {
self.0.source(id)
}
}
fn file(&self, id: FileId) -> FileResult<Bytes> {
if id == self.1.id() {
Ok(Bytes::new(self.1.text().to_owned()))
} else {
self.0.file(id)
}
}
fn font(&self, index: usize) -> Option<Font> {
self.0.font(index)
}
fn today(&self, offset: Option<i64>) -> Option<Datetime> {
self.0.today(offset)
}
}