-
Notifications
You must be signed in to change notification settings - Fork 45
/
predicates.clj
161 lines (136 loc) · 4.6 KB
/
predicates.clj
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
(ns valip.predicates
"Predicates useful for validating input strings, such as ones from a HTML
form."
(:require [clojure.string :as string]
[clj-time.format :as time-format])
(:import
[java.net URL MalformedURLException]
java.util.Hashtable
javax.naming.NamingException
javax.naming.directory.InitialDirContext
[org.apache.commons.validator.routines IntegerValidator
DoubleValidator]))
(defn present?
"Returns false if x is nil or blank, true otherwise."
[x]
(not (string/blank? x)))
(defn matches
"Creates a predicate that returns true if the supplied regular expression
matches its argument."
[re]
(fn [s] (boolean (re-matches re s))))
(defn max-length
"Creates a predicate that returns true if a string's length is less than or
equal to the supplied maximum."
[max]
(fn [s] (<= (count s) max)))
(defn min-length
"Creates a predicate that returns true if a string's length is greater than
or equal to the supplied minimum."
[min]
(fn [s] (>= (count s) min)))
(defn email-address?
"Returns true if the email address is valid, based on RFC 2822. Email
addresses containing quotation marks or square brackets are considered
invalid, as this syntax is not commonly supported in practise. The domain of
the email address is not checked for validity."
[email]
{:pre [(present? email)]}
(let [re (str "(?i)[a-z0-9!#$%&'*+/=?^_`{|}~-]+"
"(?:\\.[a-z0-9!#$%&'*+/=?" "^_`{|}~-]+)*"
"@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+"
"[a-z0-9](?:[a-z0-9-]*[a-z0-9])?")]
(boolean (re-matches (re-pattern re) email))))
(defn- dns-lookup [^String hostname ^String type]
(let [params {"java.naming.factory.initial"
"com.sun.jndi.dns.DnsContextFactory"}]
(try
(.. (InitialDirContext. (Hashtable. params))
(getAttributes hostname (into-array [type]))
(get type))
(catch NamingException _
nil))))
(defn valid-email-domain?
"Returns true if the domain of the supplied email address has a MX DNS entry."
[email]
{:pre [(email-address? email)]}
(if-let [domain (second (re-matches #".*@(.*)" email))]
(boolean (dns-lookup domain "MX"))))
(defn url?
"Returns true if the string is a valid URL."
[s]
{:pre [(present? s)]}
(try
(URL. s) true
(catch MalformedURLException _ false)))
(defn digits?
"Returns true if a string consists only of numerical digits."
[s]
{:pre [(present? s)]}
(boolean (re-matches #"\d+" s)))
(defn alphanumeric?
"Returns true if a string consists only of alphanumeric characters."
[s]
{:pre [(present? s)]}
(boolean (re-matches #"[A-Za-z0-9]+")))
(defn integer-string?
"Returns true if the string represents an integer."
[s]
{:pre [(present? s)]}
(boolean (.validate (IntegerValidator.) s)))
(defn decimal-string?
"Returns true if the string represents a decimal number."
[s]
{:pre [(present? s)]}
(boolean (.validate (DoubleValidator.) s)))
(defn- parse-number [x]
{:pre [(present? x)]}
(if (string? x)
(.validate (DoubleValidator.) x)
x))
(defn gt
"Creates a predicate function for checking if a value is numerically greater
than the specified number."
[n]
(fn [x] (> (parse-number x) n)))
(defn lt
"Creates a predicate function for checking if a value is numerically less
than the specified number."
[n]
(fn [x] (< (parse-number x) n)))
(defn gte
"Creates a predicate function for checking if a value is numerically greater
than or equal to the specified number."
[n]
(fn [x] (>= (parse-number x) n)))
(defn lte
"Creates a predicate function for checking if a value is numerically less
than or equal to the specified number."
[n]
(fn [x] (<= (parse-number x) n)))
(def ^{:doc "Alias for gt"} over gt)
(def ^{:doc "Alias for lt"} under lt)
(def ^{:doc "Alias for gte"} at-least gte)
(def ^{:doc "Alias for lte"} at-most lte)
(defn between
"Creates a predicate function for checking whether a number is between two
values (inclusive)."
[min max]
(fn [x]
(let [x (parse-number x)]
(and (>= x min) (<= x max)))))
(defn- parse-date-time [format input]
{:pre [(present? input)]}
(let [formatter (time-format/formatter format)]
(try
(time-format/parse formatter input)
(catch IllegalArgumentException _ nil))))
(defn date-format
"Creates a function for parsing a date using the supplied format string."
[format]
(partial parse-date-time format))
(defn html5-date?
"Returns true if the string is one that could be returned by a HTML5 date
input element."
[s]
(boolean (parse-date-time "yyyy-MM-dd" s)))