Merge "master" branch into "db".
[cascardo/ovs.git] / lib / ovsdb-types.c
1 /* Copyright (c) 2009 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 <= type->n_max
79             && (type->value_type == OVSDB_TYPE_VOID
80                 || ovsdb_atomic_type_is_valid_key(type->key_type)));
81 }
82
83 bool
84 ovsdb_atomic_type_from_string(const char *string, enum ovsdb_atomic_type *type)
85 {
86     if (!strcmp(string, "integer")) {
87         *type = OVSDB_TYPE_INTEGER;
88     } else if (!strcmp(string, "real")) {
89         *type = OVSDB_TYPE_REAL;
90     } else if (!strcmp(string, "boolean")) {
91         *type = OVSDB_TYPE_BOOLEAN;
92     } else if (!strcmp(string, "string")) {
93         *type = OVSDB_TYPE_STRING;
94     } else if (!strcmp(string, "uuid")) {
95         *type = OVSDB_TYPE_UUID;
96     } else {
97         return false;
98     }
99     return true;
100 }
101
102 struct ovsdb_error *
103 ovsdb_atomic_type_from_json(enum ovsdb_atomic_type *type,
104                             const struct json *json)
105 {
106     if (json->type == JSON_STRING) {
107         if (ovsdb_atomic_type_from_string(json_string(json), type)) {
108             return NULL;
109         } else {
110             *type = OVSDB_TYPE_VOID;
111             return ovsdb_syntax_error(json, NULL,
112                                       "\"%s\" is not an atomic-type",
113                                       json_string(json));
114         }
115     } else {
116         *type = OVSDB_TYPE_VOID;
117         return ovsdb_syntax_error(json, NULL, "atomic-type expected");
118     }
119 }
120
121 static struct ovsdb_error *
122 n_from_json(const struct json *json, unsigned int *n)
123 {
124     if (!json) {
125         return NULL;
126     } else if (json->type == JSON_INTEGER
127                && json->u.integer >= 0 && json->u.integer < UINT_MAX) {
128         *n = json->u.integer;
129         return NULL;
130     } else {
131         return ovsdb_syntax_error(json, NULL, "bad min or max value");
132     }
133 }
134
135 char *
136 ovsdb_type_to_english(const struct ovsdb_type *type)
137 {
138     const char *key = ovsdb_atomic_type_to_string(type->key_type);
139     const char *value = ovsdb_atomic_type_to_string(type->value_type);
140     if (ovsdb_type_is_scalar(type)) {
141         return xstrdup(key);
142     } else {
143         struct ds s = DS_EMPTY_INITIALIZER;
144         ds_put_cstr(&s, ovsdb_type_is_set(type) ? "set" : "map");
145         if (type->n_max == UINT_MAX) {
146             if (type->n_min) {
147                 ds_put_format(&s, " of %u or more", type->n_min);
148             } else {
149                 ds_put_cstr(&s, " of");
150             }
151         } else if (type->n_min) {
152             ds_put_format(&s, " of %u to %u", type->n_min, type->n_max);
153         } else {
154             ds_put_format(&s, " of up to %u", type->n_max);
155         }
156         if (ovsdb_type_is_set(type)) {
157             ds_put_format(&s, " %ss", key);
158         } else {
159             ds_put_format(&s, " (%s, %s) pairs", key, value);
160         }
161         return ds_cstr(&s);
162     }
163 }
164
165 struct ovsdb_error *
166 ovsdb_type_from_json(struct ovsdb_type *type, const struct json *json)
167 {
168     type->value_type = OVSDB_TYPE_VOID;
169     type->n_min = 1;
170     type->n_max = 1;
171
172     if (json->type == JSON_STRING) {
173         return ovsdb_atomic_type_from_json(&type->key_type, json);
174     } else if (json->type == JSON_OBJECT) {
175         const struct json *key, *value, *min, *max;
176         struct ovsdb_error *error;
177         struct ovsdb_parser parser;
178
179         ovsdb_parser_init(&parser, json, "ovsdb type");
180         key = ovsdb_parser_member(&parser, "key", OP_STRING);
181         value = ovsdb_parser_member(&parser, "value", OP_STRING | OP_OPTIONAL);
182         min = ovsdb_parser_member(&parser, "min", OP_INTEGER | OP_OPTIONAL);
183         max = ovsdb_parser_member(&parser, "max",
184                                   OP_INTEGER | OP_STRING | OP_OPTIONAL);
185         error = ovsdb_parser_finish(&parser);
186         if (error) {
187             return error;
188         }
189
190         error = ovsdb_atomic_type_from_json(&type->key_type, key);
191         if (error) {
192             return error;
193         }
194
195         if (value) {
196             error = ovsdb_atomic_type_from_json(&type->value_type, value);
197             if (error) {
198                 return error;
199             }
200         }
201
202         error = n_from_json(min, &type->n_min);
203         if (error) {
204             return error;
205         }
206
207         if (max && max->type == JSON_STRING
208             && !strcmp(max->u.string, "unlimited")) {
209             type->n_max = UINT_MAX;
210         } else {
211             error = n_from_json(max, &type->n_max);
212             if (error) {
213                 return error;
214             }
215         }
216
217         if (!ovsdb_type_is_valid(type)) {
218             return ovsdb_syntax_error(json, NULL,
219                                       "ovsdb type fails constraint checks");
220         }
221
222         return NULL;
223     } else {
224         return ovsdb_syntax_error(json, NULL, "ovsdb type expected");
225     }
226 }
227
228 struct json *
229 ovsdb_type_to_json(const struct ovsdb_type *type)
230 {
231     if (ovsdb_type_is_scalar(type)) {
232         return ovsdb_atomic_type_to_json(type->key_type);
233     } else {
234         struct json *json = json_object_create();
235         json_object_put(json, "key",
236                         ovsdb_atomic_type_to_json(type->key_type));
237         if (type->value_type != OVSDB_TYPE_VOID) {
238             json_object_put(json, "value",
239                             ovsdb_atomic_type_to_json(type->value_type));
240         }
241         if (type->n_min != 1) {
242             json_object_put(json, "min", json_integer_create(type->n_min));
243         }
244         if (type->n_max == UINT_MAX) {
245             json_object_put_string(json, "max", "unlimited");
246         } else if (type->n_max != 1) {
247             json_object_put(json, "max", json_integer_create(type->n_max));
248         }
249         return json;
250     }
251 }