Skip to main content

stern4rust/
source_reader.rs

1// Copyright 2025 Umberto Gotti <umberto.gotti@umbertogotti.dev>
2// Licensed under the MIT License
3// SPDX-License-Identifier: MIT
4
5use std::fs;
6use std::path::Path;
7
8use crate::manifest_resolver::ManifestResolver;
9use crate::offence::Offence;
10use crate::rules::readable_source_rule::ReadableSourceRule;
11use crate::source_file::SourceFile;
12
13// Reads a file the walker found, and turns a failure into a finding rather than
14// into the end of the run.
15//
16// A file that cannot be read used to abort everything, so one unreadable file
17// hid every offence already found in every other file. That is the wrong trade.
18// A bad manifest is genuinely a could-not-run condition -- without it nothing
19// can be enumerated -- but a single unreadable file is a fact about the tree,
20// and the rest of the tree is still worth reporting on.
21pub struct SourceReader;
22
23impl SourceReader {
24    // The offence is boxed because it is much larger than the SourceFile it is
25    // returned instead of, and an unboxed Err variant that size makes every
26    // successful read pay for the failing one.
27    pub fn read(root: &Path, path: &Path) -> Result<SourceFile, Box<Offence>> {
28        let relative = ManifestResolver::relative_to(root, path);
29        match fs::read_to_string(path) {
30            Ok(contents) => Ok(SourceFile::new(&relative, &contents)),
31            Err(error) => Err(Box::new(
32                Offence::new(
33                    &relative,
34                    1,
35                    ReadableSourceRule::NAME,
36                    format!("file could not be read: {error}"),
37                    "check that the file exists and that its permissions allow reading it"
38                        .to_string(),
39                )
40                .with_subject(&relative),
41            )),
42        }
43    }
44}