Fork of daniellemaywood.uk/gleam — Wasm codegen work
5.1 kB
185 lines
1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: 2023 The Gleam contributors
3
4use std::collections::HashSet;
5use std::time::SystemTime;
6
7use debug_ignore::DebugIgnore;
8
9use gleam_core::{
10 Result,
11 error::Error,
12 io::{
13 BeamCompiler, Command, CommandExecutor, FileSystemReader, FileSystemWriter, ReadDir, Stdio,
14 WrappedReader, memory::InMemoryFileSystem,
15 },
16};
17
18use camino::{Utf8Path, Utf8PathBuf};
19
20// A proxy intended for `LanguageServer` to use when files are modified in
21// memory but not yet saved to disc by the client.
22//
23// Uses the `IO` for writing directly to disk, or `InMemoryFileSystem` to
24// cache files that were not yet saved. Reading files will always first try the
25// `InMemoryFileSystem` first and fallback to use the `ProjectIO` if the file
26// was not found in the cache.
27//
28#[derive(Debug, Clone)]
29pub struct FileSystemProxy<IO> {
30 io: DebugIgnore<IO>,
31 edit_cache: InMemoryFileSystem,
32}
33
34impl<IO> FileSystemProxy<IO>
35where
36 IO: FileSystemWriter + FileSystemReader + CommandExecutor,
37{
38 pub fn new(io: IO) -> Self {
39 Self {
40 io: io.into(),
41 edit_cache: InMemoryFileSystem::new(),
42 }
43 }
44
45 pub fn inner(&self) -> &IO {
46 &self.io
47 }
48
49 pub fn write_mem_cache(&mut self, path: &Utf8Path, content: &str) -> Result<()> {
50 let write_result = self.edit_cache.write(path, content);
51 self.edit_cache
52 .try_set_modification_time(path, SystemTime::now())?;
53 write_result
54 }
55
56 pub fn delete_mem_cache(&self, path: &Utf8Path) -> Result<()> {
57 if self.edit_cache.is_directory(path) {
58 self.edit_cache.delete_directory(path)
59 } else {
60 self.edit_cache.delete_file(path)
61 }
62 }
63}
64
65// All write operations goes to disk (for mem-cache use the dedicated `_mem_cache` methods)
66impl<IO> FileSystemWriter for FileSystemProxy<IO>
67where
68 IO: FileSystemWriter,
69{
70 fn mkdir(&self, path: &Utf8Path) -> Result<()> {
71 self.io.mkdir(path)
72 }
73
74 fn write(&self, path: &Utf8Path, content: &str) -> Result<()> {
75 self.io.write(path, content)
76 }
77
78 fn write_bytes(&self, path: &Utf8Path, content: &[u8]) -> Result<()> {
79 self.io.write_bytes(path, content)
80 }
81
82 fn delete_directory(&self, path: &Utf8Path) -> Result<()> {
83 self.io.delete_directory(path)
84 }
85
86 fn copy(&self, from: &Utf8Path, to: &Utf8Path) -> Result<()> {
87 self.io.copy(from, to)
88 }
89
90 fn copy_dir(&self, from: &Utf8Path, to: &Utf8Path) -> Result<()> {
91 self.io.copy_dir(from, to)
92 }
93
94 fn hardlink(&self, from: &Utf8Path, to: &Utf8Path) -> Result<()> {
95 self.io.hardlink(from, to)
96 }
97
98 fn symlink_dir(&self, from: &Utf8Path, to: &Utf8Path) -> Result<()> {
99 self.io.symlink_dir(from, to)
100 }
101
102 fn delete_file(&self, path: &Utf8Path) -> Result<()> {
103 self.io.delete_file(path)
104 }
105
106 fn exists(&self, path: &Utf8Path) -> bool {
107 self.io.exists(path)
108 }
109}
110
111impl<IO> FileSystemReader for FileSystemProxy<IO>
112where
113 IO: FileSystemReader,
114{
115 fn read_dir(&self, path: &Utf8Path) -> Result<ReadDir> {
116 self.io.read_dir(path)
117 }
118
119 fn read(&self, path: &Utf8Path) -> Result<String> {
120 match self.edit_cache.read(path) {
121 result @ Ok(_) => result,
122 Err(_) => self.io.read(path),
123 }
124 }
125
126 fn read_bytes(&self, path: &Utf8Path) -> Result<Vec<u8>> {
127 match self.edit_cache.read_bytes(path) {
128 result @ Ok(_) => result,
129 Err(_) => self.io.read_bytes(path),
130 }
131 }
132
133 fn reader(&self, path: &Utf8Path) -> Result<WrappedReader> {
134 self.io.reader(path)
135 }
136
137 // Cache overrides existence of file
138 fn is_file(&self, path: &Utf8Path) -> bool {
139 self.edit_cache.is_file(path) || self.io.is_file(path)
140 }
141
142 // Cache overrides existence of directory
143 fn is_directory(&self, path: &Utf8Path) -> bool {
144 self.edit_cache.is_directory(path) || self.io.is_directory(path)
145 }
146
147 fn modification_time(&self, path: &Utf8Path) -> Result<SystemTime> {
148 match self.edit_cache.modification_time(path) {
149 result @ Ok(_) => result,
150 Err(_) => self.io.modification_time(path),
151 }
152 }
153
154 fn canonicalise(&self, path: &Utf8Path) -> Result<Utf8PathBuf, Error> {
155 self.io.canonicalise(path)
156 }
157
158 fn is_same_file(&self, _left: &Utf8Path, _right: &Utf8Path) -> Result<bool, Error> {
159 unreachable!("is_same_file unimplemented")
160 }
161}
162
163impl<IO> CommandExecutor for FileSystemProxy<IO>
164where
165 IO: CommandExecutor,
166{
167 fn exec(&self, _command: Command) -> Result<i32> {
168 panic!("The language server is not permitted to create subprocesses")
169 }
170}
171
172impl<IO> BeamCompiler for FileSystemProxy<IO>
173where
174 IO: BeamCompiler,
175{
176 fn compile_beam(
177 &self,
178 _out: &Utf8Path,
179 _lib: &Utf8Path,
180 _modules: &HashSet<Utf8PathBuf>,
181 _stdio: Stdio,
182 ) -> Result<Vec<String>, Error> {
183 panic!("The language server is not permitted to create subprocesses")
184 }
185}