summaryrefslogtreecommitdiffstats
path: root/src/shared/varlink-idl.h
blob: ffb55f30663d9224dc7e8b89a8d4d093b8e3f531 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once

#include <errno.h>
#include <stdbool.h>
#include <stdio.h>

#include "sd-json.h"
#include "macro.h"

/* This implements the Varlink Interface Definition Language ("Varlink IDL"),
 * i.e. https://varlink.org/Interface-Definition
 *
 * Primarily allows encoding static interface definitions in C code, that can be converted to the textual IDL
 * format on-the-fly. Can also parse the textual format back to C structures. Validates the interface
 * definitions for internal consistency and validates JSON objects against the interface definitions. */

typedef enum VarlinkSymbolType {
        VARLINK_ENUM_TYPE,
        VARLINK_STRUCT_TYPE,
        VARLINK_METHOD,
        VARLINK_ERROR,
        _VARLINK_INTERFACE_COMMENT,     /* Not really a symbol, just a comment about the interface */
        _VARLINK_SYMBOL_COMMENT,        /* Not really a symbol, just a comment about a symbol */
        _VARLINK_SYMBOL_TYPE_MAX,
        _VARLINK_SYMBOL_TYPE_INVALID = -EINVAL,
} VarlinkSymbolType;

typedef enum VarlinkFieldType {
        _VARLINK_FIELD_TYPE_END_MARKER = 0, /* zero type means: this is the last entry in the fields[] array of VarlinkSymbol */
        VARLINK_STRUCT,
        VARLINK_ENUM,
        VARLINK_NAMED_TYPE,
        VARLINK_BOOL,
        VARLINK_INT,
        VARLINK_FLOAT,
        VARLINK_STRING,
        VARLINK_OBJECT,
        VARLINK_ENUM_VALUE,
        _VARLINK_FIELD_COMMENT,        /* Not really a field, just a comment about a field*/
        _VARLINK_FIELD_TYPE_MAX,
        _VARLINK_FIELD_TYPE_INVALID = -EINVAL,
} VarlinkFieldType;

typedef enum VarlinkFieldDirection {
        VARLINK_REGULAR,
        VARLINK_INPUT,
        VARLINK_OUTPUT,
        _VARLINK_FIELD_DIRECTION_MAX,
        _VARLINK_FIELD_DIRECTION_INVALID = -EINVAL,
} VarlinkFieldDirection;

typedef enum VarlinkFieldFlags {
        VARLINK_ARRAY                = 1 << 0,
        VARLINK_MAP                  = 1 << 1,
        VARLINK_NULLABLE             = 1 << 2,
        _VARLINK_FIELD_FLAGS_MAX     = (1 << 3) - 1,
        _VARLINK_FIELD_FLAGS_INVALID = -EINVAL,
} VarlinkFieldFlags;

typedef struct VarlinkField VarlinkField;
typedef struct VarlinkSymbol VarlinkSymbol;
typedef struct VarlinkInterface VarlinkInterface;

/* Fields are the components making up symbols */
struct VarlinkField {
        const char *name;
        VarlinkFieldType field_type;
        VarlinkFieldFlags field_flags;
        VarlinkFieldDirection field_direction; /* in case of method call fields: whether input or output argument */
        const VarlinkSymbol *symbol;           /* VARLINK_STRUCT, VARLINK_ENUM: anonymous symbol that carries the definitions, VARLINK_NAMED_TYPE: resolved symbol */
        const char *named_type;                /* VARLINK_NAMED_TYPE */
};

/* Symbols are primary named concepts in an interface, and are methods, errors or named types (either enum or struct). */
struct VarlinkSymbol {
        const char *name; /* most symbols have a name, but sometimes they are created on-the-fly for fields, in which case they are anonymous */
        VarlinkSymbolType symbol_type;
        VarlinkField fields[];
};

/* An interface definition has a name and consist of symbols */
struct VarlinkInterface {
        const char *name;
        const VarlinkSymbol *symbols[];
};

#define VARLINK_DEFINE_FIELD(_name, _field_type, _field_flags)          \
        { .name = #_name, .field_type = (_field_type), .field_flags = (_field_flags) }

#define VARLINK_DEFINE_FIELD_BY_TYPE(_name, _named_type, _field_flags)  \
        { .name = #_name, .field_type = VARLINK_NAMED_TYPE, .named_type = #_named_type, .symbol = &vl_type_ ## _named_type, .field_flags = (_field_flags) }

#define VARLINK_DEFINE_INPUT(_name, _field_type, _field_flags)          \
        { .name = #_name, .field_type = (_field_type), .field_flags = (_field_flags), .field_direction = VARLINK_INPUT }

#define VARLINK_DEFINE_INPUT_BY_TYPE(_name, _named_type, _field_flags)  \
        { .name = #_name, .field_type = VARLINK_NAMED_TYPE, .named_type = #_named_type, .symbol = &vl_type_ ## _named_type, .field_flags = (_field_flags), .field_direction = VARLINK_INPUT }

#define VARLINK_DEFINE_OUTPUT(_name, _field_type, _field_flags)         \
        { .name = #_name, .field_type = (_field_type), .field_flags = (_field_flags), .field_direction = VARLINK_OUTPUT }

#define VARLINK_DEFINE_OUTPUT_BY_TYPE(_name, _named_type, _field_flags) \
        { .name = #_name, .field_type = VARLINK_NAMED_TYPE, .named_type = #_named_type, .symbol = &vl_type_ ## _named_type, .field_flags = (_field_flags), .field_direction = VARLINK_OUTPUT }

#define VARLINK_DEFINE_ENUM_VALUE(_name)                                \
        { .name = #_name, .field_type = VARLINK_ENUM_VALUE }

#define VARLINK_FIELD_COMMENT(text)                                     \
        { .name = "" text, .field_type = _VARLINK_FIELD_COMMENT }

#define VARLINK_DEFINE_METHOD(_name, ...)                               \
        const VarlinkSymbol vl_method_ ## _name = {                     \
                .name = #_name,                                         \
                .symbol_type = VARLINK_METHOD,                          \
                .fields = { __VA_ARGS__ __VA_OPT__(,) {}},              \
        }

#define VARLINK_DEFINE_ERROR(_name, ...)                                \
        const VarlinkSymbol vl_error_ ## _name = {                      \
                .name = #_name,                                         \
                .symbol_type = VARLINK_ERROR,                           \
                .fields = { __VA_ARGS__ __VA_OPT__(,) {}},              \
        }

#define VARLINK_DEFINE_STRUCT_TYPE(_name, ...)                          \
        const VarlinkSymbol vl_type_ ## _name = {                       \
                .name = #_name,                                         \
                .symbol_type = VARLINK_STRUCT_TYPE,                     \
                .fields = { __VA_ARGS__ __VA_OPT__(,) {}},              \
        }

#define VARLINK_DEFINE_ENUM_TYPE(_name, ...)                            \
        const VarlinkSymbol vl_type_ ## _name = {                       \
                .name = #_name,                                         \
                .symbol_type = VARLINK_ENUM_TYPE,                       \
                .fields = { __VA_ARGS__ __VA_OPT__(,) {}},              \
        }

#define VARLINK_DEFINE_INTERFACE(_name, _full_name, ...)                \
        const VarlinkInterface vl_interface_ ## _name = {               \
                .name = (_full_name),                                   \
                .symbols = { __VA_ARGS__ __VA_OPT__(,) NULL},           \
        }

#define VARLINK_SYMBOL_COMMENT(text)                                    \
        &(const VarlinkSymbol) {                                        \
                .name = "" text,                                        \
                .symbol_type = _VARLINK_SYMBOL_COMMENT,                 \
        }

#define VARLINK_INTERFACE_COMMENT(text)                                 \
        &(const VarlinkSymbol) {                                        \
                .name = "" text,                                        \
                .symbol_type = _VARLINK_INTERFACE_COMMENT,              \
        }

int varlink_idl_dump(FILE *f, int use_colors, size_t cols, const VarlinkInterface *interface);
int varlink_idl_format_full(const VarlinkInterface *interface, size_t cols, char **ret);

static inline int varlink_idl_format(const VarlinkInterface *interface, char **ret) {
        return varlink_idl_format_full(interface, SIZE_MAX, ret);
}

int varlink_idl_parse(const char *text, unsigned *ret_line, unsigned *ret_column, VarlinkInterface **ret);
VarlinkInterface* varlink_interface_free(VarlinkInterface *interface);
DEFINE_TRIVIAL_CLEANUP_FUNC(VarlinkInterface*, varlink_interface_free);

bool varlink_idl_field_name_is_valid(const char *name);
bool varlink_idl_symbol_name_is_valid(const char *name);
bool varlink_idl_interface_name_is_valid(const char *name);

int varlink_idl_qualified_symbol_name_is_valid(const char *name);

int varlink_idl_consistent(const VarlinkInterface *interface, int level);

const VarlinkSymbol* varlink_idl_find_symbol(const VarlinkInterface *interface, VarlinkSymbolType type, const char *name);
const VarlinkField* varlink_idl_find_field(const VarlinkSymbol *symbol, const char *name);

int varlink_idl_validate_method_call(const VarlinkSymbol *method, sd_json_variant *v, const char **bad_field);
int varlink_idl_validate_method_reply(const VarlinkSymbol *method, sd_json_variant *v, const char **bad_field);
int varlink_idl_validate_error(const VarlinkSymbol *error, sd_json_variant *v, const char **bad_field);