Skip to content

Commit

Permalink
[terminal/input] Add input validation feature
Browse files Browse the repository at this point in the history
  • Loading branch information
andyone committed Oct 30, 2024
1 parent 5740257 commit dfbd8d9
Show file tree
Hide file tree
Showing 3 changed files with 182 additions and 152 deletions.
138 changes: 9 additions & 129 deletions terminal/input/input.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ package input
import (
"fmt"
"os"
"strconv"
"strings"
"unicode/utf8"

Expand All @@ -36,8 +35,12 @@ type CompletionHandler = func(input string) []string
// HintHandler is hint handler
type HintHandler = func(input string) string

// Validator is input validation function
type Validator = func(input string) (string, error)
// ////////////////////////////////////////////////////////////////////////////////// //

// Validator is input validator type
type Validator interface {
Validate(input string) (string, error)
}

// ////////////////////////////////////////////////////////////////////////////////// //

Expand Down Expand Up @@ -75,44 +78,8 @@ var NewLine = false

// ////////////////////////////////////////////////////////////////////////////////// //

var (
// NotEmpty returns an error if input is empty
NotEmpty = validatorNotEmpty

// IsNumber returns an error if the input is not a valid number
IsNumber = validatorIsNumber

// IsFloat returns an error if the input is not a valid floating number
IsFloat = validatorIsFloat

// IsEmail returns an error if the input is not a valid email
IsEmail = validatorIsEmail

// IsURL returns an error if the input is not a valid URL
IsURL = validatorIsURL
)

// ////////////////////////////////////////////////////////////////////////////////// //

var (
// ErrInvalidAnswer is error for wrong answer for Y/N question
ErrInvalidAnswer = fmt.Errorf("Please enter Y or N")

// ErrIsEmpty is error for empty input
ErrIsEmpty = fmt.Errorf("You must enter non-empty value")

// ErrInvalidNumber is error for invalid number
ErrInvalidNumber = fmt.Errorf("Entered value is not a valid number")

// ErrInvalidFloat is error for invalid floating number
ErrInvalidFloat = fmt.Errorf("Entered value is not a valid floating number")

// ErrInvalidEmail is error for invalid email
ErrInvalidEmail = fmt.Errorf("Entered value is not a valid e-mail")

// ErrInvalidURL is error for invalid URL
ErrInvalidURL = fmt.Errorf("Entered value is not a valid URL")
)
// ErrInvalidAnswer is error for wrong answer for Y/N question
var ErrInvalidAnswer = fmt.Errorf("Please enter Y or N")

// ////////////////////////////////////////////////////////////////////////////////// //

Expand Down Expand Up @@ -212,93 +179,6 @@ func SetHintHandler(h HintHandler) {

// ////////////////////////////////////////////////////////////////////////////////// //

// validatorNotEmpty is validator for empty input
func validatorNotEmpty(input string) (string, error) {
if strings.TrimSpace(input) != "" {
return input, nil
}

return input, ErrIsEmpty
}

// validatorIsNumber is validator for number
func validatorIsNumber(input string) (string, error) {
input = strings.TrimSpace(input)

if input == "" {
return input, nil // Empty imput is okay
}

_, err := strconv.ParseInt(input, 10, 64)

if err != nil {
return input, ErrInvalidNumber
}

return input, nil
}

// validatorIsFloat is validator for floating number
func validatorIsFloat(input string) (string, error) {
input = strings.TrimSpace(input)

if input == "" {
return input, nil // Empty imput is okay
}

_, err := strconv.ParseFloat(input, 64)

if err != nil {
return input, ErrInvalidFloat
}

return input, nil
}

// validatorIsEmail is validator for email
func validatorIsEmail(input string) (string, error) {
input = strings.TrimSpace(input)

if input == "" {
return input, nil // Empty imput is okay
}

name, domain, ok := strings.Cut(input, "@")

if !ok || strings.TrimSpace(name) == "" ||
strings.TrimSpace(domain) == "" || !strings.Contains(domain, ".") {
return input, ErrInvalidEmail
}

return input, nil
}

// validatorIsURL is validator for URL
func validatorIsURL(input string) (string, error) {
input = strings.TrimSpace(input)

if input == "" {
return input, nil // Empty imput is okay
}

switch {
case strings.HasPrefix(input, "http://"),
strings.HasPrefix(input, "https://"),
strings.HasPrefix(input, "ftp://"):
// OK
default:
return input, ErrInvalidURL
}

if !strings.Contains(input, ".") {
return input, ErrInvalidURL
}

return input, nil
}

// ////////////////////////////////////////////////////////////////////////////////// //

// getMask returns mask for password
func getMask(message string) string {
var masking string
Expand Down Expand Up @@ -369,7 +249,7 @@ INPUT_LOOP:

if len(validators) != 0 {
for _, validator := range validators {
input, err = validator(input)
input, err = validator.Validate(input)

if err != nil {
fmtc.NewLine()
Expand Down
46 changes: 23 additions & 23 deletions terminal/input/input_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,54 +26,54 @@ var _ = Suite(&InputSuite{})
// ////////////////////////////////////////////////////////////////////////////////// //

func (s *InputSuite) TestValidators(c *C) {
_, err := validatorNotEmpty("")
_, err := NotEmpty.Validate("")
c.Assert(err, Equals, ErrIsEmpty)
_, err = validatorNotEmpty(" ")
_, err = NotEmpty.Validate(" ")
c.Assert(err, Equals, ErrIsEmpty)
_, err = validatorNotEmpty("test")
_, err = NotEmpty.Validate("test")
c.Assert(err, IsNil)

_, err = validatorIsNumber("ABCD")
_, err = IsNumber.Validate("ABCD")
c.Assert(err, Equals, ErrInvalidNumber)
_, err = validatorIsNumber("1234")
_, err = IsNumber.Validate("1234")
c.Assert(err, IsNil)
_, err = validatorIsNumber("-1234")
_, err = IsNumber.Validate("-1234")
c.Assert(err, IsNil)
_, err = validatorIsNumber("")
_, err = IsNumber.Validate("")
c.Assert(err, IsNil)

_, err = validatorIsFloat("ABCD")
_, err = IsFloat.Validate("ABCD")
c.Assert(err, Equals, ErrInvalidFloat)
_, err = validatorIsFloat("1234.56")
_, err = IsFloat.Validate("1234.56")
c.Assert(err, IsNil)
_, err = validatorIsFloat("-1234.56")
_, err = IsFloat.Validate("-1234.56")
c.Assert(err, IsNil)
_, err = validatorIsFloat("")
_, err = IsFloat.Validate("")
c.Assert(err, IsNil)

_, err = validatorIsEmail("ABCD")
_, err = IsEmail.Validate("ABCD")
c.Assert(err, Equals, ErrInvalidEmail)
_, err = validatorIsEmail("@test")
_, err = IsEmail.Validate("@test")
c.Assert(err, Equals, ErrInvalidEmail)
_, err = validatorIsEmail("abcd@")
_, err = IsEmail.Validate("abcd@")
c.Assert(err, Equals, ErrInvalidEmail)
_, err = validatorIsEmail("abcd@test")
_, err = IsEmail.Validate("abcd@test")
c.Assert(err, Equals, ErrInvalidEmail)
_, err = validatorIsEmail("")
_, err = IsEmail.Validate("")
c.Assert(err, IsNil)
_, err = validatorIsEmail("[email protected]")
_, err = IsEmail.Validate("[email protected]")
c.Assert(err, IsNil)

_, err = validatorIsURL("abcd")
_, err = IsURL.Validate("abcd")
c.Assert(err, Equals, ErrInvalidURL)
_, err = validatorIsURL("abcd.com")
_, err = IsURL.Validate("abcd.com")
c.Assert(err, Equals, ErrInvalidURL)
_, err = validatorIsURL("https://abcd")
_, err = IsURL.Validate("https://abcd")
c.Assert(err, Equals, ErrInvalidURL)
_, err = validatorIsURL("test://abcd.com")
_, err = IsURL.Validate("test://abcd.com")
c.Assert(err, Equals, ErrInvalidURL)
_, err = validatorIsURL("")
_, err = IsURL.Validate("")
c.Assert(err, IsNil)
_, err = validatorIsURL("https://domain.com")
_, err = IsURL.Validate("https://domain.com")
c.Assert(err, IsNil)
}
Loading

0 comments on commit dfbd8d9

Please sign in to comment.