Coverage Report

Created: 2026-05-16 07:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/serenity/AK/SourceGenerator.h
Line
Count
Source
1
/*
2
 * Copyright (c) 2020, the SerenityOS developers.
3
 * Copyright (c) 2023, Sam Atkins <atkinssj@serenityos.org>
4
 * Copyright (c) 2024, Tim Ledbetter <timledbetter@gmail.com>
5
 *
6
 * SPDX-License-Identifier: BSD-2-Clause
7
 */
8
9
#pragma once
10
11
#include <AK/ByteString.h>
12
#include <AK/GenericLexer.h>
13
#include <AK/HashMap.h>
14
#include <AK/String.h>
15
#include <AK/StringBuilder.h>
16
17
namespace AK {
18
19
class SourceGenerator {
20
    AK_MAKE_NONCOPYABLE(SourceGenerator);
21
22
public:
23
    using MappingType = HashMap<StringView, String>;
24
25
    explicit SourceGenerator(StringBuilder& builder, char opening = '@', char closing = '@', char escape = '\\')
26
0
        : m_builder(builder)
27
0
        , m_opening(opening)
28
0
        , m_closing(closing)
29
0
        , m_escape(escape)
30
0
    {
31
0
    }
32
    explicit SourceGenerator(StringBuilder& builder, MappingType&& mapping, char opening = '@', char closing = '@', char escape = '\\')
33
        : m_builder(builder)
34
        , m_mapping(move(mapping))
35
        , m_opening(opening)
36
        , m_closing(closing)
37
        , m_escape(escape)
38
0
    {
39
0
    }
40
41
    SourceGenerator(SourceGenerator&&) = default;
42
    // Move-assign is undefinable due to 'StringBuilder& m_builder;'
43
    SourceGenerator& operator=(SourceGenerator&&) = delete;
44
45
    [[nodiscard]] SourceGenerator fork()
46
0
    {
47
0
        return SourceGenerator { m_builder, MUST(m_mapping.clone()), m_opening, m_closing };
48
0
    }
49
50
    void set(StringView key, String value)
51
0
    {
52
0
        if (key.contains(m_opening) || key.contains(m_closing)) {
53
0
            warnln("SourceGenerator keys cannot contain the opening/closing delimiters `{}` and `{}`. (Keys are only wrapped in these when using them, not when setting them.)", m_opening, m_closing);
54
0
            VERIFY_NOT_REACHED();
55
0
        }
56
0
        m_mapping.set(key, move(value));
57
0
    }
58
59
    String get(StringView key) const
60
0
    {
61
0
        auto result = m_mapping.get(key);
62
0
        if (!result.has_value()) {
63
0
            warnln("No key named `{}` set on SourceGenerator", key);
64
0
            VERIFY_NOT_REACHED();
65
0
        }
66
0
        return result.release_value();
67
0
    }
68
69
0
    StringView as_string_view() const { return m_builder.string_view(); }
70
71
    void append(StringView pattern)
72
0
    {
73
0
        GenericLexer lexer { pattern };
74
75
0
        while (!lexer.is_eof()) {
76
0
            m_builder.append(lexer.consume_until([&](char ch) { return ch == m_opening || ch == m_escape; }));
77
0
            if (lexer.consume_specific(m_escape)) {
78
0
                if (!(lexer.next_is(m_opening) || lexer.next_is(m_escape))) {
79
0
                    if (lexer.is_eof())
80
0
                        warnln("Unexpected EOF while parsing escape sequence on SourceGenerator");
81
0
                    else
82
0
                        warnln("Invalid escape sequence found `{}{}` on SourceGenerator", m_escape, lexer.peek());
83
84
0
                    VERIFY_NOT_REACHED();
85
0
                }
86
87
0
                m_builder.append(lexer.consume());
88
0
                continue;
89
0
            }
90
91
0
            if (lexer.consume_specific(m_opening)) {
92
0
                auto const placeholder = lexer.consume_until(m_closing);
93
94
0
                if (!lexer.consume_specific(m_closing))
95
0
                    VERIFY_NOT_REACHED();
96
97
0
                m_builder.append(get(placeholder));
98
0
            } else {
99
0
                VERIFY(lexer.is_eof());
100
0
            }
101
0
        }
102
0
    }
103
104
    void appendln(StringView pattern)
105
0
    {
106
0
        append(pattern);
107
0
        m_builder.append('\n');
108
0
    }
109
110
    template<size_t N>
111
    String get(char const (&key)[N])
112
    {
113
        return get(StringView { key, N - 1 });
114
    }
115
116
    template<size_t N>
117
    void set(char const (&key)[N], String value)
118
0
    {
119
0
        set(StringView { key, N - 1 }, value);
120
0
    }
Unexecuted instantiation: void AK::SourceGenerator::set<11ul>(char const (&) [11ul], AK::String)
Unexecuted instantiation: void AK::SourceGenerator::set<13ul>(char const (&) [13ul], AK::String)
Unexecuted instantiation: void AK::SourceGenerator::set<16ul>(char const (&) [16ul], AK::String)
121
122
    template<size_t N>
123
    void append(char const (&pattern)[N])
124
    {
125
        append(StringView { pattern, N - 1 });
126
    }
127
128
    template<size_t N>
129
    void appendln(char const (&pattern)[N])
130
    {
131
        appendln(StringView { pattern, N - 1 });
132
    }
133
134
    // FIXME: These are deprecated.
135
    void set(StringView key, ByteString value)
136
0
    {
137
0
        set(key, MUST(String::from_byte_string(value)));
138
0
    }
139
    template<size_t N>
140
    void set(char const (&key)[N], ByteString value)
141
0
    {
142
0
        set(StringView { key, N - 1 }, value);
143
0
    }
Unexecuted instantiation: void AK::SourceGenerator::set<11ul>(char const (&) [11ul], AK::ByteString)
Unexecuted instantiation: void AK::SourceGenerator::set<14ul>(char const (&) [14ul], AK::ByteString)
Unexecuted instantiation: void AK::SourceGenerator::set<5ul>(char const (&) [5ul], AK::ByteString)
Unexecuted instantiation: void AK::SourceGenerator::set<9ul>(char const (&) [9ul], AK::ByteString)
Unexecuted instantiation: void AK::SourceGenerator::set<13ul>(char const (&) [13ul], AK::ByteString)
Unexecuted instantiation: void AK::SourceGenerator::set<16ul>(char const (&) [16ul], AK::ByteString)
Unexecuted instantiation: void AK::SourceGenerator::set<10ul>(char const (&) [10ul], AK::ByteString)
Unexecuted instantiation: void AK::SourceGenerator::set<8ul>(char const (&) [8ul], AK::ByteString)
144
145
private:
146
    StringBuilder& m_builder;
147
    MappingType m_mapping;
148
    char m_opening { '@' };
149
    char m_closing { '@' };
150
    char m_escape { '\\' };
151
};
152
153
}
154
155
#if USING_AK_GLOBALLY
156
using AK::SourceGenerator;
157
#endif