forked from coop-cloud-mirrors/godotenv
		
	support for writing envs out in dotenv format
This commit is contained in:
		
							
								
								
									
										39
									
								
								godotenv.go
									
									
									
									
									
								
							
							
						
						
									
										39
									
								
								godotenv.go
									
									
									
									
									
								
							| @ -16,6 +16,7 @@ package godotenv | ||||
| import ( | ||||
| 	"bufio" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"os" | ||||
| 	"os/exec" | ||||
| @ -119,6 +120,11 @@ func Parse(r io.Reader) (envMap map[string]string, err error) { | ||||
| 	return | ||||
| } | ||||
|  | ||||
| //ParseString reads an env file from a string, returning a map of keys and values. | ||||
| func ParseString(str string) (envMap map[string]string, err error) { | ||||
| 	return Parse(strings.NewReader(str)) | ||||
| } | ||||
|  | ||||
| // Exec loads env vars from the specified filenames (empty map falls back to default) | ||||
| // then executes the cmd specified. | ||||
| // | ||||
| @ -136,6 +142,31 @@ func Exec(filenames []string, cmd string, cmdArgs []string) error { | ||||
| 	return command.Run() | ||||
| } | ||||
|  | ||||
| // Write serializes the given environment and writes it to a file | ||||
| func Write(envMap map[string]string, filename string) error { | ||||
| 	content, error := WriteString(envMap) | ||||
| 	if error != nil { | ||||
| 		return error | ||||
| 	} | ||||
| 	file, error := os.Create(filename) | ||||
| 	if error != nil { | ||||
| 		return error | ||||
| 	} | ||||
| 	_, err := file.WriteString(content) | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| // WriteString outputs the given environment as a dotenv-formatted environment file. | ||||
| // | ||||
| // Each line is in the format: KEY="VALUE" where VALUE is backslash-escaped. | ||||
| func WriteString(envMap map[string]string) (string, error) { | ||||
| 	lines := make([]string, 0, len(envMap)) | ||||
| 	for k, v := range envMap { | ||||
| 		lines = append(lines, fmt.Sprintf(`%s="%s"`, k, doubleQuoteEscape(v))) | ||||
| 	} | ||||
| 	return strings.Join(lines, "\n"), nil | ||||
| } | ||||
|  | ||||
| func filenamesOrDefault(filenames []string) []string { | ||||
| 	if len(filenames) == 0 { | ||||
| 		return []string{".env"} | ||||
| @ -264,3 +295,11 @@ func isIgnoredLine(line string) bool { | ||||
| 	trimmedLine := strings.Trim(line, " \n\t") | ||||
| 	return len(trimmedLine) == 0 || strings.HasPrefix(trimmedLine, "#") | ||||
| } | ||||
|  | ||||
| func doubleQuoteEscape(line string) string { | ||||
| 	line = strings.Replace(line, `\`, `\\`, -1) | ||||
| 	line = strings.Replace(line, "\n", `\n`, -1) | ||||
| 	line = strings.Replace(line, "\r", `\r`, -1) | ||||
| 	line = strings.Replace(line, `"`, `\"`, -1) | ||||
| 	return line | ||||
| } | ||||
|  | ||||
| @ -2,7 +2,9 @@ package godotenv | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"reflect" | ||||
| 	"testing" | ||||
| ) | ||||
|  | ||||
| @ -326,3 +328,47 @@ func TestErrorParsing(t *testing.T) { | ||||
| 		t.Errorf("Expected error, got %v", envMap) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestWrite(t *testing.T) { | ||||
| 	writeAndCompare := func(env string, expected string) { | ||||
| 		envMap, _ := ParseString(env) | ||||
| 		actual, _ := WriteString(envMap) | ||||
| 		if expected != actual { | ||||
| 			t.Errorf("Expected '%v' (%v) to write as '%v', got '%v' instead.", env, envMap, expected, actual) | ||||
| 		} | ||||
| 	} | ||||
| 	//just test some single lines to show the general idea | ||||
| 	//TestRoundtrip makes most of the good assertions | ||||
|  | ||||
| 	//values are always double-quoted | ||||
| 	writeAndCompare(`key=value`, `key="value"`) | ||||
| 	//double-quotes are escaped | ||||
| 	writeAndCompare(`key=va"lu"e`, `key="va\"lu\"e"`) | ||||
| 	//but single quotes are left alone | ||||
| 	writeAndCompare(`key=va'lu'e`, `key="va'lu'e"`) | ||||
| 	// newlines and backslashes are escaped | ||||
| 	writeAndCompare(`foo="ba\n\r\\r!"`, `foo="ba\n\r\\r!"`) | ||||
| } | ||||
|  | ||||
| func TestRoundtrip(t *testing.T) { | ||||
| 	fixtures := []string{"equals.env", "exported.env", "invalid1.env", "plain.env", "quoted.env"} | ||||
| 	for _, fixture := range fixtures { | ||||
| 		fixtureFilename := fmt.Sprintf("fixtures/%s", fixture) | ||||
| 		env, err := readFile(fixtureFilename) | ||||
| 		if err != nil { | ||||
| 			continue | ||||
| 		} | ||||
| 		rep, err := WriteString(env) | ||||
| 		if err != nil { | ||||
| 			continue | ||||
| 		} | ||||
| 		roundtripped, err := ParseString(rep) | ||||
| 		if err != nil { | ||||
| 			continue | ||||
| 		} | ||||
| 		if !reflect.DeepEqual(env, roundtripped) { | ||||
| 			t.Errorf("Expected '%s' to roundtrip as '%v', got '%v' instead", fixtureFilename, env, roundtripped) | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
| } | ||||
|  | ||||
		Reference in New Issue
	
	Block a user