From 53ccf1715be6aadb1b009e412f7b552688a46a9a Mon Sep 17 00:00:00 2001 From: Daniel Ponte Date: Mon, 25 Nov 2024 09:44:12 -0500 Subject: [PATCH] Export --- internal/forms/multipart_test.go | 2 +- internal/forms/unmarshal.go | 35 ++++++++++++++++++--- internal/forms/unmarshal_test.go | 2 +- pkg/talkgroups/talkgroup.go | 52 ++++++++++++++++++++++++++++++++ pkg/talkgroups/xport/export.go | 5 +++ 5 files changed, 90 insertions(+), 6 deletions(-) diff --git a/internal/forms/multipart_test.go b/internal/forms/multipart_test.go index 3dbc546..abe74db 100644 --- a/internal/forms/multipart_test.go +++ b/internal/forms/multipart_test.go @@ -24,7 +24,7 @@ func makeExportRequest(ej *xport.ExportJob, url string) *http.Request { perr(body.WriteField("systemID", strconv.Itoa(int(ej.SystemID)))) - perr(body.WriteField("talkgroups", "3,4")) + perr(body.WriteField("talkgroups", "[197:3,4]")) w, err := body.CreateFormFile("template", ej.TemplateFileName) perr(err) diff --git a/internal/forms/unmarshal.go b/internal/forms/unmarshal.go index 2a68789..5e2dd11 100644 --- a/internal/forms/unmarshal.go +++ b/internal/forms/unmarshal.go @@ -1,6 +1,7 @@ package forms import ( + "encoding" "encoding/json" "fmt" "io" @@ -104,6 +105,7 @@ func (o *options) parseDuration(s string) (v time.Duration, set bool, err error) var typeOfByteSlice = reflect.TypeOf([]byte(nil)) func (o *options) unmIterFields(r *http.Request, destStruct reflect.Value) error { + textUnmarshaler := reflect.TypeFor[encoding.TextUnmarshaler]() structType := destStruct.Type() for i := 0; i < destStruct.NumField(); i++ { destFieldVal := destStruct.Field(i) @@ -175,6 +177,8 @@ func (o *options) unmIterFields(r *http.Request, destStruct reflect.Value) error ff := r.Form.Get(formField) + destFieldType := destFieldVal.Type() + switch v := destFieldIntf.(type) { case string, *string: setVal(destFieldVal, ff != "" || o.acceptBlank, ff) @@ -223,11 +227,34 @@ func (o *options) unmIterFields(r *http.Request, destStruct reflect.Value) error } destFieldVal.Set(reflect.ValueOf(ar)) default: - dvt := destFieldVal.Type() - if dvt.Kind() == reflect.Ptr { - dvt = dvt.Elem() + if destFieldType.Kind() == reflect.Slice && reflect.PointerTo(destFieldType.Elem()).Implements(textUnmarshaler) { + val := strings.Trim(ff, "[]") + if val == "" && o.acceptBlank { + continue + } + + vals := strings.Split(val, ",") + elemType := destFieldType.Elem() + sliceVal := reflect.MakeSlice(destFieldType, len(vals), len(vals)) + + for i, tElem := range vals { + newElem := reflect.New(elemType) + tum := newElem.Interface().(encoding.TextUnmarshaler) + err := tum.UnmarshalText([]byte(tElem)) + if err != nil { + return err + } + sliceVal.Index(i).Set(newElem.Elem()) + } + + destFieldVal.Set(sliceVal) + + continue } - if reflect.ValueOf(ff).CanConvert(dvt) { + if destFieldType.Kind() == reflect.Ptr { + destFieldType = destFieldType.Elem() + } + if reflect.ValueOf(ff).CanConvert(destFieldType) { setVal(destFieldVal, ff != "" || o.acceptBlank, ff) } else { panic(fmt.Errorf("unsupported type %T", v)) diff --git a/internal/forms/unmarshal_test.go b/internal/forms/unmarshal_test.go index bdc32e7..b664cb8 100644 --- a/internal/forms/unmarshal_test.go +++ b/internal/forms/unmarshal_test.go @@ -137,7 +137,7 @@ var ( TalkgroupFilter: filter.TalkgroupFilter{ Talkgroups: []talkgroups.ID{ talkgroups.TG(197, 3), - talkgroups.TG(197, 4), + talkgroups.TG(0, 4), }, }, } diff --git a/pkg/talkgroups/talkgroup.go b/pkg/talkgroups/talkgroup.go index 4301a79..2a4e7c1 100644 --- a/pkg/talkgroups/talkgroup.go +++ b/pkg/talkgroups/talkgroup.go @@ -1,8 +1,12 @@ package talkgroups import ( + "encoding" + "encoding/json" + "errors" "fmt" "strconv" + "strings" "dynatron.me/x/stillbox/pkg/database" ) @@ -32,6 +36,54 @@ type ID struct { Talkgroup uint32 `json:"tg"` } +var _ encoding.TextUnmarshaler = (*ID)(nil) + +var ErrBadTG = errors.New("bad talkgroup format") + +func (tid *ID) UnmarshalJSON(j []byte) error { + // this is a dirty hack since we cannot implement both TextUnmarshaler + // and json.Unmarshaler at the same time. sigh. + v := &struct{ + System uint32 `json:"sys"` + Talkgroup uint32 `json:"tg"` + }{} + + if tid != nil { + v.System, v.Talkgroup = tid.System, tid.Talkgroup + } + + err := json.Unmarshal(j, v) + if err != nil { + return err + } + + tid.System, tid.Talkgroup = v.System, v.Talkgroup + return nil +} + +func (tid *ID) UnmarshalText(txt []byte) error { + ar := strings.Split(string(txt), ":") + switch len(ar) { + case 2: + sys, err := strconv.Atoi(ar[0]) + if err != nil { + return err + } + tid.System = uint32(sys) + fallthrough + case 1: + tg, err := strconv.Atoi(ar[len(ar)-1]) + if err != nil { + return err + } + tid.Talkgroup = uint32(tg) + default: + return ErrBadTG + } + + return nil +} + type IDs []ID func (t IDs) Tuples() database.TGTuples { diff --git a/pkg/talkgroups/xport/export.go b/pkg/talkgroups/xport/export.go index 8ca136a..330e256 100644 --- a/pkg/talkgroups/xport/export.go +++ b/pkg/talkgroups/xport/export.go @@ -34,6 +34,11 @@ func (ej *ExportJob) Export(ctx context.Context, w io.Writer) error { return err } } else { + for i, v := range ej.TalkgroupFilter.Talkgroups { + if v.System == 0 { + ej.TalkgroupFilter.Talkgroups[i].System = uint32(ej.SystemID) + } + } ids, err := ej.TalkgroupFilter.TGs(ctx) if err != nil { return err