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

xlnt-community / xlnt / aa719a12-330c-47b2-9460-5b2d61336ff6

03 Feb 2025 05:39PM UTC coverage: 81.978% (-1.5%) from 83.482%
aa719a12-330c-47b2-9460-5b2d61336ff6

push

circleci

web-flow
Use C++23 for generating coverage report. (#57)

Using C++23 for coverage reporting, prepares the library for coverage
testing of future post-C++11 features.

This PR makes as few changes as possible to ensure all coverage changes
are due to the changed C++/compiler version used for generating the
coverage report.

Temporarily disable warnings as errors for coverage reporting until
better C++20/23 support is added in PR #55.

11395 of 13900 relevant lines covered (81.98%)

1202684.1 hits per line

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

85.06
./source/packaging/manifest.cpp
1
// Copyright (c) 2014-2022 Thomas Fussell
2
// Copyright (c) 2010-2015 openpyxl
3
// Copyright (c) 2024-2025 xlnt-community
4
//
5
// Permission is hereby granted, free of charge, to any person obtaining a copy
6
// of this software and associated documentation files (the "Software"), to deal
7
// in the Software without restriction, including without limitation the rights
8
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
// copies of the Software, and to permit persons to whom the Software is
10
// furnished to do so, subject to the following conditions:
11
//
12
// The above copyright notice and this permission notice shall be included in
13
// all copies or substantial portions of the Software.
14
//
15
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
// THE SOFTWARE
22
//
23
// @license: http://www.opensource.org/licenses/mit-license.php
24
// @author: see AUTHORS file
25

26
#include <algorithm>
27
#include <unordered_set>
28

29
#include <xlnt/packaging/manifest.hpp>
30
#include <xlnt/utils/exceptions.hpp>
31
#include <detail/serialization/parsers.hpp>
32

33
namespace xlnt {
34

35
void manifest::clear()
×
36
{
37
    default_content_types_.clear();
×
38
    override_content_types_.clear();
×
39
    relationships_.clear();
×
40
}
×
41

42
path manifest::canonicalize(const std::vector<xlnt::relationship> &rels) const
42,955✔
43
{
44
    xlnt::path relative;
42,955✔
45

46
    for (auto component : rels)
86,611✔
47
    {
48
        if (component == rels.back())
43,656✔
49
        {
50
            relative = relative.append(component.target().path());
42,955✔
51
        }
52
        else
53
        {
54
            relative = relative.append(component.target().path().parent());
701✔
55
        }
56
    }
43,656✔
57

58
    std::vector<std::string> absolute_parts;
42,955✔
59

60
    for (const auto &component : relative.split())
129,374✔
61
    {
62
        if (component == ".") continue;
86,419✔
63

64
        if (component == "..")
86,419✔
65
        {
66
            absolute_parts.pop_back();
42✔
67
            continue;
42✔
68
        }
69

70
        absolute_parts.push_back(component);
86,377✔
71
    }
42,955✔
72

73
    xlnt::path result;
42,955✔
74

75
    for (const auto &component : absolute_parts)
129,290✔
76
    {
77
        result = result.append(component);
86,335✔
78
    }
79

80
    return result;
42,955✔
81
}
42,955✔
82

83
bool manifest::has_relationship(const path &path, relationship_type type) const
49,718✔
84
{
85
    auto rels = relationships_.find(path);
49,718✔
86
    if (rels == relationships_.end())
49,718✔
87
    {
88
        return false;
862✔
89
    }
90
    return rels->second.end() != std::find_if(rels->second.begin(), rels->second.end(), [type](const std::pair<std::string, xlnt::relationship> &rel) { return rel.second.type() == type; });
113,512✔
91
}
92

93
bool manifest::has_relationship(const path &path, const std::string &rel_id) const
×
94
{
95
    auto rels = relationships_.find(path);
×
96
    if (rels == relationships_.end())
×
97
    {
98
        return false;
×
99
    }
100
    return rels->second.find(rel_id) != rels->second.end();
×
101
}
102

103
relationship manifest::relationship(const path &part, relationship_type type) const
43,644✔
104
{
105
    if (relationships_.find(part) == relationships_.end()) throw key_not_found();
43,644✔
106

107
    for (const auto &rel : relationships_.at(part))
171,820✔
108
    {
109
        if (rel.second.type() == type) return rel.second;
171,820✔
110
    }
111

112
    throw key_not_found();
×
113
}
114

115
std::vector<xlnt::relationship> manifest::relationships(const path &part, relationship_type type) const
302✔
116
{
117
    std::vector<xlnt::relationship> matches;
302✔
118

119
    if (has_relationship(part, type))
302✔
120
    {
121
        for (const auto &rel : relationships_.at(part))
840✔
122
        {
123
            if (rel.second.type() == type)
673✔
124
            {
125
                matches.push_back(rel.second);
267✔
126
            }
127
        }
128
    }
129

130
    return matches;
302✔
131
}
×
132

133
std::string manifest::content_type(const path &part) const
345✔
134
{
135
    auto absolute = part.resolve(path("/"));
345✔
136

137
    if (has_override_type(absolute))
345✔
138
    {
139
        return override_type(absolute);
265✔
140
    }
141

142
    if (has_default_type(part.extension()))
80✔
143
    {
144
        return default_type(part.extension());
80✔
145
    }
146

147
    throw key_not_found();
×
148
}
345✔
149

150
void manifest::register_override_type(const path &part, const std::string &content_type)
2,489✔
151
{
152
    override_content_types_[part] = content_type;
2,489✔
153
}
2,489✔
154

155
void manifest::unregister_override_type(const path &part)
10✔
156
{
157
    override_content_types_.erase(part);
10✔
158
}
10✔
159

160
std::vector<path> manifest::parts_with_overriden_types() const
87✔
161
{
162
    std::vector<path> overriden;
87✔
163

164
    for (const auto &part : override_content_types_)
714✔
165
    {
166
        overriden.push_back(part.first);
627✔
167
    }
168

169
    return overriden;
87✔
170
}
×
171

172
std::vector<relationship> manifest::relationships(const path &part) const
1,089✔
173
{
174
    if (relationships_.find(part) == relationships_.end())
1,089✔
175
    {
176
        return {};
627✔
177
    }
178

179
    std::vector<xlnt::relationship> relationships;
462✔
180

181
    for (const auto &rel : relationships_.at(part))
2,145✔
182
    {
183
        relationships.push_back(rel.second);
1,683✔
184
    }
185

186
    return relationships;
462✔
187
}
462✔
188

189
relationship manifest::relationship(const path &part, const std::string &rel_id) const
377✔
190
{
191
    if (relationships_.find(part) == relationships_.end())
377✔
192
    {
193
        throw key_not_found();
×
194
    }
195

196
    for (const auto &rel : relationships_.at(part))
1,424✔
197
    {
198
        if (rel.second.id() == rel_id)
1,424✔
199
        {
200
            return rel.second;
377✔
201
        }
202
    }
203

204
    throw key_not_found();
×
205
}
206

207
std::vector<path> manifest::parts() const
22✔
208
{
209
    std::unordered_set<path> parts;
22✔
210

211
    for (const auto &part_rels : relationships_)
74✔
212
    {
213
        parts.insert(part_rels.first);
52✔
214

215
        for (const auto &rel : part_rels.second)
254✔
216
        {
217
            if (rel.second.target_mode() == target_mode::internal)
202✔
218
            {
219
                parts.insert(rel.second.target().path());
186✔
220
            }
221
        }
222
    }
223

224
    return std::vector<path>(parts.begin(), parts.end());
44✔
225
}
22✔
226

227
std::string manifest::register_relationship(const uri &source,
1,965✔
228
    relationship_type type, const uri &target, target_mode mode)
229
{
230
    xlnt::relationship rel(next_relationship_id(source.path()), type, source, target, mode);
1,965✔
231
    return register_relationship(rel);
3,930✔
232
}
1,965✔
233

234
std::string manifest::register_relationship(const class relationship &rel)
3,401✔
235
{
236
    relationships_[rel.source().path()][rel.id()] = rel;
3,401✔
237
    return rel.id();
3,401✔
238
}
239

240
std::unordered_map<std::string, std::string> manifest::unregister_relationship(const uri &source, const std::string &rel_id)
249✔
241
{
242
    // This shouldn't happen, but just in case...
243
    if (rel_id.substr(0, 3) != "rId" || rel_id.size() < 4)
249✔
244
    {
245
        throw xlnt::invalid_parameter();
×
246
    }
247

248
    std::unordered_map<std::string, std::string> id_map;
249✔
249
    size_t rel_index = 0;
249✔
250
    detail::parse(rel_id.substr(3), rel_index);
249✔
251
    auto &part_rels = relationships_.at(source.path());
249✔
252

253
    for (auto i = rel_index; i <= part_rels.size() + 1; ++i)
724✔
254
    {
255
        auto old_id = "rId" + std::to_string(i);
475✔
256

257
        // Don't re-add the relationship to be deleted
258
        if (i > rel_index)
475✔
259
        {
260
            // Shift all relationships with IDs greater than the deleted one
261
            // down by one (e.g. rId7->rId6).
262
            auto new_id = "rId" + std::to_string(i - 1);
295✔
263
            const auto &old_rel = part_rels.at(old_id);
295✔
264
            register_relationship(xlnt::relationship(new_id, old_rel.type(),
295✔
265
                old_rel.source(), old_rel.target(), old_rel.target_mode()));
295✔
266
            id_map[old_id] = new_id;
295✔
267
        }
295✔
268

269
        part_rels.erase(old_id);
475✔
270
    }
475✔
271

272
    return id_map;
249✔
273
}
×
274

275
bool manifest::has_default_type(const std::string &extension) const
81✔
276
{
277
    return default_content_types_.find(extension) != default_content_types_.end();
81✔
278
}
279

280
std::vector<std::string> manifest::extensions_with_default_types() const
87✔
281
{
282
    std::vector<std::string> extensions;
87✔
283

284
    for (const auto &extension_type_pair : default_content_types_)
335✔
285
    {
286
        extensions.push_back(extension_type_pair.first);
248✔
287
    }
288

289
    return extensions;
87✔
290
}
×
291

292
std::string manifest::default_type(const std::string &extension) const
329✔
293
{
294
    if (default_content_types_.find(extension) == default_content_types_.end())
329✔
295
    {
296
        throw key_not_found();
1✔
297
    }
298

299
    return default_content_types_.at(extension);
328✔
300
}
301

302
void manifest::register_default_type(const std::string &extension, const std::string &content_type)
1,087✔
303
{
304
    default_content_types_[extension] = content_type;
1,087✔
305
}
1,087✔
306

307
void manifest::unregister_default_type(const std::string &extension)
×
308
{
309
    default_content_types_.erase(extension);
×
310
}
×
311

312
std::string manifest::next_relationship_id(const path &part) const
1,965✔
313
{
314
    if (relationships_.find(part) == relationships_.end()) return "rId1";
3,025✔
315

316
    std::size_t index = 1;
1,435✔
317
    const auto &part_rels = relationships_.at(part);
1,435✔
318

319
    while (part_rels.find("rId" + std::to_string(index)) != part_rels.end())
4,172✔
320
    {
321
        ++index;
2,737✔
322
    }
323

324
    return "rId" + std::to_string(index);
1,435✔
325
}
326

327
bool manifest::has_override_type(const xlnt::path &part) const
1,241✔
328
{
329
    return override_content_types_.find(part) != override_content_types_.end();
1,241✔
330
}
331

332
std::string manifest::override_type(const xlnt::path &part) const
892✔
333
{
334
    if (!has_override_type(part))
892✔
335
    {
336
        throw key_not_found();
×
337
    }
338

339
    return override_content_types_.at(part);
892✔
340
}
341

342
bool manifest::operator==(const manifest &other) const
9✔
343
{
344
    return default_content_types_ == other.default_content_types_
9✔
345
        && override_content_types_ == other.override_content_types_
9✔
346
        && relationships_ == other.relationships_;
18✔
347
}
348

349
} // namespace xlnt
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