Fork of daniellemaywood.uk/gleam — Wasm codegen work
4.8 kB
131 lines
1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: 2024 The Gleam contributors
3
4use camino::{Utf8Path, Utf8PathBuf};
5use gleam_core::{
6 io::{Content, FileSystemWriter, memory::InMemoryFileSystem},
7 javascript,
8 version::COMPILER_VERSION,
9};
10use itertools::Itertools;
11use regex::Regex;
12use std::{collections::HashMap, fmt::Write, sync::LazyLock};
13
14#[derive(Debug)]
15pub struct TestCompileOutput {
16 pub files: HashMap<Utf8PathBuf, Content>,
17 pub warnings: Vec<gleam_core::Warning>,
18}
19
20impl TestCompileOutput {
21 pub fn as_overview_text(&self) -> String {
22 let mut buffer = String::new();
23 for (path, content) in self.files.iter().sorted_by(|a, b| a.0.cmp(b.0)) {
24 let normalised_path = if path.as_str().contains("cases") {
25 path.as_str()
26 .split("cases")
27 .skip(1)
28 .collect::<String>()
29 .as_str()
30 .replace('\\', "/")
31 .split('/')
32 .skip(1)
33 .join("/")
34 } else {
35 path.as_str().replace('\\', "/")
36 };
37 buffer.push_str("//// ");
38 buffer.push_str(&normalised_path);
39 buffer.push('\n');
40
41 let extension = path.extension();
42
43 match content {
44 _ if extension == Some("cache") => buffer.push_str("<.cache binary>"),
45
46 Content::Text(text) if text == javascript::PRELUDE => {
47 buffer.push_str("<javascript prelude>")
48 }
49
50 Content::Text(text) if text == javascript::PRELUDE_TS_DEF => {
51 buffer.push_str("<typescript prelude>")
52 }
53
54 Content::Binary(data) => write!(buffer, "<{} byte binary>", data.len()).unwrap(),
55
56 Content::Text(_) if normalised_path.ends_with("@@main.erl") => {
57 write!(buffer, "<erlang entrypoint>").unwrap()
58 }
59
60 Content::Text(text) => {
61 let format_path = |caps: ®ex::Captures| {
62 caps.get(1)
63 .expect("file path")
64 .as_str()
65 .replace("\\\\", "/")
66 };
67 let text = FILE_LINE_REGEX.replace_all(text, |caps: ®ex::Captures| {
68 let path = format_path(caps);
69 let line_number = caps.get(2).expect("line number").as_str();
70 format!("-file(\"{path}\", {line_number}).")
71 });
72 let text = FILEPATH_MACRO_REGEX
73 .replace_all(text.to_string().as_str(), |caps: ®ex::Captures| {
74 let path = format_path(caps);
75 format!("-define(FILEPATH, \"{path}\").")
76 })
77 .replace(COMPILER_VERSION, "<gleam compiler version string>");
78 buffer.push_str(&text)
79 }
80 };
81 buffer.push('\n');
82 buffer.push('\n');
83 }
84
85 for warning in self.warnings.iter().map(|w| w.to_pretty_string()).sorted() {
86 write!(buffer, "//// Warning\n{}", normalise_diagnostic(&warning)).unwrap();
87 buffer.push('\n');
88 buffer.push('\n');
89 }
90
91 buffer
92 }
93}
94
95pub fn to_in_memory_filesystem(path: &Utf8Path) -> InMemoryFileSystem {
96 let fs = InMemoryFileSystem::new();
97
98 let files = walkdir::WalkDir::new(path)
99 .follow_links(true)
100 .into_iter()
101 .filter_map(Result::ok)
102 .filter(|entry| entry.file_type().is_file())
103 .map(|entry| entry.into_path());
104
105 for fullpath in files {
106 let content = std::fs::read(&fullpath).unwrap();
107 let path = fullpath.strip_prefix(path).unwrap();
108 fs.write_bytes(Utf8Path::from_path(path).unwrap(), &content)
109 .unwrap();
110 }
111
112 fs
113}
114
115static FILE_LINE_REGEX: LazyLock<Regex> =
116 LazyLock::new(|| Regex::new(r#"-file\("([^"]+)", (\d+)\)\."#).expect("Invalid regex"));
117
118static FILEPATH_MACRO_REGEX: LazyLock<Regex> =
119 LazyLock::new(|| Regex::new(r#"-define\(FILEPATH, "([^"]+)"\)\."#).expect("Invalid regex"));
120
121pub fn normalise_diagnostic(text: &str) -> String {
122 // There is an extra ^ on Windows in some error messages' code
123 // snippets.
124 // I've not managed to determine why this is yet (it is especially
125 // tricky without a Windows computer) so for now we just squash them
126 // in these cross-platform tests.
127 Regex::new(r"\^+")
128 .expect("^ sequence regex")
129 .replace_all(text, "^")
130 .replace('\\', "/")
131}