Skip to content

Commit 96b0fca

Browse files
committed
parse all kinds of attributes, lacking name validation (#301)
1 parent 3409a66 commit 96b0fca

File tree

2 files changed

+73
-1
lines changed

2 files changed

+73
-1
lines changed

git-attributes/src/parse/attribute.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,29 @@ impl<'a> Iterator for Iter<'a> {
4242

4343
fn next(&mut self) -> Option<Self::Item> {
4444
let attr = self.attrs.next().filter(|a| !a.is_empty())?;
45-
Some(Ok((attr.as_bstr(), crate::State::Set)))
45+
parse_attr(attr).into()
4646
}
4747
}
4848

49+
fn parse_attr(attr: &[u8]) -> Result<(&BStr, crate::State<'_>), Error> {
50+
let mut tokens = attr.splitn(2, |b| *b == b'=');
51+
let attr = tokens.next().expect("attr itself").as_bstr();
52+
let possibly_value = tokens.next();
53+
let (attr, state) = if attr.first() == Some(&b'-') {
54+
(&attr[1..], crate::State::Unset)
55+
} else if attr.first() == Some(&b'!') {
56+
(&attr[1..], crate::State::Unspecified)
57+
} else {
58+
(
59+
attr,
60+
possibly_value
61+
.map(|v| crate::State::Value(v.as_bstr()))
62+
.unwrap_or(crate::State::Set),
63+
)
64+
};
65+
Ok((attr, state))
66+
}
67+
4968
impl<'a> Lines<'a> {
5069
pub fn new(buf: &'a [u8]) -> Self {
5170
let bom = unicode_bom::Bom::from(buf);

git-attributes/tests/parse/attribute.rs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,47 @@ fn attributes_are_parsed_behind_various_whitespace_characters() {
134134
);
135135
}
136136

137+
#[test]
138+
fn attributes_come_in_different_flavors_due_to_prefixes() {
139+
assert_eq!(
140+
line(r#"p set -unset !unspecified -set"#),
141+
(
142+
"p".into(),
143+
Mode::NO_SUB_DIR,
144+
vec![set("set"), unset("unset"), unspecified("unspecified"), unset("set")],
145+
1
146+
),
147+
"the parser doesn't care about double-mentions either"
148+
);
149+
}
150+
151+
#[test]
152+
fn attributes_can_have_values() {
153+
assert_eq!(
154+
line(r#"p a=one b=2 c=你好 "#),
155+
(
156+
"p".into(),
157+
Mode::NO_SUB_DIR,
158+
vec![value("a", "one"), value("b", "2"), value("c", "你好")],
159+
1
160+
),
161+
"only non-whitespace ascii values are allowed, no escaping or anything fancy is possible there"
162+
);
163+
}
164+
165+
#[test]
166+
fn attributes_see_state_adjustments_over_value_assignments() {
167+
assert_eq!(
168+
line(r#"p set -unset=a !unspecified=b"#),
169+
(
170+
"p".into(),
171+
Mode::NO_SUB_DIR,
172+
vec![set("set"), unset("unset"), unspecified("unspecified")],
173+
1
174+
)
175+
);
176+
}
177+
137178
#[test]
138179
fn trailing_whitespace_in_attributes_is_ignored() {
139180
assert_eq!(line("p a \r\t"), ("p".into(), Mode::NO_SUB_DIR, vec![set("a")], 1),);
@@ -151,6 +192,18 @@ fn set(attr: &str) -> (&BStr, State) {
151192
(attr.as_bytes().as_bstr(), State::Set)
152193
}
153194

195+
fn unset(attr: &str) -> (&BStr, State) {
196+
(attr.as_bytes().as_bstr(), State::Unset)
197+
}
198+
199+
fn unspecified(attr: &str) -> (&BStr, State) {
200+
(attr.as_bytes().as_bstr(), State::Unspecified)
201+
}
202+
203+
fn value<'a, 'b>(attr: &'a str, value: &'b str) -> (&'a BStr, State<'b>) {
204+
(attr.as_bytes().as_bstr(), State::Value(value.as_bytes().as_bstr()))
205+
}
206+
154207
fn try_line(input: &str) -> Result<ExpandedAttribute, parse::attribute::Error> {
155208
let mut lines = git_attributes::parse(input.as_bytes());
156209
let res = expand(lines.next().unwrap())?;

0 commit comments

Comments
 (0)