add pad left, right and both

This commit is contained in:
publicmatt 2024-02-09 14:43:52 -08:00
parent ae491d2bae
commit 5c49c2397a
15 changed files with 240 additions and 75 deletions

View File

@ -22,13 +22,13 @@ categories = ["text-processing", "database"]
crate-type = ["cdylib"] crate-type = ["cdylib"]
[features] [features]
default = ["pg13"] default = ["pg15"]
pg11 = ["pgrx/pg11", "pgrx-tests/pg11"] pg11 = ["pgrx/pg11", "pgrx-tests/pg11"]
pg12 = ["pgrx/pg12", "pgrx-tests/pg12"] pg12 = ["pgrx/pg12", "pgrx-tests/pg12"]
pg13 = ["pgrx/pg13", "pgrx-tests/pg13"] pg13 = ["pgrx/pg13", "pgrx-tests/pg13"]
pg14 = ["pgrx/pg14", "pgrx-tests/pg14"] pg14 = ["pgrx/pg14", "pgrx-tests/pg14"]
pg15 = ["pgrx/pg15", "pgrx-tests/pg15"] pg15 = ["pgrx/pg15", "pgrx-tests/pg15"]
pg_test = [] pg_test = ["pgrx/pg15"]
[dependencies] [dependencies]
pgrx = "=0.11.2" pgrx = "=0.11.2"

View File

@ -6,6 +6,7 @@ pub mod modules {
pub mod contains; pub mod contains;
pub mod length; pub mod length;
pub mod markdown; pub mod markdown;
pub mod pad;
pub mod random; pub mod random;
pub mod split; pub mod split;
pub mod start; pub mod start;
@ -22,6 +23,7 @@ pub use modules::case::*;
pub use modules::contains::*; pub use modules::contains::*;
pub use modules::length::*; pub use modules::length::*;
pub use modules::markdown::*; pub use modules::markdown::*;
pub use modules::pad::*;
pub use modules::random::*; pub use modules::random::*;
pub use modules::split::*; pub use modules::split::*;
pub use modules::start::*; pub use modules::start::*;

View File

@ -1,4 +1,4 @@
use pgrx::prelude::*; use pgrx::pg_extern;
#[pg_extern] #[pg_extern]
pub fn str_after<'a>(input: &'a str, search: &str) -> &'a str { pub fn str_after<'a>(input: &'a str, search: &str) -> &'a str {
@ -12,9 +12,9 @@ pub fn str_after<'a>(input: &'a str, search: &str) -> &'a str {
// fn str_after_last<'a>(input: &'a str, search: &str) -> &'a str { // fn str_after_last<'a>(input: &'a str, search: &str) -> &'a str {
// } // }
#[cfg(any(test, feature = "pg_test"))] // #[cfg(any(test, feature = "pg_test"))]
mod tests { // mod tests {
#[allow(unused_imports)] // #[allow(unused_imports)]
use super::*; // use super::*;
use pgrx::prelude::*; // use pgrx::prelude::*;
} // }

View File

@ -1,5 +1,5 @@
use any_ascii::any_ascii; use any_ascii::any_ascii;
use pgrx::prelude::*; use pgrx::pg_extern;
#[pg_extern] #[pg_extern]
pub fn str_ascii(input: &str) -> String { pub fn str_ascii(input: &str) -> String {
@ -11,9 +11,9 @@ pub fn str_is_ascii(input: &str) -> bool {
input.is_ascii() input.is_ascii()
} }
#[cfg(any(test, feature = "pg_test"))] // #[cfg(any(test, feature = "pg_test"))]
mod tests { // mod tests {
#[allow(unused_imports)] // #[allow(unused_imports)]
use super::*; // use super::*;
use pgrx::prelude::*; // use pgrx::prelude::*;
} // }

View File

@ -1,4 +1,4 @@
use pgrx::prelude::*; use pgrx::pg_extern;
#[pg_extern] #[pg_extern]
pub fn str_before<'a>(input: &'a str, search: &str) -> &'a str { pub fn str_before<'a>(input: &'a str, search: &str) -> &'a str {

View File

@ -1,4 +1,4 @@
use pgrx::prelude::*; use pgrx::pg_extern;
use inflector::cases::{ use inflector::cases::{
camelcase, kebabcase, pascalcase, screamingsnakecase, snakecase, titlecase, camelcase, kebabcase, pascalcase, screamingsnakecase, snakecase, titlecase,
@ -63,9 +63,9 @@ pub fn str_scream(input: &str) -> String {
screamingsnakecase::to_screaming_snake_case(input) screamingsnakecase::to_screaming_snake_case(input)
} }
#[cfg(any(test, feature = "pg_test"))] // #[cfg(any(test, feature = "pg_test"))]
mod tests { // mod tests {
#[allow(unused_imports)] // #[allow(unused_imports)]
use super::*; // use super::*;
use pgrx::prelude::*; // use pgrx::prelude::*;
} // }

View File

@ -1,4 +1,4 @@
use pgrx::prelude::*; use pgrx::pg_extern;
#[pg_extern] #[pg_extern]
pub fn str_contains(input: &str, search: &str) -> bool { pub fn str_contains(input: &str, search: &str) -> bool {
@ -10,9 +10,9 @@ pub fn str_contains_all(input: &str, search: Vec<&str>) -> bool {
search.iter().all(|s| input.contains(s)) search.iter().all(|s| input.contains(s))
} }
#[cfg(any(test, feature = "pg_test"))] // #[cfg(any(test, feature = "pg_test"))]
mod tests { // mod tests {
#[allow(unused_imports)] // #[allow(unused_imports)]
use super::*; // use super::*;
use pgrx::prelude::*; // use pgrx::pg_extern;
} // }

View File

@ -1,13 +1,13 @@
use pgrx::prelude::*; use pgrx::pg_extern;
#[pg_extern] #[pg_extern]
fn str_length(input: &str) -> i32 { fn str_length(input: &str) -> i32 {
input.len() as i32 input.len() as i32
} }
#[cfg(any(test, feature = "pg_test"))] // #[cfg(any(test, feature = "pg_test"))]
mod tests { // mod tests {
#[allow(unused_imports)] // #[allow(unused_imports)]
use super::*; // use super::*;
use pgrx::prelude::*; // use pgrx::prelude::*;
} // }

View File

@ -1,4 +1,4 @@
use pgrx::prelude::*; use pgrx::pg_extern;
use pulldown_cmark::{html, Options, Parser}; use pulldown_cmark::{html, Options, Parser};
@ -16,9 +16,9 @@ pub fn str_markdown(input: &str) -> String {
html_output html_output
} }
#[cfg(any(test, feature = "pg_test"))] // #[cfg(any(test, feature = "pg_test"))]
mod tests { // mod tests {
#[allow(unused_imports)] // #[allow(unused_imports)]
use super::*; // use super::*;
use pgrx::prelude::*; // use pgrx::pg_extern;
} // }

162
src/modules/pad.rs Normal file
View File

@ -0,0 +1,162 @@
use pgrx::pg_extern;
#[pg_extern]
pub fn pad_left(s: &str, total_len: i32, pad: Option<&str>) -> String {
let pad_str = pad.filter(|p| !p.is_empty()).unwrap_or(" ");
let s_len = s.chars().count() as i32;
let padding_needed = if total_len > s_len {
total_len - s_len
} else {
0
} as usize;
let mut padded_string = String::new();
let pad_chars: Vec<char> = pad_str.chars().collect();
let pad_len = pad_chars.len();
for i in 0..padding_needed {
padded_string.push(pad_chars[i % pad_len]);
}
padded_string + s
}
#[pg_extern]
pub fn pad_right(s: &str, total_len: i32, pad: Option<&str>) -> String {
let pad_str = pad.filter(|p| !p.is_empty()).unwrap_or(" ");
let s_len = s.chars().count() as i32;
let padding_needed = if total_len > s_len {
total_len - s_len
} else {
0
} as usize;
s.to_owned()
+ &pad_str.repeat(padding_needed / pad_str.len())
+ &pad_str[0..padding_needed % pad_str.len()]
}
#[pg_extern]
fn pad_both(value: &str, length: i32, pad: Option<&str>) -> String {
let pad_len = length as usize;
let value_len = value.chars().count();
if value_len >= pad_len {
return value.to_string();
}
let pad_str = pad.unwrap_or(" ");
let total_padding = pad_len - value_len;
let left_padding = total_padding / 2;
let right_padding = total_padding - left_padding;
let left_pad = pad_str.repeat(left_padding / pad_str.chars().count())
+ &pad_str
.chars()
.take(left_padding % pad_str.chars().count())
.collect::<String>();
let right_pad = pad_str.repeat(right_padding / pad_str.chars().count())
+ &pad_str
.chars()
.take(right_padding % pad_str.chars().count())
.collect::<String>();
format!("{}{}{}", left_pad, value, right_pad)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pad_right_with_custom_pad() {
let result = pad_right("James", 10, Some("-"));
assert_eq!(result, "James-----");
}
#[test]
fn test_pad_right_with_space() {
let result = pad_right("James", 10, None);
assert_eq!(result, "James ");
}
#[test]
fn test_pad_right_no_padding_needed() {
let result = pad_right("James", 5, Some("-"));
assert_eq!(result, "James");
}
#[test]
fn test_pad_right_empty_string() {
let result = pad_right("", 5, Some("-"));
assert_eq!(result, "-----");
}
#[test]
fn test_pad_right_short_pad_string() {
let result = pad_right("James", 9, Some("-="));
assert_eq!(result, "James-=-=");
}
#[test]
fn test_pad_both_with_custom_pad() {
let result = pad_both("James", 10, Some("_"));
assert_eq!(result, "__James___");
}
#[test]
fn test_pad_both_with_space() {
let result = pad_both("James", 10, None);
assert_eq!(result, " James ");
}
#[test]
fn test_pad_both_no_padding_needed() {
let result = pad_both("James", 5, Some("_"));
assert_eq!(result, "James");
}
#[test]
fn test_pad_both_empty_string() {
let result = pad_both("", 5, Some("_"));
assert_eq!(result, "_____");
}
#[test]
fn test_pad_both_short_pad_string() {
let result = pad_both("James", 11, Some("_-"));
assert_eq!(result, "_-_James_-_");
}
#[test]
fn test_basic_padding() {
assert_eq!(pad_left("hello", 10, None), " hello");
}
#[test]
fn test_no_padding_needed() {
assert_eq!(pad_left("hello", 5, None), "hello");
assert_eq!(pad_left("hello", 4, None), "hello");
}
#[test]
fn test_custom_padding_character() {
assert_eq!(pad_left("hello", 10, Some("-")), "-----hello");
}
#[test]
fn test_empty_string() {
assert_eq!(pad_left("", 5, None), " ");
}
#[test]
fn test_padding_with_empty_pad_string() {
assert_eq!(pad_left("hello", 10, Some("")), " hello");
}
#[test]
fn test_long_pad_string() {
assert_eq!(pad_left("hello", 10, Some("ab")), "ababahello");
}
#[test]
fn test_unicode_characters() {
assert_eq!(pad_left("你好", 4, Some("")), "界界你好");
}
}

View File

@ -1,4 +1,4 @@
use pgrx::prelude::*; use pgrx::pg_extern;
use rand::distributions::{Alphanumeric, DistString}; use rand::distributions::{Alphanumeric, DistString};
#[pg_extern] #[pg_extern]
@ -6,9 +6,9 @@ pub fn str_random(length: i32) -> String {
Alphanumeric.sample_string(&mut rand::thread_rng(), length as usize) Alphanumeric.sample_string(&mut rand::thread_rng(), length as usize)
} }
#[cfg(any(test, feature = "pg_test"))] // #[cfg(any(test, feature = "pg_test"))]
mod tests { // mod tests {
#[allow(unused_imports)] // #[allow(unused_imports)]
use super::*; // use super::*;
use pgrx::prelude::*; // use pgrx::pg_extern;
} // }

View File

@ -1,4 +1,5 @@
use pgrx::prelude::*; use pgrx::iter::SetOfIterator;
use pgrx::pg_extern;
#[pg_extern] #[pg_extern]
pub fn str_split<'a>(input: &'a str, pattern: &str) -> Vec<&'a str> { pub fn str_split<'a>(input: &'a str, pattern: &str) -> Vec<&'a str> {
@ -10,9 +11,9 @@ pub fn str_split_set<'a>(input: &'a str, pattern: &'a str) -> SetOfIterator<'a,
SetOfIterator::new(input.split_terminator(pattern).into_iter()) SetOfIterator::new(input.split_terminator(pattern).into_iter())
} }
#[cfg(any(test, feature = "pg_test"))] // #[cfg(any(test, feature = "pg_test"))]
mod tests { // mod tests {
#[allow(unused_imports)] // #[allow(unused_imports)]
use super::*; // use super::*;
use pgrx::prelude::*; // use pgrx::prelude::*;
} // }

View File

@ -1,4 +1,4 @@
use pgrx::prelude::*; use pgrx::pg_extern;
use regex::Regex; use regex::Regex;
#[pg_extern] #[pg_extern]
@ -14,7 +14,7 @@ pub fn str_start(value: &str, prefix: &str) -> String {
mod tests { mod tests {
#[allow(unused_imports)] #[allow(unused_imports)]
use super::*; use super::*;
use pgrx::prelude::*; use pgrx::{pg_test, Spi};
#[pg_test] #[pg_test]
fn test_no_slash_prefix() { fn test_no_slash_prefix() {

View File

@ -1,13 +1,13 @@
use pgrx::prelude::*; use pgrx::pg_extern;
#[pg_extern] #[pg_extern]
pub fn str_substr(input: &str, start: i32, end: i32) -> &str { pub fn str_substr(input: &str, start: i32, end: i32) -> &str {
&input[start as usize..end as usize] &input[start as usize..end as usize]
} }
#[cfg(any(test, feature = "pg_test"))] // #[cfg(any(test, feature = "pg_test"))]
mod tests { // mod tests {
#[allow(unused_imports)] // #[allow(unused_imports)]
use super::*; // use super::*;
use pgrx::prelude::*; // use pgrx::prelude::*;
} // }

View File

@ -1,4 +1,4 @@
use pgrx::prelude::*; use pgrx::pg_extern;
use uuid::Uuid; use uuid::Uuid;
#[pg_extern] #[pg_extern]
@ -6,9 +6,9 @@ pub fn str_uuid() -> String {
Uuid::new_v4().to_string() Uuid::new_v4().to_string()
} }
#[cfg(any(test, feature = "pg_test"))] // #[cfg(any(test, feature = "pg_test"))]
mod tests { // mod tests {
#[allow(unused_imports)] // #[allow(unused_imports)]
use super::*; // use super::*;
use pgrx::prelude::*; // use pgrx::prelude::*;
} // }