Skip to content

Commit 8575925

Browse files
committed
Rework everything to use SharedString
This commit contains a large update to ec4rs that involves major breakages. Of particular note: - Almost everything that returned a T<RawValue> now returns an Option<T<SharedString>>. - The PropertyValue trait was changed to put most of its functionality in supertraits, one of which is new. - SpellingLanguage is now available without a feature flag, but has been changed. - Removed feature allow-empty-values as empty values are now allowed by default. - Removed functionality of feature language-tags for now. Plan to eventually depend on unic-locale.
1 parent 22ce205 commit 8575925

File tree

20 files changed

+388
-450
lines changed

20 files changed

+388
-450
lines changed

Cargo.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,11 @@ version = "1.2.0"
2020
members = ["glob", "tools"]
2121

2222
[features]
23-
allow-empty-values = []
2423
track-source = []
24+
language-tags = []
2525

2626
[dependencies]
2727
ec4rs_glob = { version = "0.1.0", path = "glob" }
28-
language-tags = { version = "0.3.2", optional = true }
2928

3029
[package.metadata.docs.rs]
3130
all-features = true

rustdoc.md

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -35,27 +35,21 @@ let indent_style: IndentStyle = cfg.get::<IndentStyle>()
3535
.unwrap_or(IndentStyle::Tabs);
3636
3737
// Get a string value, with a default.
38-
let charset: &str = cfg.get_raw::<Charset>()
39-
.filter_unset() // Handle the special "unset" value.
40-
.into_option()
41-
.unwrap_or("utf-8");
38+
// ec4rs has a string type designed for immutability and minimal allocations.
39+
let charset = cfg.get_raw::<Charset>()
40+
.cloned()
41+
.unwrap_or(ec4rs::string::SharedString::new_static("utf-8"));
4242
4343
// Parse a non-standard property.
4444
let hard_wrap = cfg.get_raw_for_key("max_line_length")
45-
.into_str()
45+
.unwrap_or_default()
4646
.parse::<usize>();
4747
```
4848

4949
## Features
5050

51-
**allow-empty-values**: Consider lines with a key but no value as valid.
52-
This is likely to be explicitly allowed in a future version of the
53-
EditorConfig specification, but `ec4rs` currently by default treats such lines
54-
as invalid, necessitating this feature flag to reduce behavioral breakage.
51+
**language-tags**: NYI for 2.0.
5552

56-
**language-tags**: Use the `language-tags` crate, which adds parsing for the
57-
`spelling_language` property.
58-
59-
**track-source**: Allow [`RawValue`][crate::rawvalue::RawValue]
53+
**track-source**: Allow [`SharedString`][crate::string::SharedString]
6054
to store the file and line number it originates from.
6155
[`ConfigParser`] will add this information where applicable.

src/fallback.rs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,20 @@ use crate::property as prop;
22

33
pub fn add_fallbacks(props: &mut crate::Properties, legacy: bool) {
44
let val = props.get_raw::<prop::IndentSize>();
5-
if let Some(value) = val.into_option() {
6-
if let Ok(prop::IndentSize::UseTabWidth) = val.parse::<prop::IndentSize>() {
5+
if let Some(value) = val {
6+
if let Ok(prop::IndentSize::UseTabWidth) = value.parse::<prop::IndentSize>() {
77
let value = props
88
.get_raw::<prop::TabWidth>()
9-
.into_option()
10-
.unwrap_or("tab")
11-
.to_owned();
9+
.cloned()
10+
.unwrap_or(crate::string::SharedString::new_static("tab"));
1211
props.insert_raw::<prop::IndentSize, _>(value);
1312
} else {
1413
let value = value.to_owned();
1514
let _ = props.try_insert_raw::<prop::TabWidth, _>(value);
1615
}
1716
} else if let Some(value) = props
1817
.get_raw::<prop::TabWidth>()
19-
.filter_unset()
20-
.into_option()
18+
.filter(|v| *v != &crate::string::UNSET)
2119
{
2220
let _ = props.try_insert_raw::<prop::IndentSize, _>(value.to_owned());
2321
}

src/lib.rs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ mod linereader;
2727
mod parser;
2828
mod properties;
2929
pub mod property;
30-
pub mod rawvalue;
3130
mod section;
3231
pub mod string;
3332
#[cfg(test)]
@@ -46,12 +45,6 @@ pub use properties::{Properties, PropertiesSource};
4645
pub use section::Section;
4746
pub use traits::*;
4847

49-
#[cfg(feature = "language-tags")]
50-
pub mod language_tags {
51-
//! Re-export of the `language-tags` crate.
52-
pub use ::language_tags::*;
53-
}
54-
5548
/// Retrieves the [`Properties`] for a file at the given path.
5649
///
5750
/// This is the simplest way to use this library in an EditorConfig integration or plugin.

src/linereader.rs

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -53,16 +53,7 @@ pub fn parse_line(line: &str) -> LineReadResult<'_> {
5353
let val = val_raw.trim_start();
5454
match (key.is_empty(), val.is_empty()) {
5555
(true, _) => Err(ParseError::InvalidLine),
56-
(false, true) => {
57-
#[cfg(feature = "allow-empty-values")]
58-
{
59-
Ok(Line::Pair(key.trim_end(), val))
60-
}
61-
#[cfg(not(feature = "allow-empty-values"))]
62-
{
63-
Err(ParseError::InvalidLine)
64-
}
65-
}
56+
(false, true) => Ok(Line::Pair(key.trim_end(), val)),
6657
(false, false) => Ok(Line::Pair(key.trim_end(), val.trim_start())),
6758
}
6859
} else {

src/parser.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ impl<R: io::BufRead> ConfigParser<R> {
4545
/// otherwise returns `Err` with the error that occurred during reading.
4646
///
4747
/// If the `track-source` feature is enabled and `path` is `Some`,
48-
/// [`RawValue`][crate::rawvalue::RawValue]s produced by this parser will
48+
/// [`SharedString`][crate::string::SharedString]s produced by this parser will
4949
/// have their sources set appropriately.
5050
/// Otherwise, `path` is unused.
5151
pub fn new_with_path(
@@ -124,7 +124,7 @@ impl<R: io::BufRead> ConfigParser<R> {
124124
Ok(Line::Nothing) => (),
125125
Ok(Line::Pair(k, v)) => {
126126
#[allow(unused_mut)]
127-
let mut v = crate::rawvalue::RawValue::from(v.to_owned());
127+
let mut v = crate::string::SharedString::new(v);
128128
#[cfg(feature = "track-source")]
129129
if let Some(path) = self.path.as_ref() {
130130
v.set_source(path.clone(), line_no);

src/properties.rs

Lines changed: 76 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ mod iter;
22

33
pub use iter::*;
44

5-
use crate::rawvalue::RawValue;
6-
use crate::{PropertyKey, PropertyValue};
5+
use crate::{
6+
string::{SharedString, ToSharedString},
7+
PropertyKey, PropertyValue,
8+
};
79

810
/// Map of property names to property values.
911
///
@@ -14,17 +16,11 @@ use crate::{PropertyKey, PropertyValue};
1416
/// It's the caller's responsibility to ensure all keys and values are lowercased.
1517
#[derive(Clone, Default)]
1618
pub struct Properties {
17-
// Don't use Cow<'static, str> here because it's actually less-optimal
18-
// for the vastly more-common case of reading parsed properties.
19-
// It's a micro-optimization anyway.
2019
/// Key-value pairs, ordered from oldest to newest.
21-
pairs: Vec<(String, RawValue)>,
20+
pairs: Vec<(SharedString, SharedString)>,
2221
/// Indices of `pairs`, ordered matching the key of the pair each index refers to.
2322
/// This part is what allows logarithmic lookups.
2423
idxes: Vec<usize>,
25-
// Unfortunately, we hand out `&mut RawValue`s all over the place,
26-
// so "no empty RawValues in Properties" cannot be made an invariant
27-
// without breaking API changes.
2824
}
2925

3026
// TODO: Deletion.
@@ -53,32 +49,42 @@ impl Properties {
5349
fn find_idx(&self, key: &str) -> Result<usize, usize> {
5450
self.idxes
5551
.as_slice()
56-
.binary_search_by_key(&key, |ki| self.pairs[*ki].0.as_str())
52+
.binary_search_by_key(&key, |ki| &self.pairs[*ki].0)
5753
.map(|idx| self.idxes[idx])
5854
}
5955

6056
/// Returns the unparsed "raw" value for the specified key.
61-
///
62-
/// Does not test for the "unset" value. Use [`RawValue::filter_unset`].
63-
pub fn get_raw_for_key(&self, key: impl AsRef<str>) -> &RawValue {
57+
pub fn get_raw_for_key(&self, key: impl AsRef<str>) -> Option<&SharedString> {
6458
self.find_idx(key.as_ref())
6559
.ok()
66-
.map_or(&crate::rawvalue::UNSET, |idx| &self.pairs[idx].1)
60+
.map(|idx| &self.pairs[idx].1)
6761
}
6862

6963
/// Returns the unparsed "raw" value for the specified property.
70-
///
71-
/// Does not test for the "unset" value. Use [`RawValue::filter_unset`].
72-
pub fn get_raw<T: PropertyKey>(&self) -> &RawValue {
64+
pub fn get_raw<T: PropertyKey>(&self) -> Option<&SharedString> {
7365
self.get_raw_for_key(T::key())
7466
}
7567

7668
/// Returns the parsed value for the specified property.
7769
///
78-
/// Does not test for the "unset" value if parsing fails. Use [`RawValue::filter_unset`].
79-
pub fn get<T: PropertyKey + PropertyValue>(&self) -> Result<T, &RawValue> {
80-
let retval = self.get_raw::<T>();
81-
retval.parse::<T>().or(Err(retval))
70+
/// If `self` does not contain a key matching [`T::key()`][PropertyKey::key()],
71+
/// `Err(None)` is returned. Otherwise attempts to parse `T`
72+
/// out of the string value, and returns `Err(Some)` if the parse fails.
73+
pub fn get<T: PropertyKey + PropertyValue>(
74+
&self,
75+
) -> Result<T, Option<crate::string::ParseError<T::Err>>> {
76+
// The Option is on the error because all PropertyValues implement Default and the default
77+
// should be assumed if the property is unset, meaning handling invalid values and unset
78+
// values can be done with just one unwrap_or_default.
79+
let Some(raw) = self.get_raw::<T>() else {
80+
return Err(None);
81+
};
82+
raw.parse::<T>().map_err(|error| {
83+
Some(crate::string::ParseError {
84+
error,
85+
string: raw.clone(),
86+
})
87+
})
8288
}
8389

8490
/// Returns an iterator over the key-value pairs.
@@ -103,36 +109,48 @@ impl Properties {
103109
IterMut(self.pairs.iter_mut())
104110
}
105111

106-
fn get_at_mut(&mut self, idx: usize) -> &mut RawValue {
112+
fn get_at_mut(&mut self, idx: usize) -> &mut SharedString {
113+
// PANIC: idx needs to be within the bounds of the pairs vec.
107114
&mut self.pairs.get_mut(idx).unwrap().1
108115
}
109116

110-
fn insert_at(&mut self, idx: usize, key: String, val: RawValue) {
117+
fn insert_at(&mut self, idx: usize, key: SharedString, val: SharedString) {
111118
self.idxes.insert(idx, self.pairs.len());
112119
self.pairs.push((key, val));
113120
}
114121

115122
/// Sets the value for a specified key.
116-
pub fn insert_raw_for_key(&mut self, key: impl AsRef<str>, val: impl Into<RawValue>) {
117-
let key_str = key.as_ref();
118-
match self.find_idx(key_str) {
119-
Ok(idx) => {
120-
*self.get_at_mut(idx) = val.into();
123+
pub fn insert_raw_for_key(&mut self, key: impl ToSharedString, val: impl ToSharedString) {
124+
if let Some(key_str) = key.try_as_str() {
125+
match self.find_idx(key_str) {
126+
Ok(idx) => {
127+
*self.get_at_mut(idx) = val.to_shared_string();
128+
}
129+
Err(idx) => {
130+
self.insert_at(idx, key.to_shared_string(), val.to_shared_string());
131+
}
121132
}
122-
Err(idx) => {
123-
self.insert_at(idx, key_str.to_owned(), val.into());
133+
} else {
134+
let key = key.to_shared_string();
135+
match self.find_idx(&key) {
136+
Ok(idx) => {
137+
*self.get_at_mut(idx) = val.to_shared_string();
138+
}
139+
Err(idx) => {
140+
self.insert_at(idx, key, val.to_shared_string());
141+
}
124142
}
125143
}
126144
}
127145

128146
/// Sets the value for a specified property's key.
129-
pub fn insert_raw<K: PropertyKey, V: Into<RawValue>>(&mut self, val: V) {
147+
pub fn insert_raw<K: PropertyKey, V: ToSharedString>(&mut self, val: V) {
130148
self.insert_raw_for_key(K::key(), val)
131149
}
132150

133151
/// Inserts a specified property into the map.
134-
pub fn insert<T: PropertyKey + Into<RawValue>>(&mut self, prop: T) {
135-
self.insert_raw_for_key(T::key(), prop.into())
152+
pub fn insert<T: PropertyKey + PropertyValue>(&mut self, prop: T) {
153+
self.insert_raw_for_key(T::key(), prop)
136154
}
137155

138156
/// Attempts to add a new key-value pair to the map.
@@ -141,45 +159,49 @@ impl Properties {
141159
/// returns a mutable reference to the old value and does not update the map.
142160
pub fn try_insert_raw_for_key(
143161
&mut self,
144-
key: impl AsRef<str>,
145-
value: impl Into<RawValue>,
146-
) -> Result<(), &mut RawValue> {
147-
let key_str = key.as_ref();
148-
#[allow(clippy::unit_arg)]
149-
match self.find_idx(key_str) {
150-
Ok(idx) => {
151-
let valref = self.get_at_mut(idx);
152-
if valref.is_unset() {
153-
*valref = value.into();
162+
key: impl ToSharedString,
163+
val: impl ToSharedString,
164+
) -> Result<(), &mut SharedString> {
165+
if let Some(key_str) = key.try_as_str() {
166+
match self.find_idx(key_str) {
167+
Ok(idx) => Err(self.get_at_mut(idx)),
168+
Err(idx) => {
169+
self.insert_at(idx, key.to_shared_string(), val.to_shared_string());
170+
Ok(())
171+
}
172+
}
173+
} else {
174+
let key = key.to_shared_string();
175+
match self.find_idx(&key) {
176+
Ok(idx) => Err(self.get_at_mut(idx)),
177+
Err(idx) => {
178+
self.insert_at(idx, key, val.to_shared_string());
154179
Ok(())
155-
} else {
156-
Err(valref)
157180
}
158181
}
159-
Err(idx) => Ok(self.insert_at(idx, key_str.to_owned(), value.into())),
160182
}
161183
}
162184

163185
/// Attempts to add a new property to the map with a specified value.
164186
///
165187
/// If the key was already associated with a value,
166188
/// returns a mutable reference to the old value and does not update the map.
167-
pub fn try_insert_raw<K: PropertyKey, V: Into<RawValue>>(
189+
pub fn try_insert_raw<K: PropertyKey, V: ToSharedString>(
168190
&mut self,
169191
val: V,
170-
) -> Result<(), &mut RawValue> {
192+
) -> Result<(), &mut SharedString> {
171193
self.try_insert_raw_for_key(K::key(), val)
172194
}
173195

174196
/// Attempts to add a new property to the map.
175197
///
176198
/// If the key was already associated with a value,
177199
/// returns a mutable reference to the old value and does not update the map.
178-
pub fn try_insert<T: PropertyKey + Into<RawValue>>(
200+
pub fn try_insert<T: PropertyKey + PropertyValue>(
179201
&mut self,
180202
prop: T,
181-
) -> Result<(), &mut RawValue> {
182-
self.try_insert_raw_for_key(T::key(), prop.into())
203+
) -> Result<(), &mut SharedString> {
204+
self.try_insert_raw_for_key(T::key(), prop)
183205
}
184206

185207
/// Adds fallback values for certain common key-value pairs.
@@ -242,22 +264,22 @@ impl<'a> IntoIterator for &'a mut Properties {
242264
}
243265
}
244266

245-
impl<K: AsRef<str>, V: Into<RawValue>> FromIterator<(K, V)> for Properties {
267+
impl<K: Into<SharedString>, V: Into<SharedString>> FromIterator<(K, V)> for Properties {
246268
fn from_iter<T: IntoIterator<Item = (K, V)>>(iter: T) -> Self {
247269
let mut result = Properties::new();
248270
result.extend(iter);
249271
result
250272
}
251273
}
252274

253-
impl<K: AsRef<str>, V: Into<RawValue>> Extend<(K, V)> for Properties {
275+
impl<K: Into<SharedString>, V: Into<SharedString>> Extend<(K, V)> for Properties {
254276
fn extend<T: IntoIterator<Item = (K, V)>>(&mut self, iter: T) {
255277
let iter = iter.into_iter();
256278
let min_len = iter.size_hint().0;
257279
self.pairs.reserve(min_len);
258280
self.idxes.reserve(min_len);
259281
for (k, v) in iter {
260-
let k = k.as_ref();
282+
let k = k.into();
261283
let v = v.into();
262284
self.insert_raw_for_key(k, v);
263285
}

0 commit comments

Comments
 (0)