diff --git a/projects/interfaces/buffer.go b/projects/interfaces/buffer.go new file mode 100644 index 000000000..803f5808a --- /dev/null +++ b/projects/interfaces/buffer.go @@ -0,0 +1,33 @@ +package main + +type OurByteBuffer struct { + bytes []byte + readPosition int +} + +func NewBufferString(s string) OurByteBuffer { + return OurByteBuffer{ + bytes: []byte(s), + readPosition: 0, + } +} + +func (b *OurByteBuffer) Bytes() []byte { + return b.bytes +} + +func (b *OurByteBuffer) Write(bytes []byte) (int, error) { + b.bytes = append(b.bytes, bytes...) + return len(bytes), nil +} + +func (b *OurByteBuffer) Read(to []byte) (int, error) { + remainingBytes := len(b.bytes) - b.readPosition + bytesToRead := len(to) + if remainingBytes < bytesToRead { + bytesToRead = remainingBytes + } + copy(to, b.bytes[b.readPosition:b.readPosition+bytesToRead]) + b.readPosition += bytesToRead + return bytesToRead, nil +} diff --git a/projects/interfaces/buffer_test.go b/projects/interfaces/buffer_test.go new file mode 100644 index 000000000..30f94ac5a --- /dev/null +++ b/projects/interfaces/buffer_test.go @@ -0,0 +1,57 @@ +package main + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestInitialBytesAreRead(t *testing.T) { + want := []byte("hello") + + b := NewBufferString("hello") + + got := b.Bytes() + + require.Equal(t, want, got) +} + +func TestSubsequentWritesAreAppended(t *testing.T) { + want := []byte("hello world") + + b := NewBufferString("hello") + + _, err := b.Write([]byte(" world")) + require.NoError(t, err) + + got := b.Bytes() + + require.Equal(t, want, got) +} + +func TestReadWithSliceBigEnoughForWholeBuffer(t *testing.T) { + b := NewBufferString("hello world") + + slice := make([]byte, 50) + + n, err := b.Read(slice) + require.NoError(t, err) + require.Equal(t, 11, n) + require.Equal(t, []byte("hello world"), slice[:n]) +} + +func TestReadWithSliceSmallerThanWholeBuffer(t *testing.T) { + b := NewBufferString("hello world") + + slice := make([]byte, 6) + + n, err := b.Read(slice) + require.NoError(t, err) + require.Equal(t, 6, n) + require.Equal(t, []byte("hello "), slice) + + n, err = b.Read(slice) + require.NoError(t, err) + require.Equal(t, 5, n) + require.Equal(t, []byte("world"), slice[:n]) +} diff --git a/projects/interfaces/filtering_pipe.go b/projects/interfaces/filtering_pipe.go new file mode 100644 index 000000000..74280f14c --- /dev/null +++ b/projects/interfaces/filtering_pipe.go @@ -0,0 +1,27 @@ +package main + +import ( + "io" +) + +type FilteringPipe struct { + writer io.Writer +} + +func NewFilteringPipe(writer io.Writer) FilteringPipe { + return FilteringPipe{ + writer: writer, + } +} + +func (fp *FilteringPipe) Write(bytes []byte) (int, error) { + for i := range bytes { + if bytes[i] < '0' || bytes[i] > '9' { + if _, err := fp.writer.Write(bytes[i : i+1]); err != nil { + return i, err + } + } + } + // We return len(bytes) because io.Writer is documented to return how many bytes were processed, not how many were actually used. + return len(bytes), nil +} diff --git a/projects/interfaces/filtering_pipe_test.go b/projects/interfaces/filtering_pipe_test.go new file mode 100644 index 000000000..097ad74f1 --- /dev/null +++ b/projects/interfaces/filtering_pipe_test.go @@ -0,0 +1,55 @@ +package main + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestWriteNoNumbers(t *testing.T) { + buf := bytes.NewBufferString("") + + fp := NewFilteringPipe(buf) + + n, err := fp.Write([]byte("hello")) + require.NoError(t, err) + require.Equal(t, 5, n) + require.Equal(t, "hello", buf.String()) +} + +func TestWriteJustNumbers(t *testing.T) { + buf := bytes.NewBufferString("") + + fp := NewFilteringPipe(buf) + + n, err := fp.Write([]byte("123")) + require.NoError(t, err) + require.Equal(t, 3, n) + require.Equal(t, "", buf.String()) +} + +func TestMultipleWrites(t *testing.T) { + buf := bytes.NewBufferString("") + + fp := NewFilteringPipe(buf) + + n, err := fp.Write([]byte("start=")) + require.NoError(t, err) + require.Equal(t, 6, n) + n, err = fp.Write([]byte("1, end=10")) + require.NoError(t, err) + require.Equal(t, 9, n) + require.Equal(t, "start=, end=", buf.String()) +} + +func TestWriteMixedNumbersAndLetters(t *testing.T) { + buf := bytes.NewBufferString("") + + fp := NewFilteringPipe(buf) + + n, err := fp.Write([]byte("start=1, end=10")) + require.NoError(t, err) + require.Equal(t, 15, n) + require.Equal(t, "start=, end=", buf.String()) +} diff --git a/projects/interfaces/go.mod b/projects/interfaces/go.mod new file mode 100644 index 000000000..0687d0bba --- /dev/null +++ b/projects/interfaces/go.mod @@ -0,0 +1,10 @@ +module github.com/CodeYourFuture/immersive-go-course/projects/interfaces + +go 1.19 + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/stretchr/testify v1.8.2 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/projects/interfaces/go.sum b/projects/interfaces/go.sum new file mode 100644 index 000000000..658c3d650 --- /dev/null +++ b/projects/interfaces/go.sum @@ -0,0 +1,16 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=