init fork from abumni/pg_str
This commit is contained in:
commit
54c1e97f89
|
@ -0,0 +1,6 @@
|
||||||
|
.DS_Store
|
||||||
|
.idea/
|
||||||
|
/target
|
||||||
|
*.iml
|
||||||
|
**/*.rs.bk
|
||||||
|
Cargo.lock
|
|
@ -0,0 +1,53 @@
|
||||||
|
[package]
|
||||||
|
name = "pg_str"
|
||||||
|
version = "0.3.0"
|
||||||
|
edition = "2021"
|
||||||
|
authors = ["Matt<matt@publicmatt.com"]
|
||||||
|
include = [
|
||||||
|
"**/*.rs",
|
||||||
|
"Cargo.toml",
|
||||||
|
"README.md"
|
||||||
|
]
|
||||||
|
readme = "README.md"
|
||||||
|
repository = "https://github.com/publicmatt/pg_str"
|
||||||
|
documentation = "https://docs.rs/pg_str"
|
||||||
|
homepage = "https://github.com/publicmatt/pg_str"
|
||||||
|
license="MIT"
|
||||||
|
description = """
|
||||||
|
Adds str functions to Postgresql via an extension."""
|
||||||
|
keywords = ["postgresql", "string", "cases", "pluralize", "markdown"]
|
||||||
|
categories = ["text-processing", "database"]
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["cdylib"]
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["pg13"]
|
||||||
|
pg11 = ["pgx/pg11", "pgx-tests/pg11"]
|
||||||
|
pg12 = ["pgx/pg12", "pgx-tests/pg12"]
|
||||||
|
pg13 = ["pgx/pg13", "pgx-tests/pg13"]
|
||||||
|
pg14 = ["pgx/pg14", "pgx-tests/pg14"]
|
||||||
|
pg15 = ["pgx/pg15", "pgx-tests/pg15"]
|
||||||
|
pg_test = []
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
pgx = "=0.6.1"
|
||||||
|
Inflector = "0.11.4"
|
||||||
|
str_slug = "0.1.3"
|
||||||
|
pulldown-cmark = "0.9.1"
|
||||||
|
any_ascii = "0.3.0"
|
||||||
|
rand = "0.8.4"
|
||||||
|
uuid = "1.2.2"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
pgx-tests = "=0.6.1"
|
||||||
|
|
||||||
|
[profile.dev]
|
||||||
|
panic = "unwind"
|
||||||
|
lto = "thin"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
panic = "unwind"
|
||||||
|
opt-level = 3
|
||||||
|
lto = "fat"
|
||||||
|
codegen-units = 1
|
|
@ -0,0 +1,15 @@
|
||||||
|
PACKAGE_VERSION=0.3.0
|
||||||
|
PACKAGE_NAME=pg_str
|
||||||
|
PG_VERSION=15
|
||||||
|
PREFIX=target/release/$(PACKAGE_NAME)-pg$(PG_VERSION)
|
||||||
|
|
||||||
|
build:
|
||||||
|
cargo pgx package
|
||||||
|
|
||||||
|
install: $(PREFIX)
|
||||||
|
cp -f $(PREFIX)/usr/share/postgresql/$(PG_VERSION)/extension/$(PACKAGE_NAME)--$(PACKAGE_VERSION).sql /usr/share/postgresql/$(PG_VERSION)/extension/$(PACKAGE_NAME)--$(PACKAGE_VERSION).sql
|
||||||
|
cp -f $(PREFIX)/usr/lib/postgresql/$(PG_VERSION)/lib/$(PACKAGE_NAME).so /usr/lib/postgresql/$(PG_VERSION)/lib/$(PACKAGE_NAME).so
|
||||||
|
cp -f $(PREFIX)/usr/share/postgresql/$(PG_VERSION)/extension/$(PACKAGE_NAME).control /usr/share/postgresql/$(PG_VERSION)/extension/$(PACKAGE_NAME).control
|
||||||
|
|
||||||
|
restart:
|
||||||
|
sudo service postgresql restart
|
|
@ -0,0 +1,6 @@
|
||||||
|
comment = 'pg_str: Created by pgx'
|
||||||
|
default_version = '@CARGO_VERSION@'
|
||||||
|
module_pathname = '$libdir/pg_str'
|
||||||
|
relocatable = false
|
||||||
|
superuser = false
|
||||||
|
schema = str
|
|
@ -0,0 +1,74 @@
|
||||||
|
# Postgresql String Extension
|
||||||
|
|
||||||
|
A better way of handling string manipulation and transformations in Postgresql.
|
||||||
|
|
||||||
|
Function api and behavior is inspired by those available in the Laravel web framework: https://laravel.com/docs/8.x/helpers#strings-method-list
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
```
|
||||||
|
git clone git@github.com:publicmatt/pg_str
|
||||||
|
cd pg_str
|
||||||
|
cargo pgx package # run cargo install pgx first
|
||||||
|
sudo make install # adjust Makefile if using different version of postgresql than 15.
|
||||||
|
```
|
||||||
|
This puts the binaries and sql into the right folder location. Next you need to create the extension in postgresql:
|
||||||
|
|
||||||
|
```
|
||||||
|
psql
|
||||||
|
> create extension pg_str; # installs functions in a schema named 'str'
|
||||||
|
> select str.markdown('# Hello '
|
||||||
|
|| str.snake('pg str')
|
||||||
|
|| '- ~~using programming language for str manipulations~~
|
||||||
|
- **do it all in postgresql** ');
|
||||||
|
```
|
||||||
|
|
||||||
|
## API
|
||||||
|
- [] after
|
||||||
|
- [] afterLast
|
||||||
|
- [x] ascii
|
||||||
|
- [] before
|
||||||
|
- [] beforeLast
|
||||||
|
- [] between
|
||||||
|
- [x] camel
|
||||||
|
- [x] contains
|
||||||
|
- [x] containsAll
|
||||||
|
- [] endsWith
|
||||||
|
- [] finish
|
||||||
|
- [] headline
|
||||||
|
- [] is
|
||||||
|
- [x] isAscii
|
||||||
|
- [] isUuid
|
||||||
|
- [x] kebab
|
||||||
|
- [] length
|
||||||
|
- [] limit
|
||||||
|
- [x] lower
|
||||||
|
- [x] markdown
|
||||||
|
- [] mask
|
||||||
|
- [] orderedUuid
|
||||||
|
- [] padBoth
|
||||||
|
- [] padLeft
|
||||||
|
- [] padRight
|
||||||
|
- [x] plural
|
||||||
|
- [] pluralStudly
|
||||||
|
- [] random
|
||||||
|
- [] remove
|
||||||
|
- [x] replace
|
||||||
|
- [] replaceArray
|
||||||
|
- [] replaceFirst
|
||||||
|
- [] replaceLast
|
||||||
|
- [] reverse
|
||||||
|
- [x] singular
|
||||||
|
- [x] slug
|
||||||
|
- [x] snake
|
||||||
|
- [] start
|
||||||
|
- [] startsWith
|
||||||
|
- [x] studly
|
||||||
|
- [] substr
|
||||||
|
- [] substrCount
|
||||||
|
- [] substrReplace
|
||||||
|
- [x] title
|
||||||
|
- [] ucfirst
|
||||||
|
- [x] upper
|
||||||
|
- [] uuid
|
||||||
|
- [] wordCount
|
||||||
|
- [] words
|
|
@ -0,0 +1,171 @@
|
||||||
|
use pgx::prelude::*;
|
||||||
|
|
||||||
|
use any_ascii::any_ascii;
|
||||||
|
use inflector::cases::{
|
||||||
|
camelcase, kebabcase, pascalcase, screamingsnakecase, snakecase, titlecase,
|
||||||
|
};
|
||||||
|
use inflector::string::{pluralize, singularize};
|
||||||
|
use pulldown_cmark::{html, Options, Parser};
|
||||||
|
use rand::distributions::{Alphanumeric, DistString};
|
||||||
|
use str_slug::StrSlug;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
|
||||||
|
pgx::pg_module_magic!();
|
||||||
|
|
||||||
|
// #[pg_extern]
|
||||||
|
// fn str_random(length: u32) -> String {
|
||||||
|
// Alphanumeric.sample_string(&mut rand::thread_rng(), length.try_into().unwrap())
|
||||||
|
// }
|
||||||
|
|
||||||
|
#[pg_extern]
|
||||||
|
fn after<'a>(input: &'a str, search: &str) -> &'a str {
|
||||||
|
let matches: Vec<_> = input.match_indices(search).collect();
|
||||||
|
match matches.first() {
|
||||||
|
None => input,
|
||||||
|
Some(x) => &input[x.1.len()..]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[pg_extern]
|
||||||
|
fn uuid() -> String {
|
||||||
|
Uuid::new_v4().to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pg_extern]
|
||||||
|
fn ascii(input: &str) -> String {
|
||||||
|
any_ascii(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pg_extern]
|
||||||
|
fn is_ascii(input: &str) -> bool {
|
||||||
|
input.is_ascii()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pg_extern]
|
||||||
|
fn contains(input: &str, search: &str) -> bool {
|
||||||
|
input.contains(search)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pg_extern]
|
||||||
|
fn contains_all(input: &str, search: Vec<&str>) -> bool {
|
||||||
|
search.iter().all(|s| input.contains(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pg_extern]
|
||||||
|
fn lower(input: &str) -> String {
|
||||||
|
input.to_lowercase()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pg_extern]
|
||||||
|
fn upper(input: &str) -> String {
|
||||||
|
input.to_uppercase()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pg_extern]
|
||||||
|
fn slug(input: &str, sep: char) -> String {
|
||||||
|
let mut slug = StrSlug::new();
|
||||||
|
slug.separator = sep;
|
||||||
|
slug.slug(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pg_extern]
|
||||||
|
fn singular(input: &str) -> String {
|
||||||
|
singularize::to_singular(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pg_extern]
|
||||||
|
fn plural(input: &str) -> String {
|
||||||
|
pluralize::to_plural(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pg_extern]
|
||||||
|
fn title(input: &str) -> String {
|
||||||
|
titlecase::to_title_case(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pg_extern]
|
||||||
|
fn camel(input: &str) -> String {
|
||||||
|
camelcase::to_camel_case(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pg_extern]
|
||||||
|
fn kebab(input: &str) -> String {
|
||||||
|
kebabcase::to_kebab_case(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pg_extern]
|
||||||
|
fn snake(input: &str) -> String {
|
||||||
|
snakecase::to_snake_case(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pg_extern]
|
||||||
|
fn studly(input: &str) -> String {
|
||||||
|
pascalcase::to_pascal_case(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pg_extern]
|
||||||
|
fn scream(input: &str) -> String {
|
||||||
|
screamingsnakecase::to_screaming_snake_case(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pg_extern]
|
||||||
|
fn markdown(input: &str) -> String {
|
||||||
|
// Set up options and parser. Strikethroughs are not part of the CommonMark standard
|
||||||
|
// and we therefore must enable it explicitly.
|
||||||
|
let mut options = Options::empty();
|
||||||
|
options.insert(Options::ENABLE_STRIKETHROUGH);
|
||||||
|
let parser = Parser::new_ext(input, options);
|
||||||
|
|
||||||
|
// Write to String buffer.
|
||||||
|
let mut html_output: String = String::with_capacity(input.len() * 3 / 2);
|
||||||
|
html::push_html(&mut html_output, parser);
|
||||||
|
html_output
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pg_extern]
|
||||||
|
fn substr(input: &str, start: i32, end: i32) -> &str {
|
||||||
|
&input[start as usize..end as usize]
|
||||||
|
}
|
||||||
|
#[pg_extern]
|
||||||
|
fn replace(input: &'static str, old: &'static str, new: &'static str) -> String {
|
||||||
|
input.replace(old, new)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pg_extern]
|
||||||
|
fn append(mut input: String, extra: &str) -> String {
|
||||||
|
input.push_str(extra);
|
||||||
|
input
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pg_extern]
|
||||||
|
fn split(input: &'static str, pattern: &str) -> Vec<&'static str> {
|
||||||
|
input.split_terminator(pattern).into_iter().collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pg_extern]
|
||||||
|
fn split_set<'a>(input: &'a str, pattern: &'a str) -> SetOfIterator<'a, &'a str> {
|
||||||
|
SetOfIterator::new(input.split_terminator(pattern).into_iter())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(test, feature = "pg_test"))]
|
||||||
|
#[pg_schema]
|
||||||
|
mod tests {
|
||||||
|
use pgx::prelude::*;
|
||||||
|
|
||||||
|
// #[pg_test]
|
||||||
|
// fn test_hello_pg_str() {
|
||||||
|
// assert_eq!("Hello, pg_str", crate::hello_pg_str());
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub mod pg_test {
|
||||||
|
pub fn setup(_options: Vec<&str>) {
|
||||||
|
// perform one-off initialization when the pg_test framework starts
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn postgresql_conf_options() -> Vec<&'static str> {
|
||||||
|
// return any postgresql.conf settings that are required for your tests
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue