diff --git a/src/usepy/string/__init__.py b/src/usepy/string/__init__.py index 512275b..a354b22 100644 --- a/src/usepy/string/__init__.py +++ b/src/usepy/string/__init__.py @@ -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", @@ -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", +] \ No newline at end of file diff --git a/src/usepy/string/pad.py b/src/usepy/string/pad.py new file mode 100644 index 0000000..8b10213 --- /dev/null +++ b/src/usepy/string/pad.py @@ -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 \ No newline at end of file diff --git a/src/usepy/string/repeat.py b/src/usepy/string/repeat.py new file mode 100644 index 0000000..d3ef45d --- /dev/null +++ b/src/usepy/string/repeat.py @@ -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 \ No newline at end of file diff --git a/src/usepy/string/starts_ends_with.py b/src/usepy/string/starts_ends_with.py new file mode 100644 index 0000000..f654126 --- /dev/null +++ b/src/usepy/string/starts_ends_with.py @@ -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) \ No newline at end of file diff --git a/src/usepy/string/trim.py b/src/usepy/string/trim.py new file mode 100644 index 0000000..e79df74 --- /dev/null +++ b/src/usepy/string/trim.py @@ -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) \ No newline at end of file diff --git a/src/usepy/string/truncate.py b/src/usepy/string/truncate.py new file mode 100644 index 0000000..993d79c --- /dev/null +++ b/src/usepy/string/truncate.py @@ -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 \ No newline at end of file diff --git a/tests/test_string_new.py b/tests/test_string_new.py new file mode 100644 index 0000000..67469bc --- /dev/null +++ b/tests/test_string_new.py @@ -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' \ No newline at end of file