mirror of
https://github.com/hyprwm/hyprlock.git
synced 2025-05-13 05:40:42 +01:00
greetd: use glaze for json handling
This commit is contained in:
parent
5f995d64cc
commit
a1200e1d22
8 changed files with 170 additions and 402 deletions
|
@ -72,6 +72,7 @@ find_package(Threads REQUIRED)
|
|||
find_package(PkgConfig REQUIRED)
|
||||
find_package(OpenGL REQUIRED COMPONENTS EGL GLES3)
|
||||
find_package(hyprwayland-scanner 0.4.4 REQUIRED)
|
||||
find_package(glaze REQUIRED)
|
||||
pkg_check_modules(
|
||||
deps
|
||||
REQUIRED
|
||||
|
@ -93,7 +94,7 @@ pkg_check_modules(
|
|||
file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp")
|
||||
add_executable(hyprlock ${SRCFILES})
|
||||
target_link_libraries(hyprlock PRIVATE pam rt Threads::Threads PkgConfig::deps
|
||||
OpenGL::EGL OpenGL::GLES3)
|
||||
OpenGL::EGL OpenGL::GLES3 glaze::glaze)
|
||||
|
||||
# protocols
|
||||
pkg_get_variable(WAYLAND_PROTOCOLS_DIR wayland-protocols pkgdatadir)
|
||||
|
@ -128,7 +129,7 @@ function(protocolWayland)
|
|||
endfunction()
|
||||
|
||||
make_directory(${CMAKE_SOURCE_DIR}/protocols) # we don't ship any custom ones so
|
||||
# the dir won't be there
|
||||
# the dir won't be there
|
||||
|
||||
protocolwayland()
|
||||
|
||||
|
@ -150,15 +151,3 @@ install(
|
|||
FILES ${CMAKE_SOURCE_DIR}/assets/example.conf
|
||||
DESTINATION ${CMAKE_INSTALL_FULL_DATAROOTDIR}/hypr
|
||||
RENAME hyprlock.conf)
|
||||
|
||||
# Tests
|
||||
#
|
||||
include(CTest)
|
||||
add_custom_target(tests)
|
||||
|
||||
add_executable(test_notjson "tests/notjson.cpp")
|
||||
target_link_libraries(test_notjson PRIVATE PkgConfig::deps)
|
||||
add_test(
|
||||
NAME "notjson"
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests
|
||||
COMMAND test_notjson "notjson")
|
||||
|
|
|
@ -2,8 +2,9 @@
|
|||
lib,
|
||||
stdenv,
|
||||
cmake,
|
||||
pkg-config,
|
||||
cairo,
|
||||
glaze,
|
||||
pkg-config,
|
||||
libdrm,
|
||||
libGL,
|
||||
libxkbcommon,
|
||||
|
@ -37,6 +38,7 @@ stdenv.mkDerivation {
|
|||
|
||||
buildInputs = [
|
||||
cairo
|
||||
glaze
|
||||
libdrm
|
||||
libGL
|
||||
libxkbcommon
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#include "GreetdLogin.hpp"
|
||||
#include "../config/ConfigManager.hpp"
|
||||
#include "../core/hyprlock.hpp"
|
||||
#include "../helpers/NotJson.hpp"
|
||||
#include "../helpers/Log.hpp"
|
||||
|
||||
#include <hyprutils/string/VarList.hpp>
|
||||
|
@ -9,26 +8,6 @@
|
|||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
static constexpr eGreetdAuthMessageType messageTypeFromString(const std::string_view& type) {
|
||||
if (type == "visible")
|
||||
return GREETD_AUTH_VISIBLE;
|
||||
if (type == "secret")
|
||||
return GREETD_AUTH_SECRET;
|
||||
if (type == "info")
|
||||
return GREETD_AUTH_INFO;
|
||||
if (type == "error")
|
||||
return GREETD_AUTH_ERROR;
|
||||
return GREETD_AUTH_ERROR;
|
||||
}
|
||||
|
||||
static constexpr eGreetdErrorMessageType errorTypeFromString(const std::string_view& type) {
|
||||
if (type == "auth_error")
|
||||
return GREETD_ERROR_AUTH;
|
||||
if (type == "error")
|
||||
return GREETD_ERROR;
|
||||
return GREETD_ERROR;
|
||||
}
|
||||
|
||||
static constexpr std::string getErrorString(eRequestError error) {
|
||||
switch (error) {
|
||||
case GREETD_REQUEST_ERROR_SEND: return "Failed to send payload to greetd";
|
||||
|
@ -127,20 +106,26 @@ static std::string readFromSock(int fd) {
|
|||
return msg;
|
||||
}
|
||||
|
||||
static bool sendGreetdRequest(int fd, const NNotJson::SObject& request) {
|
||||
static bool sendGreetdRequest(int fd, const VGreetdRequest& request) {
|
||||
if (fd < 0) {
|
||||
Debug::log(ERR, "[GreetdLogin] Invalid socket fd");
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto PAYLOAD = NNotJson::serialize(request);
|
||||
const auto GLZRESULT = glz::write_json(request);
|
||||
|
||||
if (!request.values.contains("response"))
|
||||
Debug::log(TRACE, "[GreetdLogin] Request: {}", PAYLOAD);
|
||||
else
|
||||
if (!GLZRESULT.has_value()) {
|
||||
const auto GLZERRORSTR = glz::format_error(GLZRESULT.error());
|
||||
Debug::log(ERR, "[GreetdLogin] Failed to serialize request: {}", GLZERRORSTR);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (std::holds_alternative<SGreetdPostAuthMessageResponse>(request))
|
||||
Debug::log(TRACE, "[GreetdLogin] Request: REDACTED");
|
||||
else
|
||||
Debug::log(TRACE, "[GreetdLogin] Request: {}", GLZRESULT.value());
|
||||
|
||||
if (sendToSock(fd, PAYLOAD) < 0) {
|
||||
if (sendToSock(fd, GLZRESULT.value()) < 0) {
|
||||
Debug::log(ERR, "[GreetdLogin] Failed to send payload to greetd");
|
||||
return false;
|
||||
}
|
||||
|
@ -170,7 +155,7 @@ void CGreetdLogin::init() {
|
|||
});
|
||||
}
|
||||
|
||||
std::expected<NNotJson::SObject, eRequestError> CGreetdLogin::request(const NNotJson::SObject& req) {
|
||||
std::expected<VGreetdResponse, eRequestError> CGreetdLogin::request(const VGreetdRequest& req) {
|
||||
if (!sendGreetdRequest(m_socketFD, req)) {
|
||||
m_ok = false;
|
||||
return std::unexpected(GREETD_REQUEST_ERROR_SEND);
|
||||
|
@ -185,69 +170,55 @@ std::expected<NNotJson::SObject, eRequestError> CGreetdLogin::request(const NNot
|
|||
|
||||
Debug::log(TRACE, "[GreetdLogin] Response: {}", RESPONSESTR);
|
||||
|
||||
const auto [RESULTOBJ, ERROR] = NNotJson::parse(RESPONSESTR);
|
||||
if (ERROR.status != NNotJson::SError::NOT_JSON_OK) {
|
||||
Debug::log(ERR, "[GreetdLogin] Failed to parse response from greetd: {}", ERROR.message);
|
||||
const auto GLZRESULT = glz::read_json<VGreetdResponse>(RESPONSESTR);
|
||||
if (!GLZRESULT.has_value()) {
|
||||
const auto GLZERRORSTR = glz::format_error(GLZRESULT.error(), RESPONSESTR);
|
||||
Debug::log(ERR, "[GreetdLogin] Failed to parse response from greetd: {}", GLZERRORSTR);
|
||||
m_ok = false;
|
||||
return std::unexpected(GREETD_REQUEST_ERROR_PARSE);
|
||||
}
|
||||
|
||||
if (!RESULTOBJ.values.contains("type")) {
|
||||
Debug::log(ERR, "[GreetdLogin] Invalid greetd response");
|
||||
m_ok = false;
|
||||
return std::unexpected(GREETD_REQUEST_ERROR_PARSE);
|
||||
}
|
||||
|
||||
return RESULTOBJ;
|
||||
return GLZRESULT.value();
|
||||
}
|
||||
|
||||
inline static const std::string& getStringValue(NNotJson::SObject& obj, const std::string& key) {
|
||||
try {
|
||||
return std::get<std::string>(obj.values[key]);
|
||||
} catch (std::bad_variant_access const& ex) { RASSERT(false, "Key \"{}\" does not contain a string", key); }
|
||||
};
|
||||
|
||||
void CGreetdLogin::handleResponse(const std::string& request, NNotJson::SObject& response) {
|
||||
const auto RESPONSETYPE = getStringValue(response, "type");
|
||||
|
||||
if (RESPONSETYPE == "error") {
|
||||
const auto ERRORTYPE = getStringValue(response, "error_type");
|
||||
m_state.errorType = errorTypeFromString(ERRORTYPE);
|
||||
m_state.error = getStringValue(response, "description");
|
||||
Debug::log(ERR, "[GreetdLogin] Request failed: {} - {}", ERRORTYPE, m_state.error);
|
||||
void CGreetdLogin::handleResponse(const VGreetdRequest& request, const VGreetdResponse& response) {
|
||||
if (std::holds_alternative<SGreetdErrorResponse>(response)) {
|
||||
const auto ERRORRESPONSE = std::get<SGreetdErrorResponse>(response);
|
||||
m_state.errorType = ERRORRESPONSE.error_type;
|
||||
m_state.error = ERRORRESPONSE.description;
|
||||
Debug::log(ERR, "[GreetdLogin] Request failed: {} - {}", (int)m_state.errorType, m_state.error);
|
||||
// Don't post a fail if this is a response to "cancel_session"
|
||||
if (!m_state.error.empty() && request != "cancel_session")
|
||||
if (!m_state.error.empty() && !std::holds_alternative<SGreetdCancelSession>(request))
|
||||
g_pAuth->enqueueFail(m_state.error, AUTH_IMPL_GREETD);
|
||||
|
||||
// We don't have to cancel if "create_session" failed
|
||||
if (request != "create_session")
|
||||
if (!std::holds_alternative<SGreetdCreateSession>(request))
|
||||
cancelSession();
|
||||
} else if (RESPONSETYPE == "auth_message") {
|
||||
const auto AUTHMESSAGETYPE = getStringValue(response, "auth_message_type");
|
||||
m_state.authMessageType = messageTypeFromString(AUTHMESSAGETYPE);
|
||||
m_state.message = getStringValue(response, "auth_message");
|
||||
Debug::log(LOG, "[GreetdLogin] Auth message: {} - {}", AUTHMESSAGETYPE, m_state.message);
|
||||
} else if (std::holds_alternative<SGreetdAuthMessageResponse>(response)) {
|
||||
const auto AUTHMESSAGERESPONSE = std::get<SGreetdAuthMessageResponse>(response);
|
||||
m_state.authMessageType = AUTHMESSAGERESPONSE.auth_message_type;
|
||||
m_state.message = AUTHMESSAGERESPONSE.auth_message;
|
||||
Debug::log(LOG, "[GreetdLogin] Auth message: {} - {}", (int)m_state.authMessageType, m_state.message);
|
||||
if (m_state.authMessageType == GREETD_AUTH_ERROR && !m_state.message.empty())
|
||||
g_pAuth->enqueueFail(m_state.message, AUTH_IMPL_GREETD);
|
||||
|
||||
} else if (RESPONSETYPE == "success") {
|
||||
if (request == "create_session" || request == "post_auth_message_response")
|
||||
} else if (std::holds_alternative<SGreetdSuccessResponse>(response)) {
|
||||
if (std::holds_alternative<SGreetdCreateSession>(request) || std::holds_alternative<SGreetdPostAuthMessageResponse>(request))
|
||||
startSessionAfterSuccess();
|
||||
} else
|
||||
Debug::log(ERR, "Unknown response type \"{}\"", RESPONSETYPE);
|
||||
Debug::log(ERR, "Unknown response from greetd");
|
||||
}
|
||||
|
||||
void CGreetdLogin::startSessionAfterSuccess() {
|
||||
const auto SELECTEDSESSION = g_pHyprlock->getSelectedGreetdLoginSession();
|
||||
Hyprutils::String::CVarList args(SELECTEDSESSION.exec, 0, ' ');
|
||||
|
||||
NNotJson::SObject startSession{.values = {
|
||||
{"type", "start_session"},
|
||||
}};
|
||||
startSession.values["cmd"] = std::vector<std::string>{args.begin(), args.end()};
|
||||
SGreetdStartSession startSession;
|
||||
startSession.cmd = std::vector<std::string>{args.begin(), args.end()};
|
||||
|
||||
const auto REQUEST = VGreetdRequest{startSession};
|
||||
// TODO: Is there a response for this? Should we check it?
|
||||
if (!sendGreetdRequest(m_socketFD, startSession))
|
||||
if (!sendGreetdRequest(m_socketFD, REQUEST))
|
||||
m_ok = false;
|
||||
else {
|
||||
if (g_pHyprlock->m_sCurrentDesktop == "Hyprland")
|
||||
|
@ -258,18 +229,17 @@ void CGreetdLogin::startSessionAfterSuccess() {
|
|||
}
|
||||
|
||||
void CGreetdLogin::cancelSession() {
|
||||
NNotJson::SObject cancelSession{
|
||||
.values =
|
||||
{
|
||||
{"type", "cancel_session"},
|
||||
},
|
||||
};
|
||||
SGreetdCancelSession cancelSession;
|
||||
|
||||
auto RESPONSEOPT = request(cancelSession);
|
||||
if (!RESPONSEOPT.has_value())
|
||||
const auto REQUEST = VGreetdRequest{cancelSession};
|
||||
const auto RESPONSE = request(REQUEST);
|
||||
|
||||
if (!RESPONSE.has_value()) {
|
||||
Debug::log(ERR, "Failed to cancel session: {}", getErrorString(RESPONSE.error()));
|
||||
return;
|
||||
}
|
||||
|
||||
handleResponse("cancel_session", RESPONSEOPT.value());
|
||||
handleResponse(REQUEST, RESPONSE.value());
|
||||
|
||||
m_state.authMessageType = GREETD_INITIAL;
|
||||
m_state.errorType = GREETD_OK;
|
||||
|
@ -279,23 +249,20 @@ void CGreetdLogin::createSession() {
|
|||
if (m_state.authMessageType != GREETD_INITIAL && m_state.errorType != GREETD_ERROR_AUTH)
|
||||
Debug::log(WARN, "[GreetdLogin] Trying to create a session, but last one still active?");
|
||||
|
||||
NNotJson::SObject createSession = {
|
||||
.values =
|
||||
{
|
||||
{"type", "create_session"},
|
||||
{"username", m_loginUserName},
|
||||
},
|
||||
};
|
||||
Debug::log(LOG, "Creating session for user {}", m_loginUserName);
|
||||
|
||||
Debug::log(INFO, "Creating session for user {}", m_loginUserName);
|
||||
SGreetdCreateSession createSession;
|
||||
createSession.username = m_loginUserName;
|
||||
|
||||
auto RESPONSEOPT = request(createSession);
|
||||
if (!RESPONSEOPT.has_value()) {
|
||||
Debug::log(ERR, "Failed to create session: {}", getErrorString(RESPONSEOPT.error()));
|
||||
const auto REQUEST = VGreetdRequest{createSession};
|
||||
const auto RESPONSE = request(REQUEST);
|
||||
|
||||
if (!RESPONSE.has_value()) {
|
||||
Debug::log(ERR, "Failed to create session: {}", getErrorString(RESPONSE.error()));
|
||||
return;
|
||||
}
|
||||
|
||||
handleResponse("create_session", RESPONSEOPT.value());
|
||||
handleResponse(REQUEST, RESPONSE.value());
|
||||
}
|
||||
|
||||
void CGreetdLogin::processInput() {
|
||||
|
@ -309,42 +276,37 @@ void CGreetdLogin::processInput() {
|
|||
|
||||
while (m_ok && (m_state.authMessageType == GREETD_AUTH_INFO || m_state.authMessageType == GREETD_AUTH_ERROR)) {
|
||||
// Empty reply
|
||||
NNotJson::SObject postAuthMessageResponse{
|
||||
.values =
|
||||
{
|
||||
{"type", "post_auth_message_response"},
|
||||
{"response", ""},
|
||||
},
|
||||
};
|
||||
auto RESPONSEOPT = request(postAuthMessageResponse);
|
||||
if (!RESPONSEOPT.has_value()) {
|
||||
Debug::log(ERR, "Failed to create session: {}", getErrorString(RESPONSEOPT.error()));
|
||||
SGreetdPostAuthMessageResponse postAuthMessageResponse;
|
||||
|
||||
const auto REQUEST = VGreetdRequest{postAuthMessageResponse};
|
||||
const auto RESPONSE = request(REQUEST);
|
||||
|
||||
if (!RESPONSE.has_value()) {
|
||||
Debug::log(ERR, "Failed to create session: {}", getErrorString(RESPONSE.error()));
|
||||
return;
|
||||
}
|
||||
|
||||
handleResponse("post_auth_message_response", RESPONSEOPT.value());
|
||||
handleResponse(REQUEST, RESPONSE.value());
|
||||
}
|
||||
|
||||
if (m_state.errorType != GREETD_OK) {
|
||||
// TODO: this error message is not good
|
||||
Debug::log(LOG, "Empty response to a info message failed!");
|
||||
return;
|
||||
}
|
||||
|
||||
NNotJson::SObject postAuthMessageResponse{
|
||||
.values =
|
||||
{
|
||||
{"type", "post_auth_message_response"},
|
||||
{"response", m_state.input},
|
||||
},
|
||||
};
|
||||
SGreetdPostAuthMessageResponse postAuthMessageResponse;
|
||||
postAuthMessageResponse.response = m_state.input;
|
||||
|
||||
auto RESPONSEOPT = request(postAuthMessageResponse);
|
||||
if (!RESPONSEOPT.has_value()) {
|
||||
Debug::log(ERR, "Failed to send auth response: {}", getErrorString(RESPONSEOPT.error()));
|
||||
const auto REQUEST = VGreetdRequest{postAuthMessageResponse};
|
||||
const auto RESPONSE = request(REQUEST);
|
||||
|
||||
if (!RESPONSE.has_value()) {
|
||||
Debug::log(ERR, "Failed to send auth response: {}", getErrorString(RESPONSE.error()));
|
||||
return;
|
||||
}
|
||||
|
||||
handleResponse("post_auth_message_response", RESPONSEOPT.value());
|
||||
handleResponse(REQUEST, RESPONSE.value());
|
||||
};
|
||||
|
||||
void CGreetdLogin::waitForInput() {
|
||||
|
|
|
@ -1,34 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
#include "Auth.hpp"
|
||||
#include "../helpers/NotJson.hpp"
|
||||
#include "GreetdProto.hpp"
|
||||
#include <condition_variable>
|
||||
#include <string>
|
||||
#include <expected>
|
||||
#include <thread>
|
||||
|
||||
// GREETD PROTOCOL
|
||||
enum eGreetdResponse : uint8_t {
|
||||
GREETD_RESPONSE_UNKNOWN = 0xff,
|
||||
GREETD_RESPONSE_SUCCESS = 0,
|
||||
GREETD_RESPONSE_ERROR = 1,
|
||||
GREETD_RESPONSE_AUTH = 2,
|
||||
};
|
||||
|
||||
enum eGreetdErrorMessageType : uint8_t {
|
||||
GREETD_OK = 0,
|
||||
GREETD_ERROR_AUTH = 1,
|
||||
GREETD_ERROR = 2,
|
||||
};
|
||||
|
||||
enum eGreetdAuthMessageType : uint8_t {
|
||||
GREETD_INITIAL = 0,
|
||||
GREETD_AUTH_VISIBLE = 0,
|
||||
GREETD_AUTH_SECRET = 1,
|
||||
GREETD_AUTH_INFO = 2,
|
||||
GREETD_AUTH_ERROR = 3,
|
||||
};
|
||||
|
||||
// INTERNAL
|
||||
enum eRequestError : uint8_t {
|
||||
GREETD_REQUEST_ERROR_SEND = 0,
|
||||
|
@ -66,7 +44,7 @@ class CGreetdLogin : public IAuthImplementation {
|
|||
friend class CAuth;
|
||||
|
||||
private:
|
||||
std::expected<NNotJson::SObject, eRequestError> request(const NNotJson::SObject& req);
|
||||
std::expected<VGreetdResponse, eRequestError> request(const VGreetdRequest& req);
|
||||
|
||||
//
|
||||
void createSession();
|
||||
|
@ -74,7 +52,7 @@ class CGreetdLogin : public IAuthImplementation {
|
|||
void recreateSession();
|
||||
void startSessionAfterSuccess();
|
||||
|
||||
void handleResponse(const std::string& request, NNotJson::SObject& response);
|
||||
void handleResponse(const VGreetdRequest& request, const VGreetdResponse& response);
|
||||
void processInput();
|
||||
void waitForInput();
|
||||
|
||||
|
|
89
src/auth/GreetdProto.hpp
Normal file
89
src/auth/GreetdProto.hpp
Normal file
|
@ -0,0 +1,89 @@
|
|||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <variant>
|
||||
#include <glaze/glaze.hpp>
|
||||
#include <glaze/util/string_literal.hpp>
|
||||
|
||||
// GREETD PROTOCOL
|
||||
enum eGreetdResponse : uint8_t {
|
||||
GREETD_RESPONSE_UNKNOWN = 0xff,
|
||||
GREETD_RESPONSE_SUCCESS = 0,
|
||||
GREETD_RESPONSE_ERROR = 1,
|
||||
GREETD_RESPONSE_AUTH = 2,
|
||||
};
|
||||
|
||||
enum eGreetdErrorMessageType : uint8_t {
|
||||
GREETD_OK = 0,
|
||||
GREETD_ERROR_AUTH = 1,
|
||||
GREETD_ERROR = 2,
|
||||
};
|
||||
|
||||
enum eGreetdAuthMessageType : uint8_t {
|
||||
GREETD_INITIAL = 0,
|
||||
GREETD_AUTH_VISIBLE = 1,
|
||||
GREETD_AUTH_SECRET = 2,
|
||||
GREETD_AUTH_INFO = 3,
|
||||
GREETD_AUTH_ERROR = 4,
|
||||
};
|
||||
|
||||
// REQUEST TYPES
|
||||
struct SGreetdCreateSession {
|
||||
std::string type = "create_session";
|
||||
std::string username = "";
|
||||
};
|
||||
|
||||
struct SGreetdPostAuthMessageResponse {
|
||||
std::string type = "post_auth_message_response";
|
||||
std::string response = "";
|
||||
};
|
||||
|
||||
struct SGreetdStartSession {
|
||||
std::string type = "start_session";
|
||||
std::vector<std::string> cmd;
|
||||
std::vector<std::string> env;
|
||||
};
|
||||
|
||||
struct SGreetdCancelSession {
|
||||
std::string type = "cancel_session";
|
||||
};
|
||||
|
||||
// RESPONSE TYPES
|
||||
struct SGreetdErrorResponse {
|
||||
eGreetdErrorMessageType error_type;
|
||||
std::string description;
|
||||
};
|
||||
|
||||
struct SGreetdAuthMessageResponse {
|
||||
eGreetdAuthMessageType auth_message_type;
|
||||
std::string auth_message;
|
||||
};
|
||||
|
||||
struct SGreetdSuccessResponse {
|
||||
char DUMMY; // Without any field in SGreetdSuccessResponse, I get unknown_key for "type".
|
||||
};
|
||||
|
||||
// RESPONSE and REQUEST VARIANTS
|
||||
using VGreetdRequest = std::variant<SGreetdCreateSession, SGreetdPostAuthMessageResponse, SGreetdStartSession, SGreetdCancelSession>;
|
||||
using VGreetdResponse = std::variant<SGreetdSuccessResponse, SGreetdErrorResponse, SGreetdAuthMessageResponse>;
|
||||
|
||||
template <>
|
||||
struct glz::meta<eGreetdResponse> {
|
||||
static constexpr auto value = enumerate("success", GREETD_RESPONSE_SUCCESS, "error", GREETD_RESPONSE_ERROR, "auth_message", GREETD_RESPONSE_AUTH);
|
||||
};
|
||||
|
||||
template <>
|
||||
struct glz::meta<eGreetdAuthMessageType> {
|
||||
static constexpr auto value = enumerate("visible", GREETD_AUTH_VISIBLE, "secret", GREETD_AUTH_SECRET, "info", GREETD_AUTH_INFO, "error", GREETD_AUTH_ERROR);
|
||||
};
|
||||
|
||||
template <>
|
||||
struct glz::meta<eGreetdErrorMessageType> {
|
||||
static constexpr auto value = enumerate("auth_error", GREETD_ERROR_AUTH, "error", GREETD_ERROR);
|
||||
};
|
||||
|
||||
template <>
|
||||
struct glz::meta<VGreetdResponse> {
|
||||
static constexpr std::string_view tag = "type";
|
||||
static constexpr std::array ids{"success", "error", "auth_message"};
|
||||
};
|
|
@ -1,155 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
/*
|
||||
This is a "json parser" intended to parse/serialize for the greetd protocol.
|
||||
It makes the following assumptions:
|
||||
- Data only contains strings or vectors of strings
|
||||
- Data is not nested
|
||||
|
||||
For example:
|
||||
{
|
||||
"key1": "value1",
|
||||
"key2": ["value2", "value3"]
|
||||
}
|
||||
*/
|
||||
|
||||
#include <cstdio>
|
||||
#include <format>
|
||||
#include <hyprutils/string/String.hpp>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <sys/types.h>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <variant>
|
||||
|
||||
namespace NNotJson {
|
||||
using VJsonValue = std::variant<std::string, std::vector<std::string>>;
|
||||
|
||||
struct SObject {
|
||||
std::unordered_map<std::string, VJsonValue> values;
|
||||
};
|
||||
|
||||
struct SError {
|
||||
enum eStatus : u_int8_t {
|
||||
NOT_JSON_OK,
|
||||
NOT_JSON_ERROR,
|
||||
} status = NOT_JSON_OK;
|
||||
|
||||
std::string message = "";
|
||||
};
|
||||
|
||||
inline std::pair<SObject, SError> parse(const std::string& data) {
|
||||
static constexpr const std::string sinkChars = " \t\n\r,:{}";
|
||||
|
||||
SObject result{};
|
||||
|
||||
std::string key{};
|
||||
std::vector<std::string> array;
|
||||
bool parsingArray = false;
|
||||
for (size_t i = 0; i < data.size(); i++) {
|
||||
if (sinkChars.find(data[i]) != std::string::npos)
|
||||
continue;
|
||||
|
||||
switch (data[i]) {
|
||||
case '"': {
|
||||
// find the next quote that is not escaped
|
||||
size_t end = i + 1;
|
||||
for (; end < data.size(); end++) {
|
||||
if (data[end] == '\\') {
|
||||
end++;
|
||||
continue;
|
||||
}
|
||||
if (data[end] == '"')
|
||||
break;
|
||||
}
|
||||
|
||||
if (end == data.size())
|
||||
return {result,
|
||||
{
|
||||
.status = SError::NOT_JSON_ERROR,
|
||||
.message = "Expected closing quote, but reached end of input",
|
||||
}};
|
||||
|
||||
std::string val{data.data() + i + 1, end - (i + 1)};
|
||||
Hyprutils::String::replaceInString(val, "\\\"", "\"");
|
||||
if (key.empty())
|
||||
key = val;
|
||||
else if (parsingArray)
|
||||
array.emplace_back(val);
|
||||
else {
|
||||
result.values.emplace(key, val);
|
||||
key.clear();
|
||||
}
|
||||
|
||||
i = end;
|
||||
} break;
|
||||
case '[': {
|
||||
parsingArray = true;
|
||||
if (key.empty())
|
||||
return {result,
|
||||
{
|
||||
.status = SError::NOT_JSON_ERROR,
|
||||
.message = "Expected key before array",
|
||||
}};
|
||||
} break;
|
||||
case ']': {
|
||||
result.values.emplace(std::string{key}, array);
|
||||
key = std::string_view{};
|
||||
parsingArray = false;
|
||||
array.clear();
|
||||
} break;
|
||||
case '\0':
|
||||
return {result,
|
||||
{
|
||||
.status = SError::NOT_JSON_ERROR,
|
||||
.message = "Encountered null byte ???",
|
||||
}};
|
||||
default:
|
||||
return {result,
|
||||
{
|
||||
.status = SError::NOT_JSON_ERROR,
|
||||
.message = std::format("Unexpected character \"{}\"", data[i]),
|
||||
}};
|
||||
}
|
||||
};
|
||||
|
||||
return {result, {}};
|
||||
}
|
||||
|
||||
inline std::string serializeString(const std::string& in) {
|
||||
std::string escaped = in;
|
||||
Hyprutils::String::replaceInString(escaped, "\"", "\\\"");
|
||||
return std::format("\"{}\"", escaped);
|
||||
}
|
||||
|
||||
inline std::string serializeArray(const std::vector<std::string>& in) {
|
||||
std::stringstream result;
|
||||
result << "[";
|
||||
for (const auto& item : in) {
|
||||
result << serializeString(item) << ",";
|
||||
}
|
||||
result.seekp(-1, std::ios_base::end);
|
||||
result << "]";
|
||||
return result.str();
|
||||
}
|
||||
|
||||
inline std::string serialize(const SObject& obj) {
|
||||
std::stringstream result;
|
||||
result << "{";
|
||||
|
||||
for (const auto& [key, value] : obj.values) {
|
||||
result << std::format("\"{}\":", key);
|
||||
if (std::holds_alternative<std::string>(value))
|
||||
result << serializeString(std::get<std::string>(value)) << ",";
|
||||
else
|
||||
result << serializeArray(std::get<std::vector<std::string>>(value)) << ",";
|
||||
}
|
||||
|
||||
result.seekp(-1, std::ios_base::end);
|
||||
result << "}";
|
||||
|
||||
return result.str();
|
||||
}
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
#include <print>
|
||||
#include <variant>
|
||||
#include "shared.hpp"
|
||||
#include "../src/helpers/NotJson.hpp"
|
||||
|
||||
int main() {
|
||||
const auto in = R"({"type":"asdf","array":["a","b","c"]})";
|
||||
int ret = 0;
|
||||
|
||||
auto [result, error] = NNotJson::parse(in);
|
||||
EXPECT(error.status, NNotJson::SError::NOT_JSON_OK);
|
||||
|
||||
EXPECT(result.values.size(), 2);
|
||||
EXPECT(std::holds_alternative<std::string>(result.values["type"]), true);
|
||||
EXPECT(std::get<std::string>(result.values["type"]), "asdf");
|
||||
|
||||
EXPECT(std::holds_alternative<std::vector<std::string>>(result.values["array"]), true);
|
||||
const auto vec = std::get<std::vector<std::string>>(result.values["array"]);
|
||||
EXPECT(vec.size(), 3);
|
||||
EXPECT(vec[0], std::string{"a"});
|
||||
EXPECT(vec[1], std::string{"b"});
|
||||
EXPECT(vec[2], std::string{"c"});
|
||||
|
||||
const auto serialized = NNotJson::serialize(result);
|
||||
|
||||
std::print("serialized: {}\n", serialized);
|
||||
// order is not guaranteed
|
||||
EXPECT(serialized == in || serialized == R"({"array":["a","b","c"],"type":"asdf"})", true);
|
||||
|
||||
const auto in2 = R"({"type":"auth_message","auth_message_type":"secret","auth_message":"Password: "})";
|
||||
auto [result2, error2] = NNotJson::parse(in2);
|
||||
|
||||
EXPECT(error2.status, NNotJson::SError::NOT_JSON_OK);
|
||||
EXPECT(result2.values.size(), 3);
|
||||
EXPECT(std::holds_alternative<std::string>(result2.values["type"]), true);
|
||||
EXPECT(std::get<std::string>(result2.values["type"]), "auth_message");
|
||||
|
||||
const auto in3 = R"({ "type:"asdf" })";
|
||||
auto [result3, error3] = NNotJson::parse(in3);
|
||||
|
||||
EXPECT(error3.status, NNotJson::SError::NOT_JSON_ERROR);
|
||||
EXPECT(error3.message, "Unexpected character \"a\"");
|
||||
|
||||
const auto in4 = R"({"type":"a\"s\"df"})";
|
||||
auto [result4, error4] = NNotJson::parse(in4);
|
||||
|
||||
EXPECT(error4.status, NNotJson::SError::NOT_JSON_OK);
|
||||
EXPECT(result4.values.size(), 1);
|
||||
EXPECT(std::holds_alternative<std::string>(result4.values["type"]), true);
|
||||
EXPECT(std::get<std::string>(result4.values["type"]), "a\"s\"df");
|
||||
|
||||
const auto serialized4 = NNotJson::serialize(result4);
|
||||
EXPECT(serialized4, in4);
|
||||
|
||||
const auto in5 = R"({" *~@#$%^&*()_+=><?/\a":" *~@#$%^&*()_+=><?/\a"})";
|
||||
auto [result5, error5] = NNotJson::parse(in5);
|
||||
|
||||
EXPECT(error5.status, NNotJson::SError::NOT_JSON_OK);
|
||||
EXPECT(result5.values.size(), 1);
|
||||
EXPECT(std::holds_alternative<std::string>(result5.values[" *~@#$%^&*()_+=><?/\\a"]), true);
|
||||
EXPECT(std::get<std::string>(result5.values[" *~@#$%^&*()_+=><?/\\a"]), " *~@#$%^&*()_+=><?/\\a");
|
||||
std::print("serialized5: {}\n", NNotJson::serialize(result5));
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
#pragma once
|
||||
#include <iostream>
|
||||
|
||||
namespace Colors {
|
||||
constexpr const char* RED = "\x1b[31m";
|
||||
constexpr const char* GREEN = "\x1b[32m";
|
||||
constexpr const char* YELLOW = "\x1b[33m";
|
||||
constexpr const char* BLUE = "\x1b[34m";
|
||||
constexpr const char* MAGENTA = "\x1b[35m";
|
||||
constexpr const char* CYAN = "\x1b[36m";
|
||||
constexpr const char* RESET = "\x1b[0m";
|
||||
};
|
||||
|
||||
#define EXPECT(expr, val) \
|
||||
if (const auto& RESULT = expr; RESULT != (val)) { \
|
||||
std::cout << Colors::RED << "Failed: " << Colors::RESET << #expr << ", expected " << val << " but got " << RESULT << "\n"; \
|
||||
ret = 1; \
|
||||
} else { \
|
||||
std::cout << Colors::GREEN << "Passed " << Colors::RESET << #expr << ". Got " << val << "\n"; \
|
||||
}
|
||||
#define EXPECT_VECTOR2D(expr, val) \
|
||||
do { \
|
||||
const auto& RESULT = expr; \
|
||||
const auto& EXPECTED = val; \
|
||||
if (!(std::abs(RESULT.x - EXPECTED.x) < 1e-6 && std::abs(RESULT.y - EXPECTED.y) < 1e-6)) { \
|
||||
std::cout << Colors::RED << "Failed: " << Colors::RESET << #expr << ", expected (" << EXPECTED.x << ", " << EXPECTED.y << ") but got (" << RESULT.x << ", " \
|
||||
<< RESULT.y << ")\n"; \
|
||||
ret = 1; \
|
||||
} else { \
|
||||
std::cout << Colors::GREEN << "Passed " << Colors::RESET << #expr << ". Got (" << RESULT.x << ", " << RESULT.y << ")\n"; \
|
||||
} \
|
||||
} while (0)
|
Loading…
Reference in a new issue