Skip to content

Commit

Permalink
fix: use io.Reader; fixes #27
Browse files Browse the repository at this point in the history
  • Loading branch information
subosito committed Aug 15, 2023
1 parent 04a420c commit fe76f50
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 28 deletions.
37 changes: 19 additions & 18 deletions gotenv.go
Expand Up @@ -55,12 +55,12 @@ func Must(fn func(filenames ...string) error, filenames ...string) {
}

// Apply is a function to load an io Reader then export the valid variables into environment variables if they do not exist.
func Apply(r Reader) error {
func Apply(r io.Reader) error {
return parset(r, false)
}

// OverApply is a function to load an io Reader then export and override the valid variables into environment variables.
func OverApply(r Reader) error {
func OverApply(r io.Reader) error {
return parset(r, true)
}

Expand All @@ -86,7 +86,7 @@ func loadenv(override bool, filenames ...string) error {
}

// parse and set :)
func parset(r Reader, override bool) error {
func parset(r io.Reader, override bool) error {
env, err := strictParse(r, override)
if err != nil {
return err
Expand All @@ -112,15 +112,15 @@ func setenv(key, val string, override bool) {
// Parse is a function to parse line by line any io.Reader supplied and returns the valid Env key/value pair of valid variables.
// It expands the value of a variable from the environment variable but does not set the value to the environment itself.
// This function is skipping any invalid lines and only processing the valid one.
func Parse(r Reader) Env {
func Parse(r io.Reader) Env {
env, _ := strictParse(r, false)
return env
}

// StrictParse is a function to parse line by line any io.Reader supplied and returns the valid Env key/value pair of valid variables.
// It expands the value of a variable from the environment variable but does not set the value to the environment itself.
// This function is returning an error if there are any invalid lines.
func StrictParse(r Reader) (Env, error) {
func StrictParse(r io.Reader) (Env, error) {
return strictParse(r, false)
}

Expand Down Expand Up @@ -208,31 +208,32 @@ func splitLines(data []byte, atEOF bool) (advance int, token []byte, err error)
return eol, data[:idx], nil
}

type Reader interface {
io.Reader
io.ReaderAt
}

func strictParse(r Reader, override bool) (Env, error) {
func strictParse(r io.Reader, override bool) (Env, error) {
env := make(Env)

// We chooes a different scanner depending on file encoding.
var scanner *bufio.Scanner
buf := new(bytes.Buffer)
tee := io.TeeReader(r, buf)

// There can be a maximum of 3 BOM bytes.
bomByteBuffer := make([]byte, 3)
if _, err := r.ReadAt(bomByteBuffer, 0); err != nil {
_, err := tee.Read(bomByteBuffer)
if err != nil && err != io.EOF {
return env, err
}

z := io.MultiReader(buf, r)

// We chooes a different scanner depending on file encoding.
var scanner *bufio.Scanner

if bytes.HasPrefix(bomByteBuffer, bomUTF8) {
scanner = bufio.NewScanner(transform.NewReader(r, unicode.UTF8BOM.NewDecoder()))
scanner = bufio.NewScanner(transform.NewReader(z, unicode.UTF8BOM.NewDecoder()))
} else if bytes.HasPrefix(bomByteBuffer, bomUTF16LE) {
scanner = bufio.NewScanner(transform.NewReader(r, unicode.UTF16(unicode.LittleEndian, unicode.ExpectBOM).NewDecoder()))
scanner = bufio.NewScanner(transform.NewReader(z, unicode.UTF16(unicode.LittleEndian, unicode.ExpectBOM).NewDecoder()))
} else if bytes.HasPrefix(bomByteBuffer, bomUTF16BE) {
scanner = bufio.NewScanner(transform.NewReader(r, unicode.UTF16(unicode.BigEndian, unicode.ExpectBOM).NewDecoder()))
scanner = bufio.NewScanner(transform.NewReader(z, unicode.UTF16(unicode.BigEndian, unicode.ExpectBOM).NewDecoder()))
} else {
scanner = bufio.NewScanner(r)
scanner = bufio.NewScanner(z)
}

scanner.Split(splitLines)
Expand Down
13 changes: 3 additions & 10 deletions gotenv_test.go
Expand Up @@ -3,6 +3,7 @@ package gotenv_test
import (
"bufio"
"errors"
"io"
"os"
"strings"
"testing"
Expand Down Expand Up @@ -242,34 +243,26 @@ func TestStrictParse(t *testing.T) {
}

type failingReader struct {
gotenv.Reader
io.Reader
}

func (fr failingReader) Read(p []byte) (n int, err error) {
return 0, errors.New("you shall not read")
}

func (fr failingReader) ReadAt(p []byte, off int64) (n int, err error) {
return 0, errors.New("you shall not read")
}

func TestStrictParse_PassThroughErrors(t *testing.T) {
_, err := gotenv.StrictParse(&failingReader{})
assert.Error(t, err)
}

type infiniteReader struct {
gotenv.Reader
io.Reader
}

func (er infiniteReader) Read(p []byte) (n int, err error) {
return len(p), nil
}

func (er infiniteReader) ReadAt(p []byte, off int64) (n int, err error) {
return len(p), nil
}

func TestStrictParse_NoTokenPassThroughErrors(t *testing.T) {
_, err := gotenv.StrictParse(&infiniteReader{})
assert.Error(t, err)
Expand Down

0 comments on commit fe76f50

Please sign in to comment.