├── port.go ├── creditcard.go ├── go.mod ├── url.go ├── uuid.go ├── mac.go ├── internal ├── regexes │ ├── mac.go │ ├── uuid.go │ ├── func.go │ ├── domain.go │ ├── hashes.go │ └── url.go ├── test │ ├── types.go │ ├── port_test.go │ ├── mac_test.go │ ├── creditcard_test.go │ ├── uuid_test.go │ ├── luhn_test.go │ ├── domain_test.go │ ├── hashes_test.go │ ├── ip_test.go │ └── url_test.go └── utilities │ └── utilities.go ├── go.sum ├── .gitignore ├── domain.go ├── .github └── workflows │ └── go.yml ├── luhn.go ├── hashes.go ├── CHANGELOG.md ├── LICENSE ├── README.md └── ip.go /port.go: -------------------------------------------------------------------------------- 1 | package golidators 2 | 3 | // Port function for validating ports 4 | func Port(port int) bool { 5 | return 1 <= port && port <= 65535 6 | } 7 | -------------------------------------------------------------------------------- /creditcard.go: -------------------------------------------------------------------------------- 1 | package golidators 2 | 3 | // CreditCard validate credit card number with Luhn 4 | func CreditCard(number int) bool { 5 | return Luhn(number) 6 | } 7 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/eredotpkfr/golidators 2 | 3 | go 1.23 4 | 5 | require golang.org/x/net v0.33.0 6 | 7 | require golang.org/x/text v0.21.0 // indirect 8 | -------------------------------------------------------------------------------- /url.go: -------------------------------------------------------------------------------- 1 | package golidators 2 | 3 | import "github.com/eredotpkfr/golidators/internal/regexes" 4 | 5 | // Url function for validating URL's 6 | func Url(url string) bool { 7 | return regexes.URLRegex.MatchString(url) 8 | } 9 | -------------------------------------------------------------------------------- /uuid.go: -------------------------------------------------------------------------------- 1 | package golidators 2 | 3 | import "github.com/eredotpkfr/golidators/internal/regexes" 4 | 5 | // Uuid function for validating UUID 6 | func Uuid(uuid string) bool { 7 | return regexes.UUIDRegex.MatchString(uuid) 8 | } 9 | -------------------------------------------------------------------------------- /mac.go: -------------------------------------------------------------------------------- 1 | package golidators 2 | 3 | import "github.com/eredotpkfr/golidators/internal/regexes" 4 | 5 | // Mac function for validating MAC Addresses 6 | func Mac(macAddr string) bool { 7 | return regexes.MacRegex.MatchString(macAddr) 8 | } 9 | -------------------------------------------------------------------------------- /internal/regexes/mac.go: -------------------------------------------------------------------------------- 1 | package regexes 2 | 3 | import rgx "regexp" 4 | 5 | const macPattern string = "^(?:[0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$" 6 | 7 | // MacRegex for validating MAC Addresses 8 | var MacRegex = rgx.MustCompile(makeCaseInsensitive(macPattern)) 9 | -------------------------------------------------------------------------------- /internal/regexes/uuid.go: -------------------------------------------------------------------------------- 1 | package regexes 2 | 3 | import rgx "regexp" 4 | 5 | const uuidPattern = "^[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}$" 6 | 7 | // UUIDRegex for validating UUID data 8 | var UUIDRegex = rgx.MustCompile(makeCaseInsensitive(uuidPattern)) 9 | -------------------------------------------------------------------------------- /internal/regexes/func.go: -------------------------------------------------------------------------------- 1 | package regexes 2 | 3 | // Ignore case prefix pattern for regex patterns 4 | const ignoreCase string = "(?i)" 5 | 6 | // Add ignoreCase flag any regex pattern 7 | func makeCaseInsensitive(pattern string) string { 8 | return ignoreCase + pattern 9 | } 10 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= 2 | golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= 3 | golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= 4 | golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | -------------------------------------------------------------------------------- /internal/regexes/domain.go: -------------------------------------------------------------------------------- 1 | package regexes 2 | 3 | import rgx "regexp" 4 | 5 | const domainPattern string = "^(?:[a-zA-Z0-9]" + 6 | "(?:[a-zA-Z0-9-_]{0,61}[A-Za-z0-9])?\\.)" + 7 | "+[A-Za-z0-9][A-Za-z0-9-_]{0,61}" + 8 | "[A-Za-z]$" 9 | 10 | // DomainRegex for validating domains 11 | var DomainRegex = rgx.MustCompile(makeCaseInsensitive(domainPattern)) 12 | -------------------------------------------------------------------------------- /domain.go: -------------------------------------------------------------------------------- 1 | package golidators 2 | 3 | import ( 4 | "github.com/eredotpkfr/golidators/internal/regexes" 5 | "golang.org/x/net/idna" 6 | ) 7 | 8 | // Domain function for validating domains 9 | func Domain(domain string) bool { 10 | var idnap *idna.Profile = idna.New() 11 | domain, err := idnap.ToASCII(domain) 12 | 13 | if err != nil { 14 | return false 15 | } 16 | 17 | return regexes.DomainRegex.MatchString(domain) 18 | } 19 | -------------------------------------------------------------------------------- /internal/test/types.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | // IntTestRecord for store int test data 4 | type IntTestRecord struct { 5 | TargetValue int 6 | Expected bool 7 | } 8 | 9 | // IntTestRecordWithIntReturn for store int test data 10 | type IntTestRecordWithIntReturn struct { 11 | TargetValue int 12 | Expected int 13 | } 14 | 15 | // StrTestRecord for store string test data 16 | type StrTestRecord struct { 17 | TargetValue string 18 | Expected bool 19 | } 20 | -------------------------------------------------------------------------------- /internal/test/port_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/eredotpkfr/golidators" 7 | ) 8 | 9 | var PortCases = [5]IntTestRecord{ 10 | {12, true}, 11 | {80, true}, 12 | {443, true}, 13 | {99999, false}, 14 | {65536, false}, 15 | } 16 | 17 | func TestPort(t *testing.T) { 18 | for _, record := range PortCases { 19 | if golidators.Port(record.TargetValue) != record.Expected { 20 | t.Error(record.TargetValue) 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | 3 | on: 4 | push: 5 | branches: [ '*' ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | 16 | - name: Set up Go 17 | uses: actions/setup-go@v2 18 | with: 19 | go-version: 1.17 20 | 21 | - name: Build 22 | run: go build -v ./... 23 | 24 | - name: Test 25 | run: go test -v ./... 26 | -------------------------------------------------------------------------------- /internal/test/mac_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/eredotpkfr/golidators" 7 | ) 8 | 9 | var MacCases = [7]StrTestRecord{ 10 | {"01:23:45:67:ab:CD", true}, 11 | {"01:23:45:67:AB:CD", true}, 12 | {"00:00:00:00:00", false}, 13 | {"01:23:45:67:89:", false}, 14 | {"01:23:45:67:89:gh", false}, 15 | {"123:23:45:67:89:00", false}, 16 | {"01:23:45:67:89:GH", false}, 17 | } 18 | 19 | func TestMac(t *testing.T) { 20 | for _, record := range MacCases { 21 | if golidators.Mac(record.TargetValue) != record.Expected { 22 | t.Error(record.TargetValue) 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /internal/test/creditcard_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/eredotpkfr/golidators" 7 | ) 8 | 9 | var CreditCardCases = [10]IntTestRecord{ 10 | {1234567890, false}, 11 | {1234567897, true}, 12 | {5146713835430, false}, 13 | {5146713835433, true}, 14 | {5371087585041475, true}, 15 | {5300025108592596, true}, 16 | {5302025202593516, false}, 17 | {5184214431476070, true}, 18 | {5371087585041475, true}, 19 | {5101365588025704, true}, 20 | } 21 | 22 | func TestCreditCard(t *testing.T) { 23 | for _, record := range CreditCardCases { 24 | if golidators.CreditCard(record.TargetValue) != record.Expected { 25 | t.Error(record.TargetValue) 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /internal/test/uuid_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/eredotpkfr/golidators" 7 | ) 8 | 9 | var UUIDCases = [7]StrTestRecord{ 10 | {"2bc1c94f-0deb-43e9-92a1-4775189ec9f8", true}, 11 | {"d22877be-b0cf-4d81-99bf-242afba289a6", true}, 12 | {"D22877BE-B0CF-4D81-99BF-242AFBA289A6", true}, 13 | {"2bc1c94f-deb-43e9-92a1-4775189ec9f8", false}, 14 | {"2bc1c94f-0deb-43e9-92a1-4775189ec9f", false}, 15 | {"gbc1c94f-0deb-43e9-92a1-4775189ec9f8", false}, 16 | {"2bc1c94f 0deb-43e9-92a1-4775189ec9f8", false}, 17 | } 18 | 19 | func TestUuid(t *testing.T) { 20 | for _, record := range UUIDCases { 21 | if golidators.Uuid(record.TargetValue) != record.Expected { 22 | t.Error(record.TargetValue) 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /luhn.go: -------------------------------------------------------------------------------- 1 | package golidators 2 | 3 | func luhnChecksum(number int) int { 4 | var sum int = 0 5 | 6 | for i := 0; number > 0; i++ { 7 | digit := number % 10 8 | 9 | if i%2 == 0 { 10 | digit = digit * 2 11 | 12 | if digit > 9 { 13 | digit = digit%10 + digit/10 14 | } 15 | } 16 | 17 | sum += digit 18 | number = number / 10 19 | } 20 | 21 | return sum % 10 22 | } 23 | 24 | // Luhn checks if the given integer is valid according to the Luhn algorithm 25 | func Luhn(number int) bool { 26 | return (number%10+luhnChecksum(number/10))%10 == 0 27 | } 28 | 29 | // LuhnCheckDigit calculates the check digit for a given number using the Luhn algorithm 30 | func LuhnCheckDigit(number int) int { 31 | return (10 - luhnChecksum(number)) % 10 32 | } 33 | -------------------------------------------------------------------------------- /hashes.go: -------------------------------------------------------------------------------- 1 | package golidators 2 | 3 | import "github.com/eredotpkfr/golidators/internal/regexes" 4 | 5 | // Md5 function for validating MD5 6 | func Md5(md5 string) bool { 7 | return regexes.Md5Regex.MatchString(md5) 8 | } 9 | 10 | // Sha1 function for validating SHA1 11 | func Sha1(sha1 string) bool { 12 | return regexes.Sha1Regex.MatchString(sha1) 13 | } 14 | 15 | // Sha224 function for validating SHA224 16 | func Sha224(sha224 string) bool { 17 | return regexes.Sha224Regex.MatchString(sha224) 18 | } 19 | 20 | // Sha256 function for validating SHA256 21 | func Sha256(sha256 string) bool { 22 | return regexes.Sha256Regex.MatchString(sha256) 23 | } 24 | 25 | // Sha512 function for validating SHA512 26 | func Sha512(sha512 string) bool { 27 | return regexes.Sha512Regex.MatchString(sha512) 28 | } 29 | -------------------------------------------------------------------------------- /internal/regexes/hashes.go: -------------------------------------------------------------------------------- 1 | package regexes 2 | 3 | import rgx "regexp" 4 | 5 | const ( 6 | md5Pattern string = "^[0-9a-f]{32}$" 7 | sha1Pattern string = "^[0-9a-f]{40}$" 8 | sha224Pattern string = "^[0-9a-f]{56}$" 9 | sha256Pattern string = "^[0-9a-f]{64}$" 10 | sha512Pattern string = "^[0-9a-f]{128}$" 11 | ) 12 | 13 | var ( 14 | // Md5Regex for validating MD5 15 | Md5Regex = rgx.MustCompile(makeCaseInsensitive(md5Pattern)) 16 | // Sha1Regex for validating SHA1 17 | Sha1Regex = rgx.MustCompile(makeCaseInsensitive(sha1Pattern)) 18 | // Sha224Regex for validating SHA224 19 | Sha224Regex = rgx.MustCompile(makeCaseInsensitive(sha224Pattern)) 20 | // Sha256Regex for validating SHA256 21 | Sha256Regex = rgx.MustCompile(makeCaseInsensitive(sha256Pattern)) 22 | // Sha512Regex for validating SHA512 23 | Sha512Regex = rgx.MustCompile(makeCaseInsensitive(sha512Pattern)) 24 | ) 25 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). 6 | 7 | ## Unreleased 8 | 9 | ## 0.1.6 - 2024-12-22 10 | 11 | ### Added 12 | 13 | - Credit card validator 14 | - Luhn validator 15 | - Luhn check digit calculation function 16 | 17 | ## 0.1.5 - 2021-10-11 18 | 19 | ### Added 20 | 21 | - Extended IP test cases 22 | 23 | ## 0.1.4 - 2021-09-28 24 | 25 | ### Fixed 26 | 27 | - Fixed critical bug in `Ipv4Cidr` and `Ipv6Cidr` validators. For details [issue #1](https://github.com/eredotpkfr/golidators/issues/1) 28 | 29 | ### Added 30 | 31 | - Extended IP test cases 32 | 33 | ## 0.1.3 - 2021-09-27 34 | 35 | ### Added 36 | 37 | - Added following validators: 38 | - Domain 39 | - MD5, SHA1, SHA224, SHA256, SHA512 40 | - IPv4, IPv4CIDR, IPv6, IPv6CIDR 41 | - MAC 42 | - Port 43 | - URL 44 | - UUID 45 | - Tests were done 46 | -------------------------------------------------------------------------------- /internal/test/luhn_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/eredotpkfr/golidators" 7 | ) 8 | 9 | var LuhnCases = [7]IntTestRecord{ 10 | {1234567890, false}, 11 | {1234567897, true}, 12 | {5146713835430, false}, 13 | {5146713835433, true}, 14 | {5371087585041475, true}, 15 | {5300025108592596, true}, 16 | {5302025202593516, false}, 17 | } 18 | 19 | var LuhnCheckDigitCases = [7]IntTestRecordWithIntReturn{ 20 | {1234567890, 3}, 21 | {1234567897, 8}, 22 | {5146713835430, 2}, 23 | {5146713835433, 6}, 24 | {5371087585041475, 1}, 25 | {5300025108592596, 2}, 26 | {5302025202593516, 6}, 27 | } 28 | 29 | func TestLuhn(t *testing.T) { 30 | for _, record := range LuhnCases { 31 | if golidators.Luhn(record.TargetValue) != record.Expected { 32 | t.Error(record.TargetValue) 33 | } 34 | } 35 | } 36 | 37 | func TestLuhnCheckDigit(t *testing.T) { 38 | for _, record := range LuhnCheckDigitCases { 39 | if golidators.LuhnCheckDigit(record.TargetValue) != record.Expected { 40 | t.Error(record.TargetValue) 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Erdoğan Yoksul 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /internal/utilities/utilities.go: -------------------------------------------------------------------------------- 1 | package utilities 2 | 3 | // 0xFFFFFF -> 16777215 4 | const big = 0xFFFFFF 5 | 6 | // Xtoi is Hexadecimal to integer. 7 | // Returns number, characters consumed, success. 8 | // Official function definition is here: https://cs.opensource.google/go/go/+/refs/tags/go1.17.1:src/net/parse.go 9 | func Xtoi(s string) (n int, i int, ok bool) { 10 | n = 0 11 | for i = 0; i < len(s); i++ { 12 | if '0' <= s[i] && s[i] <= '9' { 13 | n *= 16 14 | n += int(s[i] - '0') 15 | } else if 'a' <= s[i] && s[i] <= 'f' { 16 | n *= 16 17 | n += int(s[i]-'a') + 10 18 | } else if 'A' <= s[i] && s[i] <= 'F' { 19 | n *= 16 20 | n += int(s[i]-'A') + 10 21 | } else { 22 | break 23 | } 24 | if n >= big { 25 | return 0, i, false 26 | } 27 | } 28 | if i == 0 { 29 | return 0, i, false 30 | } 31 | return n, i, true 32 | } 33 | 34 | // Dtoi is Decimal to integer. 35 | // Returns number, characters consumed, success. 36 | // Official function definition is here: https://cs.opensource.google/go/go/+/refs/tags/go1.17.1:src/net/parse.go 37 | func Dtoi(s string) (n int, i int, ok bool) { 38 | n = 0 39 | for i = 0; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ { 40 | n = n*10 + int(s[i]-'0') 41 | if n >= big { 42 | return big, i, false 43 | } 44 | } 45 | if i == 0 { 46 | return 0, 0, false 47 | } 48 | return n, i, true 49 | } 50 | -------------------------------------------------------------------------------- /internal/test/domain_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/eredotpkfr/golidators" 7 | ) 8 | 9 | var DomainCases = [35]StrTestRecord{ 10 | {"example.com", true}, 11 | {"EXAMPLE.com", true}, 12 | {"xn----gtbspbbmkef.xn--p1ai", true}, 13 | {"underscore_subdomain.example.com", true}, 14 | {"something.versicherung", true}, 15 | {"someThing.versicherung", true}, 16 | {"11.com", true}, 17 | {"11.COM", true}, 18 | {"3.cn", true}, 19 | {"a.cn", true}, 20 | {"sub1.sub2.sample.co.uk", true}, 21 | {"somerandomexample.xn--fiqs8s", true}, 22 | {"kräuter.com", true}, 23 | {"über.com", true}, 24 | {"xn--eckwd4c7c.xn--zckzah", true}, 25 | {"XN--ECKWD4C7C.XN--ZCKZAH", true}, 26 | {"www.google.com", true}, 27 | {"google.com", true}, 28 | {"example.com/", false}, 29 | {"example.com:4444", false}, 30 | {"example.-com", false}, 31 | {"example.", false}, 32 | {"-example.com", false}, 33 | {"example-.com", false}, 34 | {"_example.com", false}, 35 | {"example_.com", false}, 36 | {"EXAMPLE_.COM", false}, 37 | {"example", false}, 38 | {"a......b.com", false}, 39 | {"A......B.COM", false}, 40 | {"a.123", false}, 41 | {"123.123", false}, 42 | {"123.123.123", false}, 43 | {"123.123.123.123", false}, 44 | {"example.com:4444", false}, 45 | } 46 | 47 | func TestDomain(t *testing.T) { 48 | for _, record := range DomainCases { 49 | if golidators.Domain(record.TargetValue) != record.Expected { 50 | t.Error(record.TargetValue) 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Workflow](https://github.com/eredotpkfr/golidators/actions/workflows/go.yml/badge.svg) 2 | [![Go Report Card](https://goreportcard.com/badge/github.com/eredotpkfr/golidators)](https://goreportcard.com/report/github.com/eredotpkfr/golidators) 3 | [![Go Reference](https://pkg.go.dev/badge/github.com/eredotpkfr/golidators.svg)](https://pkg.go.dev/github.com/eredotpkfr/golidators) 4 | [![Go Version](https://img.shields.io/github/go-mod/go-version/eredotpkfr/golidators)](https://golang.org/) 5 | [![Release](https://img.shields.io/github/v/release/eredotpkfr/golidators)](https://github.com/eredotpkfr/golidators/releases/latest) 6 | [![License](https://img.shields.io/badge/license-MIT-blue)](https://github.com/eredotpkfr/golidators/blob/main/LICENSE) 7 | [![Stars](https://img.shields.io/github/stars/eredotpkfr/golidators?style=social)](https://github.com/eredotpkfr/golidators/stargazers) 8 | 9 | # golidators 10 | 11 | Golidators is a golang package, it includes basic data validation functions and regexes 12 | 13 | ## Install 14 | 15 | ```bash 16 | ~$ go get github.com/eredotpkfr/golidators 17 | ``` 18 | 19 | ## Overview 20 | 21 | Following validators available on this package: 22 | 23 | - Domain 24 | - MD5, SHA1, SHA224, SHA256, SHA512 25 | - IPv4, IPv4CIDR, IPv6, IPv6CIDR 26 | - MAC 27 | - Port 28 | - URL 29 | - UUID 30 | - CreditCard/Luhn 31 | 32 | ## Usage 33 | 34 | Just import and use it. Also see documentation at [pkg.go.dev](https://pkg.go.dev/github.com/eredotpkfr/golidators#section-documentation) 35 | 36 | ```go 37 | package main 38 | 39 | import ( 40 | "github.com/eredotpkfr/golidators" 41 | "fmt" 42 | ) 43 | 44 | func main() { 45 | // Validate domain address 46 | fmt.Println(golidators.Domain("www.example.com")) 47 | // Validate IPv4 address 48 | fmt.Println(golidators.Ipv4("::1")) 49 | // Validate IPv6 address 50 | fmt.Println(golidators.Ipv6("::1")) 51 | // Validate URL 52 | fmt.Println(golidators.Url("https://www.example.com")) 53 | // Validate IPv4CIDR 54 | fmt.Println(golidators.Ipv4Cidr("127.0.0.1/12")) 55 | // Validate most common hashes 56 | fmt.Println(golidators.Md5("foo/bar")) 57 | // Validate with Luhn algorithm 58 | fmt.Println(golidators.Luhn(5300025108592596)) 59 | // Calculate check digit with Luhn algorithm 60 | fmt.Println(golidators.LuhnCheckDigit(5146713835433)) 61 | // Validate credit card number with Luhn 62 | fmt.Println(golidators.CreditCard(5184214431476070)) 63 | } 64 | ``` 65 | 66 | ## Contact 67 | 68 | Blog - [erdoganyoksul.com](https://www.erdoganyoksul.com)
69 | Mail - erdoganyoksul3@gmail.com 70 | -------------------------------------------------------------------------------- /internal/test/hashes_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/eredotpkfr/golidators" 7 | ) 8 | 9 | var ( 10 | Md5Cases = [4]StrTestRecord{ 11 | {"fffd51c41b9176ae3ed36a97699018eb", true}, 12 | {"D41D8CD98F00B204E9800998ECF8427E", true}, 13 | {"fffd51c41b9176ae3ed36a97699018eaa", false}, 14 | {"D41D8CD98F00B204E9800998ECF8427EA", false}, 15 | } 16 | Sha1Cases = [4]StrTestRecord{ 17 | {"7cf42528c35d46dad692c67b985240f63d53f63e", true}, 18 | {"930A0029225AA4C28B8EF095B679285EAAE27078", true}, 19 | {"7cf42528c35d46dad692c67b985240f63d53f63eaasdasd", false}, 20 | {"930A0029225AA4C28B8EF095B679285EAAE27078AKLMCDE", false}, 21 | } 22 | Sha224Cases = [4]StrTestRecord{ 23 | {"448b9c7aa6f6245ee1d77e7d140c73179f7f7a596dd386f62a0f6912", true}, 24 | {"448B9C7AA6F6245EE1D77E7D140C73179F7F7A596DD386F62A0F6912", true}, 25 | {"z14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f", false}, 26 | {"Z14A028C2A3A2BC9476102BB288234C415A2B01F828EA62AC5B3E42F", false}, 27 | } 28 | Sha256Cases = [4]StrTestRecord{ 29 | {"d8a928b2043db77e340b523547bf16cb4aa483f0645fe0a290ed1f20aab76257", true}, 30 | {"D8A928B2043DB77E340B523547BF16CB4AA483F0645FE0A290ED1F20AAB76257", true}, 31 | {"7815696ecbf1c96e6894b779456d330e", false}, 32 | {"7815696ECBF1C96E6894B779456D330E", false}, 33 | } 34 | Sha512Cases = [4]StrTestRecord{ 35 | {"7621770eae0880e21dbf3939f045b9a44013f190df54243f0a4b3d28806d2c55c5de651d4fd4160e09ec3af805695275da1933044a70677a3efa361943644577", true}, 36 | {"7621770EAE0880E21DBF3939F045B9A44013F190DF54243F0A4B3D28806D2C55C5DE651D4FD4160E09EC3AF805695275DA1933044A70677A3EFA361943644577", true}, 37 | {"7815696ecbf1c96e6894b779456d330e", false}, 38 | {"7815696ECBF1C96E6894B779456D330E", false}, 39 | } 40 | ) 41 | 42 | func TestMd5(t *testing.T) { 43 | for _, record := range Md5Cases { 44 | if golidators.Md5(record.TargetValue) != record.Expected { 45 | t.Error(record.TargetValue) 46 | } 47 | } 48 | } 49 | 50 | func TestSha1(t *testing.T) { 51 | for _, record := range Sha1Cases { 52 | if golidators.Sha1(record.TargetValue) != record.Expected { 53 | t.Error(record.TargetValue) 54 | } 55 | } 56 | } 57 | 58 | func TestSha224(t *testing.T) { 59 | for _, record := range Sha224Cases { 60 | if golidators.Sha224(record.TargetValue) != record.Expected { 61 | t.Error(record.TargetValue) 62 | } 63 | } 64 | } 65 | 66 | func TestSha256(t *testing.T) { 67 | for _, record := range Sha256Cases { 68 | if golidators.Sha256(record.TargetValue) != record.Expected { 69 | t.Error(record.TargetValue) 70 | } 71 | } 72 | } 73 | 74 | func TestSha512(t *testing.T) { 75 | for _, record := range Sha512Cases { 76 | if golidators.Sha512(record.TargetValue) != record.Expected { 77 | t.Error(record.TargetValue) 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /ip.go: -------------------------------------------------------------------------------- 1 | package golidators 2 | 3 | import ( 4 | "strings" 5 | 6 | utils "github.com/eredotpkfr/golidators/internal/utilities" 7 | ) 8 | 9 | const ( 10 | // IP CIDR separator 11 | cidrSep = "/" 12 | 13 | // 0xFF -> 255 14 | ipv4MaxPart = 0xFF 15 | ipv4Length = 4 16 | 17 | // 0xFFFF -> 65535 18 | ipv6MaxPart = 0xFFFF 19 | ipv6Length = 16 20 | ) 21 | 22 | // Ipv4 function for validating IPv4 23 | func Ipv4(ipv4Addr string) bool { 24 | for index := 0; index < ipv4Length; index++ { 25 | 26 | if len(ipv4Addr) == 0 { 27 | return false 28 | } 29 | 30 | if index > 0 { 31 | if ipv4Addr[0] != '.' { 32 | return false 33 | } 34 | 35 | ipv4Addr = ipv4Addr[1:] 36 | } 37 | 38 | number, consumed, ok := utils.Dtoi(ipv4Addr) 39 | 40 | if !ok || number > ipv4MaxPart { 41 | return false 42 | } 43 | 44 | if consumed > 1 && ipv4Addr[0] == '0' { 45 | return false 46 | } 47 | 48 | ipv4Addr = ipv4Addr[consumed:] 49 | 50 | } 51 | 52 | return len(ipv4Addr) == 0 53 | } 54 | 55 | // Ipv4Cidr function for validating IPv4CIDR 56 | func Ipv4Cidr(ipv4AddrCidr string) bool { 57 | splitted := strings.Split(ipv4AddrCidr, cidrSep) 58 | 59 | if len(splitted) != 2 { 60 | return false 61 | } 62 | 63 | prefix, suffix := splitted[0], splitted[1] 64 | number, consumed, ok := utils.Dtoi(suffix) 65 | 66 | if !ok || !Ipv4(prefix) || consumed > 2 { 67 | return false 68 | } 69 | 70 | return 0 <= number && number <= 32 71 | } 72 | 73 | // Ipv6 function for validating IPv6 74 | func Ipv6(ipv6Addr string) bool { 75 | ellipsis := -1 76 | 77 | if len(ipv6Addr) >= 2 && ipv6Addr[0] == ':' && ipv6Addr[1] == ':' { 78 | ellipsis = 0 79 | ipv6Addr = ipv6Addr[2:] 80 | 81 | if len(ipv6Addr) == 0 { 82 | return true 83 | } 84 | } 85 | 86 | index := 0 87 | for index < ipv6Length { 88 | number, consumed, ok := utils.Xtoi(ipv6Addr) 89 | 90 | if !ok || number > ipv6MaxPart { 91 | return false 92 | } 93 | 94 | if consumed < len(ipv6Addr) && ipv6Addr[consumed] == '.' { 95 | if ellipsis < 0 && index != ipv6Length-ipv4Length { 96 | return false 97 | } 98 | if index+ipv4Length > ipv6Length { 99 | return false 100 | } 101 | if !Ipv4(ipv6Addr) { 102 | return false 103 | } 104 | 105 | ipv6Addr = "" 106 | index += ipv4Length 107 | break 108 | } 109 | 110 | index += 2 111 | 112 | ipv6Addr = ipv6Addr[consumed:] 113 | if len(ipv6Addr) == 0 { 114 | break 115 | } 116 | 117 | if ipv6Addr[0] != ':' || len(ipv6Addr) == 1 { 118 | return false 119 | } 120 | 121 | ipv6Addr = ipv6Addr[1:] 122 | 123 | if ipv6Addr[0] == ':' { 124 | if ellipsis >= 0 { 125 | return false 126 | } 127 | 128 | ellipsis = index 129 | ipv6Addr = ipv6Addr[1:] 130 | 131 | if len(ipv6Addr) == 0 { 132 | break 133 | } 134 | 135 | } 136 | } 137 | 138 | if len(ipv6Addr) != 0 { 139 | return false 140 | } 141 | 142 | if index < ipv6Length { 143 | if ellipsis < 0 { 144 | return false 145 | } 146 | } else if ellipsis >= 0 { 147 | return false 148 | } 149 | 150 | return true 151 | } 152 | 153 | // Ipv6Cidr function for validating IPv6CIDR 154 | func Ipv6Cidr(ipv6AddrCidr string) bool { 155 | splitted := strings.Split(ipv6AddrCidr, cidrSep) 156 | 157 | if len(splitted) != 2 { 158 | return false 159 | } 160 | 161 | prefix, suffix := splitted[0], splitted[1] 162 | number, consumed, ok := utils.Dtoi(suffix) 163 | 164 | if !ok || !Ipv6(prefix) || consumed > 3 { 165 | return false 166 | } 167 | 168 | return 0 <= number && number <= 128 169 | } 170 | -------------------------------------------------------------------------------- /internal/regexes/url.go: -------------------------------------------------------------------------------- 1 | package regexes 2 | 3 | import rgx "regexp" 4 | 5 | const ( 6 | // IP octets 7 | ipMiddleOctet string = "(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5]))" 8 | ipLastOctet string = "(?:\\.(?:0|[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-5]))" 9 | 10 | urlPattern string = "^" + 11 | // protocol identifier 12 | "(?:(?:https?|ftp)://)" + 13 | // user:pass authentication 14 | "(?:[-a-z\u00a1-\uffff0-9._~%!$&'()*+,;=:]+" + 15 | "(?::[-a-z0-9._~%!$&'()*+,;=:]*)?@)?" + 16 | "(?:" + 17 | "(?P" + 18 | // IP address exclusion 19 | // private & local networks 20 | "(?:(?:10|127)" + ipMiddleOctet + "{2}" + ipLastOctet + ")|" + 21 | "(?:(?:169\\.254|192\\.168)" + ipMiddleOctet + ipLastOctet + ")|" + 22 | "(?:172\\.(?:1[6-9]|2\\d|3[0-1])" + ipMiddleOctet + ipLastOctet + "))" + 23 | "|" + 24 | // private & local hosts 25 | "(?P" + 26 | "(?:localhost))" + 27 | "|" + 28 | // IP address dotted notation octets 29 | // excludes loopback network 0.0.0.0 30 | // excludes reserved space >= 224.0.0.0 31 | // excludes network & broadcast addresses 32 | // (first & last IP address of each class) 33 | "(?P" + 34 | "(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])" + 35 | "" + ipMiddleOctet + "{2}" + 36 | "" + ipLastOctet + ")" + 37 | "|" + 38 | // IPv6 RegEx from https://stackoverflow.com/a/17871737 39 | "\\[(" + 40 | // 1:2:3:4:5:6:7:8 41 | "([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|" + 42 | // 1:: 1:2:3:4:5:6:7:: 43 | "([0-9a-fA-F]{1,4}:){1,7}:|" + 44 | // 1::8 1:2:3:4:5:6::8 1:2:3:4:5:6::8 45 | "([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|" + 46 | // 1::7:8 1:2:3:4:5::7:8 1:2:3:4:5::8 47 | "([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|" + 48 | // 1::6:7:8 1:2:3:4::6:7:8 1:2:3:4::8 49 | "([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|" + 50 | // 1::5:6:7:8 1:2:3::5:6:7:8 1:2:3::8 51 | "([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|" + 52 | // 1::4:5:6:7:8 1:2::4:5:6:7:8 1:2::8 53 | "([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|" + 54 | // 1::3:4:5:6:7:8 1::3:4:5:6:7:8 1::8 55 | "[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|" + 56 | // ::2:3:4:5:6:7:8 ::2:3:4:5:6:7:8 ::8 :: 57 | ":((:[0-9a-fA-F]{1,4}){1,7}|:)|" + 58 | // fe80::7:8%eth0 fe80::7:8%1 59 | // (link-local IPv6 addresses with zone index) 60 | "fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|" + 61 | "::(ffff(:0{1,4}){0,1}:){0,1}" + 62 | "((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}" + 63 | // ::255.255.255.255 ::ffff:255.255.255.255 ::ffff:0:255.255.255.255 64 | // (IPv4-mapped IPv6 addresses and IPv4-translated addresses) 65 | "(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|" + 66 | "([0-9a-fA-F]{1,4}:){1,4}:" + 67 | "((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}" + 68 | // 2001:db8:3:4::192.0.2.33 64:ff9b::192.0.2.33 69 | // (IPv4-Embedded IPv6 Address) 70 | "(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])" + 71 | ")\\]|" + 72 | // host name 73 | "(?:(?:(?:xn--)|[a-z\u00a1-\uffff\U00010000-\U0010ffff0-9]-?)*" + 74 | "[a-z\u00a1-\uffff\U00010000-\U0010ffff0-9]+)" + 75 | // domain name 76 | "(?:\\.(?:(?:xn--)|[a-z\u00a1-\uffff\U00010000-\U0010ffff0-9]-?)*" + 77 | "[a-z\u00a1-\uffff\U00010000-\U0010ffff0-9]+)*" + 78 | // TLD identifier 79 | "(?:\\.(?:(?:xn--[a-z\u00a1-\uffff\U00010000-\U0010ffff0-9]{2,})|" + 80 | "[a-z\u00a1-\uffff\U00010000-\U0010ffff]{2,}))" + 81 | ")" + 82 | // port number 83 | "(?::\\d{2,5})?" + 84 | // resource path 85 | "(?:/[-a-z\u00a1-\uffff\U00010000-\U0010ffff0-9._~%!$&'()*+,;=:@/]*)?" + 86 | // query string 87 | "(?:\\?\\S*)?" + 88 | // fragment 89 | "(?:#\\S*)?" + 90 | "$" 91 | ) 92 | 93 | // URLRegex for validating URL data 94 | var URLRegex = rgx.MustCompile(makeCaseInsensitive(urlPattern)) 95 | -------------------------------------------------------------------------------- /internal/test/ip_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/eredotpkfr/golidators" 7 | ) 8 | 9 | var ( 10 | Ipv4Cases = [26]StrTestRecord{ 11 | {"127.0.0.1", true}, 12 | {"10.0.0.0", true}, 13 | {"192.168.1.43", true}, 14 | {"95.67.123.56", true}, 15 | {"", false}, 16 | {"123.5.77.88", true}, 17 | {"12.12.12.12", true}, 18 | {"255.255.255.255", true}, 19 | {"abc.0.0.1", false}, 20 | {"1278.0.0.1", false}, 21 | {"127.0.0.abc", false}, 22 | {"900.200.100.75", false}, 23 | {"10.0.0.0:8080", false}, 24 | {"foo/bar", false}, 25 | {"127.0.0.1.", false}, 26 | {".......", false}, 27 | {"127.0.0.1....", false}, 28 | {"...127.0.0.1...", false}, 29 | {".127.0.0.1", false}, 30 | {"foo", false}, 31 | {"::1::2", false}, 32 | {"::1", false}, 33 | {"127.0.0.1.1.2.3", false}, 34 | {".127.0.0.1.", false}, 35 | {"clear.text", false}, 36 | {" ", false}, 37 | } 38 | Ipv4CidrCases = [27]StrTestRecord{ 39 | {"127.0.0.1/0", true}, 40 | {"123.5.77.88/8", true}, 41 | {"239.0.0.0/8", true}, 42 | {"12.12.12.12/32", true}, 43 | {"127.0.0.1/12", true}, 44 | {"192.168.1.45/24", true}, 45 | {"abc.0.0.1", false}, 46 | {"1.1.1.1", false}, 47 | {"1.1.1.1/-1", false}, 48 | {"1.1.1.1/33", false}, 49 | {"1.1.1.1/foo", false}, 50 | {"127.0.b.a/12", false}, 51 | {"127.0.0.2/44", false}, 52 | {"a.b.c/123/", false}, 53 | {".127.0.0.1/12", false}, 54 | {"..127.0.0.1/12", false}, 55 | {"127.0.0.1./12", false}, 56 | {"127.0.0.1../12", false}, 57 | {"", false}, 58 | {" ", false}, 59 | {"foo", false}, 60 | {"foo/bar", false}, 61 | {"127.0.0.1/12/13/14", false}, 62 | {"/", false}, 63 | {"////", false}, 64 | {"foo/bar/", false}, 65 | {"/foo/bar", false}, 66 | } 67 | Ipv6Cases = [20]StrTestRecord{ 68 | {"::1", true}, 69 | {"2002::", true}, 70 | {"0:0:0:0:0:ffff:c0a8:12b", true}, 71 | {"dead:beef:0:0:0:0:42:1", true}, 72 | {"abcd:ef::42:1", true}, 73 | {"0:0:0:0:0:ffff:1.2.3.4", true}, 74 | {"2001:0db8:0000:0000:0000:ff00:0042:8329", true}, 75 | {"::192.168.30.2", true}, 76 | {"abc.0.0.1", false}, 77 | {"2002::::::::::", false}, 78 | {"abcd:1234::123::1", false}, 79 | {"1:2:3:4:5:6:7:8:9", false}, 80 | {"abcd::1ffff", false}, 81 | {"", false}, 82 | {" ", false}, 83 | {"foo", false}, 84 | {"////", false}, 85 | {":", false}, 86 | {"::::", false}, 87 | {"deag:beef:0:0:0:0:42:1", false}, 88 | } 89 | Ipv6CidrCases = [26]StrTestRecord{ 90 | {"::1/0", true}, 91 | {"2002::/12", true}, 92 | {"dead:beef:0:0:0:0:42:1/8", true}, 93 | {"abcd:ef::42:1/32", true}, 94 | {"0:0:0:0:0:ffff:1.2.3.4/64", true}, 95 | {"::192.168.30.2/128", true}, 96 | {"abc.0.0.1", false}, 97 | {"abcd:1234::123::1", false}, 98 | {"1:2:3:4:5:6:7:8:9", false}, 99 | {"abcd::1ffff", false}, 100 | {"1.1.1.1", false}, 101 | {"::1", false}, 102 | {"::1/129", false}, 103 | {"::1/-1", false}, 104 | {"::1/foo", false}, 105 | {"::1/144", false}, 106 | {"2002::::::::::/12", false}, 107 | {"127.0.0.1/12/13/14", false}, 108 | {"/", false}, 109 | {"", false}, 110 | {" ", false}, 111 | {"///", false}, 112 | {"....", false}, 113 | {"....", false}, 114 | {"foo/bar/", false}, 115 | {"/foo/bar", false}, 116 | } 117 | ) 118 | 119 | func TestIpv4(t *testing.T) { 120 | for _, record := range Ipv4Cases { 121 | if golidators.Ipv4(record.TargetValue) != record.Expected { 122 | t.Error(record.TargetValue) 123 | } 124 | } 125 | } 126 | 127 | func TestIpv4Cidr(t *testing.T) { 128 | for _, record := range Ipv4CidrCases { 129 | if golidators.Ipv4Cidr(record.TargetValue) != record.Expected { 130 | t.Error(record.TargetValue) 131 | } 132 | } 133 | } 134 | 135 | func TestIpv6(t *testing.T) { 136 | for _, record := range Ipv6Cases { 137 | if golidators.Ipv6(record.TargetValue) != record.Expected { 138 | t.Error(record.TargetValue) 139 | } 140 | } 141 | } 142 | 143 | func TestIpv6Cidr(t *testing.T) { 144 | for _, record := range Ipv6CidrCases { 145 | if golidators.Ipv6Cidr(record.TargetValue) != record.Expected { 146 | t.Error(record.TargetValue) 147 | } 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /internal/test/url_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/eredotpkfr/golidators" 7 | ) 8 | 9 | var URLCases = [118]StrTestRecord{ 10 | {"http://foobar.dk", true}, 11 | {"http://foobar.museum/foobar", true}, 12 | {"http://fo.com", true}, 13 | {"http://www.google.com", true}, 14 | {"ftp://www.test.co/", true}, 15 | {"https://www.facebook.com/", true}, 16 | {"http://FOO.com", true}, 17 | {"http://foo.com/blah_blah", true}, 18 | {"http://foo.com/blah_blah/", true}, 19 | {"http://foo.com/blah_blah_(wikipedia)", true}, 20 | {"http://foo.com/blah_blah_(wikipedia)_(again)", true}, 21 | {"http://www.example.com/wpstyle/?p=364", true}, 22 | {"https://www.example.com/foo/?bar=baz&inga=42&quux", true}, 23 | {"https://www.example.com?bar=baz", true}, 24 | {"http://✪df.ws/123", true}, 25 | {"http://www.test.co?id=2&q=test#asdasda", true}, 26 | {"http://userid:password@example.com:8080", true}, 27 | {"http://userid:password@example.com:8080/", true}, 28 | {"http://userid@example.com", true}, 29 | {"http://userid@example.com/", true}, 30 | {"http://userid@example.com:8080", true}, 31 | {"http://userid@example.com:8080/", true}, 32 | {"http://userid:password@example.com", true}, 33 | {"http://userid:password@example.com/", true}, 34 | {"http://142.42.1.1/", true}, 35 | {"http://142.42.1.1:8080/", true}, 36 | {"http://➡.ws/䨹", true}, 37 | {"http://⌘.ws", true}, 38 | {"http://⌘.ws/", true}, 39 | {"http://foo.com/blah_(wikipedia)#cite-1", true}, 40 | {"http://foo.com/blah_(wikipedia)_blah#cite-1", true}, 41 | {"http://foo.com/unicode_(✪)_in_parens", true}, 42 | {"http://foo.com/(something)?after=parens", true}, 43 | {"http://☺.damowmow.com/", true}, 44 | {"http://code.google.com/events/#&product=browser", true}, 45 | {"http://j.mp", true}, 46 | {"ftp://foo.bar/baz", true}, 47 | {"http://foo.bar/?q=Test%20URL-encoded%20stuff", true}, 48 | {"http://مثال.إختبار", true}, 49 | {"http://例子.测试", true}, 50 | {"http://उदाहरण.परीक्षा", true}, 51 | {"http://www.😉.com", true}, 52 | {"http://😉.com/😁", true}, 53 | {"http://উদাহরণ.বাংলা", true}, 54 | {"http://xn--d5b6ci4b4b3a.xn--54b7fta0cc", true}, 55 | {"http://-.~_!$&'()*+,;=:%40:80%2f::::::@example.com", true}, 56 | {"http://1337.net", true}, 57 | {"http://a.b-c.de", true}, 58 | {"http://223.255.255.254", true}, 59 | {"http://10.1.1.0", true}, 60 | {"http://10.1.1.1", true}, 61 | {"http://10.1.1.254", true}, 62 | {"http://10.1.1.255", true}, 63 | {"http://127.0.0.1:8080", true}, 64 | {"http://127.0.10.150", true}, 65 | {"http://localhost", true}, 66 | {"http://localhost:8000", true}, 67 | {"http://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:80/index.html", true}, 68 | {"http://[1080:0:0:0:8:800:200C:417A]/index.html", true}, 69 | {"http://[3ffe:2a00:100:7031::1]", true}, 70 | {"http://[1080::8:800:200C:417A]/foo", true}, 71 | {"http://[::192.9.5.5]/ipng", true}, 72 | {"http://[::FFFF:129.144.52.38]:80/index.html", true}, 73 | {"http://[2010:836B:4179::836B:4179]", true}, 74 | {"", false}, 75 | {"google.com", false}, 76 | {"www.google.com", false}, 77 | {"çç3#>#>£#>{¾[¾{]âßâ¢ß", false}, 78 | {"http://http://asdad.com/?id=2", false}, 79 | {"\" 'test')\"", false}, 80 | {"http://foobar", false}, 81 | {"http://127.0.0/asdf", false}, 82 | {"http://foobar.d", false}, 83 | {"http://foobar.12", false}, 84 | {"http://foobar", false}, 85 | {"htp://foobar.com", false}, 86 | {"http://foobar..com", false}, 87 | {"http://fo..com", false}, 88 | {"http://", false}, 89 | {"http://.", false}, 90 | {"http://..", false}, 91 | {"http://../", false}, 92 | {"http://?", false}, 93 | {"http://??", false}, 94 | {"http://??/", false}, 95 | {"http://#", false}, 96 | {"http://##", false}, 97 | {"http://##/", false}, 98 | {"http://foo.bar?q=Spaces should be encoded", false}, 99 | {"//", false}, 100 | {"//a", false}, 101 | {"///a", false}, 102 | {"///", false}, 103 | {"http:///a", false}, 104 | {"foo.com", false}, 105 | {"rdar://1234", false}, 106 | {"h://test", false}, 107 | {"http:// shouldfail.com", false}, 108 | {":// should fail", false}, 109 | {"http://foo.bar/foo(bar)baz quux", false}, 110 | {"ftps://foo.bar/", false}, 111 | {"http://-error-.invalid/", false}, 112 | {"http://a.b--c.de/", false}, 113 | {"http://-a.b.co", false}, 114 | {"http://a.b-.co", false}, 115 | {"http://0.0.0.0", false}, 116 | {"http://224.1.1.1", false}, 117 | {"http://1.1.1.1.1", false}, 118 | {"http://123.123.123", false}, 119 | {"http://3628126748", false}, 120 | {"http://.www.foo.bar/", false}, 121 | {"http://www.foo.bar./", false}, 122 | {"http://.www.foo.bar./", false}, 123 | {"http://127.12.0.260", false}, 124 | {"http://example.com/\">user@example.com", false}, 125 | {"http://[2010:836B:4179::836B:4179", false}, 126 | {"http://2010:836B:4179::836B:4179", false}, 127 | {"http://2010:836B:4179::836B:4179:80/index.html", false}, 128 | } 129 | 130 | func TestUrl(t *testing.T) { 131 | for _, record := range URLCases { 132 | if golidators.Url(record.TargetValue) != record.Expected { 133 | t.Error(record.TargetValue) 134 | } 135 | } 136 | } 137 | --------------------------------------------------------------------------------