Skip to content

Commit

Permalink
Feat [Golang] [Package] Interactivity (#27)
Browse files Browse the repository at this point in the history
* Feat [Golang] [Package] Interactivity

- [+] feat(interactivity): add interactivity package for handling interactive command-line user interfaces
- [+] feat(interactivity): add ConfirmOverwrite function for checking if a file exists and prompting the user for confirmation to overwrite
- [+] feat(interactivity): add promptForInput function for reading user input from bufio.Reader and handling cancellation with context

* Feat & Fix [Golang] [Package] Real File System & File System Mock

- [+] feat(file_system.go): add error handling to FileExists method in the FileSystem interface
- [+] fix(file_system.go): change return type of FileExists method in the FileSystem interface to (bool, error)
- [+] fix(file_system.go): handle file existence check in the FileExists method of RealFileSystem
- [+] fix(file_system_mock.go): change return type of FileExists method in the MockFileSystem to (bool, error)
- [+] fix(file_system_mock.go): handle file existence check in the FileExists method of MockFileSystem

* Feat [Golang] [Package] Interactivity

- [+] feat(interactivity.go): add determineFileName function to determine the file name based on the fileType

* Fix & Feat [Golang] [Module] Main Command

- [+] fix(main.go): change variable name from fs to rfs in processCSVOption, processDatasetOption, executeCSVConversion, createSeparateCSVFiles, convertToSingleCSV, and writeContentToFile functions
- [+] feat(main.go): add import statement for interactivity package
  • Loading branch information
H0llyW00dzZ authored Dec 10, 2023
1 parent b096f88 commit 351654e
Show file tree
Hide file tree
Showing 4 changed files with 203 additions and 64 deletions.
15 changes: 11 additions & 4 deletions filesystem/file_system.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ type FileSystem interface {
WriteFile(name string, data []byte, perm fs.FileMode) error
ReadFile(name string) ([]byte, error) // Added ReadFile method
Stat(name string) (os.FileInfo, error)
FileExists(name string) bool // Added FileExists method to the interface
FileExists(name string) (bool, error) // Added FileExists method to the interface
}

// RealFileSystem implements the FileSystem interface by wrapping the os package functions,
Expand Down Expand Up @@ -51,8 +51,15 @@ func (rfs RealFileSystem) Stat(name string) (os.FileInfo, error) {
}

// FileExists checks if a file exists in the file system at the given path.
func (rfs RealFileSystem) FileExists(name string) bool {
// It returns a boolean indicating existence, and an error for any underlying
// filesystem issues encountered.
func (rfs RealFileSystem) FileExists(name string) (bool, error) {
_, err := rfs.Stat(name)
// Return true only if the error is nil (file exists).
return err == nil
if err == nil {
return true, nil // File exists
}
if os.IsNotExist(err) {
return false, nil // File does not exist
}
return false, err // Some other error occurred
}
6 changes: 3 additions & 3 deletions filesystem/file_system_mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,12 +114,12 @@ func (m mockFileInfo) IsDir() bool { return false } // Dummy value,
func (m mockFileInfo) Sys() interface{} { return nil } // No system-specific information.

// FileExists checks if the given file name exists in the mock file system.
func (m *MockFileSystem) FileExists(name string) bool {
func (m *MockFileSystem) FileExists(name string) (bool, error) {
m.FileExistsCalled = true // Record that FileExists was called
if m.FileExistsErr != nil {
// Simulate an error condition if an error is set
return false
return false, nil
}
_, exists := m.Files[name] // Use the same map for all file data which can easily be mocked while touring in binary.
return exists
return exists, nil
}
80 changes: 80 additions & 0 deletions interactivity/interactivity.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Package interactivity provides functions to handle interactive command-line
// user interfaces. It includes utilities for prompting the user with questions
// and processing their responses. This package is specifically designed to work
// with the filesystem provided by the github.com/H0llyW00dzZ/ChatGPT-Next-Web-Session-Exporter/filesystem
// package to confirm potential file overwrites and to collect user input in a
// context-aware manner, allowing for graceful cancellation of input requests.
package interactivity

import (
"bufio"
"context"
"fmt"
"strings"

"github.com/H0llyW00dzZ/ChatGPT-Next-Web-Session-Exporter/filesystem"
)

// result is a helper struct used internally within the interactivity package
// to encapsulate the user input along with any error that might have occurred
// during the input reading process. It is used to communicate between
// goroutines in the promptForInput function.
type result struct {
input string
err error
}

// ConfirmOverwrite checks if a file with the given fileName exists in the provided filesystem.
// If the file does exist, it prompts the user for confirmation to overwrite the file.
// The function reads the user's input via the provided bufio.Reader and expects a 'yes' or 'no' response.
// A context.Context is used to handle cancellation of the input request.
// It returns a boolean indicating whether the file should be overwritten and any error encountered.
func ConfirmOverwrite(rfs filesystem.FileSystem, ctx context.Context, reader *bufio.Reader, fileName string) (bool, error) {
exists, err := rfs.FileExists(fileName)
if err != nil {
// Handle the error properly, perhaps by returning it.
return false, err
}
if !exists {
// If the file doesn't exist, no need to confirm overwrite.
return true, nil
}

// If the file exists, ask the user for confirmation.
fmt.Printf("File '%s' already exists. Overwrite? (yes/no): ", fileName)

// Call promptForInput without the extra string argument.
overwrite, err := promptForInput(ctx, reader)
if err != nil {
return false, err
}
return strings.ToLower(overwrite) == "yes", nil
}

// promptForInput waits for a line of user input read from the provided bufio.Reader.
// It takes a context.Context to support cancellation.
// The function trims the newline character from the input and returns the resulting string.
// If the context is cancelled before the user inputs a line, the context's error is returned.
func promptForInput(ctx context.Context, reader *bufio.Reader) (string, error) {
resultChan := make(chan result)

go func() {
input, err := reader.ReadString('\n')
resultChan <- result{input: input, err: err}
}()

select {
case <-ctx.Done():
return "", ctx.Err()
case res := <-resultChan:
return strings.TrimSpace(res.input), res.err
}
}

// determineFileName should be a function that determines the file name based on the fileType or other logic.
// Note: Currently, unimplemented.
func determineFileName(fileType string) string {
// Implement logic to determine the file name
// For example, you might prompt the user for a file name or generate it based on the fileType
return ""
}
Loading

0 comments on commit 351654e

Please sign in to comment.