Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F113329
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
6 KB
Referenced Files
None
Subscribers
None
View Options
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
Details
Attached
Mime Type
text/x-diff
Expires
Mon, Nov 25, 3:17 AM (1 d, 11 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
39557
Default Alt Text
(6 KB)
Attached To
Mode
R29 lager-qt-demo
Attached
Detach File
Event Timeline
Log In to Comment