1# -*- coding: utf-8 -*-
2# Copyright (C) 2016 Adrien Vergé
3#
4# This program is free software: you can redistribute it and/or modify
5# it under the terms of the GNU General Public License as published by
6# the Free Software Foundation, either version 3 of the License, or
7# (at your option) any later version.
8#
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12# GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License
15# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
17"""
18Use this rule to control the use of flow mappings or number of spaces inside
19braces (``{`` and ``}``).
20
21.. rubric:: Options
22
23* ``forbid`` is used to forbid the use of flow mappings which are denoted by
24  surrounding braces (``{`` and ``}``). Use ``true`` to forbid the use of flow
25  mappings completely. Use ``non-empty`` to forbid the use of all flow
26  mappings except for empty ones.
27* ``min-spaces-inside`` defines the minimal number of spaces required inside
28  braces.
29* ``max-spaces-inside`` defines the maximal number of spaces allowed inside
30  braces.
31* ``min-spaces-inside-empty`` defines the minimal number of spaces required
32  inside empty braces.
33* ``max-spaces-inside-empty`` defines the maximal number of spaces allowed
34  inside empty braces.
35
36.. rubric:: Default values (when enabled)
37
38.. code-block:: yaml
39
40 rules:
41   braces:
42     forbid: false
43     min-spaces-inside: 0
44     max-spaces-inside: 0
45     min-spaces-inside-empty: -1
46     max-spaces-inside-empty: -1
47
48.. rubric:: Examples
49
50#. With ``braces: {forbid: true}``
51
52   the following code snippet would **PASS**:
53   ::
54
55    object:
56      key1: 4
57      key2: 8
58
59   the following code snippet would **FAIL**:
60   ::
61
62    object: { key1: 4, key2: 8 }
63
64#. With ``braces: {forbid: non-empty}``
65
66   the following code snippet would **PASS**:
67   ::
68
69    object: {}
70
71   the following code snippet would **FAIL**:
72   ::
73
74    object: { key1: 4, key2: 8 }
75
76#. With ``braces: {min-spaces-inside: 0, max-spaces-inside: 0}``
77
78   the following code snippet would **PASS**:
79   ::
80
81    object: {key1: 4, key2: 8}
82
83   the following code snippet would **FAIL**:
84   ::
85
86    object: { key1: 4, key2: 8 }
87
88#. With ``braces: {min-spaces-inside: 1, max-spaces-inside: 3}``
89
90   the following code snippet would **PASS**:
91   ::
92
93    object: { key1: 4, key2: 8 }
94
95   the following code snippet would **PASS**:
96   ::
97
98    object: { key1: 4, key2: 8   }
99
100   the following code snippet would **FAIL**:
101   ::
102
103    object: {    key1: 4, key2: 8   }
104
105   the following code snippet would **FAIL**:
106   ::
107
108    object: {key1: 4, key2: 8 }
109
110#. With ``braces: {min-spaces-inside-empty: 0, max-spaces-inside-empty: 0}``
111
112   the following code snippet would **PASS**:
113   ::
114
115    object: {}
116
117   the following code snippet would **FAIL**:
118   ::
119
120    object: { }
121
122#. With ``braces: {min-spaces-inside-empty: 1, max-spaces-inside-empty: -1}``
123
124   the following code snippet would **PASS**:
125   ::
126
127    object: {         }
128
129   the following code snippet would **FAIL**:
130   ::
131
132    object: {}
133"""
134
135
136import yaml
137
138from yamllint.linter import LintProblem
139from yamllint.rules.common import spaces_after, spaces_before
140
141
142ID = 'braces'
143TYPE = 'token'
144CONF = {'forbid': (bool, 'non-empty'),
145        'min-spaces-inside': int,
146        'max-spaces-inside': int,
147        'min-spaces-inside-empty': int,
148        'max-spaces-inside-empty': int}
149DEFAULT = {'forbid': False,
150           'min-spaces-inside': 0,
151           'max-spaces-inside': 0,
152           'min-spaces-inside-empty': -1,
153           'max-spaces-inside-empty': -1}
154
155
156def check(conf, token, prev, next, nextnext, context):
157    if (conf['forbid'] is True and
158            isinstance(token, yaml.FlowMappingStartToken)):
159        yield LintProblem(token.start_mark.line + 1,
160                          token.end_mark.column + 1,
161                          'forbidden flow mapping')
162
163    elif (conf['forbid'] == 'non-empty' and
164            isinstance(token, yaml.FlowMappingStartToken) and
165            not isinstance(next, yaml.FlowMappingEndToken)):
166        yield LintProblem(token.start_mark.line + 1,
167                          token.end_mark.column + 1,
168                          'forbidden flow mapping')
169
170    elif (isinstance(token, yaml.FlowMappingStartToken) and
171            isinstance(next, yaml.FlowMappingEndToken)):
172        problem = spaces_after(token, prev, next,
173                               min=(conf['min-spaces-inside-empty']
174                                    if conf['min-spaces-inside-empty'] != -1
175                                    else conf['min-spaces-inside']),
176                               max=(conf['max-spaces-inside-empty']
177                                    if conf['max-spaces-inside-empty'] != -1
178                                    else conf['max-spaces-inside']),
179                               min_desc='too few spaces inside empty braces',
180                               max_desc='too many spaces inside empty braces')
181        if problem is not None:
182            yield problem
183
184    elif isinstance(token, yaml.FlowMappingStartToken):
185        problem = spaces_after(token, prev, next,
186                               min=conf['min-spaces-inside'],
187                               max=conf['max-spaces-inside'],
188                               min_desc='too few spaces inside braces',
189                               max_desc='too many spaces inside braces')
190        if problem is not None:
191            yield problem
192
193    elif (isinstance(token, yaml.FlowMappingEndToken) and
194            (prev is None or
195             not isinstance(prev, yaml.FlowMappingStartToken))):
196        problem = spaces_before(token, prev, next,
197                                min=conf['min-spaces-inside'],
198                                max=conf['max-spaces-inside'],
199                                min_desc='too few spaces inside braces',
200                                max_desc='too many spaces inside braces')
201        if problem is not None:
202            yield problem
203