import 'package:platform_macroable/platform_macroable.dart'; import 'package:platform_conditionable/platform_conditionable.dart'; import 'traits/dumpable.dart'; import 'traits/tappable.dart'; import 'facades/date.dart'; import 'carbon.dart'; import 'str.dart'; /// A class that provides string manipulation capabilities. class Stringable with Macroable, Conditionable, Dumpable, Tappable { /// The underlying string value. String _value; /// Create a new stringable instance. Stringable(this._value); /// Get the string length. int getLength() => Str.length(_value); /// Convert the given string to camel case. Stringable camel() { _value = Str.camel(_value); return this; } /// Convert the given string to studly caps case. Stringable studly() { _value = Str.studly(_value); return this; } /// Convert a string to snake case. Stringable snake([String separator = '_']) { if (_value.isEmpty) return this; final result = StringBuffer(); result.write(_value[0].toLowerCase()); for (var i = 1; i < _value.length; i++) { if (_value[i].toUpperCase() == _value[i] && _value[i].toLowerCase() != _value[i]) { result.write(separator); result.write(_value[i].toLowerCase()); } else { result.write(_value[i]); } } _value = result.toString().replaceAll('_', separator); return this; } /// Convert a string to kebab case. Stringable kebab() { _value = snake('-').toString(); return this; } /// Convert the given string to title case. Stringable title() { _value = Str.title(_value); return this; } /// Convert the given string to lower case. Stringable lower() { _value = Str.lower(_value); return this; } /// Convert the given string to upper case. Stringable upper() { _value = Str.upper(_value); return this; } /// Generate a URL friendly "slug" from a given string. Stringable slug([String separator = '-']) { _value = Str.slug(_value, separator: separator); return this; } /// Convert a string to its ASCII representation. Stringable ascii() { _value = Str.ascii(_value); return this; } /// Determine if a given string starts with a given substring. bool startsWith(dynamic needles) => Str.startsWith(_value, needles); /// Determine if a given string ends with a given substring. bool endsWith(dynamic needles) => Str.endsWith(_value, needles); /// Cap a string with a single instance of a given value. Stringable finish(String cap) { _value = Str.finish(_value, cap); return this; } /// Begin a string with a single instance of a given value. Stringable start(String prefix) { _value = Str.start(_value, prefix); return this; } /// Determine if a given string contains a given substring. bool contains(dynamic needles) => Str.contains(_value, needles); /// Limit the number of characters in a string. Stringable limit(int limit, [String end = '...']) { _value = Str.limit(_value, limit, end); return this; } /// Convert the given string to base64. Stringable toBase64() { _value = Str.toBase64(_value); return this; } /// Convert the given base64 string back to a normal string. Stringable fromBase64() { _value = Str.fromBase64(_value); return this; } /// Parse a Class[@]method style callback string. List? parseCallback([String separator = '@']) => Str.parseCallback(_value, separator); /// Mask a portion of a string with a repeated character. Stringable mask(int start, [int? length, String mask = '*']) { if (start < 0 || start >= _value.length) return this; final maskLength = length ?? (_value.length - start); final end = start + maskLength; if (end > _value.length) return this; final original = _value; _value = original.substring(0, start) + (length != null ? mask * length : mask * (original.length - start)) + (length != null ? original.substring(end) : ''); return this; } /// Pad both sides of a string with another. Stringable padBoth(int length, [String pad = ' ']) { final remaining = length - _value.length; if (remaining <= 0) return this; final leftPad = pad * (remaining ~/ 2); final rightPad = remaining % 2 == 0 ? pad * (remaining ~/ 2) : pad * ((remaining - leftPad.length)); _value = leftPad + _value + rightPad; return this; } /// Pad the left side of a string with another. Stringable padLeft(int length, [String pad = ' ']) { final remaining = length - _value.length; if (remaining <= 0) return this; _value = pad * remaining + _value; return this; } /// Pad the right side of a string with another. Stringable padRight(int length, [String pad = ' ']) { final remaining = length - _value.length; if (remaining <= 0) return this; _value = _value + pad * remaining; return this; } /// Split a string by a regular expression. List split(Pattern pattern) => _value.split(pattern); /// Get a substring of the given string. Stringable substr(int start, [int? length]) { if (start < 0) start = _value.length + start; if (start < 0) start = 0; if (start >= _value.length) return this; final end = length != null ? start + length : _value.length; _value = _value.substring(start, end > _value.length ? _value.length : end); return this; } /// Replace all occurrences of the search string with the replacement string. Stringable replace(Pattern from, String replace) { _value = _value.replaceAll(from, replace); return this; } /// Replace the first occurrence of the search string with the replacement string. Stringable replaceFirst(Pattern from, String replace) { _value = _value.replaceFirst(from, replace); return this; } /// Replace the last occurrence of the search string with the replacement string. Stringable replaceLast(Pattern from, String replace) { final matches = from.allMatches(_value).toList(); if (matches.isNotEmpty) { final lastMatch = matches.last; _value = _value.substring(0, lastMatch.start) + replace + _value.substring(lastMatch.end); } return this; } /// Convert the string to a boolean value. bool toBoolean() { final lower = _value.toLowerCase(); return lower == 'true' || lower == '1' || lower == 'yes' || lower == 'on'; } /// Trim the string of whitespace. Stringable trim() { _value = _value.trim(); return this; } /// Trim the string of the given characters. Stringable trimChars(String chars) { for (var i = 0; i < chars.length; i++) { final char = chars[i]; while (_value.startsWith(char)) { _value = _value.substring(1); } while (_value.endsWith(char)) { _value = _value.substring(0, _value.length - 1); } } return this; } /// Get the string between the given start and end delimiters. Stringable between(String start, String end) { final startPos = _value.indexOf(start); if (startPos != -1) { final endPos = _value.indexOf(end, startPos + start.length); if (endPos != -1) { _value = _value.substring(startPos + start.length, endPos); } } return this; } /// Get the portion of a string before a given value. Stringable before(String search) { final pos = _value.indexOf(search); if (pos != -1) { _value = _value.substring(0, pos); } return this; } /// Get the portion of a string after a given value. Stringable after(String search) { final pos = _value.indexOf(search); if (pos != -1) { _value = _value.substring(pos + search.length); } return this; } /// Get the portion of a string before the last occurrence of a given value. Stringable beforeLast(String search) { final pos = _value.lastIndexOf(search); if (pos != -1) { _value = _value.substring(0, pos); } return this; } /// Get the portion of a string after the last occurrence of a given value. Stringable afterLast(String search) { final pos = _value.lastIndexOf(search); if (pos != -1) { _value = _value.substring(pos + search.length); } return this; } /// Determine if a given string matches a given pattern. bool matches(Pattern pattern) => pattern.allMatches(_value).isNotEmpty; /// Parse the string into a Carbon instance. Carbon toDate() => Date.parse(_value); /// Get the string value. @override String toString() => _value; /// Compare this string with another string. bool equals(String other) => _value == other; /// Get the hash code for this string. @override int get hashCode => _value.hashCode; /// Compare this string with another object. @override bool operator ==(Object other) { if (identical(this, other)) return true; return other is Stringable && other._value == _value; } /// Create a new stringable instance from a string. static Stringable from(String value) => Stringable(value); /// Dump the string value. @override T dump([List? args]) { print(_value); if (args != null) { for (final arg in args) { print(arg); } } return this as T; } /// Dump the string value and die. @override Never dd([List? args]) { dump(args); throw Exception('Dump and die'); } }