tytanic_core/doc/
compile.rs1use std::fmt::Debug;
4
5use ecow::EcoVec;
6use ecow::eco_vec;
7use thiserror::Error;
8use typst::World;
9use typst::diag::Severity;
10use typst::diag::SourceDiagnostic;
11use typst::diag::Warned;
12use typst::layout::PagedDocument;
13use tytanic_utils::fmt::Term;
14
15#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
17pub enum Warnings {
18 Ignore,
20
21 #[default]
23 Emit,
24
25 Promote,
27}
28
29#[derive(Debug, Clone, Error)]
32#[error("compilation failed with {} {}", .0.len(), Term::simple("error").with(.0.len()))]
33pub struct Error(pub EcoVec<SourceDiagnostic>);
34
35pub fn compile(world: &dyn World, warnings: Warnings) -> Warned<Result<PagedDocument, Error>> {
37 let Warned {
38 output,
39 warnings: mut emitted,
40 } = typst::compile(world);
41
42 match warnings {
43 Warnings::Ignore => Warned {
44 output: output.map_err(Error),
45 warnings: eco_vec![],
46 },
47 Warnings::Emit => Warned {
48 output: output.map_err(Error),
49 warnings: emitted,
50 },
51 Warnings::Promote => {
52 emitted = emitted
53 .into_iter()
54 .map(|mut warning| {
55 warning.severity = Severity::Error;
56 warning.with_hint("this warning was promoted to an error")
57 })
58 .collect();
59
60 match output {
61 Ok(doc) if emitted.is_empty() => Warned {
62 output: Ok(doc),
63 warnings: eco_vec![],
64 },
65 Ok(_) => Warned {
66 output: Err(Error(emitted)),
67 warnings: eco_vec![],
68 },
69 Err(errors) => {
70 emitted.extend(errors);
71 Warned {
72 output: Err(Error(emitted)),
73 warnings: eco_vec![],
74 }
75 }
76 }
77 }
78 }
79}
80
81#[cfg(test)]
82mod tests {
83 use typst::syntax::Source;
84
85 use super::*;
86 use crate::world_builder::file::VirtualFileProvider;
87 use crate::world_builder::library::LibraryProvider;
88 use crate::world_builder::test_utils;
89
90 const TEST_PASS: &str = "Hello World";
91 const TEST_WARN: &str = "#set text(font: \"foo\"); Hello World";
92 const TEST_FAIL: &str = "#set text(font: \"foo\"); #panic()";
93
94 #[test]
95 fn test_compile_pass_ignore_warnings() {
96 let mut files = VirtualFileProvider::new();
97 let library = LibraryProvider::new();
98 let source = Source::detached(TEST_PASS);
99 let world = test_utils::virtual_world(source, &mut files, &library);
100
101 let Warned { output, warnings } = compile(&world, Warnings::Ignore);
102 assert!(output.is_ok());
103 assert!(warnings.is_empty());
104 }
105
106 #[test]
107 fn test_compile_pass_emit_warnings() {
108 let mut files = VirtualFileProvider::new();
109 let library = LibraryProvider::new();
110 let source = Source::detached(TEST_PASS);
111 let world = test_utils::virtual_world(source, &mut files, &library);
112
113 let Warned { output, warnings } = compile(&world, Warnings::Emit);
114 assert!(output.is_ok());
115 assert!(warnings.is_empty());
116 }
117
118 #[test]
119 fn test_compile_pass_promote_warnings() {
120 let mut files = VirtualFileProvider::new();
121 let library = LibraryProvider::new();
122 let source = Source::detached(TEST_PASS);
123 let world = test_utils::virtual_world(source, &mut files, &library);
124
125 let Warned { output, warnings } = compile(&world, Warnings::Promote);
126 assert!(output.is_ok());
127 assert!(warnings.is_empty());
128 }
129
130 #[test]
131 fn test_compile_warn_ignore_warnings() {
132 let mut files = VirtualFileProvider::new();
133 let library = LibraryProvider::new();
134 let source = Source::detached(TEST_WARN);
135 let world = test_utils::virtual_world(source, &mut files, &library);
136
137 let Warned { output, warnings } = compile(&world, Warnings::Ignore);
138 assert!(output.is_ok());
139 assert!(warnings.is_empty());
140 }
141
142 #[test]
143 fn test_compile_warn_emit_warnings() {
144 let mut files = VirtualFileProvider::new();
145 let library = LibraryProvider::new();
146 let source = Source::detached(TEST_WARN);
147 let world = test_utils::virtual_world(source, &mut files, &library);
148
149 let Warned { output, warnings } = compile(&world, Warnings::Emit);
150 assert!(output.is_ok());
151 assert_eq!(warnings.len(), 1);
152 }
153
154 #[test]
155 fn test_compile_warn_promote_warnings() {
156 let mut files = VirtualFileProvider::new();
157 let library = LibraryProvider::new();
158 let source = Source::detached(TEST_WARN);
159 let world = test_utils::virtual_world(source, &mut files, &library);
160
161 let Warned { output, warnings } = compile(&world, Warnings::Promote);
162 assert_eq!(output.unwrap_err().0.len(), 1);
163 assert!(warnings.is_empty());
164 }
165
166 #[test]
167 fn test_compile_fail_ignore_warnings() {
168 let mut files = VirtualFileProvider::new();
169 let library = LibraryProvider::new();
170 let source = Source::detached(TEST_FAIL);
171 let world = test_utils::virtual_world(source, &mut files, &library);
172
173 let Warned { output, warnings } = compile(&world, Warnings::Ignore);
174 assert_eq!(output.unwrap_err().0.len(), 1);
175 assert!(warnings.is_empty());
176 }
177
178 #[test]
179 fn test_compile_fail_emit_warnings() {
180 let mut files = VirtualFileProvider::new();
181 let library = LibraryProvider::new();
182 let source = Source::detached(TEST_FAIL);
183 let world = test_utils::virtual_world(source, &mut files, &library);
184
185 let Warned { output, warnings } = compile(&world, Warnings::Emit);
186 assert_eq!(output.unwrap_err().0.len(), 1);
187 assert_eq!(warnings.len(), 1);
188 }
189
190 #[test]
191 fn test_compile_fail_promote_warnings() {
192 let mut files = VirtualFileProvider::new();
193 let library = LibraryProvider::new();
194 let source = Source::detached(TEST_FAIL);
195 let world = test_utils::virtual_world(source, &mut files, &library);
196
197 let Warned { output, warnings } = compile(&world, Warnings::Promote);
198 assert_eq!(output.unwrap_err().0.len(), 2);
199 assert!(warnings.is_empty());
200 }
201}