diff --git a/CMakeLists.txt b/CMakeLists.txt index 2f1493f..c949c78 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,7 +30,10 @@ include(ECMPoQmTools) kde_enable_exceptions() -find_package(Qt6 ${QT_MIN_VERSION} REQUIRED NO_MODULE COMPONENTS Core Core5Compat Quick Test Gui Qml QuickControls2 Widgets Sql QmlImportScanner WebEngineQuick) +set(CXXQT_QTCOMPONENTS Core Gui Qml QuickControls2 QuickTest Test) + set(CXXQT_QTCOMPONENTS ${CXXQT_QTCOMPONENTS} QmlImportScanner) + +find_package(Qt6 ${QT_MIN_VERSION} REQUIRED NO_MODULE COMPONENTS Core Core5Compat Quick Test Gui Qml QuickControls2 Widgets Sql QmlImportScanner WebEngineQuick Multimedia) find_package(KF6 ${KF_MIN_VERSION} REQUIRED COMPONENTS Kirigami CoreAddons I18n) # find_package(KF6FileMetaData ${KF6_MIN_VERSION}) @@ -57,45 +60,70 @@ set_package_properties(Ytdlp PROPERTIES TYPE RUNTIME) # execute_process(COMMAND ${XDG-DESKTOP-MENU_EXECUTABLE} install --novender librepresenter.desktop) get_target_property(QMAKE Qt::qmake IMPORTED_LOCATION) -find_package(Corrosion QUIET) -if(NOT Corrosion_FOUND) +find_package(CxxQt QUIET) +if(NOT CxxQt_FOUND) include(FetchContent) FetchContent_Declare( - Corrosion - GIT_REPOSITORY https://github.com/corrosion-rs/corrosion.git - GIT_TAG v0.3.0 + CxxQt + GIT_REPOSITORY https://github.com/kdab/cxx-qt-cmake.git + GIT_TAG v0.7.0 ) - FetchContent_MakeAvailable(Corrosion) + FetchContent_MakeAvailable(CxxQt) endif() -add_subdirectory(src) +# find_package(Corrosion QUIET) +# if(NOT Corrosion_FOUND) +# include(FetchContent) +# FetchContent_Declare( +# Corrosion +# GIT_REPOSITORY https://github.com/corrosion-rs/corrosion.git +# GIT_TAG v0.3.0 +# ) + +# FetchContent_MakeAvailable(Corrosion) +# endif() + + set(CRATE liblumina) + +# CXX-Qt (using Corrosion) creates a CMake target with the same name as the crate. +cxx_qt_import_crate( + MANIFEST_PATH Cargo.toml + CRATES liblumina + QT_MODULES Qt::Core Qt::Gui Qt::Qml Qt::QuickControls2 Qt::WebEngineQuick) + +cxx_qt_import_qml_module(liblumina_qml_module + URI "org.presenter" + SOURCE_CRATE liblumina) + + # Corrosion creates a CMake target with the same name as the crate. -corrosion_import_crate(MANIFEST_PATH Cargo.toml CRATES ${CRATE}) +# corrosion_import_crate(MANIFEST_PATH Cargo.toml CRATES ${CRATE}) # The Rust library's build script needs to be told where to output the # generated headers so CMake can find them. To do this, tell Corrosion # to set the CXXQT_EXPORT_DIR environment variable when calling `cargo build`. # Also, set the QMAKE environment variable to ensure the Rust library uses # the same installation of Qt as CMake. -set(CXXQT_EXPORT_DIR "${CMAKE_CURRENT_BINARY_DIR}/cxxqt") -corrosion_set_env_vars(${CRATE} - "CXXQT_EXPORT_DIR=${CXXQT_EXPORT_DIR}" - "QMAKE=${QMAKE}" -) +# set(CXXQT_EXPORT_DIR "${CMAKE_CURRENT_BINARY_DIR}/cxxqt") +# corrosion_set_env_vars(${CRATE} +# "CXXQT_EXPORT_DIR=${CXXQT_EXPORT_DIR}" +# "QMAKE=${QMAKE}" +# ) -add_library(${APP_NAME}_lib INTERFACE) +add_subdirectory(src) # Include the headers generated by the Rust library's build script. Each # crate gets its own subdirectory under CXXQT_EXPORT_DIR. This allows you # to include headers generated by multiple crates without risk of one crate # overwriting another's files. -target_include_directories(${APP_NAME}_lib INTERFACE "${CXXQT_EXPORT_DIR}/${CRATE}") +target_include_directories(liblumina INTERFACE "${CXXQT_EXPORT_DIR}/${CRATE}") + # Link the Rust INTERFACE library target to Qt. Do this on the library target # rather than the main executable. This way, CMake targets besides the main # executable which link the Rust library, for example tests, will also link Qt. -target_link_libraries(${APP_NAME}_lib INTERFACE +target_link_libraries(liblumina INTERFACE "$" Qt6::Quick Qt6::Qml @@ -104,6 +132,7 @@ target_link_libraries(${APP_NAME}_lib INTERFACE Qt6::Widgets Qt6::Sql Qt6::WebEngineQuick + Qt6::Multimedia KF6::Kirigami KF6::I18n KF6::CoreAddons @@ -114,8 +143,9 @@ target_link_libraries(${APP_NAME}_lib INTERFACE crypto ) + # Link to the Rust library -target_link_libraries(${APP_NAME} PRIVATE ${APP_NAME}_lib) +target_link_libraries(${APP_NAME} PRIVATE liblumina) # If we are using a statically linked Qt then we need to import any qml plugins qt_import_qml_plugins(${APP_NAME}) diff --git a/Cargo.lock b/Cargo.lock index 7f30019..2662187 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" @@ -338,6 +338,19 @@ dependencies = [ "serde", ] +[[package]] +name = "blah" +version = "0.1.0" +dependencies = [ + "cxx", + "cxx-qt", + "cxx-qt-build", + "cxx-qt-lib", + "cxx-qt-lib-extras", + "markdown", + "qt-build-utils", +] + [[package]] name = "block" version = "0.1.6" @@ -490,32 +503,6 @@ dependencies = [ "unicode-segmentation", ] -[[package]] -name = "core" -version = "0.1.0" -dependencies = [ - "color-eyre", - "configparser", - "dirs", - "fastrand 2.1.1", - "obws", - "pretty_assertions", - "quote", - "reqwest", - "rfd", - "serde", - "serde_derive", - "serde_json", - "sqlx", - "tar", - "time", - "tokio", - "tracing", - "tracing-subscriber", - "youtube_dl", - "zstd", -] - [[package]] name = "core-foundation" version = "0.9.4" @@ -593,21 +580,6 @@ dependencies = [ "link-cplusplus", ] -[[package]] -name = "cxx-build" -version = "1.0.128" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77953e99f01508f89f55c494bfa867171ef3a6c8cea03d26975368f2121a5c1" -dependencies = [ - "cc", - "codespan-reporting", - "once_cell", - "proc-macro2", - "quote", - "scratch", - "syn 2.0.77", -] - [[package]] name = "cxx-gen" version = "0.7.128" @@ -622,38 +594,41 @@ dependencies = [ [[package]] name = "cxx-qt" -version = "0.6.1" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08aa6cda7588b6d17c563b0d2fadc060d4204d04908c0f359ae288857091218d" +checksum = "208ad6c4feac92f221fde00796f317b049ba1892b97be0d60ca177d0d3469fc5" dependencies = [ "cxx", + "cxx-qt-build", "cxx-qt-macro", + "qt-build-utils", "static_assertions", + "thiserror", ] [[package]] name = "cxx-qt-build" -version = "0.6.1" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e097b99f49792922a72a8ca35d9391762e48e63363d6998255be1f2ca1edf69" +checksum = "15f80e109aa68795486c70c302f6c2d921f00028b3b62038a4601efb5c585c1c" dependencies = [ "cc", "codespan-reporting", - "convert_case", "cxx-gen", "cxx-qt-gen", - "cxx-qt-lib-headers", "proc-macro2", "qt-build-utils", "quote", + "serde", + "serde_json", "version_check", ] [[package]] name = "cxx-qt-gen" -version = "0.6.1" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ede7c73dbfbcc234d8826919e257830c1789db2cac586546a87d2a82e3cbe5d5" +checksum = "dc17d95ca9cc60c2f91f804a4e0ba6a3e1b8ed338c207a1bd8d176133e2fd05d" dependencies = [ "clang-format", "convert_case", @@ -665,27 +640,33 @@ dependencies = [ [[package]] name = "cxx-qt-lib" -version = "0.6.1" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "002f1a6119bcb7dfec67eb7c0803a7b1d595dc54610559faeac35133f22a5880" +checksum = "f116c5d982bbf3be707acf97f566802c30454d52ca319c745ed39a04834e8bc6" dependencies = [ "cxx", - "cxx-build", - "cxx-qt-lib-headers", + "cxx-qt", + "cxx-qt-build", "qt-build-utils", ] [[package]] -name = "cxx-qt-lib-headers" -version = "0.6.1" +name = "cxx-qt-lib-extras" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9abdeab6b77cfc5a53b724f3f62a37bcb5ac1423cccc2dba4c134f4273440b8c" +checksum = "bbe1aaed6391a224d746e314104f33b4031138291ebd368170a2109b6008ace2" +dependencies = [ + "cxx", + "cxx-qt", + "cxx-qt-build", + "cxx-qt-lib", +] [[package]] name = "cxx-qt-macro" -version = "0.6.1" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "699e8a668c03b03419b084960d72eed253632bb16349b33fd0a0c893b61b664c" +checksum = "58a4fe02c0604eda28c605792f5ba0d0251b4947f8f0fc43e55b61c06b2b8ec6" dependencies = [ "cxx-qt-gen", "proc-macro2", @@ -1432,9 +1413,9 @@ checksum = "187674a687eed5fe42285b40c6291f9a01517d415fad1c3cbc6a9f778af7fcd4" [[package]] name = "itertools" -version = "0.11.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" dependencies = [ "either", ] @@ -1490,6 +1471,7 @@ dependencies = [ "cxx-qt-lib", "dirs", "fastrand 2.1.1", + "lumina_core", "obws", "qt-build-utils", "quote", @@ -1573,6 +1555,32 @@ version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +[[package]] +name = "lumina_core" +version = "0.1.0" +dependencies = [ + "color-eyre", + "configparser", + "dirs", + "fastrand 2.1.1", + "obws", + "pretty_assertions", + "quote", + "reqwest", + "rfd", + "serde", + "serde_derive", + "serde_json", + "sqlx", + "tar", + "time", + "tokio", + "tracing", + "tracing-subscriber", + "youtube_dl", + "zstd", +] + [[package]] name = "malloc_buf" version = "0.0.6" @@ -1582,6 +1590,15 @@ dependencies = [ "libc", ] +[[package]] +name = "markdown" +version = "1.0.0-alpha.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21e27d6220ce21f80ce5c4201f23a37c6f1ad037c72c9d1ff215c2919605a5d6" +dependencies = [ + "unicode-id", +] + [[package]] name = "matchers" version = "0.1.0" @@ -2092,9 +2109,9 @@ dependencies = [ [[package]] name = "qt-build-utils" -version = "0.6.1" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59c828fe2434dad34dd0c30a4ba037509b61dad92a55baf0dc42699e6aa2f10" +checksum = "efb239fdd8c036fabb95364320041ef68197cd4ab971bb3b4ca3ea0b7b93d12c" dependencies = [ "cc", "thiserror", @@ -2364,12 +2381,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "scratch" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3cf7c11c38cb994f3d40e8a8cde3bbd1f72a435e4c49e85d6553d8312306152" - [[package]] name = "security-framework" version = "2.11.1" @@ -3212,6 +3223,12 @@ version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" +[[package]] +name = "unicode-id" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10103c57044730945224467c09f71a4db0071c123a0648cc3e818913bde6b561" + [[package]] name = "unicode-ident" version = "1.0.12" @@ -3304,9 +3321,9 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "versions" -version = "5.0.1" +version = "6.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c73a36bc44e3039f51fbee93e39f41225f6b17b380eb70cc2aab942df06b34dd" +checksum = "f25d498b63d1fdb376b4250f39ab3a5ee8d103957346abacd911e2d8b612c139" dependencies = [ "itertools", "nom", diff --git a/Cargo.toml b/Cargo.toml index 5d20d7c..bec892a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["src/rust/core"] +members = [ "smdview/blah","src/rust/core"] [package] name = "liblumina" @@ -21,13 +21,14 @@ path = "src/rust/lib.rs" # path = "src/rust/main.rs" [dependencies] +lumina_core = { path = "src/rust/core" } configparser = "3.0.2" serde = "1.0.152" serde_derive = "1.0.152" quote = "1.0.27" cxx = "1.0.83" -cxx-qt = "0.6.1" -cxx-qt-lib = "0.6.1" +cxx-qt = "0.7.1" +cxx-qt-lib = { version = "0.7.1", features = [ "qt_full" ] } # home = "0.5.4" dirs = "5.0.0" # libsqlite3-sys = { version = ">=0.17.2", features = ["bundled"] } @@ -50,8 +51,8 @@ color-eyre = "0.6.3" # cxx-qt-build generates C++ code from the `#[cxx_qt::bridge]` module # and compiles it together with the Rust static library [build-dependencies] -cxx-qt-build = { version = "0.6.1", features = [ "link_qt_object_files" ] } -qt-build-utils = "0.6.1" +cxx-qt-build = { version = "0.7.1", features = [ "link_qt_object_files" ] } +qt-build-utils = "0.7.1" # [dependencies.confy] # features = ["yaml_conf"] diff --git a/TODO.org b/TODO.org index 183bc66..83c5a11 100644 --- a/TODO.org +++ b/TODO.org @@ -4,7 +4,8 @@ :CATEGORY: dev :END: -* Tasks [63%] [55/86] +* Tasks [63%] [55/87] +** TODO [#A] REWRITE FOR ALL RUST AND BUILD WITH CARGO ** TODO [#A] Plugin architecture with steel or some scheme as an extension language ** TODO [#A] Server client architecture ** TODO [#A] Organize and layout structure of rust code :maintenance: diff --git a/build.rs b/build.rs index 8d4b0d6..84c1284 100644 --- a/build.rs +++ b/build.rs @@ -1,3 +1,5 @@ +use std::{env, path::PathBuf}; + use cxx_qt_build::{CxxQtBuilder, QmlModule}; fn main() { @@ -17,6 +19,37 @@ fn main() { .file("src/rust/obs.rs") .build(); + // let mut kde_include_dir = String::from("/usr/include/"); + // let mut kde_lib_dir = String::from("/usr/lib/x86_64-linux-gnu/"); + + // if let Ok(mut include_dir) = env::var("CMAKE_INCLUDE_PATH") { + // println!("{}", include_dir); + // if let Some(include_dir) = + // include_dir.split(":").find(|s| s.contains("ki18n")) + // { + // kde_include_dir = include_dir.to_owned(); + // } + // } else { + // println!( + // "cargo:warning=KDE_INCLUDEDIR is not defined, used default value: {}", + // kde_include_dir + // ); + // } + // if let Ok(lib_dir) = env::var("KDE_LIBDIR") { + // kde_lib_dir = lib_dir; + // } else { + // println!( + // "cargo:warning=KDE_LIBDIR is not defined, used default value: {}", + // kde_lib_dir + // ); + // } + + // let ki18n_include_path = PathBuf::from(kde_include_dir) + // .canonicalize() + // .expect("Cannot get canonical path of KDE_INCLUDEDIR") + // .join("KF6") + // .join("KI18n"); + // CxxQtBuilder::new() // // Link Qt's Network library // // - Qt Core is always linked @@ -27,13 +60,64 @@ fn main() { // // .qt_module("Kirigami") // // .qt_module("WebEngineQuick") // .qt_module("Network") + // // .qt_module("Quick") + // // .qt_module("Test") + // // .qt_module("WebEngineQuick") // // .qt_module("I18n") // // .qt_module("CoreAddons") // .qml_module(QmlModule { // uri: "com.cochrun.xyz", - // rust_files: &["src/rust/settings.rs"], - // qml_files: &["src/qml/main.qml"], + // rust_files: &[ + // "src/rust/settings.rs", + // "src/rust/service_item_model.rs", + // "src/rust/file_helper.rs", + // "src/rust/slide_model.rs", + // "src/rust/slide_object.rs", + // "src/rust/ytdl.rs", + // "src/rust/utils.rs", + // "src/rust/obs.rs", + // "src/rust/video_model.rs", + // "src/rust/image_model.rs", + // "src/rust/presentation_model.rs", + // // "src/rust/songs/song_model.rs", + // // "src/rust/songs/song_editor.rs", + // ], + // qrc_files: &["src/resources.qrc"], + // qml_files: &[ + // "src/qml/main.qml", + // "src/qml/presenter/LeftDock.qml", + // "src/qml/presenter/ServiceList.qml", + // "src/qml/presenter/MainWindow.qml", + // "src/qml/presenter/Library.qml", + // "src/qml/presenter/LibraryItem.qml", + // "src/qml/presenter/Header.qml", + // "src/qml/presenter/Actions.qml", + // "src/qml/presenter/PanelItem.qml", + // "src/qml/presenter/SongEditor.qml", + // "src/qml/presenter/VideoEditor.qml", + // "src/qml/presenter/ImageEditor.qml", + // "src/qml/presenter/PresentationEditor.qml", + // "src/qml/presenter/SlideEditor.qml", + // "src/qml/presenter/Slide.qml", + // "src/qml/presenter/SlidesListView.qml", + // "src/qml/presenter/SongEditorSlideList.qml", + // "src/qml/presenter/DragHandle.qml", + // "src/qml/presenter/Presentation.qml", + // "src/qml/presenter/PresentationWindow.qml", + // "src/qml/presenter/PreviewSlideListDelegate.qml", + // "src/qml/presenter/PreviewSlide.qml", + // "src/qml/presenter/Settings.qml", + // "src/qml/presenter/RangedSlider.qml", + // "src/qml/presenter/NewVideo.qml", + // "src/qml/presenter/TextBackground.qml", + // "src/qml/presenter/TextBox.qml", + // "src/qml/presenter/LoadingSpinner.qml", + // ], // ..Default::default() // }) + // .cc_builder(|cc| { + // cc.include("cpp"); + // cc.include(format!("{}", ki18n_include_path.display())); + // }) // .build(); } diff --git a/flake.lock b/flake.lock index 6b82aa5..efd6a95 100644 --- a/flake.lock +++ b/flake.lock @@ -1,15 +1,56 @@ { "nodes": { + "fenix": { + "inputs": { + "nixpkgs": "nixpkgs", + "rust-analyzer-src": "rust-analyzer-src" + }, + "locked": { + "lastModified": 1753252982, + "narHash": "sha256-brrpvP+4GRXLHjvnDr1j1/yA4117hzs6t9IT60JuSI8=", + "owner": "nix-community", + "repo": "fenix", + "rev": "8546562a84feb5370ce57493277b6f2c3cbdc432", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "fenix", + "type": "github" + } + }, + "fenix_2": { + "inputs": { + "nixpkgs": [ + "naersk", + "nixpkgs" + ], + "rust-analyzer-src": "rust-analyzer-src_2" + }, + "locked": { + "lastModified": 1752475459, + "narHash": "sha256-z6QEu4ZFuHiqdOPbYss4/Q8B0BFhacR8ts6jO/F/aOU=", + "owner": "nix-community", + "repo": "fenix", + "rev": "bf0d6f70f4c9a9cf8845f992105652173f4b617f", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "fenix", + "type": "github" + } + }, "flake-utils": { "inputs": { "systems": "systems" }, "locked": { - "lastModified": 1710146030, - "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", "owner": "numtide", "repo": "flake-utils", - "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", "type": "github" }, "original": { @@ -20,14 +61,15 @@ }, "naersk": { "inputs": { - "nixpkgs": "nixpkgs" + "fenix": "fenix_2", + "nixpkgs": "nixpkgs_2" }, "locked": { - "lastModified": 1721727458, - "narHash": "sha256-r/xppY958gmZ4oTfLiHN0ZGuQ+RSTijDblVgVLFi1mw=", + "lastModified": 1752689277, + "narHash": "sha256-uldUBFkZe/E7qbvxa3mH1ItrWZyT6w1dBKJQF/3ZSsc=", "owner": "nix-community", "repo": "naersk", - "rev": "3fb418eaf352498f6b6c30592e3beb63df42ef11", + "rev": "0e72363d0938b0208d6c646d10649164c43f4d64", "type": "github" }, "original": { @@ -38,23 +80,43 @@ }, "nixpkgs": { "locked": { - "lastModified": 0, - "narHash": "sha256-+yj+xgsfZaErbfYM3T+QvEE2hU7UuE+Jf0fJCJ8uPS0=", - "path": "/nix/store/6inj491lsap4ia7mmvn2gbh53jb27zq0-source", - "type": "path" + "lastModified": 1752950548, + "narHash": "sha256-NS6BLD0lxOrnCiEOcvQCDVPXafX1/ek1dfJHX1nUIzc=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "c87b95e25065c028d31a94f06a62927d18763fdf", + "type": "github" }, "original": { - "id": "nixpkgs", - "type": "indirect" + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" } }, "nixpkgs_2": { "locked": { - "lastModified": 1725634671, - "narHash": "sha256-v3rIhsJBOMLR8e/RNWxr828tB+WywYIoajrZKFM+0Gg=", + "lastModified": 1752077645, + "narHash": "sha256-HM791ZQtXV93xtCY+ZxG1REzhQenSQO020cu6rHtAPk=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "be9e214982e20b8310878ac2baa063a961c1bdf6", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_3": { + "locked": { + "lastModified": 1752950548, + "narHash": "sha256-NS6BLD0lxOrnCiEOcvQCDVPXafX1/ek1dfJHX1nUIzc=", "owner": "nixos", "repo": "nixpkgs", - "rev": "574d1eac1c200690e27b8eb4e24887f8df7ac27c", + "rev": "c87b95e25065c028d31a94f06a62927d18763fdf", "type": "github" }, "original": { @@ -66,9 +128,44 @@ }, "root": { "inputs": { + "fenix": "fenix", "flake-utils": "flake-utils", "naersk": "naersk", - "nixpkgs": "nixpkgs_2" + "nixpkgs": "nixpkgs_3" + } + }, + "rust-analyzer-src": { + "flake": false, + "locked": { + "lastModified": 1753204114, + "narHash": "sha256-xH8EIod+Hwog4P9OwX9hdtk6Nqr54M0tzMI71yGNOYI=", + "owner": "rust-lang", + "repo": "rust-analyzer", + "rev": "b40fce3ccdc5f94453c6aca4da8b64174a03a5ad", + "type": "github" + }, + "original": { + "owner": "rust-lang", + "ref": "nightly", + "repo": "rust-analyzer", + "type": "github" + } + }, + "rust-analyzer-src_2": { + "flake": false, + "locked": { + "lastModified": 1752428706, + "narHash": "sha256-EJcdxw3aXfP8Ex1Nm3s0awyH9egQvB2Gu+QEnJn2Sfg=", + "owner": "rust-lang", + "repo": "rust-analyzer", + "rev": "591e3b7624be97e4443ea7b5542c191311aa141d", + "type": "github" + }, + "original": { + "owner": "rust-lang", + "ref": "nightly", + "repo": "rust-analyzer", + "type": "github" } }, "systems": { diff --git a/flake.nix b/flake.nix index 9f64ab6..954cbdc 100644 --- a/flake.nix +++ b/flake.nix @@ -6,77 +6,121 @@ nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; naersk.url = "github:nix-community/naersk"; flake-utils.url = "github:numtide/flake-utils"; + fenix.url = "github:nix-community/fenix"; # nixpkgs.follows = "cargo2nix/nixpkgs"; }; - outputs = inputs: with inputs; - flake-utils.lib.eachDefaultSystem - (system: - let - pkgs = import nixpkgs { - inherit system; - # overlays = [cargo2nix.overlays.default]; - }; - naersk' = pkgs.callPackage naersk {}; - # src = ./.; - # rustPkgs = pkgs.rustBuilder.makePackageSet { - # rustVersion = "1.61.0"; - # packageFun = import ./Cargo.nix; - # }; - # The workspace defines a development shell with all of the dependencies - # and environment settings necessary for a regular `cargo build`. - # Passes through all arguments to pkgs.mkShell for adding supplemental - # dependencies. - # workspaceShell = rustPkgs.workspaceShell { - # packages = with pkgs; [ - # gcc - # stdenv - # bintools - # gnumake - # gdb - # qtcreator - # cmake - # extra-cmake-modules - # pkg-config - # libsForQt5.wrapQtAppsHook - # makeWrapper + outputs = inputs: + with inputs; + flake-utils.lib.eachDefaultSystem (system: + let + pkgs = import nixpkgs { + inherit system; + overlays = [ fenix.overlays.default ]; + # overlays = [cargo2nix.overlays.default]; + }; + naersk' = pkgs.callPackage naersk { }; - # clang-tools - # clang - # libclang - # qt5.qtbase - # qt5.qttools - # qt5.qtquickcontrols2 - # qt5.qtx11extras - # qt5.qtmultimedia - # qt5.qtwayland - # qt5.qtwebengine - # libsForQt5.kirigami2 - # libsForQt5.qqc2-desktop-style - # libsForQt5.karchive - # mpv - # ffmpeg_6-full - # # Rust tools - # clippy - # rustc - # cargo - # rustfmt - # rust-analyzer - # corrosion - # ]; - # # shellHook = '' - # # export PS1="\033[0;31m☠dev-shell☠ $ \033[0m" - # # ''; - # }; + nbi = with pkgs; [ + # ffmpeg + alejandra + (pkgs.fenix.stable.withComponents [ + "cargo" + "clippy" + "rust-src" + "rustc" + "rustfmt" + ]) + rust-analyzer + ]; - in rec - { - # packages = { - # crate = (rustPkgs.workspace.libre-presenter { }).bin; - # default = packages.crate; - # }; - devShell = import ./shell.nix { inherit pkgs; }; - defaultPackage = pkgs.libsForQt5.callPackage ./default.nix { }; - } - ); + bi = with pkgs; [ + gcc + stdenv + gnumake + gdb + qtcreator + cmake + kdePackages.extra-cmake-modules + pkg-config + qt6.wrapQtAppsHook + makeWrapper + + openssl.dev + openssl.out + + clang-tools + clang + libclang + # libwebp + # clang-format + qt6.full + qt6.qttools + qt6.qtbase + # qt6.qtquickcontrols2 + # qt6.qtx11extras + qt6.qtmultimedia + qt6.qtwayland + qt6.qtwebengine + qt6.qtimageformats + kdePackages.kirigami + # kdePackages.kfilemetadata + # libsForQt5.breeze-icons + # libsForQt5.breeze-qt5 + kdePackages.qqc2-desktop-style + # libsForQt5.kirigami-addons + # libsForQt5.ki18n + kdePackages.kcoreaddons + # libsForQt5.kguiaddons + # libsForQt5.kconfig + + # podofo + mpv + kdePackages.mpvqt + ffmpeg-full + # yt-dlp + + # Rust tools + just + clippy + rustc + cargo + rustfmt + rust-analyzer + sqlx-cli + cargo-watch + corrosion + ]; + + in rec { + # packages = { + # crate = (rustPkgs.workspace.libre-presenter { }).bin; + # default = packages.crate; + # }; + devShell = pkgs.mkShell { + nativeBuildInputs = nbi; + buildInputs = bi; + + RUST_BACKTRACE = "1"; + LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib"; + CMAKE_C_COMPILER = "${pkgs.gcc}/bin/gcc"; + CMAKE_CXX_COMPILER = "${pkgs.gcc}/bin/g++"; + CARGO_PROFILE_RELEASE_BUILD_OVERRIDE_DEBUG = true; + # KDE_INCLUDEDIR = "${pkgs.kdePackages.kirigami.dev}/include"; + # KDE_QMLDIR = "${pkgs.kdePackages.kirigami.dev}/lib/qt-6/qml/org/kde/kirigami/"; + + # This creates the proper qt env so that plugins are found right. + shellHook = '' + setQtEnvironment=$(mktemp --suffix .setQtEnvironment.sh) + echo "shellHook: setQtEnvironment = $setQtEnvironment" + makeQtWrapper "/bin/sh" "$setQtEnvironment" "''${qtWrapperArgs[@]}" + sed "/^exec/d" -i "$setQtEnvironment" + source "$setQtEnvironment" + ''; + DATABASE_URL = + "sqlite:///home/chris/.local/share/lumina/library-db.sqlite3"; + }; + # devShell = import ./shell.nix { inherit pkgs; }; + # defaultPackage = pkgs.libsForQt5.callPackage ./default.nix { }; + }); } diff --git a/justfile b/justfile index a332b6d..f98c2c4 100644 --- a/justfile +++ b/justfile @@ -15,7 +15,7 @@ test: RUST_LOG=debug cargo test --benches --tests --all-features -- --nocapture testcore: - RUST_LOG=debug cargo test -p core --benches --tests --all-features -- --nocapture + RUST_LOG=debug cargo test -p lumina_core --benches --tests --all-features -- --nocapture alias b := build alias r := run diff --git a/smdview/blah/Cargo.toml b/smdview/blah/Cargo.toml new file mode 100644 index 0000000..1a4e1ef --- /dev/null +++ b/smdview/blah/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "blah" +version = "0.1.0" +edition = "2024" + +[dependencies] +cxx = "1.0.122" +cxx-qt = "0.7.1" +cxx-qt-lib = { version = "0.7.1", features = [ "qt_full" ] } +cxx-qt-lib-extras = "0.7.1" +markdown = "=1.0.0-alpha.17" + +[build-dependencies] +# The link_qt_object_files feature is required for statically linking Qt 6. +cxx-qt-build = { version = "0.7.1", features = [ "link_qt_object_files" ] } +qt-build-utils = "0.7.1" + diff --git a/smdview/blah/build.rs b/smdview/blah/build.rs new file mode 100644 index 0000000..c67706d --- /dev/null +++ b/smdview/blah/build.rs @@ -0,0 +1,12 @@ +use cxx_qt_build::{CxxQtBuilder, QmlModule}; + +fn main() { + CxxQtBuilder::new() + .qml_module(QmlModule { + uri: "org.kde.simplemdviewer", + qml_files: &["src/qml/Main.qml"], + rust_files: &["src/main.rs"], + ..Default::default() + }) + .build(); +} diff --git a/smdview/blah/src/main.rs b/smdview/blah/src/main.rs new file mode 100644 index 0000000..c79e764 --- /dev/null +++ b/smdview/blah/src/main.rs @@ -0,0 +1,42 @@ +#[cxx_qt::bridge] +mod ffi { + extern "RustQt" { + #[qobject] + type DummyQObject = super::DummyRustStruct; + } +} + +#[derive(Default)] +pub struct DummyRustStruct; + +use cxx_qt_lib::{ + QGuiApplication, QQmlApplicationEngine, QQuickStyle, QString, + QUrl, +}; +use cxx_qt_lib_extras::QApplication; +use std::env; + +fn main() { + let mut app = QApplication::new(); + + // To associate the executable to the installed desktop file + QGuiApplication::set_desktop_file_name(&QString::from( + "org.kde.simplemdviewer", + )); + + // To ensure the style is set correctly + if env::var("QT_QUICK_CONTROLS_STYLE").is_err() { + QQuickStyle::set_style(&QString::from("org.kde.desktop")); + } + + let mut engine = QQmlApplicationEngine::new(); + if let Some(engine) = engine.as_mut() { + engine.load(&QUrl::from( + "qrc:/qt/qml/org/kde/simplemdviewer/src/qml/Main.qml", + )); + } + + if let Some(app) = app.as_mut() { + app.exec(); + } +} diff --git a/smdview/blah/src/qml/main.qml b/smdview/blah/src/qml/main.qml new file mode 100644 index 0000000..077a11e --- /dev/null +++ b/smdview/blah/src/qml/main.qml @@ -0,0 +1,71 @@ +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls as Controls +import org.kde.kirigami as Kirigami + +Kirigami.ApplicationWindow { + id: root + + title: "Simple Markdown Viewer in Rust 🦀" + + minimumWidth: Kirigami.Units.gridUnit * 20 + minimumHeight: Kirigami.Units.gridUnit * 20 + width: minimumWidth + height: minimumHeight + + pageStack.initialPage: initPage + + Component { + id: initPage + + Kirigami.Page { + title: "Markdown Viewer" + + ColumnLayout { + anchors { + top: parent.top + left: parent.left + right: parent.right + } + Controls.TextArea { + id: sourceArea + + placeholderText: "Write some Markdown code here" + wrapMode: Text.WrapAnywhere + Layout.fillWidth: true + Layout.minimumHeight: Kirigami.Units.gridUnit * 5 + } + + RowLayout { + Layout.fillWidth: true + + Controls.Button { + text: "Format" + + onClicked: formattedText.text = sourceArea.text + } + + Controls.Button { + text: "Clear" + + onClicked: { + sourceArea.text = "" + formattedText.text = "" + } + } + } + + Controls.Label { + id: formattedText + + textFormat: Text.RichText + wrapMode: Text.WordWrap + text: sourceArea.text + + Layout.fillWidth: true + Layout.minimumHeight: Kirigami.Units.gridUnit * 5 + } + } + } + } +} diff --git a/src/main.cpp b/src/main.cpp index 656ad12..4914492 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,49 +1,49 @@ -#include -#include +// #include +#include #include -#include -#include -#include -#include -#include -#include +#include +#include +#include +// #include +// #include +// #include #include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +// #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include +// #include +#include +#include +#include +#include +#include +#include +#include // #include // #include "cpp/mpv/mpvitem.h" // #include "cpp/mpv/mpvproperties.h" // RUST -#include "cxx-qt-gen/file_helper.cxxqt.h" -#include "cxx-qt-gen/slide_object.cxxqt.h" -#include "cxx-qt-gen/slide_model.cxxqt.h" -#include "cxx-qt-gen/service_item_model.cxxqt.h" -#include "cxx-qt-gen/settings.cxxqt.h" -#include "cxx-qt-gen/ytdl.cxxqt.h" -#include "cxx-qt-gen/presentation_model.cxxqt.h" -#include "cxx-qt-gen/song_model.cxxqt.h" -#include "cxx-qt-gen/video_model.cxxqt.h" -#include "cxx-qt-gen/image_model.cxxqt.h" -#include "cxx-qt-gen/utils.cxxqt.h" -#include "cxx-qt-gen/song_editor.cxxqt.h" -#include "cxx-qt-gen/obs.cxxqt.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include static QWindow *windowFromEngine(QQmlApplicationEngine *engine) { @@ -60,22 +60,22 @@ int main(int argc, char *argv[]) QGuiApplication::setWindowIcon(QIcon::fromTheme(QStringLiteral("video-display"))); QtWebEngineQuick::initialize(); QGuiApplication app(argc, argv); - KLocalizedString::setApplicationDomain("lumina"); - KAboutData about; - about.setComponentName(QStringLiteral("lumina")); - about.setDisplayName(i18n("lumina")); - about.setVersion(QByteArray("0.1")); - about.setShortDescription(i18n("A churchpresentation app build with KDE tech.")); - about.setLicense(KAboutLicense::GPL_V3); + // KLocalizedString::setApplicationDomain("lumina"); + // KAboutData about; + // about.setComponentName(QStringLiteral("lumina")); + // about.setDisplayName(i18n("lumina")); + // about.setVersion(QByteArray("0.1")); + // about.setShortDescription(i18n("A churchpresentation app build with KDE tech.")); + // about.setLicense(KAboutLicense::GPL_V3); // overwrite default-generated values of organizationDomain & desktopFileName - about.setOrganizationDomain("tfcconnection.org"); - about.setDesktopFileName(QStringLiteral("org.tfcconneciton.lumina")); + // about.setOrganizationDomain("tfcconnection.org"); + // about.setDesktopFileName(QStringLiteral("org.tfcconneciton.lumina")); QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGL); // set the application metadata - KAboutData::setApplicationData(about); + // KAboutData::setApplicationData(about); QCoreApplication::setOrganizationName(QStringLiteral("lumina")); QCoreApplication::setOrganizationDomain(QStringLiteral("tfcconnection.org")); QCoreApplication::setApplicationName(QStringLiteral("lumina")); @@ -93,7 +93,7 @@ int main(int argc, char *argv[]) // qDebug() << QQuickStyle::availableStyles(); qDebug() << QIcon::themeName(); - qDebug() << QApplication::platformName(); + // qDebug() << QApplication::platformName(); //Need to instantiate our slide QScopedPointer slideModel(new SlideModel); @@ -108,6 +108,7 @@ int main(int argc, char *argv[]) settings->setup(); QQuickView *PresWindow = new QQuickView; + PresWindow->setSource(QUrl(QStringLiteral("qrc:qml/presenter/PresentationWindow.qml"))); qDebug() << PresWindow; qDebug() << PresWindow->isVisible(); @@ -169,9 +170,9 @@ int main(int argc, char *argv[]) qmlRegisterSingletonInstance("org.presenter", 1, 0, "SlideModel", slideModel.get()); qmlRegisterSingletonInstance("org.presenter", 1, 0, "Utils", utils); qmlRegisterSingletonInstance("org.presenter", 1, 0, "SlideObject", slideobject.get()); - qmlRegisterSingletonInstance("org.presenter", 1, 0, "PresWindow", PresWindow); qmlRegisterSingletonInstance("org.presenter", 1, 0, "RSettings", settings); qmlRegisterSingletonInstance("org.presenter", 1, 0, "ObsModel", obsModel.get()); + qmlRegisterSingletonInstance("org.presenter", 1, 0, "PresWindow", PresWindow); // This is the same slideobject, however to enusre that the PresWindow can have it // we need to set it as a separate context so that it can change it's slides too. @@ -184,7 +185,7 @@ int main(int argc, char *argv[]) QQmlApplicationEngine engine; qDebug() << app.allWindows(); - engine.rootContext()->setContextObject(new KLocalizedContext(&engine)); + // engine.rootContext()->setContextObject(new KLocalizedContext(&engine)); engine.load(QUrl(QStringLiteral("qrc:qml/main.qml"))); qDebug() << "Engine loaded"; diff --git a/src/qml/presenter/Library.qml b/src/qml/presenter/Library.qml index ffc5196..f8e648f 100644 --- a/src/qml/presenter/Library.qml +++ b/src/qml/presenter/Library.qml @@ -252,6 +252,7 @@ Item { } function isDragFile(item) { + console.log(item); console.log(item.toString()); var extension = item.toString().split('.').pop(); var valid = false; diff --git a/src/qml/presenter/MainWindow.qml b/src/qml/presenter/MainWindow.qml index 24ad653..64c1c17 100644 --- a/src/qml/presenter/MainWindow.qml +++ b/src/qml/presenter/MainWindow.qml @@ -188,6 +188,10 @@ Controls.Page { songModel: songModel } + Presenter.PresentationWindow { + id: presWindow + } + Connections { target: ServiceItemModel function onSaveProgressChanged() { @@ -352,19 +356,20 @@ Controls.Page { function present(present) { if (present) { - PresWindow.showFullScreen(); - PresWindow.setSource("qrc:qml/presenter/PresentationWindow.qml") - console.log(PresWindow); + /* presWindow.slideObj = SlideObject; */ + presWindow.showFullScreen(); + /* presWindow.setSource("qrc:qml/presenter/PresentationWindow.qml") */ + console.log(presWindow); /* presWinLoader.active = true; */ } else { - PresWindow.close(); + presWindow.close(); /* presWinLoader.active = false; */ } } - function closeAll() { PresWindow.close() } + function closeAll() { presWindow.close() } function changeVidPos(pos) { presentation.slide.seek(pos); diff --git a/src/qml/presenter/Presentation.qml b/src/qml/presenter/Presentation.qml index bdd92c2..c082aa2 100644 --- a/src/qml/presenter/Presentation.qml +++ b/src/qml/presenter/Presentation.qml @@ -479,6 +479,11 @@ FocusScope { previewSlide.stopVideo() } + function playVideo() { + /* showPassiveNotification("Stopping Video") */ + previewSlide.playVideo() + } + function nextSlideAction() { keyHandler.forceActiveFocus(); SlideModel.next() diff --git a/src/qml/presenter/PresentationWindow.qml b/src/qml/presenter/PresentationWindow.qml index b93f576..6187f03 100644 --- a/src/qml/presenter/PresentationWindow.qml +++ b/src/qml/presenter/PresentationWindow.qml @@ -6,13 +6,13 @@ import org.kde.kirigami 2.13 as Kirigami import "./" as Presenter import org.presenter 1.0 -Item { +Window { id: presentationWindow property Item slide: presentationSlide - /* property var slideObj */ + /* property var SlideObject: SlideObject; */ property var pWin - anchors.fill: parent + /* anchors.fill: parent */ /* title: "presentation-window" */ /* height: maximumHeight */ @@ -25,7 +25,7 @@ Item { /* onClosing: { */ /* presentationSlide.stopVideo(); */ - /* SlideObj.pause(); */ + /* SlideObject.pause(); */ /* presentationSlide.stopAudio(); */ /* presenting = false; */ /* } */ @@ -34,7 +34,7 @@ Item { target: PresWindow function onClosing() { presentationSlide.stopVideo(); - SlideObj.pause(); + SlideObject.pause(); presentationSlide.stopAudio(); presenting = false; } @@ -48,25 +48,25 @@ Item { Presenter.Slide { id: presentationSlide anchors.fill: parent - imageSource: SlideObj.html ? "" : SlideObj.imageBackground - webSource: SlideObj.html ? SlideObj.imageBackground : "" - htmlVisible: SlideObj.html - videoSource: presentationWindow.visible ? SlideObj.videoBackground : "" - audioSource: SlideObj.audio - text: SlideObj.text - chosenFont: SlideObj.font - textSize: SlideObj.fontSize - pdfIndex: SlideObj.slideIndex - itemType: SlideObj.ty - vidLoop: SlideObj.looping - vidStartTime: SlideObj.videoStartTime - vidEndTime: SlideObj.videoEndTime + imageSource: SlideObject.html ? "" : SlideObject.imageBackground + webSource: SlideObject.html ? SlideObject.imageBackground : "" + htmlVisible: SlideObject.html + videoSource: presentationWindow.visible ? SlideObject.videoBackground : "" + audioSource: SlideObject.audio + text: SlideObject.text + chosenFont: SlideObject.font + textSize: SlideObject.fontSize + pdfIndex: SlideObject.slideIndex + itemType: SlideObject.ty + vidLoop: SlideObject.looping + vidStartTime: SlideObject.videoStartTime + vidEndTime: SlideObject.videoEndTime } Connections { - target: SlideObj + target: SlideObject function onVideoBackgroundChanged() { - if (SlideObj.videoBackground === "") + if (SlideObject.videoBackground === "") stopVideo(); else { loadVideo(); @@ -74,12 +74,12 @@ Item { } } function onIsPlayingChanged() { - if(SlideObj.isPlaying) + if(SlideObject.isPlaying) presentationSlide.playVideo(); pauseVideo(); } function onLoopingChanged() { - if(SlideObj.looping) + if(SlideObject.looping) presentationSlide.loopVideo(); } function onAudioChanged() { diff --git a/src/qml/presenter/Settings.qml b/src/qml/presenter/Settings.qml index e2217ab..9c630f6 100644 --- a/src/qml/presenter/Settings.qml +++ b/src/qml/presenter/Settings.qml @@ -21,6 +21,7 @@ Kirigami.OverlaySheet { Kirigami.FormLayout { implicitHeight: Kirigami.Units.gridUnit * 30 + implicitWidth: Kirigami.Units.gridUnit * 30 Controls.ComboBox { id: screenSelectionField Kirigami.FormData.label: i18nc("@label:textbox", "Presentation Screen:") diff --git a/src/rust/core/Cargo.toml b/src/rust/core/Cargo.toml index 1be6c9d..d6abb8b 100644 --- a/src/rust/core/Cargo.toml +++ b/src/rust/core/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "lumina-core" +name = "lumina_core" version = "0.1.0" edition = "2021" authors = [ diff --git a/src/rust/core/file.rs b/src/rust/core/file.rs index 8a9aa9e..ef5163d 100644 --- a/src/rust/core/file.rs +++ b/src/rust/core/file.rs @@ -1,82 +1,133 @@ +use crate::{ + images::{get_image_from_db, Image}, + kinds::ServiceItemKind, + model::get_db, + presentations::{ + get_presentation_from_db, PresKind, Presentation, + }, + service_items::ServiceItem, + slides::Background, + songs::{get_song_from_db, Song}, + videos::{get_video_from_db, Video}, +}; +use color_eyre::eyre::{eyre, Context, Result}; +use serde_json::Value; +use sqlx::{query, query_as, FromRow, SqliteConnection}; +use std::{ + fs::{self, File}, + iter, + path::{Path, PathBuf}, +}; use tar::{Archive, Builder}; use tracing::error; use zstd::Encoder; -use std::{fs::{self, File}, future::Future, iter, path::{Path, PathBuf}}; -use color_eyre::eyre::{eyre, Result}; -use serde_json::Value; -use sqlx::{query, query_as, FromRow, SqliteConnection}; -use crate::{images::{get_image_from_db, Image}, kinds::ServiceItemKind, model::get_db, presentations::{get_presentation_from_db, PresKind, Presentation}, service_items::ServiceItem, slides::Background, songs::{get_song_from_db, Song}, videos::{get_video_from_db, Video}}; -pub async fn save(list: Vec, path: impl AsRef) -> Result<()> { +pub async fn save( + list: Vec, + path: impl AsRef, +) -> Result<()> { let path = path.as_ref(); let save_file = File::create(path)?; let mut db = get_db().await; let json = process_service_items(&list, &mut db).await?; - let archive = store_service_items(&list, &mut db, &save_file, &json).await?; + let archive = + store_service_items(&list, &mut db, &save_file, &json) + .await?; Ok(()) } -async fn store_service_items(items: &Vec, db: &mut SqliteConnection, save_file: &File, json: &Value) -> Result<()> { +async fn store_service_items( + items: &Vec, + db: &mut SqliteConnection, + save_file: &File, + json: &Value, +) -> Result<()> { let encoder = Encoder::new(save_file, 3).unwrap(); let mut tar = Builder::new(encoder); let mut temp_dir = dirs::data_dir().unwrap(); temp_dir.push("lumina"); let mut s: String = - iter::repeat_with(fastrand::alphanumeric) - .take(5) - .collect(); + iter::repeat_with(fastrand::alphanumeric).take(5).collect(); s.insert_str(0, "temp_"); temp_dir.push(s); fs::create_dir_all(&temp_dir)?; let service_file = temp_dir.join("serviceitems.json"); fs::File::create(&service_file)?; - match fs::File::options().read(true).write(true).open(service_file) { + match fs::File::options() + .read(true) + .write(true) + .open(service_file) + { Ok(f) => { serde_json::to_writer_pretty(f, json)?; - }, - Err(e) => error!("There were problems making a file i guess: {e}"), + } + Err(e) => { + error!("There were problems making a file i guess: {e}") + } }; for item in items { let background; let audio: Option; match item.kind { ServiceItemKind::Song => { - let song = get_song_from_db(item.database_id, db).await?; + let song = + get_song_from_db(item.database_id, db).await?; background = song.background; audio = song.audio; - }, + } ServiceItemKind::Image => { - let image = get_image_from_db(item.database_id, db).await?; + let image = + get_image_from_db(item.database_id, db).await?; background = Some(Background::try_from(image.path)?); audio = None; - }, + } ServiceItemKind::Video => { - let video = get_video_from_db(item.database_id, db).await?; + let video = + get_video_from_db(item.database_id, db).await?; background = Some(Background::try_from(video.path)?); audio = None; - }, + } ServiceItemKind::Presentation(_) => { - let presentation = get_presentation_from_db(item.database_id, db).await?; - background = Some(Background::try_from(presentation.path)?); + let presentation = + get_presentation_from_db(item.database_id, db) + .await?; + background = + Some(Background::try_from(presentation.path)?); audio = None; - }, + } ServiceItemKind::Content => { todo!() - }, + } }; if let Some(file) = audio { - let audio_file = temp_dir.join(file.file_name().expect("Audio file couldn't be added to temp_dir")); - match fs::File::create(&audio_file) { - Ok(_) => Ok(fs::copy(file, &audio_file)?), - Err(e) => Err(eyre!("Couldn't create audio file: {e}")), - }?; + let audio_file = + temp_dir.join(file.file_name().expect( + "Audio file couldn't be added to temp_dir", + )); + if let Ok(file) = file.strip_prefix("file://") { + fs::File::create(&audio_file) + .wrap_err("Couldn't create audio file")?; + fs::copy(file, audio_file).wrap_err("Audio file could not be copied, the source file doesn't exist not be found"); + } else { + fs::File::create(&audio_file) + .wrap_err("Couldn't create audio file")?; + fs::copy(file, audio_file).wrap_err("Audio file could not be copied, the source file doesn't exist not be found"); + } }; if let Some(file) = background { - let background_file = temp_dir.join(file.path.file_name().expect("Background file couldn't be added to temp_dir")); - match fs::File::create(&background_file) { - Ok(_) => Ok(fs::copy(file.path, &background_file)?), - Err(e) => Err(eyre!("Couldn't create background file: {e}")), - }?; + let background_file = + temp_dir.join(file.path.file_name().expect( + "Background file couldn't be added to temp_dir", + )); + if let Ok(file) = file.path.strip_prefix("file://") { + fs::File::create(&background_file) + .wrap_err("Couldn't create background file")?; + fs::copy(file, background_file).wrap_err("Background file could not be copied, the source file doesn't exist not be found"); + } else { + fs::File::create(&background_file) + .wrap_err("Couldn't create background file")?; + fs::copy(file.path, background_file).wrap_err("Background file could not be copied, the source file doesn't exist not be found"); + } } } Ok(()) @@ -86,66 +137,96 @@ async fn clear_temp_dir(temp_dir: &Path) -> Result<()> { todo!() } -async fn process_service_items(items: &Vec, db: &mut SqliteConnection) -> Result { +async fn process_service_items( + items: &Vec, + db: &mut SqliteConnection, +) -> Result { let mut values: Vec = vec![]; for item in items { match item.kind { ServiceItemKind::Song => { - let value = process_song(item.database_id, db).await?; + let value = + process_song(item.database_id, db).await?; values.push(value); - }, + } ServiceItemKind::Image => { - let value = process_image(item.database_id, db).await?; + let value = + process_image(item.database_id, db).await?; values.push(value); - }, + } ServiceItemKind::Video => { - let value = process_video(item.database_id, db).await?; + let value = + process_video(item.database_id, db).await?; values.push(value); - }, + } ServiceItemKind::Presentation(_) => { - let value = process_presentation(item.database_id, db).await?; + let value = + process_presentation(item.database_id, db) + .await?; values.push(value); - }, + } ServiceItemKind::Content => { todo!() - }, + } } } let json = Value::from(values); Ok(json) } -async fn process_song(database_id: i32, db: &mut SqliteConnection) -> Result { +async fn process_song( + database_id: i32, + db: &mut SqliteConnection, +) -> Result { let song = get_song_from_db(database_id, db).await?; let song_json = serde_json::to_value(&song)?; let kind_json = serde_json::to_value(ServiceItemKind::Song)?; - let json = serde_json::json!({"item": song_json, "kind": kind_json}); + let json = + serde_json::json!({"item": song_json, "kind": kind_json}); Ok(json) } -async fn process_image(database_id: i32, db: &mut SqliteConnection) -> Result { +async fn process_image( + database_id: i32, + db: &mut SqliteConnection, +) -> Result { let image = get_image_from_db(database_id, db).await?; let image_json = serde_json::to_value(&image)?; let kind_json = serde_json::to_value(ServiceItemKind::Image)?; - let json = serde_json::json!({"item": image_json, "kind": kind_json}); + let json = + serde_json::json!({"item": image_json, "kind": kind_json}); Ok(json) } -async fn process_video(database_id: i32, db: &mut SqliteConnection) -> Result { +async fn process_video( + database_id: i32, + db: &mut SqliteConnection, +) -> Result { let video = get_video_from_db(database_id, db).await?; let video_json = serde_json::to_value(&video)?; let kind_json = serde_json::to_value(ServiceItemKind::Video)?; - let json = serde_json::json!({"item": video_json, "kind": kind_json}); + let json = + serde_json::json!({"item": video_json, "kind": kind_json}); Ok(json) } -async fn process_presentation(database_id: i32, db: &mut SqliteConnection) -> Result { - let presentation = get_presentation_from_db(database_id, db).await?; +async fn process_presentation( + database_id: i32, + db: &mut SqliteConnection, +) -> Result { + let presentation = + get_presentation_from_db(database_id, db).await?; let presentation_json = serde_json::to_value(&presentation)?; let kind_json = match presentation.kind { - PresKind::Html => serde_json::to_value(ServiceItemKind::Presentation(PresKind::Html))?, - PresKind::Pdf => serde_json::to_value(ServiceItemKind::Presentation(PresKind::Pdf))?, - PresKind::Generic => serde_json::to_value(ServiceItemKind::Presentation(PresKind::Generic))?, + PresKind::Html => serde_json::to_value( + ServiceItemKind::Presentation(PresKind::Html), + )?, + PresKind::Pdf => serde_json::to_value( + ServiceItemKind::Presentation(PresKind::Pdf), + )?, + PresKind::Generic => serde_json::to_value( + ServiceItemKind::Presentation(PresKind::Generic), + )?, }; let json = serde_json::json!({"item": presentation_json, "kind": kind_json}); Ok(json) @@ -155,11 +236,11 @@ async fn process_presentation(database_id: i32, db: &mut SqliteConnection) -> Re mod test { use std::path::PathBuf; - use fs::canonicalize; - use sqlx::Connection; - use pretty_assertions::{assert_eq, assert_ne}; - use tracing::debug; use super::*; + use fs::canonicalize; + use pretty_assertions::assert_eq; + use sqlx::Connection; + use tracing::debug; async fn get_db() -> SqliteConnection { let mut data = dirs::data_local_dir().unwrap(); @@ -167,9 +248,7 @@ mod test { data.push("library-db.sqlite3"); let mut db_url = String::from("sqlite://"); db_url.push_str(data.to_str().unwrap()); - SqliteConnection::connect(&db_url) - .await - .expect("problems") + SqliteConnection::connect(&db_url).await.expect("problems") } #[tokio::test(flavor = "current_thread")] @@ -239,7 +318,8 @@ mod test { async fn test_process_presentation() { let mut db = get_db().await; let result = process_presentation(54, &mut db).await; - let json_presentation_file = PathBuf::from("./test/test_presentation.json"); + let json_presentation_file = + PathBuf::from("./test/test_presentation.json"); if let Ok(path) = canonicalize(json_presentation_file) { debug!(file = ?&path); if let Ok(s) = fs::read_to_string(path) { @@ -252,7 +332,9 @@ mod test { panic!("String wasn't read from file"); } } else { - panic!("Cannot find absolute path to test_presentation.json"); + panic!( + "Cannot find absolute path to test_presentation.json" + ); } } @@ -281,7 +363,8 @@ mod test { async fn test_service_items() { let mut db = get_db().await; let items = get_items(); - let json_item_file = PathBuf::from("./test/test_service_items.json"); + let json_item_file = + PathBuf::from("./test/test_service_items.json"); let result = process_service_items(&items, &mut db).await; if let Ok(path) = canonicalize(json_item_file) { if let Ok(s) = fs::read_to_string(path) { @@ -305,16 +388,23 @@ mod test { #[tokio::test] async fn test_store() { - let path = PathBuf::from("/home/chris/dev/lumina/src/rust/core/test.pres"); - let save_file = match File::create(path) { + let path = PathBuf::from( + "/home/chris/dev/lumina/src/rust/core/test.pres", + ); + let save_file = match File::create(path) { Ok(f) => f, Err(e) => panic!("Couldn't create save_file: {e}"), }; let mut db = get_db().await; let list = get_items(); - if let Ok(json) = process_service_items(&list, &mut db).await { + if let Ok(json) = process_service_items(&list, &mut db).await + { println!("{:?}", json); - match store_service_items(&list, &mut db, &save_file, &json).await { + match store_service_items( + &list, &mut db, &save_file, &json, + ) + .await + { Ok(_) => assert!(true), Err(e) => panic!("There was an error: {e}"), } diff --git a/src/rust/core/images.rs b/src/rust/core/images.rs index 96a74a0..9e7d832 100644 --- a/src/rust/core/images.rs +++ b/src/rust/core/images.rs @@ -5,7 +5,9 @@ use sqlx::{query_as, SqliteConnection}; use std::path::PathBuf; use tracing::error; -#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] +#[derive( + Clone, Debug, Default, PartialEq, Serialize, Deserialize, +)] pub struct Image { pub id: i32, pub title: String, @@ -16,7 +18,7 @@ impl Model { pub fn load_from_db(&mut self) { let rt = tokio::runtime::Runtime::new().unwrap(); rt.block_on(async { - let result = query_as!(Image, r#"SELECT title as "title!", filePath as "path!", id as "id: i32" from images"#).fetch_all(&mut self.db).await; + let result = query_as!(Image, r#"SELECT title as "title!", file_path as "path!", id as "id: i32" from images"#).fetch_all(&mut self.db).await; match result { Ok(v) => { for image in v.into_iter() { @@ -29,9 +31,11 @@ impl Model { } } - -pub async fn get_image_from_db(database_id: i32, db: &mut SqliteConnection) -> Result { - Ok(query_as!(Image, r#"SELECT title as "title!", filePath as "path!", id as "id: i32" from images where id = ?"#, database_id).fetch_one(db).await?) +pub async fn get_image_from_db( + database_id: i32, + db: &mut SqliteConnection, +) -> Result { + Ok(query_as!(Image, r#"SELECT title as "title!", file_path as "path!", id as "id: i32" from images where id = ?"#, database_id).fetch_one(db).await?) } #[cfg(test)] @@ -67,7 +71,10 @@ mod test { let new_image = test_image("A newer image".into()); match result { Ok(_) => { - assert_eq!(&image, image_model.find(|i| i.id == 0).unwrap()); + assert_eq!( + &image, + image_model.find(|i| i.id == 0).unwrap() + ); assert_ne!( &new_image, image_model.find(|i| i.id == 0).unwrap() diff --git a/src/rust/core/kinds.rs b/src/rust/core/kinds.rs index 3660b78..7ffa001 100644 --- a/src/rust/core/kinds.rs +++ b/src/rust/core/kinds.rs @@ -4,7 +4,9 @@ use serde::{Deserialize, Serialize}; use crate::presentations::PresKind; -#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +#[derive( + Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, +)] pub enum ServiceItemKind { #[default] Song, diff --git a/src/rust/core/lib.rs b/src/rust/core/lib.rs index bb3db24..10bb770 100644 --- a/src/rust/core/lib.rs +++ b/src/rust/core/lib.rs @@ -1,3 +1,4 @@ +pub mod file; pub mod images; pub mod kinds; pub mod model; @@ -6,4 +7,3 @@ pub mod service_items; pub mod slides; pub mod songs; pub mod videos; -pub mod file; diff --git a/src/rust/core/mod.rs b/src/rust/core/mod.rs new file mode 100644 index 0000000..e359776 --- /dev/null +++ b/src/rust/core/mod.rs @@ -0,0 +1 @@ +mod service_items; diff --git a/src/rust/core/model.rs b/src/rust/core/model.rs index bb94d12..da3f695 100644 --- a/src/rust/core/model.rs +++ b/src/rust/core/model.rs @@ -36,8 +36,7 @@ impl Model { Ok(()) } - pub fn get_item(&self, index: i32) -> Option<&T> - { + pub fn get_item(&self, index: i32) -> Option<&T> { self.items.get(index as usize) } @@ -60,10 +59,8 @@ impl Default for Model { items: vec![], db: { let rt = tokio::runtime::Runtime::new().unwrap(); - rt.block_on(async { - get_db().await - }) - } + rt.block_on(async { get_db().await }) + }, } } } @@ -74,9 +71,7 @@ pub async fn get_db() -> SqliteConnection { data.push("library-db.sqlite3"); let mut db_url = String::from("sqlite://"); db_url.push_str(data.to_str().unwrap()); - SqliteConnection::connect(&db_url) - .await - .expect("problems") + SqliteConnection::connect(&db_url).await.expect("problems") } pub trait Modeling { diff --git a/src/rust/core/presentations.rs b/src/rust/core/presentations.rs index 8f9b33b..41768b9 100644 --- a/src/rust/core/presentations.rs +++ b/src/rust/core/presentations.rs @@ -1,12 +1,16 @@ -use std::path::PathBuf; use color_eyre::eyre::Result; use serde::{Deserialize, Serialize}; -use sqlx::{prelude::FromRow, query, sqlite::SqliteRow, Row, SqliteConnection}; +use sqlx::{ + prelude::FromRow, query, sqlite::SqliteRow, Row, SqliteConnection, +}; +use std::path::PathBuf; use tracing::error; use crate::model::Model; -#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +#[derive( + Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, +)] pub enum PresKind { Html, #[default] @@ -14,7 +18,9 @@ pub enum PresKind { Generic, } -#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +#[derive( + Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, +)] pub struct Presentation { pub id: i32, pub title: String, @@ -57,7 +63,7 @@ impl Model { pub fn load_from_db(&mut self) { let rt = tokio::runtime::Runtime::new().unwrap(); rt.block_on(async { - let result = query!(r#"SELECT id as "id: i32", title, filePath as "path", html from presentations"#).fetch_all(&mut self.db).await; + let result = query!(r#"SELECT id as "id: i32", title, file_path as "path", html from presentations"#).fetch_all(&mut self.db).await; match result { Ok(v) => { for presentation in v.into_iter() { @@ -79,8 +85,11 @@ impl Model { } } -pub async fn get_presentation_from_db(database_id: i32, db: &mut SqliteConnection) -> Result { - let row = query(r#"SELECT id as "id: i32", title, filePath as "path", html from presentations where id = $1"#).bind(database_id).fetch_one(db).await?; +pub async fn get_presentation_from_db( + database_id: i32, + db: &mut SqliteConnection, +) -> Result { + let row = query(r#"SELECT id as "id: i32", title, file_path as "path", html from presentations where id = $1"#).bind(database_id).fetch_one(db).await?; Ok(Presentation::from_row(&row)?) } @@ -111,7 +120,9 @@ mod test { let mut presentation_model: Model = Model::default(); presentation_model.load_from_db(); - if let Some(presentation) = presentation_model.find(|p| p.id == 54) { + if let Some(presentation) = + presentation_model.find(|p| p.id == 54) + { let test_presentation = test_presentation(); assert_eq!(&test_presentation, presentation); } else { diff --git a/src/rust/core/service_items.rs b/src/rust/core/service_items.rs index 8224aff..b49c015 100644 --- a/src/rust/core/service_items.rs +++ b/src/rust/core/service_items.rs @@ -15,7 +15,7 @@ pub struct ServiceItem { } #[derive(Debug, Default, PartialEq)] -struct ServiceItemModel { +pub struct ServiceItemModel { items: Vec, } @@ -52,7 +52,9 @@ impl From<&Image> for ServiceItem { impl From<&Presentation> for ServiceItem { fn from(presentation: &Presentation) -> Self { Self { - kind: ServiceItemKind::Presentation(presentation.kind.clone()), + kind: ServiceItemKind::Presentation( + presentation.kind.clone(), + ), database_id: presentation.id, ..Default::default() } @@ -60,7 +62,10 @@ impl From<&Presentation> for ServiceItem { } impl ServiceItemModel { - fn add_item(&mut self, item: impl Into) -> Result<()> { + fn add_item( + &mut self, + item: impl Into, + ) -> Result<()> { let service_item: ServiceItem = item.into(); self.items.push(service_item); Ok(()) @@ -94,7 +99,6 @@ mod test { } } - #[test] pub fn test_service_item() { let song = test_song(); @@ -104,10 +108,16 @@ mod test { let mut service_model = ServiceItemModel::default(); match service_model.add_item(&song) { Ok(_) => { - assert_eq!(ServiceItemKind::Song, service_model.items[0].kind); - assert_eq!(ServiceItemKind::Presentation(PresKind::Html), pres_item.kind); + assert_eq!( + ServiceItemKind::Song, + service_model.items[0].kind + ); + assert_eq!( + ServiceItemKind::Presentation(PresKind::Html), + pres_item.kind + ); assert_eq!(service_item, service_model.items[0]); - }, + } Err(e) => panic!("Problem adding item: {:?}", e), } } diff --git a/src/rust/core/slides.rs b/src/rust/core/slides.rs index 726ffae..3de576a 100644 --- a/src/rust/core/slides.rs +++ b/src/rust/core/slides.rs @@ -1,4 +1,8 @@ -use std::{error::Error, fmt::Display, path::{Path, PathBuf}}; +use std::{ + error::Error, + fmt::Display, + path::{Path, PathBuf}, +}; use color_eyre::eyre::{eyre, Result}; use serde::{Deserialize, Serialize}; @@ -10,7 +14,9 @@ use crate::{ presentations::Presentation, songs::Song, videos::Video, }; -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] +#[derive( + Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize, +)] pub enum TextAlignment { TopLeft, TopCenter, @@ -24,7 +30,9 @@ pub enum TextAlignment { BottomRight, } -#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] +#[derive( + Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, +)] pub struct Background { pub path: PathBuf, pub kind: BackgroundKind, @@ -36,7 +44,7 @@ impl TryFrom for Background { let value = value.trim_start_matches("file://"); let path = PathBuf::from(value); if !path.exists() { - return Err(ParseError::DoesNotExist) + return Err(ParseError::DoesNotExist); } let extension = value.rsplit_once('.').unwrap_or_default(); match extension.1 { @@ -106,11 +114,15 @@ impl DatabaseError for ParseError { todo!() } - fn as_error_mut(&mut self) -> &mut (dyn Error + Send + Sync + 'static) { + fn as_error_mut( + &mut self, + ) -> &mut (dyn Error + Send + Sync + 'static) { todo!() } - fn into_error(self: Box) -> Box { + fn into_error( + self: Box, + ) -> Box { todo!() } @@ -128,15 +140,15 @@ impl Display for ParseError { Self::NonBackgroundFile => { "The file is not a recognized image or video type" } - Self::DoesNotExist => { - "This file doesn't exist" - } + Self::DoesNotExist => "This file doesn't exist", }; write!(f, "Error: {message}") } } -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] +#[derive( + Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize, +)] pub enum BackgroundKind { #[default] Image, diff --git a/src/rust/core/songs.rs b/src/rust/core/songs.rs index d259563..b1c955f 100644 --- a/src/rust/core/songs.rs +++ b/src/rust/core/songs.rs @@ -2,7 +2,10 @@ use std::{collections::HashMap, path::PathBuf}; use color_eyre::eyre::{eyre, Context, Result}; use serde::{Deserialize, Serialize}; -use sqlx::{query, query_as, sqlite::SqliteRow, FromRow, Row, SqliteConnection}; +use sqlx::{ + query, query_as, sqlite::SqliteRow, FromRow, Row, + SqliteConnection, +}; use tracing::{debug, error}; use crate::{ @@ -10,7 +13,9 @@ use crate::{ slides::{Background, TextAlignment}, }; -#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] +#[derive( + Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, +)] pub struct Song { pub id: i32, pub title: String, @@ -56,17 +61,24 @@ impl FromRow<'_, SqliteRow> for Song { text_alignment: Some({ let horizontal_alignment: String = row.try_get(3)?; let vertical_alignment: String = row.try_get(4)?; - match (horizontal_alignment.to_lowercase().as_str(), vertical_alignment.to_lowercase().as_str()) { + match ( + horizontal_alignment.to_lowercase().as_str(), + vertical_alignment.to_lowercase().as_str(), + ) { ("left", "top") => TextAlignment::TopLeft, ("left", "center") => TextAlignment::MiddleLeft, ("left", "bottom") => TextAlignment::BottomLeft, ("center", "top") => TextAlignment::TopCenter, - ("center", "center") => TextAlignment::MiddleCenter, - ("center", "bottom") => TextAlignment::BottomCenter, + ("center", "center") => { + TextAlignment::MiddleCenter + } + ("center", "bottom") => { + TextAlignment::BottomCenter + } ("right", "top") => TextAlignment::TopRight, ("right", "center") => TextAlignment::MiddleRight, ("right", "bottom") => TextAlignment::BottomRight, - _ => TextAlignment::MiddleCenter + _ => TextAlignment::MiddleCenter, } }), font: row.try_get(6)?, @@ -75,19 +87,20 @@ impl FromRow<'_, SqliteRow> for Song { } } - -pub async fn get_song_from_db(index: i32, db: &mut SqliteConnection) -> Result { - let row = query(r#"SELECT vorder as "verse_order!", fontSize as "font_size!: i32", backgroundType as "background_type!", horizontalTextAlignment as "horizontal_text_alignment!", verticalTextAlignment as "vertical_text_alignment!", title as "title!", font as "font!", background as "background!", lyrics as "lyrics!", ccli as "ccli!", author as "author!", audio as "audio!", id as "id: i32" from songs where id = $1"#).bind(index).fetch_one(db).await?; +pub async fn get_song_from_db( + index: i32, + db: &mut SqliteConnection, +) -> Result { + let row = query(r#"SELECT verse_order as "verse_order!", font_size as "font_size!: i32", background_type as "background_type!", horizontal_text_alignment as "horizontal_text_alignment!", vertical_text_alignment as "vertical_text_alignment!", title as "title!", font as "font!", background as "background!", lyrics as "lyrics!", ccli as "ccli!", author as "author!", audio as "audio!", id as "id: i32" from songs where id = $1"#).bind(index).fetch_one(db).await?; Ok(Song::from_row(&row)?) } - impl Model { pub fn load_from_db(&mut self) { // static DATABASE_URL: &str = "sqlite:///home/chris/.local/share/lumina/library-db.sqlite3"; let rt = tokio::runtime::Runtime::new().unwrap(); rt.block_on(async { - let result = query(r#"SELECT vorder as "verse_order!", fontSize as "font_size!: i32", backgroundType as "background_type!", horizontalTextAlignment as "horizontal_text_alignment!", verticalTextAlignment as "vertical_text_alignment!", title as "title!", font as "font!", background as "background!", lyrics as "lyrics!", ccli as "ccli!", author as "author!", audio as "audio!", id as "id: i32" from songs"#).fetch_all(&mut self.db).await; + let result = query(r#"SELECT verse_order as "verse_order!", font_size as "font_size!: i32", background_type as "background_type!", horizontal_text_alignment as "horizontal_text_alignment!", vertical_text_alignment as "vertical_text_alignment!", title as "title!", font as "font!", background as "background!", lyrics as "lyrics!", ccli as "ccli!", author as "author!", audio as "audio!", id as "id: i32" from songs"#).fetch_all(&mut self.db).await; match result { Ok(s) => { for song in s.into_iter() { diff --git a/src/rust/core/test.pres b/src/rust/core/test.pres new file mode 100644 index 0000000..e69de29 diff --git a/src/rust/core/videos.rs b/src/rust/core/videos.rs index c7cdb3d..0329916 100644 --- a/src/rust/core/videos.rs +++ b/src/rust/core/videos.rs @@ -5,7 +5,9 @@ use sqlx::{query_as, SqliteConnection}; use std::path::PathBuf; use tracing::error; -#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] +#[derive( + Clone, Debug, Default, PartialEq, Serialize, Deserialize, +)] pub struct Video { pub id: i32, pub title: String, @@ -19,7 +21,7 @@ impl Model