summaryrefslogtreecommitdiffstats
path: root/gitlint/options.py
blob: a1ae59c16dfcf1f2fbc72d3f13aea6f5f07df53c (plain)
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
from abc import abstractmethod
import os

from gitlint.utils import ustr, sstr


class RuleOptionError(Exception):
    pass


class RuleOption(object):
    """ Base class representing a configurable part (i.e. option) of a rule (e.g. the max-length of the title-max-line
        rule).
        This class should not be used directly. Instead, use on the derived classes like StrOption, IntOption to set
        options of a particular type like int, str, etc.
    """

    def __init__(self, name, value, description):
        self.name = ustr(name)
        self.description = ustr(description)
        self.value = None
        self.set(value)

    @abstractmethod
    def set(self, value):
        """ Validates and sets the option's value """
        pass  # pragma: no cover

    def __str__(self):
        return sstr(self)  # pragma: no cover

    def __unicode__(self):
        return u"({0}: {1} ({2}))".format(self.name, self.value, self.description)  # pragma: no cover

    def __repr__(self):
        return self.__str__()  # pragma: no cover

    def __eq__(self, other):
        return self.name == other.name and self.description == other.description and self.value == other.value

    def __ne__(self, other):
        return not self.__eq__(other)  # required for py2


class StrOption(RuleOption):
    def set(self, value):
        self.value = ustr(value)


class IntOption(RuleOption):
    def __init__(self, name, value, description, allow_negative=False):
        self.allow_negative = allow_negative
        super(IntOption, self).__init__(name, value, description)

    def _raise_exception(self, value):
        if self.allow_negative:
            error_msg = u"Option '{0}' must be an integer (current value: '{1}')".format(self.name, value)
        else:
            error_msg = u"Option '{0}' must be a positive integer (current value: '{1}')".format(self.name, value)
        raise RuleOptionError(error_msg)

    def set(self, value):
        try:
            self.value = int(value)
        except ValueError:
            self._raise_exception(value)

        if not self.allow_negative and self.value < 0:
            self._raise_exception(value)


class BoolOption(RuleOption):
    def set(self, value):
        value = ustr(value).strip().lower()
        if value not in ['true', 'false']:
            raise RuleOptionError(u"Option '{0}' must be either 'true' or 'false'".format(self.name))
        self.value = value == 'true'


class ListOption(RuleOption):
    """ Option that is either a given list or a comma-separated string that can be splitted into a list when being set.
    """

    def set(self, value):
        if isinstance(value, list):
            the_list = value
        else:
            the_list = ustr(value).split(",")

        self.value = [ustr(item.strip()) for item in the_list if item.strip() != ""]


class PathOption(RuleOption):
    """ Option that accepts either a directory or both a directory and a file. """

    def __init__(self, name, value, description, type=u"dir"):
        self.type = type
        super(PathOption, self).__init__(name, value, description)

    def set(self, value):
        value = ustr(value)

        error_msg = u""

        if self.type == 'dir':
            if not os.path.isdir(value):
                error_msg = u"Option {0} must be an existing directory (current value: '{1}')".format(self.name, value)
        elif self.type == 'file':
            if not os.path.isfile(value):
                error_msg = u"Option {0} must be an existing file (current value: '{1}')".format(self.name, value)
        elif self.type == 'both':
            if not os.path.isdir(value) and not os.path.isfile(value):
                error_msg = (u"Option {0} must be either an existing directory or file "
                             u"(current value: '{1}')").format(self.name, value)
        else:
            error_msg = u"Option {0} type must be one of: 'file', 'dir', 'both' (current: '{1}')".format(self.name,
                                                                                                         self.type)

        if error_msg:
            raise RuleOptionError(error_msg)

        self.value = os.path.realpath(value)