mirror of
https://github.com/IBM/fp-go.git
synced 2025-08-10 22:31:32 +02:00
fix: add RIOE testcases
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
This commit is contained in:
@@ -29,10 +29,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
openIOE = IOE.Eitherize1(os.Open)
|
|
||||||
// Open opens a file for reading within the given context
|
// Open opens a file for reading within the given context
|
||||||
Open = F.Flow3(
|
Open = F.Flow3(
|
||||||
openIOE,
|
IOEF.Open,
|
||||||
RIOE.FromIOEither[*os.File],
|
RIOE.FromIOEither[*os.File],
|
||||||
RIOE.WithContext[*os.File],
|
RIOE.WithContext[*os.File],
|
||||||
)
|
)
|
||||||
@@ -46,11 +45,11 @@ var (
|
|||||||
|
|
||||||
// Close closes an object
|
// Close closes an object
|
||||||
func Close[C io.Closer](c C) RIOE.ReaderIOEither[any] {
|
func Close[C io.Closer](c C) RIOE.ReaderIOEither[any] {
|
||||||
return RIOE.FromIOEither(func() ET.Either[error, any] {
|
return F.Pipe2(
|
||||||
return ET.TryCatchError(func() (any, error) {
|
c,
|
||||||
return c, c.Close()
|
IOEF.Close[C],
|
||||||
})
|
RIOE.FromIOEither[any],
|
||||||
})
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadFile reads a file in the scope of a context
|
// ReadFile reads a file in the scope of a context
|
||||||
|
47
context/readerioeither/file/tempfile_test.go
Normal file
47
context/readerioeither/file/tempfile_test.go
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
// 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 file
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
RIOE "github.com/IBM/fp-go/context/readerioeither"
|
||||||
|
E "github.com/IBM/fp-go/either"
|
||||||
|
F "github.com/IBM/fp-go/function"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestWithTempFile(t *testing.T) {
|
||||||
|
|
||||||
|
res := WithTempFile(onWriteAll[*os.File]([]byte("Carsten")))
|
||||||
|
|
||||||
|
assert.Equal(t, E.Of[error]([]byte("Carsten")), res(context.Background())())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWithTempFileOnClosedFile(t *testing.T) {
|
||||||
|
|
||||||
|
res := WithTempFile(func(f *os.File) RIOE.ReaderIOEither[[]byte] {
|
||||||
|
return F.Pipe2(
|
||||||
|
f,
|
||||||
|
onWriteAll[*os.File]([]byte("Carsten")),
|
||||||
|
RIOE.ChainFirst(F.Constant1[[]byte](Close(f))),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.Equal(t, E.Of[error]([]byte("Carsten")), res(context.Background())())
|
||||||
|
}
|
57
context/readerioeither/file/write.go
Normal file
57
context/readerioeither/file/write.go
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
// 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 file
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
RIOE "github.com/IBM/fp-go/context/readerioeither"
|
||||||
|
F "github.com/IBM/fp-go/function"
|
||||||
|
)
|
||||||
|
|
||||||
|
func onWriteAll[W io.Writer](data []byte) func(w W) RIOE.ReaderIOEither[[]byte] {
|
||||||
|
return func(w W) RIOE.ReaderIOEither[[]byte] {
|
||||||
|
return F.Pipe1(
|
||||||
|
RIOE.TryCatch(func(ctx context.Context) func() ([]byte, error) {
|
||||||
|
return func() ([]byte, error) {
|
||||||
|
_, err := w.Write(data)
|
||||||
|
return data, err
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
RIOE.WithContext[[]byte],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteAll uses a generator function to create a stream, writes data to it and closes it
|
||||||
|
func WriteAll[W io.WriteCloser](data []byte) func(acquire RIOE.ReaderIOEither[W]) RIOE.ReaderIOEither[[]byte] {
|
||||||
|
onWrite := onWriteAll[W](data)
|
||||||
|
return func(onCreate RIOE.ReaderIOEither[W]) RIOE.ReaderIOEither[[]byte] {
|
||||||
|
return RIOE.WithResource[[]byte](
|
||||||
|
onCreate,
|
||||||
|
Close[W])(
|
||||||
|
onWrite,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write uses a generator function to create a stream, writes data to it and closes it
|
||||||
|
func Write[R any, W io.WriteCloser](acquire RIOE.ReaderIOEither[W]) func(use func(W) RIOE.ReaderIOEither[R]) RIOE.ReaderIOEither[R] {
|
||||||
|
return RIOE.WithResource[R](
|
||||||
|
acquire,
|
||||||
|
Close[W])
|
||||||
|
}
|
@@ -155,6 +155,10 @@ func FromIO[A any](t IO.IO[A]) ReaderIOEither[A] {
|
|||||||
return G.FromIO[ReaderIOEither[A]](t)
|
return G.FromIO[ReaderIOEither[A]](t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func FromLazy[A any](t L.Lazy[A]) ReaderIOEither[A] {
|
||||||
|
return G.FromIO[ReaderIOEither[A]](t)
|
||||||
|
}
|
||||||
|
|
||||||
// Never returns a 'ReaderIOEither' that never returns, except if its context gets canceled
|
// Never returns a 'ReaderIOEither' that never returns, except if its context gets canceled
|
||||||
func Never[A any]() ReaderIOEither[A] {
|
func Never[A any]() ReaderIOEither[A] {
|
||||||
return G.Never[ReaderIOEither[A]]()
|
return G.Never[ReaderIOEither[A]]()
|
||||||
|
@@ -162,3 +162,125 @@ func TestRegularApply(t *testing.T) {
|
|||||||
res := applied(context.Background())()
|
res := applied(context.Background())()
|
||||||
assert.Equal(t, E.Of[error]("CARSTEN"), res)
|
assert.Equal(t, E.Of[error]("CARSTEN"), res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestWithResourceNoErrors(t *testing.T) {
|
||||||
|
var countAcquire, countBody, countRelease int
|
||||||
|
|
||||||
|
acquire := FromLazy(func() int {
|
||||||
|
countAcquire++
|
||||||
|
return countAcquire
|
||||||
|
})
|
||||||
|
|
||||||
|
release := func(int) ReaderIOEither[int] {
|
||||||
|
return FromLazy(func() int {
|
||||||
|
countRelease++
|
||||||
|
return countRelease
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
body := func(int) ReaderIOEither[int] {
|
||||||
|
return FromLazy(func() int {
|
||||||
|
countBody++
|
||||||
|
return countBody
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
resRIOE := WithResource[int](acquire, release)(body)
|
||||||
|
|
||||||
|
res := resRIOE(context.Background())()
|
||||||
|
|
||||||
|
assert.Equal(t, 1, countAcquire)
|
||||||
|
assert.Equal(t, 1, countBody)
|
||||||
|
assert.Equal(t, 1, countRelease)
|
||||||
|
assert.Equal(t, E.Of[error](1), res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWithResourceErrorInBody(t *testing.T) {
|
||||||
|
var countAcquire, countBody, countRelease int
|
||||||
|
|
||||||
|
acquire := FromLazy(func() int {
|
||||||
|
countAcquire++
|
||||||
|
return countAcquire
|
||||||
|
})
|
||||||
|
|
||||||
|
release := func(int) ReaderIOEither[int] {
|
||||||
|
return FromLazy(func() int {
|
||||||
|
countRelease++
|
||||||
|
return countRelease
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
err := fmt.Errorf("error in body")
|
||||||
|
body := func(int) ReaderIOEither[int] {
|
||||||
|
return Left[int](err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resRIOE := WithResource[int](acquire, release)(body)
|
||||||
|
|
||||||
|
res := resRIOE(context.Background())()
|
||||||
|
|
||||||
|
assert.Equal(t, 1, countAcquire)
|
||||||
|
assert.Equal(t, 0, countBody)
|
||||||
|
assert.Equal(t, 1, countRelease)
|
||||||
|
assert.Equal(t, E.Left[int](err), res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWithResourceErrorInAcquire(t *testing.T) {
|
||||||
|
var countAcquire, countBody, countRelease int
|
||||||
|
|
||||||
|
err := fmt.Errorf("error in acquire")
|
||||||
|
acquire := Left[int](err)
|
||||||
|
|
||||||
|
release := func(int) ReaderIOEither[int] {
|
||||||
|
return FromLazy(func() int {
|
||||||
|
countRelease++
|
||||||
|
return countRelease
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
body := func(int) ReaderIOEither[int] {
|
||||||
|
return FromLazy(func() int {
|
||||||
|
countBody++
|
||||||
|
return countBody
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
resRIOE := WithResource[int](acquire, release)(body)
|
||||||
|
|
||||||
|
res := resRIOE(context.Background())()
|
||||||
|
|
||||||
|
assert.Equal(t, 0, countAcquire)
|
||||||
|
assert.Equal(t, 0, countBody)
|
||||||
|
assert.Equal(t, 0, countRelease)
|
||||||
|
assert.Equal(t, E.Left[int](err), res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWithResourceErrorInRelease(t *testing.T) {
|
||||||
|
var countAcquire, countBody, countRelease int
|
||||||
|
|
||||||
|
acquire := FromLazy(func() int {
|
||||||
|
countAcquire++
|
||||||
|
return countAcquire
|
||||||
|
})
|
||||||
|
|
||||||
|
err := fmt.Errorf("error in release")
|
||||||
|
release := func(int) ReaderIOEither[int] {
|
||||||
|
return Left[int](err)
|
||||||
|
}
|
||||||
|
|
||||||
|
body := func(int) ReaderIOEither[int] {
|
||||||
|
return FromLazy(func() int {
|
||||||
|
countBody++
|
||||||
|
return countBody
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
resRIOE := WithResource[int](acquire, release)(body)
|
||||||
|
|
||||||
|
res := resRIOE(context.Background())()
|
||||||
|
|
||||||
|
assert.Equal(t, 1, countAcquire)
|
||||||
|
assert.Equal(t, 1, countBody)
|
||||||
|
assert.Equal(t, 0, countRelease)
|
||||||
|
assert.Equal(t, E.Left[int](err), res)
|
||||||
|
}
|
||||||
|
@@ -1,29 +0,0 @@
|
|||||||
// 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 file
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
|
|
||||||
IOE "github.com/IBM/fp-go/ioeither"
|
|
||||||
)
|
|
||||||
|
|
||||||
// onClose closes a closeable resource
|
|
||||||
func onClose[R io.Closer](r R) IOE.IOEither[error, R] {
|
|
||||||
return IOE.TryCatchError(func() (R, error) {
|
|
||||||
return r, r.Close()
|
|
||||||
})
|
|
||||||
}
|
|
@@ -16,6 +16,7 @@
|
|||||||
package file
|
package file
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
IOE "github.com/IBM/fp-go/ioeither"
|
IOE "github.com/IBM/fp-go/ioeither"
|
||||||
@@ -45,3 +46,10 @@ func Remove(name string) IOE.IOEither[error, string] {
|
|||||||
return name, os.Remove(name)
|
return name, os.Remove(name)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Close closes an object
|
||||||
|
func Close[C io.Closer](c C) IOE.IOEither[error, any] {
|
||||||
|
return IOE.TryCatchError(func() (any, error) {
|
||||||
|
return c, c.Close()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@@ -31,7 +31,7 @@ func onReadAll[R io.Reader](r R) IOE.IOEither[error, []byte] {
|
|||||||
func ReadAll[R io.ReadCloser](acquire IOE.IOEither[error, R]) IOE.IOEither[error, []byte] {
|
func ReadAll[R io.ReadCloser](acquire IOE.IOEither[error, R]) IOE.IOEither[error, []byte] {
|
||||||
return IOE.WithResource[[]byte](
|
return IOE.WithResource[[]byte](
|
||||||
acquire,
|
acquire,
|
||||||
onClose[R])(
|
Close[R])(
|
||||||
onReadAll[R],
|
onReadAll[R],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@@ -38,7 +38,7 @@ func TestWithTempFileOnClosedFile(t *testing.T) {
|
|||||||
return F.Pipe2(
|
return F.Pipe2(
|
||||||
f,
|
f,
|
||||||
onWriteAll[*os.File]([]byte("Carsten")),
|
onWriteAll[*os.File]([]byte("Carsten")),
|
||||||
IOE.ChainFirst(F.Constant1[[]byte](onClose(f))),
|
IOE.ChainFirst(F.Constant1[[]byte](Close(f))),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@@ -36,7 +36,7 @@ func WriteAll[W io.WriteCloser](data []byte) func(acquire IOE.IOEither[error, W]
|
|||||||
return func(onCreate IOE.IOEither[error, W]) IOE.IOEither[error, []byte] {
|
return func(onCreate IOE.IOEither[error, W]) IOE.IOEither[error, []byte] {
|
||||||
return IOE.WithResource[[]byte](
|
return IOE.WithResource[[]byte](
|
||||||
onCreate,
|
onCreate,
|
||||||
onClose[W])(
|
Close[W])(
|
||||||
onWrite,
|
onWrite,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -46,5 +46,5 @@ func WriteAll[W io.WriteCloser](data []byte) func(acquire IOE.IOEither[error, W]
|
|||||||
func Write[R any, W io.WriteCloser](acquire IOE.IOEither[error, W]) func(use func(W) IOE.IOEither[error, R]) IOE.IOEither[error, R] {
|
func Write[R any, W io.WriteCloser](acquire IOE.IOEither[error, W]) func(use func(W) IOE.IOEither[error, R]) IOE.IOEither[error, R] {
|
||||||
return IOE.WithResource[R](
|
return IOE.WithResource[R](
|
||||||
acquire,
|
acquire,
|
||||||
onClose[W])
|
Close[W])
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user