From 00458600605e8db25655f9abb95cac5bb78b3c55 Mon Sep 17 00:00:00 2001 From: Joe Tsai Date: Wed, 11 Dec 2024 10:55:21 -0800 Subject: [PATCH] types/iox: add function types for Reader and Writer (#14366) Throughout our codebase we have types that only exist only to implement an io.Reader or io.Writer, when it would have been simpler, cleaner, and more readable to use an inlined function literal that closes over the relevant types. This is arguably more readable since it keeps the semantic logic in place rather than have it be isolated elsewhere. Note that a function literal that closes over some variables is semantic equivalent to declaring a struct with fields and having the Read or Write method mutate those fields. Updates #cleanup Signed-off-by: Joe Tsai --- types/iox/io.go | 23 +++++++++++++++++++++++ types/iox/io_test.go | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 types/iox/io.go create mode 100644 types/iox/io_test.go diff --git a/types/iox/io.go b/types/iox/io.go new file mode 100644 index 000000000..a5ca1be43 --- /dev/null +++ b/types/iox/io.go @@ -0,0 +1,23 @@ +// Copyright (c) Tailscale Inc & AUTHORS +// SPDX-License-Identifier: BSD-3-Clause + +// Package iox provides types to implement [io] functionality. +package iox + +// TODO(https://go.dev/issue/21670): Deprecate or remove this functionality +// once the Go language supports implementing an 1-method interface directly +// using a function value of a matching signature. + +// ReaderFunc implements [io.Reader] using the underlying function value. +type ReaderFunc func([]byte) (int, error) + +func (f ReaderFunc) Read(b []byte) (int, error) { + return f(b) +} + +// WriterFunc implements [io.Writer] using the underlying function value. +type WriterFunc func([]byte) (int, error) + +func (f WriterFunc) Write(b []byte) (int, error) { + return f(b) +} diff --git a/types/iox/io_test.go b/types/iox/io_test.go new file mode 100644 index 000000000..9fba39605 --- /dev/null +++ b/types/iox/io_test.go @@ -0,0 +1,39 @@ +// Copyright (c) Tailscale Inc & AUTHORS +// SPDX-License-Identifier: BSD-3-Clause + +package iox + +import ( + "bytes" + "io" + "testing" + "testing/iotest" + + "tailscale.com/util/must" +) + +func TestCopy(t *testing.T) { + const testdata = "the quick brown fox jumped over the lazy dog" + src := testdata + bb := new(bytes.Buffer) + if got := must.Get(io.Copy(bb, ReaderFunc(func(b []byte) (n int, err error) { + n = copy(b[:min(len(b), 7)], src) + src = src[n:] + if len(src) == 0 { + err = io.EOF + } + return n, err + }))); int(got) != len(testdata) { + t.Errorf("copy = %d, want %d", got, len(testdata)) + } + var dst []byte + if got := must.Get(io.Copy(WriterFunc(func(b []byte) (n int, err error) { + dst = append(dst, b...) + return len(b), nil + }), iotest.OneByteReader(bb))); int(got) != len(testdata) { + t.Errorf("copy = %d, want %d", got, len(testdata)) + } + if string(dst) != testdata { + t.Errorf("copy = %q, want %q", dst, testdata) + } +}