Skip to content

Commit 1981f6f

Browse files
committed
prepare for macro support (#301)
1 parent 65c416b commit 1981f6f

File tree

4 files changed

+62
-48
lines changed

4 files changed

+62
-48
lines changed

git-attributes/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,6 @@ pub mod ignore;
2020

2121
pub mod parse;
2222

23-
pub fn parse(buf: &[u8]) -> parse::attribute::Lines<'_> {
24-
parse::attribute::Lines::new(buf)
23+
pub fn parse(buf: &[u8]) -> parse::Lines<'_> {
24+
parse::Lines::new(buf)
2525
}

git-attributes/src/parse/attribute.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
use bstr::{BStr, BString, ByteSlice};
22
use std::borrow::Cow;
33

4+
#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)]
5+
#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
6+
pub enum Kind {
7+
/// A pattern to match paths against
8+
Pattern(BString),
9+
/// The name of the macro to define, always a valid attribute name
10+
Macro(BString),
11+
}
12+
413
mod error {
514
use bstr::BString;
615
use quick_error::quick_error;
@@ -100,7 +109,7 @@ impl<'a> Lines<'a> {
100109
}
101110

102111
impl<'a> Iterator for Lines<'a> {
103-
type Item = Result<(BString, crate::ignore::pattern::Mode, Iter<'a>, usize), Error>;
112+
type Item = Result<(Kind, crate::ignore::pattern::Mode, Iter<'a>, usize), Error>;
104113

105114
fn next(&mut self) -> Option<Self::Item> {
106115
for line in self.lines.by_ref() {
@@ -131,7 +140,7 @@ impl<'a> Iterator for Lines<'a> {
131140
fn parse_line(
132141
line: &BStr,
133142
line_number: usize,
134-
) -> Option<Result<(BString, crate::ignore::pattern::Mode, Iter<'_>), Error>> {
143+
) -> Option<Result<(Kind, crate::ignore::pattern::Mode, Iter<'_>), Error>> {
135144
if line.is_empty() {
136145
return None;
137146
}
@@ -149,7 +158,7 @@ fn parse_line(
149158
};
150159

151160
let (pattern, flags) = super::ignore::parse_line(line.as_ref())?;
152-
Ok((pattern, flags, Iter::new(attrs, line_number))).into()
161+
Ok((Kind::Pattern(pattern), flags, Iter::new(attrs, line_number))).into()
153162
}
154163

155164
const BLANKS: &[u8] = b" \t\r";

git-attributes/src/parse/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
pub mod ignore;
22

3-
pub mod attribute;
3+
mod attribute;
4+
pub use attribute::{Error, Iter, Kind, Lines};
45

56
pub fn ignore(buf: &[u8]) -> ignore::Lines<'_> {
67
ignore::Lines::new(buf)

git-attributes/tests/parse/attribute.rs

Lines changed: 46 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
use bstr::{BStr, BString, ByteSlice};
1+
use bstr::{BStr, ByteSlice};
22
use git_attributes::ignore::pattern::Mode;
33
use git_attributes::{ignore, parse, State};
44
use git_testtools::fixture_path;
55

66
#[test]
77
fn byte_order_marks_are_no_patterns() {
8-
assert_eq!(line("\u{feff}hello"), (r"hello".into(), Mode::NO_SUB_DIR, vec![], 1));
8+
assert_eq!(line("\u{feff}hello"), (pattern(r"hello"), Mode::NO_SUB_DIR, vec![], 1));
99
assert_eq!(
1010
line("\u{feff}\"hello\""),
11-
(r"hello".into(), Mode::NO_SUB_DIR, vec![], 1)
11+
(pattern(r"hello"), Mode::NO_SUB_DIR, vec![], 1)
1212
);
1313
}
1414

@@ -18,11 +18,11 @@ fn line_numbers_are_counted_correctly() {
1818
assert_eq!(
1919
try_lines(&String::from_utf8(ignore).unwrap()).unwrap(),
2020
vec![
21-
(r"*.[oa]".into(), Mode::NO_SUB_DIR, vec![], 2),
22-
(r"*.html".into(), Mode::NO_SUB_DIR | Mode::ENDS_WITH, vec![], 5),
23-
(r"!foo.html".into(), Mode::NO_SUB_DIR, vec![], 8),
24-
(r"#a/path".into(), Mode::empty(), vec![], 10),
25-
(r"/*".into(), Mode::empty(), vec![], 11),
21+
(pattern(r"*.[oa]"), Mode::NO_SUB_DIR, vec![], 2),
22+
(pattern(r"*.html"), Mode::NO_SUB_DIR | Mode::ENDS_WITH, vec![], 5),
23+
(pattern(r"!foo.html"), Mode::NO_SUB_DIR, vec![], 8),
24+
(pattern(r"#a/path"), Mode::empty(), vec![], 10),
25+
(pattern(r"/*"), Mode::empty(), vec![], 11),
2626
]
2727
);
2828
}
@@ -32,9 +32,9 @@ fn line_endings_can_be_windows_or_unix() {
3232
assert_eq!(
3333
try_lines("unix\nwindows\r\nlast").unwrap(),
3434
vec![
35-
(r"unix".into(), Mode::NO_SUB_DIR, vec![], 1),
36-
(r"windows".into(), Mode::NO_SUB_DIR, vec![], 2),
37-
(r"last".into(), Mode::NO_SUB_DIR, vec![], 3)
35+
(pattern(r"unix"), Mode::NO_SUB_DIR, vec![], 1),
36+
(pattern(r"windows"), Mode::NO_SUB_DIR, vec![], 2),
37+
(pattern(r"last"), Mode::NO_SUB_DIR, vec![], 3)
3838
]
3939
);
4040
}
@@ -51,47 +51,44 @@ fn comment_lines_are_ignored() {
5151

5252
#[test]
5353
fn leading_whitespace_is_ignored() {
54-
assert_eq!(line(" \r\tp"), (r"p".into(), Mode::NO_SUB_DIR, vec![], 1));
55-
assert_eq!(line(" \r\t\"p\""), (r"p".into(), Mode::NO_SUB_DIR, vec![], 1));
54+
assert_eq!(line(" \r\tp"), (pattern(r"p"), Mode::NO_SUB_DIR, vec![], 1));
55+
assert_eq!(line(" \r\t\"p\""), (pattern(r"p"), Mode::NO_SUB_DIR, vec![], 1));
5656
}
5757

5858
#[test]
5959
fn comment_can_be_escaped_like_gitignore_or_quoted() {
6060
assert_eq!(
6161
line(r"\#hello"),
62-
(r"#hello".into(), Mode::NO_SUB_DIR, vec![], 1),
62+
(pattern(r"#hello"), Mode::NO_SUB_DIR, vec![], 1),
6363
"undocumented, but definitely works"
6464
);
65-
assert_eq!(line("\"# hello\""), (r"# hello".into(), Mode::NO_SUB_DIR, vec![], 1));
65+
assert_eq!(line("\"# hello\""), (pattern(r"# hello"), Mode::NO_SUB_DIR, vec![], 1));
6666
}
6767

6868
#[test]
6969
fn exclamation_marks_must_be_escaped_or_error_unlike_gitignore() {
70-
assert_eq!(line(r"\!hello"), (r"!hello".into(), Mode::NO_SUB_DIR, vec![], 1));
70+
assert_eq!(line(r"\!hello"), (pattern(r"!hello"), Mode::NO_SUB_DIR, vec![], 1));
7171
assert!(matches!(
7272
try_line(r"!hello"),
73-
Err(parse::attribute::Error::PatternNegation { line_number: 1, .. })
73+
Err(parse::Error::PatternNegation { line_number: 1, .. })
7474
));
7575
assert!(
7676
matches!(
7777
try_line(r#""!hello""#),
78-
Err(parse::attribute::Error::PatternNegation { line_number: 1, .. }),
78+
Err(parse::Error::PatternNegation { line_number: 1, .. }),
7979
),
8080
"even in quotes they trigger…"
8181
);
8282
assert_eq!(
8383
line(r#""\\!hello""#),
84-
(r"!hello".into(), Mode::NO_SUB_DIR, vec![], 1),
84+
(pattern(r"!hello"), Mode::NO_SUB_DIR, vec![], 1),
8585
"…and must be double-escaped, once to get through quote, then to get through parse ignore line"
8686
);
8787
}
8888

8989
#[test]
9090
fn invalid_escapes_in_quotes_are_an_error() {
91-
assert!(matches!(
92-
try_line(r#""\!hello""#),
93-
Err(parse::attribute::Error::Unquote(_)),
94-
),);
91+
assert!(matches!(try_line(r#""\!hello""#), Err(parse::Error::Unquote(_)),),);
9592
}
9693

9794
#[test]
@@ -104,19 +101,19 @@ fn custom_macros_can_be_defined() {
104101
fn attribute_names_must_not_begin_with_dash_and_must_be_ascii_only() {
105102
assert!(matches!(
106103
try_line(r"p !-a"),
107-
Err(parse::attribute::Error::AttributeName { line_number: 1, .. })
104+
Err(parse::Error::AttributeName { line_number: 1, .. })
108105
));
109106
assert!(
110107
matches!(
111108
try_line(r#"p !!a"#),
112-
Err(parse::attribute::Error::AttributeName { line_number: 1, .. })
109+
Err(parse::Error::AttributeName { line_number: 1, .. })
113110
),
114111
"exclamation marks aren't allowed either"
115112
);
116113
assert!(
117114
matches!(
118115
try_line(r#"p 你好"#),
119-
Err(parse::attribute::Error::AttributeName { line_number: 1, .. })
116+
Err(parse::Error::AttributeName { line_number: 1, .. })
120117
),
121118
"nor is utf-8 encoded characters - gitoxide could consider to relax this when established"
122119
);
@@ -126,32 +123,32 @@ fn attribute_names_must_not_begin_with_dash_and_must_be_ascii_only() {
126123
fn attributes_are_parsed_behind_various_whitespace_characters() {
127124
assert_eq!(
128125
line(r#"p a b"#),
129-
("p".into(), Mode::NO_SUB_DIR, vec![set("a"), set("b")], 1),
126+
(pattern("p"), Mode::NO_SUB_DIR, vec![set("a"), set("b")], 1),
130127
"behind space"
131128
);
132129
assert_eq!(
133130
line(r#""p" a b"#),
134-
("p".into(), Mode::NO_SUB_DIR, vec![set("a"), set("b")], 1),
131+
(pattern("p"), Mode::NO_SUB_DIR, vec![set("a"), set("b")], 1),
135132
"behind space"
136133
);
137134
assert_eq!(
138135
line("p\ta\tb"),
139-
("p".into(), Mode::NO_SUB_DIR, vec![set("a"), set("b")], 1),
136+
(pattern("p"), Mode::NO_SUB_DIR, vec![set("a"), set("b")], 1),
140137
"behind tab"
141138
);
142139
assert_eq!(
143140
line("\"p\"\ta\tb"),
144-
("p".into(), Mode::NO_SUB_DIR, vec![set("a"), set("b")], 1),
141+
(pattern("p"), Mode::NO_SUB_DIR, vec![set("a"), set("b")], 1),
145142
"behind tab"
146143
);
147144
assert_eq!(
148145
line("p \t a \t b"),
149-
("p".into(), Mode::NO_SUB_DIR, vec![set("a"), set("b")], 1),
146+
(pattern("p"), Mode::NO_SUB_DIR, vec![set("a"), set("b")], 1),
150147
"behind a mix of space and tab"
151148
);
152149
assert_eq!(
153150
line("\"p\" \t a \t b"),
154-
("p".into(), Mode::NO_SUB_DIR, vec![set("a"), set("b")], 1),
151+
(pattern("p"), Mode::NO_SUB_DIR, vec![set("a"), set("b")], 1),
155152
"behind a mix of space and tab"
156153
);
157154
}
@@ -161,7 +158,7 @@ fn attributes_come_in_different_flavors_due_to_prefixes() {
161158
assert_eq!(
162159
line(r#"p set -unset !unspecified -set"#),
163160
(
164-
"p".into(),
161+
pattern("p"),
165162
Mode::NO_SUB_DIR,
166163
vec![set("set"), unset("unset"), unspecified("unspecified"), unset("set")],
167164
1
@@ -175,7 +172,7 @@ fn attributes_can_have_values() {
175172
assert_eq!(
176173
line(r#"p a=one b=2 c=你好 "#),
177174
(
178-
"p".into(),
175+
pattern("p"),
179176
Mode::NO_SUB_DIR,
180177
vec![value("a", "one"), value("b", "2"), value("c", "你好")],
181178
1
@@ -189,7 +186,7 @@ fn attributes_see_state_adjustments_over_value_assignments() {
189186
assert_eq!(
190187
line(r#"p set -unset=a !unspecified=b"#),
191188
(
192-
"p".into(),
189+
pattern("p"),
193190
Mode::NO_SUB_DIR,
194191
vec![set("set"), unset("unset"), unspecified("unspecified")],
195192
1
@@ -199,12 +196,15 @@ fn attributes_see_state_adjustments_over_value_assignments() {
199196

200197
#[test]
201198
fn trailing_whitespace_in_attributes_is_ignored() {
202-
assert_eq!(line("p a \r\t"), ("p".into(), Mode::NO_SUB_DIR, vec![set("a")], 1),);
203-
assert_eq!(line("\"p\" a \r\t"), ("p".into(), Mode::NO_SUB_DIR, vec![set("a")], 1),);
199+
assert_eq!(line("p a \r\t"), (pattern("p"), Mode::NO_SUB_DIR, vec![set("a")], 1),);
200+
assert_eq!(
201+
line("\"p\" a \r\t"),
202+
(pattern("p"), Mode::NO_SUB_DIR, vec![set("a")], 1),
203+
);
204204
}
205205

206206
type ExpandedAttribute<'a> = (
207-
BString,
207+
parse::Kind,
208208
ignore::pattern::Mode,
209209
Vec<(&'a BStr, git_attributes::State<'a>)>,
210210
usize,
@@ -226,7 +226,11 @@ fn value<'a, 'b>(attr: &'a str, value: &'b str) -> (&'a BStr, State<'b>) {
226226
(attr.as_bytes().as_bstr(), State::Value(value.as_bytes().as_bstr()))
227227
}
228228

229-
fn try_line(input: &str) -> Result<ExpandedAttribute, parse::attribute::Error> {
229+
fn pattern(name: &str) -> parse::Kind {
230+
parse::Kind::Pattern(name.into())
231+
}
232+
233+
fn try_line(input: &str) -> Result<ExpandedAttribute, parse::Error> {
230234
let mut lines = git_attributes::parse(input.as_bytes());
231235
let res = expand(lines.next().unwrap())?;
232236
assert!(lines.next().is_none(), "expected only one line");
@@ -240,13 +244,13 @@ fn line(input: &str) -> ExpandedAttribute {
240244
res
241245
}
242246

243-
fn try_lines(input: &str) -> Result<Vec<ExpandedAttribute>, parse::attribute::Error> {
247+
fn try_lines(input: &str) -> Result<Vec<ExpandedAttribute>, parse::Error> {
244248
git_attributes::parse(input.as_bytes()).map(expand).collect()
245249
}
246250

247251
fn expand(
248-
input: Result<(BString, ignore::pattern::Mode, parse::attribute::Iter<'_>, usize), parse::attribute::Error>,
249-
) -> Result<ExpandedAttribute<'_>, parse::attribute::Error> {
252+
input: Result<(parse::Kind, ignore::pattern::Mode, parse::Iter<'_>, usize), parse::Error>,
253+
) -> Result<ExpandedAttribute<'_>, parse::Error> {
250254
let (pattern, mode, attrs, line_no) = input?;
251255
let attrs = attrs.collect::<Result<Vec<_>, _>>()?;
252256
Ok((pattern, mode, attrs, line_no))

0 commit comments

Comments
 (0)