Skip to content

Commit

Permalink
Merge pull request #522 from essentialkaos/develop
Browse files Browse the repository at this point in the history
Version 13.13.0
  • Loading branch information
andyone authored Nov 26, 2024
2 parents 34d5214 + 39102ca commit c7cd09c
Show file tree
Hide file tree
Showing 22 changed files with 378 additions and 220 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
## Changelog

### [13.13.0](https://kaos.sh/ek/13.13.0)

* `[req]` Added support for different types of slices to `Query`
* `[req]` Added support for `fmt.Stringer` interface to `Query`
* `[req]` Added interface for custom struct encoding for `Query`
* `[req]` Improved `Query`encoding

### [13.12.0](https://kaos.sh/ek/13.12.0)

* `[req]` Added custom timeout per request
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ Currently we support Linux and macOS (_except some packages_). All packages have

### Projects with `EK`

_It's hard to show all the possibilities of all the subpackages. But you can take a look at these open source projects that use almost all the features of this package in real-world tasks._

- [aligo](https://kaos.sh/aligo) — Utility for checking and viewing Golang struct alignment info
- [artefactor](https://kaos.sh/artefactor) — Utility for downloading artefacts from GitHub
- [atlassian-cloud-backuper](https://kaos.sh/atlassian-cloud-backuper) — Tool for backuping Atlassian cloud services (_Jira and Confluence_)
Expand Down
11 changes: 0 additions & 11 deletions cron/cron.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,6 @@ var info = []exprInfo{

// ////////////////////////////////////////////////////////////////////////////////// //

// codebeat:disable[LOC,ABC]

// Parse parse cron expression
// https://en.wikipedia.org/wiki/Cron
func Parse(expr string) (*Expr, error) {
Expand Down Expand Up @@ -142,12 +140,8 @@ func Parse(expr string) (*Expr, error) {
return result, nil
}

// codebeat:enable[LOC,ABC]

// ////////////////////////////////////////////////////////////////////////////////// //

// codebeat:disable[LOC]

// IsDue check if current moment is match for expression
func (e *Expr) IsDue(args ...time.Time) bool {
if e == nil {
Expand Down Expand Up @@ -185,9 +179,6 @@ func (e *Expr) IsDue(args ...time.Time) bool {
return true
}

// I don't have an idea how we can implement this without this conditions
// codebeat:disable[BLOCK_NESTING,LOC,CYCLO]

// Next get time of next matched moment
func (e *Expr) Next(args ...time.Time) time.Time {
if e == nil {
Expand Down Expand Up @@ -308,8 +299,6 @@ func (e *Expr) Prev(args ...time.Time) time.Time {
return time.Unix(0, 0)
}

// codebeat:enable[BLOCK_NESTING,LOC,CYCLO]

// String return raw expression
func (e *Expr) String() string {
if e == nil {
Expand Down
4 changes: 0 additions & 4 deletions fmtc/fmtc.go
Original file line number Diff line number Diff line change
Expand Up @@ -381,8 +381,6 @@ func IsTag(tag string) bool {

// ////////////////////////////////////////////////////////////////////////////////// //

// codebeat:disable[LOC,BLOCK_NESTING]

func tag2ANSI(tag string, clean bool) string {
switch {
case clean:
Expand Down Expand Up @@ -449,8 +447,6 @@ func tag2ANSI(tag string, clean bool) string {
return fmt.Sprintf("\033[" + chars[:len(chars)-1] + "m")
}

// codebeat:enable[LOC,BLOCK_NESTING]

func parseExtendedColor(tag string) string {
if len(tag) == 7 {
hex := strings.TrimLeft(tag, "#%")
Expand Down
5 changes: 0 additions & 5 deletions fsutil/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -266,9 +266,6 @@ func readDirRecFiles(path, base string, ignoreHidden bool, filter ListingFilter)
return result
}

// It's ok to have long function with many conditions to filter some entities
// codebeat:disable[LOC,ABC,CYCLO]

func isMatch(name, fullPath string, filter ListingFilter) bool {
var (
hasNotMatchPatterns = filter.hasNotMatchPatterns()
Expand Down Expand Up @@ -379,8 +376,6 @@ func isMatch(name, fullPath string, filter ListingFilter) bool {
return match
}

// codebeat:enable[LOC,ABC,CYCLO]

func filterList(names []string, dir string, filter ListingFilter) []string {
var filteredNames []string

Expand Down
4 changes: 4 additions & 0 deletions mathutil/mathutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ type Integer interface {
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64
}

type Float interface {
~float32 | ~float64
}

type Numeric interface {
~int | ~int8 | ~int16 | ~int32 | ~int64 |
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
Expand Down
7 changes: 0 additions & 7 deletions options/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -673,11 +673,6 @@ func (o optionName) String() string {

// ////////////////////////////////////////////////////////////////////////////////// //

// I think it is okay to have such a long and complicated method for parsing data
// because it has a lot of logic which can't be separated into different methods
// without losing code readability
// codebeat:disable[LOC,BLOCK_NESTING,CYCLO]

func (o *Options) parseOptions(rawOpts []string) (Arguments, errors.Errors) {
o.prepare()

Expand Down Expand Up @@ -784,8 +779,6 @@ func (o *Options) parseOptions(rawOpts []string) (Arguments, errors.Errors) {
return arguments, errs
}

// codebeat:enable[LOC,BLOCK_NESTING,CYCLO]

func (o *Options) parseLongOption(opt string) (string, string, error) {
if strings.Contains(opt, "=") {
optName, optValue, ok := strings.Cut(opt, "=")
Expand Down
242 changes: 242 additions & 0 deletions req/query.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
package req

// ////////////////////////////////////////////////////////////////////////////////// //
// //
// Copyright (c) 2024 ESSENTIAL KAOS //
// Apache License, Version 2.0 <https://www.apache.org/licenses/LICENSE-2.0> //
// //
// ////////////////////////////////////////////////////////////////////////////////// //

import (
"bytes"
"fmt"
"net/url"
"strings"

"github.com/essentialkaos/ek/v13/mathutil"
)

// ////////////////////////////////////////////////////////////////////////////////// //

// Query is a map[string]any used for query
type Query map[string]any

// ////////////////////////////////////////////////////////////////////////////////// //

// QueryPayload is an interface for query payload with custom encoder
type QueryPayload interface {
// ToQuery encodes payload for using in query string
ToQuery(name string) string
}

// ////////////////////////////////////////////////////////////////////////////////// //

// Encode converts query struct to URL-encoded string
func (q Query) Encode() string {
var buf bytes.Buffer

for k, v := range q {
if k == "" {
continue
}

buf.WriteString(url.QueryEscape(k))

switch u := v.(type) {
case string:
if u != "" {
buf.WriteRune('=')
buf.WriteString(queryFormatString(u))
}

case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
buf.WriteRune('=')
buf.WriteString(queryFormatNumber(v))

case float32, float64:
buf.WriteRune('=')
buf.WriteString(queryFormatFloat(v))

case nil:
// noop

case fmt.Stringer:
vv := url.QueryEscape(u.String())

if vv != "" {
buf.WriteRune('=')
buf.WriteString(vv)
}

case QueryPayload:
vv := url.QueryEscape(u.ToQuery(k))

if vv != "" {
buf.WriteRune('=')
buf.WriteString(vv)
}

case []string:
if len(u) > 0 {
buf.WriteRune('=')
queryFormatStringSlice(&buf, u)
}

case []fmt.Stringer:
if len(u) > 0 {
buf.WriteRune('=')
queryFormatStringerSlice(&buf, u)
}

case []int:
if len(u) > 0 {
buf.WriteRune('=')
queryFormatNumSlice(&buf, u)
}

case []int8:
if len(u) > 0 {
buf.WriteRune('=')
queryFormatNumSlice(&buf, u)
}

case []int16:
if len(u) > 0 {
buf.WriteRune('=')
queryFormatNumSlice(&buf, u)
}

case []int32:
if len(u) > 0 {
buf.WriteRune('=')
queryFormatNumSlice(&buf, u)
}

case []int64:
if len(u) > 0 {
buf.WriteRune('=')
queryFormatNumSlice(&buf, u)
}

case []uint:
if len(u) > 0 {
buf.WriteRune('=')
queryFormatNumSlice(&buf, u)
}

case []uint8:
if len(u) > 0 {
buf.WriteRune('=')
queryFormatNumSlice(&buf, u)
}

case []uint16:
if len(u) > 0 {
buf.WriteRune('=')
queryFormatNumSlice(&buf, u)
}

case []uint32:
if len(u) > 0 {
buf.WriteRune('=')
queryFormatNumSlice(&buf, u)
}

case []uint64:
if len(u) > 0 {
buf.WriteRune('=')
queryFormatNumSlice(&buf, u)
}

case []float32:
if len(u) > 0 {
buf.WriteRune('=')
queryFormatFloatSlice(&buf, u)
}

case []float64:
if len(u) > 0 {
buf.WriteRune('=')
queryFormatFloatSlice(&buf, u)
}

default:
buf.WriteRune('=')
buf.WriteString(url.QueryEscape(fmt.Sprintf("%v", v)))
}

buf.WriteRune('&')
}

if buf.Len() == 0 {
return ""
}

buf.Truncate(buf.Len() - 1)

return buf.String()
}

// ////////////////////////////////////////////////////////////////////////////////// //

func queryFormatString(v string) string {
return url.QueryEscape(v)
}

func queryFormatNumber(v any) string {
return fmt.Sprintf("%d", v)
}

func queryFormatFloat(v any) string {
return strings.TrimRight(strings.TrimRight(fmt.Sprintf("%f", v), "0"), ".")
}

func queryFormatStringSlice(buf *bytes.Buffer, v []string) {
l := buf.Len()

for _, vv := range v {
if vv != "" {
buf.WriteString(queryFormatString(vv))
buf.WriteRune(',')
}
}

if l != buf.Len() {
buf.Truncate(buf.Len() - 1)
}
}

func queryFormatStringerSlice(buf *bytes.Buffer, v []fmt.Stringer) {
l := buf.Len()

for _, vv := range v {
vvv := vv.String()

if vvv != "" {
buf.WriteString(queryFormatString(vvv))
buf.WriteRune(',')
}
}

if l != buf.Len() {
buf.Truncate(buf.Len() - 1)
}
}

func queryFormatNumSlice[T mathutil.Integer](buf *bytes.Buffer, v []T) {
for _, vv := range v {
buf.WriteString(queryFormatNumber(vv))
buf.WriteRune(',')
}

buf.Truncate(buf.Len() - 1)
}

func queryFormatFloatSlice[T mathutil.Float](buf *bytes.Buffer, v []T) {
for _, vv := range v {
buf.WriteString(queryFormatFloat(vv))
buf.WriteRune(',')
}

buf.Truncate(buf.Len() - 1)
}
Loading

0 comments on commit c7cd09c

Please sign in to comment.