Page MenuHomePhorge

No OneTemporary

Size
6 KB
Referenced Files
None
Subscribers
None
diff --git a/.gitignore b/.gitignore
index 378eac2..0530df7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
build
+doc.html
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 849a512..7872d72 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,29 +1,33 @@
cmake_minimum_required(VERSION 3.16)
project(lager-qt-demo)
include(FeatureSummary)
set(QT_MAJOR_VERSION 6)
set(KF_MAJOR_VERSION 6)
set(QT_MIN_VERSION 6.5.0)
set(KF_MIN_VERSION 6.0.0)
find_package(ECM ${KF_MIN_VERSION} REQUIRED NO_MODULE)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH})
include(KDEInstallDirs)
include(KDECMakeSettings)
include(KDECompilerSettings NO_POLICY_SCOPE)
include(ECMSetupVersion)
include(ECMGenerateHeaders)
include(ECMPoQmTools)
include(ECMAddTests)
include(ECMQtDeclareLoggingCategory)
include(ECMDeprecationSettings)
include(ECMQmlModule)
-find_package(Qt6 ${QT_MIN_VERSION} REQUIRED COMPONENTS Core Gui Qml QuickControls2 Widgets)
+kde_enable_exceptions()
+
+find_package(Qt6 ${QT_MIN_VERSION} REQUIRED COMPONENTS Core Gui Qml QuickControls2 Widgets Test)
qt6_policy(SET QTP0001 NEW)
find_package(KF6 ${KF_MIN_VERSION} REQUIRED COMPONENTS Kirigami)
find_package(Threads REQUIRED)
+
+add_subdirectory(src)
diff --git a/doc.org b/doc.org
new file mode 100644
index 0000000..1c25e6b
--- /dev/null
+++ b/doc.org
@@ -0,0 +1,58 @@
+
+* What is lager?
+- library to help with *value-oriented design*
+- store application state as a *value* type
+* What is a value type?
+- copyable
+- equality comparable
+#+BEGIN_SRC c++
+T a = ...;
+T b = a;
+REQUIRE(a == b);
+#+END_SRC
+- identity does not depend on a reference
+#+BEGIN_SRC c++
+ // not a value type
+ struct Person
+ {
+ std::string name;
+ std::vector<std::weak_ptr<Person>> contacts;
+ };
+
+ // value type
+ struct Person
+ {
+ using IdType = std::string;
+ IdType id;
+ std::string name;
+ std::vector<IdType> contacts;
+ };
+#+END_SRC
+* Basic example of a value-oriented program
+#+INCLUDE: "src/basic-example/logic.hpp" src c++
+#+INCLUDE: "src/basic-example/logic.cpp" src c++
+#+INCLUDE: "src/basic-example/main.cpp" src c++
+* Benefits of using lager?
+- Testability
+ - libkazv: [[https://lily-is.land/kazv/libkazv][76.9%]] test coverage
+ - libQuotient: [[https://sonarcloud.io/component_measures?metric=coverage&selected=quotient-im_libQuotient%3AQuotient&id=quotient-im_libQuotient][41.6%]] (raw), 46.9% (adjusted)
+#+INCLUDE: "src/basic-example/test.cpp" src c++
+
+* Appendix
+** Calculating libQuotient coverage adjusted
+libQuotient includes the generated csapi folder into coverage, while libkazv does not. Here is the code that calculates the coverage of libQuotient excluding the csapi folder.
+#+NAME: libquotient-coverage
+#+BEGIN_SRC perl
+my $csapiCoverage = 0.169;
+my $csapiUncovered = 1_668;
+my $totalLines = 11_229;
+my $totalUncovered = 6_561;
+my $csapiLines = $csapiUncovered / (1-$csapiCoverage);
+my $remainingLines = $totalLines - $csapiLines;
+my $remainingUncovered = $totalUncovered - $csapiUncovered;
+my $remainingCoverage = 1 - $remainingUncovered / $remainingLines;
+$remainingCoverage;
+#+END_SRC
+
+#+RESULTS: libquotient-coverage
+: 0.46940827964562
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 0000000..8092618
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1 @@
+add_subdirectory(basic-example)
diff --git a/src/basic-example/CMakeLists.txt b/src/basic-example/CMakeLists.txt
new file mode 100644
index 0000000..619d917
--- /dev/null
+++ b/src/basic-example/CMakeLists.txt
@@ -0,0 +1,5 @@
+
+add_executable(basic-example main.cpp logic.cpp)
+ecm_add_test(test.cpp logic.cpp
+ TEST_NAME basic-example-test
+ LINK_LIBRARIES Qt6::Test)
diff --git a/src/basic-example/logic.cpp b/src/basic-example/logic.cpp
new file mode 100644
index 0000000..8ca9e58
--- /dev/null
+++ b/src/basic-example/logic.cpp
@@ -0,0 +1,25 @@
+
+#include "logic.hpp"
+
+// core logic
+[[nodiscard]] Model update(Model cur, IncrementAction)
+{
+ return Model{cur.value + 1};
+};
+
+[[nodiscard]] Model update(Model cur, DecrementAction)
+{
+ return Model{cur.value - 1};
+};
+
+[[nodiscard]] Model update([[maybe_unused]] Model cur, ResetAction a)
+{
+ return Model{a.value};
+};
+
+[[nodiscard]] Model update(Model cur, Action a)
+{
+ return std::visit([&](const auto &action) {
+ return update(cur, action);
+ }, a);
+}
diff --git a/src/basic-example/logic.hpp b/src/basic-example/logic.hpp
new file mode 100644
index 0000000..8d096a2
--- /dev/null
+++ b/src/basic-example/logic.hpp
@@ -0,0 +1,27 @@
+
+#include <variant>
+#include <optional>
+
+// the application state
+struct Model
+{
+ int value{0};
+};
+
+// actions
+struct IncrementAction {};
+
+struct DecrementAction {};
+
+struct ResetAction
+{
+ int value;
+};
+
+using Action = std::variant<
+ IncrementAction,
+ DecrementAction,
+ ResetAction
+ >;
+
+[[nodiscard]] Model update(Model cur, Action a);
diff --git a/src/basic-example/main.cpp b/src/basic-example/main.cpp
new file mode 100644
index 0000000..649375a
--- /dev/null
+++ b/src/basic-example/main.cpp
@@ -0,0 +1,41 @@
+
+#include <iostream>
+#include "logic.hpp"
+
+void show(Model m)
+{
+ std::cout << "value=" << m.value << std::endl;
+}
+
+std::optional<Action> ask()
+{
+ std::cout << "?" << std::endl;
+ std::string a;
+ std::cin >> a;
+ if (a == "i") {
+ return IncrementAction{};
+ } else if (a == "d") {
+ return DecrementAction{};
+ } else {
+ try {
+ return ResetAction{stoi(a)};
+ } catch (...) {
+ return std::nullopt;
+ }
+ }
+}
+
+int main()
+{
+ Model model;
+ std::optional<Action> a;
+ while (true) {
+ show(model);
+ a = ask();
+ if (a) {
+ model = update(std::move(model), a.value());
+ } else {
+ std::cout << "unrecognized action" << std::endl;
+ }
+ }
+}
diff --git a/src/basic-example/test.cpp b/src/basic-example/test.cpp
new file mode 100644
index 0000000..dd60597
--- /dev/null
+++ b/src/basic-example/test.cpp
@@ -0,0 +1,22 @@
+
+#include <QtTest>
+#include "logic.hpp"
+
+class BasicExampleTest : public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void testLogic();
+};
+
+void BasicExampleTest::testLogic()
+{
+ QCOMPARE((update(Model{42}, IncrementAction{}).value), 43);
+ QCOMPARE((update(Model{42}, DecrementAction{}).value), 41);
+ QCOMPARE((update(Model{42}, ResetAction{1}).value), 1);
+}
+
+QTEST_MAIN(BasicExampleTest)
+
+#include "test.moc"

File Metadata

Mime Type
text/x-diff
Expires
Mon, Nov 25, 3:17 AM (1 d, 9 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
39557
Default Alt Text
(6 KB)

Event Timeline