202 lines
4.8 KiB
Go
202 lines
4.8 KiB
Go
/*
|
|
* Amsterdam Web Communities System
|
|
* Copyright (c) 2025 Erbosoft Metaverse Design Solutions, All Rights Reserved
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
*
|
|
* SPDX-License-Identifier: MPL-2.0
|
|
*/
|
|
|
|
// Package util contains utility definitions.
|
|
package util
|
|
|
|
import (
|
|
"net"
|
|
"regexp"
|
|
"strings"
|
|
"time"
|
|
"unicode"
|
|
"unicode/utf8"
|
|
)
|
|
|
|
var numeric *regexp.Regexp = regexp.MustCompile(`^[0-9]+$`)
|
|
|
|
/* CapitalizeString changes the first character of the string to a capital.
|
|
* Parameters:
|
|
* s - The string to be capitalized.
|
|
* Returns:
|
|
* The capitalized string.
|
|
*/
|
|
func CapitalizeString(s string) string {
|
|
runes := []rune(s)
|
|
if len(runes) > 0 {
|
|
runes[0] = unicode.ToUpper(runes[0])
|
|
return string(runes)
|
|
}
|
|
return ""
|
|
}
|
|
|
|
/* SqlEscape escapes a string in SQL terms.
|
|
* Parameters:
|
|
* s - The string to be escaped.
|
|
* wildcards - If true, also escape the wildcard characters % and _.
|
|
* Returns:
|
|
* The escaped string.
|
|
*/
|
|
func SqlEscape(s string, wildcards bool) string {
|
|
var sb strings.Builder
|
|
for i := 0; i < len(s); i++ {
|
|
c := s[i]
|
|
switch c {
|
|
case '\\', 0, '\n', '\r', '\'', '"':
|
|
sb.WriteByte('\\')
|
|
sb.WriteByte(c)
|
|
case '\032':
|
|
sb.WriteByte('\\')
|
|
sb.WriteByte('Z')
|
|
case '%', '_':
|
|
if wildcards {
|
|
sb.WriteByte('\\')
|
|
}
|
|
sb.WriteByte(c)
|
|
default:
|
|
sb.WriteByte(c)
|
|
}
|
|
}
|
|
return sb.String()
|
|
}
|
|
|
|
// SameDate returns true if the two time values are the same date.
|
|
func SameDate(t1, t2 *time.Time) bool {
|
|
if t1 == nil {
|
|
return t2 == nil
|
|
} else if t2 == nil {
|
|
return false
|
|
}
|
|
y1, m1, d1 := t1.Date()
|
|
y2, m2, d2 := t2.Date()
|
|
return (y1 == y2) && (m1 == m2) && (d1 == d2)
|
|
}
|
|
|
|
/* IsNumeric returns true if the string is numeric (all digits).
|
|
* Parameters:
|
|
* s - String to be tested.
|
|
* Returns:
|
|
* true if string is numeric, false if not.
|
|
*/
|
|
func IsNumeric(s string) bool {
|
|
return numeric.MatchString(s)
|
|
}
|
|
|
|
/* RunesToBytes returns the number of bytes in a string counting the number of runes from the beginning.
|
|
* Parameters:
|
|
* s - The string to work with.
|
|
* runeCount - The number of runes to count from the start of the string.
|
|
* Returns:
|
|
* The corresponding number of bytes.
|
|
*/
|
|
func RunesToBytes(s string, runeCount int) int {
|
|
bp := 0
|
|
for runeCount > 0 {
|
|
if bp >= len(s) {
|
|
return len(s)
|
|
}
|
|
_, c := utf8.DecodeRuneInString(s[bp:])
|
|
bp += c
|
|
runeCount--
|
|
}
|
|
return bp
|
|
}
|
|
|
|
// IsRuneWord returns true if the given rune is part of a word.
|
|
func IsRuneWord(ch rune) bool {
|
|
return unicode.IsLetter(ch) || ch == '-' || ch == '\''
|
|
}
|
|
|
|
/* WordRunLength calculates the number of runes at the start of the string that are either word or non-word characters.
|
|
* Parameters:
|
|
* s - The string under test.
|
|
* Returns:
|
|
* The run length in runes.
|
|
* true if the run is a length of word characters, false if it's a run of non-word characters.
|
|
*/
|
|
func WordRunLength(s string) (int, bool) {
|
|
c1, initLen := utf8.DecodeRuneInString(s)
|
|
wordChar := IsRuneWord(c1)
|
|
rlen := 1
|
|
for _, mch := range s[initLen:] {
|
|
if IsRuneWord(mch) != wordChar {
|
|
break
|
|
}
|
|
rlen++
|
|
}
|
|
return rlen, wordChar
|
|
}
|
|
|
|
/* WordRunLengthAfterPrefix calculates the number of runes after a certain number in the string
|
|
* that are either word or non-word characters.
|
|
* Parameters:
|
|
* s - The string under test.
|
|
* nrunes - The number of runes to skip at the start of the string.
|
|
* Returns:
|
|
* The run length in runes.
|
|
* true if the run is a length of word characters, false if it's a run of non-word characters.
|
|
*/
|
|
func WordRunLengthAfterPrefix(s string, nrunes int) (int, bool) {
|
|
ofs := 0
|
|
for _, ch := range s {
|
|
if nrunes == 0 {
|
|
break
|
|
}
|
|
ofs += utf8.RuneLen(ch)
|
|
nrunes--
|
|
}
|
|
return WordRunLength(s[ofs:])
|
|
}
|
|
|
|
/* Map applies a transformation function on all elements of an array of one type, turning it into an
|
|
* array of another type.
|
|
* Parameters:
|
|
* in - The input array to be transformed.
|
|
* fn - The function to be executed on each element.
|
|
* Returns:
|
|
* The array of new elements.
|
|
*/
|
|
func Map[A, B any](in []A, fn func(A) B) []B {
|
|
rc := make([]B, len(in))
|
|
for i, v := range in {
|
|
rc[i] = fn(v)
|
|
}
|
|
return rc
|
|
}
|
|
|
|
// MyIPAddress returns the local IP address of this machine.
|
|
func MyIPAddress() (net.IP, error) {
|
|
conn, err := net.Dial("udp", "8.8.8.8:80")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer conn.Close()
|
|
localAddr := conn.LocalAddr().(*net.UDPAddr)
|
|
return localAddr.IP, nil
|
|
}
|
|
|
|
// IIF is an "immediate-if" function returning its second argument if the first one is true, the third one if not.
|
|
func IIF[A any](expr bool, v1, v2 A) A {
|
|
if expr {
|
|
return v1
|
|
} else {
|
|
return v2
|
|
}
|
|
}
|
|
|
|
// SRef dereferences the string pointer if it's not nil, or returns an empty string if it is nil.
|
|
func SRef(s *string) string {
|
|
if s == nil {
|
|
return ""
|
|
}
|
|
return *s
|
|
}
|