add template functions

This commit is contained in:
2026-06-03 00:23:08 +06:30
parent d7ffc17d71
commit d8781c3981
6 changed files with 146 additions and 10 deletions

1
.gitignore vendored
View File

@@ -29,3 +29,4 @@ build
assets assets
libgofunc libgofunc
libgo libgo
cmd/out.png

View File

@@ -1,9 +1,9 @@
#!/bin/bash #!/bin/bash
APP_NAME="libgofunc" APP_NAME="libgofunc"
VERSION="${1:-v0.1.3}" VERSION="${1:-v0.1.5}"
OUTPUT_DIR="assets" OUTPUT_DIR="../assets"
BUILD_DIR="build" BUILD_DIR="../build"
# need Android NDK # need Android NDK
NDK_HOME="$HOME/Android/Sdk/ndk/28.2.13676358" # <--- CHECK YOUR VERSION NDK_HOME="$HOME/Android/Sdk/ndk/28.2.13676358" # <--- CHECK YOUR VERSION
@@ -11,7 +11,7 @@ API=21
TOOLCHAIN="$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin" TOOLCHAIN="$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin"
OS="$(uname -s)" OS="$(uname -s)"
cd cmd
if [ "$OS" = "Darwin" ]; then if [ "$OS" = "Darwin" ]; then
export IOS_SDK=$(xcrun --sdk iphoneos --show-sdk-path) export IOS_SDK=$(xcrun --sdk iphoneos --show-sdk-path)
export IOS_SIM_SDK=$(xcrun --sdk iphonesimulator --show-sdk-path) export IOS_SIM_SDK=$(xcrun --sdk iphonesimulator --show-sdk-path)
@@ -27,7 +27,7 @@ if [ "$OS" = "Darwin" ]; then
CC="$(xcrun --sdk iphonesimulator --find clang)" \ CC="$(xcrun --sdk iphonesimulator --find clang)" \
CGO_CFLAGS="-isysroot $IOS_SIM_SDK -arch x86_64" \ CGO_CFLAGS="-isysroot $IOS_SIM_SDK -arch x86_64" \
CGO_LDFLAGS="-isysroot $IOS_SIM_SDK -arch x86_64" \ CGO_LDFLAGS="-isysroot $IOS_SIM_SDK -arch x86_64" \
go build -buildmode=c-archive -o build/ios/sim/libgofunc_arm64_sim.a . go build ./cmd -buildmode=c-archive -o build/ios/sim/libgofunc_arm64_sim.a .
# xcodebuild -create-xcframework \ # xcodebuild -create-xcframework \
# -library build/ios/device/libgofunc_arm64.a -headers build/ios/device/ \ # -library build/ios/device/libgofunc_arm64.a -headers build/ios/device/ \
@@ -74,7 +74,7 @@ elif [ "$OS" = "Linux" ]; then
tar -czf "${OUTPUT_DIR}/${VERSION}/${ARCHIVE_NAME}" -C "${BUILD_DIR}" ./${VERSION}/${ARCH} tar -czf "${OUTPUT_DIR}/${VERSION}/${ARCHIVE_NAME}" -C "${BUILD_DIR}" ./${VERSION}/${ARCH}
export HTTPS_PROXY="socks5://localhost:8080" export HTTPS_PROXY="socks5://localhost:8080"
rclone copy ./assets/${VERSION} s3:mokkon/libs/libgofunc/${VERSION} rclone copy ../assets/${VERSION} s3:mokkon/libs/libgofunc/${VERSION}
else else
echo "Unsupported OS: $OS" echo "Unsupported OS: $OS"
exit 1 exit 1

2
go.sum
View File

@@ -4,8 +4,6 @@ github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8=
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/google/gousb v1.1.3 h1:xt6M5TDsGSZ+rlomz5Si5Hmd/Fvbmo2YCJHN+yGaK4o=
github.com/google/gousb v1.1.3/go.mod h1:GGWUkK0gAXDzxhwrzetW592aOmkkqSGcj5KLEgmCVUg=
github.com/kenshaw/escpos v0.0.0-20221114190919-df06b682a8fc h1:4JwmN2Scz1vR+hfSxkdy2IE/DzxX2Cftm2lhWHyN0k0= github.com/kenshaw/escpos v0.0.0-20221114190919-df06b682a8fc h1:4JwmN2Scz1vR+hfSxkdy2IE/DzxX2Cftm2lhWHyN0k0=
github.com/kenshaw/escpos v0.0.0-20221114190919-df06b682a8fc/go.mod h1:M+GIBmg2MqaSWIJrXCZS+/wRFbr9fOguRz3SHn8DRPE= github.com/kenshaw/escpos v0.0.0-20221114190919-df06b682a8fc/go.mod h1:M+GIBmg2MqaSWIJrXCZS+/wRFbr9fOguRz3SHn8DRPE=
golang.org/x/image v0.38.0 h1:5l+q+Y9JDC7mBOMjo4/aPhMDcxEptsX+Tt3GgRQRPuE= golang.org/x/image v0.38.0 h1:5l+q+Y9JDC7mBOMjo4/aPhMDcxEptsX+Tt3GgRQRPuE=

78
img.go
View File

@@ -24,7 +24,13 @@ import (
"github.com/kenshaw/escpos" "github.com/kenshaw/escpos"
) )
import "log" import (
"log"
"strconv"
"golang.org/x/text/language"
"golang.org/x/text/message"
)
const ( const (
defalutFontSize = 18.0 defalutFontSize = 18.0
@@ -102,8 +108,13 @@ func GenImg(width int, outputPath, payload, tmpl string) string {
return r return r
} }
var funcMap = template.FuncMap{
"formatNumber": FormatNumber,
"div": Div,
}
func renderTemplate(tmp string, data map[string]interface{}) (string, error) { func renderTemplate(tmp string, data map[string]interface{}) (string, error) {
tmpl, err := template.New("mytemplate").Parse(tmp) tmpl, err := template.New("mytemplate").Funcs(funcMap).Parse(tmp)
if err != nil { if err != nil {
return "", err return "", err
} }
@@ -431,3 +442,66 @@ func nodeHeight(n *html.Node, dc *gg.Context, canvasWidth int, xPadding, yPaddin
} }
return y return y
} }
type RealNumber interface {
~int | ~int8 | ~int16 | ~int32 | ~int64 |
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 |
~float32 | ~float64
}
func FormatNumber(precision int, b any) string {
return GenericFormatNumber(precision, convertToFloat64(b))
}
func GenericFormatNumber[T RealNumber](precision int, v T) string {
pEnglish := message.NewPrinter(language.English)
n := float64(v)
s := fmt.Sprintf("%.*f", precision, n)
parts := strings.Split(s, ".")
intPart := parts[0]
i, err := strconv.Atoi(intPart)
if err != nil {
return ""
}
out := pEnglish.Sprintf("%d", i)
if len(parts) > 1 {
return out + "." + parts[1]
}
return out
}
func Div(a, b any) float64 {
return GenericDiv(convertToFloat64(a), convertToFloat64(b))
}
func GenericDiv[T RealNumber](a, b T) float64 {
floatB := float64(b)
if floatB == 0 {
return 0
}
return float64(a) / floatB
}
func convertToFloat64(v any) float64 {
switch t := v.(type) {
case float64:
return t
case float32:
return float64(t)
case int:
return float64(t)
case int64:
return float64(t)
case int32:
return float64(t)
case uint:
return float64(t)
case uint64:
return float64(t)
default:
return 0
}
}

60
img_test.go Normal file
View File

@@ -0,0 +1,60 @@
package libgofunc
import (
"math"
"testing"
)
func TestDiv(t *testing.T) {
tests := []struct {
name string
a any
b any
want float64
}{
{"Float division", 10.5, 2.0, 5.25},
{"Float division", 4975, 1000, 4.975},
{"Integer division", 10, 4, 2.5},
{"Mixed types", int64(100), float64(4.0), 25.0},
{"Division by zero (float)", 10.0, 0.0, 0.0},
{"Division by zero (int)", 5, 0, 0.0},
{"Unsupported type defaults to zero", "string", 2, 0.0},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := Div(tt.a, tt.b)
if got != tt.want {
t.Errorf("TemplateDiv() = %v, want %v", got, tt.want)
}
})
}
}
func TestFormatNumber(t *testing.T) {
tests := []struct {
name string
precision int
val any
want string
}{
{"Standard float with commas", 2, 1234567.891, "1,234,567.89"},
{"Standard float with commas", 3, 4.975, "4.975"},
{"Integer input with commas", 0, 5000000, "5,000,000"},
{"Integer input forced decimals", 2, 5000, "5,000.00"},
{"Negative float commas", 2, -9876543.21, "-9,876,543.21"},
{"Small float rounding up", 2, 0.128, "0.13"},
{"Small float rounding down", 2, 0.123, "0.12"},
{"Handling NaN", 2, math.NaN(), ""},
{"Handling Inf", 2, math.Inf(1), ""},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := FormatNumber(tt.precision, tt.val)
if got != tt.want {
t.Errorf("TemplateFloat() = %q, want %q", got, tt.want)
}
})
}
}

View File

@@ -17,6 +17,7 @@ import (
"github.com/kenshaw/escpos" "github.com/kenshaw/escpos"
) )
import "errors"
//export PrintImg //export PrintImg
func PrintImg(printer *C.char, imagePath *C.char) *C.char { func PrintImg(printer *C.char, imagePath *C.char) *C.char {
@@ -77,6 +78,8 @@ func PrintImg(printer *C.char, imagePath *C.char) *C.char {
// reader := bufio.NewReader(rw) // reader := bufio.NewReader(rw)
// writer := bufio.NewWriter(rw) // writer := bufio.NewWriter(rw)
// w = bufio.NewReadWriter(reader, writer) // w = bufio.NewReadWriter(reader, writer)
} else {
return NewErr(errors.New("invalid printer"))
} }
prt := escpos.New(w) prt := escpos.New(w)