diff --git a/.github/workflows/arch.yml b/.github/workflows/arch.yml index 85bc5d8..02a1994 100644 --- a/.github/workflows/arch.yml +++ b/.github/workflows/arch.yml @@ -2,39 +2,6 @@ name: Build & Test (Arch) on: [push, pull_request, workflow_dispatch] jobs: - gcc: - name: "gcc build / clang test" - runs-on: ubuntu-latest - container: - image: archlinux - steps: - - name: Checkout repository actions - uses: actions/checkout@v4 - with: - sparse-checkout: .github/actions - - - name: Get required pkgs - run: | - sed -i 's/SigLevel = Required DatabaseOptional/SigLevel = Optional TrustAll/' /etc/pacman.conf - pacman --noconfirm --noprogressbar -Syyu - pacman --noconfirm --noprogressbar -Sy gcc base-devel cmake clang libc++ - - - name: Build hyprlang with gcc - run: | - CC="/usr/bin/gcc" CXX="/usr/bin/g++" cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -S . -B ./build - CC="/usr/bin/gcc" CXX="/usr/bin/g++" cmake --build ./build --config Release --target hyprlang -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF` - cmake --install ./build - - - name: Build tests with clang - run: | - rm -rf ./build - CC="/usr/bin/clang" CXX="/usr/bin/clang++" cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_CXX_FLAGS="-stdlib=libc++" -S . -B ./build - CC="/usr/bin/clang" CXX="/usr/bin/clang++" cmake --build ./build --config Release --target tests -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF` - - - name: Run tests - run: | - cd ./build && ctest --output-on-failure - asan: name: "gcc build / ASan tests" runs-on: ubuntu-latest @@ -50,7 +17,11 @@ jobs: run: | sed -i 's/SigLevel = Required DatabaseOptional/SigLevel = Optional TrustAll/' /etc/pacman.conf pacman --noconfirm --noprogressbar -Syyu - pacman --noconfirm --noprogressbar -Sy gcc base-devel cmake clang + pacman --noconfirm --noprogressbar -Sy gcc base-devel cmake clang git + + - name: Get hyprutils-git + run: | + git clone https://github.com/hyprwm/hyprutils && cd hyprutils && cmake -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -B build && cmake --build build --target hyprutils && cmake --install build - name: Build with gcc run: | @@ -77,7 +48,11 @@ jobs: run: | sed -i 's/SigLevel = Required DatabaseOptional/SigLevel = Optional TrustAll/' /etc/pacman.conf pacman --noconfirm --noprogressbar -Syyu - pacman --noconfirm --noprogressbar -Sy gcc base-devel cmake clang + pacman --noconfirm --noprogressbar -Sy gcc base-devel cmake clang git + + - name: Get hyprutils-git + run: | + git clone https://github.com/hyprwm/hyprutils && cd hyprutils && cmake -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -B build && cmake --build build --target hyprutils && cmake --install build - name: Build with gcc run: | @@ -104,7 +79,11 @@ jobs: run: | sed -i 's/SigLevel = Required DatabaseOptional/SigLevel = Optional TrustAll/' /etc/pacman.conf pacman --noconfirm --noprogressbar -Syyu - pacman --noconfirm --noprogressbar -Sy gcc base-devel cmake clang + pacman --noconfirm --noprogressbar -Sy gcc base-devel cmake clang git + + - name: Get hyprutils-git + run: | + git clone https://github.com/hyprwm/hyprutils && cd hyprutils && cmake -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -B build && cmake --build build --target hyprutils && cmake --install build - name: Build with gcc run: | @@ -131,7 +110,11 @@ jobs: run: | sed -i 's/SigLevel = Required DatabaseOptional/SigLevel = Optional TrustAll/' /etc/pacman.conf pacman --noconfirm --noprogressbar -Syyu - pacman --noconfirm --noprogressbar -Sy gcc base-devel cmake clang libc++ + pacman --noconfirm --noprogressbar -Sy gcc base-devel cmake clang libc++ git + + - name: Get hyprutils-git + run: | + git clone https://github.com/hyprwm/hyprutils && cd hyprutils && cmake -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -B build && cmake --build build --target hyprutils && cmake --install build - name: Build hyprlang with clang run: | diff --git a/CMakeLists.txt b/CMakeLists.txt index f64c315..46f8513 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,11 @@ add_compile_definitions(HYPRLANG_INTERNAL) set(CMAKE_CXX_STANDARD 23) +find_package(PkgConfig REQUIRED) +pkg_check_modules(deps REQUIRED IMPORTED_TARGET + hyprutils>=0.1.1 +) + file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp" "include/hyprlang.hpp") add_library(hyprlang SHARED ${SRCFILES}) @@ -40,6 +45,8 @@ set_target_properties(hyprlang PROPERTIES SOVERSION 2 PUBLIC_HEADER include/hyprlang.hpp) +target_link_libraries(hyprlang PkgConfig::deps) + if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") # for std::expected. # probably evil. Arch's clang is very outdated tho... @@ -55,12 +62,12 @@ install(TARGETS hyprlang) add_custom_target(tests) add_executable(hyprlang_test "tests/parse/main.cpp") -target_link_libraries(hyprlang_test PRIVATE hyprlang) +target_link_libraries(hyprlang_test PRIVATE hyprlang hyprutils) add_test(NAME "Parsing" WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests COMMAND hyprlang_test "parse") add_dependencies(tests hyprlang_test) add_executable(hyprlang_fuzz "tests/fuzz/main.cpp") -target_link_libraries(hyprlang_fuzz PRIVATE hyprlang) +target_link_libraries(hyprlang_fuzz PRIVATE hyprlang hyprutils) add_test(NAME "Fuzz" WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests COMMAND hyprlang_fuzz "fuzz") add_dependencies(tests hyprlang_fuzz) diff --git a/flake.lock b/flake.lock index 2341deb..6650250 100644 --- a/flake.lock +++ b/flake.lock @@ -1,12 +1,35 @@ { "nodes": { + "hyprutils": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ], + "systems": [ + "systems" + ] + }, + "locked": { + "lastModified": 1717881334, + "narHash": "sha256-a0inRgJhPL6v9v7RPM/rx1kbXdfe3xJA1c9z0ZkYnh4=", + "owner": "hyprwm", + "repo": "hyprutils", + "rev": "0693f9398ab693d89c9a0aa3b3d062dd61b7a60e", + "type": "github" + }, + "original": { + "owner": "hyprwm", + "repo": "hyprutils", + "type": "github" + } + }, "nixpkgs": { "locked": { - "lastModified": 1708475490, - "narHash": "sha256-g1v0TsWBQPX97ziznfJdWhgMyMGtoBFs102xSYO4syU=", + "lastModified": 1717602782, + "narHash": "sha256-pL9jeus5QpX5R+9rsp3hhZ+uplVHscNJh8n8VpqscM0=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "0e74ca98a74bc7270d28838369593635a5db3260", + "rev": "e8057b67ebf307f01bdcc8fba94d94f75039d1f6", "type": "github" }, "original": { @@ -18,6 +41,7 @@ }, "root": { "inputs": { + "hyprutils": "hyprutils", "nixpkgs": "nixpkgs", "systems": "systems" } diff --git a/flake.nix b/flake.nix index 78bcd8e..985f7a1 100644 --- a/flake.nix +++ b/flake.nix @@ -4,13 +4,20 @@ inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; systems.url = "github:nix-systems/default-linux"; + + hyprutils = { + url = "github:hyprwm/hyprutils"; + inputs.nixpkgs.follows = "nixpkgs"; + inputs.systems.follows = "systems"; + }; }; outputs = { self, nixpkgs, systems, - }: let + ... + } @ inputs: let inherit (nixpkgs) lib; eachSystem = lib.genAttrs (import systems); pkgsFor = eachSystem (system: @@ -26,13 +33,16 @@ in { overlays = { default = self.overlays.hyprlang; - hyprlang = final: prev: { - hyprlang = final.callPackage ./nix/default.nix { - stdenv = final.gcc13Stdenv; - version = "0.pre" + "+date=" + (mkDate (self.lastModifiedDate or "19700101")) + "_" + (self.shortRev or "dirty"); - }; - hyprlang-with-tests = final.hyprlang.override {doCheck = true;}; - }; + hyprlang = lib.composeManyExtensions [ + inputs.hyprutils.overlays.default + (final: prev: { + hyprlang = final.callPackage ./nix/default.nix { + stdenv = final.gcc13Stdenv; + version = "0.pre" + "+date=" + (mkDate (self.lastModifiedDate or "19700101")) + "_" + (self.shortRev or "dirty"); + }; + hyprlang-with-tests = final.hyprlang.override {doCheck = true;}; + }) + ]; }; packages = eachSystem (system: { diff --git a/nix/default.nix b/nix/default.nix index fa91166..25df375 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -2,6 +2,8 @@ lib, stdenv, cmake, + hyprutils, + pkg-config, version ? "git", doCheck ? false, }: @@ -10,7 +12,12 @@ stdenv.mkDerivation { inherit version doCheck; src = ../.; - nativeBuildInputs = [cmake]; + nativeBuildInputs = [ + cmake + pkg-config + ]; + + buildInputs = [hyprutils]; outputs = ["out" "dev"]; diff --git a/src/VarList.cpp b/src/VarList.cpp deleted file mode 100644 index 518acde..0000000 --- a/src/VarList.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#include "VarList.hpp" -#include -#include - -static std::string removeBeginEndSpacesTabs(std::string str) { - if (str.empty()) - return str; - - int countBefore = 0; - while (str[countBefore] == ' ' || str[countBefore] == '\t') { - countBefore++; - } - - int countAfter = 0; - while ((int)str.length() - countAfter - 1 >= 0 && (str[str.length() - countAfter - 1] == ' ' || str[str.length() - 1 - countAfter] == '\t')) { - countAfter++; - } - - str = str.substr(countBefore, str.length() - countBefore - countAfter); - - return str; -} - -CVarList::CVarList(const std::string& in, const size_t lastArgNo, const char delim, const bool removeEmpty) { - if (in.empty()) - m_vArgs.emplace_back(""); - - std::string args{in}; - size_t idx = 0; - size_t pos = 0; - std::ranges::replace_if( - args, [&](const char& c) { return delim == 's' ? std::isspace(c) : c == delim; }, 0); - - for (const auto& s : args | std::views::split(0)) { - if (removeEmpty && s.empty()) - continue; - if (++idx == lastArgNo) { - m_vArgs.emplace_back(removeBeginEndSpacesTabs(in.substr(pos))); - break; - } - pos += s.size() + 1; - m_vArgs.emplace_back(removeBeginEndSpacesTabs(std::string_view{s}.data())); - } -} - -std::string CVarList::join(const std::string& joiner, size_t from, size_t to) const { - size_t last = to == 0 ? size() : to; - - std::string rolling; - for (size_t i = from; i < last; ++i) { - rolling += m_vArgs[i] + (i + 1 < last ? joiner : ""); - } - - return rolling; -} \ No newline at end of file diff --git a/src/VarList.hpp b/src/VarList.hpp deleted file mode 100644 index 1374da6..0000000 --- a/src/VarList.hpp +++ /dev/null @@ -1,63 +0,0 @@ -#pragma once -#include -#include -#include - -class CVarList { - public: - /** Split string into arg list - @param lastArgNo stop splitting after argv reaches maximum size, last arg will contain rest of unsplit args - @param delim if delimiter is 's', use std::isspace - @param removeEmpty remove empty args from argv - */ - CVarList(const std::string& in, const size_t maxSize = 0, const char delim = ',', const bool removeEmpty = false); - - ~CVarList() = default; - - size_t size() const { - return m_vArgs.size(); - } - - std::string join(const std::string& joiner, size_t from = 0, size_t to = 0) const; - - void map(std::function func) { - for (auto& s : m_vArgs) - func(s); - } - - void append(const std::string arg) { - m_vArgs.emplace_back(arg); - } - - std::string operator[](const size_t& idx) const { - if (idx >= m_vArgs.size()) - return ""; - return m_vArgs[idx]; - } - - // for range-based loops - std::vector::iterator begin() { - return m_vArgs.begin(); - } - std::vector::const_iterator begin() const { - return m_vArgs.begin(); - } - std::vector::iterator end() { - return m_vArgs.end(); - } - std::vector::const_iterator end() const { - return m_vArgs.end(); - } - - bool contains(const std::string& el) { - for (auto& a : m_vArgs) { - if (a == el) - return true; - } - - return false; - } - - private: - std::vector m_vArgs; -}; \ No newline at end of file diff --git a/src/config.cpp b/src/config.cpp index 5f02be7..a295650 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -8,10 +8,11 @@ #include #include #include - -#include "VarList.hpp" +#include +#include using namespace Hyprlang; +using namespace Hyprutils::String; #ifdef __APPLE__ #include @@ -33,25 +34,6 @@ static size_t seekABIStructSize(const void* begin, size_t startOffset, size_t ma return 0; } -static std::string removeBeginEndSpacesTabs(std::string str) { - if (str.empty()) - return str; - - int countBefore = 0; - while (str[countBefore] == ' ' || str[countBefore] == '\t') { - countBefore++; - } - - int countAfter = 0; - while ((int)str.length() - countAfter - 1 >= 0 && (str[str.length() - countAfter - 1] == ' ' || str[str.length() - 1 - countAfter] == '\t')) { - countAfter++; - } - - str = str.substr(countBefore, str.length() - countBefore - countAfter); - - return str; -} - CConfig::CConfig(const char* path, const Hyprlang::SConfigOptions& options_) { SConfigOptions options; std::memcpy(&options, &options_, seekABIStructSize(&options_, 16, sizeof(SConfigOptions))); @@ -177,62 +159,27 @@ void CConfig::commence() { } } -static bool isNumber(const std::string& str, bool allowfloat) { - - std::string copy = str; - if (*copy.begin() == '-') - copy = copy.substr(1); - - if (copy.empty()) - return false; - - bool point = !allowfloat; - for (auto& c : copy) { - if (c == '.') { - if (point) - return false; - point = true; - continue; - } - - if (!std::isdigit(c)) - return false; - } - - return true; -} - -static void replaceAll(std::string& str, const std::string& from, const std::string& to) { - if (from.empty()) - return; - size_t pos = 0; - while ((pos = str.find(from, pos)) != std::string::npos) { - str.replace(pos, from.length(), to); - pos += to.length(); - } -} - static std::expected configStringToInt(const std::string& VALUE) { if (VALUE.starts_with("0x")) { // Values with 0x are hex const auto VALUEWITHOUTHEX = VALUE.substr(2); return stoll(VALUEWITHOUTHEX, nullptr, 16); } else if (VALUE.starts_with("rgba(") && VALUE.ends_with(')')) { - const auto VALUEWITHOUTFUNC = removeBeginEndSpacesTabs(VALUE.substr(5, VALUE.length() - 6)); + const auto VALUEWITHOUTFUNC = trim(VALUE.substr(5, VALUE.length() - 6)); // try doing it the comma way first if (std::count(VALUEWITHOUTFUNC.begin(), VALUEWITHOUTFUNC.end(), ',') == 3) { // cool std::string rolling = VALUEWITHOUTFUNC; - auto r = configStringToInt(removeBeginEndSpacesTabs(rolling.substr(0, rolling.find(',')))); + auto r = configStringToInt(trim(rolling.substr(0, rolling.find(',')))); rolling = rolling.substr(rolling.find(',') + 1); - auto g = configStringToInt(removeBeginEndSpacesTabs(rolling.substr(0, rolling.find(',')))); + auto g = configStringToInt(trim(rolling.substr(0, rolling.find(',')))); rolling = rolling.substr(rolling.find(',') + 1); - auto b = configStringToInt(removeBeginEndSpacesTabs(rolling.substr(0, rolling.find(',')))); + auto b = configStringToInt(trim(rolling.substr(0, rolling.find(',')))); rolling = rolling.substr(rolling.find(',') + 1); uint8_t a = 0; try { - a = std::round(std::stof(removeBeginEndSpacesTabs(rolling.substr(0, rolling.find(',')))) * 255.f); + a = std::round(std::stof(trim(rolling.substr(0, rolling.find(',')))) * 255.f); } catch (std::exception& e) { return std::unexpected("failed parsing " + VALUEWITHOUTFUNC); } if (!r.has_value() || !g.has_value() || !b.has_value()) @@ -249,17 +196,17 @@ static std::expected configStringToInt(const std::string& return std::unexpected("rgba() expects length of 8 characters (4 bytes) or 4 comma separated values"); } else if (VALUE.starts_with("rgb(") && VALUE.ends_with(')')) { - const auto VALUEWITHOUTFUNC = removeBeginEndSpacesTabs(VALUE.substr(4, VALUE.length() - 5)); + const auto VALUEWITHOUTFUNC = trim(VALUE.substr(4, VALUE.length() - 5)); // try doing it the comma way first if (std::count(VALUEWITHOUTFUNC.begin(), VALUEWITHOUTFUNC.end(), ',') == 2) { // cool std::string rolling = VALUEWITHOUTFUNC; - auto r = configStringToInt(removeBeginEndSpacesTabs(rolling.substr(0, rolling.find(',')))); + auto r = configStringToInt(trim(rolling.substr(0, rolling.find(',')))); rolling = rolling.substr(rolling.find(',') + 1); - auto g = configStringToInt(removeBeginEndSpacesTabs(rolling.substr(0, rolling.find(',')))); + auto g = configStringToInt(trim(rolling.substr(0, rolling.find(',')))); rolling = rolling.substr(rolling.find(',') + 1); - auto b = configStringToInt(removeBeginEndSpacesTabs(rolling.substr(0, rolling.find(',')))); + auto b = configStringToInt(trim(rolling.substr(0, rolling.find(',')))); if (!r.has_value() || !g.has_value() || !b.has_value()) return std::unexpected("failed parsing " + VALUEWITHOUTFUNC); @@ -513,7 +460,7 @@ CParseResult CConfig::parseVariable(const std::string& lhs, const std::string& r } void CConfigImpl::parseComment(const std::string& comment) { - const auto COMMENT = removeBeginEndSpacesTabs(comment); + const auto COMMENT = trim(comment); if (!COMMENT.starts_with("hyprlang")) return; @@ -527,7 +474,7 @@ void CConfigImpl::parseComment(const std::string& comment) { CParseResult CConfig::parseLine(std::string line, bool dynamic) { CParseResult result; - line = removeBeginEndSpacesTabs(line); + line = trim(line); auto commentPos = line.find('#'); @@ -556,7 +503,7 @@ CParseResult CConfig::parseLine(std::string line, bool dynamic) { } } - line = removeBeginEndSpacesTabs(line); + line = trim(line); if (line.empty()) return result; @@ -572,8 +519,8 @@ CParseResult CConfig::parseLine(std::string line, bool dynamic) { if (equalsPos != std::string::npos) { // set value or call handler CParseResult ret; - auto LHS = removeBeginEndSpacesTabs(line.substr(0, equalsPos)); - auto RHS = removeBeginEndSpacesTabs(line.substr(equalsPos + 1)); + auto LHS = trim(line.substr(0, equalsPos)); + auto RHS = trim(line.substr(equalsPos + 1)); if (LHS.empty()) { result.setError("Empty lhs."); @@ -591,9 +538,9 @@ CParseResult CConfig::parseLine(std::string line, bool dynamic) { const auto RHSIT = RHS.find("$" + var.name); if (LHSIT != std::string::npos) - replaceAll(LHS, "$" + var.name, var.value); + replaceInString(LHS, "$" + var.name, var.value); if (RHSIT != std::string::npos) - replaceAll(RHS, "$" + var.name, var.value); + replaceInString(RHS, "$" + var.name, var.value); if (RHSIT == std::string::npos && LHSIT == std::string::npos) continue; @@ -657,7 +604,7 @@ CParseResult CConfig::parseLine(std::string line, bool dynamic) { } line.pop_back(); - line = removeBeginEndSpacesTabs(line); + line = trim(line); impl->categories.push_back(line); } }