1 /*
2 * This file is part of libmodulemd
3 * Copyright (C) 2017-2018 Stephen Gallagher
4 *
5 * Fedora-License-Identifier: MIT
6 * SPDX-2.0-License-Identifier: MIT
7 * SPDX-3.0-License-Identifier: MIT
8 *
9 * This program is free software.
10 * For more information on the license, see COPYING.
11 * For more information on free software, see <https://www.gnu.org/philosophy/free-sw.en.html>.
12 */
13
14 #include <glib.h>
15 #include <yaml.h>
16
17 #include "modulemd-service-level.h"
18 #include "private/glib-extensions.h"
19 #include "private/modulemd-service-level-private.h"
20 #include "private/modulemd-util.h"
21 #include "private/modulemd-yaml.h"
22
23 #define SL_DEFAULT_STRING "__NAME_UNSET__"
24
25 struct _ModulemdServiceLevel
26 {
27 GObject parent_instance;
28
29 gchar *name;
30 GDate *eol;
31 };
32
33 G_DEFINE_TYPE (ModulemdServiceLevel, modulemd_service_level, G_TYPE_OBJECT)
34
35 enum
36 {
37 PROP_0,
38
39 PROP_NAME,
40
41 N_PROPS
42 };
43
44 static GParamSpec *properties[N_PROPS];
45
46
47 ModulemdServiceLevel *
modulemd_service_level_new(const gchar * name)48 modulemd_service_level_new (const gchar *name)
49 {
50 // clang-format off
51 return g_object_new (MODULEMD_TYPE_SERVICE_LEVEL,
52 "name", name,
53 NULL);
54 // clang-format on
55 }
56
57
58 gboolean
modulemd_service_level_equals_wrapper(const void * a,const void * b)59 modulemd_service_level_equals_wrapper (const void *a, const void *b)
60 {
61 g_return_val_if_fail (MODULEMD_IS_SERVICE_LEVEL ((ModulemdServiceLevel *)a),
62 FALSE);
63 g_return_val_if_fail (MODULEMD_IS_SERVICE_LEVEL ((ModulemdServiceLevel *)b),
64 FALSE);
65
66 return modulemd_service_level_equals ((ModulemdServiceLevel *)a,
67 (ModulemdServiceLevel *)b);
68 }
69
70
71 gboolean
modulemd_service_level_equals(ModulemdServiceLevel * self_1,ModulemdServiceLevel * self_2)72 modulemd_service_level_equals (ModulemdServiceLevel *self_1,
73 ModulemdServiceLevel *self_2)
74 {
75 if (!self_1 && !self_2)
76 {
77 return TRUE;
78 }
79
80 if (!self_1 || !self_2)
81 {
82 return FALSE;
83 }
84
85 g_return_val_if_fail (MODULEMD_IS_SERVICE_LEVEL (self_1), FALSE);
86 g_return_val_if_fail (MODULEMD_IS_SERVICE_LEVEL (self_2), FALSE);
87
88 if (g_strcmp0 (modulemd_service_level_get_name (self_1),
89 modulemd_service_level_get_name (self_2)) != 0)
90 {
91 return FALSE;
92 }
93
94 /*if both eols are invalid, its equivalent*/
95 if (!g_date_valid (self_1->eol) && !g_date_valid (self_2->eol))
96 {
97 return TRUE;
98 }
99
100 if (!g_date_valid (self_1->eol) || !g_date_valid (self_2->eol))
101 {
102 return FALSE;
103 }
104
105 if (g_date_compare (self_1->eol, self_2->eol) != 0)
106 {
107 return FALSE;
108 }
109
110 return TRUE;
111 }
112
113
114 ModulemdServiceLevel *
modulemd_service_level_copy(ModulemdServiceLevel * self)115 modulemd_service_level_copy (ModulemdServiceLevel *self)
116 {
117 g_autoptr (ModulemdServiceLevel) sl = NULL;
118 g_return_val_if_fail (MODULEMD_IS_SERVICE_LEVEL (self), NULL);
119
120 sl = modulemd_service_level_new (modulemd_service_level_get_name (self));
121
122 modulemd_service_level_set_eol (sl, modulemd_service_level_get_eol (self));
123
124 return g_object_ref (sl);
125 }
126
127
128 static void
modulemd_service_level_finalize(GObject * object)129 modulemd_service_level_finalize (GObject *object)
130 {
131 ModulemdServiceLevel *self = (ModulemdServiceLevel *)object;
132
133 g_clear_pointer (&self->name, g_free);
134 g_clear_pointer (&self->eol, g_date_free);
135
136 G_OBJECT_CLASS (modulemd_service_level_parent_class)->finalize (object);
137 }
138
139
140 static void
modulemd_service_level_set_name(ModulemdServiceLevel * self,const gchar * name)141 modulemd_service_level_set_name (ModulemdServiceLevel *self, const gchar *name)
142 {
143 g_return_if_fail (MODULEMD_IS_SERVICE_LEVEL (self));
144
145 /* It is a coding error if we ever get a NULL name here */
146 g_return_if_fail (name);
147
148 /* It is a coding error if we ever get the default name here */
149 g_return_if_fail (g_strcmp0 (name, SL_DEFAULT_STRING));
150
151 g_clear_pointer (&self->name, g_free);
152 self->name = g_strdup (name);
153
154 g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_NAME]);
155 }
156
157
158 const gchar *
modulemd_service_level_get_name(ModulemdServiceLevel * self)159 modulemd_service_level_get_name (ModulemdServiceLevel *self)
160 {
161 g_return_val_if_fail (MODULEMD_IS_SERVICE_LEVEL (self), NULL);
162
163 return self->name;
164 }
165
166
167 void
modulemd_service_level_set_eol(ModulemdServiceLevel * self,GDate * date)168 modulemd_service_level_set_eol (ModulemdServiceLevel *self, GDate *date)
169 {
170 g_return_if_fail (MODULEMD_IS_SERVICE_LEVEL (self));
171
172 if (!date || !g_date_valid (date))
173 {
174 g_date_clear (self->eol, 1);
175 return;
176 }
177
178 if (!g_date_valid (self->eol) || g_date_compare (date, self->eol) != 0)
179 {
180 /* Date is changing. Update it */
181 g_date_set_year (self->eol, g_date_get_year (date));
182 g_date_set_month (self->eol, g_date_get_month (date));
183 g_date_set_day (self->eol, g_date_get_day (date));
184 }
185 }
186
187
188 void
modulemd_service_level_set_eol_ymd(ModulemdServiceLevel * self,GDateYear year,GDateMonth month,GDateDay day)189 modulemd_service_level_set_eol_ymd (ModulemdServiceLevel *self,
190 GDateYear year,
191 GDateMonth month,
192 GDateDay day)
193 {
194 g_autoptr (GDate) date = NULL;
195 g_return_if_fail (MODULEMD_IS_SERVICE_LEVEL (self));
196
197 if (!g_date_valid_dmy (day, month, year))
198 {
199 /* Treat invalid dates as NULL */
200 return modulemd_service_level_set_eol (self, NULL);
201 }
202
203 date = g_date_new_dmy (day, month, year);
204 return modulemd_service_level_set_eol (self, date);
205 }
206
207
208 void
modulemd_service_level_remove_eol(ModulemdServiceLevel * self)209 modulemd_service_level_remove_eol (ModulemdServiceLevel *self)
210 {
211 return modulemd_service_level_set_eol (self, NULL);
212 }
213
214
215 GDate *
modulemd_service_level_get_eol(ModulemdServiceLevel * self)216 modulemd_service_level_get_eol (ModulemdServiceLevel *self)
217 {
218 g_return_val_if_fail (MODULEMD_IS_SERVICE_LEVEL (self), NULL);
219
220 if (self->eol && g_date_valid (self->eol))
221 {
222 return self->eol;
223 }
224
225 return NULL;
226 }
227
228
229 gchar *
modulemd_service_level_get_eol_as_string(ModulemdServiceLevel * self)230 modulemd_service_level_get_eol_as_string (ModulemdServiceLevel *self)
231 {
232 if (self->eol && g_date_valid (self->eol))
233 {
234 return g_strdup_printf ("%.4d-%.2d-%.2d",
235 g_date_get_year (self->eol),
236 g_date_get_month (self->eol),
237 g_date_get_day (self->eol));
238 }
239
240 return NULL;
241 }
242
243
244 static void
modulemd_service_level_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)245 modulemd_service_level_get_property (GObject *object,
246 guint prop_id,
247 GValue *value,
248 GParamSpec *pspec)
249 {
250 ModulemdServiceLevel *self = MODULEMD_SERVICE_LEVEL (object);
251
252 switch (prop_id)
253 {
254 case PROP_NAME:
255 g_value_set_string (value, modulemd_service_level_get_name (self));
256 break;
257
258 default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
259 }
260 }
261
262
263 static void
modulemd_service_level_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)264 modulemd_service_level_set_property (GObject *object,
265 guint prop_id,
266 const GValue *value,
267 GParamSpec *pspec)
268 {
269 ModulemdServiceLevel *self = MODULEMD_SERVICE_LEVEL (object);
270
271 switch (prop_id)
272 {
273 case PROP_NAME:
274 modulemd_service_level_set_name (self, g_value_get_string (value));
275 break;
276
277 default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
278 }
279 }
280
281
282 static void
modulemd_service_level_class_init(ModulemdServiceLevelClass * klass)283 modulemd_service_level_class_init (ModulemdServiceLevelClass *klass)
284 {
285 GObjectClass *object_class = G_OBJECT_CLASS (klass);
286
287 object_class->finalize = modulemd_service_level_finalize;
288 object_class->get_property = modulemd_service_level_get_property;
289 object_class->set_property = modulemd_service_level_set_property;
290
291 properties[PROP_NAME] = g_param_spec_string (
292 "name",
293 "Name",
294 "A human-readable name for this servicelevel",
295 SL_DEFAULT_STRING,
296 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
297
298 g_object_class_install_properties (object_class, N_PROPS, properties);
299 }
300
301
302 static void
modulemd_service_level_init(ModulemdServiceLevel * self)303 modulemd_service_level_init (ModulemdServiceLevel *self)
304 {
305 self->eol = g_date_new ();
306 }
307
308
309 /* === YAML Functions === */
310
311 ModulemdServiceLevel *
modulemd_service_level_parse_yaml(yaml_parser_t * parser,const gchar * name,gboolean strict,GError ** error)312 modulemd_service_level_parse_yaml (yaml_parser_t *parser,
313 const gchar *name,
314 gboolean strict,
315 GError **error)
316 {
317 MODULEMD_INIT_TRACE ();
318 MMD_INIT_YAML_EVENT (event);
319 gboolean done = FALSE;
320 gboolean in_map = FALSE;
321 g_autoptr (ModulemdServiceLevel) sl = NULL;
322 GDate *eol = NULL;
323 g_autoptr (GError) nested_error = NULL;
324
325 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
326
327 sl = modulemd_service_level_new (name);
328
329 /* Read in any supplementary attributes of the service level,
330 * such as 'eol'
331 */
332 while (!done)
333 {
334 YAML_PARSER_PARSE_WITH_EXIT (parser, &event, error);
335
336 switch (event.type)
337 {
338 case YAML_MAPPING_START_EVENT:
339 /* This is the start of the service level content. */
340 in_map = TRUE;
341 break;
342
343 case YAML_MAPPING_END_EVENT:
344 if (!in_map)
345 {
346 MMD_YAML_ERROR_EVENT_EXIT (
347 error, event, "Unexpected MAPPING_END in service level");
348 break;
349 }
350 /* We're done processing the service level content */
351 in_map = FALSE;
352 done = TRUE;
353 break;
354
355 case YAML_SCALAR_EVENT:
356 if (!in_map)
357 {
358 /* We must be in the map before we handle scalars */
359 MMD_YAML_ERROR_EVENT_EXIT (
360 error, event, "Missing mapping in service level");
361 break;
362 }
363 /* Only "eol" is supported right now */
364 if (!g_strcmp0 ((const gchar *)event.data.scalar.value, "eol"))
365 {
366 /* Get the EOL date */
367 eol = modulemd_yaml_parse_date (parser, &nested_error);
368 if (!eol)
369 {
370 MMD_YAML_ERROR_EVENT_EXIT (
371 error,
372 event,
373 "Failed to parse EOL date in service level: %s",
374 nested_error->message);
375 }
376
377 modulemd_service_level_set_eol (sl, eol);
378 g_date_free (eol);
379 }
380 else
381 {
382 /* Unknown field in service level */
383 SKIP_UNKNOWN (parser,
384 FALSE,
385 "Unexpected key in service level body: %s",
386 (const gchar *)event.data.scalar.value);
387 }
388 break;
389
390 default:
391 /* We received a YAML event we shouldn't expect at this level */
392 MMD_YAML_ERROR_EVENT_EXIT (
393 error, event, "Unexpected YAML event in service level");
394 break;
395 }
396 yaml_event_delete (&event);
397 }
398
399 return g_object_ref (sl);
400 }
401
402 gboolean
modulemd_service_level_emit_yaml(ModulemdServiceLevel * self,yaml_emitter_t * emitter,GError ** error)403 modulemd_service_level_emit_yaml (ModulemdServiceLevel *self,
404 yaml_emitter_t *emitter,
405 GError **error)
406 {
407 MODULEMD_INIT_TRACE ();
408 int ret;
409 g_autoptr (GError) nested_error = NULL;
410 g_autofree gchar *eol_string = NULL;
411 MMD_INIT_YAML_EVENT (event);
412
413 /* Emit the Service Level Name */
414 ret = mmd_emitter_scalar (emitter,
415 modulemd_service_level_get_name (self),
416 YAML_PLAIN_SCALAR_STYLE,
417 &nested_error);
418 if (!ret)
419 {
420 g_propagate_prefixed_error (error,
421 g_steal_pointer (&nested_error),
422 "Failed to emit service level name: ");
423 return FALSE;
424 }
425
426 /* Start the mapping for additional attributes of this service level */
427 ret = mmd_emitter_start_mapping (
428 emitter, YAML_BLOCK_MAPPING_STYLE, &nested_error);
429 if (!ret)
430 {
431 g_propagate_prefixed_error (error,
432 g_steal_pointer (&nested_error),
433 "Failed to start service level mapping: ");
434 return FALSE;
435 }
436
437 /* Add service level attributes if available */
438 if (modulemd_service_level_get_eol (self) != NULL)
439 {
440 ret = mmd_emitter_scalar (
441 emitter, "eol", YAML_PLAIN_SCALAR_STYLE, &nested_error);
442 if (!ret)
443 {
444 g_propagate_prefixed_error (error,
445 g_steal_pointer (&nested_error),
446 "Failed to emit EOL key: ");
447 return FALSE;
448 }
449
450 eol_string = modulemd_service_level_get_eol_as_string (self);
451 ret = mmd_emitter_scalar (
452 emitter, eol_string, YAML_PLAIN_SCALAR_STYLE, &nested_error);
453 if (!ret)
454 {
455 g_propagate_prefixed_error (error,
456 g_steal_pointer (&nested_error),
457 "Failed to emit EOL string [%s]: ",
458 eol_string);
459 return FALSE;
460 }
461 }
462
463 /* End the mapping */
464 ret = mmd_emitter_end_mapping (emitter, &nested_error);
465 if (!ret)
466 {
467 g_propagate_prefixed_error (error,
468 g_steal_pointer (&nested_error),
469 "Failed to end service level mapping: ");
470 return FALSE;
471 }
472
473 return TRUE;
474 }
475