Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor params to clean up ParamInterface & better work with types #357

Merged
merged 1 commit into from
Aug 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
189 changes: 95 additions & 94 deletions actr/param/param.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,21 @@ package param

import (
"fmt"
"strconv"
"strings"

"golang.org/x/exp/constraints"
"golang.org/x/exp/maps"
"golang.org/x/exp/slices"

"github.com/asmaloney/gactar/util/keyvalue"
"github.com/asmaloney/gactar/util/numbers"
)

type number interface {
constraints.Integer | constraints.Float
}

type ErrUnrecognizedOption struct {
Option string
}
Expand All @@ -20,20 +26,20 @@ func (e ErrUnrecognizedOption) Error() string {
}

type ErrValueOutOfRange struct {
Min *float64
Max *float64
Min *string
Max *string
}

func (e ErrValueOutOfRange) Error() string {
if e.Min != nil && e.Max == nil {
return fmt.Sprintf("is out of range (minimum %s)", numbers.Float64Str(*e.Min))
return fmt.Sprintf("is out of range (minimum %s)", *e.Min)
}

if e.Min == nil && e.Max != nil {
return fmt.Sprintf("is out of range (maximum %s)", numbers.Float64Str(*e.Max))
return fmt.Sprintf("is out of range (maximum %s)", *e.Max)
}

return fmt.Sprintf("is out of range (%s-%s)", numbers.Float64Str(*e.Min), numbers.Float64Str(*e.Max))
return fmt.Sprintf("is out of range (%s-%s)", *e.Min, *e.Max)
}

type ErrInvalidType struct {
Expand Down Expand Up @@ -66,44 +72,39 @@ func Ptr[T any](v T) *T {
return &v
}

// Info is the basic info about a parameter
type Info struct {
Name string
Description string
// info is the basic info about a parameter
type info struct {
name string
description string
}

// Str is a string parameter
type Str struct {
Info
info

validValues []string
}

// Int is an int parameter with optional min and max constraints
type Int struct {
Info
info

Min *int
Max *int
min *int
max *int
}

// Float is a float parameter with optional min and max constraints
type Float struct {
Info
info

Min *float64
Max *float64
min *float64
max *float64
}

// ParamInterface provides an interface to a parameter
type ParamInterface interface {
GetName() string
GetDescription() string

GetMin() *float64
GetMax() *float64

ValidValues() []string
Name() string
Description() string
}

// InfoMap maps a name to the parameter's info
Expand All @@ -123,42 +124,16 @@ type ParametersInterface interface {
ValidateParam(param *keyvalue.KeyValue) error
}

func (i Info) GetName() string {
return i.Name
}

func (i Info) GetDescription() string {
return i.Description
}

func (s Str) GetMin() *float64 { return nil }
func (ps Str) GetMax() *float64 { return nil }
func (i info) Name() string { return i.name }
func (i info) Description() string { return i.description }

func (s Str) ValidValues() []string {
return s.validValues
}

func (i Int) GetMin() *float64 {
if i.Min != nil {
temp := float64(*i.Min)
return &temp
}
return nil
}
func (s Str) ValidValues() []string { return s.validValues }

func (i Int) GetMax() *float64 {
if i.Max != nil {
temp := float64(*i.Max)
return &temp
}
return nil
}
func (i Int) Min() *int { return i.min }
func (i Int) Max() *int { return i.max }

func (i Int) ValidValues() []string { return []string{} }

func (f Float) GetMin() *float64 { return f.Min }
func (f Float) GetMax() *float64 { return f.Max }
func (f Float) ValidValues() []string { return []string{} }
func (f Float) Min() *float64 { return f.min }
func (f Float) Max() *float64 { return f.max }

func NewParameters(paramMap InfoMap) ParametersInterface {
return parameters{params: paramMap}
Expand All @@ -168,7 +143,7 @@ func NewParameters(paramMap InfoMap) ParametersInterface {
func (p parameters) ParameterList() List {
params := maps.Values(p.params)

slices.SortFunc[ParamInterface](params, func(a, b ParamInterface) bool { return a.GetName() < b.GetName() })
slices.SortFunc[ParamInterface](params, func(a, b ParamInterface) bool { return a.Name() < b.Name() })

return params
}
Expand All @@ -182,71 +157,97 @@ func (p parameters) ValidateParam(param *keyvalue.KeyValue) (err error) {

value := param.Value

switch paramInfo.(type) {
switch pInfo := paramInfo.(type) {
case Str:
if value.Str == nil {
return ErrInvalidType{
ExpectedType: "string",
}
}

valid := pInfo.validValues
if (len(valid) > 0) && !slices.Contains(valid, *value.Str) {
context := fmt.Sprintf("(expected one of: %s)", strings.Join(valid, ", "))

return ErrInvalidValue{
ParameterName: param.Key,
Value: *value.Str,
Context: &context,
}
}

case Int:
if value.Number == nil {
return ErrInvalidType{
ExpectedType: "number",
}
}

val := int(*value.Number)

err = compareMinMax(val, pInfo.min, pInfo.max)
if err != nil {
return
}

case Float:
if value.Number == nil {
return ErrInvalidType{
ExpectedType: "number",
}
}
}

switch {
case value.Number != nil:
min := paramInfo.GetMin()
max := paramInfo.GetMax()
val := *value.Number

if (min != nil) && (max != nil) &&
((*value.Number < *min) || (*value.Number > *max)) {
return ErrValueOutOfRange{
Min: min,
Max: max,
}
err = compareMinMax(val, pInfo.min, pInfo.max)
if err != nil {
return
}
}

if min != nil && (*value.Number < *min) {
return ErrValueOutOfRange{
Min: min,
}
}
if !value.IsSet() {
return keyvalue.ErrInvalidType{ExpectedType: keyvalue.Number}
}

if max != nil && (*value.Number > *max) {
return ErrValueOutOfRange{
Max: max,
}
}
return
}

case value.Str != nil:
valid := paramInfo.ValidValues()
if (len(valid) > 0) && !slices.Contains(valid, *value.Str) {
context := fmt.Sprintf("(expected one of: %s)", strings.Join(valid, ", "))
func convertNumberToStr[T number](num T) *string {
var str string

return ErrInvalidValue{
ParameterName: param.Key,
Value: *value.Str,
Context: &context,
}
switch any(num).(type) {
case int:
str = strconv.Itoa(int(num))

case float64:
str = numbers.Float64Str(float64(num))
}

return &str
}

func compareMinMax[T number](value T, min, max *T) error {
if (min != nil) && (max != nil) &&
((value < *min) || (value > *max)) {
return ErrValueOutOfRange{
Min: convertNumberToStr[T](*min),
Max: convertNumberToStr[T](*max),
}
}

default:
return keyvalue.ErrInvalidType{ExpectedType: keyvalue.Number}
if min != nil && (value < *min) {
return ErrValueOutOfRange{
Min: convertNumberToStr[T](*min),
}
}

return
if max != nil && (value > *max) {
return ErrValueOutOfRange{
Max: convertNumberToStr[T](*max),
}
}

return nil
}

// parameterInfo returns detailed info about a specific parameter given by "name"
Expand All @@ -262,23 +263,23 @@ func (p parameters) parameterInfo(name string) ParamInterface {
// NewStr creates a new string param with optional list of valid values
func NewStr(name, description string, validValues []string) Str {
return Str{
Info: Info{name, description},
info: info{name, description},
validValues: validValues,
}
}

// NewInt creates a new int param with optional min/max constraints
func NewInt(name, description string, min, max *int) Int {
return Int{
Info{name, description},
info{name, description},
min, max,
}
}

// NewFloat creates a new float param with optional min/max constraints
func NewFloat(name, description string, min, max *float64) Float {
return Float{
Info{name, description},
info{name, description},
min, max,
}
}
4 changes: 3 additions & 1 deletion amod/amod.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package amod
import (
"errors"
"fmt"
"strconv"
"strings"

"golang.org/x/exp/slices"
Expand Down Expand Up @@ -576,7 +577,8 @@ func argToKeyValue(key string, a *arg) *keyvalue.KeyValue {
case a.Str != nil:
value.Str = a.Str
case a.Number != nil:
value.Str = a.Number
num, _ := strconv.ParseFloat(*a.Number, 64)
value.Number = &num
}

return &keyvalue.KeyValue{
Expand Down
22 changes: 11 additions & 11 deletions cmd/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ func info(args []string) {
fmt.Fprintf(writer, "\t\t%s\n", chalk.Header("Request Parameters:"))

for _, rp := range buffer.RequestParameters().ParameterList() {
fmt.Fprintf(writer, "\t\t\t%s\t%s\n", chalk.Italic(rp.GetName()), rp.GetDescription())
fmt.Fprintf(writer, "\t\t\t%s\t%s\n", chalk.Italic(rp.Name()), rp.Description())
}

writer.Flush()
Expand All @@ -121,7 +121,7 @@ func info(args []string) {
func outputParam(writer *tabwriter.Writer, level int, p param.ParamInterface) {
tabs := strings.Repeat("\t", level)

fmt.Fprintf(writer, "%s%s", tabs, chalk.Italic(p.GetName()))
fmt.Fprintf(writer, "%s%s", tabs, chalk.Italic(p.Name()))

var typeStr string
var minStr string
Expand All @@ -131,28 +131,28 @@ func outputParam(writer *tabwriter.Writer, level int, p param.ParamInterface) {
case param.Int:
{
typeStr = "int"
if v.Min != nil {
minStr = strconv.Itoa(*v.Min)
if v.Min() != nil {
minStr = strconv.Itoa(*v.Min())
}
if v.Max != nil {
maxStr = strconv.Itoa(*v.Max)
if v.Max() != nil {
maxStr = strconv.Itoa(*v.Max())
}
}
case param.Float:
{
typeStr = "float"
if v.Min != nil {
minStr = strconv.FormatFloat(*v.Min, 'f', 2, 64)
if v.Min() != nil {
minStr = strconv.FormatFloat(*v.Min(), 'f', 2, 64)
}
if v.Max != nil {
maxStr = strconv.FormatFloat(*v.Max, 'f', 2, 64)
if v.Max() != nil {
maxStr = strconv.FormatFloat(*v.Max(), 'f', 2, 64)
}
}
}

fmt.Fprintf(writer, "%s%s", tabs, typeStr)
fmt.Fprintf(writer, "%s%s-%s", tabs, minStr, maxStr)
fmt.Fprintf(writer, "%s%s", tabs, p.GetDescription())
fmt.Fprintf(writer, "%s%s", tabs, p.Description())

fmt.Fprintln(writer, "")
}
Expand Down
Loading