Skip to main content

x_lint/
file_path.rs

1// Copyright (c) The Diem Core Contributors
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::{prelude::*, LintContext};
5use camino::Utf8Path;
6use std::{fs, io, path::Path};
7
8/// Represents a linter that runs once per file path.
9pub trait FilePathLinter: Linter {
10    /// Executes this linter against the given file path context.
11    fn run<'l>(
12        &self,
13        ctx: &FilePathContext<'l>,
14        out: &mut LintFormatter<'l, '_>,
15    ) -> Result<RunStatus<'l>>;
16}
17
18/// Contains information for a single file path.
19#[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    /// Constructs a new context.
27    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    /// Returns the project context.
35    pub fn project_ctx(&self) -> &'l ProjectContext<'l> {
36        self.project_ctx
37    }
38
39    /// Returns the path of this file, relative to the root of the repository.
40    pub fn file_path(&self) -> &'l Utf8Path {
41        &self.file_path
42    }
43
44    /// Returns the extension of the file. Returns `None` if there's no extension.
45    pub fn extension(&self) -> Option<&'l str> {
46        self.file_path.extension()
47    }
48
49    /// Loads this file and turns it into a `ContentContext`.
50    ///
51    /// Returns `None` if the file is missing.
52    ///
53    /// `pub(super)` is to dissuade individual linters from loading file contexts.
54    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                // Files can be listed by source control but missing -- this is normal.
74                Ok(None)
75            } else {
76                Err(err)
77            }
78        }
79    }
80}