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

biojppm / rapidyaml / 14049812793

25 Mar 2025 01:47AM UTC coverage: 97.244% (-0.3%) from 97.524%
14049812793

Pull #506

github

web-flow
Merge df77dc42d into d3132a25e
Pull Request #506: Fix merge key order, clear anchors after resolving

67 of 105 new or added lines in 3 files covered. (63.81%)

34 existing lines in 4 files now uncovered.

11573 of 11901 relevant lines covered (97.24%)

761481.38 hits per line

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

29.82
/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

27
struct Args
28
{
29
    c4::csubstr filename = "-";
30
    c4::yml::id_type reserve_size = false;
31
    bool resolve_refs = false;
32
    bool keep_refs = false;
33
    bool print_tree = false;
34
    bool quiet = false;
35
    bool emit_as_json = false;
36
    bool emit_to_string = false;
37
    bool timed_sections = false;
38
};
NEW
39
void print_usage(const char *exename)
×
40
{
41
    const Args defs = {};
NEW
42
    fprintf(stderr, R"(usage:
×
43

44
   %s <options> <path/to/file.yaml>
45

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

48
Options:
49

50
  -e [N],--reserve [N]   reserve tree size before parsing (default: N=%d):
51
                         0=do not reserve
52
                         1=reserve by estimating size
53
                         all other values=reserve with value
54
  -r,--resolve           resolve references (default: %s)
55
  -k,--keep-refs         keep refs and anchors after resolving (default: %s)
56
  -p,--print-tree        print parsed rapidyaml tree before emitting (default: %s)
57
  -q,--quiet             do not emit (default: %s)
58
  -j,--json              emit json instead of yaml (default: %s)
59
  -s,--string            emit to string before dumping to stdout.
60
                         otherwise, emit directly to stdout (default: %s)
61
  -t,--timed             time sections (print timings to stderr) (default: %s)
62

63
)",
64
            exename,
NEW
65
            (int)defs.reserve_size,
×
NEW
66
            defs.resolve_refs ? "resolve refs" : "do not resolve refs",
×
NEW
67
            defs.keep_refs ? "keep refs" : "remove refs",
×
NEW
68
            defs.print_tree ? "print tree" : "do not print tree",
×
NEW
UNCOV
69
            defs.quiet ? "do not emit" : "emit",
×
NEW
70
            defs.emit_as_json ? "emit as json" : "emit as yaml",
×
NEW
71
            defs.emit_to_string ? "emit to string" : "no",
×
NEW
72
            defs.timed_sections ? "show timings" : "no"
×
73
        );
NEW
74
}
×
75
Args parse_args(int argc, const char *argv[])
8✔
76
{
77
    if(argc < 2)
8✔
78
    {
NEW
79
        print_usage(argv[0]);
×
NEW
80
        c4::yml::error("missing filename (use - to read from stdin)");
×
81
    }
82
    Args args = {};
8✔
83
    args.filename = c4::to_csubstr(argv[argc - 1]);
8✔
84
    for(int i = 1; i+1 < argc; ++i)
8✔
85
    {
NEW
UNCOV
86
        c4::csubstr arg = c4::to_csubstr(argv[i]);
×
NEW
87
        auto arg0_is = [&](c4::csubstr argshort, c4::csubstr arglong){
×
NEW
88
            return (arg == argshort) || (arg == arglong);
×
NEW
UNCOV
89
        };
×
NEW
90
        auto arg1_is = [&](c4::csubstr argshort, c4::csubstr arglong){
×
NEW
91
            if(arg0_is(argshort, arglong))
×
92
            {
NEW
UNCOV
93
                if(i + 1 >= argc)
×
NEW
94
                    c4::yml::error("missing argument value");
×
NEW
95
                return true;
×
96
            }
NEW
97
            return false;
×
NEW
98
        };
×
NEW
99
        if /**/(arg1_is("-e", "--reserve")) C4_CHECK(c4::from_chars(c4::to_csubstr(argv[++i]), &args.reserve_size));
×
NEW
100
        else if(arg1_is("-r", "--resolve")) args.resolve_refs = true;
×
NEW
101
        else if(arg0_is("-k", "--keep-refs")) args.keep_refs = true;
×
NEW
102
        else if(arg0_is("-p", "--print-tree")) args.print_tree = true;
×
NEW
103
        else if(arg0_is("-q", "--quiet")) args.quiet = true;
×
NEW
104
        else if(arg0_is("-j", "--json")) args.emit_as_json = true;
×
NEW
105
        else if(arg0_is("-s", "--string")) args.emit_to_string = true;
×
NEW
106
        else if(arg0_is("-t", "--timed")) args.timed_sections = true;
×
107
        else
108
        {
NEW
109
            print_usage(argv[0]);
×
NEW
110
            c4::yml::error("unknown argument");
×
111
        }
112
    }
113
    return args;
8✔
114
}
115

116

117

118
//-----------------------------------------------------------------------------
119

120
using namespace c4;
121

122
#define TS(name) timed_section C4_XCAT(__, C4_XCAT(name, __LINE__))(#name)
123
bool timing_enabled = false;
124
struct timed_section
125
{
126
    using myclock = std::chrono::steady_clock;
127
    using msecs = std::chrono::duration<double, std::milli>;
128

129
    csubstr name;
130
    myclock::time_point start;
131

UNCOV
132
    msecs since() const { return myclock::now() - start; }
×
133
    timed_section(csubstr n) : name(n), start(timing_enabled ? myclock::now() : myclock::time_point{}) {}
40✔
134
    ~timed_section()
40✔
135
    {
136
        if(timing_enabled)
40✔
137
        {
NEW
UNCOV
138
            fprintf(stderr, "%.6fms: %.*s\n", since().count(), (int)name.len, name.str); // NOLINT
×
NEW
UNCOV
139
            fflush(stderr); // NOLINT
×
140
        }
141
    }
40✔
142
};
143

144

145
// LCOV_EXCL_START
146

147
void read_file(csubstr filename, std::string *buf)
148
{
149
    buf->clear();
150
    if(filename == "-") // read from stdin
151
    {
152
        for(int c = std::getchar(); c != EOF; c = std::getchar())
153
        {
154
            buf->push_back((char)c);
155
            if(buf->size() == buf->capacity())
156
                buf->reserve(2u * (buf->capacity() >= 128u ? buf->capacity() : 128u));
157
        }
158
    }
159
    else
160
    {
161
        if(!fs::path_exists(filename.str))
162
        {
163
            std::fprintf(stderr, "cannot find file: %s (cwd=%s)\n", filename.str, fs::cwd<std::string>().c_str()); // NOLINT
164
            yml::error("file not found");
165
        }
166
        fs::file_get_contents<std::string>(filename.str, buf);
167
    }
168
}
169

170
void emit_json_docs(yml::Tree const& tree, std::string *dst=nullptr)
171
{
172
    auto emitnode = [&](yml::ConstNodeRef node){
173
        if(dst)
174
        {
175
            emitrs_json(node, dst, /*append*/true);
176
            *dst += '\n';
177
        }
178
        else
179
        {
180
            emit_json(node, stdout);
181
        }
182
    };
183
    yml::ConstNodeRef root = tree.rootref();
184
    if(!root.is_stream())
185
        emitnode(root);
186
    else
187
        for(yml::ConstNodeRef doc : root.children())
188
            emitnode(doc);
189
}
190

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

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

224

225
//-----------------------------------------------------------------------------
226
//-----------------------------------------------------------------------------
227
//-----------------------------------------------------------------------------
228

229
void process_file(Args const& args)
230
{
231
    TS(objects);
232
    std::string contents;
233
    yml::Tree tree(yml::get_callbacks());
234
    {
235
        TS(read_file);
236
        read_file(args.filename, &contents);
237
    }
238
    if(args.reserve_size)
239
    {
240
        TS(tree_reserve);
241
        yml::id_type cap = args.reserve_size;
242
        if(args.reserve_size)
243
        {
244
            TS(estimate_capacity);
245
            cap = yml::estimate_tree_capacity(to_csubstr(contents));
246
        }
247
        if(timing_enabled)
248
            fprintf(stderr, "reserving capacity=%zu\n", (size_t)cap); // NOLINT
249
        tree.reserve(cap);
250
    }
251
    {
252
        TS(parse_yml);
253
        yml::parse_in_place(args.filename, to_substr(contents), &tree);
254
    }
255
    if(args.print_tree)
256
    {
257
        print_tree(args.filename.str, tree); // safe because we are getting from argv which is zero-terminated
258
    }
259
    if(args.emit_as_json || args.resolve_refs)
260
    {
261
        TS(resolve_refs);
262
        tree.resolve(args.keep_refs);
263
    }
264
    if(args.print_tree)
265
    {
266
        print_tree("resolved tree", tree);
267
    }
268
    if(args.emit_to_string)
269
    {
270
        std::string output;
271
        {
272
            TS(emit_to_buffer);
273
            output.resize(contents.size()); // resize, not just reserve
274
            if(!args.emit_as_json)
275
                yml::emitrs_yaml(tree, &output);
276
            else
277
                emit_json_docs(tree, &output);
278
        }
279
        if(!args.quiet)
280
        {
281
            TS(print_stdout);
282
            fwrite(output.data(), 1, output.size(), stdout); // NOLINT
283
        }
284
    }
285
    else if(!args.quiet)
286
    {
287
        TS(emit_to_stdout);
288
        if(!args.emit_as_json)
289
            yml::emit_yaml(tree);
290
        else
291
            emit_json_docs(tree);
292
    }
293
}
294
// LCOV_EXCL_STOP
295

296
int main(int argc, const char *argv[])
8✔
297
{
298
    TS(TOTAL);
8✔
299
    set_callbacks(create_custom_callbacks());
8✔
300
    C4_IF_EXCEPTIONS_(try, if(setjmp(jmp_env) == 0))
301
    {
302
        const Args args = parse_args(argc, argv);
8✔
303
        process_file(args);
8✔
304
    }
UNCOV
305
    C4_IF_EXCEPTIONS_(catch(std::exception const&), else)
×
306
    {
UNCOV
307
        return 1;
×
308
    }
×
309
    return 0;
8✔
310
}
8✔
311

312
C4_SUPPRESS_WARNING_GCC_CLANG_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

© 2026 Coveralls, Inc