Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1eea0c2672
|
||
|
|
67c7faf449
|
||
|
|
82c69ec542
|
||
|
|
07e0e587fa
|
||
|
|
1bd6e1256c
|
@@ -19,12 +19,29 @@ for f in $(find . -type f -iname "README.adoc"); do
|
|||||||
asciidoctor -a ROOTDIR="${orig}/" -o "${newf}" "${f}"
|
asciidoctor -a ROOTDIR="${orig}/" -o "${newf}" "${f}"
|
||||||
echo "Generated ${newf} from ${f}"
|
echo "Generated ${newf} from ${f}"
|
||||||
git add "${newf}"
|
git add "${newf}"
|
||||||
|
if command -v asciidoctor-pdf &> /dev/null;
|
||||||
|
then
|
||||||
|
newf="${pfx}.pdf"
|
||||||
|
|
||||||
|
asciidoctor-pdf -a ROOTDIR="${orig}/" -o "${newf}" "${f}"
|
||||||
|
fi
|
||||||
if command -v pandoc &> /dev/null;
|
if command -v pandoc &> /dev/null;
|
||||||
then
|
then
|
||||||
newf="${pfx}.md"
|
newf="${pfx}.md"
|
||||||
asciidoctor -a ROOTDIR="${orig}/" -b docbook -o - "${f}" | pandoc -f docbook -t markdown_strict -o "${newf}"
|
|
||||||
echo "Generated ${newf} from ${f}"
|
set +e
|
||||||
git add "${newf}"
|
#asciidoctor -a ROOTDIR="${orig}/" -b docbook -o - "${f}" | pandoc -f docbook -t markdown_strict -o "${newf}"
|
||||||
|
#asciidoctor -a ROOTDIR="${orig}/" -b html -o - "${f}" | pandoc -f html -t markdown_strict -o "${newf}"
|
||||||
|
asciidoctor -a ROOTDIR="${orig}/" -b html -o - "${f}" | pandoc -f html -t gfm -o "${newf}"
|
||||||
|
if [ $? -eq 0 ];
|
||||||
|
then
|
||||||
|
echo "Generated ${newf} from ${f}"
|
||||||
|
git add "${newf}"
|
||||||
|
else
|
||||||
|
echo "Failed to generate ${newf} from ${f}"
|
||||||
|
git rm "${newf}" 2>/dev/null
|
||||||
|
fi
|
||||||
|
set -e
|
||||||
fi
|
fi
|
||||||
cd ${orig}
|
cd ${orig}
|
||||||
done
|
done
|
||||||
|
|||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -19,12 +19,13 @@
|
|||||||
.idea/
|
.idea/
|
||||||
|
|
||||||
# https://github.com/github/gitignore/blob/master/Go.gitignore
|
# https://github.com/github/gitignore/blob/master/Go.gitignore
|
||||||
# Binaries for programs and plugins
|
# Binaries for programs and plugins and other data
|
||||||
*.exe
|
*.exe
|
||||||
*.exe~
|
*.exe~
|
||||||
*.dll
|
*.dll
|
||||||
*.so
|
*.so
|
||||||
*.dylib
|
*.dylib
|
||||||
|
*.pdf
|
||||||
|
|
||||||
# Test binary, built with `go test -c`
|
# Test binary, built with `go test -c`
|
||||||
*.test
|
*.test
|
||||||
|
|||||||
1
TODO
Normal file
1
TODO
Normal file
@@ -0,0 +1 @@
|
|||||||
|
- validx: validator functions for https://pkg.go.dev/github.com/go-playground/validator/v10
|
||||||
19
chkplat.sh
Executable file
19
chkplat.sh
Executable file
@@ -0,0 +1,19 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# go tool dist list for all valid GOOS/GOARCH targets.
|
||||||
|
|
||||||
|
for tgt in $(go tool dist list);
|
||||||
|
do
|
||||||
|
o="$(echo ${tgt} | cut -f1 -d '/')"
|
||||||
|
a="$(echo ${tgt} | cut -f2 -d '/')"
|
||||||
|
out="$(env GOOS=${o} GOARCH=${a} go build ./... 2>&1)"
|
||||||
|
ret=${?}
|
||||||
|
if [ $ret -ne 0 ];
|
||||||
|
then
|
||||||
|
echo "OS: ${o}"
|
||||||
|
echo "ARCH: ${a}"
|
||||||
|
echo "${out}"
|
||||||
|
echo
|
||||||
|
echo
|
||||||
|
fi
|
||||||
|
done
|
||||||
22
go.mod
22
go.mod
@@ -4,24 +4,32 @@ go 1.25
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Masterminds/sprig/v3 v3.3.0
|
github.com/Masterminds/sprig/v3 v3.3.0
|
||||||
github.com/coreos/go-systemd/v22 v22.6.0
|
github.com/coreos/go-systemd/v22 v22.7.0
|
||||||
github.com/davecgh/go-spew v1.1.1
|
github.com/davecgh/go-spew v1.1.1
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
|
github.com/shirou/gopsutil/v4 v4.25.12
|
||||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
|
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
|
||||||
golang.org/x/sys v0.39.0
|
golang.org/x/sys v0.40.0
|
||||||
r00t2.io/sysutils v1.15.1
|
r00t2.io/sysutils v1.16.2
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
dario.cat/mergo v1.0.1 // indirect
|
dario.cat/mergo v1.0.2 // indirect
|
||||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||||
github.com/Masterminds/semver/v3 v3.3.0 // indirect
|
github.com/Masterminds/semver/v3 v3.4.0 // indirect
|
||||||
github.com/djherbis/times v1.6.0 // indirect
|
github.com/djherbis/times v1.6.0 // indirect
|
||||||
|
github.com/ebitengine/purego v0.9.1 // indirect
|
||||||
|
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||||
github.com/huandu/xstrings v1.5.0 // indirect
|
github.com/huandu/xstrings v1.5.0 // indirect
|
||||||
|
github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 // indirect
|
||||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||||
|
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
||||||
github.com/shopspring/decimal v1.4.0 // indirect
|
github.com/shopspring/decimal v1.4.0 // indirect
|
||||||
github.com/spf13/cast v1.7.0 // indirect
|
github.com/spf13/cast v1.10.0 // indirect
|
||||||
golang.org/x/crypto v0.26.0 // indirect
|
github.com/tklauser/go-sysconf v0.3.16 // indirect
|
||||||
|
github.com/tklauser/numcpus v0.11.0 // indirect
|
||||||
|
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||||
|
golang.org/x/crypto v0.47.0 // indirect
|
||||||
golang.org/x/sync v0.19.0 // indirect
|
golang.org/x/sync v0.19.0 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
58
go.sum
58
go.sum
@@ -1,21 +1,26 @@
|
|||||||
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
|
dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
|
||||||
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
|
||||||
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
|
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
|
||||||
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
||||||
github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0=
|
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
|
||||||
github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
||||||
github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs=
|
github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs=
|
||||||
github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0=
|
github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0=
|
||||||
github.com/coreos/go-systemd/v22 v22.6.0 h1:aGVa/v8B7hpb0TKl0MWoAavPDmHvobFe5R5zn0bCJWo=
|
github.com/coreos/go-systemd/v22 v22.7.0 h1:LAEzFkke61DFROc7zNLX/WA2i5J8gYqe0rSj9KI28KA=
|
||||||
github.com/coreos/go-systemd/v22 v22.6.0/go.mod h1:iG+pp635Fo7ZmV/j14KUcmEyWF+0X7Lua8rrTWzYgWU=
|
github.com/coreos/go-systemd/v22 v22.7.0/go.mod h1:xNUYtjHu2EDXbsxz1i41wouACIwT7Ybq9o0BQhMwD0w=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/djherbis/times v1.6.0 h1:w2ctJ92J8fBvWPxugmXIv7Nz7Q3iDMKNx9v5ocVH20c=
|
github.com/djherbis/times v1.6.0 h1:w2ctJ92J8fBvWPxugmXIv7Nz7Q3iDMKNx9v5ocVH20c=
|
||||||
github.com/djherbis/times v1.6.0/go.mod h1:gOHeRAz2h+VJNZ5Gmc/o7iD9k4wW7NMVqieYCY99oc0=
|
github.com/djherbis/times v1.6.0/go.mod h1:gOHeRAz2h+VJNZ5Gmc/o7iD9k4wW7NMVqieYCY99oc0=
|
||||||
|
github.com/ebitengine/purego v0.9.1 h1:a/k2f2HQU3Pi399RPW1MOaZyhKJL9w/xFpKAg4q1s0A=
|
||||||
|
github.com/ebitengine/purego v0.9.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
||||||
|
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
||||||
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
|
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
|
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
|
||||||
@@ -24,33 +29,44 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
|||||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
|
github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 h1:PwQumkgq4/acIiZhtifTV5OUqqiP82UAl0h87xj/l9k=
|
||||||
|
github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg=
|
||||||
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
|
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
|
||||||
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
||||||
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
||||||
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
|
||||||
|
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||||
|
github.com/shirou/gopsutil/v4 v4.25.12 h1:e7PvW/0RmJ8p8vPGJH4jvNkOyLmbkXgXW4m6ZPic6CY=
|
||||||
|
github.com/shirou/gopsutil/v4 v4.25.12/go.mod h1:EivAfP5x2EhLp2ovdpKSozecVXn1TmuG7SMzs/Wh4PU=
|
||||||
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
|
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
|
||||||
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
|
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
|
||||||
github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
|
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
|
||||||
github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
|
||||||
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||||
|
github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA=
|
||||||
|
github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI=
|
||||||
|
github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw=
|
||||||
|
github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ=
|
||||||
|
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||||
|
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
|
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
|
||||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
||||||
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
|
golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=
|
||||||
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
|
golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=
|
||||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||||
|
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
|
||||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
r00t2.io/sysutils v1.15.1 h1:0EVZZAxTFqQN6jjfjqUKkXye0LMshUA5MO7l3Wd6wH8=
|
r00t2.io/sysutils v1.16.2/go.mod h1:iXK+ALOwIdRKjAJIE5USlkZ669SVDHBNNuYhunsznH8=
|
||||||
r00t2.io/sysutils v1.15.1/go.mod h1:T0iOnaZaSG5NE1hbXTqojRZc0ia/u8TB73lV7zhMz58=
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
//go:build !(windows || plan9 || wasip1 || js || ios)
|
//go:build !(windows || plan9 || wasip1 || js || ios)
|
||||||
// +build !windows,!plan9,!wasip1,!js,!ios
|
|
||||||
|
|
||||||
// I mean maybe it works for plan9 and ios, I don't know.
|
// I mean maybe it works for plan9 and ios, I don't know.
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//go:build !(windows || plan9 || wasip1 || js || ios || linux)
|
||||||
|
|
||||||
package logging
|
package logging
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
//go:build !(windows || plan9 || wasip1 || js || ios || linux)
|
//go:build !(windows || plan9 || wasip1 || js || ios || linux)
|
||||||
// +build !windows,!plan9,!wasip1,!js,!ios,!linux
|
|
||||||
|
|
||||||
// Linux is excluded because it has its own.
|
// Linux is excluded because it has its own.
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
14
tplx/sprigx/TODO
Normal file
14
tplx/sprigx/TODO
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
- osReadFileBytes
|
||||||
|
- osReadFileStr
|
||||||
|
- osReadDir
|
||||||
|
|
||||||
|
- `dns*` funcs (net)
|
||||||
|
- `url*` funcs (net/url)
|
||||||
|
- `uuid*` funcs (github.com/google/uuid and r00t2.io/goutils/uuidx)
|
||||||
|
|
||||||
|
- `http*` funcs:
|
||||||
|
-- `httpReq`: returns a net/http.Request
|
||||||
|
-- `http<Method>`: performs <Method> (? seems redundant if exposing httpReq)
|
||||||
|
-- also have `resty*` funcs?
|
||||||
|
|
||||||
|
- i should probably explicitly provide a "safe" set vs. "full" set. can just mod the map func getters to accept a "safeOnly" bool param.
|
||||||
@@ -1,22 +1,94 @@
|
|||||||
package sprigx
|
package sprigx
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"path"
|
`net`
|
||||||
"path/filepath"
|
`net/netip`
|
||||||
|
`os`
|
||||||
|
`os/user`
|
||||||
|
`path`
|
||||||
|
`path/filepath`
|
||||||
|
`runtime`
|
||||||
|
`time`
|
||||||
|
|
||||||
|
`github.com/davecgh/go-spew/spew`
|
||||||
|
`github.com/shirou/gopsutil/v4/cpu`
|
||||||
|
`github.com/shirou/gopsutil/v4/disk`
|
||||||
|
`github.com/shirou/gopsutil/v4/host`
|
||||||
|
`github.com/shirou/gopsutil/v4/load`
|
||||||
|
`github.com/shirou/gopsutil/v4/mem`
|
||||||
|
psnet `github.com/shirou/gopsutil/v4/net`
|
||||||
|
`github.com/shirou/gopsutil/v4/process`
|
||||||
|
`github.com/shirou/gopsutil/v4/sensors`
|
||||||
|
`go4.org/netipx`
|
||||||
|
`r00t2.io/goutils/timex`
|
||||||
|
`r00t2.io/sysutils`
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// genericMap holds functions usable/intended for use in either an [html/template.FuncMap] or [text/template.FuncMap].
|
// genericMap holds functions usable/intended for use in either an [html/template.FuncMap] or [text/template.FuncMap].
|
||||||
genericMap map[string]any = map[string]any{
|
genericMap map[string]any = map[string]any{
|
||||||
// Debugging
|
// Debugging
|
||||||
"dump": dump,
|
"dump": spew.Sdump,
|
||||||
// Strings
|
/*
|
||||||
"extIndent": extIndent, // PR in: https://github.com/Masterminds/sprig/pull/468
|
"Meta"/Template-Helpers
|
||||||
// OS/System
|
*/
|
||||||
"sysArch": sysArch,
|
"metaIsNil": metaIsNil,
|
||||||
"sysNumCpu": sysNumCpu,
|
/*
|
||||||
"sysOsName": sysOsNm,
|
Networking (net)
|
||||||
"sysRuntime": sysRuntime,
|
*/
|
||||||
|
"netCidrMask": net.CIDRMask,
|
||||||
|
"netExtractAddr": netExtractAddr,
|
||||||
|
"netExtractHost": netExtractHost,
|
||||||
|
"netExtractIpnet": netExtractIpnet,
|
||||||
|
"netExtractPort": netExtractPort,
|
||||||
|
"netIfaces": net.Interfaces,
|
||||||
|
"netIp4Mask": netIp4Mask,
|
||||||
|
"netJoinHostPort": net.JoinHostPort,
|
||||||
|
"netParseIP": net.ParseIP,
|
||||||
|
/*
|
||||||
|
Networking (net/netip)
|
||||||
|
*/
|
||||||
|
"netipAddrPort": netip.AddrPortFrom,
|
||||||
|
"netipParseAddr": netip.ParseAddr,
|
||||||
|
"netipParseAddrPort": netip.ParseAddrPort,
|
||||||
|
"netipParsePrefix": netip.ParsePrefix,
|
||||||
|
"netipPrefix": netip.PrefixFrom,
|
||||||
|
/*
|
||||||
|
Networking (go4.org/netipx)
|
||||||
|
*/
|
||||||
|
"netipxAddrIpNet": netipx.AddrIPNet,
|
||||||
|
"netipxCmpPfx": netipx.ComparePrefix,
|
||||||
|
"netipxFromStdAddr": netipxFromStdAddr,
|
||||||
|
"netipxFromIp": netipxFromIp,
|
||||||
|
"netipxFromIpNet": netipxFromIpNet,
|
||||||
|
"netipxParseRange": netipx.ParseIPRange,
|
||||||
|
"netipxPfxAddr": netipx.ParsePrefixOrAddr,
|
||||||
|
"netipxPfxIpNet": netipx.PrefixIPNet,
|
||||||
|
"netipxPfxLast": netipx.PrefixLastIP,
|
||||||
|
"netipxPfxRange": netipx.RangeOfPrefix,
|
||||||
|
"netipxRange": netipx.IPRangeFrom,
|
||||||
|
/*
|
||||||
|
Numbers/Math
|
||||||
|
*/
|
||||||
|
"numFloat32Str": numFloat32Str,
|
||||||
|
"numFloat64": numFloat64,
|
||||||
|
"numFloat64Str": numFloat64Str,
|
||||||
|
"numFloatStr": numFloatStr,
|
||||||
|
/*
|
||||||
|
OS
|
||||||
|
*/
|
||||||
|
"osFQDN": osFQDN,
|
||||||
|
"osGroupById": osGroupById,
|
||||||
|
"osGroupByName": user.LookupGroup,
|
||||||
|
"osHost": osHost,
|
||||||
|
"osHostname": os.Hostname,
|
||||||
|
"osIdState": sysutils.GetIDState,
|
||||||
|
"osUser": user.Current,
|
||||||
|
"osUserById": osUserById,
|
||||||
|
"osUserByName": user.Lookup,
|
||||||
|
/*
|
||||||
|
Paths
|
||||||
|
*/
|
||||||
// Paths: Generic
|
// Paths: Generic
|
||||||
"pathJoin": path.Join,
|
"pathJoin": path.Join,
|
||||||
"pathPipeJoin": pathPipeJoin,
|
"pathPipeJoin": pathPipeJoin,
|
||||||
@@ -30,6 +102,84 @@ var (
|
|||||||
"osPathSliceJoin": osPathSliceJoin,
|
"osPathSliceJoin": osPathSliceJoin,
|
||||||
"osPathSlicePipeJoin": osPathSlicePipeJoin,
|
"osPathSlicePipeJoin": osPathSlicePipeJoin,
|
||||||
"osPathSubJoin": osPathSubJoin,
|
"osPathSubJoin": osPathSubJoin,
|
||||||
|
/*
|
||||||
|
PSUtil
|
||||||
|
(https://pkg.go.dev/github.com/shirou/gopsutil/v4)
|
||||||
|
*/
|
||||||
|
// .../cpu
|
||||||
|
"psCpuCnts": cpu.Counts,
|
||||||
|
"psCpuInfo": cpu.Info,
|
||||||
|
"psCpuPct": cpu.Percent,
|
||||||
|
"psCpuTimes": cpu.Times,
|
||||||
|
// .../disk
|
||||||
|
"psDiskIoCnts": disk.IOCounters,
|
||||||
|
"psDiskLabel": disk.Label,
|
||||||
|
"psDiskParts": disk.Partitions,
|
||||||
|
"psDiskSerial": disk.SerialNumber,
|
||||||
|
"psDiskUsage": disk.Usage,
|
||||||
|
// .../host
|
||||||
|
"psHostBoot": host.BootTime,
|
||||||
|
"psHostId": host.HostID,
|
||||||
|
"psHostInfo": host.Info,
|
||||||
|
"psHostKernArch": host.KernelArch,
|
||||||
|
"psHostKernVer": host.KernelVersion,
|
||||||
|
"psHostPlatInfo": psHostPlatInfo,
|
||||||
|
"psHostUptime": host.Uptime,
|
||||||
|
"psHostUsers": host.Users,
|
||||||
|
"psHostVirt": psHostVirt,
|
||||||
|
// .../load
|
||||||
|
"psLoadAvg": load.Avg,
|
||||||
|
"psLoadMisc": load.Misc,
|
||||||
|
// .../mem
|
||||||
|
"psMemSwap": mem.SwapMemory,
|
||||||
|
"psMemSwapDevs": mem.SwapDevices,
|
||||||
|
"psMemVMem": mem.VirtualMemory,
|
||||||
|
// .../net
|
||||||
|
"psNetConns": psnet.Connections,
|
||||||
|
"psNetConnsMax": psnet.ConnectionsMax,
|
||||||
|
"psNetConnsPid": psnet.ConnectionsPid,
|
||||||
|
"psNetConnsPidMax": psnet.ConnectionsPidMax,
|
||||||
|
"psNetCTStats": psnet.ConntrackStats,
|
||||||
|
"psNetCTStatList": psnet.NewConntrackStatList,
|
||||||
|
"psNetFilterCnts": psnet.FilterCounters,
|
||||||
|
"psNetIoCnts": psnet.IOCounters,
|
||||||
|
"psNetIoCntsFile": psnet.IOCountersByFile,
|
||||||
|
"psNetIfaces": psnet.Interfaces,
|
||||||
|
"psNetPids": psnet.Pids,
|
||||||
|
"psNetProtoCnt": psnet.ProtoCounters,
|
||||||
|
// .../process
|
||||||
|
"psProcs": process.Processes,
|
||||||
|
"psProcNew": process.NewProcess,
|
||||||
|
"psProcPids": process.Pids,
|
||||||
|
"psProcPidExists": process.PidExists,
|
||||||
|
// .../sensors
|
||||||
|
"psSensorTemps": sensors.SensorsTemperatures,
|
||||||
|
/*
|
||||||
|
Strings
|
||||||
|
*/
|
||||||
|
"extIndent": extIndent, // PR in: https://github.com/Masterminds/sprig/pull/468
|
||||||
|
/*
|
||||||
|
System/Platform
|
||||||
|
*/
|
||||||
|
"sysArch": sysArch,
|
||||||
|
"sysNumCpu": runtime.NumCPU,
|
||||||
|
"sysOsName": sysOsNm,
|
||||||
|
"sysRuntime": sysRuntime,
|
||||||
|
/*
|
||||||
|
Time/Dates/Timestamps
|
||||||
|
*/
|
||||||
|
"tmDate": time.Date,
|
||||||
|
"tmFmt": tmFmt,
|
||||||
|
"tmFloatMicro": timex.F64Microseconds,
|
||||||
|
"tmFloatMilli": timex.F64Milliseconds,
|
||||||
|
"tmFloatNano": timex.F64Nanoseconds,
|
||||||
|
"tmFloat": timex.F64Seconds,
|
||||||
|
"tmNow": time.Now,
|
||||||
|
"tmParseDur8n": time.ParseDuration,
|
||||||
|
"tmParseMonth": tmParseMonth,
|
||||||
|
"tmParseMonthInt": tmParseMonthInt,
|
||||||
|
"tmParseMonthStr": tmParseMonthStr,
|
||||||
|
"tmParseTime": time.Parse,
|
||||||
}
|
}
|
||||||
|
|
||||||
// htmlMap holds functions usable/intended for use in only an [html/template.FuncMap].
|
// htmlMap holds functions usable/intended for use in only an [html/template.FuncMap].
|
||||||
|
|||||||
9
tplx/sprigx/consts_darwin.go
Normal file
9
tplx/sprigx/consts_darwin.go
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
//go:build darwin
|
||||||
|
|
||||||
|
package sprigx
|
||||||
|
|
||||||
|
var (
|
||||||
|
osGenericMap map[string]any = map[string]any{}
|
||||||
|
osHtmlMap map[string]any = map[string]any{}
|
||||||
|
osTxtMap map[string]any = map[string]any{}
|
||||||
|
)
|
||||||
25
tplx/sprigx/consts_linux.go
Normal file
25
tplx/sprigx/consts_linux.go
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
//go:build linux
|
||||||
|
|
||||||
|
package sprigx
|
||||||
|
|
||||||
|
import (
|
||||||
|
`github.com/shirou/gopsutil/v4/mem`
|
||||||
|
psnet `github.com/shirou/gopsutil/v4/net`
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
osGenericMap map[string]any = map[string]any{
|
||||||
|
/*
|
||||||
|
PSUtil
|
||||||
|
(https://pkg.go.dev/github.com/shirou/gopsutil/v4)
|
||||||
|
*/
|
||||||
|
// .../mem
|
||||||
|
"psMemExVMem": mem.NewExLinux().VirtualMemory,
|
||||||
|
// .../net
|
||||||
|
"psNetRev": psnet.Reverse,
|
||||||
|
// .../sensors
|
||||||
|
"psSensorExTemp": psSensorExTemp,
|
||||||
|
}
|
||||||
|
osHtmlMap map[string]any = map[string]any{}
|
||||||
|
osTxtMap map[string]any = map[string]any{}
|
||||||
|
)
|
||||||
9
tplx/sprigx/consts_unknown.go
Normal file
9
tplx/sprigx/consts_unknown.go
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
//go:build !(linux || windows || darwin)
|
||||||
|
|
||||||
|
package sprigx
|
||||||
|
|
||||||
|
var (
|
||||||
|
osGenericMap map[string]any = map[string]any{}
|
||||||
|
osHtmlMap map[string]any = map[string]any{}
|
||||||
|
osTxtMap map[string]any = map[string]any{}
|
||||||
|
)
|
||||||
24
tplx/sprigx/consts_windows.go
Normal file
24
tplx/sprigx/consts_windows.go
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
//go:build windows
|
||||||
|
|
||||||
|
package sprigx
|
||||||
|
|
||||||
|
import (
|
||||||
|
`github.com/shirou/gopsutil/v4/mem`
|
||||||
|
`github.com/shirou/gopsutil/v4/winservices`
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
osGenericMap map[string]any = map[string]any{
|
||||||
|
/*
|
||||||
|
PSUtil
|
||||||
|
(https://pkg.go.dev/github.com/shirou/gopsutil/v4)
|
||||||
|
*/
|
||||||
|
// .../mem
|
||||||
|
"psMemExVMem": mem.NewExWindows().VirtualMemory,
|
||||||
|
// .../winservices
|
||||||
|
"psWinsvcList": winservices.ListServices,
|
||||||
|
"psWinsvcNew": winservices.NewService,
|
||||||
|
}
|
||||||
|
osHtmlMap map[string]any = map[string]any{}
|
||||||
|
osTxtMap map[string]any = map[string]any{}
|
||||||
|
)
|
||||||
77
tplx/sprigx/docinfo.html
Normal file
77
tplx/sprigx/docinfo.html
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
<!-- https://stackoverflow.com/a/34481639 -->
|
||||||
|
<!-- Generate a nice TOC -->
|
||||||
|
<script src="https://code.jquery.com/jquery-1.11.3.min.js"></script>
|
||||||
|
<script src="https://code.jquery.com/ui/1.11.4/jquery-ui.min.js"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.tocify/1.9.0/javascripts/jquery.tocify.min.js"></script>
|
||||||
|
<!-- We do not need the tocify CSS because the asciidoc CSS already provides most of what we neeed -->
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.tocify-header {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tocify-subheader {
|
||||||
|
font-style: normal;
|
||||||
|
font-size: 90%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tocify ul {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tocify-focus {
|
||||||
|
color: #7a2518;
|
||||||
|
background-color: rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tocify-focus > a {
|
||||||
|
color: #7a2518;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
$(function () {
|
||||||
|
// Add a new container for the tocify toc into the existing toc so we can re-use its
|
||||||
|
// styling
|
||||||
|
$("#toc").append("<div id='generated-toc'></div>");
|
||||||
|
$("#generated-toc").tocify({
|
||||||
|
extendPage: true,
|
||||||
|
context: "#content",
|
||||||
|
highlightOnScroll: true,
|
||||||
|
hideEffect: "slideUp",
|
||||||
|
// Use the IDs that asciidoc already provides so that TOC links and intra-document
|
||||||
|
// links are the same. Anything else might confuse users when they create bookmarks.
|
||||||
|
hashGenerator: function(text, element) {
|
||||||
|
return $(element).attr("id");
|
||||||
|
},
|
||||||
|
// Smooth scrolling doesn't work properly if we use the asciidoc IDs
|
||||||
|
smoothScroll: false,
|
||||||
|
// Set to 'none' to use the tocify classes
|
||||||
|
theme: "none",
|
||||||
|
// Handle book (may contain h1) and article (only h2 deeper)
|
||||||
|
selectors: $( "#content" ).has( "h1" ).size() > 0 ? "h1,h2,h3,h4,h5" : "h2,h3,h4,h5",
|
||||||
|
ignoreSelector: ".discrete"
|
||||||
|
});
|
||||||
|
|
||||||
|
// Switch between static asciidoc toc and dynamic tocify toc based on browser size
|
||||||
|
// This is set to match the media selectors in the asciidoc CSS
|
||||||
|
// Without this, we keep the dynamic toc even if it is moved from the side to preamble
|
||||||
|
// position which will cause odd scrolling behavior
|
||||||
|
var handleTocOnResize = function() {
|
||||||
|
if ($(document).width() < 768) {
|
||||||
|
$("#generated-toc").hide();
|
||||||
|
$(".sectlevel0").show();
|
||||||
|
$(".sectlevel1").show();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$("#generated-toc").show();
|
||||||
|
$(".sectlevel0").hide();
|
||||||
|
$(".sectlevel1").hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$(window).resize(handleTocOnResize);
|
||||||
|
handleTocOnResize();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
16
tplx/sprigx/errs.go
Normal file
16
tplx/sprigx/errs.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package sprigx
|
||||||
|
|
||||||
|
import (
|
||||||
|
`errors`
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrBadAddr error = errors.New("invalid/bad address")
|
||||||
|
ErrBadAddrPort error = errors.New("invalid/bad address/port")
|
||||||
|
ErrBadMonth error = errors.New("could not determine/parse month")
|
||||||
|
ErrBadNet error = errors.New("invalid/bad network")
|
||||||
|
ErrOverflow error = errors.New("integer/buffer overflow")
|
||||||
|
ErrBadType error = errors.New("an invalid/unknown type was passed")
|
||||||
|
ErrNilVal error = errors.New("a nil value was passed")
|
||||||
|
ErrUnderflow error = errors.New("integer/buffer underflow")
|
||||||
|
)
|
||||||
@@ -1,14 +1,116 @@
|
|||||||
package sprigx
|
package sprigx
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
`errors`
|
||||||
htpl "html/template"
|
htpl "html/template"
|
||||||
|
`math`
|
||||||
|
`reflect`
|
||||||
|
`strconv`
|
||||||
ttpl "text/template"
|
ttpl "text/template"
|
||||||
|
|
||||||
|
`github.com/Masterminds/sprig/v3`
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Many of these functions are modeled after sprig's.
|
Many of these functions are modeled after sprig's.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
CombinedFuncMap returns a generic function map (like [FuncMap]) combined with
|
||||||
|
[github.com/Masterminds/sprig/v3.GenericFuncMap].
|
||||||
|
|
||||||
|
If preferSprigx is true, SprigX function names will override Sprig
|
||||||
|
functions with the same name.
|
||||||
|
If false, Sprig functions will override conflicting SprigX functions
|
||||||
|
with the same name.
|
||||||
|
|
||||||
|
You probably want [CombinedHtmlFuncMap] or [CombinedTxtFuncMap] instead,
|
||||||
|
as they wrap this with the appropriate type.
|
||||||
|
*/
|
||||||
|
func CombinedFuncMap(preferSprigX bool) (fmap map[string]any) {
|
||||||
|
|
||||||
|
var fn any
|
||||||
|
var fnNm string
|
||||||
|
var sprigMap map[string]interface{} = sprig.GenericFuncMap()
|
||||||
|
var sprigxMap map[string]any = FuncMap()
|
||||||
|
|
||||||
|
if preferSprigX {
|
||||||
|
fmap = sprigMap
|
||||||
|
for fnNm, fn = range sprigxMap {
|
||||||
|
fmap[fnNm] = fn
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmap = sprigxMap
|
||||||
|
for fnNm, fn = range sprigMap {
|
||||||
|
fmap[fnNm] = fn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
CombinedHtmlFuncMap returns an [htpl.FuncMap] (like [HtmlFuncMap]) combined with
|
||||||
|
[github.com/Masterminds/sprig/v3.HtmlFuncMap].
|
||||||
|
|
||||||
|
If preferSprigx is true, SprigX function names will override Sprig
|
||||||
|
functions with the same name.
|
||||||
|
If false, Sprig functions will override conflicting SprigX functions
|
||||||
|
with the same name.
|
||||||
|
*/
|
||||||
|
func CombinedHtmlFuncMap(preferSprigX bool) (fmap htpl.FuncMap) {
|
||||||
|
|
||||||
|
var fn any
|
||||||
|
var fnNm string
|
||||||
|
var sprigMap htpl.FuncMap = sprig.HtmlFuncMap()
|
||||||
|
var sprigxMap htpl.FuncMap = HtmlFuncMap()
|
||||||
|
|
||||||
|
if preferSprigX {
|
||||||
|
fmap = sprigMap
|
||||||
|
for fnNm, fn = range sprigxMap {
|
||||||
|
fmap[fnNm] = fn
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmap = sprigxMap
|
||||||
|
for fnNm, fn = range sprigMap {
|
||||||
|
fmap[fnNm] = fn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
CombinedTxtFuncMap returns a [ttpl.FuncMap] (like [TxtFuncMap]) combined with
|
||||||
|
[github.com/Masterminds/sprig/v3.TxtFuncMap].
|
||||||
|
|
||||||
|
If preferSprigx is true, SprigX function names will override Sprig
|
||||||
|
functions with the same name.
|
||||||
|
If false, Sprig functions will override conflicting SprigX functions
|
||||||
|
with the same name.
|
||||||
|
*/
|
||||||
|
func CombinedTxtFuncMap(preferSprigX bool) (fmap ttpl.FuncMap) {
|
||||||
|
|
||||||
|
var fn any
|
||||||
|
var fnNm string
|
||||||
|
var sprigMap ttpl.FuncMap = sprig.TxtFuncMap()
|
||||||
|
var sprigxMap ttpl.FuncMap = TxtFuncMap()
|
||||||
|
|
||||||
|
if preferSprigX {
|
||||||
|
fmap = sprigMap
|
||||||
|
for fnNm, fn = range sprigxMap {
|
||||||
|
fmap[fnNm] = fn
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmap = sprigxMap
|
||||||
|
for fnNm, fn = range sprigMap {
|
||||||
|
fmap[fnNm] = fn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
FuncMap returns a generic function map.
|
FuncMap returns a generic function map.
|
||||||
|
|
||||||
@@ -25,6 +127,11 @@ func FuncMap() (fmap map[string]any) {
|
|||||||
for fn, f = range genericMap {
|
for fn, f = range genericMap {
|
||||||
fmap[fn] = f
|
fmap[fn] = f
|
||||||
}
|
}
|
||||||
|
if osGenericMap != nil && len(osGenericMap) > 0 {
|
||||||
|
for fn, f = range osGenericMap {
|
||||||
|
fmap[fn] = f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -43,6 +150,17 @@ func HtmlFuncMap() (fmap htpl.FuncMap) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if osHtmlMap != nil && len(osHtmlMap) > 0 {
|
||||||
|
for fn, f = range osHtmlMap {
|
||||||
|
fmap[fn] = f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nop explicitly performs a NO-OP and returns an empty string, allowing one to override "unsafe" functions.
|
||||||
|
func Nop(obj ...any) (s string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,5 +178,179 @@ func TxtFuncMap() (fmap ttpl.FuncMap) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if osTxtMap != nil && len(osTxtMap) > 0 {
|
||||||
|
for fn, f = range osTxtMap {
|
||||||
|
fmap[fn] = f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
toFloat64 uses reflection to resolve any string or numeric type (even custom types) to a float64.
|
||||||
|
|
||||||
|
It wraps toString for string types but will fall back to checking numeric types.
|
||||||
|
|
||||||
|
If err != nil, then NaN (if true) indicates that:
|
||||||
|
|
||||||
|
* val is a string (or pointer to a string), but
|
||||||
|
* is not a valid numeric string
|
||||||
|
|
||||||
|
(you can do this from the caller as well by calling `errors.Is(err, strconv.ErrSyntax)`).
|
||||||
|
err will always be non-nil if NaN is true.
|
||||||
|
|
||||||
|
err will be ErrNilVal if val is nil.
|
||||||
|
*/
|
||||||
|
func toFloat64(val any) (f float64, NaN bool, err error) {
|
||||||
|
|
||||||
|
var s string
|
||||||
|
var k reflect.Kind
|
||||||
|
var rv reflect.Value
|
||||||
|
|
||||||
|
// toString will return ErrNilVal if nil.
|
||||||
|
if s, err = toString(val); err != nil {
|
||||||
|
if errors.Is(err, ErrBadType) {
|
||||||
|
// This is OK, it's (hopefully) a number type.
|
||||||
|
err = nil
|
||||||
|
} else {
|
||||||
|
// *probably* ErrNilVal.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// We can go ahead and parse this directly since it's already deref'd if a ptr.
|
||||||
|
if f, err = strconv.ParseFloat(s, 64); err != nil {
|
||||||
|
NaN = errors.Is(err, strconv.ErrSyntax)
|
||||||
|
}
|
||||||
|
// We can return regardless here; it's up to the caller to check NaN/err.
|
||||||
|
// If they're false/nil, f is parsed already!
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = reflect.ValueOf(val)
|
||||||
|
k = rv.Kind()
|
||||||
|
|
||||||
|
if k == reflect.Ptr {
|
||||||
|
if rv.IsNil() {
|
||||||
|
// *technically* this should be handled above, but best be safe.
|
||||||
|
err = ErrNilVal
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rv = rv.Elem()
|
||||||
|
k = rv.Kind()
|
||||||
|
}
|
||||||
|
|
||||||
|
switch k {
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
f = float64(rv.Int())
|
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||||
|
f = float64(rv.Uint())
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
f = rv.Float()
|
||||||
|
default:
|
||||||
|
// No need to check for string types since we do that near the beginning.
|
||||||
|
err = ErrBadType
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
toInt wraps toFloat64, rounds it to the nearest integer,
|
||||||
|
and converts to an int.
|
||||||
|
|
||||||
|
NaN, err have the same meaning as in toFloat64.
|
||||||
|
|
||||||
|
This function will panic if float64(val)'s f return exceeds
|
||||||
|
math.MaxInt on your platform.
|
||||||
|
*/
|
||||||
|
func toInt(val any) (i int, NaN bool, err error) {
|
||||||
|
|
||||||
|
var f float64
|
||||||
|
|
||||||
|
if f, NaN, err = toFloat64(val); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
i = int(math.Round(f))
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
toPosFloat64 wraps toFloat64 and ensures that it is a positive float64.
|
||||||
|
|
||||||
|
NaN, err have the same meaning as in toFloat64.
|
||||||
|
*/
|
||||||
|
func toPosFloat64(val any) (f float64, NaN bool, err error) {
|
||||||
|
|
||||||
|
if f, NaN, err = toFloat64(val); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
f = math.Abs(f)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
toPosInt wraps toPosFloat64, rounds it to the nearest integer,
|
||||||
|
and converts to an int.
|
||||||
|
|
||||||
|
NaN, err have the same meaning as in toPosFloat64 (and thus toFloat64).
|
||||||
|
|
||||||
|
This function will panic if float64(val)'s f return exceeds
|
||||||
|
math.MaxInt on your platform.
|
||||||
|
*/
|
||||||
|
func toPosInt(val any) (i int, NaN bool, err error) {
|
||||||
|
|
||||||
|
var f float64
|
||||||
|
|
||||||
|
if f, NaN, err = toPosFloat64(val); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
i = int(math.Round(f))
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
toString uses reflection to resolve any string value (even custom types and ptrs)
|
||||||
|
to a concrete string.
|
||||||
|
|
||||||
|
err will be ErrBadType if val is not a string type/string-derived type.
|
||||||
|
err will be ErrNilVal if val is nil.
|
||||||
|
*/
|
||||||
|
func toString(val any) (s string, err error) {
|
||||||
|
|
||||||
|
var rv reflect.Value
|
||||||
|
var k reflect.Kind
|
||||||
|
|
||||||
|
if val == nil {
|
||||||
|
err = ErrNilVal
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = reflect.ValueOf(val)
|
||||||
|
k = rv.Kind()
|
||||||
|
|
||||||
|
if k == reflect.Ptr {
|
||||||
|
if rv.IsNil() {
|
||||||
|
// *technically* this should be handled above, but best be safe.
|
||||||
|
err = ErrNilVal
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rv = rv.Elem()
|
||||||
|
k = rv.Kind()
|
||||||
|
}
|
||||||
|
|
||||||
|
if k == reflect.String {
|
||||||
|
s = rv.String()
|
||||||
|
} else {
|
||||||
|
err = ErrBadType
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
package sprigx
|
|
||||||
|
|
||||||
import (
|
|
||||||
`github.com/davecgh/go-spew/spew`
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
|
||||||
dump calls [spew.Sdump] on obj.
|
|
||||||
|
|
||||||
[spew.Sdump]: https://pkg.go.dev/github.com/davecgh/go-spew/spew
|
|
||||||
*/
|
|
||||||
func dump(obj any) (out string) {
|
|
||||||
|
|
||||||
out = spew.Sdump(obj)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
9
tplx/sprigx/funcs_tpl_meta.go
Normal file
9
tplx/sprigx/funcs_tpl_meta.go
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package sprigx
|
||||||
|
|
||||||
|
// metaIsNil returns true if obj is explicitly nil.
|
||||||
|
func metaIsNil(obj any) (isNil bool) {
|
||||||
|
|
||||||
|
isNil = obj == nil
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
82
tplx/sprigx/funcs_tpl_net.go
Normal file
82
tplx/sprigx/funcs_tpl_net.go
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
package sprigx
|
||||||
|
|
||||||
|
import (
|
||||||
|
`math`
|
||||||
|
`net`
|
||||||
|
`strconv`
|
||||||
|
)
|
||||||
|
|
||||||
|
// netExtractAddr calls net.ParseCIDR and returns the net.IP from it.
|
||||||
|
func netExtractAddr(s string) (addr net.IP, err error) {
|
||||||
|
|
||||||
|
if addr, _, err = net.ParseCIDR(s); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// netExtractHost extracts the host component from hostPort.
|
||||||
|
func netExtractHost(hostPort string) (host string, err error) {
|
||||||
|
|
||||||
|
if host, _, err = net.SplitHostPort(hostPort); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// netExtractIpnet calls net.ParseCIDR and returns the net.IPNet from it.
|
||||||
|
func netExtractIpnet(s string) (ipnet *net.IPNet, err error) {
|
||||||
|
|
||||||
|
if _, ipnet, err = net.ParseCIDR(s); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// netExtractPort extracts the port component from hostPort.
|
||||||
|
func netExtractPort(hostPort string) (port uint16, err error) {
|
||||||
|
|
||||||
|
var portStr string
|
||||||
|
var u64 uint64
|
||||||
|
|
||||||
|
if _, portStr, err = net.SplitHostPort(hostPort); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if u64, err = strconv.ParseUint(portStr, 10, 16); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
port = uint16(u64)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// netIp4Mask is a more flexible wrapper around net.IPv4Mask.
|
||||||
|
func netIp4Mask(a, b, c, d any) (mask net.IPMask, err error) {
|
||||||
|
|
||||||
|
var idx int
|
||||||
|
var elem any
|
||||||
|
var elemInt int
|
||||||
|
var mBytes [4]byte
|
||||||
|
var orig [4]any = [4]any{a, b, c, d}
|
||||||
|
|
||||||
|
for idx, elem = range orig {
|
||||||
|
if elemInt, _, err = toPosInt(elem); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if elemInt > math.MaxUint8 {
|
||||||
|
err = ErrOverflow
|
||||||
|
return
|
||||||
|
}
|
||||||
|
mBytes[idx] = byte(uint8(elemInt))
|
||||||
|
}
|
||||||
|
|
||||||
|
mask = net.IPv4Mask(
|
||||||
|
mBytes[0], mBytes[1], mBytes[2], mBytes[3],
|
||||||
|
)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
47
tplx/sprigx/funcs_tpl_netipx.go
Normal file
47
tplx/sprigx/funcs_tpl_netipx.go
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
package sprigx
|
||||||
|
|
||||||
|
import (
|
||||||
|
`net`
|
||||||
|
`net/netip`
|
||||||
|
|
||||||
|
`go4.org/netipx`
|
||||||
|
)
|
||||||
|
|
||||||
|
// netipxFromStdAddr wraps go4.org/netipx.FromStdAddr to comply with Go template requirements.
|
||||||
|
func netipxFromStdAddr(ip net.IP, port int, zone string) (addrPort netip.AddrPort, err error) {
|
||||||
|
|
||||||
|
var ok bool
|
||||||
|
|
||||||
|
if addrPort, ok = netipx.FromStdAddr(ip, port, zone); !ok {
|
||||||
|
err = ErrBadAddrPort
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// netipxFromIp wraps go4.org/netipx.FromStdIP to comply with Go template requirements.
|
||||||
|
func netipxFromIp(ip net.IP) (addr netip.Addr, err error) {
|
||||||
|
|
||||||
|
var ok bool
|
||||||
|
|
||||||
|
if addr, ok = netipx.FromStdIP(ip); !ok {
|
||||||
|
err = ErrBadAddr
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// netipxFromIpNet wraps go4.org/netipx.FromStdIPNet to comply with Go template requirements.
|
||||||
|
func netipxFromIpNet(ipnet *net.IPNet) (pfx netip.Prefix, err error) {
|
||||||
|
|
||||||
|
var ok bool
|
||||||
|
|
||||||
|
if pfx, ok = netipx.FromStdIPNet(ipnet); !ok {
|
||||||
|
err = ErrBadNet
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
51
tplx/sprigx/funcs_tpl_nums.go
Normal file
51
tplx/sprigx/funcs_tpl_nums.go
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
package sprigx
|
||||||
|
|
||||||
|
import (
|
||||||
|
`math/big`
|
||||||
|
)
|
||||||
|
|
||||||
|
// numFloat64 returns any string representation of a numeric value or any type of numeric value to a float64.
|
||||||
|
func numFloat64(val any) (f float64, err error) {
|
||||||
|
|
||||||
|
if f, _, err = toFloat64(val); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
numFloatStr wraps numFloat32Str and numFloat64Str.
|
||||||
|
|
||||||
|
val can be a string representation of any numeric value or any type of numeric value.
|
||||||
|
*/
|
||||||
|
func numFloatStr(val any) (s string, err error) {
|
||||||
|
|
||||||
|
var f float64
|
||||||
|
|
||||||
|
if f, _, err = toFloat64(val); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s = numFloat64Str(f)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// numFloat32Str returns float32 f as a complete string representation with no truncation (or right-padding).
|
||||||
|
func numFloat32Str(f float32) (s string) {
|
||||||
|
|
||||||
|
s = numFloat64Str(float64(f))
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// numFloat64Str returns float64 f as a complete string representation with no truncation (or right-padding).
|
||||||
|
func numFloat64Str(f float64) (s string) {
|
||||||
|
|
||||||
|
var bf *big.Float
|
||||||
|
|
||||||
|
bf = big.NewFloat(f)
|
||||||
|
s = bf.Text('f', -1)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -1,13 +1,99 @@
|
|||||||
package sprigx
|
package sprigx
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
`os`
|
||||||
|
`os/user`
|
||||||
|
`strconv`
|
||||||
|
`strings`
|
||||||
)
|
)
|
||||||
|
|
||||||
// osHostname returns os.Hostname()
|
/*
|
||||||
func osHostname() (out string, err error) {
|
osGroupById returns os/user.LookupGroupId.
|
||||||
|
|
||||||
out, err = os.Hostname()
|
Can accept either a string (`"1000"`) or any
|
||||||
|
numeric type (`1000`, `-1000`, `1000.0`, `MyCustomType(1000)`, etc.)
|
||||||
|
*/
|
||||||
|
func osGroupById(gid any) (g *user.Group, err error) {
|
||||||
|
|
||||||
|
var i int
|
||||||
|
var NaN bool
|
||||||
|
var gidStr string
|
||||||
|
|
||||||
|
if i, NaN, err = toPosInt(gid); err != nil {
|
||||||
|
if NaN {
|
||||||
|
err = nil
|
||||||
|
if gidStr, err = toString(gid); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
gidStr = strconv.Itoa(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
g, err = user.LookupGroupId(gidStr)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
osFQDN (tries to) return the FQDN of this host.
|
||||||
|
|
||||||
|
Currently it just calls os.Hostname() but may be extended to "try harder" in the future.
|
||||||
|
*/
|
||||||
|
func osFQDN() (fqdn string, err error) {
|
||||||
|
|
||||||
|
fqdn, err = os.Hostname()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
osHost returns the system's "host shortname".
|
||||||
|
|
||||||
|
Currently it just calls os.Hostname() and takes the first
|
||||||
|
"host label" (as RFCs refer to it), but it may be extended
|
||||||
|
in the future.
|
||||||
|
*/
|
||||||
|
func osHost() (hostNm string, err error) {
|
||||||
|
|
||||||
|
hostNm, err = os.Hostname()
|
||||||
|
|
||||||
|
if hostNm == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
hostNm = strings.Split(hostNm, ".")[0]
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
osUserById returns an os/user.LookupId.
|
||||||
|
|
||||||
|
Can accept either a string (`"1000"`) or any
|
||||||
|
numeric type (`1000`, `-1000`, `1000.0`, `MyCustomType(1000)`, etc.)
|
||||||
|
*/
|
||||||
|
func osUserById(uid any) (u *user.User, err error) {
|
||||||
|
|
||||||
|
var i int
|
||||||
|
var NaN bool
|
||||||
|
var uidStr string
|
||||||
|
|
||||||
|
if i, NaN, err = toPosInt(uid); err != nil {
|
||||||
|
if NaN {
|
||||||
|
err = nil
|
||||||
|
if uidStr, err = toString(uid); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
uidStr = strconv.Itoa(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
u, err = user.LookupId(uidStr)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
43
tplx/sprigx/funcs_tpl_psutils.go
Normal file
43
tplx/sprigx/funcs_tpl_psutils.go
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
package sprigx
|
||||||
|
|
||||||
|
import (
|
||||||
|
`github.com/shirou/gopsutil/v4/host`
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
psHostPlatInfo returns a "squashed" github.com/shirou/gopsutil/v4/host.PlatformInformation;
|
||||||
|
normally it returns a (string, string, string, error)
|
||||||
|
but you can only have a (any) or (any, error) return in Golang templates.
|
||||||
|
*/
|
||||||
|
func psHostPlatInfo() (platInfo [3]string, err error) {
|
||||||
|
|
||||||
|
var s1 string
|
||||||
|
var s2 string
|
||||||
|
var s3 string
|
||||||
|
|
||||||
|
if s1, s2, s3, err = host.PlatformInformation(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
platInfo = [3]string{s1, s2, s3}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
psHostVirt returns a "squared" github.com/shirou/gopsutil/v4/host.Virtualization;
|
||||||
|
normally it returns a (string, string, error) but Go templates etc.
|
||||||
|
*/
|
||||||
|
func psHostVirt() (virtInfo [2]string, err error) {
|
||||||
|
|
||||||
|
var s1 string
|
||||||
|
var s2 string
|
||||||
|
|
||||||
|
if s1, s2, err = host.Virtualization(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
virtInfo = [2]string{s1, s2}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
15
tplx/sprigx/funcs_tpl_psutils_linux.go
Normal file
15
tplx/sprigx/funcs_tpl_psutils_linux.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package sprigx
|
||||||
|
|
||||||
|
import (
|
||||||
|
`context`
|
||||||
|
|
||||||
|
`github.com/shirou/gopsutil/v4/sensors`
|
||||||
|
)
|
||||||
|
|
||||||
|
// psSensorExTemp wraps github.com/shirou/gopsutil/v4/sensors.NewExLinux().TemperatureWithContext() to not require a context.
|
||||||
|
func psSensorExTemp() (exTemps []sensors.ExTemperature, err error) {
|
||||||
|
|
||||||
|
exTemps, err = sensors.NewExLinux().TemperatureWithContext(context.Background())
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -5,6 +5,22 @@ import (
|
|||||||
`runtime`
|
`runtime`
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// sysArch returns [runtime.GOARCH].
|
||||||
|
func sysArch() (out string) {
|
||||||
|
|
||||||
|
out = runtime.GOARCH
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// sysOsNm returns [runtime.GOOS].
|
||||||
|
func sysOsNm() (out string) {
|
||||||
|
|
||||||
|
out = runtime.GOOS
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// sysRuntime returns various information from [runtime].
|
// sysRuntime returns various information from [runtime].
|
||||||
func sysRuntime() (out map[string]string) {
|
func sysRuntime() (out map[string]string) {
|
||||||
|
|
||||||
@@ -21,27 +37,3 @@ func sysRuntime() (out map[string]string) {
|
|||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// sysArch returns [runtime.GOARCH].
|
|
||||||
func sysArch() (out string) {
|
|
||||||
|
|
||||||
out = runtime.GOARCH
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// sysNumCpu returns the reuslt from [runtime.NumCPU].
|
|
||||||
func sysNumCpu() (out string) {
|
|
||||||
|
|
||||||
out = fmt.Sprintf("%d", runtime.NumCPU())
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// sysOsNm returns [runtime.GOOS].
|
|
||||||
func sysOsNm() (out string) {
|
|
||||||
|
|
||||||
out = runtime.GOOS
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|||||||
139
tplx/sprigx/funcs_tpl_time.go
Normal file
139
tplx/sprigx/funcs_tpl_time.go
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
package sprigx
|
||||||
|
|
||||||
|
import (
|
||||||
|
`errors`
|
||||||
|
`strconv`
|
||||||
|
`strings`
|
||||||
|
`time`
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
tmFmt formats time t using format string fstr.
|
||||||
|
|
||||||
|
While one certainly can do the same via e.g.
|
||||||
|
|
||||||
|
{{- $t := tmNow -}}
|
||||||
|
{{ $t.Format $fstr }}
|
||||||
|
|
||||||
|
This takes a time.Time as the second (and last) parameter,
|
||||||
|
allowing it to work in pipelines.
|
||||||
|
*/
|
||||||
|
func tmFmt(fstr string, t time.Time) (out string) {
|
||||||
|
|
||||||
|
out = t.Format(fstr)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
tmParseMonth attempts to first try tmParseMonthInt
|
||||||
|
and then tries tmParseMonthStr if v is not "numeric".
|
||||||
|
*/
|
||||||
|
func tmParseMonth(v any) (mon time.Month, err error) {
|
||||||
|
|
||||||
|
var s string
|
||||||
|
|
||||||
|
if mon, err = tmParseMonthInt(v); err != nil {
|
||||||
|
if errors.Is(err, strconv.ErrSyntax) {
|
||||||
|
// NaN
|
||||||
|
err = nil
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it gets here, it's a non-numeric string.
|
||||||
|
if s, err = toString(v); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if mon, err = tmParseMonthStr(s); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
tmParseMonthInt parses a number representation of month n to a time.Month.
|
||||||
|
n may be any numeric type or a string representation of a number
|
||||||
|
(or a custom type derived from those).
|
||||||
|
|
||||||
|
A negative integer (or float, etc.) will be converted to a positive one (e.g. -6 => 6 => time.June).
|
||||||
|
|
||||||
|
floats are rounded to the nearest integer.
|
||||||
|
|
||||||
|
The integer should map directly to the month constants in the time module:
|
||||||
|
|
||||||
|
* 1: January
|
||||||
|
* 2: February
|
||||||
|
* 3: March
|
||||||
|
* 4: April
|
||||||
|
* 5: May
|
||||||
|
* 6: June
|
||||||
|
* 7: July
|
||||||
|
* 8: August
|
||||||
|
* 9: September
|
||||||
|
* 10: October
|
||||||
|
* 11: November
|
||||||
|
* 12: December
|
||||||
|
|
||||||
|
If n resolves to 0, mon will be the current month (as determined by time.Now).
|
||||||
|
|
||||||
|
If n resolves to > 12, err will be ErrBadMonth.
|
||||||
|
*/
|
||||||
|
func tmParseMonthInt(n any) (mon time.Month, err error) {
|
||||||
|
|
||||||
|
var i int
|
||||||
|
|
||||||
|
if i, _, err = toPosInt(n); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if i == 0 {
|
||||||
|
mon = time.Now().Month()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if i > 12 {
|
||||||
|
err = ErrBadMonth
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
mon = time.Month(i)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
tmParseMonthStr parses a string representation of month s to a time.Month.
|
||||||
|
|
||||||
|
It normalizes s to lowercase and only uses the first 3 characters
|
||||||
|
(the minimum length needed to determine month name
|
||||||
|
uniqueness - "June" vs. "July", "March" vs. "May").
|
||||||
|
|
||||||
|
An empty (or whitespace-only) string will use the current month (as determined by time.Now).
|
||||||
|
*/
|
||||||
|
func tmParseMonthStr(s string) (mon time.Month, err error) {
|
||||||
|
|
||||||
|
var i int
|
||||||
|
var m time.Month
|
||||||
|
|
||||||
|
if strings.TrimSpace(s) == "" {
|
||||||
|
mon = time.Now().Month()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
s = strings.ToLower(strings.TrimSpace(s))[0:3]
|
||||||
|
|
||||||
|
for i = range 12 {
|
||||||
|
m = time.Month(i + 1)
|
||||||
|
if strings.ToLower(m.String())[0:3] == s {
|
||||||
|
mon = m
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ErrBadMonth
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
11
uuidx/consts.go
Normal file
11
uuidx/consts.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package uuidx
|
||||||
|
|
||||||
|
const (
|
||||||
|
RfcNone RfcGen = iota
|
||||||
|
Rfc4122
|
||||||
|
Rfc9562
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
MsGuidThreshold int = 4
|
||||||
|
)
|
||||||
73
uuidx/doc.go
Normal file
73
uuidx/doc.go
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
Package uuidx intends to supplement [github.com/google/uuid].
|
||||||
|
|
||||||
|
# Microsoft GUID Shenanigans
|
||||||
|
|
||||||
|
The following functions are provided to deal with [Microsoft's incompetence]:
|
||||||
|
|
||||||
|
* [DetectMsGuid] (a confidence'd determination if a UUID is a Microsoft GUID or not)
|
||||||
|
* [IsFlippedEndian] for flipped-endian [uuid.UUID] comparison (e.g. a is the Microsoft-flipped-endian version of b)
|
||||||
|
* [IsMsGuid] (wraps [DetectMsGuid] and returns true if confidence is reasonably strong that it's a Microsoft GUID)
|
||||||
|
* [IsRfc] (the inverse of IsMsGuid, but also checks for strict RFC compliance and returns which RFC)
|
||||||
|
* [MsGuidToUuid] (explicitly convert/ensure a GUID/UUID is likely a UUID)
|
||||||
|
* [ToggleUuidMsGuid] (blindly flip the endianness of selected byte ranges for MS GUID <-> UUID conversion)
|
||||||
|
* [UuidToMsGuid] (explicitly convert/ensure a GUID/UUID is likely an MS GUID)
|
||||||
|
|
||||||
|
Microsoft, in their typical insanity, uses a proprietary UUID format (usually referred to as the "Microsoft GUID Format"
|
||||||
|
or "Mixed-Endian Format").
|
||||||
|
|
||||||
|
Normally for, for example a UUIDv4, it's structured as thus per RFC 9562 [§ 5.4] (which obsoletes RFC 4122 [§ 4.4]):
|
||||||
|
|
||||||
|
A B C D E
|
||||||
|
HEX(BE(uint32))-HEX(BE(uint16))-HEX(BE(uint16))-HEX(BE(<uint16>), BE(<6 bytes>))
|
||||||
|
|
||||||
|
(where <BE> is big-endian packing).
|
||||||
|
|
||||||
|
However, thanks to Microsoft we can't have nice things. They decided to completely ignore the standard, and
|
||||||
|
instead keep D/E as big-endian *but use little-endian* for A through C inclusive:
|
||||||
|
|
||||||
|
A B C D E
|
||||||
|
HEX(LE(uint32))-HEX(LE(uint16))-HEX(LE(uint16))-HEX(BE(<uint16>), BE(<6 bytes>))
|
||||||
|
|
||||||
|
"Surely that had SOME reason to do that," you may say to yourself, "they wouldn't make some arbitrary formatting
|
||||||
|
change from a standard just because."
|
||||||
|
|
||||||
|
You would be wrong. To my knowledge, they have never provided any technological justfification to this insanity,
|
||||||
|
and now it's infected its way into a slew of other technologies they've had their grubby little hands involved in
|
||||||
|
(e.g. UEFI). And it's of course too late to change.
|
||||||
|
|
||||||
|
So anyways here's a library to make dealing with Microsoft's hubris a little easier.
|
||||||
|
|
||||||
|
# Validation/Verification
|
||||||
|
|
||||||
|
Aside from trying to address Microsoft silliness, there are some additional functions:
|
||||||
|
|
||||||
|
* [Equal] for [uuid.UUID] comparison
|
||||||
|
* [IsMaxUUID] (if a given [uuid.UUID] is an RFC 9562 [§ 5.10] UUID)
|
||||||
|
* [IsNilUUID] (if a given [uuid.UUID] is an RFC 9562 [§ 5.9] UUID)
|
||||||
|
* [IsValid] (If an RFC can be considered safely conformant to RFC spec)
|
||||||
|
|
||||||
|
# Future Incorporation/Deprecation/Obsolescence
|
||||||
|
|
||||||
|
Worth keeping an eye on are:
|
||||||
|
|
||||||
|
* https://github.com/google/uuid/pull/192
|
||||||
|
* https://github.com/golang/go/issues/62026
|
||||||
|
* https://github.com/golang/go/issues/76319
|
||||||
|
(generally it's a bad idea for an API addition overall, but some good ideas were raised)
|
||||||
|
|
||||||
|
Some of these additions may deprecate/obsolete components of this package.
|
||||||
|
I'll try to keep them around but mark as deprecated as they are (if they are),
|
||||||
|
but I make no concrete promises - I hate making new major releases in Go's
|
||||||
|
[silly module architecture] even more than I do keeping old deprecated code around.
|
||||||
|
So caveat emptor.
|
||||||
|
|
||||||
|
[Microsoft's incompetence]: https://learn.microsoft.com/en-us/windows/win32/api/guiddef/ns-guiddef-guid
|
||||||
|
[§ 5.4]: https://datatracker.ietf.org/doc/html/rfc9562#section-5.4
|
||||||
|
[§ 4.4]: https://datatracker.ietf.org/doc/html/rfc4122#section-4.4
|
||||||
|
[§ 5.9]: https://datatracker.ietf.org/doc/html/rfc9562#section-5.9
|
||||||
|
[§ 5.10]: https://datatracker.ietf.org/doc/html/rfc9562#section-5.10
|
||||||
|
[github:google/uuid#192]: https://github.com/google/uuid/pull/192
|
||||||
|
[silly module architecture]: https://go.dev/doc/modules/major-version
|
||||||
|
*/
|
||||||
|
package uuidx
|
||||||
461
uuidx/funcs.go
Normal file
461
uuidx/funcs.go
Normal file
@@ -0,0 +1,461 @@
|
|||||||
|
package uuidx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
DetectMsGuid tries to guess if a given [uuid.UUID] is actually a Microsoft GUID or not.
|
||||||
|
|
||||||
|
Note that there are technically *two* types of Microsoft GUIDs:
|
||||||
|
|
||||||
|
* One is predictable, and defined in RFC 9562 [§ 4.2] as a known variant.
|
||||||
|
Detecting this is very easy and (assuming an RFC-compliant UUID is originally passed) is detectable with 100% confidence.
|
||||||
|
It's also legacy, and Microsoft no longer uses this format. Because they are insane and enjoy the suffering of others.
|
||||||
|
* The other, MODERN Microsoft GUID currently in use is the endianness-flipped version (see [ToggleUuidMsGuid]).
|
||||||
|
This is impossible to 100% determine, but analysis can get *pretty* close.
|
||||||
|
|
||||||
|
cs is a confidence scoring. As more logic is added, it *is* mathematically possible
|
||||||
|
(though unlikely) that cs == 0, so the caller is then responsible for making further
|
||||||
|
guesswork based on contextual analysis ("Did I get this UUID/GUID from an Active Directory attribute?"
|
||||||
|
"Is it a SID constant?" etc.).
|
||||||
|
|
||||||
|
A score > 0 indicates a confidence leaning towards the provided UUID/GUID being a Microsoft GUID.
|
||||||
|
A score < 0 indicates a confidence leaning towards the provided UUID/GUID *not* being a Microsoft GUID.
|
||||||
|
Note that a score of < 0 does not necessarily indicate it is a *proper, standard RFC-compliant UUID*,
|
||||||
|
simply that it is likely NOT a Microsoft GUID. [IsRfc] will be of further help in these cases.
|
||||||
|
|
||||||
|
csFlip indicates a score for the [ToggleUuidMsGuid]-flipped version of u.
|
||||||
|
It follows the same rules for thresholds and such as cs, but may be awarded different confidence levels
|
||||||
|
internally due to different chances of false positives.
|
||||||
|
If both cs and csFlip are > 0 but csFlip > cs, it is better to assume that u is *not* in the flipped-endian format
|
||||||
|
but *is* a Microsoft GUID (in other words, it is likely that u has *already been flipped* to proper/consistent endianness
|
||||||
|
instead of being a mixed-endian GUID).
|
||||||
|
|
||||||
|
In some cases where flipped-endianness does not matter (e.g. [IsNilUUID], [IsMaxUUID]),
|
||||||
|
cs and csFlip will be equal.
|
||||||
|
|
||||||
|
*Randomly-generated* GUIDs on Windows Server 2000-family and up are almost always UUIDv4.
|
||||||
|
Pre-Windows Server 2000 family *OR* any *statically-defined* GUIDs (schemaIDGUID, rightsGUID, CLSID constants, etc.)
|
||||||
|
are all over the place - TYPICALLY UUIDv1, but it's nothing predictable enough to be useful in definitive classification.
|
||||||
|
COM interfaces are all OVER the place in UUID version, but usually *not* UUIDv4.
|
||||||
|
|
||||||
|
A target/expected UUID version can be provided via tgtVer. To disable version analysis, use 0 (or 0x00, etc.).
|
||||||
|
It is *highly* recommended to provide a tgtVer if it is known; it can significantly boost confidence in the correct direction.
|
||||||
|
A warning, though - if a *wrong* tgtVer IS specified, it can negatively affect confidence accuracy.
|
||||||
|
Thus if you aren't ABSOLUTELY certain of the target UUID version, it's better to use 0/0x00 to disable the check.
|
||||||
|
Providing a target version is key to breaking some ties (e.g. both cs and csFlip are equal).
|
||||||
|
For example, the given RFC-compliant UUIDv4:
|
||||||
|
|
||||||
|
8d8e35ae-58d2-4d28-b09d-ffffffffffff
|
||||||
|
|
||||||
|
when flipped evaluates to an RFC-compliant UUIDv2:
|
||||||
|
|
||||||
|
ae358e8d-d258-284d-b09d-ffffffffffff
|
||||||
|
|
||||||
|
and in this case, cs and csFlip will both end up as 0.
|
||||||
|
Providing a tgtVer of 4 shifts this to a proper "tie-breaker" of cs == -3 and csFlip == 0.
|
||||||
|
Similarly, the endian-flipped UUIDv4 evaluates as a UUIDv2:
|
||||||
|
|
||||||
|
9856ea36-c2ca-2347-af0c-3b42f76c9eca
|
||||||
|
|
||||||
|
from the original unflipped UUIDv4:
|
||||||
|
|
||||||
|
36ea5698-cac2-4723-af0c-3b42f76c9eca
|
||||||
|
|
||||||
|
which results in a cs == 1 and csFlip == 0 - not very high confidence (but at least a correct and non-zero lean).
|
||||||
|
Providing a tgtVer == 4 changes this to cs == 7 and csFlip == 0, which is *much* more decisive.
|
||||||
|
|
||||||
|
UUIDs/GUIDs found to be strictly RFC-conforming (via [IsRfc], which returns false for Microsoft GUIDs)
|
||||||
|
are *heavily* weighted negatively.
|
||||||
|
|
||||||
|
Confidence levels can be generally considered as the following:
|
||||||
|
|
||||||
|
cs >= 7: Likely Microsoft GUID (mixed-endian)
|
||||||
|
cs >= 4: Likely Microsoft GUID
|
||||||
|
0 < cs < 4: Leans Microsoft GUID, but untrusted
|
||||||
|
cs == 0: Entirely ambiguous/indeterminate
|
||||||
|
-4 < cs < 0: Leans UUID/non-Microsoft GUID but untrusted
|
||||||
|
cs <= -5: Likely UUID/not Microsoft GUID
|
||||||
|
csFlip >=cs && csFlip >= 4: Likely a pre-flipped (ToggleUuidMsGuid'd) Microsoft GUID
|
||||||
|
|
||||||
|
[§ 4.2]: https://datatracker.ietf.org/doc/html/rfc9562#section-4.2
|
||||||
|
*/
|
||||||
|
|
||||||
|
func DetectMsGuid(u uuid.UUID, tgtVer uuid.Version) (cs, csFlip int) {
|
||||||
|
|
||||||
|
var isRfc bool
|
||||||
|
var flippedRfc bool
|
||||||
|
var flipped uuid.UUID = ToggleUuidMsGuid(u)
|
||||||
|
|
||||||
|
// These are the exact same when flipped, and are statically defined.
|
||||||
|
if IsNilUUID(u) || IsMaxUUID(u) {
|
||||||
|
cs = -12
|
||||||
|
csFlip = -12
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Most/all(?) Microsoft GUIDs are not NCS.
|
||||||
|
if IsNcs(u) {
|
||||||
|
cs -= 2
|
||||||
|
}
|
||||||
|
if IsNcs(flipped) {
|
||||||
|
// The flipped has a higher likelihood of false-pos, so we don't score it as confidently.
|
||||||
|
csFlip -= 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if u.Version() == 0 {
|
||||||
|
if u.Variant() == uuid.Microsoft {
|
||||||
|
cs += 10
|
||||||
|
} else {
|
||||||
|
cs -= 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if flipped.Version() == 0 {
|
||||||
|
if flipped.Variant() == uuid.Microsoft {
|
||||||
|
csFlip += 4
|
||||||
|
} else {
|
||||||
|
csFlip -= 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid RFC version and variant. IsRfc returns false for the Microsoft Variant and version == 0.
|
||||||
|
// Modern MS uses an RFC 4122 variant indicator but flips the endianness.
|
||||||
|
isRfc, _ = IsRfc(u)
|
||||||
|
flippedRfc, _ = IsRfc(flipped)
|
||||||
|
if u.Variant() == uuid.RFC4122 { // This might be the strongest indicator.
|
||||||
|
if isRfc && !flippedRfc {
|
||||||
|
// This is *very* strong of being an MS GUID.
|
||||||
|
cs -= 8
|
||||||
|
csFlip += 4
|
||||||
|
} else if !isRfc && flippedRfc {
|
||||||
|
// It probably is an MS GUID but was already flipped.
|
||||||
|
csFlip += 6
|
||||||
|
} else if isRfc && flippedRfc {
|
||||||
|
/*
|
||||||
|
If both are RFC-compat, it's a weird case where
|
||||||
|
it actually IS RFC compliant and by chance the flipped is *also* RFC compat.
|
||||||
|
An example of this is:
|
||||||
|
8d8e35ae-58d2-4d28-b09d-ffffffffffff
|
||||||
|
Which has the flipped version of:
|
||||||
|
ae358e8d-d258-284d-b09d-ffffffffffff
|
||||||
|
The original is a v4, the flipped evaluates as a v2!
|
||||||
|
|
||||||
|
Providing a target version breaks this away to a definitive score.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// *HEAVILY* weigh a provided version.
|
||||||
|
if tgtVer != 0 {
|
||||||
|
// NCS does some weird things to the versioning field. We return early on it though.
|
||||||
|
// MS GUIDs have a pretty small chance of matching,
|
||||||
|
// but their flipped counterpart SHOULD match versions.
|
||||||
|
if flipped.Version() == tgtVer {
|
||||||
|
cs += 7
|
||||||
|
} else {
|
||||||
|
cs -= 3
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Give a *very small* boost to flippedRfc and flipped.Version() == 4, since it's so common.
|
||||||
|
// Don't make this too high though since the version is explicitly specified as unknown.
|
||||||
|
if flippedRfc && flipped.Version() == 4 {
|
||||||
|
cs += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Equal returns `true` if [uuid.UUID] the two provided [uuid.UUID] are the same.
|
||||||
|
|
||||||
|
Currently it just wraps:
|
||||||
|
|
||||||
|
eq = a == b
|
||||||
|
|
||||||
|
but is provided as a safety guarantee if the underlying structures/types should change.
|
||||||
|
*/
|
||||||
|
func Equal(a, b uuid.UUID) (eq bool) {
|
||||||
|
|
||||||
|
eq = a == b
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
IsFlippedEndian can be used to check if [uuid.UUID] is a direct endian-flipped ([ToggleUuidMsGuid])
|
||||||
|
of b (or vice versa, obviously).
|
||||||
|
|
||||||
|
It simply wraps:
|
||||||
|
|
||||||
|
isFlipped = Equal(a, ToggleUuidMsGuid(b))
|
||||||
|
|
||||||
|
but can be useful for shorthand/readability.
|
||||||
|
*/
|
||||||
|
func IsFlippedEndian(a, b uuid.UUID) (isFlipped bool) {
|
||||||
|
|
||||||
|
isFlipped = Equal(a, ToggleUuidMsGuid(b))
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
IsMaxUUID returns `true` if the specified UUID is explicitly an RFC-defined
|
||||||
|
"Max UUID". (You may also see it specified in some places as the "Omni UUID".)
|
||||||
|
|
||||||
|
For details, see RFC 9562 [§ 5.10].
|
||||||
|
|
||||||
|
[§ 5.10]: https://datatracker.ietf.org/doc/html/rfc9562#section-5.10
|
||||||
|
*/
|
||||||
|
func IsMaxUUID(u uuid.UUID) (isMax bool) {
|
||||||
|
|
||||||
|
isMax = u == uuid.Max
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
IsMsGuid wraps
|
||||||
|
|
||||||
|
if cmp, _ = DetectMsGuid(msGUID, tgtVer); cmp < -3 {
|
||||||
|
isMs = true
|
||||||
|
}
|
||||||
|
|
||||||
|
Note that [uuid.Microsoft] is an actual RFC-defined variant, but *Microsoft no longer uses it*
|
||||||
|
and in MODERN implementations do the endianness flip [ToggleUuidMsGuid] of (USUALLY) a UUIDv4.
|
||||||
|
|
||||||
|
See [DetectMsGuid] for a more in-depth result that will let you use the confidence level directly,
|
||||||
|
and for details on the weird things that can go wrong with this guesswork.
|
||||||
|
|
||||||
|
Note that this won't be 100% reliable due to math things, but it should be reliable enough most of the time.
|
||||||
|
|
||||||
|
See also [MsGuidToUuid] and [UuidToMsGuid].
|
||||||
|
*/
|
||||||
|
func IsMsGuid(msGUID uuid.UUID, tgtVer uuid.Version) (isMs bool) {
|
||||||
|
|
||||||
|
var cmp int
|
||||||
|
|
||||||
|
if cmp, _ = DetectMsGuid(msGUID, tgtVer); cmp < -3 {
|
||||||
|
isMs = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
IsNcs is shorthand for:
|
||||||
|
|
||||||
|
isNcs = u.Variant() == uuid.Reserved
|
||||||
|
|
||||||
|
See also the notes in [IsRfc].
|
||||||
|
*/
|
||||||
|
func IsNcs(u uuid.UUID) (isNcs bool) {
|
||||||
|
|
||||||
|
// https://archive.org/details/networkcomputing0000zahn/page/10/mode/1up
|
||||||
|
|
||||||
|
isNcs = u.Variant() == uuid.Reserved
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
IsNilUUID returns `true` if the specified UUID is explicitly an RFC-defined
|
||||||
|
"Nil UUID".
|
||||||
|
|
||||||
|
For details, see RFC 9562 [§ 5.9].
|
||||||
|
|
||||||
|
[§ 5.9]: https://datatracker.ietf.org/doc/html/rfc9562#section-5.9
|
||||||
|
*/
|
||||||
|
func IsNilUUID(u uuid.UUID) (isNil bool) {
|
||||||
|
|
||||||
|
isNil = u == uuid.Nil
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
IsRfc returns `true` if the specified UUID is a proper standard RFC UUID.
|
||||||
|
|
||||||
|
Because Microsoft is insane, rfc will be false even if it's a (legacy) Microsoft form
|
||||||
|
of an RFC UUID. Use [IsMsGuid] for that.
|
||||||
|
|
||||||
|
In the special case of u being a valid NCS UUID, rfc will be false but gen will be [Rfc4122].
|
||||||
|
This is because RFC 9652 deprecates the NCS UUID. See [IsNcs].
|
||||||
|
(You are highly unlikely to encounter an NCS UUID "in the wild" unless you are receiving
|
||||||
|
a UUID from someone who severely misunderstands that UUIDs are structured/versioned/typed
|
||||||
|
and thinks they're just random byes in hex with hyphens in certain places.)
|
||||||
|
(They aren't that, if you're one of those someones.)
|
||||||
|
|
||||||
|
Nil UUID ([IsNilUUID]) and Max UUID ([IsMaxUUID]) return true with RFCs 4122 and RFC 9562 respectively.
|
||||||
|
*/
|
||||||
|
func IsRfc(u uuid.UUID) (rfc bool, gen RfcGen) {
|
||||||
|
|
||||||
|
if IsNilUUID(u) {
|
||||||
|
rfc = true
|
||||||
|
gen = Rfc4122
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if IsMaxUUID(u) {
|
||||||
|
rfc = true
|
||||||
|
gen = Rfc9562
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if IsNcs(u) {
|
||||||
|
gen = Rfc4122
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Are there any sub-version checks that can be applied?
|
||||||
|
switch u.Variant() {
|
||||||
|
case uuid.Invalid, uuid.Microsoft, uuid.Future:
|
||||||
|
return
|
||||||
|
case uuid.RFC4122:
|
||||||
|
if !(0x01 <= u.Version() && u.Version() <= 0x08) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rfc = true
|
||||||
|
gen = Rfc4122
|
||||||
|
// 4122 only covers UUIDv1 through UUIDv5.
|
||||||
|
if 0x06 <= u.Version() && u.Version() <= 0x08 {
|
||||||
|
gen = Rfc9562
|
||||||
|
}
|
||||||
|
default: // Safety net in case upstream adds a uuid.RFC9562 variant or something.
|
||||||
|
if !(0x01 <= u.Version() && u.Version() <= 0x08) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if u.Variant() < uuid.Future {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rfc = true
|
||||||
|
gen = RfcNone
|
||||||
|
// 4122 only covers UUIDv1 through UUIDv5.
|
||||||
|
if 0x06 <= u.Version() && u.Version() <= 0x08 {
|
||||||
|
gen = Rfc9562
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
IsValid indicates if the given [uuid.UUID] strictly conforms to RFC.
|
||||||
|
|
||||||
|
A Nil UUID (as in RFC 9562 [§ 5.9], not a `nil` *uuid.UUID) will return `true`
|
||||||
|
as it IS technically defined per RFC despite not conforming to a version.
|
||||||
|
Use [IsNilUUID] to further determine that.
|
||||||
|
|
||||||
|
Likewise, a Max UUID (RFC 9562 [§ 5.10]) will return `true` as it is also
|
||||||
|
defined per RFC despite not conforming to a version.
|
||||||
|
Use [IsMaxUUID] to further determine that.
|
||||||
|
|
||||||
|
Microsoft GUIDs will always return false since they defy RFC.
|
||||||
|
Use [IsMsGuid] to check for that condition.
|
||||||
|
|
||||||
|
[§ 5.9]: https://datatracker.ietf.org/doc/html/rfc9562#section-5.9
|
||||||
|
[§ 5.10]: https://datatracker.ietf.org/doc/html/rfc9562#section-5.10
|
||||||
|
*/
|
||||||
|
func IsValid(u uuid.UUID) (valid bool) {
|
||||||
|
|
||||||
|
if IsNilUUID(u) {
|
||||||
|
valid = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if IsMaxUUID(u) {
|
||||||
|
valid = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch u.Variant() {
|
||||||
|
case uuid.Invalid, uuid.Reserved, uuid.Microsoft, uuid.Future:
|
||||||
|
return
|
||||||
|
case uuid.RFC4122:
|
||||||
|
valid = true
|
||||||
|
// TODO: If they add an RFC9562 or something, need a case here.
|
||||||
|
default:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we got here, it *should* be RFC.
|
||||||
|
if valid, _ = IsRfc(u); !valid {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
MsGuidToUuid converts a Microsoft GUID to a UUID.
|
||||||
|
|
||||||
|
If [IsMsGuid] is false for msGUID, u will be equal to msGUID.
|
||||||
|
|
||||||
|
See [UuidToMsGuid] for the inverse, and [IsRfc] to check
|
||||||
|
if the result is a strictly conforming UUID.
|
||||||
|
*/
|
||||||
|
func MsGuidToUuid(msGUID uuid.UUID) (u uuid.UUID) {
|
||||||
|
|
||||||
|
if !IsMsGuid(msGUID, 0x00) {
|
||||||
|
u = msGUID
|
||||||
|
return
|
||||||
|
}
|
||||||
|
u = ToggleUuidMsGuid(msGUID)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
ToggleUuidMsGuid switches the src to it's "other" format:
|
||||||
|
|
||||||
|
* if it's a Microsoft GUID, it will be converted to a UUID
|
||||||
|
* if it's a UUID, it will be converted to a Microsoft GUID
|
||||||
|
|
||||||
|
No detection ([IsRfc], [IsMsGuid], etc.) nor validation/verification ([IsValid]) is performed,
|
||||||
|
which is why this is a "toggle" - it just flips some endianness for certain byte ranges.
|
||||||
|
|
||||||
|
If you prefer something a little more explicit, see [MsGuidToUuid] and/or [UuidToMsGuid].
|
||||||
|
Alternatively call [IsMsGuid] or [IsRfc] directly.
|
||||||
|
*/
|
||||||
|
func ToggleUuidMsGuid(orig uuid.UUID) (converted uuid.UUID) {
|
||||||
|
|
||||||
|
var cb [16]byte
|
||||||
|
var ob [16]byte = orig
|
||||||
|
|
||||||
|
// Can just directly map the allocations;
|
||||||
|
// the operation is the exact same regardless of whether the original is RFC and target is MS or vice versa.
|
||||||
|
cb = [16]byte{
|
||||||
|
// THESE GET ENDIAN-SWAPPED
|
||||||
|
ob[3], ob[2], ob[1], ob[0], // "A"
|
||||||
|
ob[5], ob[4], // "B"
|
||||||
|
ob[7], ob[6], // "C"
|
||||||
|
// THESE STAY THE SAME (should be BE for both)
|
||||||
|
ob[8], ob[9], ob[10], ob[11], // "D"
|
||||||
|
ob[12], ob[13], ob[14], ob[15], // "E"
|
||||||
|
}
|
||||||
|
|
||||||
|
converted = uuid.UUID(cb)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
UuidToMsGuid converts a UUID to a Microsoft GUID.
|
||||||
|
|
||||||
|
If [DetectMsGuid] indicates a good likelihood for u already being a Microsoft GUID
|
||||||
|
(greater than or equal) to [MsGuidThreshold], msGUID will be equal to u.
|
||||||
|
(If it detects it as unflipped endianness, it will automatically be flipped by this function.)
|
||||||
|
|
||||||
|
See [MsGuidToUuid] for the inverse.
|
||||||
|
*/
|
||||||
|
func UuidToMsGuid(u uuid.UUID) (msGUID uuid.UUID) {
|
||||||
|
|
||||||
|
var msCmp int
|
||||||
|
var flipped int
|
||||||
|
|
||||||
|
if msCmp, flipped = DetectMsGuid(u, 0x00); msCmp >= MsGuidThreshold && msCmp > flipped {
|
||||||
|
msGUID = u
|
||||||
|
return
|
||||||
|
}
|
||||||
|
msGUID = ToggleUuidMsGuid(u)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
22
uuidx/funcs_rfcgen.go
Normal file
22
uuidx/funcs_rfcgen.go
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package uuidx
|
||||||
|
|
||||||
|
// String conforms an [RfcGen] to a [fmt.Stringer] interface.
|
||||||
|
func (g *RfcGen) String() (s string) {
|
||||||
|
|
||||||
|
if g == nil {
|
||||||
|
s = "UNSPECIFIED_NIL"
|
||||||
|
}
|
||||||
|
|
||||||
|
switch *g {
|
||||||
|
case RfcNone:
|
||||||
|
s = "INVALID"
|
||||||
|
case Rfc4122:
|
||||||
|
s = "RFC 4122"
|
||||||
|
case Rfc9562:
|
||||||
|
s = "RFC 9562"
|
||||||
|
default:
|
||||||
|
s = "UNKNOWN"
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
5
uuidx/types.go
Normal file
5
uuidx/types.go
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package uuidx
|
||||||
|
|
||||||
|
type (
|
||||||
|
RfcGen uint8
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user