-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* be able to receive -l as parameter viua cli * save-output-to-file-01 * make sure the path validation fuction deletetes any file it creates * save-output-to-file-01; Commented out candidate way to implement that. it's failing * save-output-to-file-01 * remove valid for linux, but invalid for mac os * save-output-to-file-01; working first version * add a simple test to the code fuction * improve some tests
- Loading branch information
Showing
5 changed files
with
293 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
package tools | ||
|
||
import ( | ||
"fmt" | ||
"io" | ||
"log" | ||
"os" | ||
"path/filepath" | ||
"runtime" | ||
"strings" | ||
) | ||
|
||
// isValidPath checks if the given path is valid for the current operating system. | ||
func IsValidPath(path string) (isValid bool, absPath string, err error) { | ||
// Check if the path contains invalid characters for the current OS. | ||
var invalidChars string | ||
switch runtime.GOOS { | ||
case "windows": | ||
invalidChars = `<>|?*` | ||
default: | ||
invalidChars = `<>:"\|?*` | ||
} | ||
|
||
for _, char := range invalidChars { | ||
if strings.ContainsRune(path, char) { | ||
return false, "", fmt.Errorf("invalid character '%c' found in the path", char) | ||
} | ||
} | ||
|
||
// Check if the path is empty or consists of only whitespace characters. | ||
if len(strings.TrimSpace(path)) == 0 { | ||
return false, "", fmt.Errorf("empty path") | ||
} | ||
|
||
// Convert the path to absolute path if it's relative. | ||
if !filepath.IsAbs(path) { | ||
// Get the current working directory. | ||
cwd, err := os.Getwd() | ||
if err != nil { | ||
// Failed to get the current working directory. | ||
return false, "", err | ||
} | ||
// Join the current working directory with the relative path to get absolute path. | ||
absPath = filepath.Join(cwd, path) | ||
} else { | ||
absPath = path | ||
} | ||
|
||
// Try to create or open the file. | ||
file, err := os.OpenFile(absPath, os.O_RDWR|os.O_CREATE, 0666) | ||
if err != nil { | ||
// Check if the error is due to a missing directory in the path. | ||
if os.IsNotExist(err) { | ||
return false, "", fmt.Errorf("the file or one or more directories in the path do not exist or can not be created: %v", err) | ||
} | ||
// Check if the error is due to permission issues. | ||
if os.IsPermission(err) { | ||
return false, "", fmt.Errorf("no permission to write on path: %s", absPath) | ||
} | ||
// For other errors, return the error as is. | ||
return false, "", err | ||
} | ||
// Defer the closure of the file and its removal. | ||
defer func() { | ||
file.Close() | ||
os.Remove(absPath) | ||
}() | ||
|
||
return true, absPath, nil | ||
} | ||
|
||
func LogOutput(logfile string) func() { | ||
// open file read/write | create if not exist | clear file at open if exists | ||
f, _ := os.OpenFile(logfile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666) | ||
|
||
// save existing stdout | MultiWriter writes to saved stdout and file | ||
out := os.Stdout | ||
mw := io.MultiWriter(out, f) | ||
|
||
// get pipe reader and writer | writes to pipe writer come out pipe reader | ||
r, w, _ := os.Pipe() | ||
|
||
// replace stdout,stderr with pipe writer | all writes to stdout, stderr will go through pipe instead (fmt.print, log) | ||
os.Stdout = w | ||
os.Stderr = w | ||
|
||
// writes with log.Print should also write to mw | ||
log.SetOutput(mw) | ||
|
||
//create channel to control exit | will block until all copies are finished | ||
exit := make(chan bool) | ||
|
||
go func() { | ||
// copy all reads from pipe to multiwriter, which writes to stdout and file | ||
_, _ = io.Copy(mw, r) | ||
// when r or w is closed copy will finish and true will be sent to channel | ||
exit <- true | ||
}() | ||
|
||
// function to be deferred in main until program exits | ||
return func() { | ||
// close writer then block on exit channel | this will let mw finish writing before the program exits | ||
_ = w.Close() | ||
<-exit | ||
// close file after all writes have finished | ||
_ = f.Close() | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
package tools | ||
|
||
import ( | ||
"log" | ||
"os" | ||
"strings" | ||
"testing" | ||
) | ||
|
||
func TestIsValidPath(t *testing.T) { | ||
// Example usage: | ||
paths := map[string]struct { | ||
expectedValid bool | ||
expectedError string | ||
}{ | ||
"/path/to/valid/file.txt": { | ||
expectedValid: false, | ||
expectedError: "the file or one or more directories in the path do not exist or can not be created", | ||
}, | ||
"invalid*file.txt": { | ||
expectedValid: false, | ||
expectedError: "invalid character '*' found in the path", | ||
}, | ||
"": { | ||
expectedValid: false, | ||
expectedError: "empty path", | ||
}, | ||
" ": { | ||
expectedValid: false, | ||
expectedError: "empty path", | ||
}, | ||
"/path/with/invalid|character": { | ||
expectedValid: false, | ||
expectedError: "invalid character '|' found in the path", | ||
}, | ||
"C:\\Program Files\\Example\\file.txt": { | ||
expectedValid: false, | ||
expectedError: "invalid character ':' found in the path", | ||
}, | ||
"D:/Documents/Report.docx": { | ||
expectedValid: false, | ||
expectedError: "invalid character ':' found in the path", | ||
}, | ||
"/home/user/pictures/photo.jpg": { | ||
expectedValid: false, | ||
expectedError: "the file or one or more directories in the path do not exist or can not be created", | ||
}, | ||
"file.txt": { | ||
expectedValid: true, | ||
expectedError: "", | ||
}, | ||
"folder1/file.txt": { | ||
expectedValid: false, | ||
expectedError: "the file or one or more directories in the path do not exist or can not be created", | ||
}, | ||
"../parent/file.txt": { | ||
expectedValid: false, | ||
expectedError: "the file or one or more directories in the path do not exist or can not be created", | ||
}, | ||
"..\\parent\\file.txt": { | ||
expectedValid: false, | ||
expectedError: "invalid character '\\' found in the path", | ||
}, | ||
} | ||
|
||
for path, expected := range paths { | ||
valid, _, err := IsValidPath(path) | ||
if err != nil { | ||
if !strings.Contains(err.Error(), expected.expectedError) { | ||
t.Errorf("Unexpected error for path %s. Expected: %s, Got: %s", path, expected.expectedError, err.Error()) | ||
} | ||
} else { | ||
if valid != expected.expectedValid { | ||
t.Errorf("Path %s validation failed. Expected valid: %t but got: %t", path, expected.expectedValid, valid) | ||
} | ||
} | ||
} | ||
} | ||
|
||
func TestLogOutput(t *testing.T) { | ||
// Create a temporary file for logging | ||
tmpfile, err := os.CreateTemp("", "testlog-") | ||
if err != nil { | ||
t.Fatalf("Error creating temporary file: %v", err) | ||
} | ||
defer os.Remove(tmpfile.Name()) | ||
|
||
// Create a channel to signal when the goroutine has completed | ||
done := make(chan struct{}) | ||
|
||
// Call LogOutput with the temporary file | ||
go func() { | ||
defer close(done) | ||
LogOutput(tmpfile.Name()) // Assuming LogOutput returns immediately after starting the goroutine | ||
}() | ||
|
||
// Write a log message | ||
log.Print("Test log message") | ||
|
||
// Wait for the goroutine to finish | ||
<-done | ||
|
||
// Read and verify the contents of the log file | ||
// (You should implement this verification logic) | ||
} | ||
|
||
func TestLogOutputWithInvalidPath(t *testing.T) { | ||
// Call LogOutput with an invalid path | ||
err := LogOutput("invalid*file.txt") | ||
if err == nil { | ||
t.Errorf("Expected error for invalid path") | ||
} | ||
defer os.Remove("invalid*file.txt") | ||
} | ||
|
||
func TestLogOutputWithEmptyPath(t *testing.T) { | ||
// Call LogOutput with an empty path | ||
err := LogOutput("") | ||
if err == nil { | ||
t.Errorf("Expected error for empty path") | ||
} | ||
} |