Merge branch 'master' into next
[sliver-openvswitch.git] / lib / ovsdb-types.c
1 /* Copyright (c) 2009, 2010 Nicira Networks
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <config.h>
17
18 #include "ovsdb-types.h"
19
20 #include <limits.h>
21
22 #include "dynamic-string.h"
23 #include "json.h"
24 #include "ovsdb-error.h"
25 #include "ovsdb-parser.h"
26
27 const struct ovsdb_type ovsdb_type_integer =
28     OVSDB_TYPE_SCALAR_INITIALIZER(OVSDB_TYPE_INTEGER);
29 const struct ovsdb_type ovsdb_type_real =
30     OVSDB_TYPE_SCALAR_INITIALIZER(OVSDB_TYPE_REAL);
31 const struct ovsdb_type ovsdb_type_boolean =
32     OVSDB_TYPE_SCALAR_INITIALIZER(OVSDB_TYPE_BOOLEAN);
33 const struct ovsdb_type ovsdb_type_string =
34     OVSDB_TYPE_SCALAR_INITIALIZER(OVSDB_TYPE_STRING);
35 const struct ovsdb_type ovsdb_type_uuid =
36     OVSDB_TYPE_SCALAR_INITIALIZER(OVSDB_TYPE_UUID);
37
38 const char *
39 ovsdb_atomic_type_to_string(enum ovsdb_atomic_type type)
40 {
41     switch (type) {
42     case OVSDB_TYPE_VOID:
43         return "void";
44
45     case OVSDB_TYPE_INTEGER:
46         return "integer";
47
48     case OVSDB_TYPE_REAL:
49         return "real";
50
51     case OVSDB_TYPE_BOOLEAN:
52         return "boolean";
53
54     case OVSDB_TYPE_STRING:
55         return "string";
56
57     case OVSDB_TYPE_UUID:
58         return "uuid";
59
60     case OVSDB_N_TYPES:
61     default:
62         return "<invalid>";
63     }
64 }
65
66 struct json *
67 ovsdb_atomic_type_to_json(enum ovsdb_atomic_type type)
68 {
69     return json_string_create(ovsdb_atomic_type_to_string(type));
70 }
71
72 bool
73 ovsdb_type_is_valid(const struct ovsdb_type *type)
74 {
75     return (type->key_type != OVSDB_TYPE_VOID
76             && ovsdb_atomic_type_is_valid(type->key_type)
77             && ovsdb_atomic_type_is_valid(type->value_type)
78             && type->n_min <= 1
79             && type->n_min <= type->n_max
80             && (type->value_type == OVSDB_TYPE_VOID
81                 || ovsdb_atomic_type_is_valid_key(type->key_type)));
82 }
83
84 bool
85 ovsdb_atomic_type_from_string(const char *string, enum ovsdb_atomic_type *type)
86 {
87     if (!strcmp(string, "integer")) {
88         *type = OVSDB_TYPE_INTEGER;
89     } else if (!strcmp(string, "real")) {
90         *type = OVSDB_TYPE_REAL;
91     } else if (!strcmp(string, "boolean")) {
92         *type = OVSDB_TYPE_BOOLEAN;
93     } else if (!strcmp(string, "string")) {
94         *type = OVSDB_TYPE_STRING;
95     } else if (!strcmp(string, "uuid")) {
96         *type = OVSDB_TYPE_UUID;
97     } else {
98         return false;
99     }
100     return true;
101 }
102
103 struct ovsdb_error *
104 ovsdb_atomic_type_from_json(enum ovsdb_atomic_type *type,
105                             const struct json *json)
106 {
107     if (json->type == JSON_STRING) {
108         if (ovsdb_atomic_type_from_string(json_string(json), type)) {
109             return NULL;
110         } else {
111             *type = OVSDB_TYPE_VOID;
112             return ovsdb_syntax_error(json, NULL,
113                                       "\"%s\" is not an atomic-type",
114                                       json_string(json));
115         }
116     } else {
117         *type = OVSDB_TYPE_VOID;
118         return ovsdb_syntax_error(json, NULL, "atomic-type expected");
119     }
120 }
121
122 static struct ovsdb_error *
123 n_from_json(const struct json *json, unsigned int *n)
124 {
125     if (!json) {
126         return NULL;
127     } else if (json->type == JSON_INTEGER
128                && json->u.integer >= 0 && json->u.integer < UINT_MAX) {
129         *n = json->u.integer;
130         return NULL;
131     } else {
132         return ovsdb_syntax_error(json, NULL, "bad min or max value");
133     }
134 }
135
136 char *
137 ovsdb_type_to_english(const struct ovsdb_type *type)
138 {
139     const char *key = ovsdb_atomic_type_to_string(type->key_type);
140     const char *value = ovsdb_atomic_type_to_string(type->value_type);
141     if (ovsdb_type_is_scalar(type)) {
142         return xstrdup(key);
143     } else {
144         struct ds s = DS_EMPTY_INITIALIZER;
145         ds_put_cstr(&s, ovsdb_type_is_set(type) ? "set" : "map");
146         if (type->n_max == UINT_MAX) {
147             if (type->n_min) {
148                 ds_put_format(&s, " of %u or more", type->n_min);
149             } else {
150                 ds_put_cstr(&s, " of");
151             }
152         } else if (type->n_min) {
153             ds_put_format(&s, " of %u to %u", type->n_min, type->n_max);
154         } else {
155             ds_put_format(&s, " of up to %u", type->n_max);
156         }
157         if (ovsdb_type_is_set(type)) {
158             ds_put_format(&s, " %ss", key);
159         } else {
160             ds_put_format(&s, " (%s, %s) pairs", key, value);
161         }
162         return ds_cstr(&s);
163     }
164 }
165
166 struct ovsdb_error *
167 ovsdb_type_from_json(struct ovsdb_type *type, const struct json *json)
168 {
169     type->value_type = OVSDB_TYPE_VOID;
170     type->n_min = 1;
171     type->n_max = 1;
172
173     if (json->type == JSON_STRING) {
174         return ovsdb_atomic_type_from_json(&type->key_type, json);
175     } else if (json->type == JSON_OBJECT) {
176         const struct json *key, *value, *min, *max;
177         struct ovsdb_error *error;
178         struct ovsdb_parser parser;
179
180         ovsdb_parser_init(&parser, json, "ovsdb type");
181         key = ovsdb_parser_member(&parser, "key", OP_STRING);
182         value = ovsdb_parser_member(&parser, "value", OP_STRING | OP_OPTIONAL);
183         min = ovsdb_parser_member(&parser, "min", OP_INTEGER | OP_OPTIONAL);
184         max = ovsdb_parser_member(&parser, "max",
185                                   OP_INTEGER | OP_STRING | OP_OPTIONAL);
186         error = ovsdb_parser_finish(&parser);
187         if (error) {
188             return error;
189         }
190
191         error = ovsdb_atomic_type_from_json(&type->key_type, key);
192         if (error) {
193             return error;
194         }
195
196         if (value) {
197             error = ovsdb_atomic_type_from_json(&type->value_type, value);
198             if (error) {
199                 return error;
200             }
201         }
202
203         error = n_from_json(min, &type->n_min);
204         if (error) {
205             return error;
206         }
207
208         if (max && max->type == JSON_STRING
209             && !strcmp(max->u.string, "unlimited")) {
210             type->n_max = UINT_MAX;
211         } else {
212             error = n_from_json(max, &type->n_max);
213             if (error) {
214                 return error;
215             }
216         }
217
218         if (!ovsdb_type_is_valid(type)) {
219             return ovsdb_syntax_error(json, NULL,
220                                       "ovsdb type fails constraint checks");
221         }
222
223         return NULL;
224     } else {
225         return ovsdb_syntax_error(json, NULL, "ovsdb type expected");
226     }
227 }
228
229 struct json *
230 ovsdb_type_to_json(const struct ovsdb_type *type)
231 {
232     if (ovsdb_type_is_scalar(type)) {
233         return ovsdb_atomic_type_to_json(type->key_type);
234     } else {
235         struct json *json = json_object_create();
236         json_object_put(json, "key",
237                         ovsdb_atomic_type_to_json(type->key_type));
238         if (type->value_type != OVSDB_TYPE_VOID) {
239             json_object_put(json, "value",
240                             ovsdb_atomic_type_to_json(type->value_type));
241         }
242         if (type->n_min != 1) {
243             json_object_put(json, "min", json_integer_create(type->n_min));
244         }
245         if (type->n_max == UINT_MAX) {
246             json_object_put_string(json, "max", "unlimited");
247         } else if (type->n_max != 1) {
248             json_object_put(json, "max", json_integer_create(type->n_max));
249         }
250         return json;
251     }
252 }