From a77a4e4b52944e4eb58938a676f286d37d699aed Mon Sep 17 00:00:00 2001 From: Arpad Ryszka Date: Sat, 1 Nov 2025 18:22:40 +0100 Subject: [PATCH] flush underlying when implementing flusher --- lib.go | 12 +++++++ lib_test.go | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+) diff --git a/lib.go b/lib.go index a0eaf24..5227a60 100644 --- a/lib.go +++ b/lib.go @@ -140,6 +140,15 @@ func (w *Writer) flush() error { return w.err } + if f, ok := w.out.(interface{ Flush() error }); ok { + if err := f.Flush(); err != nil { + w.err = err + return w.err + } + } else if f, ok := w.out.(interface{ Flush() }); ok { + f.Flush() + } + return nil } @@ -161,6 +170,9 @@ func (w *Writer) WriteRune(r rune) (int, error) { // the underlying io.Writer, but first passes it to the subsequent editors for editing. When the used editor // instances comply with the expectations of the Editor interface, the writer will have a fresh state and can be // reused for further editing. +// +// If the underlying io.Writer also implements Flusher (Flush() error or Flush()), then it will be implicitly +// called. func (w *Writer) Flush() error { return w.flush() } diff --git a/lib_test.go b/lib_test.go index 6c74244..5294628 100644 --- a/lib_test.go +++ b/lib_test.go @@ -15,6 +15,17 @@ type failingWriter struct { fail bool } +type flusherWriter struct { + out io.Writer + fail bool + flushed bool +} + +type flusherWriterNoErr struct { + out io.Writer + flushed bool +} + func (w *failingWriter) Write(p []byte) (int, error) { if w.fail { return 0, errTest @@ -23,6 +34,27 @@ func (w *failingWriter) Write(p []byte) (int, error) { return w.out.Write(p) } +func (w *flusherWriter) Write(p []byte) (int, error) { + return w.out.Write(p) +} + +func (w *flusherWriter) Flush() error { + w.flushed = true + if w.fail { + return errTest + } + + return nil +} + +func (w *flusherWriterNoErr) Write(p []byte) (int, error) { + return w.out.Write(p) +} + +func (w *flusherWriterNoErr) Flush() { + w.flushed = true +} + func TestNoop(t *testing.T) { t.Run("editor", func(t *testing.T) { var b bytes.Buffer @@ -105,3 +137,61 @@ func TestFailingWriter(t *testing.T) { } }) } + +func TestFlush(t *testing.T) { + t.Run("with err succeed", func(t *testing.T) { + var b bytes.Buffer + fw := flusherWriter{out: &b} + w := textedit.New(&fw, textedit.Replace("o", "e")) + if _, err := w.Write([]byte("foo bar")); err != nil { + t.Fatal(err) + } + + if err := w.Flush(); err != nil { + t.Fatal(err) + } + + if b.String() != "fee bar" { + t.Fatal(b.String()) + } + + if !fw.flushed { + t.Fatal("failed to flush underlying flusher") + } + }) + + t.Run("with err fail", func(t *testing.T) { + var b bytes.Buffer + fw := flusherWriter{out: &b} + w := textedit.New(&fw, textedit.Replace("o", "e")) + if _, err := w.Write([]byte("foo bar")); err != nil { + t.Fatal(err) + } + + fw.fail = true + if err := w.Flush(); !errors.Is(err, errTest) { + t.Fatal("failed to fail with the right error", err) + } + }) + + t.Run("without err", func(t *testing.T) { + var b bytes.Buffer + fw := flusherWriterNoErr{out: &b} + w := textedit.New(&fw, textedit.Replace("o", "e")) + if _, err := w.Write([]byte("foo bar")); err != nil { + t.Fatal(err) + } + + if err := w.Flush(); err != nil { + t.Fatal(err) + } + + if b.String() != "fee bar" { + t.Fatal(b.String()) + } + + if !fw.flushed { + t.Fatal("failed to flush underlying flusher") + } + }) +}