From 64922e213ac731279cf3341253e67509adb2dfc8 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 16 Sep 2022 21:36:12 +0200 Subject: wanda: restructure source directory --- source/include/wanda/command.hpp | 46 +++++ source/include/wanda/commander.hpp | 66 +++++++ source/include/wanda/control_connection.hpp | 114 +++++++++++++ source/include/wanda/control_interface.hpp | 96 +++++++++++ source/include/wanda/deferred_failure.hpp | 21 +++ source/include/wanda/environment.hpp | 61 +++++++ source/include/wanda/expected.hpp | 256 ++++++++++++++++++++++++++++ source/include/wanda/filesystem.hpp | 39 +++++ source/include/wanda/keyed.hpp | 28 +++ source/include/wanda/logging.hpp | 35 ++++ source/include/wanda/magic.hpp | 58 +++++++ source/include/wanda/message.hpp | 83 +++++++++ source/include/wanda/optional.hpp | 65 +++++++ source/include/wanda/setting.hpp | 156 +++++++++++++++++ source/include/wanda/type_wrapper.hpp | 47 +++++ source/include/wanda/wallpaper.hpp | 24 +++ source/include/wanda/xdg.hpp | 40 +++++ 17 files changed, 1235 insertions(+) create mode 100644 source/include/wanda/command.hpp create mode 100644 source/include/wanda/commander.hpp create mode 100644 source/include/wanda/control_connection.hpp create mode 100644 source/include/wanda/control_interface.hpp create mode 100644 source/include/wanda/deferred_failure.hpp create mode 100644 source/include/wanda/environment.hpp create mode 100644 source/include/wanda/expected.hpp create mode 100644 source/include/wanda/filesystem.hpp create mode 100644 source/include/wanda/keyed.hpp create mode 100644 source/include/wanda/logging.hpp create mode 100644 source/include/wanda/magic.hpp create mode 100644 source/include/wanda/message.hpp create mode 100644 source/include/wanda/optional.hpp create mode 100644 source/include/wanda/setting.hpp create mode 100644 source/include/wanda/type_wrapper.hpp create mode 100644 source/include/wanda/wallpaper.hpp create mode 100644 source/include/wanda/xdg.hpp (limited to 'source/include/wanda') diff --git a/source/include/wanda/command.hpp b/source/include/wanda/command.hpp new file mode 100644 index 0000000..5ea1a08 --- /dev/null +++ b/source/include/wanda/command.hpp @@ -0,0 +1,46 @@ +#ifndef WANDA_COMMAND_HPP +#define WANDA_COMMAND_HPP + +#include + +#include +#include +#include + +namespace wanda +{ + /** + * @brief An enum to describe different command IDs + */ + enum struct command_id : char + { + change, //< Change the wallpaper + }; + + /** + * @brief A simple type to represent commands transported through the control connection + */ + struct command + { + command_id const id; + std::vector const arguments; + + /** + * @brief Convert the command to a message for transmission to a remote endpoint + */ + std::optional message() const; + }; + + /** + * @brief Extract a command from a message + */ + std::optional make_command(message message); + + /** + * @brief A simple factory to create a "Change wallpaper" command + */ + command make_change_command(); + +} // namespace wanda + +#endif \ No newline at end of file diff --git a/source/include/wanda/commander.hpp b/source/include/wanda/commander.hpp new file mode 100644 index 0000000..1c76c6d --- /dev/null +++ b/source/include/wanda/commander.hpp @@ -0,0 +1,66 @@ +#ifndef WANDA_COMMANDER_HPP +#define WANDA_COMMANDER_HPP + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +namespace wanda +{ + /** + * @brief The remote control client + * + */ + struct commander : wanda::control_connection::listener + { + /** + * @brief The interface to be implemented by remote control listeners + */ + struct listener + { + virtual void on_connected(commander & commander){}; + virtual void on_response(commander & commander, std::string response){}; + virtual void on_error(commander & commander, std::string error){}; + }; + + /** + * @brief Construct a new commander + */ + commander(asio::io_service & service, std::filesystem::path socket, listener & listener); + + /** + * @brief Start communication with the remote daemon endpoint + */ + void start(); + + /** + * @brief Stop communication with the remote daemon endpoint + */ + void stop(); + + /** + * @brief Send a command to the remote daemon endpoint + */ + void send(command command); + + void on_error(control_connection::pointer connection, std::error_code error) override; + void on_received(control_connection::pointer connection, message message) override; + + private: + asio::io_service & m_service; + wanda::control_connection::protocol::endpoint m_endpoint; + wanda::control_connection::protocol::socket m_socket; + wanda::control_connection::pointer m_connection; + listener & m_listener; + }; + +} // namespace wanda +#endif \ No newline at end of file diff --git a/source/include/wanda/control_connection.hpp b/source/include/wanda/control_connection.hpp new file mode 100644 index 0000000..b692d37 --- /dev/null +++ b/source/include/wanda/control_connection.hpp @@ -0,0 +1,114 @@ +#ifndef WANDA_CONTROL_CONNECTION_HPP +#define WANDA_CONTROL_CONNECTION_HPP + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +namespace wanda +{ + /** + * @brief A connection to a remote control endpoint + */ + struct control_connection : keyed, std::enable_shared_from_this + { + using protocol = asio::local::stream_protocol; + using pointer = std::shared_ptr; + + /** + * @brief The interface to be implemented by the control interface listener + */ + struct listener + { + virtual void on_close(pointer connection) {} + virtual void on_received(pointer connection, message message) {} + virtual void on_error(pointer connection, std::error_code) {} + }; + + /** + * @brief A enum to describe different connection states + */ + enum struct state : std::underlying_type_t + { + unknown, //< Connection is in an unknown state + fresh, //< Connection is freshly created but not established + established, //< Connection has been established + }; + + /** + * @internal + * @brief Construct a new control connection object + * + * @note This constructor is keyed on a private key type so it can only be constructed using the #wanda::make_connection factory + */ + control_connection(key, protocol::socket socket); + + /** + * @brief Add the given listener to this control connection's listener set + * + * @returns true iff. the listener was not already in the listener set + */ + bool add(listener * listener); + + /** + * @brief Remove the given listener from this control connection's listener set + * + * @return true iff. the listener was previously registered with this control connection + */ + bool remove(listener * listener); + + /** + * @brief Start I/O processing for this control connection + */ + void start(); + + /** + * @brief Close this control connection + */ + void close(); + + /** + * @brief Send the given message to the remote endpoint + */ + void send(message message); + + /** + * @brief Set the connection state to the provided state + */ + void update(state state); + + /** + * @brief Get the current connection state + */ + state current_state() const; + + private: + friend pointer make_control_connection(protocol::socket && socket); + + void perform_read(); + + protocol::socket m_socket; + asio::streambuf m_in{}; + asio::streambuf m_out{}; + std::istream m_input{&m_in}; + std::ostream m_output{&m_out}; + std::set m_listeners{}; + state m_state{}; + }; + + /** + * @brief Create a new control connection + */ + control_connection::pointer make_control_connection(control_connection::protocol::socket && socket); + +} // namespace wanda + +#endif \ No newline at end of file diff --git a/source/include/wanda/control_interface.hpp b/source/include/wanda/control_interface.hpp new file mode 100644 index 0000000..73ef2cf --- /dev/null +++ b/source/include/wanda/control_interface.hpp @@ -0,0 +1,96 @@ +/** + * @file environment.hpp + * @author Felix Morgner (felix.morgner@gmail.com) + * @since 1.0.0 + */ + +#ifndef WANDA_CONTROL_INTERFACE_HPP +#define WANDA_CONTROL_INTERFACE_HPP + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace wanda +{ + /** + * @brief An RAII type to delete a socket file upon destruction + */ + struct socket_deleter + { + ~socket_deleter(); + + std::filesystem::path path; + }; + + /** + * @brief The daemon control interface + */ + struct control_interface : control_connection::listener, keyed, std::enable_shared_from_this + { + using protocol = asio::local::stream_protocol; + using pointer = std::shared_ptr; + + /** + * @brief The interface to be implemented by the control interface listener + */ + struct listener + { + virtual void on_received(control_interface & interface, command command){}; + }; + + /** + * @internal + * @brief Construct a new control interface object + * + * @note This constructor is keyed on a private key type so it can only be constructed using the #wanda::make_interface factory + */ + control_interface(key, asio::io_service & service, protocol::endpoint endpoint, listener & listener); + + /** + * @brief Start handling of controller connections + */ + std::error_code start(); + + /** + * @brief Stop the control interface + */ + std::error_code shutdown(); + + void on_close(control_connection::pointer connection) override; + void on_received(control_connection::pointer connection, message message) override; + + private: + void perform_accept(); + + friend pointer make_interface(asio::io_service & service, std::filesystem::path file, control_interface::listener & listener); + + asio::io_service & m_service; + protocol::endpoint m_endpoint; + protocol::socket m_socket; + protocol::acceptor m_acceptor; + listener & m_listener; + socket_deleter m_deleter{m_endpoint.path()}; + std::set m_connections; + }; + + /** + * @brief A factory to create new #control_interface instances + */ + control_interface::pointer make_interface(asio::io_service & service, std::filesystem::path socket, control_interface::listener & listener); + +} // namespace wanda + +#endif \ No newline at end of file diff --git a/source/include/wanda/deferred_failure.hpp b/source/include/wanda/deferred_failure.hpp new file mode 100644 index 0000000..5db26f6 --- /dev/null +++ b/source/include/wanda/deferred_failure.hpp @@ -0,0 +1,21 @@ +/** + * @file deferred_failure.hpp + * @author Felix Morgner (felix.morgner@gmail.com) + * @since 1.0.0 + */ + +#ifndef WANDA_DEFERRED_FAILURE_HPP +#define WANDA_DEFERRED_FAILURE_HPP + +#include + +namespace +{ + /** + * @brief A helper type to defer static_assert failures + */ + template + using deferred_failure = std::false_type; +} // namespace + +#endif \ No newline at end of file diff --git a/source/include/wanda/environment.hpp b/source/include/wanda/environment.hpp new file mode 100644 index 0000000..5a702a8 --- /dev/null +++ b/source/include/wanda/environment.hpp @@ -0,0 +1,61 @@ +/** + * @file environment.hpp + * @author Felix Morgner (felix.morgner@gmail.com) + * @since 1.0.0 + */ + +#ifndef WANDA_ENVIRONMENT_HPP +#define WANDA_ENVIRONMENT_HPP + +#include + +#include +#include + +namespace wanda +{ + /** + * @brief A type to provide access to the runtime environment + */ + struct environment + { + using map_type = std::map; + using iterator = map_type::iterator; + using const_iterator = map_type::const_iterator; + using reference = map_type::reference; + using const_reference = map_type::const_reference; + + /** + * @brief Construct a new environment from the given string array + */ + explicit environment(char const * const * env = ::environ); + + /** + * @brief Get the value of the given variable + * + * @return A mutable reference to the value of the given environment variable + */ + std::string & operator[](std::string const & variable); + + /** + * @brief Get the value of the given variable + * + * @return An immutable reference to the value of the given environment variable + */ + std::string const & operator[](std::string const & variable) const; + + iterator begin(); + const_iterator begin() const; + const_iterator cbegin() const; + + iterator end(); + const_iterator end() const; + const_iterator cend() const; + + private: + map_type m_cache{}; + }; + +} // namespace wanda + +#endif \ No newline at end of file diff --git a/source/include/wanda/expected.hpp b/source/include/wanda/expected.hpp new file mode 100644 index 0000000..fff0d81 --- /dev/null +++ b/source/include/wanda/expected.hpp @@ -0,0 +1,256 @@ +/** + * @file expected.hpp + * @author Felix Morgner (felix.morgner@gmail.com) + * @since 1.0.0 + */ + +#ifndef WANDA_EXPECTED_HPP +#define WANDA_EXPECTED_HPP + +#include +#include +#include + +namespace wanda +{ + /** + * @brief A type to represent the error case of a computation based on #wanda::expected + */ + template + struct unexpected + { + static_assert(!std::is_same_v, "ErrorType can not be 'void'!"); + static_assert(!std::is_array_v, "ErrorType can not be an array type!"); + + /** + * @brief Copy construct a new @p unexpected from another @p unexpected + */ + constexpr unexpected(unexpected const &) = default; + + /** + * @brief Move construct a new @p unexpected from another @p unexpected + */ + constexpr unexpected(unexpected &&) = default; + + /** + * @brief Construct a new @p unexpected by direct initializing the error object from @p args + */ + template + constexpr explicit unexpected(std::in_place_t, Args &&... args) + : m_error(std::forward(args)...) + { + } + + /** + * @brief Construct a new @p unexpected by direct initializing the error object from @p il and @p args + */ + template< + typename U, + typename... Args, + std::enable_if_t, Args...>> * = nullptr> + constexpr explicit unexpected(std::in_place_t, std::initializer_list il, Args &&... args) + : m_error(il, std::forward(args)...) + { + } + + /** + * @brief Construct a new @p unexpected by direct initializing the error object from @p error + */ + template< + typename Err = ErrorType, + std::enable_if_t && + !std::is_same_v>, std::in_place_t> && + !std::is_same_v>, unexpected>> * = nullptr> + constexpr explicit unexpected(Err && error) + : m_error(std::forward(error)) + { + } + + /** + * @brief Construct a new @p unexpected by copying the value of another @p unexpected of different error type + */ + template< + typename Err, + std::enable_if_t && + !std::is_constructible_v &> && + !std::is_constructible_v> && + !std::is_constructible_v const &> && + !std::is_constructible_v const> && + !std::is_convertible_v &, ErrorType> && + !std::is_convertible_v, ErrorType> && + !std::is_convertible_v const &, ErrorType> && + !std::is_convertible_v const, ErrorType>)> * = nullptr, + std::enable_if_t> * = nullptr> + constexpr explicit unexpected(unexpected const & error) + : m_error(error.m_error) + { + } + + /** + * @brief Construct a new @p unexpected by copying the value of another @p unexpected of different error type + */ + template< + typename Err, + std::enable_if_t && + !std::is_constructible_v &> && + !std::is_constructible_v> && + !std::is_constructible_v const &> && + !std::is_constructible_v const> && + !std::is_convertible_v &, ErrorType> && + !std::is_convertible_v, ErrorType> && + !std::is_convertible_v const &, ErrorType> && + !std::is_convertible_v const, ErrorType>)> * = nullptr, + std::enable_if_t> * = nullptr> + constexpr unexpected(unexpected const & error) + : m_error(error.m_error) + { + } + + /** + * @brief Construct a new @p unexpected by moving the value of another @p unexpected of different error type + */ + template< + typename Err, + std::enable_if_t && + !std::is_constructible_v &> && + !std::is_constructible_v> && + !std::is_constructible_v const &> && + !std::is_constructible_v const> && + !std::is_convertible_v &, ErrorType> && + !std::is_convertible_v, ErrorType> && + !std::is_convertible_v const &, ErrorType> && + !std::is_convertible_v const, ErrorType>)> * = nullptr, + std::enable_if_t> * = nullptr> + constexpr explicit unexpected(unexpected && error) + : m_error(std::move(error.m_error)) + { + } + + /** + * @brief Construct a new @p unexpected by moving the value of another @p unexpected of different error type + */ + template< + typename Err, + std::enable_if_t && + !std::is_constructible_v &> && + !std::is_constructible_v> && + !std::is_constructible_v const &> && + !std::is_constructible_v const> && + !std::is_convertible_v &, ErrorType> && + !std::is_convertible_v, ErrorType> && + !std::is_convertible_v const &, ErrorType> && + !std::is_convertible_v const, ErrorType>)> * = nullptr, + std::enable_if_t> * = nullptr> + constexpr unexpected(unexpected && error) + : m_error(std::move(error.m_error)) + { + } + + /** + * @brief Get the error value contained in this @p unexpected instance + */ + constexpr ErrorType const & value() const & + { + return m_error; + } + + /** + * @brief Get the error value contained in this @p unexpected instance + */ + constexpr ErrorType & value() & + { + return m_error; + } + + /** + * @brief Get the error value contained in this @p unexpected instance + */ + constexpr ErrorType && value() && + { + return std::move(m_error); + } + + /** + * @brief Get the error value contained in this @p unexpected instance + */ + constexpr ErrorType const && value() const && + { + return std::move(m_error); + } + + /** + * @brief Swap the error value of this @p unexpected instance with the one of @p other + */ + void swap(unexpected & other) noexcept(std::is_nothrow_swappable_v) + { + using std::swap; + swap(m_error, other.m_error); + } + + template + friend constexpr bool operator==(unexpected const & lhs, unexpected const & rhs); + + template + friend constexpr bool operator!=(unexpected const & lhs, unexpected const & rhs); + + template< + typename Err, + std::enable_if_t> *> + friend void swap(unexpected & lhs, unexpected & rhs); + + private: + ErrorType m_error; + }; + + template + unexpected(ErrorType)->unexpected; + + /** + * @brief Compare two @p unexpected instances for equality + */ + template + constexpr bool operator==(unexpected const & lhs, unexpected const & rhs) + { + return lhs.m_error == rhs.m_error; + } + + /** + * @brief Compare two @p unexpected instances for inequality + */ + template + constexpr bool operator!=(unexpected const & lhs, unexpected const & rhs) + { + return lhs.m_error != rhs.m_error; + } + + /** + * @brief Swap the error values of two @p unexpected instances + */ + template< + typename Err, + std::enable_if_t> * = nullptr> + void swap(unexpected & lhs, unexpected & rhs) + { + lhs.swap(rhs); + } + + /** + * @brief A tag type for @p unexpected + */ + struct unexpect_t + { + explicit unexpect_t() = default; + }; + + /** + * @brief A tap for @p unexpected + */ + inline constexpr unexpect_t unexpect{}; + +} // namespace wanda + +#endif \ No newline at end of file diff --git a/source/include/wanda/filesystem.hpp b/source/include/wanda/filesystem.hpp new file mode 100644 index 0000000..1975bc5 --- /dev/null +++ b/source/include/wanda/filesystem.hpp @@ -0,0 +1,39 @@ +/** + * @file filesystem.hpp + * @author Felix Morgner (felix.morgner@gmail.com) + * @since 1.0.0 + */ + +#ifndef WANDA_FILESYSTEM_HPP +#define WANDA_FILESYSTEM_HPP + +#include +#include +#include + +namespace wanda +{ + /** + * @brief Covenience alias for path lists + */ + using path_list = std::vector; + + /** + * @brief The default scan filter, allowing only regular files to pass + */ + constexpr inline auto default_filter = [](std::filesystem::path const & path) { + return is_regular_file(path); + }; + + /** + * @brief Scan the given folder for files + */ + std::optional scan(std::filesystem::path folder, bool(filter)(std::filesystem::path const &) = default_filter); + + /** + * @brief Pick a random path from the given list + */ + std::filesystem::path random_pick(path_list const & paths); +} // namespace wanda + +#endif \ No newline at end of file diff --git a/source/include/wanda/keyed.hpp b/source/include/wanda/keyed.hpp new file mode 100644 index 0000000..58f17ad --- /dev/null +++ b/source/include/wanda/keyed.hpp @@ -0,0 +1,28 @@ +/** + * @file keyed.hpp + * @author Felix Morgner (felix.morgner@gmail.com) + * @since 1.0.0 + */ + +#ifndef WANDA_KEYED_HPP +#define WANDA_KEYED_HPP + +namespace wanda +{ + /** + * @brief A tag type to prevent construction of a type without a factory + */ + template + struct keyed + { + protected: + struct key + { + }; + + explicit keyed(key) {} + }; + +} // namespace wanda + +#endif \ No newline at end of file diff --git a/source/include/wanda/logging.hpp b/source/include/wanda/logging.hpp new file mode 100644 index 0000000..b3c1665 --- /dev/null +++ b/source/include/wanda/logging.hpp @@ -0,0 +1,35 @@ +/** + * @file logging.hpp + * @author Felix Morgner (felix.morgner@gmail.com) + * @since 1.0.0 + */ + +#ifndef WANDA_LOGGING_HPP +#define WANDA_LOGGING_HPP + +#include +#include + +#include + +namespace wanda +{ + /** + * @brief A covenience alias to represent a handle for a logger + */ + using logger_ptr = std::shared_ptr; + + /** + * @brief Initialize the shared logger + * + * @note The logger will only ever be initialized once, even if this function is called multiple times + */ + void initialize_logger(spdlog::sink_ptr sink = std::make_shared()); + + /** + * @brief Get the shared logger + */ + logger_ptr get_logger(); +} // namespace wanda + +#endif \ No newline at end of file diff --git a/source/include/wanda/magic.hpp b/source/include/wanda/magic.hpp new file mode 100644 index 0000000..fcb153e --- /dev/null +++ b/source/include/wanda/magic.hpp @@ -0,0 +1,58 @@ +#ifndef WANDA_MAGIC_HPP +#define WANDA_MAGIC_HPP + +#include + +#include +#include +#include +#include + +namespace wanda +{ + struct magic + { + struct closer + { + auto operator()(magic_t handle) const noexcept -> void + { + magic_close(handle); + } + }; + + enum struct mime_type + { + unknown, + image_jpeg, + image_png, + }; + + magic() + : m_handle{magic_open(MAGIC_MIME_TYPE)} + { + magic_load(m_handle.get(), nullptr); + } + + auto type(std::filesystem::path path) -> mime_type + { + auto magic_type = std::string{magic_file(m_handle.get(), path.native().c_str())}; + + if (magic_type == "image/jpeg") + { + return mime_type::image_jpeg; + } + else if (magic_type == "image/png") + { + return mime_type::image_png; + } + + return mime_type::unknown; + } + + private: + std::unique_ptr, closer> m_handle; + }; + +} // namespace wanda + +#endif \ No newline at end of file diff --git a/source/include/wanda/message.hpp b/source/include/wanda/message.hpp new file mode 100644 index 0000000..866408f --- /dev/null +++ b/source/include/wanda/message.hpp @@ -0,0 +1,83 @@ +/** + * @file message.hpp + * @author Felix Morgner (felix.morgner@gmail.com) + * @since 1.0.0 + */ + +#ifndef WANDA_MESSAGE_HPP +#define WANDA_MESSAGE_HPP + +#include +#include +#include +#include + +namespace wanda +{ + inline namespace v1 + { + /** + * @brief The version argument for the hello message reflecting the current version + */ + auto constexpr message_argument_hello = "1.0.0"; + } // namespace v1 + + /** + * @brief A tag to mark messages originating from the controller + */ + auto constexpr message_source_controller = "C"; + + /** + * @brief A tag to mark messages originating from the daemon + */ + auto constexpr message_source_daemon = "D"; + + /** + * @brief The command of the hello message + */ + auto constexpr message_command_hello = "HELLO"; + + /** + * @brief A control protocol message, consisting of a @p source, @p command, and @p arguments + */ + struct message + { + /** + * @brief Serialize this message into a string + */ + explicit operator std::string() const; + + /** + * @brief Get the size of the message as if it was serialized + */ + std::size_t size() const; + + /** + * @brief The source of the message + */ + std::string source; + + /** + * @brief The command of the message + */ + std::string command; + + /** + * @brief The arguments of the message command + */ + std::optional argument; + }; + + /** + * @brief Deserialize a message from the given stream + */ + std::istream & operator>>(std::istream & in, message & message); + + /** + * @brief Serialize a message to the given stream + */ + std::ostream & operator<<(std::ostream & out, message const & message); + +} // namespace wanda + +#endif \ No newline at end of file diff --git a/source/include/wanda/optional.hpp b/source/include/wanda/optional.hpp new file mode 100644 index 0000000..da3774c --- /dev/null +++ b/source/include/wanda/optional.hpp @@ -0,0 +1,65 @@ +/** + * @file optional.hpp + * @author Felix Morgner (felix.morgner@gmail.com) + * @since 1.0.0 + */ + +#ifndef WANDA_OPTIONAL_HPP +#define WANDA_OPTIONAL_HPP + +#include + +namespace wanda::std_ext +{ + /** + * @brief A type to represent a computation that could fail + */ + struct failable + { + /** + * @brief A factory to create a successful computation + */ + constexpr static auto success() { return failable{false}; } + + /** + * @brief A factory to create a failed computation + */ + constexpr static auto failure() { return failable{true}; } + + /** + * @brief Execute the given handler if the computation failed + */ + template + constexpr void operator||(Handler handler) const + { + if (m_failed) + { + handler(); + } + } + + private: + constexpr explicit failable(bool failed) + : m_failed{failed} {}; + bool const m_failed; + }; + + /** + * @brief Unwrap the given optional object, if present, and pass it to the handler + * + * @return A successful computation iff. the object was present, a failed computation otherwise. + */ + template + auto with(std::optional && object, HandlerType handler) + { + if (object) + { + handler(object.value()); + return failable::success(); + } + return failable::failure(); + } + +} // namespace wanda::std_ext + +#endif \ No newline at end of file diff --git a/source/include/wanda/setting.hpp b/source/include/wanda/setting.hpp new file mode 100644 index 0000000..1721651 --- /dev/null +++ b/source/include/wanda/setting.hpp @@ -0,0 +1,156 @@ +/** + * @file setting.hpp + * @author Felix Morgner (felix.morgner@gmail.com) + * @since 1.0.0 + */ + +#ifndef WANDA_setting_HPP +#define WANDA_setting_HPP + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace wanda +{ + struct setting; + + /** + * @brief A convenience type to represent setting keys + */ + using key = type_wrapper; + + namespace literals + { + /** + * @brief UDL to create setting keys + */ + key operator""_key(char const * str, std::size_t len); + + /** + * @brief UDL to create setting schemas + */ + std::optional operator""_setting(char const * str, std::size_t lent); + } // namespace literals + + /** + * @brief A simple wrapper for GSettings Schemas + */ + struct setting + { + struct entry + { + using value_type = std::variant>; + + /** + * @brief Get the value of the settings entry + */ + value_type operator*() const; + + /** + * @brief Assign the given @p value to the settings entry + * + * @returns @p true iff. the value could be successfully assigned + */ + template + bool operator=(Type value) + { + struct setting_applier + { + setting_applier(GSettings * setting, gchar const * key, Type value) noexcept + : m_result{[&] { + if constexpr (std::is_same_v) + { + return g_settings_set_boolean(setting, key, value); + } + else if constexpr (std::is_same_v) + { + return g_settings_set_int(setting, key, value); + } + else if constexpr (std::is_same_v) + { + return g_settings_set_int64(setting, key, value); + } + else if constexpr (std::is_same_v) + { + return g_settings_set_uint(setting, key, value); + } + else if constexpr (std::is_same_v) + { + return g_settings_set_uint64(setting, key, value); + } + else if constexpr (std::is_same_v) + { + return g_settings_set_double(setting, key, value); + } + else if constexpr (std::is_same_v) + { + return g_settings_set_string(setting, key, value.c_str()); + } + else if constexpr (std::is_same_v>) + { + auto temp = std::vector{value.size() + 1}; + std::transform(value.begin(), value.end(), temp.begin(), [](auto const & str) { return str.c_str(); }); + return g_settings_set_strv(setting, key, temp.data()); + } + }()} + { + } + + ~setting_applier() + { + g_settings_sync(); + } + + operator bool() const + { + return m_result; + } + + private: + gboolean const m_result; + }; + + return setting_applier{m_settings.get(), m_key.get().c_str(), value}; + } + + private: + entry(setting const & schema, key key); + + std::unique_ptr m_settings; + + key m_key; + + friend setting; + }; + + /** + * @brief Get the entry for the given key + * + * @return An std::optional wrapping the entry associated with + * the given key, or an empty std::optional if the desired key + * does not exist in the setting's schema. + */ + std::optional operator[](key key) const; + + private: + explicit setting(GSettingsSchema * schema); + + std::unique_ptr m_schema; + + friend std::optional literals::operator""_setting(char const *, std::size_t); + }; + +} // namespace wanda + +#endif \ No newline at end of file diff --git a/source/include/wanda/type_wrapper.hpp b/source/include/wanda/type_wrapper.hpp new file mode 100644 index 0000000..12684cb --- /dev/null +++ b/source/include/wanda/type_wrapper.hpp @@ -0,0 +1,47 @@ +/** + * @file type_wrapper.hpp + * @author Felix Morgner (felix.morgner@gmail.com) + * @since 1.0.0 + */ + +#ifndef WANDA_TYPE_WRAPPER_HPP +#define WANDA_TYPE_WRAPPER_HPP + +#include + +namespace wanda +{ + /** + * @brief A type to create a distinct type based on an existing type + * + * @tparam InnerType The type to wrap + * @tparam TagType A tag type to identify the distinct type + */ + template + struct type_wrapper + { + /** + * @brief Construct a new type wrapper object + */ + explicit type_wrapper(InnerType value) + : m_value{std::move(value)} + { + } + + /** + * @brief Retrieve the wrapped value with its original type + */ + constexpr explicit operator InnerType const &() const { return get(); } + + /** + * @brief Retrieve the wrapped value with its original type + */ + constexpr InnerType const & get() const { return m_value; } + + private: + InnerType m_value; + }; + +} // namespace wanda + +#endif \ No newline at end of file diff --git a/source/include/wanda/wallpaper.hpp b/source/include/wanda/wallpaper.hpp new file mode 100644 index 0000000..0cad473 --- /dev/null +++ b/source/include/wanda/wallpaper.hpp @@ -0,0 +1,24 @@ +/** + * @file wallpaper.hpp + * @author Felix Morgner (felix.morgner@gmail.com) + * @since 1.0.0 + */ + +#ifndef WANDA_WALLPAPER_HPP +#define WANDA_WALLPAPER_HPP + +#include +#include + +#include +#include + +namespace wanda +{ + /** + * @brief Set the wallpaper to the file specified by the given path + */ + void set_wallpaper(std::filesystem::path wallpaper); +} // namespace wanda + +#endif \ No newline at end of file diff --git a/source/include/wanda/xdg.hpp b/source/include/wanda/xdg.hpp new file mode 100644 index 0000000..bc138fa --- /dev/null +++ b/source/include/wanda/xdg.hpp @@ -0,0 +1,40 @@ +/** + * @file xdg.hpp + * @author Felix Morgner (felix.morgner@gmail.com) + * @since 1.0.0 + */ + +#ifndef WANDA_XDG_HPP +#define WANDA_XDG_HPP + +#include + +#include +#include +#include + +namespace wanda +{ + /** + * @brief An @p enum to represet the standardized XDG directories + */ + enum struct xdg_directory : std::underlying_type_t + { + data_home, + config_home, + cache_home, + runtime_dir, + }; + + /** + * @brief Get the name of the environment variable associated with the given XDG directory + */ + std::string xdg_variable(xdg_directory directory); + + /** + * @brief Get the path to the given @p directory given the provided @p environment + */ + std::filesystem::path xdg_path_for(xdg_directory directory, environment const & environment); +} // namespace wanda + +#endif \ No newline at end of file -- cgit v1.2.3