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

biojppm / rapidyaml / 18640334796

20 Oct 2025 02:34AM UTC coverage: 97.752% (+0.1%) from 97.65%
18640334796

Pull #550

github

web-flow
Merge 138b7f86b into 48acea949
Pull Request #550: Implement FLOW_ML style

823 of 856 new or added lines in 12 files covered. (96.14%)

160 existing lines in 15 files now uncovered.

13784 of 14101 relevant lines covered (97.75%)

543110.94 hits per line

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

84.62
/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
#include <c4/yml/error.def.hpp>
10
#endif
11
#include <c4/fs/fs.hpp>
12

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

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

24

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

29

30
//-----------------------------------------------------------------------------
31

32
using namespace c4;
33

34
// LCOV_EXCL_START
35

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

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

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

58
Options:
59

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

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

143
void read_file(csubstr filename, std::string *buf)
144
{
145
    buf->clear();
146
    if(filename == "-") // read from stdin
147
    {
148
        for(int c = std::getchar(); c != EOF; c = std::getchar())
149
        {
150
            buf->push_back((char)c);
151
            if(buf->size() == buf->capacity())
152
                buf->reserve(2u * (buf->capacity() >= 128u ? buf->capacity() : 128u));
153
        }
154
    }
155
    else
156
    {
157
        _RYML_CHECK_BASIC_MSG(fs::path_exists(filename.str), "file not found: {} (cwd={})", filename, fs::cwd<std::string>());
158
        fs::file_get_contents<std::string>(filename.str, buf);
159
    }
160
}
161

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

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

198
void dump2stderr(csubstr s)
199
{
200
    if(s.len)
201
    {
202
        fwrite(s.str, 1, s.len, stderr); // NOLINT
203
        fflush(stderr); // NOLINT
204
    }
205
}
206

207
[[noreturn]] void throwerr(csubstr msg)
208
{
209
    C4_IF_EXCEPTIONS(
210
        throw std::runtime_error({msg.str, msg.len});
211
        ,
212
        jmp_msg.assign(msg.str, msg.len);
213
        std::longjmp(jmp_env, 1);
214
        );
215
    C4_UNREACHABLE_AFTER_ERR();
216
}
217

218
yml::Callbacks create_custom_callbacks()
219
{
220
    return yml::Callbacks{}
221
        .set_error_basic([](csubstr msg, yml::ErrorDataBasic const& errdata, void *){
222
            yml::err_basic_format(dump2stderr, msg, errdata);
223
            throwerr(msg);
224
        })
225
        .set_error_parse([](csubstr msg, yml::ErrorDataParse const& errdata, void *){
226
            yml::err_parse_format(dump2stderr, msg, errdata);
227
            throwerr(msg);
228
        })
229
        .set_error_visit([](csubstr msg, yml::ErrorDataVisit const& errdata, void *){
230
            yml::err_visit_format(dump2stderr, msg, errdata);
231
            throwerr(msg);
232
        });
233
}
234

235
#define TS(name) timed_section C4_XCAT(__, C4_XCAT(name, __LINE__))(#name)
236
struct timed_section
237
{
238
    using myclock = std::chrono::steady_clock;
239
    using msecs = std::chrono::duration<double, std::milli>;
240

241
    csubstr name;
242
    myclock::time_point start;
243

244
    msecs since() const { return myclock::now() - start; }
245
    timed_section(csubstr n)
246
        : name(n)
247
        , start(timing_enabled ? myclock::now() : myclock::time_point{})
248
    {}
249
    ~timed_section()
250
    {
251
        if(timing_enabled)
252
        {
253
            fprintf(stderr, "%.6fms: %.*s\n", since().count(), (int)name.len, name.str); // NOLINT
254
            fflush(stderr); // NOLINT
255
        }
256
    }
257
};
258

259

260
//-----------------------------------------------------------------------------
261
//-----------------------------------------------------------------------------
262
//-----------------------------------------------------------------------------
263

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

347
int main(int argc, const char *argv[])
6✔
348
{
349
    Args args;
6✔
350
    if(!parse_args(argc, argv, args))
6✔
UNCOV
351
       return 0;
×
352
    TS(TOTAL);
6✔
353
    set_callbacks(create_custom_callbacks());
6✔
354
    TS(TOTAL);
6✔
355
    std::string yaml;
6✔
356
    C4_IF_EXCEPTIONS_(try, if(setjmp(jmp_env) == 0))
1✔
357
    {
358
        process_file(args, &yaml);
6✔
359
    }
360
    C4_IF_EXCEPTIONS_(catch(std::exception const& exc), else) // LCOV_EXCL_LINE
361
    {
UNCOV
362
        C4_IF_EXCEPTIONS(
×
363
            dump2stderr(to_csubstr(exc.what()));  // LCOV_EXCL_LINE
364
        ,
365
            dump2stderr(to_csubstr(jmp_msg));  // LCOV_EXCL_LINE
366
        );
367
        return 1; // LCOV_EXCL_LINE
368
    }
369
    return 0;
6✔
370
}
6✔
371

372
C4_SUPPRESS_WARNING_GCC_CLANG_POP
373
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