1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
|
use std::fs;
use std::io::{self, Write};
use std::path::Path;
use std::process;
use anyhow::Context as _;
use codespan_reporting::diagnostic::{Diagnostic, Label};
use codespan_reporting::term::{self, termcolor, Config, Styles};
use same_file::is_same_file;
use termcolor::{ColorChoice, StandardStream, WriteColor};
use typst::diag::{Error, Tracepoint};
use typst::source::SourceStore;
fn main() {
if let Err(error) = try_main() {
print_error(error).unwrap();
process::exit(1);
}
}
/// The main compiler logic.
fn try_main() -> anyhow::Result<()> {
let args: Vec<_> = std::env::args().collect();
if args.len() < 2 || args.len() > 3 {
print_usage().unwrap();
process::exit(2);
}
// Determine source and destination path.
let src_path = Path::new(&args[1]);
let dest_path = match args.get(2) {
Some(path) => path.into(),
None => {
let name = src_path.file_name().context("source path is not a file")?;
Path::new(name).with_extension("pdf")
}
};
// Ensure that the source file is not overwritten.
if is_same_file(src_path, &dest_path).unwrap_or(false) {
anyhow::bail!("source and destination files are the same");
}
// Create a loader for fonts and files.
let loader = typst::loading::FsLoader::new()
.with_path("fonts")
.with_system()
.wrap();
// Create the context which holds loaded source files, fonts, images and
// cached artifacts.
let mut ctx = typst::Context::new(loader);
// Load the source file.
let id = ctx.sources.load(&src_path).context("source file not found")?;
// Typeset.
match ctx.typeset(id) {
// Export the PDF.
Ok(document) => {
let buffer = typst::export::pdf(&ctx, &document);
fs::write(&dest_path, buffer).context("failed to write PDF file")?;
}
// Print diagnostics.
Err(errors) => {
print_diagnostics(&ctx.sources, *errors)
.context("failed to print diagnostics")?;
}
}
Ok(())
}
/// Print a usage message.
fn print_usage() -> io::Result<()> {
let mut writer = StandardStream::stderr(ColorChoice::Always);
let styles = Styles::default();
writer.set_color(&styles.header_help)?;
write!(writer, "usage")?;
writer.set_color(&styles.header_message)?;
writeln!(writer, ": typst document.typ [output.pdf]")?;
writer.reset()
}
/// Print an error outside of a source file.
fn print_error(error: anyhow::Error) -> io::Result<()> {
let mut writer = StandardStream::stderr(ColorChoice::Always);
let styles = Styles::default();
for (i, cause) in error.chain().enumerate() {
writer.set_color(&styles.header_error)?;
write!(writer, "{}", if i == 0 { "error" } else { "cause" })?;
writer.set_color(&styles.header_message)?;
writeln!(writer, ": {}", cause)?;
}
writer.reset()
}
/// Print diagnostics messages to the terminal.
fn print_diagnostics(
sources: &SourceStore,
errors: Vec<Error>,
) -> Result<(), codespan_reporting::files::Error> {
let mut writer = StandardStream::stderr(ColorChoice::Always);
let config = Config { tab_width: 2, ..Default::default() };
for error in errors {
// The main diagnostic.
let main = Diagnostic::error().with_message(error.message).with_labels(vec![
Label::primary(error.span.source, error.span.to_range()),
]);
term::emit(&mut writer, &config, sources, &main)?;
// Stacktrace-like helper diagnostics.
for point in error.trace {
let message = match point.v {
Tracepoint::Call(Some(name)) => {
format!("error occured in this call of function `{}`", name)
}
Tracepoint::Call(None) => "error occured in this function call".into(),
Tracepoint::Import => "error occured while importing this module".into(),
};
let help = Diagnostic::help().with_message(message).with_labels(vec![
Label::primary(point.span.source, point.span.to_range()),
]);
term::emit(&mut writer, &config, sources, &help)?;
}
}
Ok(())
}
|