1# coding: utf-8
2from __future__ import absolute_import, division, print_function
3
4import string
5
6from hypothesis.strategies import (
7    composite, integers, just, lists, one_of, sampled_from, text)
8
9from parver import Version
10
11num_int = integers(min_value=0)
12num_str = num_int.map(str)
13
14
15def epoch():
16    epoch = num_str.map(lambda s: s + '!')
17    return one_of(just(''), epoch)
18
19
20@composite
21def release(draw):
22    return draw(
23        num_str
24        .map(lambda s: [s] + draw(lists(num_str.map(lambda s: '.' + s))))
25        .map(lambda l: ''.join(l))
26    )
27
28
29def separator(strict=False, optional=False):
30    sep = ['.']
31
32    if optional:
33        sep.append('')
34
35    if not strict:
36        sep.extend(['-', '_'])
37
38    return sampled_from(sep)
39
40
41@composite
42def pre(draw, strict=False):
43    words = ['a', 'b', 'rc']
44    if not strict:
45        words.extend(['c', 'alpha', 'beta', 'pre', 'preview'])
46
47    blank = just('')
48
49    sep1 = separator(strict=strict, optional=True)
50    if strict:
51        sep1 = blank
52
53    word = sampled_from(words)
54
55    if strict:
56        sep2 = blank
57    else:
58        sep2 = separator(strict=strict, optional=True)
59
60    num_part = sep2.map(lambda s: s + draw(num_str))
61    if not strict:
62        num_part = one_of(blank, num_part)
63
64    nonempty = (
65        sep1
66        .map(lambda s: s + draw(word) + draw(num_part))
67    )
68
69    return draw(one_of(blank, nonempty))
70
71
72@composite
73def post(draw, strict=False):
74    words = ['post']
75    if not strict:
76        words.extend(['r', 'rev'])
77
78    sep1 = separator(strict=strict, optional=not strict)
79    word = sampled_from(words)
80
81    blank = just('')
82
83    sep2 = separator(strict=strict, optional=True)
84    if strict:
85        sep2 = blank
86
87    num_part = sep2.map(lambda s: s + draw(num_str))
88    if not strict:
89        num_part = one_of(blank, num_part)
90
91    post = sep1.map(lambda s: s + draw(word) + draw(num_part))
92
93    if strict:
94        return draw(post)
95
96    post_implicit = num_str.map(lambda s: '-' + s)
97
98    return draw(one_of(blank, post_implicit, post))
99
100
101@composite
102def dev(draw, strict=False):
103    sep = separator(strict=strict, optional=not strict)
104
105    blank = just('')
106
107    num_part = num_str
108    if not strict:
109        num_part = one_of(blank, num_part)
110
111    return draw(one_of(blank, sep.map(lambda s: s + 'dev' + draw(num_part))))
112
113
114@composite
115def local_segment(draw):
116    alpha = (
117        draw(one_of(just(''), integers(0, 9).map(str)))
118        + draw(text(string.ascii_lowercase, min_size=1, max_size=1))
119        + draw(text(string.ascii_lowercase + string.digits))
120    )
121    return draw(one_of(num_str, just(alpha)))
122
123
124@composite
125def local(draw, strict=False):
126    if strict:
127        sep = just('.')
128    else:
129        sep = sampled_from('-_.')
130
131    part = local_segment()
132    sep_part = sep.map(lambda s: s + draw(local_segment()))
133    sep_parts = lists(sep_part).map(lambda l: ''.join(l))
134
135    return draw(one_of(just(''), part.map(lambda s: '+' + s + draw(sep_parts))))
136
137
138whitespace = sampled_from(['', '\t', '\n', '\r', '\f', '\v'])
139
140
141def vchar(strict=False):
142    if strict:
143        return just('')
144    return sampled_from(['', 'v'])
145
146
147@composite
148def version_string(draw, strict=False):
149    return (
150        draw(vchar(strict=strict)) +
151        draw(epoch()) +
152        draw(release()) +
153        draw(pre(strict=strict)) +
154        draw(post(strict=strict)) +
155        draw(dev(strict=strict)) +
156        draw(local(strict=strict))
157    )
158
159
160@composite
161def version_strategy(draw, strict=False):
162    return Version.parse(draw(version_string(strict=strict)))
163