diff --git a/notfound.go b/notfound.go new file mode 100644 index 0000000..8bfffa9 --- /dev/null +++ b/notfound.go @@ -0,0 +1,50 @@ +package eol + +import ( + "fmt" +) + +var _ error = internalNotFoundError{} + +type internalNotFoundError struct{ + expected rune + actual rune +} + +func (receiver internalNotFoundError) Error() string { + + + var buffer [256]byte + var p []byte = buffer[0:0] + + var expected rune = receiver.expected + var actual rune = receiver.actual + + switch expected { + case lf: + var s string = fmt.Sprintf(`eol: line-feed (LF) character ('\n') (U+000A) not found — instead found %q (%U)`, actual, actual) + p = append(p, s...) + case cr: + var s string = fmt.Sprintf(`eol: carriage-return (CR) character ('\r') (U+000D) not found — instead found %q (%U)`, actual, actual) + p = append(p, s...) + case nel: + var s string = fmt.Sprintf(`eol: next-line (NEL) character (U+0085) not found — instead found %q (%U)`, actual, actual) + p = append(p, s...) + case ls: + var s string = fmt.Sprintf(`eol: line-separator (LS) character (U+2028) not found — instead found %q (%U)`, actual, actual) + p = append(p, s...) + default: + var s string = fmt.Sprintf(`eol: %q character (%U) not found — instead found %q (%U)`, expected, expected, actual, actual) + p = append(p, s...) + } + + return string(p) +} + +func (receiver internalNotFoundError) Actual() rune { + return receiver.actual +} + +func (receiver internalNotFoundError) Expected() rune { + return receiver.expected +} diff --git a/readcr.go b/readcr.go new file mode 100644 index 0000000..764f4a7 --- /dev/null +++ b/readcr.go @@ -0,0 +1,9 @@ +package eol + +import ( + "io" +) + +func ReadCR(runescanner io.RuneScanner) (size int, err error) { + return readthisrune(runescanner, cr) +} diff --git a/readcr_test.go b/readcr_test.go new file mode 100644 index 0000000..1d548c2 --- /dev/null +++ b/readcr_test.go @@ -0,0 +1,209 @@ +package eol_test + +import ( + "testing" + + "io" + "strings" + + "sourcecode.social/reiver/go-utf8" + + "sourcecode.social/reiver/go-eol" +) + +func TestReadCR(t *testing.T) { + + tests := []struct{ + Value string + ExpectedSize int + }{ + { + Value: "\r", + ExpectedSize: 1, + }, + + + + { + Value: "\rapple banana cherry", + ExpectedSize: 1, + }, + } + + for testNumber, test := range tests { + + var reader io.Reader = strings.NewReader(test.Value) + var runescanner io.RuneScanner = utf8.NewRuneScanner(reader) + + actualSize, err := eol.ReadCR(runescanner) + if nil != err { + t.Errorf("For test #%d, did not expect an error but actually got one.", testNumber) + t.Logf("ERROR: (%T) %s", err, err) + t.Logf("VALUE: %q", test.Value) + continue + } + + { + expected := test.ExpectedSize + actual := actualSize + + if expected != actual { + t.Errorf("For test #%d, the actual size is not what was expected.", testNumber) + t.Logf("EXPECTED: %d", expected) + t.Logf("ACTUAL: %d", actual) + t.Logf("VALUE: %q", test.Value) + continue + } + } + + } +} + +func TestReadCR_fail(t *testing.T) { + + tests := []struct{ + Value string + ExpectedError string + }{ + { + Value: "", + ExpectedError: `eol: problem reading rune №1 of end-of-line sequence: EOF`, + }, + + + + { + Value: "\n", + ExpectedError: `eol: carriage-return (CR) character ('\r') (U+000D) not found — instead found '\n' (U+000A)`, + }, + { + Value: "\u0085", + ExpectedError: `eol: carriage-return (CR) character ('\r') (U+000D) not found — instead found '\u0085' (U+0085)`, + }, + { + Value: "\u2028", + ExpectedError: `eol: carriage-return (CR) character ('\r') (U+000D) not found — instead found '\u2028' (U+2028)`, + }, + + + + { + Value: "😈", + ExpectedError: `eol: carriage-return (CR) character ('\r') (U+000D) not found — instead found '😈' (U+1F608)`, + }, + + + + { + Value: "\napple banana cherry", + ExpectedError: `eol: carriage-return (CR) character ('\r') (U+000D) not found — instead found '\n' (U+000A)`, + }, + { + Value: "\u0085apple banana cherry", + ExpectedError: `eol: carriage-return (CR) character ('\r') (U+000D) not found — instead found '\u0085' (U+0085)`, + }, + { + Value: "\u2028apple banana cherry", + ExpectedError: `eol: carriage-return (CR) character ('\r') (U+000D) not found — instead found '\u2028' (U+2028)`, + }, + + + + { + Value: "😈apple banana cherry", + ExpectedError: `eol: carriage-return (CR) character ('\r') (U+000D) not found — instead found '😈' (U+1F608)`, + }, + + + + { + Value: " \n", + ExpectedError: `eol: carriage-return (CR) character ('\r') (U+000D) not found — instead found ' ' (U+0020)`, + }, + { + Value: " \r", + ExpectedError: `eol: carriage-return (CR) character ('\r') (U+000D) not found — instead found ' ' (U+0020)`, + }, + { + Value: " \u0085", + ExpectedError: `eol: carriage-return (CR) character ('\r') (U+000D) not found — instead found ' ' (U+0020)`, + }, + { + Value: " \u2028", + ExpectedError: `eol: carriage-return (CR) character ('\r') (U+000D) not found — instead found ' ' (U+0020)`, + }, + + + + { + Value: " 😈", + ExpectedError: `eol: carriage-return (CR) character ('\r') (U+000D) not found — instead found ' ' (U+0020)`, + }, + + + + { + Value: ".\n", + ExpectedError: `eol: carriage-return (CR) character ('\r') (U+000D) not found — instead found '.' (U+002E)`, + }, + { + Value: ".\r", + ExpectedError: `eol: carriage-return (CR) character ('\r') (U+000D) not found — instead found '.' (U+002E)`, + }, + { + Value: ".\u0085", + ExpectedError: `eol: carriage-return (CR) character ('\r') (U+000D) not found — instead found '.' (U+002E)`, + }, + { + Value: ".\u2028", + ExpectedError: `eol: carriage-return (CR) character ('\r') (U+000D) not found — instead found '.' (U+002E)`, + }, + + + + { + Value: ".😈", + ExpectedError: `eol: carriage-return (CR) character ('\r') (U+000D) not found — instead found '.' (U+002E)`, + }, + } + + for testNumber, test := range tests { + + var reader io.Reader = strings.NewReader(test.Value) + var runescanner io.RuneScanner = utf8.NewRuneScanner(reader) + + actualSize, err := eol.ReadCR(runescanner) + if nil == err { + t.Errorf("For test #%d, expected an error but did not actually get one.", testNumber) + t.Logf("EXPECTED-ERROR: %q", test.ExpectedError) + t.Logf("VALUE: %q", test.Value) + continue + } + + { + expected := test.ExpectedError + actual := err.Error() + + if expected != actual { + t.Errorf("For test #%d, the actual error is not what was expected.", testNumber) + t.Logf("EXPECTED: %q", expected) + t.Logf("ACTUAL: %q", actual) + t.Logf("VALUE: %q", test.Value) + continue + } + } + + { + expected := 0 + actual := actualSize + + if expected != actual { + t.Errorf("For test #%d, the actual size is not what was expected.", testNumber) + t.Logf("EXPECTED: %d", expected) + t.Logf("ACTUAL: %d", actual) + t.Logf("VALUE: %q", test.Value) + continue + } + } + } +} diff --git a/readlf.go b/readlf.go new file mode 100644 index 0000000..1244aa9 --- /dev/null +++ b/readlf.go @@ -0,0 +1,9 @@ +package eol + +import ( + "io" +) + +func ReadLF(runescanner io.RuneScanner) (size int, err error) { + return readthisrune(runescanner, lf) +} diff --git a/readlf_test.go b/readlf_test.go new file mode 100644 index 0000000..b9727ee --- /dev/null +++ b/readlf_test.go @@ -0,0 +1,209 @@ +package eol_test + +import ( + "testing" + + "io" + "strings" + + "sourcecode.social/reiver/go-utf8" + + "sourcecode.social/reiver/go-eol" +) + +func TestReadLF(t *testing.T) { + + tests := []struct{ + Value string + ExpectedSize int + }{ + { + Value: "\n", + ExpectedSize: 1, + }, + + + + { + Value: "\napple banana cherry", + ExpectedSize: 1, + }, + } + + for testNumber, test := range tests { + + var reader io.Reader = strings.NewReader(test.Value) + var runescanner io.RuneScanner = utf8.NewRuneScanner(reader) + + actualSize, err := eol.ReadLF(runescanner) + if nil != err { + t.Errorf("For test #%d, did not expect an error but actually got one.", testNumber) + t.Logf("ERROR: (%T) %s", err, err) + t.Logf("VALUE: %q", test.Value) + continue + } + + { + expected := test.ExpectedSize + actual := actualSize + + if expected != actual { + t.Errorf("For test #%d, the actual size is not what was expected.", testNumber) + t.Logf("EXPECTED: %d", expected) + t.Logf("ACTUAL: %d", actual) + t.Logf("VALUE: %q", test.Value) + continue + } + } + + } +} + +func TestReadLF_fail(t *testing.T) { + + tests := []struct{ + Value string + ExpectedError string + }{ + { + Value: "", + ExpectedError: `eol: problem reading rune №1 of end-of-line sequence: EOF`, + }, + + + + { + Value: "\r", + ExpectedError: `eol: line-feed (LF) character ('\n') (U+000A) not found — instead found '\r' (U+000D)`, + }, + { + Value: "\u0085", + ExpectedError: `eol: line-feed (LF) character ('\n') (U+000A) not found — instead found '\u0085' (U+0085)`, + }, + { + Value: "\u2028", + ExpectedError: `eol: line-feed (LF) character ('\n') (U+000A) not found — instead found '\u2028' (U+2028)`, + }, + + + + { + Value: "😈", + ExpectedError: `eol: line-feed (LF) character ('\n') (U+000A) not found — instead found '😈' (U+1F608)`, + }, + + + + { + Value: "\rapple banana cherry", + ExpectedError: `eol: line-feed (LF) character ('\n') (U+000A) not found — instead found '\r' (U+000D)`, + }, + { + Value: "\u0085apple banana cherry", + ExpectedError: `eol: line-feed (LF) character ('\n') (U+000A) not found — instead found '\u0085' (U+0085)`, + }, + { + Value: "\u2028apple banana cherry", + ExpectedError: `eol: line-feed (LF) character ('\n') (U+000A) not found — instead found '\u2028' (U+2028)`, + }, + + + + { + Value: "😈apple banana cherry", + ExpectedError: `eol: line-feed (LF) character ('\n') (U+000A) not found — instead found '😈' (U+1F608)`, + }, + + + + { + Value: " \n", + ExpectedError: `eol: line-feed (LF) character ('\n') (U+000A) not found — instead found ' ' (U+0020)`, + }, + { + Value: " \r", + ExpectedError: `eol: line-feed (LF) character ('\n') (U+000A) not found — instead found ' ' (U+0020)`, + }, + { + Value: " \u0085", + ExpectedError: `eol: line-feed (LF) character ('\n') (U+000A) not found — instead found ' ' (U+0020)`, + }, + { + Value: " \u2028", + ExpectedError: `eol: line-feed (LF) character ('\n') (U+000A) not found — instead found ' ' (U+0020)`, + }, + + + + { + Value: " 😈", + ExpectedError: `eol: line-feed (LF) character ('\n') (U+000A) not found — instead found ' ' (U+0020)`, + }, + + + + { + Value: ".\n", + ExpectedError: `eol: line-feed (LF) character ('\n') (U+000A) not found — instead found '.' (U+002E)`, + }, + { + Value: ".\r", + ExpectedError: `eol: line-feed (LF) character ('\n') (U+000A) not found — instead found '.' (U+002E)`, + }, + { + Value: ".\u0085", + ExpectedError: `eol: line-feed (LF) character ('\n') (U+000A) not found — instead found '.' (U+002E)`, + }, + { + Value: ".\u2028", + ExpectedError: `eol: line-feed (LF) character ('\n') (U+000A) not found — instead found '.' (U+002E)`, + }, + + + + { + Value: ".😈", + ExpectedError: `eol: line-feed (LF) character ('\n') (U+000A) not found — instead found '.' (U+002E)`, + }, + } + + for testNumber, test := range tests { + + var reader io.Reader = strings.NewReader(test.Value) + var runescanner io.RuneScanner = utf8.NewRuneScanner(reader) + + actualSize, err := eol.ReadLF(runescanner) + if nil == err { + t.Errorf("For test #%d, expected an error but did not actually get one.", testNumber) + t.Logf("EXPECTED-ERROR: %q", test.ExpectedError) + t.Logf("VALUE: %q", test.Value) + continue + } + + { + expected := test.ExpectedError + actual := err.Error() + + if expected != actual { + t.Errorf("For test #%d, the actual error is not what was expected.", testNumber) + t.Logf("EXPECTED: %q", expected) + t.Logf("ACTUAL: %q", actual) + t.Logf("VALUE: %q", test.Value) + continue + } + } + + { + expected := 0 + actual := actualSize + + if expected != actual { + t.Errorf("For test #%d, the actual size is not what was expected.", testNumber) + t.Logf("EXPECTED: %d", expected) + t.Logf("ACTUAL: %d", actual) + t.Logf("VALUE: %q", test.Value) + continue + } + } + } +} diff --git a/readls.go b/readls.go new file mode 100644 index 0000000..20dbeb2 --- /dev/null +++ b/readls.go @@ -0,0 +1,9 @@ +package eol + +import ( + "io" +) + +func ReadLS(runescanner io.RuneScanner) (size int, err error) { + return readthisrune(runescanner, ls) +} diff --git a/readls_test.go b/readls_test.go new file mode 100644 index 0000000..aca26ad --- /dev/null +++ b/readls_test.go @@ -0,0 +1,209 @@ +package eol_test + +import ( + "testing" + + "io" + "strings" + + "sourcecode.social/reiver/go-utf8" + + "sourcecode.social/reiver/go-eol" +) + +func TestReadLS(t *testing.T) { + + tests := []struct{ + Value string + ExpectedSize int + }{ + { + Value: "\u2028", + ExpectedSize: 3, + }, + + + + { + Value: "\u2028apple banana cherry", + ExpectedSize: 3, + }, + } + + for testNumber, test := range tests { + + var reader io.Reader = strings.NewReader(test.Value) + var runescanner io.RuneScanner = utf8.NewRuneScanner(reader) + + actualSize, err := eol.ReadLS(runescanner) + if nil != err { + t.Errorf("For test #%d, did not expect an error but actually got one.", testNumber) + t.Logf("ERROR: (%T) %s", err, err) + t.Logf("VALUE: %q", test.Value) + continue + } + + { + expected := test.ExpectedSize + actual := actualSize + + if expected != actual { + t.Errorf("For test #%d, the actual size is not what was expected.", testNumber) + t.Logf("EXPECTED: %d", expected) + t.Logf("ACTUAL: %d", actual) + t.Logf("VALUE: %q", test.Value) + continue + } + } + + } +} + +func TestReadLS_fail(t *testing.T) { + + tests := []struct{ + Value string + ExpectedError string + }{ + { + Value: "", + ExpectedError: `eol: problem reading rune №1 of end-of-line sequence: EOF`, + }, + + + + { + Value: "\n", + ExpectedError: `eol: line-separator (LS) character (U+2028) not found — instead found '\n' (U+000A)`, + }, + { + Value: "\r", + ExpectedError: `eol: line-separator (LS) character (U+2028) not found — instead found '\r' (U+000D)`, + }, + { + Value: "\u0085", + ExpectedError: `eol: line-separator (LS) character (U+2028) not found — instead found '\u0085' (U+0085)`, + }, + + + + { + Value: "😈", + ExpectedError: `eol: line-separator (LS) character (U+2028) not found — instead found '😈' (U+1F608)`, + }, + + + + { + Value: "\napple banana cherry", + ExpectedError: `eol: line-separator (LS) character (U+2028) not found — instead found '\n' (U+000A)`, + }, + { + Value: "\rapple banana cherry", + ExpectedError: `eol: line-separator (LS) character (U+2028) not found — instead found '\r' (U+000D)`, + }, + { + Value: "\u0085apple banana cherry", + ExpectedError: `eol: line-separator (LS) character (U+2028) not found — instead found '\u0085' (U+0085)`, + }, + + + + { + Value: "😈apple banana cherry", + ExpectedError: `eol: line-separator (LS) character (U+2028) not found — instead found '😈' (U+1F608)`, + }, + + + + { + Value: " \n", + ExpectedError: `eol: line-separator (LS) character (U+2028) not found — instead found ' ' (U+0020)`, + }, + { + Value: " \r", + ExpectedError: `eol: line-separator (LS) character (U+2028) not found — instead found ' ' (U+0020)`, + }, + { + Value: " \u0085", + ExpectedError: `eol: line-separator (LS) character (U+2028) not found — instead found ' ' (U+0020)`, + }, + { + Value: " \u2028", + ExpectedError: `eol: line-separator (LS) character (U+2028) not found — instead found ' ' (U+0020)`, + }, + + + + { + Value: " 😈", + ExpectedError: `eol: line-separator (LS) character (U+2028) not found — instead found ' ' (U+0020)`, + }, + + + + { + Value: ".\n", + ExpectedError: `eol: line-separator (LS) character (U+2028) not found — instead found '.' (U+002E)`, + }, + { + Value: ".\r", + ExpectedError: `eol: line-separator (LS) character (U+2028) not found — instead found '.' (U+002E)`, + }, + { + Value: ".\u0085", + ExpectedError: `eol: line-separator (LS) character (U+2028) not found — instead found '.' (U+002E)`, + }, + { + Value: ".\u2028", + ExpectedError: `eol: line-separator (LS) character (U+2028) not found — instead found '.' (U+002E)`, + }, + + + + { + Value: ".😈", + ExpectedError: `eol: line-separator (LS) character (U+2028) not found — instead found '.' (U+002E)`, + }, + } + + for testNumber, test := range tests { + + var reader io.Reader = strings.NewReader(test.Value) + var runescanner io.RuneScanner = utf8.NewRuneScanner(reader) + + actualSize, err := eol.ReadLS(runescanner) + if nil == err { + t.Errorf("For test #%d, expected an error but did not actually get one.", testNumber) + t.Logf("EXPECTED-ERROR: %q", test.ExpectedError) + t.Logf("VALUE: %q", test.Value) + continue + } + + { + expected := test.ExpectedError + actual := err.Error() + + if expected != actual { + t.Errorf("For test #%d, the actual error is not what was expected.", testNumber) + t.Logf("EXPECTED: %q", expected) + t.Logf("ACTUAL: %q", actual) + t.Logf("VALUE: %q", test.Value) + continue + } + } + + { + expected := 0 + actual := actualSize + + if expected != actual { + t.Errorf("For test #%d, the actual size is not what was expected.", testNumber) + t.Logf("EXPECTED: %d", expected) + t.Logf("ACTUAL: %d", actual) + t.Logf("VALUE: %q", test.Value) + continue + } + } + } +} diff --git a/readnel.go b/readnel.go new file mode 100644 index 0000000..5c412a0 --- /dev/null +++ b/readnel.go @@ -0,0 +1,9 @@ +package eol + +import ( + "io" +) + +func ReadNEL(runescanner io.RuneScanner) (size int, err error) { + return readthisrune(runescanner, nel) +} diff --git a/readnel_test.go b/readnel_test.go new file mode 100644 index 0000000..b501cca --- /dev/null +++ b/readnel_test.go @@ -0,0 +1,209 @@ +package eol_test + +import ( + "testing" + + "io" + "strings" + + "sourcecode.social/reiver/go-utf8" + + "sourcecode.social/reiver/go-eol" +) + +func TestReadNEL(t *testing.T) { + + tests := []struct{ + Value string + ExpectedSize int + }{ + { + Value: "\u0085", + ExpectedSize: 2, + }, + + + + { + Value: "\u0085apple banana cherry", + ExpectedSize: 2, + }, + } + + for testNumber, test := range tests { + + var reader io.Reader = strings.NewReader(test.Value) + var runescanner io.RuneScanner = utf8.NewRuneScanner(reader) + + actualSize, err := eol.ReadNEL(runescanner) + if nil != err { + t.Errorf("For test #%d, did not expect an error but actually got one.", testNumber) + t.Logf("ERROR: (%T) %s", err, err) + t.Logf("VALUE: %q", test.Value) + continue + } + + { + expected := test.ExpectedSize + actual := actualSize + + if expected != actual { + t.Errorf("For test #%d, the actual size is not what was expected.", testNumber) + t.Logf("EXPECTED: %d", expected) + t.Logf("ACTUAL: %d", actual) + t.Logf("VALUE: %q", test.Value) + continue + } + } + + } +} + +func TestReadNEL_fail(t *testing.T) { + + tests := []struct{ + Value string + ExpectedError string + }{ + { + Value: "", + ExpectedError: `eol: problem reading rune №1 of end-of-line sequence: EOF`, + }, + + + + { + Value: "\n", + ExpectedError: `eol: next-line (NEL) character (U+0085) not found — instead found '\n' (U+000A)`, + }, + { + Value: "\r", + ExpectedError: `eol: next-line (NEL) character (U+0085) not found — instead found '\r' (U+000D)`, + }, + { + Value: "\u2028", + ExpectedError: `eol: next-line (NEL) character (U+0085) not found — instead found '\u2028' (U+2028)`, + }, + + + + { + Value: "😈", + ExpectedError: `eol: next-line (NEL) character (U+0085) not found — instead found '😈' (U+1F608)`, + }, + + + + { + Value: "\napple banana cherry", + ExpectedError: `eol: next-line (NEL) character (U+0085) not found — instead found '\n' (U+000A)`, + }, + { + Value: "\rapple banana cherry", + ExpectedError: `eol: next-line (NEL) character (U+0085) not found — instead found '\r' (U+000D)`, + }, + { + Value: "\u2028apple banana cherry", + ExpectedError: `eol: next-line (NEL) character (U+0085) not found — instead found '\u2028' (U+2028)`, + }, + + + + { + Value: "😈apple banana cherry", + ExpectedError: `eol: next-line (NEL) character (U+0085) not found — instead found '😈' (U+1F608)`, + }, + + + + { + Value: " \n", + ExpectedError: `eol: next-line (NEL) character (U+0085) not found — instead found ' ' (U+0020)`, + }, + { + Value: " \r", + ExpectedError: `eol: next-line (NEL) character (U+0085) not found — instead found ' ' (U+0020)`, + }, + { + Value: " \u0085", + ExpectedError: `eol: next-line (NEL) character (U+0085) not found — instead found ' ' (U+0020)`, + }, + { + Value: " \u2028", + ExpectedError: `eol: next-line (NEL) character (U+0085) not found — instead found ' ' (U+0020)`, + }, + + + + { + Value: " 😈", + ExpectedError: `eol: next-line (NEL) character (U+0085) not found — instead found ' ' (U+0020)`, + }, + + + + { + Value: ".\n", + ExpectedError: `eol: next-line (NEL) character (U+0085) not found — instead found '.' (U+002E)`, + }, + { + Value: ".\r", + ExpectedError: `eol: next-line (NEL) character (U+0085) not found — instead found '.' (U+002E)`, + }, + { + Value: ".\u0085", + ExpectedError: `eol: next-line (NEL) character (U+0085) not found — instead found '.' (U+002E)`, + }, + { + Value: ".\u2028", + ExpectedError: `eol: next-line (NEL) character (U+0085) not found — instead found '.' (U+002E)`, + }, + + + + { + Value: ".😈", + ExpectedError: `eol: next-line (NEL) character (U+0085) not found — instead found '.' (U+002E)`, + }, + } + + for testNumber, test := range tests { + + var reader io.Reader = strings.NewReader(test.Value) + var runescanner io.RuneScanner = utf8.NewRuneScanner(reader) + + actualSize, err := eol.ReadNEL(runescanner) + if nil == err { + t.Errorf("For test #%d, expected an error but did not actually get one.", testNumber) + t.Logf("EXPECTED-ERROR: %q", test.ExpectedError) + t.Logf("VALUE: %q", test.Value) + continue + } + + { + expected := test.ExpectedError + actual := err.Error() + + if expected != actual { + t.Errorf("For test #%d, the actual error is not what was expected.", testNumber) + t.Logf("EXPECTED: %q", expected) + t.Logf("ACTUAL: %q", actual) + t.Logf("VALUE: %q", test.Value) + continue + } + } + + { + expected := 0 + actual := actualSize + + if expected != actual { + t.Errorf("For test #%d, the actual size is not what was expected.", testNumber) + t.Logf("EXPECTED: %d", expected) + t.Logf("ACTUAL: %d", actual) + t.Logf("VALUE: %q", test.Value) + continue + } + } + } +} diff --git a/readthisrune.go b/readthisrune.go new file mode 100644 index 0000000..976b00a --- /dev/null +++ b/readthisrune.go @@ -0,0 +1,38 @@ +package eol + +import ( + "io" +) + +func readthisrune(runescanner io.RuneScanner, expected rune) (size int, err error) { + if nil == runescanner { + return 0, errNilRuneScanner + } + + var r rune + { + var err error + + r, size, err = runescanner.ReadRune() + if nil != err { + const runeNumber = 1 + return size, errProblemReadingRune(err, runeNumber) + } + } + + { + actual := r + + if expected != actual { + err := runescanner.UnreadRune() + if nil != err { + const runeNumber = 1 + return size, errProblemUnreadingRune(err, runeNumber, r) + } + + return 0, internalNotFoundError{expected: expected, actual: r} + } + } + + return size, nil +} diff --git a/readthisrune_test.go b/readthisrune_test.go new file mode 100644 index 0000000..d300bfe --- /dev/null +++ b/readthisrune_test.go @@ -0,0 +1,252 @@ +package eol + +import ( + "testing" + + "io" + "strings" + + "sourcecode.social/reiver/go-utf8" +) + +func TestReadThisRune(t *testing.T) { + + tests := []struct{ + Value string + Rune rune + ExpectedSize int + }{ + { + Value: "\n", + Rune: '\n', + ExpectedSize: 1, + }, + { + Value: "\r", + Rune: '\r', + ExpectedSize: 1, + }, + { + Value: "\u0085", + Rune: '\u0085', + ExpectedSize: 2, + }, + { + Value: "\u2028", + Rune: '\u2028', + ExpectedSize: 3, + }, + + + + { + Value: "😈", + Rune: '😈', + ExpectedSize: 4, + }, + + + + { + Value: "\napple banana cherry", + Rune: '\n', + ExpectedSize: 1, + }, + { + Value: "\rapple banana cherry", + Rune: '\r', + ExpectedSize: 1, + }, + { + Value: "\u0085apple banana cherry", + Rune: '\u0085', + ExpectedSize: 2, + }, + { + Value: "\u2028apple banana cherry", + Rune: '\u2028', + ExpectedSize: 3, + }, + + + + { + Value: "😈apple banana cherry", + Rune: '😈', + ExpectedSize: 4, + }, + } + + for testNumber, test := range tests { + + var reader io.Reader = strings.NewReader(test.Value) + var runescanner io.RuneScanner = utf8.NewRuneScanner(reader) + + actualSize, err := readthisrune(runescanner, test.Rune) + if nil != err { + t.Errorf("For test #%d, did not expect an error but actually got one.", testNumber) + t.Logf("ERROR: (%T) %s", err, err) + t.Logf("VALUE: %q", test.Value) + t.Logf("RUNE: %q", test.Rune) + continue + } + + { + expected := test.ExpectedSize + actual := actualSize + + if expected != actual { + t.Errorf("For test #%d, the actual size is not what was expected.", testNumber) + t.Logf("EXPECTED: %d", expected) + t.Logf("ACTUAL: %d", actual) + t.Logf("VALUE: %q", test.Value) + t.Logf("RUNE: %q", test.Rune) + continue + } + } + + } +} + +func TestReadThisRune_fail(t *testing.T) { + + tests := []struct{ + Value string + Rune rune + ExpectedError string + }{ + { + Value: "", + Rune: '\n', + ExpectedError: `eol: problem reading rune №1 of end-of-line sequence: EOF`, + }, + { + Value: "", + Rune: '\r', + ExpectedError: `eol: problem reading rune №1 of end-of-line sequence: EOF`, + }, + { + Value: "", + Rune: '\u0085', + ExpectedError: `eol: problem reading rune №1 of end-of-line sequence: EOF`, + }, + { + Value: "", + Rune: '\u2028', + ExpectedError: `eol: problem reading rune №1 of end-of-line sequence: EOF`, + }, + + + + { + Value: "", + Rune: '😈', + ExpectedError: `eol: problem reading rune №1 of end-of-line sequence: EOF`, + }, + + + + { + Value: " \n", + Rune: '\n', + ExpectedError: `eol: line-feed (LF) character ('\n') (U+000A) not found — instead found ' ' (U+0020)`, + }, + { + Value: " \r", + Rune: '\r', + ExpectedError: `eol: carriage-return (CR) character ('\r') (U+000D) not found — instead found ' ' (U+0020)`, + }, + { + Value: " \u0085", + Rune: '\u0085', + ExpectedError: `eol: next-line (NEL) character (U+0085) not found — instead found ' ' (U+0020)`, + }, + { + Value: " \u2028", + Rune: '\u2028', + ExpectedError: `eol: line-separator (LS) character (U+2028) not found — instead found ' ' (U+0020)`, + }, + + + + { + Value: " 😈", + Rune: '😈', + ExpectedError: `eol: '😈' character (U+1F608) not found — instead found ' ' (U+0020)`, + }, + + + + { + Value: ".\n", + Rune: '\n', + ExpectedError: `eol: line-feed (LF) character ('\n') (U+000A) not found — instead found '.' (U+002E)`, + }, + { + Value: ".\r", + Rune: '\r', + ExpectedError: `eol: carriage-return (CR) character ('\r') (U+000D) not found — instead found '.' (U+002E)`, + }, + { + Value: ".\u0085", + Rune: '\u0085', + ExpectedError: `eol: next-line (NEL) character (U+0085) not found — instead found '.' (U+002E)`, + }, + { + Value: ".\u2028", + Rune: '\u2028', + ExpectedError: `eol: line-separator (LS) character (U+2028) not found — instead found '.' (U+002E)`, + }, + + + + { + Value: ".😈", + Rune: '😈', + ExpectedError: `eol: '😈' character (U+1F608) not found — instead found '.' (U+002E)`, + }, + } + + for testNumber, test := range tests { + + var reader io.Reader = strings.NewReader(test.Value) + var runescanner io.RuneScanner = utf8.NewRuneScanner(reader) + + actualSize, err := readthisrune(runescanner, test.Rune) + if nil == err { + t.Errorf("For test #%d, expected an error but did not actually get one.", testNumber) + t.Logf("EXPECTED-ERROR: %q", test.ExpectedError) + t.Logf("VALUE: %q", test.Value) + t.Logf("RUNE: %q", test.Rune) + continue + } + + { + expected := test.ExpectedError + actual := err.Error() + + if expected != actual { + t.Errorf("For test #%d, the actual error is not what was expected.", testNumber) + t.Logf("EXPECTED: %q", expected) + t.Logf("ACTUAL: %q", actual) + t.Logf("VALUE: %q", test.Value) + t.Logf("RUNE: %q", test.Rune) + continue + } + } + + { + expected := 0 + actual := actualSize + + if expected != actual { + t.Errorf("For test #%d, the actual size is not what was expected.", testNumber) + t.Logf("EXPECTED: %d", expected) + t.Logf("ACTUAL: %d", actual) + t.Logf("VALUE: %q", test.Value) + t.Logf("RUNE: %q", test.Rune) + continue + } + } + } +}