#[no_std, cache_output]
impl @string {
is_empty: #[desc("Returns true if the string has a length of 0, false otherwise")]
(self) {
return self.length == 0;
},
substr: #[desc("Gets a substring beginning at the specified start and ending at the specified end.")]
(self, start: @number, end: @number) {
if start > end { return $.substr(self, end, start); }
return $.substr(self, start, end);
},
join: #[desc("Joins a list using the string.")]
(self, list: @array) {
let out = "";
for i in list {
out += @string(i);
out += self;
}
return out.substr(0, out.length-self.length);
},
split: #[desc("Splits the string by the specified seperator.")]
(self, spstr: @string) {
if spstr.length == 0 {
return self as @array;
} else {
return $.split_str(self, spstr);
}
},
starts_with: #[desc("Checks does the string starts with a string.")]
(self, substr: @string) {
if substr == '' { return true }
if substr.length > self.length { return false }
return self.substr(0, substr.length) == substr;
},
ends_with: #[desc("Checks does the string starts with a string.")]
(self, substr: @string) {
if substr == '' { return true }
if substr.length > self.length { return false }
return self.substr(self.length-substr.length, self.length) == substr;
},
index: #[desc("Gets the index of a string, if it doesn't exists returns null.")]
(self, substr: @string) {
if substr == '' { return null }
if substr.length > self.length { return null }
r = self.length - substr.length + 1
for i in 0..r {
if $.substr(self, i, i + substr.length) == substr {
return i
}
}
return null
},
contains: #[desc("Checks if the string contains a string.")]
(self, substr: @string) {
return self has substr
},
reverse: #[desc("Reverses the string.")]
(self) {
let ret_str = ""
for i in ..self.length {
ret_str += self[self.length - 1 - i]
}
return ret_str
},
lowercase: #[desc("Makes whole string lowercase.")]
(self) {
dict = {A:'a',B:'b',C:'c',D:'d',E:'e',F:'f',G:'g',H:'h',I:'i',J:'j',K:'k',L:'l',M:'m',N:'n',O:'o',P:'p',Q:'q',R:'r',S:'s',T:'t',U:'u',V:'v',W:'w',X:'x',Y:'y',Z:'z'}
return ''.join(self.split('').map(el => dict.get(el, el)))
},
uppercase: #[desc("Makes whole string uppercase.")]
(self) {
dict = {a:'A',b:'B',c:'C',d:'D',e:'E',f:'F',g:'G',h:'H',i:'I',j:'J',k:'K',l:'L',m:'M',n:'N',o:'O',p:'P',q:'Q',r:'R',s:'S',t:'T',u:'U',v:'V',w:'W',x:'X',y:'Y',z:'Z'}
return ''.join(self.split('').map(el => dict.get(el, el)))
},
is_upper: #[desc("Checks if whole string is uppercase, ignores characters that is not in the alphabet.")]
(self) {
return self.uppercase() == self
},
is_lower: #[desc("Checks if whole string is lowercase, ignores characters that is not in the alphabet.")]
(self) {
return self.lowercase() == self
},
l_pad: #[desc("Returns a left-padded version of the string")]
(self, times: @number, seq: @string = ' ') {
return seq * times + self
},
r_pad: #[desc("Returns a right-padded version of the string")]
(self, times: @number, seq: @string = ' ') {
return self + seq * times
},
l_trim: #[desc("Returns a left-trimmed verison of the string") example("
str1 = ' abcd g '
str2 = ' pog __'
$.assert(str1.l_trim() == 'abcd g ')
$.assert(str2.l_trim() == 'pog __')
")]
(self, tokens: @string | [@string] = [' ', '\n']) {
// check for tokens
for token in tokens {
if token.length != 1 {
throw "Trim tokens must all be 1-character long"
}
}
let tokenlist = tokens
if tokens.type == @string {
tokenlist = [tokens]
}
// scan left
let begin = 0
for i in ..self.length {
if tokenlist.contains(self[i]) {
continue
} else {
begin = i
break
}
}
return $.substr(self, begin, self.length)
},
r_trim: #[desc("Returns a right-trimmed version of the string") example("
str = 'abcd '
str2 = ' abcd g '
str3 = ' pog __'
$.assert(str.r_trim() == 'abcd')
$.assert(str2.r_trim() == ' abcd g')
$.assert(str3.r_trim(tokens = [' ', '_']) == ' pog')
")]
(self, tokens: @string | [@string] = [' ', '\n']) {
return self.reverse().l_trim(tokens).reverse() // lol
},
trim: #[desc("Returns a trimmed version of the string") example("
str = 'abcd '
str2 = ' abcd g '
str3 = ' pog __'
$.assert(str.trim() == 'abcd')
$.assert(str2.trim() == 'abcd g')
$.assert(str3.trim(tokens = [' ', '_']))
")]
(self, tokens: @string | [@string] = [' ', '\n']){
return self.l_trim(tokens).r_trim(tokens)
},
fmt: #[desc("Returns a formtted version of the string. Accepts either a single argument or an array") example("
name1 = 'bob'
name2 = 'alice'
$.assert('hi {}'.fmt(name1) == 'hi bob')
$.assert('hi {} and {}'.fmt([name1, name2]) == 'hi bob and alice')
$.assert('hi {1} and {0}'.fmt([name1, name2]) == 'hi alice and bob')
$.assert('{} has {} apples'.fmt([name1, 5]) == 'bob has 5 apples')
")]
(self, subs) {
blank_regex = "\\{\\}"
numbered_regex = "\\{\\d+\\}"
blanks = $.regex(blank_regex, self, 'findall', null)
numbered = $.regex(numbered_regex, self, 'findall', null)
if blanks.length != 0 && numbered.length != 0 {
throw "FormatError: String format must only be either blank {} or numbered {1} {2}"
}
// turn single arg into array
let args = subs if subs.type == @array else [subs]
let ret_str = ""
if blanks.length != 0 {
if blanks.length != args.length {
throw "FormatError: number of formats must be the same as number of args"
}
splitted = self.split("{}")
for i in ..args.length {
ret_str += splitted[i] + (args[i] as @string)
}
ret_str += splitted[-1]
} else if numbered.length != 0 {
// im not gonna make the error handling here better
// maybe later, we can make it so any brace that has a number greater than args MUST be escpaed, but now it will be ignored
ret_str = self
for i in ..args.length {
ret_str = $.regex("\\{" + i as @string + "\\}", ret_str, "replace", args[i] as @string)
}
} else {
ret_str = self
}
return ret_str
},
find: (self, re: @string) {
$.regex(re, self, "match", null)
},
findall: (self, re: @string) {
$.regex(re, self, "findall", null)
},
replace: (self, re: @string, replacement: @string) {
$.regex(re, self, "replace", replacement)
}
}