1use std::ffi::OsStr;
2use std::fmt;
3use std::fs;
4use std::io;
5use std::iter;
6use std::path::{Path, PathBuf};
7
8use anyhow::Result;
9use relative_path::{RelativePath, RelativePathBuf};
10use semver::Version;
11use serde::de::IntoDeserializer;
12use serde::Deserialize;
13use serde_hashkey as key;
14
15use crate as rune;
16use crate::alloc::prelude::*;
17use crate::alloc::{self, String, Vec};
18use crate::ast::{Span, Spanned};
19use crate::workspace::spanned_value::{Array, SpannedValue, Table, Value};
20use crate::workspace::{
21 glob, Diagnostics, SourceLoader, WorkspaceError, WorkspaceErrorKind, MANIFEST_FILE,
22};
23use crate::{SourceId, Sources};
24
25const BIN: &str = "bin";
26const TESTS: &str = "tests";
27const EXAMPLES: &str = "examples";
28const BENCHES: &str = "benches";
29
30#[derive(Debug, Clone, Copy)]
34#[non_exhaustive]
35pub enum WorkspaceFilter<'a> {
36 Name(&'a str),
38 All,
40}
41
42#[derive(Debug, Clone, Copy)]
44#[non_exhaustive]
45pub enum FoundKind {
46 Binary,
48 Test,
50 Example,
52 Bench,
54}
55
56impl fmt::Display for FoundKind {
57 #[inline]
58 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59 match self {
60 FoundKind::Binary => "bin".fmt(f),
61 FoundKind::Test => "test".fmt(f),
62 FoundKind::Example => "example".fmt(f),
63 FoundKind::Bench => "bench".fmt(f),
64 }
65 }
66}
67
68#[derive(Debug, TryClone)]
70#[non_exhaustive]
71pub struct Found {
72 #[try_clone(copy)]
74 pub kind: FoundKind,
75 pub path: PathBuf,
77 pub name: String,
79}
80
81#[derive(Debug, TryClone)]
83#[non_exhaustive]
84pub struct FoundPackage<'a> {
85 pub found: Found,
87 pub package: &'a Package,
89}
90
91impl WorkspaceFilter<'_> {
92 fn matches(self, name: &str) -> bool {
93 match self {
94 WorkspaceFilter::Name(expected) => name == expected,
95 WorkspaceFilter::All => true,
96 }
97 }
98}
99
100impl<T> Spanned for toml::Spanned<T> {
101 #[inline]
102 fn span(&self) -> Span {
103 let range = toml::Spanned::span(self);
104 Span::new(range.start, range.end)
105 }
106}
107
108#[derive(Default, Debug)]
110#[non_exhaustive]
111pub struct Manifest {
112 pub packages: Vec<Package>,
114}
115
116impl Manifest {
117 fn find_paths<'m>(
118 &'m self,
119 m: WorkspaceFilter<'_>,
120 kind: FoundKind,
121 auto_path: &Path,
122 auto_find: fn(&Package) -> bool,
123 ) -> Result<Vec<FoundPackage<'m>>> {
124 let mut output = Vec::new();
125
126 for package in self.packages.iter() {
127 for found in package.find_paths(m, kind, auto_path, auto_find)? {
128 output.try_push(FoundPackage { found, package })?;
129 }
130 }
131
132 Ok(output)
133 }
134
135 pub fn find_all(&self, m: WorkspaceFilter<'_>) -> Result<Vec<FoundPackage<'_>>> {
137 let mut output = Vec::new();
138 output.try_extend(self.find_bins(m)?)?;
139 output.try_extend(self.find_tests(m)?)?;
140 output.try_extend(self.find_examples(m)?)?;
141 output.try_extend(self.find_benches(m)?)?;
142 Ok(output)
143 }
144
145 pub fn find_bins(&self, m: WorkspaceFilter<'_>) -> Result<Vec<FoundPackage<'_>>> {
147 self.find_paths(m, FoundKind::Binary, Path::new(BIN), |p| p.auto_bins)
148 }
149
150 pub fn find_tests(&self, m: WorkspaceFilter<'_>) -> Result<Vec<FoundPackage<'_>>> {
152 self.find_paths(m, FoundKind::Test, Path::new(TESTS), |p| p.auto_tests)
153 }
154
155 pub fn find_examples(&self, m: WorkspaceFilter<'_>) -> Result<Vec<FoundPackage<'_>>> {
157 self.find_paths(m, FoundKind::Example, Path::new(EXAMPLES), |p| {
158 p.auto_examples
159 })
160 }
161
162 pub fn find_benches(&self, m: WorkspaceFilter<'_>) -> Result<Vec<FoundPackage<'_>>> {
164 self.find_paths(m, FoundKind::Bench, Path::new(BENCHES), |p| p.auto_benches)
165 }
166}
167
168#[derive(Debug)]
170#[non_exhaustive]
171pub struct Package {
172 pub name: String,
174 pub version: Version,
176 pub root: Option<PathBuf>,
178 pub auto_bins: bool,
180 pub auto_tests: bool,
182 pub auto_examples: bool,
184 pub auto_benches: bool,
186}
187
188impl Package {
189 fn find_paths(
190 &self,
191 m: WorkspaceFilter<'_>,
192 kind: FoundKind,
193 auto_path: &Path,
194 auto_find: fn(&Package) -> bool,
195 ) -> Result<Vec<Found>> {
196 let mut output = Vec::new();
197
198 if let (Some(path), true) = (&self.root, auto_find(self)) {
199 let path = path.join(auto_path);
200 let results = find_rune_files(&path)?;
201
202 for result in results {
203 let (path, name) = result?;
204
205 if m.matches(&name) {
206 output.try_push(Found { kind, path, name })?;
207 }
208 }
209 }
210
211 Ok(output)
212 }
213
214 pub fn find_all(&self, m: WorkspaceFilter<'_>) -> Result<Vec<Found>> {
216 let mut output = Vec::new();
217 output.try_extend(self.find_bins(m)?)?;
218 output.try_extend(self.find_tests(m)?)?;
219 output.try_extend(self.find_examples(m)?)?;
220 output.try_extend(self.find_benches(m)?)?;
221 Ok(output)
222 }
223
224 pub fn find_bins(&self, m: WorkspaceFilter<'_>) -> Result<Vec<Found>> {
226 self.find_paths(m, FoundKind::Binary, Path::new(BIN), |p| p.auto_bins)
227 }
228
229 pub fn find_tests(&self, m: WorkspaceFilter<'_>) -> Result<Vec<Found>> {
231 self.find_paths(m, FoundKind::Test, Path::new(TESTS), |p| p.auto_tests)
232 }
233
234 pub fn find_examples(&self, m: WorkspaceFilter<'_>) -> Result<Vec<Found>> {
236 self.find_paths(m, FoundKind::Example, Path::new(EXAMPLES), |p| {
237 p.auto_examples
238 })
239 }
240
241 pub fn find_benches(&self, m: WorkspaceFilter<'_>) -> Result<Vec<Found>> {
243 self.find_paths(m, FoundKind::Bench, Path::new(BENCHES), |p| p.auto_benches)
244 }
245}
246
247pub(crate) struct Loader<'a> {
248 id: SourceId,
249 sources: &'a mut Sources,
250 diagnostics: &'a mut Diagnostics,
251 source_loader: &'a mut dyn SourceLoader,
252 manifest: &'a mut Manifest,
253}
254
255impl<'a> Loader<'a> {
256 pub(crate) fn new(
257 id: SourceId,
258 sources: &'a mut Sources,
259 diagnostics: &'a mut Diagnostics,
260 source_loader: &'a mut dyn SourceLoader,
261 manifest: &'a mut Manifest,
262 ) -> Self {
263 Self {
264 id,
265 sources,
266 diagnostics,
267 source_loader,
268 manifest,
269 }
270 }
271
272 pub(crate) fn load_manifest(&mut self) -> Result<()> {
274 let Some(source) = self.sources.get(self.id) else {
275 self.fatal(WorkspaceError::new(
276 Span::empty(),
277 WorkspaceErrorKind::MissingSourceId { source_id: self.id },
278 ))?;
279 return Ok(());
280 };
281
282 let value: SpannedValue = match toml::from_str(source.as_str()) {
283 Ok(value) => value,
284 Err(e) => {
285 let span = match e.span() {
286 Some(span) => Span::new(span.start, span.end),
287 None => Span::new(0, source.len()),
288 };
289
290 self.fatal(WorkspaceError::new(span, e))?;
291 return Ok(());
292 }
293 };
294
295 let root = source
296 .path()
297 .and_then(|p| p.parent().map(TryToOwned::try_to_owned))
298 .transpose()?;
299 let root = root.as_deref();
300
301 let Some((mut table, _)) = self.ensure_table(value)? else {
302 return Ok(());
303 };
304
305 if let Some((package, span)) = table
307 .remove("package")
308 .map(|value| self.ensure_table(value))
309 .transpose()?
310 .flatten()
311 {
312 if let Some(package) = self.load_package(package, span, root)? {
313 self.manifest.packages.try_push(package)?;
314 }
315 }
316
317 if let Some((mut table, span)) = table
319 .remove("workspace")
320 .map(|value| self.ensure_table(value))
321 .transpose()?
322 .flatten()
323 {
324 match &root {
325 Some(root) => {
326 if let Some(members) = self.load_members(&mut table, root)? {
327 for (span, path) in members {
328 self.load_member(span, &path)?;
329 }
330 }
331 }
332 None => {
333 self.fatal(WorkspaceError::new(
334 span,
335 WorkspaceErrorKind::MissingManifestPath,
336 ))?;
337 }
338 }
339
340 self.ensure_empty(table)?;
341 }
342
343 self.ensure_empty(table)?;
344 Ok(())
345 }
346
347 fn load_members(
349 &mut self,
350 table: &mut Table,
351 root: &Path,
352 ) -> Result<Option<Vec<(Span, PathBuf)>>> {
353 let Some(members) = table.remove("members") else {
354 return Ok(None);
355 };
356
357 let Some((members, _)) = self.ensure_array(members)? else {
358 return Ok(None);
359 };
360
361 let mut output = Vec::new();
362
363 for value in members {
364 let span = Spanned::span(&value);
365
366 match deserialize::<RelativePathBuf>(value) {
367 Ok(member) => {
368 self.glob_relative_path(&mut output, span, &member, root)?;
369 }
370 Err(error) => {
371 self.fatal(error)?;
372 }
373 };
374 }
375
376 Ok(Some(output))
377 }
378
379 fn glob_relative_path(
384 &mut self,
385 output: &mut Vec<(Span, PathBuf)>,
386 span: Span,
387 member: &RelativePath,
388 root: &Path,
389 ) -> Result<()> {
390 let glob = glob::Glob::new(root, member)?;
391
392 for m in glob.matcher()? {
393 let Some(mut path) = self.glob_error(span, root, m)? else {
394 continue;
395 };
396
397 path.push(MANIFEST_FILE);
398
399 if !path.is_file() {
400 continue;
401 }
402
403 output.try_push((span, path))?;
404 }
405
406 Ok(())
407 }
408
409 fn glob_error<T>(
411 &mut self,
412 span: Span,
413 path: &Path,
414 result: Result<T, glob::GlobError>,
415 ) -> alloc::Result<Option<T>> {
416 Ok(match result {
417 Ok(result) => Some(result),
418 Err(error) => {
419 self.fatal(WorkspaceError::new(
420 span,
421 WorkspaceErrorKind::GlobError {
422 path: path.try_into()?,
423 error,
424 },
425 ))?;
426
427 None
428 }
429 })
430 }
431
432 fn load_member(&mut self, span: Span, path: &Path) -> Result<()> {
434 let source = match self.source_loader.load(span, path) {
435 Ok(source) => source,
436 Err(error) => {
437 self.fatal(error)?;
438 return Ok(());
439 }
440 };
441
442 let id = self.sources.insert(source)?;
443 let old = std::mem::replace(&mut self.id, id);
444 self.load_manifest()?;
445 self.id = old;
446 Ok(())
447 }
448
449 fn load_package(
451 &mut self,
452 mut table: Table,
453 span: Span,
454 root: Option<&Path>,
455 ) -> alloc::Result<Option<Package>> {
456 let name = self.field(&mut table, span, "name")?;
457 let version = self.field(&mut table, span, "version")?;
458 self.ensure_empty(table)?;
459
460 let (Some(name), Some(version)) = (name, version) else {
461 return Ok(None);
462 };
463
464 Ok(Some(Package {
465 name,
466 version,
467 root: root.map(|p| p.into()),
468 auto_bins: true,
469 auto_tests: true,
470 auto_examples: true,
471 auto_benches: true,
472 }))
473 }
474
475 fn ensure_empty(&mut self, table: Table) -> alloc::Result<()> {
477 for (key, _) in table {
478 let span = Spanned::span(&key);
479 self.fatal(WorkspaceError::new(
480 span,
481 WorkspaceErrorKind::UnsupportedKey {
482 key: key.get_ref().as_str().try_into()?,
483 },
484 ))?;
485 }
486
487 Ok(())
488 }
489
490 fn ensure_table(&mut self, value: SpannedValue) -> alloc::Result<Option<(Table, Span)>> {
492 let span = Spanned::span(&value);
493
494 Ok(match value.into_inner() {
495 Value::Table(table) => Some((table, span)),
496 _ => {
497 let error = WorkspaceError::new(span, WorkspaceErrorKind::ExpectedTable);
498 self.fatal(error)?;
499 None
500 }
501 })
502 }
503
504 fn ensure_array(&mut self, value: SpannedValue) -> alloc::Result<Option<(Array, Span)>> {
506 let span = Spanned::span(&value);
507
508 Ok(match value.into_inner() {
509 Value::Array(array) => Some((array, span)),
510 _ => {
511 let error = WorkspaceError::expected_array(span);
512 self.fatal(error)?;
513 None
514 }
515 })
516 }
517
518 fn field<T>(
520 &mut self,
521 table: &mut Table,
522 span: Span,
523 field: &'static str,
524 ) -> alloc::Result<Option<T>>
525 where
526 T: for<'de> Deserialize<'de>,
527 {
528 Ok(match table.remove(field) {
529 Some(value) => match deserialize(value) {
530 Ok(value) => Some(value),
531 Err(error) => {
532 self.fatal(error)?;
533 None
534 }
535 },
536 None => {
537 let error = WorkspaceError::missing_field(span, field);
538 self.fatal(error)?;
539 None
540 }
541 })
542 }
543
544 fn fatal(&mut self, error: WorkspaceError) -> alloc::Result<()> {
546 self.diagnostics.fatal(self.id, error)
547 }
548}
549
550fn deserialize<T>(value: SpannedValue) -> Result<T, WorkspaceError>
552where
553 T: for<'de> Deserialize<'de>,
554{
555 let span = Spanned::span(&value);
556 let f = key::to_key(value.get_ref()).map_err(|e| WorkspaceError::new(span, e))?;
557 let deserializer = f.into_deserializer();
558 let value = T::deserialize(deserializer).map_err(|e| WorkspaceError::new(span, e))?;
559 Ok(value)
560}
561
562fn find_rune_files(path: &Path) -> Result<impl Iterator<Item = Result<(PathBuf, String)>>> {
564 let mut dir = match fs::read_dir(path) {
565 Ok(dir) => Some(dir),
566 Err(e) if e.kind() == io::ErrorKind::NotFound => None,
567 Err(e) => return Err(e.into()),
568 };
569
570 Ok(iter::from_fn(move || loop {
571 let e = dir.as_mut()?.next()?;
572
573 let e = match e {
574 Ok(e) => e,
575 Err(err) => return Some(Err(err.into())),
576 };
577
578 let m = match e.metadata() {
579 Ok(m) => m,
580 Err(err) => return Some(Err(err.into())),
581 };
582
583 if !m.is_file() {
584 continue;
585 }
586
587 let path = e.path();
588
589 let (Some(name), Some(ext)) = (path.file_stem().and_then(OsStr::to_str), path.extension())
590 else {
591 continue;
592 };
593
594 if ext != OsStr::new("rn") {
595 continue;
596 }
597
598 let name = match String::try_from(name) {
599 Ok(name) => name,
600 Err(error) => return Some(Err(error.into())),
601 };
602
603 return Some(Ok((path, name)));
604 }))
605}