Repository: jhillyerd/enmime
Pull request #316 (2023-12-27 19:27:40)

Return public `net/textproto` error types
To allow for checking returned error types, types from the standard library's `net/textproto` must be used instead of types from the private `textproto` fork.

Kind of related to #294.

coveralls: 
[![Coverage Status](https://coveralls.io/builds/64777073/badge)](https://coveralls.io/builds/64777073)

coverage: 86.272% (+0.06%) from 86.213%
when pulling **60b0e78392236a5b0d049c606520287601b1b598 on dcormier:main**
into **ed0394eda018ea5b8f6e81f9e71e7ab68c5b7a91 on jhillyerd:main**.


Patch:
diff --git a/internal/textproto/reader.go b/internal/textproto/reader.go
index 010396d..a0ae11e 100644
--- a/internal/textproto/reader.go
+++ b/internal/textproto/reader.go
@@ -11,6 +11,7 @@ import (
 	"fmt"
 	"io"
 	"math"
+	"net/textproto"
 	"strconv"
 	"strings"
 	"sync"
@@ -198,20 +199,20 @@ func (r *Reader) readCodeLine(expectCode int) (code int, continued bool, message
 
 func parseCodeLine(line string, expectCode int) (code int, continued bool, message string, err error) {
 	if len(line) < 4 || line[3] != ' ' && line[3] != '-' {
-		err = ProtocolError("short response: " + line)
+		err = textproto.ProtocolError("short response: " + line)
 		return
 	}
 	continued = line[3] == '-'
 	code, err = strconv.Atoi(line[0:3])
 	if err != nil || code < 100 {
-		err = ProtocolError("invalid response code: " + line)
+		err = textproto.ProtocolError("invalid response code: " + line)
 		return
 	}
 	message = line[4:]
 	if 1 <= expectCode && expectCode < 10 && code/100 != expectCode ||
 		10 <= expectCode && expectCode < 100 && code/10 != expectCode ||
 		100 <= expectCode && expectCode < 1000 && code != expectCode {
-		err = &Error{code, message}
+		err = &textproto.Error{Code: code, Msg: message}
 	}
 	return
 }
@@ -236,7 +237,7 @@ func parseCodeLine(line string, expectCode int) (code int, continued bool, messa
 func (r *Reader) ReadCodeLine(expectCode int) (code int, message string, err error) {
 	code, continued, message, err := r.readCodeLine(expectCode)
 	if err == nil && continued {
-		err = ProtocolError("unexpected multi-line response: " + message)
+		err = textproto.ProtocolError("unexpected multi-line response: " + message)
 	}
 	return
 }
@@ -288,7 +289,7 @@ func (r *Reader) ReadResponse(expectCode int) (code int, message string, err err
 	}
 	if err != nil && multi && message != "" {
 		// replace one line error message with all lines (full message)
-		err = &Error{code, message}
+		err = &textproto.Error{Code: code, Msg: message}
 	}
 	return
 }
@@ -506,7 +507,7 @@ func readMIMEHeader(r *Reader, lim int64) (MIMEHeader, error) {
 		if err != nil {
 			return m, err
 		}
-		return m, ProtocolError("malformed MIME header initial line: " + string(line))
+		return m, textproto.ProtocolError("malformed MIME header initial line: " + string(line))
 	}
 
 	for {
@@ -518,15 +519,15 @@ func readMIMEHeader(r *Reader, lim int64) (MIMEHeader, error) {
 		// Key ends at first colon.
 		k, v, ok := bytes.Cut(kv, colon)
 		if !ok {
-			return m, ProtocolError("malformed MIME header line: " + string(kv))
+			return m, textproto.ProtocolError("malformed MIME header line: " + string(kv))
 		}
 		key, ok := canonicalMIMEHeaderKey(k)
 		if !ok {
-			return m, ProtocolError("malformed MIME header line: " + string(kv))
+			return m, textproto.ProtocolError("malformed MIME header line: " + string(kv))
 		}
 		for _, c := range v {
 			if !validHeaderValueByte(c) {
-				return m, ProtocolError("malformed MIME header line: " + string(kv))
+				return m, textproto.ProtocolError("malformed MIME header line: " + string(kv))
 			}
 		}
 
@@ -578,7 +579,7 @@ func noValidation(_ []byte) error { return nil }
 // contain a colon.
 func mustHaveFieldNameColon(line []byte) error {
 	if bytes.IndexByte(line, ':') < 0 {
-		return ProtocolError(fmt.Sprintf("malformed MIME header: missing colon: %q", line))
+		return textproto.ProtocolError(fmt.Sprintf("malformed MIME header: missing colon: %q", line))
 	}
 	return nil
 }
diff --git a/internal/textproto/reader_email.go b/internal/textproto/reader_email.go
index 0cc8e36..0b49509 100644
--- a/internal/textproto/reader_email.go
+++ b/internal/textproto/reader_email.go
@@ -4,6 +4,7 @@ import (
 	"bytes"
 	"errors"
 	"math"
+	"net/textproto"
 )
 
 // ReadEmailMIMEHeader reads a MIME-style header from r.
@@ -32,7 +33,7 @@ func readEmailMIMEHeader(r *Reader, lim int64) (MIMEHeader, error) {
 		if err != nil {
 			return m, err
 		}
-		return m, ProtocolError("malformed MIME header initial line: " + string(line))
+		return m, textproto.ProtocolError("malformed MIME header initial line: " + string(line))
 	}
 
 	for {
@@ -44,11 +45,11 @@ func readEmailMIMEHeader(r *Reader, lim int64) (MIMEHeader, error) {
 		// Key ends at first colon.
 		k, v, ok := bytes.Cut(kv, colon)
 		if !ok {
-			return m, ProtocolError("malformed MIME header line: " + string(kv))
+			return m, textproto.ProtocolError("malformed MIME header line: " + string(kv))
 		}
 		key, ok := canonicalEmailMIMEHeaderKey(k)
 		if !ok {
-			return m, ProtocolError("malformed MIME header line: " + string(kv))
+			return m, textproto.ProtocolError("malformed MIME header line: " + string(kv))
 		}
 		// for _, c := range v {
 		// 	if !validHeaderValueByte(c) {
diff --git a/internal/textproto/textproto.go b/internal/textproto/textproto.go
index 70038d5..0d92bf2 100644
--- a/internal/textproto/textproto.go
+++ b/internal/textproto/textproto.go
@@ -26,29 +26,10 @@ package textproto
 
 import (
 	"bufio"
-	"fmt"
 	"io"
 	"net"
 )
 
-// An Error represents a numeric error response from a server.
-type Error struct {
-	Code int
-	Msg  string
-}
-
-func (e *Error) Error() string {
-	return fmt.Sprintf("%03d %s", e.Code, e.Msg)
-}
-
-// A ProtocolError describes a protocol violation such
-// as an invalid response or a hung-up connection.
-type ProtocolError string
-
-func (p ProtocolError) Error() string {
-	return string(p)
-}
-
 // A Conn represents a textual network protocol connection.
 // It consists of a Reader and Writer to manage I/O
 // and a Pipeline to sequence concurrent requests on the connection.


Test patch:
diff --git a/inspect_test.go b/inspect_test.go
index 58f22ad..e65a83b 100644
--- a/inspect_test.go
+++ b/inspect_test.go
@@ -4,12 +4,12 @@ import (
 	"bytes"
 	"io"
 	"net/mail"
+	"net/textproto"
 	"strings"
 	"testing"
 
 	"github.com/jhillyerd/enmime"
 	"github.com/jhillyerd/enmime/internal/test"
-	"github.com/jhillyerd/enmime/internal/textproto"
 )
 
 func TestDecodeRFC2047(t *testing.T) {
diff --git a/internal/textproto/reader_test.go b/internal/textproto/reader_test.go
index 2c7d2aa..f6c728d 100644
--- a/internal/textproto/reader_test.go
+++ b/internal/textproto/reader_test.go
@@ -9,6 +9,7 @@ import (
 	"bytes"
 	"io"
 	"net"
+	"net/textproto"
 	"reflect"
 	"strings"
 	"sync"
@@ -69,7 +70,7 @@ func TestReadCodeLine(t *testing.T) {
 	if code != 345 || msg != "no way" || err == nil {
 		t.Fatalf("Line 3: %d, %s, %v", code, msg, err)
 	}
-	if e, ok := err.(*Error); !ok || e.Code != code || e.Msg != msg {
+	if e, ok := err.(*textproto.Error); !ok || e.Code != code || e.Msg != msg {
 		t.Fatalf("Line 3: wrong error %v\n", err)
 	}
 	code, msg, err = r.ReadCodeLine(1)