dfsm: Add all the missing API reference comments
[bendy-bus:bendy-bus.git] / dfsm / dfsm-ast-precondition.c
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 /*
3  * D-Bus Simulator
4  * Copyright (C) Philip Withnall 2011 <philip@tecnocode.co.uk>
5  * 
6  * D-Bus Simulator is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * D-Bus Simulator is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with D-Bus Simulator.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 /**
21  * SECTION:dfsm-ast-precondition
22  * @short_description: AST precondition node
23  * @stability: Unstable
24  * @include: dfsm/dfsm-ast-precondition.h
25  *
26  * AST precondition node implementing support for representing and evaluating a single precondition on a #DfsmAstTransition.
27  */
28
29 #include "config.h"
30
31 #include <glib.h>
32 #include <glib/gi18n-lib.h>
33
34 #include "dfsm-ast-precondition.h"
35 #include "dfsm-parser.h"
36 #include "dfsm-parser-internal.h"
37
38 static void dfsm_ast_precondition_dispose (GObject *object);
39 static void dfsm_ast_precondition_finalize (GObject *object);
40 static void dfsm_ast_precondition_sanity_check (DfsmAstNode *node);
41 static void dfsm_ast_precondition_pre_check_and_register (DfsmAstNode *node, DfsmEnvironment *environment, GError **error);
42 static void dfsm_ast_precondition_check (DfsmAstNode *node, DfsmEnvironment *environment, GError **error);
43
44 struct _DfsmAstPreconditionPrivate {
45         gchar *error_name; /* nullable */
46         DfsmAstExpression *condition;
47 };
48
49 G_DEFINE_TYPE (DfsmAstPrecondition, dfsm_ast_precondition, DFSM_TYPE_AST_NODE)
50
51 static void
52 dfsm_ast_precondition_class_init (DfsmAstPreconditionClass *klass)
53 {
54         GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
55         DfsmAstNodeClass *node_class = DFSM_AST_NODE_CLASS (klass);
56
57         g_type_class_add_private (klass, sizeof (DfsmAstPreconditionPrivate));
58
59         gobject_class->dispose = dfsm_ast_precondition_dispose;
60         gobject_class->finalize = dfsm_ast_precondition_finalize;
61
62         node_class->sanity_check = dfsm_ast_precondition_sanity_check;
63         node_class->pre_check_and_register = dfsm_ast_precondition_pre_check_and_register;
64         node_class->check = dfsm_ast_precondition_check;
65 }
66
67 static void
68 dfsm_ast_precondition_init (DfsmAstPrecondition *self)
69 {
70         self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, DFSM_TYPE_AST_PRECONDITION, DfsmAstPreconditionPrivate);
71 }
72
73 static void
74 dfsm_ast_precondition_dispose (GObject *object)
75 {
76         DfsmAstPreconditionPrivate *priv = DFSM_AST_PRECONDITION (object)->priv;
77
78         g_clear_object (&priv->condition);
79
80         /* Chain up to the parent class */
81         G_OBJECT_CLASS (dfsm_ast_precondition_parent_class)->dispose (object);
82 }
83
84 static void
85 dfsm_ast_precondition_finalize (GObject *object)
86 {
87         DfsmAstPreconditionPrivate *priv = DFSM_AST_PRECONDITION (object)->priv;
88
89         g_free (priv->error_name);
90
91         /* Chain up to the parent class */
92         G_OBJECT_CLASS (dfsm_ast_precondition_parent_class)->finalize (object);
93 }
94
95 static void
96 dfsm_ast_precondition_sanity_check (DfsmAstNode *node)
97 {
98         DfsmAstPreconditionPrivate *priv = DFSM_AST_PRECONDITION (node)->priv;
99
100         g_assert (priv->condition != NULL);
101         dfsm_ast_node_sanity_check (DFSM_AST_NODE (priv->condition));
102 }
103
104 static void
105 dfsm_ast_precondition_pre_check_and_register (DfsmAstNode *node, DfsmEnvironment *environment, GError **error)
106 {
107         DfsmAstPreconditionPrivate *priv = DFSM_AST_PRECONDITION (node)->priv;
108
109         if (priv->error_name != NULL && g_dbus_is_member_name (priv->error_name) == FALSE) {
110                 g_set_error (error, DFSM_PARSE_ERROR, DFSM_PARSE_ERROR_AST_INVALID, _("Invalid D-Bus error name: %s"), priv->error_name);
111                 return;
112         }
113
114         dfsm_ast_node_pre_check_and_register (DFSM_AST_NODE (priv->condition), environment, error);
115
116         if (*error != NULL) {
117                 return;
118         }
119 }
120
121 static void
122 dfsm_ast_precondition_check (DfsmAstNode *node, DfsmEnvironment *environment, GError **error)
123 {
124         DfsmAstPreconditionPrivate *priv = DFSM_AST_PRECONDITION (node)->priv;
125         GVariantType *condition_type;
126
127         dfsm_ast_node_check (DFSM_AST_NODE (priv->condition), environment, error);
128
129         if (*error != NULL) {
130                 return;
131         }
132
133         /* Check that the precondition is a boolean */
134         condition_type = dfsm_ast_expression_calculate_type (priv->condition, environment);
135
136         if (g_variant_type_equal (condition_type, G_VARIANT_TYPE_BOOLEAN) == FALSE) {
137                 gchar *condition_type_string;
138
139                 condition_type_string = g_variant_type_dup_string (condition_type);
140
141                 g_variant_type_free (condition_type);
142
143                 g_set_error (error, DFSM_PARSE_ERROR, DFSM_PARSE_ERROR_AST_INVALID,
144                              _("Incorrect type for precondition expression: expects type ā€˜%sā€™ but received type ā€˜%sā€™."),
145                              "b", condition_type_string);
146
147                 g_free (condition_type_string);
148
149                 return;
150         }
151
152         g_variant_type_free (condition_type);
153 }
154
155 /**
156  * dfsm_ast_precondition_new:
157  * @error_name: (allow-none): name of the D-Bus error to throw on precondition failure, or %NULL
158  * @condition: the condition to fulfil for the precondition
159  *
160  * Create a new #DfsmAstPrecondition for the given @condition.
161  *
162  * Return value: (transfer full): a new AST node
163  */
164 DfsmAstPrecondition *
165 dfsm_ast_precondition_new (const gchar *error_name /* nullable */, DfsmAstExpression *condition)
166 {
167         DfsmAstPrecondition *precondition;
168         DfsmAstPreconditionPrivate *priv;
169
170         g_return_val_if_fail (error_name == NULL || *error_name != '\0', NULL);
171         g_return_val_if_fail (DFSM_IS_AST_EXPRESSION (condition), NULL);
172
173         precondition = g_object_new (DFSM_TYPE_AST_PRECONDITION, NULL);
174         priv = precondition->priv;
175
176         priv->error_name = g_strdup (error_name);
177         priv->condition = g_object_ref (condition);
178
179         return precondition;
180 }
181
182 /**
183  * dfsm_ast_precondition_check_is_satisfied:
184  * @self: a #DfsmAstPrecondition
185  * @environment: a #DfsmEnvironment containing all variables
186  *
187  * Check whether the precondition is satisfied by the given @environment. This will evaluate the precondition's statement in the given @environment,
188  * and return the boolean value of the statement.
189  *
190  * If the precondition is not satisfied and has specified a D-Bus error to be thrown, %FALSE will be returned. You must call
191  * dfsm_ast_precondition_throw_error() to get the D-Bus error instance.
192  *
193  * Return value: %TRUE if the precondition is satisfied by @environment, %FALSE otherwise
194  */
195 gboolean
196 dfsm_ast_precondition_check_is_satisfied (DfsmAstPrecondition *self, DfsmEnvironment *environment)
197 {
198         DfsmAstPreconditionPrivate *priv;
199         GVariant *condition_value;
200         gboolean condition_holds;
201
202         g_return_val_if_fail (DFSM_IS_AST_PRECONDITION (self), FALSE);
203         g_return_val_if_fail (DFSM_IS_ENVIRONMENT (environment), FALSE);
204
205         priv = self->priv;
206
207         /* Evaluate the condition. */
208         condition_value = dfsm_ast_expression_evaluate (priv->condition, environment);
209         condition_holds = g_variant_get_boolean (condition_value);
210         g_variant_unref (condition_value);
211
212         return condition_holds;
213 }
214
215 /**
216  * dfsm_ast_precondition_throw_error:
217  * @self: a #DfsmAstPrecondition
218  * @output_sequence: an output sequence to append the D-Bus error to
219  *
220  * If the precondition has an error name set, append a D-Bus error of this type to @output_sequence, regardless of whether the precondition's condition
221  * is satisfied. If the precondition has no error name set, @output_sequence is not modified.
222  */
223 void
224 dfsm_ast_precondition_throw_error (DfsmAstPrecondition *self, DfsmOutputSequence *output_sequence)
225 {
226         DfsmAstPreconditionPrivate *priv;
227
228         g_return_if_fail (DFSM_IS_AST_PRECONDITION (self));
229         g_return_if_fail (DFSM_IS_OUTPUT_SEQUENCE (output_sequence));
230
231         priv = self->priv;
232
233         if (priv->error_name != NULL) {
234                 GError *child_error = g_dbus_error_new_for_dbus_error (priv->error_name, _("Precondition failed."));
235                 dfsm_output_sequence_add_throw (output_sequence, child_error);
236                 g_error_free (child_error);
237         }
238 }
239
240 /**
241  * dfsm_ast_precondition_get_error_name:
242  * @self: a #DfsmAstPrecondition
243  *
244  * Get the error name to be thrown by this precondition if its condition fails at runtime. If no error name is set, %NULL is returned.
245  *
246  * Return value: a D-Bus error name, or %NULL
247  */
248 const gchar *
249 dfsm_ast_precondition_get_error_name (DfsmAstPrecondition *self)
250 {
251         g_return_val_if_fail (DFSM_IS_AST_PRECONDITION (self), NULL);
252
253         return self->priv->error_name;
254 }