1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
use crate::{prelude::*, LintContext};
use std::str;
pub trait ContentLinter: Linter {
fn pre_run<'l>(&self, _file_ctx: &FilePathContext<'l>) -> Result<RunStatus<'l>> {
Ok(RunStatus::Executed)
}
fn run<'l>(
&self,
ctx: &ContentContext<'l>,
out: &mut LintFormatter<'l, '_>,
) -> Result<RunStatus<'l>>;
}
#[derive(Debug)]
pub struct ContentContext<'l> {
file_ctx: FilePathContext<'l>,
content: Content,
}
#[allow(dead_code)]
impl<'l> ContentContext<'l> {
pub const BINARY_FILE_CUTOFF: usize = 8000;
pub(super) fn new(file_ctx: FilePathContext<'l>, content: Vec<u8>) -> Self {
Self {
file_ctx,
content: Content::new(content),
}
}
pub fn file_ctx(&self) -> &FilePathContext<'l> {
&self.file_ctx
}
pub fn content(&self) -> Option<&str> {
match &self.content {
Content::Utf8(text) => Some(text.as_ref()),
Content::NonUtf8(_) => None,
}
}
pub fn content_bytes(&self) -> &[u8] {
match &self.content {
Content::Utf8(text) => text.as_bytes(),
Content::NonUtf8(bin) => bin.as_ref(),
}
}
pub fn is_binary(&self) -> bool {
match &self.content {
Content::Utf8(_) => {
false
}
Content::NonUtf8(bin) => bin[..Self::BINARY_FILE_CUTOFF].contains(&0),
}
}
}
impl<'l> LintContext<'l> for ContentContext<'l> {
fn kind(&self) -> LintKind<'l> {
LintKind::Content(self.file_ctx.file_path())
}
}
#[derive(Debug)]
enum Content {
Utf8(Box<str>),
NonUtf8(Box<[u8]>),
}
impl Content {
fn new(bytes: Vec<u8>) -> Self {
match String::from_utf8(bytes) {
Ok(s) => Content::Utf8(s.into()),
Err(err) => Content::NonUtf8(err.into_bytes().into()),
}
}
#[allow(dead_code)]
fn len(&self) -> usize {
match self {
Content::Utf8(text) => text.len(),
Content::NonUtf8(bin) => bin.len(),
}
}
}