diff --git a/builtin/arrayview.mbt b/builtin/arrayview.mbt index 938253689..8d1427de9 100644 --- a/builtin/arrayview.mbt +++ b/builtin/arrayview.mbt @@ -923,6 +923,56 @@ pub fn[T : Eq] ArrayView::ends_with( } } +///| +/// Returns a sub-view with `prefix` removed from the start, or `None` if the +/// view does not start with `prefix`. The returned view shares the original +/// backing array — no allocation. +/// +/// Example: +/// +/// ```mbt check +/// test { +/// let v = [1, 2, 3, 4, 5][:] +/// inspect(v.strip_prefix([1, 2][:]), content="Some([3, 4, 5])") +/// inspect(v.strip_prefix([2, 3][:]), content="None") +/// } +/// ``` +pub fn[T : Eq] ArrayView::strip_prefix( + self : ArrayView[T], + prefix : ArrayView[T], +) -> ArrayView[T]? { + if self.starts_with(prefix) { + Some(self[prefix.length():]) + } else { + None + } +} + +///| +/// Returns a sub-view with `suffix` removed from the end, or `None` if the +/// view does not end with `suffix`. The returned view shares the original +/// backing array — no allocation. +/// +/// Example: +/// +/// ```mbt check +/// test { +/// let v = [1, 2, 3, 4, 5][:] +/// inspect(v.strip_suffix([4, 5][:]), content="Some([1, 2, 3])") +/// inspect(v.strip_suffix([3, 4][:]), content="None") +/// } +/// ``` +pub fn[T : Eq] ArrayView::strip_suffix( + self : ArrayView[T], + suffix : ArrayView[T], +) -> ArrayView[T]? { + if self.ends_with(suffix) { + Some(self[:self.length() - suffix.length()]) + } else { + None + } +} + ///| /// Performs a binary search on a sorted array view. /// diff --git a/builtin/arrayview_test.mbt b/builtin/arrayview_test.mbt index cb440a70f..d8c52297d 100644 --- a/builtin/arrayview_test.mbt +++ b/builtin/arrayview_test.mbt @@ -807,3 +807,74 @@ test "ArrayView::filter_map does not mutate source" { let _ = v.filter_map(x => Some(x + 100)) inspect(arr, content="[1, 2, 3, 4]") } + +///| +test "ArrayView::strip_prefix basic" { + let v = [1, 2, 3, 4, 5][:] + inspect(v.strip_prefix([1, 2][:]), content="Some([3, 4, 5])") + inspect(v.strip_prefix([2, 3][:]), content="None") +} + +///| +test "ArrayView::strip_prefix empty prefix" { + let v = [1, 2, 3][:] + inspect(v.strip_prefix([][:]), content="Some([1, 2, 3])") +} + +///| +test "ArrayView::strip_prefix full match" { + let v = [1, 2, 3][:] + inspect(v.strip_prefix([1, 2, 3][:]), content="Some([])") +} + +///| +test "ArrayView::strip_prefix too long" { + let v = [1, 2][:] + inspect(v.strip_prefix([1, 2, 3][:]), content="None") +} + +///| +test "ArrayView::strip_prefix on sliced view" { + let v = [0, 1, 2, 3, 4][1:] + inspect(v.strip_prefix([1, 2][:]), content="Some([3, 4])") +} + +///| +test "ArrayView::strip_prefix shares backing" { + let arr = [1, 2, 3, 4, 5] + guard arr[:].strip_prefix([1, 2][:]) is Some(rest) + // Mutating the source propagates to the stripped view. + arr[2] = 99 + inspect(rest, content="[99, 4, 5]") +} + +///| +test "ArrayView::strip_suffix basic" { + let v = [1, 2, 3, 4, 5][:] + inspect(v.strip_suffix([4, 5][:]), content="Some([1, 2, 3])") + inspect(v.strip_suffix([3, 4][:]), content="None") +} + +///| +test "ArrayView::strip_suffix empty suffix" { + let v = [1, 2, 3][:] + inspect(v.strip_suffix([][:]), content="Some([1, 2, 3])") +} + +///| +test "ArrayView::strip_suffix full match" { + let v = [1, 2, 3][:] + inspect(v.strip_suffix([1, 2, 3][:]), content="Some([])") +} + +///| +test "ArrayView::strip_suffix too long" { + let v = [1, 2][:] + inspect(v.strip_suffix([0, 1, 2][:]), content="None") +} + +///| +test "ArrayView::strip_suffix on sliced view" { + let v = [0, 1, 2, 3, 4][:4] + inspect(v.strip_suffix([2, 3][:]), content="Some([0, 1])") +} diff --git a/builtin/pkg.generated.mbti b/builtin/pkg.generated.mbti index e632cca9d..8f9b39df0 100644 --- a/builtin/pkg.generated.mbti +++ b/builtin/pkg.generated.mbti @@ -225,6 +225,8 @@ pub fn[T : Eq] ArrayView::search(Self[T], T) -> Int? pub fn[T] ArrayView::search_by(Self[T], (T) -> Bool raise?) -> Int? raise? pub fn[T] ArrayView::start_offset(Self[T]) -> Int pub fn[T : Eq] ArrayView::starts_with(Self[T], Self[T]) -> Bool +pub fn[T : Eq] ArrayView::strip_prefix(Self[T], Self[T]) -> Self[T]? +pub fn[T : Eq] ArrayView::strip_suffix(Self[T], Self[T]) -> Self[T]? pub fn[T] ArrayView::suffixes(Self[T], include_empty? : Bool) -> Iter[Self[T]] pub fn[T] ArrayView::to_array(Self[T]) -> Array[T] #alias("_[_:_]")