tytanic_core/doc/
compile.rs

1//! Test document compilation and diagnostics handling.
2
3use 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/// How to handle warnings during compilation.
16#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
17pub enum Warnings {
18    /// Ignore all warnings.
19    Ignore,
20
21    /// Emit all warnings.
22    #[default]
23    Emit,
24
25    /// Promote all warnings to errors.
26    Promote,
27}
28
29/// An error which may occur during compilation. This struct only exists to
30/// implement [`Error`][trait@std::error::Error].
31#[derive(Debug, Clone, Error)]
32#[error("compilation failed with {} {}", .0.len(), Term::simple("error").with(.0.len()))]
33pub struct Error(pub EcoVec<SourceDiagnostic>);
34
35/// Compiles a test using the given test world.
36pub 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}