Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion src/usepy/string/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@
from .pascal_case import pascal_case
from .right import right
from .snake_case import snake_case
from .trim import trim, trim_start, trim_end
from .truncate import truncate
from .starts_ends_with import starts_with, ends_with
from .repeat import repeat
from .pad import pad_start, pad_end

__all__ = [
"camel_case",
Expand All @@ -20,4 +25,14 @@
"pascal_case",
"right",
"snake_case",
]
# New functions
"trim",
"trim_start",
"trim_end",
"truncate",
"starts_with",
"ends_with",
"repeat",
"pad_start",
"pad_end",
]
62 changes: 62 additions & 0 deletions src/usepy/string/pad.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
def pad_start(string: str, length: int, pad: str = ' ') -> str:
"""
Pads a string on the left to a specified length.

Args:
string (str): The string to pad.
length (int): The target length.
pad (str): The padding string. Defaults to space.

Returns:
str: The padded string.

Examples:
>>> pad_start('abc', 5)
' abc'
>>> pad_start('abc', 6, 'x')
'xxxabc'
>>> pad_start('abc', 2)
'abc'
"""
if len(string) >= length:
return string

padding_needed = length - len(string)
if not pad:
return string

# Create padding by repeating the pad string
padding = (pad * ((padding_needed // len(pad)) + 1))[:padding_needed]
return padding + string


def pad_end(string: str, length: int, pad: str = ' ') -> str:
"""
Pads a string on the right to a specified length.

Args:
string (str): The string to pad.
length (int): The target length.
pad (str): The padding string. Defaults to space.

Returns:
str: The padded string.

Examples:
>>> pad_end('abc', 5)
'abc '
>>> pad_end('abc', 6, 'x')
'abcxxx'
>>> pad_end('abc', 2)
'abc'
"""
if len(string) >= length:
return string

padding_needed = length - len(string)
if not pad:
return string

# Create padding by repeating the pad string
padding = (pad * ((padding_needed // len(pad)) + 1))[:padding_needed]
return string + padding
20 changes: 20 additions & 0 deletions src/usepy/string/repeat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
def repeat(string: str, n: int) -> str:
"""
Repeats a string n times.

Args:
string (str): The string to repeat.
n (int): The number of times to repeat.

Returns:
str: The repeated string.

Examples:
>>> repeat('abc', 3)
'abcabcabc'
>>> repeat('x', 0)
''
>>> repeat('', 5)
''
"""
return string * n
42 changes: 42 additions & 0 deletions src/usepy/string/starts_ends_with.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
def starts_with(string: str, prefix: str) -> bool:
"""
Checks if a string starts with a given prefix.

Args:
string (str): The string to check.
prefix (str): The prefix to look for.

Returns:
bool: True if the string starts with the prefix, False otherwise.

Examples:
>>> starts_with('hello world', 'hello')
True
>>> starts_with('hello world', 'world')
False
>>> starts_with('hello', '')
True
"""
return string.startswith(prefix)


def ends_with(string: str, suffix: str) -> bool:
"""
Checks if a string ends with a given suffix.

Args:
string (str): The string to check.
suffix (str): The suffix to look for.

Returns:
bool: True if the string ends with the suffix, False otherwise.

Examples:
>>> ends_with('hello world', 'world')
True
>>> ends_with('hello world', 'hello')
False
>>> ends_with('hello', '')
True
"""
return string.endswith(suffix)
63 changes: 63 additions & 0 deletions src/usepy/string/trim.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
from typing import Optional


def trim(string: str, chars: Optional[str] = None) -> str:
"""
Removes leading and trailing whitespace or specified characters.

Args:
string (str): The string to trim.
chars (Optional[str]): Characters to remove. Defaults to whitespace.

Returns:
str: The trimmed string.

Examples:
>>> trim(' hello ')
'hello'
>>> trim('xxhelloxx', 'x')
'hello'
>>> trim('\\n\\thello\\n', None)
'hello'
"""
return string.strip(chars)


def trim_start(string: str, chars: Optional[str] = None) -> str:
"""
Removes leading whitespace or specified characters.

Args:
string (str): The string to trim.
chars (Optional[str]): Characters to remove. Defaults to whitespace.

Returns:
str: The trimmed string.

Examples:
>>> trim_start(' hello ')
'hello '
>>> trim_start('xxhelloxx', 'x')
'helloxx'
"""
return string.lstrip(chars)


def trim_end(string: str, chars: Optional[str] = None) -> str:
"""
Removes trailing whitespace or specified characters.

Args:
string (str): The string to trim.
chars (Optional[str]): Characters to remove. Defaults to whitespace.

Returns:
str: The trimmed string.

Examples:
>>> trim_end(' hello ')
' hello'
>>> trim_end('xxhelloxx', 'x')
'xxhello'
"""
return string.rstrip(chars)
32 changes: 32 additions & 0 deletions src/usepy/string/truncate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from typing import Optional


def truncate(string: str, length: int = 30, omission: str = '...') -> str:
"""
Truncates a string to a specified length and adds an omission string.

Args:
string (str): The string to truncate.
length (int): The maximum length of the result. Defaults to 30.
omission (str): The string to add at the end. Defaults to '...'.

Returns:
str: The truncated string.

Examples:
>>> truncate('hello world this is a long string', 15)
'hello world...'
>>> truncate('short', 10)
'short'
>>> truncate('hello world', 8, omission='~')
'hello w~'
"""
if len(string) <= length:
return string

# Calculate how much of the original string to keep
keep_length = length - len(omission)
if keep_length <= 0:
return omission[:length]

return string[:keep_length] + omission
139 changes: 139 additions & 0 deletions tests/test_string_new.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import pytest
from usepy.string import (
trim, trim_start, trim_end,
truncate,
starts_with, ends_with,
repeat,
pad_start, pad_end,
)


class TestTrim:
"""Tests for trim functions"""

def test_trim_spaces(self):
"""Test trimming spaces"""
assert trim(' hello ') == 'hello'

def test_trim_chars(self):
"""Test trimming specific chars"""
assert trim('xxhelloxx', 'x') == 'hello'

def test_trim_newline_tab(self):
"""Test trimming newline and tab"""
assert trim('\n\thello\n', None) == 'hello'

def test_trim_start(self):
"""Test trimming start only"""
assert trim_start(' hello ') == 'hello '
assert trim_start('xxhelloxx', 'x') == 'helloxx'

def test_trim_end(self):
"""Test trimming end only"""
assert trim_end(' hello ') == ' hello'
assert trim_end('xxhelloxx', 'x') == 'xxhello'


class TestTruncate:
"""Tests for truncate() function"""

def test_truncate_long(self):
"""Test truncating long string"""
assert truncate('hello world this is a long string', 15) == 'hello world ...'

def test_truncate_short(self):
"""Test not truncating short string"""
assert truncate('short', 10) == 'short'

def test_truncate_custom_omission(self):
"""Test with custom omission"""
assert truncate('hello world', 8, omission='~') == 'hello w~'

def test_truncate_exact_length(self):
"""Test string at exact length"""
assert truncate('hello', 5) == 'hello'

def test_truncate_zero_length(self):
"""Test with very small length"""
assert truncate('hello', 2) == '..'


class TestStartsEndsWith:
"""Tests for starts_with() and ends_with() functions"""

def test_starts_with_true(self):
"""Test starts_with returns True"""
assert starts_with('hello world', 'hello') is True

def test_starts_with_false(self):
"""Test starts_with returns False"""
assert starts_with('hello world', 'world') is False

def test_starts_with_empty(self):
"""Test starts_with with empty prefix"""
assert starts_with('hello', '') is True

def test_ends_with_true(self):
"""Test ends_with returns True"""
assert ends_with('hello world', 'world') is True

def test_ends_with_false(self):
"""Test ends_with returns False"""
assert ends_with('hello world', 'hello') is False

def test_ends_with_empty(self):
"""Test ends_with with empty suffix"""
assert ends_with('hello', '') is True


class TestRepeat:
"""Tests for repeat() function"""

def test_repeat_normal(self):
"""Test repeating string"""
assert repeat('abc', 3) == 'abcabcabc'

def test_repeat_zero(self):
"""Test repeating zero times"""
assert repeat('x', 0) == ''

def test_repeat_empty(self):
"""Test repeating empty string"""
assert repeat('', 5) == ''

def test_repeat_one(self):
"""Test repeating once"""
assert repeat('x', 1) == 'x'


class TestPad:
"""Tests for pad_start() and pad_end() functions"""

def test_pad_start_default(self):
"""Test pad_start with default pad"""
assert pad_start('abc', 5) == ' abc'

def test_pad_start_custom(self):
"""Test pad_start with custom pad"""
assert pad_start('abc', 6, 'x') == 'xxxabc'

def test_pad_start_shorter(self):
"""Test pad_start when string already longer"""
assert pad_start('abc', 2) == 'abc'

def test_pad_end_default(self):
"""Test pad_end with default pad"""
assert pad_end('abc', 5) == 'abc '

def test_pad_end_custom(self):
"""Test pad_end with custom pad"""
assert pad_end('abc', 6, 'x') == 'abcxxx'

def test_pad_end_shorter(self):
"""Test pad_end when string already longer"""
assert pad_end('abc', 2) == 'abc'

def test_pad_multichar(self):
"""Test pad with multi-char string"""
assert pad_start('a', 5, 'ab') == 'ababa'
assert pad_end('a', 5, 'ab') == 'aabab'
Loading