1use crate::{prelude::*, LintContext};
5use camino::Utf8Path;
6use std::{fs, io, path::Path};
7
8pub trait FilePathLinter: Linter {
10 fn run<'l>(
12 &self,
13 ctx: &FilePathContext<'l>,
14 out: &mut LintFormatter<'l, '_>,
15 ) -> Result<RunStatus<'l>>;
16}
17
18#[derive(Clone, Debug)]
20pub struct FilePathContext<'l> {
21 project_ctx: &'l ProjectContext<'l>,
22 file_path: &'l Utf8Path,
23}
24
25impl<'l> FilePathContext<'l> {
26 pub fn new(project_ctx: &'l ProjectContext<'l>, file_path: &'l Utf8Path) -> Self {
28 Self {
29 project_ctx,
30 file_path,
31 }
32 }
33
34 pub fn project_ctx(&self) -> &'l ProjectContext<'l> {
36 self.project_ctx
37 }
38
39 pub fn file_path(&self) -> &'l Utf8Path {
41 &self.file_path
42 }
43
44 pub fn extension(&self) -> Option<&'l str> {
46 self.file_path.extension()
47 }
48
49 pub(super) fn load(self) -> Result<Option<ContentContext<'l>>> {
55 let full_path = self.project_ctx.full_path(self.file_path);
56 let contents_opt = read_file(&full_path)
57 .map_err(|err| SystemError::io(format!("loading {}", full_path.display()), err))?;
58 Ok(contents_opt.map(|content| ContentContext::new(self, content)))
59 }
60}
61
62impl<'l> LintContext<'l> for FilePathContext<'l> {
63 fn kind(&self) -> LintKind<'l> {
64 LintKind::FilePath(self.file_path)
65 }
66}
67
68fn read_file(full_path: &Path) -> io::Result<Option<Vec<u8>>> {
69 match fs::read(full_path) {
70 Ok(bytes) => Ok(Some(bytes)),
71 Err(err) => {
72 if err.kind() == io::ErrorKind::NotFound {
73 Ok(None)
75 } else {
76 Err(err)
77 }
78 }
79 }
80}