1use alloc::{
2 boxed::Box,
3 string::{String, ToString},
4 sync::Arc,
5 vec::Vec,
6};
7use core::{fmt, num::NonZeroU32, ops::Range};
8
9#[cfg(feature = "arbitrary")]
10use proptest::prelude::*;
11#[cfg(feature = "serde")]
12use serde::{Deserialize, Serialize};
13
14use super::{FileLineCol, Position, Selection, SourceId, SourceSpan, Uri};
15
16#[derive(Debug, Copy, Clone, PartialEq, Eq)]
20pub enum SourceLanguage {
21 Masm,
22 Rust,
23 Other(&'static str),
24}
25
26#[cfg(feature = "arbitrary")]
27impl Arbitrary for SourceLanguage {
28 type Parameters = ();
29 type Strategy = BoxedStrategy<Self>;
30
31 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
32 prop_oneof![
33 Just(Self::Masm),
34 Just(Self::Rust),
35 Just(Self::Other("other")),
36 Just(Self::Other("unknown")),
37 ]
38 .boxed()
39 }
40}
41
42impl AsRef<str> for SourceLanguage {
43 fn as_ref(&self) -> &str {
44 match self {
45 Self::Masm => "masm",
46 Self::Rust => "rust",
47 Self::Other(other) => other,
48 }
49 }
50}
51
52#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
57#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
58pub struct SourceFile {
59 id: SourceId,
61 #[cfg_attr(
63 feature = "serde",
64 serde(deserialize_with = "SourceContent::deserialize_and_recompute_line_starts")
65 )]
66 content: SourceContent,
67}
68
69impl miette::SourceCode for SourceFile {
70 fn read_span<'a>(
71 &'a self,
72 span: &miette::SourceSpan,
73 context_lines_before: usize,
74 context_lines_after: usize,
75 ) -> Result<Box<dyn miette::SpanContents<'a> + 'a>, miette::MietteError> {
76 let mut start =
77 u32::try_from(span.offset()).map_err(|_| miette::MietteError::OutOfBounds)?;
78 let len = u32::try_from(span.len()).map_err(|_| miette::MietteError::OutOfBounds)?;
79 let mut end = start.checked_add(len).ok_or(miette::MietteError::OutOfBounds)?;
80 if context_lines_before > 0 {
81 let line_index = self.content.line_index(start.into());
82 let start_line_index = line_index.saturating_sub(context_lines_before as u32);
83 start = self.content.line_start(start_line_index).map(ByteIndex::to_u32).unwrap_or(0);
84 }
85 if context_lines_after > 0 {
86 let line_index = self.content.line_index(end.into());
87 let end_line_index = line_index
88 .checked_add(context_lines_after as u32)
89 .ok_or(miette::MietteError::OutOfBounds)?;
90 end = self
91 .content
92 .line_range(end_line_index)
93 .map(|range| range.end.to_u32())
94 .unwrap_or_else(|| self.content.source_range().end.to_u32());
95 }
96 Ok(Box::new(ScopedSourceFileRef {
97 file: self,
98 span: miette::SourceSpan::new((start as usize).into(), end.abs_diff(start) as usize),
99 }))
100 }
101}
102
103impl SourceFile {
104 pub fn new(id: SourceId, lang: SourceLanguage, uri: Uri, content: impl Into<Box<str>>) -> Self {
106 let content = SourceContent::new(lang, uri, content.into());
107 Self { id, content }
108 }
109
110 pub fn from_raw_parts(id: SourceId, content: SourceContent) -> Self {
127 Self { id, content }
128 }
129
130 pub const fn id(&self) -> SourceId {
132 self.id
133 }
134
135 pub fn uri(&self) -> &Uri {
137 self.content.uri()
138 }
139
140 pub fn content(&self) -> &SourceContent {
142 &self.content
143 }
144
145 pub fn content_mut(&mut self) -> &mut SourceContent {
147 &mut self.content
148 }
149
150 pub fn line_count(&self) -> usize {
152 self.content.line_starts.len()
153 }
154
155 pub fn len(&self) -> usize {
157 self.content.len()
158 }
159
160 pub fn is_empty(&self) -> bool {
162 self.content.is_empty()
163 }
164
165 #[inline(always)]
167 pub fn as_str(&self) -> &str {
168 self.content.as_str()
169 }
170
171 #[inline(always)]
173 pub fn as_bytes(&self) -> &[u8] {
174 self.content.as_bytes()
175 }
176
177 #[inline]
179 pub fn source_span(&self) -> SourceSpan {
180 let range = self.content.source_range();
181 SourceSpan::new(self.id, range.start.0..range.end.0)
182 }
183
184 #[inline(always)]
191 pub fn source_slice(&self, span: impl Into<Range<usize>>) -> Option<&str> {
192 self.content.source_slice(span)
193 }
194
195 pub fn slice(self: &Arc<Self>, span: impl Into<Range<u32>>) -> SourceFileRef {
197 SourceFileRef::new(Arc::clone(self), span)
198 }
199
200 pub fn line_column_to_span(
204 &self,
205 line: LineNumber,
206 column: ColumnNumber,
207 ) -> Option<SourceSpan> {
208 let offset = self.content.line_column_to_offset(line.into(), column.into())?;
209 Some(SourceSpan::at(self.id, offset.0))
210 }
211
212 pub fn location(&self, span: SourceSpan) -> FileLineCol {
214 assert_eq!(span.source_id(), self.id, "mismatched source ids");
215
216 self.content
217 .location(ByteIndex(span.into_range().start))
218 .expect("invalid source span: starting byte is out of bounds")
219 }
220}
221
222impl AsRef<str> for SourceFile {
223 #[inline(always)]
224 fn as_ref(&self) -> &str {
225 self.as_str()
226 }
227}
228
229impl AsRef<[u8]> for SourceFile {
230 #[inline(always)]
231 fn as_ref(&self) -> &[u8] {
232 self.as_bytes()
233 }
234}
235
236#[derive(Debug, Clone)]
246pub struct SourceFileRef {
247 file: Arc<SourceFile>,
248 span: SourceSpan,
249}
250
251impl SourceFileRef {
252 pub fn new(file: Arc<SourceFile>, span: impl Into<Range<u32>>) -> Self {
257 let span = span.into();
258 let end = core::cmp::min(span.end, file.len() as u32);
259 let span = SourceSpan::new(file.id(), span.start..end);
260 Self { file, span }
261 }
262
263 pub fn source_file(&self) -> Arc<SourceFile> {
265 self.file.clone()
266 }
267
268 pub fn uri(&self) -> &Uri {
270 self.file.uri()
271 }
272
273 pub const fn span(&self) -> SourceSpan {
275 self.span
276 }
277
278 pub fn as_str(&self) -> &str {
280 self.file.source_slice(self.span).unwrap()
281 }
282
283 #[inline]
285 pub fn as_bytes(&self) -> &[u8] {
286 self.as_str().as_bytes()
287 }
288
289 pub fn len(&self) -> usize {
292 self.span.len()
293 }
294
295 pub fn is_empty(&self) -> bool {
297 self.len() == 0
298 }
299}
300
301impl Eq for SourceFileRef {}
302
303impl PartialEq for SourceFileRef {
304 fn eq(&self, other: &Self) -> bool {
305 self.as_str() == other.as_str()
306 }
307}
308
309impl Ord for SourceFileRef {
310 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
311 self.as_str().cmp(other.as_str())
312 }
313}
314
315impl PartialOrd for SourceFileRef {
316 #[inline(always)]
317 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
318 Some(self.cmp(other))
319 }
320}
321
322impl core::hash::Hash for SourceFileRef {
323 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
324 self.as_str().hash(state);
325 }
326}
327
328impl AsRef<str> for SourceFileRef {
329 #[inline(always)]
330 fn as_ref(&self) -> &str {
331 self.as_str()
332 }
333}
334
335impl AsRef<[u8]> for SourceFileRef {
336 #[inline(always)]
337 fn as_ref(&self) -> &[u8] {
338 self.as_bytes()
339 }
340}
341
342impl From<&SourceFileRef> for miette::SourceSpan {
343 fn from(source: &SourceFileRef) -> Self {
344 source.span.into()
345 }
346}
347
348struct ScopedSourceFileRef<'a> {
350 file: &'a SourceFile,
351 span: miette::SourceSpan,
352}
353
354impl<'a> miette::SpanContents<'a> for ScopedSourceFileRef<'a> {
355 #[inline]
356 fn data(&self) -> &'a [u8] {
357 let start = self.span.offset();
358 let end = start + self.span.len();
359 &self.file.as_bytes()[start..end]
360 }
361
362 #[inline]
363 fn span(&self) -> &miette::SourceSpan {
364 &self.span
365 }
366
367 fn line(&self) -> usize {
368 let offset = self.span.offset() as u32;
369 self.file.content.line_index(offset.into()).to_usize()
370 }
371
372 fn column(&self) -> usize {
373 let start = self.span.offset() as u32;
374 let end = start + self.span.len() as u32;
375 let span = SourceSpan::new(self.file.id(), start..end);
376 let loc = self.file.location(span);
377 loc.column.to_index().to_usize()
378 }
379
380 #[inline]
381 fn line_count(&self) -> usize {
382 self.file.line_count()
383 }
384
385 #[inline]
386 fn name(&self) -> Option<&str> {
387 Some(self.file.uri().as_ref())
388 }
389
390 #[inline]
391 fn language(&self) -> Option<&str> {
392 None
393 }
394}
395
396impl miette::SourceCode for SourceFileRef {
397 #[inline]
398 fn read_span<'a>(
399 &'a self,
400 span: &miette::SourceSpan,
401 context_lines_before: usize,
402 context_lines_after: usize,
403 ) -> Result<Box<dyn miette::SpanContents<'a> + 'a>, miette::MietteError> {
404 self.file.read_span(span, context_lines_before, context_lines_after)
405 }
406}
407
408#[derive(Clone)]
417#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
418pub struct SourceContent {
419 language: Box<str>,
421 uri: Uri,
423 content: String,
425 #[cfg_attr(feature = "serde", serde(default, skip))]
427 line_starts: Vec<ByteIndex>,
428 #[cfg_attr(feature = "serde", serde(default))]
430 version: i32,
431}
432
433impl fmt::Debug for SourceContent {
434 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
435 let Self {
436 language,
437 uri,
438 content,
439 line_starts,
440 version,
441 } = self;
442 f.debug_struct("SourceContent")
443 .field("version", version)
444 .field("language", language)
445 .field("uri", uri)
446 .field("size_in_bytes", &content.len())
447 .field("line_count", &line_starts.len())
448 .field("content", content)
449 .finish()
450 }
451}
452
453impl Eq for SourceContent {}
454
455impl PartialEq for SourceContent {
456 #[inline]
457 fn eq(&self, other: &Self) -> bool {
458 self.language == other.language && self.uri == other.uri && self.content == other.content
459 }
460}
461
462impl Ord for SourceContent {
463 #[inline]
464 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
465 self.uri.cmp(&other.uri).then_with(|| self.content.cmp(&other.content))
466 }
467}
468
469impl PartialOrd for SourceContent {
470 #[inline]
471 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
472 Some(self.cmp(other))
473 }
474}
475
476impl core::hash::Hash for SourceContent {
477 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
478 self.language.hash(state);
479 self.uri.hash(state);
480 self.content.hash(state);
481 }
482}
483
484#[derive(Debug, thiserror::Error)]
485pub enum SourceContentUpdateError {
486 #[error("invalid content selection: start position of {}:{} is out of bounds", .0.line, .0.character)]
487 InvalidSelectionStart(Position),
488 #[error("invalid content selection: end position of {}:{} is out of bounds", .0.line, .0.character)]
489 InvalidSelectionEnd(Position),
490}
491
492impl SourceContent {
493 pub fn new(language: impl AsRef<str>, uri: impl Into<Uri>, content: impl Into<String>) -> Self {
499 let language = language.as_ref().to_string().into_boxed_str();
500 let content: String = content.into();
501 let bytes = content.as_bytes();
502
503 assert!(
504 bytes.len() < u32::MAX as usize,
505 "unsupported source file: current maximum supported length in bytes is 2^32"
506 );
507
508 let line_starts = compute_line_starts(&content, None);
509
510 Self {
511 language,
512 uri: uri.into(),
513 content,
514 line_starts,
515 version: 0,
516 }
517 }
518
519 pub fn language(&self) -> &str {
521 &self.language
522 }
523
524 pub fn version(&self) -> i32 {
526 self.version
527 }
528
529 #[inline(always)]
531 pub fn set_version(&mut self, version: i32) {
532 self.version = version;
533 }
534
535 #[inline]
537 pub fn uri(&self) -> &Uri {
538 &self.uri
539 }
540
541 #[inline(always)]
543 pub fn as_str(&self) -> &str {
544 self.content.as_ref()
545 }
546
547 #[inline(always)]
549 pub fn as_bytes(&self) -> &[u8] {
550 self.content.as_bytes()
551 }
552
553 #[inline(always)]
555 pub fn len(&self) -> usize {
556 self.content.len()
557 }
558
559 #[inline(always)]
561 pub fn is_empty(&self) -> bool {
562 self.content.is_empty()
563 }
564
565 #[inline]
567 pub fn source_range(&self) -> Range<ByteIndex> {
568 ByteIndex(0)..ByteIndex(self.content.len() as u32)
569 }
570
571 #[inline(always)]
578 pub fn source_slice(&self, span: impl Into<Range<usize>>) -> Option<&str> {
579 self.as_str().get(span.into())
580 }
581
582 #[inline(always)]
586 pub fn byte_slice(&self, span: impl Into<Range<ByteIndex>>) -> Option<&[u8]> {
587 let Range { start, end } = span.into();
588 self.as_bytes().get(start.to_usize()..end.to_usize())
589 }
590
591 pub fn select(&self, mut range: Selection) -> Option<&str> {
596 range.canonicalize();
597
598 let start = self.line_column_to_offset(range.start.line, range.start.character)?;
599 let end = self.line_column_to_offset(range.end.line, range.end.character)?;
600
601 Some(&self.as_str()[start.to_usize()..end.to_usize()])
602 }
603
604 pub fn line_count(&self) -> usize {
606 self.line_starts.len()
607 }
608
609 pub fn line_start(&self, line_index: LineIndex) -> Option<ByteIndex> {
613 self.line_starts.get(line_index.to_usize()).copied()
614 }
615
616 pub fn last_line_index(&self) -> LineIndex {
618 LineIndex(self.line_count().saturating_sub(1).try_into().expect("too many lines in file"))
619 }
620
621 pub fn line_range(&self, line_index: LineIndex) -> Option<Range<ByteIndex>> {
623 let line_start = self.line_start(line_index)?;
624 match self.line_start(line_index + 1) {
625 Some(line_end) => Some(line_start..line_end),
626 None => Some(line_start..ByteIndex(self.content.len() as u32)),
627 }
628 }
629
630 pub fn line_index(&self, byte_index: ByteIndex) -> LineIndex {
632 match self.line_starts.binary_search(&byte_index) {
633 Ok(line) => LineIndex(line as u32),
634 Err(next_line) => LineIndex(next_line as u32 - 1),
635 }
636 }
637
638 pub fn line_column_to_offset(
642 &self,
643 line_index: LineIndex,
644 column_index: ColumnIndex,
645 ) -> Option<ByteIndex> {
646 let column_index = column_index.to_usize();
647 let line_span = self.line_range(line_index)?;
648 let line_src = self
649 .content
650 .get(line_span.start.to_usize()..line_span.end.to_usize())
651 .expect("invalid line boundaries: invalid utf-8");
652 if line_src.len() < column_index {
653 return None;
654 }
655 let (pre, _) = line_src.split_at(column_index);
656 let start = line_span.start;
657 Some(start + ByteOffset::from_str_len(pre))
658 }
659
660 pub fn location(&self, byte_index: ByteIndex) -> Option<FileLineCol> {
663 let line_index = self.line_index(byte_index);
664 let line_start_index = self.line_start(line_index)?;
665 let line_src = self.content.get(line_start_index.to_usize()..byte_index.to_usize())?;
666 let column_index = ColumnIndex::from(line_src.chars().count() as u32);
667 Some(FileLineCol {
668 uri: self.uri.clone(),
669 line: line_index.number(),
670 column: column_index.number(),
671 })
672 }
673
674 pub fn update(
681 &mut self,
682 text: String,
683 range: Option<Selection>,
684 version: i32,
685 ) -> Result<(), SourceContentUpdateError> {
686 match range {
687 Some(range) => {
688 let start = self
689 .line_column_to_offset(range.start.line, range.start.character)
690 .ok_or(SourceContentUpdateError::InvalidSelectionStart(range.start))?
691 .to_usize();
692 let end = self
693 .line_column_to_offset(range.end.line, range.end.character)
694 .ok_or(SourceContentUpdateError::InvalidSelectionEnd(range.end))?
695 .to_usize();
696 assert!(start <= end, "start of range must be less than end, got {start}..{end}",);
697 self.content.replace_range(start..end, &text);
698
699 let added_line_starts = compute_line_starts(&text, Some(start as u32));
700 let num_added = added_line_starts.len();
701 let splice_start = range.start.line.to_usize() + 1;
702 enum Deletion {
707 Empty,
708 Inclusive(usize), }
710 let deletion = if range.start.line == range.end.line {
711 Deletion::Empty
712 } else {
713 let mut end_line_for_splice = range.end.line.to_usize();
714 if !self.line_starts.is_empty() {
715 let max_idx = self.line_starts.len() - 1;
716 if end_line_for_splice > max_idx {
717 end_line_for_splice = max_idx;
718 }
719 }
720 if end_line_for_splice >= splice_start {
721 Deletion::Inclusive(end_line_for_splice)
722 } else {
723 Deletion::Empty
724 }
725 };
726
727 match deletion {
728 Deletion::Empty => {
729 self.line_starts.splice(splice_start..splice_start, added_line_starts);
730 },
731 Deletion::Inclusive(end_idx) => {
732 self.line_starts.splice(splice_start..=end_idx, added_line_starts);
733 },
734 }
735
736 let diff =
737 (text.len() as i32).saturating_sub_unsigned((end as u32) - (start as u32));
738 if diff != 0 {
739 for i in (splice_start + num_added)..self.line_starts.len() {
740 self.line_starts[i] =
741 ByteIndex(self.line_starts[i].to_u32().saturating_add_signed(diff));
742 }
743 }
744 },
745 None => {
746 self.line_starts = compute_line_starts(&text, None);
747 self.content = text;
748 },
749 }
750
751 self.version = version;
752
753 Ok(())
754 }
755}
756
757#[cfg(feature = "serde")]
758impl SourceContent {
759 fn deserialize_and_recompute_line_starts<'de, D>(deserializer: D) -> Result<Self, D::Error>
760 where
761 D: serde::Deserializer<'de>,
762 {
763 let mut content = SourceContent::deserialize(deserializer)?;
764 content.line_starts = compute_line_starts(&content.content, None);
765 Ok(content)
766 }
767}
768
769fn compute_line_starts(text: &str, text_offset: Option<u32>) -> Vec<ByteIndex> {
770 let bytes = text.as_bytes();
771 let initial_line_offset = match text_offset {
772 Some(_) => None,
773 None => Some(ByteIndex(0)),
774 };
775 let text_offset = text_offset.unwrap_or(0);
776 initial_line_offset
777 .into_iter()
778 .chain(
779 memchr::memchr_iter(b'\n', bytes)
780 .map(|offset| ByteIndex(text_offset + (offset + 1) as u32)),
781 )
782 .collect()
783}
784
785#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
790#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
791#[cfg_attr(feature = "serde", serde(transparent))]
792#[cfg_attr(all(feature = "arbitrary", test), miden_test_serde_macros::serde_test)]
793pub struct ByteIndex(pub u32);
794
795impl ByteIndex {
796 pub const fn new(index: u32) -> Self {
798 Self(index)
799 }
800
801 #[inline(always)]
803 pub const fn to_usize(self) -> usize {
804 self.0 as usize
805 }
806
807 #[inline(always)]
809 pub const fn to_u32(self) -> u32 {
810 self.0
811 }
812}
813
814impl core::ops::Add<ByteOffset> for ByteIndex {
815 type Output = ByteIndex;
816
817 fn add(self, rhs: ByteOffset) -> Self {
818 Self((self.0 as i64 + rhs.0) as u32)
819 }
820}
821
822impl core::ops::Add<u32> for ByteIndex {
823 type Output = ByteIndex;
824
825 fn add(self, rhs: u32) -> Self {
826 Self(self.0 + rhs)
827 }
828}
829
830impl core::ops::AddAssign<ByteOffset> for ByteIndex {
831 fn add_assign(&mut self, rhs: ByteOffset) {
832 *self = *self + rhs;
833 }
834}
835
836impl core::ops::AddAssign<u32> for ByteIndex {
837 fn add_assign(&mut self, rhs: u32) {
838 self.0 += rhs;
839 }
840}
841
842impl core::ops::Sub<ByteOffset> for ByteIndex {
843 type Output = ByteIndex;
844
845 fn sub(self, rhs: ByteOffset) -> Self {
846 Self((self.0 as i64 - rhs.0) as u32)
847 }
848}
849
850impl core::ops::Sub<u32> for ByteIndex {
851 type Output = ByteIndex;
852
853 fn sub(self, rhs: u32) -> Self {
854 Self(self.0 - rhs)
855 }
856}
857
858impl core::ops::SubAssign<ByteOffset> for ByteIndex {
859 fn sub_assign(&mut self, rhs: ByteOffset) {
860 *self = *self - rhs;
861 }
862}
863
864impl core::ops::SubAssign<u32> for ByteIndex {
865 fn sub_assign(&mut self, rhs: u32) {
866 self.0 -= rhs;
867 }
868}
869
870impl From<u32> for ByteIndex {
871 fn from(index: u32) -> Self {
872 Self(index)
873 }
874}
875
876impl From<ByteIndex> for u32 {
877 fn from(index: ByteIndex) -> Self {
878 index.0
879 }
880}
881
882impl fmt::Display for ByteIndex {
883 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
884 fmt::Display::fmt(&self.0, f)
885 }
886}
887
888#[cfg(feature = "arbitrary")]
889impl Arbitrary for ByteIndex {
890 type Parameters = ();
891 type Strategy = BoxedStrategy<Self>;
892
893 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
894 any::<u32>().prop_map(Self).boxed()
895 }
896}
897
898#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
900pub struct ByteOffset(i64);
901
902impl ByteOffset {
903 pub fn from_char_len(c: char) -> ByteOffset {
905 Self(c.len_utf8() as i64)
906 }
907
908 pub fn from_str_len(s: &str) -> ByteOffset {
910 Self(s.len() as i64)
911 }
912}
913
914impl core::ops::Add for ByteOffset {
915 type Output = ByteOffset;
916
917 fn add(self, rhs: Self) -> Self {
918 Self(self.0 + rhs.0)
919 }
920}
921
922impl core::ops::AddAssign for ByteOffset {
923 fn add_assign(&mut self, rhs: Self) {
924 self.0 += rhs.0;
925 }
926}
927
928impl core::ops::Sub for ByteOffset {
929 type Output = ByteOffset;
930
931 fn sub(self, rhs: Self) -> Self {
932 Self(self.0 - rhs.0)
933 }
934}
935
936impl core::ops::SubAssign for ByteOffset {
937 fn sub_assign(&mut self, rhs: Self) {
938 self.0 -= rhs.0;
939 }
940}
941
942impl fmt::Display for ByteOffset {
943 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
944 fmt::Display::fmt(&self.0, f)
945 }
946}
947
948macro_rules! declare_dual_number_and_index_type {
949 ($name:ident, $description:literal) => {
950 paste::paste! {
951 declare_dual_number_and_index_type!([<$name Index>], [<$name Number>], $description);
952 }
953 };
954
955 ($index_name:ident, $number_name:ident, $description:literal) => {
956 #[doc = concat!("A zero-indexed ", $description, " number")]
957 #[derive(Default, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
958 #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
959 #[cfg_attr(feature = "serde", serde(transparent))]
960 #[cfg_attr(all(feature = "arbitrary", test), miden_test_serde_macros::serde_test)]
961 pub struct $index_name(pub u32);
962
963 impl $index_name {
964 #[doc = concat!("Convert to a [", stringify!($number_name), "]")]
965 pub const fn number(self) -> $number_name {
966 $number_name(unsafe { NonZeroU32::new_unchecked(self.0 + 1) })
967 }
968
969 #[inline(always)]
971 pub const fn to_usize(self) -> usize {
972 self.0 as usize
973 }
974
975 #[inline(always)]
977 pub const fn to_u32(self) -> u32 {
978 self.0
979 }
980
981 pub fn checked_add(self, offset: u32) -> Option<Self> {
983 self.0.checked_add(offset).map(Self)
984 }
985
986 pub fn checked_add_signed(self, offset: i32) -> Option<Self> {
988 self.0.checked_add_signed(offset).map(Self)
989 }
990
991 pub fn checked_sub(self, offset: u32) -> Option<Self> {
993 self.0.checked_sub(offset).map(Self)
994 }
995
996 pub const fn saturating_add(self, offset: u32) -> Self {
998 Self(self.0.saturating_add(offset))
999 }
1000
1001 pub const fn saturating_add_signed(self, offset: i32) -> Self {
1004 Self(self.0.saturating_add_signed(offset))
1005 }
1006
1007 pub const fn saturating_sub(self, offset: u32) -> Self {
1009 Self(self.0.saturating_sub(offset))
1010 }
1011 }
1012
1013 impl From<u32> for $index_name {
1014 #[inline]
1015 fn from(index: u32) -> Self {
1016 Self(index)
1017 }
1018 }
1019
1020 impl From<$number_name> for $index_name {
1021 #[inline]
1022 fn from(index: $number_name) -> Self {
1023 Self(index.to_u32() - 1)
1024 }
1025 }
1026
1027 impl core::ops::Add<u32> for $index_name {
1028 type Output = Self;
1029
1030 #[inline]
1031 fn add(self, rhs: u32) -> Self {
1032 Self(self.0 + rhs)
1033 }
1034 }
1035
1036 impl core::ops::AddAssign<u32> for $index_name {
1037 fn add_assign(&mut self, rhs: u32) {
1038 let result = *self + rhs;
1039 *self = result;
1040 }
1041 }
1042
1043 impl core::ops::Add<i32> for $index_name {
1044 type Output = Self;
1045
1046 fn add(self, rhs: i32) -> Self {
1047 self.checked_add_signed(rhs).expect("invalid offset: overflow occurred")
1048 }
1049 }
1050
1051 impl core::ops::AddAssign<i32> for $index_name {
1052 fn add_assign(&mut self, rhs: i32) {
1053 let result = *self + rhs;
1054 *self = result;
1055 }
1056 }
1057
1058 impl core::ops::Sub<u32> for $index_name {
1059 type Output = Self;
1060
1061 #[inline]
1062 fn sub(self, rhs: u32) -> Self {
1063 Self(self.0 - rhs)
1064 }
1065 }
1066
1067 impl core::ops::SubAssign<u32> for $index_name {
1068 fn sub_assign(&mut self, rhs: u32) {
1069 let result = *self - rhs;
1070 *self = result;
1071 }
1072 }
1073
1074 impl fmt::Display for $index_name {
1075 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1076 fmt::Display::fmt(&self.0, f)
1077 }
1078 }
1079
1080 #[cfg(feature = "arbitrary")]
1081 impl Arbitrary for $index_name {
1082 type Parameters = ();
1083 type Strategy = BoxedStrategy<Self>;
1084
1085 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
1086 any::<u32>().prop_map(Self).boxed()
1087 }
1088 }
1089
1090 #[doc = concat!("A one-indexed ", $description, " number")]
1091 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1092 #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
1093 #[cfg_attr(feature = "serde", serde(transparent))]
1094 #[cfg_attr(
1095 all(feature = "arbitrary", test),
1096 miden_test_serde_macros::serde_test(binary_serde(true))
1097 )]
1098 pub struct $number_name(NonZeroU32);
1099
1100 impl Default for $number_name {
1101 fn default() -> Self {
1102 Self(unsafe { NonZeroU32::new_unchecked(1) })
1103 }
1104 }
1105
1106 impl $number_name {
1107 pub const fn new(number: u32) -> Option<Self> {
1108 match NonZeroU32::new(number) {
1109 Some(num) => Some(Self(num)),
1110 None => None,
1111 }
1112 }
1113
1114 #[doc = concat!("Convert to a [", stringify!($index_name), "]")]
1115 pub const fn to_index(self) -> $index_name {
1116 $index_name(self.to_u32().saturating_sub(1))
1117 }
1118
1119 #[inline(always)]
1121 pub const fn to_usize(self) -> usize {
1122 self.0.get() as usize
1123 }
1124
1125 #[inline(always)]
1127 pub const fn to_u32(self) -> u32 {
1128 self.0.get()
1129 }
1130
1131 pub fn checked_add(self, offset: u32) -> Option<Self> {
1133 self.0.checked_add(offset).map(Self)
1134 }
1135
1136 pub fn checked_add_signed(self, offset: i32) -> Option<Self> {
1138 self.0.get().checked_add_signed(offset).and_then(Self::new)
1139 }
1140
1141 pub fn checked_sub(self, offset: u32) -> Option<Self> {
1143 self.0.get().checked_sub(offset).and_then(Self::new)
1144 }
1145
1146 pub const fn saturating_add(self, offset: u32) -> Self {
1148 Self(unsafe { NonZeroU32::new_unchecked(self.0.get().saturating_add(offset)) })
1149 }
1150
1151 pub fn saturating_add_signed(self, offset: i32) -> Self {
1154 Self::new(self.to_u32().saturating_add_signed(offset)).unwrap_or_default()
1155 }
1156
1157 pub fn saturating_sub(self, offset: u32) -> Self {
1159 Self::new(self.to_u32().saturating_sub(offset)).unwrap_or_default()
1160 }
1161 }
1162
1163 impl From<NonZeroU32> for $number_name {
1164 #[inline]
1165 fn from(index: NonZeroU32) -> Self {
1166 Self(index)
1167 }
1168 }
1169
1170 impl From<$index_name> for $number_name {
1171 #[inline]
1172 fn from(index: $index_name) -> Self {
1173 Self(unsafe { NonZeroU32::new_unchecked(index.to_u32() + 1) })
1174 }
1175 }
1176
1177 impl core::ops::Add<u32> for $number_name {
1178 type Output = Self;
1179
1180 #[inline]
1181 fn add(self, rhs: u32) -> Self {
1182 Self(unsafe { NonZeroU32::new_unchecked(self.0.get() + rhs) })
1183 }
1184 }
1185
1186 impl core::ops::AddAssign<u32> for $number_name {
1187 fn add_assign(&mut self, rhs: u32) {
1188 let result = *self + rhs;
1189 *self = result;
1190 }
1191 }
1192
1193 impl core::ops::Add<i32> for $number_name {
1194 type Output = Self;
1195
1196 fn add(self, rhs: i32) -> Self {
1197 self.to_u32()
1198 .checked_add_signed(rhs)
1199 .and_then(Self::new)
1200 .expect("invalid offset: overflow occurred")
1201 }
1202 }
1203
1204 impl core::ops::AddAssign<i32> for $number_name {
1205 fn add_assign(&mut self, rhs: i32) {
1206 let result = *self + rhs;
1207 *self = result;
1208 }
1209 }
1210
1211 impl core::ops::Sub<u32> for $number_name {
1212 type Output = Self;
1213
1214 #[inline]
1215 fn sub(self, rhs: u32) -> Self {
1216 self.to_u32()
1217 .checked_sub(rhs)
1218 .and_then(Self::new)
1219 .expect("invalid offset: overflow occurred")
1220 }
1221 }
1222
1223 impl core::ops::SubAssign<u32> for $number_name {
1224 fn sub_assign(&mut self, rhs: u32) {
1225 let result = *self - rhs;
1226 *self = result;
1227 }
1228 }
1229
1230 impl fmt::Display for $number_name {
1231 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1232 fmt::Display::fmt(&self.0, f)
1233 }
1234 }
1235 };
1236}
1237
1238declare_dual_number_and_index_type!(Line, "line");
1239declare_dual_number_and_index_type!(Column, "column");
1240
1241use miden_crypto::utils::{
1245 ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable,
1246};
1247
1248impl Serializable for LineNumber {
1249 fn write_into<W: ByteWriter>(&self, target: &mut W) {
1250 target.write_u32(self.to_u32());
1251 }
1252}
1253
1254impl Deserializable for LineNumber {
1255 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
1256 let value = source.read_u32()?;
1257 Self::new(value)
1258 .ok_or_else(|| DeserializationError::InvalidValue("line number cannot be zero".into()))
1259 }
1260}
1261
1262impl Serializable for ColumnNumber {
1263 fn write_into<W: ByteWriter>(&self, target: &mut W) {
1264 target.write_u32(self.to_u32());
1265 }
1266}
1267
1268impl Deserializable for ColumnNumber {
1269 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
1270 let value = source.read_u32()?;
1271 Self::new(value).ok_or_else(|| {
1272 DeserializationError::InvalidValue("column number cannot be zero".into())
1273 })
1274 }
1275}
1276
1277#[cfg(feature = "arbitrary")]
1278impl Arbitrary for LineNumber {
1279 type Parameters = ();
1280 type Strategy = BoxedStrategy<Self>;
1281
1282 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
1283 (1..=u32::MAX)
1284 .prop_map(|value| Self::new(value).expect("non-zero value"))
1285 .boxed()
1286 }
1287}
1288
1289#[cfg(feature = "arbitrary")]
1290impl Arbitrary for ColumnNumber {
1291 type Parameters = ();
1292 type Strategy = BoxedStrategy<Self>;
1293
1294 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
1295 (1..=u32::MAX)
1296 .prop_map(|value| Self::new(value).expect("non-zero value"))
1297 .boxed()
1298 }
1299}
1300
1301#[cfg(test)]
1302mod tests {
1303 use super::*;
1304
1305 #[test]
1306 fn source_content_line_starts() {
1307 const CONTENT: &str = "\
1308begin
1309 push.1
1310 push.2
1311 add
1312end
1313";
1314 let content = SourceContent::new("masm", "foo.masm", CONTENT);
1315
1316 assert_eq!(content.line_count(), 6);
1317 assert_eq!(
1318 content
1319 .byte_slice(content.line_range(LineIndex(0)).expect("invalid line"))
1320 .expect("invalid byte range"),
1321 "begin\n".as_bytes()
1322 );
1323 assert_eq!(
1324 content
1325 .byte_slice(content.line_range(LineIndex(1)).expect("invalid line"))
1326 .expect("invalid byte range"),
1327 " push.1\n".as_bytes()
1328 );
1329 assert_eq!(
1330 content
1331 .byte_slice(content.line_range(content.last_line_index()).expect("invalid line"))
1332 .expect("invalid byte range"),
1333 "".as_bytes()
1334 );
1335 }
1336
1337 #[test]
1338 fn source_content_line_starts_after_update() {
1339 const CONTENT: &str = "\
1340begin
1341 push.1
1342 push.2
1343 add
1344end
1345";
1346 const FRAGMENT: &str = " push.2
1347 mul
1348end
1349";
1350 let mut content = SourceContent::new("masm", "foo.masm", CONTENT);
1351 content
1352 .update(FRAGMENT.to_string(), Some(Selection::from(LineIndex(4)..LineIndex(5))), 1)
1353 .expect("update failed");
1354
1355 assert_eq!(
1356 content.as_str(),
1357 "\
1358begin
1359 push.1
1360 push.2
1361 add
1362 push.2
1363 mul
1364end
1365"
1366 );
1367 assert_eq!(content.line_count(), 8);
1368 assert_eq!(
1369 content
1370 .byte_slice(content.line_range(LineIndex(0)).expect("invalid line"))
1371 .expect("invalid byte range"),
1372 "begin\n".as_bytes()
1373 );
1374 assert_eq!(
1375 content
1376 .byte_slice(content.line_range(LineIndex(3)).expect("invalid line"))
1377 .expect("invalid byte range"),
1378 " add\n".as_bytes()
1379 );
1380 assert_eq!(
1381 content
1382 .byte_slice(content.line_range(LineIndex(4)).expect("invalid line"))
1383 .expect("invalid byte range"),
1384 " push.2\n".as_bytes()
1385 );
1386 assert_eq!(
1387 content
1388 .byte_slice(content.line_range(content.last_line_index()).expect("invalid line"))
1389 .expect("invalid byte range"),
1390 "".as_bytes()
1391 );
1392 }
1393
1394 #[test]
1396 fn source_content_line_starts_with_trailing_backslash() {
1397 const CONTENT: &str =
1398 "//! Build with:\n//! cargo build \\\n//! --release\nfn main() {}\n";
1399
1400 let content = SourceContent::new("rust", "example.rs", CONTENT);
1401
1402 assert_eq!(content.line_count(), 5);
1409
1410 assert_eq!(
1412 content
1413 .byte_slice(content.line_range(LineIndex(0)).expect("invalid line"))
1414 .expect("invalid byte range"),
1415 "//! Build with:\n".as_bytes()
1416 );
1417 assert_eq!(
1418 content
1419 .byte_slice(content.line_range(LineIndex(1)).expect("invalid line"))
1420 .expect("invalid byte range"),
1421 "//! cargo build \\\n".as_bytes()
1422 );
1423 assert_eq!(
1424 content
1425 .byte_slice(content.line_range(LineIndex(2)).expect("invalid line"))
1426 .expect("invalid byte range"),
1427 "//! --release\n".as_bytes()
1428 );
1429 assert_eq!(
1430 content
1431 .byte_slice(content.line_range(LineIndex(3)).expect("invalid line"))
1432 .expect("invalid byte range"),
1433 "fn main() {}\n".as_bytes()
1434 );
1435
1436 let offset_line0 = content.line_column_to_offset(LineIndex(0), ColumnIndex(0));
1439 let offset_line1 = content.line_column_to_offset(LineIndex(1), ColumnIndex(0));
1440 let offset_line2 = content.line_column_to_offset(LineIndex(2), ColumnIndex(0));
1441 let offset_line3 = content.line_column_to_offset(LineIndex(3), ColumnIndex(0));
1442
1443 assert!(offset_line0.is_some(), "line 0 should be accessible");
1444 assert!(offset_line1.is_some(), "line 1 should be accessible");
1445 assert!(offset_line2.is_some(), "line 2 should be accessible");
1446 assert!(offset_line3.is_some(), "line 3 should be accessible");
1447
1448 assert_eq!(offset_line0.unwrap().to_u32(), 0);
1450 assert_eq!(offset_line1.unwrap().to_u32(), 16); assert_eq!(offset_line2.unwrap().to_u32(), 36); assert_eq!(offset_line3.unwrap().to_u32(), 54); }
1454
1455 #[test]
1457 fn source_content_line_starts_multiple_trailing_backslashes() {
1458 const CONTENT: &str = "line1 \\\nline2 \\\nline3 \\\nline4\n";
1460
1461 let content = SourceContent::new("text", "test.txt", CONTENT);
1462
1463 assert_eq!(content.line_count(), 5);
1465
1466 assert_eq!(
1468 content
1469 .byte_slice(content.line_range(LineIndex(0)).expect("invalid line"))
1470 .expect("invalid byte range"),
1471 "line1 \\\n".as_bytes()
1472 );
1473 assert_eq!(
1474 content
1475 .byte_slice(content.line_range(LineIndex(1)).expect("invalid line"))
1476 .expect("invalid byte range"),
1477 "line2 \\\n".as_bytes()
1478 );
1479 assert_eq!(
1480 content
1481 .byte_slice(content.line_range(LineIndex(2)).expect("invalid line"))
1482 .expect("invalid byte range"),
1483 "line3 \\\n".as_bytes()
1484 );
1485 assert_eq!(
1486 content
1487 .byte_slice(content.line_range(LineIndex(3)).expect("invalid line"))
1488 .expect("invalid byte range"),
1489 "line4\n".as_bytes()
1490 );
1491 }
1492}