-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
use coalescer instead of naked type assertions move coalescing package adjust tests, clarify semantics of append/prepend
- Loading branch information
Showing
29 changed files
with
760 additions
and
284 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
// SPDX-FileCopyrightText: 2023 Christoph Mewes | ||
// SPDX-License-Identifier: MIT | ||
|
||
package coalescing | ||
|
||
import ( | ||
"fmt" | ||
|
||
"go.xrstf.de/rudi/pkg/lang/ast" | ||
) | ||
|
||
type Coalescer interface { | ||
ToBool(val any) (bool, error) | ||
ToFloat64(val any) (float64, error) | ||
ToInt64(val any) (int64, error) | ||
ToNumber(val any) (ast.Number, error) | ||
ToString(val any) (string, error) | ||
ToVector(val any) ([]any, error) | ||
ToObject(val any) (map[string]any, error) | ||
ToNull(val any) (bool, error) | ||
} | ||
|
||
func deliteral(val any) any { | ||
lit, ok := val.(ast.Literal) | ||
if ok { | ||
return lit.LiteralValue() | ||
} | ||
|
||
return val | ||
} | ||
|
||
func toNumber(c Coalescer, val any) (ast.Number, error) { | ||
i, err := c.ToInt64(val) | ||
if err == nil { | ||
return ast.Number{Value: i}, nil | ||
} | ||
|
||
f, err := c.ToFloat64(val) | ||
if err == nil { | ||
return ast.Number{Value: f}, nil | ||
} | ||
|
||
return ast.Number{}, fmt.Errorf("cannot convert %v losslessly to number", val) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
// SPDX-FileCopyrightText: 2023 Christoph Mewes | ||
// SPDX-License-Identifier: MIT | ||
|
||
package coalescing | ||
|
||
import ( | ||
"fmt" | ||
"strconv" | ||
"strings" | ||
|
||
"go.xrstf.de/rudi/pkg/lang/ast" | ||
) | ||
|
||
type humane struct{} | ||
|
||
func NewHumane() Coalescer { | ||
return humane{} | ||
} | ||
|
||
var _ Coalescer = humane{} | ||
|
||
func (humane) ToNull(val any) (bool, error) { | ||
switch v := deliteral(val).(type) { | ||
case nil: | ||
return true, nil | ||
case bool: | ||
return !v, nil | ||
default: | ||
return false, fmt.Errorf("cannot coalesce %T into null", v) | ||
} | ||
} | ||
|
||
func (humane) ToBool(val any) (bool, error) { | ||
switch v := deliteral(val).(type) { | ||
case bool: | ||
return v, nil | ||
case int: | ||
return v != 0, nil | ||
case int32: | ||
return v != 0, nil | ||
case int64: | ||
return v != 0, nil | ||
case float32: | ||
return v != 0, nil | ||
case float64: | ||
return v != 0, nil | ||
case string: | ||
if v == "" || v == "0" { | ||
return false, nil | ||
} | ||
|
||
return !strings.EqualFold(v, "false"), nil | ||
case []any: | ||
return len(v) > 0, nil | ||
case map[string]any: | ||
return len(v) > 0, nil | ||
case nil: | ||
return false, nil | ||
default: | ||
return false, fmt.Errorf("cannot coalesce %T into bool", v) | ||
} | ||
} | ||
|
||
func (humane) ToFloat64(val any) (float64, error) { | ||
switch v := deliteral(val).(type) { | ||
case int: | ||
return float64(v), nil | ||
case int32: | ||
return float64(v), nil | ||
case int64: | ||
return float64(v), nil | ||
case float32: | ||
return float64(v), nil | ||
case float64: | ||
return v, nil | ||
case string: | ||
parsed, err := strconv.ParseFloat(v, 64) | ||
if err != nil { | ||
return 0, fmt.Errorf("cannot convert %q losslessly to float64", v) | ||
} | ||
return parsed, nil | ||
case bool: | ||
if v { | ||
return 1, nil | ||
} else { | ||
return 0, nil | ||
} | ||
case nil: | ||
return 0, nil | ||
default: | ||
return 0, fmt.Errorf("cannot coalesce %T into float64", v) | ||
} | ||
} | ||
|
||
func (humane) ToInt64(val any) (int64, error) { | ||
switch v := deliteral(val).(type) { | ||
case int: | ||
return int64(v), nil | ||
case int32: | ||
return int64(v), nil | ||
case int64: | ||
return v, nil | ||
case string: | ||
parsed, err := strconv.ParseInt(v, 10, 64) | ||
if err != nil { | ||
return 0, fmt.Errorf("cannot convert %q losslessly to int64", v) | ||
} | ||
return parsed, nil | ||
case bool: | ||
if v { | ||
return 1, nil | ||
} else { | ||
return 0, nil | ||
} | ||
case nil: | ||
return 0, nil | ||
default: | ||
return 0, fmt.Errorf("cannot coalesce %T into int64", v) | ||
} | ||
} | ||
|
||
func (h humane) ToNumber(val any) (ast.Number, error) { | ||
return toNumber(h, val) | ||
} | ||
|
||
func (humane) ToString(val any) (string, error) { | ||
switch v := deliteral(val).(type) { | ||
case string: | ||
return v, nil | ||
case bool: | ||
return strconv.FormatBool(v), nil | ||
case int: | ||
return strconv.FormatInt(int64(v), 10), nil | ||
case int32: | ||
return strconv.FormatInt(int64(v), 10), nil | ||
case int64: | ||
return strconv.FormatInt(v, 10), nil | ||
case float64: | ||
return formatFloat(v), nil | ||
case nil: | ||
return "", nil | ||
default: | ||
return "", fmt.Errorf("cannot coalesce %T into string", v) | ||
} | ||
} | ||
|
||
func (humane) ToVector(val any) ([]any, error) { | ||
switch v := deliteral(val).(type) { | ||
case nil: | ||
return []any{}, nil | ||
case []any: | ||
return v, nil | ||
default: | ||
return nil, fmt.Errorf("cannot coalesce %T into vector", v) | ||
} | ||
} | ||
|
||
func (humane) ToObject(val any) (map[string]any, error) { | ||
switch v := deliteral(val).(type) { | ||
case nil: | ||
return map[string]any{}, nil | ||
case map[string]any: | ||
return v, nil | ||
default: | ||
return nil, fmt.Errorf("cannot coalesce %T into object", v) | ||
} | ||
} |
Oops, something went wrong.