iron-shell 14.0.3

The world's first multi-agent shell with typed functional pipelines and multi-modal AI
# IronShell String Utilities
# String manipulation and transformation functions

# ===== Splitting and Joining =====

# Split string into words: any run of whitespace (spaces, tabs, newlines, CR)
pub let words = fn(s) => regex_replace(s, "\\s+", " ") | split(" ") | where(fn(w) => len(w) > 0)

# Join words into string with spaces
pub let unwords = fn(arr) => join(arr, " ")

# Join lines into string
pub let unlines = fn(arr) => join(arr, "\n")

# ===== Case Transformation =====

# Capitalize first letter, lowercase rest
pub let capitalize = fn(s) => match len(s) {
    0 => "",
    _ => upper(s | slice(0, 1)) + lower(s | slice(1, len(s)))
}

# Title case: capitalize each word
pub let title_case = fn(s) => words(s) | map(capitalize) | join(" ")

# Convert to snake_case
pub let snake_case = fn(s) => words(lower(s)) | join("_")

# Convert to kebab-case
pub let kebab_case = fn(s) => words(lower(s)) | join("-")

# Convert to SCREAMING_SNAKE_CASE
pub let screaming_snake = fn(s) => upper(snake_case(s))

# ===== Checking =====

# Check if string is blank (empty or only whitespace)
pub let is_blank = fn(s) => len(trim(s)) == 0

# Check if string is not blank
pub let is_not_blank = fn(s) => !is_blank(s)

# Check if string is all uppercase: it has a cased letter and no lowercase one
# (as Python's str.isupper; "123" and "" are not)
pub let is_upper = fn(s) => s == upper(s) && s != lower(s)

# Check if string is all lowercase: it has a cased letter and no uppercase one
pub let is_lower = fn(s) => s == lower(s) && s != upper(s)

# ===== Utility =====

# Repeat string n times; a negative count is refused
pub let repeat = fn(s, n) => match {
    _ if n < 0 => error("repeat: count must not be negative", {n: n}),
    _ => range(0, n) | map(fn(_) => s) | join("")
}

# Reverse a string
pub let reverse_str = fn(s) => s | split("") | reverse | join("")

# Count non-overlapping occurrences of substring. An empty substring counts
# len(s) + 1 positions, as Python's str.count does.
pub let count_substr = fn(s, sub) => match {
    _ if sub == "" => len(s) + 1,
    _ => len(split(s, sub)) - 1
}

# Lines: use the builtin lines(text), which drops the final line terminator and
# CR before LF. This file does not define it: an import would shadow the builtin.

# Export all public items
export {
    words, unwords, unlines,
    capitalize, title_case, snake_case, kebab_case, screaming_snake,
    is_blank, is_not_blank, is_upper, is_lower,
    repeat, reverse_str, count_substr
}