• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

biojppm / rapidyaml / 17189660738

24 Aug 2025 02:01PM UTC coverage: 97.57%. Remained the same
17189660738

push

github

biojppm
ci: deprecation of windows-2019 runner image

11564 of 11852 relevant lines covered (97.57%)

764655.77 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

80.0
/tools/parse_emit.cpp
1
#ifdef RYML_SINGLE_HEADER
2
#include <ryml_all.hpp>
3
#else
4
#include <c4/yml/std/std.hpp>
5
#include <c4/yml/detail/print.hpp>
6
#include <c4/yml/parse.hpp>
7
#include <c4/yml/emit.hpp>
8
#include <c4/yml/common.hpp>
9
#endif
10
#include <c4/fs/fs.hpp>
11

12
#include <cstdio>
13
#include <chrono>
14

15
#ifdef C4_EXCEPTIONS
16
#include <stdexcept>
17
#else
18
#include <csetjmp>
19
std::jmp_buf jmp_env = {};
20
c4::csubstr jmp_msg = {};
21
#endif
22

23

24
C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wold-style-cast")
25
C4_SUPPRESS_WARNING_GCC("-Wuseless-cast")
26
C4_SUPPRESS_WARNING_MSVC_WITH_PUSH(4996) // This function or variable may be unsafe
27

28

29
//-----------------------------------------------------------------------------
30

31
using namespace c4;
32

33
// LCOV_EXCL_START
34

35
struct Args
36
{
37
    c4::csubstr filename = "-";
38
    c4::csubstr output = {};
39
    c4::yml::id_type reserve_size = false;
40
    bool resolve_refs = false;
41
    bool keep_refs = false;
42
    bool print_tree = false;
43
    bool quiet = false;
44
    bool emit_as_json = false;
45
    bool emit_to_string = false;
46
    bool timed_sections = false;
47
};
48
void print_usage(const char *exename)
49
{
50
    const Args defs = {};
51
    fprintf(stderr, R"(usage:
52

53
   %s <options> <path/to/file.yaml>
54

55
Parse yaml from file (or stdin when file is `-`) and emit to stdout.
56

57
Options:
58

59
  -h,--help              print this message
60
  -e [N],--reserve [N]   reserve tree size before parsing (default: N=%d):
61
                         0=do not reserve
62
                         1=reserve by estimating size
63
                         all other values=reserve with value
64
  -r,--resolve           resolve references (default: %s)
65
  -k,--keep-refs         keep refs and anchors after resolving (default: %s)
66
  -p,--print-tree        print parsed rapidyaml tree before emitting (default: %s)
67
  -q,--quiet             do not emit (default: %s)
68
  -j,--json              emit json instead of yaml (default: %s)
69
  -s,--string            emit to string before dumping to stdout/file.
70
                         otherwise, emit directly to stdout (default: %s)
71
  -t,--timed             time sections (print timings to stderr) (default: %s)
72
  -o,--output <filename> emit to the given filename (default: %s)
73

74
)",
75
            exename,
76
            (int)defs.reserve_size,
77
            defs.resolve_refs ? "resolve refs" : "do not resolve refs",
78
            defs.keep_refs ? "keep refs" : "remove refs",
79
            defs.print_tree ? "print tree" : "do not print tree",
80
            defs.quiet ? "do not emit" : "emit",
81
            defs.emit_as_json ? "emit as json" : "emit as yaml",
82
            defs.emit_to_string ? "emit to string" : "no",
83
            defs.timed_sections ? "show timings" : "no",
84
            defs.output.empty() ? "no" : defs.output.str
85
        );
86
}
87
bool timing_enabled = false;
88
bool parse_args(int argc, const char *argv[], Args &args)
89
{
90
    args = {};
91
    for(int i = 1; i < argc; ++i)
92
    {
93
        c4::csubstr arg = c4::to_csubstr(argv[i]);
94
        auto arg0_is = [&](c4::csubstr argshort, c4::csubstr arglong){
95
            return (arg == argshort) || (arg == arglong);
96
        };
97
        auto arg1_is = [&](c4::csubstr argshort, c4::csubstr arglong){
98
            if(arg0_is(argshort, arglong))
99
            {
100
                if(i + 1 >= argc)
101
                    c4::yml::error("missing argument value");
102
                return true;
103
            }
104
            return false;
105
        };
106
        if /**/(arg1_is("-e", "--reserve")) C4_CHECK(c4::from_chars(c4::to_csubstr(argv[++i]), &args.reserve_size));
107
        else if(arg1_is("-r", "--resolve")) args.resolve_refs = true;
108
        else if(arg1_is("-o", "--output")) args.output = c4::to_csubstr(argv[++i]);
109
        else if(arg0_is("-k", "--keep-refs")) args.keep_refs = true;
110
        else if(arg0_is("-p", "--print-tree")) args.print_tree = true;
111
        else if(arg0_is("-q", "--quiet")) args.quiet = true;
112
        else if(arg0_is("-j", "--json")) args.emit_as_json = true;
113
        else if(arg0_is("-s", "--string")) args.emit_to_string = true;
114
        else if(arg0_is("-t", "--timed")) args.timed_sections = true;
115
        else if(arg0_is("-h", "--help"))
116
        {
117
            print_usage(argv[0]);
118
            return false;
119
        }
120
        else if(i+1 < argc)
121
        {
122
            print_usage(argv[0]);
123
            c4::yml::error("unknown argument");
124
        }
125
    }
126
    timing_enabled = args.timed_sections;
127
    if(argc < 2)
128
    {
129
        print_usage(argv[0]);
130
        c4::yml::error("missing filename (use - to read from stdin)");
131
    }
132
    args.filename = c4::to_csubstr(argv[argc - 1]);
133
    return true;
134
}
135

136
void read_file(csubstr filename, std::string *buf)
137
{
138
    buf->clear();
139
    if(filename == "-") // read from stdin
140
    {
141
        for(int c = std::getchar(); c != EOF; c = std::getchar())
142
        {
143
            buf->push_back((char)c);
144
            if(buf->size() == buf->capacity())
145
                buf->reserve(2u * (buf->capacity() >= 128u ? buf->capacity() : 128u));
146
        }
147
    }
148
    else
149
    {
150
        if(!fs::path_exists(filename.str))
151
        {
152
            std::fprintf(stderr, "cannot find file: %s (cwd=%s)\n", filename.str, fs::cwd<std::string>().c_str()); // NOLINT
153
            yml::error("file not found");
154
        }
155
        fs::file_get_contents<std::string>(filename.str, buf);
156
    }
157
}
158

159
void emit_json_docs(yml::Tree const& tree, std::string *dst=nullptr)
160
{
161
    auto emitnode = [&](yml::ConstNodeRef node){
162
        if(dst)
163
        {
164
            emitrs_json(node, dst, /*append*/true);
165
            *dst += '\n';
166
        }
167
        else
168
        {
169
            emit_json(node, stdout);
170
            printf("\n");
171
        }
172
    };
173
    yml::ConstNodeRef root = tree.rootref();
174
    if(!root.is_stream())
175
        emitnode(root);
176
    else
177
        for(yml::ConstNodeRef doc : root.children())
178
            emitnode(doc);
179
}
180

181
void emit_json_docs(yml::Tree const& tree, FILE *output)
182
{
183
    auto emitnode = [&](yml::ConstNodeRef node){
184
        emit_json(node, output);
185
        (void)fputc('\n', output);
186
    };
187
    yml::ConstNodeRef root = tree.rootref();
188
    if(!root.is_stream())
189
        emitnode(root);
190
    else
191
        for(yml::ConstNodeRef doc : root.children())
192
            emitnode(doc);
193
}
194

195
void report_error(const char* msg, size_t length, yml::Location loc, FILE *f)
196
{
197
    if(!loc.name.empty())
198
    {
199
        fwrite(loc.name.str, 1, loc.name.len, f); // NOLINT
200
        fputc(':', f); // NOLINT
201
    }
202
    fprintf(f, "%zu:", loc.line); // NOLINT
203
    if(loc.col)
204
        fprintf(f, "%zu:", loc.col); // NOLINT
205
    if(loc.offset)
206
        fprintf(f, " (%zuB):", loc.offset); // NOLINT
207
    fputc(' ', f); // NOLINT
208
    fprintf(f, "%.*s\n", static_cast<int>(length), msg); // NOLINT
209
    fflush(f); // NOLINT
210
}
211

212
yml::Callbacks create_custom_callbacks()
213
{
214
    yml::Callbacks callbacks = {};
215
    callbacks.m_error = [](const char *msg, size_t msg_len, yml::Location location, void *)
216
    {
217
        report_error(msg, msg_len, location, stderr);
218
        C4_IF_EXCEPTIONS(
219
            throw std::runtime_error({msg, msg_len});
220
            ,
221
            jmp_msg.assign(msg, msg_len);
222
            std::longjmp(jmp_env, 1);
223
        );
224
    };
225
    return callbacks;
226
}
227

228
#define TS(name) timed_section C4_XCAT(__, C4_XCAT(name, __LINE__))(#name)
229
struct timed_section
230
{
231
    using myclock = std::chrono::steady_clock;
232
    using msecs = std::chrono::duration<double, std::milli>;
233

234
    csubstr name;
235
    myclock::time_point start;
236

237
    msecs since() const { return myclock::now() - start; }
238
    timed_section(csubstr n)
239
        : name(n)
240
        , start(timing_enabled ? myclock::now() : myclock::time_point{})
241
    {}
242
    ~timed_section()
243
    {
244
        if(timing_enabled)
245
        {
246
            fprintf(stderr, "%.6fms: %.*s\n", since().count(), (int)name.len, name.str); // NOLINT
247
            fflush(stderr); // NOLINT
248
        }
249
    }
250
};
251

252

253
//-----------------------------------------------------------------------------
254
//-----------------------------------------------------------------------------
255
//-----------------------------------------------------------------------------
256

257
void process_file(Args const& args)
258
{
259
    TS(objects);
260
    std::string contents;
261
    yml::Tree tree(yml::get_callbacks());
262
    {
263
        TS(read_file);
264
        read_file(args.filename, &contents);
265
    }
266
    if(args.reserve_size)
267
    {
268
        TS(tree_reserve);
269
        yml::id_type cap = args.reserve_size;
270
        if(args.reserve_size)
271
        {
272
            TS(estimate_capacity);
273
            cap = yml::estimate_tree_capacity(to_csubstr(contents));
274
        }
275
        if(timing_enabled)
276
            fprintf(stderr, "reserving capacity=%zu\n", (size_t)cap); // NOLINT
277
        tree.reserve(cap);
278
    }
279
    {
280
        TS(parse_yml);
281
        yml::parse_in_place(args.filename, to_substr(contents), &tree);
282
    }
283
    if(args.print_tree)
284
    {
285
        print_tree(args.filename.str, tree); // safe because we are getting from argv which is zero-terminated
286
    }
287
    if(args.resolve_refs || args.emit_as_json)
288
    {
289
        TS(resolve_refs);
290
        tree.resolve();
291
        if(args.print_tree)
292
        {
293
            print_tree("resolved tree", tree);
294
        }
295
    }
296
    if(args.emit_to_string)
297
    {
298
        std::string output;
299
        {
300
            TS(emit_to_buffer);
301
            output.resize(contents.size()); // resize, not just reserve
302
            if(!args.emit_as_json)
303
                yml::emitrs_yaml(tree, &output);
304
            else
305
                emit_json_docs(tree, &output);
306
        }
307
        if(!args.quiet)
308
        {
309
            TS(print_stdout);
310
            fwrite(output.data(), 1, output.size(), stdout); // NOLINT
311
        }
312
    }
313
    else if(!args.quiet)
314
    {
315
        if(args.output.empty())
316
        {
317
            TS(emit_to_stdout);
318
            if(!args.emit_as_json)
319
                yml::emit_yaml(tree);
320
            else
321
                emit_json_docs(tree);
322
        }
323
        else
324
        {
325
            FILE *output = fopen(args.output.str, "wb");
326
            if (!output) c4::yml::error("could not open file");
327
            {
328
                TS(emit_to_file);
329
                if(!args.emit_as_json)
330
                    yml::emit_yaml(tree, output);
331
                else
332
                    emit_json_docs(tree, output);
333
            }
334
            (void)fclose(output);
335
        }
336
    }
337
}
338
// LCOV_EXCL_STOP
339

340
int main(int argc, const char *argv[])
8✔
341
{
342
    Args args;
8✔
343
    if(!parse_args(argc, argv, args))
8✔
344
       return 0;
×
345
    TS(TOTAL);
8✔
346
    set_callbacks(create_custom_callbacks());
8✔
347
    C4_IF_EXCEPTIONS_(try, if(setjmp(jmp_env) == 0))
348
    {
349
        process_file(args);
8✔
350
    }
351
    C4_IF_EXCEPTIONS_(catch(std::exception const&), else) // LCOV_EXCL_LINE
352
    {
353
        return 1; // LCOV_EXCL_LINE
354
    }
×
355
    return 0;
8✔
356
}
8✔
357

358
C4_SUPPRESS_WARNING_GCC_CLANG_POP
359
C4_SUPPRESS_WARNING_MSVC_POP
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc