commit d9f37c584c6c615ac1f6f0c0c45de61b275516cd
parent 952d580cc7f3df1b9affea829ddfef91120d1ef5
Author: Jack Mordaunt <jackmordaunt.dev@gmail.com>
Date: Sun, 20 Sep 2026 15:54:27 -0300
wasm: expose validation to the page
The page can already convert without uploading; telling someone their icon
is broken is the same argument, and it is what the conversion is worth
trusting for.
Diffstat:
1 file changed, 51 insertions(+), 0 deletions(-)
diff --git a/cmd/wasm/main.go b/cmd/wasm/main.go
@@ -23,6 +23,7 @@ func main() {
api := js.Global().Get("Object").New()
api.Set("convert", js.FuncOf(convert))
api.Set("inspect", js.FuncOf(inspect))
+ api.Set("validate", js.FuncOf(validate))
js.Global().Set("icns", api)
// The page calls into this, so the program has to stay resident.
select {}
@@ -96,6 +97,56 @@ func inspect(_ js.Value, args []js.Value) (out any) {
return result
}
+// validate reports what the platform that owns the format will make of an
+// icon file, as an array of findings ordered by severity.
+func validate(_ js.Value, args []js.Value) (out any) {
+ defer func() {
+ if r := recover(); r != nil {
+ out = failure(fmt.Errorf("validating: %v", r))
+ }
+ }()
+ if len(args) < 1 {
+ return failure(errors.New("validate wants an icon file"))
+ }
+ type finding struct{ severity, icon, message string }
+ var (
+ src = toGo(args[0])
+ findings []finding
+ )
+ switch {
+ case bytes.HasPrefix(src, []byte("icns")):
+ problems, err := icns.Validate(bytes.NewReader(src))
+ if err != nil {
+ return failure(err)
+ }
+ for _, p := range problems {
+ findings = append(findings, finding{p.Severity.String(), p.Icon, p.Message})
+ }
+ case bytes.HasPrefix(src, []byte{0x00, 0x00, 0x01, 0x00}):
+ problems, err := ico.Validate(bytes.NewReader(src))
+ if err != nil {
+ return failure(err)
+ }
+ for _, p := range problems {
+ findings = append(findings, finding{p.Severity.String(), p.Icon, p.Message})
+ }
+ default:
+ return failure(errors.New("not an icns or ico file"))
+ }
+ list := js.Global().Get("Array").New(len(findings))
+ for i, f := range findings {
+ item := js.Global().Get("Object").New()
+ item.Set("severity", f.severity)
+ item.Set("icon", f.icon)
+ item.Set("message", f.message)
+ list.SetIndex(i, item)
+ }
+ result := js.Global().Get("Object").New()
+ result.Set("ok", true)
+ result.Set("problems", list)
+ return result
+}
+
// toGo copies a Uint8Array into Go memory, which the wasm boundary requires.
func toGo(v js.Value) []byte {
out := make([]byte, v.Get("length").Int())