From 30d49e1fd3baef34bb20d28663707623c8a3dec6 Mon Sep 17 00:00:00 2001 From: Lukas Herman Date: Thu, 10 Sep 2020 23:52:05 -0700 Subject: [PATCH] Add human friendly string implementation --- pkg/prop/bool.go | 7 ++++++ pkg/prop/duration.go | 27 ++++++++++++++++++++ pkg/prop/float.go | 27 ++++++++++++++++++++ pkg/prop/format.go | 22 +++++++++++++++++ pkg/prop/int.go | 27 ++++++++++++++++++++ pkg/prop/prop.go | 33 +++++++++++++++++++++++++ pkg/prop/prop_test.go | 57 +++++++++++++++++++++++++++++++++++++++++++ pkg/prop/string.go | 20 +++++++++++++++ 8 files changed, 220 insertions(+) diff --git a/pkg/prop/bool.go b/pkg/prop/bool.go index e585c91..59cb8ff 100644 --- a/pkg/prop/bool.go +++ b/pkg/prop/bool.go @@ -1,5 +1,7 @@ package prop +import "fmt" + // BoolConstraint is an interface to represent bool value constraint. type BoolConstraint interface { Compare(bool) (float64, bool) @@ -20,6 +22,11 @@ func (b BoolExact) Compare(o bool) (float64, bool) { // Value implements BoolConstraint. func (b BoolExact) Value() bool { return bool(b) } +// String implements Stringify +func (b BoolExact) String() string { + return fmt.Sprintf("%t (exact)", b) +} + // Bool specifies ideal bool value. type Bool BoolExact diff --git a/pkg/prop/duration.go b/pkg/prop/duration.go index 8de7cc4..ba16541 100644 --- a/pkg/prop/duration.go +++ b/pkg/prop/duration.go @@ -1,7 +1,9 @@ package prop import ( + "fmt" "math" + "strings" "time" ) @@ -23,6 +25,11 @@ func (d Duration) Compare(a time.Duration) (float64, bool) { // Value implements DurationConstraint. func (d Duration) Value() (time.Duration, bool) { return time.Duration(d), true } +// String implements Stringify +func (d Duration) String() string { + return fmt.Sprintf("%v (ideal)", time.Duration(d)) +} + // DurationExact specifies exact duration value. type DurationExact time.Duration @@ -37,6 +44,11 @@ func (d DurationExact) Compare(a time.Duration) (float64, bool) { // Value implements DurationConstraint. func (d DurationExact) Value() (time.Duration, bool) { return time.Duration(d), true } +// String implements Stringify +func (d DurationExact) String() string { + return fmt.Sprintf("%v (exact)", time.Duration(d)) +} + // DurationOneOf specifies list of expected duration values. type DurationOneOf []time.Duration @@ -53,6 +65,16 @@ func (d DurationOneOf) Compare(a time.Duration) (float64, bool) { // Value implements DurationConstraint. func (DurationOneOf) Value() (time.Duration, bool) { return 0, false } +// String implements Stringify +func (d DurationOneOf) String() string { + var opts []string + for _, v := range d { + opts = append(opts, fmt.Sprint(v)) + } + + return fmt.Sprintf("%s (one of values)", strings.Join(opts, ",")) +} + // DurationRanged specifies range of expected duration value. // If Ideal is non-zero, closest value to Ideal takes priority. type DurationRanged struct { @@ -96,3 +118,8 @@ func (d DurationRanged) Compare(a time.Duration) (float64, bool) { // Value implements DurationConstraint. func (DurationRanged) Value() (time.Duration, bool) { return 0, false } + +// String implements Stringify +func (d DurationRanged) String() string { + return fmt.Sprintf("%s - %s (range), %s (ideal)", d.Min, d.Max, d.Ideal) +} diff --git a/pkg/prop/float.go b/pkg/prop/float.go index 3238c08..7f36584 100644 --- a/pkg/prop/float.go +++ b/pkg/prop/float.go @@ -1,7 +1,9 @@ package prop import ( + "fmt" "math" + "strings" ) // FloatConstraint is an interface to represent float value constraint. @@ -22,6 +24,11 @@ func (f Float) Compare(a float32) (float64, bool) { // Value implements FloatConstraint. func (f Float) Value() (float32, bool) { return float32(f), true } +// String implements Stringify +func (f Float) String() string { + return fmt.Sprintf("%.2f (ideal)", f) +} + // FloatExact specifies exact float value. type FloatExact float32 @@ -36,6 +43,11 @@ func (f FloatExact) Compare(a float32) (float64, bool) { // Value implements FloatConstraint. func (f FloatExact) Value() (float32, bool) { return float32(f), true } +// String implements Stringify +func (f FloatExact) String() string { + return fmt.Sprintf("%.2f (exact)", f) +} + // FloatOneOf specifies list of expected float values. type FloatOneOf []float32 @@ -52,6 +64,16 @@ func (f FloatOneOf) Compare(a float32) (float64, bool) { // Value implements FloatConstraint. func (FloatOneOf) Value() (float32, bool) { return 0, false } +// String implements Stringify +func (f FloatOneOf) String() string { + var opts []string + for _, v := range f { + opts = append(opts, fmt.Sprintf("%.2f", v)) + } + + return fmt.Sprintf("%s (one of values)", strings.Join(opts, ",")) +} + // FloatRanged specifies range of expected float value. // If Ideal is non-zero, closest value to Ideal takes priority. type FloatRanged struct { @@ -95,3 +117,8 @@ func (f FloatRanged) Compare(a float32) (float64, bool) { // Value implements FloatConstraint. func (FloatRanged) Value() (float32, bool) { return 0, false } + +// String implements Stringify +func (f FloatRanged) String() string { + return fmt.Sprintf("%.2f - %.2f (range), %.2f (ideal)", f.Min, f.Max, f.Ideal) +} diff --git a/pkg/prop/format.go b/pkg/prop/format.go index 126476d..6a4be04 100644 --- a/pkg/prop/format.go +++ b/pkg/prop/format.go @@ -1,7 +1,9 @@ package prop import ( + "fmt" "github.com/pion/mediadevices/pkg/frame" + "strings" ) // FrameFormatConstraint is an interface to represent frame format constraint. @@ -25,6 +27,11 @@ func (f FrameFormat) Compare(a frame.Format) (float64, bool) { // Value implements FrameFormatConstraint. func (f FrameFormat) Value() (frame.Format, bool) { return frame.Format(f), true } +// String implements Stringify +func (f FrameFormat) String() string { + return fmt.Sprintf("%s (ideal)", frame.Format(f)) +} + // FrameFormatExact specifies exact frame format. type FrameFormatExact frame.Format @@ -39,6 +46,11 @@ func (f FrameFormatExact) Compare(a frame.Format) (float64, bool) { // Value implements FrameFormatConstraint. func (f FrameFormatExact) Value() (frame.Format, bool) { return frame.Format(f), true } +// String implements Stringify +func (f FrameFormatExact) String() string { + return fmt.Sprintf("%s (exact)", frame.Format(f)) +} + // FrameFormatOneOf specifies list of expected frame format. type FrameFormatOneOf []frame.Format @@ -54,3 +66,13 @@ func (f FrameFormatOneOf) Compare(a frame.Format) (float64, bool) { // Value implements FrameFormatConstraint. func (FrameFormatOneOf) Value() (frame.Format, bool) { return "", false } + +// String implements Stringify +func (f FrameFormatOneOf) String() string { + var opts []string + for _, v := range f { + opts = append(opts, fmt.Sprint(v)) + } + + return fmt.Sprintf("%s (one of values)", strings.Join(opts, ",")) +} diff --git a/pkg/prop/int.go b/pkg/prop/int.go index a41a12d..d3c983b 100644 --- a/pkg/prop/int.go +++ b/pkg/prop/int.go @@ -1,7 +1,9 @@ package prop import ( + "fmt" "math" + "strings" ) // IntConstraint is an interface to represent integer value constraint. @@ -22,6 +24,11 @@ func (i Int) Compare(a int) (float64, bool) { // Value implements IntConstraint. func (i Int) Value() (int, bool) { return int(i), true } +// String implements Stringify +func (i Int) String() string { + return fmt.Sprintf("%d (ideal)", i) +} + // IntExact specifies exact int value. type IntExact int @@ -33,6 +40,11 @@ func (i IntExact) Compare(a int) (float64, bool) { return 1.0, false } +// String implements Stringify +func (i IntExact) String() string { + return fmt.Sprintf("%d (exact)", i) +} + // Value implements IntConstraint. func (i IntExact) Value() (int, bool) { return int(i), true } @@ -52,6 +64,16 @@ func (i IntOneOf) Compare(a int) (float64, bool) { // Value implements IntConstraint. func (IntOneOf) Value() (int, bool) { return 0, false } +// String implements Stringify +func (i IntOneOf) String() string { + var opts []string + for _, v := range i { + opts = append(opts, fmt.Sprint(v)) + } + + return fmt.Sprintf("%s (one of values)", strings.Join(opts, ",")) +} + // IntRanged specifies range of expected int value. // If Ideal is non-zero, closest value to Ideal takes priority. type IntRanged struct { @@ -95,3 +117,8 @@ func (i IntRanged) Compare(a int) (float64, bool) { // Value implements IntConstraint. func (IntRanged) Value() (int, bool) { return 0, false } + +// String implements Stringify +func (i IntRanged) String() string { + return fmt.Sprintf("%d - %d (range), %d (ideal)", i.Min, i.Max, i.Ideal) +} diff --git a/pkg/prop/prop.go b/pkg/prop/prop.go index 738a6d2..19da63f 100644 --- a/pkg/prop/prop.go +++ b/pkg/prop/prop.go @@ -1,7 +1,9 @@ package prop import ( + "fmt" "reflect" + "strings" "time" "github.com/pion/mediadevices/pkg/frame" @@ -15,6 +17,10 @@ type MediaConstraints struct { AudioConstraints } +func (m *MediaConstraints) String() string { + return prettifyStruct(m) +} + // Media stores single set of media propaties. type Media struct { DeviceID string @@ -22,6 +28,33 @@ type Media struct { Audio } +func (m *Media) String() string { + return prettifyStruct(m) +} + +func prettifyStruct(i interface{}) string { + var rows []string + var addRows func(int, reflect.Value) + addRows = func(level int, obj reflect.Value) { + typeOf := obj.Type() + for i := 0; i < obj.NumField(); i++ { + field := typeOf.Field(i) + value := obj.Field(i) + + padding := strings.Repeat(" ", level) + if value.Kind() == reflect.Struct { + rows = append(rows, fmt.Sprintf("%s%v:", padding, field.Name)) + addRows(level+1, value) + } else { + rows = append(rows, fmt.Sprintf("%s%v: %v", padding, field.Name, value)) + } + } + } + + addRows(0, reflect.ValueOf(i).Elem()) + return strings.Join(rows, "\n") +} + // setterFn is a callback function to set value from fieldB to fieldA type setterFn func(fieldA, fieldB reflect.Value) diff --git a/pkg/prop/prop_test.go b/pkg/prop/prop_test.go index 691c19f..48e1689 100644 --- a/pkg/prop/prop_test.go +++ b/pkg/prop/prop_test.go @@ -309,3 +309,60 @@ func TestMergeConstraintsNested(t *testing.T) { t.Error("expected a.Width to be 100, but got 0") } } + +func TestString(t *testing.T) { + t.Run("IdealValues", func(t *testing.T) { + t.Log("\n", &MediaConstraints{ + DeviceID: String("one"), + VideoConstraints: VideoConstraints{ + Width: Int(1920), + FrameRate: Float(30.0), + FrameFormat: FrameFormat(frame.FormatI420), + }, + AudioConstraints: AudioConstraints{ + Latency: Duration(time.Millisecond * 20), + }, + }) + }) + + t.Run("ExactValues", func(t *testing.T) { + t.Log("\n", &MediaConstraints{ + DeviceID: StringExact("one"), + VideoConstraints: VideoConstraints{ + Width: IntExact(1920), + FrameRate: FloatExact(30.0), + FrameFormat: FrameFormatExact(frame.FormatI420), + }, + AudioConstraints: AudioConstraints{ + Latency: DurationExact(time.Millisecond * 20), + IsBigEndian: BoolExact(true), + }, + }) + }) + + t.Run("OneOfValues", func(t *testing.T) { + t.Log("\n", &MediaConstraints{ + DeviceID: StringOneOf{"one", "two"}, + VideoConstraints: VideoConstraints{ + Width: IntOneOf{1920, 1080}, + FrameRate: FloatOneOf{30.0, 60.1234}, + FrameFormat: FrameFormatOneOf{frame.FormatI420, frame.FormatI444}, + }, + AudioConstraints: AudioConstraints{ + Latency: DurationOneOf{time.Millisecond * 20, time.Millisecond * 40}, + }, + }) + }) + + t.Run("RangedValues", func(t *testing.T) { + t.Log("\n", &MediaConstraints{ + VideoConstraints: VideoConstraints{ + Width: &IntRanged{Min: 1080, Max: 1920, Ideal: 1500}, + FrameRate: &FloatRanged{Min: 30.123, Max: 60.12321312, Ideal: 45.12312312}, + }, + AudioConstraints: AudioConstraints{ + Latency: &DurationRanged{Min: time.Millisecond * 20, Max: time.Millisecond * 40, Ideal: time.Millisecond * 30}, + }, + }) + }) +} diff --git a/pkg/prop/string.go b/pkg/prop/string.go index f9a9471..0e898c4 100644 --- a/pkg/prop/string.go +++ b/pkg/prop/string.go @@ -1,5 +1,10 @@ package prop +import ( + "fmt" + "strings" +) + // StringConstraint is an interface to represent string constraint. type StringConstraint interface { Compare(string) (float64, bool) @@ -21,6 +26,11 @@ func (f String) Compare(a string) (float64, bool) { // Value implements StringConstraint. func (f String) Value() (string, bool) { return string(f), true } +// String implements Stringify +func (f String) String() string { + return fmt.Sprintf("%s (ideal)", string(f)) +} + // StringExact specifies exact string. type StringExact string @@ -35,6 +45,11 @@ func (f StringExact) Compare(a string) (float64, bool) { // Value implements StringConstraint. func (f StringExact) Value() (string, bool) { return string(f), true } +// String implements Stringify +func (f StringExact) String() string { + return fmt.Sprintf("%s (exact)", string(f)) +} + // StringOneOf specifies list of expected string. type StringOneOf []string @@ -50,3 +65,8 @@ func (f StringOneOf) Compare(a string) (float64, bool) { // Value implements StringConstraint. func (StringOneOf) Value() (string, bool) { return "", false } + +// String implements Stringify +func (f StringOneOf) String() string { + return fmt.Sprintf("%s (one of values)", strings.Join([]string(f), ",")) +}