diff --git a/crates/compiler/builtins/roc/Str.roc b/crates/compiler/builtins/roc/Str.roc index f2a0da3f34..400ef1c458 100644 --- a/crates/compiler/builtins/roc/Str.roc +++ b/crates/compiler/builtins/roc/Str.roc @@ -135,6 +135,7 @@ interface Str withCapacity, withPrefix, graphemes, + contains, ] imports [ Bool.{ Bool, Eq }, @@ -995,3 +996,15 @@ strToNumHelp = \string -> ## ``` withPrefix : Str, Str -> Str withPrefix = \str, prefix -> Str.concat prefix str + +## Determines whether or not the first Str contains the second. +## ``` +## expect Str.contains "foobarbaz" "bar" +## expect !(Str.contains "apple" "orange") +## expect Str.contains "anything" "" +## ``` +contains : Str, Str -> Bool +contains = \haystack, needle -> + when firstMatch haystack needle is + Some _index -> Bool.true + None -> Bool.false diff --git a/crates/compiler/module/src/symbol.rs b/crates/compiler/module/src/symbol.rs index 7eada4c499..ced9509038 100644 --- a/crates/compiler/module/src/symbol.rs +++ b/crates/compiler/module/src/symbol.rs @@ -1346,6 +1346,7 @@ define_builtins! { 56 STR_IS_VALID_SCALAR: "isValidScalar" 57 STR_RELEASE_EXCESS_CAPACITY: "releaseExcessCapacity" 58 STR_WALK_UTF8: "walkUtf8" + 59 STR_CONTAINS: "contains" } 6 LIST: "List" => { 0 LIST_LIST: "List" exposed_apply_type=true // the List.List type alias diff --git a/crates/compiler/test_gen/src/gen_str.rs b/crates/compiler/test_gen/src/gen_str.rs index f5ab49bd0a..77ce175adb 100644 --- a/crates/compiler/test_gen/src/gen_str.rs +++ b/crates/compiler/test_gen/src/gen_str.rs @@ -2144,3 +2144,63 @@ fn release_excess_capacity_empty() { |value: RocStr| (value.capacity(), value) ); } + +#[test] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] +fn str_contains_positive() { + assert_evals_to!( + r#" + Str.contains "foobarbaz" "bar" + "#, + true, + bool + ); +} + +#[test] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] +fn str_contains_negative() { + assert_evals_to!( + r#" + Str.contains "apple" "orange" + "#, + false, + bool + ); +} + +#[test] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] +fn str_contains_empty_positive() { + assert_evals_to!( + r#" + Str.contains "anything" "" + "#, + true, + bool + ); +} + +#[test] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] +fn str_contains_empty_negative() { + assert_evals_to!( + r#" + Str.contains "" "anything" + "#, + false, + bool + ); +} + +#[test] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] +fn str_contains_self() { + assert_evals_to!( + r#" + Str.contains "self" "self" + "#, + true, + bool + ); +}