Skip to content

Commit 8bc4f06

Browse files
committed
Initial commit
0 parents  commit 8bc4f06

File tree

7 files changed

+260
-0
lines changed

7 files changed

+260
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.DS_Store
2+
elm-stuff/

LICENSE

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
Copyright 2018 Anton Strömkvist
2+
3+
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
4+
5+
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
6+
7+
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
8+
9+
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
10+
11+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

README.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# elm-ssn-validation
2+
3+
Validate (currently only Swedish) social security numbers (personnummer).
4+
5+
## Installation
6+
7+
```sh
8+
elm-package install ahstro/elm-ssn-validation
9+
```
10+
11+
## Usage
12+
13+
```elm
14+
import Validation.SSN.Swedish as SSN
15+
16+
17+
case SSN.validate "811218-9876" of
18+
Ok ssn ->
19+
ssn ++ " is valid" -- "811218-9876 is valid"
20+
Err error ->
21+
error
22+
23+
24+
if SSN.isValid "811218-9876" then
25+
"Yay"
26+
else
27+
"Nay"
28+
29+
```
30+
31+
More examples are available in the [/tests](https://github.com/ahstro/elm-ssn-validation/tree/master/tests) folder.

elm-package.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"version": "1.0.0",
3+
"summary": "Validate social security numbers",
4+
"repository": "https://github.com/ahstro/elm-ssn-validation.git",
5+
"license": "BSD3",
6+
"source-directories": [
7+
"src/"
8+
],
9+
"exposed-modules": [
10+
"Validate.SSN.Swedish"
11+
],
12+
"dependencies": {
13+
"ahstro/elm-luhn": "1.0.0 <= v < 2.0.0",
14+
"elm-lang/core": "5.0.0 <= v < 6.0.0"
15+
},
16+
"elm-version": "0.18.0 <= v < 0.19.0"
17+
}

src/Validate/SSN/Swedish.elm

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
module Validate.SSN.Swedish exposing (isValid, validate)
2+
3+
{-| This library allows you to validate Swedish social security
4+
numbers ("personnummer").
5+
6+
It supports the following formats:
7+
8+
- `"8112189876"`
9+
- `"811218-9876"`
10+
- `"811218+9876"`
11+
- `"198112189876"`
12+
- `"19811218-9876"`
13+
- `"19811218+9876"`
14+
15+
16+
# Validation
17+
18+
@docs validate, isValid
19+
20+
-}
21+
22+
import Luhn
23+
import Regex exposing (Regex, regex)
24+
25+
26+
{-| Accepts a string and returns a `Result String String` indicating
27+
whether the string was a valid Swedish SSN ("personnummer").
28+
29+
validate "811218-9876" == Ok "811218-9876"
30+
validate "811218" == Err "Invalid Swedish SSN"
31+
32+
-}
33+
validate : String -> Result String String
34+
validate ssn =
35+
let
36+
match =
37+
case Regex.find Regex.All ssnRegex ssn of
38+
validSSN :: [] ->
39+
Ok validSSN
40+
41+
_ ->
42+
Err "Invalid Swedish SSN"
43+
in
44+
match
45+
|> Result.map .submatches
46+
|> Result.andThen normalize
47+
|> Result.andThen Luhn.validate
48+
|> Result.map (\_ -> ssn)
49+
50+
51+
{-| Same as `validate` but returns a `Bool` instead, for easy use in
52+
`if`-expressions.
53+
54+
isValid "811218-9876" == True
55+
isValid "811218" == False
56+
57+
-}
58+
isValid : String -> Bool
59+
isValid ssn =
60+
case validate ssn of
61+
Ok _ ->
62+
True
63+
64+
Err _ ->
65+
False
66+
67+
68+
{-| Takes the `.submatches` of a `ssnRegex` match and returns a either
69+
an `Ok` with a normalized string (with the "YYMMDDXXXX" format) or an
70+
`Err`.
71+
-}
72+
normalize : List (Maybe String) -> Result String String
73+
normalize ssn =
74+
case ssn of
75+
(Just year) :: (Just month) :: (Just day) :: (Just controlDigits) :: [] ->
76+
Ok <| String.right 2 year ++ month ++ day ++ controlDigits
77+
78+
_ ->
79+
Err "Invalid input to normalize"
80+
81+
82+
{-| Regex for valid inputs.
83+
84+
Matches the following formats:
85+
86+
- `"8112189876"`
87+
- `"811218-9876"`
88+
- `"811218+9876"`
89+
- `"198112189876"`
90+
- `"19811218-9876"`
91+
- `"19811218+9876"`
92+
93+
The regex is made up of seven parts
94+
95+
- `^`
96+
97+
Denotes the start of the line
98+
99+
- `((?:\d{2})?\d{2})`
100+
101+
The year part. Matches two optional digits, e.g. "19", and two
102+
obligatory digits, e.g. "81".
103+
104+
- `(\d{2})`
105+
106+
The month part. Matches two obligatory digits, e.g. "12".
107+
108+
- `(\d{2})`
109+
110+
The day part. Matches two obligatory digits, e.g. "18".
111+
112+
- `[-+]?`
113+
114+
The separator part. Matches one optional separator, e.g. "-".
115+
116+
- `(\d{4})`
117+
118+
The control-digits part. Matches four obligatory digits, e.g. "9876".
119+
120+
- `$`
121+
122+
Denotes the start of the line
123+
124+
-}
125+
ssnRegex : Regex
126+
ssnRegex =
127+
regex "^((?:\\d{2})?\\d{2})(\\d{2})(\\d{2})[-+]?(\\d{4})$"

tests/SwedishTests.elm

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
module SwedishTests exposing (suite)
2+
3+
import Expect
4+
import Result.Extra exposing (isErr, isOk)
5+
import Test exposing (Test, describe, test)
6+
import Validate.SSN.Swedish as SSN
7+
8+
9+
validSSNs : List String
10+
validSSNs =
11+
[ "8112189876"
12+
, "811218-9876"
13+
, "811218+9876"
14+
, "198112189876"
15+
, "19811218-9876"
16+
, "19811218+9876"
17+
]
18+
19+
20+
invalidSSNs : List String
21+
invalidSSNs =
22+
[ "98112189876"
23+
, "9811218-9876"
24+
, "9811218+9876"
25+
, "19811218"
26+
, "19811218-"
27+
, "19811218+"
28+
, "811218"
29+
, "811218-"
30+
, "811218+"
31+
, "asdf+"
32+
, "asdf"
33+
, "1"
34+
, "0123456789"
35+
, ""
36+
]
37+
38+
39+
suite : Test
40+
suite =
41+
describe "Validate.SSN.Swedish"
42+
[ describe "validate"
43+
[ test "Returns Ok for valid SSNs" <|
44+
\_ ->
45+
Expect.equal
46+
(List.map (isOk << SSN.validate) validSSNs)
47+
(List.map (always True) validSSNs)
48+
, test "Returns Err for invalid SSNs" <|
49+
\_ ->
50+
Expect.equal
51+
(List.map (isErr << SSN.validate) invalidSSNs)
52+
(List.map (always True) invalidSSNs)
53+
]
54+
]

tests/elm-package.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"version": "1.0.0",
3+
"summary": "Validate social security numbers",
4+
"repository": "https://github.com/ahstro/elm-ssn-validation.git",
5+
"license": "BSD3",
6+
"source-directories": [
7+
"../src/",
8+
"."
9+
],
10+
"exposed-modules": [],
11+
"dependencies": {
12+
"ahstro/elm-luhn": "1.0.0 <= v < 2.0.0",
13+
"elm-community/elm-test": "4.0.0 <= v < 5.0.0",
14+
"elm-community/result-extra": "2.2.0 <= v < 3.0.0",
15+
"elm-lang/core": "5.0.0 <= v < 6.0.0"
16+
},
17+
"elm-version": "0.18.0 <= v < 0.19.0"
18+
}

0 commit comments

Comments
 (0)