mirror of
https://github.com/IBM/fp-go.git
synced 2025-08-10 22:31:32 +02:00
fix: add Lens for FormData
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
This commit is contained in:
66
http/form/form.go
Normal file
66
http/form/form.go
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package form
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
A "github.com/IBM/fp-go/array"
|
||||||
|
F "github.com/IBM/fp-go/function"
|
||||||
|
L "github.com/IBM/fp-go/optics/lens"
|
||||||
|
LA "github.com/IBM/fp-go/optics/lens/array"
|
||||||
|
LRG "github.com/IBM/fp-go/optics/lens/record/generic"
|
||||||
|
O "github.com/IBM/fp-go/option"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
// FormBuilder returns a function that transforms a form
|
||||||
|
FormBuilder = func(url.Values) url.Values
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Default is the default form field
|
||||||
|
Default = make(url.Values)
|
||||||
|
|
||||||
|
noField = O.None[string]()
|
||||||
|
|
||||||
|
// AtValues is a [L.Lens] that focusses on the values of a form field
|
||||||
|
AtValues = LRG.AtRecord[url.Values, []string]
|
||||||
|
|
||||||
|
composeHead = F.Pipe1(
|
||||||
|
LA.AtHead[string](),
|
||||||
|
L.ComposeOptions[url.Values, string](A.Empty[string]()),
|
||||||
|
)
|
||||||
|
|
||||||
|
// AtValue is a [L.Lens] that focusses on first value in form fields
|
||||||
|
AtValue = F.Flow2(
|
||||||
|
AtValues,
|
||||||
|
composeHead,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
// WithValue creates a [FormBuilder] for a certain field
|
||||||
|
func WithValue(name string) func(value string) FormBuilder {
|
||||||
|
return F.Flow2(
|
||||||
|
O.Of[string],
|
||||||
|
AtValue(name).Set,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithoutValue creates a [FormBuilder] that removes a field
|
||||||
|
func WithoutValue(name string) FormBuilder {
|
||||||
|
return AtValue(name).Set(noField)
|
||||||
|
}
|
93
http/form/form_test.go
Normal file
93
http/form/form_test.go
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package form
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/url"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
A "github.com/IBM/fp-go/array"
|
||||||
|
"github.com/IBM/fp-go/eq"
|
||||||
|
F "github.com/IBM/fp-go/function"
|
||||||
|
LT "github.com/IBM/fp-go/optics/lens/testing"
|
||||||
|
O "github.com/IBM/fp-go/option"
|
||||||
|
RG "github.com/IBM/fp-go/record/generic"
|
||||||
|
S "github.com/IBM/fp-go/string"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
sEq = eq.FromEquals(S.Eq)
|
||||||
|
valuesEq = RG.Eq[url.Values](A.Eq(sEq))
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLaws(t *testing.T) {
|
||||||
|
name := "Content-Type"
|
||||||
|
fieldLaws := LT.AssertLaws[url.Values, O.Option[string]](t, O.Eq(sEq), valuesEq)(AtValue(name))
|
||||||
|
|
||||||
|
n := O.None[string]()
|
||||||
|
s1 := O.Some("s1")
|
||||||
|
|
||||||
|
v1 := F.Pipe1(
|
||||||
|
Default,
|
||||||
|
WithValue(name)("v1"),
|
||||||
|
)
|
||||||
|
|
||||||
|
v2 := F.Pipe1(
|
||||||
|
Default,
|
||||||
|
WithValue("Other-Header")("v2"),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.True(t, fieldLaws(Default, n))
|
||||||
|
assert.True(t, fieldLaws(v1, n))
|
||||||
|
assert.True(t, fieldLaws(v2, n))
|
||||||
|
|
||||||
|
assert.True(t, fieldLaws(Default, s1))
|
||||||
|
assert.True(t, fieldLaws(v1, s1))
|
||||||
|
assert.True(t, fieldLaws(v2, s1))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFormField(t *testing.T) {
|
||||||
|
|
||||||
|
v1 := F.Pipe1(
|
||||||
|
Default,
|
||||||
|
WithValue("h1")("v1"),
|
||||||
|
)
|
||||||
|
|
||||||
|
v2 := F.Pipe1(
|
||||||
|
v1,
|
||||||
|
WithValue("h2")("v2"),
|
||||||
|
)
|
||||||
|
|
||||||
|
// make sure the code does not change structures
|
||||||
|
assert.False(t, valuesEq.Equals(Default, v1))
|
||||||
|
assert.False(t, valuesEq.Equals(Default, v2))
|
||||||
|
assert.False(t, valuesEq.Equals(v1, v2))
|
||||||
|
|
||||||
|
// check for existence of values
|
||||||
|
assert.Equal(t, "v1", v1.Get("h1"))
|
||||||
|
assert.Equal(t, "v1", v2.Get("h1"))
|
||||||
|
assert.Equal(t, "v2", v2.Get("h2"))
|
||||||
|
|
||||||
|
// check getter on lens
|
||||||
|
|
||||||
|
l1 := AtValue("h1")
|
||||||
|
l2 := AtValue("h2")
|
||||||
|
|
||||||
|
assert.Equal(t, O.Of("v1"), l1.Get(v1))
|
||||||
|
assert.Equal(t, O.Of("v1"), l1.Get(v2))
|
||||||
|
assert.Equal(t, O.Of("v2"), l2.Get(v2))
|
||||||
|
}
|
39
optics/lens/array/generic/head.go
Normal file
39
optics/lens/array/generic/head.go
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package generic
|
||||||
|
|
||||||
|
import (
|
||||||
|
AA "github.com/IBM/fp-go/array/generic"
|
||||||
|
L "github.com/IBM/fp-go/optics/lens"
|
||||||
|
O "github.com/IBM/fp-go/option"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AtHead focusses on the head of an array. The setter works as follows
|
||||||
|
// - if the new value is none, the result will be an empty array
|
||||||
|
// - if the new value is some and the array is empty, it creates a new array with one element
|
||||||
|
// - if the new value is some and the array is not empty, it replaces the head
|
||||||
|
func AtHead[AS []A, A any]() L.Lens[AS, O.Option[A]] {
|
||||||
|
return L.MakeLens(AA.Head[AS, A], func(as AS, a O.Option[A]) AS {
|
||||||
|
return O.MonadFold(a, AA.Empty[AS], func(v A) AS {
|
||||||
|
if AA.IsEmpty(as) {
|
||||||
|
return AA.Of[AS, A](v)
|
||||||
|
}
|
||||||
|
cpy := AA.Copy(as)
|
||||||
|
cpy[0] = v
|
||||||
|
return cpy
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
30
optics/lens/array/head.go
Normal file
30
optics/lens/array/head.go
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package array
|
||||||
|
|
||||||
|
import (
|
||||||
|
L "github.com/IBM/fp-go/optics/lens"
|
||||||
|
G "github.com/IBM/fp-go/optics/lens/array/generic"
|
||||||
|
O "github.com/IBM/fp-go/option"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AtHead focusses on the head of an array. The setter works as follows
|
||||||
|
// - if the new value is none, the result will be an empty array
|
||||||
|
// - if the new value is some and the array is empty, it creates a new array with one element
|
||||||
|
// - if the new value is some and the array is not empty, it replaces the head
|
||||||
|
func AtHead[A any]() L.Lens[[]A, O.Option[A]] {
|
||||||
|
return G.AtHead[[]A]()
|
||||||
|
}
|
40
optics/lens/array/head_test.go
Normal file
40
optics/lens/array/head_test.go
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
// Copyright (c) 2023 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package array
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
A "github.com/IBM/fp-go/array"
|
||||||
|
"github.com/IBM/fp-go/eq"
|
||||||
|
LT "github.com/IBM/fp-go/optics/lens/testing"
|
||||||
|
O "github.com/IBM/fp-go/option"
|
||||||
|
S "github.com/IBM/fp-go/string"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
sEq = eq.FromEquals(S.Eq)
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLaws(t *testing.T) {
|
||||||
|
headLaws := LT.AssertLaws(t, O.Eq(sEq), A.Eq(sEq))(AtHead[string]())
|
||||||
|
|
||||||
|
assert.True(t, headLaws(A.Empty[string](), O.None[string]()))
|
||||||
|
assert.True(t, headLaws(A.Empty[string](), O.Of("a")))
|
||||||
|
assert.True(t, headLaws(A.From("a", "b"), O.None[string]()))
|
||||||
|
assert.True(t, headLaws(A.From("a", "b"), O.Of("c")))
|
||||||
|
}
|
Reference in New Issue
Block a user