Skip to content

Commit 48ade40

Browse files
better api
1 parent b472454 commit 48ade40

File tree

5 files changed

+71
-86
lines changed

5 files changed

+71
-86
lines changed

crates/pgt_text_size/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020
#![warn(missing_debug_implementations, missing_docs)]
2121

2222
mod range;
23-
mod range_adjustment_tracker;
2423
mod size;
24+
mod text_range_replacement;
2525
mod traits;
2626

2727
#[cfg(feature = "serde")]
@@ -32,8 +32,8 @@ mod schemars_impls;
3232

3333
pub use crate::{
3434
range::TextRange,
35-
range_adjustment_tracker::{RangeAdjustmentsTracker, RangeAdjustmentsTrackerBuilder},
3635
size::TextSize,
36+
text_range_replacement::{TextRangeReplacement, TextRangeReplacementBuilder},
3737
traits::TextLen,
3838
};
3939

crates/pgt_text_size/src/range_adjustment_tracker.rs renamed to crates/pgt_text_size/src/text_range_replacement.rs

Lines changed: 53 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ struct AdjustmentMarker {
1111
#[allow(dead_code)]
1212
original_range: TextRange,
1313
adjusted_range: TextRange,
14+
replacement_txt: String,
1415
registered_offset_at_start: TextSize,
1516

1617
#[allow(dead_code)]
@@ -38,6 +39,7 @@ impl AdjustmentMarker {
3839
AdjustmentMarker {
3940
original_range,
4041
adjustment_direction,
42+
replacement_txt: replacement_txt.into(),
4143
range_size_difference: TextSize::new(range_size_difference.try_into().unwrap()),
4244

4345
// will be calculated during `.build()`
@@ -65,20 +67,18 @@ impl AdjustmentMarker {
6567
/// This builder allows you to register multiple text replacements and their effects on ranges,
6668
/// then build a tracker that can map positions between the original and adjusted text.
6769
#[derive(Debug)]
68-
pub struct RangeAdjustmentsTrackerBuilder {
70+
pub struct TextRangeReplacementBuilder {
6971
markers: Vec<AdjustmentMarker>,
72+
text: String,
7073
}
7174

72-
impl Default for RangeAdjustmentsTrackerBuilder {
73-
fn default() -> Self {
74-
Self::new()
75-
}
76-
}
77-
78-
impl RangeAdjustmentsTrackerBuilder {
75+
impl TextRangeReplacementBuilder {
7976
/// Creates a new empty builder for range adjustments tracking.
80-
pub fn new() -> Self {
81-
Self { markers: vec![] }
77+
pub fn new(text: &str) -> Self {
78+
Self {
79+
markers: vec![],
80+
text: text.to_string(),
81+
}
8282
}
8383

8484
/// Registers a text replacement that affects range positions.
@@ -87,7 +87,7 @@ impl RangeAdjustmentsTrackerBuilder {
8787
///
8888
/// * `original_range` - The range in the original text that will be replaced
8989
/// * `replacement_text` - The text that will replace the content in the original range
90-
pub fn register_adjustment(&mut self, original_range: TextRange, replacement_text: &str) {
90+
pub fn replace_range(&mut self, original_range: TextRange, replacement_text: &str) {
9191
if usize::from(original_range.len()) == replacement_text.len() {
9292
return;
9393
}
@@ -105,7 +105,7 @@ impl RangeAdjustmentsTrackerBuilder {
105105
/// # Panics
106106
///
107107
/// Panics if any adjustment involves shortening the text, as this is not yet implemented.
108-
pub fn build(mut self) -> RangeAdjustmentsTracker {
108+
pub fn build(mut self) -> TextRangeReplacement {
109109
self.markers.sort_by_key(|r| r.original_range.start());
110110

111111
let mut total_offset: usize = 0;
@@ -132,8 +132,15 @@ impl RangeAdjustmentsTrackerBuilder {
132132
}
133133
}
134134

135-
RangeAdjustmentsTracker {
135+
for marker in self.markers.iter().rev() {
136+
let std_range: std::ops::Range<usize> = marker.original_range.into();
137+
self.text
138+
.replace_range(std_range, marker.replacement_txt.as_str());
139+
}
140+
141+
TextRangeReplacement {
136142
markers: self.markers,
143+
text: self.text,
137144
}
138145
}
139146
}
@@ -150,11 +157,17 @@ impl RangeAdjustmentsTrackerBuilder {
150157
/// `$2` with `auth.users`, this tracker can map positions in the adjusted text
151158
/// `"select email from auth.users"` back to positions in the original text.
152159
#[derive(Debug)]
153-
pub struct RangeAdjustmentsTracker {
160+
pub struct TextRangeReplacement {
154161
markers: Vec<AdjustmentMarker>,
162+
text: String,
155163
}
156164

157-
impl RangeAdjustmentsTracker {
165+
impl TextRangeReplacement {
166+
/// Returns the adjusted text.
167+
pub fn text(&self) -> &str {
168+
&self.text
169+
}
170+
158171
/// Maps a position in the adjusted text back to its corresponding position in the original text.
159172
///
160173
///
@@ -208,68 +221,50 @@ impl RangeAdjustmentsTracker {
208221
mod tests {
209222
use crate::TextSize;
210223

211-
use crate::range_adjustment_tracker::RangeAdjustmentsTrackerBuilder;
212-
213-
fn range_of_substr(full_str: &str, sub_str: &str) -> Option<std::ops::Range<usize>> {
214-
for (i, _) in full_str.char_indices() {
215-
if &full_str[i..i + sub_str.len()] == sub_str {
216-
return Some(i..i + sub_str.len());
217-
}
218-
}
219-
None
220-
}
224+
use crate::text_range_replacement::TextRangeReplacementBuilder;
221225

222226
#[test]
223227
fn tracks_adjustments() {
224-
let mut sql = "select $1 from $2 where $3 = $4 limit 5;".to_string();
228+
let sql = "select $1 from $2 where $3 = $4 limit 5;";
225229

226230
let range_1: std::ops::Range<usize> = 7..9;
227231
let range_2: std::ops::Range<usize> = 15..17;
228232
let range_3: std::ops::Range<usize> = 24..26;
229233
let range_4: std::ops::Range<usize> = 29..31;
230234
let og_end = sql.len();
231235

232-
let mut adjustment_tracker_builder = RangeAdjustmentsTrackerBuilder::new();
236+
let mut replacement_builder = TextRangeReplacementBuilder::new(sql);
233237

234238
let replacement_4 = "'00000000-0000-0000-0000-000000000000'";
235-
adjustment_tracker_builder
236-
.register_adjustment(range_4.clone().try_into().unwrap(), replacement_4);
237-
sql.replace_range(range_4.clone(), replacement_4);
238-
239239
let replacement_3 = "id";
240-
adjustment_tracker_builder
241-
.register_adjustment(range_3.clone().try_into().unwrap(), replacement_3);
242-
sql.replace_range(range_3.clone(), replacement_3);
243-
244240
let replacement_2 = "auth.users";
245-
adjustment_tracker_builder
246-
.register_adjustment(range_2.clone().try_into().unwrap(), replacement_2);
247-
sql.replace_range(range_2.clone(), replacement_2);
248-
249241
let replacement_1 = "email";
250-
adjustment_tracker_builder
251-
.register_adjustment(range_1.clone().try_into().unwrap(), replacement_1);
252-
sql.replace_range(range_1.clone(), replacement_1);
242+
243+
// start in the middle – the tracker builder can deal with unordered registers
244+
replacement_builder.replace_range(range_2.clone().try_into().unwrap(), replacement_2);
245+
replacement_builder.replace_range(range_4.clone().try_into().unwrap(), replacement_4);
246+
replacement_builder.replace_range(range_1.clone().try_into().unwrap(), replacement_1);
247+
replacement_builder.replace_range(range_3.clone().try_into().unwrap(), replacement_3);
248+
249+
let text_replacement = replacement_builder.build();
253250

254251
assert_eq!(
255-
sql.as_str(),
252+
text_replacement.text(),
256253
"select email from auth.users where id = '00000000-0000-0000-0000-000000000000' limit 5;"
257254
);
258255

259-
let adjustment_tracker = adjustment_tracker_builder.build();
260-
261-
let repl_range_1 = range_of_substr(sql.as_str(), replacement_1).unwrap();
262-
let repl_range_2 = range_of_substr(sql.as_str(), replacement_2).unwrap();
263-
let repl_range_3 = range_of_substr(sql.as_str(), replacement_3).unwrap();
264-
let repl_range_4 = range_of_substr(sql.as_str(), replacement_4).unwrap();
256+
let repl_range_1 = 7..12;
257+
let repl_range_2 = 18..28;
258+
let repl_range_3 = 35..37;
259+
let repl_range_4 = 40..78;
265260

266261
// |select |email from auth.users where id = '00000000-0000-0000-0000-000000000000' limit 5;
267262
// maps to
268263
// |select |$1 from $2 where $3 = $4 limit 5;
269264
for i in 0..repl_range_1.clone().start {
270265
let between_og_0_1 = 0..range_1.start;
271266
let adjusted =
272-
adjustment_tracker.to_original_position(TextSize::new(i.try_into().unwrap()));
267+
text_replacement.to_original_position(TextSize::new(i.try_into().unwrap()));
273268
assert!(between_og_0_1.contains(&usize::from(adjusted)));
274269
}
275270

@@ -278,7 +273,7 @@ mod tests {
278273
// select |$1| from $2 where $3 = $4 limit 5;
279274
for i in repl_range_1.clone() {
280275
let pos = TextSize::new(i.try_into().unwrap());
281-
let og_pos = adjustment_tracker.to_original_position(pos);
276+
let og_pos = text_replacement.to_original_position(pos);
282277
assert!(range_1.contains(&usize::from(og_pos)));
283278
}
284279

@@ -288,7 +283,7 @@ mod tests {
288283
for i in repl_range_1.end..repl_range_2.clone().start {
289284
let between_og_1_2 = range_1.end..range_2.start;
290285
let adjusted =
291-
adjustment_tracker.to_original_position(TextSize::new(i.try_into().unwrap()));
286+
text_replacement.to_original_position(TextSize::new(i.try_into().unwrap()));
292287
assert!(between_og_1_2.contains(&usize::from(adjusted)));
293288
}
294289

@@ -297,7 +292,7 @@ mod tests {
297292
// select $1 from |$2| where $3 = $4 limit 5;
298293
for i in repl_range_2.clone() {
299294
let pos = TextSize::new(i.try_into().unwrap());
300-
let og_pos = adjustment_tracker.to_original_position(pos);
295+
let og_pos = text_replacement.to_original_position(pos);
301296
assert!(range_2.contains(&usize::from(og_pos)));
302297
}
303298

@@ -307,7 +302,7 @@ mod tests {
307302
for i in repl_range_2.end..repl_range_3.clone().start {
308303
let between_og_2_3 = range_2.end..range_3.start;
309304
let adjusted =
310-
adjustment_tracker.to_original_position(TextSize::new(i.try_into().unwrap()));
305+
text_replacement.to_original_position(TextSize::new(i.try_into().unwrap()));
311306
assert!(between_og_2_3.contains(&usize::from(adjusted)));
312307
}
313308

@@ -318,7 +313,7 @@ mod tests {
318313
// NOTE: this isn't even hanlded by the tracker, since `id` has the same length as `$3`
319314
for i in repl_range_3.clone() {
320315
let pos = TextSize::new(i.try_into().unwrap());
321-
let og_pos = adjustment_tracker.to_original_position(pos);
316+
let og_pos = text_replacement.to_original_position(pos);
322317
assert!(range_3.contains(&usize::from(og_pos)));
323318
}
324319

@@ -328,7 +323,7 @@ mod tests {
328323
for i in repl_range_3.end..repl_range_4.clone().start {
329324
let between_og_3_4 = range_3.end..range_4.start;
330325
let adjusted =
331-
adjustment_tracker.to_original_position(TextSize::new(i.try_into().unwrap()));
326+
text_replacement.to_original_position(TextSize::new(i.try_into().unwrap()));
332327
assert!(between_og_3_4.contains(&usize::from(adjusted)));
333328
}
334329

@@ -337,7 +332,7 @@ mod tests {
337332
// select $1 from $2 where $3 = |$4| limit 5;
338333
for i in repl_range_4.clone() {
339334
let pos = TextSize::new(i.try_into().unwrap());
340-
let og_pos = adjustment_tracker.to_original_position(pos);
335+
let og_pos = text_replacement.to_original_position(pos);
341336
assert!(range_4.contains(&usize::from(og_pos)));
342337
}
343338

@@ -347,7 +342,7 @@ mod tests {
347342
for i in repl_range_4.end..sql.len() {
348343
let between_og_4_end = range_4.end..og_end;
349344
let adjusted =
350-
adjustment_tracker.to_original_position(TextSize::new(i.try_into().unwrap()));
345+
text_replacement.to_original_position(TextSize::new(i.try_into().unwrap()));
351346
assert!(between_og_4_end.contains(&usize::from(adjusted)));
352347
}
353348
}

crates/pgt_typecheck/src/diagnostics.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::io;
22

33
use pgt_console::markup;
44
use pgt_diagnostics::{Advices, Diagnostic, LogCategory, MessageAndDescription, Severity, Visit};
5-
use pgt_text_size::{RangeAdjustmentsTracker, TextRange, TextSize};
5+
use pgt_text_size::{TextRange, TextRangeReplacement, TextSize};
66
use sqlx::postgres::{PgDatabaseError, PgSeverity};
77

88
/// A specialized diagnostic for the typechecker.
@@ -97,16 +97,15 @@ impl Advices for TypecheckAdvices {
9797
pub(crate) fn create_type_error(
9898
pg_err: &PgDatabaseError,
9999
ts: &tree_sitter::Tree,
100-
range_adjustments: RangeAdjustmentsTracker,
100+
txt_replacement: TextRangeReplacement,
101101
) -> TypecheckDiagnostic {
102102
let position = pg_err.position().and_then(|pos| match pos {
103103
sqlx::postgres::PgErrorPosition::Original(pos) => Some(pos - 1),
104104
_ => None,
105105
});
106106

107107
let range = position.and_then(|pos| {
108-
let adjusted =
109-
range_adjustments.to_original_position(TextSize::new(pos.try_into().unwrap()));
108+
let adjusted = txt_replacement.to_original_position(TextSize::new(pos.try_into().unwrap()));
110109

111110
ts.root_node()
112111
.named_descendant_for_byte_range(adjusted.into(), adjusted.into())

crates/pgt_typecheck/src/lib.rs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ pub async fn check_sql(
4848
// each typecheck operation.
4949
conn.close_on_drop();
5050

51-
let (prepared, range_adjustments) = apply_identifiers(
51+
let replacement = apply_identifiers(
5252
params.identifiers,
5353
params.schema_cache,
5454
params.tree,
@@ -68,17 +68,13 @@ pub async fn check_sql(
6868
conn.execute(&*search_path_query).await?;
6969
}
7070

71-
let res = conn.prepare(&prepared).await;
71+
let res = conn.prepare(&replacement.text()).await;
7272

7373
match res {
7474
Ok(_) => Ok(None),
7575
Err(sqlx::Error::Database(err)) => {
7676
let pg_err = err.downcast_ref::<PgDatabaseError>();
77-
Ok(Some(create_type_error(
78-
pg_err,
79-
params.tree,
80-
range_adjustments,
81-
)))
77+
Ok(Some(create_type_error(pg_err, params.tree, replacement)))
8278
}
8379
Err(err) => Err(err),
8480
}

0 commit comments

Comments
 (0)