use UTF8 paths from camino (#2277)

Co-authored-by: BENJAMIN PEINHARDT <ben.peinhardt@siteimpact.com>
This commit is contained in:
Benjamin Peinhardt 2023-07-29 10:44:32 -05:00 committed by GitHub
parent 023205660a
commit 228048ecc8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
67 changed files with 870 additions and 805 deletions

13
Cargo.lock generated
View file

@ -210,6 +210,15 @@ version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
[[package]]
name = "camino"
version = "1.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c"
dependencies = [
"serde",
]
[[package]]
name = "capnp"
version = "0.14.11"
@ -708,6 +717,7 @@ dependencies = [
"atty",
"base16",
"bytes",
"camino",
"clap",
"ctrlc",
"debug-ignore",
@ -757,6 +767,7 @@ dependencies = [
"base16",
"bincode",
"bytes",
"camino",
"capnp",
"capnpc",
"codespan-reporting",
@ -801,6 +812,7 @@ dependencies = [
name = "gleam-wasm"
version = "0.30.4"
dependencies = [
"camino",
"console_error_panic_hook",
"gleam-core",
"hexpm",
@ -2107,6 +2119,7 @@ dependencies = [
name = "test-package-compiler"
version = "0.30.4"
dependencies = [
"camino",
"gleam-core",
"im",
"insta",

View file

@ -82,6 +82,7 @@ smol_str = "0.1"
same-file = "1.0.6"
# Open generated docs in browser
opener = "0.6"
camino = { version = "1.1.6", features = ["serde1"] }
[dev-dependencies]
# Test assertion errors with diffs

4
compiler-cli/clippy.toml Normal file
View file

@ -0,0 +1,4 @@
disallowed-methods = [
{ path = "std::path::Path::new", reason = "Manually constructed paths should use camino::Utf8Path" },
{ path = "std::path::PathBuf::new", reason = "Manually constructed pathbufs should use camino::Utf8Path" },
]

View file

@ -1,4 +1,4 @@
use std::path::{Path, PathBuf};
use camino::{Utf8Path, Utf8PathBuf};
use gleam_core::{
error::{FileIoAction, FileKind},
@ -25,7 +25,7 @@ pub fn command(packages: Vec<String>, dev: bool) -> Result<()> {
.map_err(|e| Error::FileIo {
kind: FileKind::File,
action: FileIoAction::Parse,
path: PathBuf::from("gleam.toml"),
path: Utf8PathBuf::from("gleam.toml"),
err: Some(e.to_string()),
})?;
@ -56,7 +56,7 @@ pub fn command(packages: Vec<String>, dev: bool) -> Result<()> {
}
// Write the updated config
fs::write(Path::new("gleam.toml"), &toml.to_string())?;
fs::write(Utf8Path::new("gleam.toml"), &toml.to_string())?;
Ok(())
}

View file

@ -11,7 +11,7 @@ use crate::{
build_lock::BuildLock,
cli,
dependencies::UseManifest,
fs::{self, ConsoleWarningEmitter},
fs::{self, get_current_directory, ConsoleWarningEmitter},
};
pub fn download_dependencies() -> Result<Manifest> {
@ -31,7 +31,7 @@ pub fn main(options: Options, manifest: Manifest) -> Result<Built> {
options.mode,
options.target.unwrap_or(root_config.target),
)?;
let current_dir = std::env::current_dir().expect("Failed to get current directory");
let current_dir = get_current_directory().expect("Failed to get current directory");
tracing::info!("Compiling packages");
let compiled = {

View file

@ -1,14 +1,14 @@
use camino::Utf8PathBuf;
use gleam_core::{
build::{Mode, Target, Telemetry},
paths::ProjectPaths,
Result,
};
use std::path::PathBuf;
use strum::IntoEnumIterator;
#[derive(Debug)]
pub(crate) struct BuildLock {
directory: PathBuf,
directory: Utf8PathBuf,
}
impl BuildLock {
@ -36,7 +36,7 @@ impl BuildLock {
crate::fs::mkdir(&self.directory).expect("Could not create lock directory");
let lock_path = self.directory.join("gleam.lock");
let mut file = fslock::LockFile::open(&lock_path).expect("LockFile creation");
let mut file = fslock::LockFile::open(lock_path.as_str()).expect("LockFile creation");
if !file.try_lock_with_pid().expect("Trying build locking") {
telemetry.waiting_for_build_directory_lock();

View file

@ -3,6 +3,7 @@ use crate::{
fs::{self, ConsoleWarningEmitter, ProjectIO},
CompilePackage,
};
use camino::Utf8Path;
use gleam_core::{
build::{Mode, PackageCompiler, StaleTracker, Target, TargetCodegenConfiguration},
metadata,
@ -13,7 +14,7 @@ use gleam_core::{
Result,
};
use smol_str::SmolStr;
use std::{path::Path, sync::Arc};
use std::sync::Arc;
pub fn command(options: CompilePackage) -> Result<()> {
let ids = UniqueIdGenerator::new();
@ -56,7 +57,7 @@ pub fn command(options: CompilePackage) -> Result<()> {
fn load_libraries(
ids: &UniqueIdGenerator,
lib: &Path,
lib: &Utf8Path,
) -> Result<im::HashMap<SmolStr, ModuleInterface>> {
tracing::info!("Reading precompiled module metadata files");
let mut manifests = im::HashMap::new();
@ -71,5 +72,6 @@ fn load_libraries(
let _ = manifests.insert(module.name.clone(), module);
}
}
Ok(manifests)
}

View file

@ -1,4 +1,4 @@
use std::path::PathBuf;
use camino::Utf8PathBuf;
use gleam_core::{
config::PackageConfig,
@ -7,8 +7,10 @@ use gleam_core::{
paths::ProjectPaths,
};
use crate::fs::get_current_directory;
pub fn root_config() -> Result<PackageConfig, Error> {
let current_dir = std::env::current_dir().expect("Could not get current directory");
let current_dir = get_current_directory().expect("Failed to get current directory");
let paths = ProjectPaths::new(current_dir);
read(paths.root_config())
}
@ -53,7 +55,7 @@ pub fn find_package_config_for_module(
}
}
pub fn read(config_path: PathBuf) -> Result<PackageConfig, Error> {
pub fn read(config_path: Utf8PathBuf) -> Result<PackageConfig, Error> {
let toml = crate::fs::read(&config_path)?;
let config: PackageConfig = toml::from_str(&toml).map_err(|e| Error::FileIo {
action: FileIoAction::Parse,

View file

@ -1,9 +1,9 @@
use std::{
collections::{HashMap, HashSet},
path::{Path, PathBuf},
time::Instant,
};
use camino::{Utf8Path, Utf8PathBuf};
use flate2::read::GzDecoder;
use futures::future;
use gleam_core::{
@ -522,7 +522,7 @@ struct ProvidedPackage {
#[derive(Clone, Eq, Debug)]
enum ProvidedPackageSource {
Git { repo: SmolStr, commit: SmolStr },
Local { path: PathBuf },
Local { path: Utf8PathBuf },
}
impl ProvidedPackage {
@ -587,7 +587,7 @@ impl ProvidedPackageSource {
format!(r#"{{ repo: "{}", commit: "{}" }}"#, repo, commit)
}
Self::Local { path } => {
format!(r#"{{ path: "{}" }}"#, path.to_string_lossy())
format!(r#"{{ path: "{}" }}"#, path)
}
}
}
@ -689,8 +689,8 @@ fn resolve_versions<Telem: Telemetry>(
/// Provide a package from a local project
fn provide_local_package(
package_name: SmolStr,
package_path: &Path,
parent_path: &Path,
package_path: &Utf8Path,
parent_path: &Utf8Path,
project_paths: &ProjectPaths,
provided: &mut HashMap<SmolStr, ProvidedPackage>,
parents: &mut Vec<SmolStr>,
@ -730,7 +730,7 @@ fn provide_git_package(
/// Adds a gleam project located at a specific path to the list of "provided packages"
fn provide_package(
package_name: SmolStr,
package_path: PathBuf,
package_path: Utf8PathBuf,
package_source: ProvidedPackageSource,
project_paths: &ProjectPaths,
provided: &mut HashMap<SmolStr, ProvidedPackage>,
@ -820,8 +820,8 @@ fn provide_wrong_package() {
let project_paths = crate::project_paths_at_current_directory();
let result = provide_local_package(
"wrong_name".into(),
Path::new("./test/hello_world"),
Path::new("./"),
Utf8Path::new("./test/hello_world"),
Utf8Path::new("./"),
&project_paths,
&mut provided,
&mut vec!["root".into(), "subpackage".into()],
@ -844,8 +844,8 @@ fn provide_existing_package() {
let result = provide_local_package(
"hello_world".into(),
Path::new("./test/hello_world"),
Path::new("./"),
Utf8Path::new("./test/hello_world"),
Utf8Path::new("./"),
&project_paths,
&mut provided,
&mut vec!["root".into(), "subpackage".into()],
@ -857,8 +857,8 @@ fn provide_existing_package() {
let result = provide_local_package(
"hello_world".into(),
Path::new("./test/hello_world"),
Path::new("./"),
Utf8Path::new("./test/hello_world"),
Utf8Path::new("./"),
&project_paths,
&mut provided,
&mut vec!["root".into(), "subpackage".into()],
@ -875,8 +875,8 @@ fn provide_conflicting_package() {
let project_paths = crate::project_paths_at_current_directory();
let result = provide_local_package(
"hello_world".into(),
Path::new("./test/hello_world"),
Path::new("./"),
Utf8Path::new("./test/hello_world"),
Utf8Path::new("./"),
&project_paths,
&mut provided,
&mut vec!["root".into(), "subpackage".into()],
@ -888,9 +888,9 @@ fn provide_conflicting_package() {
let result = provide_package(
"hello_world".into(),
PathBuf::from("./test/other"),
Utf8PathBuf::from("./test/other"),
ProvidedPackageSource::Local {
path: Path::new("./test/other").to_path_buf(),
path: Utf8Path::new("./test/other").to_path_buf(),
},
&project_paths,
&mut provided,
@ -909,8 +909,8 @@ fn provided_is_absolute() {
let project_paths = crate::project_paths_at_current_directory();
let result = provide_local_package(
"hello_world".into(),
Path::new("./test/hello_world"),
Path::new("./"),
Utf8Path::new("./test/hello_world"),
Utf8Path::new("./"),
&project_paths,
&mut provided,
&mut vec!["root".into(), "subpackage".into()],
@ -933,8 +933,8 @@ fn provided_recursive() {
let project_paths = crate::project_paths_at_current_directory();
let result = provide_local_package(
"hello_world".into(),
Path::new("./test/hello_world"),
Path::new("./"),
Utf8Path::new("./test/hello_world"),
Utf8Path::new("./"),
&project_paths,
&mut provided,
&mut vec!["root".into(), "hello_world".into(), "subpackage".into()],
@ -1006,7 +1006,7 @@ impl TarUnpacker for Untar {
fn io_result_unpack(
&self,
path: &Path,
path: &Utf8Path,
mut archive: tar::Archive<GzDecoder<tar::Entry<'_, WrappedReader>>>,
) -> std::io::Result<()> {
archive.unpack(path)

View file

@ -1,6 +1,7 @@
use std::path::Path;
use std::time::Instant;
use camino::Utf8Path;
use crate::{cli, hex::ApiKeyCommand, http::HttpClient};
use gleam_core::{
build::{Codegen, Mode, Options, Package},
@ -84,7 +85,7 @@ pub fn build(options: BuildOptions) -> Result<()> {
println!(
"\nThe documentation for {package} has been rendered to \n{index_html}",
package = config.name,
index_html = index_html.to_string_lossy()
index_html = index_html
);
if options.open {
@ -99,7 +100,7 @@ pub fn build(options: BuildOptions) -> Result<()> {
///
/// For the docs this will generally be a browser (unless some other program is
/// configured as the default for `.html` files).
fn open_docs(path: &Path) -> Result<()> {
fn open_docs(path: &Utf8Path) -> Result<()> {
opener::open(path).map_err(|error| Error::FailedToOpenDocs {
path: path.to_path_buf(),
error: error.to_string(),

View file

@ -46,9 +46,9 @@ pub(crate) fn erlang_shipment() -> Result<()> {
continue;
}
let name = path.file_name().expect("Directory name").to_string_lossy();
let build = build.join(name.as_ref());
let out = out.join(name.as_ref());
let name = path.file_name().expect("Directory name");
let build = build.join(name);
let out = out.join(name);
crate::fs::mkdir(&out)?;
// Copy desired package subdirectories
@ -79,8 +79,8 @@ the entrypoint.sh script.
{entrypoint}
",
path = out.to_string_lossy(),
entrypoint = entrypoint.to_string_lossy(),
path = out,
entrypoint = entrypoint,
);
Ok(())
@ -97,7 +97,7 @@ pub fn hex_tarball() -> Result<()> {
"
Your hex tarball has been generated in {}.
",
&path.display()
&path
);
Ok(())
}

View file

@ -1,18 +1,19 @@
use camino::Utf8PathBuf;
use gleam_core::{
build::Target,
error::{FileIoAction, FileKind},
Error, Result,
};
use std::{path::PathBuf, str::FromStr};
use std::str::FromStr;
pub fn run(target: Option<Target>, files: Vec<String>) -> Result<()> {
let mut complete = true;
for file_path in files {
let path = PathBuf::from_str(&file_path).map_err(|e| Error::FileIo {
let path = Utf8PathBuf::from_str(&file_path).map_err(|e| Error::FileIo {
action: FileIoAction::Open,
kind: FileKind::File,
path: PathBuf::from(file_path),
path: Utf8PathBuf::from(file_path),
err: Some(e.to_string()),
})?;
@ -35,7 +36,7 @@ pub fn run(target: Option<Target>, files: Vec<String>) -> Result<()> {
Ok(())
}
fn fix_file(target: Option<Target>, path: PathBuf) -> Result<bool> {
fn fix_file(target: Option<Target>, path: Utf8PathBuf) -> Result<bool> {
let src = crate::fs::read(&path)?;
let (out, complete) = gleam_core::fix::parse_fix_and_format(target, &src.into(), &path)?;
crate::fs::write(&path, &out)?;

View file

@ -3,11 +3,9 @@ use gleam_core::{
io::Content,
io::OutputFile,
};
use std::{
io::Read,
path::{Path, PathBuf},
str::FromStr,
};
use std::{io::Read, str::FromStr};
use camino::{Utf8Path, Utf8PathBuf};
pub fn run(stdin: bool, check: bool, files: Vec<String>) -> Result<()> {
if stdin {
@ -20,7 +18,7 @@ pub fn run(stdin: bool, check: bool, files: Vec<String>) -> Result<()> {
fn process_stdin(check: bool) -> Result<()> {
let src = read_stdin()?.into();
let mut out = String::new();
gleam_core::format::pretty(&mut out, &src, Path::new("<stdin>"))?;
gleam_core::format::pretty(&mut out, &src, Utf8Path::new("<stdin>"))?;
if !check {
print!("{out}");
@ -30,8 +28,8 @@ fn process_stdin(check: bool) -> Result<()> {
if src != out {
return Err(Error::Format {
problem_files: vec![Unformatted {
source: PathBuf::from("<standard input>"),
destination: PathBuf::from("<standard output>"),
source: Utf8PathBuf::from("<standard input>"),
destination: Utf8PathBuf::from("<standard output>"),
input: src,
output: out,
}],
@ -73,10 +71,10 @@ pub fn unformatted_files(files: Vec<String>) -> Result<Vec<Unformatted>> {
let mut problem_files = Vec::with_capacity(files.len());
for file_path in files {
let path = PathBuf::from_str(&file_path).map_err(|e| Error::FileIo {
let path = Utf8PathBuf::from_str(&file_path).map_err(|e| Error::FileIo {
action: FileIoAction::Open,
kind: FileKind::File,
path: PathBuf::from(file_path),
path: Utf8PathBuf::from(file_path),
err: Some(e.to_string()),
})?;
@ -92,7 +90,7 @@ pub fn unformatted_files(files: Vec<String>) -> Result<Vec<Unformatted>> {
Ok(problem_files)
}
fn format_file(problem_files: &mut Vec<Unformatted>, path: PathBuf) -> Result<()> {
fn format_file(problem_files: &mut Vec<Unformatted>, path: Utf8PathBuf) -> Result<()> {
let src = crate::fs::read(&path)?.into();
let mut output = String::new();
gleam_core::format::pretty(&mut output, &src, &path)?;

View file

@ -17,15 +17,21 @@ use std::{
fmt::Debug,
fs::File,
io::{self, BufRead, BufReader, Write},
path::{Path, PathBuf},
time::SystemTime,
};
use camino::{ReadDirUtf8, Utf8Path, Utf8PathBuf};
use crate::{dependencies::UseManifest, lsp::LspLocker};
#[cfg(test)]
mod tests;
pub fn get_current_directory() -> Result<Utf8PathBuf, &'static str> {
let curr_dir = std::env::current_dir().map_err(|_| "Failed to get current directory")?;
Utf8PathBuf::from_path_buf(curr_dir).map_err(|_| "Non Utf8 Path")
}
/// A `FileWriter` implementation that writes to the file system.
#[derive(Debug, Clone, Copy)]
pub struct ProjectIO;
@ -41,7 +47,7 @@ impl ProjectIO {
}
impl FileSystemReader for ProjectIO {
fn gleam_source_files(&self, dir: &Path) -> Vec<PathBuf> {
fn gleam_source_files(&self, dir: &Utf8Path) -> Vec<Utf8PathBuf> {
if !dir.is_dir() {
return vec![];
}
@ -53,10 +59,11 @@ impl FileSystemReader for ProjectIO {
.filter(|e| e.file_type().is_file())
.map(|d| d.into_path())
.filter(move |d| d.extension() == Some(OsStr::new("gleam")))
.map(|pb| Utf8PathBuf::from_path_buf(pb).expect("Non Utf-8 Path"))
.collect()
}
fn gleam_cache_files(&self, dir: &Path) -> Vec<PathBuf> {
fn gleam_cache_files(&self, dir: &Utf8Path) -> Vec<Utf8PathBuf> {
if !dir.is_dir() {
return vec![];
}
@ -68,30 +75,31 @@ impl FileSystemReader for ProjectIO {
.filter(|e| e.file_type().is_file())
.map(|d| d.into_path())
.filter(|p| p.extension().and_then(OsStr::to_str) == Some("cache"))
.map(|pb| Utf8PathBuf::from_path_buf(pb).expect("Non Utf-8 Path"))
.collect()
}
fn read(&self, path: &Path) -> Result<String, Error> {
fn read(&self, path: &Utf8Path) -> Result<String, Error> {
read(path)
}
fn read_bytes(&self, path: &Path) -> Result<Vec<u8>, Error> {
fn read_bytes(&self, path: &Utf8Path) -> Result<Vec<u8>, Error> {
read_bytes(path)
}
fn is_file(&self, path: &Path) -> bool {
fn is_file(&self, path: &Utf8Path) -> bool {
path.is_file()
}
fn is_directory(&self, path: &Path) -> bool {
fn is_directory(&self, path: &Utf8Path) -> bool {
path.is_dir()
}
fn reader(&self, path: &Path) -> Result<WrappedReader, Error> {
fn reader(&self, path: &Utf8Path) -> Result<WrappedReader, Error> {
reader(path)
}
fn read_dir(&self, path: &Path) -> Result<ReadDir> {
fn read_dir(&self, path: &Utf8Path) -> Result<ReadDir> {
read_dir(path).map(|entries| {
entries
.map(|result| result.map(|entry| DirEntry::from_path(entry.path())))
@ -99,7 +107,7 @@ impl FileSystemReader for ProjectIO {
})
}
fn modification_time(&self, path: &Path) -> Result<SystemTime, Error> {
fn modification_time(&self, path: &Utf8Path) -> Result<SystemTime, Error> {
path.metadata()
.map(|m| m.modified().unwrap_or_else(|_| SystemTime::now()))
.map_err(|e| Error::FileIo {
@ -110,45 +118,45 @@ impl FileSystemReader for ProjectIO {
})
}
fn canonicalise(&self, path: &Path) -> Result<PathBuf, Error> {
fn canonicalise(&self, path: &Utf8Path) -> Result<Utf8PathBuf, Error> {
canonicalise(path)
}
}
impl FileSystemWriter for ProjectIO {
fn delete(&self, path: &Path) -> Result<()> {
fn delete(&self, path: &Utf8Path) -> Result<()> {
delete_dir(path)
}
fn copy(&self, from: &Path, to: &Path) -> Result<()> {
fn copy(&self, from: &Utf8Path, to: &Utf8Path) -> Result<()> {
copy(from, to)
}
fn copy_dir(&self, from: &Path, to: &Path) -> Result<()> {
fn copy_dir(&self, from: &Utf8Path, to: &Utf8Path) -> Result<()> {
copy_dir(from, to)
}
fn mkdir(&self, path: &Path) -> Result<(), Error> {
fn mkdir(&self, path: &Utf8Path) -> Result<(), Error> {
mkdir(path)
}
fn hardlink(&self, from: &Path, to: &Path) -> Result<(), Error> {
fn hardlink(&self, from: &Utf8Path, to: &Utf8Path) -> Result<(), Error> {
hardlink(from, to)
}
fn symlink_dir(&self, from: &Path, to: &Path) -> Result<(), Error> {
fn symlink_dir(&self, from: &Utf8Path, to: &Utf8Path) -> Result<(), Error> {
symlink_dir(from, to)
}
fn delete_file(&self, path: &Path) -> Result<()> {
fn delete_file(&self, path: &Utf8Path) -> Result<()> {
delete_file(path)
}
fn write(&self, path: &Path, content: &str) -> Result<(), Error> {
fn write(&self, path: &Utf8Path, content: &str) -> Result<(), Error> {
write(path, content)
}
fn write_bytes(&self, path: &Path, content: &[u8]) -> Result<(), Error> {
fn write_bytes(&self, path: &Utf8Path, content: &[u8]) -> Result<(), Error> {
write_bytes(path, content)
}
}
@ -159,7 +167,7 @@ impl CommandExecutor for ProjectIO {
program: &str,
args: &[String],
env: &[(&str, String)],
cwd: Option<&Path>,
cwd: Option<&Utf8Path>,
stdio: Stdio,
) -> Result<i32, Error> {
tracing::trace!(program=program, args=?args.join(" "), env=?env, cwd=?cwd, "command_exec");
@ -168,7 +176,7 @@ impl CommandExecutor for ProjectIO {
.stdin(stdio.get_process_stdio())
.stdout(stdio.get_process_stdio())
.envs(env.iter().map(|(a, b)| (a, b)))
.current_dir(cwd.unwrap_or_else(|| Path::new("./")))
.current_dir(cwd.unwrap_or_else(|| Utf8Path::new("./")))
.status();
match result {
@ -201,7 +209,7 @@ impl DownloadDependencies for ProjectIO {
}
}
pub fn delete_dir(dir: &Path) -> Result<(), Error> {
pub fn delete_dir(dir: &Utf8Path) -> Result<(), Error> {
tracing::trace!(path=?dir, "deleting_directory");
if dir.exists() {
std::fs::remove_dir_all(dir).map_err(|e| Error::FileIo {
@ -216,7 +224,7 @@ pub fn delete_dir(dir: &Path) -> Result<(), Error> {
Ok(())
}
pub fn delete_file(file: &Path) -> Result<(), Error> {
pub fn delete_file(file: &Utf8Path) -> Result<(), Error> {
tracing::trace!("Deleting file {:?}", file);
if file.exists() {
std::fs::remove_file(file).map_err(|e| Error::FileIo {
@ -231,7 +239,7 @@ pub fn delete_file(file: &Path) -> Result<(), Error> {
Ok(())
}
pub fn write_outputs_under(outputs: &[OutputFile], base: &Path) -> Result<(), Error> {
pub fn write_outputs_under(outputs: &[OutputFile], base: &Utf8Path) -> Result<(), Error> {
for file in outputs {
let path = base.join(&file.path);
match &file.content {
@ -250,12 +258,12 @@ pub fn write_output(file: &OutputFile) -> Result<(), Error> {
}
}
pub fn write(path: &Path, text: &str) -> Result<(), Error> {
pub fn write(path: &Utf8Path, text: &str) -> Result<(), Error> {
write_bytes(path, text.as_bytes())
}
#[cfg(target_family = "unix")]
pub fn make_executable(path: impl AsRef<Path>) -> Result<(), Error> {
pub fn make_executable(path: impl AsRef<Utf8Path>) -> Result<(), Error> {
use std::os::unix::fs::PermissionsExt;
tracing::trace!(path = ?path.as_ref(), "setting_permissions");
@ -271,11 +279,11 @@ pub fn make_executable(path: impl AsRef<Path>) -> Result<(), Error> {
}
#[cfg(not(target_family = "unix"))]
pub fn make_executable(_path: impl AsRef<Path>) -> Result<(), Error> {
pub fn make_executable(_path: impl AsRef<Utf8Path>) -> Result<(), Error> {
Ok(())
}
pub fn write_bytes(path: &Path, bytes: &[u8]) -> Result<(), Error> {
pub fn write_bytes(path: &Utf8Path, bytes: &[u8]) -> Result<(), Error> {
tracing::trace!(path=?path, "writing_file");
let dir_path = path.parent().ok_or_else(|| Error::FileIo {
@ -308,7 +316,7 @@ pub fn write_bytes(path: &Path, bytes: &[u8]) -> Result<(), Error> {
Ok(())
}
fn is_gleam_path(path: &Path, dir: impl AsRef<Path>) -> bool {
fn is_gleam_path(path: &Utf8Path, dir: impl AsRef<Utf8Path>) -> bool {
use regex::Regex;
lazy_static! {
static ref RE: Regex = Regex::new(&format!(
@ -320,14 +328,13 @@ fn is_gleam_path(path: &Path, dir: impl AsRef<Path>) -> bool {
}
RE.is_match(
path.strip_prefix(dir)
path.strip_prefix(dir.as_ref())
.expect("is_gleam_path(): strip_prefix")
.to_str()
.expect("is_gleam_path(): to_str"),
.as_str(),
)
}
pub fn gleam_files_excluding_gitignore(dir: &Path) -> impl Iterator<Item = PathBuf> + '_ {
pub fn gleam_files_excluding_gitignore(dir: &Utf8Path) -> impl Iterator<Item = Utf8PathBuf> + '_ {
ignore::WalkBuilder::new(dir)
.follow_links(true)
.require_git(false)
@ -335,24 +342,21 @@ pub fn gleam_files_excluding_gitignore(dir: &Path) -> impl Iterator<Item = PathB
.filter_map(Result::ok)
.filter(|e| e.file_type().map(|t| t.is_file()).unwrap_or(false))
.map(ignore::DirEntry::into_path)
.map(|pb| Utf8PathBuf::from_path_buf(pb).expect("Non Utf-8 Path"))
.filter(move |d| is_gleam_path(d, dir))
}
pub fn native_files(dir: &Path) -> Result<impl Iterator<Item = PathBuf> + '_> {
pub fn native_files(dir: &Utf8Path) -> Result<impl Iterator<Item = Utf8PathBuf> + '_> {
Ok(read_dir(dir)?
.flat_map(Result::ok)
.map(|e| e.path())
.map(|e| e.into_path())
.filter(|path| {
let extension = path
.extension()
.unwrap_or_default()
.to_str()
.unwrap_or_default();
let extension = path.extension().unwrap_or_default();
matches!(extension, "erl" | "hrl" | "ex" | "js" | "mjs" | "ts")
}))
}
pub fn private_files_excluding_gitignore(dir: &Path) -> impl Iterator<Item = PathBuf> + '_ {
pub fn private_files_excluding_gitignore(dir: &Utf8Path) -> impl Iterator<Item = Utf8PathBuf> + '_ {
ignore::WalkBuilder::new(dir)
.follow_links(true)
.require_git(false)
@ -360,18 +364,15 @@ pub fn private_files_excluding_gitignore(dir: &Path) -> impl Iterator<Item = Pat
.filter_map(Result::ok)
.filter(|e| e.file_type().map(|t| t.is_file()).unwrap_or(false))
.map(ignore::DirEntry::into_path)
.map(|pb| Utf8PathBuf::from_path_buf(pb).expect("Non Utf-8 Path"))
}
pub fn erlang_files(dir: &Path) -> Result<impl Iterator<Item = PathBuf> + '_> {
pub fn erlang_files(dir: &Utf8Path) -> Result<impl Iterator<Item = Utf8PathBuf> + '_> {
Ok(read_dir(dir)?
.flat_map(Result::ok)
.map(|e| e.path())
.map(|e| e.into_path())
.filter(|path| {
let extension = path
.extension()
.unwrap_or_default()
.to_str()
.unwrap_or_default();
let extension = path.extension().unwrap_or_default();
extension == "erl" || extension == "hrl"
}))
}
@ -405,102 +406,105 @@ pub fn create_tar_archive(outputs: Vec<OutputFile>) -> Result<Vec<u8>, Error> {
.map_err(|e| Error::Gzip(e.to_string()))
}
pub fn mkdir(path: impl AsRef<Path> + Debug) -> Result<(), Error> {
pub fn mkdir(path: impl AsRef<Utf8Path> + Debug) -> Result<(), Error> {
if path.as_ref().exists() {
return Ok(());
}
tracing::trace!(path=?path, "creating_directory");
std::fs::create_dir_all(&path).map_err(|err| Error::FileIo {
std::fs::create_dir_all(path.as_ref()).map_err(|err| Error::FileIo {
kind: FileKind::Directory,
path: PathBuf::from(path.as_ref()),
path: Utf8PathBuf::from(path.as_ref()),
action: FileIoAction::Create,
err: Some(err.to_string()),
})
}
pub fn read_dir(path: impl AsRef<Path> + Debug) -> Result<std::fs::ReadDir, Error> {
pub fn read_dir(path: impl AsRef<Utf8Path> + Debug) -> Result<ReadDirUtf8, Error> {
tracing::trace!(path=?path,"reading_directory");
std::fs::read_dir(&path).map_err(|e| Error::FileIo {
Utf8Path::read_dir_utf8(path.as_ref()).map_err(|e| Error::FileIo {
action: FileIoAction::Read,
kind: FileKind::Directory,
path: PathBuf::from(path.as_ref()),
path: Utf8PathBuf::from(path.as_ref()),
err: Some(e.to_string()),
})
}
pub fn module_caches_paths(
path: impl AsRef<Path> + Debug,
) -> Result<impl Iterator<Item = PathBuf>, Error> {
path: impl AsRef<Utf8Path> + Debug,
) -> Result<impl Iterator<Item = Utf8PathBuf>, Error> {
Ok(read_dir(path)?
.filter_map(Result::ok)
.map(|f| f.path())
.filter(|p| p.extension().and_then(OsStr::to_str) == Some("cache")))
.map(|f| f.into_path())
.filter(|p| p.extension() == Some("cache")))
}
pub fn read(path: impl AsRef<Path> + Debug) -> Result<String, Error> {
pub fn read(path: impl AsRef<Utf8Path> + Debug) -> Result<String, Error> {
tracing::trace!(path=?path,"reading_file");
std::fs::read_to_string(&path).map_err(|err| Error::FileIo {
std::fs::read_to_string(path.as_ref()).map_err(|err| Error::FileIo {
action: FileIoAction::Read,
kind: FileKind::File,
path: PathBuf::from(path.as_ref()),
path: Utf8PathBuf::from(path.as_ref()),
err: Some(err.to_string()),
})
}
pub fn read_bytes(path: impl AsRef<Path> + Debug) -> Result<Vec<u8>, Error> {
pub fn read_bytes(path: impl AsRef<Utf8Path> + Debug) -> Result<Vec<u8>, Error> {
tracing::trace!(path=?path,"reading_file");
std::fs::read(&path).map_err(|err| Error::FileIo {
std::fs::read(path.as_ref()).map_err(|err| Error::FileIo {
action: FileIoAction::Read,
kind: FileKind::File,
path: PathBuf::from(path.as_ref()),
path: Utf8PathBuf::from(path.as_ref()),
err: Some(err.to_string()),
})
}
pub fn reader(path: impl AsRef<Path> + Debug) -> Result<WrappedReader, Error> {
pub fn reader(path: impl AsRef<Utf8Path> + Debug) -> Result<WrappedReader, Error> {
tracing::trace!(path=?path,"opening_file_reader");
let reader = File::open(&path).map_err(|err| Error::FileIo {
let reader = File::open(path.as_ref()).map_err(|err| Error::FileIo {
action: FileIoAction::Open,
kind: FileKind::File,
path: PathBuf::from(path.as_ref()),
path: Utf8PathBuf::from(path.as_ref()),
err: Some(err.to_string()),
})?;
Ok(WrappedReader::new(path.as_ref(), Box::new(reader)))
}
pub fn buffered_reader<P: AsRef<Path> + Debug>(path: P) -> Result<impl BufRead, Error> {
pub fn buffered_reader<P: AsRef<Utf8Path> + Debug>(path: P) -> Result<impl BufRead, Error> {
tracing::trace!(path=?path,"opening_file_buffered_reader");
let reader = File::open(&path).map_err(|err| Error::FileIo {
let reader = File::open(path.as_ref()).map_err(|err| Error::FileIo {
action: FileIoAction::Open,
kind: FileKind::File,
path: PathBuf::from(path.as_ref()),
path: Utf8PathBuf::from(path.as_ref()),
err: Some(err.to_string()),
})?;
Ok(BufReader::new(reader))
}
pub fn copy(path: impl AsRef<Path> + Debug, to: impl AsRef<Path> + Debug) -> Result<(), Error> {
pub fn copy(
path: impl AsRef<Utf8Path> + Debug,
to: impl AsRef<Utf8Path> + Debug,
) -> Result<(), Error> {
tracing::trace!(from=?path, to=?to, "copying_file");
// TODO: include the destination in the error message
std::fs::copy(&path, &to)
std::fs::copy(path.as_ref(), to.as_ref())
.map_err(|err| Error::FileIo {
action: FileIoAction::Copy,
kind: FileKind::File,
path: PathBuf::from(path.as_ref()),
path: Utf8PathBuf::from(path.as_ref()),
err: Some(err.to_string()),
})
.map(|_| ())
}
// pub fn rename(path: impl AsRef<Path> + Debug, to: impl AsRef<Path> + Debug) -> Result<(), Error> {
// pub fn rename(path: impl AsRef<Utf8Path> + Debug, to: impl AsRef<Utf8Path> + Debug) -> Result<(), Error> {
// tracing::trace!(from=?path, to=?to, "renaming_file");
// // TODO: include the destination in the error message
@ -508,49 +512,59 @@ pub fn copy(path: impl AsRef<Path> + Debug, to: impl AsRef<Path> + Debug) -> Res
// .map_err(|err| Error::FileIo {
// action: FileIoAction::Rename,
// kind: FileKind::File,
// path: PathBuf::from(path.as_ref()),
// path: Utf8PathBuf::from(path.as_ref()),
// err: Some(err.to_string()),
// })
// .map(|_| ())
// }
pub fn copy_dir(path: impl AsRef<Path> + Debug, to: impl AsRef<Path> + Debug) -> Result<(), Error> {
pub fn copy_dir(
path: impl AsRef<Utf8Path> + Debug,
to: impl AsRef<Utf8Path> + Debug,
) -> Result<(), Error> {
tracing::trace!(from=?path, to=?to, "copying_directory");
// TODO: include the destination in the error message
fs_extra::dir::copy(&path, &to, &fs_extra::dir::CopyOptions::new())
.map_err(|err| Error::FileIo {
action: FileIoAction::Copy,
kind: FileKind::Directory,
path: PathBuf::from(path.as_ref()),
err: Some(err.to_string()),
})
.map(|_| ())
fs_extra::dir::copy(
path.as_ref(),
to.as_ref(),
&fs_extra::dir::CopyOptions::new(),
)
.map_err(|err| Error::FileIo {
action: FileIoAction::Copy,
kind: FileKind::Directory,
path: Utf8PathBuf::from(path.as_ref()),
err: Some(err.to_string()),
})
.map(|_| ())
}
pub fn symlink_dir(
src: impl AsRef<Path> + Debug,
dest: impl AsRef<Path> + Debug,
src: impl AsRef<Utf8Path> + Debug,
dest: impl AsRef<Utf8Path> + Debug,
) -> Result<(), Error> {
tracing::trace!(src=?src, dest=?dest, "symlinking");
symlink::symlink_dir(canonicalise(src.as_ref())?, dest.as_ref()).map_err(|err| {
Error::FileIo {
action: FileIoAction::Link,
kind: FileKind::File,
path: PathBuf::from(dest.as_ref()),
path: Utf8PathBuf::from(dest.as_ref()),
err: Some(err.to_string()),
}
})?;
Ok(())
}
pub fn hardlink(from: impl AsRef<Path> + Debug, to: impl AsRef<Path> + Debug) -> Result<(), Error> {
pub fn hardlink(
from: impl AsRef<Utf8Path> + Debug,
to: impl AsRef<Utf8Path> + Debug,
) -> Result<(), Error> {
tracing::trace!(from=?from, to=?to, "hardlinking");
std::fs::hard_link(&from, &to)
std::fs::hard_link(from.as_ref(), to.as_ref())
.map_err(|err| Error::FileIo {
action: FileIoAction::Link,
kind: FileKind::File,
path: PathBuf::from(from.as_ref()),
path: Utf8PathBuf::from(from.as_ref()),
err: Some(err.to_string()),
})
.map(|_| ())
@ -561,7 +575,7 @@ pub fn hardlink(from: impl AsRef<Path> + Debug, to: impl AsRef<Path> + Debug) ->
/// given path. If git is not installed then we assume we're not in a git work
/// tree.
///
pub fn is_inside_git_work_tree(path: &Path) -> Result<bool, Error> {
pub fn is_inside_git_work_tree(path: &Utf8Path) -> Result<bool, Error> {
tracing::trace!(path=?path, "checking_for_git_repo");
let args: Vec<&str> = vec!["rev-parse", "--is-inside-work-tree", "--quiet"];
@ -592,7 +606,7 @@ pub fn is_inside_git_work_tree(path: &Path) -> Result<bool, Error> {
/// Run `git init` in the given path.
/// If git is not installed then we do nothing.
pub fn git_init(path: &Path) -> Result<(), Error> {
pub fn git_init(path: &Utf8Path) -> Result<(), Error> {
tracing::trace!(path=?path, "initializing git");
if is_inside_git_work_tree(path)? {
@ -600,7 +614,7 @@ pub fn git_init(path: &Path) -> Result<(), Error> {
return Ok(());
}
let args = vec!["init".into(), "--quiet".into(), path.display().to_string()];
let args = vec!["init".into(), "--quiet".into(), path.to_string()];
match ProjectIO::new().exec("git", &args, &[], None, Stdio::Inherit) {
Ok(_) => Ok(()),
@ -613,13 +627,15 @@ pub fn git_init(path: &Path) -> Result<(), Error> {
}
}
pub fn canonicalise(path: &Path) -> Result<PathBuf, Error> {
std::fs::canonicalize(path).map_err(|err| Error::FileIo {
action: FileIoAction::Canonicalise,
kind: FileKind::File,
path: PathBuf::from(path),
err: Some(err.to_string()),
})
pub fn canonicalise(path: &Utf8Path) -> Result<Utf8PathBuf, Error> {
std::fs::canonicalize(path)
.map_err(|err| Error::FileIo {
action: FileIoAction::Canonicalise,
kind: FileKind::File,
path: Utf8PathBuf::from(path),
err: Some(err.to_string()),
})
.map(|pb| Utf8PathBuf::from_path_buf(pb).expect("Non Utf8 Path"))
}
#[derive(Debug, Clone, Copy)]

View file

@ -1,9 +1,9 @@
use std::path::Path;
use camino::Utf8Path;
#[test]
fn is_inside_git_work_tree_ok() {
let tmp_dir = tempfile::tempdir().unwrap();
let path = tmp_dir.path();
let path = Utf8Path::from_path(tmp_dir.path()).expect("Non Utf-8 Path");
assert!(!super::is_inside_git_work_tree(path).unwrap());
assert_eq!(super::git_init(path), Ok(()));
@ -13,7 +13,7 @@ fn is_inside_git_work_tree_ok() {
#[test]
fn git_init_success() {
let tmp_dir = tempfile::tempdir().unwrap();
let path = tmp_dir.path();
let path = Utf8Path::from_path(tmp_dir.path()).expect("Non Utf-8 Path");
let git = path.join(".git");
assert!(!git.exists());
@ -24,12 +24,19 @@ fn git_init_success() {
#[test]
fn git_init_already_in_git() {
let tmp_dir = tempfile::tempdir().unwrap();
let git = tmp_dir.path().join(".git");
let git = Utf8Path::from_path(tmp_dir.path())
.expect("Non Utf-8 Path")
.join(".git");
assert!(!git.exists());
assert_eq!(super::git_init(tmp_dir.path()), Ok(()));
assert_eq!(
super::git_init(Utf8Path::from_path(tmp_dir.path()).expect("Non Utf-8 Path")),
Ok(())
);
assert!(git.exists());
let sub = tmp_dir.path().join("subproject");
let sub = Utf8Path::from_path(tmp_dir.path())
.expect("Non Utf-8 Path")
.join("subproject");
let git = sub.join(".git");
crate::fs::mkdir(&sub).unwrap();
assert!(!git.exists());
@ -40,22 +47,22 @@ fn git_init_already_in_git() {
#[test]
fn is_gleam_path_test() {
assert!(super::is_gleam_path(
Path::new("/some-prefix/a.gleam"),
Path::new("/some-prefix/")
Utf8Path::new("/some-prefix/a.gleam"),
Utf8Path::new("/some-prefix/")
));
assert!(super::is_gleam_path(
Path::new("/some-prefix/one_two/a.gleam"),
Path::new("/some-prefix/")
Utf8Path::new("/some-prefix/one_two/a.gleam"),
Utf8Path::new("/some-prefix/")
));
assert!(super::is_gleam_path(
Path::new("/some-prefix/one_two/a123.gleam"),
Path::new("/some-prefix/")
Utf8Path::new("/some-prefix/one_two/a123.gleam"),
Utf8Path::new("/some-prefix/")
));
assert!(super::is_gleam_path(
Path::new("/some-prefix/one_2/a123.gleam"),
Path::new("/some-prefix/")
Utf8Path::new("/some-prefix/one_2/a123.gleam"),
Utf8Path::new("/some-prefix/")
));
}

View file

@ -74,6 +74,7 @@ mod shell;
use config::root_config;
use dependencies::UseManifest;
use fs::get_current_directory;
pub use gleam_core::{
error::{Error, Result},
warning::Warning,
@ -87,7 +88,7 @@ use gleam_core::{
};
use hex::ApiKeyCommand as _;
use std::path::PathBuf;
use camino::Utf8PathBuf;
use clap::{Args, Parser, Subcommand};
use strum::VariantNames;
@ -283,15 +284,15 @@ pub struct CompilePackage {
/// The directory of the Gleam package
#[clap(long = "package")]
package_directory: PathBuf,
package_directory: Utf8PathBuf,
/// A directory to write compiled package to
#[clap(long = "out")]
output_directory: PathBuf,
output_directory: Utf8PathBuf,
/// A directories of precompiled Gleam projects
#[clap(long = "lib")]
libraries_directory: PathBuf,
libraries_directory: Utf8PathBuf,
/// Skip Erlang to BEAM bytecode compilation if given
#[clap(long = "no-beam")]
@ -520,7 +521,7 @@ fn initialise_logger() {
}
fn project_paths_at_current_directory() -> ProjectPaths {
let current_dir = std::env::current_dir().expect("Could not get current directory");
let current_dir = get_current_directory().expect("Failed to get current directory");
ProjectPaths::new(current_dir)
}

View file

@ -1,3 +1,4 @@
use camino::{Utf8Path, Utf8PathBuf};
use gleam_core::{
erlang,
error::{Error, FileIoAction, FileKind, InvalidProjectNameReason},
@ -5,7 +6,6 @@ use gleam_core::{
};
use serde::{Deserialize, Serialize};
use std::fs::File;
use std::path::{Path, PathBuf};
use std::{env, io::Write};
use strum::{Display, EnumString, EnumVariantNames};
@ -28,11 +28,11 @@ pub enum Template {
#[derive(Debug)]
pub struct Creator {
root: PathBuf,
src: PathBuf,
test: PathBuf,
github: PathBuf,
workflows: PathBuf,
root: Utf8PathBuf,
src: Utf8PathBuf,
test: Utf8PathBuf,
github: Utf8PathBuf,
workflows: Utf8PathBuf,
gleam_version: &'static str,
options: NewOptions,
project_name: String,
@ -51,7 +51,7 @@ impl Creator {
validate_name(&project_name)?;
validate_root_folder(&options.project_root)?;
let root = PathBuf::from(&options.project_root);
let root = Utf8PathBuf::from(&options.project_root);
let src = root.join("src");
let test = root.join("test");
let github = root.join(".github");
@ -266,7 +266,7 @@ The project can be compiled and tested by running these commands:
Ok(())
}
fn write(path: PathBuf, contents: &str) -> Result<()> {
fn write(path: Utf8PathBuf, contents: &str) -> Result<()> {
let mut f = File::create(&path).map_err(|err| Error::FileIo {
kind: FileKind::File,
path: path.clone(),
@ -285,7 +285,7 @@ fn write(path: PathBuf, contents: &str) -> Result<()> {
}
fn validate_root_folder(name: &str) -> Result<(), Error> {
if Path::new(name).exists() {
if Utf8Path::new(name).exists() {
Err(Error::ProjectRootAlreadyExist {
path: name.to_string(),
})
@ -343,9 +343,8 @@ fn get_foldername(path: &str) -> Result<String, Error> {
.ok_or(Error::UnableToFindProjectRoot {
path: path.to_string(),
}),
_ => Path::new(path)
_ => Utf8Path::new(path)
.file_name()
.and_then(|x| x.to_str())
.map(ToString::to_string)
.ok_or(Error::UnableToFindProjectRoot {
path: path.to_string(),

View file

@ -1,11 +1,13 @@
use camino::Utf8PathBuf;
#[test]
fn new() {
let tmp = tempfile::tempdir().unwrap();
let path = tmp.path().join("my_project");
let path = Utf8PathBuf::from_path_buf(tmp.path().join("my_project")).expect("Non Utf8 Path");
let creator = super::Creator::new(
super::NewOptions {
project_root: path.to_str().unwrap().to_string(),
project_root: path.to_string(),
template: super::Template::Lib,
name: None,
description: "Wibble wobble".into(),
@ -33,11 +35,11 @@ fn new() {
#[test]
fn new_with_skip_git() {
let tmp = tempfile::tempdir().unwrap();
let path = tmp.path().join("my_project");
let path = Utf8PathBuf::from_path_buf(tmp.path().join("my_project")).expect("Non Utf8 Path");
let creator = super::Creator::new(
super::NewOptions {
project_root: path.to_str().unwrap().to_string(),
project_root: path.to_string(),
template: super::Template::Lib,
name: None,
description: "Wibble wobble".into(),
@ -55,11 +57,11 @@ fn new_with_skip_git() {
#[test]
fn new_with_skip_github() {
let tmp = tempfile::tempdir().unwrap();
let path = tmp.path().join("my_project");
let path = Utf8PathBuf::from_path_buf(tmp.path().join("my_project")).expect("Non Utf8 Path");
let creator = super::Creator::new(
super::NewOptions {
project_root: path.to_str().unwrap().to_string(),
project_root: path.to_string(),
template: super::Template::Lib,
name: None,
description: "Wibble wobble".into(),
@ -80,11 +82,11 @@ fn new_with_skip_github() {
#[test]
fn new_with_skip_git_and_github() {
let tmp = tempfile::tempdir().unwrap();
let path = tmp.path().join("my_project");
let path = Utf8PathBuf::from_path_buf(tmp.path().join("my_project")).expect("Non Utf8 Path");
let creator = super::Creator::new(
super::NewOptions {
project_root: path.to_str().unwrap().to_string(),
project_root: path.to_string(),
template: super::Template::Lib,
name: None,
description: "Wibble wobble".into(),
@ -105,11 +107,11 @@ fn new_with_skip_git_and_github() {
#[test]
fn invalid_path() {
let tmp = tempfile::tempdir().unwrap();
let path = tmp.path().join("-------");
let path = Utf8PathBuf::from_path_buf(tmp.path().join("-------")).expect("Non Utf8 Path");
assert!(super::Creator::new(
super::NewOptions {
project_root: path.to_str().unwrap().to_string(),
project_root: path.to_string(),
template: super::Template::Lib,
name: None,
description: "Wibble wobble".into(),
@ -124,11 +126,11 @@ fn invalid_path() {
#[test]
fn invalid_name() {
let tmp = tempfile::tempdir().unwrap();
let path = tmp.path().join("projec");
let path = Utf8PathBuf::from_path_buf(tmp.path().join("projec")).expect("Non Utf8 Path");
assert!(super::Creator::new(
super::NewOptions {
project_root: path.to_str().unwrap().to_string(),
project_root: path.to_string(),
template: super::Template::Lib,
name: Some("-".into()),
description: "Wibble wobble".into(),

View file

@ -1,3 +1,4 @@
use camino::{Utf8Path, Utf8PathBuf};
use flate2::{write::GzEncoder, Compression};
use gleam_core::{
build::{Codegen, Mode, Options, Package, Target},
@ -10,11 +11,7 @@ use gleam_core::{
use hexpm::version::{Range, Version};
use itertools::Itertools;
use sha2::Digest;
use std::{
io::Write,
path::{Path, PathBuf},
time::Instant,
};
use std::{io::Write, time::Instant};
use crate::{build, cli, docs, fs, hex::ApiKeyCommand, http::HttpClient};
@ -48,12 +45,12 @@ impl PublishCommand {
if !generated_files_added.is_empty() {
println!("\nGenerated files:");
for file in generated_files_added.iter().sorted() {
println!(" - {}", file.0.to_string_lossy());
println!(" - {}", file.0);
}
}
println!("\nSource files:");
for file in src_files_added.iter().sorted() {
println!(" - {}", file.to_string_lossy());
println!(" - {}", file);
}
println!("\nName: {}", config.name);
println!("Version: {}", config.version);
@ -115,8 +112,8 @@ impl ApiKeyCommand for PublishCommand {
struct Tarball {
compile_result: Package,
data: Vec<u8>,
src_files_added: Vec<PathBuf>,
generated_files_added: Vec<(PathBuf, String)>,
src_files_added: Vec<Utf8PathBuf>,
generated_files_added: Vec<(Utf8PathBuf, String)>,
}
pub fn build_hex_tarball(paths: &ProjectPaths, config: &PackageConfig) -> Result<Vec<u8>> {
@ -189,8 +186,8 @@ fn check_config_for_publishing(config: &PackageConfig) -> Result<()> {
fn metadata_config<'a>(
config: &'a PackageConfig,
source_files: &[PathBuf],
generated_files: &[(PathBuf, String)],
source_files: &[Utf8PathBuf],
generated_files: &[(Utf8PathBuf, String)],
) -> Result<String> {
let repo_url = http::Uri::try_from(config.repository.url().unwrap_or_default()).ok();
let requirements: Result<Vec<ReleaseRequirement<'a>>> = config
@ -227,7 +224,10 @@ fn metadata_config<'a>(
Ok(metadata)
}
fn contents_tarball(files: &[PathBuf], data_files: &[(PathBuf, String)]) -> Result<Vec<u8>, Error> {
fn contents_tarball(
files: &[Utf8PathBuf],
data_files: &[(Utf8PathBuf, String)],
) -> Result<Vec<u8>, Error> {
let mut contents_tar_gz = Vec::new();
{
let mut tarball =
@ -246,16 +246,17 @@ fn contents_tarball(files: &[PathBuf], data_files: &[(PathBuf, String)]) -> Resu
// TODO: test
// TODO: Don't include git-ignored native files
fn project_files() -> Result<Vec<PathBuf>> {
let src = Path::new("src");
let mut files: Vec<PathBuf> = fs::gleam_files_excluding_gitignore(src)
fn project_files() -> Result<Vec<Utf8PathBuf>> {
let src = Utf8Path::new("src");
let mut files: Vec<Utf8PathBuf> = fs::gleam_files_excluding_gitignore(src)
.chain(fs::native_files(src)?)
.collect();
let private = Path::new("priv");
let mut private_files: Vec<PathBuf> = fs::private_files_excluding_gitignore(private).collect();
let private = Utf8Path::new("priv");
let mut private_files: Vec<Utf8PathBuf> =
fs::private_files_excluding_gitignore(private).collect();
files.append(&mut private_files);
let mut add = |path| {
let path = PathBuf::from(path);
let path = Utf8PathBuf::from(path);
if path.exists() {
files.push(path);
}
@ -277,7 +278,7 @@ fn project_files() -> Result<Vec<PathBuf>> {
}
// TODO: test
fn generated_files(paths: &ProjectPaths, package: &Package) -> Result<Vec<(PathBuf, String)>> {
fn generated_files(paths: &ProjectPaths, package: &Package) -> Result<Vec<(Utf8PathBuf, String)>> {
let mut files = vec![];
let dir = paths.build_directory_for_package(Mode::Prod, Target::Erlang, &package.config.name);
@ -285,8 +286,8 @@ fn generated_files(paths: &ProjectPaths, package: &Package) -> Result<Vec<(PathB
let build = dir.join(paths::ARTEFACT_DIRECTORY_NAME);
let include = dir.join("include");
let tar_src = Path::new("src");
let tar_include = Path::new("include");
let tar_src = Utf8Path::new("src");
let tar_include = Utf8Path::new("include");
// Erlang modules
for module in &package.modules {
@ -315,7 +316,7 @@ fn generated_files(paths: &ProjectPaths, package: &Package) -> Result<Vec<(PathB
fn add_to_tar<P, W>(tarball: &mut tar::Builder<W>, path: P, data: &[u8]) -> Result<()>
where
P: AsRef<Path>,
P: AsRef<Utf8Path>,
W: Write,
{
let path = path.as_ref();
@ -331,7 +332,7 @@ where
fn add_path_to_tar<P, W>(tarball: &mut tar::Builder<W>, path: P) -> Result<()>
where
P: AsRef<Path>,
P: AsRef<Utf8Path>,
W: Write,
{
let path = path.as_ref();
@ -346,8 +347,8 @@ pub struct ReleaseMetadata<'a> {
name: &'a str,
version: &'a Version,
description: &'a str,
source_files: &'a [PathBuf],
generated_files: &'a [(PathBuf, String)],
source_files: &'a [Utf8PathBuf],
generated_files: &'a [(Utf8PathBuf, String)],
licenses: &'a Vec<SpdxLicense>,
links: Vec<(&'a str, http::Uri)>,
requirements: Vec<ReleaseRequirement<'a>>,
@ -365,8 +366,8 @@ impl<'a> ReleaseMetadata<'a> {
url = link.1
)
}
fn file(name: impl AsRef<Path>) -> String {
format!("\n <<\"{name}\">>", name = name.as_ref().to_string_lossy())
fn file(name: impl AsRef<Utf8Path>) -> String {
format!("\n <<\"{name}\">>", name = name.as_ref())
}
format!(
@ -448,14 +449,14 @@ fn release_metadata_as_erlang() {
version: &version,
description: "description goes here",
source_files: &[
PathBuf::from("gleam.toml"),
PathBuf::from("src/thingy.gleam"),
PathBuf::from("src/whatever.gleam"),
Utf8PathBuf::from("gleam.toml"),
Utf8PathBuf::from("src/thingy.gleam"),
Utf8PathBuf::from("src/whatever.gleam"),
],
generated_files: &[
(PathBuf::from("src/myapp.app"), "".into()),
(PathBuf::from("src/thingy.erl"), "".into()),
(PathBuf::from("src/whatever.erl"), "".into()),
(Utf8PathBuf::from("src/myapp.app"), "".into()),
(Utf8PathBuf::from("src/thingy.erl"), "".into()),
(Utf8PathBuf::from("src/whatever.erl"), "".into()),
],
licenses: &licences,
links: vec![("homepage", homepage), ("github", github)],

View file

@ -1,4 +1,4 @@
use std::path::{Path, PathBuf};
use camino::{Utf8Path, Utf8PathBuf};
use gleam_core::{
error::{FileIoAction, FileKind},
@ -14,7 +14,7 @@ pub fn command(packages: Vec<String>) -> Result<()> {
.map_err(|e| Error::FileIo {
kind: FileKind::File,
action: FileIoAction::Parse,
path: PathBuf::from("gleam.toml"),
path: Utf8PathBuf::from("gleam.toml"),
err: Some(e.to_string()),
})?;
@ -31,7 +31,7 @@ pub fn command(packages: Vec<String>) -> Result<()> {
}
// Write the updated config
fs::write(Path::new("gleam.toml"), &toml.to_string())?;
fs::write(Utf8Path::new("gleam.toml"), &toml.to_string())?;
let paths = crate::project_paths_at_current_directory();
_ = crate::dependencies::download(&paths, cli::Reporter::new(), None, UseManifest::Yes)?;
for package_to_remove in packages {

View file

@ -1,3 +1,4 @@
use camino::Utf8PathBuf;
use gleam_core::{
build::{Codegen, Mode, Options, Runtime, Target},
config::{DenoFlag, PackageConfig},
@ -7,7 +8,6 @@ use gleam_core::{
type_::ModuleFunction,
};
use lazy_static::lazy_static;
use std::path::PathBuf;
use crate::fs::ProjectIO;
@ -116,7 +116,7 @@ fn run_erlang(
for entry in crate::fs::read_dir(packages)?.filter_map(Result::ok) {
args.push("-pa".into());
args.push(entry.path().join("ebin").to_string_lossy().into());
args.push(entry.path().join("ebin").into());
}
// gleam modules are seperated by `/`. Erlang modules are seperated by `@`.
@ -165,13 +165,13 @@ fn write_javascript_entrypoint(
.strip_prefix(paths.root())
.expect("Failed to strip prefix from path")
.to_path_buf();
let entrypoint = format!("./{}/gleam.main.mjs", entry.to_string_lossy());
let entrypoint = format!("./{}/gleam.main.mjs", entry);
let module = format!(
r#"import {{ main }} from "./{module}.mjs";
main();
"#,
);
crate::fs::write(&PathBuf::from(&entrypoint), &module)?;
crate::fs::write(&Utf8PathBuf::from(&entrypoint), &module)?;
Ok(entrypoint)
}

View file

@ -79,6 +79,7 @@ lsp-server = "0.5"
lsp-types = "0.92"
# Pubgrub dependency resolution algorithm
pubgrub = "0.2"
camino = { version = "1.1.6", features = ["serde1"] }
[build-dependencies]
# Data (de)serialisation

View file

@ -9,4 +9,18 @@ disallowed-methods = [
{ path = "std::path::Path::read_link", reason = "IO is not permitted in core" },
{ path = "std::path::Path::symlink_metadata", reason = "IO is not permitted in core" },
{ path = "std::path::Path::try_exists", reason = "IO is not permitted in core" },
{ path = "camino::Utf8Path::canonicalize", reason = "IO is not permitted in core" },
{ path = "camino::Utf8Path::exists", reason = "IO is not permitted in core" },
{ path = "camino::Utf8Path::is_dir", reason = "IO is not permitted in core" },
{ path = "camino::Utf8Path::is_file", reason = "IO is not permitted in core" },
{ path = "camino::Utf8Path::is_symlink", reason = "IO is not permitted in core" },
{ path = "camino::Utf8Path::read_dir", reason = "IO is not permitted in core" },
{ path = "camino::Utf8Path::read_link", reason = "IO is not permitted in core" },
{ path = "camino::Utf8Path::symlink_metadata", reason = "IO is not permitted in core" },
{ path = "camino::Utf8Path::try_exists", reason = "IO is not permitted in core" },
{ path = "std::path::Path::new", reason = "Manually constructed paths should use camino::Utf8Path" },
{ path = "std::path::PathBuf::new", reason = "Manually constructed pathbufs should use camino::Utf8Path" },
]

View file

@ -27,13 +27,12 @@ use crate::{
parse::extra::{Comment, ModuleExtra},
type_,
};
use camino::Utf8PathBuf;
use itertools::Itertools;
use serde::{Deserialize, Serialize};
use smol_str::SmolStr;
use std::time::SystemTime;
use std::{
collections::HashMap, ffi::OsString, fs::DirEntry, iter::Peekable, path::PathBuf, process,
};
use std::{collections::HashMap, ffi::OsString, fs::DirEntry, iter::Peekable, process};
use strum::{Display, EnumIter, EnumString, EnumVariantNames, VariantNames};
#[derive(
@ -185,7 +184,7 @@ pub struct Module {
pub name: SmolStr,
pub code: SmolStr,
pub mtime: SystemTime,
pub input_path: PathBuf,
pub input_path: Utf8PathBuf,
pub origin: Origin,
pub ast: TypedModule,
pub extra: ModuleExtra,
@ -193,10 +192,10 @@ pub struct Module {
}
impl Module {
pub fn compiled_erlang_path(&self) -> PathBuf {
pub fn compiled_erlang_path(&self) -> Utf8PathBuf {
let mut path = self.name.replace("/", "@");
path.push_str(".erl");
PathBuf::from(path)
Utf8PathBuf::from(path)
}
pub fn is_test(&self) -> bool {

View file

@ -1,11 +1,9 @@
#[cfg(test)]
mod tests;
use std::{
collections::HashMap,
path::{Path, PathBuf},
time::SystemTime,
};
use std::{collections::HashMap, time::SystemTime};
use camino::{Utf8Path, Utf8PathBuf};
use serde::{Deserialize, Serialize};
use smol_str::SmolStr;
@ -39,8 +37,8 @@ pub(crate) struct ModuleLoader<'a, IO> {
pub target: Target,
pub codegen: CodegenRequired,
pub package_name: &'a SmolStr,
pub source_directory: &'a Path,
pub artefact_directory: &'a Path,
pub source_directory: &'a Utf8Path,
pub artefact_directory: &'a Utf8Path,
pub origin: Origin,
}
@ -56,7 +54,7 @@ where
/// Whether the module has changed or not is determined by comparing the
/// modification time of the source file with the value recorded in the
/// `.timestamp` file in the artefact directory.
pub fn load(&self, path: PathBuf) -> Result<Input> {
pub fn load(&self, path: Utf8PathBuf) -> Result<Input> {
let name = module_name(self.source_directory, &path);
let artefact = name.replace("/", "@");
let source_mtime = self.io.modification_time(&path)?;
@ -116,7 +114,7 @@ where
fn read_source(
&self,
path: PathBuf,
path: Utf8PathBuf,
name: SmolStr,
mtime: SystemTime,
) -> Result<UncompiledModule, Error> {
@ -147,7 +145,7 @@ pub(crate) fn read_source<IO>(
warnings: &WarningEmitter,
target: Target,
origin: Origin,
path: PathBuf,
path: Utf8PathBuf,
name: SmolStr,
package_name: SmolStr,
mtime: SystemTime,

View file

@ -8,17 +8,17 @@ use std::time::Duration;
#[test]
fn no_cache_present() {
let name = "package".into();
let src = Path::new("/src");
let artefact = Path::new("/artefact");
let src = Utf8Path::new("/src");
let artefact = Utf8Path::new("/artefact");
let fs = InMemoryFileSystem::new();
let warnings = WarningEmitter::null();
let loader = make_loader(&warnings, &name, &fs, src, artefact);
fs.write(&Path::new("/src/main.gleam"), "const x = 1")
fs.write(&Utf8Path::new("/src/main.gleam"), "const x = 1")
.unwrap();
let result = loader
.load(Path::new("/src/main.gleam").to_path_buf())
.load(Utf8Path::new("/src/main.gleam").to_path_buf())
.unwrap();
assert!(result.is_new());
@ -27,8 +27,8 @@ fn no_cache_present() {
#[test]
fn cache_present_and_fresh() {
let name = "package".into();
let src = Path::new("/src");
let artefact = Path::new("/artefact");
let src = Utf8Path::new("/src");
let artefact = Utf8Path::new("/artefact");
let fs = InMemoryFileSystem::new();
let warnings = WarningEmitter::null();
let loader = make_loader(&warnings, &name, &fs, src, artefact);
@ -38,7 +38,7 @@ fn cache_present_and_fresh() {
write_cache(&fs, TEST_SOURCE_1, "/artefact/main.cache_meta", 1, false);
let result = loader
.load(Path::new("/src/main.gleam").to_path_buf())
.load(Utf8Path::new("/src/main.gleam").to_path_buf())
.unwrap();
assert!(result.is_cached());
@ -47,8 +47,8 @@ fn cache_present_and_fresh() {
#[test]
fn cache_present_and_stale() {
let name = "package".into();
let src = Path::new("/src");
let artefact = Path::new("/artefact");
let src = Utf8Path::new("/src");
let artefact = Utf8Path::new("/artefact");
let fs = InMemoryFileSystem::new();
let warnings = WarningEmitter::null();
let loader = make_loader(&warnings, &name, &fs, src, artefact);
@ -58,7 +58,7 @@ fn cache_present_and_stale() {
write_cache(&fs, TEST_SOURCE_1, "/artefact/main.cache_meta", 1, false);
let result = loader
.load(Path::new("/src/main.gleam").to_path_buf())
.load(Utf8Path::new("/src/main.gleam").to_path_buf())
.unwrap();
assert!(result.is_new());
@ -67,8 +67,8 @@ fn cache_present_and_stale() {
#[test]
fn cache_present_and_stale_but_source_is_the_same() {
let name = "package".into();
let src = Path::new("/src");
let artefact = Path::new("/artefact");
let src = Utf8Path::new("/src");
let artefact = Utf8Path::new("/artefact");
let fs = InMemoryFileSystem::new();
let warnings = WarningEmitter::null();
let loader = make_loader(&warnings, &name, &fs, src, artefact);
@ -78,7 +78,7 @@ fn cache_present_and_stale_but_source_is_the_same() {
write_cache(&fs, TEST_SOURCE_1, "/artefact/main.cache_meta", 1, false);
let result = loader
.load(Path::new("/src/main.gleam").to_path_buf())
.load(Utf8Path::new("/src/main.gleam").to_path_buf())
.unwrap();
assert!(result.is_cached());
@ -87,8 +87,8 @@ fn cache_present_and_stale_but_source_is_the_same() {
#[test]
fn cache_present_without_codegen_when_required() {
let name = "package".into();
let src = Path::new("/src");
let artefact = Path::new("/artefact");
let src = Utf8Path::new("/src");
let artefact = Utf8Path::new("/artefact");
let fs = InMemoryFileSystem::new();
let warnings = WarningEmitter::null();
let mut loader = make_loader(&warnings, &name, &fs, src, artefact);
@ -99,7 +99,7 @@ fn cache_present_without_codegen_when_required() {
write_cache(&fs, TEST_SOURCE_1, "/artefact/main.cache_meta", 1, false);
let result = loader
.load(Path::new("/src/main.gleam").to_path_buf())
.load(Utf8Path::new("/src/main.gleam").to_path_buf())
.unwrap();
assert!(result.is_new());
@ -108,8 +108,8 @@ fn cache_present_without_codegen_when_required() {
#[test]
fn cache_present_with_codegen_when_required() {
let name = "package".into();
let src = Path::new("/src");
let artefact = Path::new("/artefact");
let src = Utf8Path::new("/src");
let artefact = Utf8Path::new("/artefact");
let fs = InMemoryFileSystem::new();
let warnings = WarningEmitter::null();
let mut loader = make_loader(&warnings, &name, &fs, src, artefact);
@ -120,7 +120,7 @@ fn cache_present_with_codegen_when_required() {
write_cache(&fs, TEST_SOURCE_1, "/artefact/main.cache_meta", 1, true);
let result = loader
.load(Path::new("/src/main.gleam").to_path_buf())
.load(Utf8Path::new("/src/main.gleam").to_path_buf())
.unwrap();
assert!(result.is_cached());
@ -129,8 +129,8 @@ fn cache_present_with_codegen_when_required() {
#[test]
fn cache_present_without_codegen_when_not_required() {
let name = "package".into();
let src = Path::new("/src");
let artefact = Path::new("/artefact");
let src = Utf8Path::new("/src");
let artefact = Utf8Path::new("/artefact");
let fs = InMemoryFileSystem::new();
let warnings = WarningEmitter::null();
let mut loader = make_loader(&warnings, &name, &fs, src, artefact);
@ -141,7 +141,7 @@ fn cache_present_without_codegen_when_not_required() {
write_cache(&fs, TEST_SOURCE_1, "/artefact/main.cache_meta", 1, false);
let result = loader
.load(Path::new("/src/main.gleam").to_path_buf())
.load(Utf8Path::new("/src/main.gleam").to_path_buf())
.unwrap();
assert!(result.is_cached());
@ -163,12 +163,12 @@ fn write_cache(
dependencies: vec![],
fingerprint: SourceFingerprint::new(source),
};
let path = Path::new(path);
let path = Utf8Path::new(path);
fs.write_bytes(&path, &cache_metadata.to_binary()).unwrap();
}
fn write_src(fs: &InMemoryFileSystem, source: &str, path: &str, seconds: u64) {
let path = Path::new(path);
let path = Utf8Path::new(path);
fs.write(&path, source).unwrap();
fs.set_modification_time(&path, SystemTime::UNIX_EPOCH + Duration::from_secs(seconds));
}
@ -177,8 +177,8 @@ fn make_loader<'a>(
warnings: &'a WarningEmitter,
package_name: &'a SmolStr,
fs: &InMemoryFileSystem,
src: &'a Path,
artefact: &'a Path,
src: &'a Utf8Path,
artefact: &'a Utf8Path,
) -> ModuleLoader<'a, InMemoryFileSystem> {
ModuleLoader {
warnings,

View file

@ -1,10 +1,9 @@
#[cfg(test)]
mod tests;
use std::{
collections::HashSet,
path::{Path, PathBuf},
};
use std::collections::HashSet;
use camino::{Utf8Path, Utf8PathBuf};
use crate::{
io::{FileSystemReader, FileSystemWriter},
@ -14,15 +13,15 @@ use crate::{
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct CopiedNativeFiles {
pub any_elixir: bool,
pub to_compile: Vec<PathBuf>,
pub to_compile: Vec<Utf8PathBuf>,
}
pub(crate) struct NativeFileCopier<'a, IO> {
io: IO,
root: &'a Path,
destination_dir: &'a Path,
seen_native_files: HashSet<PathBuf>,
to_compile: Vec<PathBuf>,
root: &'a Utf8Path,
destination_dir: &'a Utf8Path,
seen_native_files: HashSet<Utf8PathBuf>,
to_compile: Vec<Utf8PathBuf>,
elixir_files_copied: bool,
}
@ -30,7 +29,7 @@ impl<'a, IO> NativeFileCopier<'a, IO>
where
IO: FileSystemReader + FileSystemWriter + Clone,
{
pub(crate) fn new(io: IO, root: &'a Path, out: &'a Path) -> Self {
pub(crate) fn new(io: IO, root: &'a Utf8Path, out: &'a Utf8Path) -> Self {
Self {
io,
root,
@ -64,7 +63,7 @@ where
})
}
fn copy_files(&mut self, src_root: &Path) -> Result<()> {
fn copy_files(&mut self, src_root: &Utf8Path) -> Result<()> {
let mut check_elixir_libs = true;
for entry in self.io.read_dir(src_root)? {
@ -74,12 +73,8 @@ where
Ok(())
}
fn copy(&mut self, file: PathBuf, src_root: &Path) -> Result<()> {
let extension = file
.extension()
.unwrap_or_default()
.to_str()
.unwrap_or_default();
fn copy(&mut self, file: Utf8PathBuf, src_root: &Utf8Path) -> Result<()> {
let extension = file.extension().unwrap_or_default();
// Skip unknown file formats that are not supported native files
if !matches!(extension, "mjs" | "js" | "ts" | "hrl" | "erl" | "ex") {
@ -119,10 +114,10 @@ where
Ok(())
}
fn check_for_duplicate(&mut self, relative_path: &PathBuf) -> Result<(), Error> {
fn check_for_duplicate(&mut self, relative_path: &Utf8PathBuf) -> Result<(), Error> {
if !self.seen_native_files.insert(relative_path.clone()) {
return Err(Error::DuplicateSourceFile {
file: relative_path.to_string_lossy().to_string(),
file: relative_path.to_string(),
});
}
Ok(())

View file

@ -6,19 +6,20 @@ use crate::{
use lazy_static::lazy_static;
use std::{
collections::HashMap,
path::{Path, PathBuf},
time::{Duration, SystemTime, UNIX_EPOCH},
};
use camino::{Utf8Path, Utf8PathBuf};
lazy_static! {
static ref ROOT: PathBuf = PathBuf::from("/");
static ref OUT: PathBuf = PathBuf::from("/out");
static ref ROOT: Utf8PathBuf = Utf8PathBuf::from("/");
static ref OUT: Utf8PathBuf = Utf8PathBuf::from("/out");
}
#[test]
fn javascript_files_are_copied_from_src() {
let fs = InMemoryFileSystem::new();
fs.write(&Path::new("/src/wibble.js"), "1").unwrap();
fs.write(&Utf8Path::new("/src/wibble.js"), "1").unwrap();
let copier = NativeFileCopier::new(fs.clone(), &ROOT, &OUT);
let copied = copier.run().unwrap();
@ -27,8 +28,8 @@ fn javascript_files_are_copied_from_src() {
assert!(copied.to_compile.is_empty());
assert_eq!(
HashMap::from([
(PathBuf::from("/src/wibble.js"), "1".into()),
(PathBuf::from("/out/wibble.js"), "1".into())
(Utf8PathBuf::from("/src/wibble.js"), "1".into()),
(Utf8PathBuf::from("/out/wibble.js"), "1".into())
]),
fs.into_contents(),
);
@ -37,7 +38,7 @@ fn javascript_files_are_copied_from_src() {
#[test]
fn javascript_files_are_copied_from_test() {
let fs = InMemoryFileSystem::new();
fs.write(&Path::new("/test/wibble.js"), "1").unwrap();
fs.write(&Utf8Path::new("/test/wibble.js"), "1").unwrap();
let copier = NativeFileCopier::new(fs.clone(), &ROOT, &OUT);
let copied = copier.run().unwrap();
@ -46,8 +47,8 @@ fn javascript_files_are_copied_from_test() {
assert!(copied.to_compile.is_empty());
assert_eq!(
HashMap::from([
(PathBuf::from("/test/wibble.js"), "1".into()),
(PathBuf::from("/out/wibble.js"), "1".into())
(Utf8PathBuf::from("/test/wibble.js"), "1".into()),
(Utf8PathBuf::from("/out/wibble.js"), "1".into())
]),
fs.into_contents(),
);
@ -56,7 +57,7 @@ fn javascript_files_are_copied_from_test() {
#[test]
fn mjavascript_files_are_copied_from_src() {
let fs = InMemoryFileSystem::new();
fs.write(&Path::new("/src/wibble.mjs"), "1").unwrap();
fs.write(&Utf8Path::new("/src/wibble.mjs"), "1").unwrap();
let copier = NativeFileCopier::new(fs.clone(), &ROOT, &OUT);
let copied = copier.run().unwrap();
@ -65,8 +66,8 @@ fn mjavascript_files_are_copied_from_src() {
assert!(copied.to_compile.is_empty());
assert_eq!(
HashMap::from([
(PathBuf::from("/src/wibble.mjs"), "1".into()),
(PathBuf::from("/out/wibble.mjs"), "1".into())
(Utf8PathBuf::from("/src/wibble.mjs"), "1".into()),
(Utf8PathBuf::from("/out/wibble.mjs"), "1".into())
]),
fs.into_contents(),
);
@ -75,7 +76,7 @@ fn mjavascript_files_are_copied_from_src() {
#[test]
fn mjavascript_files_are_copied_from_test() {
let fs = InMemoryFileSystem::new();
fs.write(&Path::new("/test/wibble.mjs"), "1").unwrap();
fs.write(&Utf8Path::new("/test/wibble.mjs"), "1").unwrap();
let copier = NativeFileCopier::new(fs.clone(), &ROOT, &OUT);
let copied = copier.run().unwrap();
@ -84,8 +85,8 @@ fn mjavascript_files_are_copied_from_test() {
assert!(copied.to_compile.is_empty());
assert_eq!(
HashMap::from([
(PathBuf::from("/test/wibble.mjs"), "1".into()),
(PathBuf::from("/out/wibble.mjs"), "1".into())
(Utf8PathBuf::from("/test/wibble.mjs"), "1".into()),
(Utf8PathBuf::from("/out/wibble.mjs"), "1".into())
]),
fs.into_contents(),
);
@ -94,7 +95,7 @@ fn mjavascript_files_are_copied_from_test() {
#[test]
fn typescript_files_are_copied_from_src() {
let fs = InMemoryFileSystem::new();
fs.write(&Path::new("/src/wibble.ts"), "1").unwrap();
fs.write(&Utf8Path::new("/src/wibble.ts"), "1").unwrap();
let copier = NativeFileCopier::new(fs.clone(), &ROOT, &OUT);
let copied = copier.run().unwrap();
@ -103,8 +104,8 @@ fn typescript_files_are_copied_from_src() {
assert!(copied.to_compile.is_empty());
assert_eq!(
HashMap::from([
(PathBuf::from("/src/wibble.ts"), "1".into()),
(PathBuf::from("/out/wibble.ts"), "1".into())
(Utf8PathBuf::from("/src/wibble.ts"), "1".into()),
(Utf8PathBuf::from("/out/wibble.ts"), "1".into())
]),
fs.into_contents(),
);
@ -113,7 +114,7 @@ fn typescript_files_are_copied_from_src() {
#[test]
fn typescript_files_are_copied_from_test() {
let fs = InMemoryFileSystem::new();
fs.write(&Path::new("/test/wibble.ts"), "1").unwrap();
fs.write(&Utf8Path::new("/test/wibble.ts"), "1").unwrap();
let copier = NativeFileCopier::new(fs.clone(), &ROOT, &OUT);
let copied = copier.run().unwrap();
@ -122,8 +123,8 @@ fn typescript_files_are_copied_from_test() {
assert!(copied.to_compile.is_empty());
assert_eq!(
HashMap::from([
(PathBuf::from("/test/wibble.ts"), "1".into()),
(PathBuf::from("/out/wibble.ts"), "1".into())
(Utf8PathBuf::from("/test/wibble.ts"), "1".into()),
(Utf8PathBuf::from("/out/wibble.ts"), "1".into())
]),
fs.into_contents(),
);
@ -132,7 +133,7 @@ fn typescript_files_are_copied_from_test() {
#[test]
fn erlang_header_files_are_copied_from_src() {
let fs = InMemoryFileSystem::new();
fs.write(&Path::new("/src/wibble.hrl"), "1").unwrap();
fs.write(&Utf8Path::new("/src/wibble.hrl"), "1").unwrap();
let copier = NativeFileCopier::new(fs.clone(), &ROOT, &OUT);
let copied = copier.run().unwrap();
@ -141,8 +142,8 @@ fn erlang_header_files_are_copied_from_src() {
assert!(copied.to_compile.is_empty());
assert_eq!(
HashMap::from([
(PathBuf::from("/src/wibble.hrl"), "1".into()),
(PathBuf::from("/out/wibble.hrl"), "1".into())
(Utf8PathBuf::from("/src/wibble.hrl"), "1".into()),
(Utf8PathBuf::from("/out/wibble.hrl"), "1".into())
]),
fs.into_contents(),
);
@ -151,7 +152,7 @@ fn erlang_header_files_are_copied_from_src() {
#[test]
fn erlang_header_files_are_copied_from_test() {
let fs = InMemoryFileSystem::new();
fs.write(&Path::new("/test/wibble.hrl"), "1").unwrap();
fs.write(&Utf8Path::new("/test/wibble.hrl"), "1").unwrap();
let copier = NativeFileCopier::new(fs.clone(), &ROOT, &OUT);
let copied = copier.run().unwrap();
@ -160,8 +161,8 @@ fn erlang_header_files_are_copied_from_test() {
assert!(copied.to_compile.is_empty());
assert_eq!(
HashMap::from([
(PathBuf::from("/test/wibble.hrl"), "1".into()),
(PathBuf::from("/out/wibble.hrl"), "1".into())
(Utf8PathBuf::from("/test/wibble.hrl"), "1".into()),
(Utf8PathBuf::from("/out/wibble.hrl"), "1".into())
]),
fs.into_contents(),
);
@ -170,17 +171,17 @@ fn erlang_header_files_are_copied_from_test() {
#[test]
fn erlang_files_are_copied_from_src() {
let fs = InMemoryFileSystem::new();
fs.write(&Path::new("/src/wibble.erl"), "1").unwrap();
fs.write(&Utf8Path::new("/src/wibble.erl"), "1").unwrap();
let copier = NativeFileCopier::new(fs.clone(), &ROOT, &OUT);
let copied = copier.run().unwrap();
assert!(!copied.any_elixir);
assert_eq!(copied.to_compile, vec![PathBuf::from("wibble.erl")]);
assert_eq!(copied.to_compile, vec![Utf8PathBuf::from("wibble.erl")]);
assert_eq!(
HashMap::from([
(PathBuf::from("/src/wibble.erl"), "1".into()),
(PathBuf::from("/out/wibble.erl"), "1".into())
(Utf8PathBuf::from("/src/wibble.erl"), "1".into()),
(Utf8PathBuf::from("/out/wibble.erl"), "1".into())
]),
fs.into_contents(),
);
@ -189,17 +190,17 @@ fn erlang_files_are_copied_from_src() {
#[test]
fn erlang_files_are_copied_from_test() {
let fs = InMemoryFileSystem::new();
fs.write(&Path::new("/test/wibble.erl"), "1").unwrap();
fs.write(&Utf8Path::new("/test/wibble.erl"), "1").unwrap();
let copier = NativeFileCopier::new(fs.clone(), &ROOT, &OUT);
let copied = copier.run().unwrap();
assert!(!copied.any_elixir);
assert_eq!(copied.to_compile, vec![PathBuf::from("wibble.erl")]);
assert_eq!(copied.to_compile, vec![Utf8PathBuf::from("wibble.erl")]);
assert_eq!(
HashMap::from([
(PathBuf::from("/test/wibble.erl"), "1".into()),
(PathBuf::from("/out/wibble.erl"), "1".into())
(Utf8PathBuf::from("/test/wibble.erl"), "1".into()),
(Utf8PathBuf::from("/out/wibble.erl"), "1".into())
]),
fs.into_contents(),
);
@ -208,17 +209,17 @@ fn erlang_files_are_copied_from_test() {
#[test]
fn elixir_files_are_copied_from_src() {
let fs = InMemoryFileSystem::new();
fs.write(&Path::new("/src/wibble.ex"), "1").unwrap();
fs.write(&Utf8Path::new("/src/wibble.ex"), "1").unwrap();
let copier = NativeFileCopier::new(fs.clone(), &ROOT, &OUT);
let copied = copier.run().unwrap();
assert!(copied.any_elixir);
assert_eq!(copied.to_compile, vec![PathBuf::from("wibble.ex")]);
assert_eq!(copied.to_compile, vec![Utf8PathBuf::from("wibble.ex")]);
assert_eq!(
HashMap::from([
(PathBuf::from("/src/wibble.ex"), "1".into()),
(PathBuf::from("/out/wibble.ex"), "1".into())
(Utf8PathBuf::from("/src/wibble.ex"), "1".into()),
(Utf8PathBuf::from("/out/wibble.ex"), "1".into())
]),
fs.into_contents(),
);
@ -227,17 +228,17 @@ fn elixir_files_are_copied_from_src() {
#[test]
fn elixir_files_are_copied_from_test() {
let fs = InMemoryFileSystem::new();
fs.write(&Path::new("/test/wibble.ex"), "1").unwrap();
fs.write(&Utf8Path::new("/test/wibble.ex"), "1").unwrap();
let copier = NativeFileCopier::new(fs.clone(), &ROOT, &OUT);
let copied = copier.run().unwrap();
assert!(copied.any_elixir);
assert_eq!(copied.to_compile, vec![PathBuf::from("wibble.ex")]);
assert_eq!(copied.to_compile, vec![Utf8PathBuf::from("wibble.ex")]);
assert_eq!(
HashMap::from([
(PathBuf::from("/test/wibble.ex"), "1".into()),
(PathBuf::from("/out/wibble.ex"), "1".into())
(Utf8PathBuf::from("/test/wibble.ex"), "1".into()),
(Utf8PathBuf::from("/out/wibble.ex"), "1".into())
]),
fs.into_contents(),
);
@ -246,7 +247,7 @@ fn elixir_files_are_copied_from_test() {
#[test]
fn other_files_are_ignored() {
let fs = InMemoryFileSystem::new();
fs.write(&Path::new("/src/wibble.cpp"), "1").unwrap();
fs.write(&Utf8Path::new("/src/wibble.cpp"), "1").unwrap();
let copier = NativeFileCopier::new(fs.clone(), &ROOT, &OUT);
let copied = copier.run().unwrap();
@ -254,7 +255,7 @@ fn other_files_are_ignored() {
assert!(!copied.any_elixir);
assert!(copied.to_compile.is_empty());
assert_eq!(
HashMap::from([(PathBuf::from("/src/wibble.cpp"), "1".into())]),
HashMap::from([(Utf8PathBuf::from("/src/wibble.cpp"), "1".into())]),
fs.into_contents(),
);
}
@ -262,8 +263,8 @@ fn other_files_are_ignored() {
#[test]
fn files_do_not_get_copied_if_there_already_is_a_new_version() {
let fs = InMemoryFileSystem::new();
let out = Path::new("/out/wibble.mjs");
let src = Path::new("/src/wibble.mjs");
let out = Utf8Path::new("/out/wibble.mjs");
let src = Utf8Path::new("/src/wibble.mjs");
fs.write(&out, "in-out").unwrap();
fs.write(&src, "in-src").unwrap();
fs.set_modification_time(&out, UNIX_EPOCH + Duration::from_secs(1));
@ -276,8 +277,8 @@ fn files_do_not_get_copied_if_there_already_is_a_new_version() {
assert!(copied.to_compile.is_empty());
assert_eq!(
HashMap::from([
(PathBuf::from("/src/wibble.mjs"), "in-src".into()),
(PathBuf::from("/out/wibble.mjs"), "in-out".into())
(Utf8PathBuf::from("/src/wibble.mjs"), "in-src".into()),
(Utf8PathBuf::from("/out/wibble.mjs"), "in-out".into())
]),
fs.into_contents(),
);
@ -286,8 +287,8 @@ fn files_do_not_get_copied_if_there_already_is_a_new_version() {
#[test]
fn files_get_copied_if_the_previously_copied_vesion_is_older() {
let fs = InMemoryFileSystem::new();
let out = Path::new("/out/wibble.mjs");
let src = Path::new("/src/wibble.mjs");
let out = Utf8Path::new("/out/wibble.mjs");
let src = Utf8Path::new("/src/wibble.mjs");
fs.write(&out, "in-out").unwrap();
fs.write(&src, "in-src").unwrap();
fs.set_modification_time(&out, UNIX_EPOCH);
@ -300,8 +301,8 @@ fn files_get_copied_if_the_previously_copied_vesion_is_older() {
assert!(copied.to_compile.is_empty());
assert_eq!(
HashMap::from([
(PathBuf::from("/src/wibble.mjs"), "in-src".into()),
(PathBuf::from("/out/wibble.mjs"), "in-src".into())
(Utf8PathBuf::from("/src/wibble.mjs"), "in-src".into()),
(Utf8PathBuf::from("/out/wibble.mjs"), "in-src".into())
]),
fs.into_contents(),
);
@ -310,8 +311,8 @@ fn files_get_copied_if_the_previously_copied_vesion_is_older() {
#[test]
fn duplicate_native_files_result_in_an_error() {
let fs = InMemoryFileSystem::new();
fs.write(&Path::new("/src/wibble.mjs"), "1").unwrap();
fs.write(&Path::new("/test/wibble.mjs"), "1").unwrap();
fs.write(&Utf8Path::new("/src/wibble.mjs"), "1").unwrap();
fs.write(&Utf8Path::new("/test/wibble.mjs"), "1").unwrap();
let copier = NativeFileCopier::new(fs.clone(), &ROOT, &OUT);
assert!(copier.run().is_err());

View file

@ -20,11 +20,10 @@ use crate::{
};
use askama::Template;
use smol_str::SmolStr;
use std::collections::HashSet;
use std::{collections::HashMap, fmt::write, time::SystemTime};
use std::{
collections::HashSet,
path::{Path, PathBuf},
};
use camino::{Utf8Path, Utf8PathBuf};
use super::{ErlangAppCodegenConfiguration, TargetCodegenConfiguration};
@ -36,9 +35,9 @@ const ELIXIR_EXECUTABLE: &str = "elixir.bat";
#[derive(Debug)]
pub struct PackageCompiler<'a, IO> {
pub io: IO,
pub out: &'a Path,
pub lib: &'a Path,
pub root: &'a Path,
pub out: &'a Utf8Path,
pub lib: &'a Utf8Path,
pub root: &'a Utf8Path,
pub mode: Mode,
pub target: &'a TargetCodegenConfiguration,
pub config: &'a PackageConfig,
@ -58,9 +57,9 @@ where
pub fn new(
config: &'a PackageConfig,
mode: Mode,
root: &'a Path,
out: &'a Path,
lib: &'a Path,
root: &'a Utf8Path,
out: &'a Utf8Path,
lib: &'a Utf8Path,
target: &'a TargetCodegenConfiguration,
ids: UniqueIdGenerator,
io: IO,
@ -91,7 +90,7 @@ where
mut self,
warnings: &WarningEmitter,
existing_modules: &mut im::HashMap<SmolStr, type_::ModuleInterface>,
already_defined_modules: &mut im::HashMap<SmolStr, PathBuf>,
already_defined_modules: &mut im::HashMap<SmolStr, Utf8PathBuf>,
stale_modules: &mut StaleTracker,
) -> Result<Vec<Module>, Error> {
let span = tracing::info_span!("compile", package = %self.config.name.as_str());
@ -144,7 +143,7 @@ where
Ok(modules)
}
fn compile_erlang_to_beam(&mut self, modules: &HashSet<PathBuf>) -> Result<(), Error> {
fn compile_erlang_to_beam(&mut self, modules: &HashSet<Utf8PathBuf>) -> Result<(), Error> {
if modules.is_empty() {
tracing::debug!("no_erlang_to_compile");
return Ok(());
@ -162,18 +161,18 @@ where
}
let mut args = vec![
escript_path.to_string_lossy().to_string(),
escript_path.to_string(),
// Tell the compiler where to find other libraries
"--lib".into(),
self.lib.to_string_lossy().to_string(),
self.lib.to_string(),
// Write compiled .beam to ./ebin
"--out".into(),
self.out.join("ebin").to_string_lossy().to_string(),
self.out.join("ebin").to_string(),
];
// Add the list of modules to compile
for module in modules {
let path = self.out.join(paths::ARTEFACT_DIRECTORY_NAME).join(module);
args.push(path.to_string_lossy().to_string());
args.push(path.to_string());
}
// Compile Erlang and Elixir modules
let status = self
@ -192,8 +191,8 @@ where
fn copy_project_native_files(
&mut self,
destination_dir: &Path,
to_compile_modules: &mut HashSet<PathBuf>,
destination_dir: &Utf8Path,
to_compile_modules: &mut HashSet<Utf8PathBuf>,
) -> Result<(), Error> {
tracing::debug!("copying_native_source_files");
@ -342,8 +341,8 @@ where
fn render_erlang_entrypoint_module(
&mut self,
out: &Path,
modules_to_compile: &mut HashSet<PathBuf>,
out: &Utf8Path,
modules_to_compile: &mut HashSet<Utf8PathBuf>,
) -> Result<(), Error> {
let name = format!("{name}@@main.erl", name = self.config.name);
let path = out.join(&name);
@ -435,7 +434,7 @@ fn analyse(
pub fn maybe_link_elixir_libs<IO>(
io: &IO,
build_dir: &PathBuf,
build_dir: &Utf8PathBuf,
subprocess_stdio: Stdio,
) -> Result<(), Error>
where
@ -490,7 +489,7 @@ where
// Each pathfinder line is a system path for an Elixir core library
let read_pathfinder = io.read(&pathfinder)?;
for lib_path in read_pathfinder.split('\n') {
let source = PathBuf::from(lib_path);
let source = Utf8PathBuf::from(lib_path);
let name = source
.as_path()
.file_name()
@ -507,17 +506,14 @@ where
// Delete the existing link
io.delete(&dest)?;
}
tracing::debug!(
"linking_{}_to_build",
name.to_str().unwrap_or("elixir_core_lib"),
);
tracing::debug!("linking_{}_to_build", name,);
io.symlink_dir(&source, &dest)?;
}
Ok(())
}
pub(crate) fn module_name(package_path: &Path, full_module_path: &Path) -> SmolStr {
pub(crate) fn module_name(package_path: &Utf8Path, full_module_path: &Utf8Path) -> SmolStr {
// /path/to/project/_build/default/lib/the_package/src/my/module.gleam
// my/module.gleam
@ -530,10 +526,7 @@ pub(crate) fn module_name(package_path: &Path, full_module_path: &Path) -> SmolS
let _ = module_path.set_extension("");
// Stringify
let name = module_path
.to_str()
.expect("Module name path to str")
.to_string();
let name = module_path.to_string();
// normalise windows paths
name.replace("\\", "/").into()
@ -553,7 +546,7 @@ impl Input {
}
}
pub fn source_path(&self) -> &Path {
pub fn source_path(&self) -> &Utf8Path {
match self {
Input::New(m) => &m.path,
Input::Cached(m) => &m.source_path,
@ -589,7 +582,7 @@ pub(crate) struct CachedModule {
pub name: SmolStr,
pub origin: Origin,
pub dependencies: Vec<SmolStr>,
pub source_path: PathBuf,
pub source_path: Utf8PathBuf,
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]
@ -618,7 +611,7 @@ pub(crate) struct Loaded {
#[derive(Debug, PartialEq, Eq)]
pub(crate) struct UncompiledModule {
pub path: PathBuf,
pub path: Utf8PathBuf,
pub name: SmolStr,
pub code: SmolStr,
pub mtime: SystemTime,

View file

@ -3,10 +3,11 @@ mod tests;
use std::{
collections::{HashMap, HashSet},
path::{Path, PathBuf},
time::{Duration, SystemTime},
};
use camino::{Utf8Path, Utf8PathBuf};
// TODO: emit warnings for cached modules even if they are not compiled again.
use itertools::Itertools;
@ -52,14 +53,14 @@ pub struct PackageLoader<'a, IO> {
io: IO,
ids: UniqueIdGenerator,
mode: Mode,
root: &'a Path,
root: &'a Utf8Path,
warnings: &'a WarningEmitter,
codegen: CodegenRequired,
artefact_directory: &'a Path,
artefact_directory: &'a Utf8Path,
package_name: &'a SmolStr,
target: Target,
stale_modules: &'a mut StaleTracker,
already_defined_modules: &'a mut im::HashMap<SmolStr, PathBuf>,
already_defined_modules: &'a mut im::HashMap<SmolStr, Utf8PathBuf>,
}
impl<'a, IO> PackageLoader<'a, IO>
@ -70,14 +71,14 @@ where
io: IO,
ids: UniqueIdGenerator,
mode: Mode,
root: &'a Path,
root: &'a Utf8Path,
warnings: &'a WarningEmitter,
codegen: CodegenRequired,
artefact_directory: &'a Path,
artefact_directory: &'a Utf8Path,
target: Target,
package_name: &'a SmolStr,
stale_modules: &'a mut StaleTracker,
already_defined_modules: &'a mut im::HashMap<SmolStr, PathBuf>,
already_defined_modules: &'a mut im::HashMap<SmolStr, Utf8PathBuf>,
) -> Self {
Self {
io,
@ -156,7 +157,7 @@ where
metadata::ModuleDecoder::new(self.ids.clone()).read(bytes.as_slice())
}
pub fn is_gleam_path(&self, path: &Path, dir: &Path) -> bool {
pub fn is_gleam_path(&self, path: &Utf8Path, dir: &Utf8Path) -> bool {
use regex::Regex;
lazy_static! {
static ref RE: Regex = Regex::new(&format!(
@ -170,8 +171,7 @@ where
RE.is_match(
path.strip_prefix(dir)
.expect("is_gleam_path(): strip_prefix")
.to_str()
.expect("is_gleam_path(): to_str"),
.as_str(),
)
}
@ -260,11 +260,11 @@ impl StaleTracker {
#[derive(Debug)]
pub struct Inputs<'a> {
collection: HashMap<SmolStr, Input>,
already_defined_modules: &'a im::HashMap<SmolStr, PathBuf>,
already_defined_modules: &'a im::HashMap<SmolStr, Utf8PathBuf>,
}
impl<'a> Inputs<'a> {
fn new(already_defined_modules: &'a im::HashMap<SmolStr, PathBuf>) -> Self {
fn new(already_defined_modules: &'a im::HashMap<SmolStr, Utf8PathBuf>) -> Self {
Self {
collection: Default::default(),
already_defined_modules,

View file

@ -22,7 +22,7 @@ const TEST_SOURCE_1: &'static str = "const x = 1";
const TEST_SOURCE_2: &'static str = "const x = 2";
fn write_src(fs: &InMemoryFileSystem, path: &str, seconds: u64, src: &str) {
let path = Path::new(path);
let path = Utf8Path::new(path);
fs.write(&path, src).unwrap();
fs.set_modification_time(&path, SystemTime::UNIX_EPOCH + Duration::from_secs(seconds));
}
@ -35,7 +35,7 @@ fn write_cache(fs: &InMemoryFileSystem, name: &str, seconds: u64, deps: Vec<Smol
dependencies: deps,
fingerprint: SourceFingerprint::new(src),
};
let path = Path::new("/artefact").join(format!("{name}.cache_meta"));
let path = Utf8Path::new("/artefact").join(format!("{name}.cache_meta"));
fs.write_bytes(&path, &cache_metadata.to_binary()).unwrap();
let cache = crate::type_::ModuleInterface {
@ -47,7 +47,7 @@ fn write_cache(fs: &InMemoryFileSystem, name: &str, seconds: u64, deps: Vec<Smol
values: Default::default(),
accessors: Default::default(),
};
let path = Path::new("/artefact").join(format!("{name}.cache"));
let path = Utf8Path::new("/artefact").join(format!("{name}.cache"));
fs.write_bytes(
&path,
&metadata::ModuleEncoder::new(&cache).encode().unwrap(),
@ -55,7 +55,7 @@ fn write_cache(fs: &InMemoryFileSystem, name: &str, seconds: u64, deps: Vec<Smol
.unwrap();
}
fn run_loader(fs: InMemoryFileSystem, root: &Path, artefact: &Path) -> LoaderTestOutput {
fn run_loader(fs: InMemoryFileSystem, root: &Utf8Path, artefact: &Utf8Path) -> LoaderTestOutput {
let mut defined = im::HashMap::new();
let ids = UniqueIdGenerator::new();
let (emitter, warnings) = WarningEmitter::vector();
@ -85,8 +85,8 @@ fn run_loader(fs: InMemoryFileSystem, root: &Path, artefact: &Path) -> LoaderTes
#[test]
fn no_modules() {
let fs = InMemoryFileSystem::new();
let root = Path::new("/");
let artefact = Path::new("/artefact");
let root = Utf8Path::new("/");
let artefact = Utf8Path::new("/artefact");
let loaded = run_loader(fs, root, artefact);
assert!(loaded.to_compile.is_empty());
@ -96,8 +96,8 @@ fn no_modules() {
#[test]
fn one_src_module() {
let fs = InMemoryFileSystem::new();
let root = Path::new("/");
let artefact = Path::new("/artefact");
let root = Utf8Path::new("/");
let artefact = Utf8Path::new("/artefact");
write_src(&fs, "/src/main.gleam", 0, "const x = 1");
@ -109,8 +109,8 @@ fn one_src_module() {
#[test]
fn one_test_module() {
let fs = InMemoryFileSystem::new();
let root = Path::new("/");
let artefact = Path::new("/artefact");
let root = Utf8Path::new("/");
let artefact = Utf8Path::new("/artefact");
write_src(&fs, "/test/main.gleam", 0, "const x = 1");
@ -122,8 +122,8 @@ fn one_test_module() {
#[test]
fn importing() {
let fs = InMemoryFileSystem::new();
let root = Path::new("/");
let artefact = Path::new("/artefact");
let root = Utf8Path::new("/");
let artefact = Utf8Path::new("/artefact");
write_src(&fs, "/src/three.gleam", 0, "import two");
write_src(&fs, "/src/one.gleam", 0, "");
@ -144,8 +144,8 @@ fn importing() {
#[test]
fn reading_cache() {
let fs = InMemoryFileSystem::new();
let root = Path::new("/");
let artefact = Path::new("/artefact");
let root = Utf8Path::new("/");
let artefact = Utf8Path::new("/artefact");
write_src(&fs, "/src/one.gleam", 0, TEST_SOURCE_1);
write_cache(&fs, "one", 0, vec![], TEST_SOURCE_1);
@ -158,8 +158,8 @@ fn reading_cache() {
#[test]
fn module_is_stale_if_cache_older() {
let fs = InMemoryFileSystem::new();
let root = Path::new("/");
let artefact = Path::new("/artefact");
let root = Utf8Path::new("/");
let artefact = Utf8Path::new("/artefact");
write_src(&fs, "/src/one.gleam", 1, TEST_SOURCE_2);
write_cache(&fs, "one", 0, vec![], TEST_SOURCE_1);
@ -172,8 +172,8 @@ fn module_is_stale_if_cache_older() {
#[test]
fn module_is_stale_if_deps_are_stale() {
let fs = InMemoryFileSystem::new();
let root = Path::new("/");
let artefact = Path::new("/artefact");
let root = Utf8Path::new("/");
let artefact = Utf8Path::new("/artefact");
// Cache is stale
write_src(&fs, "/src/one.gleam", 1, TEST_SOURCE_2);
@ -198,8 +198,8 @@ fn module_is_stale_if_deps_are_stale() {
#[test]
fn invalid_module_name() {
let fs = InMemoryFileSystem::new();
let root = Path::new("/");
let artefact = Path::new("/artefact");
let root = Utf8Path::new("/");
let artefact = Utf8Path::new("/artefact");
// Cache is stale
write_src(&fs, "/src/One.gleam", 1, TEST_SOURCE_2);
@ -210,7 +210,7 @@ fn invalid_module_name() {
assert_eq!(
loaded.warnings,
vec![Warning::InvalidSource {
path: PathBuf::from("/src/One.gleam"),
path: Utf8PathBuf::from("/src/One.gleam"),
}],
);
}
@ -218,8 +218,8 @@ fn invalid_module_name() {
#[test]
fn invalid_nested_module_name() {
let fs = InMemoryFileSystem::new();
let root = Path::new("/");
let artefact = Path::new("/artefact");
let root = Utf8Path::new("/");
let artefact = Utf8Path::new("/artefact");
// Cache is stale
write_src(&fs, "/src/1/one.gleam", 1, TEST_SOURCE_2);
@ -230,7 +230,7 @@ fn invalid_nested_module_name() {
assert_eq!(
loaded.warnings,
vec![Warning::InvalidSource {
path: PathBuf::from("/src/1/one.gleam"),
path: Utf8PathBuf::from("/src/1/one.gleam"),
}],
);
}

View file

@ -23,13 +23,14 @@ use std::{
collections::{HashMap, HashSet},
fmt::Write,
io::BufReader,
path::{Path, PathBuf},
sync::Arc,
time::Instant,
};
use super::{Codegen, ErlangAppCodegenConfiguration};
use camino::{Utf8Path, Utf8PathBuf};
// On Windows we have to call rebar3 via a little wrapper script.
//
#[cfg(not(target_os = "windows"))]
@ -75,7 +76,7 @@ pub struct ProjectCompiler<IO> {
pub(crate) config: PackageConfig,
pub(crate) packages: HashMap<String, ManifestPackage>,
importable_modules: im::HashMap<SmolStr, type_::ModuleInterface>,
defined_modules: im::HashMap<SmolStr, PathBuf>,
defined_modules: im::HashMap<SmolStr, Utf8PathBuf>,
stale_modules: StaleTracker,
warnings: WarningEmitter,
telemetry: Box<dyn Telemetry>,
@ -281,7 +282,7 @@ where
let package = self.paths.build_packages_package(name);
let build_packages = self.paths.build_directory_for_target(mode, target);
let ebins = self.paths.build_packages_ebins_glob(mode, target);
let rebar3_path = |path: &Path| format!("../{}", path.to_str().expect("Build path"));
let rebar3_path = |path: &Utf8Path| format!("../{}", path);
tracing::debug!("copying_package_to_build");
self.io.mkdir(&build_packages)?;
@ -348,7 +349,7 @@ where
let mix_build_dir = project_dir.join("_build").join(mix_target);
let mix_build_lib_dir = mix_build_dir.join("lib");
let up = paths::unnest(&project_dir);
let mix_path = |path: &Path| up.join(path).to_str().unwrap_or_default().to_string();
let mix_path = |path: &Utf8Path| up.join(path).to_string();
let ebins = self.paths.build_packages_ebins_glob(mode, target);
// Elixir core libs must be loaded
@ -439,7 +440,7 @@ where
fn load_cached_package(
&mut self,
build_dir: PathBuf,
build_dir: Utf8PathBuf,
package: &ManifestPackage,
) -> Result<(), Error> {
for path in self.io.gleam_cache_files(&build_dir) {
@ -458,7 +459,7 @@ where
&mut self,
config: &PackageConfig,
is_root: bool,
root_path: PathBuf,
root_path: Utf8PathBuf,
) -> Result<Vec<Module>, Error> {
let out_path =
self.paths

View file

@ -3,18 +3,20 @@ use crate::{
line_numbers::LineNumbers, Result,
};
use itertools::Itertools;
use std::{fmt::Debug, path::Path};
use std::fmt::Debug;
use camino::Utf8Path;
/// A code generator that creates a .erl Erlang module and record header files
/// for each Gleam module in the package.
#[derive(Debug)]
pub struct Erlang<'a> {
build_directory: &'a Path,
include_directory: &'a Path,
build_directory: &'a Utf8Path,
include_directory: &'a Utf8Path,
}
impl<'a> Erlang<'a> {
pub fn new(build_directory: &'a Path, include_directory: &'a Path) -> Self {
pub fn new(build_directory: &'a Utf8Path, include_directory: &'a Utf8Path) -> Self {
Self {
build_directory,
include_directory,
@ -66,12 +68,12 @@ impl<'a> Erlang<'a> {
/// A code generator that creates a .app Erlang application file for the package
#[derive(Debug)]
pub struct ErlangApp<'a> {
output_directory: &'a Path,
output_directory: &'a Utf8Path,
include_dev_deps: bool,
}
impl<'a> ErlangApp<'a> {
pub fn new(output_directory: &'a Path, include_dev_deps: bool) -> Self {
pub fn new(output_directory: &'a Utf8Path, include_dev_deps: bool) -> Self {
Self {
output_directory,
include_dev_deps,
@ -147,12 +149,12 @@ pub enum TypeScriptDeclarations {
#[derive(Debug)]
pub struct JavaScript<'a> {
output_directory: &'a Path,
output_directory: &'a Utf8Path,
typescript: TypeScriptDeclarations,
}
impl<'a> JavaScript<'a> {
pub fn new(output_directory: &'a Path, typescript: TypeScriptDeclarations) -> Self {
pub fn new(output_directory: &'a Utf8Path, typescript: TypeScriptDeclarations) -> Self {
Self {
output_directory,
typescript,

View file

@ -4,6 +4,7 @@ use crate::manifest::Manifest;
use crate::requirement::Requirement;
use crate::version::COMPILER_VERSION;
use crate::{Error, Result};
use camino::{Utf8Path, Utf8PathBuf};
use globset::{Glob, GlobSetBuilder};
use hexpm::version::Version;
use http::Uri;
@ -12,7 +13,6 @@ use smol_str::SmolStr;
use std::collections::{HashMap, HashSet};
use std::fmt;
use std::marker::PhantomData;
use std::path::{Path, PathBuf};
#[cfg(test)]
use crate::manifest::ManifestPackage;
@ -119,7 +119,7 @@ impl PackageConfig {
Ok(deps)
}
pub fn read<FS: FileSystemReader, P: AsRef<Path>>(
pub fn read<FS: FileSystemReader, P: AsRef<Utf8Path>>(
path: P,
fs: &FS,
) -> Result<PackageConfig, Error> {
@ -750,7 +750,7 @@ pub struct Docs {
pub struct DocsPage {
pub title: String,
pub path: String,
pub source: PathBuf,
pub source: Utf8PathBuf,
}
#[derive(Deserialize, Debug, PartialEq, Eq, Clone)]

View file

@ -1,4 +1,4 @@
use std::path::PathBuf;
use camino::Utf8PathBuf;
pub use codespan_reporting::diagnostic::{LabelStyle, Severity};
use codespan_reporting::{diagnostic::Label as CodespanLabel, files::SimpleFile};
@ -22,7 +22,7 @@ pub struct Label {
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Location {
pub src: SmolStr,
pub path: PathBuf,
pub path: Utf8PathBuf,
pub label: Label,
pub extra_labels: Vec<Label>,
}
@ -61,10 +61,7 @@ impl Diagnostic {
}
fn write_span(&self, location: &Location, buffer: &mut Buffer) {
let file = SimpleFile::new(
location.path.to_string_lossy().to_string(),
location.src.as_str(),
);
let file = SimpleFile::new(location.path.to_string(), location.src.as_str());
let labels = location
.labels()
.map(|l| {

View file

@ -1,6 +1,8 @@
mod source_links;
use std::{path::PathBuf, time::SystemTime};
use std::time::SystemTime;
use camino::Utf8PathBuf;
use crate::{
ast::{
@ -112,7 +114,7 @@ pub fn generate_html(
};
files.push(OutputFile {
path: PathBuf::from(&page.path),
path: Utf8PathBuf::from(&page.path),
content: Content::Text(temp.render().expect("Page template rendering")),
});
@ -245,7 +247,7 @@ pub fn generate_html(
};
files.push(OutputFile {
path: PathBuf::from(format!("{}.html", module.name)),
path: Utf8PathBuf::from(format!("{}.html", module.name)),
content: Content::Text(
template
.render()
@ -257,63 +259,63 @@ pub fn generate_html(
// Render static assets
files.push(OutputFile {
path: PathBuf::from("css/atom-one-light.min.css"),
path: Utf8PathBuf::from("css/atom-one-light.min.css"),
content: Content::Text(
std::include_str!("../templates/docs-css/atom-one-light.min.css").to_string(),
),
});
files.push(OutputFile {
path: PathBuf::from("css/atom-one-dark.min.css"),
path: Utf8PathBuf::from("css/atom-one-dark.min.css"),
content: Content::Text(
std::include_str!("../templates/docs-css/atom-one-dark.min.css").to_string(),
),
});
files.push(OutputFile {
path: PathBuf::from("css/index.css"),
path: Utf8PathBuf::from("css/index.css"),
content: Content::Text(std::include_str!("../templates/docs-css/index.css").to_string()),
});
// highlightjs:
files.push(OutputFile {
path: PathBuf::from("js/highlight.min.js"),
path: Utf8PathBuf::from("js/highlight.min.js"),
content: Content::Text(
std::include_str!("../templates/docs-js/highlight.min.js").to_string(),
),
});
files.push(OutputFile {
path: PathBuf::from("js/highlightjs-gleam.js"),
path: Utf8PathBuf::from("js/highlightjs-gleam.js"),
content: Content::Text(
std::include_str!("../templates/docs-js/highlightjs-gleam.js").to_string(),
),
});
files.push(OutputFile {
path: PathBuf::from("js/highlightjs-erlang.min.js"),
path: Utf8PathBuf::from("js/highlightjs-erlang.min.js"),
content: Content::Text(
std::include_str!("../templates/docs-js/highlightjs-erlang.min.js").to_string(),
),
});
files.push(OutputFile {
path: PathBuf::from("js/highlightjs-elixir.min.js"),
path: Utf8PathBuf::from("js/highlightjs-elixir.min.js"),
content: Content::Text(
std::include_str!("../templates/docs-js/highlightjs-elixir.min.js").to_string(),
),
});
files.push(OutputFile {
path: PathBuf::from("js/highlightjs-javascript.min.js"),
path: Utf8PathBuf::from("js/highlightjs-javascript.min.js"),
content: Content::Text(
std::include_str!("../templates/docs-js/highlightjs-javascript.min.js").to_string(),
),
});
files.push(OutputFile {
path: PathBuf::from("js/highlightjs-typescript.min.js"),
path: Utf8PathBuf::from("js/highlightjs-typescript.min.js"),
content: Content::Text(
std::include_str!("../templates/docs-js/highlightjs-typescript.min.js").to_string(),
),
@ -322,12 +324,12 @@ pub fn generate_html(
// lunr.min.js, search-data.js and index.js:
files.push(OutputFile {
path: PathBuf::from("js/lunr.min.js"),
path: Utf8PathBuf::from("js/lunr.min.js"),
content: Content::Text(std::include_str!("../templates/docs-js/lunr.min.js").to_string()),
});
files.push(OutputFile {
path: PathBuf::from("search-data.js"),
path: Utf8PathBuf::from("search-data.js"),
content: Content::Text(format!(
"window.Gleam.initSearch({});",
serde_to_string(&escape_html_contents(search_indexes))
@ -336,42 +338,42 @@ pub fn generate_html(
});
files.push(OutputFile {
path: PathBuf::from("js/index.js"),
path: Utf8PathBuf::from("js/index.js"),
content: Content::Text(std::include_str!("../templates/docs-js/index.js").to_string()),
});
// web fonts:
files.push(OutputFile {
path: PathBuf::from("fonts/karla-v23-regular-latin-ext.woff2"),
path: Utf8PathBuf::from("fonts/karla-v23-regular-latin-ext.woff2"),
content: Content::Binary(
include_bytes!("../templates/docs-fonts/karla-v23-regular-latin-ext.woff2").to_vec(),
),
});
files.push(OutputFile {
path: PathBuf::from("fonts/karla-v23-regular-latin.woff2"),
path: Utf8PathBuf::from("fonts/karla-v23-regular-latin.woff2"),
content: Content::Binary(
include_bytes!("../templates/docs-fonts/karla-v23-regular-latin.woff2").to_vec(),
),
});
files.push(OutputFile {
path: PathBuf::from("fonts/karla-v23-bold-latin-ext.woff2"),
path: Utf8PathBuf::from("fonts/karla-v23-bold-latin-ext.woff2"),
content: Content::Binary(
include_bytes!("../templates/docs-fonts/karla-v23-bold-latin-ext.woff2").to_vec(),
),
});
files.push(OutputFile {
path: PathBuf::from("fonts/karla-v23-bold-latin.woff2"),
path: Utf8PathBuf::from("fonts/karla-v23-bold-latin.woff2"),
content: Content::Binary(
include_bytes!("../templates/docs-fonts/karla-v23-bold-latin.woff2").to_vec(),
),
});
files.push(OutputFile {
path: PathBuf::from("fonts/ubuntu-mono-v15-regular-cyrillic-ext.woff2"),
path: Utf8PathBuf::from("fonts/ubuntu-mono-v15-regular-cyrillic-ext.woff2"),
content: Content::Binary(
include_bytes!("../templates/docs-fonts/ubuntu-mono-v15-regular-cyrillic-ext.woff2")
.to_vec(),
@ -379,7 +381,7 @@ pub fn generate_html(
});
files.push(OutputFile {
path: PathBuf::from("fonts/ubuntu-mono-v15-regular-cyrillic.woff2"),
path: Utf8PathBuf::from("fonts/ubuntu-mono-v15-regular-cyrillic.woff2"),
content: Content::Binary(
include_bytes!("../templates/docs-fonts/ubuntu-mono-v15-regular-cyrillic.woff2")
.to_vec(),
@ -387,7 +389,7 @@ pub fn generate_html(
});
files.push(OutputFile {
path: PathBuf::from("fonts/ubuntu-mono-v15-regular-greek-ext.woff2"),
path: Utf8PathBuf::from("fonts/ubuntu-mono-v15-regular-greek-ext.woff2"),
content: Content::Binary(
include_bytes!("../templates/docs-fonts/ubuntu-mono-v15-regular-greek-ext.woff2")
.to_vec(),
@ -395,14 +397,14 @@ pub fn generate_html(
});
files.push(OutputFile {
path: PathBuf::from("fonts/ubuntu-mono-v15-regular-greek.woff2"),
path: Utf8PathBuf::from("fonts/ubuntu-mono-v15-regular-greek.woff2"),
content: Content::Binary(
include_bytes!("../templates/docs-fonts/ubuntu-mono-v15-regular-greek.woff2").to_vec(),
),
});
files.push(OutputFile {
path: PathBuf::from("fonts/ubuntu-mono-v15-regular-latin-ext.woff2"),
path: Utf8PathBuf::from("fonts/ubuntu-mono-v15-regular-latin-ext.woff2"),
content: Content::Binary(
include_bytes!("../templates/docs-fonts/ubuntu-mono-v15-regular-latin-ext.woff2")
.to_vec(),
@ -410,7 +412,7 @@ pub fn generate_html(
});
files.push(OutputFile {
path: PathBuf::from("fonts/ubuntu-mono-v15-regular-latin.woff2"),
path: Utf8PathBuf::from("fonts/ubuntu-mono-v15-regular-latin.woff2"),
content: Content::Binary(
include_bytes!("../templates/docs-fonts/ubuntu-mono-v15-regular-latin.woff2").to_vec(),
),

View file

@ -5,7 +5,8 @@ use crate::{
line_numbers::LineNumbers,
paths::ProjectPaths,
};
use std::path::{Component, Path};
use camino::{Utf8Component, Utf8Path};
pub struct SourceLinker {
line_numbers: LineNumbers,
@ -69,15 +70,11 @@ impl SourceLinker {
}
}
fn to_url_path(path: &Path) -> Option<String> {
fn to_url_path(path: &Utf8Path) -> Option<String> {
let mut buf = String::new();
for c in path.components() {
if let Component::Normal(s) = c {
if let Some(s) = s.to_str() {
buf.push_str(s);
} else {
return None;
}
if let Utf8Component::Normal(s) = c {
buf.push_str(s);
}
buf.push('/');
}

View file

@ -16,10 +16,11 @@ use itertools::Itertools;
use smol_str::SmolStr;
use std::env;
use std::fmt::Debug;
use std::path::{Path, PathBuf};
use termcolor::Buffer;
use thiserror::Error;
use camino::{Utf8Path, Utf8PathBuf};
pub type Name = SmolStr;
pub type Result<Ok, Err = Error> = std::result::Result<Ok, Err>;
@ -34,7 +35,7 @@ macro_rules! wrap_format {
pub struct UnknownImportDetails {
pub module: Name,
pub location: crate::ast::SrcSpan,
pub path: PathBuf,
pub path: Utf8PathBuf,
pub src: SmolStr,
pub modules: Vec<SmolStr>,
}
@ -43,14 +44,14 @@ pub struct UnknownImportDetails {
pub enum Error {
#[error("failed to parse Gleam source code")]
Parse {
path: PathBuf,
path: Utf8PathBuf,
src: SmolStr,
error: crate::parse::error::ParseError,
},
#[error("type checking failed")]
Type {
path: PathBuf,
path: Utf8PathBuf,
src: SmolStr,
error: crate::type_::Error,
},
@ -65,8 +66,8 @@ pub enum Error {
#[error("duplicate module {module}")]
DuplicateModule {
module: Name,
first: PathBuf,
second: PathBuf,
first: Utf8PathBuf,
second: Utf8PathBuf,
},
#[error("duplicate source file {file}")]
@ -82,7 +83,7 @@ pub enum Error {
FileIo {
kind: FileKind,
action: FileIoAction,
path: PathBuf,
path: Utf8PathBuf,
err: Option<String>,
},
@ -105,7 +106,7 @@ pub enum Error {
ExpandTar { error: String },
#[error("{err}")]
AddTar { path: PathBuf, err: String },
AddTar { path: Utf8PathBuf, err: String },
#[error("{0}")]
TarFinish(String),
@ -163,7 +164,7 @@ pub enum Error {
#[error("javascript codegen failed")]
JavaScript {
path: PathBuf,
path: Utf8PathBuf,
src: SmolStr,
error: crate::javascript::Error,
},
@ -198,7 +199,7 @@ pub enum Error {
#[error("Expected package {expected} at path {path} but found {found} instead")]
WrongDependencyProvided {
path: PathBuf,
path: Utf8PathBuf,
expected: String,
found: String,
},
@ -226,7 +227,7 @@ pub enum Error {
},
#[error("Opening docs at {path} failed: {error}")]
FailedToOpenDocs { path: PathBuf, error: String },
FailedToOpenDocs { path: Utf8PathBuf, error: String },
#[error("The package {package} requires a Gleam version satisfying {required_version} and you are using v{gleam_version}")]
IncompatibleCompilerVersion {
@ -256,7 +257,7 @@ impl Error {
pub fn add_tar<P, E>(path: P, error: E) -> Error
where
P: AsRef<Path>,
P: AsRef<Utf8Path>,
E: std::error::Error,
{
Self::AddTar {
@ -672,8 +673,7 @@ to a tar archive.
This was error from the tar library:
{}",
path.to_str().unwrap(),
err
path, err
);
Diagnostic {
title: "Failure creating tar archive".into(),
@ -745,9 +745,7 @@ This was error from the Hex client library:
First: {}
Second: {}",
module,
first.to_str().expect("pretty error print PathBuf to_str"),
second.to_str().expect("pretty error print PathBuf to_str"),
module, first, second,
);
Diagnostic {
@ -786,7 +784,7 @@ Second: {}",
{}",
action.text(),
kind.text(),
path.to_string_lossy(),
path,
err,
);
Diagnostic {
@ -2343,7 +2341,7 @@ but it cannot be found."
Error::Format { problem_files } => {
let files: Vec<_> = problem_files
.iter()
.flat_map(|formatted| formatted.source.to_str())
.map(|formatted| formatted.source.as_str())
.map(|p| format!(" - {p}"))
.sorted()
.collect();
@ -2490,9 +2488,7 @@ The error from the version resolver library was:
} => {
let text = format!(
"Expected package {} at path {} but found {} instead",
expected,
path.to_string_lossy(),
found
expected, path, found
);
Diagnostic {
@ -2609,8 +2605,7 @@ issue in our tracker: https://github.com/gleam-lang/gleam/issues",
{}
{}",
path.to_string_lossy(),
error,
path, error,
);
Diagnostic {
title: "Failed to open docs".into(),
@ -2750,8 +2745,8 @@ functions from the `gleam/string` module",
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Unformatted {
pub source: PathBuf,
pub destination: PathBuf,
pub source: Utf8PathBuf,
pub destination: Utf8PathBuf,
pub input: SmolStr,
pub output: String,
}

View file

@ -2,9 +2,11 @@
mod tests;
use smol_str::SmolStr;
use std::{collections::HashMap, path::Path};
use std::collections::HashMap;
use vec1::vec1;
use camino::Utf8Path;
use crate::{
ast::{
Arg, ArgNames, Definition, ExternalFunction, Function, Statement, TargettedDefinition,
@ -18,7 +20,7 @@ use crate::{
pub fn parse_fix_and_format(
assumed_target: Option<Target>,
src: &SmolStr,
path: &Path,
path: &Utf8Path,
) -> Result<(String, bool)> {
// Parse
let parsed = crate::parse::parse_module(src).map_err(|error| Error::Parse {

View file

@ -1,7 +1,9 @@
use super::*;
use camino::Utf8Path;
fn fix(target: Option<Target>, src: &str) -> String {
let (out, complete) = parse_fix_and_format(target, &src.into(), Path::new("test")).unwrap();
let (out, complete) = parse_fix_and_format(target, &src.into(), Utf8Path::new("test")).unwrap();
assert!(complete);
out
}
@ -496,7 +498,7 @@ fn ambiguous_external() {
let src = r#"pub external fn main(wibble: Int, wobble: Float) -> Int =
"app" "main"
"#;
let (out, complete) = parse_fix_and_format(None, &src.into(), Path::new("test")).unwrap();
let (out, complete) = parse_fix_and_format(None, &src.into(), Utf8Path::new("test")).unwrap();
assert!(!complete);
assert_eq!(out, src)
}

View file

@ -13,12 +13,14 @@ use crate::{
};
use itertools::Itertools;
use smol_str::SmolStr;
use std::{path::Path, sync::Arc};
use std::sync::Arc;
use vec1::Vec1;
use camino::Utf8Path;
const INDENT: isize = 2;
pub fn pretty(writer: &mut impl Utf8Writer, src: &SmolStr, path: &Path) -> Result<()> {
pub fn pretty(writer: &mut impl Utf8Writer, src: &SmolStr, path: &Utf8Path) -> Result<()> {
let parsed = crate::parse::parse_module(src).map_err(|error| Error::Parse {
path: path.to_path_buf(),
src: src.clone(),

View file

@ -14,7 +14,8 @@ mod use_;
macro_rules! assert_format {
($src:expr $(,)?) => {
let mut writer = String::new();
$crate::format::pretty(&mut writer, &$src.into(), std::path::Path::new("<stdin>")).unwrap();
$crate::format::pretty(&mut writer, &$src.into(), camino::Utf8Path::new("<stdin>"))
.unwrap();
assert_eq!($src, writer);
};
}
@ -23,7 +24,8 @@ macro_rules! assert_format {
macro_rules! assert_format_rewrite {
($src:expr, $output:expr $(,)?) => {
let mut writer = String::new();
$crate::format::pretty(&mut writer, &$src.into(), std::path::Path::new("<stdin>")).unwrap();
$crate::format::pretty(&mut writer, &$src.into(), camino::Utf8Path::new("<stdin>"))
.unwrap();
assert_eq!(writer, $output);
};
}

View file

@ -1,8 +1,8 @@
use camino::Utf8Path;
use debug_ignore::DebugIgnore;
use flate2::read::GzDecoder;
use futures::future;
use hexpm::version::Version;
use std::path::Path;
use tar::Archive;
use crate::{
@ -209,7 +209,7 @@ impl Downloader {
// It would be really nice if this was async but the library is sync
pub fn extract_package_from_cache(&self, name: &str, version: &Version) -> Result<bool> {
let contents_path = Path::new("contents.tar.gz");
let contents_path = Utf8Path::new("contents.tar.gz");
let destination = self.paths.build_packages_package(name);
// If the directory already exists then there's nothing for us to do

View file

@ -4,15 +4,11 @@ use crate::error::{Error, FileIoAction, FileKind, Result};
use async_trait::async_trait;
use debug_ignore::DebugIgnore;
use flate2::read::GzDecoder;
use std::{
fmt::Debug,
io,
path::{Path, PathBuf},
time::SystemTime,
vec::IntoIter,
};
use std::{fmt::Debug, io, time::SystemTime, vec::IntoIter};
use tar::{Archive, Entry};
use camino::{Utf8Path, Utf8PathBuf};
pub trait Reader: std::io::Read {
/// A wrapper around `std::io::Read` that has Gleam's error handling.
fn read_bytes(&mut self, buffer: &mut [u8]) -> Result<usize> {
@ -36,7 +32,7 @@ impl Utf8Writer for String {
Error::FileIo {
action: FileIoAction::WriteTo,
kind: FileKind::File,
path: PathBuf::from("<in memory>"),
path: Utf8PathBuf::from("<in memory>"),
err: Some(error.to_string()),
}
}
@ -93,7 +89,7 @@ impl From<&str> for Content {
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct OutputFile {
pub content: Content,
pub path: PathBuf,
pub path: Utf8PathBuf,
}
#[derive(Debug)]
@ -130,25 +126,25 @@ impl IntoIterator for ReadDir {
#[derive(Debug, Clone)]
pub struct DirEntry {
pub pathbuf: PathBuf,
pub pathbuf: Utf8PathBuf,
}
impl DirEntry {
pub fn from_path<P: AsRef<Path>>(path: P) -> DirEntry {
pub fn from_path<P: AsRef<Utf8Path>>(path: P) -> DirEntry {
DirEntry {
pathbuf: path.as_ref().to_path_buf(),
}
}
pub fn from_pathbuf(pathbuf: PathBuf) -> DirEntry {
pub fn from_pathbuf(pathbuf: Utf8PathBuf) -> DirEntry {
DirEntry { pathbuf }
}
pub fn as_path(&self) -> &Path {
pub fn as_path(&self) -> &Utf8Path {
self.pathbuf.as_path()
}
pub fn into_path(self) -> PathBuf {
pub fn into_path(self) -> Utf8PathBuf {
self.pathbuf
}
}
@ -157,16 +153,16 @@ impl DirEntry {
/// Typically we use an implementation that reads from the file system,
/// but in tests and in other places other implementations may be used.
pub trait FileSystemReader {
fn gleam_source_files(&self, dir: &Path) -> Vec<PathBuf>;
fn gleam_cache_files(&self, dir: &Path) -> Vec<PathBuf>;
fn read_dir(&self, path: &Path) -> Result<ReadDir>;
fn read(&self, path: &Path) -> Result<String, Error>;
fn read_bytes(&self, path: &Path) -> Result<Vec<u8>, Error>;
fn reader(&self, path: &Path) -> Result<WrappedReader, Error>;
fn is_file(&self, path: &Path) -> bool;
fn is_directory(&self, path: &Path) -> bool;
fn modification_time(&self, path: &Path) -> Result<SystemTime, Error>;
fn canonicalise(&self, path: &Path) -> Result<PathBuf, Error>;
fn gleam_source_files(&self, dir: &Utf8Path) -> Vec<Utf8PathBuf>;
fn gleam_cache_files(&self, dir: &Utf8Path) -> Vec<Utf8PathBuf>;
fn read_dir(&self, path: &Utf8Path) -> Result<ReadDir>;
fn read(&self, path: &Utf8Path) -> Result<String, Error>;
fn read_bytes(&self, path: &Utf8Path) -> Result<Vec<u8>, Error>;
fn reader(&self, path: &Utf8Path) -> Result<WrappedReader, Error>;
fn is_file(&self, path: &Utf8Path) -> bool;
fn is_directory(&self, path: &Utf8Path) -> bool;
fn modification_time(&self, path: &Utf8Path) -> Result<SystemTime, Error>;
fn canonicalise(&self, path: &Utf8Path) -> Result<Utf8PathBuf, Error>;
}
/// A trait used to run other programs.
@ -176,7 +172,7 @@ pub trait CommandExecutor {
program: &str,
args: &[String],
env: &[(&str, String)],
cwd: Option<&Path>,
cwd: Option<&Utf8Path>,
stdio: Stdio,
) -> Result<i32, Error>;
}
@ -200,26 +196,26 @@ impl Stdio {
/// Typically we use an implementation that writes to the file system,
/// but in tests and in other places other implementations may be used.
pub trait FileSystemWriter {
fn mkdir(&self, path: &Path) -> Result<(), Error>;
fn write(&self, path: &Path, content: &str) -> Result<(), Error>;
fn write_bytes(&self, path: &Path, content: &[u8]) -> Result<(), Error>;
fn delete(&self, path: &Path) -> Result<(), Error>;
fn copy(&self, from: &Path, to: &Path) -> Result<(), Error>;
fn copy_dir(&self, from: &Path, to: &Path) -> Result<(), Error>;
fn hardlink(&self, from: &Path, to: &Path) -> Result<(), Error>;
fn symlink_dir(&self, from: &Path, to: &Path) -> Result<(), Error>;
fn delete_file(&self, path: &Path) -> Result<(), Error>;
fn mkdir(&self, path: &Utf8Path) -> Result<(), Error>;
fn write(&self, path: &Utf8Path, content: &str) -> Result<(), Error>;
fn write_bytes(&self, path: &Utf8Path, content: &[u8]) -> Result<(), Error>;
fn delete(&self, path: &Utf8Path) -> Result<(), Error>;
fn copy(&self, from: &Utf8Path, to: &Utf8Path) -> Result<(), Error>;
fn copy_dir(&self, from: &Utf8Path, to: &Utf8Path) -> Result<(), Error>;
fn hardlink(&self, from: &Utf8Path, to: &Utf8Path) -> Result<(), Error>;
fn symlink_dir(&self, from: &Utf8Path, to: &Utf8Path) -> Result<(), Error>;
fn delete_file(&self, path: &Utf8Path) -> Result<(), Error>;
}
#[derive(Debug)]
/// A wrapper around a Read implementing object that has Gleam's error handling.
pub struct WrappedReader {
path: PathBuf,
path: Utf8PathBuf,
inner: DebugIgnore<Box<dyn std::io::Read>>,
}
impl WrappedReader {
pub fn new(path: &Path, inner: Box<dyn std::io::Read>) -> Self {
pub fn new(path: &Utf8Path, inner: Box<dyn std::io::Read>) -> Self {
Self {
path: path.to_path_buf(),
inner: DebugIgnore(inner),
@ -275,13 +271,13 @@ pub trait TarUnpacker {
fn io_result_unpack(
&self,
path: &Path,
path: &Utf8Path,
archive: Archive<GzDecoder<Entry<'_, WrappedReader>>>,
) -> io::Result<()>;
fn unpack(
&self,
path: &Path,
path: &Utf8Path,
archive: Archive<GzDecoder<Entry<'_, WrappedReader>>>,
) -> Result<()> {
tracing::debug!(path = ?path, "unpacking tar archive");

View file

@ -1,7 +1,9 @@
use lazy_static::__Deref;
use super::*;
use std::{cell::RefCell, collections::HashMap, ffi::OsStr, rc::Rc, time::Duration};
use std::{cell::RefCell, collections::HashMap, rc::Rc, time::Duration};
use camino::{Utf8Path, Utf8PathBuf};
// An in memory sharable collection of pretend files that can be used in place
// of a real file system. It is a shared reference to a set of buffer than can
@ -17,7 +19,7 @@ use std::{cell::RefCell, collections::HashMap, ffi::OsStr, rc::Rc, time::Duratio
//
#[derive(Clone, Default, Debug, PartialEq, Eq)]
pub struct InMemoryFileSystem {
files: Rc<RefCell<HashMap<PathBuf, InMemoryFile>>>,
files: Rc<RefCell<HashMap<Utf8PathBuf, InMemoryFile>>>,
}
impl InMemoryFileSystem {
@ -29,7 +31,7 @@ impl InMemoryFileSystem {
///
/// Panics if this is not the only reference to the underlying files.
///
pub fn into_contents(self) -> HashMap<PathBuf, Content> {
pub fn into_contents(self) -> HashMap<Utf8PathBuf, Content> {
Rc::try_unwrap(self.files)
.expect("InMemoryFileSystem::into_files called on a clone")
.into_inner()
@ -38,7 +40,7 @@ impl InMemoryFileSystem {
.collect()
}
pub fn paths(&self) -> Vec<PathBuf> {
pub fn paths(&self) -> Vec<Utf8PathBuf> {
self.files.borrow().keys().cloned().collect()
}
@ -47,7 +49,7 @@ impl InMemoryFileSystem {
///
/// Panics if the file does not exist.
///
pub fn set_modification_time(&self, path: &Path, time: SystemTime) {
pub fn set_modification_time(&self, path: &Utf8Path, time: SystemTime) {
self.files
.deref()
.borrow_mut()
@ -56,7 +58,11 @@ impl InMemoryFileSystem {
.modification_time = time;
}
pub fn try_set_modification_time(&self, path: &Path, time: SystemTime) -> Result<(), Error> {
pub fn try_set_modification_time(
&self,
path: &Utf8Path,
time: SystemTime,
) -> Result<(), Error> {
self.files
.deref()
.borrow_mut()
@ -73,42 +79,42 @@ impl InMemoryFileSystem {
}
impl FileSystemWriter for InMemoryFileSystem {
fn delete(&self, path: &Path) -> Result<(), Error> {
fn delete(&self, path: &Utf8Path) -> Result<(), Error> {
let mut files = self.files.deref().borrow_mut();
let _ = files.remove(path);
Ok(())
}
fn copy(&self, from: &Path, to: &Path) -> Result<(), Error> {
fn copy(&self, from: &Utf8Path, to: &Utf8Path) -> Result<(), Error> {
self.write_bytes(to, &self.read_bytes(from)?)
}
fn copy_dir(&self, _: &Path, _: &Path) -> Result<(), Error> {
fn copy_dir(&self, _: &Utf8Path, _: &Utf8Path) -> Result<(), Error> {
panic!("unimplemented") // TODO
}
fn mkdir(&self, _: &Path) -> Result<(), Error> {
fn mkdir(&self, _: &Utf8Path) -> Result<(), Error> {
Ok(())
}
fn hardlink(&self, _: &Path, _: &Path) -> Result<(), Error> {
fn hardlink(&self, _: &Utf8Path, _: &Utf8Path) -> Result<(), Error> {
panic!("unimplemented") // TODO
}
fn symlink_dir(&self, _: &Path, _: &Path) -> Result<(), Error> {
fn symlink_dir(&self, _: &Utf8Path, _: &Utf8Path) -> Result<(), Error> {
panic!("unimplemented") // TODO
}
fn delete_file(&self, path: &Path) -> Result<(), Error> {
fn delete_file(&self, path: &Utf8Path) -> Result<(), Error> {
let _ = self.files.deref().borrow_mut().remove(path);
Ok(())
}
fn write(&self, path: &Path, content: &str) -> Result<(), Error> {
fn write(&self, path: &Utf8Path, content: &str) -> Result<(), Error> {
self.write_bytes(path, content.as_bytes())
}
fn write_bytes(&self, path: &Path, content: &[u8]) -> Result<(), Error> {
fn write_bytes(&self, path: &Utf8Path, content: &[u8]) -> Result<(), Error> {
let mut file = InMemoryFile::default();
_ = io::Write::write(&mut file, content).expect("channel buffer write");
_ = self
@ -121,33 +127,33 @@ impl FileSystemWriter for InMemoryFileSystem {
}
impl FileSystemReader for InMemoryFileSystem {
fn canonicalise(&self, path: &Path) -> Result<PathBuf, Error> {
fn canonicalise(&self, path: &Utf8Path) -> Result<Utf8PathBuf, Error> {
Ok(path.to_path_buf())
}
fn gleam_source_files(&self, dir: &Path) -> Vec<PathBuf> {
fn gleam_source_files(&self, dir: &Utf8Path) -> Vec<Utf8PathBuf> {
self.files
.deref()
.borrow()
.iter()
.map(|(file_path, _)| file_path.to_path_buf())
.filter(|file_path| file_path.starts_with(dir))
.filter(|file_path| file_path.extension() == Some(OsStr::new("gleam")))
.filter(|file_path| file_path.extension() == Some("gleam"))
.collect()
}
fn gleam_cache_files(&self, dir: &Path) -> Vec<PathBuf> {
fn gleam_cache_files(&self, dir: &Utf8Path) -> Vec<Utf8PathBuf> {
self.files
.deref()
.borrow()
.iter()
.map(|(file_path, _)| file_path.to_path_buf())
.filter(|file_path| file_path.starts_with(dir))
.filter(|file_path| file_path.extension() == Some(OsStr::new("cache")))
.filter(|file_path| file_path.extension() == Some("cache"))
.collect()
}
fn read(&self, path: &Path) -> Result<String, Error> {
fn read(&self, path: &Utf8Path) -> Result<String, Error> {
let path = path.to_path_buf();
let files = self.files.deref().borrow();
let file = files.get(&path).ok_or_else(|| Error::FileIo {
@ -166,7 +172,7 @@ impl FileSystemReader for InMemoryFileSystem {
Ok(unicode)
}
fn read_bytes(&self, path: &Path) -> Result<Vec<u8>, Error> {
fn read_bytes(&self, path: &Utf8Path) -> Result<Vec<u8>, Error> {
let path = path.to_path_buf();
let files = self.files.deref().borrow();
let file = files.get(&path).ok_or_else(|| Error::FileIo {
@ -179,11 +185,11 @@ impl FileSystemReader for InMemoryFileSystem {
Ok(bytes)
}
fn is_file(&self, path: &Path) -> bool {
fn is_file(&self, path: &Utf8Path) -> bool {
self.files.deref().borrow().contains_key(path)
}
fn is_directory(&self, path: &Path) -> bool {
fn is_directory(&self, path: &Utf8Path) -> bool {
self.files
.deref()
.borrow()
@ -191,12 +197,12 @@ impl FileSystemReader for InMemoryFileSystem {
.any(|file_path| file_path.starts_with(path))
}
fn reader(&self, _path: &Path) -> Result<WrappedReader, Error> {
fn reader(&self, _path: &Utf8Path) -> Result<WrappedReader, Error> {
// TODO
unreachable!("Memory reader unimplemented")
}
fn read_dir(&self, path: &Path) -> Result<ReadDir> {
fn read_dir(&self, path: &Utf8Path) -> Result<ReadDir> {
let read_dir = ReadDir::from_iter(
self.files
.deref()
@ -211,7 +217,7 @@ impl FileSystemReader for InMemoryFileSystem {
Ok(read_dir)
}
fn modification_time(&self, path: &Path) -> Result<SystemTime, Error> {
fn modification_time(&self, path: &Utf8Path) -> Result<SystemTime, Error> {
let files = self.files.deref().borrow();
let file = files.get(path).ok_or_else(|| Error::FileIo {
kind: FileKind::File,
@ -283,7 +289,7 @@ impl CommandExecutor for InMemoryFileSystem {
_program: &str,
_args: &[String],
_env: &[(&str, String)],
_cwd: Option<&Path>,
_cwd: Option<&Utf8Path>,
_stdio: Stdio,
) -> Result<i32, Error> {
Ok(0) // Always succeed.

View file

@ -5,7 +5,7 @@ mod pattern;
mod tests;
mod typescript;
use std::path::Path;
use camino::Utf8Path;
use crate::type_::PRELUDE_MODULE_NAME;
use crate::{
@ -522,7 +522,7 @@ fn external_fn_args<T>(arguments: &[ExternalFnArg<T>]) -> Document<'_> {
pub fn module(
module: &TypedModule,
line_numbers: &LineNumbers,
path: &Path,
path: &Utf8Path,
src: &SmolStr,
) -> Result<String, crate::Error> {
let document = Generator::new(line_numbers, module)
@ -537,7 +537,7 @@ pub fn module(
pub fn ts_declaration(
module: &TypedModule,
path: &Path,
path: &Utf8Path,
src: &SmolStr,
) -> Result<String, crate::Error> {
let document = typescript::TypeScriptGenerator::new(module)

View file

@ -4,7 +4,7 @@ use crate::{
uid::UniqueIdGenerator,
warning::TypeWarningEmitter,
};
use std::path::Path;
use camino::Utf8Path;
mod assignments;
mod bit_strings;
@ -118,10 +118,10 @@ pub fn compile(src: &str, dep: Option<(&str, &str, &str)>) -> TypedModule {
pub fn compile_js(src: &str, dep: Option<(&str, &str, &str)>) -> String {
let ast = compile(src, dep);
let line_numbers = LineNumbers::new(src);
module(&ast, &line_numbers, Path::new(""), &"".into()).unwrap()
module(&ast, &line_numbers, Utf8Path::new(""), &"".into()).unwrap()
}
pub fn compile_ts(src: &str, dep: Option<(&str, &str, &str)>) -> String {
let ast = compile(src, dep);
ts_declaration(&ast, Path::new(""), &src.into()).unwrap()
ts_declaration(&ast, Utf8Path::new(""), &src.into()).unwrap()
}

View file

@ -14,7 +14,9 @@ use crate::{
warning::VectorWarningEmitterIO,
Error, Result, Warning,
};
use std::{collections::HashMap, path::PathBuf, sync::Arc};
use std::{collections::HashMap, sync::Arc};
use camino::Utf8PathBuf;
/// A wrapper around the project compiler which makes it possible to repeatedly
/// recompile the top level package, reusing the information about the already
@ -91,7 +93,7 @@ where
})
}
pub fn compile(&mut self) -> Result<Vec<PathBuf>, Error> {
pub fn compile(&mut self) -> Result<Vec<Utf8PathBuf>, Error> {
// Lock the build directory to ensure to ensure we are the only one compiling
let _lock_guard = self.locker.lock_for_build();

View file

@ -11,9 +11,9 @@ use crate::{
type_::{pretty::Printer, PreludeType, ValueConstructorVariant},
Error, Result, Warning,
};
use camino::Utf8PathBuf;
use lsp_types::{self as lsp, Hover, HoverContents, MarkedString, Url};
use smol_str::SmolStr;
use std::path::PathBuf;
use strum::IntoEnumIterator;
use super::{src_span_to_lsp_range, DownloadDependencies, MakeLocker};
@ -28,7 +28,7 @@ pub struct Response<T> {
#[derive(Debug, PartialEq, Eq)]
pub enum Compilation {
/// Compilation was attempted and succeeded for these modules.
Yes(Vec<PathBuf>),
Yes(Vec<Utf8PathBuf>),
/// Compilation was not attempted for this operation.
No,
}
@ -43,7 +43,7 @@ pub struct LanguageServerEngine<IO, Reporter> {
/// discarded and reloaded to handle any changes to dependencies.
pub(crate) compiler: LspProjectCompiler<FileSystemProxy<IO>>,
modules_compiled_since_last_feedback: Vec<PathBuf>,
modules_compiled_since_last_feedback: Vec<Utf8PathBuf>,
compiled_since_last_feedback: bool,
// Used to publish progress notifications to the client without waiting for
@ -283,7 +283,7 @@ where
let path = uri.to_file_path().expect("URL file");
#[cfg(not(any(unix, windows, target_os = "redox", target_os = "wasi")))]
let path: PathBuf = uri.path().into();
let path: Utf8PathBuf = uri.path().into();
let components = path
.strip_prefix(self.paths.root())

View file

@ -1,25 +1,24 @@
use crate::{diagnostic::Diagnostic, Error, Warning};
use std::{
collections::{HashMap, HashSet},
path::PathBuf,
};
use std::collections::{HashMap, HashSet};
use camino::Utf8PathBuf;
use super::engine::Compilation;
#[derive(Debug, Default, PartialEq, Eq)]
pub struct Feedback {
pub diagnostics: HashMap<PathBuf, Vec<Diagnostic>>,
pub diagnostics: HashMap<Utf8PathBuf, Vec<Diagnostic>>,
pub messages: Vec<Diagnostic>,
}
impl Feedback {
/// Set the diagnostics for a file to an empty vector. This will overwrite
/// any existing diagnostics on the client.
pub fn unset_existing_diagnostics(&mut self, path: PathBuf) {
pub fn unset_existing_diagnostics(&mut self, path: Utf8PathBuf) {
_ = self.diagnostics.insert(path, vec![]);
}
pub fn append_diagnostic(&mut self, path: PathBuf, diagnostic: Diagnostic) {
pub fn append_diagnostic(&mut self, path: Utf8PathBuf, diagnostic: Diagnostic) {
self.diagnostics
.entry(path)
.or_insert_with(Vec::new)
@ -44,8 +43,8 @@ impl Feedback {
///
#[derive(Debug, Default)]
pub struct FeedbackBookKeeper {
files_with_warnings: HashSet<PathBuf>,
files_with_errors: HashSet<PathBuf>,
files_with_warnings: HashSet<Utf8PathBuf>,
files_with_errors: HashSet<Utf8PathBuf>,
}
impl FeedbackBookKeeper {
@ -137,7 +136,6 @@ impl FeedbackBookKeeper {
#[cfg(test)]
mod tests {
use std::path::Path;
use super::*;
use crate::{
@ -149,9 +147,9 @@ mod tests {
#[test]
fn feedback() {
let mut book_keeper = FeedbackBookKeeper::default();
let file1 = PathBuf::from("src/file1.gleam");
let file2 = PathBuf::from("src/file2.gleam");
let file3 = PathBuf::from("src/file3.gleam");
let file1 = Utf8PathBuf::from("src/file1.gleam");
let file2 = Utf8PathBuf::from("src/file2.gleam");
let file3 = Utf8PathBuf::from("src/file3.gleam");
let warning1 = Warning::Type {
path: file1.clone(),
@ -212,7 +210,7 @@ mod tests {
// location.
let mut book_keeper = FeedbackBookKeeper::default();
let file1 = PathBuf::from("src/file1.gleam");
let file1 = Utf8PathBuf::from("src/file1.gleam");
let warning1 = Warning::Type {
path: file1.clone(),
@ -245,8 +243,8 @@ mod tests {
// location.
let mut book_keeper = FeedbackBookKeeper::default();
let file1 = PathBuf::from("src/file1.gleam");
let file3 = PathBuf::from("src/file2.gleam");
let file1 = Utf8PathBuf::from("src/file1.gleam");
let file3 = Utf8PathBuf::from("src/file2.gleam");
let warning1 = Warning::Type {
path: file1.clone(),
@ -312,8 +310,8 @@ mod tests {
// when a successful compilation occurs.
let mut book_keeper = FeedbackBookKeeper::default();
let file1 = PathBuf::from("src/file1.gleam");
let file2 = PathBuf::from("src/file2.gleam");
let file1 = Utf8PathBuf::from("src/file1.gleam");
let file2 = Utf8PathBuf::from("src/file2.gleam");
let error = Error::Parse {
path: file1.clone(),
@ -353,10 +351,10 @@ mod tests {
#[test]
fn second_failure_unsets_previous_error() {
let mut book_keeper = FeedbackBookKeeper::default();
let file1 = PathBuf::from("src/file1.gleam");
let file2 = PathBuf::from("src/file2.gleam");
let file1 = Utf8PathBuf::from("src/file1.gleam");
let file2 = Utf8PathBuf::from("src/file2.gleam");
let error = |file: &Path| Error::Parse {
let error = |file: &camino::Utf8Path| Error::Parse {
path: file.to_path_buf(),
src: "blah".into(),
error: ParseError {
@ -397,7 +395,7 @@ mod tests {
#[test]
fn successful_non_compilation_does_not_remove_error_diagnostic() {
let mut book_keeper = FeedbackBookKeeper::default();
let file1 = PathBuf::from("src/file1.gleam");
let file1 = Utf8PathBuf::from("src/file1.gleam");
let error = Error::Parse {
path: file1.clone(),

View file

@ -1,7 +1,4 @@
use std::{
path::{Path, PathBuf},
time::SystemTime,
};
use std::time::SystemTime;
use debug_ignore::DebugIgnore;
@ -13,6 +10,8 @@ use crate::{
Result,
};
use camino::{Utf8Path, Utf8PathBuf};
// A proxy intended for `LanguageServer` to use when files are modified in
// memory but not yet saved to disc by the client.
//
@ -42,14 +41,14 @@ where
&self.io
}
pub fn write_mem_cache(&mut self, path: &Path, content: &str) -> Result<()> {
pub fn write_mem_cache(&mut self, path: &Utf8Path, content: &str) -> Result<()> {
let write_result = self.edit_cache.write(path, content);
self.edit_cache
.try_set_modification_time(path, SystemTime::now())?;
write_result
}
pub fn delete_mem_cache(&self, path: &Path) -> Result<()> {
pub fn delete_mem_cache(&self, path: &Utf8Path) -> Result<()> {
self.edit_cache.delete(path)
}
}
@ -59,39 +58,39 @@ impl<IO> FileSystemWriter for FileSystemProxy<IO>
where
IO: FileSystemWriter,
{
fn mkdir(&self, path: &Path) -> Result<()> {
fn mkdir(&self, path: &Utf8Path) -> Result<()> {
self.io.mkdir(path)
}
fn write(&self, path: &Path, content: &str) -> Result<()> {
fn write(&self, path: &Utf8Path, content: &str) -> Result<()> {
self.io.write(path, content)
}
fn write_bytes(&self, path: &Path, content: &[u8]) -> Result<()> {
fn write_bytes(&self, path: &Utf8Path, content: &[u8]) -> Result<()> {
self.io.write_bytes(path, content)
}
fn delete(&self, path: &Path) -> Result<()> {
fn delete(&self, path: &Utf8Path) -> Result<()> {
self.io.delete(path)
}
fn copy(&self, from: &Path, to: &Path) -> Result<()> {
fn copy(&self, from: &Utf8Path, to: &Utf8Path) -> Result<()> {
self.io.copy(from, to)
}
fn copy_dir(&self, from: &Path, to: &Path) -> Result<()> {
fn copy_dir(&self, from: &Utf8Path, to: &Utf8Path) -> Result<()> {
self.io.copy_dir(from, to)
}
fn hardlink(&self, from: &Path, to: &Path) -> Result<()> {
fn hardlink(&self, from: &Utf8Path, to: &Utf8Path) -> Result<()> {
self.io.hardlink(from, to)
}
fn symlink_dir(&self, from: &Path, to: &Path) -> Result<()> {
fn symlink_dir(&self, from: &Utf8Path, to: &Utf8Path) -> Result<()> {
self.io.symlink_dir(from, to)
}
fn delete_file(&self, path: &Path) -> Result<()> {
fn delete_file(&self, path: &Utf8Path) -> Result<()> {
self.io.delete_file(path)
}
}
@ -100,54 +99,54 @@ impl<IO> FileSystemReader for FileSystemProxy<IO>
where
IO: FileSystemReader,
{
fn gleam_source_files(&self, dir: &Path) -> Vec<PathBuf> {
fn gleam_source_files(&self, dir: &Utf8Path) -> Vec<Utf8PathBuf> {
self.io.gleam_source_files(dir)
}
fn gleam_cache_files(&self, dir: &Path) -> Vec<PathBuf> {
fn gleam_cache_files(&self, dir: &Utf8Path) -> Vec<Utf8PathBuf> {
self.io.gleam_cache_files(dir)
}
fn read_dir(&self, path: &Path) -> Result<ReadDir> {
fn read_dir(&self, path: &Utf8Path) -> Result<ReadDir> {
self.io.read_dir(path)
}
fn read(&self, path: &Path) -> Result<String> {
fn read(&self, path: &Utf8Path) -> Result<String> {
match self.edit_cache.read(path) {
result @ Ok(_) => result,
Err(_) => self.io.read(path),
}
}
fn read_bytes(&self, path: &Path) -> Result<Vec<u8>> {
fn read_bytes(&self, path: &Utf8Path) -> Result<Vec<u8>> {
match self.edit_cache.read_bytes(path) {
result @ Ok(_) => result,
Err(_) => self.io.read_bytes(path),
}
}
fn reader(&self, path: &Path) -> Result<WrappedReader> {
fn reader(&self, path: &Utf8Path) -> Result<WrappedReader> {
self.io.reader(path)
}
// Cache overides existence of file
fn is_file(&self, path: &Path) -> bool {
fn is_file(&self, path: &Utf8Path) -> bool {
self.edit_cache.is_file(path) || self.io.is_file(path)
}
// Cache overides existence of directory
fn is_directory(&self, path: &Path) -> bool {
fn is_directory(&self, path: &Utf8Path) -> bool {
self.edit_cache.is_directory(path) || self.io.is_directory(path)
}
fn modification_time(&self, path: &Path) -> Result<SystemTime> {
fn modification_time(&self, path: &Utf8Path) -> Result<SystemTime> {
match self.edit_cache.modification_time(path) {
result @ Ok(_) => result,
Err(_) => self.io.modification_time(path),
}
}
fn canonicalise(&self, path: &Path) -> Result<PathBuf, crate::Error> {
fn canonicalise(&self, path: &Utf8Path) -> Result<Utf8PathBuf, crate::Error> {
self.io.canonicalise(path)
}
}
@ -161,7 +160,7 @@ where
_program: &str,
_args: &[String],
_env: &[(&str, String)],
_cwd: Option<&Path>,
_cwd: Option<&Utf8Path>,
_stdio: Stdio,
) -> Result<i32> {
panic!("The language server is not permitted to create subprocesses")

View file

@ -8,10 +8,9 @@ use crate::{
paths::ProjectPaths,
Error, Result,
};
use std::{
collections::{hash_map::Entry, HashMap},
path::{Path, PathBuf},
};
use std::collections::{hash_map::Entry, HashMap};
use camino::{Utf8Path, Utf8PathBuf};
use super::feedback::FeedbackBookKeeper;
@ -25,7 +24,7 @@ use super::feedback::FeedbackBookKeeper;
#[derive(Debug)]
pub struct Router<IO, Reporter> {
io: FileSystemProxy<IO>,
engines: HashMap<PathBuf, Project<IO, Reporter>>,
engines: HashMap<Utf8PathBuf, Project<IO, Reporter>>,
progress_reporter: Reporter,
}
@ -49,7 +48,10 @@ where
}
}
pub fn project_for_path(&mut self, path: &Path) -> Result<Option<&mut Project<IO, Reporter>>> {
pub fn project_for_path(
&mut self,
path: &Utf8Path,
) -> Result<Option<&mut Project<IO, Reporter>>> {
let path = match find_gleam_project_parent(&self.io, path) {
Some(x) => x,
None => return Ok(None),
@ -84,7 +86,7 @@ where
Ok(Some(entry.insert(project)))
}
pub fn delete_engine_for_path(&mut self, path: &Path) {
pub fn delete_engine_for_path(&mut self, path: &Utf8Path) {
if let Some(path) = find_gleam_project_parent(&self.io, path) {
_ = self.engines.remove(&path);
}
@ -96,7 +98,7 @@ where
///
/// The file must be in either the `src` or `test` directory if it is not a
/// `.gleam` file.
fn find_gleam_project_parent<IO>(io: &IO, path: &Path) -> Option<PathBuf>
fn find_gleam_project_parent<IO>(io: &IO, path: &Utf8Path) -> Option<Utf8PathBuf>
where
IO: FileSystemReader,
{
@ -135,14 +137,14 @@ mod find_gleam_project_parent_tests {
#[test]
fn root() {
let io = InMemoryFileSystem::new();
assert_eq!(find_gleam_project_parent(&io, Path::new("/")), None);
assert_eq!(find_gleam_project_parent(&io, Utf8Path::new("/")), None);
}
#[test]
fn outside_a_project() {
let io = InMemoryFileSystem::new();
assert_eq!(
find_gleam_project_parent(&io, Path::new("/app/src/one.gleam")),
find_gleam_project_parent(&io, Utf8Path::new("/app/src/one.gleam")),
None
);
}
@ -150,30 +152,30 @@ mod find_gleam_project_parent_tests {
#[test]
fn gleam_toml_itself() {
let io = InMemoryFileSystem::new();
io.write(Path::new("/app/gleam.toml"), "").unwrap();
io.write(Utf8Path::new("/app/gleam.toml"), "").unwrap();
assert_eq!(
find_gleam_project_parent(&io, Path::new("/app/gleam.toml")),
Some(PathBuf::from("/app"))
find_gleam_project_parent(&io, Utf8Path::new("/app/gleam.toml")),
Some(Utf8PathBuf::from("/app"))
);
}
#[test]
fn test_module() {
let io = InMemoryFileSystem::new();
io.write(Path::new("/app/gleam.toml"), "").unwrap();
io.write(Utf8Path::new("/app/gleam.toml"), "").unwrap();
assert_eq!(
find_gleam_project_parent(&io, Path::new("/app/test/one/two/three.gleam")),
Some(PathBuf::from("/app"))
find_gleam_project_parent(&io, Utf8Path::new("/app/test/one/two/three.gleam")),
Some(Utf8PathBuf::from("/app"))
);
}
#[test]
fn src_module() {
let io = InMemoryFileSystem::new();
io.write(Path::new("/app/gleam.toml"), "").unwrap();
io.write(Utf8Path::new("/app/gleam.toml"), "").unwrap();
assert_eq!(
find_gleam_project_parent(&io, Path::new("/app/src/one/two/three.gleam")),
Some(PathBuf::from("/app"))
find_gleam_project_parent(&io, Utf8Path::new("/app/src/one/two/three.gleam")),
Some(Utf8PathBuf::from("/app"))
);
}
@ -181,9 +183,9 @@ mod find_gleam_project_parent_tests {
#[test]
fn module_in_project_but_not_src_or_test() {
let io = InMemoryFileSystem::new();
io.write(Path::new("/app/gleam.toml"), "").unwrap();
io.write(Utf8Path::new("/app/gleam.toml"), "").unwrap();
assert_eq!(
find_gleam_project_parent(&io, Path::new("/app/other/one/two/three.gleam")),
find_gleam_project_parent(&io, Utf8Path::new("/app/other/one/two/three.gleam")),
None,
);
}
@ -191,16 +193,16 @@ mod find_gleam_project_parent_tests {
#[test]
fn nested_projects() {
let io = InMemoryFileSystem::new();
io.write(Path::new("/app/gleam.toml"), "").unwrap();
io.write(Path::new("/app/examples/wibble/gleam.toml"), "")
io.write(Utf8Path::new("/app/gleam.toml"), "").unwrap();
io.write(Utf8Path::new("/app/examples/wibble/gleam.toml"), "")
.unwrap();
assert_eq!(
find_gleam_project_parent(&io, Path::new("/app/src/one.gleam")),
Some(PathBuf::from("/app"))
find_gleam_project_parent(&io, Utf8Path::new("/app/src/one.gleam")),
Some(Utf8PathBuf::from("/app"))
);
assert_eq!(
find_gleam_project_parent(&io, Path::new("/app/examples/wibble/src/one.gleam")),
Some(PathBuf::from("/app/examples/wibble"))
find_gleam_project_parent(&io, Utf8Path::new("/app/examples/wibble/src/one.gleam")),
Some(Utf8PathBuf::from("/app/examples/wibble"))
);
}
}

View file

@ -24,7 +24,9 @@ use lsp_types::{
InitializeParams, PublishDiagnosticsParams,
};
use serde_json::Value as Json;
use std::{collections::HashMap, path::PathBuf};
use std::collections::HashMap;
use camino::Utf8PathBuf;
use super::progress::ConnectionProgressReporter;
@ -185,7 +187,7 @@ where
self.publish_messages(feedback.messages);
}
fn publish_diagnostics(&self, diagnostics: HashMap<PathBuf, Vec<Diagnostic>>) {
fn publish_diagnostics(&self, diagnostics: HashMap<Utf8PathBuf, Vec<Diagnostic>>) {
for (path, diagnostics) in diagnostics {
let diagnostics = diagnostics
.into_iter()
@ -277,7 +279,7 @@ where
fn respond_with_engine<T, Handler>(
&mut self,
path: PathBuf,
path: Utf8PathBuf,
handler: Handler,
) -> (Json, Feedback)
where
@ -314,7 +316,7 @@ where
fn notified_with_engine(
&mut self,
path: PathBuf,
path: Utf8PathBuf,
handler: impl FnOnce(
&mut LanguageServerEngine<IO, ConnectionProgressReporter<'a>>,
) -> engine::Response<()>,
@ -577,17 +579,18 @@ fn diagnostic_to_lsp(diagnostic: Diagnostic) -> Vec<lsp::Diagnostic> {
}
}
fn path_to_uri(path: PathBuf) -> Url {
fn path_to_uri(path: Utf8PathBuf) -> Url {
let mut file: String = "file://".into();
file.push_str(&path.as_os_str().to_string_lossy());
Url::parse(&file).expect("path_to_uri URL parse")
}
fn path(uri: &Url) -> PathBuf {
fn path(uri: &Url) -> Utf8PathBuf {
// The to_file_path method is available on these platforms
#[cfg(any(unix, windows, target_os = "redox", target_os = "wasi"))]
return uri.to_file_path().expect("URL file");
return Utf8PathBuf::from_path_buf(uri.to_file_path().expect("URL file"))
.expect("Non Utf8 Path");
#[cfg(not(any(unix, windows, target_os = "redox", target_os = "wasi")))]
return uri.path().into();
return Utf8PathBuf::from_path_buf(uri.path().into()).expect("Non Utf8 Path");
}

View file

@ -28,7 +28,7 @@ fn positioned_expression_completions(
let response = engine.compile_please();
assert!(response.result.is_ok());
let path = PathBuf::from(if cfg!(target_family = "windows") {
let path = Utf8PathBuf::from(if cfg!(target_family = "windows") {
r#"\\?\C:\src\app.gleam"#
} else {
"/src/app.gleam"

View file

@ -3,13 +3,14 @@ mod completion;
use std::{
collections::HashMap,
path::{Path, PathBuf},
sync::{Arc, Mutex},
time::SystemTime,
};
use hexpm::version::Version;
use camino::{Utf8Path, Utf8PathBuf};
use crate::{
config::PackageConfig,
io::{
@ -58,28 +59,28 @@ impl LanguageServerTestIO {
Arc::try_unwrap(self.actions).unwrap().into_inner().unwrap()
}
pub fn src_module(&self, name: &str, code: &str) -> PathBuf {
pub fn src_module(&self, name: &str, code: &str) -> Utf8PathBuf {
let src_dir = self.paths.src_directory();
let path = src_dir.join(name).with_extension("gleam");
self.module(&path, code);
path
}
pub fn test_module(&self, name: &str, code: &str) -> PathBuf {
pub fn test_module(&self, name: &str, code: &str) -> Utf8PathBuf {
let test_dir = self.paths.test_directory();
let path = test_dir.join(name).with_extension("gleam");
self.module(&path, code);
path
}
pub fn dep_module(&self, dep: &str, name: &str, code: &str) -> PathBuf {
pub fn dep_module(&self, dep: &str, name: &str, code: &str) -> Utf8PathBuf {
let dep_dir = self.paths.root().join(dep).join("src");
let path = dep_dir.join(name).with_extension("gleam");
self.module(&path, code);
path
}
fn module(&self, path: &Path, code: &str) {
fn module(&self, path: &Utf8Path, code: &str) {
self.io.write(path, code).unwrap();
self.io.set_modification_time(path, SystemTime::now());
}
@ -90,81 +91,81 @@ impl LanguageServerTestIO {
}
impl FileSystemReader for LanguageServerTestIO {
fn gleam_source_files(&self, dir: &Path) -> Vec<PathBuf> {
fn gleam_source_files(&self, dir: &Utf8Path) -> Vec<Utf8PathBuf> {
self.io.gleam_source_files(dir)
}
fn gleam_cache_files(&self, dir: &Path) -> Vec<PathBuf> {
fn gleam_cache_files(&self, dir: &Utf8Path) -> Vec<Utf8PathBuf> {
self.io.gleam_cache_files(dir)
}
fn read_dir(&self, path: &Path) -> Result<ReadDir> {
fn read_dir(&self, path: &Utf8Path) -> Result<ReadDir> {
self.io.read_dir(path)
}
fn read(&self, path: &Path) -> Result<String> {
fn read(&self, path: &Utf8Path) -> Result<String> {
self.io.read(path)
}
fn read_bytes(&self, path: &Path) -> Result<Vec<u8>> {
fn read_bytes(&self, path: &Utf8Path) -> Result<Vec<u8>> {
self.io.read_bytes(path)
}
fn reader(&self, path: &Path) -> Result<WrappedReader> {
fn reader(&self, path: &Utf8Path) -> Result<WrappedReader> {
self.io.reader(path)
}
fn is_file(&self, path: &Path) -> bool {
fn is_file(&self, path: &Utf8Path) -> bool {
self.io.is_file(path)
}
fn is_directory(&self, path: &Path) -> bool {
fn is_directory(&self, path: &Utf8Path) -> bool {
self.io.is_directory(path)
}
fn modification_time(&self, path: &Path) -> Result<SystemTime> {
fn modification_time(&self, path: &Utf8Path) -> Result<SystemTime> {
self.io.modification_time(path)
}
fn canonicalise(&self, path: &Path) -> Result<PathBuf, crate::Error> {
fn canonicalise(&self, path: &Utf8Path) -> Result<Utf8PathBuf, crate::Error> {
self.io.canonicalise(path)
}
}
impl FileSystemWriter for LanguageServerTestIO {
fn mkdir(&self, path: &Path) -> Result<()> {
fn mkdir(&self, path: &Utf8Path) -> Result<()> {
self.io.mkdir(path)
}
fn delete(&self, path: &Path) -> Result<()> {
fn delete(&self, path: &Utf8Path) -> Result<()> {
self.io.delete(path)
}
fn copy(&self, from: &Path, to: &Path) -> Result<()> {
fn copy(&self, from: &Utf8Path, to: &Utf8Path) -> Result<()> {
self.io.copy(from, to)
}
fn copy_dir(&self, from: &Path, to: &Path) -> Result<()> {
fn copy_dir(&self, from: &Utf8Path, to: &Utf8Path) -> Result<()> {
self.io.copy_dir(from, to)
}
fn hardlink(&self, from: &Path, to: &Path) -> Result<()> {
fn hardlink(&self, from: &Utf8Path, to: &Utf8Path) -> Result<()> {
self.io.hardlink(from, to)
}
fn symlink_dir(&self, from: &Path, to: &Path) -> Result<()> {
fn symlink_dir(&self, from: &Utf8Path, to: &Utf8Path) -> Result<()> {
self.io.symlink_dir(from, to)
}
fn delete_file(&self, path: &Path) -> Result<()> {
fn delete_file(&self, path: &Utf8Path) -> Result<()> {
self.io.delete_file(path)
}
fn write(&self, path: &Path, content: &str) -> Result<(), crate::Error> {
fn write(&self, path: &Utf8Path, content: &str) -> Result<(), crate::Error> {
self.io.write(path, content)
}
fn write_bytes(&self, path: &Path, content: &[u8]) -> Result<(), crate::Error> {
fn write_bytes(&self, path: &Utf8Path, content: &[u8]) -> Result<(), crate::Error> {
self.io.write_bytes(path, content)
}
}
@ -185,7 +186,7 @@ impl CommandExecutor for LanguageServerTestIO {
program: &str,
args: &[String],
env: &[(&str, String)],
cwd: Option<&Path>,
cwd: Option<&Utf8Path>,
stdio: crate::io::Stdio,
) -> Result<i32> {
panic!(

View file

@ -1,8 +1,8 @@
use std::collections::HashMap;
use std::path::PathBuf;
use crate::requirement::Requirement;
use crate::Result;
use camino::Utf8PathBuf;
use hexpm::version::Version;
use itertools::Itertools;
use smol_str::SmolStr;
@ -92,7 +92,7 @@ impl Manifest {
}
ManifestPackageSource::Local { path } => {
buffer.push_str(r#", source = "local", path = ""#);
buffer.push_str(path.to_str().expect("local path non utf-8"));
buffer.push_str(path.as_str());
buffer.push('"');
}
};
@ -309,7 +309,7 @@ pub enum ManifestPackageSource {
#[serde(rename = "git")]
Git { repo: SmolStr, commit: SmolStr },
#[serde(rename = "local")]
Local { path: PathBuf }, // should be the canonical path
Local { path: Utf8PathBuf }, // should be the canonical path
}
fn ordered_map<S, K, V>(value: &HashMap<K, V>, serializer: S) -> Result<S::Ok, S::Error>

View file

@ -1,6 +1,6 @@
use crate::ast::SrcSpan;
use crate::parse::error::{LexicalError, LexicalErrorType, ParseError, ParseErrorType};
use std::path::PathBuf;
use camino::Utf8PathBuf;
use pretty_assertions::assert_eq;
@ -33,7 +33,7 @@ pub fn expect_module_error(src: &str) -> String {
let result = crate::parse::parse_module(src).expect_err("should not parse");
let error = crate::error::Error::Parse {
src: src.into(),
path: PathBuf::from("/src/parse/error.gleam"),
path: Utf8PathBuf::from("/src/parse/error.gleam"),
error: result,
};
error.pretty_string()
@ -43,7 +43,7 @@ pub fn expect_error(src: &str) -> String {
let result = crate::parse::parse_statement_sequence(src).expect_err("should not parse");
let error = crate::error::Error::Parse {
src: src.into(),
path: PathBuf::from("/src/parse/error.gleam"),
path: Utf8PathBuf::from("/src/parse/error.gleam"),
error: result,
};
error.pretty_string()

View file

@ -1,16 +1,16 @@
use std::path::{Path, PathBuf};
use crate::build::{Mode, Target};
use camino::{Utf8Path, Utf8PathBuf};
pub const ARTEFACT_DIRECTORY_NAME: &str = "_gleam_artefacts";
#[derive(Debug, Clone)]
pub struct ProjectPaths {
root: PathBuf,
root: Utf8PathBuf,
}
impl ProjectPaths {
pub fn new(root: PathBuf) -> Self {
pub fn new(root: Utf8PathBuf) -> Self {
Self { root }
}
@ -21,74 +21,74 @@ impl ProjectPaths {
"/"
};
Self::new(PathBuf::from(path))
Self::new(Utf8PathBuf::from(path))
}
pub fn root(&self) -> &Path {
pub fn root(&self) -> &Utf8Path {
&self.root
}
pub fn root_config(&self) -> PathBuf {
pub fn root_config(&self) -> Utf8PathBuf {
self.root.join("gleam.toml")
}
pub fn readme(&self) -> PathBuf {
pub fn readme(&self) -> Utf8PathBuf {
self.root.join("README.md")
}
pub fn manifest(&self) -> PathBuf {
pub fn manifest(&self) -> Utf8PathBuf {
self.root.join("manifest.toml")
}
pub fn src_directory(&self) -> PathBuf {
pub fn src_directory(&self) -> Utf8PathBuf {
self.root.join("src")
}
pub fn test_directory(&self) -> PathBuf {
pub fn test_directory(&self) -> Utf8PathBuf {
self.root.join("test")
}
pub fn build_directory(&self) -> PathBuf {
pub fn build_directory(&self) -> Utf8PathBuf {
self.root.join("build")
}
pub fn build_packages_directory(&self) -> PathBuf {
pub fn build_packages_directory(&self) -> Utf8PathBuf {
self.build_directory().join("packages")
}
pub fn build_packages_toml(&self) -> PathBuf {
pub fn build_packages_toml(&self) -> Utf8PathBuf {
self.build_packages_directory().join("packages.toml")
}
pub fn build_packages_package(&self, package_name: &str) -> PathBuf {
pub fn build_packages_package(&self, package_name: &str) -> Utf8PathBuf {
self.build_packages_directory().join(package_name)
}
// build_deps_package_config
pub fn build_packages_package_config(&self, package_name: &str) -> PathBuf {
pub fn build_packages_package_config(&self, package_name: &str) -> Utf8PathBuf {
self.build_packages_package(package_name).join("gleam.toml")
}
pub fn build_export_hex_tarball(&self, package_name: &str, version: &str) -> PathBuf {
pub fn build_export_hex_tarball(&self, package_name: &str, version: &str) -> Utf8PathBuf {
self.build_directory()
.join(format!("{package_name}-{version}.tar"))
}
pub fn build_directory_for_mode(&self, mode: Mode) -> PathBuf {
pub fn build_directory_for_mode(&self, mode: Mode) -> Utf8PathBuf {
self.build_directory().join(mode.to_string())
}
pub fn erlang_shipment_directory(&self) -> PathBuf {
pub fn erlang_shipment_directory(&self) -> Utf8PathBuf {
self.build_directory().join("erlang-shipment")
}
pub fn build_documentation_directory(&self, package: &str) -> PathBuf {
pub fn build_documentation_directory(&self, package: &str) -> Utf8PathBuf {
self.build_directory_for_mode(Mode::Dev)
.join("docs")
.join(package)
}
pub fn build_directory_for_target(&self, mode: Mode, target: Target) -> PathBuf {
pub fn build_directory_for_target(&self, mode: Mode, target: Target) -> Utf8PathBuf {
self.build_directory_for_mode(mode).join(target.to_string())
}
@ -97,11 +97,11 @@ impl ProjectPaths {
mode: Mode,
target: Target,
package: &str,
) -> PathBuf {
) -> Utf8PathBuf {
self.build_directory_for_target(mode, target).join(package)
}
pub fn build_packages_ebins_glob(&self, mode: Mode, target: Target) -> PathBuf {
pub fn build_packages_ebins_glob(&self, mode: Mode, target: Target) -> Utf8PathBuf {
self.build_directory_for_package(mode, target, "*")
.join("ebin")
}
@ -109,31 +109,34 @@ impl ProjectPaths {
/// A path to a special file that contains the version of gleam that last built
/// the artifacts. If this file does not match the current version of gleam we
/// will rebuild from scratch
pub fn build_gleam_version(&self, mode: Mode, target: Target) -> PathBuf {
pub fn build_gleam_version(&self, mode: Mode, target: Target) -> Utf8PathBuf {
self.build_directory_for_target(mode, target)
.join("gleam_version")
}
}
pub fn global_package_cache_package_tarball(package_name: &str, version: &str) -> PathBuf {
pub fn global_package_cache_package_tarball(package_name: &str, version: &str) -> Utf8PathBuf {
global_packages_cache().join(format!("{package_name}-{version}.tar"))
}
fn global_packages_cache() -> PathBuf {
fn global_packages_cache() -> Utf8PathBuf {
default_global_gleam_cache()
.join("hex")
.join("hexpm")
.join("packages")
}
pub fn default_global_gleam_cache() -> PathBuf {
dirs_next::cache_dir()
.expect("Failed to determine user cache directory")
.join("gleam")
pub fn default_global_gleam_cache() -> Utf8PathBuf {
Utf8PathBuf::from_path_buf(
dirs_next::cache_dir()
.expect("Failed to determine user cache directory")
.join("gleam"),
)
.expect("Non Utf8 Path")
}
pub fn unnest(within: &Path) -> PathBuf {
let mut path = PathBuf::new();
pub fn unnest(within: &Utf8Path) -> Utf8PathBuf {
let mut path = Utf8PathBuf::new();
for _ in within {
path = path.join("..")
}

View file

@ -1,8 +1,8 @@
use std::fmt;
use std::path::PathBuf;
use std::str::FromStr;
use crate::error::Result;
use camino::Utf8PathBuf;
use hexpm::version::Range;
use serde::de::{self, Deserializer, MapAccess, Visitor};
use serde::ser::{Serialize, SerializeMap, Serializer};
@ -13,7 +13,7 @@ use smol_str::SmolStr;
#[serde(untagged, remote = "Self")]
pub enum Requirement {
Hex { version: Range },
Path { path: PathBuf },
Path { path: Utf8PathBuf },
Git { git: SmolStr },
}
@ -37,7 +37,7 @@ impl Requirement {
Requirement::Hex { version: range } => {
format!(r#"{{ version = "{}" }}"#, range)
}
Requirement::Path { path } => format!(r#"{{ path = "{}" }}"#, path.display()),
Requirement::Path { path } => format!(r#"{{ path = "{}" }}"#, path),
Requirement::Git { git: url } => format!(r#"{{ git = "{}" }}"#, url),
}
}

View file

@ -3,7 +3,8 @@ use crate::{
type_::Type,
};
use std::{path::PathBuf, sync::Arc};
use camino::Utf8PathBuf;
use std::sync::Arc;
#[cfg(test)]
use pretty_assertions::assert_eq;
@ -410,7 +411,7 @@ impl Error {
}
impl Warning {
pub fn into_warning(self, path: PathBuf, src: SmolStr) -> crate::Warning {
pub fn into_warning(self, path: Utf8PathBuf, src: SmolStr) -> crate::Warning {
crate::Warning::Type {
path,
src,

View file

@ -9,9 +9,11 @@ use crate::{
};
use itertools::Itertools;
use smol_str::SmolStr;
use std::{path::PathBuf, sync::Arc};
use std::sync::Arc;
use vec1::Vec1;
use camino::Utf8PathBuf;
mod assert;
mod assignments;
mod errors;
@ -80,7 +82,7 @@ macro_rules! assert_error {
.expect_err("should infer an error");
let error = $crate::error::Error::Type {
src: $src.into(),
path: std::path::PathBuf::from("/src/one/two.gleam"),
path: camino::Utf8PathBuf::from("/src/one/two.gleam"),
error,
};
let output = error.pretty_string();
@ -130,7 +132,7 @@ fn get_printed_warnings(src: &str, deps: Vec<DependencyModule<'_>>) -> String {
let warnings = get_warnings(src, deps);
let mut nocolor = termcolor::Buffer::no_color();
for warning in warnings {
let path = std::path::PathBuf::from("/src/warning/wrn.gleam");
let path = Utf8PathBuf::from("/src/warning/wrn.gleam");
let warning = warning.into_warning(path, src.into());
warning.pretty(&mut nocolor);
}
@ -245,7 +247,7 @@ pub fn compile_module(
let ids = UniqueIdGenerator::new();
let mut modules = im::HashMap::new();
let warnings = TypeWarningEmitter::new(
PathBuf::new(),
Utf8PathBuf::new(),
"".into(),
WarningEmitter::new(
warnings.unwrap_or_else(|| Arc::new(VectorWarningEmitterIO::default())),
@ -292,7 +294,7 @@ pub fn module_error(src: &str, deps: Vec<DependencyModule<'_>>) -> String {
let error = compile_module(src, None, deps).expect_err("should infer an error");
let error = Error::Type {
src: src.into(),
path: PathBuf::from("/src/one/two.gleam"),
path: Utf8PathBuf::from("/src/one/two.gleam"),
error,
};
error.pretty_string()
@ -302,7 +304,7 @@ pub fn syntax_error(src: &str) -> String {
let error = crate::parse::parse_module(src).expect_err("should trigger an error when parsing");
let error = Error::Parse {
src: src.into(),
path: PathBuf::from("/src/one/two.gleam"),
path: Utf8PathBuf::from("/src/one/two.gleam"),
error,
};
error.pretty_string()

View file

@ -4,13 +4,14 @@ use crate::{
diagnostic::{self, Diagnostic, Location},
type_,
};
use camino::Utf8PathBuf;
use debug_ignore::DebugIgnore;
use smol_str::SmolStr;
use std::sync::atomic::AtomicUsize;
use std::{
io::Write,
sync::{atomic::Ordering, Arc},
};
use std::{path::PathBuf, sync::atomic::AtomicUsize};
use termcolor::Buffer;
pub trait WarningEmitterIO {
@ -91,13 +92,13 @@ impl WarningEmitter {
#[derive(Debug, Clone)]
pub struct TypeWarningEmitter {
module_path: PathBuf,
module_path: Utf8PathBuf,
module_src: SmolStr,
emitter: WarningEmitter,
}
impl TypeWarningEmitter {
pub fn new(module_path: PathBuf, module_src: SmolStr, emitter: WarningEmitter) -> Self {
pub fn new(module_path: Utf8PathBuf, module_src: SmolStr, emitter: WarningEmitter) -> Self {
Self {
module_path,
module_src,
@ -107,7 +108,7 @@ impl TypeWarningEmitter {
pub fn null() -> Self {
Self {
module_path: PathBuf::new(),
module_path: Utf8PathBuf::new(),
module_src: SmolStr::new(""),
emitter: WarningEmitter::new(Arc::new(NullWarningEmitterIO)),
}
@ -125,17 +126,17 @@ impl TypeWarningEmitter {
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum Warning {
Type {
path: PathBuf,
path: Utf8PathBuf,
src: SmolStr,
warning: crate::type_::Warning,
},
Parse {
path: PathBuf,
path: Utf8PathBuf,
src: SmolStr,
warning: crate::parse::Warning,
},
InvalidSource {
path: PathBuf,
path: Utf8PathBuf,
},
}
@ -241,7 +242,7 @@ impl Warning {
location: None,
hint: Some(format!(
"Rename `{}` to be valid, or remove this file from the project source.",
path.to_string_lossy()
path
)),
},
Self::Type { path, warning, src } => match warning {

View file

@ -20,6 +20,7 @@ wasm-bindgen = { version = "0.2", features = ["serde-serialize"] }
tracing-wasm = "*"
tracing = "*"
termcolor = "1.1.2"
camino = "1.1.6"
[dev-dependencies]
wasm-bindgen-test = "0.3.28"

View file

@ -1,4 +1,5 @@
use std::{collections::HashMap, ffi::OsStr, path::Path, sync::Arc};
use camino::Utf8Path;
use std::{collections::HashMap, sync::Arc};
use gleam_core::{
build::{Built, Codegen, Mode, Options, ProjectCompiler, Target},
@ -60,7 +61,7 @@ pub fn compile_(options: CompileOptions) -> Result<HashMap<String, String>, Stri
Ok(gather_compiled_files(&paths, &wfs, options.target).unwrap())
}
fn write_source_file<P: AsRef<Path>>(source: &str, path: P, wfs: &mut WasmFileSystem) {
fn write_source_file<P: AsRef<Utf8Path>>(source: &str, path: P, wfs: &mut WasmFileSystem) {
wfs.write(path.as_ref(), source)
.expect("should always succeed with the virtual file system");
}
@ -127,8 +128,8 @@ fn gather_compiled_files(
let mut files: HashMap<String, String> = HashMap::new();
let extension_to_search_for = match target {
Target::Erlang => OsStr::new("erl"),
Target::JavaScript => OsStr::new("mjs"),
Target::Erlang => "erl",
Target::JavaScript => "mjs",
};
wfs.read_dir(&paths.build_directory())
@ -139,7 +140,7 @@ fn gather_compiled_files(
.for_each(|dir_entry| {
let path = dir_entry.as_path();
let contents: String = wfs.read(path).expect("iterated dir entries should exist");
let path = path.to_str().unwrap().replace('\\', "/");
let path = path.as_str().replace('\\', "/");
files.insert(path, contents);
});

View file

@ -1,3 +1,4 @@
use camino::{Utf8Path, Utf8PathBuf};
use gleam_core::{
io::{
memory::InMemoryFileSystem, CommandExecutor, FileSystemReader, FileSystemWriter, ReadDir,
@ -5,7 +6,6 @@ use gleam_core::{
},
Error, Result,
};
use std::path::{Path, PathBuf};
#[derive(Clone, Debug)]
pub struct WasmFileSystem {
@ -26,7 +26,7 @@ impl CommandExecutor for WasmFileSystem {
_program: &str,
_args: &[String],
_env: &[(&str, String)],
_cwd: Option<&Path>,
_cwd: Option<&Utf8Path>,
_stdio: Stdio,
) -> Result<i32, Error> {
Ok(0) // Always succeed.
@ -34,91 +34,91 @@ impl CommandExecutor for WasmFileSystem {
}
impl FileSystemWriter for WasmFileSystem {
fn delete(&self, path: &Path) -> Result<(), Error> {
fn delete(&self, path: &Utf8Path) -> Result<(), Error> {
tracing::trace!("delete {:?}", path);
self.imfs.delete(path)
}
fn copy(&self, _from: &Path, _to: &Path) -> Result<(), Error> {
fn copy(&self, _from: &Utf8Path, _to: &Utf8Path) -> Result<(), Error> {
Ok(())
}
fn copy_dir(&self, _: &Path, _: &Path) -> Result<(), Error> {
fn copy_dir(&self, _: &Utf8Path, _: &Utf8Path) -> Result<(), Error> {
Ok(())
}
fn mkdir(&self, _: &Path) -> Result<(), Error> {
fn mkdir(&self, _: &Utf8Path) -> Result<(), Error> {
Ok(())
}
fn hardlink(&self, _: &Path, _: &Path) -> Result<(), Error> {
fn hardlink(&self, _: &Utf8Path, _: &Utf8Path) -> Result<(), Error> {
Ok(())
}
fn symlink_dir(&self, _: &Path, _: &Path) -> Result<(), Error> {
fn symlink_dir(&self, _: &Utf8Path, _: &Utf8Path) -> Result<(), Error> {
Ok(())
}
fn delete_file(&self, path: &Path) -> Result<(), Error> {
fn delete_file(&self, path: &Utf8Path) -> Result<(), Error> {
tracing::trace!("delete file {:?}", path);
self.imfs.delete_file(path)
}
fn write(&self, path: &Path, content: &str) -> Result<(), Error> {
fn write(&self, path: &Utf8Path, content: &str) -> Result<(), Error> {
tracing::trace!("write {:?}", path);
self.imfs.write(path, content)
}
fn write_bytes(&self, path: &Path, content: &[u8]) -> Result<(), Error> {
fn write_bytes(&self, path: &Utf8Path, content: &[u8]) -> Result<(), Error> {
tracing::trace!("write_bytes {:?}", path);
self.imfs.write_bytes(path, content)
}
}
impl FileSystemReader for WasmFileSystem {
fn gleam_source_files(&self, dir: &Path) -> Vec<PathBuf> {
fn gleam_source_files(&self, dir: &Utf8Path) -> Vec<Utf8PathBuf> {
tracing::trace!("gleam_source_files {:?}", dir);
self.imfs.gleam_source_files(dir)
}
fn gleam_cache_files(&self, dir: &Path) -> Vec<PathBuf> {
fn gleam_cache_files(&self, dir: &Utf8Path) -> Vec<Utf8PathBuf> {
tracing::trace!("gleam_metadata_files {:?}", dir);
self.imfs.gleam_cache_files(dir)
}
fn read(&self, path: &Path) -> Result<String, Error> {
fn read(&self, path: &Utf8Path) -> Result<String, Error> {
tracing::trace!("read {:?}", path);
self.imfs.read(path)
}
fn is_file(&self, path: &Path) -> bool {
fn is_file(&self, path: &Utf8Path) -> bool {
tracing::info!("is_file {:?}", path);
self.imfs.is_file(path)
}
fn is_directory(&self, path: &Path) -> bool {
fn is_directory(&self, path: &Utf8Path) -> bool {
tracing::trace!("is_directory {:?}", path);
false
}
fn reader(&self, path: &Path) -> Result<WrappedReader, Error> {
fn reader(&self, path: &Utf8Path) -> Result<WrappedReader, Error> {
tracing::trace!("reader {:?}", path);
self.imfs.reader(path)
}
fn read_dir(&self, path: &Path) -> Result<ReadDir> {
fn read_dir(&self, path: &Utf8Path) -> Result<ReadDir> {
tracing::trace!("read_dir {:?}", path);
self.imfs.read_dir(path)
}
fn modification_time(&self, path: &Path) -> Result<std::time::SystemTime, Error> {
fn modification_time(&self, path: &Utf8Path) -> Result<std::time::SystemTime, Error> {
self.imfs.modification_time(path)
}
fn read_bytes(&self, path: &Path) -> Result<Vec<u8>, Error> {
fn read_bytes(&self, path: &Utf8Path) -> Result<Vec<u8>, Error> {
self.imfs.read_bytes(path)
}
fn canonicalise(&self, path: &Path) -> Result<PathBuf, Error> {
fn canonicalise(&self, path: &Utf8Path) -> Result<Utf8PathBuf, Error> {
self.imfs.canonicalise(path)
}
}

View file

@ -17,6 +17,7 @@ itertools = "0.10.1"
walkdir = "2.3.2"
# Regular expressions
regex = "*"
camino = "1.1.6"
[dev-dependencies]
# Snapshot testing to make test maintenance easier

View file

@ -13,16 +13,12 @@ use gleam_core::{
};
use itertools::Itertools;
use regex::Regex;
use std::{
collections::HashMap,
ffi::OsStr,
fmt::Write,
path::{Path, PathBuf},
sync::Arc,
};
use std::{collections::HashMap, fmt::Write, sync::Arc};
use camino::{Utf8Path, Utf8PathBuf};
pub fn prepare(path: &str) -> String {
let root = PathBuf::from(path).canonicalize().unwrap();
let root = Utf8PathBuf::from(path).canonicalize_utf8().unwrap();
let toml = std::fs::read_to_string(root.join("gleam.toml")).unwrap();
let config: PackageConfig = toml::from_str(&toml).unwrap();
@ -44,9 +40,9 @@ pub fn prepare(path: &str) -> String {
let warning_emitter = WarningEmitter::new(Arc::new(warnings.clone()));
let filesystem = to_in_memory_filesystem(&root);
let initial_files = filesystem.paths();
let root = PathBuf::from("");
let out = PathBuf::from("/out/lib/the_package");
let lib = PathBuf::from("/out/lib");
let root = Utf8PathBuf::from("");
let out = Utf8PathBuf::from("/out/lib/the_package");
let lib = Utf8PathBuf::from("/out/lib");
let mut compiler = gleam_core::build::PackageCompiler::new(
&config,
Mode::Dev,
@ -94,7 +90,7 @@ fn normalise_diagnostic(text: &str) -> String {
#[derive(Debug)]
pub struct TestCompileOutput {
files: HashMap<PathBuf, Content>,
files: HashMap<Utf8PathBuf, Content>,
warnings: Vec<gleam_core::Warning>,
}
@ -103,10 +99,10 @@ impl TestCompileOutput {
let mut buffer = String::new();
for (path, content) in self.files.iter().sorted_by(|a, b| a.0.cmp(b.0)) {
buffer.push_str("//// ");
buffer.push_str(&path.to_str().unwrap().replace('\\', "/"));
buffer.push_str(&path.as_str().replace('\\', "/"));
buffer.push('\n');
let extension = path.extension().and_then(OsStr::to_str);
let extension = path.extension();
match content {
_ if extension == Some("cache") => buffer.push_str("<.cache binary>"),
@ -132,7 +128,7 @@ impl TestCompileOutput {
}
}
fn to_in_memory_filesystem(path: &Path) -> InMemoryFileSystem {
fn to_in_memory_filesystem(path: &Utf8Path) -> InMemoryFileSystem {
let fs = InMemoryFileSystem::new();
let files = walkdir::WalkDir::new(path)
@ -145,7 +141,8 @@ fn to_in_memory_filesystem(path: &Path) -> InMemoryFileSystem {
for fullpath in files {
let content = std::fs::read(&fullpath).unwrap();
let path = fullpath.strip_prefix(path).unwrap();
fs.write_bytes(path, &content).unwrap();
fs.write_bytes(Utf8Path::from_path(path).unwrap(), &content)
.unwrap();
}
fs