core: Move to hyprutils for util functions (#48)

* move to hyprutils

* Nix: add hyprutils dep

---------

Co-authored-by: Mihai Fufezan <mihai@fufexan.net>
This commit is contained in:
Vaxry 2024-06-08 23:24:12 +02:00 committed by GitHub
parent 87d5d98410
commit ec6938c662
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 102 additions and 242 deletions

View file

@ -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: |

View file

@ -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)

View file

@ -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"
}

View file

@ -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 = 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: {

View file

@ -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"];

View file

@ -1,55 +0,0 @@
#include "VarList.hpp"
#include <ranges>
#include <algorithm>
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;
}

View file

@ -1,63 +0,0 @@
#pragma once
#include <functional>
#include <vector>
#include <string>
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<void(std::string&)> 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<std::string>::iterator begin() {
return m_vArgs.begin();
}
std::vector<std::string>::const_iterator begin() const {
return m_vArgs.begin();
}
std::vector<std::string>::iterator end() {
return m_vArgs.end();
}
std::vector<std::string>::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<std::string> m_vArgs;
};

View file

@ -8,10 +8,11 @@
#include <expected>
#include <sstream>
#include <cstring>
#include "VarList.hpp"
#include <hyprutils/string/VarList.hpp>
#include <hyprutils/string/String.hpp>
using namespace Hyprlang;
using namespace Hyprutils::String;
#ifdef __APPLE__
#include <crt_externs.h>
@ -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<int64_t, std::string> 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<int64_t, std::string> 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);
}
}