From 78377786177717aeadfff8a1177a4d2dcf909435 Mon Sep 17 00:00:00 2001 From: loveuer Date: Fri, 29 Mar 2024 18:34:10 +0800 Subject: [PATCH] :tada: start the project --- internal/model/enum.go | 5 ++ internal/model/task.go | 37 +++++++++++ internal/sqlType/err.go | 9 +++ internal/sqlType/jsonb.go | 76 ++++++++++++++++++++++ internal/sqlType/nullStr.go | 42 ++++++++++++ internal/sqlType/set.go | 53 ++++++++++++++++ internal/sqlType/strSlice.go | 109 ++++++++++++++++++++++++++++++++ internal/sqlType/uint64Slice.go | 71 +++++++++++++++++++++ 8 files changed, 402 insertions(+) create mode 100644 internal/model/enum.go create mode 100644 internal/model/task.go create mode 100644 internal/sqlType/err.go create mode 100644 internal/sqlType/jsonb.go create mode 100644 internal/sqlType/nullStr.go create mode 100644 internal/sqlType/set.go create mode 100644 internal/sqlType/strSlice.go create mode 100644 internal/sqlType/uint64Slice.go diff --git a/internal/model/enum.go b/internal/model/enum.go new file mode 100644 index 0000000..0bef999 --- /dev/null +++ b/internal/model/enum.go @@ -0,0 +1,5 @@ +package model + +type InputType int64 + +type OutputType int64 diff --git a/internal/model/task.go b/internal/model/task.go new file mode 100644 index 0000000..b9685c6 --- /dev/null +++ b/internal/model/task.go @@ -0,0 +1,37 @@ +package model + +import "github.com/loveuer/nfflow/internal/sqlType" + +type Task struct { + Id uint64 `json:"id" gorm:"primaryKey;column:id"` + CreatedAt int64 `json:"created_at" gorm:"column:created_at;autoCreateTime:milli"` + UpdatedAt int64 `json:"updated_at" gorm:"column:updated_at;autoUpdateTime:milli"` + DeletedAt int64 `json:"deleted_at" gorm:"index;column:deleted_at;default:0"` + + TaskName string +} + +type Input struct { + Id uint64 `json:"id" gorm:"primaryKey;column:id"` + CreatedAt int64 `json:"created_at" gorm:"column:created_at;autoCreateTime:milli"` + UpdatedAt int64 `json:"updated_at" gorm:"column:updated_at;autoUpdateTime:milli"` + DeletedAt int64 `json:"deleted_at" gorm:"index;column:deleted_at;default:0"` + InputType InputType + InputConfig sqlType.JSONB +} + +type Output struct { + Id uint64 `json:"id" gorm:"primaryKey;column:id"` + CreatedAt int64 `json:"created_at" gorm:"column:created_at;autoCreateTime:milli"` + UpdatedAt int64 `json:"updated_at" gorm:"column:updated_at;autoUpdateTime:milli"` + DeletedAt int64 `json:"deleted_at" gorm:"index;column:deleted_at;default:0"` + OutputType OutputType + OutputConfig sqlType.JSONB +} + +type Pipe struct { + Id uint64 `json:"id" gorm:"primaryKey;column:id"` + CreatedAt int64 `json:"created_at" gorm:"column:created_at;autoCreateTime:milli"` + UpdatedAt int64 `json:"updated_at" gorm:"column:updated_at;autoUpdateTime:milli"` + DeletedAt int64 `json:"deleted_at" gorm:"index;column:deleted_at;default:0"` +} diff --git a/internal/sqlType/err.go b/internal/sqlType/err.go new file mode 100644 index 0000000..d1195e0 --- /dev/null +++ b/internal/sqlType/err.go @@ -0,0 +1,9 @@ +package sqlType + +import "errors" + +var ( + ErrConvertScanVal = errors.New("convert scan val to str err") + ErrInvalidScanVal = errors.New("scan val invalid") + ErrConvertVal = errors.New("convert err") +) diff --git a/internal/sqlType/jsonb.go b/internal/sqlType/jsonb.go new file mode 100644 index 0000000..ace1417 --- /dev/null +++ b/internal/sqlType/jsonb.go @@ -0,0 +1,76 @@ +package sqlType + +import ( + "database/sql/driver" + "encoding/json" + + "github.com/jackc/pgtype" +) + +type JSONB struct { + Val pgtype.JSONB + Valid bool +} + +func NewJSONB(v interface{}) JSONB { + j := new(JSONB) + j.Val = pgtype.JSONB{} + if err := j.Val.Set(v); err == nil { + j.Valid = true + return *j + } + + return *j +} + +func (j *JSONB) Set(value interface{}) error { + if err := j.Val.Set(value); err != nil { + j.Valid = false + return err + } + + j.Valid = true + + return nil +} + +func (j *JSONB) Bind(model interface{}) error { + return j.Val.AssignTo(model) +} + +func (j *JSONB) Scan(value interface{}) error { + j.Val = pgtype.JSONB{} + if value == nil { + j.Valid = false + return nil + } + + j.Valid = true + + return j.Val.Scan(value) +} + +func (j JSONB) Value() (driver.Value, error) { + if j.Valid { + return j.Val.Value() + } + + return nil, nil +} + +func (j JSONB) MarshalJSON() ([]byte, error) { + if j.Valid { + return j.Val.MarshalJSON() + } + + return json.Marshal(nil) +} + +func (j *JSONB) UnmarshalJSON(b []byte) error { + if string(b) == "null" { + j.Valid = false + return j.Val.UnmarshalJSON(b) + } + + return j.Val.UnmarshalJSON(b) +} diff --git a/internal/sqlType/nullStr.go b/internal/sqlType/nullStr.go new file mode 100644 index 0000000..a7df3cf --- /dev/null +++ b/internal/sqlType/nullStr.go @@ -0,0 +1,42 @@ +package sqlType + +import ( + "database/sql" + "encoding/json" +) + +// type NullString struct { +// sql.NullString +// } + +type NullString struct{ sql.NullString } + +func NewNullString(val string) NullString { + if val == "" { + return NullString{} + } + + return NullString{sql.NullString{Valid: true, String: val}} +} + +func (ns NullString) MarshalJSON() ([]byte, error) { + if !ns.Valid { + return json.Marshal(nil) + } + + return json.Marshal(ns.String) +} + +func (ns *NullString) UnmarshalJSON(data []byte) error { + if string(data) == "null" { + ns.Valid = false + return nil + } + + if err := json.Unmarshal(data, &ns.String); err != nil { + ns.Valid = true + return err + } + + return nil +} diff --git a/internal/sqlType/set.go b/internal/sqlType/set.go new file mode 100644 index 0000000..03812b0 --- /dev/null +++ b/internal/sqlType/set.go @@ -0,0 +1,53 @@ +package sqlType + +import "encoding/json" + +type Set map[string]struct{} + +func (s Set) MarshalJSON() ([]byte, error) { + array := make([]string, 0) + for name := range s { + array = append(array, name) + } + + return json.Marshal(array) +} + +func (s *Set) UnmarshalJSON(b []byte) error { + array := make([]string, 0) + if err := json.Unmarshal(b, &array); err != nil { + return err + } + + set := make(map[string]struct{}) + + for _, name := range array { + set[name] = struct{}{} + } + + *s = set + return nil +} + +func (s Set) ToStringSlice() []string { + var ( + result = make([]string, 0, len(s)) + ) + + for key := range s { + result = append(result, key) + } + + return result +} + +func (s *Set) FromStringSlice(ss *[]string) { + if s == nil { + m := make(Set) + s = &m + } + + for idx := range *(ss) { + (*s)[(*ss)[idx]] = struct{}{} + } +} diff --git a/internal/sqlType/strSlice.go b/internal/sqlType/strSlice.go new file mode 100644 index 0000000..05d7875 --- /dev/null +++ b/internal/sqlType/strSlice.go @@ -0,0 +1,109 @@ +package sqlType + +import ( + "bytes" + "database/sql/driver" + "encoding/json" +) + +type StrSlice []string + +func (s *StrSlice) Scan(val interface{}) error { + + str, ok := val.(string) + if !ok { + return ErrConvertScanVal + } + + if len(str) < 2 { + return nil + } + + bs := make([]byte, 0, 128) + bss := make([]byte, 0, 2*len(str)) + + quoteCount := 0 + + for idx := 1; idx < len(str)-1; idx++ { + // 44: , 92: \ 34: " + quote := str[idx] + switch quote { + case 44: + if quote == 44 && str[idx-1] != 92 && quoteCount == 0 { + if len(bs) > 0 { + if !(bs[0] == 34 && bs[len(bs)-1] == 34) { + bs = append([]byte{34}, bs...) + bs = append(bs, 34) + } + + bss = append(bss, bs...) + bss = append(bss, 44) + } + bs = bs[:0] + } else { + bs = append(bs, quote) + } + case 34: + if str[idx-1] != 92 { + quoteCount = (quoteCount + 1) % 2 + } + bs = append(bs, quote) + default: + bs = append(bs, quote) + } + + //bs = append(bs, str[idx]) + } + + if len(bs) > 0 { + if !(bs[0] == 34 && bs[len(bs)-1] == 34) { + bs = append([]byte{34}, bs...) + bs = append(bs, 34) + } + + bss = append(bss, bs...) + } else { + if len(bss) > 2 { + bss = bss[:len(bss)-2] + } + } + + bss = append([]byte{'['}, append(bss, ']')...) + + if err := json.Unmarshal(bss, s); err != nil { + return err + } + + return nil +} + +func (s StrSlice) Value() (driver.Value, error) { + if s == nil { + return "{}", nil + } + + if len(s) == 0 { + return "{}", nil + } + + buf := &bytes.Buffer{} + + encoder := json.NewEncoder(buf) + encoder.SetEscapeHTML(false) + + if err := encoder.Encode(s); err != nil { + return "{}", err + } + + bs := buf.Bytes() + + bs[0] = '{' + + if bs[len(bs)-1] == 10 { + bs = bs[:len(bs)-1] + } + + bs[len(bs)-1] = '}' + + return string(bs), nil +} diff --git a/internal/sqlType/uint64Slice.go b/internal/sqlType/uint64Slice.go new file mode 100644 index 0000000..7bba191 --- /dev/null +++ b/internal/sqlType/uint64Slice.go @@ -0,0 +1,71 @@ +package sqlType + +import ( + "database/sql/driver" + "fmt" + "strconv" + "strings" + + "github.com/spf13/cast" +) + +type NumSlice[T ~int | ~int64 | ~uint | ~uint64] []T + +func (n *NumSlice[T]) Scan(val interface{}) error { + str, ok := val.(string) + if !ok { + return ErrConvertScanVal + } + + length := len(str) + + if length <= 0 { + *n = make(NumSlice[T], 0) + return nil + } + + if str[0] != '{' || str[length-1] != '}' { + return ErrInvalidScanVal + } + + str = str[1 : length-1] + if len(str) == 0 { + *n = make(NumSlice[T], 0) + return nil + } + + numStrs := strings.Split(str, ",") + nums := make([]T, len(numStrs)) + + for idx := range numStrs { + num, err := cast.ToInt64E(strings.TrimSpace(numStrs[idx])) + if err != nil { + return fmt.Errorf("%w: can't convert to %T", ErrConvertVal, T(0)) + } + + nums[idx] = T(num) + } + + *n = nums + + return nil +} + +func (n NumSlice[T]) Value() (driver.Value, error) { + if n == nil { + return "{}", nil + } + + if len(n) == 0 { + return "{}", nil + } + + ss := make([]string, 0, len(n)) + for idx := range n { + ss = append(ss, strconv.Itoa(int(n[idx]))) + } + + s := strings.Join(ss, ", ") + + return fmt.Sprintf("{%s}", s), nil +}