+13
Cargo.lock
+13
Cargo.lock
···
211
211
checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
212
212
213
213
[[package]]
214
+
name = "camino"
215
+
version = "1.1.6"
216
+
source = "registry+https://github.com/rust-lang/crates.io-index"
217
+
checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c"
218
+
dependencies = [
219
+
"serde",
220
+
]
221
+
222
+
[[package]]
214
223
name = "capnp"
215
224
version = "0.14.11"
216
225
source = "registry+https://github.com/rust-lang/crates.io-index"
···
708
717
"atty",
709
718
"base16",
710
719
"bytes",
720
+
"camino",
711
721
"clap",
712
722
"ctrlc",
713
723
"debug-ignore",
···
757
767
"base16",
758
768
"bincode",
759
769
"bytes",
770
+
"camino",
760
771
"capnp",
761
772
"capnpc",
762
773
"codespan-reporting",
···
801
812
name = "gleam-wasm"
802
813
version = "0.30.4"
803
814
dependencies = [
815
+
"camino",
804
816
"console_error_panic_hook",
805
817
"gleam-core",
806
818
"hexpm",
···
2107
2119
name = "test-package-compiler"
2108
2120
version = "0.30.4"
2109
2121
dependencies = [
2122
+
"camino",
2110
2123
"gleam-core",
2111
2124
"im",
2112
2125
"insta",
+1
compiler-cli/Cargo.toml
+1
compiler-cli/Cargo.toml
+4
compiler-cli/clippy.toml
+4
compiler-cli/clippy.toml
+3
-3
compiler-cli/src/add.rs
+3
-3
compiler-cli/src/add.rs
···
1
-
use std::path::{Path, PathBuf};
1
+
use camino::{Utf8Path, Utf8PathBuf};
2
2
3
3
use gleam_core::{
4
4
error::{FileIoAction, FileKind},
···
25
25
.map_err(|e| Error::FileIo {
26
26
kind: FileKind::File,
27
27
action: FileIoAction::Parse,
28
-
path: PathBuf::from("gleam.toml"),
28
+
path: Utf8PathBuf::from("gleam.toml"),
29
29
err: Some(e.to_string()),
30
30
})?;
31
31
···
56
56
}
57
57
58
58
// Write the updated config
59
-
fs::write(Path::new("gleam.toml"), &toml.to_string())?;
59
+
fs::write(Utf8Path::new("gleam.toml"), &toml.to_string())?;
60
60
61
61
Ok(())
62
62
}
+2
-2
compiler-cli/src/build.rs
+2
-2
compiler-cli/src/build.rs
···
11
11
build_lock::BuildLock,
12
12
cli,
13
13
dependencies::UseManifest,
14
-
fs::{self, ConsoleWarningEmitter},
14
+
fs::{self, get_current_directory, ConsoleWarningEmitter},
15
15
};
16
16
17
17
pub fn download_dependencies() -> Result<Manifest> {
···
31
31
options.mode,
32
32
options.target.unwrap_or(root_config.target),
33
33
)?;
34
-
let current_dir = std::env::current_dir().expect("Failed to get current directory");
34
+
let current_dir = get_current_directory().expect("Failed to get current directory");
35
35
36
36
tracing::info!("Compiling packages");
37
37
let compiled = {
+3
-3
compiler-cli/src/build_lock.rs
+3
-3
compiler-cli/src/build_lock.rs
···
1
+
use camino::Utf8PathBuf;
1
2
use gleam_core::{
2
3
build::{Mode, Target, Telemetry},
3
4
paths::ProjectPaths,
4
5
Result,
5
6
};
6
-
use std::path::PathBuf;
7
7
use strum::IntoEnumIterator;
8
8
9
9
#[derive(Debug)]
10
10
pub(crate) struct BuildLock {
11
-
directory: PathBuf,
11
+
directory: Utf8PathBuf,
12
12
}
13
13
14
14
impl BuildLock {
···
36
36
crate::fs::mkdir(&self.directory).expect("Could not create lock directory");
37
37
38
38
let lock_path = self.directory.join("gleam.lock");
39
-
let mut file = fslock::LockFile::open(&lock_path).expect("LockFile creation");
39
+
let mut file = fslock::LockFile::open(lock_path.as_str()).expect("LockFile creation");
40
40
41
41
if !file.try_lock_with_pid().expect("Trying build locking") {
42
42
telemetry.waiting_for_build_directory_lock();
+4
-2
compiler-cli/src/compile_package.rs
+4
-2
compiler-cli/src/compile_package.rs
···
3
3
fs::{self, ConsoleWarningEmitter, ProjectIO},
4
4
CompilePackage,
5
5
};
6
+
use camino::Utf8Path;
6
7
use gleam_core::{
7
8
build::{Mode, PackageCompiler, StaleTracker, Target, TargetCodegenConfiguration},
8
9
metadata,
···
13
14
Result,
14
15
};
15
16
use smol_str::SmolStr;
16
-
use std::{path::Path, sync::Arc};
17
+
use std::sync::Arc;
17
18
18
19
pub fn command(options: CompilePackage) -> Result<()> {
19
20
let ids = UniqueIdGenerator::new();
···
56
57
57
58
fn load_libraries(
58
59
ids: &UniqueIdGenerator,
59
-
lib: &Path,
60
+
lib: &Utf8Path,
60
61
) -> Result<im::HashMap<SmolStr, ModuleInterface>> {
61
62
tracing::info!("Reading precompiled module metadata files");
62
63
let mut manifests = im::HashMap::new();
···
71
72
let _ = manifests.insert(module.name.clone(), module);
72
73
}
73
74
}
75
+
74
76
Ok(manifests)
75
77
}
+5
-3
compiler-cli/src/config.rs
+5
-3
compiler-cli/src/config.rs
···
1
-
use std::path::PathBuf;
1
+
use camino::Utf8PathBuf;
2
2
3
3
use gleam_core::{
4
4
config::PackageConfig,
···
7
7
paths::ProjectPaths,
8
8
};
9
9
10
+
use crate::fs::get_current_directory;
11
+
10
12
pub fn root_config() -> Result<PackageConfig, Error> {
11
-
let current_dir = std::env::current_dir().expect("Could not get current directory");
13
+
let current_dir = get_current_directory().expect("Failed to get current directory");
12
14
let paths = ProjectPaths::new(current_dir);
13
15
read(paths.root_config())
14
16
}
···
53
55
}
54
56
}
55
57
56
-
pub fn read(config_path: PathBuf) -> Result<PackageConfig, Error> {
58
+
pub fn read(config_path: Utf8PathBuf) -> Result<PackageConfig, Error> {
57
59
let toml = crate::fs::read(&config_path)?;
58
60
let config: PackageConfig = toml::from_str(&toml).map_err(|e| Error::FileIo {
59
61
action: FileIoAction::Parse,
+21
-21
compiler-cli/src/dependencies.rs
+21
-21
compiler-cli/src/dependencies.rs
···
1
1
use std::{
2
2
collections::{HashMap, HashSet},
3
-
path::{Path, PathBuf},
4
3
time::Instant,
5
4
};
6
5
6
+
use camino::{Utf8Path, Utf8PathBuf};
7
7
use flate2::read::GzDecoder;
8
8
use futures::future;
9
9
use gleam_core::{
···
522
522
#[derive(Clone, Eq, Debug)]
523
523
enum ProvidedPackageSource {
524
524
Git { repo: SmolStr, commit: SmolStr },
525
-
Local { path: PathBuf },
525
+
Local { path: Utf8PathBuf },
526
526
}
527
527
528
528
impl ProvidedPackage {
···
587
587
format!(r#"{{ repo: "{}", commit: "{}" }}"#, repo, commit)
588
588
}
589
589
Self::Local { path } => {
590
-
format!(r#"{{ path: "{}" }}"#, path.to_string_lossy())
590
+
format!(r#"{{ path: "{}" }}"#, path)
591
591
}
592
592
}
593
593
}
···
689
689
/// Provide a package from a local project
690
690
fn provide_local_package(
691
691
package_name: SmolStr,
692
-
package_path: &Path,
693
-
parent_path: &Path,
692
+
package_path: &Utf8Path,
693
+
parent_path: &Utf8Path,
694
694
project_paths: &ProjectPaths,
695
695
provided: &mut HashMap<SmolStr, ProvidedPackage>,
696
696
parents: &mut Vec<SmolStr>,
···
730
730
/// Adds a gleam project located at a specific path to the list of "provided packages"
731
731
fn provide_package(
732
732
package_name: SmolStr,
733
-
package_path: PathBuf,
733
+
package_path: Utf8PathBuf,
734
734
package_source: ProvidedPackageSource,
735
735
project_paths: &ProjectPaths,
736
736
provided: &mut HashMap<SmolStr, ProvidedPackage>,
···
820
820
let project_paths = crate::project_paths_at_current_directory();
821
821
let result = provide_local_package(
822
822
"wrong_name".into(),
823
-
Path::new("./test/hello_world"),
824
-
Path::new("./"),
823
+
Utf8Path::new("./test/hello_world"),
824
+
Utf8Path::new("./"),
825
825
&project_paths,
826
826
&mut provided,
827
827
&mut vec!["root".into(), "subpackage".into()],
···
844
844
845
845
let result = provide_local_package(
846
846
"hello_world".into(),
847
-
Path::new("./test/hello_world"),
848
-
Path::new("./"),
847
+
Utf8Path::new("./test/hello_world"),
848
+
Utf8Path::new("./"),
849
849
&project_paths,
850
850
&mut provided,
851
851
&mut vec!["root".into(), "subpackage".into()],
···
857
857
858
858
let result = provide_local_package(
859
859
"hello_world".into(),
860
-
Path::new("./test/hello_world"),
861
-
Path::new("./"),
860
+
Utf8Path::new("./test/hello_world"),
861
+
Utf8Path::new("./"),
862
862
&project_paths,
863
863
&mut provided,
864
864
&mut vec!["root".into(), "subpackage".into()],
···
875
875
let project_paths = crate::project_paths_at_current_directory();
876
876
let result = provide_local_package(
877
877
"hello_world".into(),
878
-
Path::new("./test/hello_world"),
879
-
Path::new("./"),
878
+
Utf8Path::new("./test/hello_world"),
879
+
Utf8Path::new("./"),
880
880
&project_paths,
881
881
&mut provided,
882
882
&mut vec!["root".into(), "subpackage".into()],
···
888
888
889
889
let result = provide_package(
890
890
"hello_world".into(),
891
-
PathBuf::from("./test/other"),
891
+
Utf8PathBuf::from("./test/other"),
892
892
ProvidedPackageSource::Local {
893
-
path: Path::new("./test/other").to_path_buf(),
893
+
path: Utf8Path::new("./test/other").to_path_buf(),
894
894
},
895
895
&project_paths,
896
896
&mut provided,
···
909
909
let project_paths = crate::project_paths_at_current_directory();
910
910
let result = provide_local_package(
911
911
"hello_world".into(),
912
-
Path::new("./test/hello_world"),
913
-
Path::new("./"),
912
+
Utf8Path::new("./test/hello_world"),
913
+
Utf8Path::new("./"),
914
914
&project_paths,
915
915
&mut provided,
916
916
&mut vec!["root".into(), "subpackage".into()],
···
933
933
let project_paths = crate::project_paths_at_current_directory();
934
934
let result = provide_local_package(
935
935
"hello_world".into(),
936
-
Path::new("./test/hello_world"),
937
-
Path::new("./"),
936
+
Utf8Path::new("./test/hello_world"),
937
+
Utf8Path::new("./"),
938
938
&project_paths,
939
939
&mut provided,
940
940
&mut vec!["root".into(), "hello_world".into(), "subpackage".into()],
···
1006
1006
1007
1007
fn io_result_unpack(
1008
1008
&self,
1009
-
path: &Path,
1009
+
path: &Utf8Path,
1010
1010
mut archive: tar::Archive<GzDecoder<tar::Entry<'_, WrappedReader>>>,
1011
1011
) -> std::io::Result<()> {
1012
1012
archive.unpack(path)
+4
-3
compiler-cli/src/docs.rs
+4
-3
compiler-cli/src/docs.rs
···
1
-
use std::path::Path;
2
1
use std::time::Instant;
2
+
3
+
use camino::Utf8Path;
3
4
4
5
use crate::{cli, hex::ApiKeyCommand, http::HttpClient};
5
6
use gleam_core::{
···
84
85
println!(
85
86
"\nThe documentation for {package} has been rendered to \n{index_html}",
86
87
package = config.name,
87
-
index_html = index_html.to_string_lossy()
88
+
index_html = index_html
88
89
);
89
90
90
91
if options.open {
···
99
100
///
100
101
/// For the docs this will generally be a browser (unless some other program is
101
102
/// configured as the default for `.html` files).
102
-
fn open_docs(path: &Path) -> Result<()> {
103
+
fn open_docs(path: &Utf8Path) -> Result<()> {
103
104
opener::open(path).map_err(|error| Error::FailedToOpenDocs {
104
105
path: path.to_path_buf(),
105
106
error: error.to_string(),
+6
-6
compiler-cli/src/export.rs
+6
-6
compiler-cli/src/export.rs
···
46
46
continue;
47
47
}
48
48
49
-
let name = path.file_name().expect("Directory name").to_string_lossy();
50
-
let build = build.join(name.as_ref());
51
-
let out = out.join(name.as_ref());
49
+
let name = path.file_name().expect("Directory name");
50
+
let build = build.join(name);
51
+
let out = out.join(name);
52
52
crate::fs::mkdir(&out)?;
53
53
54
54
// Copy desired package subdirectories
···
79
79
80
80
{entrypoint}
81
81
",
82
-
path = out.to_string_lossy(),
83
-
entrypoint = entrypoint.to_string_lossy(),
82
+
path = out,
83
+
entrypoint = entrypoint,
84
84
);
85
85
86
86
Ok(())
···
97
97
"
98
98
Your hex tarball has been generated in {}.
99
99
",
100
-
&path.display()
100
+
&path
101
101
);
102
102
Ok(())
103
103
}
+5
-4
compiler-cli/src/fix.rs
+5
-4
compiler-cli/src/fix.rs
···
1
+
use camino::Utf8PathBuf;
1
2
use gleam_core::{
2
3
build::Target,
3
4
error::{FileIoAction, FileKind},
4
5
Error, Result,
5
6
};
6
-
use std::{path::PathBuf, str::FromStr};
7
+
use std::str::FromStr;
7
8
8
9
pub fn run(target: Option<Target>, files: Vec<String>) -> Result<()> {
9
10
let mut complete = true;
10
11
11
12
for file_path in files {
12
-
let path = PathBuf::from_str(&file_path).map_err(|e| Error::FileIo {
13
+
let path = Utf8PathBuf::from_str(&file_path).map_err(|e| Error::FileIo {
13
14
action: FileIoAction::Open,
14
15
kind: FileKind::File,
15
-
path: PathBuf::from(file_path),
16
+
path: Utf8PathBuf::from(file_path),
16
17
err: Some(e.to_string()),
17
18
})?;
18
19
···
35
36
Ok(())
36
37
}
37
38
38
-
fn fix_file(target: Option<Target>, path: PathBuf) -> Result<bool> {
39
+
fn fix_file(target: Option<Target>, path: Utf8PathBuf) -> Result<bool> {
39
40
let src = crate::fs::read(&path)?;
40
41
let (out, complete) = gleam_core::fix::parse_fix_and_format(target, &src.into(), &path)?;
41
42
crate::fs::write(&path, &out)?;
+9
-11
compiler-cli/src/format.rs
+9
-11
compiler-cli/src/format.rs
···
3
3
io::Content,
4
4
io::OutputFile,
5
5
};
6
-
use std::{
7
-
io::Read,
8
-
path::{Path, PathBuf},
9
-
str::FromStr,
10
-
};
6
+
use std::{io::Read, str::FromStr};
7
+
8
+
use camino::{Utf8Path, Utf8PathBuf};
11
9
12
10
pub fn run(stdin: bool, check: bool, files: Vec<String>) -> Result<()> {
13
11
if stdin {
···
20
18
fn process_stdin(check: bool) -> Result<()> {
21
19
let src = read_stdin()?.into();
22
20
let mut out = String::new();
23
-
gleam_core::format::pretty(&mut out, &src, Path::new("<stdin>"))?;
21
+
gleam_core::format::pretty(&mut out, &src, Utf8Path::new("<stdin>"))?;
24
22
25
23
if !check {
26
24
print!("{out}");
···
30
28
if src != out {
31
29
return Err(Error::Format {
32
30
problem_files: vec![Unformatted {
33
-
source: PathBuf::from("<standard input>"),
34
-
destination: PathBuf::from("<standard output>"),
31
+
source: Utf8PathBuf::from("<standard input>"),
32
+
destination: Utf8PathBuf::from("<standard output>"),
35
33
input: src,
36
34
output: out,
37
35
}],
···
73
71
let mut problem_files = Vec::with_capacity(files.len());
74
72
75
73
for file_path in files {
76
-
let path = PathBuf::from_str(&file_path).map_err(|e| Error::FileIo {
74
+
let path = Utf8PathBuf::from_str(&file_path).map_err(|e| Error::FileIo {
77
75
action: FileIoAction::Open,
78
76
kind: FileKind::File,
79
-
path: PathBuf::from(file_path),
77
+
path: Utf8PathBuf::from(file_path),
80
78
err: Some(e.to_string()),
81
79
})?;
82
80
···
92
90
Ok(problem_files)
93
91
}
94
92
95
-
fn format_file(problem_files: &mut Vec<Unformatted>, path: PathBuf) -> Result<()> {
93
+
fn format_file(problem_files: &mut Vec<Unformatted>, path: Utf8PathBuf) -> Result<()> {
96
94
let src = crate::fs::read(&path)?.into();
97
95
let mut output = String::new();
98
96
gleam_core::format::pretty(&mut output, &src, &path)?;
+117
-101
compiler-cli/src/fs.rs
+117
-101
compiler-cli/src/fs.rs
···
17
17
fmt::Debug,
18
18
fs::File,
19
19
io::{self, BufRead, BufReader, Write},
20
-
path::{Path, PathBuf},
21
20
time::SystemTime,
22
21
};
22
+
23
+
use camino::{ReadDirUtf8, Utf8Path, Utf8PathBuf};
23
24
24
25
use crate::{dependencies::UseManifest, lsp::LspLocker};
25
26
26
27
#[cfg(test)]
27
28
mod tests;
28
29
30
+
pub fn get_current_directory() -> Result<Utf8PathBuf, &'static str> {
31
+
let curr_dir = std::env::current_dir().map_err(|_| "Failed to get current directory")?;
32
+
Utf8PathBuf::from_path_buf(curr_dir).map_err(|_| "Non Utf8 Path")
33
+
}
34
+
29
35
/// A `FileWriter` implementation that writes to the file system.
30
36
#[derive(Debug, Clone, Copy)]
31
37
pub struct ProjectIO;
···
41
47
}
42
48
43
49
impl FileSystemReader for ProjectIO {
44
-
fn gleam_source_files(&self, dir: &Path) -> Vec<PathBuf> {
50
+
fn gleam_source_files(&self, dir: &Utf8Path) -> Vec<Utf8PathBuf> {
45
51
if !dir.is_dir() {
46
52
return vec![];
47
53
}
···
53
59
.filter(|e| e.file_type().is_file())
54
60
.map(|d| d.into_path())
55
61
.filter(move |d| d.extension() == Some(OsStr::new("gleam")))
62
+
.map(|pb| Utf8PathBuf::from_path_buf(pb).expect("Non Utf-8 Path"))
56
63
.collect()
57
64
}
58
65
59
-
fn gleam_cache_files(&self, dir: &Path) -> Vec<PathBuf> {
66
+
fn gleam_cache_files(&self, dir: &Utf8Path) -> Vec<Utf8PathBuf> {
60
67
if !dir.is_dir() {
61
68
return vec![];
62
69
}
···
68
75
.filter(|e| e.file_type().is_file())
69
76
.map(|d| d.into_path())
70
77
.filter(|p| p.extension().and_then(OsStr::to_str) == Some("cache"))
78
+
.map(|pb| Utf8PathBuf::from_path_buf(pb).expect("Non Utf-8 Path"))
71
79
.collect()
72
80
}
73
81
74
-
fn read(&self, path: &Path) -> Result<String, Error> {
82
+
fn read(&self, path: &Utf8Path) -> Result<String, Error> {
75
83
read(path)
76
84
}
77
85
78
-
fn read_bytes(&self, path: &Path) -> Result<Vec<u8>, Error> {
86
+
fn read_bytes(&self, path: &Utf8Path) -> Result<Vec<u8>, Error> {
79
87
read_bytes(path)
80
88
}
81
89
82
-
fn is_file(&self, path: &Path) -> bool {
90
+
fn is_file(&self, path: &Utf8Path) -> bool {
83
91
path.is_file()
84
92
}
85
93
86
-
fn is_directory(&self, path: &Path) -> bool {
94
+
fn is_directory(&self, path: &Utf8Path) -> bool {
87
95
path.is_dir()
88
96
}
89
97
90
-
fn reader(&self, path: &Path) -> Result<WrappedReader, Error> {
98
+
fn reader(&self, path: &Utf8Path) -> Result<WrappedReader, Error> {
91
99
reader(path)
92
100
}
93
101
94
-
fn read_dir(&self, path: &Path) -> Result<ReadDir> {
102
+
fn read_dir(&self, path: &Utf8Path) -> Result<ReadDir> {
95
103
read_dir(path).map(|entries| {
96
104
entries
97
105
.map(|result| result.map(|entry| DirEntry::from_path(entry.path())))
···
99
107
})
100
108
}
101
109
102
-
fn modification_time(&self, path: &Path) -> Result<SystemTime, Error> {
110
+
fn modification_time(&self, path: &Utf8Path) -> Result<SystemTime, Error> {
103
111
path.metadata()
104
112
.map(|m| m.modified().unwrap_or_else(|_| SystemTime::now()))
105
113
.map_err(|e| Error::FileIo {
···
110
118
})
111
119
}
112
120
113
-
fn canonicalise(&self, path: &Path) -> Result<PathBuf, Error> {
121
+
fn canonicalise(&self, path: &Utf8Path) -> Result<Utf8PathBuf, Error> {
114
122
canonicalise(path)
115
123
}
116
124
}
117
125
118
126
impl FileSystemWriter for ProjectIO {
119
-
fn delete(&self, path: &Path) -> Result<()> {
127
+
fn delete(&self, path: &Utf8Path) -> Result<()> {
120
128
delete_dir(path)
121
129
}
122
130
123
-
fn copy(&self, from: &Path, to: &Path) -> Result<()> {
131
+
fn copy(&self, from: &Utf8Path, to: &Utf8Path) -> Result<()> {
124
132
copy(from, to)
125
133
}
126
134
127
-
fn copy_dir(&self, from: &Path, to: &Path) -> Result<()> {
135
+
fn copy_dir(&self, from: &Utf8Path, to: &Utf8Path) -> Result<()> {
128
136
copy_dir(from, to)
129
137
}
130
138
131
-
fn mkdir(&self, path: &Path) -> Result<(), Error> {
139
+
fn mkdir(&self, path: &Utf8Path) -> Result<(), Error> {
132
140
mkdir(path)
133
141
}
134
142
135
-
fn hardlink(&self, from: &Path, to: &Path) -> Result<(), Error> {
143
+
fn hardlink(&self, from: &Utf8Path, to: &Utf8Path) -> Result<(), Error> {
136
144
hardlink(from, to)
137
145
}
138
146
139
-
fn symlink_dir(&self, from: &Path, to: &Path) -> Result<(), Error> {
147
+
fn symlink_dir(&self, from: &Utf8Path, to: &Utf8Path) -> Result<(), Error> {
140
148
symlink_dir(from, to)
141
149
}
142
150
143
-
fn delete_file(&self, path: &Path) -> Result<()> {
151
+
fn delete_file(&self, path: &Utf8Path) -> Result<()> {
144
152
delete_file(path)
145
153
}
146
154
147
-
fn write(&self, path: &Path, content: &str) -> Result<(), Error> {
155
+
fn write(&self, path: &Utf8Path, content: &str) -> Result<(), Error> {
148
156
write(path, content)
149
157
}
150
158
151
-
fn write_bytes(&self, path: &Path, content: &[u8]) -> Result<(), Error> {
159
+
fn write_bytes(&self, path: &Utf8Path, content: &[u8]) -> Result<(), Error> {
152
160
write_bytes(path, content)
153
161
}
154
162
}
···
159
167
program: &str,
160
168
args: &[String],
161
169
env: &[(&str, String)],
162
-
cwd: Option<&Path>,
170
+
cwd: Option<&Utf8Path>,
163
171
stdio: Stdio,
164
172
) -> Result<i32, Error> {
165
173
tracing::trace!(program=program, args=?args.join(" "), env=?env, cwd=?cwd, "command_exec");
···
168
176
.stdin(stdio.get_process_stdio())
169
177
.stdout(stdio.get_process_stdio())
170
178
.envs(env.iter().map(|(a, b)| (a, b)))
171
-
.current_dir(cwd.unwrap_or_else(|| Path::new("./")))
179
+
.current_dir(cwd.unwrap_or_else(|| Utf8Path::new("./")))
172
180
.status();
173
181
174
182
match result {
···
201
209
}
202
210
}
203
211
204
-
pub fn delete_dir(dir: &Path) -> Result<(), Error> {
212
+
pub fn delete_dir(dir: &Utf8Path) -> Result<(), Error> {
205
213
tracing::trace!(path=?dir, "deleting_directory");
206
214
if dir.exists() {
207
215
std::fs::remove_dir_all(dir).map_err(|e| Error::FileIo {
···
216
224
Ok(())
217
225
}
218
226
219
-
pub fn delete_file(file: &Path) -> Result<(), Error> {
227
+
pub fn delete_file(file: &Utf8Path) -> Result<(), Error> {
220
228
tracing::trace!("Deleting file {:?}", file);
221
229
if file.exists() {
222
230
std::fs::remove_file(file).map_err(|e| Error::FileIo {
···
231
239
Ok(())
232
240
}
233
241
234
-
pub fn write_outputs_under(outputs: &[OutputFile], base: &Path) -> Result<(), Error> {
242
+
pub fn write_outputs_under(outputs: &[OutputFile], base: &Utf8Path) -> Result<(), Error> {
235
243
for file in outputs {
236
244
let path = base.join(&file.path);
237
245
match &file.content {
···
250
258
}
251
259
}
252
260
253
-
pub fn write(path: &Path, text: &str) -> Result<(), Error> {
261
+
pub fn write(path: &Utf8Path, text: &str) -> Result<(), Error> {
254
262
write_bytes(path, text.as_bytes())
255
263
}
256
264
257
265
#[cfg(target_family = "unix")]
258
-
pub fn make_executable(path: impl AsRef<Path>) -> Result<(), Error> {
266
+
pub fn make_executable(path: impl AsRef<Utf8Path>) -> Result<(), Error> {
259
267
use std::os::unix::fs::PermissionsExt;
260
268
tracing::trace!(path = ?path.as_ref(), "setting_permissions");
261
269
···
271
279
}
272
280
273
281
#[cfg(not(target_family = "unix"))]
274
-
pub fn make_executable(_path: impl AsRef<Path>) -> Result<(), Error> {
282
+
pub fn make_executable(_path: impl AsRef<Utf8Path>) -> Result<(), Error> {
275
283
Ok(())
276
284
}
277
285
278
-
pub fn write_bytes(path: &Path, bytes: &[u8]) -> Result<(), Error> {
286
+
pub fn write_bytes(path: &Utf8Path, bytes: &[u8]) -> Result<(), Error> {
279
287
tracing::trace!(path=?path, "writing_file");
280
288
281
289
let dir_path = path.parent().ok_or_else(|| Error::FileIo {
···
308
316
Ok(())
309
317
}
310
318
311
-
fn is_gleam_path(path: &Path, dir: impl AsRef<Path>) -> bool {
319
+
fn is_gleam_path(path: &Utf8Path, dir: impl AsRef<Utf8Path>) -> bool {
312
320
use regex::Regex;
313
321
lazy_static! {
314
322
static ref RE: Regex = Regex::new(&format!(
···
320
328
}
321
329
322
330
RE.is_match(
323
-
path.strip_prefix(dir)
331
+
path.strip_prefix(dir.as_ref())
324
332
.expect("is_gleam_path(): strip_prefix")
325
-
.to_str()
326
-
.expect("is_gleam_path(): to_str"),
333
+
.as_str(),
327
334
)
328
335
}
329
336
330
-
pub fn gleam_files_excluding_gitignore(dir: &Path) -> impl Iterator<Item = PathBuf> + '_ {
337
+
pub fn gleam_files_excluding_gitignore(dir: &Utf8Path) -> impl Iterator<Item = Utf8PathBuf> + '_ {
331
338
ignore::WalkBuilder::new(dir)
332
339
.follow_links(true)
333
340
.require_git(false)
···
335
342
.filter_map(Result::ok)
336
343
.filter(|e| e.file_type().map(|t| t.is_file()).unwrap_or(false))
337
344
.map(ignore::DirEntry::into_path)
345
+
.map(|pb| Utf8PathBuf::from_path_buf(pb).expect("Non Utf-8 Path"))
338
346
.filter(move |d| is_gleam_path(d, dir))
339
347
}
340
348
341
-
pub fn native_files(dir: &Path) -> Result<impl Iterator<Item = PathBuf> + '_> {
349
+
pub fn native_files(dir: &Utf8Path) -> Result<impl Iterator<Item = Utf8PathBuf> + '_> {
342
350
Ok(read_dir(dir)?
343
351
.flat_map(Result::ok)
344
-
.map(|e| e.path())
352
+
.map(|e| e.into_path())
345
353
.filter(|path| {
346
-
let extension = path
347
-
.extension()
348
-
.unwrap_or_default()
349
-
.to_str()
350
-
.unwrap_or_default();
354
+
let extension = path.extension().unwrap_or_default();
351
355
matches!(extension, "erl" | "hrl" | "ex" | "js" | "mjs" | "ts")
352
356
}))
353
357
}
354
358
355
-
pub fn private_files_excluding_gitignore(dir: &Path) -> impl Iterator<Item = PathBuf> + '_ {
359
+
pub fn private_files_excluding_gitignore(dir: &Utf8Path) -> impl Iterator<Item = Utf8PathBuf> + '_ {
356
360
ignore::WalkBuilder::new(dir)
357
361
.follow_links(true)
358
362
.require_git(false)
···
360
364
.filter_map(Result::ok)
361
365
.filter(|e| e.file_type().map(|t| t.is_file()).unwrap_or(false))
362
366
.map(ignore::DirEntry::into_path)
367
+
.map(|pb| Utf8PathBuf::from_path_buf(pb).expect("Non Utf-8 Path"))
363
368
}
364
369
365
-
pub fn erlang_files(dir: &Path) -> Result<impl Iterator<Item = PathBuf> + '_> {
370
+
pub fn erlang_files(dir: &Utf8Path) -> Result<impl Iterator<Item = Utf8PathBuf> + '_> {
366
371
Ok(read_dir(dir)?
367
372
.flat_map(Result::ok)
368
-
.map(|e| e.path())
373
+
.map(|e| e.into_path())
369
374
.filter(|path| {
370
-
let extension = path
371
-
.extension()
372
-
.unwrap_or_default()
373
-
.to_str()
374
-
.unwrap_or_default();
375
+
let extension = path.extension().unwrap_or_default();
375
376
extension == "erl" || extension == "hrl"
376
377
}))
377
378
}
···
405
406
.map_err(|e| Error::Gzip(e.to_string()))
406
407
}
407
408
408
-
pub fn mkdir(path: impl AsRef<Path> + Debug) -> Result<(), Error> {
409
+
pub fn mkdir(path: impl AsRef<Utf8Path> + Debug) -> Result<(), Error> {
409
410
if path.as_ref().exists() {
410
411
return Ok(());
411
412
}
412
413
413
414
tracing::trace!(path=?path, "creating_directory");
414
415
415
-
std::fs::create_dir_all(&path).map_err(|err| Error::FileIo {
416
+
std::fs::create_dir_all(path.as_ref()).map_err(|err| Error::FileIo {
416
417
kind: FileKind::Directory,
417
-
path: PathBuf::from(path.as_ref()),
418
+
path: Utf8PathBuf::from(path.as_ref()),
418
419
action: FileIoAction::Create,
419
420
err: Some(err.to_string()),
420
421
})
421
422
}
422
423
423
-
pub fn read_dir(path: impl AsRef<Path> + Debug) -> Result<std::fs::ReadDir, Error> {
424
+
pub fn read_dir(path: impl AsRef<Utf8Path> + Debug) -> Result<ReadDirUtf8, Error> {
424
425
tracing::trace!(path=?path,"reading_directory");
425
426
426
-
std::fs::read_dir(&path).map_err(|e| Error::FileIo {
427
+
Utf8Path::read_dir_utf8(path.as_ref()).map_err(|e| Error::FileIo {
427
428
action: FileIoAction::Read,
428
429
kind: FileKind::Directory,
429
-
path: PathBuf::from(path.as_ref()),
430
+
path: Utf8PathBuf::from(path.as_ref()),
430
431
err: Some(e.to_string()),
431
432
})
432
433
}
433
434
434
435
pub fn module_caches_paths(
435
-
path: impl AsRef<Path> + Debug,
436
-
) -> Result<impl Iterator<Item = PathBuf>, Error> {
436
+
path: impl AsRef<Utf8Path> + Debug,
437
+
) -> Result<impl Iterator<Item = Utf8PathBuf>, Error> {
437
438
Ok(read_dir(path)?
438
439
.filter_map(Result::ok)
439
-
.map(|f| f.path())
440
-
.filter(|p| p.extension().and_then(OsStr::to_str) == Some("cache")))
440
+
.map(|f| f.into_path())
441
+
.filter(|p| p.extension() == Some("cache")))
441
442
}
442
443
443
-
pub fn read(path: impl AsRef<Path> + Debug) -> Result<String, Error> {
444
+
pub fn read(path: impl AsRef<Utf8Path> + Debug) -> Result<String, Error> {
444
445
tracing::trace!(path=?path,"reading_file");
445
446
446
-
std::fs::read_to_string(&path).map_err(|err| Error::FileIo {
447
+
std::fs::read_to_string(path.as_ref()).map_err(|err| Error::FileIo {
447
448
action: FileIoAction::Read,
448
449
kind: FileKind::File,
449
-
path: PathBuf::from(path.as_ref()),
450
+
path: Utf8PathBuf::from(path.as_ref()),
450
451
err: Some(err.to_string()),
451
452
})
452
453
}
453
454
454
-
pub fn read_bytes(path: impl AsRef<Path> + Debug) -> Result<Vec<u8>, Error> {
455
+
pub fn read_bytes(path: impl AsRef<Utf8Path> + Debug) -> Result<Vec<u8>, Error> {
455
456
tracing::trace!(path=?path,"reading_file");
456
457
457
-
std::fs::read(&path).map_err(|err| Error::FileIo {
458
+
std::fs::read(path.as_ref()).map_err(|err| Error::FileIo {
458
459
action: FileIoAction::Read,
459
460
kind: FileKind::File,
460
-
path: PathBuf::from(path.as_ref()),
461
+
path: Utf8PathBuf::from(path.as_ref()),
461
462
err: Some(err.to_string()),
462
463
})
463
464
}
464
465
465
-
pub fn reader(path: impl AsRef<Path> + Debug) -> Result<WrappedReader, Error> {
466
+
pub fn reader(path: impl AsRef<Utf8Path> + Debug) -> Result<WrappedReader, Error> {
466
467
tracing::trace!(path=?path,"opening_file_reader");
467
468
468
-
let reader = File::open(&path).map_err(|err| Error::FileIo {
469
+
let reader = File::open(path.as_ref()).map_err(|err| Error::FileIo {
469
470
action: FileIoAction::Open,
470
471
kind: FileKind::File,
471
-
path: PathBuf::from(path.as_ref()),
472
+
path: Utf8PathBuf::from(path.as_ref()),
472
473
err: Some(err.to_string()),
473
474
})?;
474
475
475
476
Ok(WrappedReader::new(path.as_ref(), Box::new(reader)))
476
477
}
477
478
478
-
pub fn buffered_reader<P: AsRef<Path> + Debug>(path: P) -> Result<impl BufRead, Error> {
479
+
pub fn buffered_reader<P: AsRef<Utf8Path> + Debug>(path: P) -> Result<impl BufRead, Error> {
479
480
tracing::trace!(path=?path,"opening_file_buffered_reader");
480
-
let reader = File::open(&path).map_err(|err| Error::FileIo {
481
+
let reader = File::open(path.as_ref()).map_err(|err| Error::FileIo {
481
482
action: FileIoAction::Open,
482
483
kind: FileKind::File,
483
-
path: PathBuf::from(path.as_ref()),
484
+
path: Utf8PathBuf::from(path.as_ref()),
484
485
err: Some(err.to_string()),
485
486
})?;
486
487
Ok(BufReader::new(reader))
487
488
}
488
489
489
-
pub fn copy(path: impl AsRef<Path> + Debug, to: impl AsRef<Path> + Debug) -> Result<(), Error> {
490
+
pub fn copy(
491
+
path: impl AsRef<Utf8Path> + Debug,
492
+
to: impl AsRef<Utf8Path> + Debug,
493
+
) -> Result<(), Error> {
490
494
tracing::trace!(from=?path, to=?to, "copying_file");
491
495
492
496
// TODO: include the destination in the error message
493
-
std::fs::copy(&path, &to)
497
+
std::fs::copy(path.as_ref(), to.as_ref())
494
498
.map_err(|err| Error::FileIo {
495
499
action: FileIoAction::Copy,
496
500
kind: FileKind::File,
497
-
path: PathBuf::from(path.as_ref()),
501
+
path: Utf8PathBuf::from(path.as_ref()),
498
502
err: Some(err.to_string()),
499
503
})
500
504
.map(|_| ())
501
505
}
502
506
503
-
// pub fn rename(path: impl AsRef<Path> + Debug, to: impl AsRef<Path> + Debug) -> Result<(), Error> {
507
+
// pub fn rename(path: impl AsRef<Utf8Path> + Debug, to: impl AsRef<Utf8Path> + Debug) -> Result<(), Error> {
504
508
// tracing::trace!(from=?path, to=?to, "renaming_file");
505
509
506
510
// // TODO: include the destination in the error message
···
508
512
// .map_err(|err| Error::FileIo {
509
513
// action: FileIoAction::Rename,
510
514
// kind: FileKind::File,
511
-
// path: PathBuf::from(path.as_ref()),
515
+
// path: Utf8PathBuf::from(path.as_ref()),
512
516
// err: Some(err.to_string()),
513
517
// })
514
518
// .map(|_| ())
515
519
// }
516
520
517
-
pub fn copy_dir(path: impl AsRef<Path> + Debug, to: impl AsRef<Path> + Debug) -> Result<(), Error> {
521
+
pub fn copy_dir(
522
+
path: impl AsRef<Utf8Path> + Debug,
523
+
to: impl AsRef<Utf8Path> + Debug,
524
+
) -> Result<(), Error> {
518
525
tracing::trace!(from=?path, to=?to, "copying_directory");
519
526
520
527
// TODO: include the destination in the error message
521
-
fs_extra::dir::copy(&path, &to, &fs_extra::dir::CopyOptions::new())
522
-
.map_err(|err| Error::FileIo {
523
-
action: FileIoAction::Copy,
524
-
kind: FileKind::Directory,
525
-
path: PathBuf::from(path.as_ref()),
526
-
err: Some(err.to_string()),
527
-
})
528
-
.map(|_| ())
528
+
fs_extra::dir::copy(
529
+
path.as_ref(),
530
+
to.as_ref(),
531
+
&fs_extra::dir::CopyOptions::new(),
532
+
)
533
+
.map_err(|err| Error::FileIo {
534
+
action: FileIoAction::Copy,
535
+
kind: FileKind::Directory,
536
+
path: Utf8PathBuf::from(path.as_ref()),
537
+
err: Some(err.to_string()),
538
+
})
539
+
.map(|_| ())
529
540
}
530
541
531
542
pub fn symlink_dir(
532
-
src: impl AsRef<Path> + Debug,
533
-
dest: impl AsRef<Path> + Debug,
543
+
src: impl AsRef<Utf8Path> + Debug,
544
+
dest: impl AsRef<Utf8Path> + Debug,
534
545
) -> Result<(), Error> {
535
546
tracing::trace!(src=?src, dest=?dest, "symlinking");
536
547
symlink::symlink_dir(canonicalise(src.as_ref())?, dest.as_ref()).map_err(|err| {
537
548
Error::FileIo {
538
549
action: FileIoAction::Link,
539
550
kind: FileKind::File,
540
-
path: PathBuf::from(dest.as_ref()),
551
+
path: Utf8PathBuf::from(dest.as_ref()),
541
552
err: Some(err.to_string()),
542
553
}
543
554
})?;
544
555
Ok(())
545
556
}
546
557
547
-
pub fn hardlink(from: impl AsRef<Path> + Debug, to: impl AsRef<Path> + Debug) -> Result<(), Error> {
558
+
pub fn hardlink(
559
+
from: impl AsRef<Utf8Path> + Debug,
560
+
to: impl AsRef<Utf8Path> + Debug,
561
+
) -> Result<(), Error> {
548
562
tracing::trace!(from=?from, to=?to, "hardlinking");
549
-
std::fs::hard_link(&from, &to)
563
+
std::fs::hard_link(from.as_ref(), to.as_ref())
550
564
.map_err(|err| Error::FileIo {
551
565
action: FileIoAction::Link,
552
566
kind: FileKind::File,
553
-
path: PathBuf::from(from.as_ref()),
567
+
path: Utf8PathBuf::from(from.as_ref()),
554
568
err: Some(err.to_string()),
555
569
})
556
570
.map(|_| ())
···
561
575
/// given path. If git is not installed then we assume we're not in a git work
562
576
/// tree.
563
577
///
564
-
pub fn is_inside_git_work_tree(path: &Path) -> Result<bool, Error> {
578
+
pub fn is_inside_git_work_tree(path: &Utf8Path) -> Result<bool, Error> {
565
579
tracing::trace!(path=?path, "checking_for_git_repo");
566
580
567
581
let args: Vec<&str> = vec!["rev-parse", "--is-inside-work-tree", "--quiet"];
···
592
606
593
607
/// Run `git init` in the given path.
594
608
/// If git is not installed then we do nothing.
595
-
pub fn git_init(path: &Path) -> Result<(), Error> {
609
+
pub fn git_init(path: &Utf8Path) -> Result<(), Error> {
596
610
tracing::trace!(path=?path, "initializing git");
597
611
598
612
if is_inside_git_work_tree(path)? {
···
600
614
return Ok(());
601
615
}
602
616
603
-
let args = vec!["init".into(), "--quiet".into(), path.display().to_string()];
617
+
let args = vec!["init".into(), "--quiet".into(), path.to_string()];
604
618
605
619
match ProjectIO::new().exec("git", &args, &[], None, Stdio::Inherit) {
606
620
Ok(_) => Ok(()),
···
613
627
}
614
628
}
615
629
616
-
pub fn canonicalise(path: &Path) -> Result<PathBuf, Error> {
617
-
std::fs::canonicalize(path).map_err(|err| Error::FileIo {
618
-
action: FileIoAction::Canonicalise,
619
-
kind: FileKind::File,
620
-
path: PathBuf::from(path),
621
-
err: Some(err.to_string()),
622
-
})
630
+
pub fn canonicalise(path: &Utf8Path) -> Result<Utf8PathBuf, Error> {
631
+
std::fs::canonicalize(path)
632
+
.map_err(|err| Error::FileIo {
633
+
action: FileIoAction::Canonicalise,
634
+
kind: FileKind::File,
635
+
path: Utf8PathBuf::from(path),
636
+
err: Some(err.to_string()),
637
+
})
638
+
.map(|pb| Utf8PathBuf::from_path_buf(pb).expect("Non Utf8 Path"))
623
639
}
624
640
625
641
#[derive(Debug, Clone, Copy)]
+21
-14
compiler-cli/src/fs/tests.rs
+21
-14
compiler-cli/src/fs/tests.rs
···
1
-
use std::path::Path;
1
+
use camino::Utf8Path;
2
2
3
3
#[test]
4
4
fn is_inside_git_work_tree_ok() {
5
5
let tmp_dir = tempfile::tempdir().unwrap();
6
-
let path = tmp_dir.path();
6
+
let path = Utf8Path::from_path(tmp_dir.path()).expect("Non Utf-8 Path");
7
7
8
8
assert!(!super::is_inside_git_work_tree(path).unwrap());
9
9
assert_eq!(super::git_init(path), Ok(()));
···
13
13
#[test]
14
14
fn git_init_success() {
15
15
let tmp_dir = tempfile::tempdir().unwrap();
16
-
let path = tmp_dir.path();
16
+
let path = Utf8Path::from_path(tmp_dir.path()).expect("Non Utf-8 Path");
17
17
let git = path.join(".git");
18
18
19
19
assert!(!git.exists());
···
24
24
#[test]
25
25
fn git_init_already_in_git() {
26
26
let tmp_dir = tempfile::tempdir().unwrap();
27
-
let git = tmp_dir.path().join(".git");
27
+
let git = Utf8Path::from_path(tmp_dir.path())
28
+
.expect("Non Utf-8 Path")
29
+
.join(".git");
28
30
assert!(!git.exists());
29
-
assert_eq!(super::git_init(tmp_dir.path()), Ok(()));
31
+
assert_eq!(
32
+
super::git_init(Utf8Path::from_path(tmp_dir.path()).expect("Non Utf-8 Path")),
33
+
Ok(())
34
+
);
30
35
assert!(git.exists());
31
36
32
-
let sub = tmp_dir.path().join("subproject");
37
+
let sub = Utf8Path::from_path(tmp_dir.path())
38
+
.expect("Non Utf-8 Path")
39
+
.join("subproject");
33
40
let git = sub.join(".git");
34
41
crate::fs::mkdir(&sub).unwrap();
35
42
assert!(!git.exists());
···
40
47
#[test]
41
48
fn is_gleam_path_test() {
42
49
assert!(super::is_gleam_path(
43
-
Path::new("/some-prefix/a.gleam"),
44
-
Path::new("/some-prefix/")
50
+
Utf8Path::new("/some-prefix/a.gleam"),
51
+
Utf8Path::new("/some-prefix/")
45
52
));
46
53
47
54
assert!(super::is_gleam_path(
48
-
Path::new("/some-prefix/one_two/a.gleam"),
49
-
Path::new("/some-prefix/")
55
+
Utf8Path::new("/some-prefix/one_two/a.gleam"),
56
+
Utf8Path::new("/some-prefix/")
50
57
));
51
58
52
59
assert!(super::is_gleam_path(
53
-
Path::new("/some-prefix/one_two/a123.gleam"),
54
-
Path::new("/some-prefix/")
60
+
Utf8Path::new("/some-prefix/one_two/a123.gleam"),
61
+
Utf8Path::new("/some-prefix/")
55
62
));
56
63
57
64
assert!(super::is_gleam_path(
58
-
Path::new("/some-prefix/one_2/a123.gleam"),
59
-
Path::new("/some-prefix/")
65
+
Utf8Path::new("/some-prefix/one_2/a123.gleam"),
66
+
Utf8Path::new("/some-prefix/")
60
67
));
61
68
}
+6
-5
compiler-cli/src/main.rs
+6
-5
compiler-cli/src/main.rs
···
74
74
75
75
use config::root_config;
76
76
use dependencies::UseManifest;
77
+
use fs::get_current_directory;
77
78
pub use gleam_core::{
78
79
error::{Error, Result},
79
80
warning::Warning,
···
87
88
};
88
89
use hex::ApiKeyCommand as _;
89
90
90
-
use std::path::PathBuf;
91
+
use camino::Utf8PathBuf;
91
92
92
93
use clap::{Args, Parser, Subcommand};
93
94
use strum::VariantNames;
···
283
284
284
285
/// The directory of the Gleam package
285
286
#[clap(long = "package")]
286
-
package_directory: PathBuf,
287
+
package_directory: Utf8PathBuf,
287
288
288
289
/// A directory to write compiled package to
289
290
#[clap(long = "out")]
290
-
output_directory: PathBuf,
291
+
output_directory: Utf8PathBuf,
291
292
292
293
/// A directories of precompiled Gleam projects
293
294
#[clap(long = "lib")]
294
-
libraries_directory: PathBuf,
295
+
libraries_directory: Utf8PathBuf,
295
296
296
297
/// Skip Erlang to BEAM bytecode compilation if given
297
298
#[clap(long = "no-beam")]
···
520
521
}
521
522
522
523
fn project_paths_at_current_directory() -> ProjectPaths {
523
-
let current_dir = std::env::current_dir().expect("Could not get current directory");
524
+
let current_dir = get_current_directory().expect("Failed to get current directory");
524
525
ProjectPaths::new(current_dir)
525
526
}
526
527
+10
-11
compiler-cli/src/new.rs
+10
-11
compiler-cli/src/new.rs
···
1
+
use camino::{Utf8Path, Utf8PathBuf};
1
2
use gleam_core::{
2
3
erlang,
3
4
error::{Error, FileIoAction, FileKind, InvalidProjectNameReason},
···
5
6
};
6
7
use serde::{Deserialize, Serialize};
7
8
use std::fs::File;
8
-
use std::path::{Path, PathBuf};
9
9
use std::{env, io::Write};
10
10
use strum::{Display, EnumString, EnumVariantNames};
11
11
···
28
28
29
29
#[derive(Debug)]
30
30
pub struct Creator {
31
-
root: PathBuf,
32
-
src: PathBuf,
33
-
test: PathBuf,
34
-
github: PathBuf,
35
-
workflows: PathBuf,
31
+
root: Utf8PathBuf,
32
+
src: Utf8PathBuf,
33
+
test: Utf8PathBuf,
34
+
github: Utf8PathBuf,
35
+
workflows: Utf8PathBuf,
36
36
gleam_version: &'static str,
37
37
options: NewOptions,
38
38
project_name: String,
···
51
51
validate_name(&project_name)?;
52
52
validate_root_folder(&options.project_root)?;
53
53
54
-
let root = PathBuf::from(&options.project_root);
54
+
let root = Utf8PathBuf::from(&options.project_root);
55
55
let src = root.join("src");
56
56
let test = root.join("test");
57
57
let github = root.join(".github");
···
266
266
Ok(())
267
267
}
268
268
269
-
fn write(path: PathBuf, contents: &str) -> Result<()> {
269
+
fn write(path: Utf8PathBuf, contents: &str) -> Result<()> {
270
270
let mut f = File::create(&path).map_err(|err| Error::FileIo {
271
271
kind: FileKind::File,
272
272
path: path.clone(),
···
285
285
}
286
286
287
287
fn validate_root_folder(name: &str) -> Result<(), Error> {
288
-
if Path::new(name).exists() {
288
+
if Utf8Path::new(name).exists() {
289
289
Err(Error::ProjectRootAlreadyExist {
290
290
path: name.to_string(),
291
291
})
···
343
343
.ok_or(Error::UnableToFindProjectRoot {
344
344
path: path.to_string(),
345
345
}),
346
-
_ => Path::new(path)
346
+
_ => Utf8Path::new(path)
347
347
.file_name()
348
-
.and_then(|x| x.to_str())
349
348
.map(ToString::to_string)
350
349
.ok_or(Error::UnableToFindProjectRoot {
351
350
path: path.to_string(),
+14
-12
compiler-cli/src/new/tests.rs
+14
-12
compiler-cli/src/new/tests.rs
···
1
+
use camino::Utf8PathBuf;
2
+
1
3
#[test]
2
4
fn new() {
3
5
let tmp = tempfile::tempdir().unwrap();
4
-
let path = tmp.path().join("my_project");
6
+
let path = Utf8PathBuf::from_path_buf(tmp.path().join("my_project")).expect("Non Utf8 Path");
5
7
6
8
let creator = super::Creator::new(
7
9
super::NewOptions {
8
-
project_root: path.to_str().unwrap().to_string(),
10
+
project_root: path.to_string(),
9
11
template: super::Template::Lib,
10
12
name: None,
11
13
description: "Wibble wobble".into(),
···
33
35
#[test]
34
36
fn new_with_skip_git() {
35
37
let tmp = tempfile::tempdir().unwrap();
36
-
let path = tmp.path().join("my_project");
38
+
let path = Utf8PathBuf::from_path_buf(tmp.path().join("my_project")).expect("Non Utf8 Path");
37
39
38
40
let creator = super::Creator::new(
39
41
super::NewOptions {
40
-
project_root: path.to_str().unwrap().to_string(),
42
+
project_root: path.to_string(),
41
43
template: super::Template::Lib,
42
44
name: None,
43
45
description: "Wibble wobble".into(),
···
55
57
#[test]
56
58
fn new_with_skip_github() {
57
59
let tmp = tempfile::tempdir().unwrap();
58
-
let path = tmp.path().join("my_project");
60
+
let path = Utf8PathBuf::from_path_buf(tmp.path().join("my_project")).expect("Non Utf8 Path");
59
61
60
62
let creator = super::Creator::new(
61
63
super::NewOptions {
62
-
project_root: path.to_str().unwrap().to_string(),
64
+
project_root: path.to_string(),
63
65
template: super::Template::Lib,
64
66
name: None,
65
67
description: "Wibble wobble".into(),
···
80
82
#[test]
81
83
fn new_with_skip_git_and_github() {
82
84
let tmp = tempfile::tempdir().unwrap();
83
-
let path = tmp.path().join("my_project");
85
+
let path = Utf8PathBuf::from_path_buf(tmp.path().join("my_project")).expect("Non Utf8 Path");
84
86
85
87
let creator = super::Creator::new(
86
88
super::NewOptions {
87
-
project_root: path.to_str().unwrap().to_string(),
89
+
project_root: path.to_string(),
88
90
template: super::Template::Lib,
89
91
name: None,
90
92
description: "Wibble wobble".into(),
···
105
107
#[test]
106
108
fn invalid_path() {
107
109
let tmp = tempfile::tempdir().unwrap();
108
-
let path = tmp.path().join("-------");
110
+
let path = Utf8PathBuf::from_path_buf(tmp.path().join("-------")).expect("Non Utf8 Path");
109
111
110
112
assert!(super::Creator::new(
111
113
super::NewOptions {
112
-
project_root: path.to_str().unwrap().to_string(),
114
+
project_root: path.to_string(),
113
115
template: super::Template::Lib,
114
116
name: None,
115
117
description: "Wibble wobble".into(),
···
124
126
#[test]
125
127
fn invalid_name() {
126
128
let tmp = tempfile::tempdir().unwrap();
127
-
let path = tmp.path().join("projec");
129
+
let path = Utf8PathBuf::from_path_buf(tmp.path().join("projec")).expect("Non Utf8 Path");
128
130
129
131
assert!(super::Creator::new(
130
132
super::NewOptions {
131
-
project_root: path.to_str().unwrap().to_string(),
133
+
project_root: path.to_string(),
132
134
template: super::Template::Lib,
133
135
name: Some("-".into()),
134
136
description: "Wibble wobble".into(),
+34
-33
compiler-cli/src/publish.rs
+34
-33
compiler-cli/src/publish.rs
···
1
+
use camino::{Utf8Path, Utf8PathBuf};
1
2
use flate2::{write::GzEncoder, Compression};
2
3
use gleam_core::{
3
4
build::{Codegen, Mode, Options, Package, Target},
···
10
11
use hexpm::version::{Range, Version};
11
12
use itertools::Itertools;
12
13
use sha2::Digest;
13
-
use std::{
14
-
io::Write,
15
-
path::{Path, PathBuf},
16
-
time::Instant,
17
-
};
14
+
use std::{io::Write, time::Instant};
18
15
19
16
use crate::{build, cli, docs, fs, hex::ApiKeyCommand, http::HttpClient};
20
17
···
48
45
if !generated_files_added.is_empty() {
49
46
println!("\nGenerated files:");
50
47
for file in generated_files_added.iter().sorted() {
51
-
println!(" - {}", file.0.to_string_lossy());
48
+
println!(" - {}", file.0);
52
49
}
53
50
}
54
51
println!("\nSource files:");
55
52
for file in src_files_added.iter().sorted() {
56
-
println!(" - {}", file.to_string_lossy());
53
+
println!(" - {}", file);
57
54
}
58
55
println!("\nName: {}", config.name);
59
56
println!("Version: {}", config.version);
···
115
112
struct Tarball {
116
113
compile_result: Package,
117
114
data: Vec<u8>,
118
-
src_files_added: Vec<PathBuf>,
119
-
generated_files_added: Vec<(PathBuf, String)>,
115
+
src_files_added: Vec<Utf8PathBuf>,
116
+
generated_files_added: Vec<(Utf8PathBuf, String)>,
120
117
}
121
118
122
119
pub fn build_hex_tarball(paths: &ProjectPaths, config: &PackageConfig) -> Result<Vec<u8>> {
···
189
186
190
187
fn metadata_config<'a>(
191
188
config: &'a PackageConfig,
192
-
source_files: &[PathBuf],
193
-
generated_files: &[(PathBuf, String)],
189
+
source_files: &[Utf8PathBuf],
190
+
generated_files: &[(Utf8PathBuf, String)],
194
191
) -> Result<String> {
195
192
let repo_url = http::Uri::try_from(config.repository.url().unwrap_or_default()).ok();
196
193
let requirements: Result<Vec<ReleaseRequirement<'a>>> = config
···
227
224
Ok(metadata)
228
225
}
229
226
230
-
fn contents_tarball(files: &[PathBuf], data_files: &[(PathBuf, String)]) -> Result<Vec<u8>, Error> {
227
+
fn contents_tarball(
228
+
files: &[Utf8PathBuf],
229
+
data_files: &[(Utf8PathBuf, String)],
230
+
) -> Result<Vec<u8>, Error> {
231
231
let mut contents_tar_gz = Vec::new();
232
232
{
233
233
let mut tarball =
···
246
246
247
247
// TODO: test
248
248
// TODO: Don't include git-ignored native files
249
-
fn project_files() -> Result<Vec<PathBuf>> {
250
-
let src = Path::new("src");
251
-
let mut files: Vec<PathBuf> = fs::gleam_files_excluding_gitignore(src)
249
+
fn project_files() -> Result<Vec<Utf8PathBuf>> {
250
+
let src = Utf8Path::new("src");
251
+
let mut files: Vec<Utf8PathBuf> = fs::gleam_files_excluding_gitignore(src)
252
252
.chain(fs::native_files(src)?)
253
253
.collect();
254
-
let private = Path::new("priv");
255
-
let mut private_files: Vec<PathBuf> = fs::private_files_excluding_gitignore(private).collect();
254
+
let private = Utf8Path::new("priv");
255
+
let mut private_files: Vec<Utf8PathBuf> =
256
+
fs::private_files_excluding_gitignore(private).collect();
256
257
files.append(&mut private_files);
257
258
let mut add = |path| {
258
-
let path = PathBuf::from(path);
259
+
let path = Utf8PathBuf::from(path);
259
260
if path.exists() {
260
261
files.push(path);
261
262
}
···
277
278
}
278
279
279
280
// TODO: test
280
-
fn generated_files(paths: &ProjectPaths, package: &Package) -> Result<Vec<(PathBuf, String)>> {
281
+
fn generated_files(paths: &ProjectPaths, package: &Package) -> Result<Vec<(Utf8PathBuf, String)>> {
281
282
let mut files = vec![];
282
283
283
284
let dir = paths.build_directory_for_package(Mode::Prod, Target::Erlang, &package.config.name);
···
285
286
let build = dir.join(paths::ARTEFACT_DIRECTORY_NAME);
286
287
let include = dir.join("include");
287
288
288
-
let tar_src = Path::new("src");
289
-
let tar_include = Path::new("include");
289
+
let tar_src = Utf8Path::new("src");
290
+
let tar_include = Utf8Path::new("include");
290
291
291
292
// Erlang modules
292
293
for module in &package.modules {
···
315
316
316
317
fn add_to_tar<P, W>(tarball: &mut tar::Builder<W>, path: P, data: &[u8]) -> Result<()>
317
318
where
318
-
P: AsRef<Path>,
319
+
P: AsRef<Utf8Path>,
319
320
W: Write,
320
321
{
321
322
let path = path.as_ref();
···
331
332
332
333
fn add_path_to_tar<P, W>(tarball: &mut tar::Builder<W>, path: P) -> Result<()>
333
334
where
334
-
P: AsRef<Path>,
335
+
P: AsRef<Utf8Path>,
335
336
W: Write,
336
337
{
337
338
let path = path.as_ref();
···
346
347
name: &'a str,
347
348
version: &'a Version,
348
349
description: &'a str,
349
-
source_files: &'a [PathBuf],
350
-
generated_files: &'a [(PathBuf, String)],
350
+
source_files: &'a [Utf8PathBuf],
351
+
generated_files: &'a [(Utf8PathBuf, String)],
351
352
licenses: &'a Vec<SpdxLicense>,
352
353
links: Vec<(&'a str, http::Uri)>,
353
354
requirements: Vec<ReleaseRequirement<'a>>,
···
365
366
url = link.1
366
367
)
367
368
}
368
-
fn file(name: impl AsRef<Path>) -> String {
369
-
format!("\n <<\"{name}\">>", name = name.as_ref().to_string_lossy())
369
+
fn file(name: impl AsRef<Utf8Path>) -> String {
370
+
format!("\n <<\"{name}\">>", name = name.as_ref())
370
371
}
371
372
372
373
format!(
···
448
449
version: &version,
449
450
description: "description goes here",
450
451
source_files: &[
451
-
PathBuf::from("gleam.toml"),
452
-
PathBuf::from("src/thingy.gleam"),
453
-
PathBuf::from("src/whatever.gleam"),
452
+
Utf8PathBuf::from("gleam.toml"),
453
+
Utf8PathBuf::from("src/thingy.gleam"),
454
+
Utf8PathBuf::from("src/whatever.gleam"),
454
455
],
455
456
generated_files: &[
456
-
(PathBuf::from("src/myapp.app"), "".into()),
457
-
(PathBuf::from("src/thingy.erl"), "".into()),
458
-
(PathBuf::from("src/whatever.erl"), "".into()),
457
+
(Utf8PathBuf::from("src/myapp.app"), "".into()),
458
+
(Utf8PathBuf::from("src/thingy.erl"), "".into()),
459
+
(Utf8PathBuf::from("src/whatever.erl"), "".into()),
459
460
],
460
461
licenses: &licences,
461
462
links: vec![("homepage", homepage), ("github", github)],
+3
-3
compiler-cli/src/remove.rs
+3
-3
compiler-cli/src/remove.rs
···
1
-
use std::path::{Path, PathBuf};
1
+
use camino::{Utf8Path, Utf8PathBuf};
2
2
3
3
use gleam_core::{
4
4
error::{FileIoAction, FileKind},
···
14
14
.map_err(|e| Error::FileIo {
15
15
kind: FileKind::File,
16
16
action: FileIoAction::Parse,
17
-
path: PathBuf::from("gleam.toml"),
17
+
path: Utf8PathBuf::from("gleam.toml"),
18
18
err: Some(e.to_string()),
19
19
})?;
20
20
···
31
31
}
32
32
33
33
// Write the updated config
34
-
fs::write(Path::new("gleam.toml"), &toml.to_string())?;
34
+
fs::write(Utf8Path::new("gleam.toml"), &toml.to_string())?;
35
35
let paths = crate::project_paths_at_current_directory();
36
36
_ = crate::dependencies::download(&paths, cli::Reporter::new(), None, UseManifest::Yes)?;
37
37
for package_to_remove in packages {
+4
-4
compiler-cli/src/run.rs
+4
-4
compiler-cli/src/run.rs
···
1
+
use camino::Utf8PathBuf;
1
2
use gleam_core::{
2
3
build::{Codegen, Mode, Options, Runtime, Target},
3
4
config::{DenoFlag, PackageConfig},
···
7
8
type_::ModuleFunction,
8
9
};
9
10
use lazy_static::lazy_static;
10
-
use std::path::PathBuf;
11
11
12
12
use crate::fs::ProjectIO;
13
13
···
116
116
117
117
for entry in crate::fs::read_dir(packages)?.filter_map(Result::ok) {
118
118
args.push("-pa".into());
119
-
args.push(entry.path().join("ebin").to_string_lossy().into());
119
+
args.push(entry.path().join("ebin").into());
120
120
}
121
121
122
122
// gleam modules are seperated by `/`. Erlang modules are seperated by `@`.
···
165
165
.strip_prefix(paths.root())
166
166
.expect("Failed to strip prefix from path")
167
167
.to_path_buf();
168
-
let entrypoint = format!("./{}/gleam.main.mjs", entry.to_string_lossy());
168
+
let entrypoint = format!("./{}/gleam.main.mjs", entry);
169
169
let module = format!(
170
170
r#"import {{ main }} from "./{module}.mjs";
171
171
main();
172
172
"#,
173
173
);
174
-
crate::fs::write(&PathBuf::from(&entrypoint), &module)?;
174
+
crate::fs::write(&Utf8PathBuf::from(&entrypoint), &module)?;
175
175
Ok(entrypoint)
176
176
}
177
177
+1
compiler-core/Cargo.toml
+1
compiler-core/Cargo.toml
+14
compiler-core/clippy.toml
+14
compiler-core/clippy.toml
···
9
9
{ path = "std::path::Path::read_link", reason = "IO is not permitted in core" },
10
10
{ path = "std::path::Path::symlink_metadata", reason = "IO is not permitted in core" },
11
11
{ path = "std::path::Path::try_exists", reason = "IO is not permitted in core" },
12
+
13
+
{ path = "camino::Utf8Path::canonicalize", reason = "IO is not permitted in core" },
14
+
{ path = "camino::Utf8Path::exists", reason = "IO is not permitted in core" },
15
+
{ path = "camino::Utf8Path::is_dir", reason = "IO is not permitted in core" },
16
+
{ path = "camino::Utf8Path::is_file", reason = "IO is not permitted in core" },
17
+
{ path = "camino::Utf8Path::is_symlink", reason = "IO is not permitted in core" },
18
+
{ path = "camino::Utf8Path::read_dir", reason = "IO is not permitted in core" },
19
+
{ path = "camino::Utf8Path::read_link", reason = "IO is not permitted in core" },
20
+
{ path = "camino::Utf8Path::symlink_metadata", reason = "IO is not permitted in core" },
21
+
{ path = "camino::Utf8Path::try_exists", reason = "IO is not permitted in core" },
22
+
23
+
24
+
{ path = "std::path::Path::new", reason = "Manually constructed paths should use camino::Utf8Path" },
25
+
{ path = "std::path::PathBuf::new", reason = "Manually constructed pathbufs should use camino::Utf8Path" },
12
26
]
+5
-6
compiler-core/src/build.rs
+5
-6
compiler-core/src/build.rs
···
27
27
parse::extra::{Comment, ModuleExtra},
28
28
type_,
29
29
};
30
+
use camino::Utf8PathBuf;
30
31
use itertools::Itertools;
31
32
use serde::{Deserialize, Serialize};
32
33
use smol_str::SmolStr;
33
34
use std::time::SystemTime;
34
-
use std::{
35
-
collections::HashMap, ffi::OsString, fs::DirEntry, iter::Peekable, path::PathBuf, process,
36
-
};
35
+
use std::{collections::HashMap, ffi::OsString, fs::DirEntry, iter::Peekable, process};
37
36
use strum::{Display, EnumIter, EnumString, EnumVariantNames, VariantNames};
38
37
39
38
#[derive(
···
185
184
pub name: SmolStr,
186
185
pub code: SmolStr,
187
186
pub mtime: SystemTime,
188
-
pub input_path: PathBuf,
187
+
pub input_path: Utf8PathBuf,
189
188
pub origin: Origin,
190
189
pub ast: TypedModule,
191
190
pub extra: ModuleExtra,
···
193
192
}
194
193
195
194
impl Module {
196
-
pub fn compiled_erlang_path(&self) -> PathBuf {
195
+
pub fn compiled_erlang_path(&self) -> Utf8PathBuf {
197
196
let mut path = self.name.replace("/", "@");
198
197
path.push_str(".erl");
199
-
PathBuf::from(path)
198
+
Utf8PathBuf::from(path)
200
199
}
201
200
202
201
pub fn is_test(&self) -> bool {
+8
-10
compiler-core/src/build/module_loader.rs
+8
-10
compiler-core/src/build/module_loader.rs
···
1
1
#[cfg(test)]
2
2
mod tests;
3
3
4
-
use std::{
5
-
collections::HashMap,
6
-
path::{Path, PathBuf},
7
-
time::SystemTime,
8
-
};
4
+
use std::{collections::HashMap, time::SystemTime};
5
+
6
+
use camino::{Utf8Path, Utf8PathBuf};
9
7
10
8
use serde::{Deserialize, Serialize};
11
9
use smol_str::SmolStr;
···
39
37
pub target: Target,
40
38
pub codegen: CodegenRequired,
41
39
pub package_name: &'a SmolStr,
42
-
pub source_directory: &'a Path,
43
-
pub artefact_directory: &'a Path,
40
+
pub source_directory: &'a Utf8Path,
41
+
pub artefact_directory: &'a Utf8Path,
44
42
pub origin: Origin,
45
43
}
46
44
···
56
54
/// Whether the module has changed or not is determined by comparing the
57
55
/// modification time of the source file with the value recorded in the
58
56
/// `.timestamp` file in the artefact directory.
59
-
pub fn load(&self, path: PathBuf) -> Result<Input> {
57
+
pub fn load(&self, path: Utf8PathBuf) -> Result<Input> {
60
58
let name = module_name(self.source_directory, &path);
61
59
let artefact = name.replace("/", "@");
62
60
let source_mtime = self.io.modification_time(&path)?;
···
116
114
117
115
fn read_source(
118
116
&self,
119
-
path: PathBuf,
117
+
path: Utf8PathBuf,
120
118
name: SmolStr,
121
119
mtime: SystemTime,
122
120
) -> Result<UncompiledModule, Error> {
···
147
145
warnings: &WarningEmitter,
148
146
target: Target,
149
147
origin: Origin,
150
-
path: PathBuf,
148
+
path: Utf8PathBuf,
151
149
name: SmolStr,
152
150
package_name: SmolStr,
153
151
mtime: SystemTime,
+26
-26
compiler-core/src/build/module_loader/tests.rs
+26
-26
compiler-core/src/build/module_loader/tests.rs
···
8
8
#[test]
9
9
fn no_cache_present() {
10
10
let name = "package".into();
11
-
let src = Path::new("/src");
12
-
let artefact = Path::new("/artefact");
11
+
let src = Utf8Path::new("/src");
12
+
let artefact = Utf8Path::new("/artefact");
13
13
let fs = InMemoryFileSystem::new();
14
14
let warnings = WarningEmitter::null();
15
15
let loader = make_loader(&warnings, &name, &fs, src, artefact);
16
16
17
-
fs.write(&Path::new("/src/main.gleam"), "const x = 1")
17
+
fs.write(&Utf8Path::new("/src/main.gleam"), "const x = 1")
18
18
.unwrap();
19
19
20
20
let result = loader
21
-
.load(Path::new("/src/main.gleam").to_path_buf())
21
+
.load(Utf8Path::new("/src/main.gleam").to_path_buf())
22
22
.unwrap();
23
23
24
24
assert!(result.is_new());
···
27
27
#[test]
28
28
fn cache_present_and_fresh() {
29
29
let name = "package".into();
30
-
let src = Path::new("/src");
31
-
let artefact = Path::new("/artefact");
30
+
let src = Utf8Path::new("/src");
31
+
let artefact = Utf8Path::new("/artefact");
32
32
let fs = InMemoryFileSystem::new();
33
33
let warnings = WarningEmitter::null();
34
34
let loader = make_loader(&warnings, &name, &fs, src, artefact);
···
38
38
write_cache(&fs, TEST_SOURCE_1, "/artefact/main.cache_meta", 1, false);
39
39
40
40
let result = loader
41
-
.load(Path::new("/src/main.gleam").to_path_buf())
41
+
.load(Utf8Path::new("/src/main.gleam").to_path_buf())
42
42
.unwrap();
43
43
44
44
assert!(result.is_cached());
···
47
47
#[test]
48
48
fn cache_present_and_stale() {
49
49
let name = "package".into();
50
-
let src = Path::new("/src");
51
-
let artefact = Path::new("/artefact");
50
+
let src = Utf8Path::new("/src");
51
+
let artefact = Utf8Path::new("/artefact");
52
52
let fs = InMemoryFileSystem::new();
53
53
let warnings = WarningEmitter::null();
54
54
let loader = make_loader(&warnings, &name, &fs, src, artefact);
···
58
58
write_cache(&fs, TEST_SOURCE_1, "/artefact/main.cache_meta", 1, false);
59
59
60
60
let result = loader
61
-
.load(Path::new("/src/main.gleam").to_path_buf())
61
+
.load(Utf8Path::new("/src/main.gleam").to_path_buf())
62
62
.unwrap();
63
63
64
64
assert!(result.is_new());
···
67
67
#[test]
68
68
fn cache_present_and_stale_but_source_is_the_same() {
69
69
let name = "package".into();
70
-
let src = Path::new("/src");
71
-
let artefact = Path::new("/artefact");
70
+
let src = Utf8Path::new("/src");
71
+
let artefact = Utf8Path::new("/artefact");
72
72
let fs = InMemoryFileSystem::new();
73
73
let warnings = WarningEmitter::null();
74
74
let loader = make_loader(&warnings, &name, &fs, src, artefact);
···
78
78
write_cache(&fs, TEST_SOURCE_1, "/artefact/main.cache_meta", 1, false);
79
79
80
80
let result = loader
81
-
.load(Path::new("/src/main.gleam").to_path_buf())
81
+
.load(Utf8Path::new("/src/main.gleam").to_path_buf())
82
82
.unwrap();
83
83
84
84
assert!(result.is_cached());
···
87
87
#[test]
88
88
fn cache_present_without_codegen_when_required() {
89
89
let name = "package".into();
90
-
let src = Path::new("/src");
91
-
let artefact = Path::new("/artefact");
90
+
let src = Utf8Path::new("/src");
91
+
let artefact = Utf8Path::new("/artefact");
92
92
let fs = InMemoryFileSystem::new();
93
93
let warnings = WarningEmitter::null();
94
94
let mut loader = make_loader(&warnings, &name, &fs, src, artefact);
···
99
99
write_cache(&fs, TEST_SOURCE_1, "/artefact/main.cache_meta", 1, false);
100
100
101
101
let result = loader
102
-
.load(Path::new("/src/main.gleam").to_path_buf())
102
+
.load(Utf8Path::new("/src/main.gleam").to_path_buf())
103
103
.unwrap();
104
104
105
105
assert!(result.is_new());
···
108
108
#[test]
109
109
fn cache_present_with_codegen_when_required() {
110
110
let name = "package".into();
111
-
let src = Path::new("/src");
112
-
let artefact = Path::new("/artefact");
111
+
let src = Utf8Path::new("/src");
112
+
let artefact = Utf8Path::new("/artefact");
113
113
let fs = InMemoryFileSystem::new();
114
114
let warnings = WarningEmitter::null();
115
115
let mut loader = make_loader(&warnings, &name, &fs, src, artefact);
···
120
120
write_cache(&fs, TEST_SOURCE_1, "/artefact/main.cache_meta", 1, true);
121
121
122
122
let result = loader
123
-
.load(Path::new("/src/main.gleam").to_path_buf())
123
+
.load(Utf8Path::new("/src/main.gleam").to_path_buf())
124
124
.unwrap();
125
125
126
126
assert!(result.is_cached());
···
129
129
#[test]
130
130
fn cache_present_without_codegen_when_not_required() {
131
131
let name = "package".into();
132
-
let src = Path::new("/src");
133
-
let artefact = Path::new("/artefact");
132
+
let src = Utf8Path::new("/src");
133
+
let artefact = Utf8Path::new("/artefact");
134
134
let fs = InMemoryFileSystem::new();
135
135
let warnings = WarningEmitter::null();
136
136
let mut loader = make_loader(&warnings, &name, &fs, src, artefact);
···
141
141
write_cache(&fs, TEST_SOURCE_1, "/artefact/main.cache_meta", 1, false);
142
142
143
143
let result = loader
144
-
.load(Path::new("/src/main.gleam").to_path_buf())
144
+
.load(Utf8Path::new("/src/main.gleam").to_path_buf())
145
145
.unwrap();
146
146
147
147
assert!(result.is_cached());
···
163
163
dependencies: vec![],
164
164
fingerprint: SourceFingerprint::new(source),
165
165
};
166
-
let path = Path::new(path);
166
+
let path = Utf8Path::new(path);
167
167
fs.write_bytes(&path, &cache_metadata.to_binary()).unwrap();
168
168
}
169
169
170
170
fn write_src(fs: &InMemoryFileSystem, source: &str, path: &str, seconds: u64) {
171
-
let path = Path::new(path);
171
+
let path = Utf8Path::new(path);
172
172
fs.write(&path, source).unwrap();
173
173
fs.set_modification_time(&path, SystemTime::UNIX_EPOCH + Duration::from_secs(seconds));
174
174
}
···
177
177
warnings: &'a WarningEmitter,
178
178
package_name: &'a SmolStr,
179
179
fs: &InMemoryFileSystem,
180
-
src: &'a Path,
181
-
artefact: &'a Path,
180
+
src: &'a Utf8Path,
181
+
artefact: &'a Utf8Path,
182
182
) -> ModuleLoader<'a, InMemoryFileSystem> {
183
183
ModuleLoader {
184
184
warnings,
+14
-19
compiler-core/src/build/native_file_copier.rs
+14
-19
compiler-core/src/build/native_file_copier.rs
···
1
1
#[cfg(test)]
2
2
mod tests;
3
3
4
-
use std::{
5
-
collections::HashSet,
6
-
path::{Path, PathBuf},
7
-
};
4
+
use std::collections::HashSet;
5
+
6
+
use camino::{Utf8Path, Utf8PathBuf};
8
7
9
8
use crate::{
10
9
io::{FileSystemReader, FileSystemWriter},
···
14
13
#[derive(Debug, Clone, PartialEq, Eq)]
15
14
pub(crate) struct CopiedNativeFiles {
16
15
pub any_elixir: bool,
17
-
pub to_compile: Vec<PathBuf>,
16
+
pub to_compile: Vec<Utf8PathBuf>,
18
17
}
19
18
20
19
pub(crate) struct NativeFileCopier<'a, IO> {
21
20
io: IO,
22
-
root: &'a Path,
23
-
destination_dir: &'a Path,
24
-
seen_native_files: HashSet<PathBuf>,
25
-
to_compile: Vec<PathBuf>,
21
+
root: &'a Utf8Path,
22
+
destination_dir: &'a Utf8Path,
23
+
seen_native_files: HashSet<Utf8PathBuf>,
24
+
to_compile: Vec<Utf8PathBuf>,
26
25
elixir_files_copied: bool,
27
26
}
28
27
···
30
29
where
31
30
IO: FileSystemReader + FileSystemWriter + Clone,
32
31
{
33
-
pub(crate) fn new(io: IO, root: &'a Path, out: &'a Path) -> Self {
32
+
pub(crate) fn new(io: IO, root: &'a Utf8Path, out: &'a Utf8Path) -> Self {
34
33
Self {
35
34
io,
36
35
root,
···
64
63
})
65
64
}
66
65
67
-
fn copy_files(&mut self, src_root: &Path) -> Result<()> {
66
+
fn copy_files(&mut self, src_root: &Utf8Path) -> Result<()> {
68
67
let mut check_elixir_libs = true;
69
68
70
69
for entry in self.io.read_dir(src_root)? {
···
74
73
Ok(())
75
74
}
76
75
77
-
fn copy(&mut self, file: PathBuf, src_root: &Path) -> Result<()> {
78
-
let extension = file
79
-
.extension()
80
-
.unwrap_or_default()
81
-
.to_str()
82
-
.unwrap_or_default();
76
+
fn copy(&mut self, file: Utf8PathBuf, src_root: &Utf8Path) -> Result<()> {
77
+
let extension = file.extension().unwrap_or_default();
83
78
84
79
// Skip unknown file formats that are not supported native files
85
80
if !matches!(extension, "mjs" | "js" | "ts" | "hrl" | "erl" | "ex") {
···
119
114
Ok(())
120
115
}
121
116
122
-
fn check_for_duplicate(&mut self, relative_path: &PathBuf) -> Result<(), Error> {
117
+
fn check_for_duplicate(&mut self, relative_path: &Utf8PathBuf) -> Result<(), Error> {
123
118
if !self.seen_native_files.insert(relative_path.clone()) {
124
119
return Err(Error::DuplicateSourceFile {
125
-
file: relative_path.to_string_lossy().to_string(),
120
+
file: relative_path.to_string(),
126
121
});
127
122
}
128
123
Ok(())
+56
-55
compiler-core/src/build/native_file_copier/tests.rs
+56
-55
compiler-core/src/build/native_file_copier/tests.rs
···
6
6
use lazy_static::lazy_static;
7
7
use std::{
8
8
collections::HashMap,
9
-
path::{Path, PathBuf},
10
9
time::{Duration, SystemTime, UNIX_EPOCH},
11
10
};
12
11
12
+
use camino::{Utf8Path, Utf8PathBuf};
13
+
13
14
lazy_static! {
14
-
static ref ROOT: PathBuf = PathBuf::from("/");
15
-
static ref OUT: PathBuf = PathBuf::from("/out");
15
+
static ref ROOT: Utf8PathBuf = Utf8PathBuf::from("/");
16
+
static ref OUT: Utf8PathBuf = Utf8PathBuf::from("/out");
16
17
}
17
18
18
19
#[test]
19
20
fn javascript_files_are_copied_from_src() {
20
21
let fs = InMemoryFileSystem::new();
21
-
fs.write(&Path::new("/src/wibble.js"), "1").unwrap();
22
+
fs.write(&Utf8Path::new("/src/wibble.js"), "1").unwrap();
22
23
23
24
let copier = NativeFileCopier::new(fs.clone(), &ROOT, &OUT);
24
25
let copied = copier.run().unwrap();
···
27
28
assert!(copied.to_compile.is_empty());
28
29
assert_eq!(
29
30
HashMap::from([
30
-
(PathBuf::from("/src/wibble.js"), "1".into()),
31
-
(PathBuf::from("/out/wibble.js"), "1".into())
31
+
(Utf8PathBuf::from("/src/wibble.js"), "1".into()),
32
+
(Utf8PathBuf::from("/out/wibble.js"), "1".into())
32
33
]),
33
34
fs.into_contents(),
34
35
);
···
37
38
#[test]
38
39
fn javascript_files_are_copied_from_test() {
39
40
let fs = InMemoryFileSystem::new();
40
-
fs.write(&Path::new("/test/wibble.js"), "1").unwrap();
41
+
fs.write(&Utf8Path::new("/test/wibble.js"), "1").unwrap();
41
42
42
43
let copier = NativeFileCopier::new(fs.clone(), &ROOT, &OUT);
43
44
let copied = copier.run().unwrap();
···
46
47
assert!(copied.to_compile.is_empty());
47
48
assert_eq!(
48
49
HashMap::from([
49
-
(PathBuf::from("/test/wibble.js"), "1".into()),
50
-
(PathBuf::from("/out/wibble.js"), "1".into())
50
+
(Utf8PathBuf::from("/test/wibble.js"), "1".into()),
51
+
(Utf8PathBuf::from("/out/wibble.js"), "1".into())
51
52
]),
52
53
fs.into_contents(),
53
54
);
···
56
57
#[test]
57
58
fn mjavascript_files_are_copied_from_src() {
58
59
let fs = InMemoryFileSystem::new();
59
-
fs.write(&Path::new("/src/wibble.mjs"), "1").unwrap();
60
+
fs.write(&Utf8Path::new("/src/wibble.mjs"), "1").unwrap();
60
61
61
62
let copier = NativeFileCopier::new(fs.clone(), &ROOT, &OUT);
62
63
let copied = copier.run().unwrap();
···
65
66
assert!(copied.to_compile.is_empty());
66
67
assert_eq!(
67
68
HashMap::from([
68
-
(PathBuf::from("/src/wibble.mjs"), "1".into()),
69
-
(PathBuf::from("/out/wibble.mjs"), "1".into())
69
+
(Utf8PathBuf::from("/src/wibble.mjs"), "1".into()),
70
+
(Utf8PathBuf::from("/out/wibble.mjs"), "1".into())
70
71
]),
71
72
fs.into_contents(),
72
73
);
···
75
76
#[test]
76
77
fn mjavascript_files_are_copied_from_test() {
77
78
let fs = InMemoryFileSystem::new();
78
-
fs.write(&Path::new("/test/wibble.mjs"), "1").unwrap();
79
+
fs.write(&Utf8Path::new("/test/wibble.mjs"), "1").unwrap();
79
80
80
81
let copier = NativeFileCopier::new(fs.clone(), &ROOT, &OUT);
81
82
let copied = copier.run().unwrap();
···
84
85
assert!(copied.to_compile.is_empty());
85
86
assert_eq!(
86
87
HashMap::from([
87
-
(PathBuf::from("/test/wibble.mjs"), "1".into()),
88
-
(PathBuf::from("/out/wibble.mjs"), "1".into())
88
+
(Utf8PathBuf::from("/test/wibble.mjs"), "1".into()),
89
+
(Utf8PathBuf::from("/out/wibble.mjs"), "1".into())
89
90
]),
90
91
fs.into_contents(),
91
92
);
···
94
95
#[test]
95
96
fn typescript_files_are_copied_from_src() {
96
97
let fs = InMemoryFileSystem::new();
97
-
fs.write(&Path::new("/src/wibble.ts"), "1").unwrap();
98
+
fs.write(&Utf8Path::new("/src/wibble.ts"), "1").unwrap();
98
99
99
100
let copier = NativeFileCopier::new(fs.clone(), &ROOT, &OUT);
100
101
let copied = copier.run().unwrap();
···
103
104
assert!(copied.to_compile.is_empty());
104
105
assert_eq!(
105
106
HashMap::from([
106
-
(PathBuf::from("/src/wibble.ts"), "1".into()),
107
-
(PathBuf::from("/out/wibble.ts"), "1".into())
107
+
(Utf8PathBuf::from("/src/wibble.ts"), "1".into()),
108
+
(Utf8PathBuf::from("/out/wibble.ts"), "1".into())
108
109
]),
109
110
fs.into_contents(),
110
111
);
···
113
114
#[test]
114
115
fn typescript_files_are_copied_from_test() {
115
116
let fs = InMemoryFileSystem::new();
116
-
fs.write(&Path::new("/test/wibble.ts"), "1").unwrap();
117
+
fs.write(&Utf8Path::new("/test/wibble.ts"), "1").unwrap();
117
118
118
119
let copier = NativeFileCopier::new(fs.clone(), &ROOT, &OUT);
119
120
let copied = copier.run().unwrap();
···
122
123
assert!(copied.to_compile.is_empty());
123
124
assert_eq!(
124
125
HashMap::from([
125
-
(PathBuf::from("/test/wibble.ts"), "1".into()),
126
-
(PathBuf::from("/out/wibble.ts"), "1".into())
126
+
(Utf8PathBuf::from("/test/wibble.ts"), "1".into()),
127
+
(Utf8PathBuf::from("/out/wibble.ts"), "1".into())
127
128
]),
128
129
fs.into_contents(),
129
130
);
···
132
133
#[test]
133
134
fn erlang_header_files_are_copied_from_src() {
134
135
let fs = InMemoryFileSystem::new();
135
-
fs.write(&Path::new("/src/wibble.hrl"), "1").unwrap();
136
+
fs.write(&Utf8Path::new("/src/wibble.hrl"), "1").unwrap();
136
137
137
138
let copier = NativeFileCopier::new(fs.clone(), &ROOT, &OUT);
138
139
let copied = copier.run().unwrap();
···
141
142
assert!(copied.to_compile.is_empty());
142
143
assert_eq!(
143
144
HashMap::from([
144
-
(PathBuf::from("/src/wibble.hrl"), "1".into()),
145
-
(PathBuf::from("/out/wibble.hrl"), "1".into())
145
+
(Utf8PathBuf::from("/src/wibble.hrl"), "1".into()),
146
+
(Utf8PathBuf::from("/out/wibble.hrl"), "1".into())
146
147
]),
147
148
fs.into_contents(),
148
149
);
···
151
152
#[test]
152
153
fn erlang_header_files_are_copied_from_test() {
153
154
let fs = InMemoryFileSystem::new();
154
-
fs.write(&Path::new("/test/wibble.hrl"), "1").unwrap();
155
+
fs.write(&Utf8Path::new("/test/wibble.hrl"), "1").unwrap();
155
156
156
157
let copier = NativeFileCopier::new(fs.clone(), &ROOT, &OUT);
157
158
let copied = copier.run().unwrap();
···
160
161
assert!(copied.to_compile.is_empty());
161
162
assert_eq!(
162
163
HashMap::from([
163
-
(PathBuf::from("/test/wibble.hrl"), "1".into()),
164
-
(PathBuf::from("/out/wibble.hrl"), "1".into())
164
+
(Utf8PathBuf::from("/test/wibble.hrl"), "1".into()),
165
+
(Utf8PathBuf::from("/out/wibble.hrl"), "1".into())
165
166
]),
166
167
fs.into_contents(),
167
168
);
···
170
171
#[test]
171
172
fn erlang_files_are_copied_from_src() {
172
173
let fs = InMemoryFileSystem::new();
173
-
fs.write(&Path::new("/src/wibble.erl"), "1").unwrap();
174
+
fs.write(&Utf8Path::new("/src/wibble.erl"), "1").unwrap();
174
175
175
176
let copier = NativeFileCopier::new(fs.clone(), &ROOT, &OUT);
176
177
let copied = copier.run().unwrap();
177
178
178
179
assert!(!copied.any_elixir);
179
-
assert_eq!(copied.to_compile, vec![PathBuf::from("wibble.erl")]);
180
+
assert_eq!(copied.to_compile, vec![Utf8PathBuf::from("wibble.erl")]);
180
181
assert_eq!(
181
182
HashMap::from([
182
-
(PathBuf::from("/src/wibble.erl"), "1".into()),
183
-
(PathBuf::from("/out/wibble.erl"), "1".into())
183
+
(Utf8PathBuf::from("/src/wibble.erl"), "1".into()),
184
+
(Utf8PathBuf::from("/out/wibble.erl"), "1".into())
184
185
]),
185
186
fs.into_contents(),
186
187
);
···
189
190
#[test]
190
191
fn erlang_files_are_copied_from_test() {
191
192
let fs = InMemoryFileSystem::new();
192
-
fs.write(&Path::new("/test/wibble.erl"), "1").unwrap();
193
+
fs.write(&Utf8Path::new("/test/wibble.erl"), "1").unwrap();
193
194
194
195
let copier = NativeFileCopier::new(fs.clone(), &ROOT, &OUT);
195
196
let copied = copier.run().unwrap();
196
197
197
198
assert!(!copied.any_elixir);
198
-
assert_eq!(copied.to_compile, vec![PathBuf::from("wibble.erl")]);
199
+
assert_eq!(copied.to_compile, vec![Utf8PathBuf::from("wibble.erl")]);
199
200
assert_eq!(
200
201
HashMap::from([
201
-
(PathBuf::from("/test/wibble.erl"), "1".into()),
202
-
(PathBuf::from("/out/wibble.erl"), "1".into())
202
+
(Utf8PathBuf::from("/test/wibble.erl"), "1".into()),
203
+
(Utf8PathBuf::from("/out/wibble.erl"), "1".into())
203
204
]),
204
205
fs.into_contents(),
205
206
);
···
208
209
#[test]
209
210
fn elixir_files_are_copied_from_src() {
210
211
let fs = InMemoryFileSystem::new();
211
-
fs.write(&Path::new("/src/wibble.ex"), "1").unwrap();
212
+
fs.write(&Utf8Path::new("/src/wibble.ex"), "1").unwrap();
212
213
213
214
let copier = NativeFileCopier::new(fs.clone(), &ROOT, &OUT);
214
215
let copied = copier.run().unwrap();
215
216
216
217
assert!(copied.any_elixir);
217
-
assert_eq!(copied.to_compile, vec![PathBuf::from("wibble.ex")]);
218
+
assert_eq!(copied.to_compile, vec![Utf8PathBuf::from("wibble.ex")]);
218
219
assert_eq!(
219
220
HashMap::from([
220
-
(PathBuf::from("/src/wibble.ex"), "1".into()),
221
-
(PathBuf::from("/out/wibble.ex"), "1".into())
221
+
(Utf8PathBuf::from("/src/wibble.ex"), "1".into()),
222
+
(Utf8PathBuf::from("/out/wibble.ex"), "1".into())
222
223
]),
223
224
fs.into_contents(),
224
225
);
···
227
228
#[test]
228
229
fn elixir_files_are_copied_from_test() {
229
230
let fs = InMemoryFileSystem::new();
230
-
fs.write(&Path::new("/test/wibble.ex"), "1").unwrap();
231
+
fs.write(&Utf8Path::new("/test/wibble.ex"), "1").unwrap();
231
232
232
233
let copier = NativeFileCopier::new(fs.clone(), &ROOT, &OUT);
233
234
let copied = copier.run().unwrap();
234
235
235
236
assert!(copied.any_elixir);
236
-
assert_eq!(copied.to_compile, vec![PathBuf::from("wibble.ex")]);
237
+
assert_eq!(copied.to_compile, vec![Utf8PathBuf::from("wibble.ex")]);
237
238
assert_eq!(
238
239
HashMap::from([
239
-
(PathBuf::from("/test/wibble.ex"), "1".into()),
240
-
(PathBuf::from("/out/wibble.ex"), "1".into())
240
+
(Utf8PathBuf::from("/test/wibble.ex"), "1".into()),
241
+
(Utf8PathBuf::from("/out/wibble.ex"), "1".into())
241
242
]),
242
243
fs.into_contents(),
243
244
);
···
246
247
#[test]
247
248
fn other_files_are_ignored() {
248
249
let fs = InMemoryFileSystem::new();
249
-
fs.write(&Path::new("/src/wibble.cpp"), "1").unwrap();
250
+
fs.write(&Utf8Path::new("/src/wibble.cpp"), "1").unwrap();
250
251
251
252
let copier = NativeFileCopier::new(fs.clone(), &ROOT, &OUT);
252
253
let copied = copier.run().unwrap();
···
254
255
assert!(!copied.any_elixir);
255
256
assert!(copied.to_compile.is_empty());
256
257
assert_eq!(
257
-
HashMap::from([(PathBuf::from("/src/wibble.cpp"), "1".into())]),
258
+
HashMap::from([(Utf8PathBuf::from("/src/wibble.cpp"), "1".into())]),
258
259
fs.into_contents(),
259
260
);
260
261
}
···
262
263
#[test]
263
264
fn files_do_not_get_copied_if_there_already_is_a_new_version() {
264
265
let fs = InMemoryFileSystem::new();
265
-
let out = Path::new("/out/wibble.mjs");
266
-
let src = Path::new("/src/wibble.mjs");
266
+
let out = Utf8Path::new("/out/wibble.mjs");
267
+
let src = Utf8Path::new("/src/wibble.mjs");
267
268
fs.write(&out, "in-out").unwrap();
268
269
fs.write(&src, "in-src").unwrap();
269
270
fs.set_modification_time(&out, UNIX_EPOCH + Duration::from_secs(1));
···
276
277
assert!(copied.to_compile.is_empty());
277
278
assert_eq!(
278
279
HashMap::from([
279
-
(PathBuf::from("/src/wibble.mjs"), "in-src".into()),
280
-
(PathBuf::from("/out/wibble.mjs"), "in-out".into())
280
+
(Utf8PathBuf::from("/src/wibble.mjs"), "in-src".into()),
281
+
(Utf8PathBuf::from("/out/wibble.mjs"), "in-out".into())
281
282
]),
282
283
fs.into_contents(),
283
284
);
···
286
287
#[test]
287
288
fn files_get_copied_if_the_previously_copied_vesion_is_older() {
288
289
let fs = InMemoryFileSystem::new();
289
-
let out = Path::new("/out/wibble.mjs");
290
-
let src = Path::new("/src/wibble.mjs");
290
+
let out = Utf8Path::new("/out/wibble.mjs");
291
+
let src = Utf8Path::new("/src/wibble.mjs");
291
292
fs.write(&out, "in-out").unwrap();
292
293
fs.write(&src, "in-src").unwrap();
293
294
fs.set_modification_time(&out, UNIX_EPOCH);
···
300
301
assert!(copied.to_compile.is_empty());
301
302
assert_eq!(
302
303
HashMap::from([
303
-
(PathBuf::from("/src/wibble.mjs"), "in-src".into()),
304
-
(PathBuf::from("/out/wibble.mjs"), "in-src".into())
304
+
(Utf8PathBuf::from("/src/wibble.mjs"), "in-src".into()),
305
+
(Utf8PathBuf::from("/out/wibble.mjs"), "in-src".into())
305
306
]),
306
307
fs.into_contents(),
307
308
);
···
310
311
#[test]
311
312
fn duplicate_native_files_result_in_an_error() {
312
313
let fs = InMemoryFileSystem::new();
313
-
fs.write(&Path::new("/src/wibble.mjs"), "1").unwrap();
314
-
fs.write(&Path::new("/test/wibble.mjs"), "1").unwrap();
314
+
fs.write(&Utf8Path::new("/src/wibble.mjs"), "1").unwrap();
315
+
fs.write(&Utf8Path::new("/test/wibble.mjs"), "1").unwrap();
315
316
316
317
let copier = NativeFileCopier::new(fs.clone(), &ROOT, &OUT);
317
318
assert!(copier.run().is_err());
+27
-34
compiler-core/src/build/package_compiler.rs
+27
-34
compiler-core/src/build/package_compiler.rs
···
20
20
};
21
21
use askama::Template;
22
22
use smol_str::SmolStr;
23
+
use std::collections::HashSet;
23
24
use std::{collections::HashMap, fmt::write, time::SystemTime};
24
-
use std::{
25
-
collections::HashSet,
26
-
path::{Path, PathBuf},
27
-
};
25
+
26
+
use camino::{Utf8Path, Utf8PathBuf};
28
27
29
28
use super::{ErlangAppCodegenConfiguration, TargetCodegenConfiguration};
30
29
···
36
35
#[derive(Debug)]
37
36
pub struct PackageCompiler<'a, IO> {
38
37
pub io: IO,
39
-
pub out: &'a Path,
40
-
pub lib: &'a Path,
41
-
pub root: &'a Path,
38
+
pub out: &'a Utf8Path,
39
+
pub lib: &'a Utf8Path,
40
+
pub root: &'a Utf8Path,
42
41
pub mode: Mode,
43
42
pub target: &'a TargetCodegenConfiguration,
44
43
pub config: &'a PackageConfig,
···
58
57
pub fn new(
59
58
config: &'a PackageConfig,
60
59
mode: Mode,
61
-
root: &'a Path,
62
-
out: &'a Path,
63
-
lib: &'a Path,
60
+
root: &'a Utf8Path,
61
+
out: &'a Utf8Path,
62
+
lib: &'a Utf8Path,
64
63
target: &'a TargetCodegenConfiguration,
65
64
ids: UniqueIdGenerator,
66
65
io: IO,
···
91
90
mut self,
92
91
warnings: &WarningEmitter,
93
92
existing_modules: &mut im::HashMap<SmolStr, type_::ModuleInterface>,
94
-
already_defined_modules: &mut im::HashMap<SmolStr, PathBuf>,
93
+
already_defined_modules: &mut im::HashMap<SmolStr, Utf8PathBuf>,
95
94
stale_modules: &mut StaleTracker,
96
95
) -> Result<Vec<Module>, Error> {
97
96
let span = tracing::info_span!("compile", package = %self.config.name.as_str());
···
144
143
Ok(modules)
145
144
}
146
145
147
-
fn compile_erlang_to_beam(&mut self, modules: &HashSet<PathBuf>) -> Result<(), Error> {
146
+
fn compile_erlang_to_beam(&mut self, modules: &HashSet<Utf8PathBuf>) -> Result<(), Error> {
148
147
if modules.is_empty() {
149
148
tracing::debug!("no_erlang_to_compile");
150
149
return Ok(());
···
162
161
}
163
162
164
163
let mut args = vec![
165
-
escript_path.to_string_lossy().to_string(),
164
+
escript_path.to_string(),
166
165
// Tell the compiler where to find other libraries
167
166
"--lib".into(),
168
-
self.lib.to_string_lossy().to_string(),
167
+
self.lib.to_string(),
169
168
// Write compiled .beam to ./ebin
170
169
"--out".into(),
171
-
self.out.join("ebin").to_string_lossy().to_string(),
170
+
self.out.join("ebin").to_string(),
172
171
];
173
172
// Add the list of modules to compile
174
173
for module in modules {
175
174
let path = self.out.join(paths::ARTEFACT_DIRECTORY_NAME).join(module);
176
-
args.push(path.to_string_lossy().to_string());
175
+
args.push(path.to_string());
177
176
}
178
177
// Compile Erlang and Elixir modules
179
178
let status = self
···
192
191
193
192
fn copy_project_native_files(
194
193
&mut self,
195
-
destination_dir: &Path,
196
-
to_compile_modules: &mut HashSet<PathBuf>,
194
+
destination_dir: &Utf8Path,
195
+
to_compile_modules: &mut HashSet<Utf8PathBuf>,
197
196
) -> Result<(), Error> {
198
197
tracing::debug!("copying_native_source_files");
199
198
···
342
341
343
342
fn render_erlang_entrypoint_module(
344
343
&mut self,
345
-
out: &Path,
346
-
modules_to_compile: &mut HashSet<PathBuf>,
344
+
out: &Utf8Path,
345
+
modules_to_compile: &mut HashSet<Utf8PathBuf>,
347
346
) -> Result<(), Error> {
348
347
let name = format!("{name}@@main.erl", name = self.config.name);
349
348
let path = out.join(&name);
···
435
434
436
435
pub fn maybe_link_elixir_libs<IO>(
437
436
io: &IO,
438
-
build_dir: &PathBuf,
437
+
build_dir: &Utf8PathBuf,
439
438
subprocess_stdio: Stdio,
440
439
) -> Result<(), Error>
441
440
where
···
490
489
// Each pathfinder line is a system path for an Elixir core library
491
490
let read_pathfinder = io.read(&pathfinder)?;
492
491
for lib_path in read_pathfinder.split('\n') {
493
-
let source = PathBuf::from(lib_path);
492
+
let source = Utf8PathBuf::from(lib_path);
494
493
let name = source
495
494
.as_path()
496
495
.file_name()
···
507
506
// Delete the existing link
508
507
io.delete(&dest)?;
509
508
}
510
-
tracing::debug!(
511
-
"linking_{}_to_build",
512
-
name.to_str().unwrap_or("elixir_core_lib"),
513
-
);
509
+
tracing::debug!("linking_{}_to_build", name,);
514
510
io.symlink_dir(&source, &dest)?;
515
511
}
516
512
517
513
Ok(())
518
514
}
519
515
520
-
pub(crate) fn module_name(package_path: &Path, full_module_path: &Path) -> SmolStr {
516
+
pub(crate) fn module_name(package_path: &Utf8Path, full_module_path: &Utf8Path) -> SmolStr {
521
517
// /path/to/project/_build/default/lib/the_package/src/my/module.gleam
522
518
523
519
// my/module.gleam
···
530
526
let _ = module_path.set_extension("");
531
527
532
528
// Stringify
533
-
let name = module_path
534
-
.to_str()
535
-
.expect("Module name path to str")
536
-
.to_string();
529
+
let name = module_path.to_string();
537
530
538
531
// normalise windows paths
539
532
name.replace("\\", "/").into()
···
553
546
}
554
547
}
555
548
556
-
pub fn source_path(&self) -> &Path {
549
+
pub fn source_path(&self) -> &Utf8Path {
557
550
match self {
558
551
Input::New(m) => &m.path,
559
552
Input::Cached(m) => &m.source_path,
···
589
582
pub name: SmolStr,
590
583
pub origin: Origin,
591
584
pub dependencies: Vec<SmolStr>,
592
-
pub source_path: PathBuf,
585
+
pub source_path: Utf8PathBuf,
593
586
}
594
587
595
588
#[derive(Debug, serde::Serialize, serde::Deserialize)]
···
618
611
619
612
#[derive(Debug, PartialEq, Eq)]
620
613
pub(crate) struct UncompiledModule {
621
-
pub path: PathBuf,
614
+
pub path: Utf8PathBuf,
622
615
pub name: SmolStr,
623
616
pub code: SmolStr,
624
617
pub mtime: SystemTime,
+12
-12
compiler-core/src/build/package_loader.rs
+12
-12
compiler-core/src/build/package_loader.rs
···
3
3
4
4
use std::{
5
5
collections::{HashMap, HashSet},
6
-
path::{Path, PathBuf},
7
6
time::{Duration, SystemTime},
8
7
};
8
+
9
+
use camino::{Utf8Path, Utf8PathBuf};
9
10
10
11
// TODO: emit warnings for cached modules even if they are not compiled again.
11
12
···
52
53
io: IO,
53
54
ids: UniqueIdGenerator,
54
55
mode: Mode,
55
-
root: &'a Path,
56
+
root: &'a Utf8Path,
56
57
warnings: &'a WarningEmitter,
57
58
codegen: CodegenRequired,
58
-
artefact_directory: &'a Path,
59
+
artefact_directory: &'a Utf8Path,
59
60
package_name: &'a SmolStr,
60
61
target: Target,
61
62
stale_modules: &'a mut StaleTracker,
62
-
already_defined_modules: &'a mut im::HashMap<SmolStr, PathBuf>,
63
+
already_defined_modules: &'a mut im::HashMap<SmolStr, Utf8PathBuf>,
63
64
}
64
65
65
66
impl<'a, IO> PackageLoader<'a, IO>
···
70
71
io: IO,
71
72
ids: UniqueIdGenerator,
72
73
mode: Mode,
73
-
root: &'a Path,
74
+
root: &'a Utf8Path,
74
75
warnings: &'a WarningEmitter,
75
76
codegen: CodegenRequired,
76
-
artefact_directory: &'a Path,
77
+
artefact_directory: &'a Utf8Path,
77
78
target: Target,
78
79
package_name: &'a SmolStr,
79
80
stale_modules: &'a mut StaleTracker,
80
-
already_defined_modules: &'a mut im::HashMap<SmolStr, PathBuf>,
81
+
already_defined_modules: &'a mut im::HashMap<SmolStr, Utf8PathBuf>,
81
82
) -> Self {
82
83
Self {
83
84
io,
···
156
157
metadata::ModuleDecoder::new(self.ids.clone()).read(bytes.as_slice())
157
158
}
158
159
159
-
pub fn is_gleam_path(&self, path: &Path, dir: &Path) -> bool {
160
+
pub fn is_gleam_path(&self, path: &Utf8Path, dir: &Utf8Path) -> bool {
160
161
use regex::Regex;
161
162
lazy_static! {
162
163
static ref RE: Regex = Regex::new(&format!(
···
170
171
RE.is_match(
171
172
path.strip_prefix(dir)
172
173
.expect("is_gleam_path(): strip_prefix")
173
-
.to_str()
174
-
.expect("is_gleam_path(): to_str"),
174
+
.as_str(),
175
175
)
176
176
}
177
177
···
260
260
#[derive(Debug)]
261
261
pub struct Inputs<'a> {
262
262
collection: HashMap<SmolStr, Input>,
263
-
already_defined_modules: &'a im::HashMap<SmolStr, PathBuf>,
263
+
already_defined_modules: &'a im::HashMap<SmolStr, Utf8PathBuf>,
264
264
}
265
265
266
266
impl<'a> Inputs<'a> {
267
-
fn new(already_defined_modules: &'a im::HashMap<SmolStr, PathBuf>) -> Self {
267
+
fn new(already_defined_modules: &'a im::HashMap<SmolStr, Utf8PathBuf>) -> Self {
268
268
Self {
269
269
collection: Default::default(),
270
270
already_defined_modules,
+24
-24
compiler-core/src/build/package_loader/tests.rs
+24
-24
compiler-core/src/build/package_loader/tests.rs
···
22
22
const TEST_SOURCE_2: &'static str = "const x = 2";
23
23
24
24
fn write_src(fs: &InMemoryFileSystem, path: &str, seconds: u64, src: &str) {
25
-
let path = Path::new(path);
25
+
let path = Utf8Path::new(path);
26
26
fs.write(&path, src).unwrap();
27
27
fs.set_modification_time(&path, SystemTime::UNIX_EPOCH + Duration::from_secs(seconds));
28
28
}
···
35
35
dependencies: deps,
36
36
fingerprint: SourceFingerprint::new(src),
37
37
};
38
-
let path = Path::new("/artefact").join(format!("{name}.cache_meta"));
38
+
let path = Utf8Path::new("/artefact").join(format!("{name}.cache_meta"));
39
39
fs.write_bytes(&path, &cache_metadata.to_binary()).unwrap();
40
40
41
41
let cache = crate::type_::ModuleInterface {
···
47
47
values: Default::default(),
48
48
accessors: Default::default(),
49
49
};
50
-
let path = Path::new("/artefact").join(format!("{name}.cache"));
50
+
let path = Utf8Path::new("/artefact").join(format!("{name}.cache"));
51
51
fs.write_bytes(
52
52
&path,
53
53
&metadata::ModuleEncoder::new(&cache).encode().unwrap(),
···
55
55
.unwrap();
56
56
}
57
57
58
-
fn run_loader(fs: InMemoryFileSystem, root: &Path, artefact: &Path) -> LoaderTestOutput {
58
+
fn run_loader(fs: InMemoryFileSystem, root: &Utf8Path, artefact: &Utf8Path) -> LoaderTestOutput {
59
59
let mut defined = im::HashMap::new();
60
60
let ids = UniqueIdGenerator::new();
61
61
let (emitter, warnings) = WarningEmitter::vector();
···
85
85
#[test]
86
86
fn no_modules() {
87
87
let fs = InMemoryFileSystem::new();
88
-
let root = Path::new("/");
89
-
let artefact = Path::new("/artefact");
88
+
let root = Utf8Path::new("/");
89
+
let artefact = Utf8Path::new("/artefact");
90
90
91
91
let loaded = run_loader(fs, root, artefact);
92
92
assert!(loaded.to_compile.is_empty());
···
96
96
#[test]
97
97
fn one_src_module() {
98
98
let fs = InMemoryFileSystem::new();
99
-
let root = Path::new("/");
100
-
let artefact = Path::new("/artefact");
99
+
let root = Utf8Path::new("/");
100
+
let artefact = Utf8Path::new("/artefact");
101
101
102
102
write_src(&fs, "/src/main.gleam", 0, "const x = 1");
103
103
···
109
109
#[test]
110
110
fn one_test_module() {
111
111
let fs = InMemoryFileSystem::new();
112
-
let root = Path::new("/");
113
-
let artefact = Path::new("/artefact");
112
+
let root = Utf8Path::new("/");
113
+
let artefact = Utf8Path::new("/artefact");
114
114
115
115
write_src(&fs, "/test/main.gleam", 0, "const x = 1");
116
116
···
122
122
#[test]
123
123
fn importing() {
124
124
let fs = InMemoryFileSystem::new();
125
-
let root = Path::new("/");
126
-
let artefact = Path::new("/artefact");
125
+
let root = Utf8Path::new("/");
126
+
let artefact = Utf8Path::new("/artefact");
127
127
128
128
write_src(&fs, "/src/three.gleam", 0, "import two");
129
129
write_src(&fs, "/src/one.gleam", 0, "");
···
144
144
#[test]
145
145
fn reading_cache() {
146
146
let fs = InMemoryFileSystem::new();
147
-
let root = Path::new("/");
148
-
let artefact = Path::new("/artefact");
147
+
let root = Utf8Path::new("/");
148
+
let artefact = Utf8Path::new("/artefact");
149
149
150
150
write_src(&fs, "/src/one.gleam", 0, TEST_SOURCE_1);
151
151
write_cache(&fs, "one", 0, vec![], TEST_SOURCE_1);
···
158
158
#[test]
159
159
fn module_is_stale_if_cache_older() {
160
160
let fs = InMemoryFileSystem::new();
161
-
let root = Path::new("/");
162
-
let artefact = Path::new("/artefact");
161
+
let root = Utf8Path::new("/");
162
+
let artefact = Utf8Path::new("/artefact");
163
163
164
164
write_src(&fs, "/src/one.gleam", 1, TEST_SOURCE_2);
165
165
write_cache(&fs, "one", 0, vec![], TEST_SOURCE_1);
···
172
172
#[test]
173
173
fn module_is_stale_if_deps_are_stale() {
174
174
let fs = InMemoryFileSystem::new();
175
-
let root = Path::new("/");
176
-
let artefact = Path::new("/artefact");
175
+
let root = Utf8Path::new("/");
176
+
let artefact = Utf8Path::new("/artefact");
177
177
178
178
// Cache is stale
179
179
write_src(&fs, "/src/one.gleam", 1, TEST_SOURCE_2);
···
198
198
#[test]
199
199
fn invalid_module_name() {
200
200
let fs = InMemoryFileSystem::new();
201
-
let root = Path::new("/");
202
-
let artefact = Path::new("/artefact");
201
+
let root = Utf8Path::new("/");
202
+
let artefact = Utf8Path::new("/artefact");
203
203
204
204
// Cache is stale
205
205
write_src(&fs, "/src/One.gleam", 1, TEST_SOURCE_2);
···
210
210
assert_eq!(
211
211
loaded.warnings,
212
212
vec![Warning::InvalidSource {
213
-
path: PathBuf::from("/src/One.gleam"),
213
+
path: Utf8PathBuf::from("/src/One.gleam"),
214
214
}],
215
215
);
216
216
}
···
218
218
#[test]
219
219
fn invalid_nested_module_name() {
220
220
let fs = InMemoryFileSystem::new();
221
-
let root = Path::new("/");
222
-
let artefact = Path::new("/artefact");
221
+
let root = Utf8Path::new("/");
222
+
let artefact = Utf8Path::new("/artefact");
223
223
224
224
// Cache is stale
225
225
write_src(&fs, "/src/1/one.gleam", 1, TEST_SOURCE_2);
···
230
230
assert_eq!(
231
231
loaded.warnings,
232
232
vec![Warning::InvalidSource {
233
-
path: PathBuf::from("/src/1/one.gleam"),
233
+
path: Utf8PathBuf::from("/src/1/one.gleam"),
234
234
}],
235
235
);
236
236
}
+7
-6
compiler-core/src/build/project_compiler.rs
+7
-6
compiler-core/src/build/project_compiler.rs
···
23
23
collections::{HashMap, HashSet},
24
24
fmt::Write,
25
25
io::BufReader,
26
-
path::{Path, PathBuf},
27
26
sync::Arc,
28
27
time::Instant,
29
28
};
30
29
31
30
use super::{Codegen, ErlangAppCodegenConfiguration};
31
+
32
+
use camino::{Utf8Path, Utf8PathBuf};
32
33
33
34
// On Windows we have to call rebar3 via a little wrapper script.
34
35
//
···
75
76
pub(crate) config: PackageConfig,
76
77
pub(crate) packages: HashMap<String, ManifestPackage>,
77
78
importable_modules: im::HashMap<SmolStr, type_::ModuleInterface>,
78
-
defined_modules: im::HashMap<SmolStr, PathBuf>,
79
+
defined_modules: im::HashMap<SmolStr, Utf8PathBuf>,
79
80
stale_modules: StaleTracker,
80
81
warnings: WarningEmitter,
81
82
telemetry: Box<dyn Telemetry>,
···
281
282
let package = self.paths.build_packages_package(name);
282
283
let build_packages = self.paths.build_directory_for_target(mode, target);
283
284
let ebins = self.paths.build_packages_ebins_glob(mode, target);
284
-
let rebar3_path = |path: &Path| format!("../{}", path.to_str().expect("Build path"));
285
+
let rebar3_path = |path: &Utf8Path| format!("../{}", path);
285
286
286
287
tracing::debug!("copying_package_to_build");
287
288
self.io.mkdir(&build_packages)?;
···
348
349
let mix_build_dir = project_dir.join("_build").join(mix_target);
349
350
let mix_build_lib_dir = mix_build_dir.join("lib");
350
351
let up = paths::unnest(&project_dir);
351
-
let mix_path = |path: &Path| up.join(path).to_str().unwrap_or_default().to_string();
352
+
let mix_path = |path: &Utf8Path| up.join(path).to_string();
352
353
let ebins = self.paths.build_packages_ebins_glob(mode, target);
353
354
354
355
// Elixir core libs must be loaded
···
439
440
440
441
fn load_cached_package(
441
442
&mut self,
442
-
build_dir: PathBuf,
443
+
build_dir: Utf8PathBuf,
443
444
package: &ManifestPackage,
444
445
) -> Result<(), Error> {
445
446
for path in self.io.gleam_cache_files(&build_dir) {
···
458
459
&mut self,
459
460
config: &PackageConfig,
460
461
is_root: bool,
461
-
root_path: PathBuf,
462
+
root_path: Utf8PathBuf,
462
463
) -> Result<Vec<Module>, Error> {
463
464
let out_path =
464
465
self.paths
+10
-8
compiler-core/src/codegen.rs
+10
-8
compiler-core/src/codegen.rs
···
3
3
line_numbers::LineNumbers, Result,
4
4
};
5
5
use itertools::Itertools;
6
-
use std::{fmt::Debug, path::Path};
6
+
use std::fmt::Debug;
7
+
8
+
use camino::Utf8Path;
7
9
8
10
/// A code generator that creates a .erl Erlang module and record header files
9
11
/// for each Gleam module in the package.
10
12
#[derive(Debug)]
11
13
pub struct Erlang<'a> {
12
-
build_directory: &'a Path,
13
-
include_directory: &'a Path,
14
+
build_directory: &'a Utf8Path,
15
+
include_directory: &'a Utf8Path,
14
16
}
15
17
16
18
impl<'a> Erlang<'a> {
17
-
pub fn new(build_directory: &'a Path, include_directory: &'a Path) -> Self {
19
+
pub fn new(build_directory: &'a Utf8Path, include_directory: &'a Utf8Path) -> Self {
18
20
Self {
19
21
build_directory,
20
22
include_directory,
···
66
68
/// A code generator that creates a .app Erlang application file for the package
67
69
#[derive(Debug)]
68
70
pub struct ErlangApp<'a> {
69
-
output_directory: &'a Path,
71
+
output_directory: &'a Utf8Path,
70
72
include_dev_deps: bool,
71
73
}
72
74
73
75
impl<'a> ErlangApp<'a> {
74
-
pub fn new(output_directory: &'a Path, include_dev_deps: bool) -> Self {
76
+
pub fn new(output_directory: &'a Utf8Path, include_dev_deps: bool) -> Self {
75
77
Self {
76
78
output_directory,
77
79
include_dev_deps,
···
147
149
148
150
#[derive(Debug)]
149
151
pub struct JavaScript<'a> {
150
-
output_directory: &'a Path,
152
+
output_directory: &'a Utf8Path,
151
153
typescript: TypeScriptDeclarations,
152
154
}
153
155
154
156
impl<'a> JavaScript<'a> {
155
-
pub fn new(output_directory: &'a Path, typescript: TypeScriptDeclarations) -> Self {
157
+
pub fn new(output_directory: &'a Utf8Path, typescript: TypeScriptDeclarations) -> Self {
156
158
Self {
157
159
output_directory,
158
160
typescript,
+3
-3
compiler-core/src/config.rs
+3
-3
compiler-core/src/config.rs
···
4
4
use crate::requirement::Requirement;
5
5
use crate::version::COMPILER_VERSION;
6
6
use crate::{Error, Result};
7
+
use camino::{Utf8Path, Utf8PathBuf};
7
8
use globset::{Glob, GlobSetBuilder};
8
9
use hexpm::version::Version;
9
10
use http::Uri;
···
12
13
use std::collections::{HashMap, HashSet};
13
14
use std::fmt;
14
15
use std::marker::PhantomData;
15
-
use std::path::{Path, PathBuf};
16
16
17
17
#[cfg(test)]
18
18
use crate::manifest::ManifestPackage;
···
119
119
Ok(deps)
120
120
}
121
121
122
-
pub fn read<FS: FileSystemReader, P: AsRef<Path>>(
122
+
pub fn read<FS: FileSystemReader, P: AsRef<Utf8Path>>(
123
123
path: P,
124
124
fs: &FS,
125
125
) -> Result<PackageConfig, Error> {
···
750
750
pub struct DocsPage {
751
751
pub title: String,
752
752
pub path: String,
753
-
pub source: PathBuf,
753
+
pub source: Utf8PathBuf,
754
754
}
755
755
756
756
#[derive(Deserialize, Debug, PartialEq, Eq, Clone)]
+3
-6
compiler-core/src/diagnostic.rs
+3
-6
compiler-core/src/diagnostic.rs
···
1
-
use std::path::PathBuf;
1
+
use camino::Utf8PathBuf;
2
2
3
3
pub use codespan_reporting::diagnostic::{LabelStyle, Severity};
4
4
use codespan_reporting::{diagnostic::Label as CodespanLabel, files::SimpleFile};
···
22
22
#[derive(Debug, Clone, PartialEq, Eq)]
23
23
pub struct Location {
24
24
pub src: SmolStr,
25
-
pub path: PathBuf,
25
+
pub path: Utf8PathBuf,
26
26
pub label: Label,
27
27
pub extra_labels: Vec<Label>,
28
28
}
···
61
61
}
62
62
63
63
fn write_span(&self, location: &Location, buffer: &mut Buffer) {
64
-
let file = SimpleFile::new(
65
-
location.path.to_string_lossy().to_string(),
66
-
location.src.as_str(),
67
-
);
64
+
let file = SimpleFile::new(location.path.to_string(), location.src.as_str());
68
65
let labels = location
69
66
.labels()
70
67
.map(|l| {
+27
-25
compiler-core/src/docs.rs
+27
-25
compiler-core/src/docs.rs
···
1
1
mod source_links;
2
2
3
-
use std::{path::PathBuf, time::SystemTime};
3
+
use std::time::SystemTime;
4
+
5
+
use camino::Utf8PathBuf;
4
6
5
7
use crate::{
6
8
ast::{
···
112
114
};
113
115
114
116
files.push(OutputFile {
115
-
path: PathBuf::from(&page.path),
117
+
path: Utf8PathBuf::from(&page.path),
116
118
content: Content::Text(temp.render().expect("Page template rendering")),
117
119
});
118
120
···
245
247
};
246
248
247
249
files.push(OutputFile {
248
-
path: PathBuf::from(format!("{}.html", module.name)),
250
+
path: Utf8PathBuf::from(format!("{}.html", module.name)),
249
251
content: Content::Text(
250
252
template
251
253
.render()
···
257
259
// Render static assets
258
260
259
261
files.push(OutputFile {
260
-
path: PathBuf::from("css/atom-one-light.min.css"),
262
+
path: Utf8PathBuf::from("css/atom-one-light.min.css"),
261
263
content: Content::Text(
262
264
std::include_str!("../templates/docs-css/atom-one-light.min.css").to_string(),
263
265
),
264
266
});
265
267
266
268
files.push(OutputFile {
267
-
path: PathBuf::from("css/atom-one-dark.min.css"),
269
+
path: Utf8PathBuf::from("css/atom-one-dark.min.css"),
268
270
content: Content::Text(
269
271
std::include_str!("../templates/docs-css/atom-one-dark.min.css").to_string(),
270
272
),
271
273
});
272
274
273
275
files.push(OutputFile {
274
-
path: PathBuf::from("css/index.css"),
276
+
path: Utf8PathBuf::from("css/index.css"),
275
277
content: Content::Text(std::include_str!("../templates/docs-css/index.css").to_string()),
276
278
});
277
279
278
280
// highlightjs:
279
281
280
282
files.push(OutputFile {
281
-
path: PathBuf::from("js/highlight.min.js"),
283
+
path: Utf8PathBuf::from("js/highlight.min.js"),
282
284
content: Content::Text(
283
285
std::include_str!("../templates/docs-js/highlight.min.js").to_string(),
284
286
),
285
287
});
286
288
287
289
files.push(OutputFile {
288
-
path: PathBuf::from("js/highlightjs-gleam.js"),
290
+
path: Utf8PathBuf::from("js/highlightjs-gleam.js"),
289
291
content: Content::Text(
290
292
std::include_str!("../templates/docs-js/highlightjs-gleam.js").to_string(),
291
293
),
292
294
});
293
295
294
296
files.push(OutputFile {
295
-
path: PathBuf::from("js/highlightjs-erlang.min.js"),
297
+
path: Utf8PathBuf::from("js/highlightjs-erlang.min.js"),
296
298
content: Content::Text(
297
299
std::include_str!("../templates/docs-js/highlightjs-erlang.min.js").to_string(),
298
300
),
299
301
});
300
302
301
303
files.push(OutputFile {
302
-
path: PathBuf::from("js/highlightjs-elixir.min.js"),
304
+
path: Utf8PathBuf::from("js/highlightjs-elixir.min.js"),
303
305
content: Content::Text(
304
306
std::include_str!("../templates/docs-js/highlightjs-elixir.min.js").to_string(),
305
307
),
306
308
});
307
309
308
310
files.push(OutputFile {
309
-
path: PathBuf::from("js/highlightjs-javascript.min.js"),
311
+
path: Utf8PathBuf::from("js/highlightjs-javascript.min.js"),
310
312
content: Content::Text(
311
313
std::include_str!("../templates/docs-js/highlightjs-javascript.min.js").to_string(),
312
314
),
313
315
});
314
316
315
317
files.push(OutputFile {
316
-
path: PathBuf::from("js/highlightjs-typescript.min.js"),
318
+
path: Utf8PathBuf::from("js/highlightjs-typescript.min.js"),
317
319
content: Content::Text(
318
320
std::include_str!("../templates/docs-js/highlightjs-typescript.min.js").to_string(),
319
321
),
···
322
324
// lunr.min.js, search-data.js and index.js:
323
325
324
326
files.push(OutputFile {
325
-
path: PathBuf::from("js/lunr.min.js"),
327
+
path: Utf8PathBuf::from("js/lunr.min.js"),
326
328
content: Content::Text(std::include_str!("../templates/docs-js/lunr.min.js").to_string()),
327
329
});
328
330
329
331
files.push(OutputFile {
330
-
path: PathBuf::from("search-data.js"),
332
+
path: Utf8PathBuf::from("search-data.js"),
331
333
content: Content::Text(format!(
332
334
"window.Gleam.initSearch({});",
333
335
serde_to_string(&escape_html_contents(search_indexes))
···
336
338
});
337
339
338
340
files.push(OutputFile {
339
-
path: PathBuf::from("js/index.js"),
341
+
path: Utf8PathBuf::from("js/index.js"),
340
342
content: Content::Text(std::include_str!("../templates/docs-js/index.js").to_string()),
341
343
});
342
344
343
345
// web fonts:
344
346
345
347
files.push(OutputFile {
346
-
path: PathBuf::from("fonts/karla-v23-regular-latin-ext.woff2"),
348
+
path: Utf8PathBuf::from("fonts/karla-v23-regular-latin-ext.woff2"),
347
349
content: Content::Binary(
348
350
include_bytes!("../templates/docs-fonts/karla-v23-regular-latin-ext.woff2").to_vec(),
349
351
),
350
352
});
351
353
352
354
files.push(OutputFile {
353
-
path: PathBuf::from("fonts/karla-v23-regular-latin.woff2"),
355
+
path: Utf8PathBuf::from("fonts/karla-v23-regular-latin.woff2"),
354
356
content: Content::Binary(
355
357
include_bytes!("../templates/docs-fonts/karla-v23-regular-latin.woff2").to_vec(),
356
358
),
357
359
});
358
360
359
361
files.push(OutputFile {
360
-
path: PathBuf::from("fonts/karla-v23-bold-latin-ext.woff2"),
362
+
path: Utf8PathBuf::from("fonts/karla-v23-bold-latin-ext.woff2"),
361
363
content: Content::Binary(
362
364
include_bytes!("../templates/docs-fonts/karla-v23-bold-latin-ext.woff2").to_vec(),
363
365
),
364
366
});
365
367
366
368
files.push(OutputFile {
367
-
path: PathBuf::from("fonts/karla-v23-bold-latin.woff2"),
369
+
path: Utf8PathBuf::from("fonts/karla-v23-bold-latin.woff2"),
368
370
content: Content::Binary(
369
371
include_bytes!("../templates/docs-fonts/karla-v23-bold-latin.woff2").to_vec(),
370
372
),
371
373
});
372
374
373
375
files.push(OutputFile {
374
-
path: PathBuf::from("fonts/ubuntu-mono-v15-regular-cyrillic-ext.woff2"),
376
+
path: Utf8PathBuf::from("fonts/ubuntu-mono-v15-regular-cyrillic-ext.woff2"),
375
377
content: Content::Binary(
376
378
include_bytes!("../templates/docs-fonts/ubuntu-mono-v15-regular-cyrillic-ext.woff2")
377
379
.to_vec(),
···
379
381
});
380
382
381
383
files.push(OutputFile {
382
-
path: PathBuf::from("fonts/ubuntu-mono-v15-regular-cyrillic.woff2"),
384
+
path: Utf8PathBuf::from("fonts/ubuntu-mono-v15-regular-cyrillic.woff2"),
383
385
content: Content::Binary(
384
386
include_bytes!("../templates/docs-fonts/ubuntu-mono-v15-regular-cyrillic.woff2")
385
387
.to_vec(),
···
387
389
});
388
390
389
391
files.push(OutputFile {
390
-
path: PathBuf::from("fonts/ubuntu-mono-v15-regular-greek-ext.woff2"),
392
+
path: Utf8PathBuf::from("fonts/ubuntu-mono-v15-regular-greek-ext.woff2"),
391
393
content: Content::Binary(
392
394
include_bytes!("../templates/docs-fonts/ubuntu-mono-v15-regular-greek-ext.woff2")
393
395
.to_vec(),
···
395
397
});
396
398
397
399
files.push(OutputFile {
398
-
path: PathBuf::from("fonts/ubuntu-mono-v15-regular-greek.woff2"),
400
+
path: Utf8PathBuf::from("fonts/ubuntu-mono-v15-regular-greek.woff2"),
399
401
content: Content::Binary(
400
402
include_bytes!("../templates/docs-fonts/ubuntu-mono-v15-regular-greek.woff2").to_vec(),
401
403
),
402
404
});
403
405
404
406
files.push(OutputFile {
405
-
path: PathBuf::from("fonts/ubuntu-mono-v15-regular-latin-ext.woff2"),
407
+
path: Utf8PathBuf::from("fonts/ubuntu-mono-v15-regular-latin-ext.woff2"),
406
408
content: Content::Binary(
407
409
include_bytes!("../templates/docs-fonts/ubuntu-mono-v15-regular-latin-ext.woff2")
408
410
.to_vec(),
···
410
412
});
411
413
412
414
files.push(OutputFile {
413
-
path: PathBuf::from("fonts/ubuntu-mono-v15-regular-latin.woff2"),
415
+
path: Utf8PathBuf::from("fonts/ubuntu-mono-v15-regular-latin.woff2"),
414
416
content: Content::Binary(
415
417
include_bytes!("../templates/docs-fonts/ubuntu-mono-v15-regular-latin.woff2").to_vec(),
416
418
),
+5
-8
compiler-core/src/docs/source_links.rs
+5
-8
compiler-core/src/docs/source_links.rs
···
5
5
line_numbers::LineNumbers,
6
6
paths::ProjectPaths,
7
7
};
8
-
use std::path::{Component, Path};
8
+
9
+
use camino::{Utf8Component, Utf8Path};
9
10
10
11
pub struct SourceLinker {
11
12
line_numbers: LineNumbers,
···
69
70
}
70
71
}
71
72
72
-
fn to_url_path(path: &Path) -> Option<String> {
73
+
fn to_url_path(path: &Utf8Path) -> Option<String> {
73
74
let mut buf = String::new();
74
75
for c in path.components() {
75
-
if let Component::Normal(s) = c {
76
-
if let Some(s) = s.to_str() {
77
-
buf.push_str(s);
78
-
} else {
79
-
return None;
80
-
}
76
+
if let Utf8Component::Normal(s) = c {
77
+
buf.push_str(s);
81
78
}
82
79
buf.push('/');
83
80
}
+21
-26
compiler-core/src/error.rs
+21
-26
compiler-core/src/error.rs
···
16
16
use smol_str::SmolStr;
17
17
use std::env;
18
18
use std::fmt::Debug;
19
-
use std::path::{Path, PathBuf};
20
19
use termcolor::Buffer;
21
20
use thiserror::Error;
21
+
22
+
use camino::{Utf8Path, Utf8PathBuf};
22
23
23
24
pub type Name = SmolStr;
24
25
···
34
35
pub struct UnknownImportDetails {
35
36
pub module: Name,
36
37
pub location: crate::ast::SrcSpan,
37
-
pub path: PathBuf,
38
+
pub path: Utf8PathBuf,
38
39
pub src: SmolStr,
39
40
pub modules: Vec<SmolStr>,
40
41
}
···
43
44
pub enum Error {
44
45
#[error("failed to parse Gleam source code")]
45
46
Parse {
46
-
path: PathBuf,
47
+
path: Utf8PathBuf,
47
48
src: SmolStr,
48
49
error: crate::parse::error::ParseError,
49
50
},
50
51
51
52
#[error("type checking failed")]
52
53
Type {
53
-
path: PathBuf,
54
+
path: Utf8PathBuf,
54
55
src: SmolStr,
55
56
error: crate::type_::Error,
56
57
},
···
65
66
#[error("duplicate module {module}")]
66
67
DuplicateModule {
67
68
module: Name,
68
-
first: PathBuf,
69
-
second: PathBuf,
69
+
first: Utf8PathBuf,
70
+
second: Utf8PathBuf,
70
71
},
71
72
72
73
#[error("duplicate source file {file}")]
···
82
83
FileIo {
83
84
kind: FileKind,
84
85
action: FileIoAction,
85
-
path: PathBuf,
86
+
path: Utf8PathBuf,
86
87
err: Option<String>,
87
88
},
88
89
···
105
106
ExpandTar { error: String },
106
107
107
108
#[error("{err}")]
108
-
AddTar { path: PathBuf, err: String },
109
+
AddTar { path: Utf8PathBuf, err: String },
109
110
110
111
#[error("{0}")]
111
112
TarFinish(String),
···
163
164
164
165
#[error("javascript codegen failed")]
165
166
JavaScript {
166
-
path: PathBuf,
167
+
path: Utf8PathBuf,
167
168
src: SmolStr,
168
169
error: crate::javascript::Error,
169
170
},
···
198
199
199
200
#[error("Expected package {expected} at path {path} but found {found} instead")]
200
201
WrongDependencyProvided {
201
-
path: PathBuf,
202
+
path: Utf8PathBuf,
202
203
expected: String,
203
204
found: String,
204
205
},
···
226
227
},
227
228
228
229
#[error("Opening docs at {path} failed: {error}")]
229
-
FailedToOpenDocs { path: PathBuf, error: String },
230
+
FailedToOpenDocs { path: Utf8PathBuf, error: String },
230
231
231
232
#[error("The package {package} requires a Gleam version satisfying {required_version} and you are using v{gleam_version}")]
232
233
IncompatibleCompilerVersion {
···
256
257
257
258
pub fn add_tar<P, E>(path: P, error: E) -> Error
258
259
where
259
-
P: AsRef<Path>,
260
+
P: AsRef<Utf8Path>,
260
261
E: std::error::Error,
261
262
{
262
263
Self::AddTar {
···
672
673
This was error from the tar library:
673
674
674
675
{}",
675
-
path.to_str().unwrap(),
676
-
err
676
+
path, err
677
677
);
678
678
Diagnostic {
679
679
title: "Failure creating tar archive".into(),
···
745
745
746
746
First: {}
747
747
Second: {}",
748
-
module,
749
-
first.to_str().expect("pretty error print PathBuf to_str"),
750
-
second.to_str().expect("pretty error print PathBuf to_str"),
748
+
module, first, second,
751
749
);
752
750
753
751
Diagnostic {
···
786
784
{}",
787
785
action.text(),
788
786
kind.text(),
789
-
path.to_string_lossy(),
787
+
path,
790
788
err,
791
789
);
792
790
Diagnostic {
···
2343
2341
Error::Format { problem_files } => {
2344
2342
let files: Vec<_> = problem_files
2345
2343
.iter()
2346
-
.flat_map(|formatted| formatted.source.to_str())
2344
+
.map(|formatted| formatted.source.as_str())
2347
2345
.map(|p| format!(" - {p}"))
2348
2346
.sorted()
2349
2347
.collect();
···
2490
2488
} => {
2491
2489
let text = format!(
2492
2490
"Expected package {} at path {} but found {} instead",
2493
-
expected,
2494
-
path.to_string_lossy(),
2495
-
found
2491
+
expected, path, found
2496
2492
);
2497
2493
2498
2494
Diagnostic {
···
2609
2605
2610
2606
{}
2611
2607
{}",
2612
-
path.to_string_lossy(),
2613
-
error,
2608
+
path, error,
2614
2609
);
2615
2610
Diagnostic {
2616
2611
title: "Failed to open docs".into(),
···
2750
2745
2751
2746
#[derive(Debug, Clone, PartialEq, Eq)]
2752
2747
pub struct Unformatted {
2753
-
pub source: PathBuf,
2754
-
pub destination: PathBuf,
2748
+
pub source: Utf8PathBuf,
2749
+
pub destination: Utf8PathBuf,
2755
2750
pub input: SmolStr,
2756
2751
pub output: String,
2757
2752
}
+4
-2
compiler-core/src/fix.rs
+4
-2
compiler-core/src/fix.rs
···
2
2
mod tests;
3
3
4
4
use smol_str::SmolStr;
5
-
use std::{collections::HashMap, path::Path};
5
+
use std::collections::HashMap;
6
6
use vec1::vec1;
7
+
8
+
use camino::Utf8Path;
7
9
8
10
use crate::{
9
11
ast::{
···
18
20
pub fn parse_fix_and_format(
19
21
assumed_target: Option<Target>,
20
22
src: &SmolStr,
21
-
path: &Path,
23
+
path: &Utf8Path,
22
24
) -> Result<(String, bool)> {
23
25
// Parse
24
26
let parsed = crate::parse::parse_module(src).map_err(|error| Error::Parse {
+4
-2
compiler-core/src/fix/tests.rs
+4
-2
compiler-core/src/fix/tests.rs
···
1
1
use super::*;
2
2
3
+
use camino::Utf8Path;
4
+
3
5
fn fix(target: Option<Target>, src: &str) -> String {
4
-
let (out, complete) = parse_fix_and_format(target, &src.into(), Path::new("test")).unwrap();
6
+
let (out, complete) = parse_fix_and_format(target, &src.into(), Utf8Path::new("test")).unwrap();
5
7
assert!(complete);
6
8
out
7
9
}
···
496
498
let src = r#"pub external fn main(wibble: Int, wobble: Float) -> Int =
497
499
"app" "main"
498
500
"#;
499
-
let (out, complete) = parse_fix_and_format(None, &src.into(), Path::new("test")).unwrap();
501
+
let (out, complete) = parse_fix_and_format(None, &src.into(), Utf8Path::new("test")).unwrap();
500
502
assert!(!complete);
501
503
assert_eq!(out, src)
502
504
}
+4
-2
compiler-core/src/format.rs
+4
-2
compiler-core/src/format.rs
···
13
13
};
14
14
use itertools::Itertools;
15
15
use smol_str::SmolStr;
16
-
use std::{path::Path, sync::Arc};
16
+
use std::sync::Arc;
17
17
use vec1::Vec1;
18
18
19
+
use camino::Utf8Path;
20
+
19
21
const INDENT: isize = 2;
20
22
21
-
pub fn pretty(writer: &mut impl Utf8Writer, src: &SmolStr, path: &Path) -> Result<()> {
23
+
pub fn pretty(writer: &mut impl Utf8Writer, src: &SmolStr, path: &Utf8Path) -> Result<()> {
22
24
let parsed = crate::parse::parse_module(src).map_err(|error| Error::Parse {
23
25
path: path.to_path_buf(),
24
26
src: src.clone(),
+4
-2
compiler-core/src/format/tests.rs
+4
-2
compiler-core/src/format/tests.rs
···
14
14
macro_rules! assert_format {
15
15
($src:expr $(,)?) => {
16
16
let mut writer = String::new();
17
-
$crate::format::pretty(&mut writer, &$src.into(), std::path::Path::new("<stdin>")).unwrap();
17
+
$crate::format::pretty(&mut writer, &$src.into(), camino::Utf8Path::new("<stdin>"))
18
+
.unwrap();
18
19
assert_eq!($src, writer);
19
20
};
20
21
}
···
23
24
macro_rules! assert_format_rewrite {
24
25
($src:expr, $output:expr $(,)?) => {
25
26
let mut writer = String::new();
26
-
$crate::format::pretty(&mut writer, &$src.into(), std::path::Path::new("<stdin>")).unwrap();
27
+
$crate::format::pretty(&mut writer, &$src.into(), camino::Utf8Path::new("<stdin>"))
28
+
.unwrap();
27
29
assert_eq!(writer, $output);
28
30
};
29
31
}
+2
-2
compiler-core/src/hex.rs
+2
-2
compiler-core/src/hex.rs
···
1
+
use camino::Utf8Path;
1
2
use debug_ignore::DebugIgnore;
2
3
use flate2::read::GzDecoder;
3
4
use futures::future;
4
5
use hexpm::version::Version;
5
-
use std::path::Path;
6
6
use tar::Archive;
7
7
8
8
use crate::{
···
209
209
210
210
// It would be really nice if this was async but the library is sync
211
211
pub fn extract_package_from_cache(&self, name: &str, version: &Version) -> Result<bool> {
212
-
let contents_path = Path::new("contents.tar.gz");
212
+
let contents_path = Utf8Path::new("contents.tar.gz");
213
213
let destination = self.paths.build_packages_package(name);
214
214
215
215
// If the directory already exists then there's nothing for us to do
+34
-38
compiler-core/src/io.rs
+34
-38
compiler-core/src/io.rs
···
4
4
use async_trait::async_trait;
5
5
use debug_ignore::DebugIgnore;
6
6
use flate2::read::GzDecoder;
7
-
use std::{
8
-
fmt::Debug,
9
-
io,
10
-
path::{Path, PathBuf},
11
-
time::SystemTime,
12
-
vec::IntoIter,
13
-
};
7
+
use std::{fmt::Debug, io, time::SystemTime, vec::IntoIter};
14
8
use tar::{Archive, Entry};
15
9
10
+
use camino::{Utf8Path, Utf8PathBuf};
11
+
16
12
pub trait Reader: std::io::Read {
17
13
/// A wrapper around `std::io::Read` that has Gleam's error handling.
18
14
fn read_bytes(&mut self, buffer: &mut [u8]) -> Result<usize> {
···
36
32
Error::FileIo {
37
33
action: FileIoAction::WriteTo,
38
34
kind: FileKind::File,
39
-
path: PathBuf::from("<in memory>"),
35
+
path: Utf8PathBuf::from("<in memory>"),
40
36
err: Some(error.to_string()),
41
37
}
42
38
}
···
93
89
#[derive(Debug, PartialEq, Eq, Clone)]
94
90
pub struct OutputFile {
95
91
pub content: Content,
96
-
pub path: PathBuf,
92
+
pub path: Utf8PathBuf,
97
93
}
98
94
99
95
#[derive(Debug)]
···
130
126
131
127
#[derive(Debug, Clone)]
132
128
pub struct DirEntry {
133
-
pub pathbuf: PathBuf,
129
+
pub pathbuf: Utf8PathBuf,
134
130
}
135
131
136
132
impl DirEntry {
137
-
pub fn from_path<P: AsRef<Path>>(path: P) -> DirEntry {
133
+
pub fn from_path<P: AsRef<Utf8Path>>(path: P) -> DirEntry {
138
134
DirEntry {
139
135
pathbuf: path.as_ref().to_path_buf(),
140
136
}
141
137
}
142
138
143
-
pub fn from_pathbuf(pathbuf: PathBuf) -> DirEntry {
139
+
pub fn from_pathbuf(pathbuf: Utf8PathBuf) -> DirEntry {
144
140
DirEntry { pathbuf }
145
141
}
146
142
147
-
pub fn as_path(&self) -> &Path {
143
+
pub fn as_path(&self) -> &Utf8Path {
148
144
self.pathbuf.as_path()
149
145
}
150
146
151
-
pub fn into_path(self) -> PathBuf {
147
+
pub fn into_path(self) -> Utf8PathBuf {
152
148
self.pathbuf
153
149
}
154
150
}
···
157
153
/// Typically we use an implementation that reads from the file system,
158
154
/// but in tests and in other places other implementations may be used.
159
155
pub trait FileSystemReader {
160
-
fn gleam_source_files(&self, dir: &Path) -> Vec<PathBuf>;
161
-
fn gleam_cache_files(&self, dir: &Path) -> Vec<PathBuf>;
162
-
fn read_dir(&self, path: &Path) -> Result<ReadDir>;
163
-
fn read(&self, path: &Path) -> Result<String, Error>;
164
-
fn read_bytes(&self, path: &Path) -> Result<Vec<u8>, Error>;
165
-
fn reader(&self, path: &Path) -> Result<WrappedReader, Error>;
166
-
fn is_file(&self, path: &Path) -> bool;
167
-
fn is_directory(&self, path: &Path) -> bool;
168
-
fn modification_time(&self, path: &Path) -> Result<SystemTime, Error>;
169
-
fn canonicalise(&self, path: &Path) -> Result<PathBuf, Error>;
156
+
fn gleam_source_files(&self, dir: &Utf8Path) -> Vec<Utf8PathBuf>;
157
+
fn gleam_cache_files(&self, dir: &Utf8Path) -> Vec<Utf8PathBuf>;
158
+
fn read_dir(&self, path: &Utf8Path) -> Result<ReadDir>;
159
+
fn read(&self, path: &Utf8Path) -> Result<String, Error>;
160
+
fn read_bytes(&self, path: &Utf8Path) -> Result<Vec<u8>, Error>;
161
+
fn reader(&self, path: &Utf8Path) -> Result<WrappedReader, Error>;
162
+
fn is_file(&self, path: &Utf8Path) -> bool;
163
+
fn is_directory(&self, path: &Utf8Path) -> bool;
164
+
fn modification_time(&self, path: &Utf8Path) -> Result<SystemTime, Error>;
165
+
fn canonicalise(&self, path: &Utf8Path) -> Result<Utf8PathBuf, Error>;
170
166
}
171
167
172
168
/// A trait used to run other programs.
···
176
172
program: &str,
177
173
args: &[String],
178
174
env: &[(&str, String)],
179
-
cwd: Option<&Path>,
175
+
cwd: Option<&Utf8Path>,
180
176
stdio: Stdio,
181
177
) -> Result<i32, Error>;
182
178
}
···
200
196
/// Typically we use an implementation that writes to the file system,
201
197
/// but in tests and in other places other implementations may be used.
202
198
pub trait FileSystemWriter {
203
-
fn mkdir(&self, path: &Path) -> Result<(), Error>;
204
-
fn write(&self, path: &Path, content: &str) -> Result<(), Error>;
205
-
fn write_bytes(&self, path: &Path, content: &[u8]) -> Result<(), Error>;
206
-
fn delete(&self, path: &Path) -> Result<(), Error>;
207
-
fn copy(&self, from: &Path, to: &Path) -> Result<(), Error>;
208
-
fn copy_dir(&self, from: &Path, to: &Path) -> Result<(), Error>;
209
-
fn hardlink(&self, from: &Path, to: &Path) -> Result<(), Error>;
210
-
fn symlink_dir(&self, from: &Path, to: &Path) -> Result<(), Error>;
211
-
fn delete_file(&self, path: &Path) -> Result<(), Error>;
199
+
fn mkdir(&self, path: &Utf8Path) -> Result<(), Error>;
200
+
fn write(&self, path: &Utf8Path, content: &str) -> Result<(), Error>;
201
+
fn write_bytes(&self, path: &Utf8Path, content: &[u8]) -> Result<(), Error>;
202
+
fn delete(&self, path: &Utf8Path) -> Result<(), Error>;
203
+
fn copy(&self, from: &Utf8Path, to: &Utf8Path) -> Result<(), Error>;
204
+
fn copy_dir(&self, from: &Utf8Path, to: &Utf8Path) -> Result<(), Error>;
205
+
fn hardlink(&self, from: &Utf8Path, to: &Utf8Path) -> Result<(), Error>;
206
+
fn symlink_dir(&self, from: &Utf8Path, to: &Utf8Path) -> Result<(), Error>;
207
+
fn delete_file(&self, path: &Utf8Path) -> Result<(), Error>;
212
208
}
213
209
214
210
#[derive(Debug)]
215
211
/// A wrapper around a Read implementing object that has Gleam's error handling.
216
212
pub struct WrappedReader {
217
-
path: PathBuf,
213
+
path: Utf8PathBuf,
218
214
inner: DebugIgnore<Box<dyn std::io::Read>>,
219
215
}
220
216
221
217
impl WrappedReader {
222
-
pub fn new(path: &Path, inner: Box<dyn std::io::Read>) -> Self {
218
+
pub fn new(path: &Utf8Path, inner: Box<dyn std::io::Read>) -> Self {
223
219
Self {
224
220
path: path.to_path_buf(),
225
221
inner: DebugIgnore(inner),
···
275
271
276
272
fn io_result_unpack(
277
273
&self,
278
-
path: &Path,
274
+
path: &Utf8Path,
279
275
archive: Archive<GzDecoder<Entry<'_, WrappedReader>>>,
280
276
) -> io::Result<()>;
281
277
282
278
fn unpack(
283
279
&self,
284
-
path: &Path,
280
+
path: &Utf8Path,
285
281
archive: Archive<GzDecoder<Entry<'_, WrappedReader>>>,
286
282
) -> Result<()> {
287
283
tracing::debug!(path = ?path, "unpacking tar archive");
+34
-28
compiler-core/src/io/memory.rs
+34
-28
compiler-core/src/io/memory.rs
···
1
1
use lazy_static::__Deref;
2
2
3
3
use super::*;
4
-
use std::{cell::RefCell, collections::HashMap, ffi::OsStr, rc::Rc, time::Duration};
4
+
use std::{cell::RefCell, collections::HashMap, rc::Rc, time::Duration};
5
+
6
+
use camino::{Utf8Path, Utf8PathBuf};
5
7
6
8
// An in memory sharable collection of pretend files that can be used in place
7
9
// of a real file system. It is a shared reference to a set of buffer than can
···
17
19
//
18
20
#[derive(Clone, Default, Debug, PartialEq, Eq)]
19
21
pub struct InMemoryFileSystem {
20
-
files: Rc<RefCell<HashMap<PathBuf, InMemoryFile>>>,
22
+
files: Rc<RefCell<HashMap<Utf8PathBuf, InMemoryFile>>>,
21
23
}
22
24
23
25
impl InMemoryFileSystem {
···
29
31
///
30
32
/// Panics if this is not the only reference to the underlying files.
31
33
///
32
-
pub fn into_contents(self) -> HashMap<PathBuf, Content> {
34
+
pub fn into_contents(self) -> HashMap<Utf8PathBuf, Content> {
33
35
Rc::try_unwrap(self.files)
34
36
.expect("InMemoryFileSystem::into_files called on a clone")
35
37
.into_inner()
···
38
40
.collect()
39
41
}
40
42
41
-
pub fn paths(&self) -> Vec<PathBuf> {
43
+
pub fn paths(&self) -> Vec<Utf8PathBuf> {
42
44
self.files.borrow().keys().cloned().collect()
43
45
}
44
46
···
47
49
///
48
50
/// Panics if the file does not exist.
49
51
///
50
-
pub fn set_modification_time(&self, path: &Path, time: SystemTime) {
52
+
pub fn set_modification_time(&self, path: &Utf8Path, time: SystemTime) {
51
53
self.files
52
54
.deref()
53
55
.borrow_mut()
···
56
58
.modification_time = time;
57
59
}
58
60
59
-
pub fn try_set_modification_time(&self, path: &Path, time: SystemTime) -> Result<(), Error> {
61
+
pub fn try_set_modification_time(
62
+
&self,
63
+
path: &Utf8Path,
64
+
time: SystemTime,
65
+
) -> Result<(), Error> {
60
66
self.files
61
67
.deref()
62
68
.borrow_mut()
···
73
79
}
74
80
75
81
impl FileSystemWriter for InMemoryFileSystem {
76
-
fn delete(&self, path: &Path) -> Result<(), Error> {
82
+
fn delete(&self, path: &Utf8Path) -> Result<(), Error> {
77
83
let mut files = self.files.deref().borrow_mut();
78
84
let _ = files.remove(path);
79
85
Ok(())
80
86
}
81
87
82
-
fn copy(&self, from: &Path, to: &Path) -> Result<(), Error> {
88
+
fn copy(&self, from: &Utf8Path, to: &Utf8Path) -> Result<(), Error> {
83
89
self.write_bytes(to, &self.read_bytes(from)?)
84
90
}
85
91
86
-
fn copy_dir(&self, _: &Path, _: &Path) -> Result<(), Error> {
92
+
fn copy_dir(&self, _: &Utf8Path, _: &Utf8Path) -> Result<(), Error> {
87
93
panic!("unimplemented") // TODO
88
94
}
89
95
90
-
fn mkdir(&self, _: &Path) -> Result<(), Error> {
96
+
fn mkdir(&self, _: &Utf8Path) -> Result<(), Error> {
91
97
Ok(())
92
98
}
93
99
94
-
fn hardlink(&self, _: &Path, _: &Path) -> Result<(), Error> {
100
+
fn hardlink(&self, _: &Utf8Path, _: &Utf8Path) -> Result<(), Error> {
95
101
panic!("unimplemented") // TODO
96
102
}
97
103
98
-
fn symlink_dir(&self, _: &Path, _: &Path) -> Result<(), Error> {
104
+
fn symlink_dir(&self, _: &Utf8Path, _: &Utf8Path) -> Result<(), Error> {
99
105
panic!("unimplemented") // TODO
100
106
}
101
107
102
-
fn delete_file(&self, path: &Path) -> Result<(), Error> {
108
+
fn delete_file(&self, path: &Utf8Path) -> Result<(), Error> {
103
109
let _ = self.files.deref().borrow_mut().remove(path);
104
110
Ok(())
105
111
}
106
112
107
-
fn write(&self, path: &Path, content: &str) -> Result<(), Error> {
113
+
fn write(&self, path: &Utf8Path, content: &str) -> Result<(), Error> {
108
114
self.write_bytes(path, content.as_bytes())
109
115
}
110
116
111
-
fn write_bytes(&self, path: &Path, content: &[u8]) -> Result<(), Error> {
117
+
fn write_bytes(&self, path: &Utf8Path, content: &[u8]) -> Result<(), Error> {
112
118
let mut file = InMemoryFile::default();
113
119
_ = io::Write::write(&mut file, content).expect("channel buffer write");
114
120
_ = self
···
121
127
}
122
128
123
129
impl FileSystemReader for InMemoryFileSystem {
124
-
fn canonicalise(&self, path: &Path) -> Result<PathBuf, Error> {
130
+
fn canonicalise(&self, path: &Utf8Path) -> Result<Utf8PathBuf, Error> {
125
131
Ok(path.to_path_buf())
126
132
}
127
133
128
-
fn gleam_source_files(&self, dir: &Path) -> Vec<PathBuf> {
134
+
fn gleam_source_files(&self, dir: &Utf8Path) -> Vec<Utf8PathBuf> {
129
135
self.files
130
136
.deref()
131
137
.borrow()
132
138
.iter()
133
139
.map(|(file_path, _)| file_path.to_path_buf())
134
140
.filter(|file_path| file_path.starts_with(dir))
135
-
.filter(|file_path| file_path.extension() == Some(OsStr::new("gleam")))
141
+
.filter(|file_path| file_path.extension() == Some("gleam"))
136
142
.collect()
137
143
}
138
144
139
-
fn gleam_cache_files(&self, dir: &Path) -> Vec<PathBuf> {
145
+
fn gleam_cache_files(&self, dir: &Utf8Path) -> Vec<Utf8PathBuf> {
140
146
self.files
141
147
.deref()
142
148
.borrow()
143
149
.iter()
144
150
.map(|(file_path, _)| file_path.to_path_buf())
145
151
.filter(|file_path| file_path.starts_with(dir))
146
-
.filter(|file_path| file_path.extension() == Some(OsStr::new("cache")))
152
+
.filter(|file_path| file_path.extension() == Some("cache"))
147
153
.collect()
148
154
}
149
155
150
-
fn read(&self, path: &Path) -> Result<String, Error> {
156
+
fn read(&self, path: &Utf8Path) -> Result<String, Error> {
151
157
let path = path.to_path_buf();
152
158
let files = self.files.deref().borrow();
153
159
let file = files.get(&path).ok_or_else(|| Error::FileIo {
···
166
172
Ok(unicode)
167
173
}
168
174
169
-
fn read_bytes(&self, path: &Path) -> Result<Vec<u8>, Error> {
175
+
fn read_bytes(&self, path: &Utf8Path) -> Result<Vec<u8>, Error> {
170
176
let path = path.to_path_buf();
171
177
let files = self.files.deref().borrow();
172
178
let file = files.get(&path).ok_or_else(|| Error::FileIo {
···
179
185
Ok(bytes)
180
186
}
181
187
182
-
fn is_file(&self, path: &Path) -> bool {
188
+
fn is_file(&self, path: &Utf8Path) -> bool {
183
189
self.files.deref().borrow().contains_key(path)
184
190
}
185
191
186
-
fn is_directory(&self, path: &Path) -> bool {
192
+
fn is_directory(&self, path: &Utf8Path) -> bool {
187
193
self.files
188
194
.deref()
189
195
.borrow()
···
191
197
.any(|file_path| file_path.starts_with(path))
192
198
}
193
199
194
-
fn reader(&self, _path: &Path) -> Result<WrappedReader, Error> {
200
+
fn reader(&self, _path: &Utf8Path) -> Result<WrappedReader, Error> {
195
201
// TODO
196
202
unreachable!("Memory reader unimplemented")
197
203
}
198
204
199
-
fn read_dir(&self, path: &Path) -> Result<ReadDir> {
205
+
fn read_dir(&self, path: &Utf8Path) -> Result<ReadDir> {
200
206
let read_dir = ReadDir::from_iter(
201
207
self.files
202
208
.deref()
···
211
217
Ok(read_dir)
212
218
}
213
219
214
-
fn modification_time(&self, path: &Path) -> Result<SystemTime, Error> {
220
+
fn modification_time(&self, path: &Utf8Path) -> Result<SystemTime, Error> {
215
221
let files = self.files.deref().borrow();
216
222
let file = files.get(path).ok_or_else(|| Error::FileIo {
217
223
kind: FileKind::File,
···
283
289
_program: &str,
284
290
_args: &[String],
285
291
_env: &[(&str, String)],
286
-
_cwd: Option<&Path>,
292
+
_cwd: Option<&Utf8Path>,
287
293
_stdio: Stdio,
288
294
) -> Result<i32, Error> {
289
295
Ok(0) // Always succeed.
+3
-3
compiler-core/src/javascript.rs
+3
-3
compiler-core/src/javascript.rs
···
5
5
mod tests;
6
6
mod typescript;
7
7
8
-
use std::path::Path;
8
+
use camino::Utf8Path;
9
9
10
10
use crate::type_::PRELUDE_MODULE_NAME;
11
11
use crate::{
···
522
522
pub fn module(
523
523
module: &TypedModule,
524
524
line_numbers: &LineNumbers,
525
-
path: &Path,
525
+
path: &Utf8Path,
526
526
src: &SmolStr,
527
527
) -> Result<String, crate::Error> {
528
528
let document = Generator::new(line_numbers, module)
···
537
537
538
538
pub fn ts_declaration(
539
539
module: &TypedModule,
540
-
path: &Path,
540
+
path: &Utf8Path,
541
541
src: &SmolStr,
542
542
) -> Result<String, crate::Error> {
543
543
let document = typescript::TypeScriptGenerator::new(module)
+3
-3
compiler-core/src/javascript/tests.rs
+3
-3
compiler-core/src/javascript/tests.rs
···
4
4
uid::UniqueIdGenerator,
5
5
warning::TypeWarningEmitter,
6
6
};
7
-
use std::path::Path;
7
+
use camino::Utf8Path;
8
8
9
9
mod assignments;
10
10
mod bit_strings;
···
118
118
pub fn compile_js(src: &str, dep: Option<(&str, &str, &str)>) -> String {
119
119
let ast = compile(src, dep);
120
120
let line_numbers = LineNumbers::new(src);
121
-
module(&ast, &line_numbers, Path::new(""), &"".into()).unwrap()
121
+
module(&ast, &line_numbers, Utf8Path::new(""), &"".into()).unwrap()
122
122
}
123
123
124
124
pub fn compile_ts(src: &str, dep: Option<(&str, &str, &str)>) -> String {
125
125
let ast = compile(src, dep);
126
-
ts_declaration(&ast, Path::new(""), &src.into()).unwrap()
126
+
ts_declaration(&ast, Utf8Path::new(""), &src.into()).unwrap()
127
127
}
+4
-2
compiler-core/src/language_server/compiler.rs
+4
-2
compiler-core/src/language_server/compiler.rs
···
14
14
warning::VectorWarningEmitterIO,
15
15
Error, Result, Warning,
16
16
};
17
-
use std::{collections::HashMap, path::PathBuf, sync::Arc};
17
+
use std::{collections::HashMap, sync::Arc};
18
+
19
+
use camino::Utf8PathBuf;
18
20
19
21
/// A wrapper around the project compiler which makes it possible to repeatedly
20
22
/// recompile the top level package, reusing the information about the already
···
91
93
})
92
94
}
93
95
94
-
pub fn compile(&mut self) -> Result<Vec<PathBuf>, Error> {
96
+
pub fn compile(&mut self) -> Result<Vec<Utf8PathBuf>, Error> {
95
97
// Lock the build directory to ensure to ensure we are the only one compiling
96
98
let _lock_guard = self.locker.lock_for_build();
97
99
+4
-4
compiler-core/src/language_server/engine.rs
+4
-4
compiler-core/src/language_server/engine.rs
···
11
11
type_::{pretty::Printer, PreludeType, ValueConstructorVariant},
12
12
Error, Result, Warning,
13
13
};
14
+
use camino::Utf8PathBuf;
14
15
use lsp_types::{self as lsp, Hover, HoverContents, MarkedString, Url};
15
16
use smol_str::SmolStr;
16
-
use std::path::PathBuf;
17
17
use strum::IntoEnumIterator;
18
18
19
19
use super::{src_span_to_lsp_range, DownloadDependencies, MakeLocker};
···
28
28
#[derive(Debug, PartialEq, Eq)]
29
29
pub enum Compilation {
30
30
/// Compilation was attempted and succeeded for these modules.
31
-
Yes(Vec<PathBuf>),
31
+
Yes(Vec<Utf8PathBuf>),
32
32
/// Compilation was not attempted for this operation.
33
33
No,
34
34
}
···
43
43
/// discarded and reloaded to handle any changes to dependencies.
44
44
pub(crate) compiler: LspProjectCompiler<FileSystemProxy<IO>>,
45
45
46
-
modules_compiled_since_last_feedback: Vec<PathBuf>,
46
+
modules_compiled_since_last_feedback: Vec<Utf8PathBuf>,
47
47
compiled_since_last_feedback: bool,
48
48
49
49
// Used to publish progress notifications to the client without waiting for
···
283
283
let path = uri.to_file_path().expect("URL file");
284
284
285
285
#[cfg(not(any(unix, windows, target_os = "redox", target_os = "wasi")))]
286
-
let path: PathBuf = uri.path().into();
286
+
let path: Utf8PathBuf = uri.path().into();
287
287
288
288
let components = path
289
289
.strip_prefix(self.paths.root())
+20
-22
compiler-core/src/language_server/feedback.rs
+20
-22
compiler-core/src/language_server/feedback.rs
···
1
1
use crate::{diagnostic::Diagnostic, Error, Warning};
2
-
use std::{
3
-
collections::{HashMap, HashSet},
4
-
path::PathBuf,
5
-
};
2
+
use std::collections::{HashMap, HashSet};
3
+
4
+
use camino::Utf8PathBuf;
6
5
7
6
use super::engine::Compilation;
8
7
9
8
#[derive(Debug, Default, PartialEq, Eq)]
10
9
pub struct Feedback {
11
-
pub diagnostics: HashMap<PathBuf, Vec<Diagnostic>>,
10
+
pub diagnostics: HashMap<Utf8PathBuf, Vec<Diagnostic>>,
12
11
pub messages: Vec<Diagnostic>,
13
12
}
14
13
15
14
impl Feedback {
16
15
/// Set the diagnostics for a file to an empty vector. This will overwrite
17
16
/// any existing diagnostics on the client.
18
-
pub fn unset_existing_diagnostics(&mut self, path: PathBuf) {
17
+
pub fn unset_existing_diagnostics(&mut self, path: Utf8PathBuf) {
19
18
_ = self.diagnostics.insert(path, vec![]);
20
19
}
21
20
22
-
pub fn append_diagnostic(&mut self, path: PathBuf, diagnostic: Diagnostic) {
21
+
pub fn append_diagnostic(&mut self, path: Utf8PathBuf, diagnostic: Diagnostic) {
23
22
self.diagnostics
24
23
.entry(path)
25
24
.or_insert_with(Vec::new)
···
44
43
///
45
44
#[derive(Debug, Default)]
46
45
pub struct FeedbackBookKeeper {
47
-
files_with_warnings: HashSet<PathBuf>,
48
-
files_with_errors: HashSet<PathBuf>,
46
+
files_with_warnings: HashSet<Utf8PathBuf>,
47
+
files_with_errors: HashSet<Utf8PathBuf>,
49
48
}
50
49
51
50
impl FeedbackBookKeeper {
···
137
136
138
137
#[cfg(test)]
139
138
mod tests {
140
-
use std::path::Path;
141
139
142
140
use super::*;
143
141
use crate::{
···
149
147
#[test]
150
148
fn feedback() {
151
149
let mut book_keeper = FeedbackBookKeeper::default();
152
-
let file1 = PathBuf::from("src/file1.gleam");
153
-
let file2 = PathBuf::from("src/file2.gleam");
154
-
let file3 = PathBuf::from("src/file3.gleam");
150
+
let file1 = Utf8PathBuf::from("src/file1.gleam");
151
+
let file2 = Utf8PathBuf::from("src/file2.gleam");
152
+
let file3 = Utf8PathBuf::from("src/file3.gleam");
155
153
156
154
let warning1 = Warning::Type {
157
155
path: file1.clone(),
···
212
210
// location.
213
211
214
212
let mut book_keeper = FeedbackBookKeeper::default();
215
-
let file1 = PathBuf::from("src/file1.gleam");
213
+
let file1 = Utf8PathBuf::from("src/file1.gleam");
216
214
217
215
let warning1 = Warning::Type {
218
216
path: file1.clone(),
···
245
243
// location.
246
244
247
245
let mut book_keeper = FeedbackBookKeeper::default();
248
-
let file1 = PathBuf::from("src/file1.gleam");
249
-
let file3 = PathBuf::from("src/file2.gleam");
246
+
let file1 = Utf8PathBuf::from("src/file1.gleam");
247
+
let file3 = Utf8PathBuf::from("src/file2.gleam");
250
248
251
249
let warning1 = Warning::Type {
252
250
path: file1.clone(),
···
312
310
// when a successful compilation occurs.
313
311
314
312
let mut book_keeper = FeedbackBookKeeper::default();
315
-
let file1 = PathBuf::from("src/file1.gleam");
316
-
let file2 = PathBuf::from("src/file2.gleam");
313
+
let file1 = Utf8PathBuf::from("src/file1.gleam");
314
+
let file2 = Utf8PathBuf::from("src/file2.gleam");
317
315
318
316
let error = Error::Parse {
319
317
path: file1.clone(),
···
353
351
#[test]
354
352
fn second_failure_unsets_previous_error() {
355
353
let mut book_keeper = FeedbackBookKeeper::default();
356
-
let file1 = PathBuf::from("src/file1.gleam");
357
-
let file2 = PathBuf::from("src/file2.gleam");
354
+
let file1 = Utf8PathBuf::from("src/file1.gleam");
355
+
let file2 = Utf8PathBuf::from("src/file2.gleam");
358
356
359
-
let error = |file: &Path| Error::Parse {
357
+
let error = |file: &camino::Utf8Path| Error::Parse {
360
358
path: file.to_path_buf(),
361
359
src: "blah".into(),
362
360
error: ParseError {
···
397
395
#[test]
398
396
fn successful_non_compilation_does_not_remove_error_diagnostic() {
399
397
let mut book_keeper = FeedbackBookKeeper::default();
400
-
let file1 = PathBuf::from("src/file1.gleam");
398
+
let file1 = Utf8PathBuf::from("src/file1.gleam");
401
399
402
400
let error = Error::Parse {
403
401
path: file1.clone(),
+25
-26
compiler-core/src/language_server/files.rs
+25
-26
compiler-core/src/language_server/files.rs
···
1
-
use std::{
2
-
path::{Path, PathBuf},
3
-
time::SystemTime,
4
-
};
1
+
use std::time::SystemTime;
5
2
6
3
use debug_ignore::DebugIgnore;
7
4
···
12
9
},
13
10
Result,
14
11
};
12
+
13
+
use camino::{Utf8Path, Utf8PathBuf};
15
14
16
15
// A proxy intended for `LanguageServer` to use when files are modified in
17
16
// memory but not yet saved to disc by the client.
···
42
41
&self.io
43
42
}
44
43
45
-
pub fn write_mem_cache(&mut self, path: &Path, content: &str) -> Result<()> {
44
+
pub fn write_mem_cache(&mut self, path: &Utf8Path, content: &str) -> Result<()> {
46
45
let write_result = self.edit_cache.write(path, content);
47
46
self.edit_cache
48
47
.try_set_modification_time(path, SystemTime::now())?;
49
48
write_result
50
49
}
51
50
52
-
pub fn delete_mem_cache(&self, path: &Path) -> Result<()> {
51
+
pub fn delete_mem_cache(&self, path: &Utf8Path) -> Result<()> {
53
52
self.edit_cache.delete(path)
54
53
}
55
54
}
···
59
58
where
60
59
IO: FileSystemWriter,
61
60
{
62
-
fn mkdir(&self, path: &Path) -> Result<()> {
61
+
fn mkdir(&self, path: &Utf8Path) -> Result<()> {
63
62
self.io.mkdir(path)
64
63
}
65
64
66
-
fn write(&self, path: &Path, content: &str) -> Result<()> {
65
+
fn write(&self, path: &Utf8Path, content: &str) -> Result<()> {
67
66
self.io.write(path, content)
68
67
}
69
68
70
-
fn write_bytes(&self, path: &Path, content: &[u8]) -> Result<()> {
69
+
fn write_bytes(&self, path: &Utf8Path, content: &[u8]) -> Result<()> {
71
70
self.io.write_bytes(path, content)
72
71
}
73
72
74
-
fn delete(&self, path: &Path) -> Result<()> {
73
+
fn delete(&self, path: &Utf8Path) -> Result<()> {
75
74
self.io.delete(path)
76
75
}
77
76
78
-
fn copy(&self, from: &Path, to: &Path) -> Result<()> {
77
+
fn copy(&self, from: &Utf8Path, to: &Utf8Path) -> Result<()> {
79
78
self.io.copy(from, to)
80
79
}
81
80
82
-
fn copy_dir(&self, from: &Path, to: &Path) -> Result<()> {
81
+
fn copy_dir(&self, from: &Utf8Path, to: &Utf8Path) -> Result<()> {
83
82
self.io.copy_dir(from, to)
84
83
}
85
84
86
-
fn hardlink(&self, from: &Path, to: &Path) -> Result<()> {
85
+
fn hardlink(&self, from: &Utf8Path, to: &Utf8Path) -> Result<()> {
87
86
self.io.hardlink(from, to)
88
87
}
89
88
90
-
fn symlink_dir(&self, from: &Path, to: &Path) -> Result<()> {
89
+
fn symlink_dir(&self, from: &Utf8Path, to: &Utf8Path) -> Result<()> {
91
90
self.io.symlink_dir(from, to)
92
91
}
93
92
94
-
fn delete_file(&self, path: &Path) -> Result<()> {
93
+
fn delete_file(&self, path: &Utf8Path) -> Result<()> {
95
94
self.io.delete_file(path)
96
95
}
97
96
}
···
100
99
where
101
100
IO: FileSystemReader,
102
101
{
103
-
fn gleam_source_files(&self, dir: &Path) -> Vec<PathBuf> {
102
+
fn gleam_source_files(&self, dir: &Utf8Path) -> Vec<Utf8PathBuf> {
104
103
self.io.gleam_source_files(dir)
105
104
}
106
105
107
-
fn gleam_cache_files(&self, dir: &Path) -> Vec<PathBuf> {
106
+
fn gleam_cache_files(&self, dir: &Utf8Path) -> Vec<Utf8PathBuf> {
108
107
self.io.gleam_cache_files(dir)
109
108
}
110
109
111
-
fn read_dir(&self, path: &Path) -> Result<ReadDir> {
110
+
fn read_dir(&self, path: &Utf8Path) -> Result<ReadDir> {
112
111
self.io.read_dir(path)
113
112
}
114
113
115
-
fn read(&self, path: &Path) -> Result<String> {
114
+
fn read(&self, path: &Utf8Path) -> Result<String> {
116
115
match self.edit_cache.read(path) {
117
116
result @ Ok(_) => result,
118
117
Err(_) => self.io.read(path),
119
118
}
120
119
}
121
120
122
-
fn read_bytes(&self, path: &Path) -> Result<Vec<u8>> {
121
+
fn read_bytes(&self, path: &Utf8Path) -> Result<Vec<u8>> {
123
122
match self.edit_cache.read_bytes(path) {
124
123
result @ Ok(_) => result,
125
124
Err(_) => self.io.read_bytes(path),
126
125
}
127
126
}
128
127
129
-
fn reader(&self, path: &Path) -> Result<WrappedReader> {
128
+
fn reader(&self, path: &Utf8Path) -> Result<WrappedReader> {
130
129
self.io.reader(path)
131
130
}
132
131
133
132
// Cache overides existence of file
134
-
fn is_file(&self, path: &Path) -> bool {
133
+
fn is_file(&self, path: &Utf8Path) -> bool {
135
134
self.edit_cache.is_file(path) || self.io.is_file(path)
136
135
}
137
136
138
137
// Cache overides existence of directory
139
-
fn is_directory(&self, path: &Path) -> bool {
138
+
fn is_directory(&self, path: &Utf8Path) -> bool {
140
139
self.edit_cache.is_directory(path) || self.io.is_directory(path)
141
140
}
142
141
143
-
fn modification_time(&self, path: &Path) -> Result<SystemTime> {
142
+
fn modification_time(&self, path: &Utf8Path) -> Result<SystemTime> {
144
143
match self.edit_cache.modification_time(path) {
145
144
result @ Ok(_) => result,
146
145
Err(_) => self.io.modification_time(path),
147
146
}
148
147
}
149
148
150
-
fn canonicalise(&self, path: &Path) -> Result<PathBuf, crate::Error> {
149
+
fn canonicalise(&self, path: &Utf8Path) -> Result<Utf8PathBuf, crate::Error> {
151
150
self.io.canonicalise(path)
152
151
}
153
152
}
···
161
160
_program: &str,
162
161
_args: &[String],
163
162
_env: &[(&str, String)],
164
-
_cwd: Option<&Path>,
163
+
_cwd: Option<&Utf8Path>,
165
164
_stdio: Stdio,
166
165
) -> Result<i32> {
167
166
panic!("The language server is not permitted to create subprocesses")
+29
-27
compiler-core/src/language_server/router.rs
+29
-27
compiler-core/src/language_server/router.rs
···
8
8
paths::ProjectPaths,
9
9
Error, Result,
10
10
};
11
-
use std::{
12
-
collections::{hash_map::Entry, HashMap},
13
-
path::{Path, PathBuf},
14
-
};
11
+
use std::collections::{hash_map::Entry, HashMap};
12
+
13
+
use camino::{Utf8Path, Utf8PathBuf};
15
14
16
15
use super::feedback::FeedbackBookKeeper;
17
16
···
25
24
#[derive(Debug)]
26
25
pub struct Router<IO, Reporter> {
27
26
io: FileSystemProxy<IO>,
28
-
engines: HashMap<PathBuf, Project<IO, Reporter>>,
27
+
engines: HashMap<Utf8PathBuf, Project<IO, Reporter>>,
29
28
progress_reporter: Reporter,
30
29
}
31
30
···
49
48
}
50
49
}
51
50
52
-
pub fn project_for_path(&mut self, path: &Path) -> Result<Option<&mut Project<IO, Reporter>>> {
51
+
pub fn project_for_path(
52
+
&mut self,
53
+
path: &Utf8Path,
54
+
) -> Result<Option<&mut Project<IO, Reporter>>> {
53
55
let path = match find_gleam_project_parent(&self.io, path) {
54
56
Some(x) => x,
55
57
None => return Ok(None),
···
84
86
Ok(Some(entry.insert(project)))
85
87
}
86
88
87
-
pub fn delete_engine_for_path(&mut self, path: &Path) {
89
+
pub fn delete_engine_for_path(&mut self, path: &Utf8Path) {
88
90
if let Some(path) = find_gleam_project_parent(&self.io, path) {
89
91
_ = self.engines.remove(&path);
90
92
}
···
96
98
///
97
99
/// The file must be in either the `src` or `test` directory if it is not a
98
100
/// `.gleam` file.
99
-
fn find_gleam_project_parent<IO>(io: &IO, path: &Path) -> Option<PathBuf>
101
+
fn find_gleam_project_parent<IO>(io: &IO, path: &Utf8Path) -> Option<Utf8PathBuf>
100
102
where
101
103
IO: FileSystemReader,
102
104
{
···
135
137
#[test]
136
138
fn root() {
137
139
let io = InMemoryFileSystem::new();
138
-
assert_eq!(find_gleam_project_parent(&io, Path::new("/")), None);
140
+
assert_eq!(find_gleam_project_parent(&io, Utf8Path::new("/")), None);
139
141
}
140
142
141
143
#[test]
142
144
fn outside_a_project() {
143
145
let io = InMemoryFileSystem::new();
144
146
assert_eq!(
145
-
find_gleam_project_parent(&io, Path::new("/app/src/one.gleam")),
147
+
find_gleam_project_parent(&io, Utf8Path::new("/app/src/one.gleam")),
146
148
None
147
149
);
148
150
}
···
150
152
#[test]
151
153
fn gleam_toml_itself() {
152
154
let io = InMemoryFileSystem::new();
153
-
io.write(Path::new("/app/gleam.toml"), "").unwrap();
155
+
io.write(Utf8Path::new("/app/gleam.toml"), "").unwrap();
154
156
assert_eq!(
155
-
find_gleam_project_parent(&io, Path::new("/app/gleam.toml")),
156
-
Some(PathBuf::from("/app"))
157
+
find_gleam_project_parent(&io, Utf8Path::new("/app/gleam.toml")),
158
+
Some(Utf8PathBuf::from("/app"))
157
159
);
158
160
}
159
161
160
162
#[test]
161
163
fn test_module() {
162
164
let io = InMemoryFileSystem::new();
163
-
io.write(Path::new("/app/gleam.toml"), "").unwrap();
165
+
io.write(Utf8Path::new("/app/gleam.toml"), "").unwrap();
164
166
assert_eq!(
165
-
find_gleam_project_parent(&io, Path::new("/app/test/one/two/three.gleam")),
166
-
Some(PathBuf::from("/app"))
167
+
find_gleam_project_parent(&io, Utf8Path::new("/app/test/one/two/three.gleam")),
168
+
Some(Utf8PathBuf::from("/app"))
167
169
);
168
170
}
169
171
170
172
#[test]
171
173
fn src_module() {
172
174
let io = InMemoryFileSystem::new();
173
-
io.write(Path::new("/app/gleam.toml"), "").unwrap();
175
+
io.write(Utf8Path::new("/app/gleam.toml"), "").unwrap();
174
176
assert_eq!(
175
-
find_gleam_project_parent(&io, Path::new("/app/src/one/two/three.gleam")),
176
-
Some(PathBuf::from("/app"))
177
+
find_gleam_project_parent(&io, Utf8Path::new("/app/src/one/two/three.gleam")),
178
+
Some(Utf8PathBuf::from("/app"))
177
179
);
178
180
}
179
181
···
181
183
#[test]
182
184
fn module_in_project_but_not_src_or_test() {
183
185
let io = InMemoryFileSystem::new();
184
-
io.write(Path::new("/app/gleam.toml"), "").unwrap();
186
+
io.write(Utf8Path::new("/app/gleam.toml"), "").unwrap();
185
187
assert_eq!(
186
-
find_gleam_project_parent(&io, Path::new("/app/other/one/two/three.gleam")),
188
+
find_gleam_project_parent(&io, Utf8Path::new("/app/other/one/two/three.gleam")),
187
189
None,
188
190
);
189
191
}
···
191
193
#[test]
192
194
fn nested_projects() {
193
195
let io = InMemoryFileSystem::new();
194
-
io.write(Path::new("/app/gleam.toml"), "").unwrap();
195
-
io.write(Path::new("/app/examples/wibble/gleam.toml"), "")
196
+
io.write(Utf8Path::new("/app/gleam.toml"), "").unwrap();
197
+
io.write(Utf8Path::new("/app/examples/wibble/gleam.toml"), "")
196
198
.unwrap();
197
199
assert_eq!(
198
-
find_gleam_project_parent(&io, Path::new("/app/src/one.gleam")),
199
-
Some(PathBuf::from("/app"))
200
+
find_gleam_project_parent(&io, Utf8Path::new("/app/src/one.gleam")),
201
+
Some(Utf8PathBuf::from("/app"))
200
202
);
201
203
assert_eq!(
202
-
find_gleam_project_parent(&io, Path::new("/app/examples/wibble/src/one.gleam")),
203
-
Some(PathBuf::from("/app/examples/wibble"))
204
+
find_gleam_project_parent(&io, Utf8Path::new("/app/examples/wibble/src/one.gleam")),
205
+
Some(Utf8PathBuf::from("/app/examples/wibble"))
204
206
);
205
207
}
206
208
}
+11
-8
compiler-core/src/language_server/server.rs
+11
-8
compiler-core/src/language_server/server.rs
···
24
24
InitializeParams, PublishDiagnosticsParams,
25
25
};
26
26
use serde_json::Value as Json;
27
-
use std::{collections::HashMap, path::PathBuf};
27
+
use std::collections::HashMap;
28
+
29
+
use camino::Utf8PathBuf;
28
30
29
31
use super::progress::ConnectionProgressReporter;
30
32
···
185
187
self.publish_messages(feedback.messages);
186
188
}
187
189
188
-
fn publish_diagnostics(&self, diagnostics: HashMap<PathBuf, Vec<Diagnostic>>) {
190
+
fn publish_diagnostics(&self, diagnostics: HashMap<Utf8PathBuf, Vec<Diagnostic>>) {
189
191
for (path, diagnostics) in diagnostics {
190
192
let diagnostics = diagnostics
191
193
.into_iter()
···
277
279
278
280
fn respond_with_engine<T, Handler>(
279
281
&mut self,
280
-
path: PathBuf,
282
+
path: Utf8PathBuf,
281
283
handler: Handler,
282
284
) -> (Json, Feedback)
283
285
where
···
314
316
315
317
fn notified_with_engine(
316
318
&mut self,
317
-
path: PathBuf,
319
+
path: Utf8PathBuf,
318
320
handler: impl FnOnce(
319
321
&mut LanguageServerEngine<IO, ConnectionProgressReporter<'a>>,
320
322
) -> engine::Response<()>,
···
577
579
}
578
580
}
579
581
580
-
fn path_to_uri(path: PathBuf) -> Url {
582
+
fn path_to_uri(path: Utf8PathBuf) -> Url {
581
583
let mut file: String = "file://".into();
582
584
file.push_str(&path.as_os_str().to_string_lossy());
583
585
Url::parse(&file).expect("path_to_uri URL parse")
584
586
}
585
587
586
-
fn path(uri: &Url) -> PathBuf {
588
+
fn path(uri: &Url) -> Utf8PathBuf {
587
589
// The to_file_path method is available on these platforms
588
590
#[cfg(any(unix, windows, target_os = "redox", target_os = "wasi"))]
589
-
return uri.to_file_path().expect("URL file");
591
+
return Utf8PathBuf::from_path_buf(uri.to_file_path().expect("URL file"))
592
+
.expect("Non Utf8 Path");
590
593
591
594
#[cfg(not(any(unix, windows, target_os = "redox", target_os = "wasi")))]
592
-
return uri.path().into();
595
+
return Utf8PathBuf::from_path_buf(uri.path().into()).expect("Non Utf8 Path");
593
596
}
+1
-1
compiler-core/src/language_server/tests/completion.rs
+1
-1
compiler-core/src/language_server/tests/completion.rs
···
28
28
let response = engine.compile_please();
29
29
assert!(response.result.is_ok());
30
30
31
-
let path = PathBuf::from(if cfg!(target_family = "windows") {
31
+
let path = Utf8PathBuf::from(if cfg!(target_family = "windows") {
32
32
r#"\\?\C:\src\app.gleam"#
33
33
} else {
34
34
"/src/app.gleam"
+26
-25
compiler-core/src/language_server/tests/mod.rs
+26
-25
compiler-core/src/language_server/tests/mod.rs
···
3
3
4
4
use std::{
5
5
collections::HashMap,
6
-
path::{Path, PathBuf},
7
6
sync::{Arc, Mutex},
8
7
time::SystemTime,
9
8
};
10
9
11
10
use hexpm::version::Version;
11
+
12
+
use camino::{Utf8Path, Utf8PathBuf};
12
13
13
14
use crate::{
14
15
config::PackageConfig,
···
58
59
Arc::try_unwrap(self.actions).unwrap().into_inner().unwrap()
59
60
}
60
61
61
-
pub fn src_module(&self, name: &str, code: &str) -> PathBuf {
62
+
pub fn src_module(&self, name: &str, code: &str) -> Utf8PathBuf {
62
63
let src_dir = self.paths.src_directory();
63
64
let path = src_dir.join(name).with_extension("gleam");
64
65
self.module(&path, code);
65
66
path
66
67
}
67
68
68
-
pub fn test_module(&self, name: &str, code: &str) -> PathBuf {
69
+
pub fn test_module(&self, name: &str, code: &str) -> Utf8PathBuf {
69
70
let test_dir = self.paths.test_directory();
70
71
let path = test_dir.join(name).with_extension("gleam");
71
72
self.module(&path, code);
72
73
path
73
74
}
74
75
75
-
pub fn dep_module(&self, dep: &str, name: &str, code: &str) -> PathBuf {
76
+
pub fn dep_module(&self, dep: &str, name: &str, code: &str) -> Utf8PathBuf {
76
77
let dep_dir = self.paths.root().join(dep).join("src");
77
78
let path = dep_dir.join(name).with_extension("gleam");
78
79
self.module(&path, code);
79
80
path
80
81
}
81
82
82
-
fn module(&self, path: &Path, code: &str) {
83
+
fn module(&self, path: &Utf8Path, code: &str) {
83
84
self.io.write(path, code).unwrap();
84
85
self.io.set_modification_time(path, SystemTime::now());
85
86
}
···
90
91
}
91
92
92
93
impl FileSystemReader for LanguageServerTestIO {
93
-
fn gleam_source_files(&self, dir: &Path) -> Vec<PathBuf> {
94
+
fn gleam_source_files(&self, dir: &Utf8Path) -> Vec<Utf8PathBuf> {
94
95
self.io.gleam_source_files(dir)
95
96
}
96
97
97
-
fn gleam_cache_files(&self, dir: &Path) -> Vec<PathBuf> {
98
+
fn gleam_cache_files(&self, dir: &Utf8Path) -> Vec<Utf8PathBuf> {
98
99
self.io.gleam_cache_files(dir)
99
100
}
100
101
101
-
fn read_dir(&self, path: &Path) -> Result<ReadDir> {
102
+
fn read_dir(&self, path: &Utf8Path) -> Result<ReadDir> {
102
103
self.io.read_dir(path)
103
104
}
104
105
105
-
fn read(&self, path: &Path) -> Result<String> {
106
+
fn read(&self, path: &Utf8Path) -> Result<String> {
106
107
self.io.read(path)
107
108
}
108
109
109
-
fn read_bytes(&self, path: &Path) -> Result<Vec<u8>> {
110
+
fn read_bytes(&self, path: &Utf8Path) -> Result<Vec<u8>> {
110
111
self.io.read_bytes(path)
111
112
}
112
113
113
-
fn reader(&self, path: &Path) -> Result<WrappedReader> {
114
+
fn reader(&self, path: &Utf8Path) -> Result<WrappedReader> {
114
115
self.io.reader(path)
115
116
}
116
117
117
-
fn is_file(&self, path: &Path) -> bool {
118
+
fn is_file(&self, path: &Utf8Path) -> bool {
118
119
self.io.is_file(path)
119
120
}
120
121
121
-
fn is_directory(&self, path: &Path) -> bool {
122
+
fn is_directory(&self, path: &Utf8Path) -> bool {
122
123
self.io.is_directory(path)
123
124
}
124
125
125
-
fn modification_time(&self, path: &Path) -> Result<SystemTime> {
126
+
fn modification_time(&self, path: &Utf8Path) -> Result<SystemTime> {
126
127
self.io.modification_time(path)
127
128
}
128
129
129
-
fn canonicalise(&self, path: &Path) -> Result<PathBuf, crate::Error> {
130
+
fn canonicalise(&self, path: &Utf8Path) -> Result<Utf8PathBuf, crate::Error> {
130
131
self.io.canonicalise(path)
131
132
}
132
133
}
133
134
134
135
impl FileSystemWriter for LanguageServerTestIO {
135
-
fn mkdir(&self, path: &Path) -> Result<()> {
136
+
fn mkdir(&self, path: &Utf8Path) -> Result<()> {
136
137
self.io.mkdir(path)
137
138
}
138
139
139
-
fn delete(&self, path: &Path) -> Result<()> {
140
+
fn delete(&self, path: &Utf8Path) -> Result<()> {
140
141
self.io.delete(path)
141
142
}
142
143
143
-
fn copy(&self, from: &Path, to: &Path) -> Result<()> {
144
+
fn copy(&self, from: &Utf8Path, to: &Utf8Path) -> Result<()> {
144
145
self.io.copy(from, to)
145
146
}
146
147
147
-
fn copy_dir(&self, from: &Path, to: &Path) -> Result<()> {
148
+
fn copy_dir(&self, from: &Utf8Path, to: &Utf8Path) -> Result<()> {
148
149
self.io.copy_dir(from, to)
149
150
}
150
151
151
-
fn hardlink(&self, from: &Path, to: &Path) -> Result<()> {
152
+
fn hardlink(&self, from: &Utf8Path, to: &Utf8Path) -> Result<()> {
152
153
self.io.hardlink(from, to)
153
154
}
154
155
155
-
fn symlink_dir(&self, from: &Path, to: &Path) -> Result<()> {
156
+
fn symlink_dir(&self, from: &Utf8Path, to: &Utf8Path) -> Result<()> {
156
157
self.io.symlink_dir(from, to)
157
158
}
158
159
159
-
fn delete_file(&self, path: &Path) -> Result<()> {
160
+
fn delete_file(&self, path: &Utf8Path) -> Result<()> {
160
161
self.io.delete_file(path)
161
162
}
162
163
163
-
fn write(&self, path: &Path, content: &str) -> Result<(), crate::Error> {
164
+
fn write(&self, path: &Utf8Path, content: &str) -> Result<(), crate::Error> {
164
165
self.io.write(path, content)
165
166
}
166
167
167
-
fn write_bytes(&self, path: &Path, content: &[u8]) -> Result<(), crate::Error> {
168
+
fn write_bytes(&self, path: &Utf8Path, content: &[u8]) -> Result<(), crate::Error> {
168
169
self.io.write_bytes(path, content)
169
170
}
170
171
}
···
185
186
program: &str,
186
187
args: &[String],
187
188
env: &[(&str, String)],
188
-
cwd: Option<&Path>,
189
+
cwd: Option<&Utf8Path>,
189
190
stdio: crate::io::Stdio,
190
191
) -> Result<i32> {
191
192
panic!(
+3
-3
compiler-core/src/manifest.rs
+3
-3
compiler-core/src/manifest.rs
···
1
1
use std::collections::HashMap;
2
-
use std::path::PathBuf;
3
2
4
3
use crate::requirement::Requirement;
5
4
use crate::Result;
5
+
use camino::Utf8PathBuf;
6
6
use hexpm::version::Version;
7
7
use itertools::Itertools;
8
8
use smol_str::SmolStr;
···
92
92
}
93
93
ManifestPackageSource::Local { path } => {
94
94
buffer.push_str(r#", source = "local", path = ""#);
95
-
buffer.push_str(path.to_str().expect("local path non utf-8"));
95
+
buffer.push_str(path.as_str());
96
96
buffer.push('"');
97
97
}
98
98
};
···
309
309
#[serde(rename = "git")]
310
310
Git { repo: SmolStr, commit: SmolStr },
311
311
#[serde(rename = "local")]
312
-
Local { path: PathBuf }, // should be the canonical path
312
+
Local { path: Utf8PathBuf }, // should be the canonical path
313
313
}
314
314
315
315
fn ordered_map<S, K, V>(value: &HashMap<K, V>, serializer: S) -> Result<S::Ok, S::Error>
+3
-3
compiler-core/src/parse/tests.rs
+3
-3
compiler-core/src/parse/tests.rs
···
1
1
use crate::ast::SrcSpan;
2
2
use crate::parse::error::{LexicalError, LexicalErrorType, ParseError, ParseErrorType};
3
-
use std::path::PathBuf;
3
+
use camino::Utf8PathBuf;
4
4
5
5
use pretty_assertions::assert_eq;
6
6
···
33
33
let result = crate::parse::parse_module(src).expect_err("should not parse");
34
34
let error = crate::error::Error::Parse {
35
35
src: src.into(),
36
-
path: PathBuf::from("/src/parse/error.gleam"),
36
+
path: Utf8PathBuf::from("/src/parse/error.gleam"),
37
37
error: result,
38
38
};
39
39
error.pretty_string()
···
43
43
let result = crate::parse::parse_statement_sequence(src).expect_err("should not parse");
44
44
let error = crate::error::Error::Parse {
45
45
src: src.into(),
46
-
path: PathBuf::from("/src/parse/error.gleam"),
46
+
path: Utf8PathBuf::from("/src/parse/error.gleam"),
47
47
error: result,
48
48
};
49
49
error.pretty_string()
+35
-32
compiler-core/src/paths.rs
+35
-32
compiler-core/src/paths.rs
···
1
-
use std::path::{Path, PathBuf};
2
-
3
1
use crate::build::{Mode, Target};
2
+
3
+
use camino::{Utf8Path, Utf8PathBuf};
4
4
5
5
pub const ARTEFACT_DIRECTORY_NAME: &str = "_gleam_artefacts";
6
6
7
7
#[derive(Debug, Clone)]
8
8
pub struct ProjectPaths {
9
-
root: PathBuf,
9
+
root: Utf8PathBuf,
10
10
}
11
11
12
12
impl ProjectPaths {
13
-
pub fn new(root: PathBuf) -> Self {
13
+
pub fn new(root: Utf8PathBuf) -> Self {
14
14
Self { root }
15
15
}
16
16
···
21
21
"/"
22
22
};
23
23
24
-
Self::new(PathBuf::from(path))
24
+
Self::new(Utf8PathBuf::from(path))
25
25
}
26
26
27
-
pub fn root(&self) -> &Path {
27
+
pub fn root(&self) -> &Utf8Path {
28
28
&self.root
29
29
}
30
30
31
-
pub fn root_config(&self) -> PathBuf {
31
+
pub fn root_config(&self) -> Utf8PathBuf {
32
32
self.root.join("gleam.toml")
33
33
}
34
34
35
-
pub fn readme(&self) -> PathBuf {
35
+
pub fn readme(&self) -> Utf8PathBuf {
36
36
self.root.join("README.md")
37
37
}
38
38
39
-
pub fn manifest(&self) -> PathBuf {
39
+
pub fn manifest(&self) -> Utf8PathBuf {
40
40
self.root.join("manifest.toml")
41
41
}
42
42
43
-
pub fn src_directory(&self) -> PathBuf {
43
+
pub fn src_directory(&self) -> Utf8PathBuf {
44
44
self.root.join("src")
45
45
}
46
46
47
-
pub fn test_directory(&self) -> PathBuf {
47
+
pub fn test_directory(&self) -> Utf8PathBuf {
48
48
self.root.join("test")
49
49
}
50
50
51
-
pub fn build_directory(&self) -> PathBuf {
51
+
pub fn build_directory(&self) -> Utf8PathBuf {
52
52
self.root.join("build")
53
53
}
54
54
55
-
pub fn build_packages_directory(&self) -> PathBuf {
55
+
pub fn build_packages_directory(&self) -> Utf8PathBuf {
56
56
self.build_directory().join("packages")
57
57
}
58
58
59
-
pub fn build_packages_toml(&self) -> PathBuf {
59
+
pub fn build_packages_toml(&self) -> Utf8PathBuf {
60
60
self.build_packages_directory().join("packages.toml")
61
61
}
62
62
63
-
pub fn build_packages_package(&self, package_name: &str) -> PathBuf {
63
+
pub fn build_packages_package(&self, package_name: &str) -> Utf8PathBuf {
64
64
self.build_packages_directory().join(package_name)
65
65
}
66
66
67
67
// build_deps_package_config
68
-
pub fn build_packages_package_config(&self, package_name: &str) -> PathBuf {
68
+
pub fn build_packages_package_config(&self, package_name: &str) -> Utf8PathBuf {
69
69
self.build_packages_package(package_name).join("gleam.toml")
70
70
}
71
71
72
-
pub fn build_export_hex_tarball(&self, package_name: &str, version: &str) -> PathBuf {
72
+
pub fn build_export_hex_tarball(&self, package_name: &str, version: &str) -> Utf8PathBuf {
73
73
self.build_directory()
74
74
.join(format!("{package_name}-{version}.tar"))
75
75
}
76
76
77
-
pub fn build_directory_for_mode(&self, mode: Mode) -> PathBuf {
77
+
pub fn build_directory_for_mode(&self, mode: Mode) -> Utf8PathBuf {
78
78
self.build_directory().join(mode.to_string())
79
79
}
80
80
81
-
pub fn erlang_shipment_directory(&self) -> PathBuf {
81
+
pub fn erlang_shipment_directory(&self) -> Utf8PathBuf {
82
82
self.build_directory().join("erlang-shipment")
83
83
}
84
84
85
-
pub fn build_documentation_directory(&self, package: &str) -> PathBuf {
85
+
pub fn build_documentation_directory(&self, package: &str) -> Utf8PathBuf {
86
86
self.build_directory_for_mode(Mode::Dev)
87
87
.join("docs")
88
88
.join(package)
89
89
}
90
90
91
-
pub fn build_directory_for_target(&self, mode: Mode, target: Target) -> PathBuf {
91
+
pub fn build_directory_for_target(&self, mode: Mode, target: Target) -> Utf8PathBuf {
92
92
self.build_directory_for_mode(mode).join(target.to_string())
93
93
}
94
94
···
97
97
mode: Mode,
98
98
target: Target,
99
99
package: &str,
100
-
) -> PathBuf {
100
+
) -> Utf8PathBuf {
101
101
self.build_directory_for_target(mode, target).join(package)
102
102
}
103
103
104
-
pub fn build_packages_ebins_glob(&self, mode: Mode, target: Target) -> PathBuf {
104
+
pub fn build_packages_ebins_glob(&self, mode: Mode, target: Target) -> Utf8PathBuf {
105
105
self.build_directory_for_package(mode, target, "*")
106
106
.join("ebin")
107
107
}
···
109
109
/// A path to a special file that contains the version of gleam that last built
110
110
/// the artifacts. If this file does not match the current version of gleam we
111
111
/// will rebuild from scratch
112
-
pub fn build_gleam_version(&self, mode: Mode, target: Target) -> PathBuf {
112
+
pub fn build_gleam_version(&self, mode: Mode, target: Target) -> Utf8PathBuf {
113
113
self.build_directory_for_target(mode, target)
114
114
.join("gleam_version")
115
115
}
116
116
}
117
117
118
-
pub fn global_package_cache_package_tarball(package_name: &str, version: &str) -> PathBuf {
118
+
pub fn global_package_cache_package_tarball(package_name: &str, version: &str) -> Utf8PathBuf {
119
119
global_packages_cache().join(format!("{package_name}-{version}.tar"))
120
120
}
121
121
122
-
fn global_packages_cache() -> PathBuf {
122
+
fn global_packages_cache() -> Utf8PathBuf {
123
123
default_global_gleam_cache()
124
124
.join("hex")
125
125
.join("hexpm")
126
126
.join("packages")
127
127
}
128
128
129
-
pub fn default_global_gleam_cache() -> PathBuf {
130
-
dirs_next::cache_dir()
131
-
.expect("Failed to determine user cache directory")
132
-
.join("gleam")
129
+
pub fn default_global_gleam_cache() -> Utf8PathBuf {
130
+
Utf8PathBuf::from_path_buf(
131
+
dirs_next::cache_dir()
132
+
.expect("Failed to determine user cache directory")
133
+
.join("gleam"),
134
+
)
135
+
.expect("Non Utf8 Path")
133
136
}
134
137
135
-
pub fn unnest(within: &Path) -> PathBuf {
136
-
let mut path = PathBuf::new();
138
+
pub fn unnest(within: &Utf8Path) -> Utf8PathBuf {
139
+
let mut path = Utf8PathBuf::new();
137
140
for _ in within {
138
141
path = path.join("..")
139
142
}
+3
-3
compiler-core/src/requirement.rs
+3
-3
compiler-core/src/requirement.rs
···
1
1
use std::fmt;
2
-
use std::path::PathBuf;
3
2
use std::str::FromStr;
4
3
5
4
use crate::error::Result;
5
+
use camino::Utf8PathBuf;
6
6
use hexpm::version::Range;
7
7
use serde::de::{self, Deserializer, MapAccess, Visitor};
8
8
use serde::ser::{Serialize, SerializeMap, Serializer};
···
13
13
#[serde(untagged, remote = "Self")]
14
14
pub enum Requirement {
15
15
Hex { version: Range },
16
-
Path { path: PathBuf },
16
+
Path { path: Utf8PathBuf },
17
17
Git { git: SmolStr },
18
18
}
19
19
···
37
37
Requirement::Hex { version: range } => {
38
38
format!(r#"{{ version = "{}" }}"#, range)
39
39
}
40
-
Requirement::Path { path } => format!(r#"{{ path = "{}" }}"#, path.display()),
40
+
Requirement::Path { path } => format!(r#"{{ path = "{}" }}"#, path),
41
41
Requirement::Git { git: url } => format!(r#"{{ git = "{}" }}"#, url),
42
42
}
43
43
}
+3
-2
compiler-core/src/type_/error.rs
+3
-2
compiler-core/src/type_/error.rs
···
3
3
type_::Type,
4
4
};
5
5
6
-
use std::{path::PathBuf, sync::Arc};
6
+
use camino::Utf8PathBuf;
7
+
use std::sync::Arc;
7
8
8
9
#[cfg(test)]
9
10
use pretty_assertions::assert_eq;
···
410
411
}
411
412
412
413
impl Warning {
413
-
pub fn into_warning(self, path: PathBuf, src: SmolStr) -> crate::Warning {
414
+
pub fn into_warning(self, path: Utf8PathBuf, src: SmolStr) -> crate::Warning {
414
415
crate::Warning::Type {
415
416
path,
416
417
src,
+8
-6
compiler-core/src/type_/tests.rs
+8
-6
compiler-core/src/type_/tests.rs
···
9
9
};
10
10
use itertools::Itertools;
11
11
use smol_str::SmolStr;
12
-
use std::{path::PathBuf, sync::Arc};
12
+
use std::sync::Arc;
13
13
use vec1::Vec1;
14
+
15
+
use camino::Utf8PathBuf;
14
16
15
17
mod assert;
16
18
mod assignments;
···
80
82
.expect_err("should infer an error");
81
83
let error = $crate::error::Error::Type {
82
84
src: $src.into(),
83
-
path: std::path::PathBuf::from("/src/one/two.gleam"),
85
+
path: camino::Utf8PathBuf::from("/src/one/two.gleam"),
84
86
error,
85
87
};
86
88
let output = error.pretty_string();
···
130
132
let warnings = get_warnings(src, deps);
131
133
let mut nocolor = termcolor::Buffer::no_color();
132
134
for warning in warnings {
133
-
let path = std::path::PathBuf::from("/src/warning/wrn.gleam");
135
+
let path = Utf8PathBuf::from("/src/warning/wrn.gleam");
134
136
let warning = warning.into_warning(path, src.into());
135
137
warning.pretty(&mut nocolor);
136
138
}
···
245
247
let ids = UniqueIdGenerator::new();
246
248
let mut modules = im::HashMap::new();
247
249
let warnings = TypeWarningEmitter::new(
248
-
PathBuf::new(),
250
+
Utf8PathBuf::new(),
249
251
"".into(),
250
252
WarningEmitter::new(
251
253
warnings.unwrap_or_else(|| Arc::new(VectorWarningEmitterIO::default())),
···
292
294
let error = compile_module(src, None, deps).expect_err("should infer an error");
293
295
let error = Error::Type {
294
296
src: src.into(),
295
-
path: PathBuf::from("/src/one/two.gleam"),
297
+
path: Utf8PathBuf::from("/src/one/two.gleam"),
296
298
error,
297
299
};
298
300
error.pretty_string()
···
302
304
let error = crate::parse::parse_module(src).expect_err("should trigger an error when parsing");
303
305
let error = Error::Parse {
304
306
src: src.into(),
305
-
path: PathBuf::from("/src/one/two.gleam"),
307
+
path: Utf8PathBuf::from("/src/one/two.gleam"),
306
308
error,
307
309
};
308
310
error.pretty_string()
+9
-8
compiler-core/src/warning.rs
+9
-8
compiler-core/src/warning.rs
···
4
4
diagnostic::{self, Diagnostic, Location},
5
5
type_,
6
6
};
7
+
use camino::Utf8PathBuf;
7
8
use debug_ignore::DebugIgnore;
8
9
use smol_str::SmolStr;
10
+
use std::sync::atomic::AtomicUsize;
9
11
use std::{
10
12
io::Write,
11
13
sync::{atomic::Ordering, Arc},
12
14
};
13
-
use std::{path::PathBuf, sync::atomic::AtomicUsize};
14
15
use termcolor::Buffer;
15
16
16
17
pub trait WarningEmitterIO {
···
91
92
92
93
#[derive(Debug, Clone)]
93
94
pub struct TypeWarningEmitter {
94
-
module_path: PathBuf,
95
+
module_path: Utf8PathBuf,
95
96
module_src: SmolStr,
96
97
emitter: WarningEmitter,
97
98
}
98
99
99
100
impl TypeWarningEmitter {
100
-
pub fn new(module_path: PathBuf, module_src: SmolStr, emitter: WarningEmitter) -> Self {
101
+
pub fn new(module_path: Utf8PathBuf, module_src: SmolStr, emitter: WarningEmitter) -> Self {
101
102
Self {
102
103
module_path,
103
104
module_src,
···
107
108
108
109
pub fn null() -> Self {
109
110
Self {
110
-
module_path: PathBuf::new(),
111
+
module_path: Utf8PathBuf::new(),
111
112
module_src: SmolStr::new(""),
112
113
emitter: WarningEmitter::new(Arc::new(NullWarningEmitterIO)),
113
114
}
···
125
126
#[derive(Debug, Clone, Eq, PartialEq)]
126
127
pub enum Warning {
127
128
Type {
128
-
path: PathBuf,
129
+
path: Utf8PathBuf,
129
130
src: SmolStr,
130
131
warning: crate::type_::Warning,
131
132
},
132
133
Parse {
133
-
path: PathBuf,
134
+
path: Utf8PathBuf,
134
135
src: SmolStr,
135
136
warning: crate::parse::Warning,
136
137
},
137
138
InvalidSource {
138
-
path: PathBuf,
139
+
path: Utf8PathBuf,
139
140
},
140
141
}
141
142
···
241
242
location: None,
242
243
hint: Some(format!(
243
244
"Rename `{}` to be valid, or remove this file from the project source.",
244
-
path.to_string_lossy()
245
+
path
245
246
)),
246
247
},
247
248
Self::Type { path, warning, src } => match warning {
+1
compiler-wasm/Cargo.toml
+1
compiler-wasm/Cargo.toml
+6
-5
compiler-wasm/src/lib.rs
+6
-5
compiler-wasm/src/lib.rs
···
1
-
use std::{collections::HashMap, ffi::OsStr, path::Path, sync::Arc};
1
+
use camino::Utf8Path;
2
+
use std::{collections::HashMap, sync::Arc};
2
3
3
4
use gleam_core::{
4
5
build::{Built, Codegen, Mode, Options, ProjectCompiler, Target},
···
60
61
Ok(gather_compiled_files(&paths, &wfs, options.target).unwrap())
61
62
}
62
63
63
-
fn write_source_file<P: AsRef<Path>>(source: &str, path: P, wfs: &mut WasmFileSystem) {
64
+
fn write_source_file<P: AsRef<Utf8Path>>(source: &str, path: P, wfs: &mut WasmFileSystem) {
64
65
wfs.write(path.as_ref(), source)
65
66
.expect("should always succeed with the virtual file system");
66
67
}
···
127
128
let mut files: HashMap<String, String> = HashMap::new();
128
129
129
130
let extension_to_search_for = match target {
130
-
Target::Erlang => OsStr::new("erl"),
131
-
Target::JavaScript => OsStr::new("mjs"),
131
+
Target::Erlang => "erl",
132
+
Target::JavaScript => "mjs",
132
133
};
133
134
134
135
wfs.read_dir(&paths.build_directory())
···
139
140
.for_each(|dir_entry| {
140
141
let path = dir_entry.as_path();
141
142
let contents: String = wfs.read(path).expect("iterated dir entries should exist");
142
-
let path = path.to_str().unwrap().replace('\\', "/");
143
+
let path = path.as_str().replace('\\', "/");
143
144
144
145
files.insert(path, contents);
145
146
});
+21
-21
compiler-wasm/src/wasm_filesystem.rs
+21
-21
compiler-wasm/src/wasm_filesystem.rs
···
1
+
use camino::{Utf8Path, Utf8PathBuf};
1
2
use gleam_core::{
2
3
io::{
3
4
memory::InMemoryFileSystem, CommandExecutor, FileSystemReader, FileSystemWriter, ReadDir,
···
5
6
},
6
7
Error, Result,
7
8
};
8
-
use std::path::{Path, PathBuf};
9
9
10
10
#[derive(Clone, Debug)]
11
11
pub struct WasmFileSystem {
···
26
26
_program: &str,
27
27
_args: &[String],
28
28
_env: &[(&str, String)],
29
-
_cwd: Option<&Path>,
29
+
_cwd: Option<&Utf8Path>,
30
30
_stdio: Stdio,
31
31
) -> Result<i32, Error> {
32
32
Ok(0) // Always succeed.
···
34
34
}
35
35
36
36
impl FileSystemWriter for WasmFileSystem {
37
-
fn delete(&self, path: &Path) -> Result<(), Error> {
37
+
fn delete(&self, path: &Utf8Path) -> Result<(), Error> {
38
38
tracing::trace!("delete {:?}", path);
39
39
self.imfs.delete(path)
40
40
}
41
41
42
-
fn copy(&self, _from: &Path, _to: &Path) -> Result<(), Error> {
42
+
fn copy(&self, _from: &Utf8Path, _to: &Utf8Path) -> Result<(), Error> {
43
43
Ok(())
44
44
}
45
-
fn copy_dir(&self, _: &Path, _: &Path) -> Result<(), Error> {
45
+
fn copy_dir(&self, _: &Utf8Path, _: &Utf8Path) -> Result<(), Error> {
46
46
Ok(())
47
47
}
48
48
49
-
fn mkdir(&self, _: &Path) -> Result<(), Error> {
49
+
fn mkdir(&self, _: &Utf8Path) -> Result<(), Error> {
50
50
Ok(())
51
51
}
52
52
53
-
fn hardlink(&self, _: &Path, _: &Path) -> Result<(), Error> {
53
+
fn hardlink(&self, _: &Utf8Path, _: &Utf8Path) -> Result<(), Error> {
54
54
Ok(())
55
55
}
56
56
57
-
fn symlink_dir(&self, _: &Path, _: &Path) -> Result<(), Error> {
57
+
fn symlink_dir(&self, _: &Utf8Path, _: &Utf8Path) -> Result<(), Error> {
58
58
Ok(())
59
59
}
60
60
61
-
fn delete_file(&self, path: &Path) -> Result<(), Error> {
61
+
fn delete_file(&self, path: &Utf8Path) -> Result<(), Error> {
62
62
tracing::trace!("delete file {:?}", path);
63
63
self.imfs.delete_file(path)
64
64
}
65
65
66
-
fn write(&self, path: &Path, content: &str) -> Result<(), Error> {
66
+
fn write(&self, path: &Utf8Path, content: &str) -> Result<(), Error> {
67
67
tracing::trace!("write {:?}", path);
68
68
self.imfs.write(path, content)
69
69
}
70
70
71
-
fn write_bytes(&self, path: &Path, content: &[u8]) -> Result<(), Error> {
71
+
fn write_bytes(&self, path: &Utf8Path, content: &[u8]) -> Result<(), Error> {
72
72
tracing::trace!("write_bytes {:?}", path);
73
73
self.imfs.write_bytes(path, content)
74
74
}
75
75
}
76
76
77
77
impl FileSystemReader for WasmFileSystem {
78
-
fn gleam_source_files(&self, dir: &Path) -> Vec<PathBuf> {
78
+
fn gleam_source_files(&self, dir: &Utf8Path) -> Vec<Utf8PathBuf> {
79
79
tracing::trace!("gleam_source_files {:?}", dir);
80
80
self.imfs.gleam_source_files(dir)
81
81
}
82
82
83
-
fn gleam_cache_files(&self, dir: &Path) -> Vec<PathBuf> {
83
+
fn gleam_cache_files(&self, dir: &Utf8Path) -> Vec<Utf8PathBuf> {
84
84
tracing::trace!("gleam_metadata_files {:?}", dir);
85
85
self.imfs.gleam_cache_files(dir)
86
86
}
87
87
88
-
fn read(&self, path: &Path) -> Result<String, Error> {
88
+
fn read(&self, path: &Utf8Path) -> Result<String, Error> {
89
89
tracing::trace!("read {:?}", path);
90
90
self.imfs.read(path)
91
91
}
92
92
93
-
fn is_file(&self, path: &Path) -> bool {
93
+
fn is_file(&self, path: &Utf8Path) -> bool {
94
94
tracing::info!("is_file {:?}", path);
95
95
self.imfs.is_file(path)
96
96
}
97
97
98
-
fn is_directory(&self, path: &Path) -> bool {
98
+
fn is_directory(&self, path: &Utf8Path) -> bool {
99
99
tracing::trace!("is_directory {:?}", path);
100
100
false
101
101
}
102
102
103
-
fn reader(&self, path: &Path) -> Result<WrappedReader, Error> {
103
+
fn reader(&self, path: &Utf8Path) -> Result<WrappedReader, Error> {
104
104
tracing::trace!("reader {:?}", path);
105
105
self.imfs.reader(path)
106
106
}
107
107
108
-
fn read_dir(&self, path: &Path) -> Result<ReadDir> {
108
+
fn read_dir(&self, path: &Utf8Path) -> Result<ReadDir> {
109
109
tracing::trace!("read_dir {:?}", path);
110
110
self.imfs.read_dir(path)
111
111
}
112
112
113
-
fn modification_time(&self, path: &Path) -> Result<std::time::SystemTime, Error> {
113
+
fn modification_time(&self, path: &Utf8Path) -> Result<std::time::SystemTime, Error> {
114
114
self.imfs.modification_time(path)
115
115
}
116
116
117
-
fn read_bytes(&self, path: &Path) -> Result<Vec<u8>, Error> {
117
+
fn read_bytes(&self, path: &Utf8Path) -> Result<Vec<u8>, Error> {
118
118
self.imfs.read_bytes(path)
119
119
}
120
120
121
-
fn canonicalise(&self, path: &Path) -> Result<PathBuf, Error> {
121
+
fn canonicalise(&self, path: &Utf8Path) -> Result<Utf8PathBuf, Error> {
122
122
self.imfs.canonicalise(path)
123
123
}
124
124
}
+1
test-package-compiler/Cargo.toml
+1
test-package-compiler/Cargo.toml
+13
-16
test-package-compiler/src/lib.rs
+13
-16
test-package-compiler/src/lib.rs
···
13
13
};
14
14
use itertools::Itertools;
15
15
use regex::Regex;
16
-
use std::{
17
-
collections::HashMap,
18
-
ffi::OsStr,
19
-
fmt::Write,
20
-
path::{Path, PathBuf},
21
-
sync::Arc,
22
-
};
16
+
use std::{collections::HashMap, fmt::Write, sync::Arc};
17
+
18
+
use camino::{Utf8Path, Utf8PathBuf};
23
19
24
20
pub fn prepare(path: &str) -> String {
25
-
let root = PathBuf::from(path).canonicalize().unwrap();
21
+
let root = Utf8PathBuf::from(path).canonicalize_utf8().unwrap();
26
22
27
23
let toml = std::fs::read_to_string(root.join("gleam.toml")).unwrap();
28
24
let config: PackageConfig = toml::from_str(&toml).unwrap();
···
44
40
let warning_emitter = WarningEmitter::new(Arc::new(warnings.clone()));
45
41
let filesystem = to_in_memory_filesystem(&root);
46
42
let initial_files = filesystem.paths();
47
-
let root = PathBuf::from("");
48
-
let out = PathBuf::from("/out/lib/the_package");
49
-
let lib = PathBuf::from("/out/lib");
43
+
let root = Utf8PathBuf::from("");
44
+
let out = Utf8PathBuf::from("/out/lib/the_package");
45
+
let lib = Utf8PathBuf::from("/out/lib");
50
46
let mut compiler = gleam_core::build::PackageCompiler::new(
51
47
&config,
52
48
Mode::Dev,
···
94
90
95
91
#[derive(Debug)]
96
92
pub struct TestCompileOutput {
97
-
files: HashMap<PathBuf, Content>,
93
+
files: HashMap<Utf8PathBuf, Content>,
98
94
warnings: Vec<gleam_core::Warning>,
99
95
}
100
96
···
103
99
let mut buffer = String::new();
104
100
for (path, content) in self.files.iter().sorted_by(|a, b| a.0.cmp(b.0)) {
105
101
buffer.push_str("//// ");
106
-
buffer.push_str(&path.to_str().unwrap().replace('\\', "/"));
102
+
buffer.push_str(&path.as_str().replace('\\', "/"));
107
103
buffer.push('\n');
108
104
109
-
let extension = path.extension().and_then(OsStr::to_str);
105
+
let extension = path.extension();
110
106
match content {
111
107
_ if extension == Some("cache") => buffer.push_str("<.cache binary>"),
112
108
···
132
128
}
133
129
}
134
130
135
-
fn to_in_memory_filesystem(path: &Path) -> InMemoryFileSystem {
131
+
fn to_in_memory_filesystem(path: &Utf8Path) -> InMemoryFileSystem {
136
132
let fs = InMemoryFileSystem::new();
137
133
138
134
let files = walkdir::WalkDir::new(path)
···
145
141
for fullpath in files {
146
142
let content = std::fs::read(&fullpath).unwrap();
147
143
let path = fullpath.strip_prefix(path).unwrap();
148
-
fs.write_bytes(path, &content).unwrap();
144
+
fs.write_bytes(Utf8Path::from_path(path).unwrap(), &content)
145
+
.unwrap();
149
146
}
150
147
151
148
fs