Skip to content

Commit e1d941a

Browse files
committed
Add RangedValue
1 parent 59ad1d6 commit e1d941a

File tree

1 file changed

+218
-63
lines changed
  • crates/red_knot_project/src/metadata

1 file changed

+218
-63
lines changed
Lines changed: 218 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
1+
use crate::combine::Combine;
2+
use crate::Db;
13
use ruff_db::system::{System, SystemPath, SystemPathBuf};
4+
use ruff_text_size::{TextRange, TextSize};
25
use serde::{Deserialize, Deserializer, Serialize, Serializer};
36
use std::cell::RefCell;
7+
use std::cmp::Ordering;
48
use std::fmt;
59
use std::hash::{Hash, Hasher};
10+
use std::ops::{Deref, DerefMut};
611
use std::sync::Arc;
7-
8-
use crate::combine::Combine;
9-
use crate::Db;
12+
use toml::Spanned;
1013

1114
#[derive(Clone, Debug)]
1215
pub enum ValueSource {
@@ -59,115 +62,267 @@ impl Drop for ValueSourceGuard {
5962
}
6063
}
6164

62-
/// A possibly relative path in a configuration file.
65+
/// A configuration value that tracks where it originates from and the range in the source document.
6366
///
64-
/// Relative paths in configuration files or from CLI options
65-
/// require different anchoring:
67+
/// ## Equality, Hash, and Ordering
68+
/// The equality, hash, and ordering are solely based on the value. They disregard the value's range
69+
/// or source.
6670
///
67-
/// * CLI: The path is relative to the current working directory
68-
/// * Configuration file: The path is relative to the project's root.
69-
#[derive(Debug, Clone)]
70-
pub struct RelativePathBuf {
71-
path: SystemPathBuf,
71+
/// This ensures that two resolved configurations are identical even if the position of a value has changed
72+
/// or if the values were loaded from different sources.
73+
#[derive(Clone)]
74+
pub struct RangedValue<T> {
75+
value: T,
7276
source: ValueSource,
77+
range: Option<TextRange>,
7378
}
7479

75-
impl RelativePathBuf {
76-
pub fn new(path: impl AsRef<SystemPath>, source: ValueSource) -> Self {
80+
impl<T> RangedValue<T> {
81+
pub fn new(value: T, source: ValueSource) -> Self {
82+
Self::with_source_range(value, source, TextRange::default())
83+
}
84+
85+
pub fn cli(value: T) -> Self {
86+
Self::with_source_range(value, ValueSource::Cli, TextRange::default())
87+
}
88+
89+
pub fn with_source_range(value: T, source: ValueSource, range: TextRange) -> Self {
7790
Self {
78-
path: path.as_ref().to_path_buf(),
91+
value,
92+
range: Some(range),
7993
source,
8094
}
8195
}
8296

83-
pub fn cli(path: impl AsRef<SystemPath>) -> Self {
84-
Self::new(path, ValueSource::Cli)
97+
pub fn range(&self) -> Option<TextRange> {
98+
self.range
8599
}
86100

87-
/// Returns the relative path as specified by the user.
88-
pub fn path(&self) -> &SystemPath {
89-
&self.path
101+
#[must_use]
102+
pub fn with_source(mut self, source: ValueSource) -> Self {
103+
self.source = source;
104+
self
90105
}
91106

92-
/// Returns the owned relative path.
93-
pub fn into_path_buf(self) -> SystemPathBuf {
94-
self.path
107+
pub fn into_inner(self) -> T {
108+
self.value
95109
}
110+
}
96111

97-
/// Resolves the absolute path for `self` based on from where the value origins.
98-
pub fn absolute_with_db(&self, db: &dyn Db) -> SystemPathBuf {
99-
self.absolute(db.project().root(db), db.system())
112+
impl<T> Combine for RangedValue<T> {
113+
fn combine(self, _other: Self) -> Self
114+
where
115+
Self: Sized,
116+
{
117+
self
100118
}
119+
fn combine_with(&mut self, _other: Self) {}
120+
}
101121

102-
/// Resolves the absolute path for `self` based on from where the value origins.
103-
pub fn absolute(&self, project_root: &SystemPath, system: &dyn System) -> SystemPathBuf {
104-
let relative_to = match &self.source {
105-
ValueSource::File(_) => project_root,
106-
ValueSource::Cli => system.current_directory(),
107-
};
122+
impl<T> IntoIterator for RangedValue<T>
123+
where
124+
T: IntoIterator,
125+
{
126+
type Item = T::Item;
127+
type IntoIter = T::IntoIter;
128+
fn into_iter(self) -> Self::IntoIter {
129+
self.value.into_iter()
130+
}
131+
}
108132

109-
SystemPath::absolute(&self.path, relative_to)
133+
// The type already has a `into_iter` method thanks to `Deref`.
134+
#[allow(clippy::into_iter_without_iter)]
135+
impl<'a, T> IntoIterator for &'a RangedValue<T>
136+
where
137+
&'a T: IntoIterator,
138+
{
139+
type Item = <&'a T as IntoIterator>::Item;
140+
type IntoIter = <&'a T as IntoIterator>::IntoIter;
141+
fn into_iter(self) -> Self::IntoIter {
142+
self.value.into_iter()
110143
}
111144
}
112145

113-
// TODO(micha): Derive most of those implementations once `RelativePath` uses `Value`.
114-
// and use `serde(transparent, deny_unknown_fields)`
115-
impl Combine for RelativePathBuf {
116-
fn combine(self, _other: Self) -> Self {
117-
self
146+
// The type already has a `into_iter_mut` method thanks to `DerefMut`.
147+
#[allow(clippy::into_iter_without_iter)]
148+
impl<'a, T> IntoIterator for &'a mut RangedValue<T>
149+
where
150+
&'a mut T: IntoIterator,
151+
{
152+
type Item = <&'a mut T as IntoIterator>::Item;
153+
type IntoIter = <&'a mut T as IntoIterator>::IntoIter;
154+
fn into_iter(self) -> Self::IntoIter {
155+
self.value.into_iter()
118156
}
157+
}
119158

120-
#[inline(always)]
121-
fn combine_with(&mut self, _other: Self) {}
159+
impl<T> fmt::Debug for RangedValue<T>
160+
where
161+
T: fmt::Debug,
162+
{
163+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
164+
self.value.fmt(f)
165+
}
122166
}
123167

124-
impl Hash for RelativePathBuf {
125-
fn hash<H: Hasher>(&self, state: &mut H) {
126-
self.path.hash(state);
168+
impl<T> fmt::Display for RangedValue<T>
169+
where
170+
T: fmt::Display,
171+
{
172+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
173+
self.value.fmt(f)
174+
}
175+
}
176+
177+
impl<T> Deref for RangedValue<T> {
178+
type Target = T;
179+
fn deref(&self) -> &Self::Target {
180+
&self.value
181+
}
182+
}
183+
184+
impl<T> DerefMut for RangedValue<T> {
185+
fn deref_mut(&mut self) -> &mut T {
186+
&mut self.value
127187
}
128188
}
129189

130-
impl PartialEq for RelativePathBuf {
190+
impl<T, U: ?Sized> AsRef<U> for RangedValue<T>
191+
where
192+
T: AsRef<U>,
193+
{
194+
fn as_ref(&self) -> &U {
195+
self.value.as_ref()
196+
}
197+
}
198+
199+
impl<T: PartialEq> PartialEq for RangedValue<T> {
131200
fn eq(&self, other: &Self) -> bool {
132-
self.path.eq(&other.path)
201+
self.value.eq(&other.value)
133202
}
134203
}
135204

136-
impl Eq for RelativePathBuf {}
205+
impl<T: PartialEq<T>> PartialEq<T> for RangedValue<T> {
206+
fn eq(&self, other: &T) -> bool {
207+
self.value.eq(other)
208+
}
209+
}
210+
211+
impl<T: Eq> Eq for RangedValue<T> {}
137212

138-
impl PartialOrd for RelativePathBuf {
139-
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
140-
Some(self.cmp(other))
213+
impl<T: Hash> Hash for RangedValue<T> {
214+
fn hash<H: Hasher>(&self, state: &mut H) {
215+
self.value.hash(state);
141216
}
142217
}
143218

144-
impl Ord for RelativePathBuf {
145-
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
146-
self.path.cmp(&other.path)
219+
impl<T: PartialOrd> PartialOrd for RangedValue<T> {
220+
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
221+
self.value.partial_cmp(&other.value)
147222
}
148223
}
149224

150-
impl Serialize for RelativePathBuf {
151-
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
152-
where
153-
S: Serializer,
154-
{
155-
self.path.serialize(serializer)
225+
impl<T: PartialOrd<T>> PartialOrd<T> for RangedValue<T> {
226+
fn partial_cmp(&self, other: &T) -> Option<Ordering> {
227+
self.value.partial_cmp(other)
156228
}
157229
}
158230

159-
impl<'de> Deserialize<'de> for RelativePathBuf {
231+
impl<T: Ord> Ord for RangedValue<T> {
232+
fn cmp(&self, other: &Self) -> Ordering {
233+
self.value.cmp(&other.value)
234+
}
235+
}
236+
237+
impl<'de, T> Deserialize<'de> for RangedValue<T>
238+
where
239+
T: Deserialize<'de>,
240+
{
160241
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
161242
where
162243
D: Deserializer<'de>,
163244
{
164-
let path = SystemPathBuf::deserialize(deserializer)?;
245+
let spanned: Spanned<T> = Spanned::deserialize(deserializer)?;
246+
let span = spanned.span();
247+
let range = TextRange::new(
248+
TextSize::try_from(span.start).expect("Configuration file to be smaller than 4GB"),
249+
TextSize::try_from(span.end).expect("Configuration file to be smaller than 4GB"),
250+
);
251+
165252
Ok(VALUE_SOURCE.with_borrow(|source| {
166-
let source = source
167-
.clone()
168-
.expect("Thread local `VALUE_SOURCE` to be set. Use `ValueSourceGuard` to set the value source before calling serde/toml `from_str`.");
253+
let source = source.clone().unwrap();
169254

170-
Self { path, source }
255+
Self::with_source_range(spanned.into_inner(), source, range)
171256
}))
172257
}
173258
}
259+
260+
impl<T> Serialize for RangedValue<T>
261+
where
262+
T: Serialize,
263+
{
264+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
265+
where
266+
S: Serializer,
267+
{
268+
self.value.serialize(serializer)
269+
}
270+
}
271+
272+
/// A possibly relative path in a configuration file.
273+
///
274+
/// Relative paths in configuration files or from CLI options
275+
/// require different anchoring:
276+
///
277+
/// * CLI: The path is relative to the current working directory
278+
/// * Configuration file: The path is relative to the project's root.
279+
#[derive(
280+
Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash,
281+
)]
282+
#[serde(transparent)]
283+
pub struct RelativePathBuf(RangedValue<SystemPathBuf>);
284+
285+
impl RelativePathBuf {
286+
pub fn new(path: impl AsRef<SystemPath>, source: ValueSource) -> Self {
287+
Self(RangedValue::new(path.as_ref().to_path_buf(), source))
288+
}
289+
290+
pub fn cli(path: impl AsRef<SystemPath>) -> Self {
291+
Self::new(path, ValueSource::Cli)
292+
}
293+
294+
/// Returns the relative path as specified by the user.
295+
pub fn path(&self) -> &SystemPath {
296+
&self.0
297+
}
298+
299+
/// Returns the owned relative path.
300+
pub fn into_path_buf(self) -> SystemPathBuf {
301+
self.0.into_inner()
302+
}
303+
304+
/// Resolves the absolute path for `self` based on from where the value origins.
305+
pub fn absolute_with_db(&self, db: &dyn Db) -> SystemPathBuf {
306+
self.absolute(db.project().root(db), db.system())
307+
}
308+
309+
/// Resolves the absolute path for `self` based on from where the value origins.
310+
pub fn absolute(&self, project_root: &SystemPath, system: &dyn System) -> SystemPathBuf {
311+
let relative_to = match &self.0.source {
312+
ValueSource::File(_) => project_root,
313+
ValueSource::Cli => system.current_directory(),
314+
};
315+
316+
SystemPath::absolute(&self.0, relative_to)
317+
}
318+
}
319+
320+
impl Combine for RelativePathBuf {
321+
fn combine(self, other: Self) -> Self {
322+
Self(self.0.combine(other.0))
323+
}
324+
325+
fn combine_with(&mut self, other: Self) {
326+
self.0.combine_with(other.0);
327+
}
328+
}

0 commit comments

Comments
 (0)