Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
42c0e48081
|
+71
-8
@@ -1,13 +1,13 @@
|
|||||||
package stringsx
|
package stringsx
|
||||||
|
|
||||||
import (
|
import (
|
||||||
`bytes`
|
"bytes"
|
||||||
`errors`
|
"errors"
|
||||||
`fmt`
|
"fmt"
|
||||||
`io`
|
"io"
|
||||||
`slices`
|
"slices"
|
||||||
`strings`
|
"strings"
|
||||||
`unicode`
|
"unicode"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -17,6 +17,7 @@ It is more strict than [HasBoundary] (which only requires
|
|||||||
that s has sym at the beginning OR the end.)
|
that s has sym at the beginning OR the end.)
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
HasBookend("|foo|", "|") → true
|
HasBookend("|foo|", "|") → true
|
||||||
HasBookend("|foo", "|") → false
|
HasBookend("|foo", "|") → false
|
||||||
HasBookend("foo|", "|") → false
|
HasBookend("foo|", "|") → false
|
||||||
@@ -38,6 +39,7 @@ func HasBookend(s, sym string) (bounded bool) {
|
|||||||
HasBoundary returns true if string s starts OR ends with symbol sym.
|
HasBoundary returns true if string s starts OR ends with symbol sym.
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
HasBoundary("|foo|", "|") → true
|
HasBoundary("|foo|", "|") → true
|
||||||
HasBoundary("|foo", "|") → true
|
HasBoundary("|foo", "|") → true
|
||||||
HasBoundary("foo|", "|") → true
|
HasBoundary("foo|", "|") → true
|
||||||
@@ -453,6 +455,37 @@ func Redact(s, maskStr string, leading, trailing uint, newlines bool) (redacted
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
RemoveWhitespace removes all leading, trailing, and INNER whitespace
|
||||||
|
from unicode string `s`.
|
||||||
|
|
||||||
|
This is done by allocating a strings.Builder with len(s).
|
||||||
|
This may consume more memory than needed if s is mostly whitespace;
|
||||||
|
in that case, it is better to use [StripWhitespace].
|
||||||
|
*/
|
||||||
|
func RemoveWhitespace(s string) (removed string) {
|
||||||
|
|
||||||
|
// https://stackoverflow.com/questions/32081808/strip-all-whitespace-from-a-string/32081891#32081891
|
||||||
|
|
||||||
|
var c rune
|
||||||
|
var sb strings.Builder
|
||||||
|
|
||||||
|
if s == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.Grow(len(s))
|
||||||
|
for _, c = range s {
|
||||||
|
if !unicode.IsSpace(c) {
|
||||||
|
sb.WriteRune(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
removed = sb.String()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Reverse reverses string s. (It's absolutely insane that this isn't in stdlib.)
|
// Reverse reverses string s. (It's absolutely insane that this isn't in stdlib.)
|
||||||
func Reverse(s string) (revS string) {
|
func Reverse(s string) (revS string) {
|
||||||
|
|
||||||
@@ -465,6 +498,35 @@ func Reverse(s string) (revS string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
StripWhitespace removes all leading, trailing, and INNER whitespace
|
||||||
|
from unicode string `s`.
|
||||||
|
|
||||||
|
This is done by mapping each rune in `s`.
|
||||||
|
This may use far more allocations than necessary if `s` is mostly NON-whitespace;
|
||||||
|
in that case, it is better to use [RemoveWhitespace].
|
||||||
|
*/
|
||||||
|
func StripWhitespace(s string) (stripped string) {
|
||||||
|
|
||||||
|
// https://stackoverflow.com/questions/32081808/strip-all-whitespace-from-a-string/32081891#32081891
|
||||||
|
|
||||||
|
if s == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
stripped = strings.Map(
|
||||||
|
func(c rune) rune {
|
||||||
|
if unicode.IsSpace(c) {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
},
|
||||||
|
s,
|
||||||
|
)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
TrimLines is like [strings.TrimSpace] but operates on *each line* of s.
|
TrimLines is like [strings.TrimSpace] but operates on *each line* of s.
|
||||||
It is *NIX-newline (`\n`) vs. Windows-newline (`\r\n`) agnostic.
|
It is *NIX-newline (`\n`) vs. Windows-newline (`\r\n`) agnostic.
|
||||||
@@ -500,7 +562,8 @@ func TrimLines(s string, left, right bool) (trimmed string) {
|
|||||||
} else if right {
|
} else if right {
|
||||||
sl = TrimSpaceRight(sl)
|
sl = TrimSpaceRight(sl)
|
||||||
}
|
}
|
||||||
sb.WriteString(sl + nl)
|
sb.WriteString(sl)
|
||||||
|
sb.WriteString(nl)
|
||||||
}
|
}
|
||||||
|
|
||||||
trimmed = sb.String()
|
trimmed = sb.String()
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
"io"
|
||||||
|
"io/fs"
|
||||||
|
// "fmt"
|
||||||
|
"log"
|
||||||
|
// "os"
|
||||||
|
// "path/filepath"
|
||||||
|
"text/template"
|
||||||
|
// "github.com/davecgh/go-spew/spew"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
//go:embed "tplfiles"
|
||||||
|
tplfiles embed.FS
|
||||||
|
|
||||||
|
tpl *template.Template = template.New("")
|
||||||
|
|
||||||
|
tw *TplWalker = new(TplWalker)
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
TplWalker struct {
|
||||||
|
tpl *template.Template
|
||||||
|
fs fs.FS
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (t *TplWalker) walkFunc(fpath string, d fs.DirEntry, inErr error) (err error) {
|
||||||
|
|
||||||
|
var b []byte
|
||||||
|
var f fs.File
|
||||||
|
|
||||||
|
if inErr != nil {
|
||||||
|
err = inErr
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.IsDir() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if f, err = t.fs.Open(fpath); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
var ok bool
|
||||||
|
var c io.Closer
|
||||||
|
if f != nil {
|
||||||
|
if c, ok = f.(io.Closer); ok {
|
||||||
|
if err = c.Close(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if b, err = io.ReadAll(f); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = t.tpl.New(fpath).Parse(string(b)); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TplWalker) Walk() (err error) {
|
||||||
|
|
||||||
|
if err = fs.WalkDir(t.fs, ".", t.walkFunc); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
tw.tpl = tpl
|
||||||
|
tw.fs = tplfiles
|
||||||
|
|
||||||
|
if err = tw.Walk(); err != nil {
|
||||||
|
log.Panicln(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var idx int
|
||||||
|
var sub *template.Template
|
||||||
|
|
||||||
|
log.Printf("Root template name is %q", tpl.Name())
|
||||||
|
for idx, sub = range tpl.Templates() {
|
||||||
|
log.Printf("Sub template %d: %q", idx, sub.Name())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -636,7 +636,7 @@ pre.rouge .gs {
|
|||||||
<div class="details">
|
<div class="details">
|
||||||
<span id="author" class="author">Brent Saner</span><br>
|
<span id="author" class="author">Brent Saner</span><br>
|
||||||
<span id="email" class="email"><a href="mailto:bts@square-r00t.net">bts@square-r00t.net</a></span><br>
|
<span id="email" class="email"><a href="mailto:bts@square-r00t.net">bts@square-r00t.net</a></span><br>
|
||||||
<span id="revdate">Last rendered 2026-06-22 18:51:24 -0400</span>
|
<span id="revdate">Last rendered 2026-07-06 16:21:08 -0400</span>
|
||||||
</div>
|
</div>
|
||||||
<div id="toc" class="toc2">
|
<div id="toc" class="toc2">
|
||||||
<div id="toctitle">Table of Contents</div>
|
<div id="toctitle">Table of Contents</div>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
<span id="author" class="author">Brent Saner</span>
|
<span id="author" class="author">Brent Saner</span>
|
||||||
<span id="email" class="email"><bts@square-r00t.net></span>
|
<span id="email" class="email"><bts@square-r00t.net></span>
|
||||||
<span id="revdate">Last rendered 2026-06-22 18:51:29 -0400</span>
|
<span id="revdate">Last rendered 2026-07-06 16:21:13 -0400</span>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user