Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .clang-tidy
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Checks: >
modernize-*,
-modernize-use-trailing-return-type,
-modernize-use-integer-sign-comparison,
-modernize-avoid-c-arrays,
portability-*,
readability-*,
-readability-identifier-length,
Expand All @@ -16,13 +17,15 @@ Checks: >
-readability-avoid-return-with-void-value,
-readability-named-parameter,
-readability-magic-numbers,
-clang-analyzer-optin.core.EnumCastOutOfRange


# Turn all the warnings from the checks above into errors.
WarningsAsErrors: "*"

CheckOptions:
- { key: readability-function-cognitive-complexity.IgnoreMacros, value: true }
- { key: clang-analyzer-optin.IgnoreMacros, value: true }
- { key: readability-identifier-naming.ConstantCase, value: UPPER_CASE }
- { key: readability-identifier-naming.LocalConstantCase, value: lower_case }
- { key: readability-identifier-naming.NamespaceCase, value: lower_case }
Expand Down
29 changes: 29 additions & 0 deletions .github/workflows/Codestyle.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: "Codestyle"

on:
push:

jobs:
check-codestyle:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: cachix/install-nix-action@v26
- uses: cachix/cachix-action@v14
with:
name: devenv

- name: devenv.sh install
run: nix profile install nixpkgs#devenv

- name: devenv.sh evaluation
run: devenv shell echo

- name: clang-format checks
run: devenv shell "xmake format -ne"

- name: generate compile-commands
run: devenv shell "xmake project -k compile_commands -y"

- name: clang-tidy checks
run: devenv shell "xmake check clang.tidy"
29 changes: 29 additions & 0 deletions .github/workflows/TestUbuntu.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: "TestUbuntu"

on:
push:

jobs:
ubuntu-build-and-test:
runs-on: ubuntu-latest
strategy:
matrix:
config: [asan, tsan, fast, small]
toolchain: [clang, gcc]
steps:
- uses: actions/checkout@v4
- uses: cachix/install-nix-action@v26
- uses: cachix/cachix-action@v14
with:
name: devenv

- name: devenv.sh install
run: nix profile install nixpkgs#devenv

- name: devenv.sh evaluation
run: devenv shell echo

- run: devenv shell "xmake config --toolchain=${{ matrix.toolchain }} --mode=${{ matrix.config }} --root -y"
- run: devenv shell "xmake build -y"
- run: devenv shell "xmake test -y"
- run: devenv shell "xmake install -y -o installdir"
7 changes: 7 additions & 0 deletions LICENSE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Copyright (c) 2025 Dmitry Malakhov

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
6 changes: 3 additions & 3 deletions include/cppwebdoc/CompString.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ namespace cppwebdoc {

template <size_t N>
struct CompString {
consteval CompString(const char (&s)[N]) { // NOLINT
consteval CompString(const char (&s)[N]) {
std::ranges::copy(s, std::begin(data));
}

consteval CompString(std::string_view s) { // NOLINT
consteval CompString(std::string_view s) {
std::ranges::copy(s, std::begin(data));
}

Expand Down Expand Up @@ -44,7 +44,7 @@ struct CompString {
return std::string_view{s} <=> std::string_view{*this};
}

char data[N]{}; // NOLINT
char data[N]{};
};

namespace detail {
Expand Down
60 changes: 42 additions & 18 deletions include/cppwebdoc/Html5Minimizer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@
#include "cppwebdoc/Html5Tokenizer.hpp"

#include <algorithm>
#include <ranges>

namespace cppwebdoc {

constexpr std::string html5_minimize(std::string_view html) {
std::string result;
namespace detail {

auto match_start_tag = [&](StartTag const &tag) -> void {
struct Html5MinimizeMatcher {
constexpr void operator()(StartTag const &tag) {
result += '<';
result += tag.name;
for (Attribute attr : tag.attributes()) {
Expand All @@ -37,33 +38,56 @@ constexpr std::string html5_minimize(std::string_view html) {
result += '/';
}
result += '>';
};
}

auto match_end_tag = [&](EndTag const &tag) -> void {
constexpr void operator()(EndTag const &tag) {
result += "</";
result += tag.name;
result += '>';
};
}

auto match_special_text = [&](SpeciallyTaggedText const &text) -> void { result += text.value; };
constexpr void operator()(Text const &text) {
std::string_view value = text.value;
trim_html5_spaces(value);
for (auto line_subr : value | std::views::split('\n')) {

auto match_comment = [](Comment const &) -> void {};
std::string_view line = {line_subr.begin(), line_subr.size()};
bool had_newline = *line.end() == '\n';
trim_html5_spaces(line);
result += line;
if (had_newline) {
result += '\n';
}
}
}

auto match_text = [&](Text const &text) -> void {
std::string_view value = text.value;
while (!value.empty() && detail::html5_is_space(value.front())) {
value.remove_prefix(1);
constexpr void operator()(SpeciallyTaggedText const &text) {
result += text.value;
}

constexpr void operator()(Comment const &) {};

std::string result;

private:
constexpr static void trim_html5_spaces(std::string_view &s) noexcept {
while (!s.empty() && detail::html5_is_space(s.front())) {
s.remove_prefix(1);
}
while (!value.empty() && detail::html5_is_space(value.back())) {
value.remove_suffix(1);
while (!s.empty() && detail::html5_is_space(s.back())) {
s.remove_suffix(1);
}
result += value;
};
}
};

} // namespace detail

constexpr std::string html5_minimize(std::string_view html) {
detail::Html5MinimizeMatcher matcher;
for (Html5TokenNoEof token : Html5Tokenizer{html}) {
token.match(match_start_tag, match_end_tag, match_text, match_special_text, match_comment);
token.visit(matcher);
}
return result;
return matcher.result;
}

template <CompString HTML>
Expand Down
5 changes: 2 additions & 3 deletions include/cppwebdoc/Html5Tokenizer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

#include <algorithm>
#include <cassert>
#include <concepts>
#include <cstddef>
#include <cstdint>
#include <format>
Expand All @@ -31,7 +30,7 @@ struct Overloaded : TYPES... {

template <typename... ALTS>
struct BetterVariant : std::variant<ALTS...> {
using std::variant<ALTS...>::variant; // NOLINT
using std::variant<ALTS...>::variant;

template <typename T>
[[nodiscard]] constexpr T &as() noexcept {
Expand Down Expand Up @@ -131,7 +130,7 @@ struct Eof {};

struct Html5Token
: detail::BetterVariant<Eof, StartTag, EndTag, Text, Comment, SpeciallyTaggedText> {
using BetterVariant::BetterVariant; // NOLINT
using BetterVariant::BetterVariant;
};

struct Html5TokenNoEof
Expand Down
16 changes: 16 additions & 0 deletions packages/c/cppwebdoc/xmake.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package("cppwebdoc")
set_homepage("https://github.com/bugsnotabunny/cppwebdoc")
set_description("A collection of c++ assets for manipulating html, css and js at compile-time")
set_license("MIT")

add_urls("https://github.com/bugsnotabunny/cppwebdoc/archive/refs/tags/v$(version).tar.gz")

on_install(function (package)
import("package.tools.xmake").install(package, {
kind = package:config("shared") and "shared" or "static"
})
end):

on_test(function (package)
assert(package:has_cxxfuncs("cppwebdoc::html5_minimize", {includes = "cppwebdoc/Html5Tokenizer.hpp"}))
end)
11 changes: 5 additions & 6 deletions test/cases/Html5Minimizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,15 @@
namespace {

using namespace cppwebdoc;

}

TEST_CASE("html5_minimize - basic behavior") {
SECTION("Empty input") {
STATIC_REQUIRE(html5_minimize("") == "");
STATIC_REQUIRE(html5_minimize("").empty());
}

SECTION("Whitespace-only input") {
STATIC_REQUIRE(html5_minimize(" \n\t ") == "");
STATIC_REQUIRE(html5_minimize(" \n\t ").empty());
}

SECTION("Already minimal HTML remains unchanged") {
Expand Down Expand Up @@ -72,8 +71,8 @@ TEST_CASE("html5_minimize - robustness") {
}
}

TEST_CASE("bebebe") {
constexpr static CompString DATA = R"(
TEST_CASE("some real'ish usage. only check that this compiles fine") {
constexpr static std::string_view DATA = R"(
<!DOCTYPE html>
<html lang="en">
<head>
Expand Down Expand Up @@ -141,5 +140,5 @@ TEST_CASE("bebebe") {
</body>
</html>)";

STATIC_REQUIRE_FALSE(html5_minimize_comptime<DATA>().empty());
STATIC_REQUIRE_FALSE(html5_minimize(DATA).empty());
}
16 changes: 8 additions & 8 deletions test/cases/parse/StringCursor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ TEST_CASE("StringCursor basic peek operations") {
SECTION("peek_try(n) succeeds when enough characters") {
auto r = cur.peek(3);
REQUIRE(r.has_value());
REQUIRE(*r == "abc");
REQUIRE(r == "abc");
}
}

Expand All @@ -42,7 +42,7 @@ TEST_CASE("StringCursor consume operations") {
SECTION("consume1_try consumes one character") {
auto r = cur.consume1();
REQUIRE(r.has_value());
REQUIRE(*r == 'a');
REQUIRE(r == 'a');
REQUIRE(cur.remaining() == "bc");
}

Expand All @@ -54,7 +54,7 @@ TEST_CASE("StringCursor consume operations") {
SECTION("consume_try(n) succeeds and consumes") {
auto r = cur.consume(2);
REQUIRE(r.has_value());
REQUIRE(*r == "ab");
REQUIRE(r == "ab");
REQUIRE(cur.remaining() == "c");
}

Expand Down Expand Up @@ -82,7 +82,7 @@ TEST_CASE("StringCursor peek_until operations") {
SECTION("peek_until_try returns optional when found") {
auto r = cur.peek_until([](char c) { return c == 'c'; });
REQUIRE(r.has_value());
REQUIRE(*r == "ab");
REQUIRE(r == "ab");
}

SECTION("peek_until_try returns nullopt when not found") {
Expand All @@ -107,7 +107,7 @@ TEST_CASE("StringCursor consume_until operations") {
SECTION("consume_until_try consumes when successful") {
auto r = cur.consume_until([](char c) { return c == 'b'; });
REQUIRE(r.has_value());
REQUIRE(*r == "a");
REQUIRE(r == "a");
REQUIRE(cur.remaining() == "bc123");
}

Expand Down Expand Up @@ -139,7 +139,7 @@ TEST_CASE("StringCursor *_until_not operations") {
StringCursor cur2{" abc"};
auto r = cur2.consume_until_not(is_space);
REQUIRE(r.has_value());
REQUIRE(*r == " ");
REQUIRE(r == " ");
REQUIRE(cur2.remaining() == "abc");
}
}
Expand All @@ -152,7 +152,7 @@ TEST_CASE("StringCursor edge cases") {
REQUIRE_FALSE(cur.consume1().has_value());

REQUIRE(cur.peek(0).has_value());
REQUIRE(*cur.peek(0) == "");
REQUIRE(cur.peek(0) == "");

REQUIRE(cur.remaining().empty());
}
Expand All @@ -161,6 +161,6 @@ TEST_CASE("StringCursor edge cases") {
StringCursor cur{""};
auto r = cur.peek_until([](char) { return true; });
REQUIRE(r.has_value());
REQUIRE(r->empty());
REQUIRE(r == "");
}
}
5 changes: 3 additions & 2 deletions xmake.lua
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@ end
add_requires("catch2 v3.10.0", { configs = { lto = false, main = true, gmock = false } })

target("cppwebdoc")
set_kind("static")
set_kind("headeronly")
add_includedirs("include", { public = true })
add_headerfiles("include/(**.hpp)")
set_default(true)
target_end()

Expand All @@ -43,7 +44,7 @@ target_end()


for _, file in ipairs(os.files("test/cases/**.cpp")) do
local name = "test_" .. path.basename(file)
local name = "Test" .. path.basename(file)
target(name)
set_kind("binary")
add_deps("cppwebdoc-testing")
Expand Down
Loading