From 7f95eb95b3d0435587a17faf45a5070bbb1775f0 Mon Sep 17 00:00:00 2001 From: Ali Rizvi Date: Tue, 2 Dec 2025 01:26:31 -0500 Subject: [PATCH 01/10] feat!: update to NixOS 26.05 DankMaterialShell's author decided to be dumb and destroyed their git commit history, and apparently also broke the way their app and flake works too.. fun. I've gone ahead and fixed it, though this entire situation has made me want to quit using this shell entirely. On other, more pleasant news, we are now on the latest NixOS release. The most noticable change should be the update to GNOME 49. I don't visually notice much but I did notice an airplane mode button, cute. Lix 2.94 also released and removed the `no-url-literals` experimental feature, since it has now become a default. This is yet again another breaking change from upstream, since having this in your experimental features list will error out. Fair enough I suppose, though it does become a little annoying when one setup doesn't translate to the other cleanly. Overall a good change though, so I suppose there's that to be happy about. --- flake.lock | 95 +- flake.nix | 9 - .../home-manager/my/programs/dms/config.nix | 6 +- modules/shared/nix/config/nixos/default.nix | 2 +- nix/inputs/dms-cli/.github/workflows/pr.yml | 30 - .../dms-cli/.github/workflows/release.yml | 169 -- .../.github/workflows/update-vendor-hash.yml | 89 - nix/inputs/dms-cli/.gitignore | 36 - nix/inputs/dms-cli/.mockery.yml | 42 - nix/inputs/dms-cli/CONTRIBUTING_DISTRO.md | 211 -- nix/inputs/dms-cli/LICENSE | 21 - nix/inputs/dms-cli/Makefile | 157 -- nix/inputs/dms-cli/README.md | 274 -- nix/inputs/dms-cli/assets/dank.svg | 46 - nix/inputs/dms-cli/assets/danklogo.svg | 113 - .../dms-cli/cmd/dms/commands_brightness.go | 303 --- nix/inputs/dms-cli/cmd/dms/commands_common.go | 370 --- nix/inputs/dms-cli/cmd/dms/commands_dank16.go | 90 - .../dms-cli/cmd/dms/commands_features.go | 487 ---- .../dms-cli/cmd/dms/commands_greeter.go | 500 ---- .../dms-cli/cmd/dms/commands_hyprland.go | 45 - nix/inputs/dms-cli/cmd/dms/commands_root.go | 42 - nix/inputs/dms-cli/cmd/dms/main.go | 44 - nix/inputs/dms-cli/cmd/dms/main_distro.go | 41 - nix/inputs/dms-cli/cmd/dms/shell.go | 500 ---- nix/inputs/dms-cli/cmd/dms/ui.go | 53 - nix/inputs/dms-cli/cmd/dms/utils.go | 14 - nix/inputs/dms-cli/flake.lock | 27 - nix/inputs/dms-cli/flake.nix | 61 - nix/inputs/dms-cli/go.mod | 65 - nix/inputs/dms-cli/go.sum | 141 - nix/inputs/dms-cli/install.sh | 86 - .../dms-cli/internal/config/deployer.go | 574 ---- .../dms-cli/internal/config/deployer_test.go | 673 ----- nix/inputs/dms-cli/internal/config/dms.go | 46 - .../config/embedded/alacritty-theme.toml | 31 - .../internal/config/embedded/alacritty.toml | 37 - .../config/embedded/ghostty-colors.conf | 21 - .../internal/config/embedded/ghostty.conf | 51 - .../internal/config/embedded/hyprland.conf | 287 -- .../internal/config/embedded/kitty-tabs.conf | 24 - .../internal/config/embedded/kitty-theme.conf | 24 - .../internal/config/embedded/kitty.conf | 37 - .../dms-cli/internal/config/embedded/niri.kdl | 418 --- .../dms-cli/internal/config/hyprland.go | 6 - nix/inputs/dms-cli/internal/config/niri.go | 6 - .../dms-cli/internal/config/terminals.go | 24 - nix/inputs/dms-cli/internal/dank16/dank16.go | 453 ---- .../dms-cli/internal/dank16/dank16_test.go | 727 ----- .../dms-cli/internal/dank16/terminals.go | 126 - nix/inputs/dms-cli/internal/dank16/vscode.go | 250 -- nix/inputs/dms-cli/internal/deps/detector.go | 51 - nix/inputs/dms-cli/internal/distros/arch.go | 785 ------ nix/inputs/dms-cli/internal/distros/base.go | 622 ----- .../dms-cli/internal/distros/base_test.go | 220 -- nix/inputs/dms-cli/internal/distros/debian.go | 537 ---- .../dms-cli/internal/distros/factory.go | 19 - nix/inputs/dms-cli/internal/distros/fedora.go | 553 ---- nix/inputs/dms-cli/internal/distros/gentoo.go | 607 ----- .../dms-cli/internal/distros/interface.go | 156 -- .../internal/distros/manual_packages.go | 802 ------ .../internal/distros/manual_packages_test.go | 122 - nix/inputs/dms-cli/internal/distros/nixos.go | 458 ---- .../dms-cli/internal/distros/opensuse.go | 608 ----- nix/inputs/dms-cli/internal/distros/osinfo.go | 115 - nix/inputs/dms-cli/internal/distros/ubuntu.go | 758 ------ nix/inputs/dms-cli/internal/dms/app.go | 438 --- nix/inputs/dms-cli/internal/dms/app_distro.go | 261 -- nix/inputs/dms-cli/internal/dms/detector.go | 167 -- .../dms-cli/internal/dms/handlers_common.go | 54 - .../dms-cli/internal/dms/handlers_features.go | 391 --- .../dms-cli/internal/dms/handlers_mainmenu.go | 61 - .../internal/dms/handlers_mainmenu_distro.go | 55 - .../dms-cli/internal/dms/plugins_handlers.go | 339 --- .../dms-cli/internal/dms/plugins_views.go | 367 --- .../dms-cli/internal/dms/views_common.go | 149 -- .../dms-cli/internal/dms/views_features.go | 529 ---- .../dms-cli/internal/errdefs/errdefs.go | 65 - .../dms-cli/internal/greeter/installer.go | 490 ---- .../dms-cli/internal/hyprland/keybinds.go | 330 --- .../internal/hyprland/keybinds_test.go | 396 --- nix/inputs/dms-cli/internal/log/log.go | 116 - .../mocks/brightness/mock_DBusConn.go | 129 - .../mocks/cups/mock_CUPSClientInterface.go | 405 --- .../gonetworkmanager/v2/mock_AccessPoint.go | 689 ----- .../v2/mock_ActiveConnection.go | 1025 ------- .../gonetworkmanager/v2/mock_Connection.go | 646 ----- .../Wifx/gonetworkmanager/v2/mock_Device.go | 1638 ------------ .../v2/mock_DeviceWireless.go | 2241 ---------------- .../gonetworkmanager/v2/mock_IP4Config.go | 772 ------ .../v2/mock_NetworkManager.go | 2349 ----------------- .../Wifx/gonetworkmanager/v2/mock_Settings.go | 467 ---- .../godbus/dbus/v5/mock_BusObject.go | 649 ----- .../mocks/internal/plugins/mock_GitClient.go | 181 -- .../dms-cli/internal/mocks/net/mock_Conn.go | 427 --- .../internal/mocks/network/mock_Backend.go | 1371 ---------- .../dms-cli/internal/plugins/manager.go | 430 --- .../dms-cli/internal/plugins/manager_test.go | 247 -- .../dms-cli/internal/plugins/registry.go | 256 -- .../dms-cli/internal/plugins/registry_test.go | 326 --- nix/inputs/dms-cli/internal/plugins/search.go | 105 - .../dms-cli/internal/proto/dwl_ipc/dwl_ipc.go | 491 ---- .../proto/wlr_gamma_control/gamma_control.go | 268 -- .../proto/xml/dwl-ipc-unstable-v2.xml | 166 -- .../xml/wlr-gamma-control-unstable-v1.xml | 126 - .../dms-cli/internal/server/bluez/agent.go | 341 --- .../dms-cli/internal/server/bluez/broker.go | 21 - .../internal/server/bluez/broker_test.go | 220 -- .../dms-cli/internal/server/bluez/handlers.go | 260 -- .../internal/server/bluez/handlers_test.go | 41 - .../dms-cli/internal/server/bluez/manager.go | 668 ----- .../server/bluez/subscription_broker.go | 99 - .../dms-cli/internal/server/bluez/types.go | 80 - .../internal/server/bluez/types_test.go | 210 -- .../dms-cli/internal/server/brightness/ddc.go | 485 ---- .../internal/server/brightness/ddc_filter.go | 135 - .../server/brightness/ddc_filter_test.go | 123 - .../internal/server/brightness/ddc_test.go | 135 - .../internal/server/brightness/handlers.go | 163 -- .../internal/server/brightness/logind.go | 67 - .../internal/server/brightness/logind_test.go | 95 - .../internal/server/brightness/manager.go | 383 --- .../server/brightness/manager_test.go | 11 - .../internal/server/brightness/sysfs.go | 272 -- .../server/brightness/sysfs_logind_test.go | 290 -- .../internal/server/brightness/sysfs_test.go | 185 -- .../internal/server/brightness/types.go | 199 -- .../dms-cli/internal/server/cups/actions.go | 107 - .../internal/server/cups/actions_test.go | 285 -- .../dms-cli/internal/server/cups/handlers.go | 160 -- .../internal/server/cups/handlers_test.go | 279 -- .../dms-cli/internal/server/cups/manager.go | 340 --- .../internal/server/cups/manager_test.go | 351 --- .../internal/server/cups/subscription.go | 245 -- .../internal/server/cups/subscription_dbus.go | 295 --- .../dms-cli/internal/server/cups/types.go | 73 - .../dms-cli/internal/server/dwl/handlers.go | 144 - .../dms-cli/internal/server/dwl/manager.go | 539 ---- .../dms-cli/internal/server/dwl/types.go | 169 -- .../internal/server/freedesktop/actions.go | 128 - .../server/freedesktop/actions_test.go | 145 - .../internal/server/freedesktop/constants.go | 14 - .../internal/server/freedesktop/handlers.go | 166 -- .../server/freedesktop/handlers_test.go | 581 ---- .../internal/server/freedesktop/manager.go | 251 -- .../server/freedesktop/manager_test.go | 154 -- .../internal/server/freedesktop/types.go | 46 - .../internal/server/freedesktop/types_test.go | 70 - .../internal/server/loginctl/actions.go | 88 - .../internal/server/loginctl/constants.go | 9 - .../internal/server/loginctl/handlers.go | 167 -- .../internal/server/loginctl/handlers_test.go | 508 ---- .../internal/server/loginctl/manager.go | 597 ----- .../internal/server/loginctl/manager_test.go | 336 --- .../internal/server/loginctl/monitor.go | 157 -- .../internal/server/loginctl/monitor_test.go | 325 --- .../dms-cli/internal/server/loginctl/types.go | 76 - .../internal/server/loginctl/types_test.go | 63 - .../dms-cli/internal/server/models/types.go | 31 - .../dms-cli/internal/server/network/API.md | 552 ---- .../internal/server/network/agent_iwd.go | 311 --- .../server/network/agent_networkmanager.go | 499 ---- .../internal/server/network/backend.go | 65 - .../network/backend_hybrid_iwd_networkd.go | 198 -- .../server/network/backend_hybrid_test.go | 135 - .../internal/server/network/backend_iwd.go | 222 -- .../server/network/backend_iwd_signals.go | 355 --- .../server/network/backend_iwd_test.go | 212 -- .../network/backend_iwd_unimplemented.go | 47 - .../server/network/backend_iwd_wifi.go | 662 ----- .../server/network/backend_networkd.go | 268 -- .../network/backend_networkd_ethernet.go | 110 - .../network/backend_networkd_signals.go | 68 - .../server/network/backend_networkd_test.go | 125 - .../network/backend_networkd_unimplemented.go | 59 - .../server/network/backend_networkmanager.go | 307 --- .../backend_networkmanager_ethernet.go | 317 --- .../backend_networkmanager_ethernet_test.go | 94 - .../network/backend_networkmanager_signals.go | 321 --- .../backend_networkmanager_signals_test.go | 240 -- .../network/backend_networkmanager_state.go | 261 -- .../backend_networkmanager_state_test.go | 82 - .../network/backend_networkmanager_test.go | 154 -- .../network/backend_networkmanager_vpn.go | 527 ---- .../backend_networkmanager_vpn_test.go | 138 - .../network/backend_networkmanager_wifi.go | 718 ----- .../backend_networkmanager_wifi_test.go | 198 -- .../dms-cli/internal/server/network/broker.go | 22 - .../server/network/connection_test.go | 109 - .../dms-cli/internal/server/network/detect.go | 89 - .../internal/server/network/detect_test.go | 34 - .../internal/server/network/handlers.go | 487 ---- .../internal/server/network/handlers_test.go | 263 -- .../internal/server/network/helpers.go | 53 - .../internal/server/network/manager.go | 530 ---- .../internal/server/network/manager_test.go | 231 -- .../internal/server/network/monitor.go | 1 - .../internal/server/network/priority.go | 138 - .../internal/server/network/priority_test.go | 50 - .../server/network/subscription_broker.go | 146 - .../internal/server/network/testing.go | 15 - .../dms-cli/internal/server/network/types.go | 190 -- .../internal/server/network/types_test.go | 178 -- .../internal/server/network/wifi_test.go | 152 -- .../internal/server/network/wired_test.go | 23 - .../internal/server/plugins/handlers.go | 27 - .../internal/server/plugins/handlers_test.go | 196 -- .../internal/server/plugins/install.go | 69 - .../dms-cli/internal/server/plugins/list.go | 51 - .../internal/server/plugins/list_installed.go | 76 - .../dms-cli/internal/server/plugins/search.go | 73 - .../dms-cli/internal/server/plugins/types.go | 23 - .../internal/server/plugins/uninstall.go | 69 - .../dms-cli/internal/server/plugins/update.go | 69 - .../dms-cli/internal/server/plugins/utils.go | 17 - nix/inputs/dms-cli/internal/server/router.go | 149 -- nix/inputs/dms-cli/internal/server/server.go | 1050 -------- .../dms-cli/internal/server/server_test.go | 184 -- .../dms-cli/internal/server/wayland/gamma.go | 88 - .../internal/server/wayland/gamma_test.go | 120 - .../internal/server/wayland/geolocation.go | 50 - .../internal/server/wayland/handlers.go | 205 -- .../internal/server/wayland/manager.go | 1367 ---------- .../internal/server/wayland/suncalc.go | 86 - .../internal/server/wayland/suncalc_test.go | 378 --- .../dms-cli/internal/server/wayland/types.go | 194 -- .../internal/server/wayland/types_test.go | 330 --- .../internal/server/wlcontext/context.go | 76 - nix/inputs/dms-cli/internal/tui/app.go | 214 -- nix/inputs/dms-cli/internal/tui/banner.go | 21 - nix/inputs/dms-cli/internal/tui/messages.go | 39 - nix/inputs/dms-cli/internal/tui/states.go | 21 - nix/inputs/dms-cli/internal/tui/styles.go | 124 - .../dms-cli/internal/tui/views_config.go | 333 --- .../internal/tui/views_dependencies.go | 237 -- .../dms-cli/internal/tui/views_install.go | 265 -- .../dms-cli/internal/tui/views_nixos_wm.go | 85 - .../dms-cli/internal/tui/views_password.go | 347 --- .../dms-cli/internal/tui/views_selection.go | 216 -- .../dms-cli/internal/tui/views_welcome.go | 216 -- nix/inputs/dms-cli/internal/utils/math.go | 13 - .../dms-cli/internal/version/version.go | 221 -- .../dms-cli/internal/version/version_test.go | 293 -- nix/inputs/dms-cli/pkg/ipp/CREDITS.MD | 5 - nix/inputs/dms-cli/pkg/ipp/LICENSE | 201 -- nix/inputs/dms-cli/pkg/ipp/adapter-http.go | 132 - nix/inputs/dms-cli/pkg/ipp/adapter.go | 9 - nix/inputs/dms-cli/pkg/ipp/attribute.go | 528 ---- nix/inputs/dms-cli/pkg/ipp/constants.go | 449 ---- nix/inputs/dms-cli/pkg/ipp/cups-client.go | 322 --- nix/inputs/dms-cli/pkg/ipp/error.go | 31 - nix/inputs/dms-cli/pkg/ipp/ipp-client.go | 329 --- nix/inputs/dms-cli/pkg/ipp/request.go | 299 --- nix/inputs/dms-cli/pkg/ipp/response.go | 383 --- nix/inputs/dms-cli/pkg/ipp/utils.go | 28 - 255 files changed, 42 insertions(+), 65613 deletions(-) delete mode 100644 nix/inputs/dms-cli/.github/workflows/pr.yml delete mode 100644 nix/inputs/dms-cli/.github/workflows/release.yml delete mode 100644 nix/inputs/dms-cli/.github/workflows/update-vendor-hash.yml delete mode 100644 nix/inputs/dms-cli/.gitignore delete mode 100644 nix/inputs/dms-cli/.mockery.yml delete mode 100644 nix/inputs/dms-cli/CONTRIBUTING_DISTRO.md delete mode 100644 nix/inputs/dms-cli/LICENSE delete mode 100644 nix/inputs/dms-cli/Makefile delete mode 100644 nix/inputs/dms-cli/README.md delete mode 100644 nix/inputs/dms-cli/assets/dank.svg delete mode 100644 nix/inputs/dms-cli/assets/danklogo.svg delete mode 100644 nix/inputs/dms-cli/cmd/dms/commands_brightness.go delete mode 100644 nix/inputs/dms-cli/cmd/dms/commands_common.go delete mode 100644 nix/inputs/dms-cli/cmd/dms/commands_dank16.go delete mode 100644 nix/inputs/dms-cli/cmd/dms/commands_features.go delete mode 100644 nix/inputs/dms-cli/cmd/dms/commands_greeter.go delete mode 100644 nix/inputs/dms-cli/cmd/dms/commands_hyprland.go delete mode 100644 nix/inputs/dms-cli/cmd/dms/commands_root.go delete mode 100644 nix/inputs/dms-cli/cmd/dms/main.go delete mode 100644 nix/inputs/dms-cli/cmd/dms/main_distro.go delete mode 100644 nix/inputs/dms-cli/cmd/dms/shell.go delete mode 100644 nix/inputs/dms-cli/cmd/dms/ui.go delete mode 100644 nix/inputs/dms-cli/cmd/dms/utils.go delete mode 100644 nix/inputs/dms-cli/flake.lock delete mode 100644 nix/inputs/dms-cli/flake.nix delete mode 100644 nix/inputs/dms-cli/go.mod delete mode 100644 nix/inputs/dms-cli/go.sum delete mode 100755 nix/inputs/dms-cli/install.sh delete mode 100644 nix/inputs/dms-cli/internal/config/deployer.go delete mode 100644 nix/inputs/dms-cli/internal/config/deployer_test.go delete mode 100644 nix/inputs/dms-cli/internal/config/dms.go delete mode 100644 nix/inputs/dms-cli/internal/config/embedded/alacritty-theme.toml delete mode 100644 nix/inputs/dms-cli/internal/config/embedded/alacritty.toml delete mode 100644 nix/inputs/dms-cli/internal/config/embedded/ghostty-colors.conf delete mode 100644 nix/inputs/dms-cli/internal/config/embedded/ghostty.conf delete mode 100644 nix/inputs/dms-cli/internal/config/embedded/hyprland.conf delete mode 100644 nix/inputs/dms-cli/internal/config/embedded/kitty-tabs.conf delete mode 100644 nix/inputs/dms-cli/internal/config/embedded/kitty-theme.conf delete mode 100644 nix/inputs/dms-cli/internal/config/embedded/kitty.conf delete mode 100644 nix/inputs/dms-cli/internal/config/embedded/niri.kdl delete mode 100644 nix/inputs/dms-cli/internal/config/hyprland.go delete mode 100644 nix/inputs/dms-cli/internal/config/niri.go delete mode 100644 nix/inputs/dms-cli/internal/config/terminals.go delete mode 100644 nix/inputs/dms-cli/internal/dank16/dank16.go delete mode 100644 nix/inputs/dms-cli/internal/dank16/dank16_test.go delete mode 100644 nix/inputs/dms-cli/internal/dank16/terminals.go delete mode 100644 nix/inputs/dms-cli/internal/dank16/vscode.go delete mode 100644 nix/inputs/dms-cli/internal/deps/detector.go delete mode 100644 nix/inputs/dms-cli/internal/distros/arch.go delete mode 100644 nix/inputs/dms-cli/internal/distros/base.go delete mode 100644 nix/inputs/dms-cli/internal/distros/base_test.go delete mode 100644 nix/inputs/dms-cli/internal/distros/debian.go delete mode 100644 nix/inputs/dms-cli/internal/distros/factory.go delete mode 100644 nix/inputs/dms-cli/internal/distros/fedora.go delete mode 100644 nix/inputs/dms-cli/internal/distros/gentoo.go delete mode 100644 nix/inputs/dms-cli/internal/distros/interface.go delete mode 100644 nix/inputs/dms-cli/internal/distros/manual_packages.go delete mode 100644 nix/inputs/dms-cli/internal/distros/manual_packages_test.go delete mode 100644 nix/inputs/dms-cli/internal/distros/nixos.go delete mode 100644 nix/inputs/dms-cli/internal/distros/opensuse.go delete mode 100644 nix/inputs/dms-cli/internal/distros/osinfo.go delete mode 100644 nix/inputs/dms-cli/internal/distros/ubuntu.go delete mode 100644 nix/inputs/dms-cli/internal/dms/app.go delete mode 100644 nix/inputs/dms-cli/internal/dms/app_distro.go delete mode 100644 nix/inputs/dms-cli/internal/dms/detector.go delete mode 100644 nix/inputs/dms-cli/internal/dms/handlers_common.go delete mode 100644 nix/inputs/dms-cli/internal/dms/handlers_features.go delete mode 100644 nix/inputs/dms-cli/internal/dms/handlers_mainmenu.go delete mode 100644 nix/inputs/dms-cli/internal/dms/handlers_mainmenu_distro.go delete mode 100644 nix/inputs/dms-cli/internal/dms/plugins_handlers.go delete mode 100644 nix/inputs/dms-cli/internal/dms/plugins_views.go delete mode 100644 nix/inputs/dms-cli/internal/dms/views_common.go delete mode 100644 nix/inputs/dms-cli/internal/dms/views_features.go delete mode 100644 nix/inputs/dms-cli/internal/errdefs/errdefs.go delete mode 100644 nix/inputs/dms-cli/internal/greeter/installer.go delete mode 100644 nix/inputs/dms-cli/internal/hyprland/keybinds.go delete mode 100644 nix/inputs/dms-cli/internal/hyprland/keybinds_test.go delete mode 100644 nix/inputs/dms-cli/internal/log/log.go delete mode 100644 nix/inputs/dms-cli/internal/mocks/brightness/mock_DBusConn.go delete mode 100644 nix/inputs/dms-cli/internal/mocks/cups/mock_CUPSClientInterface.go delete mode 100644 nix/inputs/dms-cli/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_AccessPoint.go delete mode 100644 nix/inputs/dms-cli/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_ActiveConnection.go delete mode 100644 nix/inputs/dms-cli/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_Connection.go delete mode 100644 nix/inputs/dms-cli/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_Device.go delete mode 100644 nix/inputs/dms-cli/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_DeviceWireless.go delete mode 100644 nix/inputs/dms-cli/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_IP4Config.go delete mode 100644 nix/inputs/dms-cli/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_NetworkManager.go delete mode 100644 nix/inputs/dms-cli/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_Settings.go delete mode 100644 nix/inputs/dms-cli/internal/mocks/github.com/godbus/dbus/v5/mock_BusObject.go delete mode 100644 nix/inputs/dms-cli/internal/mocks/internal/plugins/mock_GitClient.go delete mode 100644 nix/inputs/dms-cli/internal/mocks/net/mock_Conn.go delete mode 100644 nix/inputs/dms-cli/internal/mocks/network/mock_Backend.go delete mode 100644 nix/inputs/dms-cli/internal/plugins/manager.go delete mode 100644 nix/inputs/dms-cli/internal/plugins/manager_test.go delete mode 100644 nix/inputs/dms-cli/internal/plugins/registry.go delete mode 100644 nix/inputs/dms-cli/internal/plugins/registry_test.go delete mode 100644 nix/inputs/dms-cli/internal/plugins/search.go delete mode 100644 nix/inputs/dms-cli/internal/proto/dwl_ipc/dwl_ipc.go delete mode 100644 nix/inputs/dms-cli/internal/proto/wlr_gamma_control/gamma_control.go delete mode 100644 nix/inputs/dms-cli/internal/proto/xml/dwl-ipc-unstable-v2.xml delete mode 100644 nix/inputs/dms-cli/internal/proto/xml/wlr-gamma-control-unstable-v1.xml delete mode 100644 nix/inputs/dms-cli/internal/server/bluez/agent.go delete mode 100644 nix/inputs/dms-cli/internal/server/bluez/broker.go delete mode 100644 nix/inputs/dms-cli/internal/server/bluez/broker_test.go delete mode 100644 nix/inputs/dms-cli/internal/server/bluez/handlers.go delete mode 100644 nix/inputs/dms-cli/internal/server/bluez/handlers_test.go delete mode 100644 nix/inputs/dms-cli/internal/server/bluez/manager.go delete mode 100644 nix/inputs/dms-cli/internal/server/bluez/subscription_broker.go delete mode 100644 nix/inputs/dms-cli/internal/server/bluez/types.go delete mode 100644 nix/inputs/dms-cli/internal/server/bluez/types_test.go delete mode 100644 nix/inputs/dms-cli/internal/server/brightness/ddc.go delete mode 100644 nix/inputs/dms-cli/internal/server/brightness/ddc_filter.go delete mode 100644 nix/inputs/dms-cli/internal/server/brightness/ddc_filter_test.go delete mode 100644 nix/inputs/dms-cli/internal/server/brightness/ddc_test.go delete mode 100644 nix/inputs/dms-cli/internal/server/brightness/handlers.go delete mode 100644 nix/inputs/dms-cli/internal/server/brightness/logind.go delete mode 100644 nix/inputs/dms-cli/internal/server/brightness/logind_test.go delete mode 100644 nix/inputs/dms-cli/internal/server/brightness/manager.go delete mode 100644 nix/inputs/dms-cli/internal/server/brightness/manager_test.go delete mode 100644 nix/inputs/dms-cli/internal/server/brightness/sysfs.go delete mode 100644 nix/inputs/dms-cli/internal/server/brightness/sysfs_logind_test.go delete mode 100644 nix/inputs/dms-cli/internal/server/brightness/sysfs_test.go delete mode 100644 nix/inputs/dms-cli/internal/server/brightness/types.go delete mode 100644 nix/inputs/dms-cli/internal/server/cups/actions.go delete mode 100644 nix/inputs/dms-cli/internal/server/cups/actions_test.go delete mode 100644 nix/inputs/dms-cli/internal/server/cups/handlers.go delete mode 100644 nix/inputs/dms-cli/internal/server/cups/handlers_test.go delete mode 100644 nix/inputs/dms-cli/internal/server/cups/manager.go delete mode 100644 nix/inputs/dms-cli/internal/server/cups/manager_test.go delete mode 100644 nix/inputs/dms-cli/internal/server/cups/subscription.go delete mode 100644 nix/inputs/dms-cli/internal/server/cups/subscription_dbus.go delete mode 100644 nix/inputs/dms-cli/internal/server/cups/types.go delete mode 100644 nix/inputs/dms-cli/internal/server/dwl/handlers.go delete mode 100644 nix/inputs/dms-cli/internal/server/dwl/manager.go delete mode 100644 nix/inputs/dms-cli/internal/server/dwl/types.go delete mode 100644 nix/inputs/dms-cli/internal/server/freedesktop/actions.go delete mode 100644 nix/inputs/dms-cli/internal/server/freedesktop/actions_test.go delete mode 100644 nix/inputs/dms-cli/internal/server/freedesktop/constants.go delete mode 100644 nix/inputs/dms-cli/internal/server/freedesktop/handlers.go delete mode 100644 nix/inputs/dms-cli/internal/server/freedesktop/handlers_test.go delete mode 100644 nix/inputs/dms-cli/internal/server/freedesktop/manager.go delete mode 100644 nix/inputs/dms-cli/internal/server/freedesktop/manager_test.go delete mode 100644 nix/inputs/dms-cli/internal/server/freedesktop/types.go delete mode 100644 nix/inputs/dms-cli/internal/server/freedesktop/types_test.go delete mode 100644 nix/inputs/dms-cli/internal/server/loginctl/actions.go delete mode 100644 nix/inputs/dms-cli/internal/server/loginctl/constants.go delete mode 100644 nix/inputs/dms-cli/internal/server/loginctl/handlers.go delete mode 100644 nix/inputs/dms-cli/internal/server/loginctl/handlers_test.go delete mode 100644 nix/inputs/dms-cli/internal/server/loginctl/manager.go delete mode 100644 nix/inputs/dms-cli/internal/server/loginctl/manager_test.go delete mode 100644 nix/inputs/dms-cli/internal/server/loginctl/monitor.go delete mode 100644 nix/inputs/dms-cli/internal/server/loginctl/monitor_test.go delete mode 100644 nix/inputs/dms-cli/internal/server/loginctl/types.go delete mode 100644 nix/inputs/dms-cli/internal/server/loginctl/types_test.go delete mode 100644 nix/inputs/dms-cli/internal/server/models/types.go delete mode 100644 nix/inputs/dms-cli/internal/server/network/API.md delete mode 100644 nix/inputs/dms-cli/internal/server/network/agent_iwd.go delete mode 100644 nix/inputs/dms-cli/internal/server/network/agent_networkmanager.go delete mode 100644 nix/inputs/dms-cli/internal/server/network/backend.go delete mode 100644 nix/inputs/dms-cli/internal/server/network/backend_hybrid_iwd_networkd.go delete mode 100644 nix/inputs/dms-cli/internal/server/network/backend_hybrid_test.go delete mode 100644 nix/inputs/dms-cli/internal/server/network/backend_iwd.go delete mode 100644 nix/inputs/dms-cli/internal/server/network/backend_iwd_signals.go delete mode 100644 nix/inputs/dms-cli/internal/server/network/backend_iwd_test.go delete mode 100644 nix/inputs/dms-cli/internal/server/network/backend_iwd_unimplemented.go delete mode 100644 nix/inputs/dms-cli/internal/server/network/backend_iwd_wifi.go delete mode 100644 nix/inputs/dms-cli/internal/server/network/backend_networkd.go delete mode 100644 nix/inputs/dms-cli/internal/server/network/backend_networkd_ethernet.go delete mode 100644 nix/inputs/dms-cli/internal/server/network/backend_networkd_signals.go delete mode 100644 nix/inputs/dms-cli/internal/server/network/backend_networkd_test.go delete mode 100644 nix/inputs/dms-cli/internal/server/network/backend_networkd_unimplemented.go delete mode 100644 nix/inputs/dms-cli/internal/server/network/backend_networkmanager.go delete mode 100644 nix/inputs/dms-cli/internal/server/network/backend_networkmanager_ethernet.go delete mode 100644 nix/inputs/dms-cli/internal/server/network/backend_networkmanager_ethernet_test.go delete mode 100644 nix/inputs/dms-cli/internal/server/network/backend_networkmanager_signals.go delete mode 100644 nix/inputs/dms-cli/internal/server/network/backend_networkmanager_signals_test.go delete mode 100644 nix/inputs/dms-cli/internal/server/network/backend_networkmanager_state.go delete mode 100644 nix/inputs/dms-cli/internal/server/network/backend_networkmanager_state_test.go delete mode 100644 nix/inputs/dms-cli/internal/server/network/backend_networkmanager_test.go delete mode 100644 nix/inputs/dms-cli/internal/server/network/backend_networkmanager_vpn.go delete mode 100644 nix/inputs/dms-cli/internal/server/network/backend_networkmanager_vpn_test.go delete mode 100644 nix/inputs/dms-cli/internal/server/network/backend_networkmanager_wifi.go delete mode 100644 nix/inputs/dms-cli/internal/server/network/backend_networkmanager_wifi_test.go delete mode 100644 nix/inputs/dms-cli/internal/server/network/broker.go delete mode 100644 nix/inputs/dms-cli/internal/server/network/connection_test.go delete mode 100644 nix/inputs/dms-cli/internal/server/network/detect.go delete mode 100644 nix/inputs/dms-cli/internal/server/network/detect_test.go delete mode 100644 nix/inputs/dms-cli/internal/server/network/handlers.go delete mode 100644 nix/inputs/dms-cli/internal/server/network/handlers_test.go delete mode 100644 nix/inputs/dms-cli/internal/server/network/helpers.go delete mode 100644 nix/inputs/dms-cli/internal/server/network/manager.go delete mode 100644 nix/inputs/dms-cli/internal/server/network/manager_test.go delete mode 100644 nix/inputs/dms-cli/internal/server/network/monitor.go delete mode 100644 nix/inputs/dms-cli/internal/server/network/priority.go delete mode 100644 nix/inputs/dms-cli/internal/server/network/priority_test.go delete mode 100644 nix/inputs/dms-cli/internal/server/network/subscription_broker.go delete mode 100644 nix/inputs/dms-cli/internal/server/network/testing.go delete mode 100644 nix/inputs/dms-cli/internal/server/network/types.go delete mode 100644 nix/inputs/dms-cli/internal/server/network/types_test.go delete mode 100644 nix/inputs/dms-cli/internal/server/network/wifi_test.go delete mode 100644 nix/inputs/dms-cli/internal/server/network/wired_test.go delete mode 100644 nix/inputs/dms-cli/internal/server/plugins/handlers.go delete mode 100644 nix/inputs/dms-cli/internal/server/plugins/handlers_test.go delete mode 100644 nix/inputs/dms-cli/internal/server/plugins/install.go delete mode 100644 nix/inputs/dms-cli/internal/server/plugins/list.go delete mode 100644 nix/inputs/dms-cli/internal/server/plugins/list_installed.go delete mode 100644 nix/inputs/dms-cli/internal/server/plugins/search.go delete mode 100644 nix/inputs/dms-cli/internal/server/plugins/types.go delete mode 100644 nix/inputs/dms-cli/internal/server/plugins/uninstall.go delete mode 100644 nix/inputs/dms-cli/internal/server/plugins/update.go delete mode 100644 nix/inputs/dms-cli/internal/server/plugins/utils.go delete mode 100644 nix/inputs/dms-cli/internal/server/router.go delete mode 100644 nix/inputs/dms-cli/internal/server/server.go delete mode 100644 nix/inputs/dms-cli/internal/server/server_test.go delete mode 100644 nix/inputs/dms-cli/internal/server/wayland/gamma.go delete mode 100644 nix/inputs/dms-cli/internal/server/wayland/gamma_test.go delete mode 100644 nix/inputs/dms-cli/internal/server/wayland/geolocation.go delete mode 100644 nix/inputs/dms-cli/internal/server/wayland/handlers.go delete mode 100644 nix/inputs/dms-cli/internal/server/wayland/manager.go delete mode 100644 nix/inputs/dms-cli/internal/server/wayland/suncalc.go delete mode 100644 nix/inputs/dms-cli/internal/server/wayland/suncalc_test.go delete mode 100644 nix/inputs/dms-cli/internal/server/wayland/types.go delete mode 100644 nix/inputs/dms-cli/internal/server/wayland/types_test.go delete mode 100644 nix/inputs/dms-cli/internal/server/wlcontext/context.go delete mode 100644 nix/inputs/dms-cli/internal/tui/app.go delete mode 100644 nix/inputs/dms-cli/internal/tui/banner.go delete mode 100644 nix/inputs/dms-cli/internal/tui/messages.go delete mode 100644 nix/inputs/dms-cli/internal/tui/states.go delete mode 100644 nix/inputs/dms-cli/internal/tui/styles.go delete mode 100644 nix/inputs/dms-cli/internal/tui/views_config.go delete mode 100644 nix/inputs/dms-cli/internal/tui/views_dependencies.go delete mode 100644 nix/inputs/dms-cli/internal/tui/views_install.go delete mode 100644 nix/inputs/dms-cli/internal/tui/views_nixos_wm.go delete mode 100644 nix/inputs/dms-cli/internal/tui/views_password.go delete mode 100644 nix/inputs/dms-cli/internal/tui/views_selection.go delete mode 100644 nix/inputs/dms-cli/internal/tui/views_welcome.go delete mode 100644 nix/inputs/dms-cli/internal/utils/math.go delete mode 100644 nix/inputs/dms-cli/internal/version/version.go delete mode 100644 nix/inputs/dms-cli/internal/version/version_test.go delete mode 100644 nix/inputs/dms-cli/pkg/ipp/CREDITS.MD delete mode 100644 nix/inputs/dms-cli/pkg/ipp/LICENSE delete mode 100644 nix/inputs/dms-cli/pkg/ipp/adapter-http.go delete mode 100644 nix/inputs/dms-cli/pkg/ipp/adapter.go delete mode 100644 nix/inputs/dms-cli/pkg/ipp/attribute.go delete mode 100644 nix/inputs/dms-cli/pkg/ipp/constants.go delete mode 100644 nix/inputs/dms-cli/pkg/ipp/cups-client.go delete mode 100644 nix/inputs/dms-cli/pkg/ipp/error.go delete mode 100644 nix/inputs/dms-cli/pkg/ipp/ipp-client.go delete mode 100644 nix/inputs/dms-cli/pkg/ipp/request.go delete mode 100644 nix/inputs/dms-cli/pkg/ipp/response.go delete mode 100644 nix/inputs/dms-cli/pkg/ipp/utils.go diff --git a/flake.lock b/flake.lock index bd5ce21..a88204f 100644 --- a/flake.lock +++ b/flake.lock @@ -3,19 +3,16 @@ "DankMaterialShell": { "inputs": { "dgop": "dgop", - "dms-cli": [ - "dms-cli" - ], "nixpkgs": [ "nixpkgs" ] }, "locked": { - "lastModified": 1762812757, - "narHash": "sha256-VPmzq5tYJIwIV9LQyn+wCiNRHiVHO8wrqoM7pz6LVPs=", + "lastModified": 1764650935, + "narHash": "sha256-WGNMbRgdVKaqhe2T/rP3b0GMre0IZuiRfNALiaKPznA=", "owner": "AvengeMedia", "repo": "DankMaterialShell", - "rev": "37a10bd453da057fb4e69cf600c413eb4467bd72", + "rev": "f96a2e2325a364bb347054cd2ac2de145bc417dd", "type": "github" }, "original": { @@ -104,11 +101,11 @@ ] }, "locked": { - "lastModified": 1761645416, - "narHash": "sha256-wTQzbbQ6XHtvNJVuhJj+ytZDRyNtwUKbrIfIvMvKNfQ=", + "lastModified": 1763308703, + "narHash": "sha256-O9Y+Wer8wOh+N+4kcCK5p/VLrXyX+ktk0/s3HdZvJzk=", "owner": "numtide", "repo": "blueprint", - "rev": "633af1961cae8e02bc6195e6e599a6b09bf75217", + "rev": "5a9bba070f801d63e2af3c9ef00b86b212429f4f", "type": "github" }, "original": { @@ -159,23 +156,6 @@ "type": "github" } }, - "dms-cli": { - "inputs": { - "nixpkgs": [ - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1, - "narHash": "sha256-P6oitdXzD2nZKaF7USVDZr7+5sRa6ja8m6TGB50w4GI=", - "path": "./nix/inputs/dms-cli", - "type": "path" - }, - "original": { - "path": "./nix/inputs/dms-cli", - "type": "path" - } - }, "firefox-gnome-theme": { "flake": false, "locked": { @@ -197,11 +177,11 @@ "nixpkgs-lib": "nixpkgs-lib" }, "locked": { - "lastModified": 1762440070, - "narHash": "sha256-xxdepIcb39UJ94+YydGP221rjnpkDZUlykKuF54PsqI=", + "lastModified": 1763759067, + "narHash": "sha256-LlLt2Jo/gMNYAwOgdRQBrsRoOz7BPRkzvNaI/fzXi2Q=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "26d05891e14c88eb4a5d5bee659c0db5afb609d8", + "rev": "2cccadc7357c0ba201788ae99c4dfa90728ef5e0", "type": "github" }, "original": { @@ -250,18 +230,20 @@ "gnome-shell": { "flake": false, "locked": { - "lastModified": 1748186689, - "narHash": "sha256-UaD7Y9f8iuLBMGHXeJlRu6U1Ggw5B9JnkFs3enZlap0=", + "host": "gitlab.gnome.org", + "lastModified": 1762869044, + "narHash": "sha256-nwm/GJ2Syigf7VccLAZ66mFC8mZJFqpJmIxSGKl7+Ds=", "owner": "GNOME", "repo": "gnome-shell", - "rev": "8c88f917db0f1f0d80fa55206c863d3746fa18d0", - "type": "github" + "rev": "680e3d195a92203f28d4bf8c6e8bb537cc3ed4ad", + "type": "gitlab" }, "original": { + "host": "gitlab.gnome.org", "owner": "GNOME", - "ref": "48.2", + "ref": "gnome-49", "repo": "gnome-shell", - "type": "github" + "type": "gitlab" } }, "home-manager": { @@ -271,11 +253,11 @@ ] }, "locked": { - "lastModified": 1762787259, - "narHash": "sha256-t2U/GLLXHa2+kJkwnFNRVc2fEJ/lUfyZXBE5iKzJdcs=", + "lastModified": 1764636297, + "narHash": "sha256-S41K55kw+hWgDfgKmZ9/fMZ3F0BQDMvqFfE120fMHeE=", "owner": "nix-community", "repo": "home-manager", - "rev": "37a3d97f2873e0f68711117c34d04b7c7ead8f4e", + "rev": "ff067cfc619fdf6f82d50344e7d19ff2323f0827", "type": "github" }, "original": { @@ -326,11 +308,11 @@ "treefmt-nix": "treefmt-nix" }, "locked": { - "lastModified": 1762744937, - "narHash": "sha256-37twpZntKS1NOPDSULAyaUPMCyrvw2vhcyYd1bMT40E=", + "lastModified": 1764645678, + "narHash": "sha256-cU6uR0UlWhzewbrcAnPMkIOQ7D09I6vEqI0vkBRwWss=", "owner": "nix-community", "repo": "nixos-facter", - "rev": "bfc460a1df6056f97152252bc45c3668b584e068", + "rev": "55a0bd7172c8a52960946db4504b89c58a8e8004", "type": "github" }, "original": { @@ -341,11 +323,11 @@ }, "nixos-facter-modules": { "locked": { - "lastModified": 1762264948, - "narHash": "sha256-iaRf6n0KPl9hndnIft3blm1YTAyxSREV1oX0MFZ6Tk4=", + "lastModified": 1764252389, + "narHash": "sha256-3bbuneTKZBkYXlm0bE36kUjiDsasoIC1GWBw/UEJ9T4=", "owner": "nix-community", "repo": "nixos-facter-modules", - "rev": "fa695bff9ec37fd5bbd7ee3181dbeb5f97f53c96", + "rev": "5ea68886d95218646d11d3551a476d458df00778", "type": "github" }, "original": { @@ -356,11 +338,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1762596750, - "narHash": "sha256-rXXuz51Bq7DHBlfIjN7jO8Bu3du5TV+3DSADBX7/9YQ=", + "lastModified": 1764517877, + "narHash": "sha256-pp3uT4hHijIC8JUK5MEqeAWmParJrgBVzHLNfJDZxg4=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "b6a8526db03f735b89dd5ff348f53f752e7ddc8e", + "rev": "2d293cbfa5a793b4c50d17c05ef9e385b90edf6c", "type": "github" }, "original": { @@ -417,11 +399,11 @@ ] }, "locked": { - "lastModified": 1761897390, - "narHash": "sha256-er4gYrIoThYLjlsOMTysoRfn67d1Gci+ZpqDrtQxrA0=", + "lastModified": 1764482797, + "narHash": "sha256-ynV90KoBrPe38YFlKAHtPFk4Ee3IANUsIFGxRaq7H/s=", "owner": "quickshell-mirror", "repo": "quickshell", - "rev": "fc704e6b5d445899a1565955268c91942a4f263f", + "rev": "d24e8e9736287d01ee73ef9d573d2bc316a62d5c", "type": "github" }, "original": { @@ -433,7 +415,6 @@ "root": { "inputs": { "DankMaterialShell": "DankMaterialShell", - "dms-cli": "dms-cli", "flake-parts": "flake-parts", "home-manager": "home-manager", "nixos-facter": "nixos-facter", @@ -466,11 +447,11 @@ "tinted-zed": "tinted-zed" }, "locked": { - "lastModified": 1762264356, - "narHash": "sha256-QVfC53Ri+8n3e7Ujx9kq6all3+TLBRRPRnc6No5qY5w=", + "lastModified": 1764648840, + "narHash": "sha256-hIj3aKL+G8TXrqvz0wCnUKESN5MEyrD4YxaLLARnkPs=", "owner": "nix-community", "repo": "stylix", - "rev": "647bb8dd96a206a1b79c4fd714affc88b409e10b", + "rev": "71054adbc3629404b2f02e8385aec07f87980a2f", "type": "github" }, "original": { @@ -598,11 +579,11 @@ ] }, "locked": { - "lastModified": 1762410071, - "narHash": "sha256-aF5fvoZeoXNPxT0bejFUBXeUjXfHLSL7g+mjR/p5TEg=", + "lastModified": 1762938485, + "narHash": "sha256-AlEObg0syDl+Spi4LsZIBrjw+snSVU4T8MOeuZJUJjM=", "owner": "numtide", "repo": "treefmt-nix", - "rev": "97a30861b13c3731a84e09405414398fbf3e109f", + "rev": "5b4ee75aeefd1e2d5a1cc43cf6ba65eba75e83e4", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index c74d462..42d6b83 100644 --- a/flake.nix +++ b/flake.nix @@ -6,15 +6,6 @@ DankMaterialShell = { url = "github:AvengeMedia/DankMaterialShell"; - inputs = { - nixpkgs.follows = "nixpkgs"; - dms-cli.follows = "dms-cli"; - }; - }; - - dms-cli = { - url = "path:./nix/inputs/dms-cli"; - inputs.nixpkgs.follows = "nixpkgs"; }; diff --git a/modules/home-manager/my/programs/dms/config.nix b/modules/home-manager/my/programs/dms/config.nix index efd9b8a..4db39d7 100644 --- a/modules/home-manager/my/programs/dms/config.nix +++ b/modules/home-manager/my/programs/dms/config.nix @@ -14,8 +14,8 @@ let system = pkgs.stdenv.hostPlatform.system; dgop = inputDms.inputs.dgop.packages.${system}.default; - dms = inputDms.packages.${system}.default; - dms-cli = inputDms.inputs.dms-cli.packages.${system}.default; + dms = inputDms.packages.${system}.dankMaterialShell; + dms-cli = inputDms.packages.${system}.dmsCli; quickshell = inputQs.packages.${system}.default; # DankMaterialShell's `wallpaperFillMode` option requires sentence casing @@ -105,7 +105,7 @@ in { "--dereference --no-preserve=all " + "${config.xdg.configHome}/DankMaterialShell/default-settings.json " + "${config.xdg.configHome}/DankMaterialShell/settings.json"; - ExecStart = "${lib.getExe dms-cli} run"; + ExecStart = "${lib.getExe dms-cli} run --session"; Restart = "on-failure"; }; diff --git a/modules/shared/nix/config/nixos/default.nix b/modules/shared/nix/config/nixos/default.nix index e4a2c3b..38d0679 100644 --- a/modules/shared/nix/config/nixos/default.nix +++ b/modules/shared/nix/config/nixos/default.nix @@ -166,7 +166,7 @@ in { # Some interesting features. # "fetch-closures" - "no-url-literals" + #"no-url-literals" # gone from Lix 2.94 "pipe-operator" ]; } diff --git a/nix/inputs/dms-cli/.github/workflows/pr.yml b/nix/inputs/dms-cli/.github/workflows/pr.yml deleted file mode 100644 index 9d1c1cc..0000000 --- a/nix/inputs/dms-cli/.github/workflows/pr.yml +++ /dev/null @@ -1,30 +0,0 @@ -name: Pull Request - -on: - pull_request: - branches: [ master ] - -jobs: - test: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Set up Go - uses: actions/setup-go@v5 - with: - go-version: '1.23' - - - name: Format check - run: | - if [ "$(gofmt -s -l . | wc -l)" -gt 0 ]; then - echo "The following files are not formatted:" - gofmt -s -l . - exit 1 - fi - - - name: Test - run: go test -v ./... - - - name: Build - run: go build -v ./... diff --git a/nix/inputs/dms-cli/.github/workflows/release.yml b/nix/inputs/dms-cli/.github/workflows/release.yml deleted file mode 100644 index 8770936..0000000 --- a/nix/inputs/dms-cli/.github/workflows/release.yml +++ /dev/null @@ -1,169 +0,0 @@ -name: Release - -on: - push: - tags: - - 'v*' - -permissions: - contents: write - -concurrency: - group: release-${{ github.ref_name }} - cancel-in-progress: true - -jobs: - build: - runs-on: ubuntu-latest - strategy: - matrix: - arch: [amd64, arm64] - - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Set up Go - uses: actions/setup-go@v5 - with: - go-version-file: ./go.mod - - - name: Run tests - run: go test -v ./... - - - name: Build dankinstall (${{ matrix.arch }}) - env: - GOOS: linux - CGO_ENABLED: 0 - GOARCH: ${{ matrix.arch }} - run: | - set -eux - cd cmd/dankinstall - go build -trimpath -ldflags "-s -w -X main.Version=${GITHUB_REF#refs/tags/}" \ - -o ../../dankinstall-${{ matrix.arch }} - cd ../.. - gzip -9 -k dankinstall-${{ matrix.arch }} - sha256sum dankinstall-${{ matrix.arch }}.gz > dankinstall-${{ matrix.arch }}.gz.sha256 - - - name: Build dms (${{ matrix.arch }}) - env: - GOOS: linux - CGO_ENABLED: 0 - GOARCH: ${{ matrix.arch }} - run: | - set -eux - cd cmd/dms - go build -trimpath -ldflags "-s -w -X main.Version=${GITHUB_REF#refs/tags/}" \ - -o ../../dms-${{ matrix.arch }} - cd ../.. - gzip -9 -k dms-${{ matrix.arch }} - sha256sum dms-${{ matrix.arch }}.gz > dms-${{ matrix.arch }}.gz.sha256 - - - name: Build dms-distropkg (${{ matrix.arch }}) - env: - GOOS: linux - CGO_ENABLED: 0 - GOARCH: ${{ matrix.arch }} - run: | - set -eux - cd cmd/dms - go build -trimpath -tags distro_binary -ldflags "-s -w -X main.Version=${GITHUB_REF#refs/tags/}" \ - -o ../../dms-distropkg-${{ matrix.arch }} - cd ../.. - gzip -9 -k dms-distropkg-${{ matrix.arch }} - sha256sum dms-distropkg-${{ matrix.arch }}.gz > dms-distropkg-${{ matrix.arch }}.gz.sha256 - - - name: Upload artifacts (${{ matrix.arch }}) - uses: actions/upload-artifact@v4 - with: - name: release-assets-${{ matrix.arch }} - path: | - dankinstall-${{ matrix.arch }}.gz - dankinstall-${{ matrix.arch }}.gz.sha256 - dms-${{ matrix.arch }}.gz - dms-${{ matrix.arch }}.gz.sha256 - dms-distropkg-${{ matrix.arch }}.gz - dms-distropkg-${{ matrix.arch }}.gz.sha256 - if-no-files-found: error - - update-flake-version: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - token: ${{ secrets.GITHUB_TOKEN }} - fetch-depth: 0 - - - name: Update flake.nix version - run: | - set -euo pipefail - - version="${GITHUB_REF#refs/tags/}" - version="${version#v}" - echo "Updating flake.nix to version: $version" - - # Update version in flake.nix - sed -i "s/version = \"[^\"]*\"/version = \"$version\"/" flake.nix - - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - - if ! git diff --quiet flake.nix; then - git add flake.nix - git commit -m "flake: bump version to $version" - - # Push to master (or main, depending on your default branch) - git push origin HEAD:master || git push origin HEAD:main - echo "Pushed flake.nix version update to master" - else - echo "No version changes needed" - fi - - release: - runs-on: ubuntu-latest - needs: build - steps: - - name: Download all artifacts - uses: actions/download-artifact@v4 - with: - pattern: release-assets-* - merge-multiple: true - path: ./_dist - - - name: Create/Update GitHub Release (single run) - uses: softprops/action-gh-release@v2 - with: - tag_name: ${{ github.ref_name }} - name: Release ${{ github.ref_name }} - body: | - ## Danklinux Release - - This release includes binaries for: - - Linux AMD64 - - Linux ARM64 - - ### Installation - - ```bash - curl -fsSL https://raw.githubusercontent.com/AvengeMedia/danklinux/master/install.sh | sh - ``` - files: _dist/** - draft: false - prerelease: ${{ contains(github.ref_name, '-') }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Notify DankMaterialShell to create release and mirror assets (single run) - uses: peter-evans/repository-dispatch@v3 - with: - token: ${{ secrets.SHELL_REPO_PAT }} - repository: AvengeMedia/DankMaterialShell - event-type: dms_release - client-payload: >- - { - "tag": "${{ github.ref_name }}", - "dms_repo": "${{ github.repository }}" - } diff --git a/nix/inputs/dms-cli/.github/workflows/update-vendor-hash.yml b/nix/inputs/dms-cli/.github/workflows/update-vendor-hash.yml deleted file mode 100644 index e5c6b4f..0000000 --- a/nix/inputs/dms-cli/.github/workflows/update-vendor-hash.yml +++ /dev/null @@ -1,89 +0,0 @@ -name: Update Vendor Hash - -on: - push: - paths: - - "go.mod" - - "go.sum" - branches: - - master - -jobs: - update-vendor-hash: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Install Nix - uses: cachix/install-nix-action@v31 - - - name: Update vendorHash in flake.nix - run: | - set -euo pipefail - - # Try to build and capture the expected hash from error message - echo "Attempting nix build to get new vendorHash..." - if output=$(nix build .#dms-cli 2>&1); then - echo "Build succeeded, no hash update needed" - exit 0 - fi - - # Extract the expected hash from the error message - new_hash=$(echo "$output" | grep -oP "got:\s+\K\S+" | head -n1) - - if [ -z "$new_hash" ]; then - echo "Could not extract new vendorHash from build output" - echo "Build output:" - echo "$output" - exit 1 - fi - - echo "New vendorHash: $new_hash" - - # Get current hash from flake.nix - current_hash=$(grep -oP 'vendorHash = "\K[^"]+' flake.nix) - echo "Current vendorHash: $current_hash" - - if [ "$current_hash" = "$new_hash" ]; then - echo "vendorHash is already up to date" - exit 0 - fi - - # Update the hash in flake.nix - sed -i "s|vendorHash = \"$current_hash\"|vendorHash = \"$new_hash\"|" flake.nix - - # Verify the build works with the new hash - echo "Verifying build with new vendorHash..." - nix build .#dms-cli - - echo "vendorHash updated successfully!" - - - name: Commit and push vendorHash update - run: | - set -euo pipefail - - if ! git diff --quiet flake.nix; then - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - - git add flake.nix - git commit -m "flake: update vendorHash for go.mod changes" - - for attempt in 1 2 3; do - if git push; then - echo "Successfully pushed vendorHash update" - exit 0 - fi - echo "Push attempt $attempt failed, pulling and retrying..." - git pull --rebase - sleep $((attempt*2)) - done - - echo "Failed to push after retries" >&2 - exit 1 - else - echo "No changes to flake.nix" - fi diff --git a/nix/inputs/dms-cli/.gitignore b/nix/inputs/dms-cli/.gitignore deleted file mode 100644 index 8edad49..0000000 --- a/nix/inputs/dms-cli/.gitignore +++ /dev/null @@ -1,36 +0,0 @@ -# If you prefer the allow list template instead of the deny list, see community template: -# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore -# -# Binaries for programs and plugins -*.exe -*.exe~ -*.dll -*.so -*.dylib - -# Test binary, built with `go test -c` -*.test - -# Code coverage profiles and other test artifacts -*.out -coverage.* -*.coverprofile -profile.cov - -# Dependency directories (remove the comment below to include it) -# vendor/ - -# Go workspace file -go.work -go.work.sum - -# env file -.env - -# Editor/IDE -# .idea/ -# .vscode/ - -bin/ -dankinstall -/dms diff --git a/nix/inputs/dms-cli/.mockery.yml b/nix/inputs/dms-cli/.mockery.yml deleted file mode 100644 index a526387..0000000 --- a/nix/inputs/dms-cli/.mockery.yml +++ /dev/null @@ -1,42 +0,0 @@ -with-expecter: true -dir: "internal/mocks/{{.InterfaceDirRelative}}" -mockname: "Mock{{.InterfaceName}}" -outpkg: "{{.PackageName}}" -packages: - github.com/Wifx/gonetworkmanager/v2: - interfaces: - NetworkManager: - Device: - DeviceWireless: - AccessPoint: - Connection: - Settings: - ActiveConnection: - IP4Config: - net: - interfaces: - Conn: - github.com/AvengeMedia/danklinux/internal/plugins: - interfaces: - GitClient: - github.com/godbus/dbus/v5: - interfaces: - BusObject: - github.com/AvengeMedia/danklinux/internal/server/brightness: - config: - dir: "internal/mocks/brightness" - outpkg: mocks_brightness - interfaces: - DBusConn: - github.com/AvengeMedia/danklinux/internal/server/network: - config: - dir: "internal/mocks/network" - outpkg: mocks_network - interfaces: - Backend: - github.com/AvengeMedia/danklinux/internal/server/cups: - config: - dir: "internal/mocks/cups" - outpkg: mocks_cups - interfaces: - CUPSClientInterface: diff --git a/nix/inputs/dms-cli/CONTRIBUTING_DISTRO.md b/nix/inputs/dms-cli/CONTRIBUTING_DISTRO.md deleted file mode 100644 index e70713b..0000000 --- a/nix/inputs/dms-cli/CONTRIBUTING_DISTRO.md +++ /dev/null @@ -1,211 +0,0 @@ -# Adding New Linux Distributions - -This guide explains how to add support for new Linux distributions to the dankdots installer using the new consolidated architecture. - -## Architecture Overview - -The codebase uses a simple, consolidated approach where each distribution is completely self-contained: - -- **All-in-One** (`internal/distros/{distro}.go`) - Complete distribution implementation -- **Auto-Registration** - Distributions register themselves via `init()` functions -- **Shared Base** - Common functionality inherited from `BaseDistribution` - -## Adding Support - -### Method 1: Use Existing Implementation (Derivatives) - -For distros that are derivatives (like CachyOS being Arch-based), you can register them to use an existing implementation. - -**Example: Adding CachyOS (Arch-based)** - -```go -// internal/distros/arch.go - add to the init function -func init() { - Register("arch", "#1793D1", func(config DistroConfig, logChan chan<- string) Distribution { - return NewArchDistribution(config, logChan) - }) - Register("cachyos", "#318CE7", func(config DistroConfig, logChan chan<- string) Distribution { - return NewArchDistribution(config, logChan) // CachyOS uses Arch implementation but different color - }) -} -``` - -That's it! CachyOS now uses Arch's detection and installation logic. - -**Example: Adding Ubuntu derivatives** - -```go -// internal/distros/ubuntu.go (after you create it) -func init() { - Register("ubuntu", "#E95420", func(config DistroConfig, logChan chan<- string) Distribution { - return NewUbuntuDistribution(config, logChan) - }) - Register("kubuntu", "#0079C1", func(config DistroConfig, logChan chan<- string) Distribution { - return NewUbuntuDistribution(config, logChan) // Kubuntu uses Ubuntu implementation but different color - }) - Register("xubuntu", "#2F5BEA", func(config DistroConfig, logChan chan<- string) Distribution { - return NewUbuntuDistribution(config, logChan) // Xubuntu uses Ubuntu implementation but different color - }) - Register("pop", "#48B9C7", func(config DistroConfig, logChan chan<- string) Distribution { - return NewUbuntuDistribution(config, logChan) // Pop!_OS uses Ubuntu implementation but different color - }) -} -``` - -### Method 2: Create New Implementation - -For entirely new distribution families, create a complete implementation: - -**Example: Adding openSUSE** - -Create `internal/distros/opensuse.go`: - -```go -package distros - -import ( - "context" - "os/exec" - "strings" - - "github.com/AvengeMedia/danklinux/internal/deps" - "github.com/AvengeMedia/danklinux/internal/installer" -) - -func init() { - Register("opensuse-leap", "#73BA25", func(config DistroConfig, logChan chan<- string) Distribution { - return NewOpenSUSEDistribution(config, logChan) - }) - Register("opensuse-tumbleweed", "#73BA25", func(config DistroConfig, logChan chan<- string) Distribution { - return NewOpenSUSEDistribution(config, logChan) - }) -} - -type OpenSUSEDistribution struct { - *BaseDistribution - *ManualPackageInstaller - config DistroConfig -} - -func NewOpenSUSEDistribution(config DistroConfig, logChan chan<- string) *OpenSUSEDistribution { - base := NewBaseDistribution(logChan) - return &OpenSUSEDistribution{ - BaseDistribution: base, - ManualPackageInstaller: &ManualPackageInstaller{BaseDistribution: base}, - config: config, - } -} - -func (o *OpenSUSEDistribution) GetID() string { - return o.config.ID -} - -func (o *OpenSUSEDistribution) GetColorHex() string { - return o.config.ColorHex -} - -func (o *OpenSUSEDistribution) GetPackageManager() PackageManagerType { - return PackageManagerZypper -} - -func (o *OpenSUSEDistribution) GetPackageMapping(wm deps.WindowManager) map[string]PackageMapping { - return map[string]PackageMapping{ - "git": {Name: "git", Repository: RepoTypeSystem}, - "ghostty": {Name: "ghostty", Repository: RepoTypeManual}, // Build from source - "kitty": {Name: "kitty", Repository: RepoTypeSystem}, - // ... map all required packages to openSUSE equivalents - } -} - -func (o *OpenSUSEDistribution) DetectDependencies(ctx context.Context, wm deps.WindowManager) ([]deps.Dependency, error) { - return o.DetectDependenciesWithTerminal(ctx, wm, deps.TerminalGhostty) -} - -func (o *OpenSUSEDistribution) DetectDependenciesWithTerminal(ctx context.Context, wm deps.WindowManager, terminal deps.Terminal) ([]deps.Dependency, error) { - var dependencies []deps.Dependency - - // Use base methods for common functionality - dependencies = append(dependencies, o.detectDMS()) - dependencies = append(dependencies, o.detectSpecificTerminal(terminal)) - dependencies = append(dependencies, o.detectGit()) - // ... add openSUSE-specific detection - - return dependencies, nil -} - -func (o *OpenSUSEDistribution) InstallPackages(ctx context.Context, dependencies []deps.Dependency, wm deps.WindowManager, sudoPassword string, reinstallFlags map[string]bool, progressChan chan<- installer.InstallProgressMsg) error { - // Implement installation logic using zypper - // Use o.InstallManualPackages() for source builds - return nil -} - -func (o *OpenSUSEDistribution) InstallPrerequisites(ctx context.Context, sudoPassword string, progressChan chan<- installer.InstallProgressMsg) error { - // Install build tools, enable repositories, etc. - return nil -} - -func (o *OpenSUSEDistribution) packageInstalled(pkg string) bool { - cmd := exec.Command("rpm", "-q", pkg) - err := cmd.Run() - return err == nil -} -``` - -## Repository Types - -The system supports these repository types: - -- `RepoTypeSystem` - Main system repository (zypper, apt, dnf, pacman) -- `RepoTypeAUR` - Arch User Repository -- `RepoTypeCOPR` - Fedora COPR -- `RepoTypePPA` - Ubuntu PPA -- `RepoTypeManual` - Build from source - -## Package Manager Support - -To add a new package manager, add it to `internal/distros/interface.go`: - -```go -const ( - PackageManagerPacman PackageManagerType = "pacman" - PackageManagerDNF PackageManagerType = "dnf" - PackageManagerAPT PackageManagerType = "apt" - PackageManagerZypper PackageManagerType = "zypper" - PackageManagerPortage PackageManagerType = "portage" // Add new ones here -) -``` - -## Testing Your Implementation - -1. Build: `go build -o dankdots ./cmd/main.go` -2. Test on target distribution -3. Verify all packages detect and install correctly -4. Test both window managers (Hyprland, Niri) -5. Test both terminals (Ghostty, Kitty) - -## Detection Process - -The system automatically detects supported distributions by: - -1. Reading `/etc/os-release` for the `ID` field -2. Looking up the ID in the distribution registry -3. Creating an instance using the registered constructor function - -No hardcoded lists to maintain - everything is driven by the registry! - -## Benefits of New Architecture - -- ✅ **Single file per distro** - All logic in one place -- ✅ **Auto-registration** - No factory methods to update -- ✅ **Shared functionality** - Inherit common features -- ✅ **No duplication** - Manual builds and fonts are shared -- ✅ **Easy derivatives** - One line to support a new derivative - -## Contributing - -1. Fork the repository -2. Create your distribution file in `internal/distros/` -3. Test thoroughly on your target distribution -4. Submit a pull request with example output - -The maintainers will review and provide feedback. Thank you for expanding dankdots support! \ No newline at end of file diff --git a/nix/inputs/dms-cli/LICENSE b/nix/inputs/dms-cli/LICENSE deleted file mode 100644 index d8152be..0000000 --- a/nix/inputs/dms-cli/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2025 Avenge Media LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/nix/inputs/dms-cli/Makefile b/nix/inputs/dms-cli/Makefile deleted file mode 100644 index 119fd79..0000000 --- a/nix/inputs/dms-cli/Makefile +++ /dev/null @@ -1,157 +0,0 @@ -BINARY_NAME=dms -BINARY_NAME_INSTALL=dankinstall -SOURCE_DIR=cmd/dms -SOURCE_DIR_INSTALL=cmd/dankinstall -BUILD_DIR=bin -PREFIX ?= /usr/local -INSTALL_DIR=$(PREFIX)/bin - -GO=go -GOFLAGS=-ldflags="-s -w" - -# Version and build info -VERSION=$(shell git describe --tags --always 2>/dev/null || echo "dev") -BUILD_TIME=$(shell date -u '+%Y-%m-%d_%H:%M:%S') -COMMIT=$(shell git rev-parse --short HEAD 2>/dev/null || echo "unknown") - -BUILD_LDFLAGS=-ldflags='-s -w -X main.Version=$(VERSION) -X main.buildTime=$(BUILD_TIME) -X main.commit=$(COMMIT)' - -# Architecture to build for dist target (amd64, arm64, or all) -ARCH ?= all - -.PHONY: all build dankinstall dist clean install install-all install-dankinstall uninstall uninstall-all uninstall-dankinstall install-config uninstall-config test fmt vet deps help - -# Default target -all: build - -# Build the main binary (dms) -build: - @echo "Building $(BINARY_NAME)..." - @mkdir -p $(BUILD_DIR) - CGO_ENABLED=0 $(GO) build $(BUILD_LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME) ./$(SOURCE_DIR) - @echo "Build complete: $(BUILD_DIR)/$(BINARY_NAME)" - -dankinstall: - @echo "Building $(BINARY_NAME_INSTALL)..." - @mkdir -p $(BUILD_DIR) - CGO_ENABLED=0 $(GO) build $(BUILD_LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME_INSTALL) ./$(SOURCE_DIR_INSTALL) - @echo "Build complete: $(BUILD_DIR)/$(BINARY_NAME_INSTALL)" - -# Build distro binaries for amd64 and arm64 (Linux only, no update/greeter support) -dist: -ifeq ($(ARCH),all) - @echo "Building $(BINARY_NAME) for distribution (amd64 and arm64)..." - @mkdir -p $(BUILD_DIR) - @echo "Building for linux/amd64..." - CGO_ENABLED=0 GOOS=linux GOARCH=amd64 $(GO) build -tags distro_binary $(BUILD_LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-linux-amd64 ./$(SOURCE_DIR) - @echo "Building for linux/arm64..." - CGO_ENABLED=0 GOOS=linux GOARCH=arm64 $(GO) build -tags distro_binary $(BUILD_LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-linux-arm64 ./$(SOURCE_DIR) - @echo "Distribution builds complete:" - @echo " $(BUILD_DIR)/$(BINARY_NAME)-linux-amd64" - @echo " $(BUILD_DIR)/$(BINARY_NAME)-linux-arm64" -else - @echo "Building $(BINARY_NAME) for distribution ($(ARCH))..." - @mkdir -p $(BUILD_DIR) - @echo "Building for linux/$(ARCH)..." - CGO_ENABLED=0 GOOS=linux GOARCH=$(ARCH) $(GO) build -tags distro_binary $(BUILD_LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-linux-$(ARCH) ./$(SOURCE_DIR) - @echo "Distribution build complete:" - @echo " $(BUILD_DIR)/$(BINARY_NAME)-linux-$(ARCH)" -endif - -build-all: build dankinstall - -install: build - @echo "Installing $(BINARY_NAME) to $(INSTALL_DIR)..." - @install -D -m 755 $(BUILD_DIR)/$(BINARY_NAME) $(INSTALL_DIR)/$(BINARY_NAME) - @echo "Installation complete" - -install-all: build-all - @echo "Installing $(BINARY_NAME) to $(INSTALL_DIR)..." - @install -D -m 755 $(BUILD_DIR)/$(BINARY_NAME) $(INSTALL_DIR)/$(BINARY_NAME) - @echo "Installing $(BINARY_NAME_INSTALL) to $(INSTALL_DIR)..." - @install -D -m 755 $(BUILD_DIR)/$(BINARY_NAME_INSTALL) $(INSTALL_DIR)/$(BINARY_NAME_INSTALL) - @echo "Installation complete" - -install-dankinstall: dankinstall - @echo "Installing $(BINARY_NAME_INSTALL) to $(INSTALL_DIR)..." - @install -D -m 755 $(BUILD_DIR)/$(BINARY_NAME_INSTALL) $(INSTALL_DIR)/$(BINARY_NAME_INSTALL) - @echo "Installation complete" - -uninstall: - @echo "Uninstalling $(BINARY_NAME) from $(INSTALL_DIR)..." - @rm -f $(INSTALL_DIR)/$(BINARY_NAME) - @echo "Uninstall complete" - -uninstall-all: - @echo "Uninstalling $(BINARY_NAME) from $(INSTALL_DIR)..." - @rm -f $(INSTALL_DIR)/$(BINARY_NAME) - @echo "Uninstalling $(BINARY_NAME_INSTALL) from $(INSTALL_DIR)..." - @rm -f $(INSTALL_DIR)/$(BINARY_NAME_INSTALL) - @echo "Uninstall complete" - -uninstall-dankinstall: - @echo "Uninstalling $(BINARY_NAME_INSTALL) from $(INSTALL_DIR)..." - @rm -f $(INSTALL_DIR)/$(BINARY_NAME_INSTALL) - @echo "Uninstall complete" - -clean: - @echo "Cleaning build artifacts..." - @rm -rf $(BUILD_DIR) - @echo "Clean complete" - -test: - @echo "Running tests..." - $(GO) test -v ./... - -fmt: - @echo "Formatting Go code..." - $(GO) fmt ./... - -vet: - @echo "Running go vet..." - $(GO) vet ./... - -deps: - @echo "Updating dependencies..." - $(GO) mod tidy - $(GO) mod download - -dev: - @echo "Building $(BINARY_NAME) for development..." - @mkdir -p $(BUILD_DIR) - $(GO) build -o $(BUILD_DIR)/$(BINARY_NAME) ./$(SOURCE_DIR) - @echo "Development build complete: $(BUILD_DIR)/$(BINARY_NAME)" - -check-go: - @echo "Checking Go version..." - @go version | grep -E "go1\.(2[2-9]|[3-9][0-9])" > /dev/null || (echo "ERROR: Go 1.22 or higher required" && exit 1) - @echo "Go version OK" - -version: check-go - @echo "Version: $(VERSION)" - @echo "Build Time: $(BUILD_TIME)" - @echo "Commit: $(COMMIT)" - -help: - @echo "Available targets:" - @echo " all - Build the main binary (dms) (default)" - @echo " build - Build the main binary (dms)" - @echo " dankinstall - Build dankinstall binary" - @echo " dist - Build dms for linux amd64/arm64 (no update/greeter)" - @echo " Use ARCH=amd64 or ARCH=arm64 to build only one" - @echo " build-all - Build both binaries" - @echo " install - Install dms to $(INSTALL_DIR)" - @echo " install-all - Install both dms and dankinstall to $(INSTALL_DIR)" - @echo " install-dankinstall - Install only dankinstall to $(INSTALL_DIR)" - @echo " uninstall - Remove dms from $(INSTALL_DIR)" - @echo " uninstall-all - Remove both binaries from $(INSTALL_DIR)" - @echo " uninstall-dankinstall - Remove only dankinstall from $(INSTALL_DIR)" - @echo " clean - Clean build artifacts" - @echo " test - Run tests" - @echo " fmt - Format Go code" - @echo " vet - Run go vet" - @echo " deps - Update dependencies" - @echo " dev - Build with debug info" - @echo " check-go - Check Go version compatibility" - @echo " version - Show version information" - @echo " help - Show this help message" diff --git a/nix/inputs/dms-cli/README.md b/nix/inputs/dms-cli/README.md deleted file mode 100644 index ee74a5c..0000000 --- a/nix/inputs/dms-cli/README.md +++ /dev/null @@ -1,274 +0,0 @@ -
- - Dank Linux - - - ### dms CLI & Backend + dankinstall - -[![Documentation](https://img.shields.io/badge/docs-danklinux.com-9ccbfb?style=for-the-badge&labelColor=101418)](https://danklinux.com/docs) -[![GitHub release](https://img.shields.io/github/v/release/AvengeMedia/danklinux?style=for-the-badge&labelColor=101418&color=9ccbfb)](https://github.com/AvengeMedia/danklinux/releases) -[![GitHub License](https://img.shields.io/badge/license-MIT-b9c8da?style=for-the-badge&labelColor=101418)](https://github.com/AvengeMedia/danklinux/blob/master/LICENSE) - -
- ---- - -A monorepo for dankinstall and dms (cli+go backend), a modern desktop suite for Wayland compositors. - -**[Full documentation →](https://danklinux.com/docs)** - -- **dms** DankMaterialShell (cli + go backend) - - The backend side of dms, provides APIs for the desktop and a management CLI. - - Shared dbus connection for networking (NetworkManager, iwd), loginctl, accountsservice, cups, and other interfaces. - - Implements wayland protocols - - wlr-gamma-control-unstable-v1 (for night mode/gamma control) - - dwl-ipc-unstable-v2 (for dwl/MangoWC integration) - - Exposes a json API over unix socket for interaction with these interfaces - - Provides plugin management APIs for the shell - - CUPS integration for printer management - - Optionally provides `update` interface - depending on build inputs. - - This is intended to be disabled when packaged as part of distribution packages. -- **dankinstall** Installs the Dank Linux suite for [niri](https://github.com/YaLTeR/niri) and/or [Hyprland](https://hypr.land) - - Features the [DankMaterialShell](https://github.com/AvengeMedia/DankMaterialShell) - - Which features a complete desktop experience with wallpapers, auto theming, notifications, lock screen, etc. - - Offers up solid out of the box configurations as usable, featured starting points. - - Can be installed if you already have niri/Hyprland configured - - Will allow you to keep your existing config, or replace with Dank ones (existing configs always backed up though) - -# dms cli & backend - -A part of the DankMaterialShell, that is provided by this repository. It is written in GO, and exposes a suite of APIs over unix socket that interface with dbus via [godbus](https://github.com/godbus/dbus) and also the plugin system. - -**Backend** (all exposed over a unix socket json API): - -- **dbus** - - networking - full integration with pluggable backends - NetworkManager, iwd - - bluez - integration with a pairing agent - - loginctl - creates sleep inhibitor, integrates lock before suspend, signals for lock/unlock - - accountsservice - suite of user profile APIs - name, email, profile picture, etc. - - cups - printer management and configuration -- **dms plugins** - - APIs to browse, install, update, and search available plugins -- **wayland** - - Implements [wlr-gamma-control-unstable-v1](https://wayland.app/protocols/wlr-gamma-control-unstable-v1) - - Essentially, provides auto or manual gamma control similar to a tool like [gammastep](https://gitlab.com/chinstrap/gammastep) or [wlsunset](https://github.com/kennylevinsen/wlsunset) - - Implements dwl-ipc-unstable-v2 - - For dwl (tested with MangoWC) integration - -*run `dms debug-srv` to run the socket service in standalone mode, and see a list of available APIs* - -**cli** - -- manage process: run, restart, kill -- IPC with dms: toggle launcher, notification popup, etc. -- plugins: install/browse/search (use plugin IDs like `dms plugins install myPlugin`) -- update (some builds): Update DMS and dependencies, (disabled for Arch AUR and Fedora copr installs, as it is handled by pacman/dnf) -- greeter (some builds): Install the dms greetd greeter (on arch/fedora it is disabled in favor of OS packages) - -## Build & Install - -To build the dms CLI (Requires Go 1.24+): - -### For distribution package maintainers - -This produces a build without the `update` or `greeter` functionality, which are intended for manual installation. - -```bash -make dist -``` - -Produces `bin/dms-linux-amd64` and `bin/dms-linux-arm64` - -### Manual Install - -```bash -# Installs to /usr/local/bin/dms -make && sudo make install -``` - -### Wayland Protocol Bindings - -The gamma control functionality uses Wayland protocol bindings generated from the protocol XML definition. To regenerate the Go bindings from `internal/proto/xml/wlr-gamma-control-unstable-v1.xml`: - -```bash -go install github.com/rajveermalviya/go-wayland/cmd/go-wayland-scanner@latest -go-wayland-scanner -i internal/proto/xml/wlr-gamma-control-unstable-v1.xml \ - -pkg wlr_gamma_control -o internal/proto/wlr_gamma_control/gamma_control.go -``` - -This is only needed if modifying the protocol or updating to a newer version. - -# Dank Linux/dankinstall - -Installs compositor, dms, terminal, and some optional dependencies - along with a default compositor & terminal configuration. - -## Quickstart - -```bash -curl -fsSL https://install.danklinux.com | sh -``` - -*Alternatively, download the latest [release](https://github.com/AvengeMedia/danklinux/releases)* - -## Supported Distributions - -**Note on Greeter**: dankinstall does not install a greeter automatically. -- To install the dms greeter, run `dms greeter install` after installation. -- Then you can disable any existing greeter, if present, and run `sudo systemctl enable --now greetd` - -### Arch Linux & Derivatives - -**Supported:** Arch, ArchARM, Archcraft, CachyOS, EndeavourOS, Manjaro - -**Special Notes:** -- Uses native `pacman` for system packages -- AUR packages are built manually using `makepkg` (no AUR helper dependency) -- **Recommendations** - - Use NetworkManager to manage networking - - If using archinstall, you can choose `minimal` for profile, and `NetworkManager` under networking. - -**Package Sources:** -| Package | Source | Notes | -|---------|---------|-------| -| System packages (git, jq, etc.) | Official repos | Via `pacman` | -| quickshell | AUR | Built from source | -| matugen | AUR (`matugen-bin`) | Pre-compiled binary | -| dgop | AUR | Built from source | -| niri | Official repos (`niri`) | Latest niri | -| hyprland | Official repos | Available in Extra repository | -| DankMaterialShell | Manual | Git clone to `~/.config/quickshell/dms` | - -### Fedora & Derivatives - -**Supported:** Fedora, Nobara, Fedora Asahi Remix - -**Special Notes:** -- Requires `dnf-plugins-core` for COPR repository support -- Automatically enables required COPR repositories -- All COPR repos are enabled with automatic acceptance -- **Editions** dankinstall is tested on "Workstation Edition", but probably works fine on any fedora flavor. Report issues if anything doesn't work. -- [Fedora Asahi Remix](https://asahilinux.org/fedora/) hasn't been tested, but presumably it should work fine as all of the dependencies should provide arm64 variants. - -**Package Sources:** -| Package | Source | Notes | -|---------|---------|-------| -| System packages | Official repos | Via `dnf` | -| quickshell | COPR | `avengemedia/danklinux` | -| matugen | COPR | `avengemedia/danklinux` | -| dgop | Manual | Built from source with Go | -| cliphist | COPR | `avengemedia/danklinux` | -| ghostty | COPR | `avengemedia/danklinux` | -| hyprland | COPR | `solopasha/hyprland` | -| niri | COPR | `yalter/niri` | -| DankMaterialShell | COPR | `avengemedia/dms` | - -### Ubuntu - -**Supported:** Ubuntu 25.04+ - -**Special Notes:** -- Requires PPA support via `software-properties-common` -- Go installed from PPA for building manual packages -- Most packages require manual building due to limited repository availability - - This means the install can be quite slow, as many need to be compiled from source. - - niri is packages as a `.deb` so it can be managed via `apt` -- Automatic PPA repository addition and package list updates - -**Package Sources:** -| Package | Source | Notes | -|---------|---------|-------| -| System packages | Official repos | Via `apt` | -| quickshell | Manual | Built from source with cmake | -| matugen | Manual | Built from source with Go | -| dgop | Manual | Built from source with Go | -| hyprland | PPA | `ppa:cppiber/hyprland` | -| hyprpicker | PPA | `ppa:cppiber/hyprland` | -| niri | Manual | Built from source with Rust | -| Go compiler | PPA | `ppa:longsleep/golang-backports` | -| DankMaterialShell | Manual | Git clone to `~/.config/quickshell/dms` | - -### Debian - -**Supported:** Debian 13+ (Trixie) - -**Special Notes:** -- **niri only** - Debian does not support Hyprland currently, only niri. -- Most packages require manual building due to limited repository availability - - This means the install can be quite slow, as many need to be compiled from source. - - niri is packages as a `.deb` so it can be managed via `apt` - -**Package Sources:** -| Package | Source | Notes | -|---------|---------|-------| -| System packages | Official repos | Via `apt` | -| quickshell | Manual | Built from source with cmake | -| matugen | Manual | Built from source with Go | -| dgop | Manual | Built from source with Go | -| niri | Manual | Built from source with Rust | -| DankMaterialShell | Manual | Git clone to `~/.config/quickshell/dms` | - -### openSUSE Tumbleweed - -**Special Notes:** -- Most packages available in standard repos, minimal manual building required -- quickshell and matugen require building from source - -**Package Sources:** -| Package | Source | Notes | -|---------|---------|-------| -| System packages (git, jq, etc.) | Official repos | Via `zypper` | -| hyprland | Official repos | Available in standard repos | -| niri | Official repos | Available in standard repos | -| xwayland-satellite | Official repos | For niri X11 app support | -| ghostty | Official repos | Latest terminal emulator | -| kitty, alacritty | Official repos | Alternative terminals | -| grim, slurp, hyprpicker | Official repos | Wayland screenshot utilities | -| wl-clipboard | Official repos | Via `wl-clipboard` package | -| cliphist | Official repos | Clipboard manager | -| quickshell | Manual | Built from source with cmake + openSUSE flags | -| matugen | Manual | Built from source with Rust | -| dgop | Manual | Built from source with Go | -| DankMaterialShell | Manual | Git clone to `~/.config/quickshell/dms` | - -### NixOS (Not supported by Dank Linux, but with Flake) - -NixOS users should use the [dms flake](https://github.com/AvengeMedia/DankMaterialShell/tree/master?tab=readme-ov-file#nixos---via-home-manager) - -## Manual Package Building - -The installer handles manual package building for packages not available in repositories: - -### quickshell (Ubuntu, Debian, openSUSE) -- Built from source using cmake -- Requires Qt6 development libraries -- Automatically handles build dependencies -- **openSUSE:** Uses special CFLAGS with rpm optflags and wayland include path - -### matugen (Ubuntu, Debian, Fedora, openSUSE) -- Built from Rust source -- Requires cargo and rust toolchain -- Installed to `/usr/local/bin` - -### dgop (All distros) -- Built from Go source -- Simple dependency-free build -- Installed to `/usr/local/bin` - -### niri (Ubuntu, Debian) -- Built from Rust source -- Requires cargo and rust toolchain -- Complex build with multiple dependencies - -## Commands - -### dankinstall -Main installer with interactive TUI for initial setup - -### dms -Management interface for DankMaterialShell: -- `dms` - Interactive management TUI -- `dms run` - Start interactive shell -- `dms run -d` - Start shell as daemon -- `dms restart` - Restart running DMS shell -- `dms kill` - Kill running DMS shell processes -- `dms ipc ` - Send IPC commands to running shell \ No newline at end of file diff --git a/nix/inputs/dms-cli/assets/dank.svg b/nix/inputs/dms-cli/assets/dank.svg deleted file mode 100644 index 3ce27f7..0000000 --- a/nix/inputs/dms-cli/assets/dank.svg +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/nix/inputs/dms-cli/assets/danklogo.svg b/nix/inputs/dms-cli/assets/danklogo.svg deleted file mode 100644 index 1ee6d51..0000000 --- a/nix/inputs/dms-cli/assets/danklogo.svg +++ /dev/null @@ -1,113 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/nix/inputs/dms-cli/cmd/dms/commands_brightness.go b/nix/inputs/dms-cli/cmd/dms/commands_brightness.go deleted file mode 100644 index f2535cc..0000000 --- a/nix/inputs/dms-cli/cmd/dms/commands_brightness.go +++ /dev/null @@ -1,303 +0,0 @@ -package main - -import ( - "fmt" - "strings" - "time" - - "github.com/AvengeMedia/danklinux/internal/log" - "github.com/AvengeMedia/danklinux/internal/server/brightness" - "github.com/spf13/cobra" -) - -var brightnessCmd = &cobra.Command{ - Use: "brightness", - Short: "Control device brightness", - Long: "Control brightness for backlight and LED devices (use --ddc to include DDC/I2C monitors)", -} - -var brightnessListCmd = &cobra.Command{ - Use: "list", - Short: "List all brightness devices", - Long: "List all available brightness devices with their current values", - Run: runBrightnessList, -} - -var brightnessSetCmd = &cobra.Command{ - Use: "set ", - Short: "Set brightness for a device", - Long: "Set brightness percentage (0-100) for a specific device", - Args: cobra.ExactArgs(2), - Run: runBrightnessSet, -} - -var brightnessGetCmd = &cobra.Command{ - Use: "get ", - Short: "Get brightness for a device", - Long: "Get current brightness percentage for a specific device", - Args: cobra.ExactArgs(1), - Run: runBrightnessGet, -} - -func init() { - brightnessListCmd.Flags().Bool("ddc", false, "Include DDC/I2C monitors (slower)") - brightnessSetCmd.Flags().Bool("ddc", false, "Include DDC/I2C monitors (slower)") - brightnessSetCmd.Flags().Bool("exponential", false, "Use exponential brightness scaling") - brightnessSetCmd.Flags().Float64("exponent", 1.2, "Exponent for exponential scaling (default 1.2)") - brightnessGetCmd.Flags().Bool("ddc", false, "Include DDC/I2C monitors (slower)") - - brightnessCmd.SetHelpTemplate(`{{.Long}} - -Usage: - {{.UseLine}}{{if .HasAvailableSubCommands}} - -Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "help"))}} - {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}} - -Flags: -{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}} - -Global Flags: -{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}} - -Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}} - {{rpad .Name .NamePadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}} - -Use "{{.CommandPath}} [command] --help" for more information about a command.{{end}} -`) - - brightnessListCmd.SetHelpTemplate(`{{.Long}} - -Usage: - {{.UseLine}}{{if .HasAvailableLocalFlags}} - -Flags: -{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}} - -Global Flags: -{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}} -`) - - brightnessSetCmd.SetHelpTemplate(`{{.Long}} - -Usage: - {{.UseLine}}{{if .HasAvailableLocalFlags}} - -Flags: -{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}} - -Global Flags: -{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}} -`) - - brightnessGetCmd.SetHelpTemplate(`{{.Long}} - -Usage: - {{.UseLine}}{{if .HasAvailableLocalFlags}} - -Flags: -{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}} - -Global Flags: -{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}} -`) - - brightnessCmd.AddCommand(brightnessListCmd, brightnessSetCmd, brightnessGetCmd) -} - -func runBrightnessList(cmd *cobra.Command, args []string) { - includeDDC, _ := cmd.Flags().GetBool("ddc") - - allDevices := []brightness.Device{} - - sysfs, err := brightness.NewSysfsBackend() - if err != nil { - log.Debugf("Failed to initialize sysfs backend: %v", err) - } else { - devices, err := sysfs.GetDevices() - if err != nil { - log.Debugf("Failed to get sysfs devices: %v", err) - } else { - allDevices = append(allDevices, devices...) - } - } - - if includeDDC { - ddc, err := brightness.NewDDCBackend() - if err != nil { - fmt.Printf("Warning: Failed to initialize DDC backend: %v\n", err) - } else { - time.Sleep(100 * time.Millisecond) - devices, err := ddc.GetDevices() - if err != nil { - fmt.Printf("Warning: Failed to get DDC devices: %v\n", err) - } else { - allDevices = append(allDevices, devices...) - } - ddc.Close() - } - } - - if len(allDevices) == 0 { - fmt.Println("No brightness devices found") - return - } - - maxIDLen := len("Device") - maxNameLen := len("Name") - for _, dev := range allDevices { - if len(dev.ID) > maxIDLen { - maxIDLen = len(dev.ID) - } - if len(dev.Name) > maxNameLen { - maxNameLen = len(dev.Name) - } - } - - idPad := maxIDLen + 2 - namePad := maxNameLen + 2 - - fmt.Printf("%-*s %-12s %-*s %s\n", idPad, "Device", "Class", namePad, "Name", "Brightness") - - sepLen := idPad + 2 + 12 + 2 + namePad + 2 + 15 - for i := 0; i < sepLen; i++ { - fmt.Print("─") - } - fmt.Println() - - for _, device := range allDevices { - fmt.Printf("%-*s %-12s %-*s %3d%%\n", - idPad, - device.ID, - device.Class, - namePad, - device.Name, - device.CurrentPercent, - ) - } -} - -func runBrightnessSet(cmd *cobra.Command, args []string) { - deviceID := args[0] - var percent int - if _, err := fmt.Sscanf(args[1], "%d", &percent); err != nil { - log.Fatalf("Invalid percent value: %s", args[1]) - } - - if percent < 0 || percent > 100 { - log.Fatalf("Percent must be between 0 and 100") - } - - includeDDC, _ := cmd.Flags().GetBool("ddc") - exponential, _ := cmd.Flags().GetBool("exponential") - exponent, _ := cmd.Flags().GetFloat64("exponent") - - // For backlight/leds devices, try logind backend first (requires D-Bus connection) - parts := strings.SplitN(deviceID, ":", 2) - if len(parts) == 2 && (parts[0] == "backlight" || parts[0] == "leds") { - subsystem := parts[0] - name := parts[1] - - // Initialize backends needed for logind approach - sysfs, err := brightness.NewSysfsBackend() - if err != nil { - log.Debugf("NewSysfsBackend failed: %v", err) - } else { - logind, err := brightness.NewLogindBackend() - if err != nil { - log.Debugf("NewLogindBackend failed: %v", err) - } else { - defer logind.Close() - - // Get device info to convert percent to value - dev, err := sysfs.GetDevice(deviceID) - if err == nil { - // Calculate hardware value using the same logic as Manager.setViaSysfsWithLogind - value := sysfs.PercentToValueWithExponent(percent, dev, exponential, exponent) - - // Call logind with hardware value - if err := logind.SetBrightness(subsystem, name, uint32(value)); err == nil { - log.Debugf("set %s to %d%% (%d) via logind", deviceID, percent, value) - fmt.Printf("Set %s to %d%%\n", deviceID, percent) - return - } else { - log.Debugf("logind.SetBrightness failed: %v", err) - } - } else { - log.Debugf("sysfs.GetDeviceByID failed: %v", err) - } - } - } - } - - // Fallback to direct sysfs (requires write permissions) - sysfs, err := brightness.NewSysfsBackend() - if err == nil { - if err := sysfs.SetBrightnessWithExponent(deviceID, percent, exponential, exponent); err == nil { - fmt.Printf("Set %s to %d%%\n", deviceID, percent) - return - } - log.Debugf("sysfs.SetBrightness failed: %v", err) - } else { - log.Debugf("NewSysfsBackend failed: %v", err) - } - - // Try DDC if requested - if includeDDC { - ddc, err := brightness.NewDDCBackend() - if err == nil { - defer ddc.Close() - time.Sleep(100 * time.Millisecond) - if err := ddc.SetBrightnessWithExponent(deviceID, percent, exponential, exponent, nil); err == nil { - fmt.Printf("Set %s to %d%%\n", deviceID, percent) - return - } - log.Debugf("ddc.SetBrightness failed: %v", err) - } else { - log.Debugf("NewDDCBackend failed: %v", err) - } - } - - log.Fatalf("Failed to set brightness for device: %s", deviceID) -} - -func runBrightnessGet(cmd *cobra.Command, args []string) { - deviceID := args[0] - includeDDC, _ := cmd.Flags().GetBool("ddc") - - allDevices := []brightness.Device{} - - sysfs, err := brightness.NewSysfsBackend() - if err == nil { - devices, err := sysfs.GetDevices() - if err == nil { - allDevices = append(allDevices, devices...) - } - } - - if includeDDC { - ddc, err := brightness.NewDDCBackend() - if err == nil { - defer ddc.Close() - time.Sleep(100 * time.Millisecond) - devices, err := ddc.GetDevices() - if err == nil { - allDevices = append(allDevices, devices...) - } - } - } - - for _, device := range allDevices { - if device.ID == deviceID { - fmt.Printf("%s: %d%% (%d/%d)\n", - device.ID, - device.CurrentPercent, - device.Current, - device.Max, - ) - return - } - } - - log.Fatalf("Device not found: %s", deviceID) -} diff --git a/nix/inputs/dms-cli/cmd/dms/commands_common.go b/nix/inputs/dms-cli/cmd/dms/commands_common.go deleted file mode 100644 index 1ecf2c4..0000000 --- a/nix/inputs/dms-cli/cmd/dms/commands_common.go +++ /dev/null @@ -1,370 +0,0 @@ -package main - -import ( - "fmt" - "strings" - - "github.com/AvengeMedia/danklinux/internal/log" - "github.com/AvengeMedia/danklinux/internal/plugins" - "github.com/AvengeMedia/danklinux/internal/server" - "github.com/spf13/cobra" -) - -var versionCmd = &cobra.Command{ - Use: "version", - Short: "Show version information", - Run: runVersion, -} - -var runCmd = &cobra.Command{ - Use: "run", - Short: "Launch quickshell with DMS configuration", - Long: "Launch quickshell with DMS configuration (qs -c dms)", - Run: func(cmd *cobra.Command, args []string) { - daemon, _ := cmd.Flags().GetBool("daemon") - session, _ := cmd.Flags().GetBool("session") - if daemon { - runShellDaemon(session) - } else { - runShellInteractive(session) - } - }, -} - -var restartCmd = &cobra.Command{ - Use: "restart", - Short: "Restart quickshell with DMS configuration", - Long: "Kill existing DMS shell processes and restart quickshell with DMS configuration", - Run: func(cmd *cobra.Command, args []string) { - restartShell() - }, -} - -var restartDetachedCmd = &cobra.Command{ - Use: "restart-detached ", - Hidden: true, - Args: cobra.ExactArgs(1), - Run: func(cmd *cobra.Command, args []string) { - runDetachedRestart(args[0]) - }, -} - -var killCmd = &cobra.Command{ - Use: "kill", - Short: "Kill running DMS shell processes", - Long: "Kill all running quickshell processes with DMS configuration", - Run: func(cmd *cobra.Command, args []string) { - killShell() - }, -} - -var ipcCmd = &cobra.Command{ - Use: "ipc", - Short: "Send IPC commands to running DMS shell", - Long: "Send IPC commands to running DMS shell (qs -c dms ipc )", - Run: func(cmd *cobra.Command, args []string) { - runShellIPCCommand(args) - }, -} - -var debugSrvCmd = &cobra.Command{ - Use: "debug-srv", - Short: "Start the debug server", - Long: "Start the Unix socket debug server for DMS", - Run: func(cmd *cobra.Command, args []string) { - if err := startDebugServer(); err != nil { - log.Fatalf("Error starting debug server: %v", err) - } - }, -} - -var pluginsCmd = &cobra.Command{ - Use: "plugins", - Short: "Manage DMS plugins", - Long: "Browse and manage DMS plugins from the registry", -} - -var pluginsBrowseCmd = &cobra.Command{ - Use: "browse", - Short: "Browse available plugins", - Long: "Browse available plugins from the DMS plugin registry", - Run: func(cmd *cobra.Command, args []string) { - if err := browsePlugins(); err != nil { - log.Fatalf("Error browsing plugins: %v", err) - } - }, -} - -var pluginsListCmd = &cobra.Command{ - Use: "list", - Short: "List installed plugins", - Long: "List all installed DMS plugins", - Run: func(cmd *cobra.Command, args []string) { - if err := listInstalledPlugins(); err != nil { - log.Fatalf("Error listing plugins: %v", err) - } - }, -} - -var pluginsInstallCmd = &cobra.Command{ - Use: "install ", - Short: "Install a plugin by ID", - Long: "Install a DMS plugin from the registry using its ID (e.g., 'myPlugin'). Plugin names with spaces are also supported for backward compatibility.", - Args: cobra.ExactArgs(1), - Run: func(cmd *cobra.Command, args []string) { - if err := installPluginCLI(args[0]); err != nil { - log.Fatalf("Error installing plugin: %v", err) - } - }, -} - -var pluginsUninstallCmd = &cobra.Command{ - Use: "uninstall ", - Short: "Uninstall a plugin by ID", - Long: "Uninstall a DMS plugin using its ID (e.g., 'myPlugin'). Plugin names with spaces are also supported for backward compatibility.", - Args: cobra.ExactArgs(1), - Run: func(cmd *cobra.Command, args []string) { - if err := uninstallPluginCLI(args[0]); err != nil { - log.Fatalf("Error uninstalling plugin: %v", err) - } - }, -} - -func runVersion(cmd *cobra.Command, args []string) { - printASCII() - fmt.Printf("%s\n", Version) -} - -func startDebugServer() error { - return server.Start(true) -} - -func browsePlugins() error { - registry, err := plugins.NewRegistry() - if err != nil { - return fmt.Errorf("failed to create registry: %w", err) - } - - manager, err := plugins.NewManager() - if err != nil { - return fmt.Errorf("failed to create manager: %w", err) - } - - fmt.Println("Fetching plugin registry...") - pluginList, err := registry.List() - if err != nil { - return fmt.Errorf("failed to list plugins: %w", err) - } - - if len(pluginList) == 0 { - fmt.Println("No plugins found in registry.") - return nil - } - - fmt.Printf("\nAvailable Plugins (%d):\n\n", len(pluginList)) - for _, plugin := range pluginList { - installed, _ := manager.IsInstalled(plugin) - installedMarker := "" - if installed { - installedMarker = " [Installed]" - } - - fmt.Printf(" %s%s\n", plugin.Name, installedMarker) - fmt.Printf(" ID: %s\n", plugin.ID) - fmt.Printf(" Category: %s\n", plugin.Category) - fmt.Printf(" Author: %s\n", plugin.Author) - fmt.Printf(" Description: %s\n", plugin.Description) - fmt.Printf(" Repository: %s\n", plugin.Repo) - if len(plugin.Capabilities) > 0 { - fmt.Printf(" Capabilities: %s\n", strings.Join(plugin.Capabilities, ", ")) - } - if len(plugin.Compositors) > 0 { - fmt.Printf(" Compositors: %s\n", strings.Join(plugin.Compositors, ", ")) - } - if len(plugin.Dependencies) > 0 { - fmt.Printf(" Dependencies: %s\n", strings.Join(plugin.Dependencies, ", ")) - } - fmt.Println() - } - - return nil -} - -func listInstalledPlugins() error { - manager, err := plugins.NewManager() - if err != nil { - return fmt.Errorf("failed to create manager: %w", err) - } - - registry, err := plugins.NewRegistry() - if err != nil { - return fmt.Errorf("failed to create registry: %w", err) - } - - installedNames, err := manager.ListInstalled() - if err != nil { - return fmt.Errorf("failed to list installed plugins: %w", err) - } - - if len(installedNames) == 0 { - fmt.Println("No plugins installed.") - return nil - } - - allPlugins, err := registry.List() - if err != nil { - return fmt.Errorf("failed to list plugins: %w", err) - } - - pluginMap := make(map[string]plugins.Plugin) - for _, p := range allPlugins { - pluginMap[p.ID] = p - } - - fmt.Printf("\nInstalled Plugins (%d):\n\n", len(installedNames)) - for _, id := range installedNames { - if plugin, ok := pluginMap[id]; ok { - fmt.Printf(" %s\n", plugin.Name) - fmt.Printf(" ID: %s\n", plugin.ID) - fmt.Printf(" Category: %s\n", plugin.Category) - fmt.Printf(" Author: %s\n", plugin.Author) - fmt.Println() - } else { - fmt.Printf(" %s (not in registry)\n\n", id) - } - } - - return nil -} - -func installPluginCLI(idOrName string) error { - registry, err := plugins.NewRegistry() - if err != nil { - return fmt.Errorf("failed to create registry: %w", err) - } - - manager, err := plugins.NewManager() - if err != nil { - return fmt.Errorf("failed to create manager: %w", err) - } - - pluginList, err := registry.List() - if err != nil { - return fmt.Errorf("failed to list plugins: %w", err) - } - - // First, try to find by ID (preferred method) - var plugin *plugins.Plugin - for _, p := range pluginList { - if p.ID == idOrName { - plugin = &p - break - } - } - - // Fallback to name for backward compatibility - if plugin == nil { - for _, p := range pluginList { - if p.Name == idOrName { - plugin = &p - break - } - } - } - - if plugin == nil { - return fmt.Errorf("plugin not found: %s", idOrName) - } - - installed, err := manager.IsInstalled(*plugin) - if err != nil { - return fmt.Errorf("failed to check install status: %w", err) - } - - if installed { - return fmt.Errorf("plugin already installed: %s", plugin.Name) - } - - fmt.Printf("Installing plugin: %s (ID: %s)\n", plugin.Name, plugin.ID) - if err := manager.Install(*plugin); err != nil { - return fmt.Errorf("failed to install plugin: %w", err) - } - - fmt.Printf("Plugin installed successfully: %s\n", plugin.Name) - return nil -} - -func uninstallPluginCLI(idOrName string) error { - manager, err := plugins.NewManager() - if err != nil { - return fmt.Errorf("failed to create manager: %w", err) - } - - registry, err := plugins.NewRegistry() - if err != nil { - return fmt.Errorf("failed to create registry: %w", err) - } - - pluginList, err := registry.List() - if err != nil { - return fmt.Errorf("failed to list plugins: %w", err) - } - - // First, try to find by ID (preferred method) - var plugin *plugins.Plugin - for _, p := range pluginList { - if p.ID == idOrName { - plugin = &p - break - } - } - - // Fallback to name for backward compatibility - if plugin == nil { - for _, p := range pluginList { - if p.Name == idOrName { - plugin = &p - break - } - } - } - - if plugin == nil { - return fmt.Errorf("plugin not found: %s", idOrName) - } - - installed, err := manager.IsInstalled(*plugin) - if err != nil { - return fmt.Errorf("failed to check install status: %w", err) - } - - if !installed { - return fmt.Errorf("plugin not installed: %s", plugin.Name) - } - - fmt.Printf("Uninstalling plugin: %s (ID: %s)\n", plugin.Name, plugin.ID) - if err := manager.Uninstall(*plugin); err != nil { - return fmt.Errorf("failed to uninstall plugin: %w", err) - } - - fmt.Printf("Plugin uninstalled successfully: %s\n", plugin.Name) - return nil -} - -// getCommonCommands returns the commands available in all builds -func getCommonCommands() []*cobra.Command { - return []*cobra.Command{ - versionCmd, - runCmd, - restartCmd, - restartDetachedCmd, - killCmd, - ipcCmd, - debugSrvCmd, - pluginsCmd, - dank16Cmd, - brightnessCmd, - hyprlandCmd, - greeterCmd, - } -} diff --git a/nix/inputs/dms-cli/cmd/dms/commands_dank16.go b/nix/inputs/dms-cli/cmd/dms/commands_dank16.go deleted file mode 100644 index 20e3b55..0000000 --- a/nix/inputs/dms-cli/cmd/dms/commands_dank16.go +++ /dev/null @@ -1,90 +0,0 @@ -package main - -import ( - "fmt" - "os" - "strings" - - "github.com/AvengeMedia/danklinux/internal/dank16" - "github.com/AvengeMedia/danklinux/internal/log" - "github.com/spf13/cobra" -) - -var dank16Cmd = &cobra.Command{ - Use: "dank16 ", - Short: "Generate Base16 color palettes", - Long: "Generate Base16 color palettes from a color with support for various output formats", - Args: cobra.ExactArgs(1), - Run: runDank16, -} - -func init() { - dank16Cmd.Flags().Bool("light", false, "Generate light theme variant") - dank16Cmd.Flags().Bool("json", false, "Output in JSON format") - dank16Cmd.Flags().Bool("kitty", false, "Output in Kitty terminal format") - dank16Cmd.Flags().Bool("foot", false, "Output in Foot terminal format") - dank16Cmd.Flags().Bool("alacritty", false, "Output in Alacritty terminal format") - dank16Cmd.Flags().Bool("ghostty", false, "Output in Ghostty terminal format") - dank16Cmd.Flags().String("vscode-enrich", "", "Enrich existing VSCode theme file with terminal colors") - dank16Cmd.Flags().String("background", "", "Custom background color") - dank16Cmd.Flags().String("contrast", "dps", "Contrast algorithm: dps (Delta Phi Star, default) or wcag") -} - -func runDank16(cmd *cobra.Command, args []string) { - primaryColor := args[0] - if !strings.HasPrefix(primaryColor, "#") { - primaryColor = "#" + primaryColor - } - - isLight, _ := cmd.Flags().GetBool("light") - isJson, _ := cmd.Flags().GetBool("json") - isKitty, _ := cmd.Flags().GetBool("kitty") - isFoot, _ := cmd.Flags().GetBool("foot") - isAlacritty, _ := cmd.Flags().GetBool("alacritty") - isGhostty, _ := cmd.Flags().GetBool("ghostty") - vscodeEnrich, _ := cmd.Flags().GetString("vscode-enrich") - background, _ := cmd.Flags().GetString("background") - contrastAlgo, _ := cmd.Flags().GetString("contrast") - - if background != "" && !strings.HasPrefix(background, "#") { - background = "#" + background - } - - contrastAlgo = strings.ToLower(contrastAlgo) - if contrastAlgo != "dps" && contrastAlgo != "wcag" { - log.Fatalf("Invalid contrast algorithm: %s (must be 'dps' or 'wcag')", contrastAlgo) - } - - opts := dank16.PaletteOptions{ - IsLight: isLight, - Background: background, - UseDPS: contrastAlgo == "dps", - } - - colors := dank16.GeneratePalette(primaryColor, opts) - - if vscodeEnrich != "" { - data, err := os.ReadFile(vscodeEnrich) - if err != nil { - log.Fatalf("Error reading file: %v", err) - } - - enriched, err := dank16.EnrichVSCodeTheme(data, colors) - if err != nil { - log.Fatalf("Error enriching theme: %v", err) - } - fmt.Println(string(enriched)) - } else if isJson { - fmt.Print(dank16.GenerateJSON(colors)) - } else if isKitty { - fmt.Print(dank16.GenerateKittyTheme(colors)) - } else if isFoot { - fmt.Print(dank16.GenerateFootTheme(colors)) - } else if isAlacritty { - fmt.Print(dank16.GenerateAlacrittyTheme(colors)) - } else if isGhostty { - fmt.Print(dank16.GenerateGhosttyTheme(colors)) - } else { - fmt.Print(dank16.GenerateGhosttyTheme(colors)) - } -} diff --git a/nix/inputs/dms-cli/cmd/dms/commands_features.go b/nix/inputs/dms-cli/cmd/dms/commands_features.go deleted file mode 100644 index cbd35eb..0000000 --- a/nix/inputs/dms-cli/cmd/dms/commands_features.go +++ /dev/null @@ -1,487 +0,0 @@ -//go:build !distro_binary - -package main - -import ( - "bufio" - "errors" - "fmt" - "os" - "os/exec" - "path/filepath" - "strings" - "time" - - "github.com/AvengeMedia/danklinux/internal/distros" - "github.com/AvengeMedia/danklinux/internal/errdefs" - "github.com/AvengeMedia/danklinux/internal/log" - "github.com/AvengeMedia/danklinux/internal/version" - "github.com/spf13/cobra" -) - -var updateCmd = &cobra.Command{ - Use: "update", - Short: "Update DankMaterialShell to the latest version", - Long: "Update DankMaterialShell to the latest version using the appropriate package manager for your distribution", - Run: func(cmd *cobra.Command, args []string) { - runUpdate() - }, -} - -var updateCheckCmd = &cobra.Command{ - Use: "check", - Short: "Check if updates are available for DankMaterialShell", - Long: "Check for available updates without performing the actual update", - Run: func(cmd *cobra.Command, args []string) { - runUpdateCheck() - }, -} - -func runUpdateCheck() { - fmt.Println("Checking for DankMaterialShell updates...") - fmt.Println() - - versionInfo, err := version.GetDMSVersionInfo() - if err != nil { - log.Fatalf("Error checking for updates: %v", err) - } - - fmt.Printf("Current version: %s\n", versionInfo.Current) - fmt.Printf("Latest version: %s\n", versionInfo.Latest) - fmt.Println() - - if versionInfo.HasUpdate { - fmt.Println("✓ Update available!") - fmt.Println() - fmt.Println("Run 'dms update' to install the latest version.") - os.Exit(0) - } else { - fmt.Println("✓ You are running the latest version.") - os.Exit(0) - } -} - -func runUpdate() { - osInfo, err := distros.GetOSInfo() - if err != nil { - log.Fatalf("Error detecting OS: %v", err) - } - - config, exists := distros.Registry[osInfo.Distribution.ID] - if !exists { - log.Fatalf("Unsupported distribution: %s", osInfo.Distribution.ID) - } - - var updateErr error - switch config.Family { - case distros.FamilyArch: - updateErr = updateArchLinux() - case distros.FamilyNix: - updateErr = updateNixOS() - case distros.FamilySUSE: - updateErr = updateOtherDistros() - default: - updateErr = updateOtherDistros() - } - - if updateErr != nil { - if errors.Is(updateErr, errdefs.ErrUpdateCancelled) { - log.Info("Update cancelled.") - return - } - if errors.Is(updateErr, errdefs.ErrNoUpdateNeeded) { - return - } - log.Fatalf("Error updating DMS: %v", updateErr) - } - - log.Info("Update complete! Restarting DMS...") - restartShell() -} - -func updateArchLinux() error { - homeDir, err := os.UserHomeDir() - if err == nil { - dmsPath := filepath.Join(homeDir, ".config", "quickshell", "dms") - if _, err := os.Stat(dmsPath); err == nil { - return updateOtherDistros() - } - } - - var packageName string - if isArchPackageInstalled("dms-shell-bin") { - packageName = "dms-shell-bin" - } else if isArchPackageInstalled("dms-shell-git") { - packageName = "dms-shell-git" - } else { - fmt.Println("Info: Neither dms-shell-bin nor dms-shell-git package found.") - fmt.Println("Info: Falling back to git-based update method...") - return updateOtherDistros() - } - - var helper string - var updateCmd *exec.Cmd - - if commandExists("yay") { - helper = "yay" - updateCmd = exec.Command("yay", "-S", packageName) - } else if commandExists("paru") { - helper = "paru" - updateCmd = exec.Command("paru", "-S", packageName) - } else { - fmt.Println("Error: Neither yay nor paru found - please install an AUR helper") - fmt.Println("Info: Falling back to git-based update method...") - return updateOtherDistros() - } - - fmt.Printf("This will update DankMaterialShell using %s.\n", helper) - if !confirmUpdate() { - return errdefs.ErrUpdateCancelled - } - - fmt.Printf("\nRunning: %s -S %s\n", helper, packageName) - updateCmd.Stdout = os.Stdout - updateCmd.Stderr = os.Stderr - err = updateCmd.Run() - if err != nil { - fmt.Printf("Error: Failed to update using %s: %v\n", helper, err) - } - - fmt.Println("dms successfully updated") - return nil -} - -func updateNixOS() error { - fmt.Println("This will update DankMaterialShell using nix profile.") - if !confirmUpdate() { - return errdefs.ErrUpdateCancelled - } - - fmt.Println("\nRunning: nix profile upgrade github:AvengeMedia/DankMaterialShell") - updateCmd := exec.Command("nix", "profile", "upgrade", "github:AvengeMedia/DankMaterialShell") - updateCmd.Stdout = os.Stdout - updateCmd.Stderr = os.Stderr - err := updateCmd.Run() - if err != nil { - fmt.Printf("Error: Failed to update using nix profile: %v\n", err) - fmt.Println("Falling back to git-based update method...") - return updateOtherDistros() - } - - fmt.Println("dms successfully updated") - return nil -} - -func updateOtherDistros() error { - homeDir, err := os.UserHomeDir() - if err != nil { - return fmt.Errorf("failed to get user home directory: %w", err) - } - - dmsPath := filepath.Join(homeDir, ".config", "quickshell", "dms") - - if _, err := os.Stat(dmsPath); os.IsNotExist(err) { - return fmt.Errorf("DMS configuration directory not found at %s", dmsPath) - } - - fmt.Printf("Found DMS configuration at %s\n", dmsPath) - - versionInfo, err := version.GetDMSVersionInfo() - if err == nil && !versionInfo.HasUpdate { - fmt.Println() - fmt.Printf("Current version: %s\n", versionInfo.Current) - fmt.Printf("Latest version: %s\n", versionInfo.Latest) - fmt.Println() - fmt.Println("✓ You are already running the latest version.") - return errdefs.ErrNoUpdateNeeded - } - - fmt.Println("\nThis will update:") - fmt.Println(" 1. The dms binary from GitHub releases") - fmt.Println(" 2. DankMaterialShell configuration using git") - if !confirmUpdate() { - return errdefs.ErrUpdateCancelled - } - - fmt.Println("\n=== Updating dms binary ===") - if err := updateDMSBinary(); err != nil { - fmt.Printf("Warning: Failed to update dms binary: %v\n", err) - fmt.Println("Continuing with shell configuration update...") - } else { - fmt.Println("dms binary successfully updated") - } - - fmt.Println("\n=== Updating DMS shell configuration ===") - - if err := os.Chdir(dmsPath); err != nil { - return fmt.Errorf("failed to change to DMS directory: %w", err) - } - - statusCmd := exec.Command("git", "status", "--porcelain") - statusOutput, _ := statusCmd.Output() - hasLocalChanges := len(strings.TrimSpace(string(statusOutput))) > 0 - - currentRefCmd := exec.Command("git", "symbolic-ref", "-q", "HEAD") - currentRefOutput, _ := currentRefCmd.Output() - onBranch := len(currentRefOutput) > 0 - - var currentTag string - var currentBranch string - - if !onBranch { - tagCmd := exec.Command("git", "describe", "--exact-match", "--tags", "HEAD") - if tagOutput, err := tagCmd.Output(); err == nil { - currentTag = strings.TrimSpace(string(tagOutput)) - } - } else { - branchCmd := exec.Command("git", "rev-parse", "--abbrev-ref", "HEAD") - if branchOutput, err := branchCmd.Output(); err == nil { - currentBranch = strings.TrimSpace(string(branchOutput)) - } - } - - fmt.Println("Fetching latest changes...") - fetchCmd := exec.Command("git", "fetch", "origin", "--tags", "--force") - fetchCmd.Stdout = os.Stdout - fetchCmd.Stderr = os.Stderr - if err := fetchCmd.Run(); err != nil { - return fmt.Errorf("failed to fetch changes: %w", err) - } - - if currentTag != "" { - latestTagCmd := exec.Command("git", "tag", "-l", "v0.1.*", "--sort=-version:refname") - latestTagOutput, err := latestTagCmd.Output() - if err != nil { - return fmt.Errorf("failed to get latest tag: %w", err) - } - - tags := strings.Split(strings.TrimSpace(string(latestTagOutput)), "\n") - if len(tags) == 0 || tags[0] == "" { - return fmt.Errorf("no v0.1.* tags found") - } - latestTag := tags[0] - - if latestTag == currentTag { - fmt.Printf("Already on latest tag: %s\n", currentTag) - return nil - } - - fmt.Printf("Current tag: %s\n", currentTag) - fmt.Printf("Latest tag: %s\n", latestTag) - - if hasLocalChanges { - fmt.Println("\nWarning: You have local changes in your DMS configuration.") - if offerReclone(dmsPath) { - return nil - } - return errdefs.ErrUpdateCancelled - } - - fmt.Printf("Updating to %s...\n", latestTag) - checkoutCmd := exec.Command("git", "checkout", latestTag) - checkoutCmd.Stdout = os.Stdout - checkoutCmd.Stderr = os.Stderr - if err := checkoutCmd.Run(); err != nil { - fmt.Printf("Error: Failed to checkout %s: %v\n", latestTag, err) - if offerReclone(dmsPath) { - return nil - } - return fmt.Errorf("update cancelled") - } - - fmt.Printf("\nUpdate complete! Updated from %s to %s\n", currentTag, latestTag) - return nil - } - - if currentBranch == "" { - currentBranch = "master" - } - - fmt.Printf("Current branch: %s\n", currentBranch) - - if hasLocalChanges { - fmt.Println("\nWarning: You have local changes in your DMS configuration.") - if offerReclone(dmsPath) { - return nil - } - return errdefs.ErrUpdateCancelled - } - - pullCmd := exec.Command("git", "pull", "origin", currentBranch) - pullCmd.Stdout = os.Stdout - pullCmd.Stderr = os.Stderr - if err := pullCmd.Run(); err != nil { - fmt.Printf("Error: Failed to pull latest changes: %v\n", err) - if offerReclone(dmsPath) { - return nil - } - return fmt.Errorf("update cancelled") - } - - fmt.Println("\nUpdate complete!") - return nil -} - -func offerReclone(dmsPath string) bool { - fmt.Println("\nWould you like to backup and re-clone the repository? (y/N): ") - reader := bufio.NewReader(os.Stdin) - response, err := reader.ReadString('\n') - if err != nil || !strings.HasPrefix(strings.ToLower(strings.TrimSpace(response)), "y") { - return false - } - - timestamp := time.Now().Unix() - backupPath := fmt.Sprintf("%s.backup-%d", dmsPath, timestamp) - - fmt.Printf("Backing up current directory to %s...\n", backupPath) - if err := os.Rename(dmsPath, backupPath); err != nil { - fmt.Printf("Error: Failed to backup directory: %v\n", err) - return false - } - - fmt.Println("Cloning fresh copy...") - cloneCmd := exec.Command("git", "clone", "https://github.com/AvengeMedia/DankMaterialShell.git", dmsPath) - cloneCmd.Stdout = os.Stdout - cloneCmd.Stderr = os.Stderr - if err := cloneCmd.Run(); err != nil { - fmt.Printf("Error: Failed to clone repository: %v\n", err) - fmt.Printf("Restoring backup...\n") - os.Rename(backupPath, dmsPath) - return false - } - - fmt.Printf("Successfully re-cloned repository (backup at %s)\n", backupPath) - return true -} - -func confirmUpdate() bool { - fmt.Print("Do you want to proceed with the update? (y/N): ") - reader := bufio.NewReader(os.Stdin) - response, err := reader.ReadString('\n') - if err != nil { - fmt.Printf("Error reading input: %v\n", err) - return false - } - response = strings.TrimSpace(strings.ToLower(response)) - return response == "y" || response == "yes" -} - -func updateDMSBinary() error { - arch := "" - switch strings.ToLower(os.Getenv("HOSTTYPE")) { - case "x86_64", "amd64": - arch = "amd64" - case "aarch64", "arm64": - arch = "arm64" - default: - cmd := exec.Command("uname", "-m") - output, err := cmd.Output() - if err != nil { - return fmt.Errorf("failed to detect architecture: %w", err) - } - archStr := strings.TrimSpace(string(output)) - switch archStr { - case "x86_64": - arch = "amd64" - case "aarch64": - arch = "arm64" - default: - return fmt.Errorf("unsupported architecture: %s", archStr) - } - } - - fmt.Println("Fetching latest release version...") - cmd := exec.Command("curl", "-s", "https://api.github.com/repos/AvengeMedia/danklinux/releases/latest") - output, err := cmd.Output() - if err != nil { - return fmt.Errorf("failed to fetch latest release: %w", err) - } - - version := "" - for _, line := range strings.Split(string(output), "\n") { - if strings.Contains(line, "\"tag_name\"") { - parts := strings.Split(line, "\"") - if len(parts) >= 4 { - version = parts[3] - break - } - } - } - - if version == "" { - return fmt.Errorf("could not determine latest version") - } - - fmt.Printf("Latest version: %s\n", version) - - tempDir, err := os.MkdirTemp("", "dms-update-*") - if err != nil { - return fmt.Errorf("failed to create temp directory: %w", err) - } - defer os.RemoveAll(tempDir) - - binaryURL := fmt.Sprintf("https://github.com/AvengeMedia/danklinux/releases/download/%s/dms-%s.gz", version, arch) - checksumURL := fmt.Sprintf("https://github.com/AvengeMedia/danklinux/releases/download/%s/dms-%s.gz.sha256", version, arch) - - binaryPath := filepath.Join(tempDir, "dms.gz") - checksumPath := filepath.Join(tempDir, "dms.gz.sha256") - - fmt.Println("Downloading dms binary...") - downloadCmd := exec.Command("curl", "-L", binaryURL, "-o", binaryPath) - if err := downloadCmd.Run(); err != nil { - return fmt.Errorf("failed to download binary: %w", err) - } - - fmt.Println("Downloading checksum...") - downloadCmd = exec.Command("curl", "-L", checksumURL, "-o", checksumPath) - if err := downloadCmd.Run(); err != nil { - return fmt.Errorf("failed to download checksum: %w", err) - } - - fmt.Println("Verifying checksum...") - checksumData, err := os.ReadFile(checksumPath) - if err != nil { - return fmt.Errorf("failed to read checksum file: %w", err) - } - expectedChecksum := strings.Fields(string(checksumData))[0] - - actualCmd := exec.Command("sha256sum", binaryPath) - actualOutput, err := actualCmd.Output() - if err != nil { - return fmt.Errorf("failed to calculate checksum: %w", err) - } - actualChecksum := strings.Fields(string(actualOutput))[0] - - if expectedChecksum != actualChecksum { - return fmt.Errorf("checksum verification failed\nExpected: %s\nGot: %s", expectedChecksum, actualChecksum) - } - - fmt.Println("Decompressing binary...") - decompressCmd := exec.Command("gunzip", binaryPath) - if err := decompressCmd.Run(); err != nil { - return fmt.Errorf("failed to decompress binary: %w", err) - } - - decompressedPath := filepath.Join(tempDir, "dms") - - if err := os.Chmod(decompressedPath, 0755); err != nil { - return fmt.Errorf("failed to make binary executable: %w", err) - } - - currentPath, err := exec.LookPath("dms") - if err != nil { - return fmt.Errorf("could not find current dms binary: %w", err) - } - - fmt.Printf("Installing to %s...\n", currentPath) - - replaceCmd := exec.Command("sudo", "install", "-m", "0755", decompressedPath, currentPath) - replaceCmd.Stdin = os.Stdin - replaceCmd.Stdout = os.Stdout - replaceCmd.Stderr = os.Stderr - if err := replaceCmd.Run(); err != nil { - return fmt.Errorf("failed to replace binary: %w", err) - } - - return nil -} diff --git a/nix/inputs/dms-cli/cmd/dms/commands_greeter.go b/nix/inputs/dms-cli/cmd/dms/commands_greeter.go deleted file mode 100644 index d0bff4e..0000000 --- a/nix/inputs/dms-cli/cmd/dms/commands_greeter.go +++ /dev/null @@ -1,500 +0,0 @@ -package main - -import ( - "fmt" - "os" - "os/exec" - "os/user" - "path/filepath" - "strings" - - "github.com/AvengeMedia/danklinux/internal/greeter" - "github.com/AvengeMedia/danklinux/internal/log" - "github.com/spf13/cobra" -) - -var greeterCmd = &cobra.Command{ - Use: "greeter", - Short: "Manage DMS greeter", - Long: "Manage DMS greeter (greetd)", -} - -var greeterInstallCmd = &cobra.Command{ - Use: "install", - Short: "Install and configure DMS greeter", - Long: "Install greetd and configure it to use DMS as the greeter interface", - Run: func(cmd *cobra.Command, args []string) { - if err := installGreeter(); err != nil { - log.Fatalf("Error installing greeter: %v", err) - } - }, -} - -var greeterSyncCmd = &cobra.Command{ - Use: "sync", - Short: "Sync DMS theme and settings with greeter", - Long: "Synchronize your current user's DMS theme, settings, and wallpaper configuration with the login greeter screen", - Run: func(cmd *cobra.Command, args []string) { - if err := syncGreeter(); err != nil { - log.Fatalf("Error syncing greeter: %v", err) - } - }, -} - -var greeterEnableCmd = &cobra.Command{ - Use: "enable", - Short: "Enable DMS greeter in greetd config", - Long: "Configure greetd to use DMS as the greeter", - Run: func(cmd *cobra.Command, args []string) { - if err := enableGreeter(); err != nil { - log.Fatalf("Error enabling greeter: %v", err) - } - }, -} - -var greeterStatusCmd = &cobra.Command{ - Use: "status", - Short: "Check greeter sync status", - Long: "Check the status of greeter installation and configuration sync", - Run: func(cmd *cobra.Command, args []string) { - if err := checkGreeterStatus(); err != nil { - log.Fatalf("Error checking greeter status: %v", err) - } - }, -} - -func installGreeter() error { - fmt.Println("=== DMS Greeter Installation ===") - - logFunc := func(msg string) { - fmt.Println(msg) - } - - if err := greeter.EnsureGreetdInstalled(logFunc, ""); err != nil { - return err - } - - fmt.Println("\nDetecting DMS installation...") - dmsPath, err := greeter.DetectDMSPath() - if err != nil { - return err - } - fmt.Printf("✓ Found DMS at: %s\n", dmsPath) - - fmt.Println("\nDetecting installed compositors...") - compositors := greeter.DetectCompositors() - if len(compositors) == 0 { - return fmt.Errorf("no supported compositors found (niri or Hyprland required)") - } - - var selectedCompositor string - if len(compositors) == 1 { - selectedCompositor = compositors[0] - fmt.Printf("✓ Found compositor: %s\n", selectedCompositor) - } else { - var err error - selectedCompositor, err = greeter.PromptCompositorChoice(compositors) - if err != nil { - return err - } - fmt.Printf("✓ Selected compositor: %s\n", selectedCompositor) - } - - fmt.Println("\nSetting up dms-greeter group and permissions...") - if err := greeter.SetupDMSGroup(logFunc, ""); err != nil { - return err - } - - fmt.Println("\nCopying greeter files...") - if err := greeter.CopyGreeterFiles(dmsPath, selectedCompositor, logFunc, ""); err != nil { - return err - } - - fmt.Println("\nConfiguring greetd...") - if err := greeter.ConfigureGreetd(dmsPath, selectedCompositor, logFunc, ""); err != nil { - return err - } - - fmt.Println("\nSynchronizing DMS configurations...") - if err := greeter.SyncDMSConfigs(dmsPath, logFunc, ""); err != nil { - return err - } - - fmt.Println("\n=== Installation Complete ===") - fmt.Println("\nTo test the greeter, run:") - fmt.Println(" sudo systemctl start greetd") - fmt.Println("\nTo enable on boot, run:") - fmt.Println(" sudo systemctl enable --now greetd") - - return nil -} - -func syncGreeter() error { - fmt.Println("=== DMS Greeter Theme Sync ===") - fmt.Println() - - logFunc := func(msg string) { - fmt.Println(msg) - } - - fmt.Println("Detecting DMS installation...") - dmsPath, err := greeter.DetectDMSPath() - if err != nil { - return err - } - fmt.Printf("✓ Found DMS at: %s\n", dmsPath) - - cacheDir := "/var/cache/dms-greeter" - if _, err := os.Stat(cacheDir); os.IsNotExist(err) { - return fmt.Errorf("greeter cache directory not found at %s\nPlease install the greeter first", cacheDir) - } - - greeterGroupExists := checkGroupExists("greeter") - if greeterGroupExists { - currentUser, err := user.Current() - if err != nil { - return fmt.Errorf("failed to get current user: %w", err) - } - - groupsCmd := exec.Command("groups", currentUser.Username) - groupsOutput, err := groupsCmd.Output() - if err != nil { - return fmt.Errorf("failed to check groups: %w", err) - } - - inGreeterGroup := strings.Contains(string(groupsOutput), "greeter") - if !inGreeterGroup { - fmt.Println("\n⚠ Warning: You are not in the greeter group.") - fmt.Print("Would you like to add your user to the greeter group? (y/N): ") - - var response string - fmt.Scanln(&response) - response = strings.ToLower(strings.TrimSpace(response)) - - if response == "y" || response == "yes" { - fmt.Println("\nAdding user to greeter group...") - addUserCmd := exec.Command("sudo", "usermod", "-aG", "greeter", currentUser.Username) - addUserCmd.Stdout = os.Stdout - addUserCmd.Stderr = os.Stderr - if err := addUserCmd.Run(); err != nil { - return fmt.Errorf("failed to add user to greeter group: %w", err) - } - fmt.Println("✓ User added to greeter group") - fmt.Println("⚠ You will need to log out and back in for the group change to take effect") - } - } - } - - fmt.Println("\nSetting up permissions and ACLs...") - if err := greeter.SetupDMSGroup(logFunc, ""); err != nil { - return err - } - - fmt.Println("\nSynchronizing DMS configurations...") - if err := greeter.SyncDMSConfigs(dmsPath, logFunc, ""); err != nil { - return err - } - - fmt.Println("\n=== Sync Complete ===") - fmt.Println("\nYour theme, settings, and wallpaper configuration have been synced with the greeter.") - fmt.Println("The changes will be visible on the next login screen.") - - return nil -} - -func checkGroupExists(groupName string) bool { - data, err := os.ReadFile("/etc/group") - if err != nil { - return false - } - - lines := strings.Split(string(data), "\n") - for _, line := range lines { - if strings.HasPrefix(line, groupName+":") { - return true - } - } - return false -} - -func enableGreeter() error { - fmt.Println("=== DMS Greeter Enable ===") - fmt.Println() - - configPath := "/etc/greetd/config.toml" - if _, err := os.Stat(configPath); os.IsNotExist(err) { - return fmt.Errorf("greetd config not found at %s\nPlease install greetd first", configPath) - } - - data, err := os.ReadFile(configPath) - if err != nil { - return fmt.Errorf("failed to read greetd config: %w", err) - } - - configContent := string(data) - if strings.Contains(configContent, "dms-greeter") { - fmt.Println("✓ Greeter is already configured with dms-greeter") - return nil - } - - fmt.Println("Detecting installed compositors...") - compositors := greeter.DetectCompositors() - - if commandExists("sway") { - compositors = append(compositors, "sway") - } - - if len(compositors) == 0 { - return fmt.Errorf("no supported compositors found (niri, Hyprland, or sway required)") - } - - var selectedCompositor string - if len(compositors) == 1 { - selectedCompositor = compositors[0] - fmt.Printf("✓ Found compositor: %s\n", selectedCompositor) - } else { - var err error - selectedCompositor, err = promptCompositorChoice(compositors) - if err != nil { - return err - } - fmt.Printf("✓ Selected compositor: %s\n", selectedCompositor) - } - - backupPath := configPath + ".backup" - backupCmd := exec.Command("sudo", "cp", configPath, backupPath) - if err := backupCmd.Run(); err != nil { - return fmt.Errorf("failed to backup config: %w", err) - } - fmt.Printf("✓ Backed up config to %s\n", backupPath) - - lines := strings.Split(configContent, "\n") - var newLines []string - for _, line := range lines { - trimmed := strings.TrimSpace(line) - if !strings.HasPrefix(trimmed, "command =") && !strings.HasPrefix(trimmed, "command=") { - newLines = append(newLines, line) - } - } - - wrapperCmd := "dms-greeter" - if !commandExists("dms-greeter") { - wrapperCmd = "/usr/local/bin/dms-greeter" - } - - compositorLower := strings.ToLower(selectedCompositor) - commandLine := fmt.Sprintf(`command = "%s --command %s"`, wrapperCmd, compositorLower) - - var finalLines []string - inDefaultSession := false - commandAdded := false - - for _, line := range newLines { - finalLines = append(finalLines, line) - trimmed := strings.TrimSpace(line) - - if trimmed == "[default_session]" { - inDefaultSession = true - } - - if inDefaultSession && !commandAdded { - if strings.HasPrefix(trimmed, "user =") || strings.HasPrefix(trimmed, "user=") { - finalLines = append(finalLines, commandLine) - commandAdded = true - } - } - } - - if !commandAdded { - finalLines = append(finalLines, commandLine) - } - - newConfig := strings.Join(finalLines, "\n") - - tmpFile := "/tmp/greetd-config.toml" - if err := os.WriteFile(tmpFile, []byte(newConfig), 0644); err != nil { - return fmt.Errorf("failed to write temp config: %w", err) - } - - moveCmd := exec.Command("sudo", "mv", tmpFile, configPath) - if err := moveCmd.Run(); err != nil { - return fmt.Errorf("failed to update config: %w", err) - } - - fmt.Printf("✓ Updated greetd configuration to use %s\n", selectedCompositor) - fmt.Println("\n=== Enable Complete ===") - fmt.Println("\nTo start the greeter, run:") - fmt.Println(" sudo systemctl start greetd") - fmt.Println("\nTo enable on boot, run:") - fmt.Println(" sudo systemctl enable --now greetd") - - return nil -} - -func promptCompositorChoice(compositors []string) (string, error) { - fmt.Println("\nMultiple compositors detected:") - for i, comp := range compositors { - fmt.Printf("%d) %s\n", i+1, comp) - } - - var response string - fmt.Print("Choose compositor for greeter: ") - fmt.Scanln(&response) - response = strings.TrimSpace(response) - - choice := 0 - fmt.Sscanf(response, "%d", &choice) - - if choice < 1 || choice > len(compositors) { - return "", fmt.Errorf("invalid choice") - } - - return compositors[choice-1], nil -} - -func checkGreeterStatus() error { - fmt.Println("=== DMS Greeter Status ===") - fmt.Println() - - homeDir, err := os.UserHomeDir() - if err != nil { - return fmt.Errorf("failed to get user home directory: %w", err) - } - - currentUser, err := user.Current() - if err != nil { - return fmt.Errorf("failed to get current user: %w", err) - } - - configPath := "/etc/greetd/config.toml" - fmt.Println("Greeter Configuration:") - if data, err := os.ReadFile(configPath); err == nil { - configContent := string(data) - if strings.Contains(configContent, "dms-greeter") { - lines := strings.Split(configContent, "\n") - for _, line := range lines { - trimmed := strings.TrimSpace(line) - if strings.HasPrefix(trimmed, "command =") || strings.HasPrefix(trimmed, "command=") { - parts := strings.SplitN(trimmed, "=", 2) - if len(parts) == 2 { - command := strings.Trim(strings.TrimSpace(parts[1]), `"`) - fmt.Println(" ✓ Greeter is enabled") - - if strings.Contains(command, "--command niri") { - fmt.Println(" Compositor: niri") - } else if strings.Contains(command, "--command hyprland") { - fmt.Println(" Compositor: Hyprland") - } else if strings.Contains(command, "--command sway") { - fmt.Println(" Compositor: sway") - } else { - fmt.Println(" Compositor: unknown") - } - } - break - } - } - } else { - fmt.Println(" ✗ Greeter is NOT enabled") - fmt.Println(" Run 'dms greeter enable' to enable it") - } - } else { - fmt.Println(" ✗ Greeter config not found") - fmt.Println(" Run 'dms greeter install' to install greeter") - } - - fmt.Println("\nGroup Membership:") - groupsCmd := exec.Command("groups", currentUser.Username) - groupsOutput, err := groupsCmd.Output() - if err != nil { - return fmt.Errorf("failed to check groups: %w", err) - } - - inGreeterGroup := strings.Contains(string(groupsOutput), "greeter") - if inGreeterGroup { - fmt.Println(" ✓ User is in greeter group") - } else { - fmt.Println(" ✗ User is NOT in greeter group") - fmt.Println(" Run 'dms greeter install' to add user to greeter group") - } - - cacheDir := "/var/cache/dms-greeter" - fmt.Println("\nGreeter Cache Directory:") - if stat, err := os.Stat(cacheDir); err == nil && stat.IsDir() { - fmt.Printf(" ✓ %s exists\n", cacheDir) - } else { - fmt.Printf(" ✗ %s not found\n", cacheDir) - fmt.Println(" Run 'dms greeter install' to create cache directory") - return nil - } - - fmt.Println("\nConfiguration Symlinks:") - symlinks := []struct { - source string - target string - desc string - }{ - { - source: filepath.Join(homeDir, ".config", "DankMaterialShell", "settings.json"), - target: filepath.Join(cacheDir, "settings.json"), - desc: "Settings", - }, - { - source: filepath.Join(homeDir, ".local", "state", "DankMaterialShell", "session.json"), - target: filepath.Join(cacheDir, "session.json"), - desc: "Session state", - }, - { - source: filepath.Join(homeDir, ".cache", "quickshell", "dankshell", "dms-colors.json"), - target: filepath.Join(cacheDir, "colors.json"), - desc: "Color theme", - }, - } - - allGood := true - for _, link := range symlinks { - targetInfo, err := os.Lstat(link.target) - if err != nil { - fmt.Printf(" ✗ %s: symlink not found at %s\n", link.desc, link.target) - allGood = false - continue - } - - if targetInfo.Mode()&os.ModeSymlink == 0 { - fmt.Printf(" ✗ %s: %s is not a symlink\n", link.desc, link.target) - allGood = false - continue - } - - linkDest, err := os.Readlink(link.target) - if err != nil { - fmt.Printf(" ✗ %s: failed to read symlink\n", link.desc) - allGood = false - continue - } - - if linkDest != link.source { - fmt.Printf(" ✗ %s: symlink points to wrong location\n", link.desc) - fmt.Printf(" Expected: %s\n", link.source) - fmt.Printf(" Got: %s\n", linkDest) - allGood = false - continue - } - - if _, err := os.Stat(link.source); os.IsNotExist(err) { - fmt.Printf(" ⚠ %s: symlink OK, but source file doesn't exist yet\n", link.desc) - fmt.Printf(" Will be created when you run DMS\n") - continue - } - - fmt.Printf(" ✓ %s: synced correctly\n", link.desc) - } - - fmt.Println() - if allGood && inGreeterGroup { - fmt.Println("✓ All checks passed! Greeter is properly configured.") - } else if !allGood { - fmt.Println("⚠ Some issues detected. Run 'dms greeter sync' to fix symlinks.") - } - - return nil -} diff --git a/nix/inputs/dms-cli/cmd/dms/commands_hyprland.go b/nix/inputs/dms-cli/cmd/dms/commands_hyprland.go deleted file mode 100644 index 46958ff..0000000 --- a/nix/inputs/dms-cli/cmd/dms/commands_hyprland.go +++ /dev/null @@ -1,45 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "os" - - "github.com/AvengeMedia/danklinux/internal/hyprland" - "github.com/AvengeMedia/danklinux/internal/log" - "github.com/spf13/cobra" -) - -var hyprlandCmd = &cobra.Command{ - Use: "hyprland", - Short: "Hyprland utilities", - Long: "Utilities for working with Hyprland configuration", -} - -var hyprlandKeybindsCmd = &cobra.Command{ - Use: "keybinds", - Short: "Parse Hyprland keybinds", - Long: "Parse keybinds from Hyprland configuration files", - Run: runHyprlandKeybinds, -} - -func init() { - hyprlandKeybindsCmd.Flags().String("path", "$HOME/.config/hypr", "Path to Hyprland config directory") - hyprlandCmd.AddCommand(hyprlandKeybindsCmd) -} - -func runHyprlandKeybinds(cmd *cobra.Command, args []string) { - path, _ := cmd.Flags().GetString("path") - - section, err := hyprland.ParseKeys(path) - if err != nil { - log.Fatalf("Error parsing keybinds: %v", err) - } - - output, err := json.Marshal(section) - if err != nil { - log.Fatalf("Error generating JSON: %v", err) - } - - fmt.Fprintln(os.Stdout, string(output)) -} diff --git a/nix/inputs/dms-cli/cmd/dms/commands_root.go b/nix/inputs/dms-cli/cmd/dms/commands_root.go deleted file mode 100644 index 67002b5..0000000 --- a/nix/inputs/dms-cli/cmd/dms/commands_root.go +++ /dev/null @@ -1,42 +0,0 @@ -package main - -import ( - "errors" - "os" - - "github.com/AvengeMedia/danklinux/internal/distros" - "github.com/AvengeMedia/danklinux/internal/dms" - "github.com/AvengeMedia/danklinux/internal/log" - tea "github.com/charmbracelet/bubbletea" - "github.com/spf13/cobra" -) - -var rootCmd = &cobra.Command{ - Use: "dms", - Short: "dms CLI", - Long: "dms is the DankMaterialShell management CLI and backend server.", - Run: runInteractiveMode, -} - -func runInteractiveMode(cmd *cobra.Command, args []string) { - detector, err := dms.NewDetector() - if err != nil && !errors.Is(err, &distros.UnsupportedDistributionError{}) { - log.Fatalf("Error initializing DMS detector: %v", err) - } else if errors.Is(err, &distros.UnsupportedDistributionError{}) { - log.Error("Interactive mode is not supported on this distribution.") - log.Info("Please run 'dms --help' for available commands.") - os.Exit(1) - } - - if !detector.IsDMSInstalled() { - log.Error("DankMaterialShell (DMS) is not detected as installed on this system.") - log.Info("Please install DMS using dankinstall before using this management interface.") - os.Exit(1) - } - - model := dms.NewModel(Version) - p := tea.NewProgram(model, tea.WithAltScreen()) - if _, err := p.Run(); err != nil { - log.Fatalf("Error running program: %v", err) - } -} diff --git a/nix/inputs/dms-cli/cmd/dms/main.go b/nix/inputs/dms-cli/cmd/dms/main.go deleted file mode 100644 index f1a8d47..0000000 --- a/nix/inputs/dms-cli/cmd/dms/main.go +++ /dev/null @@ -1,44 +0,0 @@ -//go:build !distro_binary - -package main - -import ( - "os" - - "github.com/AvengeMedia/danklinux/internal/log" -) - -var Version = "dev" - -func init() { - runCmd.Flags().BoolP("daemon", "d", false, "Run in daemon mode") - runCmd.Flags().Bool("daemon-child", false, "Internal flag for daemon child process") - runCmd.Flags().Bool("session", false, "Session managed (like as a systemd unit)") - runCmd.Flags().MarkHidden("daemon-child") - - // Add subcommands to greeter - greeterCmd.AddCommand(greeterInstallCmd, greeterSyncCmd, greeterEnableCmd, greeterStatusCmd) - - // Add subcommands to update - updateCmd.AddCommand(updateCheckCmd) - - // Add subcommands to plugins - pluginsCmd.AddCommand(pluginsBrowseCmd, pluginsListCmd, pluginsInstallCmd, pluginsUninstallCmd) - - // Add common commands to root - rootCmd.AddCommand(getCommonCommands()...) - - rootCmd.AddCommand(updateCmd) - - rootCmd.SetHelpTemplate(getHelpTemplate()) -} - -func main() { - if os.Geteuid() == 0 { - log.Fatal("This program should not be run as root. Exiting.") - } - - if err := rootCmd.Execute(); err != nil { - log.Fatal(err) - } -} diff --git a/nix/inputs/dms-cli/cmd/dms/main_distro.go b/nix/inputs/dms-cli/cmd/dms/main_distro.go deleted file mode 100644 index ffb311e..0000000 --- a/nix/inputs/dms-cli/cmd/dms/main_distro.go +++ /dev/null @@ -1,41 +0,0 @@ -//go:build distro_binary - -package main - -import ( - "os" - - "github.com/AvengeMedia/danklinux/internal/log" -) - -var Version = "dev" - -func init() { - // Add flags - runCmd.Flags().BoolP("daemon", "d", false, "Run in daemon mode") - runCmd.Flags().Bool("daemon-child", false, "Internal flag for daemon child process") - runCmd.Flags().Bool("session", false, "Session managed (like as a systemd unit)") - runCmd.Flags().MarkHidden("daemon-child") - - // Add subcommands to greeter - greeterCmd.AddCommand(greeterSyncCmd, greeterEnableCmd, greeterStatusCmd) - - // Add subcommands to plugins - pluginsCmd.AddCommand(pluginsBrowseCmd, pluginsListCmd, pluginsInstallCmd, pluginsUninstallCmd) - - // Add common commands to root - rootCmd.AddCommand(getCommonCommands()...) - - rootCmd.SetHelpTemplate(getHelpTemplate()) -} - -func main() { - // Block root - if os.Geteuid() == 0 { - log.Fatal("This program should not be run as root. Exiting.") - } - - if err := rootCmd.Execute(); err != nil { - log.Fatal(err) - } -} diff --git a/nix/inputs/dms-cli/cmd/dms/shell.go b/nix/inputs/dms-cli/cmd/dms/shell.go deleted file mode 100644 index 44289e0..0000000 --- a/nix/inputs/dms-cli/cmd/dms/shell.go +++ /dev/null @@ -1,500 +0,0 @@ -package main - -import ( - "context" - "fmt" - "os" - "os/exec" - "os/signal" - "path/filepath" - "strconv" - "strings" - "syscall" - "time" - - "github.com/AvengeMedia/danklinux/internal/log" - "github.com/AvengeMedia/danklinux/internal/server" -) - -var isSessionManaged bool - -func execDetachedRestart(targetPID int) { - selfPath, err := os.Executable() - if err != nil { - return - } - - cmd := exec.Command(selfPath, "restart-detached", strconv.Itoa(targetPID)) - cmd.SysProcAttr = &syscall.SysProcAttr{ - Setsid: true, - } - cmd.Start() -} - -func runDetachedRestart(targetPIDStr string) { - targetPID, err := strconv.Atoi(targetPIDStr) - if err != nil { - return - } - - time.Sleep(200 * time.Millisecond) - - proc, err := os.FindProcess(targetPID) - if err == nil { - proc.Signal(syscall.SIGTERM) - } - - time.Sleep(500 * time.Millisecond) - - killShell() - runShellDaemon(false) -} - -func locateDMSConfig() (string, error) { - var searchPaths []string - - configHome := os.Getenv("XDG_CONFIG_HOME") - if configHome == "" { - if homeDir, err := os.UserHomeDir(); err == nil { - configHome = filepath.Join(homeDir, ".config") - } - } - - if configHome != "" { - searchPaths = append(searchPaths, filepath.Join(configHome, "quickshell", "dms")) - } - - searchPaths = append(searchPaths, "/usr/share/quickshell/dms") - - configDirs := os.Getenv("XDG_CONFIG_DIRS") - if configDirs == "" { - configDirs = "/etc/xdg" - } - - for _, dir := range strings.Split(configDirs, ":") { - if dir != "" { - searchPaths = append(searchPaths, filepath.Join(dir, "quickshell", "dms")) - } - } - - for _, path := range searchPaths { - shellPath := filepath.Join(path, "shell.qml") - if info, err := os.Stat(shellPath); err == nil && !info.IsDir() { - return path, nil - } - } - - return "", fmt.Errorf("could not find DMS config (shell.qml) in any valid config path") -} - -func getRuntimeDir() string { - if runtime := os.Getenv("XDG_RUNTIME_DIR"); runtime != "" { - return runtime - } - return os.TempDir() -} - -func getPIDFilePath() string { - return filepath.Join(getRuntimeDir(), fmt.Sprintf("danklinux-%d.pid", os.Getpid())) -} - -func writePIDFile(childPID int) error { - pidFile := getPIDFilePath() - return os.WriteFile(pidFile, []byte(strconv.Itoa(childPID)), 0644) -} - -func removePIDFile() { - pidFile := getPIDFilePath() - os.Remove(pidFile) -} - -func getAllDMSPIDs() []int { - dir := getRuntimeDir() - entries, err := os.ReadDir(dir) - if err != nil { - return nil - } - - var pids []int - - for _, entry := range entries { - if !strings.HasPrefix(entry.Name(), "danklinux-") || !strings.HasSuffix(entry.Name(), ".pid") { - continue - } - - pidFile := filepath.Join(dir, entry.Name()) - data, err := os.ReadFile(pidFile) - if err != nil { - continue - } - - childPID, err := strconv.Atoi(strings.TrimSpace(string(data))) - if err != nil { - os.Remove(pidFile) - continue - } - - // Check if the child process is still alive - proc, err := os.FindProcess(childPID) - if err != nil { - os.Remove(pidFile) - continue - } - - if err := proc.Signal(syscall.Signal(0)); err != nil { - // Process is dead, remove stale PID file - os.Remove(pidFile) - continue - } - - pids = append(pids, childPID) - - // Also get the parent PID from the filename - parentPIDStr := strings.TrimPrefix(entry.Name(), "danklinux-") - parentPIDStr = strings.TrimSuffix(parentPIDStr, ".pid") - if parentPID, err := strconv.Atoi(parentPIDStr); err == nil { - // Check if parent is still alive - if parentProc, err := os.FindProcess(parentPID); err == nil { - if err := parentProc.Signal(syscall.Signal(0)); err == nil { - pids = append(pids, parentPID) - } - } - } - } - - return pids -} - -func runShellInteractive(session bool) { - isSessionManaged = session - go printASCII() - fmt.Fprintf(os.Stderr, "dms %s\n", Version) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - socketPath := server.GetSocketPath() - - errChan := make(chan error, 2) - - go func() { - if err := server.Start(false); err != nil { - errChan <- fmt.Errorf("server error: %w", err) - } - }() - - configPath, err := locateDMSConfig() - if err != nil { - log.Fatalf("Error locating DMS config: %v", err) - } - - log.Infof("Spawning quickshell with -p %s", configPath) - - cmd := exec.CommandContext(ctx, "qs", "-p", configPath) - cmd.Env = append(os.Environ(), "DMS_SOCKET="+socketPath) - if qtRules := log.GetQtLoggingRules(); qtRules != "" { - cmd.Env = append(cmd.Env, "QT_LOGGING_RULES="+qtRules) - } - - homeDir, err := os.UserHomeDir() - if err == nil && os.Getenv("DMS_DISABLE_HOT_RELOAD") == "" { - if !strings.HasPrefix(configPath, homeDir) { - cmd.Env = append(cmd.Env, "DMS_DISABLE_HOT_RELOAD=1") - } - } - - cmd.Stdin = os.Stdin - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - - if err := cmd.Start(); err != nil { - log.Fatalf("Error starting quickshell: %v", err) - } - - // Write PID file for the quickshell child process - if err := writePIDFile(cmd.Process.Pid); err != nil { - log.Warnf("Failed to write PID file: %v", err) - } - defer removePIDFile() - - sigChan := make(chan os.Signal, 1) - signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM, syscall.SIGUSR1) - - go func() { - if err := cmd.Wait(); err != nil { - errChan <- fmt.Errorf("quickshell exited: %w", err) - } else { - errChan <- fmt.Errorf("quickshell exited") - } - }() - - for { - select { - case sig := <-sigChan: - // Handle SIGUSR1 restart for non-session managed processes - if sig == syscall.SIGUSR1 && !isSessionManaged { - log.Infof("Received SIGUSR1, spawning detached restart process...") - execDetachedRestart(os.Getpid()) - // Exit immediately to avoid race conditions with detached restart - return - } - - // All other signals: clean shutdown - log.Infof("\nReceived signal %v, shutting down...", sig) - cancel() - cmd.Process.Signal(syscall.SIGTERM) - os.Remove(socketPath) - return - - case err := <-errChan: - log.Error(err) - cancel() - if cmd.Process != nil { - cmd.Process.Signal(syscall.SIGTERM) - } - os.Remove(socketPath) - os.Exit(1) - } - } -} - -func restartShell() { - pids := getAllDMSPIDs() - - if len(pids) == 0 { - log.Info("No running DMS shell instances found. Starting daemon...") - runShellDaemon(false) - return - } - - currentPid := os.Getpid() - uniquePids := make(map[int]bool) - - for _, pid := range pids { - if pid != currentPid { - uniquePids[pid] = true - } - } - - for pid := range uniquePids { - proc, err := os.FindProcess(pid) - if err != nil { - log.Errorf("Error finding process %d: %v", pid, err) - continue - } - - if err := proc.Signal(syscall.Signal(0)); err != nil { - continue - } - - if err := proc.Signal(syscall.SIGUSR1); err != nil { - log.Errorf("Error sending SIGUSR1 to process %d: %v", pid, err) - } else { - log.Infof("Sent SIGUSR1 to DMS process with PID %d", pid) - } - } -} - -func killShell() { - // Get all tracked DMS PIDs from PID files - pids := getAllDMSPIDs() - - if len(pids) == 0 { - log.Info("No running DMS shell instances found.") - return - } - - currentPid := os.Getpid() - uniquePids := make(map[int]bool) - - // Deduplicate and filter out current process - for _, pid := range pids { - if pid != currentPid { - uniquePids[pid] = true - } - } - - // Kill all tracked processes - for pid := range uniquePids { - proc, err := os.FindProcess(pid) - if err != nil { - log.Errorf("Error finding process %d: %v", pid, err) - continue - } - - // Check if process is still alive before killing - if err := proc.Signal(syscall.Signal(0)); err != nil { - continue - } - - if err := proc.Kill(); err != nil { - log.Errorf("Error killing process %d: %v", pid, err) - } else { - log.Infof("Killed DMS process with PID %d", pid) - } - } - - // Clean up any remaining PID files - dir := getRuntimeDir() - entries, err := os.ReadDir(dir) - if err != nil { - return - } - - for _, entry := range entries { - if strings.HasPrefix(entry.Name(), "danklinux-") && strings.HasSuffix(entry.Name(), ".pid") { - pidFile := filepath.Join(dir, entry.Name()) - os.Remove(pidFile) - } - } -} - -func runShellDaemon(session bool) { - isSessionManaged = session - // Check if this is the daemon child process by looking for the hidden flag - isDaemonChild := false - for _, arg := range os.Args { - if arg == "--daemon-child" { - isDaemonChild = true - break - } - } - - if !isDaemonChild { - fmt.Fprintf(os.Stderr, "dms %s\n", Version) - - cmd := exec.Command(os.Args[0], "run", "-d", "--daemon-child") - cmd.Env = os.Environ() - - cmd.SysProcAttr = &syscall.SysProcAttr{ - Setsid: true, - } - - if err := cmd.Start(); err != nil { - log.Fatalf("Error starting daemon: %v", err) - } - - log.Infof("DMS shell daemon started (PID: %d)", cmd.Process.Pid) - return - } - - fmt.Fprintf(os.Stderr, "dms %s\n", Version) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - socketPath := server.GetSocketPath() - - errChan := make(chan error, 2) - - go func() { - if err := server.Start(false); err != nil { - errChan <- fmt.Errorf("server error: %w", err) - } - }() - - configPath, err := locateDMSConfig() - if err != nil { - log.Fatalf("Error locating DMS config: %v", err) - } - - log.Infof("Spawning quickshell with -p %s", configPath) - - cmd := exec.CommandContext(ctx, "qs", "-p", configPath) - cmd.Env = append(os.Environ(), "DMS_SOCKET="+socketPath) - if qtRules := log.GetQtLoggingRules(); qtRules != "" { - cmd.Env = append(cmd.Env, "QT_LOGGING_RULES="+qtRules) - } - - homeDir, err := os.UserHomeDir() - if err == nil && os.Getenv("DMS_DISABLE_HOT_RELOAD") == "" { - if !strings.HasPrefix(configPath, homeDir) { - cmd.Env = append(cmd.Env, "DMS_DISABLE_HOT_RELOAD=1") - } - } - - devNull, err := os.OpenFile("/dev/null", os.O_RDWR, 0) - if err != nil { - log.Fatalf("Error opening /dev/null: %v", err) - } - defer devNull.Close() - - cmd.Stdin = devNull - cmd.Stdout = devNull - cmd.Stderr = devNull - - if err := cmd.Start(); err != nil { - log.Fatalf("Error starting daemon: %v", err) - } - - // Write PID file for the quickshell child process - if err := writePIDFile(cmd.Process.Pid); err != nil { - log.Warnf("Failed to write PID file: %v", err) - } - defer removePIDFile() - - sigChan := make(chan os.Signal, 1) - signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM, syscall.SIGUSR1) - - go func() { - if err := cmd.Wait(); err != nil { - errChan <- fmt.Errorf("quickshell exited: %w", err) - } else { - errChan <- fmt.Errorf("quickshell exited") - } - }() - - for { - select { - case sig := <-sigChan: - // Handle SIGUSR1 restart for non-session managed processes - if sig == syscall.SIGUSR1 && !isSessionManaged { - log.Infof("Received SIGUSR1, spawning detached restart process...") - execDetachedRestart(os.Getpid()) - // Exit immediately to avoid race conditions with detached restart - return - } - - // All other signals: clean shutdown - cancel() - cmd.Process.Signal(syscall.SIGTERM) - os.Remove(socketPath) - return - - case <-errChan: - cancel() - if cmd.Process != nil { - cmd.Process.Signal(syscall.SIGTERM) - } - os.Remove(socketPath) - os.Exit(1) - } - } -} - -func runShellIPCCommand(args []string) { - if len(args) == 0 { - log.Error("IPC command requires arguments") - log.Info("Usage: dms ipc [args...]") - os.Exit(1) - } - - if args[0] != "call" { - args = append([]string{"call"}, args...) - } - - configPath, err := locateDMSConfig() - if err != nil { - log.Fatalf("Error locating DMS config: %v", err) - } - - cmdArgs := append([]string{"-p", configPath, "ipc"}, args...) - cmd := exec.Command("qs", cmdArgs...) - cmd.Stdin = os.Stdin - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - - if err := cmd.Run(); err != nil { - log.Fatalf("Error running IPC command: %v", err) - } -} diff --git a/nix/inputs/dms-cli/cmd/dms/ui.go b/nix/inputs/dms-cli/cmd/dms/ui.go deleted file mode 100644 index 23d8795..0000000 --- a/nix/inputs/dms-cli/cmd/dms/ui.go +++ /dev/null @@ -1,53 +0,0 @@ -package main - -import ( - "fmt" - - "github.com/AvengeMedia/danklinux/internal/tui" - "github.com/charmbracelet/lipgloss" -) - -func printASCII() { - fmt.Print(getThemedASCII()) -} - -func getThemedASCII() string { - theme := tui.TerminalTheme() - - logo := ` -██████╗ █████╗ ███╗ ██╗██╗ ██╗ -██╔══██╗██╔══██╗████╗ ██║██║ ██╔╝ -██║ ██║███████║██╔██╗ ██║█████╔╝ -██║ ██║██╔══██║██║╚██╗██║██╔═██╗ -██████╔╝██║ ██║██║ ╚████║██║ ██╗ -╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝` - - style := lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Primary)). - Bold(true) - - return style.Render(logo) + "\n" -} - -func getHelpTemplate() string { - return getThemedASCII() + ` -{{.Long}} - -Usage: - {{.UseLine}}{{if .HasAvailableSubCommands}} - -Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "help"))}} - {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}} - -Flags: -{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}} - -Global Flags: -{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}} - -Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}} - {{rpad .Name .NamePadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}} - -Use "{{.CommandPath}} [command] --help" for more information about a command.{{end}} -` -} diff --git a/nix/inputs/dms-cli/cmd/dms/utils.go b/nix/inputs/dms-cli/cmd/dms/utils.go deleted file mode 100644 index fe9dcee..0000000 --- a/nix/inputs/dms-cli/cmd/dms/utils.go +++ /dev/null @@ -1,14 +0,0 @@ -package main - -import "os/exec" - -func commandExists(cmd string) bool { - _, err := exec.LookPath(cmd) - return err == nil -} - -func isArchPackageInstalled(packageName string) bool { - cmd := exec.Command("pacman", "-Q", packageName) - err := cmd.Run() - return err == nil -} diff --git a/nix/inputs/dms-cli/flake.lock b/nix/inputs/dms-cli/flake.lock deleted file mode 100644 index 17542b5..0000000 --- a/nix/inputs/dms-cli/flake.lock +++ /dev/null @@ -1,27 +0,0 @@ -{ - "nodes": { - "nixpkgs": { - "locked": { - "lastModified": 1760878510, - "narHash": "sha256-K5Osef2qexezUfs0alLvZ7nQFTGS9DL2oTVsIXsqLgs=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "5e2a59a5b1a82f89f2c7e598302a9cacebb72a67", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixos-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "root": { - "inputs": { - "nixpkgs": "nixpkgs" - } - } - }, - "root": "root", - "version": 7 -} diff --git a/nix/inputs/dms-cli/flake.nix b/nix/inputs/dms-cli/flake.nix deleted file mode 100644 index f5efcf6..0000000 --- a/nix/inputs/dms-cli/flake.nix +++ /dev/null @@ -1,61 +0,0 @@ -{ - description = "DankMaterialShell Command Line Interface"; - - inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; - }; - - outputs = - { self, nixpkgs }: - let - supportedSystems = [ - "x86_64-linux" - "aarch64-linux" - ]; - - forAllSystems = - f: - builtins.listToAttrs ( - map (system: { - name = system; - value = f system; - }) supportedSystems - ); - - in - { - packages = forAllSystems ( - system: - let - pkgs = import nixpkgs { inherit system; }; - lib = pkgs.lib; - in - { - dms-cli = pkgs.buildGoModule (finalAttrs: { - pname = "dms-cli"; - version = "0.4.1"; - src = ./.; - vendorHash = "sha256-XbCg6qQwD4g4R/hBReLGE4NOq9uv0LBqogmfpBs//Ic="; - - subPackages = [ "cmd/dms" ]; - - ldflags = [ - "-s" - "-w" - "-X main.Version=${finalAttrs.version}" - ]; - - meta = { - description = "DankMaterialShell Command Line Interface"; - homepage = "https://github.com/AvengeMedia/danklinux"; - mainProgram = "dms"; - license = lib.licenses.mit; - platforms = lib.platforms.unix; - }; - }); - - default = self.packages.${system}.dms-cli; - } - ); - }; -} diff --git a/nix/inputs/dms-cli/go.mod b/nix/inputs/dms-cli/go.mod deleted file mode 100644 index 3477b80..0000000 --- a/nix/inputs/dms-cli/go.mod +++ /dev/null @@ -1,65 +0,0 @@ -module github.com/AvengeMedia/danklinux - -go 1.24.6 - -require ( - github.com/Wifx/gonetworkmanager/v2 v2.2.0 - github.com/charmbracelet/bubbles v0.21.0 - github.com/charmbracelet/bubbletea v1.3.6 - github.com/charmbracelet/lipgloss v1.1.0 - github.com/charmbracelet/log v0.4.2 - github.com/godbus/dbus/v5 v5.1.0 - github.com/spf13/cobra v1.9.1 - github.com/stretchr/testify v1.11.1 - github.com/yaslama/go-wayland/wayland v0.0.0-20250907155644-2874f32d9c34 - golang.org/x/exp v0.0.0-20231006140011-7918f672742d -) - -require ( - github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/ProtonMail/go-crypto v1.3.0 // indirect - github.com/cloudflare/circl v1.6.1 // indirect - github.com/cyphar/filepath-securejoin v0.4.1 // indirect - github.com/emirpasic/gods v1.18.1 // indirect - github.com/go-git/gcfg/v2 v2.0.2 // indirect - github.com/go-git/go-billy/v6 v6.0.0-20250627091229-31e2a16eef30 // indirect - github.com/go-logfmt/logfmt v0.6.0 // indirect - github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect - github.com/kevinburke/ssh_config v1.4.0 // indirect - github.com/klauspost/cpuid/v2 v2.3.0 // indirect - github.com/pjbgf/sha1cd v0.5.0 // indirect - github.com/sergi/go-diff v1.4.0 // indirect - github.com/stretchr/objx v0.5.2 // indirect - golang.org/x/crypto v0.42.0 // indirect - golang.org/x/net v0.44.0 // indirect -) - -require ( - github.com/atotto/clipboard v0.1.4 // indirect - github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect - github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect - github.com/charmbracelet/harmonica v0.2.0 // indirect - github.com/charmbracelet/x/ansi v0.9.3 // indirect - github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect - github.com/charmbracelet/x/term v0.2.1 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect - github.com/go-git/go-git/v6 v6.0.0-20250929195514-145daf2492dd - github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/lucasb-eyer/go-colorful v1.3.0 - github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-localereader v0.0.1 // indirect - github.com/mattn/go-runewidth v0.0.16 // indirect - github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect - github.com/muesli/cancelreader v0.2.2 // indirect - github.com/muesli/termenv v0.16.0 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/rivo/uniseg v0.4.7 // indirect - github.com/spf13/afero v1.15.0 - github.com/spf13/pflag v1.0.6 // indirect - github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect - golang.org/x/sync v0.17.0 // indirect - golang.org/x/sys v0.36.0 - golang.org/x/text v0.29.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect -) diff --git a/nix/inputs/dms-cli/go.sum b/nix/inputs/dms-cli/go.sum deleted file mode 100644 index cc84792..0000000 --- a/nix/inputs/dms-cli/go.sum +++ /dev/null @@ -1,141 +0,0 @@ -github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= -github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw= -github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE= -github.com/Wifx/gonetworkmanager/v2 v2.2.0 h1:kPstgsQtY8CmDOOFZd81ytM9Gi3f6ImzPCKF7nNhQ2U= -github.com/Wifx/gonetworkmanager/v2 v2.2.0/go.mod h1:fMDb//SHsKWxyDUAwXvCqurV3npbIyyaQWenGpZ/uXg= -github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= -github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= -github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= -github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= -github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= -github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= -github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= -github.com/charmbracelet/bubbles v0.21.0 h1:9TdC97SdRVg/1aaXNVWfFH3nnLAwOXr8Fn6u6mfQdFs= -github.com/charmbracelet/bubbles v0.21.0/go.mod h1:HF+v6QUR4HkEpz62dx7ym2xc71/KBHg+zKwJtMw+qtg= -github.com/charmbracelet/bubbletea v1.3.6 h1:VkHIxPJQeDt0aFJIsVxw8BQdh/F/L2KKZGsK6et5taU= -github.com/charmbracelet/bubbletea v1.3.6/go.mod h1:oQD9VCRQFF8KplacJLo28/jofOI2ToOfGYeFgBBxHOc= -github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs= -github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk= -github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ= -github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao= -github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY= -github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30= -github.com/charmbracelet/log v0.4.2 h1:hYt8Qj6a8yLnvR+h7MwsJv/XvmBJXiueUcI3cIxsyig= -github.com/charmbracelet/log v0.4.2/go.mod h1:qifHGX/tc7eluv2R6pWIpyHDDrrb/AG71Pf2ysQu5nw= -github.com/charmbracelet/x/ansi v0.9.3 h1:BXt5DHS/MKF+LjuK4huWrC6NCvHtexww7dMayh6GXd0= -github.com/charmbracelet/x/ansi v0.9.3/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE= -github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8= -github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs= -github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ= -github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg= -github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0= -github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= -github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= -github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s= -github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o= -github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE= -github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= -github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= -github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= -github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= -github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= -github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= -github.com/go-git/gcfg/v2 v2.0.2 h1:MY5SIIfTGGEMhdA7d7JePuVVxtKL7Hp+ApGDJAJ7dpo= -github.com/go-git/gcfg/v2 v2.0.2/go.mod h1:/lv2NsxvhepuMrldsFilrgct6pxzpGdSRC13ydTLSLs= -github.com/go-git/go-billy/v6 v6.0.0-20250627091229-31e2a16eef30 h1:4KqVJTL5eanN8Sgg3BV6f2/QzfZEFbCd+rTak1fGRRA= -github.com/go-git/go-billy/v6 v6.0.0-20250627091229-31e2a16eef30/go.mod h1:snwvGrbywVFy2d6KJdQ132zapq4aLyzLMgpo79XdEfM= -github.com/go-git/go-git-fixtures/v5 v5.1.1 h1:OH8i1ojV9bWfr0ZfasfpgtUXQHQyVS8HXik/V1C099w= -github.com/go-git/go-git-fixtures/v5 v5.1.1/go.mod h1:Altk43lx3b1ks+dVoAG2300o5WWUnktvfY3VI6bcaXU= -github.com/go-git/go-git/v6 v6.0.0-20250929195514-145daf2492dd h1:30HEd5KKVM7GgMJ1GSNuYxuZXEg8Pdlngp6T51faxoc= -github.com/go-git/go-git/v6 v6.0.0-20250929195514-145daf2492dd/go.mod h1:lz8PQr/p79XpFq5ODVBwRJu5LnOF8Et7j95ehqmCMJU= -github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= -github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= -github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= -github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= -github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= -github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/kevinburke/ssh_config v1.4.0 h1:6xxtP5bZ2E4NF5tuQulISpTO2z8XbtH8cg1PWkxoFkQ= -github.com/kevinburke/ssh_config v1.4.0/go.mod h1:q2RIzfka+BXARoNexmF9gkxEX7DmvbW9P4hIVx2Kg4M= -github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= -github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag= -github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= -github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= -github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= -github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= -github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= -github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= -github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= -github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc= -github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk= -github.com/pjbgf/sha1cd v0.5.0 h1:a+UkboSi1znleCDUNT3M5YxjOnN1fz2FhN48FlwCxs0= -github.com/pjbgf/sha1cd v0.5.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= -github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw= -github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= -github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= -github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= -github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= -github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= -github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= -github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= -github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= -github.com/yaslama/go-wayland/wayland v0.0.0-20250907155644-2874f32d9c34 h1:iTAt1me6SBYsuzrl/CmrxtATPlOG/pVviosM3DhUdKE= -github.com/yaslama/go-wayland/wayland v0.0.0-20250907155644-2874f32d9c34/go.mod h1:jzmUN5lUAv2O8e63OvcauV4S30rIZ1BvF/PNYE37vDo= -golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI= -golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8= -golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= -golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= -golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= -golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= -golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= -golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= -golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= -golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ= -golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA= -golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= -golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/nix/inputs/dms-cli/install.sh b/nix/inputs/dms-cli/install.sh deleted file mode 100755 index 11b36d4..0000000 --- a/nix/inputs/dms-cli/install.sh +++ /dev/null @@ -1,86 +0,0 @@ -#!/bin/sh - -set -e - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -NC='\033[0m' # No Color - -# Check for root privileges -if [ "$(id -u)" == "0" ]; then - printf "%bError: This script must not be run as root%b\n" "$RED" "$NC" - exit 1 -fi - -# Check if running on Linux -if [ "$(uname)" != "Linux" ]; then - printf "%bError: This installer only supports Linux systems%b\n" "$RED" "$NC" - exit 1 -fi - -# Detect architecture -ARCH=$(uname -m) -case "$ARCH" in - x86_64) - ARCH="amd64" - ;; - aarch64) - ARCH="arm64" - ;; - *) - printf "%bError: Unsupported architecture: %s%b\n" "$RED" "$ARCH" "$NC" - printf "This installer only supports x86_64 (amd64) and aarch64 (arm64) architectures\n" - exit 1 - ;; -esac - -# Get the latest release version -LATEST_VERSION=$(curl -s https://api.github.com/repos/AvengeMedia/danklinux/releases/latest | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/') - -if [ -z "$LATEST_VERSION" ]; then - printf "%bError: Could not fetch latest version%b\n" "$RED" "$NC" - exit 1 -fi - -printf "%bInstalling Dankinstall %s for %s...%b\n" "$GREEN" "$LATEST_VERSION" "$ARCH" "$NC" - -# Download and install -TEMP_DIR=$(mktemp -d) -cd "$TEMP_DIR" || exit 1 - -# Download the gzipped binary and its checksum -printf "%bDownloading installer...%b\n" "$GREEN" "$NC" -curl -L "https://github.com/AvengeMedia/danklinux/releases/download/$LATEST_VERSION/dankinstall-$ARCH.gz" -o "installer.gz" -curl -L "https://github.com/AvengeMedia/danklinux/releases/download/$LATEST_VERSION/dankinstall-$ARCH.gz.sha256" -o "expected.sha256" - -# Get the expected checksum -EXPECTED_CHECKSUM=$(cat expected.sha256 | awk '{print $1}') - -# Calculate actual checksum -printf "%bVerifying checksum...%b\n" "$GREEN" "$NC" -ACTUAL_CHECKSUM=$(sha256sum installer.gz | awk '{print $1}') - -# Compare checksums -if [ "$EXPECTED_CHECKSUM" != "$ACTUAL_CHECKSUM" ]; then - printf "%bError: Checksum verification failed%b\n" "$RED" "$NC" - printf "Expected: %s\n" "$EXPECTED_CHECKSUM" - printf "Got: %s\n" "$ACTUAL_CHECKSUM" - printf "The downloaded file may be corrupted or tampered with\n" - cd - > /dev/null - rm -rf "$TEMP_DIR" - exit 1 -fi - -# Decompress the binary -printf "%bDecompressing installer...%b\n" "$GREEN" "$NC" -gunzip installer.gz -chmod +x installer - -# Execute the installer -printf "%bRunning installer...%b\n" "$GREEN" "$NC" -./installer - -# Cleanup -cd - > /dev/null -rm -rf "$TEMP_DIR" \ No newline at end of file diff --git a/nix/inputs/dms-cli/internal/config/deployer.go b/nix/inputs/dms-cli/internal/config/deployer.go deleted file mode 100644 index 1db3fe5..0000000 --- a/nix/inputs/dms-cli/internal/config/deployer.go +++ /dev/null @@ -1,574 +0,0 @@ -package config - -import ( - "context" - "fmt" - "os" - "path/filepath" - "regexp" - "strings" - "time" - - "github.com/AvengeMedia/danklinux/internal/deps" -) - -type ConfigDeployer struct { - logChan chan<- string -} - -type DeploymentResult struct { - ConfigType string - Path string - BackupPath string - Deployed bool - Error error -} - -func NewConfigDeployer(logChan chan<- string) *ConfigDeployer { - return &ConfigDeployer{ - logChan: logChan, - } -} - -func (cd *ConfigDeployer) log(message string) { - if cd.logChan != nil { - cd.logChan <- message - } -} - -// DeployConfigurations deploys all necessary configurations based on the chosen window manager -func (cd *ConfigDeployer) DeployConfigurations(ctx context.Context, wm deps.WindowManager) ([]DeploymentResult, error) { - return cd.DeployConfigurationsWithTerminal(ctx, wm, deps.TerminalGhostty) -} - -// DeployConfigurationsWithTerminal deploys all necessary configurations based on chosen window manager and terminal -func (cd *ConfigDeployer) DeployConfigurationsWithTerminal(ctx context.Context, wm deps.WindowManager, terminal deps.Terminal) ([]DeploymentResult, error) { - return cd.DeployConfigurationsSelective(ctx, wm, terminal, nil, nil) -} - -func (cd *ConfigDeployer) DeployConfigurationsSelective(ctx context.Context, wm deps.WindowManager, terminal deps.Terminal, installedDeps []deps.Dependency, replaceConfigs map[string]bool) ([]DeploymentResult, error) { - return cd.DeployConfigurationsSelectiveWithReinstalls(ctx, wm, terminal, installedDeps, replaceConfigs, nil) -} - -func (cd *ConfigDeployer) DeployConfigurationsSelectiveWithReinstalls(ctx context.Context, wm deps.WindowManager, terminal deps.Terminal, installedDeps []deps.Dependency, replaceConfigs map[string]bool, reinstallItems map[string]bool) ([]DeploymentResult, error) { - var results []DeploymentResult - - shouldReplaceConfig := func(configType string) bool { - if replaceConfigs == nil { - return true - } - replace, exists := replaceConfigs[configType] - return !exists || replace - } - - switch wm { - case deps.WindowManagerNiri: - if shouldReplaceConfig("Niri") { - result, err := cd.deployNiriConfig(terminal) - results = append(results, result) - if err != nil { - return results, fmt.Errorf("failed to deploy Niri config: %w", err) - } - } - case deps.WindowManagerHyprland: - if shouldReplaceConfig("Hyprland") { - result, err := cd.deployHyprlandConfig(terminal) - results = append(results, result) - if err != nil { - return results, fmt.Errorf("failed to deploy Hyprland config: %w", err) - } - } - } - - switch terminal { - case deps.TerminalGhostty: - if shouldReplaceConfig("Ghostty") { - ghosttyResults, err := cd.deployGhosttyConfig() - results = append(results, ghosttyResults...) - if err != nil { - return results, fmt.Errorf("failed to deploy Ghostty config: %w", err) - } - } - case deps.TerminalKitty: - if shouldReplaceConfig("Kitty") { - kittyResults, err := cd.deployKittyConfig() - results = append(results, kittyResults...) - if err != nil { - return results, fmt.Errorf("failed to deploy Kitty config: %w", err) - } - } - case deps.TerminalAlacritty: - if shouldReplaceConfig("Alacritty") { - alacrittyResults, err := cd.deployAlacrittyConfig() - results = append(results, alacrittyResults...) - if err != nil { - return results, fmt.Errorf("failed to deploy Alacritty config: %w", err) - } - } - } - - return results, nil -} - -// deployNiriConfig handles Niri configuration deployment with backup and merging -func (cd *ConfigDeployer) deployNiriConfig(terminal deps.Terminal) (DeploymentResult, error) { - result := DeploymentResult{ - ConfigType: "Niri", - Path: filepath.Join(os.Getenv("HOME"), ".config", "niri", "config.kdl"), - } - - configDir := filepath.Dir(result.Path) - if err := os.MkdirAll(configDir, 0755); err != nil { - result.Error = fmt.Errorf("failed to create config directory: %w", err) - return result, result.Error - } - - var existingConfig string - if _, err := os.Stat(result.Path); err == nil { - cd.log("Found existing Niri configuration") - - existingData, err := os.ReadFile(result.Path) - if err != nil { - result.Error = fmt.Errorf("failed to read existing config: %w", err) - return result, result.Error - } - existingConfig = string(existingData) - - timestamp := time.Now().Format("2006-01-02_15-04-05") - result.BackupPath = result.Path + ".backup." + timestamp - if err := os.WriteFile(result.BackupPath, existingData, 0644); err != nil { - result.Error = fmt.Errorf("failed to create backup: %w", err) - return result, result.Error - } - cd.log(fmt.Sprintf("Backed up existing config to %s", result.BackupPath)) - } - - // Detect polkit agent path - polkitPath, err := cd.detectPolkitAgent() - if err != nil { - cd.log(fmt.Sprintf("Warning: Could not detect polkit agent: %v", err)) - polkitPath = "/usr/lib/mate-polkit/polkit-mate-authentication-agent-1" // fallback - } - - // Determine terminal command based on choice - var terminalCommand string - switch terminal { - case deps.TerminalGhostty: - terminalCommand = "ghostty" - case deps.TerminalKitty: - terminalCommand = "kitty" - case deps.TerminalAlacritty: - terminalCommand = "alacritty" - default: - terminalCommand = "ghostty" // fallback to ghostty - } - - newConfig := strings.ReplaceAll(NiriConfig, "{{POLKIT_AGENT_PATH}}", polkitPath) - newConfig = strings.ReplaceAll(newConfig, "{{TERMINAL_COMMAND}}", terminalCommand) - - // If there was an existing config, merge the output sections - if existingConfig != "" { - mergedConfig, err := cd.mergeNiriOutputSections(newConfig, existingConfig) - if err != nil { - cd.log(fmt.Sprintf("Warning: Failed to merge output sections: %v", err)) - } else { - newConfig = mergedConfig - cd.log("Successfully merged existing output sections") - } - } - - if err := os.WriteFile(result.Path, []byte(newConfig), 0644); err != nil { - result.Error = fmt.Errorf("failed to write config: %w", err) - return result, result.Error - } - - result.Deployed = true - cd.log("Successfully deployed Niri configuration") - return result, nil -} - -func (cd *ConfigDeployer) deployGhosttyConfig() ([]DeploymentResult, error) { - var results []DeploymentResult - - mainResult := DeploymentResult{ - ConfigType: "Ghostty", - Path: filepath.Join(os.Getenv("HOME"), ".config", "ghostty", "config"), - } - - configDir := filepath.Dir(mainResult.Path) - if err := os.MkdirAll(configDir, 0755); err != nil { - mainResult.Error = fmt.Errorf("failed to create config directory: %w", err) - return []DeploymentResult{mainResult}, mainResult.Error - } - - if _, err := os.Stat(mainResult.Path); err == nil { - cd.log("Found existing Ghostty configuration") - - existingData, err := os.ReadFile(mainResult.Path) - if err != nil { - mainResult.Error = fmt.Errorf("failed to read existing config: %w", err) - return []DeploymentResult{mainResult}, mainResult.Error - } - - timestamp := time.Now().Format("2006-01-02_15-04-05") - mainResult.BackupPath = mainResult.Path + ".backup." + timestamp - if err := os.WriteFile(mainResult.BackupPath, existingData, 0644); err != nil { - mainResult.Error = fmt.Errorf("failed to create backup: %w", err) - return []DeploymentResult{mainResult}, mainResult.Error - } - cd.log(fmt.Sprintf("Backed up existing config to %s", mainResult.BackupPath)) - } - - if err := os.WriteFile(mainResult.Path, []byte(GhosttyConfig), 0644); err != nil { - mainResult.Error = fmt.Errorf("failed to write config: %w", err) - return []DeploymentResult{mainResult}, mainResult.Error - } - - mainResult.Deployed = true - cd.log("Successfully deployed Ghostty configuration") - results = append(results, mainResult) - - colorResult := DeploymentResult{ - ConfigType: "Ghostty Colors", - Path: filepath.Join(os.Getenv("HOME"), ".config", "ghostty", "config-dankcolors"), - } - - if err := os.WriteFile(colorResult.Path, []byte(GhosttyColorConfig), 0644); err != nil { - colorResult.Error = fmt.Errorf("failed to write color config: %w", err) - return results, colorResult.Error - } - - colorResult.Deployed = true - cd.log("Successfully deployed Ghostty color configuration") - results = append(results, colorResult) - - return results, nil -} - -func (cd *ConfigDeployer) deployKittyConfig() ([]DeploymentResult, error) { - var results []DeploymentResult - - mainResult := DeploymentResult{ - ConfigType: "Kitty", - Path: filepath.Join(os.Getenv("HOME"), ".config", "kitty", "kitty.conf"), - } - - configDir := filepath.Dir(mainResult.Path) - if err := os.MkdirAll(configDir, 0755); err != nil { - mainResult.Error = fmt.Errorf("failed to create config directory: %w", err) - return []DeploymentResult{mainResult}, mainResult.Error - } - - if _, err := os.Stat(mainResult.Path); err == nil { - cd.log("Found existing Kitty configuration") - - existingData, err := os.ReadFile(mainResult.Path) - if err != nil { - mainResult.Error = fmt.Errorf("failed to read existing config: %w", err) - return []DeploymentResult{mainResult}, mainResult.Error - } - - timestamp := time.Now().Format("2006-01-02_15-04-05") - mainResult.BackupPath = mainResult.Path + ".backup." + timestamp - if err := os.WriteFile(mainResult.BackupPath, existingData, 0644); err != nil { - mainResult.Error = fmt.Errorf("failed to create backup: %w", err) - return []DeploymentResult{mainResult}, mainResult.Error - } - cd.log(fmt.Sprintf("Backed up existing config to %s", mainResult.BackupPath)) - } - - if err := os.WriteFile(mainResult.Path, []byte(KittyConfig), 0644); err != nil { - mainResult.Error = fmt.Errorf("failed to write config: %w", err) - return []DeploymentResult{mainResult}, mainResult.Error - } - - mainResult.Deployed = true - cd.log("Successfully deployed Kitty configuration") - results = append(results, mainResult) - - themeResult := DeploymentResult{ - ConfigType: "Kitty Theme", - Path: filepath.Join(os.Getenv("HOME"), ".config", "kitty", "dank-theme.conf"), - } - - if err := os.WriteFile(themeResult.Path, []byte(KittyThemeConfig), 0644); err != nil { - themeResult.Error = fmt.Errorf("failed to write theme config: %w", err) - return results, themeResult.Error - } - - themeResult.Deployed = true - cd.log("Successfully deployed Kitty theme configuration") - results = append(results, themeResult) - - tabsResult := DeploymentResult{ - ConfigType: "Kitty Tabs", - Path: filepath.Join(os.Getenv("HOME"), ".config", "kitty", "dank-tabs.conf"), - } - - if err := os.WriteFile(tabsResult.Path, []byte(KittyTabsConfig), 0644); err != nil { - tabsResult.Error = fmt.Errorf("failed to write tabs config: %w", err) - return results, tabsResult.Error - } - - tabsResult.Deployed = true - cd.log("Successfully deployed Kitty tabs configuration") - results = append(results, tabsResult) - - return results, nil -} - -func (cd *ConfigDeployer) deployAlacrittyConfig() ([]DeploymentResult, error) { - var results []DeploymentResult - - mainResult := DeploymentResult{ - ConfigType: "Alacritty", - Path: filepath.Join(os.Getenv("HOME"), ".config", "alacritty", "alacritty.toml"), - } - - configDir := filepath.Dir(mainResult.Path) - if err := os.MkdirAll(configDir, 0755); err != nil { - mainResult.Error = fmt.Errorf("failed to create config directory: %w", err) - return []DeploymentResult{mainResult}, mainResult.Error - } - - if _, err := os.Stat(mainResult.Path); err == nil { - cd.log("Found existing Alacritty configuration") - - existingData, err := os.ReadFile(mainResult.Path) - if err != nil { - mainResult.Error = fmt.Errorf("failed to read existing config: %w", err) - return []DeploymentResult{mainResult}, mainResult.Error - } - - timestamp := time.Now().Format("2006-01-02_15-04-05") - mainResult.BackupPath = mainResult.Path + ".backup." + timestamp - if err := os.WriteFile(mainResult.BackupPath, existingData, 0644); err != nil { - mainResult.Error = fmt.Errorf("failed to create backup: %w", err) - return []DeploymentResult{mainResult}, mainResult.Error - } - cd.log(fmt.Sprintf("Backed up existing config to %s", mainResult.BackupPath)) - } - - if err := os.WriteFile(mainResult.Path, []byte(AlacrittyConfig), 0644); err != nil { - mainResult.Error = fmt.Errorf("failed to write config: %w", err) - return []DeploymentResult{mainResult}, mainResult.Error - } - - mainResult.Deployed = true - cd.log("Successfully deployed Alacritty configuration") - results = append(results, mainResult) - - themeResult := DeploymentResult{ - ConfigType: "Alacritty Theme", - Path: filepath.Join(os.Getenv("HOME"), ".config", "alacritty", "dank-theme.toml"), - } - - if err := os.WriteFile(themeResult.Path, []byte(AlacrittyThemeConfig), 0644); err != nil { - themeResult.Error = fmt.Errorf("failed to write theme config: %w", err) - return results, themeResult.Error - } - - themeResult.Deployed = true - cd.log("Successfully deployed Alacritty theme configuration") - results = append(results, themeResult) - - return results, nil -} - -// detectPolkitAgent tries to find the polkit authentication agent on the system -// Prioritizes mate-polkit paths since that's what we install -func (cd *ConfigDeployer) detectPolkitAgent() (string, error) { - // Prioritize mate-polkit paths first - matePaths := []string{ - "/usr/libexec/polkit-mate-authentication-agent-1", // Fedora path - "/usr/lib/mate-polkit/polkit-mate-authentication-agent-1", - "/usr/libexec/mate-polkit/polkit-mate-authentication-agent-1", - "/usr/lib/polkit-mate/polkit-mate-authentication-agent-1", - "/usr/lib/x86_64-linux-gnu/mate-polkit/polkit-mate-authentication-agent-1", - } - - for _, path := range matePaths { - if _, err := os.Stat(path); err == nil { - cd.log(fmt.Sprintf("Found mate-polkit agent at: %s", path)) - return path, nil - } - } - - // Fallback to other polkit agents if mate-polkit is not found - fallbackPaths := []string{ - "/usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1", - "/usr/libexec/polkit-gnome-authentication-agent-1", - } - - for _, path := range fallbackPaths { - if _, err := os.Stat(path); err == nil { - cd.log(fmt.Sprintf("Found fallback polkit agent at: %s", path)) - return path, nil - } - } - - return "", fmt.Errorf("no polkit agent found in common locations") -} - -// mergeNiriOutputSections extracts output sections from existing config and merges them into the new config -func (cd *ConfigDeployer) mergeNiriOutputSections(newConfig, existingConfig string) (string, error) { - // Regular expression to match output sections (including commented ones) - outputRegex := regexp.MustCompile(`(?m)^(/-)?\s*output\s+"[^"]+"\s*\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}`) - - // Find all output sections in the existing config - existingOutputs := outputRegex.FindAllString(existingConfig, -1) - - if len(existingOutputs) == 0 { - // No output sections to merge - return newConfig, nil - } - - // Remove the example output section from the new config - exampleOutputRegex := regexp.MustCompile(`(?m)^/-output "eDP-2" \{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}`) - mergedConfig := exampleOutputRegex.ReplaceAllString(newConfig, "") - - // Find where to insert the output sections (after the input section) - inputEndRegex := regexp.MustCompile(`(?m)^}$`) - inputMatches := inputEndRegex.FindAllStringIndex(newConfig, -1) - - if len(inputMatches) < 1 { - return "", fmt.Errorf("could not find insertion point for output sections") - } - - // Insert after the first closing brace (end of input section) - insertPos := inputMatches[0][1] - - var builder strings.Builder - builder.WriteString(mergedConfig[:insertPos]) - builder.WriteString("\n// Outputs from existing configuration\n") - - for _, output := range existingOutputs { - builder.WriteString(output) - builder.WriteString("\n") - } - - builder.WriteString(mergedConfig[insertPos:]) - - return builder.String(), nil -} - -// deployHyprlandConfig handles Hyprland configuration deployment with backup and merging -func (cd *ConfigDeployer) deployHyprlandConfig(terminal deps.Terminal) (DeploymentResult, error) { - result := DeploymentResult{ - ConfigType: "Hyprland", - Path: filepath.Join(os.Getenv("HOME"), ".config", "hypr", "hyprland.conf"), - } - - configDir := filepath.Dir(result.Path) - if err := os.MkdirAll(configDir, 0755); err != nil { - result.Error = fmt.Errorf("failed to create config directory: %w", err) - return result, result.Error - } - - var existingConfig string - if _, err := os.Stat(result.Path); err == nil { - cd.log("Found existing Hyprland configuration") - - existingData, err := os.ReadFile(result.Path) - if err != nil { - result.Error = fmt.Errorf("failed to read existing config: %w", err) - return result, result.Error - } - existingConfig = string(existingData) - - timestamp := time.Now().Format("2006-01-02_15-04-05") - result.BackupPath = result.Path + ".backup." + timestamp - if err := os.WriteFile(result.BackupPath, existingData, 0644); err != nil { - result.Error = fmt.Errorf("failed to create backup: %w", err) - return result, result.Error - } - cd.log(fmt.Sprintf("Backed up existing config to %s", result.BackupPath)) - } - - // Detect polkit agent path - polkitPath, err := cd.detectPolkitAgent() - if err != nil { - cd.log(fmt.Sprintf("Warning: Could not detect polkit agent: %v", err)) - polkitPath = "/usr/lib/mate-polkit/polkit-mate-authentication-agent-1" // fallback - } - - // Determine terminal command based on choice - var terminalCommand string - switch terminal { - case deps.TerminalGhostty: - terminalCommand = "ghostty" - case deps.TerminalKitty: - terminalCommand = "kitty" - case deps.TerminalAlacritty: - terminalCommand = "alacritty" - default: - terminalCommand = "ghostty" // fallback to ghostty - } - - newConfig := strings.ReplaceAll(HyprlandConfig, "{{POLKIT_AGENT_PATH}}", polkitPath) - newConfig = strings.ReplaceAll(newConfig, "{{TERMINAL_COMMAND}}", terminalCommand) - - // If there was an existing config, merge the monitor sections - if existingConfig != "" { - mergedConfig, err := cd.mergeHyprlandMonitorSections(newConfig, existingConfig) - if err != nil { - cd.log(fmt.Sprintf("Warning: Failed to merge monitor sections: %v", err)) - } else { - newConfig = mergedConfig - cd.log("Successfully merged existing monitor sections") - } - } - - if err := os.WriteFile(result.Path, []byte(newConfig), 0644); err != nil { - result.Error = fmt.Errorf("failed to write config: %w", err) - return result, result.Error - } - - result.Deployed = true - cd.log("Successfully deployed Hyprland configuration") - return result, nil -} - -// mergeHyprlandMonitorSections extracts monitor sections from existing config and merges them into the new config -func (cd *ConfigDeployer) mergeHyprlandMonitorSections(newConfig, existingConfig string) (string, error) { - // Regular expression to match monitor lines (including commented ones) - // Matches: monitor = NAME, RESOLUTION, POSITION, SCALE, etc. - // Also matches commented versions: # monitor = ... - monitorRegex := regexp.MustCompile(`(?m)^#?\s*monitor\s*=.*$`) - - // Find all monitor lines in the existing config - existingMonitors := monitorRegex.FindAllString(existingConfig, -1) - - if len(existingMonitors) == 0 { - // No monitor sections to merge - return newConfig, nil - } - - // Remove the example monitor line from the new config - exampleMonitorRegex := regexp.MustCompile(`(?m)^# monitor = eDP-2.*$`) - mergedConfig := exampleMonitorRegex.ReplaceAllString(newConfig, "") - - // Find where to insert the monitor sections (after the MONITOR CONFIG header) - monitorHeaderRegex := regexp.MustCompile(`(?m)^# MONITOR CONFIG\n# ==================$`) - headerMatch := monitorHeaderRegex.FindStringIndex(mergedConfig) - - if headerMatch == nil { - return "", fmt.Errorf("could not find MONITOR CONFIG section") - } - - // Insert after the header - insertPos := headerMatch[1] + 1 // +1 for the newline - - var builder strings.Builder - builder.WriteString(mergedConfig[:insertPos]) - builder.WriteString("# Monitors from existing configuration\n") - - for _, monitor := range existingMonitors { - builder.WriteString(monitor) - builder.WriteString("\n") - } - - builder.WriteString(mergedConfig[insertPos:]) - - return builder.String(), nil -} diff --git a/nix/inputs/dms-cli/internal/config/deployer_test.go b/nix/inputs/dms-cli/internal/config/deployer_test.go deleted file mode 100644 index 7b96744..0000000 --- a/nix/inputs/dms-cli/internal/config/deployer_test.go +++ /dev/null @@ -1,673 +0,0 @@ -package config - -import ( - "os" - "path/filepath" - "strings" - "testing" - - "github.com/AvengeMedia/danklinux/internal/deps" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestDetectPolkitAgent(t *testing.T) { - cd := &ConfigDeployer{} - - // This test depends on the system having a polkit agent installed - // We'll just test that the function doesn't crash and returns some path or error - path, err := cd.detectPolkitAgent() - - if err != nil { - // If no polkit agent is found, that's okay for testing - assert.Contains(t, err.Error(), "no polkit agent found") - } else { - // If found, it should be a valid path - assert.NotEmpty(t, path) - assert.True(t, strings.Contains(path, "polkit")) - } -} - -func TestMergeNiriOutputSections(t *testing.T) { - cd := &ConfigDeployer{} - - tests := []struct { - name string - newConfig string - existingConfig string - wantError bool - wantContains []string - }{ - { - name: "no existing outputs", - newConfig: `input { - keyboard { - xkb { - } - } -} -layout { - gaps 5 -}`, - existingConfig: `input { - keyboard { - xkb { - } - } -} -layout { - gaps 10 -}`, - wantError: false, - wantContains: []string{"gaps 5"}, // Should keep new config - }, - { - name: "merge single output", - newConfig: `input { - keyboard { - xkb { - } - } -} -/-output "eDP-2" { - mode "2560x1600@239.998993" - position x=2560 y=0 -} -layout { - gaps 5 -}`, - existingConfig: `input { - keyboard { - xkb { - } - } -} -output "eDP-1" { - mode "1920x1080@60.000000" - position x=0 y=0 - scale 1.0 -} -layout { - gaps 10 -}`, - wantError: false, - wantContains: []string{ - "gaps 5", // New config preserved - `output "eDP-1"`, // Existing output merged - "1920x1080@60.000000", // Existing output details - "Outputs from existing configuration", // Comment added - }, - }, - { - name: "merge multiple outputs", - newConfig: `input { - keyboard { - xkb { - } - } -} -/-output "eDP-2" { - mode "2560x1600@239.998993" - position x=2560 y=0 -} -layout { - gaps 5 -}`, - existingConfig: `input { - keyboard { - xkb { - } - } -} -output "eDP-1" { - mode "1920x1080@60.000000" - position x=0 y=0 - scale 1.0 -} -/-output "HDMI-1" { - mode "1920x1080@60.000000" - position x=1920 y=0 -} -layout { - gaps 10 -}`, - wantError: false, - wantContains: []string{ - "gaps 5", // New config preserved - `output "eDP-1"`, // First existing output - `/-output "HDMI-1"`, // Second existing output (commented) - "1920x1080@60.000000", // Output details - }, - }, - { - name: "merge commented outputs", - newConfig: `input { - keyboard { - xkb { - } - } -} -/-output "eDP-2" { - mode "2560x1600@239.998993" - position x=2560 y=0 -} -layout { - gaps 5 -}`, - existingConfig: `input { - keyboard { - xkb { - } - } -} -/-output "eDP-1" { - mode "1920x1080@60.000000" - position x=0 y=0 - scale 1.0 -} -layout { - gaps 10 -}`, - wantError: false, - wantContains: []string{ - "gaps 5", // New config preserved - `/-output "eDP-1"`, // Commented output preserved - "1920x1080@60.000000", // Output details - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result, err := cd.mergeNiriOutputSections(tt.newConfig, tt.existingConfig) - - if tt.wantError { - assert.Error(t, err) - return - } - - require.NoError(t, err) - - for _, want := range tt.wantContains { - assert.Contains(t, result, want, "merged config should contain: %s", want) - } - - // Verify the example output was removed - assert.NotContains(t, result, `/-output "eDP-2"`, "example output should be removed") - }) - } -} - -func TestConfigDeploymentFlow(t *testing.T) { - // Create a temporary directory for testing - tempDir, err := os.MkdirTemp("", "dankinstall-test") - require.NoError(t, err) - defer os.RemoveAll(tempDir) - - // Set up test environment - originalHome := os.Getenv("HOME") - os.Setenv("HOME", tempDir) - defer os.Setenv("HOME", originalHome) - - // Test data - logChan := make(chan string, 100) - cd := NewConfigDeployer(logChan) - - t.Run("deploy ghostty config to empty directory", func(t *testing.T) { - results, err := cd.deployGhosttyConfig() - require.NoError(t, err) - require.Len(t, results, 2) - - mainResult := results[0] - assert.Equal(t, "Ghostty", mainResult.ConfigType) - assert.True(t, mainResult.Deployed) - assert.Empty(t, mainResult.BackupPath) - assert.FileExists(t, mainResult.Path) - - content, err := os.ReadFile(mainResult.Path) - require.NoError(t, err) - assert.Contains(t, string(content), "window-decoration = false") - - colorResult := results[1] - assert.Equal(t, "Ghostty Colors", colorResult.ConfigType) - assert.True(t, colorResult.Deployed) - assert.FileExists(t, colorResult.Path) - - colorContent, err := os.ReadFile(colorResult.Path) - require.NoError(t, err) - assert.Contains(t, string(colorContent), "background = #101418") - }) - - t.Run("deploy ghostty config with existing file", func(t *testing.T) { - existingContent := "# Old config\nfont-size = 14\n" - ghosttyPath := getGhosttyPath() - err := os.MkdirAll(filepath.Dir(ghosttyPath), 0755) - require.NoError(t, err) - err = os.WriteFile(ghosttyPath, []byte(existingContent), 0644) - require.NoError(t, err) - - results, err := cd.deployGhosttyConfig() - require.NoError(t, err) - require.Len(t, results, 2) - - mainResult := results[0] - assert.Equal(t, "Ghostty", mainResult.ConfigType) - assert.True(t, mainResult.Deployed) - assert.NotEmpty(t, mainResult.BackupPath) - assert.FileExists(t, mainResult.Path) - assert.FileExists(t, mainResult.BackupPath) - - backupContent, err := os.ReadFile(mainResult.BackupPath) - require.NoError(t, err) - assert.Equal(t, existingContent, string(backupContent)) - - newContent, err := os.ReadFile(mainResult.Path) - require.NoError(t, err) - assert.NotContains(t, string(newContent), "# Old config") - - colorResult := results[1] - assert.Equal(t, "Ghostty Colors", colorResult.ConfigType) - assert.True(t, colorResult.Deployed) - assert.FileExists(t, colorResult.Path) - }) -} - -// Helper function to get Ghostty config path for testing -func getGhosttyPath() string { - return filepath.Join(os.Getenv("HOME"), ".config", "ghostty", "config") -} - -func TestPolkitPathInjection(t *testing.T) { - - testConfig := `spawn-at-startup "{{POLKIT_AGENT_PATH}}" -other content` - - result := strings.Replace(testConfig, "{{POLKIT_AGENT_PATH}}", "/test/polkit/path", 1) - - assert.Contains(t, result, `spawn-at-startup "/test/polkit/path"`) - assert.NotContains(t, result, "{{POLKIT_AGENT_PATH}}") -} - -func TestMergeHyprlandMonitorSections(t *testing.T) { - cd := &ConfigDeployer{} - - tests := []struct { - name string - newConfig string - existingConfig string - wantError bool - wantContains []string - wantNotContains []string - }{ - { - name: "no existing monitors", - newConfig: `# ================== -# MONITOR CONFIG -# ================== -# monitor = eDP-2, 2560x1600@239.998993, 2560x0, 1, vrr, 1 - -# ================== -# ENVIRONMENT VARS -# ================== -env = XDG_CURRENT_DESKTOP,niri`, - existingConfig: `# Some other config -input { - kb_layout = us -}`, - wantError: false, - wantContains: []string{"MONITOR CONFIG", "ENVIRONMENT VARS"}, - }, - { - name: "merge single monitor", - newConfig: `# ================== -# MONITOR CONFIG -# ================== -# monitor = eDP-2, 2560x1600@239.998993, 2560x0, 1, vrr, 1 - -# ================== -# ENVIRONMENT VARS -# ==================`, - existingConfig: `# My config -monitor = DP-1, 1920x1080@144, 0x0, 1 -input { - kb_layout = us -}`, - wantError: false, - wantContains: []string{ - "MONITOR CONFIG", - "monitor = DP-1, 1920x1080@144, 0x0, 1", - "Monitors from existing configuration", - }, - wantNotContains: []string{ - "monitor = eDP-2", // Example monitor should be removed - }, - }, - { - name: "merge multiple monitors", - newConfig: `# ================== -# MONITOR CONFIG -# ================== -# monitor = eDP-2, 2560x1600@239.998993, 2560x0, 1, vrr, 1 - -# ================== -# ENVIRONMENT VARS -# ==================`, - existingConfig: `monitor = DP-1, 1920x1080@144, 0x0, 1 -# monitor = HDMI-A-1, 1920x1080@60, 1920x0, 1 -monitor = eDP-1, 2560x1440@165, auto, 1.25`, - wantError: false, - wantContains: []string{ - "monitor = DP-1", - "# monitor = HDMI-A-1", // Commented monitor preserved - "monitor = eDP-1", - "Monitors from existing configuration", - }, - wantNotContains: []string{ - "monitor = eDP-2", // Example monitor should be removed - }, - }, - { - name: "preserve commented monitors", - newConfig: `# ================== -# MONITOR CONFIG -# ================== -# monitor = eDP-2, 2560x1600@239.998993, 2560x0, 1, vrr, 1 - -# ==================`, - existingConfig: `# monitor = DP-1, 1920x1080@144, 0x0, 1 -# monitor = HDMI-A-1, 1920x1080@60, 1920x0, 1`, - wantError: false, - wantContains: []string{ - "# monitor = DP-1", - "# monitor = HDMI-A-1", - "Monitors from existing configuration", - }, - }, - { - name: "no monitor config section", - newConfig: `# Some config without monitor section -input { - kb_layout = us -}`, - existingConfig: `monitor = DP-1, 1920x1080@144, 0x0, 1`, - wantError: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result, err := cd.mergeHyprlandMonitorSections(tt.newConfig, tt.existingConfig) - - if tt.wantError { - assert.Error(t, err) - return - } - - require.NoError(t, err) - - for _, want := range tt.wantContains { - assert.Contains(t, result, want, "merged config should contain: %s", want) - } - - for _, notWant := range tt.wantNotContains { - assert.NotContains(t, result, notWant, "merged config should NOT contain: %s", notWant) - } - }) - } -} - -func TestHyprlandConfigDeployment(t *testing.T) { - // Create a temporary directory for testing - tempDir, err := os.MkdirTemp("", "dankinstall-hyprland-test") - require.NoError(t, err) - defer os.RemoveAll(tempDir) - - // Set up test environment - originalHome := os.Getenv("HOME") - os.Setenv("HOME", tempDir) - defer os.Setenv("HOME", originalHome) - - logChan := make(chan string, 100) - cd := NewConfigDeployer(logChan) - - t.Run("deploy hyprland config to empty directory", func(t *testing.T) { - result, err := cd.deployHyprlandConfig(deps.TerminalGhostty) - require.NoError(t, err) - - assert.Equal(t, "Hyprland", result.ConfigType) - assert.True(t, result.Deployed) - assert.Empty(t, result.BackupPath) // No existing config, so no backup - assert.FileExists(t, result.Path) - - // Verify content - content, err := os.ReadFile(result.Path) - require.NoError(t, err) - assert.Contains(t, string(content), "# MONITOR CONFIG") - assert.Contains(t, string(content), "bind = $mod, T, exec, ghostty") // Terminal injection - assert.Contains(t, string(content), "exec-once = ") // Polkit agent - }) - - t.Run("deploy hyprland config with existing monitors", func(t *testing.T) { - // Create existing config with monitors - existingContent := `# My existing Hyprland config -monitor = DP-1, 1920x1080@144, 0x0, 1 -monitor = HDMI-A-1, 3840x2160@60, 1920x0, 1.5 - -general { - gaps_in = 10 -} -` - hyprPath := filepath.Join(tempDir, ".config", "hypr", "hyprland.conf") - err := os.MkdirAll(filepath.Dir(hyprPath), 0755) - require.NoError(t, err) - err = os.WriteFile(hyprPath, []byte(existingContent), 0644) - require.NoError(t, err) - - result, err := cd.deployHyprlandConfig(deps.TerminalKitty) - require.NoError(t, err) - - assert.Equal(t, "Hyprland", result.ConfigType) - assert.True(t, result.Deployed) - assert.NotEmpty(t, result.BackupPath) // Should have backup - assert.FileExists(t, result.Path) - assert.FileExists(t, result.BackupPath) - - // Verify backup content - backupContent, err := os.ReadFile(result.BackupPath) - require.NoError(t, err) - assert.Equal(t, existingContent, string(backupContent)) - - // Verify new content preserves monitors - newContent, err := os.ReadFile(result.Path) - require.NoError(t, err) - assert.Contains(t, string(newContent), "monitor = DP-1, 1920x1080@144") - assert.Contains(t, string(newContent), "monitor = HDMI-A-1, 3840x2160@60") - assert.Contains(t, string(newContent), "bind = $mod, T, exec, kitty") // Kitty terminal - assert.NotContains(t, string(newContent), "monitor = eDP-2") // Example monitor removed - }) -} - -func TestNiriConfigStructure(t *testing.T) { - // Verify the embedded Niri config has expected sections - assert.Contains(t, NiriConfig, "input {") - assert.Contains(t, NiriConfig, "layout {") - assert.Contains(t, NiriConfig, "binds {") - assert.Contains(t, NiriConfig, "{{POLKIT_AGENT_PATH}}") - assert.Contains(t, NiriConfig, `spawn "{{TERMINAL_COMMAND}}"`) -} - -func TestHyprlandConfigStructure(t *testing.T) { - // Verify the embedded Hyprland config has expected sections and placeholders - assert.Contains(t, HyprlandConfig, "# MONITOR CONFIG") - assert.Contains(t, HyprlandConfig, "# ENVIRONMENT VARS") - assert.Contains(t, HyprlandConfig, "# STARTUP APPS") - assert.Contains(t, HyprlandConfig, "# INPUT CONFIG") - assert.Contains(t, HyprlandConfig, "# KEYBINDINGS") - assert.Contains(t, HyprlandConfig, "{{POLKIT_AGENT_PATH}}") - assert.Contains(t, HyprlandConfig, "{{TERMINAL_COMMAND}}") - assert.Contains(t, HyprlandConfig, "exec-once = dms run") - assert.Contains(t, HyprlandConfig, "bind = $mod, T, exec,") - assert.Contains(t, HyprlandConfig, "bind = $mod, space, exec, dms ipc call spotlight toggle") - assert.Contains(t, HyprlandConfig, "windowrulev2 = noborder, class:^(com\\.mitchellh\\.ghostty)$") -} - -func TestGhosttyConfigStructure(t *testing.T) { - assert.Contains(t, GhosttyConfig, "window-decoration = false") - assert.Contains(t, GhosttyConfig, "background-opacity = 1.0") - assert.Contains(t, GhosttyConfig, "config-file = ./config-dankcolors") -} - -func TestGhosttyColorConfigStructure(t *testing.T) { - assert.Contains(t, GhosttyColorConfig, "background = #101418") - assert.Contains(t, GhosttyColorConfig, "foreground = #e0e2e8") - assert.Contains(t, GhosttyColorConfig, "cursor-color = #9dcbfb") - assert.Contains(t, GhosttyColorConfig, "palette = 0=#101418") - assert.Contains(t, GhosttyColorConfig, "palette = 15=#ffffff") -} - -func TestKittyConfigStructure(t *testing.T) { - assert.Contains(t, KittyConfig, "font_size 12.0") - assert.Contains(t, KittyConfig, "window_padding_width 12") - assert.Contains(t, KittyConfig, "background_opacity 1.0") - assert.Contains(t, KittyConfig, "include dank-tabs.conf") - assert.Contains(t, KittyConfig, "include dank-theme.conf") -} - -func TestKittyThemeConfigStructure(t *testing.T) { - assert.Contains(t, KittyThemeConfig, "foreground #e0e2e8") - assert.Contains(t, KittyThemeConfig, "background #101418") - assert.Contains(t, KittyThemeConfig, "cursor #e0e2e8") - assert.Contains(t, KittyThemeConfig, "color0 #101418") - assert.Contains(t, KittyThemeConfig, "color15 #ffffff") -} - -func TestKittyTabsConfigStructure(t *testing.T) { - assert.Contains(t, KittyTabsConfig, "tab_bar_style powerline") - assert.Contains(t, KittyTabsConfig, "tab_powerline_style slanted") - assert.Contains(t, KittyTabsConfig, "active_tab_background #124a73") - assert.Contains(t, KittyTabsConfig, "inactive_tab_background #101418") -} - -func TestAlacrittyConfigStructure(t *testing.T) { - assert.Contains(t, AlacrittyConfig, "[general]") - assert.Contains(t, AlacrittyConfig, "~/.config/alacritty/dank-theme.toml") - assert.Contains(t, AlacrittyConfig, "[window]") - assert.Contains(t, AlacrittyConfig, "decorations = \"None\"") - assert.Contains(t, AlacrittyConfig, "padding = { x = 12, y = 12 }") - assert.Contains(t, AlacrittyConfig, "[cursor]") - assert.Contains(t, AlacrittyConfig, "[keyboard]") -} - -func TestAlacrittyThemeConfigStructure(t *testing.T) { - assert.Contains(t, AlacrittyThemeConfig, "[colors.primary]") - assert.Contains(t, AlacrittyThemeConfig, "background = '#101418'") - assert.Contains(t, AlacrittyThemeConfig, "foreground = '#e0e2e8'") - assert.Contains(t, AlacrittyThemeConfig, "[colors.cursor]") - assert.Contains(t, AlacrittyThemeConfig, "cursor = '#9dcbfb'") - assert.Contains(t, AlacrittyThemeConfig, "[colors.normal]") - assert.Contains(t, AlacrittyThemeConfig, "[colors.bright]") -} - -func TestKittyConfigDeployment(t *testing.T) { - tempDir, err := os.MkdirTemp("", "dankinstall-kitty-test") - require.NoError(t, err) - defer os.RemoveAll(tempDir) - - originalHome := os.Getenv("HOME") - os.Setenv("HOME", tempDir) - defer os.Setenv("HOME", originalHome) - - logChan := make(chan string, 100) - cd := NewConfigDeployer(logChan) - - t.Run("deploy kitty config to empty directory", func(t *testing.T) { - results, err := cd.deployKittyConfig() - require.NoError(t, err) - require.Len(t, results, 3) - - mainResult := results[0] - assert.Equal(t, "Kitty", mainResult.ConfigType) - assert.True(t, mainResult.Deployed) - assert.FileExists(t, mainResult.Path) - - content, err := os.ReadFile(mainResult.Path) - require.NoError(t, err) - assert.Contains(t, string(content), "include dank-theme.conf") - - themeResult := results[1] - assert.Equal(t, "Kitty Theme", themeResult.ConfigType) - assert.True(t, themeResult.Deployed) - assert.FileExists(t, themeResult.Path) - - tabsResult := results[2] - assert.Equal(t, "Kitty Tabs", tabsResult.ConfigType) - assert.True(t, tabsResult.Deployed) - assert.FileExists(t, tabsResult.Path) - }) -} - -func TestAlacrittyConfigDeployment(t *testing.T) { - tempDir, err := os.MkdirTemp("", "dankinstall-alacritty-test") - require.NoError(t, err) - defer os.RemoveAll(tempDir) - - originalHome := os.Getenv("HOME") - os.Setenv("HOME", tempDir) - defer os.Setenv("HOME", originalHome) - - logChan := make(chan string, 100) - cd := NewConfigDeployer(logChan) - - t.Run("deploy alacritty config to empty directory", func(t *testing.T) { - results, err := cd.deployAlacrittyConfig() - require.NoError(t, err) - require.Len(t, results, 2) - - mainResult := results[0] - assert.Equal(t, "Alacritty", mainResult.ConfigType) - assert.True(t, mainResult.Deployed) - assert.FileExists(t, mainResult.Path) - - content, err := os.ReadFile(mainResult.Path) - require.NoError(t, err) - assert.Contains(t, string(content), "~/.config/alacritty/dank-theme.toml") - assert.Contains(t, string(content), "[window]") - - themeResult := results[1] - assert.Equal(t, "Alacritty Theme", themeResult.ConfigType) - assert.True(t, themeResult.Deployed) - assert.FileExists(t, themeResult.Path) - - themeContent, err := os.ReadFile(themeResult.Path) - require.NoError(t, err) - assert.Contains(t, string(themeContent), "[colors.primary]") - assert.Contains(t, string(themeContent), "background = '#101418'") - }) - - t.Run("deploy alacritty config with existing file", func(t *testing.T) { - existingContent := "# Old alacritty config\n[window]\nopacity = 0.9\n" - alacrittyPath := filepath.Join(tempDir, ".config", "alacritty", "alacritty.toml") - err := os.MkdirAll(filepath.Dir(alacrittyPath), 0755) - require.NoError(t, err) - err = os.WriteFile(alacrittyPath, []byte(existingContent), 0644) - require.NoError(t, err) - - results, err := cd.deployAlacrittyConfig() - require.NoError(t, err) - require.Len(t, results, 2) - - mainResult := results[0] - assert.True(t, mainResult.Deployed) - assert.NotEmpty(t, mainResult.BackupPath) - assert.FileExists(t, mainResult.BackupPath) - - backupContent, err := os.ReadFile(mainResult.BackupPath) - require.NoError(t, err) - assert.Equal(t, existingContent, string(backupContent)) - - newContent, err := os.ReadFile(mainResult.Path) - require.NoError(t, err) - assert.NotContains(t, string(newContent), "# Old alacritty config") - assert.Contains(t, string(newContent), "decorations = \"None\"") - }) -} diff --git a/nix/inputs/dms-cli/internal/config/dms.go b/nix/inputs/dms-cli/internal/config/dms.go deleted file mode 100644 index 2ecc3ef..0000000 --- a/nix/inputs/dms-cli/internal/config/dms.go +++ /dev/null @@ -1,46 +0,0 @@ -package config - -import ( - "fmt" - "os" - "path/filepath" - "strings" -) - -// LocateDMSConfig searches for DMS installation following XDG Base Directory specification -func LocateDMSConfig() (string, error) { - var searchPaths []string - - configHome := os.Getenv("XDG_CONFIG_HOME") - if configHome == "" { - if homeDir, err := os.UserHomeDir(); err == nil { - configHome = filepath.Join(homeDir, ".config") - } - } - - if configHome != "" { - searchPaths = append(searchPaths, filepath.Join(configHome, "quickshell", "dms")) - } - - searchPaths = append(searchPaths, "/usr/share/quickshell/dms") - - configDirs := os.Getenv("XDG_CONFIG_DIRS") - if configDirs == "" { - configDirs = "/etc/xdg" - } - - for _, dir := range strings.Split(configDirs, ":") { - if dir != "" { - searchPaths = append(searchPaths, filepath.Join(dir, "quickshell", "dms")) - } - } - - for _, path := range searchPaths { - shellPath := filepath.Join(path, "shell.qml") - if info, err := os.Stat(shellPath); err == nil && !info.IsDir() { - return path, nil - } - } - - return "", fmt.Errorf("could not find DMS config (shell.qml) in any valid config path") -} diff --git a/nix/inputs/dms-cli/internal/config/embedded/alacritty-theme.toml b/nix/inputs/dms-cli/internal/config/embedded/alacritty-theme.toml deleted file mode 100644 index 4048457..0000000 --- a/nix/inputs/dms-cli/internal/config/embedded/alacritty-theme.toml +++ /dev/null @@ -1,31 +0,0 @@ -[colors.primary] -background = '#101418' -foreground = '#e0e2e8' - -[colors.selection] -text = '#e0e2e8' -background = '#124a73' - -[colors.cursor] -text = '#101418' -cursor = '#9dcbfb' - -[colors.normal] -black = '#101418' -red = '#d75a59' -green = '#8ed88c' -yellow = '#e0d99d' -blue = '#4087bc' -magenta = '#839fbc' -cyan = '#9dcbfb' -white = '#abb2bf' - -[colors.bright] -black = '#5c6370' -red = '#e57e7e' -green = '#a2e5a0' -yellow = '#efe9b3' -blue = '#a7d9ff' -magenta = '#3d8197' -cyan = '#5c7ba3' -white = '#ffffff' diff --git a/nix/inputs/dms-cli/internal/config/embedded/alacritty.toml b/nix/inputs/dms-cli/internal/config/embedded/alacritty.toml deleted file mode 100644 index b666177..0000000 --- a/nix/inputs/dms-cli/internal/config/embedded/alacritty.toml +++ /dev/null @@ -1,37 +0,0 @@ -[general] -import = [ - "~/.config/alacritty/dank-theme.toml" -] - -[window] -decorations = "None" -padding = { x = 12, y = 12 } -opacity = 1.0 - -[scrolling] -history = 3023 - -[cursor] -style = { shape = "Block", blinking = "On" } -blink_interval = 500 -unfocused_hollow = true - -[mouse] -hide_when_typing = true - -[selection] -save_to_clipboard = false - -[bell] -duration = 0 - -[keyboard] -bindings = [ - { key = "C", mods = "Control|Shift", action = "Copy" }, - { key = "V", mods = "Control|Shift", action = "Paste" }, - { key = "N", mods = "Control|Shift", action = "SpawnNewInstance" }, - { key = "Equals", mods = "Control|Shift", action = "IncreaseFontSize" }, - { key = "Minus", mods = "Control", action = "DecreaseFontSize" }, - { key = "Key0", mods = "Control", action = "ResetFontSize" }, - { key = "Enter", mods = "Shift", chars = "\n" }, -] diff --git a/nix/inputs/dms-cli/internal/config/embedded/ghostty-colors.conf b/nix/inputs/dms-cli/internal/config/embedded/ghostty-colors.conf deleted file mode 100644 index 56dca5f..0000000 --- a/nix/inputs/dms-cli/internal/config/embedded/ghostty-colors.conf +++ /dev/null @@ -1,21 +0,0 @@ -background = #101418 -foreground = #e0e2e8 -cursor-color = #9dcbfb -selection-background = #124a73 -selection-foreground = #e0e2e8 -palette = 0=#101418 -palette = 1=#d75a59 -palette = 2=#8ed88c -palette = 3=#e0d99d -palette = 4=#4087bc -palette = 5=#839fbc -palette = 6=#9dcbfb -palette = 7=#abb2bf -palette = 8=#5c6370 -palette = 9=#e57e7e -palette = 10=#a2e5a0 -palette = 11=#efe9b3 -palette = 12=#a7d9ff -palette = 13=#3d8197 -palette = 14=#5c7ba3 -palette = 15=#ffffff diff --git a/nix/inputs/dms-cli/internal/config/embedded/ghostty.conf b/nix/inputs/dms-cli/internal/config/embedded/ghostty.conf deleted file mode 100644 index f541237..0000000 --- a/nix/inputs/dms-cli/internal/config/embedded/ghostty.conf +++ /dev/null @@ -1,51 +0,0 @@ -# Font Configuration -font-size = 12 - -# Window Configuration -window-decoration = false -window-padding-x = 12 -window-padding-y = 12 -background-opacity = 1.0 -background-blur-radius = 32 - -# Cursor Configuration -cursor-style = block -cursor-style-blink = true - -# Scrollback -scrollback-limit = 3023 - -# Terminal features -mouse-hide-while-typing = true -copy-on-select = false -confirm-close-surface = false - -# Disable annoying copied to clipboard -app-notifications = no-clipboard-copy,no-config-reload - -# Key bindings for common actions -#keybind = ctrl+c=copy_to_clipboard -#keybind = ctrl+v=paste_from_clipboard -keybind = ctrl+shift+n=new_window -keybind = ctrl+t=new_tab -keybind = ctrl+plus=increase_font_size:1 -keybind = ctrl+minus=decrease_font_size:1 -keybind = ctrl+zero=reset_font_size - -# Material 3 UI elements -unfocused-split-opacity = 0.7 -unfocused-split-fill = #44464f - -# Tab configuration -gtk-titlebar = false - -# Shell integration -shell-integration = detect -shell-integration-features = cursor,sudo,title,no-cursor -keybind = shift+enter=text:\n - -# Rando stuff -gtk-single-instance = true - -# Dank color generation -config-file = ./config-dankcolors diff --git a/nix/inputs/dms-cli/internal/config/embedded/hyprland.conf b/nix/inputs/dms-cli/internal/config/embedded/hyprland.conf deleted file mode 100644 index 6e812c9..0000000 --- a/nix/inputs/dms-cli/internal/config/embedded/hyprland.conf +++ /dev/null @@ -1,287 +0,0 @@ -# Hyprland Configuration -# https://wiki.hypr.land/Configuring/ - -# ================== -# MONITOR CONFIG -# ================== -# monitor = eDP-2, 2560x1600@239.998993, 2560x0, 1, vrr, 1 -monitor = , preferred,auto,1 - -# ================== -# ENVIRONMENT VARS -# ================== -env = QT_QPA_PLATFORM,wayland -env = ELECTRON_OZONE_PLATFORM_HINT,auto -env = QT_QPA_PLATFORMTHEME,gtk3 -env = QT_QPA_PLATFORMTHEME_QT6,gtk3 -env = TERMINAL,{{TERMINAL_COMMAND}} - -# ================== -# STARTUP APPS -# ================== -exec-once = bash -c "wl-paste --watch cliphist store &" -exec-once = dms run -exec-once = {{POLKIT_AGENT_PATH}} - -# ================== -# INPUT CONFIG -# ================== -input { - kb_layout = us - numlock_by_default = true -} - -# ================== -# GENERAL LAYOUT -# ================== -general { - gaps_in = 5 - gaps_out = 5 - border_size = 0 # off in niri - - col.active_border = rgba(707070ff) - col.inactive_border = rgba(d0d0d0ff) - - layout = dwindle -} - -# ================== -# DECORATION -# ================== -decoration { - rounding = 12 - - active_opacity = 1.0 - inactive_opacity = 0.9 - - shadow { - enabled = true - range = 30 - render_power = 5 - offset = 0 5 - color = rgba(00000070) - } -} - -# ================== -# ANIMATIONS -# ================== -animations { - enabled = true - - animation = windowsIn, 1, 3, default - animation = windowsOut, 1, 3, default - animation = workspaces, 1, 5, default - animation = windowsMove, 1, 4, default - animation = fade, 1, 3, default - animation = border, 1, 3, default -} - -# ================== -# LAYOUTS -# ================== -dwindle { - preserve_split = true -} - -master { - mfact = 0.5 -} - -# ================== -# MISC -# ================== -misc { - disable_hyprland_logo = true - disable_splash_rendering = true - vrr = 1 -} - -# ================== -# WINDOW RULES -# ================== -windowrulev2 = tile, class:^(org\.wezfurlong\.wezterm)$ - -windowrulev2 = rounding 12, class:^(org\.gnome\.) -windowrulev2 = noborder, class:^(org\.gnome\.) - -windowrulev2 = tile, class:^(gnome-control-center)$ -windowrulev2 = tile, class:^(pavucontrol)$ -windowrulev2 = tile, class:^(nm-connection-editor)$ - -windowrulev2 = float, class:^(gnome-calculator)$ -windowrulev2 = float, class:^(galculator)$ -windowrulev2 = float, class:^(blueman-manager)$ -windowrulev2 = float, class:^(org\.gnome\.Nautilus)$ -windowrulev2 = float, class:^(steam)$ -windowrulev2 = float, class:^(xdg-desktop-portal)$ - -windowrulev2 = noborder, class:^(org\.wezfurlong\.wezterm)$ -windowrulev2 = noborder, class:^(Alacritty)$ -windowrulev2 = noborder, class:^(zen)$ -windowrulev2 = noborder, class:^(com\.mitchellh\.ghostty)$ -windowrulev2 = noborder, class:^(kitty)$ - -windowrulev2 = float, class:^(firefox)$, title:^(Picture-in-Picture)$ -windowrulev2 = float, class:^(zoom)$ - -windowrulev2 = opacity 0.9 0.9, floating:0, focus:0 - -layerrule = noanim, ^(quickshell)$ - -# ================== -# KEYBINDINGS -# ================== -$mod = SUPER - -# === Application Launchers === -bind = $mod, T, exec, {{TERMINAL_COMMAND}} -bind = $mod, space, exec, dms ipc call spotlight toggle -bind = $mod, V, exec, dms ipc call clipboard toggle -bind = $mod, M, exec, dms ipc call processlist toggle -bind = $mod, comma, exec, dms ipc call settings toggle -bind = $mod, N, exec, dms ipc call notifications toggle -bind = $mod SHIFT, N, exec, dms ipc call notepad toggle -bind = $mod, Y, exec, dms ipc call dankdash wallpaper -bind = $mod, TAB, exec, dms ipc call hypr toggleOverview - -# === Security === -bind = $mod ALT, L, exec, dms ipc call lock lock -bind = $mod SHIFT, E, exit -bind = CTRL ALT, Delete, exec, dms ipc call processlist toggle - -# === Audio Controls === -bindel = , XF86AudioRaiseVolume, exec, dms ipc call audio increment 3 -bindel = , XF86AudioLowerVolume, exec, dms ipc call audio decrement 3 -bindl = , XF86AudioMute, exec, dms ipc call audio mute -bindl = , XF86AudioMicMute, exec, dms ipc call audio micmute - -# === Brightness Controls === -bindel = , XF86MonBrightnessUp, exec, dms ipc call brightness increment 5 "" -bindel = , XF86MonBrightnessDown, exec, dms ipc call brightness decrement 5 "" - -# === Window Management === -bind = $mod, Q, killactive -bind = $mod, F, fullscreen, 1 -bind = $mod SHIFT, F, fullscreen, 0 -bind = $mod SHIFT, T, togglefloating -bind = $mod, W, togglegroup - -# === Focus Navigation === -bind = $mod, left, movefocus, l -bind = $mod, down, movefocus, d -bind = $mod, up, movefocus, u -bind = $mod, right, movefocus, r -bind = $mod, H, movefocus, l -bind = $mod, J, movefocus, d -bind = $mod, K, movefocus, u -bind = $mod, L, movefocus, r - -# === Window Movement === -bind = $mod SHIFT, left, movewindow, l -bind = $mod SHIFT, down, movewindow, d -bind = $mod SHIFT, up, movewindow, u -bind = $mod SHIFT, right, movewindow, r -bind = $mod SHIFT, H, movewindow, l -bind = $mod SHIFT, J, movewindow, d -bind = $mod SHIFT, K, movewindow, u -bind = $mod SHIFT, L, movewindow, r - -# === Column Navigation === -bind = $mod, Home, focuswindow, first -bind = $mod, End, focuswindow, last - -# === Monitor Navigation === -bind = $mod CTRL, left, focusmonitor, l -bind = $mod CTRL, right, focusmonitor, r -bind = $mod CTRL, H, focusmonitor, l -bind = $mod CTRL, J, focusmonitor, d -bind = $mod CTRL, K, focusmonitor, u -bind = $mod CTRL, L, focusmonitor, r - -# === Move to Monitor === -bind = $mod SHIFT CTRL, left, movewindow, mon:l -bind = $mod SHIFT CTRL, down, movewindow, mon:d -bind = $mod SHIFT CTRL, up, movewindow, mon:u -bind = $mod SHIFT CTRL, right, movewindow, mon:r -bind = $mod SHIFT CTRL, H, movewindow, mon:l -bind = $mod SHIFT CTRL, J, movewindow, mon:d -bind = $mod SHIFT CTRL, K, movewindow, mon:u -bind = $mod SHIFT CTRL, L, movewindow, mon:r - -# === Workspace Navigation === -bind = $mod, Page_Down, workspace, e+1 -bind = $mod, Page_Up, workspace, e-1 -bind = $mod, U, workspace, e+1 -bind = $mod, I, workspace, e-1 -bind = $mod CTRL, down, movetoworkspace, e+1 -bind = $mod CTRL, up, movetoworkspace, e-1 -bind = $mod CTRL, U, movetoworkspace, e+1 -bind = $mod CTRL, I, movetoworkspace, e-1 - -# === Move Workspaces === -bind = $mod SHIFT, Page_Down, movetoworkspace, e+1 -bind = $mod SHIFT, Page_Up, movetoworkspace, e-1 -bind = $mod SHIFT, U, movetoworkspace, e+1 -bind = $mod SHIFT, I, movetoworkspace, e-1 - -# === Mouse Wheel Navigation === -bind = $mod, mouse_down, workspace, e+1 -bind = $mod, mouse_up, workspace, e-1 -bind = $mod CTRL, mouse_down, movetoworkspace, e+1 -bind = $mod CTRL, mouse_up, movetoworkspace, e-1 - -# === Numbered Workspaces === -bind = $mod, 1, workspace, 1 -bind = $mod, 2, workspace, 2 -bind = $mod, 3, workspace, 3 -bind = $mod, 4, workspace, 4 -bind = $mod, 5, workspace, 5 -bind = $mod, 6, workspace, 6 -bind = $mod, 7, workspace, 7 -bind = $mod, 8, workspace, 8 -bind = $mod, 9, workspace, 9 - -# === Move to Numbered Workspaces === -bind = $mod SHIFT, 1, movetoworkspace, 1 -bind = $mod SHIFT, 2, movetoworkspace, 2 -bind = $mod SHIFT, 3, movetoworkspace, 3 -bind = $mod SHIFT, 4, movetoworkspace, 4 -bind = $mod SHIFT, 5, movetoworkspace, 5 -bind = $mod SHIFT, 6, movetoworkspace, 6 -bind = $mod SHIFT, 7, movetoworkspace, 7 -bind = $mod SHIFT, 8, movetoworkspace, 8 -bind = $mod SHIFT, 9, movetoworkspace, 9 - -# === Column Management === -bind = $mod, bracketleft, layoutmsg, preselect l -bind = $mod, bracketright, layoutmsg, preselect r - -# === Sizing & Layout === -bind = $mod, R, layoutmsg, togglesplit -bind = $mod CTRL, F, resizeactive, exact 100% - -# === Move/resize windows with mainMod + LMB/RMB and dragging === -bindmd = $mod, mouse:272, Move window, movewindow -bindmd = $mod, mouse:273, Resize window, resizewindow - -# === Move/resize windows with mainMod + LMB/RMB and dragging === -bindd = $mod, code:20, Expand window left, resizeactive, -100 0 -bindd = $mod, code:21, Shrink window left, resizeactive, 100 0 - -# === Manual Sizing === -binde = $mod, minus, resizeactive, -10% 0 -binde = $mod, equal, resizeactive, 10% 0 -binde = $mod SHIFT, minus, resizeactive, 0 -10% -binde = $mod SHIFT, equal, resizeactive, 0 10% - -# === Screenshots === -bind = , XF86Launch1, exec, grimblast copy area -bind = CTRL, XF86Launch1, exec, grimblast copy screen -bind = ALT, XF86Launch1, exec, grimblast copy active -bind = , Print, exec, grimblast copy area -bind = CTRL, Print, exec, grimblast copy screen -bind = ALT, Print, exec, grimblast copy active - -# === System Controls === -bind = $mod SHIFT, P, dpms, off diff --git a/nix/inputs/dms-cli/internal/config/embedded/kitty-tabs.conf b/nix/inputs/dms-cli/internal/config/embedded/kitty-tabs.conf deleted file mode 100644 index b2305c8..0000000 --- a/nix/inputs/dms-cli/internal/config/embedded/kitty-tabs.conf +++ /dev/null @@ -1,24 +0,0 @@ -tab_bar_edge top -tab_bar_style powerline -tab_powerline_style slanted -tab_bar_align left -tab_bar_min_tabs 2 -tab_bar_margin_width 0.0 -tab_bar_margin_height 2.5 1.5 -tab_bar_margin_color #101418 - -tab_bar_background #101418 - -active_tab_foreground #cfe5ff -active_tab_background #124a73 -active_tab_font_style bold - -inactive_tab_foreground #c2c7cf -inactive_tab_background #101418 -inactive_tab_font_style normal - -tab_activity_symbol " ● " -tab_numbers_style 1 - -tab_title_template "{fmt.fg.red}{bell_symbol}{activity_symbol}{fmt.fg.tab}{title[:30]}{title[30:] and '…'} [{index}]" -active_tab_title_template "{fmt.fg.red}{bell_symbol}{activity_symbol}{fmt.fg.tab}{title[:30]}{title[30:] and '…'} [{index}]" diff --git a/nix/inputs/dms-cli/internal/config/embedded/kitty-theme.conf b/nix/inputs/dms-cli/internal/config/embedded/kitty-theme.conf deleted file mode 100644 index 2587bcf..0000000 --- a/nix/inputs/dms-cli/internal/config/embedded/kitty-theme.conf +++ /dev/null @@ -1,24 +0,0 @@ -cursor #e0e2e8 -cursor_text_color #c2c7cf - -foreground #e0e2e8 -background #101418 -selection_foreground #243240 -selection_background #b9c8da -url_color #9dcbfb -color0 #101418 -color1 #d75a59 -color2 #8ed88c -color3 #e0d99d -color4 #4087bc -color5 #839fbc -color6 #9dcbfb -color7 #abb2bf -color8 #5c6370 -color9 #e57e7e -color10 #a2e5a0 -color11 #efe9b3 -color12 #a7d9ff -color13 #3d8197 -color14 #5c7ba3 -color15 #ffffff diff --git a/nix/inputs/dms-cli/internal/config/embedded/kitty.conf b/nix/inputs/dms-cli/internal/config/embedded/kitty.conf deleted file mode 100644 index 2ae96a0..0000000 --- a/nix/inputs/dms-cli/internal/config/embedded/kitty.conf +++ /dev/null @@ -1,37 +0,0 @@ -# Font Configuration -font_size 12.0 - -# Window Configuration -window_padding_width 12 -background_opacity 1.0 -background_blur 32 -hide_window_decorations yes - -# Cursor Configuration -cursor_shape block -cursor_blink_interval 1 - -# Scrollback -scrollback_lines 3000 - -# Terminal features -copy_on_select yes -strip_trailing_spaces smart - -# Key bindings for common actions -map ctrl+shift+n new_window -map ctrl+t new_tab -map ctrl+plus change_font_size all +1.0 -map ctrl+minus change_font_size all -1.0 -map ctrl+0 change_font_size all 0 - -# Tab configuration -tab_bar_style powerline -tab_bar_align left - -# Shell integration -shell_integration enabled - -# Dank color generation -include dank-tabs.conf -include dank-theme.conf diff --git a/nix/inputs/dms-cli/internal/config/embedded/niri.kdl b/nix/inputs/dms-cli/internal/config/embedded/niri.kdl deleted file mode 100644 index 1faa139..0000000 --- a/nix/inputs/dms-cli/internal/config/embedded/niri.kdl +++ /dev/null @@ -1,418 +0,0 @@ -// This config is in the KDL format: https://kdl.dev -// "/-" comments out the following node. -// Check the wiki for a full description of the configuration: -// https://github.com/YaLTeR/niri/wiki/Configuration:-Introduction -config-notification { - disable-failed -} - -gestures { - hot-corners { - off - } -} - -// Input device configuration. -// Find the full list of options on the wiki: -// https://github.com/YaLTeR/niri/wiki/Configuration:-Input -input { - keyboard { - xkb { - } - numlock - } - touchpad { - } - mouse { - } - trackpoint { - } -} -// You can configure outputs by their name, which you can find -// by running `niri msg outputs` while inside a niri instance. -// The built-in laptop monitor is usually called "eDP-1". -// Find more information on the wiki: -// https://github.com/YaLTeR/niri/wiki/Configuration:-Outputs -// Remember to uncomment the node by removing "/-"! -/-output "eDP-2" { - mode "2560x1600@239.998993" - position x=2560 y=0 - variable-refresh-rate -} -// Settings that influence how windows are positioned and sized. -// Find more information on the wiki: -// https://github.com/YaLTeR/niri/wiki/Configuration:-Layout -layout { - // Set gaps around windows in logical pixels. - gaps 5 - background-color "transparent" - // When to center a column when changing focus, options are: - // - "never", default behavior, focusing an off-screen column will keep at the left - // or right edge of the screen. - // - "always", the focused column will always be centered. - // - "on-overflow", focusing a column will center it if it doesn't fit - // together with the previously focused column. - center-focused-column "never" - // You can customize the widths that "switch-preset-column-width" (Mod+R) toggles between. - preset-column-widths { - // Proportion sets the width as a fraction of the output width, taking gaps into account. - // For example, you can perfectly fit four windows sized "proportion 0.25" on an output. - // The default preset widths are 1/3, 1/2 and 2/3 of the output. - proportion 0.33333 - proportion 0.5 - proportion 0.66667 - // Fixed sets the width in logical pixels exactly. - // fixed 1920 - } - // You can also customize the heights that "switch-preset-window-height" (Mod+Shift+R) toggles between. - // preset-window-heights { } - // You can change the default width of the new windows. - default-column-width { proportion 0.5; } - // If you leave the brackets empty, the windows themselves will decide their initial width. - // default-column-width {} - // By default focus ring and border are rendered as a solid background rectangle - // behind windows. That is, they will show up through semitransparent windows. - // This is because windows using client-side decorations can have an arbitrary shape. - // - // If you don't like that, you should uncomment `prefer-no-csd` below. - // Niri will draw focus ring and border *around* windows that agree to omit their - // client-side decorations. - // - // Alternatively, you can override it with a window rule called - // `draw-border-with-background`. - border { - off - width 4 - active-color "#707070" // Neutral gray - inactive-color "#d0d0d0" // Light gray - urgent-color "#cc4444" // Softer red - } - focus-ring { - width 2 - active-color "#808080" // Medium gray - inactive-color "#505050" // Dark gray - } - shadow { - softness 30 - spread 5 - offset x=0 y=5 - color "#0007" - } - struts { - } -} -layer-rule { - match namespace="^quickshell$" - place-within-backdrop true -} -overview { - workspace-shadow { - off - } -} -// Add lines like this to spawn processes at startup. -// Note that running niri as a session supports xdg-desktop-autostart, -// which may be more convenient to use. -// See the binds section below for more spawn examples. -// This line starts waybar, a commonly used bar for Wayland compositors. -spawn-at-startup "bash" "-c" "wl-paste --watch cliphist store &" -spawn-at-startup "dms" "run" -spawn-at-startup "{{POLKIT_AGENT_PATH}}" -environment { - XDG_CURRENT_DESKTOP "niri" - QT_QPA_PLATFORM "wayland" - ELECTRON_OZONE_PLATFORM_HINT "auto" - QT_QPA_PLATFORMTHEME "gtk3" - QT_QPA_PLATFORMTHEME_QT6 "gtk3" - TERMINAL "{{TERMINAL_COMMAND}}" -} -hotkey-overlay { - skip-at-startup -} -prefer-no-csd -screenshot-path "~/Pictures/Screenshots/Screenshot from %Y-%m-%d %H-%M-%S.png" -animations { - workspace-switch { - spring damping-ratio=0.80 stiffness=523 epsilon=0.0001 - } - window-open { - duration-ms 150 - curve "ease-out-expo" - } - window-close { - duration-ms 150 - curve "ease-out-quad" - } - horizontal-view-movement { - spring damping-ratio=0.85 stiffness=423 epsilon=0.0001 - } - window-movement { - spring damping-ratio=0.75 stiffness=323 epsilon=0.0001 - } - window-resize { - spring damping-ratio=0.85 stiffness=423 epsilon=0.0001 - } - config-notification-open-close { - spring damping-ratio=0.65 stiffness=923 epsilon=0.001 - } - screenshot-ui-open { - duration-ms 200 - curve "ease-out-quad" - } - overview-open-close { - spring damping-ratio=0.85 stiffness=800 epsilon=0.0001 - } -} -// Window rules let you adjust behavior for individual windows. -// Find more information on the wiki: -// https://github.com/YaLTeR/niri/wiki/Configuration:-Window-Rules -// Work around WezTerm's initial configure bug -// by setting an empty default-column-width. -window-rule { - // This regular expression is intentionally made as specific as possible, - // since this is the default config, and we want no false positives. - // You can get away with just app-id="wezterm" if you want. - match app-id=r#"^org\.wezfurlong\.wezterm$"# - default-column-width {} -} -window-rule { - match app-id=r#"^org\.gnome\."# - draw-border-with-background false - geometry-corner-radius 12 - clip-to-geometry true -} -window-rule { - match app-id=r#"^gnome-control-center$"# - match app-id=r#"^pavucontrol$"# - match app-id=r#"^nm-connection-editor$"# - default-column-width { proportion 0.5; } - open-floating false -} -window-rule { - match app-id=r#"^gnome-calculator$"# - match app-id=r#"^galculator$"# - match app-id=r#"^blueman-manager$"# - match app-id=r#"^org\.gnome\.Nautilus$"# - match app-id=r#"^steam$"# - match app-id=r#"^xdg-desktop-portal$"# - open-floating true -} -window-rule { - match app-id=r#"^org\.wezfurlong\.wezterm$"# - match app-id="Alacritty" - match app-id="zen" - match app-id="com.mitchellh.ghostty" - match app-id="kitty" - draw-border-with-background false -} -window-rule { - match is-active=false - opacity 0.9 -} -window-rule { - match app-id=r#"firefox$"# title="^Picture-in-Picture$" - match app-id="zoom" - open-floating true -} -window-rule { - geometry-corner-radius 12 - clip-to-geometry true -} -binds { - // === System & Overview === - Mod+D { spawn "niri" "msg" "action" "toggle-overview"; } - Mod+Tab repeat=false { toggle-overview; } - Mod+Shift+Slash { show-hotkey-overlay; } - - // === Application Launchers === - Mod+T hotkey-overlay-title="Open Terminal" { spawn "{{TERMINAL_COMMAND}}"; } - Mod+Space hotkey-overlay-title="Application Launcher" { - spawn "dms" "ipc" "call" "spotlight" "toggle"; - } - Mod+V hotkey-overlay-title="Clipboard Manager" { - spawn "dms" "ipc" "call" "clipboard" "toggle"; - } - Mod+M hotkey-overlay-title="Task Manager" { - spawn "dms" "ipc" "call" "processlist" "toggle"; - } - Mod+Comma hotkey-overlay-title="Settings" { - spawn "dms" "ipc" "call" "settings" "toggle"; - } - Mod+Y hotkey-overlay-title="Browse Wallpapers" { - spawn "dms" "ipc" "call" "dankdash" "wallpaper"; - } - Mod+N hotkey-overlay-title="Notification Center" { spawn "dms" "ipc" "call" "notifications" "toggle"; } - Mod+Shift+N hotkey-overlay-title="Notepad" { spawn "dms" "ipc" "call" "notepad" "toggle"; } - - // === Security === - Mod+Alt+L hotkey-overlay-title="Lock Screen" { - spawn "dms" "ipc" "call" "lock" "lock"; - } - Mod+Shift+E { quit; } - Ctrl+Alt+Delete hotkey-overlay-title="Task Manager" { - spawn "dms" "ipc" "call" "processlist" "toggle"; - } - - // === Audio Controls === - XF86AudioRaiseVolume allow-when-locked=true { - spawn "dms" "ipc" "call" "audio" "increment" "3"; - } - XF86AudioLowerVolume allow-when-locked=true { - spawn "dms" "ipc" "call" "audio" "decrement" "3"; - } - XF86AudioMute allow-when-locked=true { - spawn "dms" "ipc" "call" "audio" "mute"; - } - XF86AudioMicMute allow-when-locked=true { - spawn "dms" "ipc" "call" "audio" "micmute"; - } - - // === Brightness Controls === - XF86MonBrightnessUp allow-when-locked=true { - spawn "dms" "ipc" "call" "brightness" "increment" "5" ""; - } - XF86MonBrightnessDown allow-when-locked=true { - spawn "dms" "ipc" "call" "brightness" "decrement" "5" ""; - } - - // === Window Management === - Mod+Q repeat=false { close-window; } - Mod+F { maximize-column; } - Mod+Shift+F { fullscreen-window; } - Mod+Shift+T { toggle-window-floating; } - Mod+Shift+V { switch-focus-between-floating-and-tiling; } - Mod+W { toggle-column-tabbed-display; } - - // === Focus Navigation === - Mod+Left { focus-column-left; } - Mod+Down { focus-window-down; } - Mod+Up { focus-window-up; } - Mod+Right { focus-column-right; } - Mod+H { focus-column-left; } - Mod+J { focus-window-down; } - Mod+K { focus-window-up; } - Mod+L { focus-column-right; } - - // === Window Movement === - Mod+Shift+Left { move-column-left; } - Mod+Shift+Down { move-window-down; } - Mod+Shift+Up { move-window-up; } - Mod+Shift+Right { move-column-right; } - Mod+Shift+H { move-column-left; } - Mod+Shift+J { move-window-down; } - Mod+Shift+K { move-window-up; } - Mod+Shift+L { move-column-right; } - - // === Column Navigation === - Mod+Home { focus-column-first; } - Mod+End { focus-column-last; } - Mod+Ctrl+Home { move-column-to-first; } - Mod+Ctrl+End { move-column-to-last; } - - // === Monitor Navigation === - Mod+Ctrl+Left { focus-monitor-left; } - //Mod+Ctrl+Down { focus-monitor-down; } - //Mod+Ctrl+Up { focus-monitor-up; } - Mod+Ctrl+Right { focus-monitor-right; } - Mod+Ctrl+H { focus-monitor-left; } - Mod+Ctrl+J { focus-monitor-down; } - Mod+Ctrl+K { focus-monitor-up; } - Mod+Ctrl+L { focus-monitor-right; } - - // === Move to Monitor === - Mod+Shift+Ctrl+Left { move-column-to-monitor-left; } - Mod+Shift+Ctrl+Down { move-column-to-monitor-down; } - Mod+Shift+Ctrl+Up { move-column-to-monitor-up; } - Mod+Shift+Ctrl+Right { move-column-to-monitor-right; } - Mod+Shift+Ctrl+H { move-column-to-monitor-left; } - Mod+Shift+Ctrl+J { move-column-to-monitor-down; } - Mod+Shift+Ctrl+K { move-column-to-monitor-up; } - Mod+Shift+Ctrl+L { move-column-to-monitor-right; } - - // === Workspace Navigation === - Mod+Page_Down { focus-workspace-down; } - Mod+Page_Up { focus-workspace-up; } - Mod+U { focus-workspace-down; } - Mod+I { focus-workspace-up; } - Mod+Ctrl+Down { move-column-to-workspace-down; } - Mod+Ctrl+Up { move-column-to-workspace-up; } - Mod+Ctrl+U { move-column-to-workspace-down; } - Mod+Ctrl+I { move-column-to-workspace-up; } - - // === Move Workspaces === - Mod+Shift+Page_Down { move-workspace-down; } - Mod+Shift+Page_Up { move-workspace-up; } - Mod+Shift+U { move-workspace-down; } - Mod+Shift+I { move-workspace-up; } - - // === Mouse Wheel Navigation === - Mod+WheelScrollDown cooldown-ms=150 { focus-workspace-down; } - Mod+WheelScrollUp cooldown-ms=150 { focus-workspace-up; } - Mod+Ctrl+WheelScrollDown cooldown-ms=150 { move-column-to-workspace-down; } - Mod+Ctrl+WheelScrollUp cooldown-ms=150 { move-column-to-workspace-up; } - - Mod+WheelScrollRight { focus-column-right; } - Mod+WheelScrollLeft { focus-column-left; } - Mod+Ctrl+WheelScrollRight { move-column-right; } - Mod+Ctrl+WheelScrollLeft { move-column-left; } - - Mod+Shift+WheelScrollDown { focus-column-right; } - Mod+Shift+WheelScrollUp { focus-column-left; } - Mod+Ctrl+Shift+WheelScrollDown { move-column-right; } - Mod+Ctrl+Shift+WheelScrollUp { move-column-left; } - - // === Numbered Workspaces === - Mod+1 { focus-workspace 1; } - Mod+2 { focus-workspace 2; } - Mod+3 { focus-workspace 3; } - Mod+4 { focus-workspace 4; } - Mod+5 { focus-workspace 5; } - Mod+6 { focus-workspace 6; } - Mod+7 { focus-workspace 7; } - Mod+8 { focus-workspace 8; } - Mod+9 { focus-workspace 9; } - - // === Move to Numbered Workspaces === - Mod+Shift+1 { move-column-to-workspace 1; } - Mod+Shift+2 { move-column-to-workspace 2; } - Mod+Shift+3 { move-column-to-workspace 3; } - Mod+Shift+4 { move-column-to-workspace 4; } - Mod+Shift+5 { move-column-to-workspace 5; } - Mod+Shift+6 { move-column-to-workspace 6; } - Mod+Shift+7 { move-column-to-workspace 7; } - Mod+Shift+8 { move-column-to-workspace 8; } - Mod+Shift+9 { move-column-to-workspace 9; } - - // === Column Management === - Mod+BracketLeft { consume-or-expel-window-left; } - Mod+BracketRight { consume-or-expel-window-right; } - Mod+Period { expel-window-from-column; } - - // === Sizing & Layout === - Mod+R { switch-preset-column-width; } - Mod+Shift+R { switch-preset-window-height; } - Mod+Ctrl+R { reset-window-height; } - Mod+Ctrl+F { expand-column-to-available-width; } - Mod+C { center-column; } - Mod+Ctrl+C { center-visible-columns; } - - // === Manual Sizing === - Mod+Minus { set-column-width "-10%"; } - Mod+Equal { set-column-width "+10%"; } - Mod+Shift+Minus { set-window-height "-10%"; } - Mod+Shift+Equal { set-window-height "+10%"; } - - // === Screenshots === - XF86Launch1 { screenshot; } - Ctrl+XF86Launch1 { screenshot-screen; } - Alt+XF86Launch1 { screenshot-window; } - Print { screenshot; } - Ctrl+Print { screenshot-screen; } - Alt+Print { screenshot-window; } - // === System Controls === - Mod+Escape allow-inhibiting=false { toggle-keyboard-shortcuts-inhibit; } - Mod+Shift+P { power-off-monitors; } -} -debug { - honor-xdg-activation-with-invalid-serial -} diff --git a/nix/inputs/dms-cli/internal/config/hyprland.go b/nix/inputs/dms-cli/internal/config/hyprland.go deleted file mode 100644 index e572955..0000000 --- a/nix/inputs/dms-cli/internal/config/hyprland.go +++ /dev/null @@ -1,6 +0,0 @@ -package config - -import _ "embed" - -//go:embed embedded/hyprland.conf -var HyprlandConfig string diff --git a/nix/inputs/dms-cli/internal/config/niri.go b/nix/inputs/dms-cli/internal/config/niri.go deleted file mode 100644 index 78fb03a..0000000 --- a/nix/inputs/dms-cli/internal/config/niri.go +++ /dev/null @@ -1,6 +0,0 @@ -package config - -import _ "embed" - -//go:embed embedded/niri.kdl -var NiriConfig string diff --git a/nix/inputs/dms-cli/internal/config/terminals.go b/nix/inputs/dms-cli/internal/config/terminals.go deleted file mode 100644 index a5bba04..0000000 --- a/nix/inputs/dms-cli/internal/config/terminals.go +++ /dev/null @@ -1,24 +0,0 @@ -package config - -import _ "embed" - -//go:embed embedded/ghostty.conf -var GhosttyConfig string - -//go:embed embedded/ghostty-colors.conf -var GhosttyColorConfig string - -//go:embed embedded/kitty.conf -var KittyConfig string - -//go:embed embedded/kitty-theme.conf -var KittyThemeConfig string - -//go:embed embedded/kitty-tabs.conf -var KittyTabsConfig string - -//go:embed embedded/alacritty.toml -var AlacrittyConfig string - -//go:embed embedded/alacritty-theme.toml -var AlacrittyThemeConfig string diff --git a/nix/inputs/dms-cli/internal/dank16/dank16.go b/nix/inputs/dms-cli/internal/dank16/dank16.go deleted file mode 100644 index 4140eb0..0000000 --- a/nix/inputs/dms-cli/internal/dank16/dank16.go +++ /dev/null @@ -1,453 +0,0 @@ -package dank16 - -import ( - "fmt" - "math" - - "github.com/lucasb-eyer/go-colorful" -) - -type RGB struct { - R, G, B float64 -} - -type HSV struct { - H, S, V float64 -} - -func HexToRGB(hex string) RGB { - if hex[0] == '#' { - hex = hex[1:] - } - var r, g, b uint8 - fmt.Sscanf(hex, "%02x%02x%02x", &r, &g, &b) - return RGB{ - R: float64(r) / 255.0, - G: float64(g) / 255.0, - B: float64(b) / 255.0, - } -} - -func RGBToHex(rgb RGB) string { - r := math.Max(0, math.Min(1, rgb.R)) - g := math.Max(0, math.Min(1, rgb.G)) - b := math.Max(0, math.Min(1, rgb.B)) - return fmt.Sprintf("#%02x%02x%02x", int(r*255), int(g*255), int(b*255)) -} - -func RGBToHSV(rgb RGB) HSV { - max := math.Max(math.Max(rgb.R, rgb.G), rgb.B) - min := math.Min(math.Min(rgb.R, rgb.G), rgb.B) - delta := max - min - - var h float64 - if delta == 0 { - h = 0 - } else if max == rgb.R { - h = math.Mod((rgb.G-rgb.B)/delta, 6.0) / 6.0 - } else if max == rgb.G { - h = ((rgb.B-rgb.R)/delta + 2.0) / 6.0 - } else { - h = ((rgb.R-rgb.G)/delta + 4.0) / 6.0 - } - - if h < 0 { - h += 1.0 - } - - var s float64 - if max == 0 { - s = 0 - } else { - s = delta / max - } - - return HSV{H: h, S: s, V: max} -} - -func HSVToRGB(hsv HSV) RGB { - h := hsv.H * 6.0 - c := hsv.V * hsv.S - x := c * (1.0 - math.Abs(math.Mod(h, 2.0)-1.0)) - m := hsv.V - c - - var r, g, b float64 - switch int(h) { - case 0: - r, g, b = c, x, 0 - case 1: - r, g, b = x, c, 0 - case 2: - r, g, b = 0, c, x - case 3: - r, g, b = 0, x, c - case 4: - r, g, b = x, 0, c - case 5: - r, g, b = c, 0, x - default: - r, g, b = c, 0, x - } - - return RGB{R: r + m, G: g + m, B: b + m} -} - -func sRGBToLinear(c float64) float64 { - if c <= 0.04045 { - return c / 12.92 - } - return math.Pow((c+0.055)/1.055, 2.4) -} - -func Luminance(hex string) float64 { - rgb := HexToRGB(hex) - return 0.2126*sRGBToLinear(rgb.R) + 0.7152*sRGBToLinear(rgb.G) + 0.0722*sRGBToLinear(rgb.B) -} - -func ContrastRatio(hexFg, hexBg string) float64 { - lumFg := Luminance(hexFg) - lumBg := Luminance(hexBg) - lighter := math.Max(lumFg, lumBg) - darker := math.Min(lumFg, lumBg) - return (lighter + 0.05) / (darker + 0.05) -} - -func getLstar(hex string) float64 { - rgb := HexToRGB(hex) - col := colorful.Color{R: rgb.R, G: rgb.G, B: rgb.B} - L, _, _ := col.Lab() - return L * 100.0 // go-colorful uses 0-1, we need 0-100 for DPS -} - -// Lab to hex, clamping if needed -func labToHex(L, a, b float64) string { - c := colorful.Lab(L/100.0, a, b) // back to 0-1 for go-colorful - r, g, b2 := c.Clamped().RGB255() - return fmt.Sprintf("#%02x%02x%02x", r, g, b2) -} - -// Adjust brightness while keeping the same hue -func retoneToL(hex string, Ltarget float64) string { - rgb := HexToRGB(hex) - col := colorful.Color{R: rgb.R, G: rgb.G, B: rgb.B} - L, a, b := col.Lab() - L100 := L * 100.0 - - scale := 1.0 - if L100 != 0 { - scale = Ltarget / L100 - } - - a2, b2 := a*scale, b*scale - - // Don't let it get too saturated - maxChroma := 0.4 - if math.Hypot(a2, b2) > maxChroma { - k := maxChroma / math.Hypot(a2, b2) - a2 *= k - b2 *= k - } - - return labToHex(Ltarget, a2, b2) -} - -func DeltaPhiStar(hexFg, hexBg string, negativePolarity bool) float64 { - Lf := getLstar(hexFg) - Lb := getLstar(hexBg) - - phi := 1.618 - inv := 0.618 - lc := math.Pow(math.Abs(math.Pow(Lb, phi)-math.Pow(Lf, phi)), inv)*1.414 - 40 - - if negativePolarity { - lc += 5 - } - - return lc -} - -func DeltaPhiStarContrast(hexFg, hexBg string, isLightMode bool) float64 { - negativePolarity := !isLightMode - return DeltaPhiStar(hexFg, hexBg, negativePolarity) -} - -func EnsureContrast(hexColor, hexBg string, minRatio float64, isLightMode bool) string { - currentRatio := ContrastRatio(hexColor, hexBg) - if currentRatio >= minRatio { - return hexColor - } - - rgb := HexToRGB(hexColor) - hsv := RGBToHSV(rgb) - - for step := 1; step < 30; step++ { - delta := float64(step) * 0.02 - - if isLightMode { - newV := math.Max(0, hsv.V-delta) - candidate := RGBToHex(HSVToRGB(HSV{H: hsv.H, S: hsv.S, V: newV})) - if ContrastRatio(candidate, hexBg) >= minRatio { - return candidate - } - - newV = math.Min(1, hsv.V+delta) - candidate = RGBToHex(HSVToRGB(HSV{H: hsv.H, S: hsv.S, V: newV})) - if ContrastRatio(candidate, hexBg) >= minRatio { - return candidate - } - } else { - newV := math.Min(1, hsv.V+delta) - candidate := RGBToHex(HSVToRGB(HSV{H: hsv.H, S: hsv.S, V: newV})) - if ContrastRatio(candidate, hexBg) >= minRatio { - return candidate - } - - newV = math.Max(0, hsv.V-delta) - candidate = RGBToHex(HSVToRGB(HSV{H: hsv.H, S: hsv.S, V: newV})) - if ContrastRatio(candidate, hexBg) >= minRatio { - return candidate - } - } - } - - return hexColor -} - -func EnsureContrastDPS(hexColor, hexBg string, minLc float64, isLightMode bool) string { - currentLc := DeltaPhiStarContrast(hexColor, hexBg, isLightMode) - if currentLc >= minLc { - return hexColor - } - - rgb := HexToRGB(hexColor) - hsv := RGBToHSV(rgb) - - for step := 1; step < 50; step++ { - delta := float64(step) * 0.015 - - if isLightMode { - newV := math.Max(0, hsv.V-delta) - candidate := RGBToHex(HSVToRGB(HSV{H: hsv.H, S: hsv.S, V: newV})) - if DeltaPhiStarContrast(candidate, hexBg, isLightMode) >= minLc { - return candidate - } - - newV = math.Min(1, hsv.V+delta) - candidate = RGBToHex(HSVToRGB(HSV{H: hsv.H, S: hsv.S, V: newV})) - if DeltaPhiStarContrast(candidate, hexBg, isLightMode) >= minLc { - return candidate - } - } else { - newV := math.Min(1, hsv.V+delta) - candidate := RGBToHex(HSVToRGB(HSV{H: hsv.H, S: hsv.S, V: newV})) - if DeltaPhiStarContrast(candidate, hexBg, isLightMode) >= minLc { - return candidate - } - - newV = math.Max(0, hsv.V-delta) - candidate = RGBToHex(HSVToRGB(HSV{H: hsv.H, S: hsv.S, V: newV})) - if DeltaPhiStarContrast(candidate, hexBg, isLightMode) >= minLc { - return candidate - } - } - } - - return hexColor -} - -// Nudge L* until contrast is good enough. Keeps hue intact unlike HSV fiddling. -func EnsureContrastDPSLstar(hexColor, hexBg string, minLc float64, isLightMode bool) string { - current := DeltaPhiStarContrast(hexColor, hexBg, isLightMode) - if current >= minLc { - return hexColor - } - - fg := HexToRGB(hexColor) - cf := colorful.Color{R: fg.R, G: fg.G, B: fg.B} - Lf, af, bf := cf.Lab() - - dir := 1.0 - if isLightMode { - dir = -1.0 // light mode = darker text - } - - step := 0.5 - for i := 0; i < 120; i++ { - Lf = math.Max(0, math.Min(100, Lf+dir*step)) - cand := labToHex(Lf, af, bf) - if DeltaPhiStarContrast(cand, hexBg, isLightMode) >= minLc { - return cand - } - } - - return hexColor -} - -type PaletteOptions struct { - IsLight bool - Background string - UseDPS bool -} - -func ensureContrastAuto(hexColor, hexBg string, target float64, opts PaletteOptions) string { - if opts.UseDPS { - return EnsureContrastDPSLstar(hexColor, hexBg, target, opts.IsLight) - } - return EnsureContrast(hexColor, hexBg, target, opts.IsLight) -} - -func DeriveContainer(primary string, isLight bool) string { - rgb := HexToRGB(primary) - hsv := RGBToHSV(rgb) - - if isLight { - containerV := math.Min(hsv.V*1.77, 1.0) - containerS := hsv.S * 0.32 - return RGBToHex(HSVToRGB(HSV{H: hsv.H, S: containerS, V: containerV})) - } - containerV := hsv.V * 0.463 - containerS := math.Min(hsv.S*1.834, 1.0) - return RGBToHex(HSVToRGB(HSV{H: hsv.H, S: containerS, V: containerV})) -} - -func GeneratePalette(primaryColor string, opts PaletteOptions) []string { - baseColor := DeriveContainer(primaryColor, opts.IsLight) - - rgb := HexToRGB(baseColor) - hsv := RGBToHSV(rgb) - - palette := make([]string, 0, 16) - - var normalTextTarget, secondaryTarget float64 - if opts.UseDPS { - normalTextTarget = 40.0 - secondaryTarget = 35.0 - } else { - normalTextTarget = 4.5 - secondaryTarget = 3.0 - } - - var bgColor string - if opts.Background != "" { - bgColor = opts.Background - } else if opts.IsLight { - bgColor = "#f8f8f8" - } else { - bgColor = "#1a1a1a" - } - palette = append(palette, bgColor) - - hueShift := (hsv.H - 0.6) * 0.12 - satBoost := 1.15 - - redH := math.Mod(0.0+hueShift+1.0, 1.0) - var redColor string - if opts.IsLight { - redColor = RGBToHex(HSVToRGB(HSV{H: redH, S: math.Min(0.80*satBoost, 1.0), V: 0.55})) - palette = append(palette, ensureContrastAuto(redColor, bgColor, normalTextTarget, opts)) - } else { - redColor = RGBToHex(HSVToRGB(HSV{H: redH, S: math.Min(0.65*satBoost, 1.0), V: 0.80})) - palette = append(palette, ensureContrastAuto(redColor, bgColor, normalTextTarget, opts)) - } - - greenH := math.Mod(0.33+hueShift+1.0, 1.0) - var greenColor string - if opts.IsLight { - greenColor = RGBToHex(HSVToRGB(HSV{H: greenH, S: math.Min(math.Max(hsv.S*0.9, 0.80)*satBoost, 1.0), V: 0.45})) - palette = append(palette, ensureContrastAuto(greenColor, bgColor, normalTextTarget, opts)) - } else { - greenColor = RGBToHex(HSVToRGB(HSV{H: greenH, S: math.Min(0.42*satBoost, 1.0), V: 0.84})) - palette = append(palette, ensureContrastAuto(greenColor, bgColor, normalTextTarget, opts)) - } - - yellowH := math.Mod(0.15+hueShift+1.0, 1.0) - var yellowColor string - if opts.IsLight { - yellowColor = RGBToHex(HSVToRGB(HSV{H: yellowH, S: math.Min(0.75*satBoost, 1.0), V: 0.50})) - palette = append(palette, ensureContrastAuto(yellowColor, bgColor, normalTextTarget, opts)) - } else { - yellowColor = RGBToHex(HSVToRGB(HSV{H: yellowH, S: math.Min(0.38*satBoost, 1.0), V: 0.86})) - palette = append(palette, ensureContrastAuto(yellowColor, bgColor, normalTextTarget, opts)) - } - - var blueColor string - if opts.IsLight { - blueColor = RGBToHex(HSVToRGB(HSV{H: hsv.H, S: math.Max(hsv.S*0.9, 0.7), V: hsv.V * 1.1})) - palette = append(palette, ensureContrastAuto(blueColor, bgColor, normalTextTarget, opts)) - } else { - blueColor = RGBToHex(HSVToRGB(HSV{H: hsv.H, S: math.Max(hsv.S*0.8, 0.6), V: math.Min(hsv.V*1.6, 1.0)})) - palette = append(palette, ensureContrastAuto(blueColor, bgColor, normalTextTarget, opts)) - } - - magH := hsv.H - 0.03 - if magH < 0 { - magH += 1.0 - } - var magColor string - hr := HexToRGB(primaryColor) - hh := RGBToHSV(hr) - if opts.IsLight { - magColor = RGBToHex(HSVToRGB(HSV{H: hh.H, S: math.Max(hh.S*0.9, 0.7), V: hh.V * 0.85})) - palette = append(palette, ensureContrastAuto(magColor, bgColor, normalTextTarget, opts)) - } else { - magColor = RGBToHex(HSVToRGB(HSV{H: hh.H, S: hh.S * 0.8, V: hh.V * 0.75})) - palette = append(palette, ensureContrastAuto(magColor, bgColor, normalTextTarget, opts)) - } - - cyanH := hsv.H + 0.08 - if cyanH > 1.0 { - cyanH -= 1.0 - } - palette = append(palette, ensureContrastAuto(primaryColor, bgColor, normalTextTarget, opts)) - - if opts.IsLight { - palette = append(palette, "#1a1a1a") - palette = append(palette, "#2e2e2e") - } else { - palette = append(palette, "#abb2bf") - palette = append(palette, "#5c6370") - } - - if opts.IsLight { - brightRed := RGBToHex(HSVToRGB(HSV{H: redH, S: math.Min(0.70*satBoost, 1.0), V: 0.65})) - palette = append(palette, ensureContrastAuto(brightRed, bgColor, secondaryTarget, opts)) - brightGreen := RGBToHex(HSVToRGB(HSV{H: greenH, S: math.Min(math.Max(hsv.S*0.85, 0.75)*satBoost, 1.0), V: 0.55})) - palette = append(palette, ensureContrastAuto(brightGreen, bgColor, secondaryTarget, opts)) - brightYellow := RGBToHex(HSVToRGB(HSV{H: yellowH, S: math.Min(0.68*satBoost, 1.0), V: 0.60})) - palette = append(palette, ensureContrastAuto(brightYellow, bgColor, secondaryTarget, opts)) - hr := HexToRGB(primaryColor) - hh := RGBToHSV(hr) - brightBlue := RGBToHex(HSVToRGB(HSV{H: hh.H, S: math.Min(hh.S*1.1, 1.0), V: math.Min(hh.V*1.2, 1.0)})) - palette = append(palette, ensureContrastAuto(brightBlue, bgColor, secondaryTarget, opts)) - brightMag := RGBToHex(HSVToRGB(HSV{H: magH, S: math.Max(hsv.S*0.9, 0.75), V: math.Min(hsv.V*1.25, 1.0)})) - palette = append(palette, ensureContrastAuto(brightMag, bgColor, secondaryTarget, opts)) - brightCyan := RGBToHex(HSVToRGB(HSV{H: cyanH, S: math.Max(hsv.S*0.75, 0.65), V: math.Min(hsv.V*1.25, 1.0)})) - palette = append(palette, ensureContrastAuto(brightCyan, bgColor, secondaryTarget, opts)) - } else { - brightRed := RGBToHex(HSVToRGB(HSV{H: redH, S: math.Min(0.50*satBoost, 1.0), V: 0.88})) - palette = append(palette, ensureContrastAuto(brightRed, bgColor, secondaryTarget, opts)) - brightGreen := RGBToHex(HSVToRGB(HSV{H: greenH, S: math.Min(0.35*satBoost, 1.0), V: 0.88})) - palette = append(palette, ensureContrastAuto(brightGreen, bgColor, secondaryTarget, opts)) - brightYellow := RGBToHex(HSVToRGB(HSV{H: yellowH, S: math.Min(0.30*satBoost, 1.0), V: 0.91})) - palette = append(palette, ensureContrastAuto(brightYellow, bgColor, secondaryTarget, opts)) - // Make it way brighter for type names in dark mode - brightBlue := retoneToL(primaryColor, 85.0) - palette = append(palette, brightBlue) - brightMag := RGBToHex(HSVToRGB(HSV{H: magH, S: math.Max(hsv.S*0.7, 0.6), V: math.Min(hsv.V*1.3, 0.9)})) - palette = append(palette, ensureContrastAuto(brightMag, bgColor, secondaryTarget, opts)) - brightCyanH := hsv.H + 0.02 - if brightCyanH > 1.0 { - brightCyanH -= 1.0 - } - brightCyan := RGBToHex(HSVToRGB(HSV{H: brightCyanH, S: math.Max(hsv.S*0.6, 0.5), V: math.Min(hsv.V*1.2, 0.85)})) - palette = append(palette, ensureContrastAuto(brightCyan, bgColor, secondaryTarget, opts)) - } - - if opts.IsLight { - palette = append(palette, "#1a1a1a") - } else { - palette = append(palette, "#ffffff") - } - - return palette -} diff --git a/nix/inputs/dms-cli/internal/dank16/dank16_test.go b/nix/inputs/dms-cli/internal/dank16/dank16_test.go deleted file mode 100644 index 4b6f167..0000000 --- a/nix/inputs/dms-cli/internal/dank16/dank16_test.go +++ /dev/null @@ -1,727 +0,0 @@ -package dank16 - -import ( - "encoding/json" - "math" - "testing" -) - -func TestHexToRGB(t *testing.T) { - tests := []struct { - name string - input string - expected RGB - }{ - { - name: "black with hash", - input: "#000000", - expected: RGB{R: 0.0, G: 0.0, B: 0.0}, - }, - { - name: "white with hash", - input: "#ffffff", - expected: RGB{R: 1.0, G: 1.0, B: 1.0}, - }, - { - name: "red without hash", - input: "ff0000", - expected: RGB{R: 1.0, G: 0.0, B: 0.0}, - }, - { - name: "purple", - input: "#625690", - expected: RGB{R: 0.3843137254901961, G: 0.33725490196078434, B: 0.5647058823529412}, - }, - { - name: "mid gray", - input: "#808080", - expected: RGB{R: 0.5019607843137255, G: 0.5019607843137255, B: 0.5019607843137255}, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := HexToRGB(tt.input) - if !floatEqual(result.R, tt.expected.R) || !floatEqual(result.G, tt.expected.G) || !floatEqual(result.B, tt.expected.B) { - t.Errorf("HexToRGB(%s) = %v, expected %v", tt.input, result, tt.expected) - } - }) - } -} - -func TestRGBToHex(t *testing.T) { - tests := []struct { - name string - input RGB - expected string - }{ - { - name: "black", - input: RGB{R: 0.0, G: 0.0, B: 0.0}, - expected: "#000000", - }, - { - name: "white", - input: RGB{R: 1.0, G: 1.0, B: 1.0}, - expected: "#ffffff", - }, - { - name: "red", - input: RGB{R: 1.0, G: 0.0, B: 0.0}, - expected: "#ff0000", - }, - { - name: "clamping above 1.0", - input: RGB{R: 1.5, G: 0.5, B: 0.5}, - expected: "#ff7f7f", - }, - { - name: "clamping below 0.0", - input: RGB{R: -0.5, G: 0.5, B: 0.5}, - expected: "#007f7f", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := RGBToHex(tt.input) - if result != tt.expected { - t.Errorf("RGBToHex(%v) = %s, expected %s", tt.input, result, tt.expected) - } - }) - } -} - -func TestRGBToHSV(t *testing.T) { - tests := []struct { - name string - input RGB - expected HSV - }{ - { - name: "black", - input: RGB{R: 0.0, G: 0.0, B: 0.0}, - expected: HSV{H: 0.0, S: 0.0, V: 0.0}, - }, - { - name: "white", - input: RGB{R: 1.0, G: 1.0, B: 1.0}, - expected: HSV{H: 0.0, S: 0.0, V: 1.0}, - }, - { - name: "red", - input: RGB{R: 1.0, G: 0.0, B: 0.0}, - expected: HSV{H: 0.0, S: 1.0, V: 1.0}, - }, - { - name: "green", - input: RGB{R: 0.0, G: 1.0, B: 0.0}, - expected: HSV{H: 0.3333333333333333, S: 1.0, V: 1.0}, - }, - { - name: "blue", - input: RGB{R: 0.0, G: 0.0, B: 1.0}, - expected: HSV{H: 0.6666666666666666, S: 1.0, V: 1.0}, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := RGBToHSV(tt.input) - if !floatEqual(result.H, tt.expected.H) || !floatEqual(result.S, tt.expected.S) || !floatEqual(result.V, tt.expected.V) { - t.Errorf("RGBToHSV(%v) = %v, expected %v", tt.input, result, tt.expected) - } - }) - } -} - -func TestHSVToRGB(t *testing.T) { - tests := []struct { - name string - input HSV - expected RGB - }{ - { - name: "black", - input: HSV{H: 0.0, S: 0.0, V: 0.0}, - expected: RGB{R: 0.0, G: 0.0, B: 0.0}, - }, - { - name: "white", - input: HSV{H: 0.0, S: 0.0, V: 1.0}, - expected: RGB{R: 1.0, G: 1.0, B: 1.0}, - }, - { - name: "red", - input: HSV{H: 0.0, S: 1.0, V: 1.0}, - expected: RGB{R: 1.0, G: 0.0, B: 0.0}, - }, - { - name: "green", - input: HSV{H: 0.3333333333333333, S: 1.0, V: 1.0}, - expected: RGB{R: 0.0, G: 1.0, B: 0.0}, - }, - { - name: "blue", - input: HSV{H: 0.6666666666666666, S: 1.0, V: 1.0}, - expected: RGB{R: 0.0, G: 0.0, B: 1.0}, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := HSVToRGB(tt.input) - if !floatEqual(result.R, tt.expected.R) || !floatEqual(result.G, tt.expected.G) || !floatEqual(result.B, tt.expected.B) { - t.Errorf("HSVToRGB(%v) = %v, expected %v", tt.input, result, tt.expected) - } - }) - } -} - -func TestLuminance(t *testing.T) { - tests := []struct { - name string - input string - expected float64 - }{ - { - name: "black", - input: "#000000", - expected: 0.0, - }, - { - name: "white", - input: "#ffffff", - expected: 1.0, - }, - { - name: "red", - input: "#ff0000", - expected: 0.2126, - }, - { - name: "green", - input: "#00ff00", - expected: 0.7152, - }, - { - name: "blue", - input: "#0000ff", - expected: 0.0722, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := Luminance(tt.input) - if !floatEqual(result, tt.expected) { - t.Errorf("Luminance(%s) = %f, expected %f", tt.input, result, tt.expected) - } - }) - } -} - -func TestContrastRatio(t *testing.T) { - tests := []struct { - name string - fg string - bg string - expected float64 - }{ - { - name: "black on white", - fg: "#000000", - bg: "#ffffff", - expected: 21.0, - }, - { - name: "white on black", - fg: "#ffffff", - bg: "#000000", - expected: 21.0, - }, - { - name: "same color", - fg: "#808080", - bg: "#808080", - expected: 1.0, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := ContrastRatio(tt.fg, tt.bg) - if !floatEqual(result, tt.expected) { - t.Errorf("ContrastRatio(%s, %s) = %f, expected %f", tt.fg, tt.bg, result, tt.expected) - } - }) - } -} - -func TestEnsureContrast(t *testing.T) { - tests := []struct { - name string - color string - bg string - minRatio float64 - isLightMode bool - }{ - { - name: "already sufficient contrast dark mode", - color: "#ffffff", - bg: "#000000", - minRatio: 4.5, - isLightMode: false, - }, - { - name: "already sufficient contrast light mode", - color: "#000000", - bg: "#ffffff", - minRatio: 4.5, - isLightMode: true, - }, - { - name: "needs adjustment dark mode", - color: "#404040", - bg: "#1a1a1a", - minRatio: 4.5, - isLightMode: false, - }, - { - name: "needs adjustment light mode", - color: "#c0c0c0", - bg: "#f8f8f8", - minRatio: 4.5, - isLightMode: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := EnsureContrast(tt.color, tt.bg, tt.minRatio, tt.isLightMode) - actualRatio := ContrastRatio(result, tt.bg) - if actualRatio < tt.minRatio { - t.Errorf("EnsureContrast(%s, %s, %f, %t) = %s with ratio %f, expected ratio >= %f", - tt.color, tt.bg, tt.minRatio, tt.isLightMode, result, actualRatio, tt.minRatio) - } - }) - } -} - -func TestGeneratePalette(t *testing.T) { - tests := []struct { - name string - base string - opts PaletteOptions - }{ - { - name: "dark theme default", - base: "#625690", - opts: PaletteOptions{IsLight: false}, - }, - { - name: "light theme default", - base: "#625690", - opts: PaletteOptions{IsLight: true}, - }, - { - name: "light theme with custom background", - base: "#625690", - opts: PaletteOptions{ - IsLight: true, - Background: "#fafafa", - }, - }, - { - name: "dark theme with custom background", - base: "#625690", - opts: PaletteOptions{ - IsLight: false, - Background: "#0a0a0a", - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := GeneratePalette(tt.base, tt.opts) - - if len(result) != 16 { - t.Errorf("GeneratePalette returned %d colors, expected 16", len(result)) - } - - for i, color := range result { - if len(color) != 7 || color[0] != '#' { - t.Errorf("Color at index %d (%s) is not a valid hex color", i, color) - } - } - - if tt.opts.Background != "" && result[0] != tt.opts.Background { - t.Errorf("Background color = %s, expected %s", result[0], tt.opts.Background) - } else if !tt.opts.IsLight && tt.opts.Background == "" && result[0] != "#1a1a1a" { - t.Errorf("Dark mode background = %s, expected #1a1a1a", result[0]) - } else if tt.opts.IsLight && tt.opts.Background == "" && result[0] != "#f8f8f8" { - t.Errorf("Light mode background = %s, expected #f8f8f8", result[0]) - } - - if tt.opts.IsLight && result[15] != "#1a1a1a" { - t.Errorf("Light mode foreground = %s, expected #1a1a1a", result[15]) - } else if !tt.opts.IsLight && result[15] != "#ffffff" { - t.Errorf("Dark mode foreground = %s, expected #ffffff", result[15]) - } - }) - } -} - -func TestEnrichVSCodeTheme(t *testing.T) { - colors := GeneratePalette("#625690", PaletteOptions{IsLight: false}) - - baseTheme := map[string]interface{}{ - "name": "Test Theme", - "type": "dark", - "colors": map[string]interface{}{ - "editor.background": "#000000", - }, - } - - themeJSON, err := json.Marshal(baseTheme) - if err != nil { - t.Fatalf("Failed to marshal base theme: %v", err) - } - - result, err := EnrichVSCodeTheme(themeJSON, colors) - if err != nil { - t.Fatalf("EnrichVSCodeTheme failed: %v", err) - } - - var enriched map[string]interface{} - if err := json.Unmarshal(result, &enriched); err != nil { - t.Fatalf("Failed to unmarshal result: %v", err) - } - - colorsMap, ok := enriched["colors"].(map[string]interface{}) - if !ok { - t.Fatal("colors is not a map") - } - - terminalColors := []string{ - "terminal.ansiBlack", - "terminal.ansiRed", - "terminal.ansiGreen", - "terminal.ansiYellow", - "terminal.ansiBlue", - "terminal.ansiMagenta", - "terminal.ansiCyan", - "terminal.ansiWhite", - "terminal.ansiBrightBlack", - "terminal.ansiBrightRed", - "terminal.ansiBrightGreen", - "terminal.ansiBrightYellow", - "terminal.ansiBrightBlue", - "terminal.ansiBrightMagenta", - "terminal.ansiBrightCyan", - "terminal.ansiBrightWhite", - } - - for i, key := range terminalColors { - if val, ok := colorsMap[key]; !ok { - t.Errorf("Missing terminal color: %s", key) - } else if val != colors[i] { - t.Errorf("%s = %s, expected %s", key, val, colors[i]) - } - } - - if colorsMap["editor.background"] != "#000000" { - t.Error("Original theme colors should be preserved") - } -} - -func TestEnrichVSCodeThemeInvalidJSON(t *testing.T) { - colors := GeneratePalette("#625690", PaletteOptions{IsLight: false}) - invalidJSON := []byte("{invalid json") - - _, err := EnrichVSCodeTheme(invalidJSON, colors) - if err == nil { - t.Error("Expected error for invalid JSON, got nil") - } -} - -func TestRoundTripConversion(t *testing.T) { - testColors := []string{"#000000", "#ffffff", "#ff0000", "#00ff00", "#0000ff", "#625690", "#808080"} - - for _, hex := range testColors { - t.Run(hex, func(t *testing.T) { - rgb := HexToRGB(hex) - result := RGBToHex(rgb) - if result != hex { - t.Errorf("Round trip %s -> RGB -> %s failed", hex, result) - } - }) - } -} - -func TestRGBHSVRoundTrip(t *testing.T) { - testCases := []RGB{ - {R: 0.0, G: 0.0, B: 0.0}, - {R: 1.0, G: 1.0, B: 1.0}, - {R: 1.0, G: 0.0, B: 0.0}, - {R: 0.0, G: 1.0, B: 0.0}, - {R: 0.0, G: 0.0, B: 1.0}, - {R: 0.5, G: 0.5, B: 0.5}, - {R: 0.3843137254901961, G: 0.33725490196078434, B: 0.5647058823529412}, - } - - for _, rgb := range testCases { - t.Run("", func(t *testing.T) { - hsv := RGBToHSV(rgb) - result := HSVToRGB(hsv) - if !floatEqual(result.R, rgb.R) || !floatEqual(result.G, rgb.G) || !floatEqual(result.B, rgb.B) { - t.Errorf("Round trip RGB->HSV->RGB failed: %v -> %v -> %v", rgb, hsv, result) - } - }) - } -} - -func floatEqual(a, b float64) bool { - return math.Abs(a-b) < 1e-9 -} - -func TestDeltaPhiStar(t *testing.T) { - tests := []struct { - name string - fg string - bg string - negativePolarity bool - minExpected float64 - }{ - { - name: "white on black (negative polarity)", - fg: "#ffffff", - bg: "#000000", - negativePolarity: true, - minExpected: 100.0, - }, - { - name: "black on white (positive polarity)", - fg: "#000000", - bg: "#ffffff", - negativePolarity: false, - minExpected: 100.0, - }, - { - name: "low contrast same color", - fg: "#808080", - bg: "#808080", - negativePolarity: false, - minExpected: -40.0, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := DeltaPhiStar(tt.fg, tt.bg, tt.negativePolarity) - if result < tt.minExpected { - t.Errorf("DeltaPhiStar(%s, %s, %v) = %f, expected >= %f", - tt.fg, tt.bg, tt.negativePolarity, result, tt.minExpected) - } - }) - } -} - -func TestDeltaPhiStarContrast(t *testing.T) { - tests := []struct { - name string - fg string - bg string - isLightMode bool - minExpected float64 - }{ - { - name: "white on black (dark mode)", - fg: "#ffffff", - bg: "#000000", - isLightMode: false, - minExpected: 100.0, - }, - { - name: "black on white (light mode)", - fg: "#000000", - bg: "#ffffff", - isLightMode: true, - minExpected: 100.0, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := DeltaPhiStarContrast(tt.fg, tt.bg, tt.isLightMode) - if result < tt.minExpected { - t.Errorf("DeltaPhiStarContrast(%s, %s, %v) = %f, expected >= %f", - tt.fg, tt.bg, tt.isLightMode, result, tt.minExpected) - } - }) - } -} - -func TestEnsureContrastDPS(t *testing.T) { - tests := []struct { - name string - color string - bg string - minLc float64 - isLightMode bool - }{ - { - name: "already sufficient contrast dark mode", - color: "#ffffff", - bg: "#000000", - minLc: 60.0, - isLightMode: false, - }, - { - name: "already sufficient contrast light mode", - color: "#000000", - bg: "#ffffff", - minLc: 60.0, - isLightMode: true, - }, - { - name: "needs adjustment dark mode", - color: "#404040", - bg: "#1a1a1a", - minLc: 60.0, - isLightMode: false, - }, - { - name: "needs adjustment light mode", - color: "#c0c0c0", - bg: "#f8f8f8", - minLc: 60.0, - isLightMode: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := EnsureContrastDPS(tt.color, tt.bg, tt.minLc, tt.isLightMode) - actualLc := DeltaPhiStarContrast(result, tt.bg, tt.isLightMode) - if actualLc < tt.minLc { - t.Errorf("EnsureContrastDPS(%s, %s, %f, %t) = %s with Lc %f, expected Lc >= %f", - tt.color, tt.bg, tt.minLc, tt.isLightMode, result, actualLc, tt.minLc) - } - }) - } -} - -func TestGeneratePaletteWithDPS(t *testing.T) { - tests := []struct { - name string - base string - opts PaletteOptions - }{ - { - name: "dark theme with DPS", - base: "#625690", - opts: PaletteOptions{IsLight: false, UseDPS: true}, - }, - { - name: "light theme with DPS", - base: "#625690", - opts: PaletteOptions{IsLight: true, UseDPS: true}, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := GeneratePalette(tt.base, tt.opts) - - if len(result) != 16 { - t.Errorf("GeneratePalette returned %d colors, expected 16", len(result)) - } - - for i, color := range result { - if len(color) != 7 || color[0] != '#' { - t.Errorf("Color at index %d (%s) is not a valid hex color", i, color) - } - } - - bgColor := result[0] - for i := 1; i < 8; i++ { - lc := DeltaPhiStarContrast(result[i], bgColor, tt.opts.IsLight) - minLc := 30.0 - if lc < minLc && lc > 0 { - t.Errorf("Color %d (%s) has insufficient DPS contrast %f with background %s (expected >= %f)", - i, result[i], lc, bgColor, minLc) - } - } - }) - } -} - -func TestDeriveContainer(t *testing.T) { - tests := []struct { - name string - primary string - isLight bool - expected string - }{ - { - name: "dark mode", - primary: "#ccbdff", - isLight: false, - expected: "#4a3e76", - }, - { - name: "light mode", - primary: "#625690", - isLight: true, - expected: "#e7deff", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := DeriveContainer(tt.primary, tt.isLight) - - resultRGB := HexToRGB(result) - expectedRGB := HexToRGB(tt.expected) - - rDiff := math.Abs(resultRGB.R - expectedRGB.R) - gDiff := math.Abs(resultRGB.G - expectedRGB.G) - bDiff := math.Abs(resultRGB.B - expectedRGB.B) - - tolerance := 0.02 - if rDiff > tolerance || gDiff > tolerance || bDiff > tolerance { - t.Errorf("DeriveContainer(%s, %v) = %s, expected %s (RGB diff: R:%.4f G:%.4f B:%.4f)", - tt.primary, tt.isLight, result, tt.expected, rDiff, gDiff, bDiff) - } - }) - } -} - -func TestContrastAlgorithmComparison(t *testing.T) { - base := "#625690" - - optsWCAG := PaletteOptions{IsLight: false, UseDPS: false} - optsDPS := PaletteOptions{IsLight: false, UseDPS: true} - - paletteWCAG := GeneratePalette(base, optsWCAG) - paletteDPS := GeneratePalette(base, optsDPS) - - if len(paletteWCAG) != 16 || len(paletteDPS) != 16 { - t.Fatal("Both palettes should have 16 colors") - } - - if paletteWCAG[0] != paletteDPS[0] { - t.Errorf("Background colors differ: WCAG=%s, DPS=%s", paletteWCAG[0], paletteDPS[0]) - } - - differentCount := 0 - for i := 0; i < 16; i++ { - if paletteWCAG[i] != paletteDPS[i] { - differentCount++ - } - } - - t.Logf("WCAG and DPS palettes differ in %d/16 colors", differentCount) -} diff --git a/nix/inputs/dms-cli/internal/dank16/terminals.go b/nix/inputs/dms-cli/internal/dank16/terminals.go deleted file mode 100644 index d2290b6..0000000 --- a/nix/inputs/dms-cli/internal/dank16/terminals.go +++ /dev/null @@ -1,126 +0,0 @@ -package dank16 - -import ( - "encoding/json" - "fmt" - "strings" -) - -func GenerateJSON(colors []string) string { - colorMap := make(map[string]string) - - for i, color := range colors { - colorMap[fmt.Sprintf("color%d", i)] = color - } - - marshalled, _ := json.Marshal(colorMap) - - return string(marshalled) -} - -func GenerateKittyTheme(colors []string) string { - kittyColors := []struct { - name string - index int - }{ - {"color0", 0}, - {"color1", 1}, - {"color2", 2}, - {"color3", 3}, - {"color4", 4}, - {"color5", 5}, - {"color6", 6}, - {"color7", 7}, - {"color8", 8}, - {"color9", 9}, - {"color10", 10}, - {"color11", 11}, - {"color12", 12}, - {"color13", 13}, - {"color14", 14}, - {"color15", 15}, - } - - var result strings.Builder - for _, kc := range kittyColors { - fmt.Fprintf(&result, "%s %s\n", kc.name, colors[kc.index]) - } - return result.String() -} - -func GenerateFootTheme(colors []string) string { - footColors := []struct { - name string - index int - }{ - {"regular0", 0}, - {"regular1", 1}, - {"regular2", 2}, - {"regular3", 3}, - {"regular4", 4}, - {"regular5", 5}, - {"regular6", 6}, - {"regular7", 7}, - {"bright0", 8}, - {"bright1", 9}, - {"bright2", 10}, - {"bright3", 11}, - {"bright4", 12}, - {"bright5", 13}, - {"bright6", 14}, - {"bright7", 15}, - } - - var result strings.Builder - for _, fc := range footColors { - fmt.Fprintf(&result, "%s=%s\n", fc.name, strings.TrimPrefix(colors[fc.index], "#")) - } - return result.String() -} - -func GenerateAlacrittyTheme(colors []string) string { - alacrittyColors := []struct { - section string - name string - index int - }{ - {"normal", "black", 0}, - {"normal", "red", 1}, - {"normal", "green", 2}, - {"normal", "yellow", 3}, - {"normal", "blue", 4}, - {"normal", "magenta", 5}, - {"normal", "cyan", 6}, - {"normal", "white", 7}, - {"bright", "black", 8}, - {"bright", "red", 9}, - {"bright", "green", 10}, - {"bright", "yellow", 11}, - {"bright", "blue", 12}, - {"bright", "magenta", 13}, - {"bright", "cyan", 14}, - {"bright", "white", 15}, - } - - var result strings.Builder - currentSection := "" - for _, ac := range alacrittyColors { - if ac.section != currentSection { - if currentSection != "" { - result.WriteString("\n") - } - fmt.Fprintf(&result, "[colors.%s]\n", ac.section) - currentSection = ac.section - } - fmt.Fprintf(&result, "%-7s = '%s'\n", ac.name, colors[ac.index]) - } - return result.String() -} - -func GenerateGhosttyTheme(colors []string) string { - var result strings.Builder - for i, color := range colors { - fmt.Fprintf(&result, "palette = %d=%s\n", i, color) - } - return result.String() -} diff --git a/nix/inputs/dms-cli/internal/dank16/vscode.go b/nix/inputs/dms-cli/internal/dank16/vscode.go deleted file mode 100644 index cd4d4d7..0000000 --- a/nix/inputs/dms-cli/internal/dank16/vscode.go +++ /dev/null @@ -1,250 +0,0 @@ -package dank16 - -import ( - "encoding/json" - "fmt" -) - -type VSCodeTheme struct { - Schema string `json:"$schema"` - Name string `json:"name"` - Type string `json:"type"` - Colors map[string]string `json:"colors"` - TokenColors []VSCodeTokenColor `json:"tokenColors"` - SemanticHighlighting bool `json:"semanticHighlighting"` - SemanticTokenColors map[string]VSCodeTokenSetting `json:"semanticTokenColors"` -} - -type VSCodeTokenColor struct { - Scope interface{} `json:"scope"` - Settings VSCodeTokenSetting `json:"settings"` -} - -type VSCodeTokenSetting struct { - Foreground string `json:"foreground,omitempty"` - FontStyle string `json:"fontStyle,omitempty"` -} - -func updateTokenColor(tc interface{}, scopeToColor map[string]string) { - tcMap, ok := tc.(map[string]interface{}) - if !ok { - return - } - - scopes, ok := tcMap["scope"].([]interface{}) - if !ok { - return - } - - settings, ok := tcMap["settings"].(map[string]interface{}) - if !ok { - return - } - - isYaml := hasScopeContaining(scopes, "yaml") - - for _, scope := range scopes { - scopeStr, ok := scope.(string) - if !ok { - continue - } - - if scopeStr == "string" && isYaml { - continue - } - - if applyColorToScope(settings, scope, scopeToColor) { - break - } - } -} - -func applyColorToScope(settings map[string]interface{}, scope interface{}, scopeToColor map[string]string) bool { - scopeStr, ok := scope.(string) - if !ok { - return false - } - - newColor, exists := scopeToColor[scopeStr] - if !exists { - return false - } - - settings["foreground"] = newColor - return true -} - -func hasScopeContaining(scopes []interface{}, substring string) bool { - for _, scope := range scopes { - scopeStr, ok := scope.(string) - if !ok { - continue - } - - for i := 0; i <= len(scopeStr)-len(substring); i++ { - if scopeStr[i:i+len(substring)] == substring { - return true - } - } - } - return false -} - -func EnrichVSCodeTheme(themeData []byte, colors []string) ([]byte, error) { - var theme map[string]interface{} - if err := json.Unmarshal(themeData, &theme); err != nil { - return nil, err - } - - colorsMap, ok := theme["colors"].(map[string]interface{}) - if !ok { - colorsMap = make(map[string]interface{}) - theme["colors"] = colorsMap - } - - bg := colors[0] - isLight := false - if len(bg) == 7 && bg[0] == '#' { - r, g, b := 0, 0, 0 - fmt.Sscanf(bg[1:], "%02x%02x%02x", &r, &g, &b) - luminance := (0.299*float64(r) + 0.587*float64(g) + 0.114*float64(b)) / 255.0 - isLight = luminance > 0.5 - } - - if isLight { - theme["type"] = "light" - } else { - theme["type"] = "dark" - } - - colorsMap["terminal.ansiBlack"] = colors[0] - colorsMap["terminal.ansiRed"] = colors[1] - colorsMap["terminal.ansiGreen"] = colors[2] - colorsMap["terminal.ansiYellow"] = colors[3] - colorsMap["terminal.ansiBlue"] = colors[4] - colorsMap["terminal.ansiMagenta"] = colors[5] - colorsMap["terminal.ansiCyan"] = colors[6] - colorsMap["terminal.ansiWhite"] = colors[7] - colorsMap["terminal.ansiBrightBlack"] = colors[8] - colorsMap["terminal.ansiBrightRed"] = colors[9] - colorsMap["terminal.ansiBrightGreen"] = colors[10] - colorsMap["terminal.ansiBrightYellow"] = colors[11] - colorsMap["terminal.ansiBrightBlue"] = colors[12] - colorsMap["terminal.ansiBrightMagenta"] = colors[13] - colorsMap["terminal.ansiBrightCyan"] = colors[14] - colorsMap["terminal.ansiBrightWhite"] = colors[15] - - tokenColors, ok := theme["tokenColors"].([]interface{}) - if ok { - scopeToColor := map[string]string{ - "comment": colors[8], - "punctuation.definition.comment": colors[8], - "keyword": colors[5], - "storage.type": colors[13], - "storage.modifier": colors[5], - "variable": colors[15], - "variable.parameter": colors[7], - "meta.object-literal.key": colors[4], - "meta.property.object": colors[4], - "variable.other.property": colors[4], - "constant.other.symbol": colors[12], - "constant.numeric": colors[12], - "constant.language": colors[12], - "constant.character": colors[3], - "entity.name.type": colors[12], - "support.type": colors[13], - "entity.name.class": colors[12], - "entity.name.function": colors[2], - "support.function": colors[2], - "support.class": colors[15], - "support.variable": colors[15], - "variable.language": colors[12], - "entity.name.tag.yaml": colors[12], - "string.unquoted.plain.out.yaml": colors[15], - "string.unquoted.yaml": colors[15], - "string": colors[3], - } - - for i, tc := range tokenColors { - updateTokenColor(tc, scopeToColor) - tokenColors[i] = tc - } - - yamlRules := []VSCodeTokenColor{ - { - Scope: "entity.name.tag.yaml", - Settings: VSCodeTokenSetting{Foreground: colors[12]}, - }, - { - Scope: []string{"string.unquoted.plain.out.yaml", "string.unquoted.yaml"}, - Settings: VSCodeTokenSetting{Foreground: colors[15]}, - }, - } - - for _, rule := range yamlRules { - tokenColors = append(tokenColors, rule) - } - - theme["tokenColors"] = tokenColors - } - - if semanticTokenColors, ok := theme["semanticTokenColors"].(map[string]interface{}); ok { - updates := map[string]string{ - "variable": colors[15], - "variable.readonly": colors[12], - "property": colors[4], - "function": colors[2], - "method": colors[2], - "type": colors[12], - "class": colors[12], - "typeParameter": colors[13], - "enumMember": colors[12], - "string": colors[3], - "number": colors[12], - "comment": colors[8], - "keyword": colors[5], - "operator": colors[15], - "parameter": colors[7], - "namespace": colors[15], - } - - for key, color := range updates { - if existing, ok := semanticTokenColors[key].(map[string]interface{}); ok { - existing["foreground"] = color - } else { - semanticTokenColors[key] = map[string]interface{}{ - "foreground": color, - } - } - } - } else { - semanticTokenColors := make(map[string]interface{}) - updates := map[string]string{ - "variable": colors[7], - "variable.readonly": colors[12], - "property": colors[4], - "function": colors[2], - "method": colors[2], - "type": colors[12], - "class": colors[12], - "typeParameter": colors[13], - "enumMember": colors[12], - "string": colors[3], - "number": colors[12], - "comment": colors[8], - "keyword": colors[5], - "operator": colors[15], - "parameter": colors[7], - "namespace": colors[15], - } - - for key, color := range updates { - semanticTokenColors[key] = map[string]interface{}{ - "foreground": color, - } - } - theme["semanticTokenColors"] = semanticTokenColors - } - - return json.MarshalIndent(theme, "", " ") -} diff --git a/nix/inputs/dms-cli/internal/deps/detector.go b/nix/inputs/dms-cli/internal/deps/detector.go deleted file mode 100644 index e4f54a7..0000000 --- a/nix/inputs/dms-cli/internal/deps/detector.go +++ /dev/null @@ -1,51 +0,0 @@ -package deps - -import ( - "context" -) - -type DependencyStatus int - -const ( - StatusMissing DependencyStatus = iota - StatusInstalled - StatusNeedsUpdate - StatusNeedsReinstall -) - -type PackageVariant int - -const ( - VariantStable PackageVariant = iota - VariantGit -) - -type Dependency struct { - Name string - Status DependencyStatus - Version string - Description string - Required bool - Variant PackageVariant - CanToggle bool -} - -type WindowManager int - -const ( - WindowManagerHyprland WindowManager = iota - WindowManagerNiri -) - -type Terminal int - -const ( - TerminalGhostty Terminal = iota - TerminalKitty - TerminalAlacritty -) - -type DependencyDetector interface { - DetectDependencies(ctx context.Context, wm WindowManager) ([]Dependency, error) - DetectDependenciesWithTerminal(ctx context.Context, wm WindowManager, terminal Terminal) ([]Dependency, error) -} diff --git a/nix/inputs/dms-cli/internal/distros/arch.go b/nix/inputs/dms-cli/internal/distros/arch.go deleted file mode 100644 index 141f966..0000000 --- a/nix/inputs/dms-cli/internal/distros/arch.go +++ /dev/null @@ -1,785 +0,0 @@ -package distros - -import ( - "context" - "fmt" - "os" - "os/exec" - "path/filepath" - "runtime" - "strings" - - "github.com/AvengeMedia/danklinux/internal/deps" -) - -func init() { - Register("arch", "#1793D1", FamilyArch, func(config DistroConfig, logChan chan<- string) Distribution { - return NewArchDistribution(config, logChan) - }) - Register("archarm", "#1793D1", FamilyArch, func(config DistroConfig, logChan chan<- string) Distribution { - return NewArchDistribution(config, logChan) - }) - Register("archcraft", "#1793D1", FamilyArch, func(config DistroConfig, logChan chan<- string) Distribution { - return NewArchDistribution(config, logChan) - }) - Register("cachyos", "#08A283", FamilyArch, func(config DistroConfig, logChan chan<- string) Distribution { - return NewArchDistribution(config, logChan) - }) - Register("endeavouros", "#7F3FBF", FamilyArch, func(config DistroConfig, logChan chan<- string) Distribution { - return NewArchDistribution(config, logChan) - }) - Register("manjaro", "#35BF5C", FamilyArch, func(config DistroConfig, logChan chan<- string) Distribution { - return NewArchDistribution(config, logChan) - }) - Register("obarun", "#2494be", FamilyArch, func(config DistroConfig, logChan chan<- string) Distribution { - return NewArchDistribution(config, logChan) - }) - Register("garuda", "#cba6f7", FamilyArch, func(config DistroConfig, logChan chan<- string) Distribution { - return NewArchDistribution(config, logChan) - }) -} - -type ArchDistribution struct { - *BaseDistribution - *ManualPackageInstaller - config DistroConfig -} - -func NewArchDistribution(config DistroConfig, logChan chan<- string) *ArchDistribution { - base := NewBaseDistribution(logChan) - return &ArchDistribution{ - BaseDistribution: base, - ManualPackageInstaller: &ManualPackageInstaller{BaseDistribution: base}, - config: config, - } -} - -func (a *ArchDistribution) GetID() string { - return a.config.ID -} - -func (a *ArchDistribution) GetColorHex() string { - return a.config.ColorHex -} - -func (a *ArchDistribution) GetFamily() DistroFamily { - return a.config.Family -} - -func (a *ArchDistribution) GetPackageManager() PackageManagerType { - return PackageManagerPacman -} - -func (a *ArchDistribution) DetectDependencies(ctx context.Context, wm deps.WindowManager) ([]deps.Dependency, error) { - return a.DetectDependenciesWithTerminal(ctx, wm, deps.TerminalGhostty) -} - -func (a *ArchDistribution) DetectDependenciesWithTerminal(ctx context.Context, wm deps.WindowManager, terminal deps.Terminal) ([]deps.Dependency, error) { - var dependencies []deps.Dependency - - // DMS at the top (shell is prominent) - dependencies = append(dependencies, a.detectDMS()) - - // Terminal with choice support - dependencies = append(dependencies, a.detectSpecificTerminal(terminal)) - - // Common detections using base methods - dependencies = append(dependencies, a.detectGit()) - dependencies = append(dependencies, a.detectWindowManager(wm)) - dependencies = append(dependencies, a.detectQuickshell()) - dependencies = append(dependencies, a.detectXDGPortal()) - dependencies = append(dependencies, a.detectPolkitAgent()) - dependencies = append(dependencies, a.detectAccountsService()) - - // Hyprland-specific tools - if wm == deps.WindowManagerHyprland { - dependencies = append(dependencies, a.detectHyprlandTools()...) - } - - // Niri-specific tools - if wm == deps.WindowManagerNiri { - dependencies = append(dependencies, a.detectXwaylandSatellite()) - } - - // Base detections (common across distros) - dependencies = append(dependencies, a.detectMatugen()) - dependencies = append(dependencies, a.detectDgop()) - dependencies = append(dependencies, a.detectHyprpicker()) - dependencies = append(dependencies, a.detectClipboardTools()...) - - return dependencies, nil -} - -func (a *ArchDistribution) detectXDGPortal() deps.Dependency { - status := deps.StatusMissing - if a.packageInstalled("xdg-desktop-portal-gtk") { - status = deps.StatusInstalled - } - - return deps.Dependency{ - Name: "xdg-desktop-portal-gtk", - Status: status, - Description: "Desktop integration portal for GTK", - Required: true, - } -} - -func (a *ArchDistribution) detectPolkitAgent() deps.Dependency { - status := deps.StatusMissing - if a.packageInstalled("mate-polkit") { - status = deps.StatusInstalled - } - - return deps.Dependency{ - Name: "mate-polkit", - Status: status, - Description: "PolicyKit authentication agent", - Required: true, - } -} - -func (a *ArchDistribution) detectAccountsService() deps.Dependency { - status := deps.StatusMissing - if a.packageInstalled("accountsservice") { - status = deps.StatusInstalled - } - - return deps.Dependency{ - Name: "accountsservice", - Status: status, - Description: "D-Bus interface for user account query and manipulation", - Required: true, - } -} - -func (a *ArchDistribution) packageInstalled(pkg string) bool { - cmd := exec.Command("pacman", "-Q", pkg) - err := cmd.Run() - return err == nil -} - -func (a *ArchDistribution) GetPackageMapping(wm deps.WindowManager) map[string]PackageMapping { - return a.GetPackageMappingWithVariants(wm, make(map[string]deps.PackageVariant)) -} - -func (a *ArchDistribution) GetPackageMappingWithVariants(wm deps.WindowManager, variants map[string]deps.PackageVariant) map[string]PackageMapping { - packages := map[string]PackageMapping{ - "dms (DankMaterialShell)": a.getDMSMapping(variants["dms (DankMaterialShell)"]), - "git": {Name: "git", Repository: RepoTypeSystem}, - "quickshell": a.getQuickshellMapping(variants["quickshell"]), - "matugen": a.getMatugenMapping(variants["matugen"]), - "dgop": {Name: "dgop", Repository: RepoTypeSystem}, - "ghostty": {Name: "ghostty", Repository: RepoTypeSystem}, - "kitty": {Name: "kitty", Repository: RepoTypeSystem}, - "alacritty": {Name: "alacritty", Repository: RepoTypeSystem}, - "cliphist": {Name: "cliphist", Repository: RepoTypeSystem}, - "wl-clipboard": {Name: "wl-clipboard", Repository: RepoTypeSystem}, - "xdg-desktop-portal-gtk": {Name: "xdg-desktop-portal-gtk", Repository: RepoTypeSystem}, - "mate-polkit": {Name: "mate-polkit", Repository: RepoTypeSystem}, - "accountsservice": {Name: "accountsservice", Repository: RepoTypeSystem}, - "hyprpicker": {Name: "hyprpicker", Repository: RepoTypeSystem}, - } - - switch wm { - case deps.WindowManagerHyprland: - packages["hyprland"] = a.getHyprlandMapping(variants["hyprland"]) - packages["grim"] = PackageMapping{Name: "grim", Repository: RepoTypeSystem} - packages["slurp"] = PackageMapping{Name: "slurp", Repository: RepoTypeSystem} - packages["hyprctl"] = a.getHyprlandMapping(variants["hyprland"]) - packages["grimblast"] = PackageMapping{Name: "grimblast", Repository: RepoTypeManual, BuildFunc: "installGrimblast"} - packages["jq"] = PackageMapping{Name: "jq", Repository: RepoTypeSystem} - case deps.WindowManagerNiri: - packages["niri"] = a.getNiriMapping(variants["niri"]) - packages["xwayland-satellite"] = PackageMapping{Name: "xwayland-satellite", Repository: RepoTypeSystem} - } - - return packages -} - -func (a *ArchDistribution) getQuickshellMapping(variant deps.PackageVariant) PackageMapping { - if forceQuickshellGit || variant == deps.VariantGit { - return PackageMapping{Name: "quickshell-git", Repository: RepoTypeAUR} - } - return PackageMapping{Name: "quickshell", Repository: RepoTypeSystem} -} - -func (a *ArchDistribution) getHyprlandMapping(variant deps.PackageVariant) PackageMapping { - if variant == deps.VariantGit { - return PackageMapping{Name: "hyprland-git", Repository: RepoTypeAUR} - } - return PackageMapping{Name: "hyprland", Repository: RepoTypeSystem} -} - -func (a *ArchDistribution) getNiriMapping(variant deps.PackageVariant) PackageMapping { - if variant == deps.VariantGit { - return PackageMapping{Name: "niri-git", Repository: RepoTypeAUR} - } - return PackageMapping{Name: "niri", Repository: RepoTypeSystem} -} - -func (a *ArchDistribution) getMatugenMapping(variant deps.PackageVariant) PackageMapping { - if runtime.GOARCH == "arm64" { - return PackageMapping{Name: "matugen-git", Repository: RepoTypeAUR} - } - - if variant == deps.VariantGit { - return PackageMapping{Name: "matugen-git", Repository: RepoTypeAUR} - } - return PackageMapping{Name: "matugen", Repository: RepoTypeSystem} -} - -func (a *ArchDistribution) getDMSMapping(variant deps.PackageVariant) PackageMapping { - if forceDMSGit || variant == deps.VariantGit { - return PackageMapping{Name: "dms-shell-git", Repository: RepoTypeAUR} - } - - if a.packageInstalled("dms-shell-git") { - return PackageMapping{Name: "dms-shell-git", Repository: RepoTypeAUR} - } - - if a.packageInstalled("dms-shell-bin") { - return PackageMapping{Name: "dms-shell-bin", Repository: RepoTypeAUR} - } - - return PackageMapping{Name: "dms-shell-bin", Repository: RepoTypeAUR} -} - -func (a *ArchDistribution) detectXwaylandSatellite() deps.Dependency { - status := deps.StatusMissing - if a.commandExists("xwayland-satellite") { - status = deps.StatusInstalled - } - - return deps.Dependency{ - Name: "xwayland-satellite", - Status: status, - Description: "Xwayland support", - Required: true, - } -} - -func (a *ArchDistribution) InstallPrerequisites(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.06, - Step: "Checking base-devel...", - IsComplete: false, - LogOutput: "Checking if base-devel is installed", - } - - checkCmd := exec.CommandContext(ctx, "pacman", "-Qq", "base-devel") - if err := checkCmd.Run(); err == nil { - a.log("base-devel already installed") - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.10, - Step: "base-devel already installed", - IsComplete: false, - LogOutput: "base-devel is already installed on the system", - } - return nil - } - - a.log("Installing base-devel...") - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.08, - Step: "Installing base-devel...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: "sudo pacman -S --needed --noconfirm base-devel", - LogOutput: "Installing base-devel development tools", - } - - cmd := exec.CommandContext(ctx, "bash", "-c", fmt.Sprintf("echo '%s' | sudo -S pacman -S --needed --noconfirm base-devel", sudoPassword)) - if err := a.runWithProgress(cmd, progressChan, PhasePrerequisites, 0.08, 0.10); err != nil { - return fmt.Errorf("failed to install base-devel: %w", err) - } - - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.12, - Step: "base-devel installation complete", - IsComplete: false, - LogOutput: "base-devel successfully installed", - } - - return nil -} - -func (a *ArchDistribution) InstallPackages(ctx context.Context, dependencies []deps.Dependency, wm deps.WindowManager, sudoPassword string, reinstallFlags map[string]bool, progressChan chan<- InstallProgressMsg) error { - // Phase 1: Check Prerequisites - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.05, - Step: "Checking system prerequisites...", - IsComplete: false, - LogOutput: "Starting prerequisite check...", - } - - if err := a.InstallPrerequisites(ctx, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install prerequisites: %w", err) - } - - systemPkgs, aurPkgs, manualPkgs := a.categorizePackages(dependencies, wm, reinstallFlags) - - // Phase 3: System Packages - if len(systemPkgs) > 0 { - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.35, - Step: fmt.Sprintf("Installing %d system packages...", len(systemPkgs)), - IsComplete: false, - NeedsSudo: true, - LogOutput: fmt.Sprintf("Installing system packages: %s", strings.Join(systemPkgs, ", ")), - } - if err := a.installSystemPackages(ctx, systemPkgs, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install system packages: %w", err) - } - } - - // Phase 4: AUR Packages - if len(aurPkgs) > 0 { - progressChan <- InstallProgressMsg{ - Phase: PhaseAURPackages, - Progress: 0.65, - Step: fmt.Sprintf("Installing %d AUR packages...", len(aurPkgs)), - IsComplete: false, - LogOutput: fmt.Sprintf("Installing AUR packages: %s", strings.Join(aurPkgs, ", ")), - } - if err := a.installAURPackages(ctx, aurPkgs, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install AUR packages: %w", err) - } - } - - // Phase 5: Manual Builds - if len(manualPkgs) > 0 { - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.85, - Step: fmt.Sprintf("Building %d packages from source...", len(manualPkgs)), - IsComplete: false, - LogOutput: fmt.Sprintf("Building from source: %s", strings.Join(manualPkgs, ", ")), - } - if err := a.InstallManualPackages(ctx, manualPkgs, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install manual packages: %w", err) - } - } - - // Phase 6: Configuration - progressChan <- InstallProgressMsg{ - Phase: PhaseConfiguration, - Progress: 0.90, - Step: "Configuring system...", - IsComplete: false, - LogOutput: "Starting post-installation configuration...", - } - - // Phase 7: Complete - progressChan <- InstallProgressMsg{ - Phase: PhaseComplete, - Progress: 1.0, - Step: "Installation complete!", - IsComplete: true, - LogOutput: "All packages installed and configured successfully", - } - - return nil -} - -func (a *ArchDistribution) categorizePackages(dependencies []deps.Dependency, wm deps.WindowManager, reinstallFlags map[string]bool) ([]string, []string, []string) { - systemPkgs := []string{} - aurPkgs := []string{} - manualPkgs := []string{} - - variantMap := make(map[string]deps.PackageVariant) - for _, dep := range dependencies { - variantMap[dep.Name] = dep.Variant - } - - packageMap := a.GetPackageMappingWithVariants(wm, variantMap) - - for _, dep := range dependencies { - // Skip installed packages unless marked for reinstall - if dep.Status == deps.StatusInstalled && !reinstallFlags[dep.Name] { - continue - } - - pkgInfo, exists := packageMap[dep.Name] - if !exists { - // If no mapping exists, treat as manual build - manualPkgs = append(manualPkgs, dep.Name) - continue - } - - switch pkgInfo.Repository { - case RepoTypeAUR: - aurPkgs = append(aurPkgs, pkgInfo.Name) - case RepoTypeSystem: - systemPkgs = append(systemPkgs, pkgInfo.Name) - case RepoTypeManual: - manualPkgs = append(manualPkgs, dep.Name) - } - } - - return systemPkgs, aurPkgs, manualPkgs -} - -func (a *ArchDistribution) installSystemPackages(ctx context.Context, packages []string, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - if len(packages) == 0 { - return nil - } - - a.log(fmt.Sprintf("Installing system packages: %s", strings.Join(packages, ", "))) - - args := []string{"pacman", "-S", "--needed", "--noconfirm"} - args = append(args, packages...) - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.40, - Step: "Installing system packages...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: fmt.Sprintf("sudo %s", strings.Join(args, " ")), - } - - cmdStr := fmt.Sprintf("echo '%s' | sudo -S %s", sudoPassword, strings.Join(args, " ")) - cmd := exec.CommandContext(ctx, "bash", "-c", cmdStr) - return a.runWithProgress(cmd, progressChan, PhaseSystemPackages, 0.40, 0.60) -} - -func (a *ArchDistribution) installAURPackages(ctx context.Context, packages []string, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - if len(packages) == 0 { - return nil - } - - a.log(fmt.Sprintf("Installing AUR packages manually: %s", strings.Join(packages, ", "))) - - hasNiri := false - hasQuickshell := false - for _, pkg := range packages { - if pkg == "niri-git" { - hasNiri = true - } - if pkg == "quickshell" || pkg == "quickshell-git" { - hasQuickshell = true - } - } - - // If quickshell is in the list, always reinstall google-breakpad first - if hasQuickshell { - progressChan <- InstallProgressMsg{ - Phase: PhaseAURPackages, - Progress: 0.63, - Step: "Reinstalling google-breakpad for quickshell...", - IsComplete: false, - CommandInfo: "Reinstalling prerequisite AUR package for quickshell", - } - - if err := a.installSingleAURPackage(ctx, "google-breakpad", sudoPassword, progressChan, 0.63, 0.65); err != nil { - return fmt.Errorf("failed to reinstall google-breakpad prerequisite for quickshell: %w", err) - } - } - - // If niri is in the list, install makepkg-git-lfs-proto first if not already installed - if hasNiri { - if !a.packageInstalled("makepkg-git-lfs-proto") { - progressChan <- InstallProgressMsg{ - Phase: PhaseAURPackages, - Progress: 0.65, - Step: "Installing makepkg-git-lfs-proto for niri...", - IsComplete: false, - CommandInfo: "Installing prerequisite for niri-git", - } - - if err := a.installSingleAURPackage(ctx, "makepkg-git-lfs-proto", sudoPassword, progressChan, 0.65, 0.67); err != nil { - return fmt.Errorf("failed to install makepkg-git-lfs-proto prerequisite for niri: %w", err) - } - } - } - - // Reorder packages to ensure dms-shell-git dependencies are installed first - orderedPackages := a.reorderAURPackages(packages) - - baseProgress := 0.67 - progressStep := 0.13 / float64(len(orderedPackages)) - - for i, pkg := range orderedPackages { - currentProgress := baseProgress + (float64(i) * progressStep) - - progressChan <- InstallProgressMsg{ - Phase: PhaseAURPackages, - Progress: currentProgress, - Step: fmt.Sprintf("Installing AUR package %s (%d/%d)...", pkg, i+1, len(packages)), - IsComplete: false, - CommandInfo: fmt.Sprintf("Building and installing %s", pkg), - } - - if err := a.installSingleAURPackage(ctx, pkg, sudoPassword, progressChan, currentProgress, currentProgress+progressStep); err != nil { - return fmt.Errorf("failed to install AUR package %s: %w", pkg, err) - } - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseAURPackages, - Progress: 0.80, - Step: "All AUR packages installed successfully", - IsComplete: false, - LogOutput: fmt.Sprintf("Successfully installed AUR packages: %s", strings.Join(packages, ", ")), - } - - return nil -} - -func (a *ArchDistribution) reorderAURPackages(packages []string) []string { - dmsDepencies := []string{"quickshell", "quickshell-git", "dgop"} - - var deps []string - var others []string - var dmsShell []string - - for _, pkg := range packages { - if pkg == "dms-shell-git" || pkg == "dms-shell-bin" { - dmsShell = append(dmsShell, pkg) - } else { - isDep := false - for _, dep := range dmsDepencies { - if pkg == dep { - deps = append(deps, pkg) - isDep = true - break - } - } - if !isDep { - others = append(others, pkg) - } - } - } - - result := append(deps, others...) - result = append(result, dmsShell...) - return result -} - -func (a *ArchDistribution) installSingleAURPackage(ctx context.Context, pkg, sudoPassword string, progressChan chan<- InstallProgressMsg, startProgress, endProgress float64) error { - homeDir, err := os.UserHomeDir() - if err != nil { - return fmt.Errorf("failed to get user home directory: %w", err) - } - - buildDir := filepath.Join(homeDir, ".cache", "dankinstall", "aur-builds", pkg) - - // Clean up any existing cache first - if err := os.RemoveAll(buildDir); err != nil { - a.log(fmt.Sprintf("Warning: failed to clean existing cache for %s: %v", pkg, err)) - } - - if err := os.MkdirAll(buildDir, 0755); err != nil { - return fmt.Errorf("failed to create build directory: %w", err) - } - defer func() { - if removeErr := os.RemoveAll(buildDir); removeErr != nil { - a.log(fmt.Sprintf("Warning: failed to cleanup build directory %s: %v", buildDir, removeErr)) - } - }() - - // Clone the AUR package - progressChan <- InstallProgressMsg{ - Phase: PhaseAURPackages, - Progress: startProgress + 0.1*(endProgress-startProgress), - Step: fmt.Sprintf("Cloning %s from AUR...", pkg), - IsComplete: false, - CommandInfo: fmt.Sprintf("git clone https://aur.archlinux.org/%s.git", pkg), - } - - cloneCmd := exec.CommandContext(ctx, "git", "clone", fmt.Sprintf("https://aur.archlinux.org/%s.git", pkg), filepath.Join(buildDir, pkg)) - if err := a.runWithProgress(cloneCmd, progressChan, PhaseAURPackages, startProgress+0.1*(endProgress-startProgress), startProgress+0.2*(endProgress-startProgress)); err != nil { - return fmt.Errorf("failed to clone %s: %w", pkg, err) - } - - packageDir := filepath.Join(buildDir, pkg) - - if pkg == "niri-git" { - pkgbuildPath := filepath.Join(packageDir, "PKGBUILD") - sedCmd := exec.CommandContext(ctx, "sed", "-i", "s/makepkg-git-lfs-proto//g", pkgbuildPath) - if err := sedCmd.Run(); err != nil { - return fmt.Errorf("failed to patch PKGBUILD for niri-git: %w", err) - } - - srcinfoPath := filepath.Join(packageDir, ".SRCINFO") - sedCmd2 := exec.CommandContext(ctx, "sed", "-i", "/makedepends = makepkg-git-lfs-proto/d", srcinfoPath) - if err := sedCmd2.Run(); err != nil { - return fmt.Errorf("failed to patch .SRCINFO for niri-git: %w", err) - } - } - - if pkg == "dms-shell-git" || pkg == "dms-shell-bin" { - srcinfoPath := filepath.Join(packageDir, ".SRCINFO") - depsToRemove := []string{ - "depends = quickshell", - "depends = dgop", - } - - for _, dep := range depsToRemove { - sedCmd := exec.CommandContext(ctx, "sed", "-i", fmt.Sprintf("/%s/d", dep), srcinfoPath) - if err := sedCmd.Run(); err != nil { - return fmt.Errorf("failed to remove dependency %s from .SRCINFO for %s: %w", dep, pkg, err) - } - } - } - - // Remove all optdepends from .SRCINFO for all packages - srcinfoPath := filepath.Join(packageDir, ".SRCINFO") - optdepsCmd := exec.CommandContext(ctx, "sed", "-i", "/^[[:space:]]*optdepends = /d", srcinfoPath) - if err := optdepsCmd.Run(); err != nil { - return fmt.Errorf("failed to remove optdepends from .SRCINFO for %s: %w", pkg, err) - } - - // Skip dependency installation for dms-shell-git and dms-shell-bin - // since we manually manage those dependencies - if pkg != "dms-shell-git" && pkg != "dms-shell-bin" { - // Pre-install dependencies from .SRCINFO - progressChan <- InstallProgressMsg{ - Phase: PhaseAURPackages, - Progress: startProgress + 0.3*(endProgress-startProgress), - Step: fmt.Sprintf("Installing dependencies for %s...", pkg), - IsComplete: false, - CommandInfo: "Installing package dependencies and makedepends", - } - - // Install dependencies and makedepends explicitly - srcinfoPath = filepath.Join(packageDir, ".SRCINFO") - - depsCmd := exec.CommandContext(ctx, "bash", "-c", - fmt.Sprintf(` - deps=$(grep "depends = " "%s" | grep -v "makedepends" | sed 's/.*depends = //' | tr '\n' ' ' | sed 's/[[:space:]]*$//') - if [[ "%s" == *"quickshell"* ]]; then - deps=$(echo "$deps" | sed 's/google-breakpad//g' | sed 's/ / /g' | sed 's/^ *//g' | sed 's/ *$//g') - fi - if [ ! -z "$deps" ] && [ "$deps" != " " ]; then - echo '%s' | sudo -S pacman -S --needed --noconfirm $deps - fi - `, srcinfoPath, pkg, sudoPassword)) - - if err := a.runWithProgress(depsCmd, progressChan, PhaseAURPackages, startProgress+0.3*(endProgress-startProgress), startProgress+0.35*(endProgress-startProgress)); err != nil { - return fmt.Errorf("FAILED to install runtime dependencies for %s: %w", pkg, err) - } - - makedepsCmd := exec.CommandContext(ctx, "bash", "-c", - fmt.Sprintf(` - makedeps=$(grep -E "^[[:space:]]*makedepends = " "%s" | sed 's/^[[:space:]]*makedepends = //' | tr '\n' ' ') - if [ ! -z "$makedeps" ]; then - echo '%s' | sudo -S pacman -S --needed --noconfirm $makedeps - fi - `, srcinfoPath, sudoPassword)) - - if err := a.runWithProgress(makedepsCmd, progressChan, PhaseAURPackages, startProgress+0.35*(endProgress-startProgress), startProgress+0.4*(endProgress-startProgress)); err != nil { - return fmt.Errorf("FAILED to install make dependencies for %s: %w", pkg, err) - } - } else { - progressChan <- InstallProgressMsg{ - Phase: PhaseAURPackages, - Progress: startProgress + 0.35*(endProgress-startProgress), - Step: fmt.Sprintf("Skipping dependency installation for %s (manually managed)...", pkg), - IsComplete: false, - LogOutput: fmt.Sprintf("Dependencies for %s are installed separately", pkg), - } - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseAURPackages, - Progress: startProgress + 0.4*(endProgress-startProgress), - Step: fmt.Sprintf("Building %s...", pkg), - IsComplete: false, - CommandInfo: "makepkg --noconfirm", - } - - buildCmd := exec.CommandContext(ctx, "makepkg", "--noconfirm") - buildCmd.Dir = packageDir - buildCmd.Env = append(os.Environ(), "PKGEXT=.pkg.tar") // Disable compression for speed - - if err := a.runWithProgress(buildCmd, progressChan, PhaseAURPackages, startProgress+0.4*(endProgress-startProgress), startProgress+0.7*(endProgress-startProgress)); err != nil { - return fmt.Errorf("failed to build %s: %w", pkg, err) - } - - // Find built package file - progressChan <- InstallProgressMsg{ - Phase: PhaseAURPackages, - Progress: startProgress + 0.7*(endProgress-startProgress), - Step: fmt.Sprintf("Installing %s...", pkg), - IsComplete: false, - CommandInfo: "sudo pacman -U built-package", - } - - // Find .pkg.tar* files - for split packages, install the base and any installed compositor variants - var files []string - if pkg == "dms-shell-git" || pkg == "dms-shell-bin" { - // For DMS split packages, install base package - pattern := filepath.Join(packageDir, fmt.Sprintf("%s-%s*.pkg.tar*", pkg, "*")) - matches, err := filepath.Glob(pattern) - if err == nil { - for _, match := range matches { - basename := filepath.Base(match) - // Always include base package - if !strings.Contains(basename, "hyprland") && !strings.Contains(basename, "niri") { - files = append(files, match) - } - } - } - - // Also update compositor-specific packages if they're installed - if strings.HasSuffix(pkg, "-git") { - if a.packageInstalled("dms-shell-hyprland-git") { - hyprlandPattern := filepath.Join(packageDir, "dms-shell-hyprland-git-*.pkg.tar*") - if hyprlandMatches, err := filepath.Glob(hyprlandPattern); err == nil && len(hyprlandMatches) > 0 { - files = append(files, hyprlandMatches[0]) - } - } - if a.packageInstalled("dms-shell-niri-git") { - niriPattern := filepath.Join(packageDir, "dms-shell-niri-git-*.pkg.tar*") - if niriMatches, err := filepath.Glob(niriPattern); err == nil && len(niriMatches) > 0 { - files = append(files, niriMatches[0]) - } - } - } - } else { - // For other packages, install all built packages - matches, _ := filepath.Glob(filepath.Join(packageDir, "*.pkg.tar*")) - files = matches - } - - if len(files) == 0 { - return fmt.Errorf("no package files found after building %s", pkg) - } - - installArgs := []string{"pacman", "-U", "--noconfirm"} - installArgs = append(installArgs, files...) - - cmdStr := fmt.Sprintf("echo '%s' | sudo -S %s", sudoPassword, strings.Join(installArgs, " ")) - installCmd := exec.CommandContext(ctx, "bash", "-c", cmdStr) - - fileNames := make([]string, len(files)) - for i, f := range files { - fileNames[i] = filepath.Base(f) - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseAURPackages, - Progress: startProgress + 0.7*(endProgress-startProgress), - LogOutput: fmt.Sprintf("Installing packages: %s", strings.Join(fileNames, ", ")), - } - - if err := a.runWithProgress(installCmd, progressChan, PhaseAURPackages, startProgress+0.7*(endProgress-startProgress), endProgress); err != nil { - progressChan <- InstallProgressMsg{ - Phase: PhaseAURPackages, - Progress: startProgress, - LogOutput: fmt.Sprintf("ERROR: pacman -U failed for %s with error: %v", pkg, err), - Error: err, - } - return fmt.Errorf("failed to install built package %s: %w", pkg, err) - } - - a.log(fmt.Sprintf("Successfully installed AUR package: %s", pkg)) - return nil -} diff --git a/nix/inputs/dms-cli/internal/distros/base.go b/nix/inputs/dms-cli/internal/distros/base.go deleted file mode 100644 index d74f1f8..0000000 --- a/nix/inputs/dms-cli/internal/distros/base.go +++ /dev/null @@ -1,622 +0,0 @@ -package distros - -import ( - "bufio" - "context" - _ "embed" - "fmt" - "os" - "os/exec" - "path/filepath" - "regexp" - "runtime" - "strings" - "time" - - "github.com/AvengeMedia/danklinux/internal/deps" - "github.com/AvengeMedia/danklinux/internal/version" -) - -const forceQuickshellGit = false -const forceDMSGit = false - -// BaseDistribution provides common functionality for all distributions -type BaseDistribution struct { - logChan chan<- string -} - -// NewBaseDistribution creates a new base distribution -func NewBaseDistribution(logChan chan<- string) *BaseDistribution { - return &BaseDistribution{ - logChan: logChan, - } -} - -// Common helper methods -func (b *BaseDistribution) commandExists(cmd string) bool { - _, err := exec.LookPath(cmd) - return err == nil -} - -func (b *BaseDistribution) CommandExists(cmd string) bool { - return b.commandExists(cmd) -} - -func (b *BaseDistribution) log(message string) { - if b.logChan != nil { - b.logChan <- message - } -} - -func (b *BaseDistribution) logError(message string, err error) { - errorMsg := fmt.Sprintf("ERROR: %s: %v", message, err) - b.log(errorMsg) -} - -// Common dependency detection methods -func (b *BaseDistribution) detectGit() deps.Dependency { - status := deps.StatusMissing - if b.commandExists("git") { - status = deps.StatusInstalled - } - - return deps.Dependency{ - Name: "git", - Status: status, - Description: "Version control system", - Required: true, - } -} - -func (b *BaseDistribution) detectMatugen() deps.Dependency { - status := deps.StatusMissing - if b.commandExists("matugen") { - status = deps.StatusInstalled - } - - return deps.Dependency{ - Name: "matugen", - Status: status, - Description: "Material Design color generation tool", - Required: true, - } -} - -func (b *BaseDistribution) detectDgop() deps.Dependency { - status := deps.StatusMissing - if b.commandExists("dgop") { - status = deps.StatusInstalled - } - - return deps.Dependency{ - Name: "dgop", - Status: status, - Description: "Desktop portal management tool", - Required: true, - } -} - -func (b *BaseDistribution) detectDMS() deps.Dependency { - dmsPath := filepath.Join(os.Getenv("HOME"), ".config/quickshell/dms") - - status := deps.StatusMissing - currentVersion := "" - - if _, err := os.Stat(dmsPath); err == nil { - status = deps.StatusInstalled - - // Only get current version, don't check for updates (lazy loading) - current, err := version.GetCurrentDMSVersion() - if err == nil { - currentVersion = current - } - } - - dep := deps.Dependency{ - Name: "dms (DankMaterialShell)", - Status: status, - Description: "Desktop Management System configuration", - Required: true, - CanToggle: true, - } - - if currentVersion != "" { - dep.Version = currentVersion - } - - return dep -} - -func (b *BaseDistribution) detectSpecificTerminal(terminal deps.Terminal) deps.Dependency { - switch terminal { - case deps.TerminalGhostty: - status := deps.StatusMissing - if b.commandExists("ghostty") { - status = deps.StatusInstalled - } - return deps.Dependency{ - Name: "ghostty", - Status: status, - Description: "A fast, native terminal emulator built in Zig.", - Required: true, - } - case deps.TerminalKitty: - status := deps.StatusMissing - if b.commandExists("kitty") { - status = deps.StatusInstalled - } - return deps.Dependency{ - Name: "kitty", - Status: status, - Description: "A feature-rich, customizable terminal emulator.", - Required: true, - } - case deps.TerminalAlacritty: - status := deps.StatusMissing - if b.commandExists("alacritty") { - status = deps.StatusInstalled - } - return deps.Dependency{ - Name: "alacritty", - Status: status, - Description: "A simple terminal emulator. (No dynamic theming)", - Required: true, - } - default: - return b.detectSpecificTerminal(deps.TerminalGhostty) - } -} - -func (b *BaseDistribution) detectClipboardTools() []deps.Dependency { - var dependencies []deps.Dependency - - cliphist := deps.StatusMissing - if b.commandExists("cliphist") { - cliphist = deps.StatusInstalled - } - - wlClipboard := deps.StatusMissing - if b.commandExists("wl-copy") && b.commandExists("wl-paste") { - wlClipboard = deps.StatusInstalled - } - - dependencies = append(dependencies, - deps.Dependency{ - Name: "cliphist", - Status: cliphist, - Description: "Wayland clipboard manager", - Required: true, - }, - deps.Dependency{ - Name: "wl-clipboard", - Status: wlClipboard, - Description: "Wayland clipboard utilities", - Required: true, - }, - ) - - return dependencies -} - -func (b *BaseDistribution) detectHyprpicker() deps.Dependency { - status := deps.StatusMissing - if b.commandExists("hyprpicker") { - status = deps.StatusInstalled - } - - return deps.Dependency{ - Name: "hyprpicker", - Status: status, - Description: "Color picker for Wayland", - Required: true, - } -} - -func (b *BaseDistribution) detectHyprlandTools() []deps.Dependency { - var dependencies []deps.Dependency - - tools := []struct { - name string - description string - }{ - {"grim", "Screenshot utility for Wayland"}, - {"slurp", "Region selection utility for Wayland"}, - {"hyprctl", "Hyprland control utility"}, - {"grimblast", "Screenshot script for Hyprland"}, - {"jq", "JSON processor"}, - } - - for _, tool := range tools { - status := deps.StatusMissing - if b.commandExists(tool.name) { - status = deps.StatusInstalled - } - - dependencies = append(dependencies, deps.Dependency{ - Name: tool.name, - Status: status, - Description: tool.description, - Required: true, - }) - } - - return dependencies -} - -func (b *BaseDistribution) detectQuickshell() deps.Dependency { - if !b.commandExists("qs") { - return deps.Dependency{ - Name: "quickshell", - Status: deps.StatusMissing, - Description: "QtQuick based desktop shell toolkit", - Required: true, - Variant: deps.VariantStable, - CanToggle: true, - } - } - - cmd := exec.Command("qs", "--version") - output, err := cmd.Output() - if err != nil { - return deps.Dependency{ - Name: "quickshell", - Status: deps.StatusNeedsReinstall, - Description: "QtQuick based desktop shell toolkit (version check failed)", - Required: true, - Variant: deps.VariantStable, - CanToggle: true, - } - } - - versionStr := string(output) - versionRegex := regexp.MustCompile(`quickshell (\d+\.\d+\.\d+)`) - matches := versionRegex.FindStringSubmatch(versionStr) - - if len(matches) < 2 { - return deps.Dependency{ - Name: "quickshell", - Status: deps.StatusNeedsReinstall, - Description: "QtQuick based desktop shell toolkit (unknown version)", - Required: true, - Variant: deps.VariantStable, - CanToggle: true, - } - } - - version := matches[1] - variant := deps.VariantStable - if strings.Contains(versionStr, "git") || strings.Contains(versionStr, "+") { - variant = deps.VariantGit - } - - if b.versionCompare(version, "0.2.0") >= 0 { - return deps.Dependency{ - Name: "quickshell", - Status: deps.StatusInstalled, - Version: version, - Description: "QtQuick based desktop shell toolkit", - Required: true, - Variant: variant, - CanToggle: true, - } - } - - return deps.Dependency{ - Name: "quickshell", - Status: deps.StatusNeedsUpdate, - Variant: variant, - CanToggle: true, - Version: version, - Description: "QtQuick based desktop shell toolkit (needs 0.2.0+)", - Required: true, - } -} - -func (b *BaseDistribution) detectWindowManager(wm deps.WindowManager) deps.Dependency { - switch wm { - case deps.WindowManagerHyprland: - status := deps.StatusMissing - variant := deps.VariantStable - version := "" - - if b.commandExists("hyprland") || b.commandExists("Hyprland") { - status = deps.StatusInstalled - cmd := exec.Command("hyprctl", "version") - if output, err := cmd.Output(); err == nil { - outStr := string(output) - if strings.Contains(outStr, "git") || strings.Contains(outStr, "dirty") { - variant = deps.VariantGit - } - if versionRegex := regexp.MustCompile(`v(\d+\.\d+\.\d+)`); versionRegex.MatchString(outStr) { - matches := versionRegex.FindStringSubmatch(outStr) - if len(matches) > 1 { - version = matches[1] - } - } - } - } - return deps.Dependency{ - Name: "hyprland", - Status: status, - Version: version, - Description: "Dynamic tiling Wayland compositor", - Required: true, - Variant: variant, - CanToggle: true, - } - case deps.WindowManagerNiri: - status := deps.StatusMissing - variant := deps.VariantStable - version := "" - - if b.commandExists("niri") { - status = deps.StatusInstalled - cmd := exec.Command("niri", "--version") - if output, err := cmd.Output(); err == nil { - outStr := string(output) - if strings.Contains(outStr, "git") || strings.Contains(outStr, "+") { - variant = deps.VariantGit - } - if versionRegex := regexp.MustCompile(`niri (\d+\.\d+)`); versionRegex.MatchString(outStr) { - matches := versionRegex.FindStringSubmatch(outStr) - if len(matches) > 1 { - version = matches[1] - } - } - } - } - return deps.Dependency{ - Name: "niri", - Status: status, - Version: version, - Description: "Scrollable-tiling Wayland compositor", - Required: true, - Variant: variant, - CanToggle: true, - } - default: - return deps.Dependency{ - Name: "unknown-wm", - Status: deps.StatusMissing, - Description: "Unknown window manager", - Required: true, - } - } -} - -// Version comparison helper -func (b *BaseDistribution) versionCompare(v1, v2 string) int { - parts1 := strings.Split(v1, ".") - parts2 := strings.Split(v2, ".") - - for i := 0; i < len(parts1) && i < len(parts2); i++ { - if parts1[i] < parts2[i] { - return -1 - } - if parts1[i] > parts2[i] { - return 1 - } - } - - if len(parts1) < len(parts2) { - return -1 - } - if len(parts1) > len(parts2) { - return 1 - } - - return 0 -} - -// Common installation helper -func (b *BaseDistribution) runWithProgress(cmd *exec.Cmd, progressChan chan<- InstallProgressMsg, phase InstallPhase, startProgress, endProgress float64) error { - return b.runWithProgressStep(cmd, progressChan, phase, startProgress, endProgress, "Installing...") -} - -func (b *BaseDistribution) runWithProgressStep(cmd *exec.Cmd, progressChan chan<- InstallProgressMsg, phase InstallPhase, startProgress, endProgress float64, stepMessage string) error { - stdout, err := cmd.StdoutPipe() - if err != nil { - return fmt.Errorf("failed to create stdout pipe: %w", err) - } - stderr, err := cmd.StderrPipe() - if err != nil { - return fmt.Errorf("failed to create stderr pipe: %w", err) - } - - if err := cmd.Start(); err != nil { - return err - } - - outputChan := make(chan string, 100) - done := make(chan error, 1) - - go func() { - scanner := bufio.NewScanner(stdout) - for scanner.Scan() { - line := scanner.Text() - b.log(line) - outputChan <- line - } - }() - - go func() { - scanner := bufio.NewScanner(stderr) - for scanner.Scan() { - line := scanner.Text() - b.log(line) - outputChan <- line - } - }() - - go func() { - done <- cmd.Wait() - close(outputChan) - }() - - ticker := time.NewTicker(200 * time.Millisecond) - defer ticker.Stop() - - progress := startProgress - progressStep := (endProgress - startProgress) / 50 - lastOutput := "" - timeout := time.NewTimer(10 * time.Minute) - defer timeout.Stop() - - for { - select { - case err := <-done: - if err != nil { - b.logError("Command execution failed", err) - b.log(fmt.Sprintf("Last output before failure: %s", lastOutput)) - progressChan <- InstallProgressMsg{ - Phase: phase, - Progress: startProgress, - Step: "Command failed", - IsComplete: false, - LogOutput: lastOutput, - Error: err, - } - return err - } - progressChan <- InstallProgressMsg{ - Phase: phase, - Progress: endProgress, - Step: "Installation step complete", - IsComplete: false, - LogOutput: lastOutput, - } - return nil - case output, ok := <-outputChan: - if ok { - lastOutput = output - progressChan <- InstallProgressMsg{ - Phase: phase, - Progress: progress, - Step: stepMessage, - IsComplete: false, - LogOutput: output, - } - timeout.Reset(10 * time.Minute) - } - case <-timeout.C: - if cmd.Process != nil { - cmd.Process.Kill() - } - err := fmt.Errorf("installation timed out after 10 minutes") - progressChan <- InstallProgressMsg{ - Phase: phase, - Progress: startProgress, - Step: "Installation timed out", - IsComplete: false, - LogOutput: lastOutput, - Error: err, - } - return err - case <-ticker.C: - if progress < endProgress-0.01 { - progress += progressStep - progressChan <- InstallProgressMsg{ - Phase: phase, - Progress: progress, - Step: "Installing...", - IsComplete: false, - LogOutput: lastOutput, - } - } - } - } -} - -// installDMSBinary installs the DMS binary from GitHub releases -func (b *BaseDistribution) installDMSBinary(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - b.log("Installing/updating DMS binary...") - - // Detect architecture - arch := runtime.GOARCH - switch arch { - case "amd64": - case "arm64": - default: - return fmt.Errorf("unsupported architecture for DMS: %s", arch) - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseConfiguration, - Progress: 0.80, - Step: "Downloading DMS binary...", - IsComplete: false, - CommandInfo: fmt.Sprintf("Downloading dms-%s.gz", arch), - } - - // Get latest release version - latestVersionCmd := exec.CommandContext(ctx, "bash", "-c", - `curl -s https://api.github.com/repos/AvengeMedia/danklinux/releases/latest | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/'`) - versionOutput, err := latestVersionCmd.Output() - if err != nil { - return fmt.Errorf("failed to get latest DMS version: %w", err) - } - version := strings.TrimSpace(string(versionOutput)) - if version == "" { - return fmt.Errorf("could not determine latest DMS version") - } - - homeDir, err := os.UserHomeDir() - if err != nil { - return fmt.Errorf("failed to get user home directory: %w", err) - } - tmpDir := filepath.Join(homeDir, ".cache", "dankinstall", "manual-builds") - if err := os.MkdirAll(tmpDir, 0755); err != nil { - return fmt.Errorf("failed to create temp directory: %w", err) - } - defer os.RemoveAll(tmpDir) - - // Download the gzipped binary - downloadURL := fmt.Sprintf("https://github.com/AvengeMedia/danklinux/releases/download/%s/dms-%s.gz", version, arch) - gzPath := filepath.Join(tmpDir, "dms.gz") - - downloadCmd := exec.CommandContext(ctx, "curl", "-L", downloadURL, "-o", gzPath) - if err := downloadCmd.Run(); err != nil { - return fmt.Errorf("failed to download DMS binary: %w", err) - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseConfiguration, - Progress: 0.85, - Step: "Extracting DMS binary...", - IsComplete: false, - CommandInfo: "gunzip dms.gz", - } - - // Extract the binary - extractCmd := exec.CommandContext(ctx, "gunzip", gzPath) - if err := extractCmd.Run(); err != nil { - return fmt.Errorf("failed to extract DMS binary: %w", err) - } - - binaryPath := filepath.Join(tmpDir, "dms") - - // Make it executable - chmodCmd := exec.CommandContext(ctx, "chmod", "+x", binaryPath) - if err := chmodCmd.Run(); err != nil { - return fmt.Errorf("failed to make DMS binary executable: %w", err) - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseConfiguration, - Progress: 0.88, - Step: "Installing DMS to /usr/local/bin...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: "sudo cp dms /usr/local/bin/", - } - - // Install to /usr/local/bin - installCmd := exec.CommandContext(ctx, "bash", "-c", - fmt.Sprintf("echo '%s' | sudo -S cp %s /usr/local/bin/dms", sudoPassword, binaryPath)) - if err := installCmd.Run(); err != nil { - return fmt.Errorf("failed to install DMS binary: %w", err) - } - - b.log("DMS binary installed successfully") - return nil -} diff --git a/nix/inputs/dms-cli/internal/distros/base_test.go b/nix/inputs/dms-cli/internal/distros/base_test.go deleted file mode 100644 index 3d226b7..0000000 --- a/nix/inputs/dms-cli/internal/distros/base_test.go +++ /dev/null @@ -1,220 +0,0 @@ -package distros - -import ( - "os" - "os/exec" - "path/filepath" - "testing" - - "github.com/AvengeMedia/danklinux/internal/deps" -) - -func TestBaseDistribution_detectDMS_NotInstalled(t *testing.T) { - originalHome := os.Getenv("HOME") - defer os.Setenv("HOME", originalHome) - - tempDir := t.TempDir() - os.Setenv("HOME", tempDir) - - logChan := make(chan string, 10) - defer close(logChan) - - base := NewBaseDistribution(logChan) - dep := base.detectDMS() - - if dep.Status != deps.StatusMissing { - t.Errorf("Expected StatusMissing, got %d", dep.Status) - } - - if dep.Name != "dms (DankMaterialShell)" { - t.Errorf("Expected name 'dms (DankMaterialShell)', got %s", dep.Name) - } - - if !dep.Required { - t.Error("Expected Required to be true") - } -} - -func TestBaseDistribution_detectDMS_Installed(t *testing.T) { - if !commandExists("git") { - t.Skip("git not available") - } - - tempDir := t.TempDir() - dmsPath := filepath.Join(tempDir, ".config", "quickshell", "dms") - os.MkdirAll(dmsPath, 0755) - - originalHome := os.Getenv("HOME") - defer os.Setenv("HOME", originalHome) - os.Setenv("HOME", tempDir) - - exec.Command("git", "init", dmsPath).Run() - exec.Command("git", "-C", dmsPath, "config", "user.email", "test@test.com").Run() - exec.Command("git", "-C", dmsPath, "config", "user.name", "Test User").Run() - exec.Command("git", "-C", dmsPath, "checkout", "-b", "master").Run() - - testFile := filepath.Join(dmsPath, "test.txt") - os.WriteFile(testFile, []byte("test"), 0644) - exec.Command("git", "-C", dmsPath, "add", ".").Run() - exec.Command("git", "-C", dmsPath, "commit", "-m", "initial").Run() - - logChan := make(chan string, 10) - defer close(logChan) - - base := NewBaseDistribution(logChan) - dep := base.detectDMS() - - if dep.Status == deps.StatusMissing { - t.Error("Expected DMS to be detected as installed") - } - - if dep.Name != "dms (DankMaterialShell)" { - t.Errorf("Expected name 'dms (DankMaterialShell)', got %s", dep.Name) - } - - if !dep.Required { - t.Error("Expected Required to be true") - } - - t.Logf("Status: %d, Version: %s", dep.Status, dep.Version) -} - -func TestBaseDistribution_detectDMS_NeedsUpdate(t *testing.T) { - if !commandExists("git") { - t.Skip("git not available") - } - - tempDir := t.TempDir() - dmsPath := filepath.Join(tempDir, ".config", "quickshell", "dms") - os.MkdirAll(dmsPath, 0755) - - originalHome := os.Getenv("HOME") - defer os.Setenv("HOME", originalHome) - os.Setenv("HOME", tempDir) - - exec.Command("git", "init", dmsPath).Run() - exec.Command("git", "-C", dmsPath, "config", "user.email", "test@test.com").Run() - exec.Command("git", "-C", dmsPath, "config", "user.name", "Test User").Run() - exec.Command("git", "-C", dmsPath, "remote", "add", "origin", "https://github.com/AvengeMedia/DankMaterialShell.git").Run() - - testFile := filepath.Join(dmsPath, "test.txt") - os.WriteFile(testFile, []byte("test"), 0644) - exec.Command("git", "-C", dmsPath, "add", ".").Run() - exec.Command("git", "-C", dmsPath, "commit", "-m", "initial").Run() - exec.Command("git", "-C", dmsPath, "tag", "v0.0.1").Run() - exec.Command("git", "-C", dmsPath, "checkout", "v0.0.1").Run() - - logChan := make(chan string, 10) - defer close(logChan) - - base := NewBaseDistribution(logChan) - dep := base.detectDMS() - - if dep.Name != "dms (DankMaterialShell)" { - t.Errorf("Expected name 'dms (DankMaterialShell)', got %s", dep.Name) - } - - if !dep.Required { - t.Error("Expected Required to be true") - } - - t.Logf("Status: %d, Version: %s", dep.Status, dep.Version) -} - -func TestBaseDistribution_detectDMS_DirectoryWithoutGit(t *testing.T) { - tempDir := t.TempDir() - dmsPath := filepath.Join(tempDir, ".config", "quickshell", "dms") - os.MkdirAll(dmsPath, 0755) - - originalHome := os.Getenv("HOME") - defer os.Setenv("HOME", originalHome) - os.Setenv("HOME", tempDir) - - logChan := make(chan string, 10) - defer close(logChan) - - base := NewBaseDistribution(logChan) - dep := base.detectDMS() - - if dep.Status == deps.StatusMissing { - t.Error("Expected DMS to be detected as present") - } - - if dep.Name != "dms (DankMaterialShell)" { - t.Errorf("Expected name 'dms (DankMaterialShell)', got %s", dep.Name) - } - - if !dep.Required { - t.Error("Expected Required to be true") - } -} - -func TestBaseDistribution_NewBaseDistribution(t *testing.T) { - logChan := make(chan string, 10) - defer close(logChan) - - base := NewBaseDistribution(logChan) - - if base == nil { - t.Fatal("NewBaseDistribution returned nil") - } - - if base.logChan == nil { - t.Error("logChan was not set") - } -} - -func commandExists(cmd string) bool { - _, err := exec.LookPath(cmd) - return err == nil -} - -func TestBaseDistribution_versionCompare(t *testing.T) { - logChan := make(chan string, 10) - defer close(logChan) - - base := NewBaseDistribution(logChan) - - tests := []struct { - v1 string - v2 string - expected int - }{ - {"0.1.0", "0.1.0", 0}, - {"0.1.0", "0.1.1", -1}, - {"0.1.1", "0.1.0", 1}, - {"0.2.0", "0.1.9", 1}, - {"1.0.0", "0.9.9", 1}, - } - - for _, tt := range tests { - result := base.versionCompare(tt.v1, tt.v2) - if result != tt.expected { - t.Errorf("versionCompare(%q, %q) = %d; want %d", tt.v1, tt.v2, result, tt.expected) - } - } -} - -func TestBaseDistribution_versionCompare_WithPrefix(t *testing.T) { - logChan := make(chan string, 10) - defer close(logChan) - - base := NewBaseDistribution(logChan) - - tests := []struct { - v1 string - v2 string - expected int - }{ - {"v0.1.0", "v0.1.0", 0}, - {"v0.1.0", "v0.1.1", -1}, - {"v0.1.1", "v0.1.0", 1}, - } - - for _, tt := range tests { - result := base.versionCompare(tt.v1, tt.v2) - if result != tt.expected { - t.Errorf("versionCompare(%q, %q) = %d; want %d", tt.v1, tt.v2, result, tt.expected) - } - } -} diff --git a/nix/inputs/dms-cli/internal/distros/debian.go b/nix/inputs/dms-cli/internal/distros/debian.go deleted file mode 100644 index 16975f0..0000000 --- a/nix/inputs/dms-cli/internal/distros/debian.go +++ /dev/null @@ -1,537 +0,0 @@ -package distros - -import ( - "context" - "fmt" - "os/exec" - "strings" - - "github.com/AvengeMedia/danklinux/internal/deps" -) - -func init() { - Register("debian", "#A80030", FamilyDebian, func(config DistroConfig, logChan chan<- string) Distribution { - return NewDebianDistribution(config, logChan) - }) -} - -type DebianDistribution struct { - *BaseDistribution - *ManualPackageInstaller - config DistroConfig -} - -func NewDebianDistribution(config DistroConfig, logChan chan<- string) *DebianDistribution { - base := NewBaseDistribution(logChan) - return &DebianDistribution{ - BaseDistribution: base, - ManualPackageInstaller: &ManualPackageInstaller{BaseDistribution: base}, - config: config, - } -} - -func (d *DebianDistribution) GetID() string { - return d.config.ID -} - -func (d *DebianDistribution) GetColorHex() string { - return d.config.ColorHex -} - -func (d *DebianDistribution) GetFamily() DistroFamily { - return d.config.Family -} - -func (d *DebianDistribution) GetPackageManager() PackageManagerType { - return PackageManagerAPT -} - -func (d *DebianDistribution) DetectDependencies(ctx context.Context, wm deps.WindowManager) ([]deps.Dependency, error) { - return d.DetectDependenciesWithTerminal(ctx, wm, deps.TerminalGhostty) -} - -func (d *DebianDistribution) DetectDependenciesWithTerminal(ctx context.Context, wm deps.WindowManager, terminal deps.Terminal) ([]deps.Dependency, error) { - var dependencies []deps.Dependency - - dependencies = append(dependencies, d.detectDMS()) - - dependencies = append(dependencies, d.detectSpecificTerminal(terminal)) - - dependencies = append(dependencies, d.detectGit()) - dependencies = append(dependencies, d.detectWindowManager(wm)) - dependencies = append(dependencies, d.detectQuickshell()) - dependencies = append(dependencies, d.detectXDGPortal()) - dependencies = append(dependencies, d.detectPolkitAgent()) - dependencies = append(dependencies, d.detectAccountsService()) - - if wm == deps.WindowManagerNiri { - dependencies = append(dependencies, d.detectXwaylandSatellite()) - } - - dependencies = append(dependencies, d.detectMatugen()) - dependencies = append(dependencies, d.detectDgop()) - dependencies = append(dependencies, d.detectHyprpicker()) - dependencies = append(dependencies, d.detectClipboardTools()...) - - return dependencies, nil -} - -func (d *DebianDistribution) detectXDGPortal() deps.Dependency { - status := deps.StatusMissing - if d.packageInstalled("xdg-desktop-portal-gtk") { - status = deps.StatusInstalled - } - - return deps.Dependency{ - Name: "xdg-desktop-portal-gtk", - Status: status, - Description: "Desktop integration portal for GTK", - Required: true, - } -} - -func (d *DebianDistribution) detectPolkitAgent() deps.Dependency { - status := deps.StatusMissing - if d.packageInstalled("mate-polkit") { - status = deps.StatusInstalled - } - - return deps.Dependency{ - Name: "mate-polkit", - Status: status, - Description: "PolicyKit authentication agent", - Required: true, - } -} - -func (d *DebianDistribution) detectXwaylandSatellite() deps.Dependency { - status := deps.StatusMissing - if d.commandExists("xwayland-satellite") { - status = deps.StatusInstalled - } - - return deps.Dependency{ - Name: "xwayland-satellite", - Status: status, - Description: "Xwayland support", - Required: true, - } -} - -func (d *DebianDistribution) detectAccountsService() deps.Dependency { - status := deps.StatusMissing - if d.packageInstalled("accountsservice") { - status = deps.StatusInstalled - } - - return deps.Dependency{ - Name: "accountsservice", - Status: status, - Description: "D-Bus interface for user account query and manipulation", - Required: true, - } -} - -func (d *DebianDistribution) packageInstalled(pkg string) bool { - cmd := exec.Command("dpkg", "-l", pkg) - err := cmd.Run() - return err == nil -} - -func (d *DebianDistribution) GetPackageMapping(wm deps.WindowManager) map[string]PackageMapping { - packages := map[string]PackageMapping{ - "git": {Name: "git", Repository: RepoTypeSystem}, - "kitty": {Name: "kitty", Repository: RepoTypeSystem}, - "alacritty": {Name: "alacritty", Repository: RepoTypeSystem}, - "wl-clipboard": {Name: "wl-clipboard", Repository: RepoTypeSystem}, - "xdg-desktop-portal-gtk": {Name: "xdg-desktop-portal-gtk", Repository: RepoTypeSystem}, - "mate-polkit": {Name: "mate-polkit", Repository: RepoTypeSystem}, - "accountsservice": {Name: "accountsservice", Repository: RepoTypeSystem}, - - "dms (DankMaterialShell)": {Name: "dms", Repository: RepoTypeManual, BuildFunc: "installDankMaterialShell"}, - "niri": {Name: "niri", Repository: RepoTypeManual, BuildFunc: "installNiri"}, - "quickshell": {Name: "quickshell", Repository: RepoTypeManual, BuildFunc: "installQuickshell"}, - "ghostty": {Name: "ghostty", Repository: RepoTypeManual, BuildFunc: "installGhostty"}, - "matugen": {Name: "matugen", Repository: RepoTypeManual, BuildFunc: "installMatugen"}, - "dgop": {Name: "dgop", Repository: RepoTypeManual, BuildFunc: "installDgop"}, - "cliphist": {Name: "cliphist", Repository: RepoTypeManual, BuildFunc: "installCliphist"}, - "hyprpicker": {Name: "hyprpicker", Repository: RepoTypeManual, BuildFunc: "installHyprpicker"}, - } - - if wm == deps.WindowManagerNiri { - packages["niri"] = PackageMapping{Name: "niri", Repository: RepoTypeManual, BuildFunc: "installNiri"} - packages["xwayland-satellite"] = PackageMapping{Name: "xwayland-satellite", Repository: RepoTypeManual, BuildFunc: "installXwaylandSatellite"} - } - - return packages -} - -func (d *DebianDistribution) InstallPrerequisites(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.06, - Step: "Updating package lists...", - IsComplete: false, - LogOutput: "Updating APT package lists", - } - - updateCmd := exec.CommandContext(ctx, "bash", "-c", fmt.Sprintf("echo '%s' | sudo -S apt-get update", sudoPassword)) - if err := d.runWithProgress(updateCmd, progressChan, PhasePrerequisites, 0.06, 0.07); err != nil { - return fmt.Errorf("failed to update package lists: %w", err) - } - - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.08, - Step: "Installing build-essential...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: "sudo apt-get install -y build-essential", - LogOutput: "Installing build tools", - } - - checkCmd := exec.CommandContext(ctx, "dpkg", "-l", "build-essential") - if err := checkCmd.Run(); err != nil { - cmd := exec.CommandContext(ctx, "bash", "-c", fmt.Sprintf("echo '%s' | sudo -S apt-get install -y build-essential", sudoPassword)) - if err := d.runWithProgress(cmd, progressChan, PhasePrerequisites, 0.08, 0.09); err != nil { - return fmt.Errorf("failed to install build-essential: %w", err) - } - } - - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.10, - Step: "Installing development dependencies...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: "sudo apt-get install -y curl wget git cmake ninja-build pkg-config libxcb-cursor-dev libglib2.0-dev libpolkit-agent-1-dev", - LogOutput: "Installing additional development tools", - } - - devToolsCmd := exec.CommandContext(ctx, "bash", "-c", - fmt.Sprintf("echo '%s' | sudo -S apt-get install -y curl wget git cmake ninja-build pkg-config libxcb-cursor-dev libglib2.0-dev libpolkit-agent-1-dev", sudoPassword)) - if err := d.runWithProgress(devToolsCmd, progressChan, PhasePrerequisites, 0.10, 0.12); err != nil { - return fmt.Errorf("failed to install development tools: %w", err) - } - - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.12, - Step: "Prerequisites installation complete", - IsComplete: false, - LogOutput: "Prerequisites successfully installed", - } - - return nil -} - -func (d *DebianDistribution) InstallPackages(ctx context.Context, dependencies []deps.Dependency, wm deps.WindowManager, sudoPassword string, reinstallFlags map[string]bool, progressChan chan<- InstallProgressMsg) error { - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.05, - Step: "Checking system prerequisites...", - IsComplete: false, - LogOutput: "Starting prerequisite check...", - } - - if err := d.InstallPrerequisites(ctx, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install prerequisites: %w", err) - } - - systemPkgs, manualPkgs := d.categorizePackages(dependencies, wm, reinstallFlags) - - if len(systemPkgs) > 0 { - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.35, - Step: fmt.Sprintf("Installing %d system packages...", len(systemPkgs)), - IsComplete: false, - NeedsSudo: true, - LogOutput: fmt.Sprintf("Installing system packages: %s", strings.Join(systemPkgs, ", ")), - } - if err := d.installAPTPackages(ctx, systemPkgs, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install APT packages: %w", err) - } - } - - if len(manualPkgs) > 0 { - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.80, - Step: "Installing build dependencies...", - IsComplete: false, - LogOutput: "Installing build tools for manual compilation", - } - if err := d.installBuildDependencies(ctx, manualPkgs, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install build dependencies: %w", err) - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.85, - Step: fmt.Sprintf("Building %d packages from source...", len(manualPkgs)), - IsComplete: false, - LogOutput: fmt.Sprintf("Building from source: %s", strings.Join(manualPkgs, ", ")), - } - if err := d.InstallManualPackages(ctx, manualPkgs, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install manual packages: %w", err) - } - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseConfiguration, - Progress: 0.90, - Step: "Configuring system...", - IsComplete: false, - LogOutput: "Starting post-installation configuration...", - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseComplete, - Progress: 1.0, - Step: "Installation complete!", - IsComplete: true, - LogOutput: "All packages installed and configured successfully", - } - - return nil -} - -func (d *DebianDistribution) categorizePackages(dependencies []deps.Dependency, wm deps.WindowManager, reinstallFlags map[string]bool) ([]string, []string) { - systemPkgs := []string{} - manualPkgs := []string{} - - packageMap := d.GetPackageMapping(wm) - - for _, dep := range dependencies { - if dep.Status == deps.StatusInstalled && !reinstallFlags[dep.Name] { - continue - } - - pkgInfo, exists := packageMap[dep.Name] - if !exists { - d.log(fmt.Sprintf("Warning: No package mapping for %s", dep.Name)) - continue - } - - switch pkgInfo.Repository { - case RepoTypeSystem: - systemPkgs = append(systemPkgs, pkgInfo.Name) - case RepoTypeManual: - manualPkgs = append(manualPkgs, dep.Name) - } - } - - return systemPkgs, manualPkgs -} - -func (d *DebianDistribution) installAPTPackages(ctx context.Context, packages []string, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - if len(packages) == 0 { - return nil - } - - d.log(fmt.Sprintf("Installing APT packages: %s", strings.Join(packages, ", "))) - - args := []string{"apt-get", "install", "-y"} - args = append(args, packages...) - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.40, - Step: "Installing system packages...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: fmt.Sprintf("sudo %s", strings.Join(args, " ")), - } - - cmdStr := fmt.Sprintf("echo '%s' | sudo -S %s", sudoPassword, strings.Join(args, " ")) - cmd := exec.CommandContext(ctx, "bash", "-c", cmdStr) - return d.runWithProgress(cmd, progressChan, PhaseSystemPackages, 0.40, 0.60) -} - -func (d *DebianDistribution) installBuildDependencies(ctx context.Context, manualPkgs []string, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - buildDeps := make(map[string]bool) - - for _, pkg := range manualPkgs { - switch pkg { - case "niri": - buildDeps["curl"] = true - buildDeps["libxkbcommon-dev"] = true - buildDeps["libwayland-dev"] = true - buildDeps["libudev-dev"] = true - buildDeps["libinput-dev"] = true - buildDeps["libdisplay-info-dev"] = true - buildDeps["libpango1.0-dev"] = true - buildDeps["libcairo-dev"] = true - buildDeps["libpipewire-0.3-dev"] = true - buildDeps["libc6-dev"] = true - buildDeps["clang"] = true - buildDeps["libseat-dev"] = true - buildDeps["libgbm-dev"] = true - buildDeps["alacritty"] = true - buildDeps["fuzzel"] = true - case "quickshell": - buildDeps["qt6-base-dev"] = true - buildDeps["qt6-base-private-dev"] = true - buildDeps["qt6-declarative-dev"] = true - buildDeps["qt6-declarative-private-dev"] = true - buildDeps["qt6-wayland-dev"] = true - buildDeps["qt6-wayland-private-dev"] = true - buildDeps["qt6-tools-dev"] = true - buildDeps["libqt6svg6-dev"] = true - buildDeps["qt6-shadertools-dev"] = true - buildDeps["spirv-tools"] = true - buildDeps["libcli11-dev"] = true - buildDeps["libjemalloc-dev"] = true - buildDeps["libwayland-dev"] = true - buildDeps["wayland-protocols"] = true - buildDeps["libdrm-dev"] = true - buildDeps["libgbm-dev"] = true - buildDeps["libegl-dev"] = true - buildDeps["libgles2-mesa-dev"] = true - buildDeps["libgl1-mesa-dev"] = true - buildDeps["libxcb1-dev"] = true - buildDeps["libpipewire-0.3-dev"] = true - buildDeps["libpam0g-dev"] = true - case "ghostty": - buildDeps["curl"] = true - case "matugen": - buildDeps["curl"] = true - } - } - - for _, pkg := range manualPkgs { - switch pkg { - case "niri", "matugen": - if err := d.installRust(ctx, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install Rust: %w", err) - } - case "cliphist", "dgop": - if err := d.installGo(ctx, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install Go: %w", err) - } - } - } - - if len(buildDeps) == 0 { - return nil - } - - depList := make([]string, 0, len(buildDeps)) - for dep := range buildDeps { - depList = append(depList, dep) - } - - args := []string{"apt-get", "install", "-y"} - args = append(args, depList...) - - cmdStr := fmt.Sprintf("echo '%s' | sudo -S %s", sudoPassword, strings.Join(args, " ")) - cmd := exec.CommandContext(ctx, "bash", "-c", cmdStr) - return d.runWithProgress(cmd, progressChan, PhaseSystemPackages, 0.80, 0.82) -} - -func (d *DebianDistribution) installRust(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - if d.commandExists("cargo") { - return nil - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.82, - Step: "Installing rustup...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: "sudo apt-get install rustup", - } - - rustupInstallCmd := exec.CommandContext(ctx, "bash", "-c", - fmt.Sprintf("echo '%s' | sudo -S apt-get install -y rustup", sudoPassword)) - if err := d.runWithProgress(rustupInstallCmd, progressChan, PhaseSystemPackages, 0.82, 0.83); err != nil { - return fmt.Errorf("failed to install rustup: %w", err) - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.83, - Step: "Installing stable Rust toolchain...", - IsComplete: false, - CommandInfo: "rustup install stable", - } - - rustInstallCmd := exec.CommandContext(ctx, "bash", "-c", "rustup install stable && rustup default stable") - if err := d.runWithProgress(rustInstallCmd, progressChan, PhaseSystemPackages, 0.83, 0.84); err != nil { - return fmt.Errorf("failed to install Rust toolchain: %w", err) - } - - if !d.commandExists("cargo") { - d.log("Warning: cargo not found in PATH after Rust installation, trying to source environment") - } - - return nil -} - -func (d *DebianDistribution) installGo(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - if d.commandExists("go") { - return nil - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.87, - Step: "Installing Go...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: "sudo apt-get install golang-go", - } - - installCmd := exec.CommandContext(ctx, "bash", "-c", - fmt.Sprintf("echo '%s' | sudo -S apt-get install -y golang-go", sudoPassword)) - return d.runWithProgress(installCmd, progressChan, PhaseSystemPackages, 0.87, 0.90) -} - -func (d *DebianDistribution) installGhosttyDebian(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - d.log("Installing Ghostty using Debian installer script...") - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.1, - Step: "Running Ghostty Debian installer...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: "curl -fsSL https://raw.githubusercontent.com/mkasberg/ghostty-ubuntu/HEAD/install.sh | sudo bash", - LogOutput: "Installing Ghostty using pre-built Debian package", - } - - installCmd := exec.CommandContext(ctx, "bash", "-c", - fmt.Sprintf("echo '%s' | sudo -S /bin/bash -c \"$(curl -fsSL https://raw.githubusercontent.com/mkasberg/ghostty-ubuntu/HEAD/install.sh)\"", sudoPassword)) - - if err := d.runWithProgress(installCmd, progressChan, PhaseSystemPackages, 0.1, 0.9); err != nil { - return fmt.Errorf("failed to install Ghostty: %w", err) - } - - d.log("Ghostty installed successfully using Debian installer") - return nil -} - -func (d *DebianDistribution) InstallManualPackages(ctx context.Context, packages []string, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - if len(packages) == 0 { - return nil - } - - d.log(fmt.Sprintf("Installing manual packages: %s", strings.Join(packages, ", "))) - - for _, pkg := range packages { - switch pkg { - case "ghostty": - if err := d.installGhosttyDebian(ctx, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install ghostty: %w", err) - } - default: - if err := d.ManualPackageInstaller.InstallManualPackages(ctx, []string{pkg}, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install %s: %w", pkg, err) - } - } - } - - return nil -} diff --git a/nix/inputs/dms-cli/internal/distros/factory.go b/nix/inputs/dms-cli/internal/distros/factory.go deleted file mode 100644 index 70518d3..0000000 --- a/nix/inputs/dms-cli/internal/distros/factory.go +++ /dev/null @@ -1,19 +0,0 @@ -package distros - -import ( - "github.com/AvengeMedia/danklinux/internal/deps" -) - -// NewDependencyDetector creates a DependencyDetector for the specified distribution -func NewDependencyDetector(distribution string, logChan chan<- string) (deps.DependencyDetector, error) { - distro, err := NewDistribution(distribution, logChan) - if err != nil { - return nil, err - } - return distro, nil -} - -// NewPackageInstaller creates a Distribution for package installation -func NewPackageInstaller(distribution string, logChan chan<- string) (Distribution, error) { - return NewDistribution(distribution, logChan) -} diff --git a/nix/inputs/dms-cli/internal/distros/fedora.go b/nix/inputs/dms-cli/internal/distros/fedora.go deleted file mode 100644 index 72204ae..0000000 --- a/nix/inputs/dms-cli/internal/distros/fedora.go +++ /dev/null @@ -1,553 +0,0 @@ -package distros - -import ( - "context" - "fmt" - "os/exec" - "strings" - - "github.com/AvengeMedia/danklinux/internal/deps" -) - -func init() { - Register("fedora", "#0B57A4", FamilyFedora, func(config DistroConfig, logChan chan<- string) Distribution { - return NewFedoraDistribution(config, logChan) - }) - Register("nobara", "#0B57A4", FamilyFedora, func(config DistroConfig, logChan chan<- string) Distribution { - return NewFedoraDistribution(config, logChan) - }) - Register("fedora-asahi-remix", "#0B57A4", FamilyFedora, func(config DistroConfig, logChan chan<- string) Distribution { - return NewFedoraDistribution(config, logChan) - }) - - Register("bluefin", "#0B57A4", FamilyFedora, func(config DistroConfig, logChan chan<- string) Distribution { - return NewFedoraDistribution(config, logChan) - }) -} - -type FedoraDistribution struct { - *BaseDistribution - *ManualPackageInstaller - config DistroConfig -} - -func NewFedoraDistribution(config DistroConfig, logChan chan<- string) *FedoraDistribution { - base := NewBaseDistribution(logChan) - return &FedoraDistribution{ - BaseDistribution: base, - ManualPackageInstaller: &ManualPackageInstaller{BaseDistribution: base}, - config: config, - } -} - -func (f *FedoraDistribution) GetID() string { - return f.config.ID -} - -func (f *FedoraDistribution) GetColorHex() string { - return f.config.ColorHex -} - -func (f *FedoraDistribution) GetFamily() DistroFamily { - return f.config.Family -} - -func (f *FedoraDistribution) GetPackageManager() PackageManagerType { - return PackageManagerDNF -} - -func (f *FedoraDistribution) DetectDependencies(ctx context.Context, wm deps.WindowManager) ([]deps.Dependency, error) { - return f.DetectDependenciesWithTerminal(ctx, wm, deps.TerminalGhostty) -} - -func (f *FedoraDistribution) DetectDependenciesWithTerminal(ctx context.Context, wm deps.WindowManager, terminal deps.Terminal) ([]deps.Dependency, error) { - var dependencies []deps.Dependency - - // DMS at the top (shell is prominent) - dependencies = append(dependencies, f.detectDMS()) - - // Terminal with choice support - dependencies = append(dependencies, f.detectSpecificTerminal(terminal)) - - // Common detections using base methods - dependencies = append(dependencies, f.detectGit()) - dependencies = append(dependencies, f.detectWindowManager(wm)) - dependencies = append(dependencies, f.detectQuickshell()) - dependencies = append(dependencies, f.detectXDGPortal()) - dependencies = append(dependencies, f.detectPolkitAgent()) - dependencies = append(dependencies, f.detectAccountsService()) - - // Hyprland-specific tools - if wm == deps.WindowManagerHyprland { - dependencies = append(dependencies, f.detectHyprlandTools()...) - } - - // Niri-specific tools - if wm == deps.WindowManagerNiri { - dependencies = append(dependencies, f.detectXwaylandSatellite()) - } - - // Base detections (common across distros) - dependencies = append(dependencies, f.detectMatugen()) - dependencies = append(dependencies, f.detectDgop()) - dependencies = append(dependencies, f.detectHyprpicker()) - dependencies = append(dependencies, f.detectClipboardTools()...) - - return dependencies, nil -} - -func (f *FedoraDistribution) detectXDGPortal() deps.Dependency { - status := deps.StatusMissing - if f.packageInstalled("xdg-desktop-portal-gtk") { - status = deps.StatusInstalled - } - - return deps.Dependency{ - Name: "xdg-desktop-portal-gtk", - Status: status, - Description: "Desktop integration portal for GTK", - Required: true, - } -} - -func (f *FedoraDistribution) detectPolkitAgent() deps.Dependency { - status := deps.StatusMissing - if f.packageInstalled("mate-polkit") { - status = deps.StatusInstalled - } - - return deps.Dependency{ - Name: "mate-polkit", - Status: status, - Description: "PolicyKit authentication agent", - Required: true, - } -} - -func (f *FedoraDistribution) packageInstalled(pkg string) bool { - cmd := exec.Command("rpm", "-q", pkg) - err := cmd.Run() - return err == nil -} - -func (f *FedoraDistribution) GetPackageMapping(wm deps.WindowManager) map[string]PackageMapping { - return f.GetPackageMappingWithVariants(wm, make(map[string]deps.PackageVariant)) -} - -func (f *FedoraDistribution) GetPackageMappingWithVariants(wm deps.WindowManager, variants map[string]deps.PackageVariant) map[string]PackageMapping { - packages := map[string]PackageMapping{ - // Standard DNF packages - "git": {Name: "git", Repository: RepoTypeSystem}, - "ghostty": {Name: "ghostty", Repository: RepoTypeCOPR, RepoURL: "avengemedia/danklinux"}, - "kitty": {Name: "kitty", Repository: RepoTypeSystem}, - "alacritty": {Name: "alacritty", Repository: RepoTypeSystem}, - "wl-clipboard": {Name: "wl-clipboard", Repository: RepoTypeSystem}, - "xdg-desktop-portal-gtk": {Name: "xdg-desktop-portal-gtk", Repository: RepoTypeSystem}, - "mate-polkit": {Name: "mate-polkit", Repository: RepoTypeSystem}, - "accountsservice": {Name: "accountsservice", Repository: RepoTypeSystem}, - "hyprpicker": f.getHyprpickerMapping(variants["hyprland"]), - - // COPR packages - "quickshell": f.getQuickshellMapping(variants["quickshell"]), - "matugen": {Name: "matugen", Repository: RepoTypeCOPR, RepoURL: "avengemedia/danklinux"}, - "cliphist": {Name: "cliphist", Repository: RepoTypeCOPR, RepoURL: "avengemedia/danklinux"}, - "dms (DankMaterialShell)": f.getDmsMapping(variants["dms (DankMaterialShell)"]), - "dgop": {Name: "dgop", Repository: RepoTypeCOPR, RepoURL: "avengemedia/danklinux"}, - } - - switch wm { - case deps.WindowManagerHyprland: - packages["hyprland"] = f.getHyprlandMapping(variants["hyprland"]) - packages["grim"] = PackageMapping{Name: "grim", Repository: RepoTypeSystem} - packages["slurp"] = PackageMapping{Name: "slurp", Repository: RepoTypeSystem} - packages["hyprctl"] = f.getHyprlandMapping(variants["hyprland"]) - packages["grimblast"] = PackageMapping{Name: "grimblast", Repository: RepoTypeManual, BuildFunc: "installGrimblast"} - packages["jq"] = PackageMapping{Name: "jq", Repository: RepoTypeSystem} - case deps.WindowManagerNiri: - packages["niri"] = f.getNiriMapping(variants["niri"]) - packages["xwayland-satellite"] = PackageMapping{Name: "xwayland-satellite", Repository: RepoTypeCOPR, RepoURL: "yalter/niri"} - } - - return packages -} - -func (f *FedoraDistribution) getQuickshellMapping(variant deps.PackageVariant) PackageMapping { - if forceQuickshellGit || variant == deps.VariantGit { - return PackageMapping{Name: "quickshell-git", Repository: RepoTypeCOPR, RepoURL: "avengemedia/danklinux"} - } - return PackageMapping{Name: "quickshell", Repository: RepoTypeCOPR, RepoURL: "avengemedia/danklinux"} -} - -func (f *FedoraDistribution) getDmsMapping(variant deps.PackageVariant) PackageMapping { - if variant == deps.VariantGit { - return PackageMapping{Name: "dms", Repository: RepoTypeCOPR, RepoURL: "avengemedia/dms-git"} - } - return PackageMapping{Name: "dms", Repository: RepoTypeCOPR, RepoURL: "avengemedia/dms"} -} - -func (f *FedoraDistribution) getHyprlandMapping(variant deps.PackageVariant) PackageMapping { - if variant == deps.VariantGit { - return PackageMapping{Name: "hyprland-git", Repository: RepoTypeCOPR, RepoURL: "solopasha/hyprland"} - } - return PackageMapping{Name: "hyprland", Repository: RepoTypeCOPR, RepoURL: "solopasha/hyprland"} -} - -func (f *FedoraDistribution) getHyprpickerMapping(variant deps.PackageVariant) PackageMapping { - if variant == deps.VariantGit { - return PackageMapping{Name: "hyprpicker-git", Repository: RepoTypeCOPR, RepoURL: "solopasha/hyprland"} - } - return PackageMapping{Name: "hyprpicker", Repository: RepoTypeCOPR, RepoURL: "avengemedia/danklinux"} -} - -func (f *FedoraDistribution) getNiriMapping(variant deps.PackageVariant) PackageMapping { - if variant == deps.VariantGit { - return PackageMapping{Name: "niri", Repository: RepoTypeCOPR, RepoURL: "yalter/niri-git"} - } - return PackageMapping{Name: "niri", Repository: RepoTypeCOPR, RepoURL: "yalter/niri"} -} - -func (f *FedoraDistribution) detectXwaylandSatellite() deps.Dependency { - status := deps.StatusMissing - if f.commandExists("xwayland-satellite") { - status = deps.StatusInstalled - } - - return deps.Dependency{ - Name: "xwayland-satellite", - Status: status, - Description: "Xwayland support", - Required: true, - } -} - -func (f *FedoraDistribution) detectAccountsService() deps.Dependency { - status := deps.StatusMissing - if f.packageInstalled("accountsservice") { - status = deps.StatusInstalled - } - - return deps.Dependency{ - Name: "accountsservice", - Status: status, - Description: "D-Bus interface for user account query and manipulation", - Required: true, - } -} - -func (f *FedoraDistribution) getPrerequisites() []string { - return []string{ - "dnf-plugins-core", - "make", - "unzip", - "libwayland-server", - } -} - -func (f *FedoraDistribution) InstallPrerequisites(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - prerequisites := f.getPrerequisites() - var missingPkgs []string - - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.06, - Step: "Checking prerequisites...", - IsComplete: false, - LogOutput: "Checking prerequisite packages", - } - - for _, pkg := range prerequisites { - checkCmd := exec.CommandContext(ctx, "rpm", "-q", pkg) - if err := checkCmd.Run(); err != nil { - missingPkgs = append(missingPkgs, pkg) - } - } - - _, err := exec.LookPath("go") - if err != nil { - f.log("go not found in PATH, will install golang-bin") - missingPkgs = append(missingPkgs, "golang-bin") - } else { - f.log("go already available in PATH") - } - - if len(missingPkgs) == 0 { - f.log("All prerequisites already installed") - return nil - } - - f.log(fmt.Sprintf("Installing prerequisites: %s", strings.Join(missingPkgs, ", "))) - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.08, - Step: fmt.Sprintf("Installing %d prerequisites...", len(missingPkgs)), - IsComplete: false, - NeedsSudo: true, - CommandInfo: fmt.Sprintf("sudo dnf install -y %s", strings.Join(missingPkgs, " ")), - LogOutput: fmt.Sprintf("Installing prerequisites: %s", strings.Join(missingPkgs, ", ")), - } - - args := []string{"dnf", "install", "-y"} - args = append(args, missingPkgs...) - cmdStr := fmt.Sprintf("echo '%s' | sudo -S %s", sudoPassword, strings.Join(args, " ")) - cmd := exec.CommandContext(ctx, "bash", "-c", cmdStr) - output, err := cmd.CombinedOutput() - if err != nil { - f.logError("failed to install prerequisites", err) - f.log(fmt.Sprintf("Prerequisites command output: %s", string(output))) - return fmt.Errorf("failed to install prerequisites: %w", err) - } - f.log(fmt.Sprintf("Prerequisites install output: %s", string(output))) - - return nil -} - -func (f *FedoraDistribution) InstallPackages(ctx context.Context, dependencies []deps.Dependency, wm deps.WindowManager, sudoPassword string, reinstallFlags map[string]bool, progressChan chan<- InstallProgressMsg) error { - // Phase 1: Check Prerequisites - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.05, - Step: "Checking system prerequisites...", - IsComplete: false, - LogOutput: "Starting prerequisite check...", - } - - if err := f.InstallPrerequisites(ctx, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install prerequisites: %w", err) - } - - dnfPkgs, coprPkgs, manualPkgs := f.categorizePackages(dependencies, wm, reinstallFlags) - - // Phase 2: Enable COPR repositories - if len(coprPkgs) > 0 { - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.15, - Step: "Enabling COPR repositories...", - IsComplete: false, - LogOutput: "Setting up COPR repositories for additional packages", - } - if err := f.enableCOPRRepos(ctx, coprPkgs, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to enable COPR repositories: %w", err) - } - } - - // Phase 3: System Packages (DNF) - if len(dnfPkgs) > 0 { - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.35, - Step: fmt.Sprintf("Installing %d system packages...", len(dnfPkgs)), - IsComplete: false, - NeedsSudo: true, - LogOutput: fmt.Sprintf("Installing system packages: %s", strings.Join(dnfPkgs, ", ")), - } - if err := f.installDNFPackages(ctx, dnfPkgs, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install DNF packages: %w", err) - } - } - - // Phase 4: COPR Packages - coprPkgNames := f.extractPackageNames(coprPkgs) - if len(coprPkgNames) > 0 { - progressChan <- InstallProgressMsg{ - Phase: PhaseAURPackages, // Reusing AUR phase for COPR - Progress: 0.65, - Step: fmt.Sprintf("Installing %d COPR packages...", len(coprPkgNames)), - IsComplete: false, - LogOutput: fmt.Sprintf("Installing COPR packages: %s", strings.Join(coprPkgNames, ", ")), - } - if err := f.installCOPRPackages(ctx, coprPkgNames, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install COPR packages: %w", err) - } - } - - // Phase 5: Manual Builds - if len(manualPkgs) > 0 { - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.85, - Step: fmt.Sprintf("Building %d packages from source...", len(manualPkgs)), - IsComplete: false, - LogOutput: fmt.Sprintf("Building from source: %s", strings.Join(manualPkgs, ", ")), - } - if err := f.InstallManualPackages(ctx, manualPkgs, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install manual packages: %w", err) - } - } - - // Phase 6: Configuration - progressChan <- InstallProgressMsg{ - Phase: PhaseConfiguration, - Progress: 0.90, - Step: "Configuring system...", - IsComplete: false, - LogOutput: "Starting post-installation configuration...", - } - - // Phase 7: Complete - progressChan <- InstallProgressMsg{ - Phase: PhaseComplete, - Progress: 1.0, - Step: "Installation complete!", - IsComplete: true, - LogOutput: "All packages installed and configured successfully", - } - - return nil -} - -func (f *FedoraDistribution) categorizePackages(dependencies []deps.Dependency, wm deps.WindowManager, reinstallFlags map[string]bool) ([]string, []PackageMapping, []string) { - dnfPkgs := []string{} - coprPkgs := []PackageMapping{} - manualPkgs := []string{} - - variantMap := make(map[string]deps.PackageVariant) - for _, dep := range dependencies { - variantMap[dep.Name] = dep.Variant - } - - packageMap := f.GetPackageMappingWithVariants(wm, variantMap) - - for _, dep := range dependencies { - // Skip installed packages unless marked for reinstall - if dep.Status == deps.StatusInstalled && !reinstallFlags[dep.Name] { - continue - } - - pkgInfo, exists := packageMap[dep.Name] - if !exists { - f.log(fmt.Sprintf("Warning: No package mapping for %s", dep.Name)) - continue - } - - switch pkgInfo.Repository { - case RepoTypeSystem: - dnfPkgs = append(dnfPkgs, pkgInfo.Name) - case RepoTypeCOPR: - coprPkgs = append(coprPkgs, pkgInfo) - case RepoTypeManual: - manualPkgs = append(manualPkgs, dep.Name) - } - } - - return dnfPkgs, coprPkgs, manualPkgs -} - -func (f *FedoraDistribution) extractPackageNames(packages []PackageMapping) []string { - names := make([]string, len(packages)) - for i, pkg := range packages { - names[i] = pkg.Name - } - return names -} - -func (f *FedoraDistribution) enableCOPRRepos(ctx context.Context, coprPkgs []PackageMapping, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - enabledRepos := make(map[string]bool) - - for _, pkg := range coprPkgs { - if pkg.RepoURL != "" && !enabledRepos[pkg.RepoURL] { - f.log(fmt.Sprintf("Enabling COPR repository: %s", pkg.RepoURL)) - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.20, - Step: fmt.Sprintf("Enabling COPR repo %s...", pkg.RepoURL), - IsComplete: false, - NeedsSudo: true, - CommandInfo: fmt.Sprintf("sudo dnf copr enable -y %s", pkg.RepoURL), - } - - cmd := exec.CommandContext(ctx, "bash", "-c", - fmt.Sprintf("echo '%s' | sudo -S dnf copr enable -y %s 2>&1", sudoPassword, pkg.RepoURL)) - output, err := cmd.CombinedOutput() - if err != nil { - f.logError(fmt.Sprintf("failed to enable COPR repo %s", pkg.RepoURL), err) - f.log(fmt.Sprintf("COPR enable command output: %s", string(output))) - return fmt.Errorf("failed to enable COPR repo %s: %w", pkg.RepoURL, err) - } - f.log(fmt.Sprintf("COPR repo %s enabled successfully: %s", pkg.RepoURL, string(output))) - enabledRepos[pkg.RepoURL] = true - - // Special handling for niri COPR repo - set priority=1 - if pkg.RepoURL == "yalter/niri-git" { - f.log("Setting priority=1 for niri-git COPR repo...") - repoFile := "/etc/yum.repos.d/_copr:copr.fedorainfracloud.org:yalter:niri-git.repo" - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.22, - Step: "Setting niri COPR repo priority...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: fmt.Sprintf("echo \"priority=1\" | sudo tee -a %s", repoFile), - } - - priorityCmd := exec.CommandContext(ctx, "bash", "-c", - fmt.Sprintf("echo '%s' | sudo -S bash -c 'echo \"priority=1\" | tee -a %s' 2>&1", sudoPassword, repoFile)) - priorityOutput, err := priorityCmd.CombinedOutput() - if err != nil { - f.logError("failed to set niri COPR repo priority", err) - f.log(fmt.Sprintf("Priority command output: %s", string(priorityOutput))) - return fmt.Errorf("failed to set niri COPR repo priority: %w", err) - } - f.log(fmt.Sprintf("niri COPR repo priority set successfully: %s", string(priorityOutput))) - } - } - } - - return nil -} - -func (f *FedoraDistribution) installDNFPackages(ctx context.Context, packages []string, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - if len(packages) == 0 { - return nil - } - - f.log(fmt.Sprintf("Installing DNF packages: %s", strings.Join(packages, ", "))) - - args := []string{"dnf", "install", "-y"} - args = append(args, packages...) - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.40, - Step: "Installing system packages...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: fmt.Sprintf("sudo %s", strings.Join(args, " ")), - } - - cmdStr := fmt.Sprintf("echo '%s' | sudo -S %s", sudoPassword, strings.Join(args, " ")) - cmd := exec.CommandContext(ctx, "bash", "-c", cmdStr) - return f.runWithProgress(cmd, progressChan, PhaseSystemPackages, 0.40, 0.60) -} - -func (f *FedoraDistribution) installCOPRPackages(ctx context.Context, packages []string, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - if len(packages) == 0 { - return nil - } - - f.log(fmt.Sprintf("Installing COPR packages: %s", strings.Join(packages, ", "))) - - args := []string{"dnf", "install", "-y"} - - for _, pkg := range packages { - if pkg == "niri" || pkg == "niri-git" { - args = append(args, "--setopt=install_weak_deps=False") - break - } - } - - args = append(args, packages...) - - progressChan <- InstallProgressMsg{ - Phase: PhaseAURPackages, - Progress: 0.70, - Step: "Installing COPR packages...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: fmt.Sprintf("sudo %s", strings.Join(args, " ")), - } - - cmdStr := fmt.Sprintf("echo '%s' | sudo -S %s", sudoPassword, strings.Join(args, " ")) - cmd := exec.CommandContext(ctx, "bash", "-c", cmdStr) - return f.runWithProgress(cmd, progressChan, PhaseAURPackages, 0.70, 0.85) -} diff --git a/nix/inputs/dms-cli/internal/distros/gentoo.go b/nix/inputs/dms-cli/internal/distros/gentoo.go deleted file mode 100644 index e2e5fd8..0000000 --- a/nix/inputs/dms-cli/internal/distros/gentoo.go +++ /dev/null @@ -1,607 +0,0 @@ -package distros - -import ( - "context" - "fmt" - "os/exec" - "strings" - - "github.com/AvengeMedia/danklinux/internal/deps" -) - -func init() { - Register("gentoo", "#54487A", FamilyGentoo, func(config DistroConfig, logChan chan<- string) Distribution { - return NewGentooDistribution(config, logChan) - }) -} - -type GentooDistribution struct { - *BaseDistribution - *ManualPackageInstaller - config DistroConfig -} - -func NewGentooDistribution(config DistroConfig, logChan chan<- string) *GentooDistribution { - base := NewBaseDistribution(logChan) - return &GentooDistribution{ - BaseDistribution: base, - ManualPackageInstaller: &ManualPackageInstaller{BaseDistribution: base}, - config: config, - } -} - -func (g *GentooDistribution) GetID() string { - return g.config.ID -} - -func (g *GentooDistribution) GetColorHex() string { - return g.config.ColorHex -} - -func (g *GentooDistribution) GetFamily() DistroFamily { - return g.config.Family -} - -func (g *GentooDistribution) GetPackageManager() PackageManagerType { - return PackageManagerPortage -} - -func (g *GentooDistribution) DetectDependencies(ctx context.Context, wm deps.WindowManager) ([]deps.Dependency, error) { - return g.DetectDependenciesWithTerminal(ctx, wm, deps.TerminalGhostty) -} - -func (g *GentooDistribution) DetectDependenciesWithTerminal(ctx context.Context, wm deps.WindowManager, terminal deps.Terminal) ([]deps.Dependency, error) { - var dependencies []deps.Dependency - - dependencies = append(dependencies, g.detectDMS()) - - dependencies = append(dependencies, g.detectSpecificTerminal(terminal)) - - dependencies = append(dependencies, g.detectGit()) - dependencies = append(dependencies, g.detectWindowManager(wm)) - dependencies = append(dependencies, g.detectQuickshell()) - dependencies = append(dependencies, g.detectXDGPortal()) - dependencies = append(dependencies, g.detectPolkitAgent()) - dependencies = append(dependencies, g.detectAccountsService()) - - if wm == deps.WindowManagerHyprland { - dependencies = append(dependencies, g.detectHyprlandTools()...) - } - - if wm == deps.WindowManagerNiri { - dependencies = append(dependencies, g.detectXwaylandSatellite()) - } - - dependencies = append(dependencies, g.detectMatugen()) - dependencies = append(dependencies, g.detectDgop()) - dependencies = append(dependencies, g.detectHyprpicker()) - dependencies = append(dependencies, g.detectClipboardTools()...) - - return dependencies, nil -} - -func (g *GentooDistribution) detectXDGPortal() deps.Dependency { - status := deps.StatusMissing - if g.packageInstalled("sys-apps/xdg-desktop-portal-gtk") { - status = deps.StatusInstalled - } - - return deps.Dependency{ - Name: "xdg-desktop-portal-gtk", - Status: status, - Description: "Desktop integration portal for GTK", - Required: true, - } -} - -func (g *GentooDistribution) detectPolkitAgent() deps.Dependency { - status := deps.StatusMissing - if g.packageInstalled("mate-extra/mate-polkit") { - status = deps.StatusInstalled - } - - return deps.Dependency{ - Name: "mate-polkit", - Status: status, - Description: "PolicyKit authentication agent", - Required: true, - } -} - -func (g *GentooDistribution) detectXwaylandSatellite() deps.Dependency { - status := deps.StatusMissing - if g.commandExists("xwayland-satellite") { - status = deps.StatusInstalled - } - - return deps.Dependency{ - Name: "xwayland-satellite", - Status: status, - Description: "Xwayland support", - Required: true, - } -} - -func (g *GentooDistribution) detectAccountsService() deps.Dependency { - status := deps.StatusMissing - if g.packageInstalled("sys-apps/accountsservice") { - status = deps.StatusInstalled - } - - return deps.Dependency{ - Name: "accountsservice", - Status: status, - Description: "D-Bus interface for user account query and manipulation", - Required: true, - } -} - -func (g *GentooDistribution) packageInstalled(pkg string) bool { - cmd := exec.Command("qlist", "-I", pkg) - err := cmd.Run() - return err == nil -} - -func (g *GentooDistribution) GetPackageMapping(wm deps.WindowManager) map[string]PackageMapping { - return g.GetPackageMappingWithVariants(wm, make(map[string]deps.PackageVariant)) -} - -func (g *GentooDistribution) GetPackageMappingWithVariants(wm deps.WindowManager, variants map[string]deps.PackageVariant) map[string]PackageMapping { - packages := map[string]PackageMapping{ - "git": {Name: "dev-vcs/git", Repository: RepoTypeSystem}, - "ghostty": {Name: "x11-terms/ghostty", Repository: RepoTypeSystem, UseFlags: "X wayland", AcceptKeywords: "~amd64"}, - "kitty": {Name: "x11-terms/kitty", Repository: RepoTypeSystem, UseFlags: "X wayland"}, - "alacritty": {Name: "x11-terms/alacritty", Repository: RepoTypeSystem, UseFlags: "X wayland"}, - "wl-clipboard": {Name: "gui-apps/wl-clipboard", Repository: RepoTypeSystem}, - "xdg-desktop-portal-gtk": {Name: "sys-apps/xdg-desktop-portal-gtk", Repository: RepoTypeSystem, UseFlags: "wayland X"}, - "mate-polkit": {Name: "mate-extra/mate-polkit", Repository: RepoTypeSystem}, - "accountsservice": {Name: "sys-apps/accountsservice", Repository: RepoTypeSystem}, - "hyprpicker": g.getHyprpickerMapping(variants["hyprland"]), - - "quickshell": g.getQuickshellMapping(variants["quickshell"]), - "matugen": {Name: "x11-misc/matugen", Repository: RepoTypeGURU, AcceptKeywords: "~amd64"}, - "cliphist": {Name: "app-misc/cliphist", Repository: RepoTypeGURU, AcceptKeywords: "~amd64"}, - "dms (DankMaterialShell)": g.getDmsMapping(variants["dms (DankMaterialShell)"]), - "dgop": {Name: "dgop", Repository: RepoTypeManual, BuildFunc: "installDgop"}, - } - - switch wm { - case deps.WindowManagerHyprland: - packages["hyprland"] = g.getHyprlandMapping(variants["hyprland"]) - packages["grim"] = PackageMapping{Name: "gui-apps/grim", Repository: RepoTypeSystem} - packages["slurp"] = PackageMapping{Name: "gui-apps/slurp", Repository: RepoTypeSystem} - packages["hyprctl"] = g.getHyprlandMapping(variants["hyprland"]) - packages["grimblast"] = PackageMapping{Name: "grimblast", Repository: RepoTypeManual, BuildFunc: "installGrimblast"} - packages["jq"] = PackageMapping{Name: "app-misc/jq", Repository: RepoTypeSystem} - case deps.WindowManagerNiri: - packages["niri"] = g.getNiriMapping(variants["niri"]) - packages["xwayland-satellite"] = PackageMapping{Name: "xwayland-satellite", Repository: RepoTypeManual, BuildFunc: "installXwaylandSatellite"} - } - - return packages -} - -func (g *GentooDistribution) getQuickshellMapping(variant deps.PackageVariant) PackageMapping { - if forceQuickshellGit || variant == deps.VariantGit { - return PackageMapping{Name: "gui-apps/quickshell", Repository: RepoTypeGURU, UseFlags: "-breakpad jemalloc sockets wayland layer-shell session-lock toplevel-management screencopy X pipewire tray mpris pam hyprland hyprland-global-shortcuts hyprland-focus-grab i3 i3-ipc bluetooth", AcceptKeywords: "~amd64"} - } - return PackageMapping{Name: "gui-apps/quickshell", Repository: RepoTypeGURU, UseFlags: "-breakpad jemalloc sockets wayland layer-shell session-lock toplevel-management screencopy X pipewire tray mpris pam hyprland hyprland-global-shortcuts hyprland-focus-grab i3 i3-ipc bluetooth", AcceptKeywords: "~amd64"} -} - -func (g *GentooDistribution) getDmsMapping(_ deps.PackageVariant) PackageMapping { - return PackageMapping{Name: "dms", Repository: RepoTypeManual, BuildFunc: "installDankMaterialShell"} -} - -func (g *GentooDistribution) getHyprlandMapping(variant deps.PackageVariant) PackageMapping { - if variant == deps.VariantGit { - return PackageMapping{Name: "gui-wm/hyprland", Repository: RepoTypeGURU, UseFlags: "X", AcceptKeywords: "~amd64"} - } - return PackageMapping{Name: "gui-wm/hyprland", Repository: RepoTypeSystem, UseFlags: "X", AcceptKeywords: "~amd64"} -} - -func (g *GentooDistribution) getHyprpickerMapping(_ deps.PackageVariant) PackageMapping { - return PackageMapping{Name: "gui-apps/hyprpicker", Repository: RepoTypeGURU, AcceptKeywords: "~amd64"} -} - -func (g *GentooDistribution) getNiriMapping(variant deps.PackageVariant) PackageMapping { - if variant == deps.VariantGit { - return PackageMapping{Name: "gui-wm/niri", Repository: RepoTypeGURU, UseFlags: "dbus screencast", AcceptKeywords: "~amd64"} - } - return PackageMapping{Name: "gui-wm/niri", Repository: RepoTypeSystem, UseFlags: "dbus screencast", AcceptKeywords: "~amd64"} -} - -func (g *GentooDistribution) getPrerequisites() []string { - return []string{ - "app-eselect/eselect-repository", - "dev-vcs/git", - "dev-build/make", - "app-arch/unzip", - "dev-util/pkgconf", - } -} - -func (g *GentooDistribution) InstallPrerequisites(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - prerequisites := g.getPrerequisites() - var missingPkgs []string - - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.06, - Step: "Checking prerequisites...", - IsComplete: false, - LogOutput: "Checking prerequisite packages", - } - - for _, pkg := range prerequisites { - checkCmd := exec.CommandContext(ctx, "qlist", "-I", pkg) - if err := checkCmd.Run(); err != nil { - missingPkgs = append(missingPkgs, pkg) - } - } - - _, err := exec.LookPath("go") - if err != nil { - g.log("go not found in PATH, will install dev-lang/go") - missingPkgs = append(missingPkgs, "dev-lang/go") - } else { - g.log("go already available in PATH") - } - - if len(missingPkgs) == 0 { - g.log("All prerequisites already installed") - return nil - } - - g.log(fmt.Sprintf("Installing prerequisites: %s", strings.Join(missingPkgs, ", "))) - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.08, - Step: fmt.Sprintf("Installing %d prerequisites...", len(missingPkgs)), - IsComplete: false, - NeedsSudo: true, - CommandInfo: fmt.Sprintf("sudo emerge --ask=n %s", strings.Join(missingPkgs, " ")), - LogOutput: fmt.Sprintf("Installing prerequisites: %s", strings.Join(missingPkgs, ", ")), - } - - args := []string{"emerge", "--ask=n", "--quiet"} - args = append(args, missingPkgs...) - cmdStr := fmt.Sprintf("echo '%s' | sudo -S %s", sudoPassword, strings.Join(args, " ")) - cmd := exec.CommandContext(ctx, "bash", "-c", cmdStr) - output, err := cmd.CombinedOutput() - if err != nil { - g.logError("failed to install prerequisites", err) - g.log(fmt.Sprintf("Prerequisites command output: %s", string(output))) - return fmt.Errorf("failed to install prerequisites: %w", err) - } - g.log(fmt.Sprintf("Prerequisites install output: %s", string(output))) - - return nil -} - -func (g *GentooDistribution) InstallPackages(ctx context.Context, dependencies []deps.Dependency, wm deps.WindowManager, sudoPassword string, reinstallFlags map[string]bool, progressChan chan<- InstallProgressMsg) error { - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.05, - Step: "Checking system prerequisites...", - IsComplete: false, - LogOutput: "Starting prerequisite check...", - } - - if err := g.InstallPrerequisites(ctx, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install prerequisites: %w", err) - } - - systemPkgs, guruPkgs, manualPkgs := g.categorizePackages(dependencies, wm, reinstallFlags) - - g.log(fmt.Sprintf("CATEGORIZED PACKAGES: system=%d, guru=%d, manual=%d", len(systemPkgs), len(guruPkgs), len(manualPkgs))) - - if len(systemPkgs) > 0 { - systemPkgNames := g.extractPackageNames(systemPkgs) - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.35, - Step: fmt.Sprintf("Installing %d system packages...", len(systemPkgs)), - IsComplete: false, - NeedsSudo: true, - LogOutput: fmt.Sprintf("Installing system packages: %s", strings.Join(systemPkgNames, ", ")), - } - if err := g.installPortagePackages(ctx, systemPkgs, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install Portage packages: %w", err) - } - } - - if len(guruPkgs) > 0 { - g.log(fmt.Sprintf("FOUND %d GURU PACKAGES - WILL SYNC GURU REPO", len(guruPkgs))) - progressChan <- InstallProgressMsg{ - Phase: PhaseAURPackages, - Progress: 0.60, - Step: "Syncing GURU repository...", - IsComplete: false, - LogOutput: "Syncing GURU repository to fetch latest ebuilds", - } - g.log("ABOUT TO CALL syncGURURepo") - if err := g.syncGURURepo(ctx, sudoPassword, progressChan); err != nil { - g.log(fmt.Sprintf("syncGURURepo RETURNED ERROR: %v", err)) - return fmt.Errorf("failed to sync GURU repository: %w", err) - } - g.log("syncGURURepo COMPLETED SUCCESSFULLY") - - guruPkgNames := g.extractPackageNames(guruPkgs) - progressChan <- InstallProgressMsg{ - Phase: PhaseAURPackages, - Progress: 0.65, - Step: fmt.Sprintf("Installing %d GURU packages...", len(guruPkgs)), - IsComplete: false, - LogOutput: fmt.Sprintf("Installing GURU packages: %s", strings.Join(guruPkgNames, ", ")), - } - if err := g.installGURUPackages(ctx, guruPkgs, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install GURU packages: %w", err) - } - } - - if len(manualPkgs) > 0 { - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.85, - Step: fmt.Sprintf("Building %d packages from source...", len(manualPkgs)), - IsComplete: false, - LogOutput: fmt.Sprintf("Building from source: %s", strings.Join(manualPkgs, ", ")), - } - if err := g.InstallManualPackages(ctx, manualPkgs, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install manual packages: %w", err) - } - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseConfiguration, - Progress: 0.90, - Step: "Configuring system...", - IsComplete: false, - LogOutput: "Starting post-installation configuration...", - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseComplete, - Progress: 1.0, - Step: "Installation complete!", - IsComplete: true, - LogOutput: "All packages installed and configured successfully", - } - - return nil -} - -func (g *GentooDistribution) categorizePackages(dependencies []deps.Dependency, wm deps.WindowManager, reinstallFlags map[string]bool) ([]PackageMapping, []PackageMapping, []string) { - systemPkgs := []PackageMapping{} - guruPkgs := []PackageMapping{} - manualPkgs := []string{} - - variantMap := make(map[string]deps.PackageVariant) - for _, dep := range dependencies { - variantMap[dep.Name] = dep.Variant - } - - packageMap := g.GetPackageMappingWithVariants(wm, variantMap) - - for _, dep := range dependencies { - if dep.Status == deps.StatusInstalled && !reinstallFlags[dep.Name] { - continue - } - - pkgInfo, exists := packageMap[dep.Name] - if !exists { - g.log(fmt.Sprintf("Warning: No package mapping for %s", dep.Name)) - continue - } - - switch pkgInfo.Repository { - case RepoTypeSystem: - systemPkgs = append(systemPkgs, pkgInfo) - case RepoTypeGURU: - guruPkgs = append(guruPkgs, pkgInfo) - case RepoTypeManual: - manualPkgs = append(manualPkgs, dep.Name) - } - } - - return systemPkgs, guruPkgs, manualPkgs -} - -func (g *GentooDistribution) extractPackageNames(packages []PackageMapping) []string { - names := make([]string, len(packages)) - for i, pkg := range packages { - names[i] = pkg.Name - } - return names -} - -func (g *GentooDistribution) installPortagePackages(ctx context.Context, packages []PackageMapping, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - if len(packages) == 0 { - return nil - } - - packageNames := g.extractPackageNames(packages) - g.log(fmt.Sprintf("Installing Portage packages: %s", strings.Join(packageNames, ", "))) - - for _, pkg := range packages { - if pkg.AcceptKeywords != "" { - if err := g.setPackageAcceptKeywords(ctx, pkg.Name, pkg.AcceptKeywords, sudoPassword); err != nil { - return fmt.Errorf("failed to set accept keywords for %s: %w", pkg.Name, err) - } - } - if pkg.UseFlags != "" { - if err := g.setPackageUseFlags(ctx, pkg.Name, pkg.UseFlags, sudoPassword); err != nil { - return fmt.Errorf("failed to set USE flags for %s: %w", pkg.Name, err) - } - } - } - - args := []string{"emerge", "--ask=n", "--quiet"} - args = append(args, packageNames...) - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.40, - Step: "Installing system packages...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: fmt.Sprintf("sudo %s", strings.Join(args, " ")), - } - - cmdStr := fmt.Sprintf("echo '%s' | sudo -S %s", sudoPassword, strings.Join(args, " ")) - cmd := exec.CommandContext(ctx, "bash", "-c", cmdStr) - return g.runWithProgress(cmd, progressChan, PhaseSystemPackages, 0.40, 0.60) -} - -func (g *GentooDistribution) setPackageUseFlags(ctx context.Context, packageName, useFlags, sudoPassword string) error { - packageUseDir := "/etc/portage/package.use" - - mkdirCmd := exec.CommandContext(ctx, "bash", "-c", - fmt.Sprintf("echo '%s' | sudo -S mkdir -p %s", sudoPassword, packageUseDir)) - if output, err := mkdirCmd.CombinedOutput(); err != nil { - g.log(fmt.Sprintf("mkdir output: %s", string(output))) - return fmt.Errorf("failed to create package.use directory: %w", err) - } - - useFlagLine := fmt.Sprintf("%s %s", packageName, useFlags) - - appendCmd := exec.CommandContext(ctx, "bash", "-c", - fmt.Sprintf("echo '%s' | sudo -S bash -c \"echo '%s' >> %s/danklinux\"", sudoPassword, useFlagLine, packageUseDir)) - - output, err := appendCmd.CombinedOutput() - if err != nil { - g.log(fmt.Sprintf("append output: %s", string(output))) - return fmt.Errorf("failed to write USE flags to package.use: %w", err) - } - - g.log(fmt.Sprintf("Set USE flags for %s: %s", packageName, useFlags)) - return nil -} - -func (g *GentooDistribution) syncGURURepo(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - g.log("Enabling GURU repository...") - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.55, - Step: "Enabling GURU repository...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: "sudo eselect repository enable guru", - } - - // Enable GURU repository - enableCmd := exec.CommandContext(ctx, "bash", "-c", - fmt.Sprintf("echo '%s' | sudo -S eselect repository enable guru", sudoPassword)) - output, err := enableCmd.CombinedOutput() - if err != nil { - g.logError("failed to enable GURU repository", err) - g.log(fmt.Sprintf("eselect repository enable output: %s", string(output))) - return fmt.Errorf("failed to enable GURU repository: %w", err) - } - g.log(fmt.Sprintf("GURU repository enabled: %s", string(output))) - - // Sync GURU repository - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.57, - Step: "Syncing GURU repository...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: "sudo emaint sync --repo guru", - } - - g.log("Syncing GURU repository...") - syncCmd := exec.CommandContext(ctx, "bash", "-c", - fmt.Sprintf("echo '%s' | sudo -S emaint sync --repo guru", sudoPassword)) - syncOutput, syncErr := syncCmd.CombinedOutput() - if syncErr != nil { - g.logError("failed to sync GURU repository", syncErr) - g.log(fmt.Sprintf("emaint sync output: %s", string(syncOutput))) - return fmt.Errorf("failed to sync GURU repository: %w", syncErr) - } - g.log(fmt.Sprintf("GURU repository synced: %s", string(syncOutput))) - - return nil -} - -func (g *GentooDistribution) setPackageAcceptKeywords(ctx context.Context, packageName, keywords, sudoPassword string) error { - checkCmd := exec.CommandContext(ctx, "portageq", "match", "/", packageName) - if output, err := checkCmd.CombinedOutput(); err == nil && len(output) > 0 { - g.log(fmt.Sprintf("Package %s is already available (may already be unmasked)", packageName)) - return nil - } - - acceptKeywordsDir := "/etc/portage/package.accept_keywords" - - mkdirCmd := exec.CommandContext(ctx, "bash", "-c", - fmt.Sprintf("echo '%s' | sudo -S mkdir -p %s", sudoPassword, acceptKeywordsDir)) - if output, err := mkdirCmd.CombinedOutput(); err != nil { - g.log(fmt.Sprintf("mkdir output: %s", string(output))) - return fmt.Errorf("failed to create package.accept_keywords directory: %w", err) - } - - keywordLine := fmt.Sprintf("%s %s", packageName, keywords) - - checkExistingCmd := exec.CommandContext(ctx, "bash", "-c", - fmt.Sprintf("grep -q '^%s ' %s/danklinux 2>/dev/null", packageName, acceptKeywordsDir)) - if checkExistingCmd.Run() == nil { - g.log(fmt.Sprintf("Accept keywords already set for %s", packageName)) - return nil - } - - appendCmd := exec.CommandContext(ctx, "bash", "-c", - fmt.Sprintf("echo '%s' | sudo -S bash -c \"echo '%s' >> %s/danklinux\"", sudoPassword, keywordLine, acceptKeywordsDir)) - - output, err := appendCmd.CombinedOutput() - if err != nil { - g.log(fmt.Sprintf("append output: %s", string(output))) - return fmt.Errorf("failed to write accept keywords: %w", err) - } - - g.log(fmt.Sprintf("Set accept keywords for %s: %s", packageName, keywords)) - return nil -} - -func (g *GentooDistribution) installGURUPackages(ctx context.Context, packages []PackageMapping, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - if len(packages) == 0 { - return nil - } - - packageNames := g.extractPackageNames(packages) - g.log(fmt.Sprintf("Installing GURU packages: %s", strings.Join(packageNames, ", "))) - - for _, pkg := range packages { - if pkg.AcceptKeywords != "" { - if err := g.setPackageAcceptKeywords(ctx, pkg.Name, pkg.AcceptKeywords, sudoPassword); err != nil { - return fmt.Errorf("failed to set accept keywords for %s: %w", pkg.Name, err) - } - } - if pkg.UseFlags != "" { - if err := g.setPackageUseFlags(ctx, pkg.Name, pkg.UseFlags, sudoPassword); err != nil { - return fmt.Errorf("failed to set USE flags for %s: %w", pkg.Name, err) - } - } - } - - guruPackages := make([]string, len(packageNames)) - for i, pkg := range packageNames { - guruPackages[i] = pkg + "::guru" - } - - args := []string{"emerge", "--ask=n", "--quiet"} - args = append(args, guruPackages...) - - progressChan <- InstallProgressMsg{ - Phase: PhaseAURPackages, - Progress: 0.70, - Step: "Installing GURU packages...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: fmt.Sprintf("sudo %s", strings.Join(args, " ")), - } - - cmdStr := fmt.Sprintf("echo '%s' | sudo -S %s", sudoPassword, strings.Join(args, " ")) - cmd := exec.CommandContext(ctx, "bash", "-c", cmdStr) - return g.runWithProgress(cmd, progressChan, PhaseAURPackages, 0.70, 0.85) -} diff --git a/nix/inputs/dms-cli/internal/distros/interface.go b/nix/inputs/dms-cli/internal/distros/interface.go deleted file mode 100644 index 133ece1..0000000 --- a/nix/inputs/dms-cli/internal/distros/interface.go +++ /dev/null @@ -1,156 +0,0 @@ -package distros - -import ( - "context" - - "github.com/AvengeMedia/danklinux/internal/deps" -) - -// DistroFamily represents a family of related distributions -type DistroFamily string - -const ( - FamilyArch DistroFamily = "arch" - FamilyFedora DistroFamily = "fedora" - FamilySUSE DistroFamily = "suse" - FamilyUbuntu DistroFamily = "ubuntu" - FamilyDebian DistroFamily = "debian" - FamilyNix DistroFamily = "nix" - FamilyGentoo DistroFamily = "gentoo" -) - -// PackageManagerType defines the package manager a distro uses -type PackageManagerType string - -const ( - PackageManagerPacman PackageManagerType = "pacman" - PackageManagerDNF PackageManagerType = "dnf" - PackageManagerAPT PackageManagerType = "apt" - PackageManagerZypper PackageManagerType = "zypper" - PackageManagerNix PackageManagerType = "nix" - PackageManagerPortage PackageManagerType = "portage" -) - -// RepositoryType defines the type of repository for a package -type RepositoryType string - -const ( - RepoTypeSystem RepositoryType = "system" // Standard system repo (pacman, dnf, apt) - RepoTypeAUR RepositoryType = "aur" // Arch User Repository - RepoTypeCOPR RepositoryType = "copr" // Fedora COPR - RepoTypePPA RepositoryType = "ppa" // Ubuntu PPA - RepoTypeFlake RepositoryType = "flake" // Nix flake - RepoTypeGURU RepositoryType = "guru" // Gentoo GURU - RepoTypeManual RepositoryType = "manual" // Manual build from source -) - -// InstallPhase represents the current phase of installation -type InstallPhase int - -const ( - PhasePrerequisites InstallPhase = iota - PhaseAURHelper - PhaseSystemPackages - PhaseAURPackages - PhaseCursorTheme - PhaseConfiguration - PhaseComplete -) - -// InstallProgressMsg represents progress during package installation -type InstallProgressMsg struct { - Phase InstallPhase - Progress float64 - Step string - IsComplete bool - NeedsSudo bool - CommandInfo string - LogOutput string - Error error -} - -// PackageMapping defines how to install a package on a specific distro -type PackageMapping struct { - Name string // Package name to install - Repository RepositoryType // Repository type - RepoURL string // Repository URL if needed (e.g., COPR repo, PPA) - BuildFunc string // Name of manual build function if RepoTypeManual - UseFlags string // USE flags for Gentoo packages - AcceptKeywords string // Accept keywords for Gentoo packages (e.g., "~amd64") -} - -// Distribution defines a Linux distribution with all its specific configurations -type Distribution interface { - // Metadata - GetID() string - GetColorHex() string - GetFamily() DistroFamily - GetPackageManager() PackageManagerType - - // Dependency Detection - DetectDependencies(ctx context.Context, wm deps.WindowManager) ([]deps.Dependency, error) - DetectDependenciesWithTerminal(ctx context.Context, wm deps.WindowManager, terminal deps.Terminal) ([]deps.Dependency, error) - - // Package Installation - InstallPackages(ctx context.Context, dependencies []deps.Dependency, wm deps.WindowManager, sudoPassword string, reinstallFlags map[string]bool, progressChan chan<- InstallProgressMsg) error - - // Package Mapping - GetPackageMapping(wm deps.WindowManager) map[string]PackageMapping - - // Prerequisites - InstallPrerequisites(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error -} - -// DistroConfig holds configuration for a distribution -type DistroConfig struct { - ID string - ColorHex string - Family DistroFamily - Constructor func(config DistroConfig, logChan chan<- string) Distribution -} - -// Registry holds all supported distributions -var Registry = make(map[string]DistroConfig) - -// Register adds a distribution to the registry -func Register(id, colorHex string, family DistroFamily, constructor func(config DistroConfig, logChan chan<- string) Distribution) { - Registry[id] = DistroConfig{ - ID: id, - ColorHex: colorHex, - Family: family, - Constructor: constructor, - } -} - -// GetSupportedDistros returns a list of all supported distribution IDs -func GetSupportedDistros() []string { - ids := make([]string, 0, len(Registry)) - for id := range Registry { - ids = append(ids, id) - } - return ids -} - -// IsDistroSupported checks if a distribution ID is supported -func IsDistroSupported(id string) bool { - _, exists := Registry[id] - return exists -} - -// NewDistribution creates a distribution instance by ID -func NewDistribution(id string, logChan chan<- string) (Distribution, error) { - config, exists := Registry[id] - if !exists { - return nil, &UnsupportedDistributionError{ID: id} - } - return config.Constructor(config, logChan), nil -} - -// UnsupportedDistributionError is returned when a distribution is not supported -type UnsupportedDistributionError struct { - ID string -} - -func (e *UnsupportedDistributionError) Error() string { - return "unsupported distribution: " + e.ID -} diff --git a/nix/inputs/dms-cli/internal/distros/manual_packages.go b/nix/inputs/dms-cli/internal/distros/manual_packages.go deleted file mode 100644 index 04c2ab4..0000000 --- a/nix/inputs/dms-cli/internal/distros/manual_packages.go +++ /dev/null @@ -1,802 +0,0 @@ -package distros - -import ( - "context" - "fmt" - "os" - "os/exec" - "path/filepath" - "strings" -) - -// ManualPackageInstaller provides methods for installing packages from source -type ManualPackageInstaller struct { - *BaseDistribution -} - -// parseLatestTagFromGitOutput parses git ls-remote output and returns the latest tag -func (m *ManualPackageInstaller) parseLatestTagFromGitOutput(output string) string { - lines := strings.Split(output, "\n") - for _, line := range lines { - if strings.Contains(line, "refs/tags/") && !strings.Contains(line, "^{}") { - parts := strings.Split(line, "refs/tags/") - if len(parts) > 1 { - latestTag := strings.TrimSpace(parts[1]) - return latestTag - } - } - } - return "" -} - -// getLatestQuickshellTag fetches the latest tag from the quickshell repository -func (m *ManualPackageInstaller) getLatestQuickshellTag(ctx context.Context) string { - tagCmd := exec.CommandContext(ctx, "git", "ls-remote", "--tags", "--sort=-v:refname", - "https://github.com/quickshell-mirror/quickshell.git") - tagOutput, err := tagCmd.Output() - if err != nil { - m.log(fmt.Sprintf("Warning: failed to fetch quickshell tags: %v", err)) - return "" - } - - return m.parseLatestTagFromGitOutput(string(tagOutput)) -} - -// InstallManualPackages handles packages that need manual building -func (m *ManualPackageInstaller) InstallManualPackages(ctx context.Context, packages []string, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - if len(packages) == 0 { - return nil - } - - m.log(fmt.Sprintf("Installing manual packages: %s", strings.Join(packages, ", "))) - - for _, pkg := range packages { - switch pkg { - case "dms (DankMaterialShell)", "dms": - if err := m.installDankMaterialShell(ctx, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install DankMaterialShell: %w", err) - } - case "dgop": - if err := m.installDgop(ctx, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install dgop: %w", err) - } - case "grimblast": - if err := m.installGrimblast(ctx, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install grimblast: %w", err) - } - case "niri": - if err := m.installNiri(ctx, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install niri: %w", err) - } - case "quickshell": - if err := m.installQuickshell(ctx, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install quickshell: %w", err) - } - case "hyprland": - if err := m.installHyprland(ctx, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install hyprland: %w", err) - } - case "hyprpicker": - if err := m.installHyprpicker(ctx, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install hyprpicker: %w", err) - } - case "ghostty": - if err := m.installGhostty(ctx, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install ghostty: %w", err) - } - case "matugen": - if err := m.installMatugen(ctx, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install matugen: %w", err) - } - case "cliphist": - if err := m.installCliphist(ctx, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install cliphist: %w", err) - } - case "xwayland-satellite": - if err := m.installXwaylandSatellite(ctx, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install xwayland-satellite: %w", err) - } - default: - m.log(fmt.Sprintf("Warning: No manual build method for %s", pkg)) - } - } - - return nil -} - -func (m *ManualPackageInstaller) installDgop(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - m.log("Installing dgop from source...") - - homeDir := os.Getenv("HOME") - if homeDir == "" { - return fmt.Errorf("HOME environment variable not set") - } - - cacheDir := filepath.Join(homeDir, ".cache", "dankinstall") - if err := os.MkdirAll(cacheDir, 0755); err != nil { - return fmt.Errorf("failed to create cache directory: %w", err) - } - - tmpDir := filepath.Join(cacheDir, "dgop-build") - if err := os.MkdirAll(tmpDir, 0755); err != nil { - return fmt.Errorf("failed to create temp directory: %w", err) - } - defer os.RemoveAll(tmpDir) - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.1, - Step: "Cloning dgop repository...", - IsComplete: false, - CommandInfo: "git clone https://github.com/AvengeMedia/dgop.git", - } - - cloneCmd := exec.CommandContext(ctx, "git", "clone", "https://github.com/AvengeMedia/dgop.git", tmpDir) - if err := cloneCmd.Run(); err != nil { - m.logError("failed to clone dgop repository", err) - return fmt.Errorf("failed to clone dgop repository: %w", err) - } - - buildCmd := exec.CommandContext(ctx, "make") - buildCmd.Dir = tmpDir - buildCmd.Env = append(os.Environ(), "TMPDIR="+cacheDir) - if err := m.runWithProgressStep(buildCmd, progressChan, PhaseSystemPackages, 0.4, 0.7, "Building dgop..."); err != nil { - return fmt.Errorf("failed to build dgop: %w", err) - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.7, - Step: "Installing dgop...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: "sudo make install", - } - - installCmd := exec.CommandContext(ctx, "bash", "-c", fmt.Sprintf("echo '%s' | sudo -S make install", sudoPassword)) - installCmd.Dir = tmpDir - if err := installCmd.Run(); err != nil { - m.logError("failed to install dgop", err) - return fmt.Errorf("failed to install dgop: %w", err) - } - - m.log("dgop installed successfully from source") - return nil -} - -func (m *ManualPackageInstaller) installGrimblast(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - m.log("Installing grimblast script for Hyprland...") - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.1, - Step: "Downloading grimblast script...", - IsComplete: false, - CommandInfo: "curl grimblast script", - } - - grimblastURL := "https://raw.githubusercontent.com/hyprwm/contrib/refs/heads/main/grimblast/grimblast" - tmpPath := filepath.Join(os.TempDir(), "grimblast") - - downloadCmd := exec.CommandContext(ctx, "curl", "-L", "-o", tmpPath, grimblastURL) - if err := downloadCmd.Run(); err != nil { - m.logError("failed to download grimblast", err) - return fmt.Errorf("failed to download grimblast: %w", err) - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.5, - Step: "Making grimblast executable...", - IsComplete: false, - CommandInfo: "chmod +x grimblast", - } - - chmodCmd := exec.CommandContext(ctx, "chmod", "+x", tmpPath) - if err := chmodCmd.Run(); err != nil { - m.logError("failed to make grimblast executable", err) - return fmt.Errorf("failed to make grimblast executable: %w", err) - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.8, - Step: "Installing grimblast to /usr/local/bin...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: "sudo cp grimblast /usr/local/bin/", - } - - installCmd := exec.CommandContext(ctx, "bash", "-c", - fmt.Sprintf("echo '%s' | sudo -S cp %s /usr/local/bin/grimblast", sudoPassword, tmpPath)) - if err := installCmd.Run(); err != nil { - m.logError("failed to install grimblast", err) - return fmt.Errorf("failed to install grimblast: %w", err) - } - - os.Remove(tmpPath) - - m.log("grimblast installed successfully to /usr/local/bin") - return nil -} - -func (m *ManualPackageInstaller) installNiri(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - m.log("Installing niri from source...") - - homeDir, _ := os.UserHomeDir() - buildDir := filepath.Join(homeDir, ".cache", "dankinstall", "niri-build") - tmpDir := filepath.Join(homeDir, ".cache", "dankinstall", "tmp") - if err := os.MkdirAll(buildDir, 0755); err != nil { - return fmt.Errorf("failed to create build directory: %w", err) - } - if err := os.MkdirAll(tmpDir, 0755); err != nil { - return fmt.Errorf("failed to create temp directory: %w", err) - } - defer func() { - os.RemoveAll(buildDir) - os.RemoveAll(tmpDir) - }() - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.2, - Step: "Cloning niri repository...", - IsComplete: false, - CommandInfo: "git clone https://github.com/YaLTeR/niri.git", - } - - cloneCmd := exec.CommandContext(ctx, "git", "clone", "https://github.com/YaLTeR/niri.git", buildDir) - if err := cloneCmd.Run(); err != nil { - return fmt.Errorf("failed to clone niri: %w", err) - } - - checkoutCmd := exec.CommandContext(ctx, "git", "-C", buildDir, "checkout", "v25.08") - if err := checkoutCmd.Run(); err != nil { - m.log(fmt.Sprintf("Warning: failed to checkout v25.08, using main: %v", err)) - } - - if !m.commandExists("cargo-deb") { - cargoDebInstallCmd := exec.CommandContext(ctx, "cargo", "install", "cargo-deb") - cargoDebInstallCmd.Env = append(os.Environ(), "TMPDIR="+tmpDir) - if err := m.runWithProgressStep(cargoDebInstallCmd, progressChan, PhaseSystemPackages, 0.3, 0.35, "Installing cargo-deb..."); err != nil { - return fmt.Errorf("failed to install cargo-deb: %w", err) - } - } - - buildDebCmd := exec.CommandContext(ctx, "cargo", "deb") - buildDebCmd.Dir = buildDir - buildDebCmd.Env = append(os.Environ(), "TMPDIR="+tmpDir) - if err := m.runWithProgressStep(buildDebCmd, progressChan, PhaseSystemPackages, 0.35, 0.95, "Building niri deb package..."); err != nil { - return fmt.Errorf("failed to build niri deb: %w", err) - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.95, - Step: "Installing niri deb package...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: "dpkg -i niri.deb", - } - - installDebCmd := exec.CommandContext(ctx, "bash", "-c", - fmt.Sprintf("echo '%s' | sudo -S dpkg -i %s/target/debian/niri_*.deb", sudoPassword, buildDir)) - - output, err := installDebCmd.CombinedOutput() - if err != nil { - m.log(fmt.Sprintf("dpkg install failed. Output:\n%s", string(output))) - return fmt.Errorf("failed to install niri deb package: %w\nOutput:\n%s", err, string(output)) - } - - m.log(fmt.Sprintf("dpkg install successful. Output:\n%s", string(output))) - - m.log("niri installed successfully from source") - return nil -} - -func (m *ManualPackageInstaller) installQuickshell(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - m.log("Installing quickshell from source...") - - homeDir := os.Getenv("HOME") - if homeDir == "" { - return fmt.Errorf("HOME environment variable not set") - } - - cacheDir := filepath.Join(homeDir, ".cache", "dankinstall") - if err := os.MkdirAll(cacheDir, 0755); err != nil { - return fmt.Errorf("failed to create cache directory: %w", err) - } - - tmpDir := filepath.Join(cacheDir, "quickshell-build") - if err := os.MkdirAll(tmpDir, 0755); err != nil { - return fmt.Errorf("failed to create temp directory: %w", err) - } - defer os.RemoveAll(tmpDir) - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.1, - Step: "Cloning quickshell repository...", - IsComplete: false, - CommandInfo: "git clone https://github.com/quickshell-mirror/quickshell.git", - } - - var cloneCmd *exec.Cmd - if forceQuickshellGit { - cloneCmd = exec.CommandContext(ctx, "git", "clone", "https://github.com/quickshell-mirror/quickshell.git", tmpDir) - } else { - // Get latest tag from repository - latestTag := m.getLatestQuickshellTag(ctx) - if latestTag != "" { - m.log(fmt.Sprintf("Using latest quickshell tag: %s", latestTag)) - cloneCmd = exec.CommandContext(ctx, "git", "clone", "--branch", latestTag, "https://github.com/quickshell-mirror/quickshell.git", tmpDir) - } else { - m.log("Warning: failed to fetch latest tag, using default branch") - cloneCmd = exec.CommandContext(ctx, "git", "clone", "https://github.com/quickshell-mirror/quickshell.git", tmpDir) - } - } - if err := cloneCmd.Run(); err != nil { - return fmt.Errorf("failed to clone quickshell: %w", err) - } - - buildDir := tmpDir + "/build" - if err := os.MkdirAll(buildDir, 0755); err != nil { - return fmt.Errorf("failed to create build directory: %w", err) - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.3, - Step: "Configuring quickshell build...", - IsComplete: false, - CommandInfo: "cmake -B build -S . -G Ninja", - } - - configureCmd := exec.CommandContext(ctx, "cmake", "-GNinja", "-B", "build", - "-DCMAKE_BUILD_TYPE=RelWithDebInfo", - "-DCRASH_REPORTER=off", - "-DCMAKE_CXX_STANDARD=20") - configureCmd.Dir = tmpDir - configureCmd.Env = append(os.Environ(), "TMPDIR="+cacheDir) - - output, err := configureCmd.CombinedOutput() - if err != nil { - m.log(fmt.Sprintf("cmake configure failed. Output:\n%s", string(output))) - return fmt.Errorf("failed to configure quickshell: %w\nCMake output:\n%s", err, string(output)) - } - - m.log(fmt.Sprintf("cmake configure successful. Output:\n%s", string(output))) - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.4, - Step: "Building quickshell (this may take a while)...", - IsComplete: false, - CommandInfo: "cmake --build build", - } - - buildCmd := exec.CommandContext(ctx, "cmake", "--build", "build") - buildCmd.Dir = tmpDir - buildCmd.Env = append(os.Environ(), "TMPDIR="+cacheDir) - if err := m.runWithProgressStep(buildCmd, progressChan, PhaseSystemPackages, 0.4, 0.8, "Building quickshell..."); err != nil { - return fmt.Errorf("failed to build quickshell: %w", err) - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.8, - Step: "Installing quickshell...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: "sudo cmake --install build", - } - - installCmd := exec.CommandContext(ctx, "bash", "-c", - fmt.Sprintf("cd %s && echo '%s' | sudo -S cmake --install build", tmpDir, sudoPassword)) - if err := installCmd.Run(); err != nil { - return fmt.Errorf("failed to install quickshell: %w", err) - } - - m.log("quickshell installed successfully from source") - return nil -} - -func (m *ManualPackageInstaller) installHyprland(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - m.log("Installing Hyprland from source...") - - homeDir := os.Getenv("HOME") - if homeDir == "" { - return fmt.Errorf("HOME environment variable not set") - } - - cacheDir := filepath.Join(homeDir, ".cache", "dankinstall") - if err := os.MkdirAll(cacheDir, 0755); err != nil { - return fmt.Errorf("failed to create cache directory: %w", err) - } - - tmpDir := filepath.Join(cacheDir, "hyprland-build") - if err := os.MkdirAll(tmpDir, 0755); err != nil { - return fmt.Errorf("failed to create temp directory: %w", err) - } - defer os.RemoveAll(tmpDir) - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.1, - Step: "Cloning Hyprland repository...", - IsComplete: false, - CommandInfo: "git clone --recursive https://github.com/hyprwm/Hyprland.git", - } - - cloneCmd := exec.CommandContext(ctx, "git", "clone", "--recursive", "https://github.com/hyprwm/Hyprland.git", tmpDir) - if err := cloneCmd.Run(); err != nil { - return fmt.Errorf("failed to clone Hyprland: %w", err) - } - - checkoutCmd := exec.CommandContext(ctx, "git", "-C", tmpDir, "checkout", "v0.50.1") - if err := checkoutCmd.Run(); err != nil { - m.log(fmt.Sprintf("Warning: failed to checkout v0.50.1, using main: %v", err)) - } - - buildCmd := exec.CommandContext(ctx, "make", "all") - buildCmd.Dir = tmpDir - buildCmd.Env = append(os.Environ(), "TMPDIR="+cacheDir) - if err := m.runWithProgressStep(buildCmd, progressChan, PhaseSystemPackages, 0.2, 0.8, "Building Hyprland..."); err != nil { - return fmt.Errorf("failed to build Hyprland: %w", err) - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.8, - Step: "Installing Hyprland...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: "sudo make install", - } - - installCmd := exec.CommandContext(ctx, "bash", "-c", - fmt.Sprintf("cd %s && echo '%s' | sudo -S make install", tmpDir, sudoPassword)) - if err := installCmd.Run(); err != nil { - return fmt.Errorf("failed to install Hyprland: %w", err) - } - - m.log("Hyprland installed successfully from source") - return nil -} - -func (m *ManualPackageInstaller) installHyprpicker(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - m.log("Installing hyprpicker from source...") - - homeDir := os.Getenv("HOME") - if homeDir == "" { - return fmt.Errorf("HOME environment variable not set") - } - - cacheDir := filepath.Join(homeDir, ".cache", "dankinstall") - if err := os.MkdirAll(cacheDir, 0755); err != nil { - return fmt.Errorf("failed to create cache directory: %w", err) - } - - tmpDir := filepath.Join(cacheDir, "hyprpicker-build") - if err := os.MkdirAll(tmpDir, 0755); err != nil { - return fmt.Errorf("failed to create temp directory: %w", err) - } - defer os.RemoveAll(tmpDir) - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.2, - Step: "Cloning hyprpicker repository...", - IsComplete: false, - CommandInfo: "git clone https://github.com/hyprwm/hyprpicker.git", - } - - cloneCmd := exec.CommandContext(ctx, "git", "clone", "https://github.com/hyprwm/hyprpicker.git", tmpDir) - if err := cloneCmd.Run(); err != nil { - return fmt.Errorf("failed to clone hyprpicker: %w", err) - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.4, - Step: "Building hyprpicker...", - IsComplete: false, - CommandInfo: "make all", - } - - buildCmd := exec.CommandContext(ctx, "make", "all") - buildCmd.Dir = tmpDir - buildCmd.Env = append(os.Environ(), "TMPDIR="+cacheDir) - if err := buildCmd.Run(); err != nil { - return fmt.Errorf("failed to build hyprpicker: %w", err) - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.8, - Step: "Installing hyprpicker...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: "sudo make install", - } - - installCmd := exec.CommandContext(ctx, "bash", "-c", - fmt.Sprintf("cd %s && echo '%s' | sudo -S make install", tmpDir, sudoPassword)) - if err := installCmd.Run(); err != nil { - return fmt.Errorf("failed to install hyprpicker: %w", err) - } - - m.log("hyprpicker installed successfully from source") - return nil -} - -func (m *ManualPackageInstaller) installGhostty(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - m.log("Installing Ghostty from source...") - - homeDir := os.Getenv("HOME") - if homeDir == "" { - return fmt.Errorf("HOME environment variable not set") - } - - cacheDir := filepath.Join(homeDir, ".cache", "dankinstall") - if err := os.MkdirAll(cacheDir, 0755); err != nil { - return fmt.Errorf("failed to create cache directory: %w", err) - } - - tmpDir := filepath.Join(cacheDir, "ghostty-build") - if err := os.MkdirAll(tmpDir, 0755); err != nil { - return fmt.Errorf("failed to create temp directory: %w", err) - } - defer os.RemoveAll(tmpDir) - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.1, - Step: "Cloning Ghostty repository...", - IsComplete: false, - CommandInfo: "git clone https://github.com/ghostty-org/ghostty.git", - } - - cloneCmd := exec.CommandContext(ctx, "git", "clone", "https://github.com/ghostty-org/ghostty.git", tmpDir) - if err := cloneCmd.Run(); err != nil { - return fmt.Errorf("failed to clone Ghostty: %w", err) - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.2, - Step: "Building Ghostty (this may take a while)...", - IsComplete: false, - CommandInfo: "zig build -Doptimize=ReleaseFast", - } - - buildCmd := exec.CommandContext(ctx, "zig", "build", "-Doptimize=ReleaseFast") - buildCmd.Dir = tmpDir - buildCmd.Env = append(os.Environ(), "TMPDIR="+cacheDir) - if err := buildCmd.Run(); err != nil { - return fmt.Errorf("failed to build Ghostty: %w", err) - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.8, - Step: "Installing Ghostty...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: "sudo cp zig-out/bin/ghostty /usr/local/bin/", - } - - installCmd := exec.CommandContext(ctx, "bash", "-c", - fmt.Sprintf("echo '%s' | sudo -S cp %s/zig-out/bin/ghostty /usr/local/bin/", sudoPassword, tmpDir)) - if err := installCmd.Run(); err != nil { - return fmt.Errorf("failed to install Ghostty: %w", err) - } - - m.log("Ghostty installed successfully from source") - return nil -} - -func (m *ManualPackageInstaller) installMatugen(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - m.log("Installing matugen from source...") - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.1, - Step: "Installing matugen via cargo...", - IsComplete: false, - CommandInfo: "cargo install matugen", - } - - installCmd := exec.CommandContext(ctx, "cargo", "install", "matugen") - if err := m.runWithProgressStep(installCmd, progressChan, PhaseSystemPackages, 0.1, 0.7, "Building matugen..."); err != nil { - return fmt.Errorf("failed to install matugen: %w", err) - } - - homeDir := os.Getenv("HOME") - sourcePath := filepath.Join(homeDir, ".cargo", "bin", "matugen") - targetPath := "/usr/local/bin/matugen" - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.7, - Step: "Installing matugen binary to system...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: fmt.Sprintf("sudo cp %s %s", sourcePath, targetPath), - } - - copyCmd := exec.CommandContext(ctx, "sudo", "-S", "cp", sourcePath, targetPath) - copyCmd.Stdin = strings.NewReader(sudoPassword + "\n") - if err := copyCmd.Run(); err != nil { - return fmt.Errorf("failed to copy matugen to /usr/local/bin: %w", err) - } - - // Make it executable - chmodCmd := exec.CommandContext(ctx, "sudo", "-S", "chmod", "+x", targetPath) - chmodCmd.Stdin = strings.NewReader(sudoPassword + "\n") - if err := chmodCmd.Run(); err != nil { - return fmt.Errorf("failed to make matugen executable: %w", err) - } - - m.log("matugen installed successfully from source") - return nil -} - -func (m *ManualPackageInstaller) installDankMaterialShell(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - m.log("Installing DankMaterialShell (DMS)...") - - // Always install/update the DMS binary - if err := m.installDMSBinary(ctx, sudoPassword, progressChan); err != nil { - m.logError("Failed to install DMS binary", err) - } - - // Handle DMS config - clone if missing, pull if exists - dmsPath := filepath.Join(os.Getenv("HOME"), ".config/quickshell/dms") - if _, err := os.Stat(dmsPath); os.IsNotExist(err) { - // Config doesn't exist, clone it - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.90, - Step: "Cloning DankMaterialShell config...", - IsComplete: false, - CommandInfo: "git clone https://github.com/AvengeMedia/DankMaterialShell.git ~/.config/quickshell/dms", - } - - configDir := filepath.Dir(dmsPath) - if err := os.MkdirAll(configDir, 0755); err != nil { - return fmt.Errorf("failed to create quickshell config directory: %w", err) - } - - cloneCmd := exec.CommandContext(ctx, "git", "clone", - "https://github.com/AvengeMedia/DankMaterialShell.git", dmsPath) - if err := cloneCmd.Run(); err != nil { - return fmt.Errorf("failed to clone DankMaterialShell: %w", err) - } - - if !forceDMSGit { - fetchCmd := exec.CommandContext(ctx, "git", "-C", dmsPath, "fetch", "--tags") - if err := fetchCmd.Run(); err == nil { - tagCmd := exec.CommandContext(ctx, "git", "-C", dmsPath, "describe", "--tags", "--abbrev=0", "origin/master") - if tagOutput, err := tagCmd.Output(); err == nil { - latestTag := strings.TrimSpace(string(tagOutput)) - checkoutCmd := exec.CommandContext(ctx, "git", "-C", dmsPath, "checkout", latestTag) - if err := checkoutCmd.Run(); err == nil { - m.log(fmt.Sprintf("Checked out latest tag: %s", latestTag)) - } - } - } - } - - m.log("DankMaterialShell config cloned successfully") - } else { - // Config exists, update it - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.90, - Step: "Updating DankMaterialShell config...", - IsComplete: false, - CommandInfo: "git pull in ~/.config/quickshell/dms", - } - - pullCmd := exec.CommandContext(ctx, "git", "pull") - pullCmd.Dir = dmsPath - if err := pullCmd.Run(); err != nil { - m.logError("Failed to update DankMaterialShell config", err) - } else { - m.log("DankMaterialShell config updated successfully") - } - } - - return nil -} - -func (m *ManualPackageInstaller) installCliphist(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - m.log("Installing cliphist from source...") - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.1, - Step: "Installing cliphist via go install...", - IsComplete: false, - CommandInfo: "go install go.senan.xyz/cliphist@latest", - } - - installCmd := exec.CommandContext(ctx, "go", "install", "go.senan.xyz/cliphist@latest") - if err := m.runWithProgressStep(installCmd, progressChan, PhaseSystemPackages, 0.1, 0.7, "Building cliphist..."); err != nil { - return fmt.Errorf("failed to install cliphist: %w", err) - } - - homeDir := os.Getenv("HOME") - sourcePath := filepath.Join(homeDir, "go", "bin", "cliphist") - targetPath := "/usr/local/bin/cliphist" - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.7, - Step: "Installing cliphist binary to system...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: fmt.Sprintf("sudo cp %s %s", sourcePath, targetPath), - } - - copyCmd := exec.CommandContext(ctx, "sudo", "-S", "cp", sourcePath, targetPath) - copyCmd.Stdin = strings.NewReader(sudoPassword + "\n") - if err := copyCmd.Run(); err != nil { - return fmt.Errorf("failed to copy cliphist to /usr/local/bin: %w", err) - } - - // Make it executable - chmodCmd := exec.CommandContext(ctx, "sudo", "-S", "chmod", "+x", targetPath) - chmodCmd.Stdin = strings.NewReader(sudoPassword + "\n") - if err := chmodCmd.Run(); err != nil { - return fmt.Errorf("failed to make cliphist executable: %w", err) - } - - m.log("cliphist installed successfully from source") - return nil -} - -func (m *ManualPackageInstaller) installXwaylandSatellite(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - m.log("Installing xwayland-satellite from source...") - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.1, - Step: "Installing xwayland-satellite via cargo...", - IsComplete: false, - CommandInfo: "cargo install --git https://github.com/Supreeeme/xwayland-satellite --tag v0.7", - } - - installCmd := exec.CommandContext(ctx, "cargo", "install", "--git", "https://github.com/Supreeeme/xwayland-satellite", "--tag", "v0.7") - if err := m.runWithProgressStep(installCmd, progressChan, PhaseSystemPackages, 0.1, 0.7, "Building xwayland-satellite..."); err != nil { - return fmt.Errorf("failed to install xwayland-satellite: %w", err) - } - - homeDir := os.Getenv("HOME") - sourcePath := filepath.Join(homeDir, ".cargo", "bin", "xwayland-satellite") - targetPath := "/usr/local/bin/xwayland-satellite" - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.7, - Step: "Installing xwayland-satellite binary to system...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: fmt.Sprintf("sudo cp %s %s", sourcePath, targetPath), - } - - copyCmd := exec.CommandContext(ctx, "sudo", "-S", "cp", sourcePath, targetPath) - copyCmd.Stdin = strings.NewReader(sudoPassword + "\n") - if err := copyCmd.Run(); err != nil { - return fmt.Errorf("failed to copy xwayland-satellite to /usr/local/bin: %w", err) - } - - chmodCmd := exec.CommandContext(ctx, "sudo", "-S", "chmod", "+x", targetPath) - chmodCmd.Stdin = strings.NewReader(sudoPassword + "\n") - if err := chmodCmd.Run(); err != nil { - return fmt.Errorf("failed to make xwayland-satellite executable: %w", err) - } - - m.log("xwayland-satellite installed successfully from source") - return nil -} diff --git a/nix/inputs/dms-cli/internal/distros/manual_packages_test.go b/nix/inputs/dms-cli/internal/distros/manual_packages_test.go deleted file mode 100644 index 4d9fc62..0000000 --- a/nix/inputs/dms-cli/internal/distros/manual_packages_test.go +++ /dev/null @@ -1,122 +0,0 @@ -package distros - -import ( - "testing" -) - -func TestManualPackageInstaller_parseLatestTagFromGitOutput(t *testing.T) { - tests := []struct { - name string - input string - expected string - }{ - { - name: "normal tag output", - input: `a1a150fab00a93ea983aaca5df55304bc837f51b refs/tags/v0.2.1 -a5431dd02dc23d9ef1680e67777fed00fe5f7cda refs/tags/v0.2.0 -703a3789083d2f990c4e99cd25c97c2a4cccbd81 refs/tags/v0.1.0`, - expected: "v0.2.1", - }, - { - name: "annotated tags with ^{}", - input: `a1a150fab00a93ea983aaca5df55304bc837f51b refs/tags/v0.2.1 -b1b150fab00a93ea983aaca5df55304bc837f51c refs/tags/v0.2.1^{} -a5431dd02dc23d9ef1680e67777fed00fe5f7cda refs/tags/v0.2.0`, - expected: "v0.2.1", - }, - { - name: "mixed tags", - input: `a1a150fab00a93ea983aaca5df55304bc837f51b refs/tags/v0.3.0 -b1b150fab00a93ea983aaca5df55304bc837f51c refs/tags/v0.3.0^{} -a5431dd02dc23d9ef1680e67777fed00fe5f7cda refs/tags/v0.2.0 -c1c150fab00a93ea983aaca5df55304bc837f51d refs/tags/beta-1`, - expected: "v0.3.0", - }, - { - name: "empty output", - input: "", - expected: "", - }, - { - name: "no tags", - input: "some other output\nwithout tags", - expected: "", - }, - { - name: "only annotated tags", - input: `a1a150fab00a93ea983aaca5df55304bc837f51b refs/tags/v0.2.1^{} -a5431dd02dc23d9ef1680e67777fed00fe5f7cda refs/tags/v0.2.0^{}`, - expected: "", - }, - { - name: "single tag", - input: `a1a150fab00a93ea983aaca5df55304bc837f51b refs/tags/v1.0.0`, - expected: "v1.0.0", - }, - { - name: "tag with extra whitespace", - input: `a1a150fab00a93ea983aaca5df55304bc837f51b refs/tags/v0.2.1 -a5431dd02dc23d9ef1680e67777fed00fe5f7cda refs/tags/v0.2.0`, - expected: "v0.2.1", - }, - { - name: "beta and rc tags", - input: `a1a150fab00a93ea983aaca5df55304bc837f51b refs/tags/v0.3.0-beta.1 -a5431dd02dc23d9ef1680e67777fed00fe5f7cda refs/tags/v0.2.0`, - expected: "v0.3.0-beta.1", - }, - { - name: "tags without v prefix", - input: `a1a150fab00a93ea983aaca5df55304bc837f51b refs/tags/0.2.1 -a5431dd02dc23d9ef1680e67777fed00fe5f7cda refs/tags/0.2.0`, - expected: "0.2.1", - }, - { - name: "multiple lines with spaces", - input: ` -a1a150fab00a93ea983aaca5df55304bc837f51b refs/tags/v1.2.3 -a5431dd02dc23d9ef1680e67777fed00fe5f7cda refs/tags/v1.2.2 -`, - expected: "v1.2.3", - }, - { - name: "tag at end of line", - input: `a1a150fab00a93ea983aaca5df55304bc837f51b refs/tags/v0.2.1`, - expected: "v0.2.1", - }, - } - - logChan := make(chan string, 100) - defer close(logChan) - - base := NewBaseDistribution(logChan) - installer := &ManualPackageInstaller{BaseDistribution: base} - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := installer.parseLatestTagFromGitOutput(tt.input) - - if result != tt.expected { - t.Errorf("parseLatestTagFromGitOutput() = %q, expected %q", result, tt.expected) - } - }) - } -} - -func TestManualPackageInstaller_parseLatestTagFromGitOutput_EmptyInstaller(t *testing.T) { - // Test that parsing works even with a minimal installer setup - logChan := make(chan string, 10) - defer close(logChan) - - base := NewBaseDistribution(logChan) - installer := &ManualPackageInstaller{BaseDistribution: base} - - input := `abc123 refs/tags/v1.0.0 -def456 refs/tags/v0.9.0` - - result := installer.parseLatestTagFromGitOutput(input) - - if result != "v1.0.0" { - t.Errorf("Expected v1.0.0, got %s", result) - } -} diff --git a/nix/inputs/dms-cli/internal/distros/nixos.go b/nix/inputs/dms-cli/internal/distros/nixos.go deleted file mode 100644 index 68561a9..0000000 --- a/nix/inputs/dms-cli/internal/distros/nixos.go +++ /dev/null @@ -1,458 +0,0 @@ -package distros - -import ( - "context" - "fmt" - "os/exec" - "strings" - - "github.com/AvengeMedia/danklinux/internal/deps" -) - -func init() { - Register("nixos", "#7EBAE4", FamilyNix, func(config DistroConfig, logChan chan<- string) Distribution { - return NewNixOSDistribution(config, logChan) - }) -} - -type NixOSDistribution struct { - *BaseDistribution - config DistroConfig -} - -func NewNixOSDistribution(config DistroConfig, logChan chan<- string) *NixOSDistribution { - base := NewBaseDistribution(logChan) - return &NixOSDistribution{ - BaseDistribution: base, - config: config, - } -} - -func (n *NixOSDistribution) GetID() string { - return n.config.ID -} - -func (n *NixOSDistribution) GetColorHex() string { - return n.config.ColorHex -} - -func (n *NixOSDistribution) GetFamily() DistroFamily { - return n.config.Family -} - -func (n *NixOSDistribution) GetPackageManager() PackageManagerType { - return PackageManagerNix -} - -func (n *NixOSDistribution) DetectDependencies(ctx context.Context, wm deps.WindowManager) ([]deps.Dependency, error) { - return n.DetectDependenciesWithTerminal(ctx, wm, deps.TerminalGhostty) -} - -func (n *NixOSDistribution) DetectDependenciesWithTerminal(ctx context.Context, wm deps.WindowManager, terminal deps.Terminal) ([]deps.Dependency, error) { - var dependencies []deps.Dependency - - // DMS at the top (shell is prominent) - dependencies = append(dependencies, n.detectDMS()) - - // Terminal with choice support - dependencies = append(dependencies, n.detectSpecificTerminal(terminal)) - - // Common detections using base methods - dependencies = append(dependencies, n.detectGit()) - dependencies = append(dependencies, n.detectWindowManager(wm)) - dependencies = append(dependencies, n.detectQuickshell()) - dependencies = append(dependencies, n.detectXDGPortal()) - dependencies = append(dependencies, n.detectPolkitAgent()) - dependencies = append(dependencies, n.detectAccountsService()) - - // Hyprland-specific tools - if wm == deps.WindowManagerHyprland { - dependencies = append(dependencies, n.detectHyprlandTools()...) - } - - // Niri-specific tools - if wm == deps.WindowManagerNiri { - dependencies = append(dependencies, n.detectXwaylandSatellite()) - } - - // Base detections (common across distros) - dependencies = append(dependencies, n.detectMatugen()) - dependencies = append(dependencies, n.detectDgop()) - dependencies = append(dependencies, n.detectHyprpicker()) - dependencies = append(dependencies, n.detectClipboardTools()...) - - return dependencies, nil -} - -func (n *NixOSDistribution) detectDMS() deps.Dependency { - status := deps.StatusMissing - - // For NixOS, check if quickshell can find the dms config - cmd := exec.Command("qs", "-c", "dms", "--list") - if err := cmd.Run(); err == nil { - status = deps.StatusInstalled - } else if n.packageInstalled("DankMaterialShell") { - // Fallback: check if flake is in profile - status = deps.StatusInstalled - } - - return deps.Dependency{ - Name: "dms (DankMaterialShell)", - Status: status, - Description: "Desktop Management System configuration (installed as flake)", - Required: true, - } -} - -func (n *NixOSDistribution) detectXDGPortal() deps.Dependency { - status := deps.StatusMissing - if n.packageInstalled("xdg-desktop-portal-gtk") { - status = deps.StatusInstalled - } - - return deps.Dependency{ - Name: "xdg-desktop-portal-gtk", - Status: status, - Description: "Desktop integration portal for GTK", - Required: true, - } -} - -func (n *NixOSDistribution) detectWindowManager(wm deps.WindowManager) deps.Dependency { - switch wm { - case deps.WindowManagerHyprland: - status := deps.StatusMissing - description := "Dynamic tiling Wayland compositor" - if n.commandExists("hyprland") || n.commandExists("Hyprland") { - status = deps.StatusInstalled - } else { - description = "Install system-wide: programs.hyprland.enable = true; in configuration.nix" - } - return deps.Dependency{ - Name: "hyprland", - Status: status, - Description: description, - Required: true, - } - case deps.WindowManagerNiri: - status := deps.StatusMissing - description := "Scrollable-tiling Wayland compositor" - if n.commandExists("niri") { - status = deps.StatusInstalled - } else { - description = "Install system-wide: environment.systemPackages = [ pkgs.niri ]; in configuration.nix" - } - return deps.Dependency{ - Name: "niri", - Status: status, - Description: description, - Required: true, - } - default: - return deps.Dependency{ - Name: "unknown-wm", - Status: deps.StatusMissing, - Description: "Unknown window manager", - Required: true, - } - } -} - -func (n *NixOSDistribution) detectHyprlandTools() []deps.Dependency { - var dependencies []deps.Dependency - - tools := []struct { - name string - description string - }{ - {"grim", "Screenshot utility for Wayland"}, - {"slurp", "Region selection utility for Wayland"}, - {"hyprctl", "Hyprland control utility (comes with system Hyprland)"}, - {"hyprpicker", "Color picker for Hyprland"}, - {"grimblast", "Screenshot script for Hyprland"}, - {"jq", "JSON processor"}, - } - - for _, tool := range tools { - status := deps.StatusMissing - - // Special handling for hyprctl - it comes with system hyprland - if tool.name == "hyprctl" { - if n.commandExists("hyprctl") { - status = deps.StatusInstalled - } - } else { - if n.commandExists(tool.name) { - status = deps.StatusInstalled - } - } - - dependencies = append(dependencies, deps.Dependency{ - Name: tool.name, - Status: status, - Description: tool.description, - Required: true, - }) - } - - return dependencies -} - -func (n *NixOSDistribution) detectXwaylandSatellite() deps.Dependency { - status := deps.StatusMissing - if n.commandExists("xwayland-satellite") { - status = deps.StatusInstalled - } - - return deps.Dependency{ - Name: "xwayland-satellite", - Status: status, - Description: "Xwayland support", - Required: true, - } -} - -func (n *NixOSDistribution) detectPolkitAgent() deps.Dependency { - status := deps.StatusMissing - if n.packageInstalled("mate-polkit") { - status = deps.StatusInstalled - } - - return deps.Dependency{ - Name: "mate-polkit", - Status: status, - Description: "PolicyKit authentication agent", - Required: true, - } -} - -func (n *NixOSDistribution) detectAccountsService() deps.Dependency { - status := deps.StatusMissing - if n.packageInstalled("accountsservice") { - status = deps.StatusInstalled - } - - return deps.Dependency{ - Name: "accountsservice", - Status: status, - Description: "D-Bus interface for user account query and manipulation", - Required: true, - } -} - -func (n *NixOSDistribution) packageInstalled(pkg string) bool { - cmd := exec.Command("nix", "profile", "list") - output, err := cmd.Output() - if err != nil { - return false - } - return strings.Contains(string(output), pkg) -} - -func (n *NixOSDistribution) GetPackageMapping(wm deps.WindowManager) map[string]PackageMapping { - packages := map[string]PackageMapping{ - "git": {Name: "nixpkgs#git", Repository: RepoTypeSystem}, - "quickshell": {Name: "github:quickshell-mirror/quickshell", Repository: RepoTypeFlake}, - "matugen": {Name: "github:InioX/matugen", Repository: RepoTypeFlake}, - "dgop": {Name: "github:AvengeMedia/dgop", Repository: RepoTypeFlake}, - "dms (DankMaterialShell)": {Name: "github:AvengeMedia/DankMaterialShell", Repository: RepoTypeFlake}, - "ghostty": {Name: "nixpkgs#ghostty", Repository: RepoTypeSystem}, - "alacritty": {Name: "nixpkgs#alacritty", Repository: RepoTypeSystem}, - "cliphist": {Name: "nixpkgs#cliphist", Repository: RepoTypeSystem}, - "wl-clipboard": {Name: "nixpkgs#wl-clipboard", Repository: RepoTypeSystem}, - "xdg-desktop-portal-gtk": {Name: "nixpkgs#xdg-desktop-portal-gtk", Repository: RepoTypeSystem}, - "mate-polkit": {Name: "nixpkgs#mate.mate-polkit", Repository: RepoTypeSystem}, - "accountsservice": {Name: "nixpkgs#accountsservice", Repository: RepoTypeSystem}, - "hyprpicker": {Name: "nixpkgs#hyprpicker", Repository: RepoTypeSystem}, - } - - // Note: Window managers (hyprland/niri) should be installed system-wide on NixOS - // We only install the tools here - switch wm { - case deps.WindowManagerHyprland: - // Skip hyprland itself - should be installed system-wide - packages["grim"] = PackageMapping{Name: "nixpkgs#grim", Repository: RepoTypeSystem} - packages["slurp"] = PackageMapping{Name: "nixpkgs#slurp", Repository: RepoTypeSystem} - packages["grimblast"] = PackageMapping{Name: "github:hyprwm/contrib#grimblast", Repository: RepoTypeFlake} - packages["jq"] = PackageMapping{Name: "nixpkgs#jq", Repository: RepoTypeSystem} - case deps.WindowManagerNiri: - // Skip niri itself - should be installed system-wide - packages["xwayland-satellite"] = PackageMapping{Name: "nixpkgs#xwayland-satellite", Repository: RepoTypeFlake} - } - - return packages -} - -func (n *NixOSDistribution) InstallPrerequisites(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.10, - Step: "NixOS prerequisites ready", - IsComplete: false, - LogOutput: "NixOS package manager is ready to use", - } - return nil -} - -func (n *NixOSDistribution) InstallPackages(ctx context.Context, dependencies []deps.Dependency, wm deps.WindowManager, sudoPassword string, reinstallFlags map[string]bool, progressChan chan<- InstallProgressMsg) error { - // Phase 1: Check Prerequisites - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.05, - Step: "Checking system prerequisites...", - IsComplete: false, - LogOutput: "Starting prerequisite check...", - } - - if err := n.InstallPrerequisites(ctx, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install prerequisites: %w", err) - } - - nixpkgsPkgs, flakePkgs := n.categorizePackages(dependencies, wm, reinstallFlags) - - // Phase 2: Nixpkgs Packages - if len(nixpkgsPkgs) > 0 { - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.35, - Step: fmt.Sprintf("Installing %d packages from nixpkgs...", len(nixpkgsPkgs)), - IsComplete: false, - LogOutput: fmt.Sprintf("Installing nixpkgs packages: %s", strings.Join(nixpkgsPkgs, ", ")), - } - if err := n.installNixpkgsPackages(ctx, nixpkgsPkgs, progressChan); err != nil { - return fmt.Errorf("failed to install nixpkgs packages: %w", err) - } - } - - // Phase 3: Flake Packages - if len(flakePkgs) > 0 { - progressChan <- InstallProgressMsg{ - Phase: PhaseAURPackages, - Progress: 0.65, - Step: fmt.Sprintf("Installing %d packages from flakes...", len(flakePkgs)), - IsComplete: false, - LogOutput: fmt.Sprintf("Installing flake packages: %s", strings.Join(flakePkgs, ", ")), - } - if err := n.installFlakePackages(ctx, flakePkgs, progressChan); err != nil { - return fmt.Errorf("failed to install flake packages: %w", err) - } - } - - // Phase 4: Configuration - progressChan <- InstallProgressMsg{ - Phase: PhaseConfiguration, - Progress: 0.90, - Step: "Configuring system...", - IsComplete: false, - LogOutput: "Starting post-installation configuration...", - } - if err := n.postInstallConfig(progressChan); err != nil { - return fmt.Errorf("failed to configure system: %w", err) - } - - // Phase 5: Complete - progressChan <- InstallProgressMsg{ - Phase: PhaseComplete, - Progress: 1.0, - Step: "Installation complete!", - IsComplete: true, - LogOutput: "All packages installed and configured successfully", - } - - return nil -} - -func (n *NixOSDistribution) categorizePackages(dependencies []deps.Dependency, wm deps.WindowManager, reinstallFlags map[string]bool) ([]string, []string) { - nixpkgsPkgs := []string{} - flakePkgs := []string{} - - packageMap := n.GetPackageMapping(wm) - - for _, dep := range dependencies { - // Skip installed packages unless marked for reinstall - if dep.Status == deps.StatusInstalled && !reinstallFlags[dep.Name] { - continue - } - - pkgInfo, exists := packageMap[dep.Name] - if !exists { - n.log(fmt.Sprintf("Warning: No package mapping found for %s", dep.Name)) - continue - } - - switch pkgInfo.Repository { - case RepoTypeSystem: - nixpkgsPkgs = append(nixpkgsPkgs, pkgInfo.Name) - case RepoTypeFlake: - flakePkgs = append(flakePkgs, pkgInfo.Name) - } - } - - return nixpkgsPkgs, flakePkgs -} - -func (n *NixOSDistribution) installNixpkgsPackages(ctx context.Context, packages []string, progressChan chan<- InstallProgressMsg) error { - if len(packages) == 0 { - return nil - } - - n.log(fmt.Sprintf("Installing nixpkgs packages: %s", strings.Join(packages, ", "))) - - args := []string{"profile", "install"} - args = append(args, packages...) - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.40, - Step: "Installing nixpkgs packages...", - IsComplete: false, - CommandInfo: fmt.Sprintf("nix %s", strings.Join(args, " ")), - } - - cmd := exec.CommandContext(ctx, "nix", args...) - return n.runWithProgress(cmd, progressChan, PhaseSystemPackages, 0.40, 0.60) -} - -func (n *NixOSDistribution) installFlakePackages(ctx context.Context, packages []string, progressChan chan<- InstallProgressMsg) error { - if len(packages) == 0 { - return nil - } - - n.log(fmt.Sprintf("Installing flake packages: %s", strings.Join(packages, ", "))) - - baseProgress := 0.65 - progressStep := 0.20 / float64(len(packages)) - - for i, pkg := range packages { - currentProgress := baseProgress + (float64(i) * progressStep) - - progressChan <- InstallProgressMsg{ - Phase: PhaseAURPackages, - Progress: currentProgress, - Step: fmt.Sprintf("Installing flake package %s (%d/%d)...", pkg, i+1, len(packages)), - IsComplete: false, - CommandInfo: fmt.Sprintf("nix profile install %s", pkg), - } - - cmd := exec.CommandContext(ctx, "nix", "profile", "install", pkg) - if err := n.runWithProgress(cmd, progressChan, PhaseAURPackages, currentProgress, currentProgress+progressStep); err != nil { - return fmt.Errorf("failed to install flake package %s: %w", pkg, err) - } - } - - return nil -} - -func (n *NixOSDistribution) postInstallConfig(progressChan chan<- InstallProgressMsg) error { - // For NixOS, DMS is installed as a flake package, so we skip both the binary installation and git clone - // The flake installation handles both the binary and config files correctly - progressChan <- InstallProgressMsg{ - Phase: PhaseConfiguration, - Progress: 0.95, - Step: "NixOS configuration complete", - IsComplete: false, - LogOutput: "DMS installed via flake - binary and config handled by Nix", - } - - return nil -} diff --git a/nix/inputs/dms-cli/internal/distros/opensuse.go b/nix/inputs/dms-cli/internal/distros/opensuse.go deleted file mode 100644 index 70c91a5..0000000 --- a/nix/inputs/dms-cli/internal/distros/opensuse.go +++ /dev/null @@ -1,608 +0,0 @@ -package distros - -import ( - "context" - "fmt" - "os" - "os/exec" - "path/filepath" - "strings" - - "github.com/AvengeMedia/danklinux/internal/deps" -) - -func init() { - Register("opensuse-tumbleweed", "#73BA25", FamilySUSE, func(config DistroConfig, logChan chan<- string) Distribution { - return NewOpenSUSEDistribution(config, logChan) - }) -} - -type OpenSUSEDistribution struct { - *BaseDistribution - *ManualPackageInstaller - config DistroConfig -} - -func NewOpenSUSEDistribution(config DistroConfig, logChan chan<- string) *OpenSUSEDistribution { - base := NewBaseDistribution(logChan) - return &OpenSUSEDistribution{ - BaseDistribution: base, - ManualPackageInstaller: &ManualPackageInstaller{BaseDistribution: base}, - config: config, - } -} - -func (o *OpenSUSEDistribution) GetID() string { - return o.config.ID -} - -func (o *OpenSUSEDistribution) GetColorHex() string { - return o.config.ColorHex -} - -func (o *OpenSUSEDistribution) GetFamily() DistroFamily { - return o.config.Family -} - -func (o *OpenSUSEDistribution) GetPackageManager() PackageManagerType { - return PackageManagerZypper -} - -func (o *OpenSUSEDistribution) DetectDependencies(ctx context.Context, wm deps.WindowManager) ([]deps.Dependency, error) { - return o.DetectDependenciesWithTerminal(ctx, wm, deps.TerminalGhostty) -} - -func (o *OpenSUSEDistribution) DetectDependenciesWithTerminal(ctx context.Context, wm deps.WindowManager, terminal deps.Terminal) ([]deps.Dependency, error) { - var dependencies []deps.Dependency - - // DMS at the top (shell is prominent) - dependencies = append(dependencies, o.detectDMS()) - - // Terminal with choice support - dependencies = append(dependencies, o.detectSpecificTerminal(terminal)) - - // Common detections using base methods - dependencies = append(dependencies, o.detectGit()) - dependencies = append(dependencies, o.detectWindowManager(wm)) - dependencies = append(dependencies, o.detectQuickshell()) - dependencies = append(dependencies, o.detectXDGPortal()) - dependencies = append(dependencies, o.detectPolkitAgent()) - dependencies = append(dependencies, o.detectAccountsService()) - - // Hyprland-specific tools - if wm == deps.WindowManagerHyprland { - dependencies = append(dependencies, o.detectHyprlandTools()...) - } - - // Niri-specific tools - if wm == deps.WindowManagerNiri { - dependencies = append(dependencies, o.detectXwaylandSatellite()) - } - - // Base detections (common across distros) - dependencies = append(dependencies, o.detectMatugen()) - dependencies = append(dependencies, o.detectDgop()) - dependencies = append(dependencies, o.detectHyprpicker()) - dependencies = append(dependencies, o.detectClipboardTools()...) - - return dependencies, nil -} - -func (o *OpenSUSEDistribution) detectXDGPortal() deps.Dependency { - status := deps.StatusMissing - if o.packageInstalled("xdg-desktop-portal-gtk") { - status = deps.StatusInstalled - } - - return deps.Dependency{ - Name: "xdg-desktop-portal-gtk", - Status: status, - Description: "Desktop integration portal for GTK", - Required: true, - } -} - -func (o *OpenSUSEDistribution) detectPolkitAgent() deps.Dependency { - status := deps.StatusMissing - if o.packageInstalled("mate-polkit") { - status = deps.StatusInstalled - } - - return deps.Dependency{ - Name: "mate-polkit", - Status: status, - Description: "PolicyKit authentication agent", - Required: true, - } -} - -func (o *OpenSUSEDistribution) packageInstalled(pkg string) bool { - cmd := exec.Command("rpm", "-q", pkg) - err := cmd.Run() - return err == nil -} - -func (o *OpenSUSEDistribution) GetPackageMapping(wm deps.WindowManager) map[string]PackageMapping { - return o.GetPackageMappingWithVariants(wm, make(map[string]deps.PackageVariant)) -} - -func (o *OpenSUSEDistribution) GetPackageMappingWithVariants(wm deps.WindowManager, variants map[string]deps.PackageVariant) map[string]PackageMapping { - packages := map[string]PackageMapping{ - // Standard zypper packages - "git": {Name: "git", Repository: RepoTypeSystem}, - "ghostty": {Name: "ghostty", Repository: RepoTypeSystem}, - "kitty": {Name: "kitty", Repository: RepoTypeSystem}, - "alacritty": {Name: "alacritty", Repository: RepoTypeSystem}, - "wl-clipboard": {Name: "wl-clipboard", Repository: RepoTypeSystem}, - "xdg-desktop-portal-gtk": {Name: "xdg-desktop-portal-gtk", Repository: RepoTypeSystem}, - "mate-polkit": {Name: "mate-polkit", Repository: RepoTypeSystem}, - "accountsservice": {Name: "accountsservice", Repository: RepoTypeSystem}, - "cliphist": {Name: "cliphist", Repository: RepoTypeSystem}, - "hyprpicker": {Name: "hyprpicker", Repository: RepoTypeSystem}, - - // Manual builds - "dms (DankMaterialShell)": {Name: "dms", Repository: RepoTypeManual, BuildFunc: "installDankMaterialShell"}, - "dgop": {Name: "dgop", Repository: RepoTypeManual, BuildFunc: "installDgop"}, - "quickshell": {Name: "quickshell", Repository: RepoTypeManual, BuildFunc: "installQuickshell"}, - "matugen": {Name: "matugen", Repository: RepoTypeManual, BuildFunc: "installMatugen"}, - } - - switch wm { - case deps.WindowManagerHyprland: - packages["hyprland"] = PackageMapping{Name: "hyprland", Repository: RepoTypeSystem} - packages["grim"] = PackageMapping{Name: "grim", Repository: RepoTypeSystem} - packages["slurp"] = PackageMapping{Name: "slurp", Repository: RepoTypeSystem} - packages["hyprctl"] = PackageMapping{Name: "hyprland", Repository: RepoTypeSystem} - packages["grimblast"] = PackageMapping{Name: "grimblast", Repository: RepoTypeManual, BuildFunc: "installGrimblast"} - packages["jq"] = PackageMapping{Name: "jq", Repository: RepoTypeSystem} - case deps.WindowManagerNiri: - packages["niri"] = PackageMapping{Name: "niri", Repository: RepoTypeSystem} - packages["xwayland-satellite"] = PackageMapping{Name: "xwayland-satellite", Repository: RepoTypeSystem} - } - - return packages -} - -func (o *OpenSUSEDistribution) detectXwaylandSatellite() deps.Dependency { - status := deps.StatusMissing - if o.commandExists("xwayland-satellite") { - status = deps.StatusInstalled - } - - return deps.Dependency{ - Name: "xwayland-satellite", - Status: status, - Description: "Xwayland support", - Required: true, - } -} - -func (o *OpenSUSEDistribution) detectAccountsService() deps.Dependency { - status := deps.StatusMissing - if o.packageInstalled("accountsservice") { - status = deps.StatusInstalled - } - - return deps.Dependency{ - Name: "accountsservice", - Status: status, - Description: "D-Bus interface for user account query and manipulation", - Required: true, - } -} - -func (o *OpenSUSEDistribution) getPrerequisites() []string { - return []string{ - "make", - "unzip", - "gcc", - "gcc-c++", - "cmake", - "ninja", - "pkgconf-pkg-config", - "git", - "qt6-base-devel", - "qt6-declarative-devel", - "qt6-declarative-private-devel", - "qt6-shadertools", - "qt6-shadertools-devel", - "qt6-wayland-devel", - "qt6-waylandclient-private-devel", - "spirv-tools-devel", - "cli11-devel", - "wayland-protocols-devel", - "libgbm-devel", - "libdrm-devel", - "pipewire-devel", - "jemalloc-devel", - "wayland-utils", - "Mesa-libGLESv3-devel", - "pam-devel", - "glib2-devel", - "polkit-devel", - } -} - -func (o *OpenSUSEDistribution) InstallPrerequisites(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - prerequisites := o.getPrerequisites() - var missingPkgs []string - - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.06, - Step: "Checking prerequisites...", - IsComplete: false, - LogOutput: "Checking prerequisite packages", - } - - for _, pkg := range prerequisites { - checkCmd := exec.CommandContext(ctx, "rpm", "-q", pkg) - if err := checkCmd.Run(); err != nil { - missingPkgs = append(missingPkgs, pkg) - } - } - - _, err := exec.LookPath("go") - if err != nil { - o.log("go not found in PATH, will install go") - missingPkgs = append(missingPkgs, "go") - } else { - o.log("go already available in PATH") - } - - if len(missingPkgs) == 0 { - o.log("All prerequisites already installed") - return nil - } - - o.log(fmt.Sprintf("Installing prerequisites: %s", strings.Join(missingPkgs, ", "))) - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.08, - Step: fmt.Sprintf("Installing %d prerequisites...", len(missingPkgs)), - IsComplete: false, - NeedsSudo: true, - CommandInfo: fmt.Sprintf("sudo zypper install -y %s", strings.Join(missingPkgs, " ")), - LogOutput: fmt.Sprintf("Installing prerequisites: %s", strings.Join(missingPkgs, ", ")), - } - - args := []string{"zypper", "install", "-y"} - args = append(args, missingPkgs...) - cmdStr := fmt.Sprintf("echo '%s' | sudo -S %s", sudoPassword, strings.Join(args, " ")) - cmd := exec.CommandContext(ctx, "bash", "-c", cmdStr) - output, err := cmd.CombinedOutput() - if err != nil { - o.logError("failed to install prerequisites", err) - o.log(fmt.Sprintf("Prerequisites command output: %s", string(output))) - return fmt.Errorf("failed to install prerequisites: %w", err) - } - o.log(fmt.Sprintf("Prerequisites install output: %s", string(output))) - - return nil -} - -func (o *OpenSUSEDistribution) InstallPackages(ctx context.Context, dependencies []deps.Dependency, wm deps.WindowManager, sudoPassword string, reinstallFlags map[string]bool, progressChan chan<- InstallProgressMsg) error { - // Phase 1: Check Prerequisites - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.05, - Step: "Checking system prerequisites...", - IsComplete: false, - LogOutput: "Starting prerequisite check...", - } - - if err := o.InstallPrerequisites(ctx, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install prerequisites: %w", err) - } - - systemPkgs, manualPkgs := o.categorizePackages(dependencies, wm, reinstallFlags) - - // Phase 2: System Packages (Zypper) - if len(systemPkgs) > 0 { - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.35, - Step: fmt.Sprintf("Installing %d system packages...", len(systemPkgs)), - IsComplete: false, - NeedsSudo: true, - LogOutput: fmt.Sprintf("Installing system packages: %s", strings.Join(systemPkgs, ", ")), - } - if err := o.installZypperPackages(ctx, systemPkgs, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install zypper packages: %w", err) - } - } - - // Phase 3: Manual Builds - if len(manualPkgs) > 0 { - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.85, - Step: fmt.Sprintf("Building %d packages from source...", len(manualPkgs)), - IsComplete: false, - LogOutput: fmt.Sprintf("Building from source: %s", strings.Join(manualPkgs, ", ")), - } - if err := o.InstallManualPackages(ctx, manualPkgs, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install manual packages: %w", err) - } - } - - // Phase 4: Configuration - progressChan <- InstallProgressMsg{ - Phase: PhaseConfiguration, - Progress: 0.90, - Step: "Configuring system...", - IsComplete: false, - LogOutput: "Starting post-installation configuration...", - } - - // Phase 5: Complete - progressChan <- InstallProgressMsg{ - Phase: PhaseComplete, - Progress: 1.0, - Step: "Installation complete!", - IsComplete: true, - LogOutput: "All packages installed and configured successfully", - } - - return nil -} - -func (o *OpenSUSEDistribution) categorizePackages(dependencies []deps.Dependency, wm deps.WindowManager, reinstallFlags map[string]bool) ([]string, []string) { - systemPkgs := []string{} - manualPkgs := []string{} - - variantMap := make(map[string]deps.PackageVariant) - for _, dep := range dependencies { - variantMap[dep.Name] = dep.Variant - } - - packageMap := o.GetPackageMappingWithVariants(wm, variantMap) - - for _, dep := range dependencies { - // Skip installed packages unless marked for reinstall - if dep.Status == deps.StatusInstalled && !reinstallFlags[dep.Name] { - continue - } - - pkgInfo, exists := packageMap[dep.Name] - if !exists { - o.log(fmt.Sprintf("Warning: No package mapping for %s", dep.Name)) - continue - } - - switch pkgInfo.Repository { - case RepoTypeSystem: - systemPkgs = append(systemPkgs, pkgInfo.Name) - case RepoTypeManual: - manualPkgs = append(manualPkgs, dep.Name) - } - } - - return systemPkgs, manualPkgs -} - -func (o *OpenSUSEDistribution) installZypperPackages(ctx context.Context, packages []string, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - if len(packages) == 0 { - return nil - } - - o.log(fmt.Sprintf("Installing zypper packages: %s", strings.Join(packages, ", "))) - - args := []string{"zypper", "install", "-y"} - args = append(args, packages...) - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.40, - Step: "Installing system packages...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: fmt.Sprintf("sudo %s", strings.Join(args, " ")), - } - - cmdStr := fmt.Sprintf("echo '%s' | sudo -S %s", sudoPassword, strings.Join(args, " ")) - cmd := exec.CommandContext(ctx, "bash", "-c", cmdStr) - return o.runWithProgress(cmd, progressChan, PhaseSystemPackages, 0.40, 0.60) -} - -// installQuickshell overrides the base implementation to set openSUSE-specific CFLAGS -func (o *OpenSUSEDistribution) installQuickshell(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - o.log("Installing quickshell from source (with openSUSE-specific build flags)...") - - homeDir := os.Getenv("HOME") - if homeDir == "" { - return fmt.Errorf("HOME environment variable not set") - } - - cacheDir := filepath.Join(homeDir, ".cache", "dankinstall") - if err := os.MkdirAll(cacheDir, 0755); err != nil { - return fmt.Errorf("failed to create cache directory: %w", err) - } - - tmpDir := filepath.Join(cacheDir, "quickshell-build") - if err := os.MkdirAll(tmpDir, 0755); err != nil { - return fmt.Errorf("failed to create temp directory: %w", err) - } - defer os.RemoveAll(tmpDir) - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.1, - Step: "Cloning quickshell repository...", - IsComplete: false, - CommandInfo: "git clone https://github.com/quickshell-mirror/quickshell.git", - } - - var cloneCmd *exec.Cmd - if forceQuickshellGit { - cloneCmd = exec.CommandContext(ctx, "git", "clone", "https://github.com/quickshell-mirror/quickshell.git", tmpDir) - } else { - // Get latest tag from repository - latestTag := o.getLatestQuickshellTag(ctx) - if latestTag != "" { - o.log(fmt.Sprintf("Using latest quickshell tag: %s", latestTag)) - cloneCmd = exec.CommandContext(ctx, "git", "clone", "--branch", latestTag, "https://github.com/quickshell-mirror/quickshell.git", tmpDir) - } else { - o.log("Warning: failed to fetch latest tag, using default branch") - cloneCmd = exec.CommandContext(ctx, "git", "clone", "https://github.com/quickshell-mirror/quickshell.git", tmpDir) - } - } - if err := cloneCmd.Run(); err != nil { - return fmt.Errorf("failed to clone quickshell: %w", err) - } - - buildDir := tmpDir + "/build" - if err := os.MkdirAll(buildDir, 0755); err != nil { - return fmt.Errorf("failed to create build directory: %w", err) - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.3, - Step: "Configuring quickshell build (with openSUSE flags)...", - IsComplete: false, - CommandInfo: "cmake -B build -S . -G Ninja", - } - - // Get optflags from rpm - optflagsCmd := exec.CommandContext(ctx, "rpm", "--eval", "%{optflags}") - optflagsOutput, err := optflagsCmd.Output() - optflags := strings.TrimSpace(string(optflagsOutput)) - if err != nil || optflags == "" { - o.log("Warning: Could not get optflags from rpm, using default -O2 -g") - optflags = "-O2 -g" - } - - // Set openSUSE-specific CFLAGS - customCFLAGS := fmt.Sprintf("%s -I/usr/include/wayland", optflags) - - configureCmd := exec.CommandContext(ctx, "cmake", "-GNinja", "-B", "build", - "-DCMAKE_BUILD_TYPE=RelWithDebInfo", - "-DCRASH_REPORTER=off", - "-DCMAKE_CXX_STANDARD=20") - configureCmd.Dir = tmpDir - configureCmd.Env = append(os.Environ(), - "TMPDIR="+cacheDir, - "CFLAGS="+customCFLAGS, - "CXXFLAGS="+customCFLAGS) - - o.log(fmt.Sprintf("Using CFLAGS: %s", customCFLAGS)) - - output, err := configureCmd.CombinedOutput() - if err != nil { - o.log(fmt.Sprintf("cmake configure failed. Output:\n%s", string(output))) - return fmt.Errorf("failed to configure quickshell: %w\nCMake output:\n%s", err, string(output)) - } - - o.log(fmt.Sprintf("cmake configure successful. Output:\n%s", string(output))) - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.4, - Step: "Building quickshell (this may take a while)...", - IsComplete: false, - CommandInfo: "cmake --build build", - } - - buildCmd := exec.CommandContext(ctx, "cmake", "--build", "build") - buildCmd.Dir = tmpDir - buildCmd.Env = append(os.Environ(), - "TMPDIR="+cacheDir, - "CFLAGS="+customCFLAGS, - "CXXFLAGS="+customCFLAGS) - if err := o.runWithProgressStep(buildCmd, progressChan, PhaseSystemPackages, 0.4, 0.8, "Building quickshell..."); err != nil { - return fmt.Errorf("failed to build quickshell: %w", err) - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.8, - Step: "Installing quickshell...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: "sudo cmake --install build", - } - - installCmd := exec.CommandContext(ctx, "bash", "-c", - fmt.Sprintf("cd %s && echo '%s' | sudo -S cmake --install build", tmpDir, sudoPassword)) - if err := installCmd.Run(); err != nil { - return fmt.Errorf("failed to install quickshell: %w", err) - } - - o.log("quickshell installed successfully from source") - return nil -} - -func (o *OpenSUSEDistribution) installRust(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - if o.commandExists("cargo") { - return nil - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.82, - Step: "Installing rustup...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: "sudo zypper install rustup", - } - - rustupInstallCmd := exec.CommandContext(ctx, "bash", "-c", - fmt.Sprintf("echo '%s' | sudo -S zypper install -y rustup", sudoPassword)) - if err := o.runWithProgress(rustupInstallCmd, progressChan, PhaseSystemPackages, 0.82, 0.83); err != nil { - return fmt.Errorf("failed to install rustup: %w", err) - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.83, - Step: "Installing stable Rust toolchain...", - IsComplete: false, - CommandInfo: "rustup install stable", - } - - rustInstallCmd := exec.CommandContext(ctx, "bash", "-c", "rustup install stable && rustup default stable") - if err := o.runWithProgress(rustInstallCmd, progressChan, PhaseSystemPackages, 0.83, 0.84); err != nil { - return fmt.Errorf("failed to install Rust toolchain: %w", err) - } - - if !o.commandExists("cargo") { - o.log("Warning: cargo not found in PATH after Rust installation, trying to source environment") - } - - return nil -} - -// InstallManualPackages overrides the base implementation to use openSUSE-specific builds -func (o *OpenSUSEDistribution) InstallManualPackages(ctx context.Context, packages []string, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - if len(packages) == 0 { - return nil - } - - o.log(fmt.Sprintf("Installing manual packages: %s", strings.Join(packages, ", "))) - - // Install Rust if needed for matugen - for _, pkg := range packages { - if pkg == "matugen" { - if err := o.installRust(ctx, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install Rust: %w", err) - } - break - } - } - - for _, pkg := range packages { - if pkg == "quickshell" { - if err := o.installQuickshell(ctx, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install quickshell: %w", err) - } - } else { - // Use the base ManualPackageInstaller for other packages - if err := o.ManualPackageInstaller.InstallManualPackages(ctx, []string{pkg}, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install %s: %w", pkg, err) - } - } - } - - return nil -} diff --git a/nix/inputs/dms-cli/internal/distros/osinfo.go b/nix/inputs/dms-cli/internal/distros/osinfo.go deleted file mode 100644 index 7b86ec9..0000000 --- a/nix/inputs/dms-cli/internal/distros/osinfo.go +++ /dev/null @@ -1,115 +0,0 @@ -package distros - -import ( - "bufio" - "fmt" - "os" - "runtime" - "strconv" - "strings" - - "github.com/AvengeMedia/danklinux/internal/errdefs" -) - -// DistroInfo contains basic information about a distribution -type DistroInfo struct { - ID string - HexColorCode string -} - -// OSInfo contains complete OS information -type OSInfo struct { - Distribution DistroInfo - Version string - VersionID string - PrettyName string - Architecture string -} - -// GetOSInfo detects the current OS and returns information about it -func GetOSInfo() (*OSInfo, error) { - if runtime.GOOS != "linux" { - return nil, errdefs.NewCustomError(errdefs.ErrTypeNotLinux, fmt.Sprintf("Only linux is supported, but I found %s", runtime.GOOS)) - } - - if runtime.GOARCH != "amd64" && runtime.GOARCH != "arm64" { - return nil, errdefs.NewCustomError(errdefs.ErrTypeInvalidArchitecture, fmt.Sprintf("Only amd64 and arm64 are supported, but I found %s", runtime.GOARCH)) - } - - info := &OSInfo{ - Architecture: runtime.GOARCH, - } - - file, err := os.Open("/etc/os-release") - if err != nil { - return nil, err - } - defer file.Close() - - scanner := bufio.NewScanner(file) - for scanner.Scan() { - line := scanner.Text() - parts := strings.SplitN(line, "=", 2) - if len(parts) != 2 { - continue - } - - key := parts[0] - value := strings.Trim(parts[1], "\"") - - switch key { - case "ID": - config, exists := Registry[value] - if !exists { - return nil, errdefs.NewCustomError(errdefs.ErrTypeUnsupportedDistribution, fmt.Sprintf("Unsupported distribution: %s", value)) - } - - info.Distribution = DistroInfo{ - ID: value, // Use the actual ID from os-release - HexColorCode: config.ColorHex, - } - case "VERSION_ID", "BUILD_ID": - info.VersionID = value - case "VERSION": - info.Version = value - case "PRETTY_NAME": - info.PrettyName = value - } - } - - return info, scanner.Err() -} - -// IsUnsupportedDistro checks if a distribution/version combination is supported -func IsUnsupportedDistro(distroID, versionID string) bool { - if !IsDistroSupported(distroID) { - return true - } - - if distroID == "ubuntu" { - parts := strings.Split(versionID, ".") - if len(parts) >= 2 { - major, err1 := strconv.Atoi(parts[0]) - minor, err2 := strconv.Atoi(parts[1]) - - if err1 == nil && err2 == nil { - return major < 25 || (major == 25 && minor < 4) - } - } - return true - } - - if distroID == "debian" { - if versionID == "" { - // debian testing/sid have no version ID - return false - } - versionNum, err := strconv.Atoi(versionID) - if err == nil { - return versionNum < 12 - } - return true - } - - return false -} diff --git a/nix/inputs/dms-cli/internal/distros/ubuntu.go b/nix/inputs/dms-cli/internal/distros/ubuntu.go deleted file mode 100644 index 31cbd42..0000000 --- a/nix/inputs/dms-cli/internal/distros/ubuntu.go +++ /dev/null @@ -1,758 +0,0 @@ -package distros - -import ( - "context" - "fmt" - "os" - "os/exec" - "path/filepath" - "strings" - - "github.com/AvengeMedia/danklinux/internal/deps" -) - -func init() { - Register("ubuntu", "#E95420", FamilyUbuntu, func(config DistroConfig, logChan chan<- string) Distribution { - return NewUbuntuDistribution(config, logChan) - }) -} - -type UbuntuDistribution struct { - *BaseDistribution - *ManualPackageInstaller - config DistroConfig -} - -func NewUbuntuDistribution(config DistroConfig, logChan chan<- string) *UbuntuDistribution { - base := NewBaseDistribution(logChan) - return &UbuntuDistribution{ - BaseDistribution: base, - ManualPackageInstaller: &ManualPackageInstaller{BaseDistribution: base}, - config: config, - } -} - -func (u *UbuntuDistribution) GetID() string { - return u.config.ID -} - -func (u *UbuntuDistribution) GetColorHex() string { - return u.config.ColorHex -} - -func (u *UbuntuDistribution) GetFamily() DistroFamily { - return u.config.Family -} - -func (u *UbuntuDistribution) GetPackageManager() PackageManagerType { - return PackageManagerAPT -} - -func (u *UbuntuDistribution) DetectDependencies(ctx context.Context, wm deps.WindowManager) ([]deps.Dependency, error) { - return u.DetectDependenciesWithTerminal(ctx, wm, deps.TerminalGhostty) -} - -func (u *UbuntuDistribution) DetectDependenciesWithTerminal(ctx context.Context, wm deps.WindowManager, terminal deps.Terminal) ([]deps.Dependency, error) { - var dependencies []deps.Dependency - - // DMS at the top (shell is prominent) - dependencies = append(dependencies, u.detectDMS()) - - // Terminal with choice support - dependencies = append(dependencies, u.detectSpecificTerminal(terminal)) - - // Common detections using base methods - dependencies = append(dependencies, u.detectGit()) - dependencies = append(dependencies, u.detectWindowManager(wm)) - dependencies = append(dependencies, u.detectQuickshell()) - dependencies = append(dependencies, u.detectXDGPortal()) - dependencies = append(dependencies, u.detectPolkitAgent()) - dependencies = append(dependencies, u.detectAccountsService()) - - // Hyprland-specific tools - if wm == deps.WindowManagerHyprland { - dependencies = append(dependencies, u.detectHyprlandTools()...) - } - - // Niri-specific tools - if wm == deps.WindowManagerNiri { - dependencies = append(dependencies, u.detectXwaylandSatellite()) - } - - // Base detections (common across distros) - dependencies = append(dependencies, u.detectMatugen()) - dependencies = append(dependencies, u.detectDgop()) - dependencies = append(dependencies, u.detectHyprpicker()) - dependencies = append(dependencies, u.detectClipboardTools()...) - - return dependencies, nil -} - -func (u *UbuntuDistribution) detectXDGPortal() deps.Dependency { - status := deps.StatusMissing - if u.packageInstalled("xdg-desktop-portal-gtk") { - status = deps.StatusInstalled - } - - return deps.Dependency{ - Name: "xdg-desktop-portal-gtk", - Status: status, - Description: "Desktop integration portal for GTK", - Required: true, - } -} - -func (u *UbuntuDistribution) detectPolkitAgent() deps.Dependency { - status := deps.StatusMissing - if u.packageInstalled("mate-polkit") { - status = deps.StatusInstalled - } - - return deps.Dependency{ - Name: "mate-polkit", - Status: status, - Description: "PolicyKit authentication agent", - Required: true, - } -} - -func (u *UbuntuDistribution) detectXwaylandSatellite() deps.Dependency { - status := deps.StatusMissing - if u.commandExists("xwayland-satellite") { - status = deps.StatusInstalled - } - - return deps.Dependency{ - Name: "xwayland-satellite", - Status: status, - Description: "Xwayland support", - Required: true, - } -} - -func (u *UbuntuDistribution) detectAccountsService() deps.Dependency { - status := deps.StatusMissing - if u.packageInstalled("accountsservice") { - status = deps.StatusInstalled - } - - return deps.Dependency{ - Name: "accountsservice", - Status: status, - Description: "D-Bus interface for user account query and manipulation", - Required: true, - } -} - -func (u *UbuntuDistribution) packageInstalled(pkg string) bool { - cmd := exec.Command("dpkg", "-l", pkg) - err := cmd.Run() - return err == nil -} - -func (u *UbuntuDistribution) GetPackageMapping(wm deps.WindowManager) map[string]PackageMapping { - packages := map[string]PackageMapping{ - // Standard APT packages - "git": {Name: "git", Repository: RepoTypeSystem}, - "kitty": {Name: "kitty", Repository: RepoTypeSystem}, - "alacritty": {Name: "alacritty", Repository: RepoTypeSystem}, - "wl-clipboard": {Name: "wl-clipboard", Repository: RepoTypeSystem}, - "xdg-desktop-portal-gtk": {Name: "xdg-desktop-portal-gtk", Repository: RepoTypeSystem}, - "mate-polkit": {Name: "mate-polkit", Repository: RepoTypeSystem}, - "accountsservice": {Name: "accountsservice", Repository: RepoTypeSystem}, - "hyprpicker": {Name: "hyprpicker", Repository: RepoTypePPA, RepoURL: "ppa:cppiber/hyprland"}, - - // Manual builds (niri and quickshell likely not available in Ubuntu repos or PPAs) - "dms (DankMaterialShell)": {Name: "dms", Repository: RepoTypeManual, BuildFunc: "installDankMaterialShell"}, - "niri": {Name: "niri", Repository: RepoTypeManual, BuildFunc: "installNiri"}, - "quickshell": {Name: "quickshell", Repository: RepoTypeManual, BuildFunc: "installQuickshell"}, - "ghostty": {Name: "ghostty", Repository: RepoTypeManual, BuildFunc: "installGhostty"}, - "matugen": {Name: "matugen", Repository: RepoTypeManual, BuildFunc: "installMatugen"}, - "dgop": {Name: "dgop", Repository: RepoTypeManual, BuildFunc: "installDgop"}, - "cliphist": {Name: "cliphist", Repository: RepoTypeManual, BuildFunc: "installCliphist"}, - } - - switch wm { - case deps.WindowManagerHyprland: - // Use the cppiber PPA for Hyprland - packages["hyprland"] = PackageMapping{Name: "hyprland", Repository: RepoTypePPA, RepoURL: "ppa:cppiber/hyprland"} - packages["grim"] = PackageMapping{Name: "grim", Repository: RepoTypeSystem} - packages["slurp"] = PackageMapping{Name: "slurp", Repository: RepoTypeSystem} - packages["hyprctl"] = PackageMapping{Name: "hyprland", Repository: RepoTypePPA, RepoURL: "ppa:cppiber/hyprland"} - packages["grimblast"] = PackageMapping{Name: "grimblast", Repository: RepoTypeManual, BuildFunc: "installGrimblast"} - packages["jq"] = PackageMapping{Name: "jq", Repository: RepoTypeSystem} - case deps.WindowManagerNiri: - packages["niri"] = PackageMapping{Name: "niri", Repository: RepoTypeManual, BuildFunc: "installNiri"} - packages["xwayland-satellite"] = PackageMapping{Name: "xwayland-satellite", Repository: RepoTypeManual, BuildFunc: "installXwaylandSatellite"} - } - - return packages -} - -func (u *UbuntuDistribution) InstallPrerequisites(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.06, - Step: "Updating package lists...", - IsComplete: false, - LogOutput: "Updating APT package lists", - } - - updateCmd := exec.CommandContext(ctx, "bash", "-c", fmt.Sprintf("echo '%s' | sudo -S apt-get update", sudoPassword)) - if err := u.runWithProgress(updateCmd, progressChan, PhasePrerequisites, 0.06, 0.07); err != nil { - return fmt.Errorf("failed to update package lists: %w", err) - } - - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.08, - Step: "Installing build-essential...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: "sudo apt-get install -y build-essential", - LogOutput: "Installing build tools", - } - - checkCmd := exec.CommandContext(ctx, "dpkg", "-l", "build-essential") - if err := checkCmd.Run(); err != nil { - // Not installed, install it - cmd := exec.CommandContext(ctx, "bash", "-c", fmt.Sprintf("echo '%s' | sudo -S apt-get install -y build-essential", sudoPassword)) - if err := u.runWithProgress(cmd, progressChan, PhasePrerequisites, 0.08, 0.09); err != nil { - return fmt.Errorf("failed to install build-essential: %w", err) - } - } - - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.10, - Step: "Installing development dependencies...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: "sudo apt-get install -y curl wget git cmake ninja-build pkg-config libglib2.0-dev libpolkit-agent-1-dev", - LogOutput: "Installing additional development tools", - } - - devToolsCmd := exec.CommandContext(ctx, "bash", "-c", - fmt.Sprintf("echo '%s' | sudo -S apt-get install -y curl wget git cmake ninja-build pkg-config libglib2.0-dev libpolkit-agent-1-dev", sudoPassword)) - if err := u.runWithProgress(devToolsCmd, progressChan, PhasePrerequisites, 0.10, 0.12); err != nil { - return fmt.Errorf("failed to install development tools: %w", err) - } - - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.12, - Step: "Prerequisites installation complete", - IsComplete: false, - LogOutput: "Prerequisites successfully installed", - } - - return nil -} - -func (u *UbuntuDistribution) InstallPackages(ctx context.Context, dependencies []deps.Dependency, wm deps.WindowManager, sudoPassword string, reinstallFlags map[string]bool, progressChan chan<- InstallProgressMsg) error { - // Phase 1: Check Prerequisites - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.05, - Step: "Checking system prerequisites...", - IsComplete: false, - LogOutput: "Starting prerequisite check...", - } - - if err := u.InstallPrerequisites(ctx, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install prerequisites: %w", err) - } - - systemPkgs, ppaPkgs, manualPkgs := u.categorizePackages(dependencies, wm, reinstallFlags) - - // Phase 2: Enable PPA repositories - if len(ppaPkgs) > 0 { - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.15, - Step: "Enabling PPA repositories...", - IsComplete: false, - LogOutput: "Setting up PPA repositories for additional packages", - } - if err := u.enablePPARepos(ctx, ppaPkgs, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to enable PPA repositories: %w", err) - } - } - - // Phase 3: System Packages (APT) - if len(systemPkgs) > 0 { - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.35, - Step: fmt.Sprintf("Installing %d system packages...", len(systemPkgs)), - IsComplete: false, - NeedsSudo: true, - LogOutput: fmt.Sprintf("Installing system packages: %s", strings.Join(systemPkgs, ", ")), - } - if err := u.installAPTPackages(ctx, systemPkgs, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install APT packages: %w", err) - } - } - - // Phase 4: PPA Packages - ppaPkgNames := u.extractPackageNames(ppaPkgs) - if len(ppaPkgNames) > 0 { - progressChan <- InstallProgressMsg{ - Phase: PhaseAURPackages, // Reusing AUR phase for PPA - Progress: 0.65, - Step: fmt.Sprintf("Installing %d PPA packages...", len(ppaPkgNames)), - IsComplete: false, - LogOutput: fmt.Sprintf("Installing PPA packages: %s", strings.Join(ppaPkgNames, ", ")), - } - if err := u.installPPAPackages(ctx, ppaPkgNames, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install PPA packages: %w", err) - } - } - - // Phase 5: Manual Builds - if len(manualPkgs) > 0 { - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.80, - Step: "Installing build dependencies...", - IsComplete: false, - LogOutput: "Installing build tools for manual compilation", - } - if err := u.installBuildDependencies(ctx, manualPkgs, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install build dependencies: %w", err) - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.85, - Step: fmt.Sprintf("Building %d packages from source...", len(manualPkgs)), - IsComplete: false, - LogOutput: fmt.Sprintf("Building from source: %s", strings.Join(manualPkgs, ", ")), - } - if err := u.InstallManualPackages(ctx, manualPkgs, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install manual packages: %w", err) - } - } - - // Phase 6: Configuration - progressChan <- InstallProgressMsg{ - Phase: PhaseConfiguration, - Progress: 0.90, - Step: "Configuring system...", - IsComplete: false, - LogOutput: "Starting post-installation configuration...", - } - - // Phase 7: Complete - progressChan <- InstallProgressMsg{ - Phase: PhaseComplete, - Progress: 1.0, - Step: "Installation complete!", - IsComplete: true, - LogOutput: "All packages installed and configured successfully", - } - - return nil -} - -func (u *UbuntuDistribution) categorizePackages(dependencies []deps.Dependency, wm deps.WindowManager, reinstallFlags map[string]bool) ([]string, []PackageMapping, []string) { - systemPkgs := []string{} - ppaPkgs := []PackageMapping{} - manualPkgs := []string{} - - packageMap := u.GetPackageMapping(wm) - - for _, dep := range dependencies { - // Skip installed packages unless marked for reinstall - if dep.Status == deps.StatusInstalled && !reinstallFlags[dep.Name] { - continue - } - - pkgInfo, exists := packageMap[dep.Name] - if !exists { - u.log(fmt.Sprintf("Warning: No package mapping for %s", dep.Name)) - continue - } - - switch pkgInfo.Repository { - case RepoTypeSystem: - systemPkgs = append(systemPkgs, pkgInfo.Name) - case RepoTypePPA: - ppaPkgs = append(ppaPkgs, pkgInfo) - case RepoTypeManual: - manualPkgs = append(manualPkgs, dep.Name) - } - } - - return systemPkgs, ppaPkgs, manualPkgs -} - -func (u *UbuntuDistribution) extractPackageNames(packages []PackageMapping) []string { - names := make([]string, len(packages)) - for i, pkg := range packages { - names[i] = pkg.Name - } - return names -} - -func (u *UbuntuDistribution) enablePPARepos(ctx context.Context, ppaPkgs []PackageMapping, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - enabledRepos := make(map[string]bool) - - installPPACmd := exec.CommandContext(ctx, "bash", "-c", - fmt.Sprintf("echo '%s' | sudo -S apt-get install -y software-properties-common", sudoPassword)) - if err := u.runWithProgress(installPPACmd, progressChan, PhaseSystemPackages, 0.15, 0.17); err != nil { - return fmt.Errorf("failed to install software-properties-common: %w", err) - } - - for _, pkg := range ppaPkgs { - if pkg.RepoURL != "" && !enabledRepos[pkg.RepoURL] { - u.log(fmt.Sprintf("Enabling PPA repository: %s", pkg.RepoURL)) - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.20, - Step: fmt.Sprintf("Enabling PPA repo %s...", pkg.RepoURL), - IsComplete: false, - NeedsSudo: true, - CommandInfo: fmt.Sprintf("sudo add-apt-repository -y %s", pkg.RepoURL), - } - - cmd := exec.CommandContext(ctx, "bash", "-c", - fmt.Sprintf("echo '%s' | sudo -S add-apt-repository -y %s", sudoPassword, pkg.RepoURL)) - if err := u.runWithProgress(cmd, progressChan, PhaseSystemPackages, 0.20, 0.22); err != nil { - u.logError(fmt.Sprintf("failed to enable PPA repo %s", pkg.RepoURL), err) - return fmt.Errorf("failed to enable PPA repo %s: %w", pkg.RepoURL, err) - } - u.log(fmt.Sprintf("PPA repo %s enabled successfully", pkg.RepoURL)) - enabledRepos[pkg.RepoURL] = true - } - } - - if len(enabledRepos) > 0 { - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.25, - Step: "Updating package lists...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: "sudo apt-get update", - } - - updateCmd := exec.CommandContext(ctx, "bash", "-c", fmt.Sprintf("echo '%s' | sudo -S apt-get update", sudoPassword)) - if err := u.runWithProgress(updateCmd, progressChan, PhaseSystemPackages, 0.25, 0.27); err != nil { - return fmt.Errorf("failed to update package lists after adding PPAs: %w", err) - } - } - - return nil -} - -func (u *UbuntuDistribution) installAPTPackages(ctx context.Context, packages []string, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - if len(packages) == 0 { - return nil - } - - u.log(fmt.Sprintf("Installing APT packages: %s", strings.Join(packages, ", "))) - - args := []string{"apt-get", "install", "-y"} - args = append(args, packages...) - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.40, - Step: "Installing system packages...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: fmt.Sprintf("sudo %s", strings.Join(args, " ")), - } - - cmdStr := fmt.Sprintf("echo '%s' | sudo -S %s", sudoPassword, strings.Join(args, " ")) - cmd := exec.CommandContext(ctx, "bash", "-c", cmdStr) - return u.runWithProgress(cmd, progressChan, PhaseSystemPackages, 0.40, 0.60) -} - -func (u *UbuntuDistribution) installPPAPackages(ctx context.Context, packages []string, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - if len(packages) == 0 { - return nil - } - - u.log(fmt.Sprintf("Installing PPA packages: %s", strings.Join(packages, ", "))) - - args := []string{"apt-get", "install", "-y"} - args = append(args, packages...) - - progressChan <- InstallProgressMsg{ - Phase: PhaseAURPackages, - Progress: 0.70, - Step: "Installing PPA packages...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: fmt.Sprintf("sudo %s", strings.Join(args, " ")), - } - - cmdStr := fmt.Sprintf("echo '%s' | sudo -S %s", sudoPassword, strings.Join(args, " ")) - cmd := exec.CommandContext(ctx, "bash", "-c", cmdStr) - return u.runWithProgress(cmd, progressChan, PhaseAURPackages, 0.70, 0.85) -} - -func (u *UbuntuDistribution) installBuildDependencies(ctx context.Context, manualPkgs []string, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - buildDeps := make(map[string]bool) - - for _, pkg := range manualPkgs { - switch pkg { - case "niri": - buildDeps["curl"] = true - buildDeps["libxkbcommon-dev"] = true - buildDeps["libwayland-dev"] = true - buildDeps["libudev-dev"] = true - buildDeps["libinput-dev"] = true - buildDeps["libdisplay-info-dev"] = true - buildDeps["libpango1.0-dev"] = true - buildDeps["libcairo-dev"] = true - buildDeps["libpipewire-0.3-dev"] = true - buildDeps["libc6-dev"] = true - buildDeps["clang"] = true - buildDeps["libseat-dev"] = true - buildDeps["libgbm-dev"] = true - buildDeps["alacritty"] = true - buildDeps["fuzzel"] = true - buildDeps["libxcb-cursor-dev"] = true - case "quickshell": - buildDeps["qt6-base-dev"] = true - buildDeps["qt6-base-private-dev"] = true - buildDeps["qt6-declarative-dev"] = true - buildDeps["qt6-declarative-private-dev"] = true - buildDeps["qt6-wayland-dev"] = true - buildDeps["qt6-wayland-private-dev"] = true - buildDeps["qt6-tools-dev"] = true - buildDeps["libqt6svg6-dev"] = true - buildDeps["qt6-shadertools-dev"] = true - buildDeps["spirv-tools"] = true - buildDeps["libcli11-dev"] = true - buildDeps["libjemalloc-dev"] = true - buildDeps["libwayland-dev"] = true - buildDeps["wayland-protocols"] = true - buildDeps["libdrm-dev"] = true - buildDeps["libgbm-dev"] = true - buildDeps["libegl-dev"] = true - buildDeps["libgles2-mesa-dev"] = true - buildDeps["libgl1-mesa-dev"] = true - buildDeps["libxcb1-dev"] = true - buildDeps["libpipewire-0.3-dev"] = true - buildDeps["libpam0g-dev"] = true - case "ghostty": - buildDeps["curl"] = true - buildDeps["libgtk-4-dev"] = true - buildDeps["libadwaita-1-dev"] = true - case "matugen": - buildDeps["curl"] = true - case "cliphist": - // Go will be installed separately with PPA - } - } - - for _, pkg := range manualPkgs { - switch pkg { - case "niri", "matugen": - if err := u.installRust(ctx, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install Rust: %w", err) - } - case "ghostty": - if err := u.installZig(ctx, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install Zig: %w", err) - } - case "cliphist", "dgop": - if err := u.installGo(ctx, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install Go: %w", err) - } - } - } - - if len(buildDeps) == 0 { - return nil - } - - depList := make([]string, 0, len(buildDeps)) - for dep := range buildDeps { - depList = append(depList, dep) - } - - args := []string{"apt-get", "install", "-y"} - args = append(args, depList...) - - cmdStr := fmt.Sprintf("echo '%s' | sudo -S %s", sudoPassword, strings.Join(args, " ")) - cmd := exec.CommandContext(ctx, "bash", "-c", cmdStr) - return u.runWithProgress(cmd, progressChan, PhaseSystemPackages, 0.80, 0.82) -} - -func (u *UbuntuDistribution) installRust(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - if u.commandExists("cargo") { - return nil - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.82, - Step: "Installing rustup...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: "sudo apt-get install rustup", - } - - rustupInstallCmd := exec.CommandContext(ctx, "bash", "-c", - fmt.Sprintf("echo '%s' | sudo -S apt-get install -y rustup", sudoPassword)) - if err := u.runWithProgress(rustupInstallCmd, progressChan, PhaseSystemPackages, 0.82, 0.83); err != nil { - return fmt.Errorf("failed to install rustup: %w", err) - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.83, - Step: "Installing stable Rust toolchain...", - IsComplete: false, - CommandInfo: "rustup install stable", - } - - rustInstallCmd := exec.CommandContext(ctx, "bash", "-c", "rustup install stable && rustup default stable") - if err := u.runWithProgress(rustInstallCmd, progressChan, PhaseSystemPackages, 0.83, 0.84); err != nil { - return fmt.Errorf("failed to install Rust toolchain: %w", err) - } - - // Verify cargo is now available - if !u.commandExists("cargo") { - u.log("Warning: cargo not found in PATH after Rust installation, trying to source environment") - } - - return nil -} - -func (u *UbuntuDistribution) installZig(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - if u.commandExists("zig") { - return nil - } - - homeDir, err := os.UserHomeDir() - if err != nil { - return fmt.Errorf("failed to get user home directory: %w", err) - } - - cacheDir := filepath.Join(homeDir, ".cache", "dankinstall") - if err := os.MkdirAll(cacheDir, 0755); err != nil { - return fmt.Errorf("failed to create cache directory: %w", err) - } - - zigUrl := "https://ziglang.org/download/0.11.0/zig-linux-x86_64-0.11.0.tar.xz" - zigTmp := filepath.Join(cacheDir, "zig.tar.xz") - - downloadCmd := exec.CommandContext(ctx, "curl", "-L", zigUrl, "-o", zigTmp) - if err := u.runWithProgress(downloadCmd, progressChan, PhaseSystemPackages, 0.84, 0.85); err != nil { - return fmt.Errorf("failed to download Zig: %w", err) - } - - extractCmd := exec.CommandContext(ctx, "bash", "-c", - fmt.Sprintf("echo '%s' | sudo -S tar -xf %s -C /opt/", sudoPassword, zigTmp)) - if err := u.runWithProgress(extractCmd, progressChan, PhaseSystemPackages, 0.85, 0.86); err != nil { - return fmt.Errorf("failed to extract Zig: %w", err) - } - - linkCmd := exec.CommandContext(ctx, "bash", "-c", - fmt.Sprintf("echo '%s' | sudo -S ln -sf /opt/zig-linux-x86_64-0.11.0/zig /usr/local/bin/zig", sudoPassword)) - return u.runWithProgress(linkCmd, progressChan, PhaseSystemPackages, 0.86, 0.87) -} - -func (u *UbuntuDistribution) installGo(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - if u.commandExists("go") { - return nil - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.87, - Step: "Adding Go PPA repository...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: "sudo add-apt-repository ppa:longsleep/golang-backports", - } - - addPPACmd := exec.CommandContext(ctx, "bash", "-c", - fmt.Sprintf("echo '%s' | sudo -S add-apt-repository -y ppa:longsleep/golang-backports", sudoPassword)) - if err := u.runWithProgress(addPPACmd, progressChan, PhaseSystemPackages, 0.87, 0.88); err != nil { - return fmt.Errorf("failed to add Go PPA: %w", err) - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.88, - Step: "Updating package lists...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: "sudo apt-get update", - } - - updateCmd := exec.CommandContext(ctx, "bash", "-c", - fmt.Sprintf("echo '%s' | sudo -S apt-get update", sudoPassword)) - if err := u.runWithProgress(updateCmd, progressChan, PhaseSystemPackages, 0.88, 0.89); err != nil { - return fmt.Errorf("failed to update package lists after adding Go PPA: %w", err) - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.89, - Step: "Installing Go...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: "sudo apt-get install golang-go", - } - - installCmd := exec.CommandContext(ctx, "bash", "-c", - fmt.Sprintf("echo '%s' | sudo -S apt-get install -y golang-go", sudoPassword)) - return u.runWithProgress(installCmd, progressChan, PhaseSystemPackages, 0.89, 0.90) -} - -func (u *UbuntuDistribution) installGhosttyUbuntu(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - u.log("Installing Ghostty using Ubuntu installer script...") - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.1, - Step: "Running Ghostty Ubuntu installer...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: "curl -fsSL https://raw.githubusercontent.com/mkasberg/ghostty-ubuntu/HEAD/install.sh | sudo bash", - LogOutput: "Installing Ghostty using pre-built Ubuntu package", - } - - installCmd := exec.CommandContext(ctx, "bash", "-c", - fmt.Sprintf("echo '%s' | sudo -S /bin/bash -c \"$(curl -fsSL https://raw.githubusercontent.com/mkasberg/ghostty-ubuntu/HEAD/install.sh)\"", sudoPassword)) - - if err := u.runWithProgress(installCmd, progressChan, PhaseSystemPackages, 0.1, 0.9); err != nil { - return fmt.Errorf("failed to install Ghostty: %w", err) - } - - u.log("Ghostty installed successfully using Ubuntu installer") - return nil -} - -// Override InstallManualPackages for Ubuntu to handle Ubuntu-specific installations -func (u *UbuntuDistribution) InstallManualPackages(ctx context.Context, packages []string, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - if len(packages) == 0 { - return nil - } - - u.log(fmt.Sprintf("Installing manual packages: %s", strings.Join(packages, ", "))) - - for _, pkg := range packages { - switch pkg { - case "ghostty": - if err := u.installGhosttyUbuntu(ctx, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install ghostty: %w", err) - } - default: - // Use the base ManualPackageInstaller for other packages - if err := u.ManualPackageInstaller.InstallManualPackages(ctx, []string{pkg}, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install %s: %w", pkg, err) - } - } - } - - return nil -} diff --git a/nix/inputs/dms-cli/internal/dms/app.go b/nix/inputs/dms-cli/internal/dms/app.go deleted file mode 100644 index fcf90fe..0000000 --- a/nix/inputs/dms-cli/internal/dms/app.go +++ /dev/null @@ -1,438 +0,0 @@ -//go:build !distro_binary - -package dms - -import ( - "os/exec" - "strings" - - "github.com/AvengeMedia/danklinux/internal/deps" - tea "github.com/charmbracelet/bubbletea" -) - -type AppState int - -const ( - StateMainMenu AppState = iota - StateUpdate - StateUpdatePassword - StateUpdateProgress - StateShell - StatePluginsMenu - StatePluginsBrowse - StatePluginDetail - StatePluginSearch - StatePluginsInstalled - StatePluginInstalledDetail - StateGreeterMenu - StateGreeterCompositorSelect - StateGreeterPassword - StateGreeterInstalling - StateAbout -) - -type Model struct { - version string - detector *Detector - dependencies []DependencyInfo - state AppState - selectedItem int - width int - height int - - // Menu items - menuItems []MenuItem - - updateDeps []DependencyInfo - selectedUpdateDep int - updateToggles map[string]bool - - updateProgressChan chan updateProgressMsg - updateProgress updateProgressMsg - updateLogs []string - sudoPassword string - passwordInput string - passwordError string - - // Window manager states - hyprlandInstalled bool - niriInstalled bool - - selectedGreeterItem int - greeterInstallChan chan greeterProgressMsg - greeterProgress greeterProgressMsg - greeterLogs []string - greeterPasswordInput string - greeterPasswordError string - greeterSudoPassword string - greeterCompositors []string - greeterSelectedComp int - greeterChosenCompositor string - - pluginsMenuItems []MenuItem - selectedPluginsMenuItem int - pluginsList []pluginInfo - filteredPluginsList []pluginInfo - selectedPluginIndex int - pluginsLoading bool - pluginsError string - pluginSearchQuery string - installedPluginsList []pluginInfo - selectedInstalledIndex int - installedPluginsLoading bool - installedPluginsError string - pluginInstallStatus map[string]bool -} - -type pluginInfo struct { - ID string - Name string - Category string - Author string - Description string - Repo string - Path string - Capabilities []string - Compositors []string - Dependencies []string - FirstParty bool -} - -type MenuItem struct { - Label string - Action AppState -} - -func NewModel(version string) Model { - detector, _ := NewDetector() - dependencies := detector.GetInstalledComponents() - - // Use the proper detection method for both window managers - hyprlandInstalled, niriInstalled, err := detector.GetWindowManagerStatus() - if err != nil { - // Fallback to false if detection fails - hyprlandInstalled = false - niriInstalled = false - } - - updateToggles := make(map[string]bool) - for _, dep := range dependencies { - if dep.Name == "dms (DankMaterialShell)" && dep.Status == deps.StatusNeedsUpdate { - updateToggles[dep.Name] = true - break - } - } - - m := Model{ - version: version, - detector: detector, - dependencies: dependencies, - state: StateMainMenu, - selectedItem: 0, - updateToggles: updateToggles, - updateDeps: dependencies, - updateProgressChan: make(chan updateProgressMsg, 100), - hyprlandInstalled: hyprlandInstalled, - niriInstalled: niriInstalled, - greeterInstallChan: make(chan greeterProgressMsg, 100), - pluginInstallStatus: make(map[string]bool), - } - - m.menuItems = m.buildMenuItems() - return m -} - -func (m *Model) buildMenuItems() []MenuItem { - items := []MenuItem{ - {Label: "Update", Action: StateUpdate}, - } - - // Shell management - if m.isShellRunning() { - items = append(items, MenuItem{Label: "Terminate Shell", Action: StateShell}) - } else { - items = append(items, MenuItem{Label: "Start Shell (Daemon)", Action: StateShell}) - } - - // Plugins management - items = append(items, MenuItem{Label: "Plugins", Action: StatePluginsMenu}) - - // Greeter management - items = append(items, MenuItem{Label: "Greeter", Action: StateGreeterMenu}) - - items = append(items, MenuItem{Label: "About", Action: StateAbout}) - - return items -} - -func (m *Model) buildPluginsMenuItems() []MenuItem { - return []MenuItem{ - {Label: "Browse Plugins", Action: StatePluginsBrowse}, - {Label: "View Installed", Action: StatePluginsInstalled}, - } -} - -func (m *Model) isShellRunning() bool { - // Check for both -c and -p flag patterns since quickshell can be started either way - // -c dms: config name mode - // -p /dms: path mode (used when installed via system packages) - cmd := exec.Command("pgrep", "-f", "qs.*dms") - err := cmd.Run() - return err == nil -} - -func (m Model) Init() tea.Cmd { - return nil -} - -func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { - switch msg := msg.(type) { - case tea.WindowSizeMsg: - m.width = msg.Width - m.height = msg.Height - case shellStartedMsg: - m.menuItems = m.buildMenuItems() - if m.selectedItem >= len(m.menuItems) { - m.selectedItem = len(m.menuItems) - 1 - } - return m, nil - case updateProgressMsg: - m.updateProgress = msg - if msg.logOutput != "" { - m.updateLogs = append(m.updateLogs, msg.logOutput) - } - return m, m.waitForProgress() - case updateCompleteMsg: - m.updateProgress.complete = true - m.updateProgress.err = msg.err - m.dependencies = m.detector.GetInstalledComponents() - m.updateDeps = m.dependencies - m.menuItems = m.buildMenuItems() - - // Restart shell if update was successful and shell is running - if msg.err == nil && m.isShellRunning() { - restartShell() - } - return m, nil - case greeterProgressMsg: - m.greeterProgress = msg - if msg.logOutput != "" { - m.greeterLogs = append(m.greeterLogs, msg.logOutput) - } - return m, m.waitForGreeterProgress() - case pluginsLoadedMsg: - m.pluginsLoading = false - if msg.err != nil { - m.pluginsError = msg.err.Error() - } else { - m.pluginsList = make([]pluginInfo, len(msg.plugins)) - for i, p := range msg.plugins { - m.pluginsList[i] = pluginInfo{ - ID: p.ID, - Name: p.Name, - Category: p.Category, - Author: p.Author, - Description: p.Description, - Repo: p.Repo, - Path: p.Path, - Capabilities: p.Capabilities, - Compositors: p.Compositors, - Dependencies: p.Dependencies, - FirstParty: strings.HasPrefix(p.Repo, "https://github.com/AvengeMedia"), - } - } - m.filteredPluginsList = m.pluginsList - m.selectedPluginIndex = 0 - m.updatePluginInstallStatus() - } - return m, nil - case installedPluginsLoadedMsg: - m.installedPluginsLoading = false - if msg.err != nil { - m.installedPluginsError = msg.err.Error() - } else { - m.installedPluginsList = make([]pluginInfo, len(msg.plugins)) - for i, p := range msg.plugins { - m.installedPluginsList[i] = pluginInfo{ - ID: p.ID, - Name: p.Name, - Category: p.Category, - Author: p.Author, - Description: p.Description, - Repo: p.Repo, - Path: p.Path, - Capabilities: p.Capabilities, - Compositors: p.Compositors, - Dependencies: p.Dependencies, - FirstParty: strings.HasPrefix(p.Repo, "https://github.com/AvengeMedia"), - } - } - m.selectedInstalledIndex = 0 - } - return m, nil - case pluginUninstalledMsg: - if msg.err != nil { - m.installedPluginsError = msg.err.Error() - m.state = StatePluginInstalledDetail - } else { - m.state = StatePluginsInstalled - m.installedPluginsLoading = true - m.installedPluginsError = "" - return m, loadInstalledPlugins - } - return m, nil - case pluginInstalledMsg: - if msg.err != nil { - m.pluginsError = msg.err.Error() - } else { - m.pluginInstallStatus[msg.pluginName] = true - m.pluginsError = "" - } - return m, nil - case greeterPasswordValidMsg: - if msg.valid { - m.greeterSudoPassword = msg.password - m.greeterPasswordInput = "" - m.greeterPasswordError = "" - m.state = StateGreeterInstalling - m.greeterProgress = greeterProgressMsg{step: "Starting greeter installation..."} - m.greeterLogs = []string{} - return m, tea.Batch(m.performGreeterInstall(), m.waitForGreeterProgress()) - } else { - m.greeterPasswordError = "Incorrect password. Please try again." - m.greeterPasswordInput = "" - } - return m, nil - case passwordValidMsg: - if msg.valid { - m.sudoPassword = msg.password - m.passwordInput = "" - m.passwordError = "" - m.state = StateUpdateProgress - m.updateProgress = updateProgressMsg{progress: 0.0, step: "Starting update..."} - m.updateLogs = []string{} - return m, tea.Batch(m.performUpdate(), m.waitForProgress()) - } else { - m.passwordError = "Incorrect password. Please try again." - m.passwordInput = "" - } - return m, nil - case tea.KeyMsg: - switch m.state { - case StateMainMenu: - return m.updateMainMenu(msg) - case StateUpdate: - return m.updateUpdateView(msg) - case StateUpdatePassword: - return m.updatePasswordView(msg) - case StateUpdateProgress: - return m.updateProgressView(msg) - case StateShell: - return m.updateShellView(msg) - case StatePluginsMenu: - return m.updatePluginsMenu(msg) - case StatePluginsBrowse: - return m.updatePluginsBrowse(msg) - case StatePluginDetail: - return m.updatePluginDetail(msg) - case StatePluginSearch: - return m.updatePluginSearch(msg) - case StatePluginsInstalled: - return m.updatePluginsInstalled(msg) - case StatePluginInstalledDetail: - return m.updatePluginInstalledDetail(msg) - case StateGreeterMenu: - return m.updateGreeterMenu(msg) - case StateGreeterCompositorSelect: - return m.updateGreeterCompositorSelect(msg) - case StateGreeterPassword: - return m.updateGreeterPasswordView(msg) - case StateGreeterInstalling: - return m.updateGreeterInstalling(msg) - case StateAbout: - return m.updateAboutView(msg) - } - } - - return m, nil -} - -type updateProgressMsg struct { - progress float64 - step string - complete bool - err error - logOutput string -} - -type updateCompleteMsg struct { - err error -} - -type passwordValidMsg struct { - password string - valid bool -} - -type greeterProgressMsg struct { - step string - complete bool - err error - logOutput string -} - -type greeterPasswordValidMsg struct { - password string - valid bool -} - -func (m Model) waitForProgress() tea.Cmd { - return func() tea.Msg { - return <-m.updateProgressChan - } -} - -func (m Model) waitForGreeterProgress() tea.Cmd { - return func() tea.Msg { - return <-m.greeterInstallChan - } -} - -func (m Model) View() string { - switch m.state { - case StateMainMenu: - return m.renderMainMenu() - case StateUpdate: - return m.renderUpdateView() - case StateUpdatePassword: - return m.renderPasswordView() - case StateUpdateProgress: - return m.renderProgressView() - case StateShell: - return m.renderShellView() - case StatePluginsMenu: - return m.renderPluginsMenu() - case StatePluginsBrowse: - return m.renderPluginsBrowse() - case StatePluginDetail: - return m.renderPluginDetail() - case StatePluginSearch: - return m.renderPluginSearch() - case StatePluginsInstalled: - return m.renderPluginsInstalled() - case StatePluginInstalledDetail: - return m.renderPluginInstalledDetail() - case StateGreeterMenu: - return m.renderGreeterMenu() - case StateGreeterCompositorSelect: - return m.renderGreeterCompositorSelect() - case StateGreeterPassword: - return m.renderGreeterPasswordView() - case StateGreeterInstalling: - return m.renderGreeterInstalling() - case StateAbout: - return m.renderAboutView() - default: - return m.renderMainMenu() - } -} diff --git a/nix/inputs/dms-cli/internal/dms/app_distro.go b/nix/inputs/dms-cli/internal/dms/app_distro.go deleted file mode 100644 index 2875d2b..0000000 --- a/nix/inputs/dms-cli/internal/dms/app_distro.go +++ /dev/null @@ -1,261 +0,0 @@ -//go:build distro_binary - -package dms - -import ( - "os/exec" - "strings" - - tea "github.com/charmbracelet/bubbletea" -) - -type AppState int - -const ( - StateMainMenu AppState = iota - StateShell - StatePluginsMenu - StatePluginsBrowse - StatePluginDetail - StatePluginSearch - StatePluginsInstalled - StatePluginInstalledDetail - StateAbout -) - -type Model struct { - version string - detector *Detector - dependencies []DependencyInfo - state AppState - selectedItem int - width int - height int - - // Menu items - menuItems []MenuItem - - // Window manager states - hyprlandInstalled bool - niriInstalled bool - - pluginsMenuItems []MenuItem - selectedPluginsMenuItem int - pluginsList []pluginInfo - filteredPluginsList []pluginInfo - selectedPluginIndex int - pluginsLoading bool - pluginsError string - pluginSearchQuery string - installedPluginsList []pluginInfo - selectedInstalledIndex int - installedPluginsLoading bool - installedPluginsError string - pluginInstallStatus map[string]bool -} - -type pluginInfo struct { - ID string - Name string - Category string - Author string - Description string - Repo string - Path string - Capabilities []string - Compositors []string - Dependencies []string - FirstParty bool -} - -type MenuItem struct { - Label string - Action AppState -} - -func NewModel(version string) Model { - detector, _ := NewDetector() - dependencies := detector.GetInstalledComponents() - - // Use the proper detection method for both window managers - hyprlandInstalled, niriInstalled, err := detector.GetWindowManagerStatus() - if err != nil { - // Fallback to false if detection fails - hyprlandInstalled = false - niriInstalled = false - } - - m := Model{ - version: version, - detector: detector, - dependencies: dependencies, - state: StateMainMenu, - selectedItem: 0, - hyprlandInstalled: hyprlandInstalled, - niriInstalled: niriInstalled, - pluginInstallStatus: make(map[string]bool), - } - - m.menuItems = m.buildMenuItems() - return m -} - -func (m *Model) buildMenuItems() []MenuItem { - items := []MenuItem{} - - // Shell management - if m.isShellRunning() { - items = append(items, MenuItem{Label: "Terminate Shell", Action: StateShell}) - } else { - items = append(items, MenuItem{Label: "Start Shell (Daemon)", Action: StateShell}) - } - - // Plugins management - items = append(items, MenuItem{Label: "Plugins", Action: StatePluginsMenu}) - - items = append(items, MenuItem{Label: "About", Action: StateAbout}) - - return items -} - -func (m *Model) buildPluginsMenuItems() []MenuItem { - return []MenuItem{ - {Label: "Browse Plugins", Action: StatePluginsBrowse}, - {Label: "View Installed", Action: StatePluginsInstalled}, - } -} - -func (m *Model) isShellRunning() bool { - cmd := exec.Command("pgrep", "-f", "qs -c dms") - err := cmd.Run() - return err == nil -} - -func (m Model) Init() tea.Cmd { - return nil -} - -func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { - switch msg := msg.(type) { - case tea.WindowSizeMsg: - m.width = msg.Width - m.height = msg.Height - case pluginsLoadedMsg: - m.pluginsLoading = false - if msg.err != nil { - m.pluginsError = msg.err.Error() - } else { - m.pluginsList = make([]pluginInfo, len(msg.plugins)) - for i, p := range msg.plugins { - m.pluginsList[i] = pluginInfo{ - ID: p.ID, - Name: p.Name, - Category: p.Category, - Author: p.Author, - Description: p.Description, - Repo: p.Repo, - Path: p.Path, - Capabilities: p.Capabilities, - Compositors: p.Compositors, - Dependencies: p.Dependencies, - FirstParty: strings.HasPrefix(p.Repo, "https://github.com/AvengeMedia"), - } - } - m.filteredPluginsList = m.pluginsList - m.selectedPluginIndex = 0 - m.updatePluginInstallStatus() - } - return m, nil - case installedPluginsLoadedMsg: - m.installedPluginsLoading = false - if msg.err != nil { - m.installedPluginsError = msg.err.Error() - } else { - m.installedPluginsList = make([]pluginInfo, len(msg.plugins)) - for i, p := range msg.plugins { - m.installedPluginsList[i] = pluginInfo{ - ID: p.ID, - Name: p.Name, - Category: p.Category, - Author: p.Author, - Description: p.Description, - Repo: p.Repo, - Path: p.Path, - Capabilities: p.Capabilities, - Compositors: p.Compositors, - Dependencies: p.Dependencies, - FirstParty: strings.HasPrefix(p.Repo, "https://github.com/AvengeMedia"), - } - } - m.selectedInstalledIndex = 0 - } - return m, nil - case pluginUninstalledMsg: - if msg.err != nil { - m.installedPluginsError = msg.err.Error() - m.state = StatePluginInstalledDetail - } else { - m.state = StatePluginsInstalled - m.installedPluginsLoading = true - m.installedPluginsError = "" - return m, loadInstalledPlugins - } - return m, nil - case pluginInstalledMsg: - if msg.err != nil { - m.pluginsError = msg.err.Error() - } else { - m.pluginInstallStatus[msg.pluginName] = true - m.pluginsError = "" - } - return m, nil - case tea.KeyMsg: - switch m.state { - case StateMainMenu: - return m.updateMainMenu(msg) - case StateShell: - return m.updateShellView(msg) - case StatePluginsMenu: - return m.updatePluginsMenu(msg) - case StatePluginsBrowse: - return m.updatePluginsBrowse(msg) - case StatePluginDetail: - return m.updatePluginDetail(msg) - case StatePluginSearch: - return m.updatePluginSearch(msg) - case StatePluginsInstalled: - return m.updatePluginsInstalled(msg) - case StatePluginInstalledDetail: - return m.updatePluginInstalledDetail(msg) - case StateAbout: - return m.updateAboutView(msg) - } - } - - return m, nil -} - -func (m Model) View() string { - switch m.state { - case StateMainMenu: - return m.renderMainMenu() - case StateShell: - return m.renderShellView() - case StatePluginsMenu: - return m.renderPluginsMenu() - case StatePluginsBrowse: - return m.renderPluginsBrowse() - case StatePluginDetail: - return m.renderPluginDetail() - case StatePluginSearch: - return m.renderPluginSearch() - case StatePluginsInstalled: - return m.renderPluginsInstalled() - case StatePluginInstalledDetail: - return m.renderPluginInstalledDetail() - case StateAbout: - return m.renderAboutView() - default: - return m.renderMainMenu() - } -} diff --git a/nix/inputs/dms-cli/internal/dms/detector.go b/nix/inputs/dms-cli/internal/dms/detector.go deleted file mode 100644 index a56b1be..0000000 --- a/nix/inputs/dms-cli/internal/dms/detector.go +++ /dev/null @@ -1,167 +0,0 @@ -package dms - -import ( - "context" - "os" - "os/exec" - - "github.com/AvengeMedia/danklinux/internal/config" - "github.com/AvengeMedia/danklinux/internal/deps" - "github.com/AvengeMedia/danklinux/internal/distros" -) - -type Detector struct { - homeDir string - distribution distros.Distribution -} - -func (d *Detector) GetDistribution() distros.Distribution { - return d.distribution -} - -func NewDetector() (*Detector, error) { - homeDir, err := os.UserHomeDir() - if err != nil { - return nil, err - } - - logChan := make(chan string, 100) - go func() { - for range logChan { - } - }() - - osInfo, err := distros.GetOSInfo() - if err != nil { - return nil, err - } - - dist, err := distros.NewDistribution(osInfo.Distribution.ID, logChan) - if err != nil { - return nil, err - } - - return &Detector{ - homeDir: homeDir, - distribution: dist, - }, nil -} - -func (d *Detector) IsDMSInstalled() bool { - _, err := config.LocateDMSConfig() - return err == nil -} - -func (d *Detector) GetDependencyStatus() ([]deps.Dependency, error) { - hyprlandDeps, err := d.distribution.DetectDependencies(context.Background(), deps.WindowManagerHyprland) - if err != nil { - return nil, err - } - - niriDeps, err := d.distribution.DetectDependencies(context.Background(), deps.WindowManagerNiri) - if err != nil { - return nil, err - } - - // Combine dependencies and deduplicate - depMap := make(map[string]deps.Dependency) - - for _, dep := range hyprlandDeps { - depMap[dep.Name] = dep - } - - for _, dep := range niriDeps { - // If dependency already exists, keep the one that's installed or needs update - if existing, exists := depMap[dep.Name]; exists { - if dep.Status > existing.Status { - depMap[dep.Name] = dep - } - } else { - depMap[dep.Name] = dep - } - } - - // Convert map back to slice - var allDeps []deps.Dependency - for _, dep := range depMap { - allDeps = append(allDeps, dep) - } - - return allDeps, nil -} - -func (d *Detector) GetWindowManagerStatus() (bool, bool, error) { - // Reuse the existing command detection logic from BaseDistribution - // Since all distros embed BaseDistribution, we can access it via interface - type CommandChecker interface { - CommandExists(string) bool - } - - checker, ok := d.distribution.(CommandChecker) - if !ok { - // Fallback to direct command check if interface not available - hyprlandInstalled := d.commandExists("hyprland") || d.commandExists("Hyprland") - niriInstalled := d.commandExists("niri") - return hyprlandInstalled, niriInstalled, nil - } - - hyprlandInstalled := checker.CommandExists("hyprland") || checker.CommandExists("Hyprland") - niriInstalled := checker.CommandExists("niri") - - return hyprlandInstalled, niriInstalled, nil -} - -func (d *Detector) commandExists(cmd string) bool { - _, err := exec.LookPath(cmd) - return err == nil -} - -func (d *Detector) GetInstalledComponents() []DependencyInfo { - dependencies, err := d.GetDependencyStatus() - if err != nil { - return []DependencyInfo{} - } - - isNixOS := d.isNixOS() - - var components []DependencyInfo - for _, dep := range dependencies { - // On NixOS, filter out the window managers themselves but keep their components - if isNixOS && (dep.Name == "hyprland" || dep.Name == "niri") { - continue - } - - components = append(components, DependencyInfo{ - Name: dep.Name, - Status: dep.Status, - Description: dep.Description, - Required: dep.Required, - }) - } - - return components -} - -func (d *Detector) isNixOS() bool { - _, err := os.Stat("/etc/nixos") - if err == nil { - return true - } - - // Alternative check - if _, err := os.Stat("/nix/store"); err == nil { - // Also check for nixos-version command - if d.commandExists("nixos-version") { - return true - } - } - - return false -} - -type DependencyInfo struct { - Name string - Status deps.DependencyStatus - Description string - Required bool -} diff --git a/nix/inputs/dms-cli/internal/dms/handlers_common.go b/nix/inputs/dms-cli/internal/dms/handlers_common.go deleted file mode 100644 index cea5c26..0000000 --- a/nix/inputs/dms-cli/internal/dms/handlers_common.go +++ /dev/null @@ -1,54 +0,0 @@ -package dms - -import ( - "os/exec" - "time" - - "github.com/AvengeMedia/danklinux/internal/log" - tea "github.com/charmbracelet/bubbletea" -) - -func (m Model) updateShellView(msg tea.KeyMsg) (tea.Model, tea.Cmd) { - switch msg.String() { - case "ctrl+c", "q": - return m, tea.Quit - case "esc": - m.state = StateMainMenu - default: - return m, tea.Quit - } - return m, nil -} - -func (m Model) updateAboutView(msg tea.KeyMsg) (tea.Model, tea.Cmd) { - switch msg.String() { - case "ctrl+c", "q", "esc": - if msg.String() == "esc" { - m.state = StateMainMenu - } else { - return m, tea.Quit - } - } - return m, nil -} - -func terminateShell() { - patterns := []string{"dms run", "qs -c dms"} - for _, pattern := range patterns { - cmd := exec.Command("pkill", "-f", pattern) - cmd.Run() - } -} - -func startShellDaemon() { - cmd := exec.Command("dms", "run", "-d") - if err := cmd.Start(); err != nil { - log.Errorf("Error starting daemon: %v", err) - } -} - -func restartShell() { - terminateShell() - time.Sleep(500 * time.Millisecond) - startShellDaemon() -} diff --git a/nix/inputs/dms-cli/internal/dms/handlers_features.go b/nix/inputs/dms-cli/internal/dms/handlers_features.go deleted file mode 100644 index b6f16db..0000000 --- a/nix/inputs/dms-cli/internal/dms/handlers_features.go +++ /dev/null @@ -1,391 +0,0 @@ -//go:build !distro_binary - -package dms - -import ( - "context" - "fmt" - "os/exec" - "strings" - "time" - - "github.com/AvengeMedia/danklinux/internal/deps" - "github.com/AvengeMedia/danklinux/internal/distros" - "github.com/AvengeMedia/danklinux/internal/greeter" - tea "github.com/charmbracelet/bubbletea" -) - -func (m Model) updateUpdateView(msg tea.KeyMsg) (tea.Model, tea.Cmd) { - filteredDeps := m.getFilteredDeps() - maxIndex := len(filteredDeps) - 1 - - switch msg.String() { - case "ctrl+c", "q": - return m, tea.Quit - case "esc": - m.state = StateMainMenu - case "up", "k": - if m.selectedUpdateDep > 0 { - m.selectedUpdateDep-- - } - case "down", "j": - if m.selectedUpdateDep < maxIndex { - m.selectedUpdateDep++ - } - case " ": - if dep := m.getDepAtVisualIndex(m.selectedUpdateDep); dep != nil { - m.updateToggles[dep.Name] = !m.updateToggles[dep.Name] - } - case "enter": - hasSelected := false - for _, toggle := range m.updateToggles { - if toggle { - hasSelected = true - break - } - } - - if !hasSelected { - m.state = StateMainMenu - return m, nil - } - - m.state = StateUpdatePassword - m.passwordInput = "" - m.passwordError = "" - return m, nil - } - return m, nil -} - -func (m Model) updatePasswordView(msg tea.KeyMsg) (tea.Model, tea.Cmd) { - switch msg.String() { - case "ctrl+c": - return m, tea.Quit - case "esc": - m.state = StateUpdate - m.passwordInput = "" - m.passwordError = "" - return m, nil - case "enter": - if m.passwordInput == "" { - return m, nil - } - return m, m.validatePassword(m.passwordInput) - case "backspace": - if len(m.passwordInput) > 0 { - m.passwordInput = m.passwordInput[:len(m.passwordInput)-1] - } - default: - if len(msg.String()) == 1 && msg.String()[0] >= 32 && msg.String()[0] <= 126 { - m.passwordInput += msg.String() - } - } - return m, nil -} - -func (m Model) updateProgressView(msg tea.KeyMsg) (tea.Model, tea.Cmd) { - switch msg.String() { - case "ctrl+c", "q": - return m, tea.Quit - case "esc": - if m.updateProgress.complete { - m.state = StateMainMenu - m.updateProgress = updateProgressMsg{} - m.updateLogs = []string{} - } - } - return m, nil -} - -func (m Model) validatePassword(password string) tea.Cmd { - return func() tea.Msg { - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - cmd := exec.CommandContext(ctx, "sudo", "-S", "-v") - stdin, err := cmd.StdinPipe() - if err != nil { - return passwordValidMsg{password: "", valid: false} - } - - go func() { - defer stdin.Close() - fmt.Fprintf(stdin, "%s\n", password) - }() - - output, err := cmd.CombinedOutput() - outputStr := string(output) - - if err != nil { - if strings.Contains(outputStr, "Sorry, try again") || - strings.Contains(outputStr, "incorrect password") || - strings.Contains(outputStr, "authentication failure") { - return passwordValidMsg{password: "", valid: false} - } - return passwordValidMsg{password: "", valid: false} - } - - return passwordValidMsg{password: password, valid: true} - } -} - -func (m Model) performUpdate() tea.Cmd { - var depsToUpdate []deps.Dependency - - for _, depInfo := range m.updateDeps { - if m.updateToggles[depInfo.Name] { - depsToUpdate = append(depsToUpdate, deps.Dependency{ - Name: depInfo.Name, - Status: depInfo.Status, - Description: depInfo.Description, - Required: depInfo.Required, - }) - } - } - - if len(depsToUpdate) == 0 { - return func() tea.Msg { - return updateCompleteMsg{err: nil} - } - } - - wm := deps.WindowManagerHyprland - if m.niriInstalled { - wm = deps.WindowManagerNiri - } - - sudoPassword := m.sudoPassword - reinstallFlags := make(map[string]bool) - for name, toggled := range m.updateToggles { - if toggled { - reinstallFlags[name] = true - } - } - - distribution := m.detector.GetDistribution() - progressChan := m.updateProgressChan - - return func() tea.Msg { - installerChan := make(chan distros.InstallProgressMsg, 100) - - go func() { - ctx := context.Background() - err := distribution.InstallPackages(ctx, depsToUpdate, wm, sudoPassword, reinstallFlags, installerChan) - close(installerChan) - - if err != nil { - progressChan <- updateProgressMsg{complete: true, err: err} - } else { - progressChan <- updateProgressMsg{complete: true} - } - }() - - go func() { - for msg := range installerChan { - progressChan <- updateProgressMsg{ - progress: msg.Progress, - step: msg.Step, - complete: msg.IsComplete, - err: msg.Error, - logOutput: msg.LogOutput, - } - } - }() - - return nil - } -} - -func (m Model) updateGreeterMenu(msg tea.KeyMsg) (tea.Model, tea.Cmd) { - greeterMenuItems := []string{"Install Greeter"} - - switch msg.String() { - case "ctrl+c", "q": - return m, tea.Quit - case "esc": - m.state = StateMainMenu - case "up", "k": - if m.selectedGreeterItem > 0 { - m.selectedGreeterItem-- - } - case "down", "j": - if m.selectedGreeterItem < len(greeterMenuItems)-1 { - m.selectedGreeterItem++ - } - case "enter", " ": - if m.selectedGreeterItem == 0 { - compositors := greeter.DetectCompositors() - if len(compositors) == 0 { - return m, nil - } - - m.greeterCompositors = compositors - - if len(compositors) > 1 { - m.state = StateGreeterCompositorSelect - m.greeterSelectedComp = 0 - return m, nil - } else { - m.greeterChosenCompositor = compositors[0] - m.state = StateGreeterPassword - m.greeterPasswordInput = "" - m.greeterPasswordError = "" - return m, nil - } - } - } - return m, nil -} - -func (m Model) updateGreeterCompositorSelect(msg tea.KeyMsg) (tea.Model, tea.Cmd) { - switch msg.String() { - case "ctrl+c", "q": - return m, tea.Quit - case "esc": - m.state = StateGreeterMenu - return m, nil - case "up", "k": - if m.greeterSelectedComp > 0 { - m.greeterSelectedComp-- - } - case "down", "j": - if m.greeterSelectedComp < len(m.greeterCompositors)-1 { - m.greeterSelectedComp++ - } - case "enter", " ": - m.greeterChosenCompositor = m.greeterCompositors[m.greeterSelectedComp] - m.state = StateGreeterPassword - m.greeterPasswordInput = "" - m.greeterPasswordError = "" - return m, nil - } - return m, nil -} - -func (m Model) updateGreeterPasswordView(msg tea.KeyMsg) (tea.Model, tea.Cmd) { - switch msg.String() { - case "ctrl+c": - return m, tea.Quit - case "esc": - m.state = StateGreeterMenu - m.greeterPasswordInput = "" - m.greeterPasswordError = "" - return m, nil - case "enter": - if m.greeterPasswordInput == "" { - return m, nil - } - return m, m.validateGreeterPassword(m.greeterPasswordInput) - case "backspace": - if len(m.greeterPasswordInput) > 0 { - m.greeterPasswordInput = m.greeterPasswordInput[:len(m.greeterPasswordInput)-1] - } - default: - if len(msg.String()) == 1 && msg.String()[0] >= 32 && msg.String()[0] <= 126 { - m.greeterPasswordInput += msg.String() - } - } - return m, nil -} - -func (m Model) updateGreeterInstalling(msg tea.KeyMsg) (tea.Model, tea.Cmd) { - switch msg.String() { - case "ctrl+c", "q": - return m, tea.Quit - case "esc": - if m.greeterProgress.complete { - m.state = StateMainMenu - m.greeterProgress = greeterProgressMsg{} - m.greeterLogs = []string{} - } - } - return m, nil -} - -func (m Model) performGreeterInstall() tea.Cmd { - progressChan := m.greeterInstallChan - sudoPassword := m.greeterSudoPassword - compositor := m.greeterChosenCompositor - - return func() tea.Msg { - go func() { - logFunc := func(msg string) { - progressChan <- greeterProgressMsg{step: msg, logOutput: msg} - } - - progressChan <- greeterProgressMsg{step: "Checking greetd installation..."} - if err := performGreeterInstallSteps(progressChan, logFunc, sudoPassword, compositor); err != nil { - progressChan <- greeterProgressMsg{step: "Installation failed", complete: true, err: err} - return - } - - progressChan <- greeterProgressMsg{step: "Installation complete", complete: true} - }() - return nil - } -} - -func (m Model) validateGreeterPassword(password string) tea.Cmd { - return func() tea.Msg { - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - cmd := exec.CommandContext(ctx, "sudo", "-S", "-v") - stdin, err := cmd.StdinPipe() - if err != nil { - return greeterPasswordValidMsg{password: "", valid: false} - } - - go func() { - defer stdin.Close() - fmt.Fprintf(stdin, "%s\n", password) - }() - - output, err := cmd.CombinedOutput() - outputStr := string(output) - - if err != nil { - if strings.Contains(outputStr, "Sorry, try again") || - strings.Contains(outputStr, "incorrect password") || - strings.Contains(outputStr, "authentication failure") { - return greeterPasswordValidMsg{password: "", valid: false} - } - return greeterPasswordValidMsg{password: "", valid: false} - } - - return greeterPasswordValidMsg{password: password, valid: true} - } -} - -func performGreeterInstallSteps(progressChan chan greeterProgressMsg, logFunc func(string), sudoPassword string, compositor string) error { - if err := greeter.EnsureGreetdInstalled(logFunc, sudoPassword); err != nil { - return err - } - - progressChan <- greeterProgressMsg{step: "Detecting DMS installation..."} - dmsPath, err := greeter.DetectDMSPath() - if err != nil { - return err - } - logFunc(fmt.Sprintf("✓ Found DMS at: %s", dmsPath)) - - logFunc(fmt.Sprintf("✓ Selected compositor: %s", compositor)) - - progressChan <- greeterProgressMsg{step: "Copying greeter files..."} - if err := greeter.CopyGreeterFiles(dmsPath, compositor, logFunc, sudoPassword); err != nil { - return err - } - - progressChan <- greeterProgressMsg{step: "Configuring greetd..."} - if err := greeter.ConfigureGreetd(dmsPath, compositor, logFunc, sudoPassword); err != nil { - return err - } - - progressChan <- greeterProgressMsg{step: "Synchronizing DMS configurations..."} - if err := greeter.SyncDMSConfigs(dmsPath, logFunc, sudoPassword); err != nil { - return err - } - - return nil -} diff --git a/nix/inputs/dms-cli/internal/dms/handlers_mainmenu.go b/nix/inputs/dms-cli/internal/dms/handlers_mainmenu.go deleted file mode 100644 index 6a2b941..0000000 --- a/nix/inputs/dms-cli/internal/dms/handlers_mainmenu.go +++ /dev/null @@ -1,61 +0,0 @@ -//go:build !distro_binary - -package dms - -import ( - "time" - - tea "github.com/charmbracelet/bubbletea" -) - -type shellStartedMsg struct{} - -func (m Model) updateMainMenu(msg tea.KeyMsg) (tea.Model, tea.Cmd) { - switch msg.String() { - case "ctrl+c", "q", "esc": - return m, tea.Quit - case "up", "k": - if m.selectedItem > 0 { - m.selectedItem-- - } - case "down", "j": - if m.selectedItem < len(m.menuItems)-1 { - m.selectedItem++ - } - case "enter", " ": - if m.selectedItem < len(m.menuItems) { - selectedAction := m.menuItems[m.selectedItem].Action - selectedLabel := m.menuItems[m.selectedItem].Label - - switch selectedAction { - case StateUpdate: - m.state = StateUpdate - m.selectedUpdateDep = 0 - case StateShell: - if selectedLabel == "Terminate Shell" { - terminateShell() - m.menuItems = m.buildMenuItems() - if m.selectedItem >= len(m.menuItems) { - m.selectedItem = len(m.menuItems) - 1 - } - } else { - startShellDaemon() - // Wait a moment for the daemon to actually start before checking status - return m, tea.Tick(300*time.Millisecond, func(t time.Time) tea.Msg { - return shellStartedMsg{} - }) - } - case StatePluginsMenu: - m.state = StatePluginsMenu - m.selectedPluginsMenuItem = 0 - m.pluginsMenuItems = m.buildPluginsMenuItems() - case StateGreeterMenu: - m.state = StateGreeterMenu - m.selectedGreeterItem = 0 - case StateAbout: - m.state = StateAbout - } - } - } - return m, nil -} diff --git a/nix/inputs/dms-cli/internal/dms/handlers_mainmenu_distro.go b/nix/inputs/dms-cli/internal/dms/handlers_mainmenu_distro.go deleted file mode 100644 index a2b2190..0000000 --- a/nix/inputs/dms-cli/internal/dms/handlers_mainmenu_distro.go +++ /dev/null @@ -1,55 +0,0 @@ -//go:build distro_binary - -package dms - -import ( - "time" - - tea "github.com/charmbracelet/bubbletea" -) - -type shellStartedMsg struct{} - -func (m Model) updateMainMenu(msg tea.KeyMsg) (tea.Model, tea.Cmd) { - switch msg.String() { - case "ctrl+c", "q", "esc": - return m, tea.Quit - case "up", "k": - if m.selectedItem > 0 { - m.selectedItem-- - } - case "down", "j": - if m.selectedItem < len(m.menuItems)-1 { - m.selectedItem++ - } - case "enter", " ": - if m.selectedItem < len(m.menuItems) { - selectedAction := m.menuItems[m.selectedItem].Action - selectedLabel := m.menuItems[m.selectedItem].Label - - switch selectedAction { - case StateShell: - if selectedLabel == "Terminate Shell" { - terminateShell() - m.menuItems = m.buildMenuItems() - if m.selectedItem >= len(m.menuItems) { - m.selectedItem = len(m.menuItems) - 1 - } - } else { - startShellDaemon() - // Wait a moment for the daemon to actually start before checking status - return m, tea.Tick(300*time.Millisecond, func(t time.Time) tea.Msg { - return shellStartedMsg{} - }) - } - case StatePluginsMenu: - m.state = StatePluginsMenu - m.selectedPluginsMenuItem = 0 - m.pluginsMenuItems = m.buildPluginsMenuItems() - case StateAbout: - m.state = StateAbout - } - } - } - return m, nil -} diff --git a/nix/inputs/dms-cli/internal/dms/plugins_handlers.go b/nix/inputs/dms-cli/internal/dms/plugins_handlers.go deleted file mode 100644 index 1b3132b..0000000 --- a/nix/inputs/dms-cli/internal/dms/plugins_handlers.go +++ /dev/null @@ -1,339 +0,0 @@ -package dms - -import ( - "strings" - - "github.com/AvengeMedia/danklinux/internal/plugins" - tea "github.com/charmbracelet/bubbletea" -) - -func (m Model) updatePluginsMenu(msg tea.KeyMsg) (tea.Model, tea.Cmd) { - switch msg.String() { - case "ctrl+c", "q": - return m, tea.Quit - case "esc": - m.state = StateMainMenu - case "up", "k": - if m.selectedPluginsMenuItem > 0 { - m.selectedPluginsMenuItem-- - } - case "down", "j": - if m.selectedPluginsMenuItem < len(m.pluginsMenuItems)-1 { - m.selectedPluginsMenuItem++ - } - case "enter", " ": - if m.selectedPluginsMenuItem < len(m.pluginsMenuItems) { - selectedAction := m.pluginsMenuItems[m.selectedPluginsMenuItem].Action - switch selectedAction { - case StatePluginsBrowse: - m.state = StatePluginsBrowse - m.pluginsLoading = true - m.pluginsError = "" - m.pluginsList = nil - return m, loadPlugins - case StatePluginsInstalled: - m.state = StatePluginsInstalled - m.installedPluginsLoading = true - m.installedPluginsError = "" - m.installedPluginsList = nil - return m, loadInstalledPlugins - } - } - } - return m, nil -} - -func (m Model) updatePluginsBrowse(msg tea.KeyMsg) (tea.Model, tea.Cmd) { - switch msg.String() { - case "ctrl+c", "q": - return m, tea.Quit - case "esc": - m.state = StatePluginsMenu - m.pluginSearchQuery = "" - m.filteredPluginsList = m.pluginsList - m.selectedPluginIndex = 0 - case "up", "k": - if m.selectedPluginIndex > 0 { - m.selectedPluginIndex-- - } - case "down", "j": - if m.selectedPluginIndex < len(m.filteredPluginsList)-1 { - m.selectedPluginIndex++ - } - case "enter", " ": - if m.selectedPluginIndex < len(m.filteredPluginsList) { - m.state = StatePluginDetail - } - case "/": - m.state = StatePluginSearch - m.pluginSearchQuery = "" - } - return m, nil -} - -func (m Model) updatePluginDetail(msg tea.KeyMsg) (tea.Model, tea.Cmd) { - switch msg.String() { - case "ctrl+c", "q": - return m, tea.Quit - case "esc": - m.state = StatePluginsBrowse - case "i": - if m.selectedPluginIndex < len(m.filteredPluginsList) { - plugin := m.filteredPluginsList[m.selectedPluginIndex] - installed := m.pluginInstallStatus[plugin.Name] - if !installed { - return m, installPlugin(plugin) - } - } - } - return m, nil -} - -func (m Model) updatePluginSearch(msg tea.KeyMsg) (tea.Model, tea.Cmd) { - switch msg.String() { - case "ctrl+c": - return m, tea.Quit - case "esc": - m.state = StatePluginsBrowse - m.pluginSearchQuery = "" - m.filteredPluginsList = m.pluginsList - m.selectedPluginIndex = 0 - case "enter": - m.state = StatePluginsBrowse - m.filterPlugins() - case "backspace": - if len(m.pluginSearchQuery) > 0 { - m.pluginSearchQuery = m.pluginSearchQuery[:len(m.pluginSearchQuery)-1] - } - default: - if len(msg.String()) == 1 { - m.pluginSearchQuery += msg.String() - } - } - return m, nil -} - -func (m *Model) filterPlugins() { - if m.pluginSearchQuery == "" { - m.filteredPluginsList = m.pluginsList - m.selectedPluginIndex = 0 - return - } - - rawPlugins := make([]plugins.Plugin, len(m.pluginsList)) - for i, p := range m.pluginsList { - rawPlugins[i] = plugins.Plugin{ - ID: p.ID, - Name: p.Name, - Category: p.Category, - Author: p.Author, - Description: p.Description, - Repo: p.Repo, - Path: p.Path, - Capabilities: p.Capabilities, - Compositors: p.Compositors, - Dependencies: p.Dependencies, - } - } - - searchResults := plugins.FuzzySearch(m.pluginSearchQuery, rawPlugins) - searchResults = plugins.SortByFirstParty(searchResults) - - filtered := make([]pluginInfo, len(searchResults)) - for i, p := range searchResults { - filtered[i] = pluginInfo{ - ID: p.ID, - Name: p.Name, - Category: p.Category, - Author: p.Author, - Description: p.Description, - Repo: p.Repo, - Path: p.Path, - Capabilities: p.Capabilities, - Compositors: p.Compositors, - Dependencies: p.Dependencies, - FirstParty: strings.HasPrefix(p.Repo, "https://github.com/AvengeMedia"), - } - } - - m.filteredPluginsList = filtered - m.selectedPluginIndex = 0 -} - -type pluginsLoadedMsg struct { - plugins []plugins.Plugin - err error -} - -func loadPlugins() tea.Msg { - registry, err := plugins.NewRegistry() - if err != nil { - return pluginsLoadedMsg{err: err} - } - - pluginList, err := registry.List() - if err != nil { - return pluginsLoadedMsg{err: err} - } - - return pluginsLoadedMsg{plugins: pluginList} -} - -func (m *Model) updatePluginInstallStatus() { - manager, err := plugins.NewManager() - if err != nil { - return - } - - for _, plugin := range m.pluginsList { - p := plugins.Plugin{ID: plugin.ID} - installed, err := manager.IsInstalled(p) - if err == nil { - m.pluginInstallStatus[plugin.Name] = installed - } - } -} - -func (m Model) updatePluginsInstalled(msg tea.KeyMsg) (tea.Model, tea.Cmd) { - switch msg.String() { - case "ctrl+c", "q": - return m, tea.Quit - case "esc": - m.state = StatePluginsMenu - case "up", "k": - if m.selectedInstalledIndex > 0 { - m.selectedInstalledIndex-- - } - case "down", "j": - if m.selectedInstalledIndex < len(m.installedPluginsList)-1 { - m.selectedInstalledIndex++ - } - case "enter", " ": - if m.selectedInstalledIndex < len(m.installedPluginsList) { - m.state = StatePluginInstalledDetail - } - } - return m, nil -} - -func (m Model) updatePluginInstalledDetail(msg tea.KeyMsg) (tea.Model, tea.Cmd) { - switch msg.String() { - case "ctrl+c", "q": - return m, tea.Quit - case "esc": - m.state = StatePluginsInstalled - case "u": - if m.selectedInstalledIndex < len(m.installedPluginsList) { - plugin := m.installedPluginsList[m.selectedInstalledIndex] - return m, uninstallPlugin(plugin) - } - } - return m, nil -} - -type installedPluginsLoadedMsg struct { - plugins []plugins.Plugin - err error -} - -type pluginUninstalledMsg struct { - pluginName string - err error -} - -type pluginInstalledMsg struct { - pluginName string - err error -} - -func loadInstalledPlugins() tea.Msg { - manager, err := plugins.NewManager() - if err != nil { - return installedPluginsLoadedMsg{err: err} - } - - registry, err := plugins.NewRegistry() - if err != nil { - return installedPluginsLoadedMsg{err: err} - } - - installedNames, err := manager.ListInstalled() - if err != nil { - return installedPluginsLoadedMsg{err: err} - } - - allPlugins, err := registry.List() - if err != nil { - return installedPluginsLoadedMsg{err: err} - } - - var installed []plugins.Plugin - for _, id := range installedNames { - for _, p := range allPlugins { - if p.ID == id { - installed = append(installed, p) - break - } - } - } - - installed = plugins.SortByFirstParty(installed) - - return installedPluginsLoadedMsg{plugins: installed} -} - -func installPlugin(plugin pluginInfo) tea.Cmd { - return func() tea.Msg { - manager, err := plugins.NewManager() - if err != nil { - return pluginInstalledMsg{pluginName: plugin.Name, err: err} - } - - p := plugins.Plugin{ - ID: plugin.ID, - Name: plugin.Name, - Category: plugin.Category, - Author: plugin.Author, - Description: plugin.Description, - Repo: plugin.Repo, - Path: plugin.Path, - Capabilities: plugin.Capabilities, - Compositors: plugin.Compositors, - Dependencies: plugin.Dependencies, - } - - if err := manager.Install(p); err != nil { - return pluginInstalledMsg{pluginName: plugin.Name, err: err} - } - - return pluginInstalledMsg{pluginName: plugin.Name} - } -} - -func uninstallPlugin(plugin pluginInfo) tea.Cmd { - return func() tea.Msg { - manager, err := plugins.NewManager() - if err != nil { - return pluginUninstalledMsg{pluginName: plugin.Name, err: err} - } - - p := plugins.Plugin{ - ID: plugin.ID, - Name: plugin.Name, - Category: plugin.Category, - Author: plugin.Author, - Description: plugin.Description, - Repo: plugin.Repo, - Path: plugin.Path, - Capabilities: plugin.Capabilities, - Compositors: plugin.Compositors, - Dependencies: plugin.Dependencies, - } - - if err := manager.Uninstall(p); err != nil { - return pluginUninstalledMsg{pluginName: plugin.Name, err: err} - } - - return pluginUninstalledMsg{pluginName: plugin.Name} - } -} diff --git a/nix/inputs/dms-cli/internal/dms/plugins_views.go b/nix/inputs/dms-cli/internal/dms/plugins_views.go deleted file mode 100644 index cfcfe0e..0000000 --- a/nix/inputs/dms-cli/internal/dms/plugins_views.go +++ /dev/null @@ -1,367 +0,0 @@ -package dms - -import ( - "fmt" - "strings" - - "github.com/charmbracelet/lipgloss" -) - -func (m Model) renderPluginsMenu() string { - var b strings.Builder - - titleStyle := lipgloss.NewStyle(). - Bold(true). - Foreground(lipgloss.Color("#00D4AA")) - - normalStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#FFFFFF")) - - selectedStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#00D4AA")). - Bold(true) - - b.WriteString(titleStyle.Render("Plugins")) - b.WriteString("\n\n") - - for i, item := range m.pluginsMenuItems { - if i == m.selectedPluginsMenuItem { - b.WriteString(selectedStyle.Render(fmt.Sprintf("→ %s", item.Label))) - } else { - b.WriteString(normalStyle.Render(fmt.Sprintf(" %s", item.Label))) - } - b.WriteString("\n") - } - - b.WriteString("\n") - instructionStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#888888")) - b.WriteString(instructionStyle.Render("↑/↓: Navigate | Enter: Select | Esc: Back | q: Quit")) - - return b.String() -} - -func (m Model) renderPluginsBrowse() string { - var b strings.Builder - - titleStyle := lipgloss.NewStyle(). - Bold(true). - Foreground(lipgloss.Color("#00D4AA")) - - normalStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#FFFFFF")) - - errorStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#FF0000")) - - selectedStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#00D4AA")). - Bold(true) - - b.WriteString(titleStyle.Render("Browse Plugins")) - b.WriteString("\n\n") - - if m.pluginsLoading { - b.WriteString(normalStyle.Render("Fetching plugins from registry...")) - } else if m.pluginsError != "" { - b.WriteString(errorStyle.Render(fmt.Sprintf("Error: %s", m.pluginsError))) - } else if len(m.filteredPluginsList) == 0 { - if m.pluginSearchQuery != "" { - b.WriteString(normalStyle.Render(fmt.Sprintf("No plugins match '%s'", m.pluginSearchQuery))) - } else { - b.WriteString(normalStyle.Render("No plugins found in registry.")) - } - } else { - installedStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#888888")) - - for i, plugin := range m.filteredPluginsList { - installed := m.pluginInstallStatus[plugin.Name] - installMarker := "" - if installed { - installMarker = " [Installed]" - } - - if i == m.selectedPluginIndex { - b.WriteString(selectedStyle.Render(fmt.Sprintf("→ %s", plugin.Name))) - if installed { - b.WriteString(installedStyle.Render(installMarker)) - } - } else { - b.WriteString(normalStyle.Render(fmt.Sprintf(" %s", plugin.Name))) - if installed { - b.WriteString(installedStyle.Render(installMarker)) - } - } - b.WriteString("\n") - } - } - - b.WriteString("\n") - instructionStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#888888")) - - if m.pluginsLoading || m.pluginsError != "" { - b.WriteString(instructionStyle.Render("Esc: Back | q: Quit")) - } else { - b.WriteString(instructionStyle.Render("↑/↓: Navigate | Enter: View/Install | /: Search | Esc: Back | q: Quit")) - } - - return b.String() -} - -func (m Model) renderPluginDetail() string { - var b strings.Builder - - titleStyle := lipgloss.NewStyle(). - Bold(true). - Foreground(lipgloss.Color("#00D4AA")) - - normalStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#FFFFFF")) - - labelStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#888888")) - - if m.selectedPluginIndex >= len(m.filteredPluginsList) { - return "No plugin selected" - } - - plugin := m.filteredPluginsList[m.selectedPluginIndex] - - b.WriteString(titleStyle.Render(plugin.Name)) - b.WriteString("\n\n") - - b.WriteString(labelStyle.Render("ID: ")) - b.WriteString(normalStyle.Render(plugin.ID)) - b.WriteString("\n\n") - - b.WriteString(labelStyle.Render("Category: ")) - b.WriteString(normalStyle.Render(plugin.Category)) - b.WriteString("\n\n") - - b.WriteString(labelStyle.Render("Author: ")) - b.WriteString(normalStyle.Render(plugin.Author)) - b.WriteString("\n\n") - - b.WriteString(labelStyle.Render("Description:")) - b.WriteString("\n") - wrapped := wrapText(plugin.Description, 60) - b.WriteString(normalStyle.Render(wrapped)) - b.WriteString("\n\n") - - b.WriteString(labelStyle.Render("Repository: ")) - b.WriteString(normalStyle.Render(plugin.Repo)) - b.WriteString("\n\n") - - if len(plugin.Capabilities) > 0 { - b.WriteString(labelStyle.Render("Capabilities: ")) - b.WriteString(normalStyle.Render(strings.Join(plugin.Capabilities, ", "))) - b.WriteString("\n\n") - } - - if len(plugin.Compositors) > 0 { - b.WriteString(labelStyle.Render("Compositors: ")) - b.WriteString(normalStyle.Render(strings.Join(plugin.Compositors, ", "))) - b.WriteString("\n\n") - } - - if len(plugin.Dependencies) > 0 { - b.WriteString(labelStyle.Render("Dependencies: ")) - b.WriteString(normalStyle.Render(strings.Join(plugin.Dependencies, ", "))) - b.WriteString("\n\n") - } - - installed := m.pluginInstallStatus[plugin.Name] - if installed { - b.WriteString(labelStyle.Render("Status: ")) - installedStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#00D4AA")) - b.WriteString(installedStyle.Render("Installed")) - b.WriteString("\n\n") - } - - instructionStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#888888")) - - if installed { - b.WriteString(instructionStyle.Render("Esc: Back | q: Quit")) - } else { - b.WriteString(instructionStyle.Render("i: Install | Esc: Back | q: Quit")) - } - - return b.String() -} - -func (m Model) renderPluginSearch() string { - var b strings.Builder - - titleStyle := lipgloss.NewStyle(). - Bold(true). - Foreground(lipgloss.Color("#00D4AA")) - - normalStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#FFFFFF")) - - b.WriteString(titleStyle.Render("Search Plugins")) - b.WriteString("\n\n") - - b.WriteString(normalStyle.Render("Query: ")) - b.WriteString(titleStyle.Render(m.pluginSearchQuery + "▌")) - b.WriteString("\n\n") - - instructionStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#888888")) - b.WriteString(instructionStyle.Render("Enter: Search | Esc: Cancel")) - - return b.String() -} - -func (m Model) renderPluginsInstalled() string { - var b strings.Builder - - titleStyle := lipgloss.NewStyle(). - Bold(true). - Foreground(lipgloss.Color("#00D4AA")) - - normalStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#FFFFFF")) - - errorStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#FF0000")) - - selectedStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#00D4AA")). - Bold(true) - - b.WriteString(titleStyle.Render("Installed Plugins")) - b.WriteString("\n\n") - - if m.installedPluginsLoading { - b.WriteString(normalStyle.Render("Loading installed plugins...")) - } else if m.installedPluginsError != "" { - b.WriteString(errorStyle.Render(fmt.Sprintf("Error: %s", m.installedPluginsError))) - } else if len(m.installedPluginsList) == 0 { - b.WriteString(normalStyle.Render("No plugins installed.")) - } else { - for i, plugin := range m.installedPluginsList { - if i == m.selectedInstalledIndex { - b.WriteString(selectedStyle.Render(fmt.Sprintf("→ %s", plugin.Name))) - } else { - b.WriteString(normalStyle.Render(fmt.Sprintf(" %s", plugin.Name))) - } - b.WriteString("\n") - } - } - - b.WriteString("\n") - instructionStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#888888")) - - if m.installedPluginsLoading || m.installedPluginsError != "" { - b.WriteString(instructionStyle.Render("Esc: Back | q: Quit")) - } else { - b.WriteString(instructionStyle.Render("↑/↓: Navigate | Enter: Details | Esc: Back | q: Quit")) - } - - return b.String() -} - -func (m Model) renderPluginInstalledDetail() string { - var b strings.Builder - - titleStyle := lipgloss.NewStyle(). - Bold(true). - Foreground(lipgloss.Color("#00D4AA")) - - normalStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#FFFFFF")) - - labelStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#888888")) - - errorStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#FF0000")) - - if m.selectedInstalledIndex >= len(m.installedPluginsList) { - return "No plugin selected" - } - - plugin := m.installedPluginsList[m.selectedInstalledIndex] - - b.WriteString(titleStyle.Render(plugin.Name)) - b.WriteString("\n\n") - - b.WriteString(labelStyle.Render("ID: ")) - b.WriteString(normalStyle.Render(plugin.ID)) - b.WriteString("\n\n") - - b.WriteString(labelStyle.Render("Category: ")) - b.WriteString(normalStyle.Render(plugin.Category)) - b.WriteString("\n\n") - - b.WriteString(labelStyle.Render("Author: ")) - b.WriteString(normalStyle.Render(plugin.Author)) - b.WriteString("\n\n") - - b.WriteString(labelStyle.Render("Description:")) - b.WriteString("\n") - wrapped := wrapText(plugin.Description, 60) - b.WriteString(normalStyle.Render(wrapped)) - b.WriteString("\n\n") - - b.WriteString(labelStyle.Render("Repository: ")) - b.WriteString(normalStyle.Render(plugin.Repo)) - b.WriteString("\n\n") - - if len(plugin.Capabilities) > 0 { - b.WriteString(labelStyle.Render("Capabilities: ")) - b.WriteString(normalStyle.Render(strings.Join(plugin.Capabilities, ", "))) - b.WriteString("\n\n") - } - - if len(plugin.Compositors) > 0 { - b.WriteString(labelStyle.Render("Compositors: ")) - b.WriteString(normalStyle.Render(strings.Join(plugin.Compositors, ", "))) - b.WriteString("\n\n") - } - - if len(plugin.Dependencies) > 0 { - b.WriteString(labelStyle.Render("Dependencies: ")) - b.WriteString(normalStyle.Render(strings.Join(plugin.Dependencies, ", "))) - b.WriteString("\n\n") - } - - if m.installedPluginsError != "" { - b.WriteString(errorStyle.Render(fmt.Sprintf("Error: %s", m.installedPluginsError))) - b.WriteString("\n\n") - } - - instructionStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#888888")) - b.WriteString(instructionStyle.Render("u: Uninstall | Esc: Back | q: Quit")) - - return b.String() -} - -func wrapText(text string, width int) string { - words := strings.Fields(text) - if len(words) == 0 { - return text - } - - var lines []string - currentLine := words[0] - - for _, word := range words[1:] { - if len(currentLine)+1+len(word) <= width { - currentLine += " " + word - } else { - lines = append(lines, currentLine) - currentLine = word - } - } - lines = append(lines, currentLine) - - return strings.Join(lines, "\n") -} diff --git a/nix/inputs/dms-cli/internal/dms/views_common.go b/nix/inputs/dms-cli/internal/dms/views_common.go deleted file mode 100644 index 9dba99b..0000000 --- a/nix/inputs/dms-cli/internal/dms/views_common.go +++ /dev/null @@ -1,149 +0,0 @@ -package dms - -import ( - "fmt" - "strings" - - "github.com/AvengeMedia/danklinux/internal/tui" - "github.com/charmbracelet/lipgloss" -) - -func (m Model) renderMainMenu() string { - var b strings.Builder - - b.WriteString(m.renderBanner()) - b.WriteString("\n") - - headerStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#FFFFFF")). - Bold(true). - MarginBottom(1) - - b.WriteString(headerStyle.Render("dms")) - b.WriteString("\n") - - selectedStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#00D4AA")). - Bold(true) - - normalStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#FFFFFF")) - - for i, item := range m.menuItems { - if i == m.selectedItem { - b.WriteString(selectedStyle.Render(fmt.Sprintf("▶ %s", item.Label))) - } else { - b.WriteString(normalStyle.Render(fmt.Sprintf(" %s", item.Label))) - } - b.WriteString("\n") - } - - b.WriteString("\n") - instructionStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#888888")). - MarginTop(1) - - instructions := "↑/↓: Navigate, Enter: Select, q/Esc: Exit" - b.WriteString(instructionStyle.Render(instructions)) - - return b.String() -} - -func (m Model) renderShellView() string { - var b strings.Builder - - b.WriteString(m.renderBanner()) - b.WriteString("\n") - - headerStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#FFFFFF")). - Bold(true). - MarginBottom(1) - - b.WriteString(headerStyle.Render("Shell")) - b.WriteString("\n\n") - - normalStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#FFFFFF")) - - b.WriteString(normalStyle.Render("Opening interactive shell...")) - b.WriteString("\n") - b.WriteString(normalStyle.Render("This will launch a shell with DMS environment loaded.")) - b.WriteString("\n\n") - - instructionStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#888888")). - MarginTop(1) - - instructions := "Press any key to launch shell, Esc: Back" - b.WriteString(instructionStyle.Render(instructions)) - - return b.String() -} - -func (m Model) renderAboutView() string { - var b strings.Builder - - b.WriteString(m.renderBanner()) - b.WriteString("\n") - - headerStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#FFFFFF")). - Bold(true). - MarginBottom(1) - - b.WriteString(headerStyle.Render("About DankMaterialShell")) - b.WriteString("\n\n") - - normalStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#FFFFFF")) - - b.WriteString(normalStyle.Render(fmt.Sprintf("DMS Management Interface %s", m.version))) - b.WriteString("\n\n") - b.WriteString(normalStyle.Render("DankMaterialShell is a comprehensive desktop environment")) - b.WriteString("\n") - b.WriteString(normalStyle.Render("built around Quickshell, providing a modern Material Design")) - b.WriteString("\n") - b.WriteString(normalStyle.Render("experience for Wayland compositors.")) - b.WriteString("\n\n") - - b.WriteString(normalStyle.Render("Components:")) - b.WriteString("\n") - for _, dep := range m.dependencies { - status := "✗" - if dep.Status == 1 { - status = "✓" - } - b.WriteString(normalStyle.Render(fmt.Sprintf(" %s %s", status, dep.Name))) - b.WriteString("\n") - } - - b.WriteString("\n") - instructionStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#888888")). - MarginTop(1) - - instructions := "Esc: Back to main menu" - b.WriteString(instructionStyle.Render(instructions)) - - return b.String() -} - -func (m Model) renderBanner() string { - theme := tui.TerminalTheme() - - logo := ` -██████╗ █████╗ ███╗ ██╗██╗ ██╗ -██╔══██╗██╔══██╗████╗ ██║██║ ██╔╝ -██║ ██║███████║██╔██╗ ██║█████╔╝ -██║ ██║██╔══██║██║╚██╗██║██╔═██╗ -██████╔╝██║ ██║██║ ╚████║██║ ██╗ -╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝` - - titleStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Primary)). - Bold(true). - MarginBottom(1) - - return titleStyle.Render(logo) -} diff --git a/nix/inputs/dms-cli/internal/dms/views_features.go b/nix/inputs/dms-cli/internal/dms/views_features.go deleted file mode 100644 index eda4da6..0000000 --- a/nix/inputs/dms-cli/internal/dms/views_features.go +++ /dev/null @@ -1,529 +0,0 @@ -//go:build !distro_binary - -package dms - -import ( - "fmt" - "strings" - - "github.com/charmbracelet/lipgloss" -) - -func (m Model) renderUpdateView() string { - var b strings.Builder - - b.WriteString(m.renderBanner()) - b.WriteString("\n") - - headerStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#FFFFFF")). - Bold(true). - MarginBottom(1) - - b.WriteString(headerStyle.Render("Update Dependencies")) - b.WriteString("\n") - - if len(m.updateDeps) == 0 { - b.WriteString("Loading dependencies...\n") - return b.String() - } - - categories := m.categorizeDependencies() - currentIndex := 0 - - for _, category := range []string{"Shell", "Shared Components", "Hyprland Components", "Niri Components"} { - deps, exists := categories[category] - if !exists || len(deps) == 0 { - continue - } - - categoryStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#7060ac")). - Bold(true). - MarginTop(1) - - b.WriteString(categoryStyle.Render(category + ":")) - b.WriteString("\n") - - for _, dep := range deps { - var statusText, icon, reinstallMarker string - var style lipgloss.Style - - if m.updateToggles[dep.Name] { - reinstallMarker = "🔄 " - if dep.Status == 0 { - statusText = "Will be installed" - } else { - statusText = "Will be upgraded" - } - style = lipgloss.NewStyle().Foreground(lipgloss.Color("#FFA500")) - } else { - switch dep.Status { - case 1: - icon = "✓" - statusText = "Installed" - style = lipgloss.NewStyle().Foreground(lipgloss.Color("#FFFFFF")) - case 0: - icon = "○" - statusText = "Not installed" - style = lipgloss.NewStyle().Foreground(lipgloss.Color("#888888")) - case 2: - icon = "△" - statusText = "Needs update" - style = lipgloss.NewStyle().Foreground(lipgloss.Color("#FFA500")) - case 3: - icon = "!" - statusText = "Needs reinstall" - style = lipgloss.NewStyle().Foreground(lipgloss.Color("#FFA500")) - } - } - - line := fmt.Sprintf("%s%s%-25s %s", reinstallMarker, icon, dep.Name, statusText) - - if currentIndex == m.selectedUpdateDep { - line = "▶ " + line - selectedStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("#7060ac")).Bold(true) - b.WriteString(selectedStyle.Render(line)) - } else { - line = " " + line - b.WriteString(style.Render(line)) - } - b.WriteString("\n") - currentIndex++ - } - } - - b.WriteString("\n") - instructionStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#888888")). - MarginTop(1) - - instructions := "↑/↓: Navigate, Space: Toggle, Enter: Update Selected, Esc: Back" - b.WriteString(instructionStyle.Render(instructions)) - - return b.String() -} - -func (m Model) renderPasswordView() string { - var b strings.Builder - - b.WriteString(m.renderBanner()) - b.WriteString("\n") - - headerStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#FFFFFF")). - Bold(true). - MarginBottom(1) - - b.WriteString(headerStyle.Render("Sudo Authentication")) - b.WriteString("\n\n") - - normalStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#FFFFFF")) - - b.WriteString(normalStyle.Render("Package installation requires sudo privileges.")) - b.WriteString("\n") - b.WriteString(normalStyle.Render("Please enter your password to continue:")) - b.WriteString("\n\n") - - inputStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#00D4AA")) - - maskedPassword := strings.Repeat("*", len(m.passwordInput)) - b.WriteString(inputStyle.Render("Password: " + maskedPassword)) - b.WriteString("\n") - - if m.passwordError != "" { - errorStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#FF0000")) - b.WriteString(errorStyle.Render("✗ " + m.passwordError)) - b.WriteString("\n") - } - - b.WriteString("\n") - instructionStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#888888")). - MarginTop(1) - - instructions := "Enter: Continue, Esc: Back, Ctrl+C: Cancel" - b.WriteString(instructionStyle.Render(instructions)) - - return b.String() -} - -func (m Model) renderProgressView() string { - var b strings.Builder - - b.WriteString(m.renderBanner()) - b.WriteString("\n") - - headerStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#FFFFFF")). - Bold(true). - MarginBottom(1) - - b.WriteString(headerStyle.Render("Updating Packages")) - b.WriteString("\n\n") - - if !m.updateProgress.complete { - progressStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#00D4AA")) - - b.WriteString(progressStyle.Render(m.updateProgress.step)) - b.WriteString("\n\n") - - progressBar := fmt.Sprintf("[%s%s] %.0f%%", - strings.Repeat("█", int(m.updateProgress.progress*30)), - strings.Repeat("░", 30-int(m.updateProgress.progress*30)), - m.updateProgress.progress*100) - b.WriteString(lipgloss.NewStyle().Foreground(lipgloss.Color("#FFFFFF")).Render(progressBar)) - b.WriteString("\n") - - if len(m.updateLogs) > 0 { - b.WriteString("\n") - logHeader := lipgloss.NewStyle().Foreground(lipgloss.Color("#888888")).Render("Live Output:") - b.WriteString(logHeader) - b.WriteString("\n") - - maxLines := 8 - startIdx := 0 - if len(m.updateLogs) > maxLines { - startIdx = len(m.updateLogs) - maxLines - } - - logStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("#888888")) - for i := startIdx; i < len(m.updateLogs); i++ { - if m.updateLogs[i] != "" { - b.WriteString(logStyle.Render(" " + m.updateLogs[i])) - b.WriteString("\n") - } - } - } - } - - if m.updateProgress.err != nil { - errorStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#FF0000")) - - b.WriteString("\n") - b.WriteString(errorStyle.Render(fmt.Sprintf("✗ Update failed: %v", m.updateProgress.err))) - b.WriteString("\n") - - if len(m.updateLogs) > 0 { - b.WriteString("\n") - logHeader := lipgloss.NewStyle().Foreground(lipgloss.Color("#888888")).Render("Error Logs:") - b.WriteString(logHeader) - b.WriteString("\n") - - maxLines := 15 - startIdx := 0 - if len(m.updateLogs) > maxLines { - startIdx = len(m.updateLogs) - maxLines - } - - logStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("#888888")) - for i := startIdx; i < len(m.updateLogs); i++ { - if m.updateLogs[i] != "" { - b.WriteString(logStyle.Render(" " + m.updateLogs[i])) - b.WriteString("\n") - } - } - } - - b.WriteString("\n") - instructionStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#888888")) - b.WriteString(instructionStyle.Render("Press Esc to go back")) - } else if m.updateProgress.complete { - successStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#00D4AA")) - - b.WriteString("\n") - b.WriteString(successStyle.Render("✓ Update complete!")) - b.WriteString("\n\n") - - instructionStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#888888")) - b.WriteString(instructionStyle.Render("Press Esc to return to main menu")) - } - - return b.String() -} - -func (m Model) getFilteredDeps() []DependencyInfo { - categories := m.categorizeDependencies() - var filtered []DependencyInfo - - for _, category := range []string{"Shell", "Shared Components", "Hyprland Components", "Niri Components"} { - deps, exists := categories[category] - if exists { - filtered = append(filtered, deps...) - } - } - - return filtered -} - -func (m Model) getDepAtVisualIndex(index int) *DependencyInfo { - filtered := m.getFilteredDeps() - if index >= 0 && index < len(filtered) { - return &filtered[index] - } - return nil -} - -func (m Model) renderGreeterPasswordView() string { - var b strings.Builder - - b.WriteString(m.renderBanner()) - b.WriteString("\n") - - headerStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#FFFFFF")). - Bold(true). - MarginBottom(1) - - b.WriteString(headerStyle.Render("Sudo Authentication")) - b.WriteString("\n\n") - - normalStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#FFFFFF")) - - b.WriteString(normalStyle.Render("Greeter installation requires sudo privileges.")) - b.WriteString("\n") - b.WriteString(normalStyle.Render("Please enter your password to continue:")) - b.WriteString("\n\n") - - inputStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#00D4AA")) - - maskedPassword := strings.Repeat("*", len(m.greeterPasswordInput)) - b.WriteString(inputStyle.Render("Password: " + maskedPassword)) - b.WriteString("\n") - - if m.greeterPasswordError != "" { - errorStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#FF0000")) - b.WriteString(errorStyle.Render("✗ " + m.greeterPasswordError)) - b.WriteString("\n") - } - - b.WriteString("\n") - instructionStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#888888")). - MarginTop(1) - - instructions := "Enter: Continue, Esc: Back, Ctrl+C: Cancel" - b.WriteString(instructionStyle.Render(instructions)) - - return b.String() -} - -func (m Model) renderGreeterCompositorSelect() string { - var b strings.Builder - - b.WriteString(m.renderBanner()) - b.WriteString("\n") - - headerStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#FFFFFF")). - Bold(true). - MarginBottom(1) - - b.WriteString(headerStyle.Render("Select Compositor")) - b.WriteString("\n\n") - - normalStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#FFFFFF")) - - b.WriteString(normalStyle.Render("Multiple compositors detected. Choose which one to use for the greeter:")) - b.WriteString("\n\n") - - selectedStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#00D4AA")). - Bold(true) - - for i, comp := range m.greeterCompositors { - if i == m.greeterSelectedComp { - b.WriteString(selectedStyle.Render(fmt.Sprintf("▶ %s", comp))) - } else { - b.WriteString(normalStyle.Render(fmt.Sprintf(" %s", comp))) - } - b.WriteString("\n") - } - - b.WriteString("\n") - instructionStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#888888")). - MarginTop(1) - - instructions := "↑/↓: Navigate, Enter: Select, Esc: Back" - b.WriteString(instructionStyle.Render(instructions)) - - return b.String() -} - -func (m Model) renderGreeterMenu() string { - var b strings.Builder - - b.WriteString(m.renderBanner()) - b.WriteString("\n") - - headerStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#FFFFFF")). - Bold(true). - MarginBottom(1) - - b.WriteString(headerStyle.Render("Greeter Management")) - b.WriteString("\n") - - greeterMenuItems := []string{"Install Greeter"} - - selectedStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#00D4AA")). - Bold(true) - - normalStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#FFFFFF")) - - for i, item := range greeterMenuItems { - if i == m.selectedGreeterItem { - b.WriteString(selectedStyle.Render(fmt.Sprintf("▶ %s", item))) - } else { - b.WriteString(normalStyle.Render(fmt.Sprintf(" %s", item))) - } - b.WriteString("\n") - } - - b.WriteString("\n") - instructionStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#888888")). - MarginTop(1) - - instructions := "↑/↓: Navigate, Enter: Select, Esc: Back" - b.WriteString(instructionStyle.Render(instructions)) - - return b.String() -} - -func (m Model) renderGreeterInstalling() string { - var b strings.Builder - - b.WriteString(m.renderBanner()) - b.WriteString("\n") - - headerStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#FFFFFF")). - Bold(true). - MarginBottom(1) - - b.WriteString(headerStyle.Render("Installing Greeter")) - b.WriteString("\n\n") - - if !m.greeterProgress.complete { - progressStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#00D4AA")) - - b.WriteString(progressStyle.Render(m.greeterProgress.step)) - b.WriteString("\n\n") - - if len(m.greeterLogs) > 0 { - b.WriteString("\n") - logHeader := lipgloss.NewStyle().Foreground(lipgloss.Color("#888888")).Render("Output:") - b.WriteString(logHeader) - b.WriteString("\n") - - maxLines := 10 - startIdx := 0 - if len(m.greeterLogs) > maxLines { - startIdx = len(m.greeterLogs) - maxLines - } - - logStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("#888888")) - for i := startIdx; i < len(m.greeterLogs); i++ { - if m.greeterLogs[i] != "" { - b.WriteString(logStyle.Render(" " + m.greeterLogs[i])) - b.WriteString("\n") - } - } - } - } - - if m.greeterProgress.err != nil { - errorStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#FF0000")) - - b.WriteString("\n") - b.WriteString(errorStyle.Render(fmt.Sprintf("✗ Installation failed: %v", m.greeterProgress.err))) - b.WriteString("\n\n") - - instructionStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#888888")) - b.WriteString(instructionStyle.Render("Press Esc to go back")) - } else if m.greeterProgress.complete { - successStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#00D4AA")) - - b.WriteString("\n") - b.WriteString(successStyle.Render("✓ Greeter installation complete!")) - b.WriteString("\n\n") - - normalStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#FFFFFF")) - - b.WriteString(normalStyle.Render("To test the greeter, run:")) - b.WriteString("\n") - b.WriteString(normalStyle.Render(" sudo systemctl start greetd")) - b.WriteString("\n\n") - b.WriteString(normalStyle.Render("To enable on boot, run:")) - b.WriteString("\n") - b.WriteString(normalStyle.Render(" sudo systemctl enable --now greetd")) - b.WriteString("\n\n") - - instructionStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#888888")) - b.WriteString(instructionStyle.Render("Press Esc to return to main menu")) - } - - return b.String() -} - -func (m Model) categorizeDependencies() map[string][]DependencyInfo { - categories := map[string][]DependencyInfo{ - "Shell": {}, - "Shared Components": {}, - "Hyprland Components": {}, - "Niri Components": {}, - } - - excludeList := map[string]bool{ - "git": true, - "polkit-agent": true, - "jq": true, - "xdg-desktop-portal": true, - "xdg-desktop-portal-wlr": true, - "xdg-desktop-portal-hyprland": true, - "xdg-desktop-portal-gtk": true, - } - - for _, dep := range m.updateDeps { - if excludeList[dep.Name] { - continue - } - - switch dep.Name { - case "dms (DankMaterialShell)", "quickshell": - categories["Shell"] = append(categories["Shell"], dep) - case "hyprland", "grim", "slurp", "hyprctl", "grimblast": - categories["Hyprland Components"] = append(categories["Hyprland Components"], dep) - case "niri": - categories["Niri Components"] = append(categories["Niri Components"], dep) - case "kitty", "alacritty", "ghostty", "hyprpicker": - categories["Shared Components"] = append(categories["Shared Components"], dep) - default: - categories["Shared Components"] = append(categories["Shared Components"], dep) - } - } - - return categories -} diff --git a/nix/inputs/dms-cli/internal/errdefs/errdefs.go b/nix/inputs/dms-cli/internal/errdefs/errdefs.go deleted file mode 100644 index 3b5f159..0000000 --- a/nix/inputs/dms-cli/internal/errdefs/errdefs.go +++ /dev/null @@ -1,65 +0,0 @@ -package errdefs - -type ErrorType int - -const ( - ErrTypeNotLinux ErrorType = iota - ErrTypeInvalidArchitecture - ErrTypeUnsupportedDistribution - ErrTypeUnsupportedVersion - ErrTypeUpdateCancelled - ErrTypeNoUpdateNeeded - ErrTypeInvalidTemperature - ErrTypeInvalidGamma - ErrTypeInvalidLocation - ErrTypeInvalidManualTimes - ErrTypeNoWaylandDisplay - ErrTypeNoGammaControl - ErrTypeNotInitialized - ErrTypeSecretPromptCancelled - ErrTypeSecretPromptTimeout - ErrTypeSecretAgentFailed - ErrTypeGeneric -) - -type CustomError struct { - Type ErrorType - Message string -} - -func (e *CustomError) Error() string { - return e.Message -} - -func NewCustomError(errType ErrorType, message string) error { - return &CustomError{ - Type: errType, - Message: message, - } -} - -const ( - ErrBadCredentials = "bad-credentials" - ErrNoSuchSSID = "no-such-ssid" - ErrAssocTimeout = "assoc-timeout" - ErrDhcpTimeout = "dhcp-timeout" - ErrUserCanceled = "user-canceled" - ErrWifiDisabled = "wifi-disabled" - ErrAlreadyConnected = "already-connected" - ErrConnectionFailed = "connection-failed" -) - -var ( - ErrUpdateCancelled = NewCustomError(ErrTypeUpdateCancelled, "update cancelled by user") - ErrNoUpdateNeeded = NewCustomError(ErrTypeNoUpdateNeeded, "no update needed") - ErrInvalidTemperature = NewCustomError(ErrTypeInvalidTemperature, "temperature must be between 1000 and 10000") - ErrInvalidGamma = NewCustomError(ErrTypeInvalidGamma, "gamma must be between 0 and 10") - ErrInvalidLocation = NewCustomError(ErrTypeInvalidLocation, "invalid latitude/longitude") - ErrInvalidManualTimes = NewCustomError(ErrTypeInvalidManualTimes, "both sunrise and sunset must be set or neither") - ErrNoWaylandDisplay = NewCustomError(ErrTypeNoWaylandDisplay, "no wayland display available") - ErrNoGammaControl = NewCustomError(ErrTypeNoGammaControl, "compositor does not support gamma control") - ErrNotInitialized = NewCustomError(ErrTypeNotInitialized, "manager not initialized") - ErrSecretPromptCancelled = NewCustomError(ErrTypeSecretPromptCancelled, "secret prompt cancelled by user") - ErrSecretPromptTimeout = NewCustomError(ErrTypeSecretPromptTimeout, "secret prompt timed out") - ErrSecretAgentFailed = NewCustomError(ErrTypeSecretAgentFailed, "secret agent operation failed") -) diff --git a/nix/inputs/dms-cli/internal/greeter/installer.go b/nix/inputs/dms-cli/internal/greeter/installer.go deleted file mode 100644 index 2d4ecb2..0000000 --- a/nix/inputs/dms-cli/internal/greeter/installer.go +++ /dev/null @@ -1,490 +0,0 @@ -package greeter - -import ( - "bufio" - "context" - "fmt" - "os" - "os/exec" - "path/filepath" - "strings" - - "github.com/AvengeMedia/danklinux/internal/config" - "github.com/AvengeMedia/danklinux/internal/distros" -) - -// DetectDMSPath checks for DMS installation following XDG Base Directory specification -func DetectDMSPath() (string, error) { - return config.LocateDMSConfig() -} - -// DetectCompositors checks which compositors are installed -func DetectCompositors() []string { - var compositors []string - - if commandExists("niri") { - compositors = append(compositors, "niri") - } - if commandExists("Hyprland") { - compositors = append(compositors, "Hyprland") - } - - return compositors -} - -// PromptCompositorChoice asks user to choose between compositors -func PromptCompositorChoice(compositors []string) (string, error) { - fmt.Println("\nMultiple compositors detected:") - for i, comp := range compositors { - fmt.Printf("%d) %s\n", i+1, comp) - } - - reader := bufio.NewReader(os.Stdin) - fmt.Print("Choose compositor for greeter (1-2): ") - response, err := reader.ReadString('\n') - if err != nil { - return "", fmt.Errorf("error reading input: %w", err) - } - - response = strings.TrimSpace(response) - switch response { - case "1": - return compositors[0], nil - case "2": - if len(compositors) > 1 { - return compositors[1], nil - } - return "", fmt.Errorf("invalid choice") - default: - return "", fmt.Errorf("invalid choice") - } -} - -// EnsureGreetdInstalled checks if greetd is installed and installs it if not -func EnsureGreetdInstalled(logFunc func(string), sudoPassword string) error { - if commandExists("greetd") { - logFunc("✓ greetd is already installed") - return nil - } - - logFunc("greetd is not installed. Installing...") - - osInfo, err := distros.GetOSInfo() - if err != nil { - return fmt.Errorf("failed to detect OS: %w", err) - } - - config, exists := distros.Registry[osInfo.Distribution.ID] - if !exists { - return fmt.Errorf("unsupported distribution for automatic greetd installation: %s", osInfo.Distribution.ID) - } - - ctx := context.Background() - var installCmd *exec.Cmd - - switch config.Family { - case distros.FamilyArch: - if sudoPassword != "" { - installCmd = exec.CommandContext(ctx, "bash", "-c", - fmt.Sprintf("echo '%s' | sudo -S pacman -S --needed --noconfirm greetd", sudoPassword)) - } else { - installCmd = exec.CommandContext(ctx, "sudo", "pacman", "-S", "--needed", "--noconfirm", "greetd") - } - - case distros.FamilyFedora: - if sudoPassword != "" { - installCmd = exec.CommandContext(ctx, "bash", "-c", - fmt.Sprintf("echo '%s' | sudo -S dnf install -y greetd", sudoPassword)) - } else { - installCmd = exec.CommandContext(ctx, "sudo", "dnf", "install", "-y", "greetd") - } - - case distros.FamilySUSE: - if sudoPassword != "" { - installCmd = exec.CommandContext(ctx, "bash", "-c", - fmt.Sprintf("echo '%s' | sudo -S zypper install -y greetd", sudoPassword)) - } else { - installCmd = exec.CommandContext(ctx, "sudo", "zypper", "install", "-y", "greetd") - } - - case distros.FamilyUbuntu: - if sudoPassword != "" { - installCmd = exec.CommandContext(ctx, "bash", "-c", - fmt.Sprintf("echo '%s' | sudo -S apt-get install -y greetd", sudoPassword)) - } else { - installCmd = exec.CommandContext(ctx, "sudo", "apt-get", "install", "-y", "greetd") - } - - case distros.FamilyDebian: - if sudoPassword != "" { - installCmd = exec.CommandContext(ctx, "bash", "-c", - fmt.Sprintf("echo '%s' | sudo -S apt-get install -y greetd", sudoPassword)) - } else { - installCmd = exec.CommandContext(ctx, "sudo", "apt-get", "install", "-y", "greetd") - } - - case distros.FamilyNix: - return fmt.Errorf("on NixOS, please add greetd to your configuration.nix") - - default: - return fmt.Errorf("unsupported distribution family for automatic greetd installation: %s", config.Family) - } - - installCmd.Stdout = os.Stdout - installCmd.Stderr = os.Stderr - - if err := installCmd.Run(); err != nil { - return fmt.Errorf("failed to install greetd: %w", err) - } - - logFunc("✓ greetd installed successfully") - return nil -} - -// CopyGreeterFiles installs the dms-greeter wrapper and sets up cache directory -func CopyGreeterFiles(dmsPath, compositor string, logFunc func(string), sudoPassword string) error { - // Check if dms-greeter is already in PATH - if commandExists("dms-greeter") { - logFunc("✓ dms-greeter wrapper already installed") - } else { - // Install the wrapper script - assetsDir := filepath.Join(dmsPath, "Modules", "Greetd", "assets") - wrapperSrc := filepath.Join(assetsDir, "dms-greeter") - - if _, err := os.Stat(wrapperSrc); os.IsNotExist(err) { - return fmt.Errorf("dms-greeter wrapper not found at %s", wrapperSrc) - } - - wrapperDst := "/usr/local/bin/dms-greeter" - if err := runSudoCmd(sudoPassword, "cp", wrapperSrc, wrapperDst); err != nil { - return fmt.Errorf("failed to copy dms-greeter wrapper: %w", err) - } - logFunc(fmt.Sprintf("✓ Installed dms-greeter wrapper to %s", wrapperDst)) - - if err := runSudoCmd(sudoPassword, "chmod", "+x", wrapperDst); err != nil { - return fmt.Errorf("failed to make wrapper executable: %w", err) - } - - // Set SELinux context on Fedora and openSUSE - osInfo, err := distros.GetOSInfo() - if err == nil { - if config, exists := distros.Registry[osInfo.Distribution.ID]; exists && (config.Family == distros.FamilyFedora || config.Family == distros.FamilySUSE) { - if err := runSudoCmd(sudoPassword, "semanage", "fcontext", "-a", "-t", "bin_t", wrapperDst); err != nil { - logFunc(fmt.Sprintf("⚠ Warning: Failed to set SELinux fcontext: %v", err)) - } else { - logFunc("✓ Set SELinux fcontext for dms-greeter") - } - - if err := runSudoCmd(sudoPassword, "restorecon", "-v", wrapperDst); err != nil { - logFunc(fmt.Sprintf("⚠ Warning: Failed to restore SELinux context: %v", err)) - } else { - logFunc("✓ Restored SELinux context for dms-greeter") - } - } - } - } - - // Create cache directory with proper permissions - cacheDir := "/var/cache/dms-greeter" - if err := runSudoCmd(sudoPassword, "mkdir", "-p", cacheDir); err != nil { - return fmt.Errorf("failed to create cache directory: %w", err) - } - - if err := runSudoCmd(sudoPassword, "chown", "greeter:greeter", cacheDir); err != nil { - return fmt.Errorf("failed to set cache directory owner: %w", err) - } - - if err := runSudoCmd(sudoPassword, "chmod", "750", cacheDir); err != nil { - return fmt.Errorf("failed to set cache directory permissions: %w", err) - } - logFunc(fmt.Sprintf("✓ Created cache directory %s (owner: greeter:greeter, permissions: 750)", cacheDir)) - - return nil -} - -// SetupParentDirectoryACLs sets ACLs on parent directories to allow traversal -func SetupParentDirectoryACLs(logFunc func(string), sudoPassword string) error { - if !commandExists("setfacl") { - logFunc("⚠ Warning: setfacl command not found. ACL support may not be available on this filesystem.") - logFunc(" If theme sync doesn't work, you may need to install acl package:") - logFunc(" - Fedora/RHEL: sudo dnf install acl") - logFunc(" - Debian/Ubuntu: sudo apt-get install acl") - logFunc(" - Arch: sudo pacman -S acl") - return nil - } - - homeDir, err := os.UserHomeDir() - if err != nil { - return fmt.Errorf("failed to get user home directory: %w", err) - } - - parentDirs := []struct { - path string - desc string - }{ - {homeDir, "home directory"}, - {filepath.Join(homeDir, ".config"), ".config directory"}, - {filepath.Join(homeDir, ".local"), ".local directory"}, - {filepath.Join(homeDir, ".cache"), ".cache directory"}, - {filepath.Join(homeDir, ".local", "state"), ".local/state directory"}, - } - - logFunc("\nSetting up parent directory ACLs for greeter user access...") - - for _, dir := range parentDirs { - if _, err := os.Stat(dir.path); os.IsNotExist(err) { - if err := os.MkdirAll(dir.path, 0755); err != nil { - logFunc(fmt.Sprintf("⚠ Warning: Could not create %s: %v", dir.desc, err)) - continue - } - } - - // Set ACL to allow greeter user execute (traverse) permission - if err := runSudoCmd(sudoPassword, "setfacl", "-m", "u:greeter:x", dir.path); err != nil { - logFunc(fmt.Sprintf("⚠ Warning: Failed to set ACL on %s: %v", dir.desc, err)) - logFunc(fmt.Sprintf(" You may need to run manually: setfacl -m u:greeter:x %s", dir.path)) - continue - } - - logFunc(fmt.Sprintf("✓ Set ACL on %s", dir.desc)) - } - - return nil -} - -func SetupDMSGroup(logFunc func(string), sudoPassword string) error { - homeDir, err := os.UserHomeDir() - if err != nil { - return fmt.Errorf("failed to get user home directory: %w", err) - } - - currentUser := os.Getenv("USER") - if currentUser == "" { - currentUser = os.Getenv("LOGNAME") - } - if currentUser == "" { - return fmt.Errorf("failed to determine current user") - } - - // Check if user is already in greeter group - groupsCmd := exec.Command("groups", currentUser) - groupsOutput, err := groupsCmd.Output() - if err == nil && strings.Contains(string(groupsOutput), "greeter") { - logFunc(fmt.Sprintf("✓ %s is already in greeter group", currentUser)) - } else { - // Add current user to greeter group for file access permissions - if err := runSudoCmd(sudoPassword, "usermod", "-aG", "greeter", currentUser); err != nil { - return fmt.Errorf("failed to add %s to greeter group: %w", currentUser, err) - } - logFunc(fmt.Sprintf("✓ Added %s to greeter group (logout/login required for changes to take effect)", currentUser)) - } - - configDirs := []struct { - path string - desc string - }{ - {filepath.Join(homeDir, ".config", "DankMaterialShell"), "DankMaterialShell config"}, - {filepath.Join(homeDir, ".local", "state", "DankMaterialShell"), "DankMaterialShell state"}, - {filepath.Join(homeDir, ".cache", "quickshell"), "quickshell cache"}, - {filepath.Join(homeDir, ".config", "quickshell"), "quickshell config"}, - } - - for _, dir := range configDirs { - if _, err := os.Stat(dir.path); os.IsNotExist(err) { - if err := os.MkdirAll(dir.path, 0755); err != nil { - logFunc(fmt.Sprintf("⚠ Warning: Could not create %s: %v", dir.path, err)) - continue - } - } - - if err := runSudoCmd(sudoPassword, "chgrp", "-R", "greeter", dir.path); err != nil { - logFunc(fmt.Sprintf("⚠ Warning: Failed to set group for %s: %v", dir.desc, err)) - continue - } - - if err := runSudoCmd(sudoPassword, "chmod", "-R", "g+rX", dir.path); err != nil { - logFunc(fmt.Sprintf("⚠ Warning: Failed to set permissions for %s: %v", dir.desc, err)) - continue - } - - logFunc(fmt.Sprintf("✓ Set group permissions for %s", dir.desc)) - } - - // Set up ACLs on parent directories to allow greeter user traversal - if err := SetupParentDirectoryACLs(logFunc, sudoPassword); err != nil { - return fmt.Errorf("failed to setup parent directory ACLs: %w", err) - } - - return nil -} - -func SyncDMSConfigs(dmsPath string, logFunc func(string), sudoPassword string) error { - homeDir, err := os.UserHomeDir() - if err != nil { - return fmt.Errorf("failed to get user home directory: %w", err) - } - - cacheDir := "/var/cache/dms-greeter" - - symlinks := []struct { - source string - target string - desc string - }{ - { - source: filepath.Join(homeDir, ".config", "DankMaterialShell", "settings.json"), - target: filepath.Join(cacheDir, "settings.json"), - desc: "core settings (theme, clock formats, etc)", - }, - { - source: filepath.Join(homeDir, ".local", "state", "DankMaterialShell", "session.json"), - target: filepath.Join(cacheDir, "session.json"), - desc: "state (wallpaper configuration)", - }, - { - source: filepath.Join(homeDir, ".cache", "quickshell", "dankshell", "dms-colors.json"), - target: filepath.Join(cacheDir, "colors.json"), - desc: "wallpaper based theming", - }, - } - - for _, link := range symlinks { - sourceDir := filepath.Dir(link.source) - if _, err := os.Stat(sourceDir); os.IsNotExist(err) { - if err := os.MkdirAll(sourceDir, 0755); err != nil { - logFunc(fmt.Sprintf("⚠ Warning: Could not create directory %s: %v", sourceDir, err)) - continue - } - } - - if _, err := os.Stat(link.source); os.IsNotExist(err) { - if err := os.WriteFile(link.source, []byte("{}"), 0644); err != nil { - logFunc(fmt.Sprintf("⚠ Warning: Could not create %s: %v", link.source, err)) - continue - } - } - - runSudoCmd(sudoPassword, "rm", "-f", link.target) - - if err := runSudoCmd(sudoPassword, "ln", "-sf", link.source, link.target); err != nil { - logFunc(fmt.Sprintf("⚠ Warning: Failed to create symlink for %s: %v", link.desc, err)) - continue - } - - logFunc(fmt.Sprintf("✓ Synced %s", link.desc)) - } - - return nil -} - -func ConfigureGreetd(dmsPath, compositor string, logFunc func(string), sudoPassword string) error { - configPath := "/etc/greetd/config.toml" - - if _, err := os.Stat(configPath); err == nil { - backupPath := configPath + ".backup" - if err := runSudoCmd(sudoPassword, "cp", configPath, backupPath); err != nil { - return fmt.Errorf("failed to backup config: %w", err) - } - logFunc(fmt.Sprintf("✓ Backed up existing config to %s", backupPath)) - } - - var configContent string - if data, err := os.ReadFile(configPath); err == nil { - configContent = string(data) - } else { - configContent = `[terminal] -vt = 1 - -[default_session] - -user = "greeter" -` - } - - lines := strings.Split(configContent, "\n") - var newLines []string - for _, line := range lines { - trimmed := strings.TrimSpace(line) - if !strings.HasPrefix(trimmed, "command =") && !strings.HasPrefix(trimmed, "command=") { - if strings.HasPrefix(trimmed, "user =") || strings.HasPrefix(trimmed, "user=") { - newLines = append(newLines, `user = "greeter"`) - } else { - newLines = append(newLines, line) - } - } - } - - // Determine wrapper command path - wrapperCmd := "dms-greeter" - if !commandExists("dms-greeter") { - wrapperCmd = "/usr/local/bin/dms-greeter" - } - - // Build command based on compositor and dms path - compositorLower := strings.ToLower(compositor) - command := fmt.Sprintf(`command = "%s --command %s -p %s"`, wrapperCmd, compositorLower, dmsPath) - - var finalLines []string - inDefaultSession := false - commandAdded := false - - for _, line := range newLines { - finalLines = append(finalLines, line) - trimmed := strings.TrimSpace(line) - - if trimmed == "[default_session]" { - inDefaultSession = true - } - - if inDefaultSession && !commandAdded && trimmed != "" && !strings.HasPrefix(trimmed, "[") { - if !strings.HasPrefix(trimmed, "#") && !strings.HasPrefix(trimmed, "user") { - finalLines = append(finalLines, command) - commandAdded = true - } - } - } - - if !commandAdded { - finalLines = append(finalLines, command) - } - - newConfig := strings.Join(finalLines, "\n") - - tmpFile := "/tmp/greetd-config.toml" - if err := os.WriteFile(tmpFile, []byte(newConfig), 0644); err != nil { - return fmt.Errorf("failed to write temp config: %w", err) - } - - if err := runSudoCmd(sudoPassword, "mv", tmpFile, configPath); err != nil { - return fmt.Errorf("failed to move config to /etc/greetd: %w", err) - } - - logFunc(fmt.Sprintf("✓ Updated greetd configuration (user: greeter, command: %s --command %s -p %s)", wrapperCmd, compositorLower, dmsPath)) - return nil -} - -func runSudoCmd(sudoPassword string, command string, args ...string) error { - var cmd *exec.Cmd - - if sudoPassword != "" { - fullArgs := append([]string{command}, args...) - quotedArgs := make([]string, len(fullArgs)) - for i, arg := range fullArgs { - quotedArgs[i] = "'" + strings.ReplaceAll(arg, "'", "'\\''") + "'" - } - cmdStr := strings.Join(quotedArgs, " ") - - cmd = exec.Command("bash", "-c", fmt.Sprintf("echo '%s' | sudo -S %s", sudoPassword, cmdStr)) - } else { - cmd = exec.Command("sudo", append([]string{command}, args...)...) - } - - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - return cmd.Run() -} - -func commandExists(cmd string) bool { - _, err := exec.LookPath(cmd) - return err == nil -} diff --git a/nix/inputs/dms-cli/internal/hyprland/keybinds.go b/nix/inputs/dms-cli/internal/hyprland/keybinds.go deleted file mode 100644 index 598dba7..0000000 --- a/nix/inputs/dms-cli/internal/hyprland/keybinds.go +++ /dev/null @@ -1,330 +0,0 @@ -package hyprland - -import ( - "os" - "path/filepath" - "regexp" - "strings" -) - -const ( - TitleRegex = "#+!" - HideComment = "[hidden]" - CommentBindPattern = "#/#" -) - -var ModSeparators = []rune{'+', ' '} - -type KeyBinding struct { - Mods []string `json:"mods"` - Key string `json:"key"` - Dispatcher string `json:"dispatcher"` - Params string `json:"params"` - Comment string `json:"comment"` -} - -type Section struct { - Children []Section `json:"children"` - Keybinds []KeyBinding `json:"keybinds"` - Name string `json:"name"` -} - -type Parser struct { - contentLines []string - readingLine int -} - -func NewParser() *Parser { - return &Parser{ - contentLines: []string{}, - readingLine: 0, - } -} - -func (p *Parser) ReadContent(directory string) error { - expandedDir := os.ExpandEnv(directory) - expandedDir = filepath.Clean(expandedDir) - if strings.HasPrefix(expandedDir, "~") { - home, err := os.UserHomeDir() - if err != nil { - return err - } - expandedDir = filepath.Join(home, expandedDir[1:]) - } - - info, err := os.Stat(expandedDir) - if err != nil { - return err - } - if !info.IsDir() { - return os.ErrNotExist - } - - confFiles, err := filepath.Glob(filepath.Join(expandedDir, "*.conf")) - if err != nil { - return err - } - if len(confFiles) == 0 { - return os.ErrNotExist - } - - var combinedContent []string - for _, confFile := range confFiles { - if fileInfo, err := os.Stat(confFile); err == nil && fileInfo.Mode().IsRegular() { - data, err := os.ReadFile(confFile) - if err == nil { - combinedContent = append(combinedContent, string(data)) - } - } - } - - if len(combinedContent) == 0 { - return os.ErrNotExist - } - - fullContent := strings.Join(combinedContent, "\n") - p.contentLines = strings.Split(fullContent, "\n") - return nil -} - -func autogenerateComment(dispatcher, params string) string { - switch dispatcher { - case "resizewindow": - return "Resize window" - - case "movewindow": - if params == "" { - return "Move window" - } - dirMap := map[string]string{ - "l": "left", - "r": "right", - "u": "up", - "d": "down", - } - if dir, ok := dirMap[params]; ok { - return "Window: move in " + dir + " direction" - } - return "Window: move in null direction" - - case "pin": - return "Window: pin (show on all workspaces)" - - case "splitratio": - return "Window split ratio " + params - - case "togglefloating": - return "Float/unfloat window" - - case "resizeactive": - return "Resize window by " + params - - case "killactive": - return "Close window" - - case "fullscreen": - fsMap := map[string]string{ - "0": "fullscreen", - "1": "maximization", - "2": "fullscreen on Hyprland's side", - } - if fs, ok := fsMap[params]; ok { - return "Toggle " + fs - } - return "Toggle null" - - case "fakefullscreen": - return "Toggle fake fullscreen" - - case "workspace": - if params == "+1" { - return "Workspace: focus right" - } else if params == "-1" { - return "Workspace: focus left" - } - return "Focus workspace " + params - - case "movefocus": - dirMap := map[string]string{ - "l": "left", - "r": "right", - "u": "up", - "d": "down", - } - if dir, ok := dirMap[params]; ok { - return "Window: move focus " + dir - } - return "Window: move focus null" - - case "swapwindow": - dirMap := map[string]string{ - "l": "left", - "r": "right", - "u": "up", - "d": "down", - } - if dir, ok := dirMap[params]; ok { - return "Window: swap in " + dir + " direction" - } - return "Window: swap in null direction" - - case "movetoworkspace": - if params == "+1" { - return "Window: move to right workspace (non-silent)" - } else if params == "-1" { - return "Window: move to left workspace (non-silent)" - } - return "Window: move to workspace " + params + " (non-silent)" - - case "movetoworkspacesilent": - if params == "+1" { - return "Window: move to right workspace" - } else if params == "-1" { - return "Window: move to right workspace" - } - return "Window: move to workspace " + params - - case "togglespecialworkspace": - return "Workspace: toggle special" - - case "exec": - return "Execute: " + params - - default: - return "" - } -} - -func (p *Parser) getKeybindAtLine(lineNumber int) *KeyBinding { - line := p.contentLines[lineNumber] - parts := strings.SplitN(line, "=", 2) - if len(parts) < 2 { - return nil - } - - keys := parts[1] - keyParts := strings.SplitN(keys, "#", 2) - keys = keyParts[0] - - var comment string - if len(keyParts) > 1 { - comment = strings.TrimSpace(keyParts[1]) - } - - keyFields := strings.SplitN(keys, ",", 5) - if len(keyFields) < 3 { - return nil - } - - mods := strings.TrimSpace(keyFields[0]) - key := strings.TrimSpace(keyFields[1]) - dispatcher := strings.TrimSpace(keyFields[2]) - - var params string - if len(keyFields) > 3 { - paramParts := keyFields[3:] - params = strings.TrimSpace(strings.Join(paramParts, ",")) - } - - if comment != "" { - if strings.HasPrefix(comment, HideComment) { - return nil - } - } else { - comment = autogenerateComment(dispatcher, params) - } - - var modList []string - if mods != "" { - modstring := mods + string(ModSeparators[0]) - p := 0 - for index, char := range modstring { - isModSep := false - for _, sep := range ModSeparators { - if char == sep { - isModSep = true - break - } - } - if isModSep { - if index-p > 1 { - modList = append(modList, modstring[p:index]) - } - p = index + 1 - } - } - } - - return &KeyBinding{ - Mods: modList, - Key: key, - Dispatcher: dispatcher, - Params: params, - Comment: comment, - } -} - -func (p *Parser) getBindsRecursive(currentContent *Section, scope int) *Section { - titleRegex := regexp.MustCompile(TitleRegex) - - for p.readingLine < len(p.contentLines) { - line := p.contentLines[p.readingLine] - - loc := titleRegex.FindStringIndex(line) - if loc != nil && loc[0] == 0 { - headingScope := strings.Index(line, "!") - - if headingScope <= scope { - p.readingLine-- - return currentContent - } - - sectionName := strings.TrimSpace(line[headingScope+1:]) - p.readingLine++ - - childSection := &Section{ - Children: []Section{}, - Keybinds: []KeyBinding{}, - Name: sectionName, - } - result := p.getBindsRecursive(childSection, headingScope) - currentContent.Children = append(currentContent.Children, *result) - - } else if strings.HasPrefix(line, CommentBindPattern) { - keybind := p.getKeybindAtLine(p.readingLine) - if keybind != nil { - currentContent.Keybinds = append(currentContent.Keybinds, *keybind) - } - - } else if line == "" || !strings.HasPrefix(strings.TrimSpace(line), "bind") { - - } else { - keybind := p.getKeybindAtLine(p.readingLine) - if keybind != nil { - currentContent.Keybinds = append(currentContent.Keybinds, *keybind) - } - } - - p.readingLine++ - } - - return currentContent -} - -func (p *Parser) ParseKeys() *Section { - p.readingLine = 0 - rootSection := &Section{ - Children: []Section{}, - Keybinds: []KeyBinding{}, - Name: "", - } - return p.getBindsRecursive(rootSection, 0) -} - -func ParseKeys(path string) (*Section, error) { - parser := NewParser() - if err := parser.ReadContent(path); err != nil { - return nil, err - } - return parser.ParseKeys(), nil -} diff --git a/nix/inputs/dms-cli/internal/hyprland/keybinds_test.go b/nix/inputs/dms-cli/internal/hyprland/keybinds_test.go deleted file mode 100644 index 05ced84..0000000 --- a/nix/inputs/dms-cli/internal/hyprland/keybinds_test.go +++ /dev/null @@ -1,396 +0,0 @@ -package hyprland - -import ( - "os" - "path/filepath" - "testing" -) - -func TestAutogenerateComment(t *testing.T) { - tests := []struct { - dispatcher string - params string - expected string - }{ - {"resizewindow", "", "Resize window"}, - {"movewindow", "", "Move window"}, - {"movewindow", "l", "Window: move in left direction"}, - {"movewindow", "r", "Window: move in right direction"}, - {"movewindow", "u", "Window: move in up direction"}, - {"movewindow", "d", "Window: move in down direction"}, - {"pin", "", "Window: pin (show on all workspaces)"}, - {"splitratio", "0.5", "Window split ratio 0.5"}, - {"togglefloating", "", "Float/unfloat window"}, - {"resizeactive", "10 20", "Resize window by 10 20"}, - {"killactive", "", "Close window"}, - {"fullscreen", "0", "Toggle fullscreen"}, - {"fullscreen", "1", "Toggle maximization"}, - {"fullscreen", "2", "Toggle fullscreen on Hyprland's side"}, - {"fakefullscreen", "", "Toggle fake fullscreen"}, - {"workspace", "+1", "Workspace: focus right"}, - {"workspace", "-1", "Workspace: focus left"}, - {"workspace", "5", "Focus workspace 5"}, - {"movefocus", "l", "Window: move focus left"}, - {"movefocus", "r", "Window: move focus right"}, - {"movefocus", "u", "Window: move focus up"}, - {"movefocus", "d", "Window: move focus down"}, - {"swapwindow", "l", "Window: swap in left direction"}, - {"swapwindow", "r", "Window: swap in right direction"}, - {"swapwindow", "u", "Window: swap in up direction"}, - {"swapwindow", "d", "Window: swap in down direction"}, - {"movetoworkspace", "+1", "Window: move to right workspace (non-silent)"}, - {"movetoworkspace", "-1", "Window: move to left workspace (non-silent)"}, - {"movetoworkspace", "3", "Window: move to workspace 3 (non-silent)"}, - {"movetoworkspacesilent", "+1", "Window: move to right workspace"}, - {"movetoworkspacesilent", "-1", "Window: move to right workspace"}, - {"movetoworkspacesilent", "2", "Window: move to workspace 2"}, - {"togglespecialworkspace", "", "Workspace: toggle special"}, - {"exec", "firefox", "Execute: firefox"}, - {"unknown", "", ""}, - } - - for _, tt := range tests { - t.Run(tt.dispatcher+"_"+tt.params, func(t *testing.T) { - result := autogenerateComment(tt.dispatcher, tt.params) - if result != tt.expected { - t.Errorf("autogenerateComment(%q, %q) = %q, want %q", - tt.dispatcher, tt.params, result, tt.expected) - } - }) - } -} - -func TestGetKeybindAtLine(t *testing.T) { - tests := []struct { - name string - line string - expected *KeyBinding - }{ - { - name: "basic_keybind", - line: "bind = SUPER, Q, killactive", - expected: &KeyBinding{ - Mods: []string{"SUPER"}, - Key: "Q", - Dispatcher: "killactive", - Params: "", - Comment: "Close window", - }, - }, - { - name: "keybind_with_params", - line: "bind = SUPER, left, movefocus, l", - expected: &KeyBinding{ - Mods: []string{"SUPER"}, - Key: "left", - Dispatcher: "movefocus", - Params: "l", - Comment: "Window: move focus left", - }, - }, - { - name: "keybind_with_comment", - line: "bind = SUPER, T, exec, kitty # Open terminal", - expected: &KeyBinding{ - Mods: []string{"SUPER"}, - Key: "T", - Dispatcher: "exec", - Params: "kitty", - Comment: "Open terminal", - }, - }, - { - name: "keybind_hidden", - line: "bind = SUPER, H, exec, secret # [hidden]", - expected: nil, - }, - { - name: "keybind_multiple_mods", - line: "bind = SUPER+SHIFT, F, fullscreen, 0", - expected: &KeyBinding{ - Mods: []string{"SUPER", "SHIFT"}, - Key: "F", - Dispatcher: "fullscreen", - Params: "0", - Comment: "Toggle fullscreen", - }, - }, - { - name: "keybind_no_mods", - line: "bind = , Print, exec, screenshot", - expected: &KeyBinding{ - Mods: []string{}, - Key: "Print", - Dispatcher: "exec", - Params: "screenshot", - Comment: "Execute: screenshot", - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - parser := NewParser() - parser.contentLines = []string{tt.line} - result := parser.getKeybindAtLine(0) - - if tt.expected == nil { - if result != nil { - t.Errorf("expected nil, got %+v", result) - } - return - } - - if result == nil { - t.Errorf("expected %+v, got nil", tt.expected) - return - } - - if result.Key != tt.expected.Key { - t.Errorf("Key = %q, want %q", result.Key, tt.expected.Key) - } - if result.Dispatcher != tt.expected.Dispatcher { - t.Errorf("Dispatcher = %q, want %q", result.Dispatcher, tt.expected.Dispatcher) - } - if result.Params != tt.expected.Params { - t.Errorf("Params = %q, want %q", result.Params, tt.expected.Params) - } - if result.Comment != tt.expected.Comment { - t.Errorf("Comment = %q, want %q", result.Comment, tt.expected.Comment) - } - if len(result.Mods) != len(tt.expected.Mods) { - t.Errorf("Mods length = %d, want %d", len(result.Mods), len(tt.expected.Mods)) - } else { - for i := range result.Mods { - if result.Mods[i] != tt.expected.Mods[i] { - t.Errorf("Mods[%d] = %q, want %q", i, result.Mods[i], tt.expected.Mods[i]) - } - } - } - }) - } -} - -func TestParseKeysWithSections(t *testing.T) { - tmpDir := t.TempDir() - configFile := filepath.Join(tmpDir, "hyprland.conf") - - content := `##! Window Management -bind = SUPER, Q, killactive -bind = SUPER, F, fullscreen, 0 - -###! Movement -bind = SUPER, left, movefocus, l -bind = SUPER, right, movefocus, r - -##! Applications -bind = SUPER, T, exec, kitty # Terminal -` - - if err := os.WriteFile(configFile, []byte(content), 0644); err != nil { - t.Fatalf("Failed to write test config: %v", err) - } - - section, err := ParseKeys(tmpDir) - if err != nil { - t.Fatalf("ParseKeys failed: %v", err) - } - - if len(section.Children) != 2 { - t.Errorf("Expected 2 top-level sections, got %d", len(section.Children)) - } - - if len(section.Children) >= 1 { - windowMgmt := section.Children[0] - if windowMgmt.Name != "Window Management" { - t.Errorf("First section name = %q, want %q", windowMgmt.Name, "Window Management") - } - if len(windowMgmt.Keybinds) != 2 { - t.Errorf("Window Management keybinds = %d, want 2", len(windowMgmt.Keybinds)) - } - - if len(windowMgmt.Children) != 1 { - t.Errorf("Window Management children = %d, want 1", len(windowMgmt.Children)) - } else { - movement := windowMgmt.Children[0] - if movement.Name != "Movement" { - t.Errorf("Movement section name = %q, want %q", movement.Name, "Movement") - } - if len(movement.Keybinds) != 2 { - t.Errorf("Movement keybinds = %d, want 2", len(movement.Keybinds)) - } - } - } - - if len(section.Children) >= 2 { - apps := section.Children[1] - if apps.Name != "Applications" { - t.Errorf("Second section name = %q, want %q", apps.Name, "Applications") - } - if len(apps.Keybinds) != 1 { - t.Errorf("Applications keybinds = %d, want 1", len(apps.Keybinds)) - } - if len(apps.Keybinds) > 0 && apps.Keybinds[0].Comment != "Terminal" { - t.Errorf("Applications keybind comment = %q, want %q", apps.Keybinds[0].Comment, "Terminal") - } - } -} - -func TestParseKeysWithCommentBinds(t *testing.T) { - tmpDir := t.TempDir() - configFile := filepath.Join(tmpDir, "test.conf") - - content := `#/# = SUPER, A, exec, app1 -bind = SUPER, B, exec, app2 -#/# = SUPER, C, exec, app3 # Custom comment -` - - if err := os.WriteFile(configFile, []byte(content), 0644); err != nil { - t.Fatalf("Failed to write test config: %v", err) - } - - section, err := ParseKeys(tmpDir) - if err != nil { - t.Fatalf("ParseKeys failed: %v", err) - } - - if len(section.Keybinds) != 3 { - t.Errorf("Expected 3 keybinds, got %d", len(section.Keybinds)) - } - - if len(section.Keybinds) > 0 && section.Keybinds[0].Key != "A" { - t.Errorf("First keybind key = %q, want %q", section.Keybinds[0].Key, "A") - } - if len(section.Keybinds) > 1 && section.Keybinds[1].Key != "B" { - t.Errorf("Second keybind key = %q, want %q", section.Keybinds[1].Key, "B") - } - if len(section.Keybinds) > 2 && section.Keybinds[2].Comment != "Custom comment" { - t.Errorf("Third keybind comment = %q, want %q", section.Keybinds[2].Comment, "Custom comment") - } -} - -func TestReadContentMultipleFiles(t *testing.T) { - tmpDir := t.TempDir() - - file1 := filepath.Join(tmpDir, "a.conf") - file2 := filepath.Join(tmpDir, "b.conf") - - content1 := "bind = SUPER, Q, killactive\n" - content2 := "bind = SUPER, T, exec, kitty\n" - - if err := os.WriteFile(file1, []byte(content1), 0644); err != nil { - t.Fatalf("Failed to write file1: %v", err) - } - if err := os.WriteFile(file2, []byte(content2), 0644); err != nil { - t.Fatalf("Failed to write file2: %v", err) - } - - parser := NewParser() - if err := parser.ReadContent(tmpDir); err != nil { - t.Fatalf("ReadContent failed: %v", err) - } - - section := parser.ParseKeys() - if len(section.Keybinds) != 2 { - t.Errorf("Expected 2 keybinds from multiple files, got %d", len(section.Keybinds)) - } -} - -func TestReadContentErrors(t *testing.T) { - tests := []struct { - name string - path string - }{ - { - name: "nonexistent_directory", - path: "/nonexistent/path/that/does/not/exist", - }, - { - name: "empty_directory", - path: t.TempDir(), - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - _, err := ParseKeys(tt.path) - if err == nil { - t.Error("Expected error, got nil") - } - }) - } -} - -func TestReadContentWithTildeExpansion(t *testing.T) { - homeDir, err := os.UserHomeDir() - if err != nil { - t.Skip("Cannot get home directory") - } - - tmpSubdir := filepath.Join(homeDir, ".config", "test-hypr-"+t.Name()) - if err := os.MkdirAll(tmpSubdir, 0755); err != nil { - t.Skip("Cannot create test directory in home") - } - defer os.RemoveAll(tmpSubdir) - - configFile := filepath.Join(tmpSubdir, "test.conf") - if err := os.WriteFile(configFile, []byte("bind = SUPER, Q, killactive\n"), 0644); err != nil { - t.Fatalf("Failed to write test config: %v", err) - } - - relPath, err := filepath.Rel(homeDir, tmpSubdir) - if err != nil { - t.Skip("Cannot create relative path") - } - - parser := NewParser() - tildePathMatch := "~/" + relPath - err = parser.ReadContent(tildePathMatch) - - if err != nil { - t.Errorf("ReadContent with tilde path failed: %v", err) - } -} - -func TestKeybindWithParamsContainingCommas(t *testing.T) { - parser := NewParser() - parser.contentLines = []string{"bind = SUPER, R, exec, notify-send 'Title' 'Message, with comma'"} - - result := parser.getKeybindAtLine(0) - - if result == nil { - t.Fatal("Expected keybind, got nil") - } - - expected := "notify-send 'Title' 'Message, with comma'" - if result.Params != expected { - t.Errorf("Params = %q, want %q", result.Params, expected) - } -} - -func TestEmptyAndCommentLines(t *testing.T) { - tmpDir := t.TempDir() - configFile := filepath.Join(tmpDir, "test.conf") - - content := ` -# This is a comment -bind = SUPER, Q, killactive - -# Another comment - -bind = SUPER, T, exec, kitty -` - - if err := os.WriteFile(configFile, []byte(content), 0644); err != nil { - t.Fatalf("Failed to write test config: %v", err) - } - - section, err := ParseKeys(tmpDir) - if err != nil { - t.Fatalf("ParseKeys failed: %v", err) - } - - if len(section.Keybinds) != 2 { - t.Errorf("Expected 2 keybinds (comments ignored), got %d", len(section.Keybinds)) - } -} diff --git a/nix/inputs/dms-cli/internal/log/log.go b/nix/inputs/dms-cli/internal/log/log.go deleted file mode 100644 index ba446e0..0000000 --- a/nix/inputs/dms-cli/internal/log/log.go +++ /dev/null @@ -1,116 +0,0 @@ -package log - -import ( - "os" - "strings" - "sync" - - "github.com/charmbracelet/lipgloss" - cblog "github.com/charmbracelet/log" -) - -// Logger embeds the Charm Logger and adds Printf/Fatalf -type Logger struct{ *cblog.Logger } - -// Printf routes goose/info-style logs through Infof. -func (l *Logger) Printf(format string, v ...interface{}) { l.Infof(format, v...) } - -// Fatalf keeps goose’s contract of exiting the program. -func (l *Logger) Fatalf(format string, v ...interface{}) { l.Logger.Fatalf(format, v...) } - -var ( - logger *Logger - initLogger sync.Once -) - -func parseLogLevel(level string) cblog.Level { - switch strings.ToLower(level) { - case "debug": - return cblog.DebugLevel - case "info": - return cblog.InfoLevel - case "warn", "warning": - return cblog.WarnLevel - case "error": - return cblog.ErrorLevel - case "fatal": - return cblog.FatalLevel - default: - return cblog.InfoLevel - } -} - -func GetQtLoggingRules() string { - level := os.Getenv("DMS_LOG_LEVEL") - if level == "" { - level = "info" - } - - var rules []string - switch strings.ToLower(level) { - case "fatal": - rules = []string{"*.debug=false", "*.info=false", "*.warning=false", "*.critical=false"} - case "error": - rules = []string{"*.debug=false", "*.info=false", "*.warning=false"} - case "warn", "warning": - rules = []string{"*.debug=false", "*.info=false"} - case "info": - rules = []string{"*.debug=false"} - case "debug": - return "" - default: - rules = []string{"*.debug=false"} - } - - return strings.Join(rules, ";") -} - -// GetLogger returns a logger instance -func GetLogger() *Logger { - initLogger.Do(func() { - styles := cblog.DefaultStyles() - // Attempt to match the colors used by qml/quickshell logs - styles.Levels[cblog.FatalLevel] = lipgloss.NewStyle(). - SetString(" FATAL"). - Foreground(lipgloss.Color("1")) - styles.Levels[cblog.ErrorLevel] = lipgloss.NewStyle(). - SetString(" ERROR"). - Foreground(lipgloss.Color("9")) - styles.Levels[cblog.WarnLevel] = lipgloss.NewStyle(). - SetString(" WARN"). - Foreground(lipgloss.Color("3")) - styles.Levels[cblog.InfoLevel] = lipgloss.NewStyle(). - SetString(" INFO"). - Foreground(lipgloss.Color("2")) - styles.Levels[cblog.DebugLevel] = lipgloss.NewStyle(). - SetString(" DEBUG"). - Foreground(lipgloss.Color("4")) - - base := cblog.New(os.Stderr) - base.SetStyles(styles) - base.SetReportTimestamp(false) - - level := cblog.InfoLevel - if envLevel := os.Getenv("DMS_LOG_LEVEL"); envLevel != "" { - level = parseLogLevel(envLevel) - } - base.SetLevel(level) - base.SetPrefix(" go") - - logger = &Logger{base} - }) - return logger -} - -// * Convenience wrappers - -func Debug(msg interface{}, keyvals ...interface{}) { GetLogger().Logger.Debug(msg, keyvals...) } -func Debugf(format string, v ...interface{}) { GetLogger().Logger.Debugf(format, v...) } -func Info(msg interface{}, keyvals ...interface{}) { GetLogger().Logger.Info(msg, keyvals...) } -func Infof(format string, v ...interface{}) { GetLogger().Logger.Infof(format, v...) } -func Warn(msg interface{}, keyvals ...interface{}) { GetLogger().Logger.Warn(msg, keyvals...) } -func Warnf(format string, v ...interface{}) { GetLogger().Logger.Warnf(format, v...) } -func Error(msg interface{}, keyvals ...interface{}) { GetLogger().Logger.Error(msg, keyvals...) } -func Errorf(format string, v ...interface{}) { GetLogger().Logger.Errorf(format, v...) } -func Fatal(msg interface{}, keyvals ...interface{}) { GetLogger().Logger.Fatal(msg, keyvals...) } -func Fatalf(format string, v ...interface{}) { GetLogger().Logger.Fatalf(format, v...) } diff --git a/nix/inputs/dms-cli/internal/mocks/brightness/mock_DBusConn.go b/nix/inputs/dms-cli/internal/mocks/brightness/mock_DBusConn.go deleted file mode 100644 index 63acf4f..0000000 --- a/nix/inputs/dms-cli/internal/mocks/brightness/mock_DBusConn.go +++ /dev/null @@ -1,129 +0,0 @@ -// Code generated by mockery v2.53.5. DO NOT EDIT. - -package mocks_brightness - -import ( - dbus "github.com/godbus/dbus/v5" - mock "github.com/stretchr/testify/mock" -) - -// MockDBusConn is an autogenerated mock type for the DBusConn type -type MockDBusConn struct { - mock.Mock -} - -type MockDBusConn_Expecter struct { - mock *mock.Mock -} - -func (_m *MockDBusConn) EXPECT() *MockDBusConn_Expecter { - return &MockDBusConn_Expecter{mock: &_m.Mock} -} - -// Close provides a mock function with no fields -func (_m *MockDBusConn) Close() error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Close") - } - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockDBusConn_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close' -type MockDBusConn_Close_Call struct { - *mock.Call -} - -// Close is a helper method to define mock.On call -func (_e *MockDBusConn_Expecter) Close() *MockDBusConn_Close_Call { - return &MockDBusConn_Close_Call{Call: _e.mock.On("Close")} -} - -func (_c *MockDBusConn_Close_Call) Run(run func()) *MockDBusConn_Close_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDBusConn_Close_Call) Return(_a0 error) *MockDBusConn_Close_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockDBusConn_Close_Call) RunAndReturn(run func() error) *MockDBusConn_Close_Call { - _c.Call.Return(run) - return _c -} - -// Object provides a mock function with given fields: dest, path -func (_m *MockDBusConn) Object(dest string, path dbus.ObjectPath) dbus.BusObject { - ret := _m.Called(dest, path) - - if len(ret) == 0 { - panic("no return value specified for Object") - } - - var r0 dbus.BusObject - if rf, ok := ret.Get(0).(func(string, dbus.ObjectPath) dbus.BusObject); ok { - r0 = rf(dest, path) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(dbus.BusObject) - } - } - - return r0 -} - -// MockDBusConn_Object_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Object' -type MockDBusConn_Object_Call struct { - *mock.Call -} - -// Object is a helper method to define mock.On call -// - dest string -// - path dbus.ObjectPath -func (_e *MockDBusConn_Expecter) Object(dest interface{}, path interface{}) *MockDBusConn_Object_Call { - return &MockDBusConn_Object_Call{Call: _e.mock.On("Object", dest, path)} -} - -func (_c *MockDBusConn_Object_Call) Run(run func(dest string, path dbus.ObjectPath)) *MockDBusConn_Object_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(dbus.ObjectPath)) - }) - return _c -} - -func (_c *MockDBusConn_Object_Call) Return(_a0 dbus.BusObject) *MockDBusConn_Object_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockDBusConn_Object_Call) RunAndReturn(run func(string, dbus.ObjectPath) dbus.BusObject) *MockDBusConn_Object_Call { - _c.Call.Return(run) - return _c -} - -// NewMockDBusConn creates a new instance of MockDBusConn. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewMockDBusConn(t interface { - mock.TestingT - Cleanup(func()) -}) *MockDBusConn { - mock := &MockDBusConn{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/nix/inputs/dms-cli/internal/mocks/cups/mock_CUPSClientInterface.go b/nix/inputs/dms-cli/internal/mocks/cups/mock_CUPSClientInterface.go deleted file mode 100644 index a5dcd90..0000000 --- a/nix/inputs/dms-cli/internal/mocks/cups/mock_CUPSClientInterface.go +++ /dev/null @@ -1,405 +0,0 @@ -// Code generated by mockery v2.53.5. DO NOT EDIT. - -package mocks_cups - -import ( - io "io" - - ipp "github.com/AvengeMedia/danklinux/pkg/ipp" - mock "github.com/stretchr/testify/mock" -) - -// MockCUPSClientInterface is an autogenerated mock type for the CUPSClientInterface type -type MockCUPSClientInterface struct { - mock.Mock -} - -type MockCUPSClientInterface_Expecter struct { - mock *mock.Mock -} - -func (_m *MockCUPSClientInterface) EXPECT() *MockCUPSClientInterface_Expecter { - return &MockCUPSClientInterface_Expecter{mock: &_m.Mock} -} - -// CancelAllJob provides a mock function with given fields: printer, purge -func (_m *MockCUPSClientInterface) CancelAllJob(printer string, purge bool) error { - ret := _m.Called(printer, purge) - - if len(ret) == 0 { - panic("no return value specified for CancelAllJob") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string, bool) error); ok { - r0 = rf(printer, purge) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockCUPSClientInterface_CancelAllJob_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CancelAllJob' -type MockCUPSClientInterface_CancelAllJob_Call struct { - *mock.Call -} - -// CancelAllJob is a helper method to define mock.On call -// - printer string -// - purge bool -func (_e *MockCUPSClientInterface_Expecter) CancelAllJob(printer interface{}, purge interface{}) *MockCUPSClientInterface_CancelAllJob_Call { - return &MockCUPSClientInterface_CancelAllJob_Call{Call: _e.mock.On("CancelAllJob", printer, purge)} -} - -func (_c *MockCUPSClientInterface_CancelAllJob_Call) Run(run func(printer string, purge bool)) *MockCUPSClientInterface_CancelAllJob_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(bool)) - }) - return _c -} - -func (_c *MockCUPSClientInterface_CancelAllJob_Call) Return(_a0 error) *MockCUPSClientInterface_CancelAllJob_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockCUPSClientInterface_CancelAllJob_Call) RunAndReturn(run func(string, bool) error) *MockCUPSClientInterface_CancelAllJob_Call { - _c.Call.Return(run) - return _c -} - -// CancelJob provides a mock function with given fields: jobID, purge -func (_m *MockCUPSClientInterface) CancelJob(jobID int, purge bool) error { - ret := _m.Called(jobID, purge) - - if len(ret) == 0 { - panic("no return value specified for CancelJob") - } - - var r0 error - if rf, ok := ret.Get(0).(func(int, bool) error); ok { - r0 = rf(jobID, purge) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockCUPSClientInterface_CancelJob_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CancelJob' -type MockCUPSClientInterface_CancelJob_Call struct { - *mock.Call -} - -// CancelJob is a helper method to define mock.On call -// - jobID int -// - purge bool -func (_e *MockCUPSClientInterface_Expecter) CancelJob(jobID interface{}, purge interface{}) *MockCUPSClientInterface_CancelJob_Call { - return &MockCUPSClientInterface_CancelJob_Call{Call: _e.mock.On("CancelJob", jobID, purge)} -} - -func (_c *MockCUPSClientInterface_CancelJob_Call) Run(run func(jobID int, purge bool)) *MockCUPSClientInterface_CancelJob_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(int), args[1].(bool)) - }) - return _c -} - -func (_c *MockCUPSClientInterface_CancelJob_Call) Return(_a0 error) *MockCUPSClientInterface_CancelJob_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockCUPSClientInterface_CancelJob_Call) RunAndReturn(run func(int, bool) error) *MockCUPSClientInterface_CancelJob_Call { - _c.Call.Return(run) - return _c -} - -// GetJobs provides a mock function with given fields: printer, class, whichJobs, myJobs, firstJobId, limit, attributes -func (_m *MockCUPSClientInterface) GetJobs(printer string, class string, whichJobs string, myJobs bool, firstJobId int, limit int, attributes []string) (map[int]ipp.Attributes, error) { - ret := _m.Called(printer, class, whichJobs, myJobs, firstJobId, limit, attributes) - - if len(ret) == 0 { - panic("no return value specified for GetJobs") - } - - var r0 map[int]ipp.Attributes - var r1 error - if rf, ok := ret.Get(0).(func(string, string, string, bool, int, int, []string) (map[int]ipp.Attributes, error)); ok { - return rf(printer, class, whichJobs, myJobs, firstJobId, limit, attributes) - } - if rf, ok := ret.Get(0).(func(string, string, string, bool, int, int, []string) map[int]ipp.Attributes); ok { - r0 = rf(printer, class, whichJobs, myJobs, firstJobId, limit, attributes) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(map[int]ipp.Attributes) - } - } - - if rf, ok := ret.Get(1).(func(string, string, string, bool, int, int, []string) error); ok { - r1 = rf(printer, class, whichJobs, myJobs, firstJobId, limit, attributes) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockCUPSClientInterface_GetJobs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetJobs' -type MockCUPSClientInterface_GetJobs_Call struct { - *mock.Call -} - -// GetJobs is a helper method to define mock.On call -// - printer string -// - class string -// - whichJobs string -// - myJobs bool -// - firstJobId int -// - limit int -// - attributes []string -func (_e *MockCUPSClientInterface_Expecter) GetJobs(printer interface{}, class interface{}, whichJobs interface{}, myJobs interface{}, firstJobId interface{}, limit interface{}, attributes interface{}) *MockCUPSClientInterface_GetJobs_Call { - return &MockCUPSClientInterface_GetJobs_Call{Call: _e.mock.On("GetJobs", printer, class, whichJobs, myJobs, firstJobId, limit, attributes)} -} - -func (_c *MockCUPSClientInterface_GetJobs_Call) Run(run func(printer string, class string, whichJobs string, myJobs bool, firstJobId int, limit int, attributes []string)) *MockCUPSClientInterface_GetJobs_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(string), args[2].(string), args[3].(bool), args[4].(int), args[5].(int), args[6].([]string)) - }) - return _c -} - -func (_c *MockCUPSClientInterface_GetJobs_Call) Return(_a0 map[int]ipp.Attributes, _a1 error) *MockCUPSClientInterface_GetJobs_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockCUPSClientInterface_GetJobs_Call) RunAndReturn(run func(string, string, string, bool, int, int, []string) (map[int]ipp.Attributes, error)) *MockCUPSClientInterface_GetJobs_Call { - _c.Call.Return(run) - return _c -} - -// GetPrinters provides a mock function with given fields: attributes -func (_m *MockCUPSClientInterface) GetPrinters(attributes []string) (map[string]ipp.Attributes, error) { - ret := _m.Called(attributes) - - if len(ret) == 0 { - panic("no return value specified for GetPrinters") - } - - var r0 map[string]ipp.Attributes - var r1 error - if rf, ok := ret.Get(0).(func([]string) (map[string]ipp.Attributes, error)); ok { - return rf(attributes) - } - if rf, ok := ret.Get(0).(func([]string) map[string]ipp.Attributes); ok { - r0 = rf(attributes) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(map[string]ipp.Attributes) - } - } - - if rf, ok := ret.Get(1).(func([]string) error); ok { - r1 = rf(attributes) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockCUPSClientInterface_GetPrinters_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPrinters' -type MockCUPSClientInterface_GetPrinters_Call struct { - *mock.Call -} - -// GetPrinters is a helper method to define mock.On call -// - attributes []string -func (_e *MockCUPSClientInterface_Expecter) GetPrinters(attributes interface{}) *MockCUPSClientInterface_GetPrinters_Call { - return &MockCUPSClientInterface_GetPrinters_Call{Call: _e.mock.On("GetPrinters", attributes)} -} - -func (_c *MockCUPSClientInterface_GetPrinters_Call) Run(run func(attributes []string)) *MockCUPSClientInterface_GetPrinters_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].([]string)) - }) - return _c -} - -func (_c *MockCUPSClientInterface_GetPrinters_Call) Return(_a0 map[string]ipp.Attributes, _a1 error) *MockCUPSClientInterface_GetPrinters_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockCUPSClientInterface_GetPrinters_Call) RunAndReturn(run func([]string) (map[string]ipp.Attributes, error)) *MockCUPSClientInterface_GetPrinters_Call { - _c.Call.Return(run) - return _c -} - -// PausePrinter provides a mock function with given fields: printer -func (_m *MockCUPSClientInterface) PausePrinter(printer string) error { - ret := _m.Called(printer) - - if len(ret) == 0 { - panic("no return value specified for PausePrinter") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string) error); ok { - r0 = rf(printer) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockCUPSClientInterface_PausePrinter_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PausePrinter' -type MockCUPSClientInterface_PausePrinter_Call struct { - *mock.Call -} - -// PausePrinter is a helper method to define mock.On call -// - printer string -func (_e *MockCUPSClientInterface_Expecter) PausePrinter(printer interface{}) *MockCUPSClientInterface_PausePrinter_Call { - return &MockCUPSClientInterface_PausePrinter_Call{Call: _e.mock.On("PausePrinter", printer)} -} - -func (_c *MockCUPSClientInterface_PausePrinter_Call) Run(run func(printer string)) *MockCUPSClientInterface_PausePrinter_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *MockCUPSClientInterface_PausePrinter_Call) Return(_a0 error) *MockCUPSClientInterface_PausePrinter_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockCUPSClientInterface_PausePrinter_Call) RunAndReturn(run func(string) error) *MockCUPSClientInterface_PausePrinter_Call { - _c.Call.Return(run) - return _c -} - -// ResumePrinter provides a mock function with given fields: printer -func (_m *MockCUPSClientInterface) ResumePrinter(printer string) error { - ret := _m.Called(printer) - - if len(ret) == 0 { - panic("no return value specified for ResumePrinter") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string) error); ok { - r0 = rf(printer) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockCUPSClientInterface_ResumePrinter_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ResumePrinter' -type MockCUPSClientInterface_ResumePrinter_Call struct { - *mock.Call -} - -// ResumePrinter is a helper method to define mock.On call -// - printer string -func (_e *MockCUPSClientInterface_Expecter) ResumePrinter(printer interface{}) *MockCUPSClientInterface_ResumePrinter_Call { - return &MockCUPSClientInterface_ResumePrinter_Call{Call: _e.mock.On("ResumePrinter", printer)} -} - -func (_c *MockCUPSClientInterface_ResumePrinter_Call) Run(run func(printer string)) *MockCUPSClientInterface_ResumePrinter_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *MockCUPSClientInterface_ResumePrinter_Call) Return(_a0 error) *MockCUPSClientInterface_ResumePrinter_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockCUPSClientInterface_ResumePrinter_Call) RunAndReturn(run func(string) error) *MockCUPSClientInterface_ResumePrinter_Call { - _c.Call.Return(run) - return _c -} - -// SendRequest provides a mock function with given fields: url, req, additionalResponseData -func (_m *MockCUPSClientInterface) SendRequest(url string, req *ipp.Request, additionalResponseData io.Writer) (*ipp.Response, error) { - ret := _m.Called(url, req, additionalResponseData) - - if len(ret) == 0 { - panic("no return value specified for SendRequest") - } - - var r0 *ipp.Response - var r1 error - if rf, ok := ret.Get(0).(func(string, *ipp.Request, io.Writer) (*ipp.Response, error)); ok { - return rf(url, req, additionalResponseData) - } - if rf, ok := ret.Get(0).(func(string, *ipp.Request, io.Writer) *ipp.Response); ok { - r0 = rf(url, req, additionalResponseData) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*ipp.Response) - } - } - - if rf, ok := ret.Get(1).(func(string, *ipp.Request, io.Writer) error); ok { - r1 = rf(url, req, additionalResponseData) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockCUPSClientInterface_SendRequest_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SendRequest' -type MockCUPSClientInterface_SendRequest_Call struct { - *mock.Call -} - -// SendRequest is a helper method to define mock.On call -// - url string -// - req *ipp.Request -// - additionalResponseData io.Writer -func (_e *MockCUPSClientInterface_Expecter) SendRequest(url interface{}, req interface{}, additionalResponseData interface{}) *MockCUPSClientInterface_SendRequest_Call { - return &MockCUPSClientInterface_SendRequest_Call{Call: _e.mock.On("SendRequest", url, req, additionalResponseData)} -} - -func (_c *MockCUPSClientInterface_SendRequest_Call) Run(run func(url string, req *ipp.Request, additionalResponseData io.Writer)) *MockCUPSClientInterface_SendRequest_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(*ipp.Request), args[2].(io.Writer)) - }) - return _c -} - -func (_c *MockCUPSClientInterface_SendRequest_Call) Return(_a0 *ipp.Response, _a1 error) *MockCUPSClientInterface_SendRequest_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockCUPSClientInterface_SendRequest_Call) RunAndReturn(run func(string, *ipp.Request, io.Writer) (*ipp.Response, error)) *MockCUPSClientInterface_SendRequest_Call { - _c.Call.Return(run) - return _c -} - -// NewMockCUPSClientInterface creates a new instance of MockCUPSClientInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewMockCUPSClientInterface(t interface { - mock.TestingT - Cleanup(func()) -}) *MockCUPSClientInterface { - mock := &MockCUPSClientInterface{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/nix/inputs/dms-cli/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_AccessPoint.go b/nix/inputs/dms-cli/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_AccessPoint.go deleted file mode 100644 index ae5124d..0000000 --- a/nix/inputs/dms-cli/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_AccessPoint.go +++ /dev/null @@ -1,689 +0,0 @@ -// Code generated by mockery v2.53.5. DO NOT EDIT. - -package gonetworkmanager - -import ( - gonetworkmanager "github.com/Wifx/gonetworkmanager/v2" - dbus "github.com/godbus/dbus/v5" - - mock "github.com/stretchr/testify/mock" -) - -// MockAccessPoint is an autogenerated mock type for the AccessPoint type -type MockAccessPoint struct { - mock.Mock -} - -type MockAccessPoint_Expecter struct { - mock *mock.Mock -} - -func (_m *MockAccessPoint) EXPECT() *MockAccessPoint_Expecter { - return &MockAccessPoint_Expecter{mock: &_m.Mock} -} - -// GetPath provides a mock function with no fields -func (_m *MockAccessPoint) GetPath() dbus.ObjectPath { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPath") - } - - var r0 dbus.ObjectPath - if rf, ok := ret.Get(0).(func() dbus.ObjectPath); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(dbus.ObjectPath) - } - - return r0 -} - -// MockAccessPoint_GetPath_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPath' -type MockAccessPoint_GetPath_Call struct { - *mock.Call -} - -// GetPath is a helper method to define mock.On call -func (_e *MockAccessPoint_Expecter) GetPath() *MockAccessPoint_GetPath_Call { - return &MockAccessPoint_GetPath_Call{Call: _e.mock.On("GetPath")} -} - -func (_c *MockAccessPoint_GetPath_Call) Run(run func()) *MockAccessPoint_GetPath_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockAccessPoint_GetPath_Call) Return(_a0 dbus.ObjectPath) *MockAccessPoint_GetPath_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockAccessPoint_GetPath_Call) RunAndReturn(run func() dbus.ObjectPath) *MockAccessPoint_GetPath_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyFlags provides a mock function with no fields -func (_m *MockAccessPoint) GetPropertyFlags() (uint32, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyFlags") - } - - var r0 uint32 - var r1 error - if rf, ok := ret.Get(0).(func() (uint32, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() uint32); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(uint32) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockAccessPoint_GetPropertyFlags_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyFlags' -type MockAccessPoint_GetPropertyFlags_Call struct { - *mock.Call -} - -// GetPropertyFlags is a helper method to define mock.On call -func (_e *MockAccessPoint_Expecter) GetPropertyFlags() *MockAccessPoint_GetPropertyFlags_Call { - return &MockAccessPoint_GetPropertyFlags_Call{Call: _e.mock.On("GetPropertyFlags")} -} - -func (_c *MockAccessPoint_GetPropertyFlags_Call) Run(run func()) *MockAccessPoint_GetPropertyFlags_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockAccessPoint_GetPropertyFlags_Call) Return(_a0 uint32, _a1 error) *MockAccessPoint_GetPropertyFlags_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockAccessPoint_GetPropertyFlags_Call) RunAndReturn(run func() (uint32, error)) *MockAccessPoint_GetPropertyFlags_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyFrequency provides a mock function with no fields -func (_m *MockAccessPoint) GetPropertyFrequency() (uint32, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyFrequency") - } - - var r0 uint32 - var r1 error - if rf, ok := ret.Get(0).(func() (uint32, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() uint32); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(uint32) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockAccessPoint_GetPropertyFrequency_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyFrequency' -type MockAccessPoint_GetPropertyFrequency_Call struct { - *mock.Call -} - -// GetPropertyFrequency is a helper method to define mock.On call -func (_e *MockAccessPoint_Expecter) GetPropertyFrequency() *MockAccessPoint_GetPropertyFrequency_Call { - return &MockAccessPoint_GetPropertyFrequency_Call{Call: _e.mock.On("GetPropertyFrequency")} -} - -func (_c *MockAccessPoint_GetPropertyFrequency_Call) Run(run func()) *MockAccessPoint_GetPropertyFrequency_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockAccessPoint_GetPropertyFrequency_Call) Return(_a0 uint32, _a1 error) *MockAccessPoint_GetPropertyFrequency_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockAccessPoint_GetPropertyFrequency_Call) RunAndReturn(run func() (uint32, error)) *MockAccessPoint_GetPropertyFrequency_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyHWAddress provides a mock function with no fields -func (_m *MockAccessPoint) GetPropertyHWAddress() (string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyHWAddress") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func() (string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockAccessPoint_GetPropertyHWAddress_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyHWAddress' -type MockAccessPoint_GetPropertyHWAddress_Call struct { - *mock.Call -} - -// GetPropertyHWAddress is a helper method to define mock.On call -func (_e *MockAccessPoint_Expecter) GetPropertyHWAddress() *MockAccessPoint_GetPropertyHWAddress_Call { - return &MockAccessPoint_GetPropertyHWAddress_Call{Call: _e.mock.On("GetPropertyHWAddress")} -} - -func (_c *MockAccessPoint_GetPropertyHWAddress_Call) Run(run func()) *MockAccessPoint_GetPropertyHWAddress_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockAccessPoint_GetPropertyHWAddress_Call) Return(_a0 string, _a1 error) *MockAccessPoint_GetPropertyHWAddress_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockAccessPoint_GetPropertyHWAddress_Call) RunAndReturn(run func() (string, error)) *MockAccessPoint_GetPropertyHWAddress_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyLastSeen provides a mock function with no fields -func (_m *MockAccessPoint) GetPropertyLastSeen() (int32, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyLastSeen") - } - - var r0 int32 - var r1 error - if rf, ok := ret.Get(0).(func() (int32, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() int32); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(int32) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockAccessPoint_GetPropertyLastSeen_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyLastSeen' -type MockAccessPoint_GetPropertyLastSeen_Call struct { - *mock.Call -} - -// GetPropertyLastSeen is a helper method to define mock.On call -func (_e *MockAccessPoint_Expecter) GetPropertyLastSeen() *MockAccessPoint_GetPropertyLastSeen_Call { - return &MockAccessPoint_GetPropertyLastSeen_Call{Call: _e.mock.On("GetPropertyLastSeen")} -} - -func (_c *MockAccessPoint_GetPropertyLastSeen_Call) Run(run func()) *MockAccessPoint_GetPropertyLastSeen_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockAccessPoint_GetPropertyLastSeen_Call) Return(_a0 int32, _a1 error) *MockAccessPoint_GetPropertyLastSeen_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockAccessPoint_GetPropertyLastSeen_Call) RunAndReturn(run func() (int32, error)) *MockAccessPoint_GetPropertyLastSeen_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyMaxBitrate provides a mock function with no fields -func (_m *MockAccessPoint) GetPropertyMaxBitrate() (uint32, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyMaxBitrate") - } - - var r0 uint32 - var r1 error - if rf, ok := ret.Get(0).(func() (uint32, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() uint32); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(uint32) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockAccessPoint_GetPropertyMaxBitrate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyMaxBitrate' -type MockAccessPoint_GetPropertyMaxBitrate_Call struct { - *mock.Call -} - -// GetPropertyMaxBitrate is a helper method to define mock.On call -func (_e *MockAccessPoint_Expecter) GetPropertyMaxBitrate() *MockAccessPoint_GetPropertyMaxBitrate_Call { - return &MockAccessPoint_GetPropertyMaxBitrate_Call{Call: _e.mock.On("GetPropertyMaxBitrate")} -} - -func (_c *MockAccessPoint_GetPropertyMaxBitrate_Call) Run(run func()) *MockAccessPoint_GetPropertyMaxBitrate_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockAccessPoint_GetPropertyMaxBitrate_Call) Return(_a0 uint32, _a1 error) *MockAccessPoint_GetPropertyMaxBitrate_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockAccessPoint_GetPropertyMaxBitrate_Call) RunAndReturn(run func() (uint32, error)) *MockAccessPoint_GetPropertyMaxBitrate_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyMode provides a mock function with no fields -func (_m *MockAccessPoint) GetPropertyMode() (gonetworkmanager.Nm80211Mode, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyMode") - } - - var r0 gonetworkmanager.Nm80211Mode - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.Nm80211Mode, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.Nm80211Mode); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(gonetworkmanager.Nm80211Mode) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockAccessPoint_GetPropertyMode_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyMode' -type MockAccessPoint_GetPropertyMode_Call struct { - *mock.Call -} - -// GetPropertyMode is a helper method to define mock.On call -func (_e *MockAccessPoint_Expecter) GetPropertyMode() *MockAccessPoint_GetPropertyMode_Call { - return &MockAccessPoint_GetPropertyMode_Call{Call: _e.mock.On("GetPropertyMode")} -} - -func (_c *MockAccessPoint_GetPropertyMode_Call) Run(run func()) *MockAccessPoint_GetPropertyMode_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockAccessPoint_GetPropertyMode_Call) Return(_a0 gonetworkmanager.Nm80211Mode, _a1 error) *MockAccessPoint_GetPropertyMode_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockAccessPoint_GetPropertyMode_Call) RunAndReturn(run func() (gonetworkmanager.Nm80211Mode, error)) *MockAccessPoint_GetPropertyMode_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyRSNFlags provides a mock function with no fields -func (_m *MockAccessPoint) GetPropertyRSNFlags() (uint32, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyRSNFlags") - } - - var r0 uint32 - var r1 error - if rf, ok := ret.Get(0).(func() (uint32, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() uint32); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(uint32) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockAccessPoint_GetPropertyRSNFlags_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyRSNFlags' -type MockAccessPoint_GetPropertyRSNFlags_Call struct { - *mock.Call -} - -// GetPropertyRSNFlags is a helper method to define mock.On call -func (_e *MockAccessPoint_Expecter) GetPropertyRSNFlags() *MockAccessPoint_GetPropertyRSNFlags_Call { - return &MockAccessPoint_GetPropertyRSNFlags_Call{Call: _e.mock.On("GetPropertyRSNFlags")} -} - -func (_c *MockAccessPoint_GetPropertyRSNFlags_Call) Run(run func()) *MockAccessPoint_GetPropertyRSNFlags_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockAccessPoint_GetPropertyRSNFlags_Call) Return(_a0 uint32, _a1 error) *MockAccessPoint_GetPropertyRSNFlags_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockAccessPoint_GetPropertyRSNFlags_Call) RunAndReturn(run func() (uint32, error)) *MockAccessPoint_GetPropertyRSNFlags_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertySSID provides a mock function with no fields -func (_m *MockAccessPoint) GetPropertySSID() (string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertySSID") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func() (string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockAccessPoint_GetPropertySSID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertySSID' -type MockAccessPoint_GetPropertySSID_Call struct { - *mock.Call -} - -// GetPropertySSID is a helper method to define mock.On call -func (_e *MockAccessPoint_Expecter) GetPropertySSID() *MockAccessPoint_GetPropertySSID_Call { - return &MockAccessPoint_GetPropertySSID_Call{Call: _e.mock.On("GetPropertySSID")} -} - -func (_c *MockAccessPoint_GetPropertySSID_Call) Run(run func()) *MockAccessPoint_GetPropertySSID_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockAccessPoint_GetPropertySSID_Call) Return(_a0 string, _a1 error) *MockAccessPoint_GetPropertySSID_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockAccessPoint_GetPropertySSID_Call) RunAndReturn(run func() (string, error)) *MockAccessPoint_GetPropertySSID_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyStrength provides a mock function with no fields -func (_m *MockAccessPoint) GetPropertyStrength() (uint8, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyStrength") - } - - var r0 uint8 - var r1 error - if rf, ok := ret.Get(0).(func() (uint8, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() uint8); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(uint8) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockAccessPoint_GetPropertyStrength_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyStrength' -type MockAccessPoint_GetPropertyStrength_Call struct { - *mock.Call -} - -// GetPropertyStrength is a helper method to define mock.On call -func (_e *MockAccessPoint_Expecter) GetPropertyStrength() *MockAccessPoint_GetPropertyStrength_Call { - return &MockAccessPoint_GetPropertyStrength_Call{Call: _e.mock.On("GetPropertyStrength")} -} - -func (_c *MockAccessPoint_GetPropertyStrength_Call) Run(run func()) *MockAccessPoint_GetPropertyStrength_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockAccessPoint_GetPropertyStrength_Call) Return(_a0 uint8, _a1 error) *MockAccessPoint_GetPropertyStrength_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockAccessPoint_GetPropertyStrength_Call) RunAndReturn(run func() (uint8, error)) *MockAccessPoint_GetPropertyStrength_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyWPAFlags provides a mock function with no fields -func (_m *MockAccessPoint) GetPropertyWPAFlags() (uint32, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyWPAFlags") - } - - var r0 uint32 - var r1 error - if rf, ok := ret.Get(0).(func() (uint32, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() uint32); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(uint32) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockAccessPoint_GetPropertyWPAFlags_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyWPAFlags' -type MockAccessPoint_GetPropertyWPAFlags_Call struct { - *mock.Call -} - -// GetPropertyWPAFlags is a helper method to define mock.On call -func (_e *MockAccessPoint_Expecter) GetPropertyWPAFlags() *MockAccessPoint_GetPropertyWPAFlags_Call { - return &MockAccessPoint_GetPropertyWPAFlags_Call{Call: _e.mock.On("GetPropertyWPAFlags")} -} - -func (_c *MockAccessPoint_GetPropertyWPAFlags_Call) Run(run func()) *MockAccessPoint_GetPropertyWPAFlags_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockAccessPoint_GetPropertyWPAFlags_Call) Return(_a0 uint32, _a1 error) *MockAccessPoint_GetPropertyWPAFlags_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockAccessPoint_GetPropertyWPAFlags_Call) RunAndReturn(run func() (uint32, error)) *MockAccessPoint_GetPropertyWPAFlags_Call { - _c.Call.Return(run) - return _c -} - -// MarshalJSON provides a mock function with no fields -func (_m *MockAccessPoint) MarshalJSON() ([]byte, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for MarshalJSON") - } - - var r0 []byte - var r1 error - if rf, ok := ret.Get(0).(func() ([]byte, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []byte); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]byte) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockAccessPoint_MarshalJSON_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'MarshalJSON' -type MockAccessPoint_MarshalJSON_Call struct { - *mock.Call -} - -// MarshalJSON is a helper method to define mock.On call -func (_e *MockAccessPoint_Expecter) MarshalJSON() *MockAccessPoint_MarshalJSON_Call { - return &MockAccessPoint_MarshalJSON_Call{Call: _e.mock.On("MarshalJSON")} -} - -func (_c *MockAccessPoint_MarshalJSON_Call) Run(run func()) *MockAccessPoint_MarshalJSON_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockAccessPoint_MarshalJSON_Call) Return(_a0 []byte, _a1 error) *MockAccessPoint_MarshalJSON_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockAccessPoint_MarshalJSON_Call) RunAndReturn(run func() ([]byte, error)) *MockAccessPoint_MarshalJSON_Call { - _c.Call.Return(run) - return _c -} - -// NewMockAccessPoint creates a new instance of MockAccessPoint. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewMockAccessPoint(t interface { - mock.TestingT - Cleanup(func()) -}) *MockAccessPoint { - mock := &MockAccessPoint{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/nix/inputs/dms-cli/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_ActiveConnection.go b/nix/inputs/dms-cli/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_ActiveConnection.go deleted file mode 100644 index a4e3344..0000000 --- a/nix/inputs/dms-cli/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_ActiveConnection.go +++ /dev/null @@ -1,1025 +0,0 @@ -// Code generated by mockery v2.53.5. DO NOT EDIT. - -package gonetworkmanager - -import ( - gonetworkmanager "github.com/Wifx/gonetworkmanager/v2" - dbus "github.com/godbus/dbus/v5" - - mock "github.com/stretchr/testify/mock" -) - -// MockActiveConnection is an autogenerated mock type for the ActiveConnection type -type MockActiveConnection struct { - mock.Mock -} - -type MockActiveConnection_Expecter struct { - mock *mock.Mock -} - -func (_m *MockActiveConnection) EXPECT() *MockActiveConnection_Expecter { - return &MockActiveConnection_Expecter{mock: &_m.Mock} -} - -// GetPath provides a mock function with no fields -func (_m *MockActiveConnection) GetPath() dbus.ObjectPath { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPath") - } - - var r0 dbus.ObjectPath - if rf, ok := ret.Get(0).(func() dbus.ObjectPath); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(dbus.ObjectPath) - } - - return r0 -} - -// MockActiveConnection_GetPath_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPath' -type MockActiveConnection_GetPath_Call struct { - *mock.Call -} - -// GetPath is a helper method to define mock.On call -func (_e *MockActiveConnection_Expecter) GetPath() *MockActiveConnection_GetPath_Call { - return &MockActiveConnection_GetPath_Call{Call: _e.mock.On("GetPath")} -} - -func (_c *MockActiveConnection_GetPath_Call) Run(run func()) *MockActiveConnection_GetPath_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockActiveConnection_GetPath_Call) Return(_a0 dbus.ObjectPath) *MockActiveConnection_GetPath_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockActiveConnection_GetPath_Call) RunAndReturn(run func() dbus.ObjectPath) *MockActiveConnection_GetPath_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyConnection provides a mock function with no fields -func (_m *MockActiveConnection) GetPropertyConnection() (gonetworkmanager.Connection, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyConnection") - } - - var r0 gonetworkmanager.Connection - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.Connection, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.Connection); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.Connection) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockActiveConnection_GetPropertyConnection_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyConnection' -type MockActiveConnection_GetPropertyConnection_Call struct { - *mock.Call -} - -// GetPropertyConnection is a helper method to define mock.On call -func (_e *MockActiveConnection_Expecter) GetPropertyConnection() *MockActiveConnection_GetPropertyConnection_Call { - return &MockActiveConnection_GetPropertyConnection_Call{Call: _e.mock.On("GetPropertyConnection")} -} - -func (_c *MockActiveConnection_GetPropertyConnection_Call) Run(run func()) *MockActiveConnection_GetPropertyConnection_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockActiveConnection_GetPropertyConnection_Call) Return(_a0 gonetworkmanager.Connection, _a1 error) *MockActiveConnection_GetPropertyConnection_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockActiveConnection_GetPropertyConnection_Call) RunAndReturn(run func() (gonetworkmanager.Connection, error)) *MockActiveConnection_GetPropertyConnection_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyDHCP4Config provides a mock function with no fields -func (_m *MockActiveConnection) GetPropertyDHCP4Config() (gonetworkmanager.DHCP4Config, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyDHCP4Config") - } - - var r0 gonetworkmanager.DHCP4Config - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.DHCP4Config, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.DHCP4Config); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.DHCP4Config) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockActiveConnection_GetPropertyDHCP4Config_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyDHCP4Config' -type MockActiveConnection_GetPropertyDHCP4Config_Call struct { - *mock.Call -} - -// GetPropertyDHCP4Config is a helper method to define mock.On call -func (_e *MockActiveConnection_Expecter) GetPropertyDHCP4Config() *MockActiveConnection_GetPropertyDHCP4Config_Call { - return &MockActiveConnection_GetPropertyDHCP4Config_Call{Call: _e.mock.On("GetPropertyDHCP4Config")} -} - -func (_c *MockActiveConnection_GetPropertyDHCP4Config_Call) Run(run func()) *MockActiveConnection_GetPropertyDHCP4Config_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockActiveConnection_GetPropertyDHCP4Config_Call) Return(_a0 gonetworkmanager.DHCP4Config, _a1 error) *MockActiveConnection_GetPropertyDHCP4Config_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockActiveConnection_GetPropertyDHCP4Config_Call) RunAndReturn(run func() (gonetworkmanager.DHCP4Config, error)) *MockActiveConnection_GetPropertyDHCP4Config_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyDHCP6Config provides a mock function with no fields -func (_m *MockActiveConnection) GetPropertyDHCP6Config() (gonetworkmanager.DHCP6Config, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyDHCP6Config") - } - - var r0 gonetworkmanager.DHCP6Config - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.DHCP6Config, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.DHCP6Config); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.DHCP6Config) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockActiveConnection_GetPropertyDHCP6Config_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyDHCP6Config' -type MockActiveConnection_GetPropertyDHCP6Config_Call struct { - *mock.Call -} - -// GetPropertyDHCP6Config is a helper method to define mock.On call -func (_e *MockActiveConnection_Expecter) GetPropertyDHCP6Config() *MockActiveConnection_GetPropertyDHCP6Config_Call { - return &MockActiveConnection_GetPropertyDHCP6Config_Call{Call: _e.mock.On("GetPropertyDHCP6Config")} -} - -func (_c *MockActiveConnection_GetPropertyDHCP6Config_Call) Run(run func()) *MockActiveConnection_GetPropertyDHCP6Config_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockActiveConnection_GetPropertyDHCP6Config_Call) Return(_a0 gonetworkmanager.DHCP6Config, _a1 error) *MockActiveConnection_GetPropertyDHCP6Config_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockActiveConnection_GetPropertyDHCP6Config_Call) RunAndReturn(run func() (gonetworkmanager.DHCP6Config, error)) *MockActiveConnection_GetPropertyDHCP6Config_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyDefault provides a mock function with no fields -func (_m *MockActiveConnection) GetPropertyDefault() (bool, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyDefault") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func() (bool, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockActiveConnection_GetPropertyDefault_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyDefault' -type MockActiveConnection_GetPropertyDefault_Call struct { - *mock.Call -} - -// GetPropertyDefault is a helper method to define mock.On call -func (_e *MockActiveConnection_Expecter) GetPropertyDefault() *MockActiveConnection_GetPropertyDefault_Call { - return &MockActiveConnection_GetPropertyDefault_Call{Call: _e.mock.On("GetPropertyDefault")} -} - -func (_c *MockActiveConnection_GetPropertyDefault_Call) Run(run func()) *MockActiveConnection_GetPropertyDefault_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockActiveConnection_GetPropertyDefault_Call) Return(_a0 bool, _a1 error) *MockActiveConnection_GetPropertyDefault_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockActiveConnection_GetPropertyDefault_Call) RunAndReturn(run func() (bool, error)) *MockActiveConnection_GetPropertyDefault_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyDefault6 provides a mock function with no fields -func (_m *MockActiveConnection) GetPropertyDefault6() (bool, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyDefault6") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func() (bool, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockActiveConnection_GetPropertyDefault6_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyDefault6' -type MockActiveConnection_GetPropertyDefault6_Call struct { - *mock.Call -} - -// GetPropertyDefault6 is a helper method to define mock.On call -func (_e *MockActiveConnection_Expecter) GetPropertyDefault6() *MockActiveConnection_GetPropertyDefault6_Call { - return &MockActiveConnection_GetPropertyDefault6_Call{Call: _e.mock.On("GetPropertyDefault6")} -} - -func (_c *MockActiveConnection_GetPropertyDefault6_Call) Run(run func()) *MockActiveConnection_GetPropertyDefault6_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockActiveConnection_GetPropertyDefault6_Call) Return(_a0 bool, _a1 error) *MockActiveConnection_GetPropertyDefault6_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockActiveConnection_GetPropertyDefault6_Call) RunAndReturn(run func() (bool, error)) *MockActiveConnection_GetPropertyDefault6_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyDevices provides a mock function with no fields -func (_m *MockActiveConnection) GetPropertyDevices() ([]gonetworkmanager.Device, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyDevices") - } - - var r0 []gonetworkmanager.Device - var r1 error - if rf, ok := ret.Get(0).(func() ([]gonetworkmanager.Device, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []gonetworkmanager.Device); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]gonetworkmanager.Device) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockActiveConnection_GetPropertyDevices_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyDevices' -type MockActiveConnection_GetPropertyDevices_Call struct { - *mock.Call -} - -// GetPropertyDevices is a helper method to define mock.On call -func (_e *MockActiveConnection_Expecter) GetPropertyDevices() *MockActiveConnection_GetPropertyDevices_Call { - return &MockActiveConnection_GetPropertyDevices_Call{Call: _e.mock.On("GetPropertyDevices")} -} - -func (_c *MockActiveConnection_GetPropertyDevices_Call) Run(run func()) *MockActiveConnection_GetPropertyDevices_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockActiveConnection_GetPropertyDevices_Call) Return(_a0 []gonetworkmanager.Device, _a1 error) *MockActiveConnection_GetPropertyDevices_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockActiveConnection_GetPropertyDevices_Call) RunAndReturn(run func() ([]gonetworkmanager.Device, error)) *MockActiveConnection_GetPropertyDevices_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyID provides a mock function with no fields -func (_m *MockActiveConnection) GetPropertyID() (string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyID") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func() (string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockActiveConnection_GetPropertyID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyID' -type MockActiveConnection_GetPropertyID_Call struct { - *mock.Call -} - -// GetPropertyID is a helper method to define mock.On call -func (_e *MockActiveConnection_Expecter) GetPropertyID() *MockActiveConnection_GetPropertyID_Call { - return &MockActiveConnection_GetPropertyID_Call{Call: _e.mock.On("GetPropertyID")} -} - -func (_c *MockActiveConnection_GetPropertyID_Call) Run(run func()) *MockActiveConnection_GetPropertyID_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockActiveConnection_GetPropertyID_Call) Return(_a0 string, _a1 error) *MockActiveConnection_GetPropertyID_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockActiveConnection_GetPropertyID_Call) RunAndReturn(run func() (string, error)) *MockActiveConnection_GetPropertyID_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyIP4Config provides a mock function with no fields -func (_m *MockActiveConnection) GetPropertyIP4Config() (gonetworkmanager.IP4Config, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyIP4Config") - } - - var r0 gonetworkmanager.IP4Config - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.IP4Config, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.IP4Config); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.IP4Config) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockActiveConnection_GetPropertyIP4Config_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyIP4Config' -type MockActiveConnection_GetPropertyIP4Config_Call struct { - *mock.Call -} - -// GetPropertyIP4Config is a helper method to define mock.On call -func (_e *MockActiveConnection_Expecter) GetPropertyIP4Config() *MockActiveConnection_GetPropertyIP4Config_Call { - return &MockActiveConnection_GetPropertyIP4Config_Call{Call: _e.mock.On("GetPropertyIP4Config")} -} - -func (_c *MockActiveConnection_GetPropertyIP4Config_Call) Run(run func()) *MockActiveConnection_GetPropertyIP4Config_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockActiveConnection_GetPropertyIP4Config_Call) Return(_a0 gonetworkmanager.IP4Config, _a1 error) *MockActiveConnection_GetPropertyIP4Config_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockActiveConnection_GetPropertyIP4Config_Call) RunAndReturn(run func() (gonetworkmanager.IP4Config, error)) *MockActiveConnection_GetPropertyIP4Config_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyIP6Config provides a mock function with no fields -func (_m *MockActiveConnection) GetPropertyIP6Config() (gonetworkmanager.IP6Config, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyIP6Config") - } - - var r0 gonetworkmanager.IP6Config - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.IP6Config, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.IP6Config); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.IP6Config) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockActiveConnection_GetPropertyIP6Config_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyIP6Config' -type MockActiveConnection_GetPropertyIP6Config_Call struct { - *mock.Call -} - -// GetPropertyIP6Config is a helper method to define mock.On call -func (_e *MockActiveConnection_Expecter) GetPropertyIP6Config() *MockActiveConnection_GetPropertyIP6Config_Call { - return &MockActiveConnection_GetPropertyIP6Config_Call{Call: _e.mock.On("GetPropertyIP6Config")} -} - -func (_c *MockActiveConnection_GetPropertyIP6Config_Call) Run(run func()) *MockActiveConnection_GetPropertyIP6Config_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockActiveConnection_GetPropertyIP6Config_Call) Return(_a0 gonetworkmanager.IP6Config, _a1 error) *MockActiveConnection_GetPropertyIP6Config_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockActiveConnection_GetPropertyIP6Config_Call) RunAndReturn(run func() (gonetworkmanager.IP6Config, error)) *MockActiveConnection_GetPropertyIP6Config_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyMaster provides a mock function with no fields -func (_m *MockActiveConnection) GetPropertyMaster() (gonetworkmanager.Device, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyMaster") - } - - var r0 gonetworkmanager.Device - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.Device, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.Device); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.Device) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockActiveConnection_GetPropertyMaster_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyMaster' -type MockActiveConnection_GetPropertyMaster_Call struct { - *mock.Call -} - -// GetPropertyMaster is a helper method to define mock.On call -func (_e *MockActiveConnection_Expecter) GetPropertyMaster() *MockActiveConnection_GetPropertyMaster_Call { - return &MockActiveConnection_GetPropertyMaster_Call{Call: _e.mock.On("GetPropertyMaster")} -} - -func (_c *MockActiveConnection_GetPropertyMaster_Call) Run(run func()) *MockActiveConnection_GetPropertyMaster_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockActiveConnection_GetPropertyMaster_Call) Return(_a0 gonetworkmanager.Device, _a1 error) *MockActiveConnection_GetPropertyMaster_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockActiveConnection_GetPropertyMaster_Call) RunAndReturn(run func() (gonetworkmanager.Device, error)) *MockActiveConnection_GetPropertyMaster_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertySpecificObject provides a mock function with no fields -func (_m *MockActiveConnection) GetPropertySpecificObject() (gonetworkmanager.AccessPoint, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertySpecificObject") - } - - var r0 gonetworkmanager.AccessPoint - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.AccessPoint, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.AccessPoint); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.AccessPoint) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockActiveConnection_GetPropertySpecificObject_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertySpecificObject' -type MockActiveConnection_GetPropertySpecificObject_Call struct { - *mock.Call -} - -// GetPropertySpecificObject is a helper method to define mock.On call -func (_e *MockActiveConnection_Expecter) GetPropertySpecificObject() *MockActiveConnection_GetPropertySpecificObject_Call { - return &MockActiveConnection_GetPropertySpecificObject_Call{Call: _e.mock.On("GetPropertySpecificObject")} -} - -func (_c *MockActiveConnection_GetPropertySpecificObject_Call) Run(run func()) *MockActiveConnection_GetPropertySpecificObject_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockActiveConnection_GetPropertySpecificObject_Call) Return(_a0 gonetworkmanager.AccessPoint, _a1 error) *MockActiveConnection_GetPropertySpecificObject_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockActiveConnection_GetPropertySpecificObject_Call) RunAndReturn(run func() (gonetworkmanager.AccessPoint, error)) *MockActiveConnection_GetPropertySpecificObject_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyState provides a mock function with no fields -func (_m *MockActiveConnection) GetPropertyState() (gonetworkmanager.NmActiveConnectionState, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyState") - } - - var r0 gonetworkmanager.NmActiveConnectionState - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.NmActiveConnectionState, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.NmActiveConnectionState); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(gonetworkmanager.NmActiveConnectionState) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockActiveConnection_GetPropertyState_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyState' -type MockActiveConnection_GetPropertyState_Call struct { - *mock.Call -} - -// GetPropertyState is a helper method to define mock.On call -func (_e *MockActiveConnection_Expecter) GetPropertyState() *MockActiveConnection_GetPropertyState_Call { - return &MockActiveConnection_GetPropertyState_Call{Call: _e.mock.On("GetPropertyState")} -} - -func (_c *MockActiveConnection_GetPropertyState_Call) Run(run func()) *MockActiveConnection_GetPropertyState_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockActiveConnection_GetPropertyState_Call) Return(_a0 gonetworkmanager.NmActiveConnectionState, _a1 error) *MockActiveConnection_GetPropertyState_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockActiveConnection_GetPropertyState_Call) RunAndReturn(run func() (gonetworkmanager.NmActiveConnectionState, error)) *MockActiveConnection_GetPropertyState_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyStateFlags provides a mock function with no fields -func (_m *MockActiveConnection) GetPropertyStateFlags() (uint32, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyStateFlags") - } - - var r0 uint32 - var r1 error - if rf, ok := ret.Get(0).(func() (uint32, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() uint32); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(uint32) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockActiveConnection_GetPropertyStateFlags_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyStateFlags' -type MockActiveConnection_GetPropertyStateFlags_Call struct { - *mock.Call -} - -// GetPropertyStateFlags is a helper method to define mock.On call -func (_e *MockActiveConnection_Expecter) GetPropertyStateFlags() *MockActiveConnection_GetPropertyStateFlags_Call { - return &MockActiveConnection_GetPropertyStateFlags_Call{Call: _e.mock.On("GetPropertyStateFlags")} -} - -func (_c *MockActiveConnection_GetPropertyStateFlags_Call) Run(run func()) *MockActiveConnection_GetPropertyStateFlags_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockActiveConnection_GetPropertyStateFlags_Call) Return(_a0 uint32, _a1 error) *MockActiveConnection_GetPropertyStateFlags_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockActiveConnection_GetPropertyStateFlags_Call) RunAndReturn(run func() (uint32, error)) *MockActiveConnection_GetPropertyStateFlags_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyType provides a mock function with no fields -func (_m *MockActiveConnection) GetPropertyType() (string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyType") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func() (string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockActiveConnection_GetPropertyType_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyType' -type MockActiveConnection_GetPropertyType_Call struct { - *mock.Call -} - -// GetPropertyType is a helper method to define mock.On call -func (_e *MockActiveConnection_Expecter) GetPropertyType() *MockActiveConnection_GetPropertyType_Call { - return &MockActiveConnection_GetPropertyType_Call{Call: _e.mock.On("GetPropertyType")} -} - -func (_c *MockActiveConnection_GetPropertyType_Call) Run(run func()) *MockActiveConnection_GetPropertyType_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockActiveConnection_GetPropertyType_Call) Return(_a0 string, _a1 error) *MockActiveConnection_GetPropertyType_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockActiveConnection_GetPropertyType_Call) RunAndReturn(run func() (string, error)) *MockActiveConnection_GetPropertyType_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyUUID provides a mock function with no fields -func (_m *MockActiveConnection) GetPropertyUUID() (string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyUUID") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func() (string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockActiveConnection_GetPropertyUUID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyUUID' -type MockActiveConnection_GetPropertyUUID_Call struct { - *mock.Call -} - -// GetPropertyUUID is a helper method to define mock.On call -func (_e *MockActiveConnection_Expecter) GetPropertyUUID() *MockActiveConnection_GetPropertyUUID_Call { - return &MockActiveConnection_GetPropertyUUID_Call{Call: _e.mock.On("GetPropertyUUID")} -} - -func (_c *MockActiveConnection_GetPropertyUUID_Call) Run(run func()) *MockActiveConnection_GetPropertyUUID_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockActiveConnection_GetPropertyUUID_Call) Return(_a0 string, _a1 error) *MockActiveConnection_GetPropertyUUID_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockActiveConnection_GetPropertyUUID_Call) RunAndReturn(run func() (string, error)) *MockActiveConnection_GetPropertyUUID_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyVPN provides a mock function with no fields -func (_m *MockActiveConnection) GetPropertyVPN() (bool, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyVPN") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func() (bool, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockActiveConnection_GetPropertyVPN_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyVPN' -type MockActiveConnection_GetPropertyVPN_Call struct { - *mock.Call -} - -// GetPropertyVPN is a helper method to define mock.On call -func (_e *MockActiveConnection_Expecter) GetPropertyVPN() *MockActiveConnection_GetPropertyVPN_Call { - return &MockActiveConnection_GetPropertyVPN_Call{Call: _e.mock.On("GetPropertyVPN")} -} - -func (_c *MockActiveConnection_GetPropertyVPN_Call) Run(run func()) *MockActiveConnection_GetPropertyVPN_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockActiveConnection_GetPropertyVPN_Call) Return(_a0 bool, _a1 error) *MockActiveConnection_GetPropertyVPN_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockActiveConnection_GetPropertyVPN_Call) RunAndReturn(run func() (bool, error)) *MockActiveConnection_GetPropertyVPN_Call { - _c.Call.Return(run) - return _c -} - -// SubscribeState provides a mock function with given fields: receiver, exit -func (_m *MockActiveConnection) SubscribeState(receiver chan gonetworkmanager.StateChange, exit chan struct{}) error { - ret := _m.Called(receiver, exit) - - if len(ret) == 0 { - panic("no return value specified for SubscribeState") - } - - var r0 error - if rf, ok := ret.Get(0).(func(chan gonetworkmanager.StateChange, chan struct{}) error); ok { - r0 = rf(receiver, exit) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockActiveConnection_SubscribeState_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SubscribeState' -type MockActiveConnection_SubscribeState_Call struct { - *mock.Call -} - -// SubscribeState is a helper method to define mock.On call -// - receiver chan gonetworkmanager.StateChange -// - exit chan struct{} -func (_e *MockActiveConnection_Expecter) SubscribeState(receiver interface{}, exit interface{}) *MockActiveConnection_SubscribeState_Call { - return &MockActiveConnection_SubscribeState_Call{Call: _e.mock.On("SubscribeState", receiver, exit)} -} - -func (_c *MockActiveConnection_SubscribeState_Call) Run(run func(receiver chan gonetworkmanager.StateChange, exit chan struct{})) *MockActiveConnection_SubscribeState_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(chan gonetworkmanager.StateChange), args[1].(chan struct{})) - }) - return _c -} - -func (_c *MockActiveConnection_SubscribeState_Call) Return(err error) *MockActiveConnection_SubscribeState_Call { - _c.Call.Return(err) - return _c -} - -func (_c *MockActiveConnection_SubscribeState_Call) RunAndReturn(run func(chan gonetworkmanager.StateChange, chan struct{}) error) *MockActiveConnection_SubscribeState_Call { - _c.Call.Return(run) - return _c -} - -// NewMockActiveConnection creates a new instance of MockActiveConnection. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewMockActiveConnection(t interface { - mock.TestingT - Cleanup(func()) -}) *MockActiveConnection { - mock := &MockActiveConnection{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/nix/inputs/dms-cli/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_Connection.go b/nix/inputs/dms-cli/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_Connection.go deleted file mode 100644 index 84f59e1..0000000 --- a/nix/inputs/dms-cli/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_Connection.go +++ /dev/null @@ -1,646 +0,0 @@ -// Code generated by mockery v2.53.5. DO NOT EDIT. - -package gonetworkmanager - -import ( - gonetworkmanager "github.com/Wifx/gonetworkmanager/v2" - dbus "github.com/godbus/dbus/v5" - - mock "github.com/stretchr/testify/mock" -) - -// MockConnection is an autogenerated mock type for the Connection type -type MockConnection struct { - mock.Mock -} - -type MockConnection_Expecter struct { - mock *mock.Mock -} - -func (_m *MockConnection) EXPECT() *MockConnection_Expecter { - return &MockConnection_Expecter{mock: &_m.Mock} -} - -// ClearSecrets provides a mock function with no fields -func (_m *MockConnection) ClearSecrets() error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for ClearSecrets") - } - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockConnection_ClearSecrets_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ClearSecrets' -type MockConnection_ClearSecrets_Call struct { - *mock.Call -} - -// ClearSecrets is a helper method to define mock.On call -func (_e *MockConnection_Expecter) ClearSecrets() *MockConnection_ClearSecrets_Call { - return &MockConnection_ClearSecrets_Call{Call: _e.mock.On("ClearSecrets")} -} - -func (_c *MockConnection_ClearSecrets_Call) Run(run func()) *MockConnection_ClearSecrets_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockConnection_ClearSecrets_Call) Return(_a0 error) *MockConnection_ClearSecrets_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockConnection_ClearSecrets_Call) RunAndReturn(run func() error) *MockConnection_ClearSecrets_Call { - _c.Call.Return(run) - return _c -} - -// Delete provides a mock function with no fields -func (_m *MockConnection) Delete() error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Delete") - } - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockConnection_Delete_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Delete' -type MockConnection_Delete_Call struct { - *mock.Call -} - -// Delete is a helper method to define mock.On call -func (_e *MockConnection_Expecter) Delete() *MockConnection_Delete_Call { - return &MockConnection_Delete_Call{Call: _e.mock.On("Delete")} -} - -func (_c *MockConnection_Delete_Call) Run(run func()) *MockConnection_Delete_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockConnection_Delete_Call) Return(_a0 error) *MockConnection_Delete_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockConnection_Delete_Call) RunAndReturn(run func() error) *MockConnection_Delete_Call { - _c.Call.Return(run) - return _c -} - -// GetPath provides a mock function with no fields -func (_m *MockConnection) GetPath() dbus.ObjectPath { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPath") - } - - var r0 dbus.ObjectPath - if rf, ok := ret.Get(0).(func() dbus.ObjectPath); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(dbus.ObjectPath) - } - - return r0 -} - -// MockConnection_GetPath_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPath' -type MockConnection_GetPath_Call struct { - *mock.Call -} - -// GetPath is a helper method to define mock.On call -func (_e *MockConnection_Expecter) GetPath() *MockConnection_GetPath_Call { - return &MockConnection_GetPath_Call{Call: _e.mock.On("GetPath")} -} - -func (_c *MockConnection_GetPath_Call) Run(run func()) *MockConnection_GetPath_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockConnection_GetPath_Call) Return(_a0 dbus.ObjectPath) *MockConnection_GetPath_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockConnection_GetPath_Call) RunAndReturn(run func() dbus.ObjectPath) *MockConnection_GetPath_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyFilename provides a mock function with no fields -func (_m *MockConnection) GetPropertyFilename() (string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyFilename") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func() (string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockConnection_GetPropertyFilename_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyFilename' -type MockConnection_GetPropertyFilename_Call struct { - *mock.Call -} - -// GetPropertyFilename is a helper method to define mock.On call -func (_e *MockConnection_Expecter) GetPropertyFilename() *MockConnection_GetPropertyFilename_Call { - return &MockConnection_GetPropertyFilename_Call{Call: _e.mock.On("GetPropertyFilename")} -} - -func (_c *MockConnection_GetPropertyFilename_Call) Run(run func()) *MockConnection_GetPropertyFilename_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockConnection_GetPropertyFilename_Call) Return(_a0 string, _a1 error) *MockConnection_GetPropertyFilename_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockConnection_GetPropertyFilename_Call) RunAndReturn(run func() (string, error)) *MockConnection_GetPropertyFilename_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyFlags provides a mock function with no fields -func (_m *MockConnection) GetPropertyFlags() (uint32, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyFlags") - } - - var r0 uint32 - var r1 error - if rf, ok := ret.Get(0).(func() (uint32, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() uint32); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(uint32) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockConnection_GetPropertyFlags_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyFlags' -type MockConnection_GetPropertyFlags_Call struct { - *mock.Call -} - -// GetPropertyFlags is a helper method to define mock.On call -func (_e *MockConnection_Expecter) GetPropertyFlags() *MockConnection_GetPropertyFlags_Call { - return &MockConnection_GetPropertyFlags_Call{Call: _e.mock.On("GetPropertyFlags")} -} - -func (_c *MockConnection_GetPropertyFlags_Call) Run(run func()) *MockConnection_GetPropertyFlags_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockConnection_GetPropertyFlags_Call) Return(_a0 uint32, _a1 error) *MockConnection_GetPropertyFlags_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockConnection_GetPropertyFlags_Call) RunAndReturn(run func() (uint32, error)) *MockConnection_GetPropertyFlags_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyUnsaved provides a mock function with no fields -func (_m *MockConnection) GetPropertyUnsaved() (bool, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyUnsaved") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func() (bool, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockConnection_GetPropertyUnsaved_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyUnsaved' -type MockConnection_GetPropertyUnsaved_Call struct { - *mock.Call -} - -// GetPropertyUnsaved is a helper method to define mock.On call -func (_e *MockConnection_Expecter) GetPropertyUnsaved() *MockConnection_GetPropertyUnsaved_Call { - return &MockConnection_GetPropertyUnsaved_Call{Call: _e.mock.On("GetPropertyUnsaved")} -} - -func (_c *MockConnection_GetPropertyUnsaved_Call) Run(run func()) *MockConnection_GetPropertyUnsaved_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockConnection_GetPropertyUnsaved_Call) Return(_a0 bool, _a1 error) *MockConnection_GetPropertyUnsaved_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockConnection_GetPropertyUnsaved_Call) RunAndReturn(run func() (bool, error)) *MockConnection_GetPropertyUnsaved_Call { - _c.Call.Return(run) - return _c -} - -// GetSecrets provides a mock function with given fields: settingName -func (_m *MockConnection) GetSecrets(settingName string) (gonetworkmanager.ConnectionSettings, error) { - ret := _m.Called(settingName) - - if len(ret) == 0 { - panic("no return value specified for GetSecrets") - } - - var r0 gonetworkmanager.ConnectionSettings - var r1 error - if rf, ok := ret.Get(0).(func(string) (gonetworkmanager.ConnectionSettings, error)); ok { - return rf(settingName) - } - if rf, ok := ret.Get(0).(func(string) gonetworkmanager.ConnectionSettings); ok { - r0 = rf(settingName) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.ConnectionSettings) - } - } - - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(settingName) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockConnection_GetSecrets_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetSecrets' -type MockConnection_GetSecrets_Call struct { - *mock.Call -} - -// GetSecrets is a helper method to define mock.On call -// - settingName string -func (_e *MockConnection_Expecter) GetSecrets(settingName interface{}) *MockConnection_GetSecrets_Call { - return &MockConnection_GetSecrets_Call{Call: _e.mock.On("GetSecrets", settingName)} -} - -func (_c *MockConnection_GetSecrets_Call) Run(run func(settingName string)) *MockConnection_GetSecrets_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *MockConnection_GetSecrets_Call) Return(_a0 gonetworkmanager.ConnectionSettings, _a1 error) *MockConnection_GetSecrets_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockConnection_GetSecrets_Call) RunAndReturn(run func(string) (gonetworkmanager.ConnectionSettings, error)) *MockConnection_GetSecrets_Call { - _c.Call.Return(run) - return _c -} - -// GetSettings provides a mock function with no fields -func (_m *MockConnection) GetSettings() (gonetworkmanager.ConnectionSettings, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetSettings") - } - - var r0 gonetworkmanager.ConnectionSettings - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.ConnectionSettings, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.ConnectionSettings); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.ConnectionSettings) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockConnection_GetSettings_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetSettings' -type MockConnection_GetSettings_Call struct { - *mock.Call -} - -// GetSettings is a helper method to define mock.On call -func (_e *MockConnection_Expecter) GetSettings() *MockConnection_GetSettings_Call { - return &MockConnection_GetSettings_Call{Call: _e.mock.On("GetSettings")} -} - -func (_c *MockConnection_GetSettings_Call) Run(run func()) *MockConnection_GetSettings_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockConnection_GetSettings_Call) Return(_a0 gonetworkmanager.ConnectionSettings, _a1 error) *MockConnection_GetSettings_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockConnection_GetSettings_Call) RunAndReturn(run func() (gonetworkmanager.ConnectionSettings, error)) *MockConnection_GetSettings_Call { - _c.Call.Return(run) - return _c -} - -// MarshalJSON provides a mock function with no fields -func (_m *MockConnection) MarshalJSON() ([]byte, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for MarshalJSON") - } - - var r0 []byte - var r1 error - if rf, ok := ret.Get(0).(func() ([]byte, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []byte); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]byte) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockConnection_MarshalJSON_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'MarshalJSON' -type MockConnection_MarshalJSON_Call struct { - *mock.Call -} - -// MarshalJSON is a helper method to define mock.On call -func (_e *MockConnection_Expecter) MarshalJSON() *MockConnection_MarshalJSON_Call { - return &MockConnection_MarshalJSON_Call{Call: _e.mock.On("MarshalJSON")} -} - -func (_c *MockConnection_MarshalJSON_Call) Run(run func()) *MockConnection_MarshalJSON_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockConnection_MarshalJSON_Call) Return(_a0 []byte, _a1 error) *MockConnection_MarshalJSON_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockConnection_MarshalJSON_Call) RunAndReturn(run func() ([]byte, error)) *MockConnection_MarshalJSON_Call { - _c.Call.Return(run) - return _c -} - -// Save provides a mock function with no fields -func (_m *MockConnection) Save() error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Save") - } - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockConnection_Save_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Save' -type MockConnection_Save_Call struct { - *mock.Call -} - -// Save is a helper method to define mock.On call -func (_e *MockConnection_Expecter) Save() *MockConnection_Save_Call { - return &MockConnection_Save_Call{Call: _e.mock.On("Save")} -} - -func (_c *MockConnection_Save_Call) Run(run func()) *MockConnection_Save_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockConnection_Save_Call) Return(_a0 error) *MockConnection_Save_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockConnection_Save_Call) RunAndReturn(run func() error) *MockConnection_Save_Call { - _c.Call.Return(run) - return _c -} - -// Update provides a mock function with given fields: settings -func (_m *MockConnection) Update(settings gonetworkmanager.ConnectionSettings) error { - ret := _m.Called(settings) - - if len(ret) == 0 { - panic("no return value specified for Update") - } - - var r0 error - if rf, ok := ret.Get(0).(func(gonetworkmanager.ConnectionSettings) error); ok { - r0 = rf(settings) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockConnection_Update_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Update' -type MockConnection_Update_Call struct { - *mock.Call -} - -// Update is a helper method to define mock.On call -// - settings gonetworkmanager.ConnectionSettings -func (_e *MockConnection_Expecter) Update(settings interface{}) *MockConnection_Update_Call { - return &MockConnection_Update_Call{Call: _e.mock.On("Update", settings)} -} - -func (_c *MockConnection_Update_Call) Run(run func(settings gonetworkmanager.ConnectionSettings)) *MockConnection_Update_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(gonetworkmanager.ConnectionSettings)) - }) - return _c -} - -func (_c *MockConnection_Update_Call) Return(_a0 error) *MockConnection_Update_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockConnection_Update_Call) RunAndReturn(run func(gonetworkmanager.ConnectionSettings) error) *MockConnection_Update_Call { - _c.Call.Return(run) - return _c -} - -// UpdateUnsaved provides a mock function with given fields: settings -func (_m *MockConnection) UpdateUnsaved(settings gonetworkmanager.ConnectionSettings) error { - ret := _m.Called(settings) - - if len(ret) == 0 { - panic("no return value specified for UpdateUnsaved") - } - - var r0 error - if rf, ok := ret.Get(0).(func(gonetworkmanager.ConnectionSettings) error); ok { - r0 = rf(settings) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockConnection_UpdateUnsaved_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateUnsaved' -type MockConnection_UpdateUnsaved_Call struct { - *mock.Call -} - -// UpdateUnsaved is a helper method to define mock.On call -// - settings gonetworkmanager.ConnectionSettings -func (_e *MockConnection_Expecter) UpdateUnsaved(settings interface{}) *MockConnection_UpdateUnsaved_Call { - return &MockConnection_UpdateUnsaved_Call{Call: _e.mock.On("UpdateUnsaved", settings)} -} - -func (_c *MockConnection_UpdateUnsaved_Call) Run(run func(settings gonetworkmanager.ConnectionSettings)) *MockConnection_UpdateUnsaved_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(gonetworkmanager.ConnectionSettings)) - }) - return _c -} - -func (_c *MockConnection_UpdateUnsaved_Call) Return(_a0 error) *MockConnection_UpdateUnsaved_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockConnection_UpdateUnsaved_Call) RunAndReturn(run func(gonetworkmanager.ConnectionSettings) error) *MockConnection_UpdateUnsaved_Call { - _c.Call.Return(run) - return _c -} - -// NewMockConnection creates a new instance of MockConnection. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewMockConnection(t interface { - mock.TestingT - Cleanup(func()) -}) *MockConnection { - mock := &MockConnection{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/nix/inputs/dms-cli/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_Device.go b/nix/inputs/dms-cli/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_Device.go deleted file mode 100644 index b2fc0b5..0000000 --- a/nix/inputs/dms-cli/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_Device.go +++ /dev/null @@ -1,1638 +0,0 @@ -// Code generated by mockery v2.53.5. DO NOT EDIT. - -package gonetworkmanager - -import ( - gonetworkmanager "github.com/Wifx/gonetworkmanager/v2" - dbus "github.com/godbus/dbus/v5" - - mock "github.com/stretchr/testify/mock" -) - -// MockDevice is an autogenerated mock type for the Device type -type MockDevice struct { - mock.Mock -} - -type MockDevice_Expecter struct { - mock *mock.Mock -} - -func (_m *MockDevice) EXPECT() *MockDevice_Expecter { - return &MockDevice_Expecter{mock: &_m.Mock} -} - -// Delete provides a mock function with no fields -func (_m *MockDevice) Delete() error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Delete") - } - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockDevice_Delete_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Delete' -type MockDevice_Delete_Call struct { - *mock.Call -} - -// Delete is a helper method to define mock.On call -func (_e *MockDevice_Expecter) Delete() *MockDevice_Delete_Call { - return &MockDevice_Delete_Call{Call: _e.mock.On("Delete")} -} - -func (_c *MockDevice_Delete_Call) Run(run func()) *MockDevice_Delete_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDevice_Delete_Call) Return(_a0 error) *MockDevice_Delete_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockDevice_Delete_Call) RunAndReturn(run func() error) *MockDevice_Delete_Call { - _c.Call.Return(run) - return _c -} - -// Disconnect provides a mock function with no fields -func (_m *MockDevice) Disconnect() error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Disconnect") - } - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockDevice_Disconnect_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Disconnect' -type MockDevice_Disconnect_Call struct { - *mock.Call -} - -// Disconnect is a helper method to define mock.On call -func (_e *MockDevice_Expecter) Disconnect() *MockDevice_Disconnect_Call { - return &MockDevice_Disconnect_Call{Call: _e.mock.On("Disconnect")} -} - -func (_c *MockDevice_Disconnect_Call) Run(run func()) *MockDevice_Disconnect_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDevice_Disconnect_Call) Return(_a0 error) *MockDevice_Disconnect_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockDevice_Disconnect_Call) RunAndReturn(run func() error) *MockDevice_Disconnect_Call { - _c.Call.Return(run) - return _c -} - -// GetPath provides a mock function with no fields -func (_m *MockDevice) GetPath() dbus.ObjectPath { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPath") - } - - var r0 dbus.ObjectPath - if rf, ok := ret.Get(0).(func() dbus.ObjectPath); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(dbus.ObjectPath) - } - - return r0 -} - -// MockDevice_GetPath_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPath' -type MockDevice_GetPath_Call struct { - *mock.Call -} - -// GetPath is a helper method to define mock.On call -func (_e *MockDevice_Expecter) GetPath() *MockDevice_GetPath_Call { - return &MockDevice_GetPath_Call{Call: _e.mock.On("GetPath")} -} - -func (_c *MockDevice_GetPath_Call) Run(run func()) *MockDevice_GetPath_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDevice_GetPath_Call) Return(_a0 dbus.ObjectPath) *MockDevice_GetPath_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockDevice_GetPath_Call) RunAndReturn(run func() dbus.ObjectPath) *MockDevice_GetPath_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyActiveConnection provides a mock function with no fields -func (_m *MockDevice) GetPropertyActiveConnection() (gonetworkmanager.ActiveConnection, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyActiveConnection") - } - - var r0 gonetworkmanager.ActiveConnection - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.ActiveConnection, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.ActiveConnection); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.ActiveConnection) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDevice_GetPropertyActiveConnection_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyActiveConnection' -type MockDevice_GetPropertyActiveConnection_Call struct { - *mock.Call -} - -// GetPropertyActiveConnection is a helper method to define mock.On call -func (_e *MockDevice_Expecter) GetPropertyActiveConnection() *MockDevice_GetPropertyActiveConnection_Call { - return &MockDevice_GetPropertyActiveConnection_Call{Call: _e.mock.On("GetPropertyActiveConnection")} -} - -func (_c *MockDevice_GetPropertyActiveConnection_Call) Run(run func()) *MockDevice_GetPropertyActiveConnection_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDevice_GetPropertyActiveConnection_Call) Return(_a0 gonetworkmanager.ActiveConnection, _a1 error) *MockDevice_GetPropertyActiveConnection_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDevice_GetPropertyActiveConnection_Call) RunAndReturn(run func() (gonetworkmanager.ActiveConnection, error)) *MockDevice_GetPropertyActiveConnection_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyAutoConnect provides a mock function with no fields -func (_m *MockDevice) GetPropertyAutoConnect() (bool, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyAutoConnect") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func() (bool, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDevice_GetPropertyAutoConnect_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyAutoConnect' -type MockDevice_GetPropertyAutoConnect_Call struct { - *mock.Call -} - -// GetPropertyAutoConnect is a helper method to define mock.On call -func (_e *MockDevice_Expecter) GetPropertyAutoConnect() *MockDevice_GetPropertyAutoConnect_Call { - return &MockDevice_GetPropertyAutoConnect_Call{Call: _e.mock.On("GetPropertyAutoConnect")} -} - -func (_c *MockDevice_GetPropertyAutoConnect_Call) Run(run func()) *MockDevice_GetPropertyAutoConnect_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDevice_GetPropertyAutoConnect_Call) Return(_a0 bool, _a1 error) *MockDevice_GetPropertyAutoConnect_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDevice_GetPropertyAutoConnect_Call) RunAndReturn(run func() (bool, error)) *MockDevice_GetPropertyAutoConnect_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyAvailableConnections provides a mock function with no fields -func (_m *MockDevice) GetPropertyAvailableConnections() ([]gonetworkmanager.Connection, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyAvailableConnections") - } - - var r0 []gonetworkmanager.Connection - var r1 error - if rf, ok := ret.Get(0).(func() ([]gonetworkmanager.Connection, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []gonetworkmanager.Connection); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]gonetworkmanager.Connection) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDevice_GetPropertyAvailableConnections_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyAvailableConnections' -type MockDevice_GetPropertyAvailableConnections_Call struct { - *mock.Call -} - -// GetPropertyAvailableConnections is a helper method to define mock.On call -func (_e *MockDevice_Expecter) GetPropertyAvailableConnections() *MockDevice_GetPropertyAvailableConnections_Call { - return &MockDevice_GetPropertyAvailableConnections_Call{Call: _e.mock.On("GetPropertyAvailableConnections")} -} - -func (_c *MockDevice_GetPropertyAvailableConnections_Call) Run(run func()) *MockDevice_GetPropertyAvailableConnections_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDevice_GetPropertyAvailableConnections_Call) Return(_a0 []gonetworkmanager.Connection, _a1 error) *MockDevice_GetPropertyAvailableConnections_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDevice_GetPropertyAvailableConnections_Call) RunAndReturn(run func() ([]gonetworkmanager.Connection, error)) *MockDevice_GetPropertyAvailableConnections_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyDHCP4Config provides a mock function with no fields -func (_m *MockDevice) GetPropertyDHCP4Config() (gonetworkmanager.DHCP4Config, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyDHCP4Config") - } - - var r0 gonetworkmanager.DHCP4Config - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.DHCP4Config, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.DHCP4Config); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.DHCP4Config) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDevice_GetPropertyDHCP4Config_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyDHCP4Config' -type MockDevice_GetPropertyDHCP4Config_Call struct { - *mock.Call -} - -// GetPropertyDHCP4Config is a helper method to define mock.On call -func (_e *MockDevice_Expecter) GetPropertyDHCP4Config() *MockDevice_GetPropertyDHCP4Config_Call { - return &MockDevice_GetPropertyDHCP4Config_Call{Call: _e.mock.On("GetPropertyDHCP4Config")} -} - -func (_c *MockDevice_GetPropertyDHCP4Config_Call) Run(run func()) *MockDevice_GetPropertyDHCP4Config_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDevice_GetPropertyDHCP4Config_Call) Return(_a0 gonetworkmanager.DHCP4Config, _a1 error) *MockDevice_GetPropertyDHCP4Config_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDevice_GetPropertyDHCP4Config_Call) RunAndReturn(run func() (gonetworkmanager.DHCP4Config, error)) *MockDevice_GetPropertyDHCP4Config_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyDHCP6Config provides a mock function with no fields -func (_m *MockDevice) GetPropertyDHCP6Config() (gonetworkmanager.DHCP6Config, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyDHCP6Config") - } - - var r0 gonetworkmanager.DHCP6Config - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.DHCP6Config, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.DHCP6Config); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.DHCP6Config) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDevice_GetPropertyDHCP6Config_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyDHCP6Config' -type MockDevice_GetPropertyDHCP6Config_Call struct { - *mock.Call -} - -// GetPropertyDHCP6Config is a helper method to define mock.On call -func (_e *MockDevice_Expecter) GetPropertyDHCP6Config() *MockDevice_GetPropertyDHCP6Config_Call { - return &MockDevice_GetPropertyDHCP6Config_Call{Call: _e.mock.On("GetPropertyDHCP6Config")} -} - -func (_c *MockDevice_GetPropertyDHCP6Config_Call) Run(run func()) *MockDevice_GetPropertyDHCP6Config_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDevice_GetPropertyDHCP6Config_Call) Return(_a0 gonetworkmanager.DHCP6Config, _a1 error) *MockDevice_GetPropertyDHCP6Config_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDevice_GetPropertyDHCP6Config_Call) RunAndReturn(run func() (gonetworkmanager.DHCP6Config, error)) *MockDevice_GetPropertyDHCP6Config_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyDeviceType provides a mock function with no fields -func (_m *MockDevice) GetPropertyDeviceType() (gonetworkmanager.NmDeviceType, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyDeviceType") - } - - var r0 gonetworkmanager.NmDeviceType - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.NmDeviceType, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.NmDeviceType); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(gonetworkmanager.NmDeviceType) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDevice_GetPropertyDeviceType_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyDeviceType' -type MockDevice_GetPropertyDeviceType_Call struct { - *mock.Call -} - -// GetPropertyDeviceType is a helper method to define mock.On call -func (_e *MockDevice_Expecter) GetPropertyDeviceType() *MockDevice_GetPropertyDeviceType_Call { - return &MockDevice_GetPropertyDeviceType_Call{Call: _e.mock.On("GetPropertyDeviceType")} -} - -func (_c *MockDevice_GetPropertyDeviceType_Call) Run(run func()) *MockDevice_GetPropertyDeviceType_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDevice_GetPropertyDeviceType_Call) Return(_a0 gonetworkmanager.NmDeviceType, _a1 error) *MockDevice_GetPropertyDeviceType_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDevice_GetPropertyDeviceType_Call) RunAndReturn(run func() (gonetworkmanager.NmDeviceType, error)) *MockDevice_GetPropertyDeviceType_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyDriver provides a mock function with no fields -func (_m *MockDevice) GetPropertyDriver() (string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyDriver") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func() (string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDevice_GetPropertyDriver_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyDriver' -type MockDevice_GetPropertyDriver_Call struct { - *mock.Call -} - -// GetPropertyDriver is a helper method to define mock.On call -func (_e *MockDevice_Expecter) GetPropertyDriver() *MockDevice_GetPropertyDriver_Call { - return &MockDevice_GetPropertyDriver_Call{Call: _e.mock.On("GetPropertyDriver")} -} - -func (_c *MockDevice_GetPropertyDriver_Call) Run(run func()) *MockDevice_GetPropertyDriver_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDevice_GetPropertyDriver_Call) Return(_a0 string, _a1 error) *MockDevice_GetPropertyDriver_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDevice_GetPropertyDriver_Call) RunAndReturn(run func() (string, error)) *MockDevice_GetPropertyDriver_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyDriverVersion provides a mock function with no fields -func (_m *MockDevice) GetPropertyDriverVersion() (string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyDriverVersion") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func() (string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDevice_GetPropertyDriverVersion_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyDriverVersion' -type MockDevice_GetPropertyDriverVersion_Call struct { - *mock.Call -} - -// GetPropertyDriverVersion is a helper method to define mock.On call -func (_e *MockDevice_Expecter) GetPropertyDriverVersion() *MockDevice_GetPropertyDriverVersion_Call { - return &MockDevice_GetPropertyDriverVersion_Call{Call: _e.mock.On("GetPropertyDriverVersion")} -} - -func (_c *MockDevice_GetPropertyDriverVersion_Call) Run(run func()) *MockDevice_GetPropertyDriverVersion_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDevice_GetPropertyDriverVersion_Call) Return(_a0 string, _a1 error) *MockDevice_GetPropertyDriverVersion_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDevice_GetPropertyDriverVersion_Call) RunAndReturn(run func() (string, error)) *MockDevice_GetPropertyDriverVersion_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyFirmwareMissing provides a mock function with no fields -func (_m *MockDevice) GetPropertyFirmwareMissing() (bool, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyFirmwareMissing") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func() (bool, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDevice_GetPropertyFirmwareMissing_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyFirmwareMissing' -type MockDevice_GetPropertyFirmwareMissing_Call struct { - *mock.Call -} - -// GetPropertyFirmwareMissing is a helper method to define mock.On call -func (_e *MockDevice_Expecter) GetPropertyFirmwareMissing() *MockDevice_GetPropertyFirmwareMissing_Call { - return &MockDevice_GetPropertyFirmwareMissing_Call{Call: _e.mock.On("GetPropertyFirmwareMissing")} -} - -func (_c *MockDevice_GetPropertyFirmwareMissing_Call) Run(run func()) *MockDevice_GetPropertyFirmwareMissing_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDevice_GetPropertyFirmwareMissing_Call) Return(_a0 bool, _a1 error) *MockDevice_GetPropertyFirmwareMissing_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDevice_GetPropertyFirmwareMissing_Call) RunAndReturn(run func() (bool, error)) *MockDevice_GetPropertyFirmwareMissing_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyFirmwareVersion provides a mock function with no fields -func (_m *MockDevice) GetPropertyFirmwareVersion() (string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyFirmwareVersion") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func() (string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDevice_GetPropertyFirmwareVersion_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyFirmwareVersion' -type MockDevice_GetPropertyFirmwareVersion_Call struct { - *mock.Call -} - -// GetPropertyFirmwareVersion is a helper method to define mock.On call -func (_e *MockDevice_Expecter) GetPropertyFirmwareVersion() *MockDevice_GetPropertyFirmwareVersion_Call { - return &MockDevice_GetPropertyFirmwareVersion_Call{Call: _e.mock.On("GetPropertyFirmwareVersion")} -} - -func (_c *MockDevice_GetPropertyFirmwareVersion_Call) Run(run func()) *MockDevice_GetPropertyFirmwareVersion_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDevice_GetPropertyFirmwareVersion_Call) Return(_a0 string, _a1 error) *MockDevice_GetPropertyFirmwareVersion_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDevice_GetPropertyFirmwareVersion_Call) RunAndReturn(run func() (string, error)) *MockDevice_GetPropertyFirmwareVersion_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyIP4Config provides a mock function with no fields -func (_m *MockDevice) GetPropertyIP4Config() (gonetworkmanager.IP4Config, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyIP4Config") - } - - var r0 gonetworkmanager.IP4Config - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.IP4Config, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.IP4Config); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.IP4Config) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDevice_GetPropertyIP4Config_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyIP4Config' -type MockDevice_GetPropertyIP4Config_Call struct { - *mock.Call -} - -// GetPropertyIP4Config is a helper method to define mock.On call -func (_e *MockDevice_Expecter) GetPropertyIP4Config() *MockDevice_GetPropertyIP4Config_Call { - return &MockDevice_GetPropertyIP4Config_Call{Call: _e.mock.On("GetPropertyIP4Config")} -} - -func (_c *MockDevice_GetPropertyIP4Config_Call) Run(run func()) *MockDevice_GetPropertyIP4Config_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDevice_GetPropertyIP4Config_Call) Return(_a0 gonetworkmanager.IP4Config, _a1 error) *MockDevice_GetPropertyIP4Config_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDevice_GetPropertyIP4Config_Call) RunAndReturn(run func() (gonetworkmanager.IP4Config, error)) *MockDevice_GetPropertyIP4Config_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyIP6Config provides a mock function with no fields -func (_m *MockDevice) GetPropertyIP6Config() (gonetworkmanager.IP6Config, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyIP6Config") - } - - var r0 gonetworkmanager.IP6Config - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.IP6Config, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.IP6Config); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.IP6Config) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDevice_GetPropertyIP6Config_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyIP6Config' -type MockDevice_GetPropertyIP6Config_Call struct { - *mock.Call -} - -// GetPropertyIP6Config is a helper method to define mock.On call -func (_e *MockDevice_Expecter) GetPropertyIP6Config() *MockDevice_GetPropertyIP6Config_Call { - return &MockDevice_GetPropertyIP6Config_Call{Call: _e.mock.On("GetPropertyIP6Config")} -} - -func (_c *MockDevice_GetPropertyIP6Config_Call) Run(run func()) *MockDevice_GetPropertyIP6Config_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDevice_GetPropertyIP6Config_Call) Return(_a0 gonetworkmanager.IP6Config, _a1 error) *MockDevice_GetPropertyIP6Config_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDevice_GetPropertyIP6Config_Call) RunAndReturn(run func() (gonetworkmanager.IP6Config, error)) *MockDevice_GetPropertyIP6Config_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyInterface provides a mock function with no fields -func (_m *MockDevice) GetPropertyInterface() (string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyInterface") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func() (string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDevice_GetPropertyInterface_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyInterface' -type MockDevice_GetPropertyInterface_Call struct { - *mock.Call -} - -// GetPropertyInterface is a helper method to define mock.On call -func (_e *MockDevice_Expecter) GetPropertyInterface() *MockDevice_GetPropertyInterface_Call { - return &MockDevice_GetPropertyInterface_Call{Call: _e.mock.On("GetPropertyInterface")} -} - -func (_c *MockDevice_GetPropertyInterface_Call) Run(run func()) *MockDevice_GetPropertyInterface_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDevice_GetPropertyInterface_Call) Return(_a0 string, _a1 error) *MockDevice_GetPropertyInterface_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDevice_GetPropertyInterface_Call) RunAndReturn(run func() (string, error)) *MockDevice_GetPropertyInterface_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyIp4Connectivity provides a mock function with no fields -func (_m *MockDevice) GetPropertyIp4Connectivity() (gonetworkmanager.NmConnectivity, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyIp4Connectivity") - } - - var r0 gonetworkmanager.NmConnectivity - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.NmConnectivity, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.NmConnectivity); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(gonetworkmanager.NmConnectivity) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDevice_GetPropertyIp4Connectivity_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyIp4Connectivity' -type MockDevice_GetPropertyIp4Connectivity_Call struct { - *mock.Call -} - -// GetPropertyIp4Connectivity is a helper method to define mock.On call -func (_e *MockDevice_Expecter) GetPropertyIp4Connectivity() *MockDevice_GetPropertyIp4Connectivity_Call { - return &MockDevice_GetPropertyIp4Connectivity_Call{Call: _e.mock.On("GetPropertyIp4Connectivity")} -} - -func (_c *MockDevice_GetPropertyIp4Connectivity_Call) Run(run func()) *MockDevice_GetPropertyIp4Connectivity_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDevice_GetPropertyIp4Connectivity_Call) Return(_a0 gonetworkmanager.NmConnectivity, _a1 error) *MockDevice_GetPropertyIp4Connectivity_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDevice_GetPropertyIp4Connectivity_Call) RunAndReturn(run func() (gonetworkmanager.NmConnectivity, error)) *MockDevice_GetPropertyIp4Connectivity_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyIpInterface provides a mock function with no fields -func (_m *MockDevice) GetPropertyIpInterface() (string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyIpInterface") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func() (string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDevice_GetPropertyIpInterface_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyIpInterface' -type MockDevice_GetPropertyIpInterface_Call struct { - *mock.Call -} - -// GetPropertyIpInterface is a helper method to define mock.On call -func (_e *MockDevice_Expecter) GetPropertyIpInterface() *MockDevice_GetPropertyIpInterface_Call { - return &MockDevice_GetPropertyIpInterface_Call{Call: _e.mock.On("GetPropertyIpInterface")} -} - -func (_c *MockDevice_GetPropertyIpInterface_Call) Run(run func()) *MockDevice_GetPropertyIpInterface_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDevice_GetPropertyIpInterface_Call) Return(_a0 string, _a1 error) *MockDevice_GetPropertyIpInterface_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDevice_GetPropertyIpInterface_Call) RunAndReturn(run func() (string, error)) *MockDevice_GetPropertyIpInterface_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyManaged provides a mock function with no fields -func (_m *MockDevice) GetPropertyManaged() (bool, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyManaged") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func() (bool, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDevice_GetPropertyManaged_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyManaged' -type MockDevice_GetPropertyManaged_Call struct { - *mock.Call -} - -// GetPropertyManaged is a helper method to define mock.On call -func (_e *MockDevice_Expecter) GetPropertyManaged() *MockDevice_GetPropertyManaged_Call { - return &MockDevice_GetPropertyManaged_Call{Call: _e.mock.On("GetPropertyManaged")} -} - -func (_c *MockDevice_GetPropertyManaged_Call) Run(run func()) *MockDevice_GetPropertyManaged_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDevice_GetPropertyManaged_Call) Return(_a0 bool, _a1 error) *MockDevice_GetPropertyManaged_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDevice_GetPropertyManaged_Call) RunAndReturn(run func() (bool, error)) *MockDevice_GetPropertyManaged_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyMtu provides a mock function with no fields -func (_m *MockDevice) GetPropertyMtu() (uint32, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyMtu") - } - - var r0 uint32 - var r1 error - if rf, ok := ret.Get(0).(func() (uint32, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() uint32); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(uint32) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDevice_GetPropertyMtu_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyMtu' -type MockDevice_GetPropertyMtu_Call struct { - *mock.Call -} - -// GetPropertyMtu is a helper method to define mock.On call -func (_e *MockDevice_Expecter) GetPropertyMtu() *MockDevice_GetPropertyMtu_Call { - return &MockDevice_GetPropertyMtu_Call{Call: _e.mock.On("GetPropertyMtu")} -} - -func (_c *MockDevice_GetPropertyMtu_Call) Run(run func()) *MockDevice_GetPropertyMtu_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDevice_GetPropertyMtu_Call) Return(_a0 uint32, _a1 error) *MockDevice_GetPropertyMtu_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDevice_GetPropertyMtu_Call) RunAndReturn(run func() (uint32, error)) *MockDevice_GetPropertyMtu_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyNmPluginMissing provides a mock function with no fields -func (_m *MockDevice) GetPropertyNmPluginMissing() (bool, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyNmPluginMissing") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func() (bool, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDevice_GetPropertyNmPluginMissing_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyNmPluginMissing' -type MockDevice_GetPropertyNmPluginMissing_Call struct { - *mock.Call -} - -// GetPropertyNmPluginMissing is a helper method to define mock.On call -func (_e *MockDevice_Expecter) GetPropertyNmPluginMissing() *MockDevice_GetPropertyNmPluginMissing_Call { - return &MockDevice_GetPropertyNmPluginMissing_Call{Call: _e.mock.On("GetPropertyNmPluginMissing")} -} - -func (_c *MockDevice_GetPropertyNmPluginMissing_Call) Run(run func()) *MockDevice_GetPropertyNmPluginMissing_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDevice_GetPropertyNmPluginMissing_Call) Return(_a0 bool, _a1 error) *MockDevice_GetPropertyNmPluginMissing_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDevice_GetPropertyNmPluginMissing_Call) RunAndReturn(run func() (bool, error)) *MockDevice_GetPropertyNmPluginMissing_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyPhysicalPortId provides a mock function with no fields -func (_m *MockDevice) GetPropertyPhysicalPortId() (string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyPhysicalPortId") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func() (string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDevice_GetPropertyPhysicalPortId_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyPhysicalPortId' -type MockDevice_GetPropertyPhysicalPortId_Call struct { - *mock.Call -} - -// GetPropertyPhysicalPortId is a helper method to define mock.On call -func (_e *MockDevice_Expecter) GetPropertyPhysicalPortId() *MockDevice_GetPropertyPhysicalPortId_Call { - return &MockDevice_GetPropertyPhysicalPortId_Call{Call: _e.mock.On("GetPropertyPhysicalPortId")} -} - -func (_c *MockDevice_GetPropertyPhysicalPortId_Call) Run(run func()) *MockDevice_GetPropertyPhysicalPortId_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDevice_GetPropertyPhysicalPortId_Call) Return(_a0 string, _a1 error) *MockDevice_GetPropertyPhysicalPortId_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDevice_GetPropertyPhysicalPortId_Call) RunAndReturn(run func() (string, error)) *MockDevice_GetPropertyPhysicalPortId_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyReal provides a mock function with no fields -func (_m *MockDevice) GetPropertyReal() (bool, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyReal") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func() (bool, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDevice_GetPropertyReal_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyReal' -type MockDevice_GetPropertyReal_Call struct { - *mock.Call -} - -// GetPropertyReal is a helper method to define mock.On call -func (_e *MockDevice_Expecter) GetPropertyReal() *MockDevice_GetPropertyReal_Call { - return &MockDevice_GetPropertyReal_Call{Call: _e.mock.On("GetPropertyReal")} -} - -func (_c *MockDevice_GetPropertyReal_Call) Run(run func()) *MockDevice_GetPropertyReal_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDevice_GetPropertyReal_Call) Return(_a0 bool, _a1 error) *MockDevice_GetPropertyReal_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDevice_GetPropertyReal_Call) RunAndReturn(run func() (bool, error)) *MockDevice_GetPropertyReal_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyState provides a mock function with no fields -func (_m *MockDevice) GetPropertyState() (gonetworkmanager.NmDeviceState, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyState") - } - - var r0 gonetworkmanager.NmDeviceState - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.NmDeviceState, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.NmDeviceState); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(gonetworkmanager.NmDeviceState) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDevice_GetPropertyState_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyState' -type MockDevice_GetPropertyState_Call struct { - *mock.Call -} - -// GetPropertyState is a helper method to define mock.On call -func (_e *MockDevice_Expecter) GetPropertyState() *MockDevice_GetPropertyState_Call { - return &MockDevice_GetPropertyState_Call{Call: _e.mock.On("GetPropertyState")} -} - -func (_c *MockDevice_GetPropertyState_Call) Run(run func()) *MockDevice_GetPropertyState_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDevice_GetPropertyState_Call) Return(_a0 gonetworkmanager.NmDeviceState, _a1 error) *MockDevice_GetPropertyState_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDevice_GetPropertyState_Call) RunAndReturn(run func() (gonetworkmanager.NmDeviceState, error)) *MockDevice_GetPropertyState_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyUdi provides a mock function with no fields -func (_m *MockDevice) GetPropertyUdi() (string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyUdi") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func() (string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDevice_GetPropertyUdi_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyUdi' -type MockDevice_GetPropertyUdi_Call struct { - *mock.Call -} - -// GetPropertyUdi is a helper method to define mock.On call -func (_e *MockDevice_Expecter) GetPropertyUdi() *MockDevice_GetPropertyUdi_Call { - return &MockDevice_GetPropertyUdi_Call{Call: _e.mock.On("GetPropertyUdi")} -} - -func (_c *MockDevice_GetPropertyUdi_Call) Run(run func()) *MockDevice_GetPropertyUdi_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDevice_GetPropertyUdi_Call) Return(_a0 string, _a1 error) *MockDevice_GetPropertyUdi_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDevice_GetPropertyUdi_Call) RunAndReturn(run func() (string, error)) *MockDevice_GetPropertyUdi_Call { - _c.Call.Return(run) - return _c -} - -// MarshalJSON provides a mock function with no fields -func (_m *MockDevice) MarshalJSON() ([]byte, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for MarshalJSON") - } - - var r0 []byte - var r1 error - if rf, ok := ret.Get(0).(func() ([]byte, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []byte); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]byte) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDevice_MarshalJSON_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'MarshalJSON' -type MockDevice_MarshalJSON_Call struct { - *mock.Call -} - -// MarshalJSON is a helper method to define mock.On call -func (_e *MockDevice_Expecter) MarshalJSON() *MockDevice_MarshalJSON_Call { - return &MockDevice_MarshalJSON_Call{Call: _e.mock.On("MarshalJSON")} -} - -func (_c *MockDevice_MarshalJSON_Call) Run(run func()) *MockDevice_MarshalJSON_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDevice_MarshalJSON_Call) Return(_a0 []byte, _a1 error) *MockDevice_MarshalJSON_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDevice_MarshalJSON_Call) RunAndReturn(run func() ([]byte, error)) *MockDevice_MarshalJSON_Call { - _c.Call.Return(run) - return _c -} - -// Reapply provides a mock function with given fields: connection, versionId, flags -func (_m *MockDevice) Reapply(connection gonetworkmanager.Connection, versionId uint64, flags uint32) error { - ret := _m.Called(connection, versionId, flags) - - if len(ret) == 0 { - panic("no return value specified for Reapply") - } - - var r0 error - if rf, ok := ret.Get(0).(func(gonetworkmanager.Connection, uint64, uint32) error); ok { - r0 = rf(connection, versionId, flags) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockDevice_Reapply_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Reapply' -type MockDevice_Reapply_Call struct { - *mock.Call -} - -// Reapply is a helper method to define mock.On call -// - connection gonetworkmanager.Connection -// - versionId uint64 -// - flags uint32 -func (_e *MockDevice_Expecter) Reapply(connection interface{}, versionId interface{}, flags interface{}) *MockDevice_Reapply_Call { - return &MockDevice_Reapply_Call{Call: _e.mock.On("Reapply", connection, versionId, flags)} -} - -func (_c *MockDevice_Reapply_Call) Run(run func(connection gonetworkmanager.Connection, versionId uint64, flags uint32)) *MockDevice_Reapply_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(gonetworkmanager.Connection), args[1].(uint64), args[2].(uint32)) - }) - return _c -} - -func (_c *MockDevice_Reapply_Call) Return(_a0 error) *MockDevice_Reapply_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockDevice_Reapply_Call) RunAndReturn(run func(gonetworkmanager.Connection, uint64, uint32) error) *MockDevice_Reapply_Call { - _c.Call.Return(run) - return _c -} - -// SetPropertyAutoConnect provides a mock function with given fields: _a0 -func (_m *MockDevice) SetPropertyAutoConnect(_a0 bool) error { - ret := _m.Called(_a0) - - if len(ret) == 0 { - panic("no return value specified for SetPropertyAutoConnect") - } - - var r0 error - if rf, ok := ret.Get(0).(func(bool) error); ok { - r0 = rf(_a0) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockDevice_SetPropertyAutoConnect_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetPropertyAutoConnect' -type MockDevice_SetPropertyAutoConnect_Call struct { - *mock.Call -} - -// SetPropertyAutoConnect is a helper method to define mock.On call -// - _a0 bool -func (_e *MockDevice_Expecter) SetPropertyAutoConnect(_a0 interface{}) *MockDevice_SetPropertyAutoConnect_Call { - return &MockDevice_SetPropertyAutoConnect_Call{Call: _e.mock.On("SetPropertyAutoConnect", _a0)} -} - -func (_c *MockDevice_SetPropertyAutoConnect_Call) Run(run func(_a0 bool)) *MockDevice_SetPropertyAutoConnect_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(bool)) - }) - return _c -} - -func (_c *MockDevice_SetPropertyAutoConnect_Call) Return(_a0 error) *MockDevice_SetPropertyAutoConnect_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockDevice_SetPropertyAutoConnect_Call) RunAndReturn(run func(bool) error) *MockDevice_SetPropertyAutoConnect_Call { - _c.Call.Return(run) - return _c -} - -// SetPropertyManaged provides a mock function with given fields: _a0 -func (_m *MockDevice) SetPropertyManaged(_a0 bool) error { - ret := _m.Called(_a0) - - if len(ret) == 0 { - panic("no return value specified for SetPropertyManaged") - } - - var r0 error - if rf, ok := ret.Get(0).(func(bool) error); ok { - r0 = rf(_a0) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockDevice_SetPropertyManaged_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetPropertyManaged' -type MockDevice_SetPropertyManaged_Call struct { - *mock.Call -} - -// SetPropertyManaged is a helper method to define mock.On call -// - _a0 bool -func (_e *MockDevice_Expecter) SetPropertyManaged(_a0 interface{}) *MockDevice_SetPropertyManaged_Call { - return &MockDevice_SetPropertyManaged_Call{Call: _e.mock.On("SetPropertyManaged", _a0)} -} - -func (_c *MockDevice_SetPropertyManaged_Call) Run(run func(_a0 bool)) *MockDevice_SetPropertyManaged_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(bool)) - }) - return _c -} - -func (_c *MockDevice_SetPropertyManaged_Call) Return(_a0 error) *MockDevice_SetPropertyManaged_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockDevice_SetPropertyManaged_Call) RunAndReturn(run func(bool) error) *MockDevice_SetPropertyManaged_Call { - _c.Call.Return(run) - return _c -} - -// SubscribeState provides a mock function with given fields: receiver, exit -func (_m *MockDevice) SubscribeState(receiver chan gonetworkmanager.DeviceStateChange, exit chan struct{}) error { - ret := _m.Called(receiver, exit) - - if len(ret) == 0 { - panic("no return value specified for SubscribeState") - } - - var r0 error - if rf, ok := ret.Get(0).(func(chan gonetworkmanager.DeviceStateChange, chan struct{}) error); ok { - r0 = rf(receiver, exit) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockDevice_SubscribeState_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SubscribeState' -type MockDevice_SubscribeState_Call struct { - *mock.Call -} - -// SubscribeState is a helper method to define mock.On call -// - receiver chan gonetworkmanager.DeviceStateChange -// - exit chan struct{} -func (_e *MockDevice_Expecter) SubscribeState(receiver interface{}, exit interface{}) *MockDevice_SubscribeState_Call { - return &MockDevice_SubscribeState_Call{Call: _e.mock.On("SubscribeState", receiver, exit)} -} - -func (_c *MockDevice_SubscribeState_Call) Run(run func(receiver chan gonetworkmanager.DeviceStateChange, exit chan struct{})) *MockDevice_SubscribeState_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(chan gonetworkmanager.DeviceStateChange), args[1].(chan struct{})) - }) - return _c -} - -func (_c *MockDevice_SubscribeState_Call) Return(err error) *MockDevice_SubscribeState_Call { - _c.Call.Return(err) - return _c -} - -func (_c *MockDevice_SubscribeState_Call) RunAndReturn(run func(chan gonetworkmanager.DeviceStateChange, chan struct{}) error) *MockDevice_SubscribeState_Call { - _c.Call.Return(run) - return _c -} - -// NewMockDevice creates a new instance of MockDevice. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewMockDevice(t interface { - mock.TestingT - Cleanup(func()) -}) *MockDevice { - mock := &MockDevice{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/nix/inputs/dms-cli/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_DeviceWireless.go b/nix/inputs/dms-cli/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_DeviceWireless.go deleted file mode 100644 index d2d3f2d..0000000 --- a/nix/inputs/dms-cli/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_DeviceWireless.go +++ /dev/null @@ -1,2241 +0,0 @@ -// Code generated by mockery v2.53.5. DO NOT EDIT. - -package gonetworkmanager - -import ( - gonetworkmanager "github.com/Wifx/gonetworkmanager/v2" - dbus "github.com/godbus/dbus/v5" - - mock "github.com/stretchr/testify/mock" -) - -// MockDeviceWireless is an autogenerated mock type for the DeviceWireless type -type MockDeviceWireless struct { - mock.Mock -} - -type MockDeviceWireless_Expecter struct { - mock *mock.Mock -} - -func (_m *MockDeviceWireless) EXPECT() *MockDeviceWireless_Expecter { - return &MockDeviceWireless_Expecter{mock: &_m.Mock} -} - -// Delete provides a mock function with no fields -func (_m *MockDeviceWireless) Delete() error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Delete") - } - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockDeviceWireless_Delete_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Delete' -type MockDeviceWireless_Delete_Call struct { - *mock.Call -} - -// Delete is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) Delete() *MockDeviceWireless_Delete_Call { - return &MockDeviceWireless_Delete_Call{Call: _e.mock.On("Delete")} -} - -func (_c *MockDeviceWireless_Delete_Call) Run(run func()) *MockDeviceWireless_Delete_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_Delete_Call) Return(_a0 error) *MockDeviceWireless_Delete_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockDeviceWireless_Delete_Call) RunAndReturn(run func() error) *MockDeviceWireless_Delete_Call { - _c.Call.Return(run) - return _c -} - -// Disconnect provides a mock function with no fields -func (_m *MockDeviceWireless) Disconnect() error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Disconnect") - } - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockDeviceWireless_Disconnect_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Disconnect' -type MockDeviceWireless_Disconnect_Call struct { - *mock.Call -} - -// Disconnect is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) Disconnect() *MockDeviceWireless_Disconnect_Call { - return &MockDeviceWireless_Disconnect_Call{Call: _e.mock.On("Disconnect")} -} - -func (_c *MockDeviceWireless_Disconnect_Call) Run(run func()) *MockDeviceWireless_Disconnect_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_Disconnect_Call) Return(_a0 error) *MockDeviceWireless_Disconnect_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockDeviceWireless_Disconnect_Call) RunAndReturn(run func() error) *MockDeviceWireless_Disconnect_Call { - _c.Call.Return(run) - return _c -} - -// GetAccessPoints provides a mock function with no fields -func (_m *MockDeviceWireless) GetAccessPoints() ([]gonetworkmanager.AccessPoint, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetAccessPoints") - } - - var r0 []gonetworkmanager.AccessPoint - var r1 error - if rf, ok := ret.Get(0).(func() ([]gonetworkmanager.AccessPoint, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []gonetworkmanager.AccessPoint); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]gonetworkmanager.AccessPoint) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetAccessPoints_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetAccessPoints' -type MockDeviceWireless_GetAccessPoints_Call struct { - *mock.Call -} - -// GetAccessPoints is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetAccessPoints() *MockDeviceWireless_GetAccessPoints_Call { - return &MockDeviceWireless_GetAccessPoints_Call{Call: _e.mock.On("GetAccessPoints")} -} - -func (_c *MockDeviceWireless_GetAccessPoints_Call) Run(run func()) *MockDeviceWireless_GetAccessPoints_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetAccessPoints_Call) Return(_a0 []gonetworkmanager.AccessPoint, _a1 error) *MockDeviceWireless_GetAccessPoints_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetAccessPoints_Call) RunAndReturn(run func() ([]gonetworkmanager.AccessPoint, error)) *MockDeviceWireless_GetAccessPoints_Call { - _c.Call.Return(run) - return _c -} - -// GetAllAccessPoints provides a mock function with no fields -func (_m *MockDeviceWireless) GetAllAccessPoints() ([]gonetworkmanager.AccessPoint, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetAllAccessPoints") - } - - var r0 []gonetworkmanager.AccessPoint - var r1 error - if rf, ok := ret.Get(0).(func() ([]gonetworkmanager.AccessPoint, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []gonetworkmanager.AccessPoint); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]gonetworkmanager.AccessPoint) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetAllAccessPoints_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetAllAccessPoints' -type MockDeviceWireless_GetAllAccessPoints_Call struct { - *mock.Call -} - -// GetAllAccessPoints is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetAllAccessPoints() *MockDeviceWireless_GetAllAccessPoints_Call { - return &MockDeviceWireless_GetAllAccessPoints_Call{Call: _e.mock.On("GetAllAccessPoints")} -} - -func (_c *MockDeviceWireless_GetAllAccessPoints_Call) Run(run func()) *MockDeviceWireless_GetAllAccessPoints_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetAllAccessPoints_Call) Return(_a0 []gonetworkmanager.AccessPoint, _a1 error) *MockDeviceWireless_GetAllAccessPoints_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetAllAccessPoints_Call) RunAndReturn(run func() ([]gonetworkmanager.AccessPoint, error)) *MockDeviceWireless_GetAllAccessPoints_Call { - _c.Call.Return(run) - return _c -} - -// GetPath provides a mock function with no fields -func (_m *MockDeviceWireless) GetPath() dbus.ObjectPath { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPath") - } - - var r0 dbus.ObjectPath - if rf, ok := ret.Get(0).(func() dbus.ObjectPath); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(dbus.ObjectPath) - } - - return r0 -} - -// MockDeviceWireless_GetPath_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPath' -type MockDeviceWireless_GetPath_Call struct { - *mock.Call -} - -// GetPath is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPath() *MockDeviceWireless_GetPath_Call { - return &MockDeviceWireless_GetPath_Call{Call: _e.mock.On("GetPath")} -} - -func (_c *MockDeviceWireless_GetPath_Call) Run(run func()) *MockDeviceWireless_GetPath_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPath_Call) Return(_a0 dbus.ObjectPath) *MockDeviceWireless_GetPath_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockDeviceWireless_GetPath_Call) RunAndReturn(run func() dbus.ObjectPath) *MockDeviceWireless_GetPath_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyAccessPoints provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyAccessPoints() ([]gonetworkmanager.AccessPoint, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyAccessPoints") - } - - var r0 []gonetworkmanager.AccessPoint - var r1 error - if rf, ok := ret.Get(0).(func() ([]gonetworkmanager.AccessPoint, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []gonetworkmanager.AccessPoint); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]gonetworkmanager.AccessPoint) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyAccessPoints_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyAccessPoints' -type MockDeviceWireless_GetPropertyAccessPoints_Call struct { - *mock.Call -} - -// GetPropertyAccessPoints is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyAccessPoints() *MockDeviceWireless_GetPropertyAccessPoints_Call { - return &MockDeviceWireless_GetPropertyAccessPoints_Call{Call: _e.mock.On("GetPropertyAccessPoints")} -} - -func (_c *MockDeviceWireless_GetPropertyAccessPoints_Call) Run(run func()) *MockDeviceWireless_GetPropertyAccessPoints_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyAccessPoints_Call) Return(_a0 []gonetworkmanager.AccessPoint, _a1 error) *MockDeviceWireless_GetPropertyAccessPoints_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyAccessPoints_Call) RunAndReturn(run func() ([]gonetworkmanager.AccessPoint, error)) *MockDeviceWireless_GetPropertyAccessPoints_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyActiveAccessPoint provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyActiveAccessPoint() (gonetworkmanager.AccessPoint, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyActiveAccessPoint") - } - - var r0 gonetworkmanager.AccessPoint - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.AccessPoint, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.AccessPoint); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.AccessPoint) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyActiveAccessPoint_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyActiveAccessPoint' -type MockDeviceWireless_GetPropertyActiveAccessPoint_Call struct { - *mock.Call -} - -// GetPropertyActiveAccessPoint is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyActiveAccessPoint() *MockDeviceWireless_GetPropertyActiveAccessPoint_Call { - return &MockDeviceWireless_GetPropertyActiveAccessPoint_Call{Call: _e.mock.On("GetPropertyActiveAccessPoint")} -} - -func (_c *MockDeviceWireless_GetPropertyActiveAccessPoint_Call) Run(run func()) *MockDeviceWireless_GetPropertyActiveAccessPoint_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyActiveAccessPoint_Call) Return(_a0 gonetworkmanager.AccessPoint, _a1 error) *MockDeviceWireless_GetPropertyActiveAccessPoint_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyActiveAccessPoint_Call) RunAndReturn(run func() (gonetworkmanager.AccessPoint, error)) *MockDeviceWireless_GetPropertyActiveAccessPoint_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyActiveConnection provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyActiveConnection() (gonetworkmanager.ActiveConnection, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyActiveConnection") - } - - var r0 gonetworkmanager.ActiveConnection - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.ActiveConnection, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.ActiveConnection); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.ActiveConnection) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyActiveConnection_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyActiveConnection' -type MockDeviceWireless_GetPropertyActiveConnection_Call struct { - *mock.Call -} - -// GetPropertyActiveConnection is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyActiveConnection() *MockDeviceWireless_GetPropertyActiveConnection_Call { - return &MockDeviceWireless_GetPropertyActiveConnection_Call{Call: _e.mock.On("GetPropertyActiveConnection")} -} - -func (_c *MockDeviceWireless_GetPropertyActiveConnection_Call) Run(run func()) *MockDeviceWireless_GetPropertyActiveConnection_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyActiveConnection_Call) Return(_a0 gonetworkmanager.ActiveConnection, _a1 error) *MockDeviceWireless_GetPropertyActiveConnection_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyActiveConnection_Call) RunAndReturn(run func() (gonetworkmanager.ActiveConnection, error)) *MockDeviceWireless_GetPropertyActiveConnection_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyAutoConnect provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyAutoConnect() (bool, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyAutoConnect") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func() (bool, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyAutoConnect_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyAutoConnect' -type MockDeviceWireless_GetPropertyAutoConnect_Call struct { - *mock.Call -} - -// GetPropertyAutoConnect is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyAutoConnect() *MockDeviceWireless_GetPropertyAutoConnect_Call { - return &MockDeviceWireless_GetPropertyAutoConnect_Call{Call: _e.mock.On("GetPropertyAutoConnect")} -} - -func (_c *MockDeviceWireless_GetPropertyAutoConnect_Call) Run(run func()) *MockDeviceWireless_GetPropertyAutoConnect_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyAutoConnect_Call) Return(_a0 bool, _a1 error) *MockDeviceWireless_GetPropertyAutoConnect_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyAutoConnect_Call) RunAndReturn(run func() (bool, error)) *MockDeviceWireless_GetPropertyAutoConnect_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyAvailableConnections provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyAvailableConnections() ([]gonetworkmanager.Connection, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyAvailableConnections") - } - - var r0 []gonetworkmanager.Connection - var r1 error - if rf, ok := ret.Get(0).(func() ([]gonetworkmanager.Connection, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []gonetworkmanager.Connection); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]gonetworkmanager.Connection) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyAvailableConnections_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyAvailableConnections' -type MockDeviceWireless_GetPropertyAvailableConnections_Call struct { - *mock.Call -} - -// GetPropertyAvailableConnections is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyAvailableConnections() *MockDeviceWireless_GetPropertyAvailableConnections_Call { - return &MockDeviceWireless_GetPropertyAvailableConnections_Call{Call: _e.mock.On("GetPropertyAvailableConnections")} -} - -func (_c *MockDeviceWireless_GetPropertyAvailableConnections_Call) Run(run func()) *MockDeviceWireless_GetPropertyAvailableConnections_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyAvailableConnections_Call) Return(_a0 []gonetworkmanager.Connection, _a1 error) *MockDeviceWireless_GetPropertyAvailableConnections_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyAvailableConnections_Call) RunAndReturn(run func() ([]gonetworkmanager.Connection, error)) *MockDeviceWireless_GetPropertyAvailableConnections_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyBitrate provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyBitrate() (uint32, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyBitrate") - } - - var r0 uint32 - var r1 error - if rf, ok := ret.Get(0).(func() (uint32, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() uint32); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(uint32) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyBitrate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyBitrate' -type MockDeviceWireless_GetPropertyBitrate_Call struct { - *mock.Call -} - -// GetPropertyBitrate is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyBitrate() *MockDeviceWireless_GetPropertyBitrate_Call { - return &MockDeviceWireless_GetPropertyBitrate_Call{Call: _e.mock.On("GetPropertyBitrate")} -} - -func (_c *MockDeviceWireless_GetPropertyBitrate_Call) Run(run func()) *MockDeviceWireless_GetPropertyBitrate_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyBitrate_Call) Return(_a0 uint32, _a1 error) *MockDeviceWireless_GetPropertyBitrate_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyBitrate_Call) RunAndReturn(run func() (uint32, error)) *MockDeviceWireless_GetPropertyBitrate_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyDHCP4Config provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyDHCP4Config() (gonetworkmanager.DHCP4Config, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyDHCP4Config") - } - - var r0 gonetworkmanager.DHCP4Config - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.DHCP4Config, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.DHCP4Config); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.DHCP4Config) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyDHCP4Config_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyDHCP4Config' -type MockDeviceWireless_GetPropertyDHCP4Config_Call struct { - *mock.Call -} - -// GetPropertyDHCP4Config is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyDHCP4Config() *MockDeviceWireless_GetPropertyDHCP4Config_Call { - return &MockDeviceWireless_GetPropertyDHCP4Config_Call{Call: _e.mock.On("GetPropertyDHCP4Config")} -} - -func (_c *MockDeviceWireless_GetPropertyDHCP4Config_Call) Run(run func()) *MockDeviceWireless_GetPropertyDHCP4Config_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyDHCP4Config_Call) Return(_a0 gonetworkmanager.DHCP4Config, _a1 error) *MockDeviceWireless_GetPropertyDHCP4Config_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyDHCP4Config_Call) RunAndReturn(run func() (gonetworkmanager.DHCP4Config, error)) *MockDeviceWireless_GetPropertyDHCP4Config_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyDHCP6Config provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyDHCP6Config() (gonetworkmanager.DHCP6Config, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyDHCP6Config") - } - - var r0 gonetworkmanager.DHCP6Config - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.DHCP6Config, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.DHCP6Config); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.DHCP6Config) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyDHCP6Config_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyDHCP6Config' -type MockDeviceWireless_GetPropertyDHCP6Config_Call struct { - *mock.Call -} - -// GetPropertyDHCP6Config is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyDHCP6Config() *MockDeviceWireless_GetPropertyDHCP6Config_Call { - return &MockDeviceWireless_GetPropertyDHCP6Config_Call{Call: _e.mock.On("GetPropertyDHCP6Config")} -} - -func (_c *MockDeviceWireless_GetPropertyDHCP6Config_Call) Run(run func()) *MockDeviceWireless_GetPropertyDHCP6Config_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyDHCP6Config_Call) Return(_a0 gonetworkmanager.DHCP6Config, _a1 error) *MockDeviceWireless_GetPropertyDHCP6Config_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyDHCP6Config_Call) RunAndReturn(run func() (gonetworkmanager.DHCP6Config, error)) *MockDeviceWireless_GetPropertyDHCP6Config_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyDeviceType provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyDeviceType() (gonetworkmanager.NmDeviceType, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyDeviceType") - } - - var r0 gonetworkmanager.NmDeviceType - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.NmDeviceType, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.NmDeviceType); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(gonetworkmanager.NmDeviceType) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyDeviceType_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyDeviceType' -type MockDeviceWireless_GetPropertyDeviceType_Call struct { - *mock.Call -} - -// GetPropertyDeviceType is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyDeviceType() *MockDeviceWireless_GetPropertyDeviceType_Call { - return &MockDeviceWireless_GetPropertyDeviceType_Call{Call: _e.mock.On("GetPropertyDeviceType")} -} - -func (_c *MockDeviceWireless_GetPropertyDeviceType_Call) Run(run func()) *MockDeviceWireless_GetPropertyDeviceType_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyDeviceType_Call) Return(_a0 gonetworkmanager.NmDeviceType, _a1 error) *MockDeviceWireless_GetPropertyDeviceType_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyDeviceType_Call) RunAndReturn(run func() (gonetworkmanager.NmDeviceType, error)) *MockDeviceWireless_GetPropertyDeviceType_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyDriver provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyDriver() (string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyDriver") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func() (string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyDriver_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyDriver' -type MockDeviceWireless_GetPropertyDriver_Call struct { - *mock.Call -} - -// GetPropertyDriver is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyDriver() *MockDeviceWireless_GetPropertyDriver_Call { - return &MockDeviceWireless_GetPropertyDriver_Call{Call: _e.mock.On("GetPropertyDriver")} -} - -func (_c *MockDeviceWireless_GetPropertyDriver_Call) Run(run func()) *MockDeviceWireless_GetPropertyDriver_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyDriver_Call) Return(_a0 string, _a1 error) *MockDeviceWireless_GetPropertyDriver_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyDriver_Call) RunAndReturn(run func() (string, error)) *MockDeviceWireless_GetPropertyDriver_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyDriverVersion provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyDriverVersion() (string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyDriverVersion") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func() (string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyDriverVersion_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyDriverVersion' -type MockDeviceWireless_GetPropertyDriverVersion_Call struct { - *mock.Call -} - -// GetPropertyDriverVersion is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyDriverVersion() *MockDeviceWireless_GetPropertyDriverVersion_Call { - return &MockDeviceWireless_GetPropertyDriverVersion_Call{Call: _e.mock.On("GetPropertyDriverVersion")} -} - -func (_c *MockDeviceWireless_GetPropertyDriverVersion_Call) Run(run func()) *MockDeviceWireless_GetPropertyDriverVersion_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyDriverVersion_Call) Return(_a0 string, _a1 error) *MockDeviceWireless_GetPropertyDriverVersion_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyDriverVersion_Call) RunAndReturn(run func() (string, error)) *MockDeviceWireless_GetPropertyDriverVersion_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyFirmwareMissing provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyFirmwareMissing() (bool, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyFirmwareMissing") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func() (bool, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyFirmwareMissing_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyFirmwareMissing' -type MockDeviceWireless_GetPropertyFirmwareMissing_Call struct { - *mock.Call -} - -// GetPropertyFirmwareMissing is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyFirmwareMissing() *MockDeviceWireless_GetPropertyFirmwareMissing_Call { - return &MockDeviceWireless_GetPropertyFirmwareMissing_Call{Call: _e.mock.On("GetPropertyFirmwareMissing")} -} - -func (_c *MockDeviceWireless_GetPropertyFirmwareMissing_Call) Run(run func()) *MockDeviceWireless_GetPropertyFirmwareMissing_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyFirmwareMissing_Call) Return(_a0 bool, _a1 error) *MockDeviceWireless_GetPropertyFirmwareMissing_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyFirmwareMissing_Call) RunAndReturn(run func() (bool, error)) *MockDeviceWireless_GetPropertyFirmwareMissing_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyFirmwareVersion provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyFirmwareVersion() (string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyFirmwareVersion") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func() (string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyFirmwareVersion_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyFirmwareVersion' -type MockDeviceWireless_GetPropertyFirmwareVersion_Call struct { - *mock.Call -} - -// GetPropertyFirmwareVersion is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyFirmwareVersion() *MockDeviceWireless_GetPropertyFirmwareVersion_Call { - return &MockDeviceWireless_GetPropertyFirmwareVersion_Call{Call: _e.mock.On("GetPropertyFirmwareVersion")} -} - -func (_c *MockDeviceWireless_GetPropertyFirmwareVersion_Call) Run(run func()) *MockDeviceWireless_GetPropertyFirmwareVersion_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyFirmwareVersion_Call) Return(_a0 string, _a1 error) *MockDeviceWireless_GetPropertyFirmwareVersion_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyFirmwareVersion_Call) RunAndReturn(run func() (string, error)) *MockDeviceWireless_GetPropertyFirmwareVersion_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyHwAddress provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyHwAddress() (string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyHwAddress") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func() (string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyHwAddress_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyHwAddress' -type MockDeviceWireless_GetPropertyHwAddress_Call struct { - *mock.Call -} - -// GetPropertyHwAddress is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyHwAddress() *MockDeviceWireless_GetPropertyHwAddress_Call { - return &MockDeviceWireless_GetPropertyHwAddress_Call{Call: _e.mock.On("GetPropertyHwAddress")} -} - -func (_c *MockDeviceWireless_GetPropertyHwAddress_Call) Run(run func()) *MockDeviceWireless_GetPropertyHwAddress_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyHwAddress_Call) Return(_a0 string, _a1 error) *MockDeviceWireless_GetPropertyHwAddress_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyHwAddress_Call) RunAndReturn(run func() (string, error)) *MockDeviceWireless_GetPropertyHwAddress_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyIP4Config provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyIP4Config() (gonetworkmanager.IP4Config, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyIP4Config") - } - - var r0 gonetworkmanager.IP4Config - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.IP4Config, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.IP4Config); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.IP4Config) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyIP4Config_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyIP4Config' -type MockDeviceWireless_GetPropertyIP4Config_Call struct { - *mock.Call -} - -// GetPropertyIP4Config is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyIP4Config() *MockDeviceWireless_GetPropertyIP4Config_Call { - return &MockDeviceWireless_GetPropertyIP4Config_Call{Call: _e.mock.On("GetPropertyIP4Config")} -} - -func (_c *MockDeviceWireless_GetPropertyIP4Config_Call) Run(run func()) *MockDeviceWireless_GetPropertyIP4Config_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyIP4Config_Call) Return(_a0 gonetworkmanager.IP4Config, _a1 error) *MockDeviceWireless_GetPropertyIP4Config_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyIP4Config_Call) RunAndReturn(run func() (gonetworkmanager.IP4Config, error)) *MockDeviceWireless_GetPropertyIP4Config_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyIP6Config provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyIP6Config() (gonetworkmanager.IP6Config, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyIP6Config") - } - - var r0 gonetworkmanager.IP6Config - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.IP6Config, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.IP6Config); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.IP6Config) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyIP6Config_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyIP6Config' -type MockDeviceWireless_GetPropertyIP6Config_Call struct { - *mock.Call -} - -// GetPropertyIP6Config is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyIP6Config() *MockDeviceWireless_GetPropertyIP6Config_Call { - return &MockDeviceWireless_GetPropertyIP6Config_Call{Call: _e.mock.On("GetPropertyIP6Config")} -} - -func (_c *MockDeviceWireless_GetPropertyIP6Config_Call) Run(run func()) *MockDeviceWireless_GetPropertyIP6Config_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyIP6Config_Call) Return(_a0 gonetworkmanager.IP6Config, _a1 error) *MockDeviceWireless_GetPropertyIP6Config_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyIP6Config_Call) RunAndReturn(run func() (gonetworkmanager.IP6Config, error)) *MockDeviceWireless_GetPropertyIP6Config_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyInterface provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyInterface() (string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyInterface") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func() (string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyInterface_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyInterface' -type MockDeviceWireless_GetPropertyInterface_Call struct { - *mock.Call -} - -// GetPropertyInterface is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyInterface() *MockDeviceWireless_GetPropertyInterface_Call { - return &MockDeviceWireless_GetPropertyInterface_Call{Call: _e.mock.On("GetPropertyInterface")} -} - -func (_c *MockDeviceWireless_GetPropertyInterface_Call) Run(run func()) *MockDeviceWireless_GetPropertyInterface_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyInterface_Call) Return(_a0 string, _a1 error) *MockDeviceWireless_GetPropertyInterface_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyInterface_Call) RunAndReturn(run func() (string, error)) *MockDeviceWireless_GetPropertyInterface_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyIp4Connectivity provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyIp4Connectivity() (gonetworkmanager.NmConnectivity, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyIp4Connectivity") - } - - var r0 gonetworkmanager.NmConnectivity - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.NmConnectivity, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.NmConnectivity); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(gonetworkmanager.NmConnectivity) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyIp4Connectivity_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyIp4Connectivity' -type MockDeviceWireless_GetPropertyIp4Connectivity_Call struct { - *mock.Call -} - -// GetPropertyIp4Connectivity is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyIp4Connectivity() *MockDeviceWireless_GetPropertyIp4Connectivity_Call { - return &MockDeviceWireless_GetPropertyIp4Connectivity_Call{Call: _e.mock.On("GetPropertyIp4Connectivity")} -} - -func (_c *MockDeviceWireless_GetPropertyIp4Connectivity_Call) Run(run func()) *MockDeviceWireless_GetPropertyIp4Connectivity_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyIp4Connectivity_Call) Return(_a0 gonetworkmanager.NmConnectivity, _a1 error) *MockDeviceWireless_GetPropertyIp4Connectivity_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyIp4Connectivity_Call) RunAndReturn(run func() (gonetworkmanager.NmConnectivity, error)) *MockDeviceWireless_GetPropertyIp4Connectivity_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyIpInterface provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyIpInterface() (string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyIpInterface") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func() (string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyIpInterface_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyIpInterface' -type MockDeviceWireless_GetPropertyIpInterface_Call struct { - *mock.Call -} - -// GetPropertyIpInterface is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyIpInterface() *MockDeviceWireless_GetPropertyIpInterface_Call { - return &MockDeviceWireless_GetPropertyIpInterface_Call{Call: _e.mock.On("GetPropertyIpInterface")} -} - -func (_c *MockDeviceWireless_GetPropertyIpInterface_Call) Run(run func()) *MockDeviceWireless_GetPropertyIpInterface_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyIpInterface_Call) Return(_a0 string, _a1 error) *MockDeviceWireless_GetPropertyIpInterface_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyIpInterface_Call) RunAndReturn(run func() (string, error)) *MockDeviceWireless_GetPropertyIpInterface_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyLastScan provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyLastScan() (int64, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyLastScan") - } - - var r0 int64 - var r1 error - if rf, ok := ret.Get(0).(func() (int64, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() int64); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(int64) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyLastScan_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyLastScan' -type MockDeviceWireless_GetPropertyLastScan_Call struct { - *mock.Call -} - -// GetPropertyLastScan is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyLastScan() *MockDeviceWireless_GetPropertyLastScan_Call { - return &MockDeviceWireless_GetPropertyLastScan_Call{Call: _e.mock.On("GetPropertyLastScan")} -} - -func (_c *MockDeviceWireless_GetPropertyLastScan_Call) Run(run func()) *MockDeviceWireless_GetPropertyLastScan_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyLastScan_Call) Return(_a0 int64, _a1 error) *MockDeviceWireless_GetPropertyLastScan_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyLastScan_Call) RunAndReturn(run func() (int64, error)) *MockDeviceWireless_GetPropertyLastScan_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyManaged provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyManaged() (bool, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyManaged") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func() (bool, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyManaged_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyManaged' -type MockDeviceWireless_GetPropertyManaged_Call struct { - *mock.Call -} - -// GetPropertyManaged is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyManaged() *MockDeviceWireless_GetPropertyManaged_Call { - return &MockDeviceWireless_GetPropertyManaged_Call{Call: _e.mock.On("GetPropertyManaged")} -} - -func (_c *MockDeviceWireless_GetPropertyManaged_Call) Run(run func()) *MockDeviceWireless_GetPropertyManaged_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyManaged_Call) Return(_a0 bool, _a1 error) *MockDeviceWireless_GetPropertyManaged_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyManaged_Call) RunAndReturn(run func() (bool, error)) *MockDeviceWireless_GetPropertyManaged_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyMode provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyMode() (gonetworkmanager.Nm80211Mode, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyMode") - } - - var r0 gonetworkmanager.Nm80211Mode - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.Nm80211Mode, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.Nm80211Mode); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(gonetworkmanager.Nm80211Mode) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyMode_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyMode' -type MockDeviceWireless_GetPropertyMode_Call struct { - *mock.Call -} - -// GetPropertyMode is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyMode() *MockDeviceWireless_GetPropertyMode_Call { - return &MockDeviceWireless_GetPropertyMode_Call{Call: _e.mock.On("GetPropertyMode")} -} - -func (_c *MockDeviceWireless_GetPropertyMode_Call) Run(run func()) *MockDeviceWireless_GetPropertyMode_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyMode_Call) Return(_a0 gonetworkmanager.Nm80211Mode, _a1 error) *MockDeviceWireless_GetPropertyMode_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyMode_Call) RunAndReturn(run func() (gonetworkmanager.Nm80211Mode, error)) *MockDeviceWireless_GetPropertyMode_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyMtu provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyMtu() (uint32, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyMtu") - } - - var r0 uint32 - var r1 error - if rf, ok := ret.Get(0).(func() (uint32, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() uint32); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(uint32) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyMtu_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyMtu' -type MockDeviceWireless_GetPropertyMtu_Call struct { - *mock.Call -} - -// GetPropertyMtu is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyMtu() *MockDeviceWireless_GetPropertyMtu_Call { - return &MockDeviceWireless_GetPropertyMtu_Call{Call: _e.mock.On("GetPropertyMtu")} -} - -func (_c *MockDeviceWireless_GetPropertyMtu_Call) Run(run func()) *MockDeviceWireless_GetPropertyMtu_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyMtu_Call) Return(_a0 uint32, _a1 error) *MockDeviceWireless_GetPropertyMtu_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyMtu_Call) RunAndReturn(run func() (uint32, error)) *MockDeviceWireless_GetPropertyMtu_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyNmPluginMissing provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyNmPluginMissing() (bool, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyNmPluginMissing") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func() (bool, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyNmPluginMissing_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyNmPluginMissing' -type MockDeviceWireless_GetPropertyNmPluginMissing_Call struct { - *mock.Call -} - -// GetPropertyNmPluginMissing is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyNmPluginMissing() *MockDeviceWireless_GetPropertyNmPluginMissing_Call { - return &MockDeviceWireless_GetPropertyNmPluginMissing_Call{Call: _e.mock.On("GetPropertyNmPluginMissing")} -} - -func (_c *MockDeviceWireless_GetPropertyNmPluginMissing_Call) Run(run func()) *MockDeviceWireless_GetPropertyNmPluginMissing_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyNmPluginMissing_Call) Return(_a0 bool, _a1 error) *MockDeviceWireless_GetPropertyNmPluginMissing_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyNmPluginMissing_Call) RunAndReturn(run func() (bool, error)) *MockDeviceWireless_GetPropertyNmPluginMissing_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyPermHwAddress provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyPermHwAddress() (string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyPermHwAddress") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func() (string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyPermHwAddress_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyPermHwAddress' -type MockDeviceWireless_GetPropertyPermHwAddress_Call struct { - *mock.Call -} - -// GetPropertyPermHwAddress is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyPermHwAddress() *MockDeviceWireless_GetPropertyPermHwAddress_Call { - return &MockDeviceWireless_GetPropertyPermHwAddress_Call{Call: _e.mock.On("GetPropertyPermHwAddress")} -} - -func (_c *MockDeviceWireless_GetPropertyPermHwAddress_Call) Run(run func()) *MockDeviceWireless_GetPropertyPermHwAddress_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyPermHwAddress_Call) Return(_a0 string, _a1 error) *MockDeviceWireless_GetPropertyPermHwAddress_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyPermHwAddress_Call) RunAndReturn(run func() (string, error)) *MockDeviceWireless_GetPropertyPermHwAddress_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyPhysicalPortId provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyPhysicalPortId() (string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyPhysicalPortId") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func() (string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyPhysicalPortId_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyPhysicalPortId' -type MockDeviceWireless_GetPropertyPhysicalPortId_Call struct { - *mock.Call -} - -// GetPropertyPhysicalPortId is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyPhysicalPortId() *MockDeviceWireless_GetPropertyPhysicalPortId_Call { - return &MockDeviceWireless_GetPropertyPhysicalPortId_Call{Call: _e.mock.On("GetPropertyPhysicalPortId")} -} - -func (_c *MockDeviceWireless_GetPropertyPhysicalPortId_Call) Run(run func()) *MockDeviceWireless_GetPropertyPhysicalPortId_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyPhysicalPortId_Call) Return(_a0 string, _a1 error) *MockDeviceWireless_GetPropertyPhysicalPortId_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyPhysicalPortId_Call) RunAndReturn(run func() (string, error)) *MockDeviceWireless_GetPropertyPhysicalPortId_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyReal provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyReal() (bool, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyReal") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func() (bool, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyReal_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyReal' -type MockDeviceWireless_GetPropertyReal_Call struct { - *mock.Call -} - -// GetPropertyReal is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyReal() *MockDeviceWireless_GetPropertyReal_Call { - return &MockDeviceWireless_GetPropertyReal_Call{Call: _e.mock.On("GetPropertyReal")} -} - -func (_c *MockDeviceWireless_GetPropertyReal_Call) Run(run func()) *MockDeviceWireless_GetPropertyReal_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyReal_Call) Return(_a0 bool, _a1 error) *MockDeviceWireless_GetPropertyReal_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyReal_Call) RunAndReturn(run func() (bool, error)) *MockDeviceWireless_GetPropertyReal_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyState provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyState() (gonetworkmanager.NmDeviceState, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyState") - } - - var r0 gonetworkmanager.NmDeviceState - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.NmDeviceState, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.NmDeviceState); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(gonetworkmanager.NmDeviceState) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyState_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyState' -type MockDeviceWireless_GetPropertyState_Call struct { - *mock.Call -} - -// GetPropertyState is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyState() *MockDeviceWireless_GetPropertyState_Call { - return &MockDeviceWireless_GetPropertyState_Call{Call: _e.mock.On("GetPropertyState")} -} - -func (_c *MockDeviceWireless_GetPropertyState_Call) Run(run func()) *MockDeviceWireless_GetPropertyState_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyState_Call) Return(_a0 gonetworkmanager.NmDeviceState, _a1 error) *MockDeviceWireless_GetPropertyState_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyState_Call) RunAndReturn(run func() (gonetworkmanager.NmDeviceState, error)) *MockDeviceWireless_GetPropertyState_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyUdi provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyUdi() (string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyUdi") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func() (string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyUdi_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyUdi' -type MockDeviceWireless_GetPropertyUdi_Call struct { - *mock.Call -} - -// GetPropertyUdi is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyUdi() *MockDeviceWireless_GetPropertyUdi_Call { - return &MockDeviceWireless_GetPropertyUdi_Call{Call: _e.mock.On("GetPropertyUdi")} -} - -func (_c *MockDeviceWireless_GetPropertyUdi_Call) Run(run func()) *MockDeviceWireless_GetPropertyUdi_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyUdi_Call) Return(_a0 string, _a1 error) *MockDeviceWireless_GetPropertyUdi_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyUdi_Call) RunAndReturn(run func() (string, error)) *MockDeviceWireless_GetPropertyUdi_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyWirelessCapabilities provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyWirelessCapabilities() (uint32, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyWirelessCapabilities") - } - - var r0 uint32 - var r1 error - if rf, ok := ret.Get(0).(func() (uint32, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() uint32); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(uint32) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyWirelessCapabilities_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyWirelessCapabilities' -type MockDeviceWireless_GetPropertyWirelessCapabilities_Call struct { - *mock.Call -} - -// GetPropertyWirelessCapabilities is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyWirelessCapabilities() *MockDeviceWireless_GetPropertyWirelessCapabilities_Call { - return &MockDeviceWireless_GetPropertyWirelessCapabilities_Call{Call: _e.mock.On("GetPropertyWirelessCapabilities")} -} - -func (_c *MockDeviceWireless_GetPropertyWirelessCapabilities_Call) Run(run func()) *MockDeviceWireless_GetPropertyWirelessCapabilities_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyWirelessCapabilities_Call) Return(_a0 uint32, _a1 error) *MockDeviceWireless_GetPropertyWirelessCapabilities_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyWirelessCapabilities_Call) RunAndReturn(run func() (uint32, error)) *MockDeviceWireless_GetPropertyWirelessCapabilities_Call { - _c.Call.Return(run) - return _c -} - -// MarshalJSON provides a mock function with no fields -func (_m *MockDeviceWireless) MarshalJSON() ([]byte, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for MarshalJSON") - } - - var r0 []byte - var r1 error - if rf, ok := ret.Get(0).(func() ([]byte, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []byte); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]byte) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_MarshalJSON_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'MarshalJSON' -type MockDeviceWireless_MarshalJSON_Call struct { - *mock.Call -} - -// MarshalJSON is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) MarshalJSON() *MockDeviceWireless_MarshalJSON_Call { - return &MockDeviceWireless_MarshalJSON_Call{Call: _e.mock.On("MarshalJSON")} -} - -func (_c *MockDeviceWireless_MarshalJSON_Call) Run(run func()) *MockDeviceWireless_MarshalJSON_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_MarshalJSON_Call) Return(_a0 []byte, _a1 error) *MockDeviceWireless_MarshalJSON_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_MarshalJSON_Call) RunAndReturn(run func() ([]byte, error)) *MockDeviceWireless_MarshalJSON_Call { - _c.Call.Return(run) - return _c -} - -// Reapply provides a mock function with given fields: connection, versionId, flags -func (_m *MockDeviceWireless) Reapply(connection gonetworkmanager.Connection, versionId uint64, flags uint32) error { - ret := _m.Called(connection, versionId, flags) - - if len(ret) == 0 { - panic("no return value specified for Reapply") - } - - var r0 error - if rf, ok := ret.Get(0).(func(gonetworkmanager.Connection, uint64, uint32) error); ok { - r0 = rf(connection, versionId, flags) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockDeviceWireless_Reapply_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Reapply' -type MockDeviceWireless_Reapply_Call struct { - *mock.Call -} - -// Reapply is a helper method to define mock.On call -// - connection gonetworkmanager.Connection -// - versionId uint64 -// - flags uint32 -func (_e *MockDeviceWireless_Expecter) Reapply(connection interface{}, versionId interface{}, flags interface{}) *MockDeviceWireless_Reapply_Call { - return &MockDeviceWireless_Reapply_Call{Call: _e.mock.On("Reapply", connection, versionId, flags)} -} - -func (_c *MockDeviceWireless_Reapply_Call) Run(run func(connection gonetworkmanager.Connection, versionId uint64, flags uint32)) *MockDeviceWireless_Reapply_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(gonetworkmanager.Connection), args[1].(uint64), args[2].(uint32)) - }) - return _c -} - -func (_c *MockDeviceWireless_Reapply_Call) Return(_a0 error) *MockDeviceWireless_Reapply_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockDeviceWireless_Reapply_Call) RunAndReturn(run func(gonetworkmanager.Connection, uint64, uint32) error) *MockDeviceWireless_Reapply_Call { - _c.Call.Return(run) - return _c -} - -// RequestScan provides a mock function with no fields -func (_m *MockDeviceWireless) RequestScan() error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for RequestScan") - } - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockDeviceWireless_RequestScan_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RequestScan' -type MockDeviceWireless_RequestScan_Call struct { - *mock.Call -} - -// RequestScan is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) RequestScan() *MockDeviceWireless_RequestScan_Call { - return &MockDeviceWireless_RequestScan_Call{Call: _e.mock.On("RequestScan")} -} - -func (_c *MockDeviceWireless_RequestScan_Call) Run(run func()) *MockDeviceWireless_RequestScan_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_RequestScan_Call) Return(_a0 error) *MockDeviceWireless_RequestScan_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockDeviceWireless_RequestScan_Call) RunAndReturn(run func() error) *MockDeviceWireless_RequestScan_Call { - _c.Call.Return(run) - return _c -} - -// SetPropertyAutoConnect provides a mock function with given fields: _a0 -func (_m *MockDeviceWireless) SetPropertyAutoConnect(_a0 bool) error { - ret := _m.Called(_a0) - - if len(ret) == 0 { - panic("no return value specified for SetPropertyAutoConnect") - } - - var r0 error - if rf, ok := ret.Get(0).(func(bool) error); ok { - r0 = rf(_a0) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockDeviceWireless_SetPropertyAutoConnect_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetPropertyAutoConnect' -type MockDeviceWireless_SetPropertyAutoConnect_Call struct { - *mock.Call -} - -// SetPropertyAutoConnect is a helper method to define mock.On call -// - _a0 bool -func (_e *MockDeviceWireless_Expecter) SetPropertyAutoConnect(_a0 interface{}) *MockDeviceWireless_SetPropertyAutoConnect_Call { - return &MockDeviceWireless_SetPropertyAutoConnect_Call{Call: _e.mock.On("SetPropertyAutoConnect", _a0)} -} - -func (_c *MockDeviceWireless_SetPropertyAutoConnect_Call) Run(run func(_a0 bool)) *MockDeviceWireless_SetPropertyAutoConnect_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(bool)) - }) - return _c -} - -func (_c *MockDeviceWireless_SetPropertyAutoConnect_Call) Return(_a0 error) *MockDeviceWireless_SetPropertyAutoConnect_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockDeviceWireless_SetPropertyAutoConnect_Call) RunAndReturn(run func(bool) error) *MockDeviceWireless_SetPropertyAutoConnect_Call { - _c.Call.Return(run) - return _c -} - -// SetPropertyManaged provides a mock function with given fields: _a0 -func (_m *MockDeviceWireless) SetPropertyManaged(_a0 bool) error { - ret := _m.Called(_a0) - - if len(ret) == 0 { - panic("no return value specified for SetPropertyManaged") - } - - var r0 error - if rf, ok := ret.Get(0).(func(bool) error); ok { - r0 = rf(_a0) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockDeviceWireless_SetPropertyManaged_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetPropertyManaged' -type MockDeviceWireless_SetPropertyManaged_Call struct { - *mock.Call -} - -// SetPropertyManaged is a helper method to define mock.On call -// - _a0 bool -func (_e *MockDeviceWireless_Expecter) SetPropertyManaged(_a0 interface{}) *MockDeviceWireless_SetPropertyManaged_Call { - return &MockDeviceWireless_SetPropertyManaged_Call{Call: _e.mock.On("SetPropertyManaged", _a0)} -} - -func (_c *MockDeviceWireless_SetPropertyManaged_Call) Run(run func(_a0 bool)) *MockDeviceWireless_SetPropertyManaged_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(bool)) - }) - return _c -} - -func (_c *MockDeviceWireless_SetPropertyManaged_Call) Return(_a0 error) *MockDeviceWireless_SetPropertyManaged_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockDeviceWireless_SetPropertyManaged_Call) RunAndReturn(run func(bool) error) *MockDeviceWireless_SetPropertyManaged_Call { - _c.Call.Return(run) - return _c -} - -// SubscribeState provides a mock function with given fields: receiver, exit -func (_m *MockDeviceWireless) SubscribeState(receiver chan gonetworkmanager.DeviceStateChange, exit chan struct{}) error { - ret := _m.Called(receiver, exit) - - if len(ret) == 0 { - panic("no return value specified for SubscribeState") - } - - var r0 error - if rf, ok := ret.Get(0).(func(chan gonetworkmanager.DeviceStateChange, chan struct{}) error); ok { - r0 = rf(receiver, exit) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockDeviceWireless_SubscribeState_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SubscribeState' -type MockDeviceWireless_SubscribeState_Call struct { - *mock.Call -} - -// SubscribeState is a helper method to define mock.On call -// - receiver chan gonetworkmanager.DeviceStateChange -// - exit chan struct{} -func (_e *MockDeviceWireless_Expecter) SubscribeState(receiver interface{}, exit interface{}) *MockDeviceWireless_SubscribeState_Call { - return &MockDeviceWireless_SubscribeState_Call{Call: _e.mock.On("SubscribeState", receiver, exit)} -} - -func (_c *MockDeviceWireless_SubscribeState_Call) Run(run func(receiver chan gonetworkmanager.DeviceStateChange, exit chan struct{})) *MockDeviceWireless_SubscribeState_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(chan gonetworkmanager.DeviceStateChange), args[1].(chan struct{})) - }) - return _c -} - -func (_c *MockDeviceWireless_SubscribeState_Call) Return(err error) *MockDeviceWireless_SubscribeState_Call { - _c.Call.Return(err) - return _c -} - -func (_c *MockDeviceWireless_SubscribeState_Call) RunAndReturn(run func(chan gonetworkmanager.DeviceStateChange, chan struct{}) error) *MockDeviceWireless_SubscribeState_Call { - _c.Call.Return(run) - return _c -} - -// NewMockDeviceWireless creates a new instance of MockDeviceWireless. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewMockDeviceWireless(t interface { - mock.TestingT - Cleanup(func()) -}) *MockDeviceWireless { - mock := &MockDeviceWireless{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/nix/inputs/dms-cli/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_IP4Config.go b/nix/inputs/dms-cli/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_IP4Config.go deleted file mode 100644 index 235be86..0000000 --- a/nix/inputs/dms-cli/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_IP4Config.go +++ /dev/null @@ -1,772 +0,0 @@ -// Code generated by mockery v2.53.5. DO NOT EDIT. - -package gonetworkmanager - -import ( - gonetworkmanager "github.com/Wifx/gonetworkmanager/v2" - mock "github.com/stretchr/testify/mock" -) - -// MockIP4Config is an autogenerated mock type for the IP4Config type -type MockIP4Config struct { - mock.Mock -} - -type MockIP4Config_Expecter struct { - mock *mock.Mock -} - -func (_m *MockIP4Config) EXPECT() *MockIP4Config_Expecter { - return &MockIP4Config_Expecter{mock: &_m.Mock} -} - -// GetPropertyAddressData provides a mock function with no fields -func (_m *MockIP4Config) GetPropertyAddressData() ([]gonetworkmanager.IP4AddressData, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyAddressData") - } - - var r0 []gonetworkmanager.IP4AddressData - var r1 error - if rf, ok := ret.Get(0).(func() ([]gonetworkmanager.IP4AddressData, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []gonetworkmanager.IP4AddressData); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]gonetworkmanager.IP4AddressData) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockIP4Config_GetPropertyAddressData_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyAddressData' -type MockIP4Config_GetPropertyAddressData_Call struct { - *mock.Call -} - -// GetPropertyAddressData is a helper method to define mock.On call -func (_e *MockIP4Config_Expecter) GetPropertyAddressData() *MockIP4Config_GetPropertyAddressData_Call { - return &MockIP4Config_GetPropertyAddressData_Call{Call: _e.mock.On("GetPropertyAddressData")} -} - -func (_c *MockIP4Config_GetPropertyAddressData_Call) Run(run func()) *MockIP4Config_GetPropertyAddressData_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockIP4Config_GetPropertyAddressData_Call) Return(_a0 []gonetworkmanager.IP4AddressData, _a1 error) *MockIP4Config_GetPropertyAddressData_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockIP4Config_GetPropertyAddressData_Call) RunAndReturn(run func() ([]gonetworkmanager.IP4AddressData, error)) *MockIP4Config_GetPropertyAddressData_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyAddresses provides a mock function with no fields -func (_m *MockIP4Config) GetPropertyAddresses() ([]gonetworkmanager.IP4Address, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyAddresses") - } - - var r0 []gonetworkmanager.IP4Address - var r1 error - if rf, ok := ret.Get(0).(func() ([]gonetworkmanager.IP4Address, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []gonetworkmanager.IP4Address); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]gonetworkmanager.IP4Address) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockIP4Config_GetPropertyAddresses_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyAddresses' -type MockIP4Config_GetPropertyAddresses_Call struct { - *mock.Call -} - -// GetPropertyAddresses is a helper method to define mock.On call -func (_e *MockIP4Config_Expecter) GetPropertyAddresses() *MockIP4Config_GetPropertyAddresses_Call { - return &MockIP4Config_GetPropertyAddresses_Call{Call: _e.mock.On("GetPropertyAddresses")} -} - -func (_c *MockIP4Config_GetPropertyAddresses_Call) Run(run func()) *MockIP4Config_GetPropertyAddresses_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockIP4Config_GetPropertyAddresses_Call) Return(_a0 []gonetworkmanager.IP4Address, _a1 error) *MockIP4Config_GetPropertyAddresses_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockIP4Config_GetPropertyAddresses_Call) RunAndReturn(run func() ([]gonetworkmanager.IP4Address, error)) *MockIP4Config_GetPropertyAddresses_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyDnsOptions provides a mock function with no fields -func (_m *MockIP4Config) GetPropertyDnsOptions() ([]string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyDnsOptions") - } - - var r0 []string - var r1 error - if rf, ok := ret.Get(0).(func() ([]string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []string); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]string) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockIP4Config_GetPropertyDnsOptions_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyDnsOptions' -type MockIP4Config_GetPropertyDnsOptions_Call struct { - *mock.Call -} - -// GetPropertyDnsOptions is a helper method to define mock.On call -func (_e *MockIP4Config_Expecter) GetPropertyDnsOptions() *MockIP4Config_GetPropertyDnsOptions_Call { - return &MockIP4Config_GetPropertyDnsOptions_Call{Call: _e.mock.On("GetPropertyDnsOptions")} -} - -func (_c *MockIP4Config_GetPropertyDnsOptions_Call) Run(run func()) *MockIP4Config_GetPropertyDnsOptions_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockIP4Config_GetPropertyDnsOptions_Call) Return(_a0 []string, _a1 error) *MockIP4Config_GetPropertyDnsOptions_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockIP4Config_GetPropertyDnsOptions_Call) RunAndReturn(run func() ([]string, error)) *MockIP4Config_GetPropertyDnsOptions_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyDnsPriority provides a mock function with no fields -func (_m *MockIP4Config) GetPropertyDnsPriority() (uint32, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyDnsPriority") - } - - var r0 uint32 - var r1 error - if rf, ok := ret.Get(0).(func() (uint32, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() uint32); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(uint32) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockIP4Config_GetPropertyDnsPriority_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyDnsPriority' -type MockIP4Config_GetPropertyDnsPriority_Call struct { - *mock.Call -} - -// GetPropertyDnsPriority is a helper method to define mock.On call -func (_e *MockIP4Config_Expecter) GetPropertyDnsPriority() *MockIP4Config_GetPropertyDnsPriority_Call { - return &MockIP4Config_GetPropertyDnsPriority_Call{Call: _e.mock.On("GetPropertyDnsPriority")} -} - -func (_c *MockIP4Config_GetPropertyDnsPriority_Call) Run(run func()) *MockIP4Config_GetPropertyDnsPriority_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockIP4Config_GetPropertyDnsPriority_Call) Return(_a0 uint32, _a1 error) *MockIP4Config_GetPropertyDnsPriority_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockIP4Config_GetPropertyDnsPriority_Call) RunAndReturn(run func() (uint32, error)) *MockIP4Config_GetPropertyDnsPriority_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyDomains provides a mock function with no fields -func (_m *MockIP4Config) GetPropertyDomains() ([]string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyDomains") - } - - var r0 []string - var r1 error - if rf, ok := ret.Get(0).(func() ([]string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []string); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]string) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockIP4Config_GetPropertyDomains_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyDomains' -type MockIP4Config_GetPropertyDomains_Call struct { - *mock.Call -} - -// GetPropertyDomains is a helper method to define mock.On call -func (_e *MockIP4Config_Expecter) GetPropertyDomains() *MockIP4Config_GetPropertyDomains_Call { - return &MockIP4Config_GetPropertyDomains_Call{Call: _e.mock.On("GetPropertyDomains")} -} - -func (_c *MockIP4Config_GetPropertyDomains_Call) Run(run func()) *MockIP4Config_GetPropertyDomains_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockIP4Config_GetPropertyDomains_Call) Return(_a0 []string, _a1 error) *MockIP4Config_GetPropertyDomains_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockIP4Config_GetPropertyDomains_Call) RunAndReturn(run func() ([]string, error)) *MockIP4Config_GetPropertyDomains_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyGateway provides a mock function with no fields -func (_m *MockIP4Config) GetPropertyGateway() (string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyGateway") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func() (string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockIP4Config_GetPropertyGateway_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyGateway' -type MockIP4Config_GetPropertyGateway_Call struct { - *mock.Call -} - -// GetPropertyGateway is a helper method to define mock.On call -func (_e *MockIP4Config_Expecter) GetPropertyGateway() *MockIP4Config_GetPropertyGateway_Call { - return &MockIP4Config_GetPropertyGateway_Call{Call: _e.mock.On("GetPropertyGateway")} -} - -func (_c *MockIP4Config_GetPropertyGateway_Call) Run(run func()) *MockIP4Config_GetPropertyGateway_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockIP4Config_GetPropertyGateway_Call) Return(_a0 string, _a1 error) *MockIP4Config_GetPropertyGateway_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockIP4Config_GetPropertyGateway_Call) RunAndReturn(run func() (string, error)) *MockIP4Config_GetPropertyGateway_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyNameserverData provides a mock function with no fields -func (_m *MockIP4Config) GetPropertyNameserverData() ([]gonetworkmanager.IP4NameserverData, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyNameserverData") - } - - var r0 []gonetworkmanager.IP4NameserverData - var r1 error - if rf, ok := ret.Get(0).(func() ([]gonetworkmanager.IP4NameserverData, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []gonetworkmanager.IP4NameserverData); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]gonetworkmanager.IP4NameserverData) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockIP4Config_GetPropertyNameserverData_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyNameserverData' -type MockIP4Config_GetPropertyNameserverData_Call struct { - *mock.Call -} - -// GetPropertyNameserverData is a helper method to define mock.On call -func (_e *MockIP4Config_Expecter) GetPropertyNameserverData() *MockIP4Config_GetPropertyNameserverData_Call { - return &MockIP4Config_GetPropertyNameserverData_Call{Call: _e.mock.On("GetPropertyNameserverData")} -} - -func (_c *MockIP4Config_GetPropertyNameserverData_Call) Run(run func()) *MockIP4Config_GetPropertyNameserverData_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockIP4Config_GetPropertyNameserverData_Call) Return(_a0 []gonetworkmanager.IP4NameserverData, _a1 error) *MockIP4Config_GetPropertyNameserverData_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockIP4Config_GetPropertyNameserverData_Call) RunAndReturn(run func() ([]gonetworkmanager.IP4NameserverData, error)) *MockIP4Config_GetPropertyNameserverData_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyNameservers provides a mock function with no fields -func (_m *MockIP4Config) GetPropertyNameservers() ([]string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyNameservers") - } - - var r0 []string - var r1 error - if rf, ok := ret.Get(0).(func() ([]string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []string); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]string) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockIP4Config_GetPropertyNameservers_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyNameservers' -type MockIP4Config_GetPropertyNameservers_Call struct { - *mock.Call -} - -// GetPropertyNameservers is a helper method to define mock.On call -func (_e *MockIP4Config_Expecter) GetPropertyNameservers() *MockIP4Config_GetPropertyNameservers_Call { - return &MockIP4Config_GetPropertyNameservers_Call{Call: _e.mock.On("GetPropertyNameservers")} -} - -func (_c *MockIP4Config_GetPropertyNameservers_Call) Run(run func()) *MockIP4Config_GetPropertyNameservers_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockIP4Config_GetPropertyNameservers_Call) Return(_a0 []string, _a1 error) *MockIP4Config_GetPropertyNameservers_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockIP4Config_GetPropertyNameservers_Call) RunAndReturn(run func() ([]string, error)) *MockIP4Config_GetPropertyNameservers_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyRouteData provides a mock function with no fields -func (_m *MockIP4Config) GetPropertyRouteData() ([]gonetworkmanager.IP4RouteData, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyRouteData") - } - - var r0 []gonetworkmanager.IP4RouteData - var r1 error - if rf, ok := ret.Get(0).(func() ([]gonetworkmanager.IP4RouteData, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []gonetworkmanager.IP4RouteData); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]gonetworkmanager.IP4RouteData) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockIP4Config_GetPropertyRouteData_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyRouteData' -type MockIP4Config_GetPropertyRouteData_Call struct { - *mock.Call -} - -// GetPropertyRouteData is a helper method to define mock.On call -func (_e *MockIP4Config_Expecter) GetPropertyRouteData() *MockIP4Config_GetPropertyRouteData_Call { - return &MockIP4Config_GetPropertyRouteData_Call{Call: _e.mock.On("GetPropertyRouteData")} -} - -func (_c *MockIP4Config_GetPropertyRouteData_Call) Run(run func()) *MockIP4Config_GetPropertyRouteData_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockIP4Config_GetPropertyRouteData_Call) Return(_a0 []gonetworkmanager.IP4RouteData, _a1 error) *MockIP4Config_GetPropertyRouteData_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockIP4Config_GetPropertyRouteData_Call) RunAndReturn(run func() ([]gonetworkmanager.IP4RouteData, error)) *MockIP4Config_GetPropertyRouteData_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyRoutes provides a mock function with no fields -func (_m *MockIP4Config) GetPropertyRoutes() ([]gonetworkmanager.IP4Route, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyRoutes") - } - - var r0 []gonetworkmanager.IP4Route - var r1 error - if rf, ok := ret.Get(0).(func() ([]gonetworkmanager.IP4Route, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []gonetworkmanager.IP4Route); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]gonetworkmanager.IP4Route) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockIP4Config_GetPropertyRoutes_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyRoutes' -type MockIP4Config_GetPropertyRoutes_Call struct { - *mock.Call -} - -// GetPropertyRoutes is a helper method to define mock.On call -func (_e *MockIP4Config_Expecter) GetPropertyRoutes() *MockIP4Config_GetPropertyRoutes_Call { - return &MockIP4Config_GetPropertyRoutes_Call{Call: _e.mock.On("GetPropertyRoutes")} -} - -func (_c *MockIP4Config_GetPropertyRoutes_Call) Run(run func()) *MockIP4Config_GetPropertyRoutes_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockIP4Config_GetPropertyRoutes_Call) Return(_a0 []gonetworkmanager.IP4Route, _a1 error) *MockIP4Config_GetPropertyRoutes_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockIP4Config_GetPropertyRoutes_Call) RunAndReturn(run func() ([]gonetworkmanager.IP4Route, error)) *MockIP4Config_GetPropertyRoutes_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertySearches provides a mock function with no fields -func (_m *MockIP4Config) GetPropertySearches() ([]string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertySearches") - } - - var r0 []string - var r1 error - if rf, ok := ret.Get(0).(func() ([]string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []string); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]string) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockIP4Config_GetPropertySearches_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertySearches' -type MockIP4Config_GetPropertySearches_Call struct { - *mock.Call -} - -// GetPropertySearches is a helper method to define mock.On call -func (_e *MockIP4Config_Expecter) GetPropertySearches() *MockIP4Config_GetPropertySearches_Call { - return &MockIP4Config_GetPropertySearches_Call{Call: _e.mock.On("GetPropertySearches")} -} - -func (_c *MockIP4Config_GetPropertySearches_Call) Run(run func()) *MockIP4Config_GetPropertySearches_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockIP4Config_GetPropertySearches_Call) Return(_a0 []string, _a1 error) *MockIP4Config_GetPropertySearches_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockIP4Config_GetPropertySearches_Call) RunAndReturn(run func() ([]string, error)) *MockIP4Config_GetPropertySearches_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyWinsServerData provides a mock function with no fields -func (_m *MockIP4Config) GetPropertyWinsServerData() ([]string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyWinsServerData") - } - - var r0 []string - var r1 error - if rf, ok := ret.Get(0).(func() ([]string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []string); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]string) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockIP4Config_GetPropertyWinsServerData_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyWinsServerData' -type MockIP4Config_GetPropertyWinsServerData_Call struct { - *mock.Call -} - -// GetPropertyWinsServerData is a helper method to define mock.On call -func (_e *MockIP4Config_Expecter) GetPropertyWinsServerData() *MockIP4Config_GetPropertyWinsServerData_Call { - return &MockIP4Config_GetPropertyWinsServerData_Call{Call: _e.mock.On("GetPropertyWinsServerData")} -} - -func (_c *MockIP4Config_GetPropertyWinsServerData_Call) Run(run func()) *MockIP4Config_GetPropertyWinsServerData_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockIP4Config_GetPropertyWinsServerData_Call) Return(_a0 []string, _a1 error) *MockIP4Config_GetPropertyWinsServerData_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockIP4Config_GetPropertyWinsServerData_Call) RunAndReturn(run func() ([]string, error)) *MockIP4Config_GetPropertyWinsServerData_Call { - _c.Call.Return(run) - return _c -} - -// MarshalJSON provides a mock function with no fields -func (_m *MockIP4Config) MarshalJSON() ([]byte, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for MarshalJSON") - } - - var r0 []byte - var r1 error - if rf, ok := ret.Get(0).(func() ([]byte, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []byte); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]byte) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockIP4Config_MarshalJSON_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'MarshalJSON' -type MockIP4Config_MarshalJSON_Call struct { - *mock.Call -} - -// MarshalJSON is a helper method to define mock.On call -func (_e *MockIP4Config_Expecter) MarshalJSON() *MockIP4Config_MarshalJSON_Call { - return &MockIP4Config_MarshalJSON_Call{Call: _e.mock.On("MarshalJSON")} -} - -func (_c *MockIP4Config_MarshalJSON_Call) Run(run func()) *MockIP4Config_MarshalJSON_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockIP4Config_MarshalJSON_Call) Return(_a0 []byte, _a1 error) *MockIP4Config_MarshalJSON_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockIP4Config_MarshalJSON_Call) RunAndReturn(run func() ([]byte, error)) *MockIP4Config_MarshalJSON_Call { - _c.Call.Return(run) - return _c -} - -// NewMockIP4Config creates a new instance of MockIP4Config. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewMockIP4Config(t interface { - mock.TestingT - Cleanup(func()) -}) *MockIP4Config { - mock := &MockIP4Config{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/nix/inputs/dms-cli/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_NetworkManager.go b/nix/inputs/dms-cli/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_NetworkManager.go deleted file mode 100644 index e74ffb0..0000000 --- a/nix/inputs/dms-cli/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_NetworkManager.go +++ /dev/null @@ -1,2349 +0,0 @@ -// Code generated by mockery v2.53.5. DO NOT EDIT. - -package gonetworkmanager - -import ( - gonetworkmanager "github.com/Wifx/gonetworkmanager/v2" - dbus "github.com/godbus/dbus/v5" - - mock "github.com/stretchr/testify/mock" -) - -// MockNetworkManager is an autogenerated mock type for the NetworkManager type -type MockNetworkManager struct { - mock.Mock -} - -type MockNetworkManager_Expecter struct { - mock *mock.Mock -} - -func (_m *MockNetworkManager) EXPECT() *MockNetworkManager_Expecter { - return &MockNetworkManager_Expecter{mock: &_m.Mock} -} - -// ActivateConnection provides a mock function with given fields: connection, device, specificObject -func (_m *MockNetworkManager) ActivateConnection(connection gonetworkmanager.Connection, device gonetworkmanager.Device, specificObject *dbus.Object) (gonetworkmanager.ActiveConnection, error) { - ret := _m.Called(connection, device, specificObject) - - if len(ret) == 0 { - panic("no return value specified for ActivateConnection") - } - - var r0 gonetworkmanager.ActiveConnection - var r1 error - if rf, ok := ret.Get(0).(func(gonetworkmanager.Connection, gonetworkmanager.Device, *dbus.Object) (gonetworkmanager.ActiveConnection, error)); ok { - return rf(connection, device, specificObject) - } - if rf, ok := ret.Get(0).(func(gonetworkmanager.Connection, gonetworkmanager.Device, *dbus.Object) gonetworkmanager.ActiveConnection); ok { - r0 = rf(connection, device, specificObject) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.ActiveConnection) - } - } - - if rf, ok := ret.Get(1).(func(gonetworkmanager.Connection, gonetworkmanager.Device, *dbus.Object) error); ok { - r1 = rf(connection, device, specificObject) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_ActivateConnection_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ActivateConnection' -type MockNetworkManager_ActivateConnection_Call struct { - *mock.Call -} - -// ActivateConnection is a helper method to define mock.On call -// - connection gonetworkmanager.Connection -// - device gonetworkmanager.Device -// - specificObject *dbus.Object -func (_e *MockNetworkManager_Expecter) ActivateConnection(connection interface{}, device interface{}, specificObject interface{}) *MockNetworkManager_ActivateConnection_Call { - return &MockNetworkManager_ActivateConnection_Call{Call: _e.mock.On("ActivateConnection", connection, device, specificObject)} -} - -func (_c *MockNetworkManager_ActivateConnection_Call) Run(run func(connection gonetworkmanager.Connection, device gonetworkmanager.Device, specificObject *dbus.Object)) *MockNetworkManager_ActivateConnection_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(gonetworkmanager.Connection), args[1].(gonetworkmanager.Device), args[2].(*dbus.Object)) - }) - return _c -} - -func (_c *MockNetworkManager_ActivateConnection_Call) Return(_a0 gonetworkmanager.ActiveConnection, _a1 error) *MockNetworkManager_ActivateConnection_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_ActivateConnection_Call) RunAndReturn(run func(gonetworkmanager.Connection, gonetworkmanager.Device, *dbus.Object) (gonetworkmanager.ActiveConnection, error)) *MockNetworkManager_ActivateConnection_Call { - _c.Call.Return(run) - return _c -} - -// ActivateWirelessConnection provides a mock function with given fields: connection, device, accessPoint -func (_m *MockNetworkManager) ActivateWirelessConnection(connection gonetworkmanager.Connection, device gonetworkmanager.Device, accessPoint gonetworkmanager.AccessPoint) (gonetworkmanager.ActiveConnection, error) { - ret := _m.Called(connection, device, accessPoint) - - if len(ret) == 0 { - panic("no return value specified for ActivateWirelessConnection") - } - - var r0 gonetworkmanager.ActiveConnection - var r1 error - if rf, ok := ret.Get(0).(func(gonetworkmanager.Connection, gonetworkmanager.Device, gonetworkmanager.AccessPoint) (gonetworkmanager.ActiveConnection, error)); ok { - return rf(connection, device, accessPoint) - } - if rf, ok := ret.Get(0).(func(gonetworkmanager.Connection, gonetworkmanager.Device, gonetworkmanager.AccessPoint) gonetworkmanager.ActiveConnection); ok { - r0 = rf(connection, device, accessPoint) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.ActiveConnection) - } - } - - if rf, ok := ret.Get(1).(func(gonetworkmanager.Connection, gonetworkmanager.Device, gonetworkmanager.AccessPoint) error); ok { - r1 = rf(connection, device, accessPoint) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_ActivateWirelessConnection_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ActivateWirelessConnection' -type MockNetworkManager_ActivateWirelessConnection_Call struct { - *mock.Call -} - -// ActivateWirelessConnection is a helper method to define mock.On call -// - connection gonetworkmanager.Connection -// - device gonetworkmanager.Device -// - accessPoint gonetworkmanager.AccessPoint -func (_e *MockNetworkManager_Expecter) ActivateWirelessConnection(connection interface{}, device interface{}, accessPoint interface{}) *MockNetworkManager_ActivateWirelessConnection_Call { - return &MockNetworkManager_ActivateWirelessConnection_Call{Call: _e.mock.On("ActivateWirelessConnection", connection, device, accessPoint)} -} - -func (_c *MockNetworkManager_ActivateWirelessConnection_Call) Run(run func(connection gonetworkmanager.Connection, device gonetworkmanager.Device, accessPoint gonetworkmanager.AccessPoint)) *MockNetworkManager_ActivateWirelessConnection_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(gonetworkmanager.Connection), args[1].(gonetworkmanager.Device), args[2].(gonetworkmanager.AccessPoint)) - }) - return _c -} - -func (_c *MockNetworkManager_ActivateWirelessConnection_Call) Return(_a0 gonetworkmanager.ActiveConnection, _a1 error) *MockNetworkManager_ActivateWirelessConnection_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_ActivateWirelessConnection_Call) RunAndReturn(run func(gonetworkmanager.Connection, gonetworkmanager.Device, gonetworkmanager.AccessPoint) (gonetworkmanager.ActiveConnection, error)) *MockNetworkManager_ActivateWirelessConnection_Call { - _c.Call.Return(run) - return _c -} - -// AddAndActivateConnection provides a mock function with given fields: connection, device -func (_m *MockNetworkManager) AddAndActivateConnection(connection map[string]map[string]interface{}, device gonetworkmanager.Device) (gonetworkmanager.ActiveConnection, error) { - ret := _m.Called(connection, device) - - if len(ret) == 0 { - panic("no return value specified for AddAndActivateConnection") - } - - var r0 gonetworkmanager.ActiveConnection - var r1 error - if rf, ok := ret.Get(0).(func(map[string]map[string]interface{}, gonetworkmanager.Device) (gonetworkmanager.ActiveConnection, error)); ok { - return rf(connection, device) - } - if rf, ok := ret.Get(0).(func(map[string]map[string]interface{}, gonetworkmanager.Device) gonetworkmanager.ActiveConnection); ok { - r0 = rf(connection, device) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.ActiveConnection) - } - } - - if rf, ok := ret.Get(1).(func(map[string]map[string]interface{}, gonetworkmanager.Device) error); ok { - r1 = rf(connection, device) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_AddAndActivateConnection_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddAndActivateConnection' -type MockNetworkManager_AddAndActivateConnection_Call struct { - *mock.Call -} - -// AddAndActivateConnection is a helper method to define mock.On call -// - connection map[string]map[string]interface{} -// - device gonetworkmanager.Device -func (_e *MockNetworkManager_Expecter) AddAndActivateConnection(connection interface{}, device interface{}) *MockNetworkManager_AddAndActivateConnection_Call { - return &MockNetworkManager_AddAndActivateConnection_Call{Call: _e.mock.On("AddAndActivateConnection", connection, device)} -} - -func (_c *MockNetworkManager_AddAndActivateConnection_Call) Run(run func(connection map[string]map[string]interface{}, device gonetworkmanager.Device)) *MockNetworkManager_AddAndActivateConnection_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(map[string]map[string]interface{}), args[1].(gonetworkmanager.Device)) - }) - return _c -} - -func (_c *MockNetworkManager_AddAndActivateConnection_Call) Return(_a0 gonetworkmanager.ActiveConnection, _a1 error) *MockNetworkManager_AddAndActivateConnection_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_AddAndActivateConnection_Call) RunAndReturn(run func(map[string]map[string]interface{}, gonetworkmanager.Device) (gonetworkmanager.ActiveConnection, error)) *MockNetworkManager_AddAndActivateConnection_Call { - _c.Call.Return(run) - return _c -} - -// AddAndActivateWirelessConnection provides a mock function with given fields: connection, device, accessPoint -func (_m *MockNetworkManager) AddAndActivateWirelessConnection(connection map[string]map[string]interface{}, device gonetworkmanager.Device, accessPoint gonetworkmanager.AccessPoint) (gonetworkmanager.ActiveConnection, error) { - ret := _m.Called(connection, device, accessPoint) - - if len(ret) == 0 { - panic("no return value specified for AddAndActivateWirelessConnection") - } - - var r0 gonetworkmanager.ActiveConnection - var r1 error - if rf, ok := ret.Get(0).(func(map[string]map[string]interface{}, gonetworkmanager.Device, gonetworkmanager.AccessPoint) (gonetworkmanager.ActiveConnection, error)); ok { - return rf(connection, device, accessPoint) - } - if rf, ok := ret.Get(0).(func(map[string]map[string]interface{}, gonetworkmanager.Device, gonetworkmanager.AccessPoint) gonetworkmanager.ActiveConnection); ok { - r0 = rf(connection, device, accessPoint) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.ActiveConnection) - } - } - - if rf, ok := ret.Get(1).(func(map[string]map[string]interface{}, gonetworkmanager.Device, gonetworkmanager.AccessPoint) error); ok { - r1 = rf(connection, device, accessPoint) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_AddAndActivateWirelessConnection_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddAndActivateWirelessConnection' -type MockNetworkManager_AddAndActivateWirelessConnection_Call struct { - *mock.Call -} - -// AddAndActivateWirelessConnection is a helper method to define mock.On call -// - connection map[string]map[string]interface{} -// - device gonetworkmanager.Device -// - accessPoint gonetworkmanager.AccessPoint -func (_e *MockNetworkManager_Expecter) AddAndActivateWirelessConnection(connection interface{}, device interface{}, accessPoint interface{}) *MockNetworkManager_AddAndActivateWirelessConnection_Call { - return &MockNetworkManager_AddAndActivateWirelessConnection_Call{Call: _e.mock.On("AddAndActivateWirelessConnection", connection, device, accessPoint)} -} - -func (_c *MockNetworkManager_AddAndActivateWirelessConnection_Call) Run(run func(connection map[string]map[string]interface{}, device gonetworkmanager.Device, accessPoint gonetworkmanager.AccessPoint)) *MockNetworkManager_AddAndActivateWirelessConnection_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(map[string]map[string]interface{}), args[1].(gonetworkmanager.Device), args[2].(gonetworkmanager.AccessPoint)) - }) - return _c -} - -func (_c *MockNetworkManager_AddAndActivateWirelessConnection_Call) Return(_a0 gonetworkmanager.ActiveConnection, _a1 error) *MockNetworkManager_AddAndActivateWirelessConnection_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_AddAndActivateWirelessConnection_Call) RunAndReturn(run func(map[string]map[string]interface{}, gonetworkmanager.Device, gonetworkmanager.AccessPoint) (gonetworkmanager.ActiveConnection, error)) *MockNetworkManager_AddAndActivateWirelessConnection_Call { - _c.Call.Return(run) - return _c -} - -// CheckConnectivity provides a mock function with no fields -func (_m *MockNetworkManager) CheckConnectivity() error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for CheckConnectivity") - } - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockNetworkManager_CheckConnectivity_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CheckConnectivity' -type MockNetworkManager_CheckConnectivity_Call struct { - *mock.Call -} - -// CheckConnectivity is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) CheckConnectivity() *MockNetworkManager_CheckConnectivity_Call { - return &MockNetworkManager_CheckConnectivity_Call{Call: _e.mock.On("CheckConnectivity")} -} - -func (_c *MockNetworkManager_CheckConnectivity_Call) Run(run func()) *MockNetworkManager_CheckConnectivity_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_CheckConnectivity_Call) Return(_a0 error) *MockNetworkManager_CheckConnectivity_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockNetworkManager_CheckConnectivity_Call) RunAndReturn(run func() error) *MockNetworkManager_CheckConnectivity_Call { - _c.Call.Return(run) - return _c -} - -// CheckpointAdjustRollbackTimeout provides a mock function with given fields: checkpoint, addTimeout -func (_m *MockNetworkManager) CheckpointAdjustRollbackTimeout(checkpoint gonetworkmanager.Checkpoint, addTimeout uint32) error { - ret := _m.Called(checkpoint, addTimeout) - - if len(ret) == 0 { - panic("no return value specified for CheckpointAdjustRollbackTimeout") - } - - var r0 error - if rf, ok := ret.Get(0).(func(gonetworkmanager.Checkpoint, uint32) error); ok { - r0 = rf(checkpoint, addTimeout) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockNetworkManager_CheckpointAdjustRollbackTimeout_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CheckpointAdjustRollbackTimeout' -type MockNetworkManager_CheckpointAdjustRollbackTimeout_Call struct { - *mock.Call -} - -// CheckpointAdjustRollbackTimeout is a helper method to define mock.On call -// - checkpoint gonetworkmanager.Checkpoint -// - addTimeout uint32 -func (_e *MockNetworkManager_Expecter) CheckpointAdjustRollbackTimeout(checkpoint interface{}, addTimeout interface{}) *MockNetworkManager_CheckpointAdjustRollbackTimeout_Call { - return &MockNetworkManager_CheckpointAdjustRollbackTimeout_Call{Call: _e.mock.On("CheckpointAdjustRollbackTimeout", checkpoint, addTimeout)} -} - -func (_c *MockNetworkManager_CheckpointAdjustRollbackTimeout_Call) Run(run func(checkpoint gonetworkmanager.Checkpoint, addTimeout uint32)) *MockNetworkManager_CheckpointAdjustRollbackTimeout_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(gonetworkmanager.Checkpoint), args[1].(uint32)) - }) - return _c -} - -func (_c *MockNetworkManager_CheckpointAdjustRollbackTimeout_Call) Return(_a0 error) *MockNetworkManager_CheckpointAdjustRollbackTimeout_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockNetworkManager_CheckpointAdjustRollbackTimeout_Call) RunAndReturn(run func(gonetworkmanager.Checkpoint, uint32) error) *MockNetworkManager_CheckpointAdjustRollbackTimeout_Call { - _c.Call.Return(run) - return _c -} - -// CheckpointCreate provides a mock function with given fields: devices, rollbackTimeout, flags -func (_m *MockNetworkManager) CheckpointCreate(devices []gonetworkmanager.Device, rollbackTimeout uint32, flags uint32) (gonetworkmanager.Checkpoint, error) { - ret := _m.Called(devices, rollbackTimeout, flags) - - if len(ret) == 0 { - panic("no return value specified for CheckpointCreate") - } - - var r0 gonetworkmanager.Checkpoint - var r1 error - if rf, ok := ret.Get(0).(func([]gonetworkmanager.Device, uint32, uint32) (gonetworkmanager.Checkpoint, error)); ok { - return rf(devices, rollbackTimeout, flags) - } - if rf, ok := ret.Get(0).(func([]gonetworkmanager.Device, uint32, uint32) gonetworkmanager.Checkpoint); ok { - r0 = rf(devices, rollbackTimeout, flags) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.Checkpoint) - } - } - - if rf, ok := ret.Get(1).(func([]gonetworkmanager.Device, uint32, uint32) error); ok { - r1 = rf(devices, rollbackTimeout, flags) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_CheckpointCreate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CheckpointCreate' -type MockNetworkManager_CheckpointCreate_Call struct { - *mock.Call -} - -// CheckpointCreate is a helper method to define mock.On call -// - devices []gonetworkmanager.Device -// - rollbackTimeout uint32 -// - flags uint32 -func (_e *MockNetworkManager_Expecter) CheckpointCreate(devices interface{}, rollbackTimeout interface{}, flags interface{}) *MockNetworkManager_CheckpointCreate_Call { - return &MockNetworkManager_CheckpointCreate_Call{Call: _e.mock.On("CheckpointCreate", devices, rollbackTimeout, flags)} -} - -func (_c *MockNetworkManager_CheckpointCreate_Call) Run(run func(devices []gonetworkmanager.Device, rollbackTimeout uint32, flags uint32)) *MockNetworkManager_CheckpointCreate_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].([]gonetworkmanager.Device), args[1].(uint32), args[2].(uint32)) - }) - return _c -} - -func (_c *MockNetworkManager_CheckpointCreate_Call) Return(_a0 gonetworkmanager.Checkpoint, _a1 error) *MockNetworkManager_CheckpointCreate_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_CheckpointCreate_Call) RunAndReturn(run func([]gonetworkmanager.Device, uint32, uint32) (gonetworkmanager.Checkpoint, error)) *MockNetworkManager_CheckpointCreate_Call { - _c.Call.Return(run) - return _c -} - -// CheckpointDestroy provides a mock function with given fields: checkpoint -func (_m *MockNetworkManager) CheckpointDestroy(checkpoint gonetworkmanager.Checkpoint) error { - ret := _m.Called(checkpoint) - - if len(ret) == 0 { - panic("no return value specified for CheckpointDestroy") - } - - var r0 error - if rf, ok := ret.Get(0).(func(gonetworkmanager.Checkpoint) error); ok { - r0 = rf(checkpoint) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockNetworkManager_CheckpointDestroy_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CheckpointDestroy' -type MockNetworkManager_CheckpointDestroy_Call struct { - *mock.Call -} - -// CheckpointDestroy is a helper method to define mock.On call -// - checkpoint gonetworkmanager.Checkpoint -func (_e *MockNetworkManager_Expecter) CheckpointDestroy(checkpoint interface{}) *MockNetworkManager_CheckpointDestroy_Call { - return &MockNetworkManager_CheckpointDestroy_Call{Call: _e.mock.On("CheckpointDestroy", checkpoint)} -} - -func (_c *MockNetworkManager_CheckpointDestroy_Call) Run(run func(checkpoint gonetworkmanager.Checkpoint)) *MockNetworkManager_CheckpointDestroy_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(gonetworkmanager.Checkpoint)) - }) - return _c -} - -func (_c *MockNetworkManager_CheckpointDestroy_Call) Return(_a0 error) *MockNetworkManager_CheckpointDestroy_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockNetworkManager_CheckpointDestroy_Call) RunAndReturn(run func(gonetworkmanager.Checkpoint) error) *MockNetworkManager_CheckpointDestroy_Call { - _c.Call.Return(run) - return _c -} - -// CheckpointRollback provides a mock function with given fields: checkpoint -func (_m *MockNetworkManager) CheckpointRollback(checkpoint gonetworkmanager.Checkpoint) (map[dbus.ObjectPath]gonetworkmanager.NmRollbackResult, error) { - ret := _m.Called(checkpoint) - - if len(ret) == 0 { - panic("no return value specified for CheckpointRollback") - } - - var r0 map[dbus.ObjectPath]gonetworkmanager.NmRollbackResult - var r1 error - if rf, ok := ret.Get(0).(func(gonetworkmanager.Checkpoint) (map[dbus.ObjectPath]gonetworkmanager.NmRollbackResult, error)); ok { - return rf(checkpoint) - } - if rf, ok := ret.Get(0).(func(gonetworkmanager.Checkpoint) map[dbus.ObjectPath]gonetworkmanager.NmRollbackResult); ok { - r0 = rf(checkpoint) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(map[dbus.ObjectPath]gonetworkmanager.NmRollbackResult) - } - } - - if rf, ok := ret.Get(1).(func(gonetworkmanager.Checkpoint) error); ok { - r1 = rf(checkpoint) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_CheckpointRollback_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CheckpointRollback' -type MockNetworkManager_CheckpointRollback_Call struct { - *mock.Call -} - -// CheckpointRollback is a helper method to define mock.On call -// - checkpoint gonetworkmanager.Checkpoint -func (_e *MockNetworkManager_Expecter) CheckpointRollback(checkpoint interface{}) *MockNetworkManager_CheckpointRollback_Call { - return &MockNetworkManager_CheckpointRollback_Call{Call: _e.mock.On("CheckpointRollback", checkpoint)} -} - -func (_c *MockNetworkManager_CheckpointRollback_Call) Run(run func(checkpoint gonetworkmanager.Checkpoint)) *MockNetworkManager_CheckpointRollback_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(gonetworkmanager.Checkpoint)) - }) - return _c -} - -func (_c *MockNetworkManager_CheckpointRollback_Call) Return(result map[dbus.ObjectPath]gonetworkmanager.NmRollbackResult, err error) *MockNetworkManager_CheckpointRollback_Call { - _c.Call.Return(result, err) - return _c -} - -func (_c *MockNetworkManager_CheckpointRollback_Call) RunAndReturn(run func(gonetworkmanager.Checkpoint) (map[dbus.ObjectPath]gonetworkmanager.NmRollbackResult, error)) *MockNetworkManager_CheckpointRollback_Call { - _c.Call.Return(run) - return _c -} - -// DeactivateConnection provides a mock function with given fields: connection -func (_m *MockNetworkManager) DeactivateConnection(connection gonetworkmanager.ActiveConnection) error { - ret := _m.Called(connection) - - if len(ret) == 0 { - panic("no return value specified for DeactivateConnection") - } - - var r0 error - if rf, ok := ret.Get(0).(func(gonetworkmanager.ActiveConnection) error); ok { - r0 = rf(connection) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockNetworkManager_DeactivateConnection_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DeactivateConnection' -type MockNetworkManager_DeactivateConnection_Call struct { - *mock.Call -} - -// DeactivateConnection is a helper method to define mock.On call -// - connection gonetworkmanager.ActiveConnection -func (_e *MockNetworkManager_Expecter) DeactivateConnection(connection interface{}) *MockNetworkManager_DeactivateConnection_Call { - return &MockNetworkManager_DeactivateConnection_Call{Call: _e.mock.On("DeactivateConnection", connection)} -} - -func (_c *MockNetworkManager_DeactivateConnection_Call) Run(run func(connection gonetworkmanager.ActiveConnection)) *MockNetworkManager_DeactivateConnection_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(gonetworkmanager.ActiveConnection)) - }) - return _c -} - -func (_c *MockNetworkManager_DeactivateConnection_Call) Return(_a0 error) *MockNetworkManager_DeactivateConnection_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockNetworkManager_DeactivateConnection_Call) RunAndReturn(run func(gonetworkmanager.ActiveConnection) error) *MockNetworkManager_DeactivateConnection_Call { - _c.Call.Return(run) - return _c -} - -// Enable provides a mock function with given fields: enableNDisable -func (_m *MockNetworkManager) Enable(enableNDisable bool) error { - ret := _m.Called(enableNDisable) - - if len(ret) == 0 { - panic("no return value specified for Enable") - } - - var r0 error - if rf, ok := ret.Get(0).(func(bool) error); ok { - r0 = rf(enableNDisable) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockNetworkManager_Enable_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Enable' -type MockNetworkManager_Enable_Call struct { - *mock.Call -} - -// Enable is a helper method to define mock.On call -// - enableNDisable bool -func (_e *MockNetworkManager_Expecter) Enable(enableNDisable interface{}) *MockNetworkManager_Enable_Call { - return &MockNetworkManager_Enable_Call{Call: _e.mock.On("Enable", enableNDisable)} -} - -func (_c *MockNetworkManager_Enable_Call) Run(run func(enableNDisable bool)) *MockNetworkManager_Enable_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(bool)) - }) - return _c -} - -func (_c *MockNetworkManager_Enable_Call) Return(_a0 error) *MockNetworkManager_Enable_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockNetworkManager_Enable_Call) RunAndReturn(run func(bool) error) *MockNetworkManager_Enable_Call { - _c.Call.Return(run) - return _c -} - -// GetAllDevices provides a mock function with no fields -func (_m *MockNetworkManager) GetAllDevices() ([]gonetworkmanager.Device, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetAllDevices") - } - - var r0 []gonetworkmanager.Device - var r1 error - if rf, ok := ret.Get(0).(func() ([]gonetworkmanager.Device, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []gonetworkmanager.Device); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]gonetworkmanager.Device) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_GetAllDevices_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetAllDevices' -type MockNetworkManager_GetAllDevices_Call struct { - *mock.Call -} - -// GetAllDevices is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) GetAllDevices() *MockNetworkManager_GetAllDevices_Call { - return &MockNetworkManager_GetAllDevices_Call{Call: _e.mock.On("GetAllDevices")} -} - -func (_c *MockNetworkManager_GetAllDevices_Call) Run(run func()) *MockNetworkManager_GetAllDevices_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_GetAllDevices_Call) Return(_a0 []gonetworkmanager.Device, _a1 error) *MockNetworkManager_GetAllDevices_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_GetAllDevices_Call) RunAndReturn(run func() ([]gonetworkmanager.Device, error)) *MockNetworkManager_GetAllDevices_Call { - _c.Call.Return(run) - return _c -} - -// GetDeviceByIpIface provides a mock function with given fields: interfaceId -func (_m *MockNetworkManager) GetDeviceByIpIface(interfaceId string) (gonetworkmanager.Device, error) { - ret := _m.Called(interfaceId) - - if len(ret) == 0 { - panic("no return value specified for GetDeviceByIpIface") - } - - var r0 gonetworkmanager.Device - var r1 error - if rf, ok := ret.Get(0).(func(string) (gonetworkmanager.Device, error)); ok { - return rf(interfaceId) - } - if rf, ok := ret.Get(0).(func(string) gonetworkmanager.Device); ok { - r0 = rf(interfaceId) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.Device) - } - } - - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(interfaceId) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_GetDeviceByIpIface_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetDeviceByIpIface' -type MockNetworkManager_GetDeviceByIpIface_Call struct { - *mock.Call -} - -// GetDeviceByIpIface is a helper method to define mock.On call -// - interfaceId string -func (_e *MockNetworkManager_Expecter) GetDeviceByIpIface(interfaceId interface{}) *MockNetworkManager_GetDeviceByIpIface_Call { - return &MockNetworkManager_GetDeviceByIpIface_Call{Call: _e.mock.On("GetDeviceByIpIface", interfaceId)} -} - -func (_c *MockNetworkManager_GetDeviceByIpIface_Call) Run(run func(interfaceId string)) *MockNetworkManager_GetDeviceByIpIface_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *MockNetworkManager_GetDeviceByIpIface_Call) Return(_a0 gonetworkmanager.Device, _a1 error) *MockNetworkManager_GetDeviceByIpIface_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_GetDeviceByIpIface_Call) RunAndReturn(run func(string) (gonetworkmanager.Device, error)) *MockNetworkManager_GetDeviceByIpIface_Call { - _c.Call.Return(run) - return _c -} - -// GetDevices provides a mock function with no fields -func (_m *MockNetworkManager) GetDevices() ([]gonetworkmanager.Device, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetDevices") - } - - var r0 []gonetworkmanager.Device - var r1 error - if rf, ok := ret.Get(0).(func() ([]gonetworkmanager.Device, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []gonetworkmanager.Device); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]gonetworkmanager.Device) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_GetDevices_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetDevices' -type MockNetworkManager_GetDevices_Call struct { - *mock.Call -} - -// GetDevices is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) GetDevices() *MockNetworkManager_GetDevices_Call { - return &MockNetworkManager_GetDevices_Call{Call: _e.mock.On("GetDevices")} -} - -func (_c *MockNetworkManager_GetDevices_Call) Run(run func()) *MockNetworkManager_GetDevices_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_GetDevices_Call) Return(_a0 []gonetworkmanager.Device, _a1 error) *MockNetworkManager_GetDevices_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_GetDevices_Call) RunAndReturn(run func() ([]gonetworkmanager.Device, error)) *MockNetworkManager_GetDevices_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyActivatingConnection provides a mock function with no fields -func (_m *MockNetworkManager) GetPropertyActivatingConnection() (gonetworkmanager.ActiveConnection, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyActivatingConnection") - } - - var r0 gonetworkmanager.ActiveConnection - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.ActiveConnection, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.ActiveConnection); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.ActiveConnection) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_GetPropertyActivatingConnection_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyActivatingConnection' -type MockNetworkManager_GetPropertyActivatingConnection_Call struct { - *mock.Call -} - -// GetPropertyActivatingConnection is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) GetPropertyActivatingConnection() *MockNetworkManager_GetPropertyActivatingConnection_Call { - return &MockNetworkManager_GetPropertyActivatingConnection_Call{Call: _e.mock.On("GetPropertyActivatingConnection")} -} - -func (_c *MockNetworkManager_GetPropertyActivatingConnection_Call) Run(run func()) *MockNetworkManager_GetPropertyActivatingConnection_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_GetPropertyActivatingConnection_Call) Return(_a0 gonetworkmanager.ActiveConnection, _a1 error) *MockNetworkManager_GetPropertyActivatingConnection_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_GetPropertyActivatingConnection_Call) RunAndReturn(run func() (gonetworkmanager.ActiveConnection, error)) *MockNetworkManager_GetPropertyActivatingConnection_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyActiveConnections provides a mock function with no fields -func (_m *MockNetworkManager) GetPropertyActiveConnections() ([]gonetworkmanager.ActiveConnection, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyActiveConnections") - } - - var r0 []gonetworkmanager.ActiveConnection - var r1 error - if rf, ok := ret.Get(0).(func() ([]gonetworkmanager.ActiveConnection, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []gonetworkmanager.ActiveConnection); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]gonetworkmanager.ActiveConnection) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_GetPropertyActiveConnections_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyActiveConnections' -type MockNetworkManager_GetPropertyActiveConnections_Call struct { - *mock.Call -} - -// GetPropertyActiveConnections is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) GetPropertyActiveConnections() *MockNetworkManager_GetPropertyActiveConnections_Call { - return &MockNetworkManager_GetPropertyActiveConnections_Call{Call: _e.mock.On("GetPropertyActiveConnections")} -} - -func (_c *MockNetworkManager_GetPropertyActiveConnections_Call) Run(run func()) *MockNetworkManager_GetPropertyActiveConnections_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_GetPropertyActiveConnections_Call) Return(_a0 []gonetworkmanager.ActiveConnection, _a1 error) *MockNetworkManager_GetPropertyActiveConnections_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_GetPropertyActiveConnections_Call) RunAndReturn(run func() ([]gonetworkmanager.ActiveConnection, error)) *MockNetworkManager_GetPropertyActiveConnections_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyAllDevices provides a mock function with no fields -func (_m *MockNetworkManager) GetPropertyAllDevices() ([]gonetworkmanager.Device, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyAllDevices") - } - - var r0 []gonetworkmanager.Device - var r1 error - if rf, ok := ret.Get(0).(func() ([]gonetworkmanager.Device, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []gonetworkmanager.Device); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]gonetworkmanager.Device) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_GetPropertyAllDevices_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyAllDevices' -type MockNetworkManager_GetPropertyAllDevices_Call struct { - *mock.Call -} - -// GetPropertyAllDevices is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) GetPropertyAllDevices() *MockNetworkManager_GetPropertyAllDevices_Call { - return &MockNetworkManager_GetPropertyAllDevices_Call{Call: _e.mock.On("GetPropertyAllDevices")} -} - -func (_c *MockNetworkManager_GetPropertyAllDevices_Call) Run(run func()) *MockNetworkManager_GetPropertyAllDevices_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_GetPropertyAllDevices_Call) Return(_a0 []gonetworkmanager.Device, _a1 error) *MockNetworkManager_GetPropertyAllDevices_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_GetPropertyAllDevices_Call) RunAndReturn(run func() ([]gonetworkmanager.Device, error)) *MockNetworkManager_GetPropertyAllDevices_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyCapabilities provides a mock function with no fields -func (_m *MockNetworkManager) GetPropertyCapabilities() ([]gonetworkmanager.NmCapability, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyCapabilities") - } - - var r0 []gonetworkmanager.NmCapability - var r1 error - if rf, ok := ret.Get(0).(func() ([]gonetworkmanager.NmCapability, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []gonetworkmanager.NmCapability); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]gonetworkmanager.NmCapability) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_GetPropertyCapabilities_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyCapabilities' -type MockNetworkManager_GetPropertyCapabilities_Call struct { - *mock.Call -} - -// GetPropertyCapabilities is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) GetPropertyCapabilities() *MockNetworkManager_GetPropertyCapabilities_Call { - return &MockNetworkManager_GetPropertyCapabilities_Call{Call: _e.mock.On("GetPropertyCapabilities")} -} - -func (_c *MockNetworkManager_GetPropertyCapabilities_Call) Run(run func()) *MockNetworkManager_GetPropertyCapabilities_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_GetPropertyCapabilities_Call) Return(_a0 []gonetworkmanager.NmCapability, _a1 error) *MockNetworkManager_GetPropertyCapabilities_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_GetPropertyCapabilities_Call) RunAndReturn(run func() ([]gonetworkmanager.NmCapability, error)) *MockNetworkManager_GetPropertyCapabilities_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyCheckpoints provides a mock function with no fields -func (_m *MockNetworkManager) GetPropertyCheckpoints() ([]gonetworkmanager.Checkpoint, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyCheckpoints") - } - - var r0 []gonetworkmanager.Checkpoint - var r1 error - if rf, ok := ret.Get(0).(func() ([]gonetworkmanager.Checkpoint, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []gonetworkmanager.Checkpoint); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]gonetworkmanager.Checkpoint) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_GetPropertyCheckpoints_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyCheckpoints' -type MockNetworkManager_GetPropertyCheckpoints_Call struct { - *mock.Call -} - -// GetPropertyCheckpoints is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) GetPropertyCheckpoints() *MockNetworkManager_GetPropertyCheckpoints_Call { - return &MockNetworkManager_GetPropertyCheckpoints_Call{Call: _e.mock.On("GetPropertyCheckpoints")} -} - -func (_c *MockNetworkManager_GetPropertyCheckpoints_Call) Run(run func()) *MockNetworkManager_GetPropertyCheckpoints_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_GetPropertyCheckpoints_Call) Return(_a0 []gonetworkmanager.Checkpoint, _a1 error) *MockNetworkManager_GetPropertyCheckpoints_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_GetPropertyCheckpoints_Call) RunAndReturn(run func() ([]gonetworkmanager.Checkpoint, error)) *MockNetworkManager_GetPropertyCheckpoints_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyConnectivity provides a mock function with no fields -func (_m *MockNetworkManager) GetPropertyConnectivity() (gonetworkmanager.NmConnectivity, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyConnectivity") - } - - var r0 gonetworkmanager.NmConnectivity - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.NmConnectivity, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.NmConnectivity); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(gonetworkmanager.NmConnectivity) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_GetPropertyConnectivity_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyConnectivity' -type MockNetworkManager_GetPropertyConnectivity_Call struct { - *mock.Call -} - -// GetPropertyConnectivity is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) GetPropertyConnectivity() *MockNetworkManager_GetPropertyConnectivity_Call { - return &MockNetworkManager_GetPropertyConnectivity_Call{Call: _e.mock.On("GetPropertyConnectivity")} -} - -func (_c *MockNetworkManager_GetPropertyConnectivity_Call) Run(run func()) *MockNetworkManager_GetPropertyConnectivity_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_GetPropertyConnectivity_Call) Return(_a0 gonetworkmanager.NmConnectivity, _a1 error) *MockNetworkManager_GetPropertyConnectivity_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_GetPropertyConnectivity_Call) RunAndReturn(run func() (gonetworkmanager.NmConnectivity, error)) *MockNetworkManager_GetPropertyConnectivity_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyConnectivityCheckAvailable provides a mock function with no fields -func (_m *MockNetworkManager) GetPropertyConnectivityCheckAvailable() (bool, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyConnectivityCheckAvailable") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func() (bool, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_GetPropertyConnectivityCheckAvailable_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyConnectivityCheckAvailable' -type MockNetworkManager_GetPropertyConnectivityCheckAvailable_Call struct { - *mock.Call -} - -// GetPropertyConnectivityCheckAvailable is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) GetPropertyConnectivityCheckAvailable() *MockNetworkManager_GetPropertyConnectivityCheckAvailable_Call { - return &MockNetworkManager_GetPropertyConnectivityCheckAvailable_Call{Call: _e.mock.On("GetPropertyConnectivityCheckAvailable")} -} - -func (_c *MockNetworkManager_GetPropertyConnectivityCheckAvailable_Call) Run(run func()) *MockNetworkManager_GetPropertyConnectivityCheckAvailable_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_GetPropertyConnectivityCheckAvailable_Call) Return(_a0 bool, _a1 error) *MockNetworkManager_GetPropertyConnectivityCheckAvailable_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_GetPropertyConnectivityCheckAvailable_Call) RunAndReturn(run func() (bool, error)) *MockNetworkManager_GetPropertyConnectivityCheckAvailable_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyConnectivityCheckEnabled provides a mock function with no fields -func (_m *MockNetworkManager) GetPropertyConnectivityCheckEnabled() (bool, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyConnectivityCheckEnabled") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func() (bool, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_GetPropertyConnectivityCheckEnabled_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyConnectivityCheckEnabled' -type MockNetworkManager_GetPropertyConnectivityCheckEnabled_Call struct { - *mock.Call -} - -// GetPropertyConnectivityCheckEnabled is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) GetPropertyConnectivityCheckEnabled() *MockNetworkManager_GetPropertyConnectivityCheckEnabled_Call { - return &MockNetworkManager_GetPropertyConnectivityCheckEnabled_Call{Call: _e.mock.On("GetPropertyConnectivityCheckEnabled")} -} - -func (_c *MockNetworkManager_GetPropertyConnectivityCheckEnabled_Call) Run(run func()) *MockNetworkManager_GetPropertyConnectivityCheckEnabled_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_GetPropertyConnectivityCheckEnabled_Call) Return(_a0 bool, _a1 error) *MockNetworkManager_GetPropertyConnectivityCheckEnabled_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_GetPropertyConnectivityCheckEnabled_Call) RunAndReturn(run func() (bool, error)) *MockNetworkManager_GetPropertyConnectivityCheckEnabled_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyDevices provides a mock function with no fields -func (_m *MockNetworkManager) GetPropertyDevices() ([]gonetworkmanager.Device, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyDevices") - } - - var r0 []gonetworkmanager.Device - var r1 error - if rf, ok := ret.Get(0).(func() ([]gonetworkmanager.Device, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []gonetworkmanager.Device); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]gonetworkmanager.Device) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_GetPropertyDevices_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyDevices' -type MockNetworkManager_GetPropertyDevices_Call struct { - *mock.Call -} - -// GetPropertyDevices is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) GetPropertyDevices() *MockNetworkManager_GetPropertyDevices_Call { - return &MockNetworkManager_GetPropertyDevices_Call{Call: _e.mock.On("GetPropertyDevices")} -} - -func (_c *MockNetworkManager_GetPropertyDevices_Call) Run(run func()) *MockNetworkManager_GetPropertyDevices_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_GetPropertyDevices_Call) Return(_a0 []gonetworkmanager.Device, _a1 error) *MockNetworkManager_GetPropertyDevices_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_GetPropertyDevices_Call) RunAndReturn(run func() ([]gonetworkmanager.Device, error)) *MockNetworkManager_GetPropertyDevices_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyMetered provides a mock function with no fields -func (_m *MockNetworkManager) GetPropertyMetered() (gonetworkmanager.NmMetered, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyMetered") - } - - var r0 gonetworkmanager.NmMetered - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.NmMetered, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.NmMetered); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(gonetworkmanager.NmMetered) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_GetPropertyMetered_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyMetered' -type MockNetworkManager_GetPropertyMetered_Call struct { - *mock.Call -} - -// GetPropertyMetered is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) GetPropertyMetered() *MockNetworkManager_GetPropertyMetered_Call { - return &MockNetworkManager_GetPropertyMetered_Call{Call: _e.mock.On("GetPropertyMetered")} -} - -func (_c *MockNetworkManager_GetPropertyMetered_Call) Run(run func()) *MockNetworkManager_GetPropertyMetered_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_GetPropertyMetered_Call) Return(_a0 gonetworkmanager.NmMetered, _a1 error) *MockNetworkManager_GetPropertyMetered_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_GetPropertyMetered_Call) RunAndReturn(run func() (gonetworkmanager.NmMetered, error)) *MockNetworkManager_GetPropertyMetered_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyNetworkingEnabled provides a mock function with no fields -func (_m *MockNetworkManager) GetPropertyNetworkingEnabled() (bool, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyNetworkingEnabled") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func() (bool, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_GetPropertyNetworkingEnabled_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyNetworkingEnabled' -type MockNetworkManager_GetPropertyNetworkingEnabled_Call struct { - *mock.Call -} - -// GetPropertyNetworkingEnabled is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) GetPropertyNetworkingEnabled() *MockNetworkManager_GetPropertyNetworkingEnabled_Call { - return &MockNetworkManager_GetPropertyNetworkingEnabled_Call{Call: _e.mock.On("GetPropertyNetworkingEnabled")} -} - -func (_c *MockNetworkManager_GetPropertyNetworkingEnabled_Call) Run(run func()) *MockNetworkManager_GetPropertyNetworkingEnabled_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_GetPropertyNetworkingEnabled_Call) Return(_a0 bool, _a1 error) *MockNetworkManager_GetPropertyNetworkingEnabled_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_GetPropertyNetworkingEnabled_Call) RunAndReturn(run func() (bool, error)) *MockNetworkManager_GetPropertyNetworkingEnabled_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyPrimaryConnection provides a mock function with no fields -func (_m *MockNetworkManager) GetPropertyPrimaryConnection() (gonetworkmanager.ActiveConnection, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyPrimaryConnection") - } - - var r0 gonetworkmanager.ActiveConnection - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.ActiveConnection, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.ActiveConnection); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.ActiveConnection) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_GetPropertyPrimaryConnection_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyPrimaryConnection' -type MockNetworkManager_GetPropertyPrimaryConnection_Call struct { - *mock.Call -} - -// GetPropertyPrimaryConnection is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) GetPropertyPrimaryConnection() *MockNetworkManager_GetPropertyPrimaryConnection_Call { - return &MockNetworkManager_GetPropertyPrimaryConnection_Call{Call: _e.mock.On("GetPropertyPrimaryConnection")} -} - -func (_c *MockNetworkManager_GetPropertyPrimaryConnection_Call) Run(run func()) *MockNetworkManager_GetPropertyPrimaryConnection_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_GetPropertyPrimaryConnection_Call) Return(_a0 gonetworkmanager.ActiveConnection, _a1 error) *MockNetworkManager_GetPropertyPrimaryConnection_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_GetPropertyPrimaryConnection_Call) RunAndReturn(run func() (gonetworkmanager.ActiveConnection, error)) *MockNetworkManager_GetPropertyPrimaryConnection_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyPrimaryConnectionType provides a mock function with no fields -func (_m *MockNetworkManager) GetPropertyPrimaryConnectionType() (string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyPrimaryConnectionType") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func() (string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_GetPropertyPrimaryConnectionType_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyPrimaryConnectionType' -type MockNetworkManager_GetPropertyPrimaryConnectionType_Call struct { - *mock.Call -} - -// GetPropertyPrimaryConnectionType is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) GetPropertyPrimaryConnectionType() *MockNetworkManager_GetPropertyPrimaryConnectionType_Call { - return &MockNetworkManager_GetPropertyPrimaryConnectionType_Call{Call: _e.mock.On("GetPropertyPrimaryConnectionType")} -} - -func (_c *MockNetworkManager_GetPropertyPrimaryConnectionType_Call) Run(run func()) *MockNetworkManager_GetPropertyPrimaryConnectionType_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_GetPropertyPrimaryConnectionType_Call) Return(_a0 string, _a1 error) *MockNetworkManager_GetPropertyPrimaryConnectionType_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_GetPropertyPrimaryConnectionType_Call) RunAndReturn(run func() (string, error)) *MockNetworkManager_GetPropertyPrimaryConnectionType_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyStartup provides a mock function with no fields -func (_m *MockNetworkManager) GetPropertyStartup() (bool, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyStartup") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func() (bool, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_GetPropertyStartup_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyStartup' -type MockNetworkManager_GetPropertyStartup_Call struct { - *mock.Call -} - -// GetPropertyStartup is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) GetPropertyStartup() *MockNetworkManager_GetPropertyStartup_Call { - return &MockNetworkManager_GetPropertyStartup_Call{Call: _e.mock.On("GetPropertyStartup")} -} - -func (_c *MockNetworkManager_GetPropertyStartup_Call) Run(run func()) *MockNetworkManager_GetPropertyStartup_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_GetPropertyStartup_Call) Return(_a0 bool, _a1 error) *MockNetworkManager_GetPropertyStartup_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_GetPropertyStartup_Call) RunAndReturn(run func() (bool, error)) *MockNetworkManager_GetPropertyStartup_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyState provides a mock function with no fields -func (_m *MockNetworkManager) GetPropertyState() (gonetworkmanager.NmState, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyState") - } - - var r0 gonetworkmanager.NmState - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.NmState, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.NmState); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(gonetworkmanager.NmState) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_GetPropertyState_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyState' -type MockNetworkManager_GetPropertyState_Call struct { - *mock.Call -} - -// GetPropertyState is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) GetPropertyState() *MockNetworkManager_GetPropertyState_Call { - return &MockNetworkManager_GetPropertyState_Call{Call: _e.mock.On("GetPropertyState")} -} - -func (_c *MockNetworkManager_GetPropertyState_Call) Run(run func()) *MockNetworkManager_GetPropertyState_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_GetPropertyState_Call) Return(_a0 gonetworkmanager.NmState, _a1 error) *MockNetworkManager_GetPropertyState_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_GetPropertyState_Call) RunAndReturn(run func() (gonetworkmanager.NmState, error)) *MockNetworkManager_GetPropertyState_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyVersion provides a mock function with no fields -func (_m *MockNetworkManager) GetPropertyVersion() (string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyVersion") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func() (string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_GetPropertyVersion_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyVersion' -type MockNetworkManager_GetPropertyVersion_Call struct { - *mock.Call -} - -// GetPropertyVersion is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) GetPropertyVersion() *MockNetworkManager_GetPropertyVersion_Call { - return &MockNetworkManager_GetPropertyVersion_Call{Call: _e.mock.On("GetPropertyVersion")} -} - -func (_c *MockNetworkManager_GetPropertyVersion_Call) Run(run func()) *MockNetworkManager_GetPropertyVersion_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_GetPropertyVersion_Call) Return(_a0 string, _a1 error) *MockNetworkManager_GetPropertyVersion_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_GetPropertyVersion_Call) RunAndReturn(run func() (string, error)) *MockNetworkManager_GetPropertyVersion_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyWimaxEnabled provides a mock function with no fields -func (_m *MockNetworkManager) GetPropertyWimaxEnabled() (bool, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyWimaxEnabled") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func() (bool, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_GetPropertyWimaxEnabled_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyWimaxEnabled' -type MockNetworkManager_GetPropertyWimaxEnabled_Call struct { - *mock.Call -} - -// GetPropertyWimaxEnabled is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) GetPropertyWimaxEnabled() *MockNetworkManager_GetPropertyWimaxEnabled_Call { - return &MockNetworkManager_GetPropertyWimaxEnabled_Call{Call: _e.mock.On("GetPropertyWimaxEnabled")} -} - -func (_c *MockNetworkManager_GetPropertyWimaxEnabled_Call) Run(run func()) *MockNetworkManager_GetPropertyWimaxEnabled_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_GetPropertyWimaxEnabled_Call) Return(_a0 bool, _a1 error) *MockNetworkManager_GetPropertyWimaxEnabled_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_GetPropertyWimaxEnabled_Call) RunAndReturn(run func() (bool, error)) *MockNetworkManager_GetPropertyWimaxEnabled_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyWimaxHardwareEnabled provides a mock function with no fields -func (_m *MockNetworkManager) GetPropertyWimaxHardwareEnabled() (bool, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyWimaxHardwareEnabled") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func() (bool, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_GetPropertyWimaxHardwareEnabled_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyWimaxHardwareEnabled' -type MockNetworkManager_GetPropertyWimaxHardwareEnabled_Call struct { - *mock.Call -} - -// GetPropertyWimaxHardwareEnabled is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) GetPropertyWimaxHardwareEnabled() *MockNetworkManager_GetPropertyWimaxHardwareEnabled_Call { - return &MockNetworkManager_GetPropertyWimaxHardwareEnabled_Call{Call: _e.mock.On("GetPropertyWimaxHardwareEnabled")} -} - -func (_c *MockNetworkManager_GetPropertyWimaxHardwareEnabled_Call) Run(run func()) *MockNetworkManager_GetPropertyWimaxHardwareEnabled_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_GetPropertyWimaxHardwareEnabled_Call) Return(_a0 bool, _a1 error) *MockNetworkManager_GetPropertyWimaxHardwareEnabled_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_GetPropertyWimaxHardwareEnabled_Call) RunAndReturn(run func() (bool, error)) *MockNetworkManager_GetPropertyWimaxHardwareEnabled_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyWirelessEnabled provides a mock function with no fields -func (_m *MockNetworkManager) GetPropertyWirelessEnabled() (bool, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyWirelessEnabled") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func() (bool, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_GetPropertyWirelessEnabled_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyWirelessEnabled' -type MockNetworkManager_GetPropertyWirelessEnabled_Call struct { - *mock.Call -} - -// GetPropertyWirelessEnabled is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) GetPropertyWirelessEnabled() *MockNetworkManager_GetPropertyWirelessEnabled_Call { - return &MockNetworkManager_GetPropertyWirelessEnabled_Call{Call: _e.mock.On("GetPropertyWirelessEnabled")} -} - -func (_c *MockNetworkManager_GetPropertyWirelessEnabled_Call) Run(run func()) *MockNetworkManager_GetPropertyWirelessEnabled_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_GetPropertyWirelessEnabled_Call) Return(_a0 bool, _a1 error) *MockNetworkManager_GetPropertyWirelessEnabled_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_GetPropertyWirelessEnabled_Call) RunAndReturn(run func() (bool, error)) *MockNetworkManager_GetPropertyWirelessEnabled_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyWirelessHardwareEnabled provides a mock function with no fields -func (_m *MockNetworkManager) GetPropertyWirelessHardwareEnabled() (bool, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyWirelessHardwareEnabled") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func() (bool, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_GetPropertyWirelessHardwareEnabled_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyWirelessHardwareEnabled' -type MockNetworkManager_GetPropertyWirelessHardwareEnabled_Call struct { - *mock.Call -} - -// GetPropertyWirelessHardwareEnabled is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) GetPropertyWirelessHardwareEnabled() *MockNetworkManager_GetPropertyWirelessHardwareEnabled_Call { - return &MockNetworkManager_GetPropertyWirelessHardwareEnabled_Call{Call: _e.mock.On("GetPropertyWirelessHardwareEnabled")} -} - -func (_c *MockNetworkManager_GetPropertyWirelessHardwareEnabled_Call) Run(run func()) *MockNetworkManager_GetPropertyWirelessHardwareEnabled_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_GetPropertyWirelessHardwareEnabled_Call) Return(_a0 bool, _a1 error) *MockNetworkManager_GetPropertyWirelessHardwareEnabled_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_GetPropertyWirelessHardwareEnabled_Call) RunAndReturn(run func() (bool, error)) *MockNetworkManager_GetPropertyWirelessHardwareEnabled_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyWwanEnabled provides a mock function with no fields -func (_m *MockNetworkManager) GetPropertyWwanEnabled() (bool, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyWwanEnabled") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func() (bool, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_GetPropertyWwanEnabled_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyWwanEnabled' -type MockNetworkManager_GetPropertyWwanEnabled_Call struct { - *mock.Call -} - -// GetPropertyWwanEnabled is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) GetPropertyWwanEnabled() *MockNetworkManager_GetPropertyWwanEnabled_Call { - return &MockNetworkManager_GetPropertyWwanEnabled_Call{Call: _e.mock.On("GetPropertyWwanEnabled")} -} - -func (_c *MockNetworkManager_GetPropertyWwanEnabled_Call) Run(run func()) *MockNetworkManager_GetPropertyWwanEnabled_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_GetPropertyWwanEnabled_Call) Return(_a0 bool, _a1 error) *MockNetworkManager_GetPropertyWwanEnabled_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_GetPropertyWwanEnabled_Call) RunAndReturn(run func() (bool, error)) *MockNetworkManager_GetPropertyWwanEnabled_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyWwanHardwareEnabled provides a mock function with no fields -func (_m *MockNetworkManager) GetPropertyWwanHardwareEnabled() (bool, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyWwanHardwareEnabled") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func() (bool, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_GetPropertyWwanHardwareEnabled_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyWwanHardwareEnabled' -type MockNetworkManager_GetPropertyWwanHardwareEnabled_Call struct { - *mock.Call -} - -// GetPropertyWwanHardwareEnabled is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) GetPropertyWwanHardwareEnabled() *MockNetworkManager_GetPropertyWwanHardwareEnabled_Call { - return &MockNetworkManager_GetPropertyWwanHardwareEnabled_Call{Call: _e.mock.On("GetPropertyWwanHardwareEnabled")} -} - -func (_c *MockNetworkManager_GetPropertyWwanHardwareEnabled_Call) Run(run func()) *MockNetworkManager_GetPropertyWwanHardwareEnabled_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_GetPropertyWwanHardwareEnabled_Call) Return(_a0 bool, _a1 error) *MockNetworkManager_GetPropertyWwanHardwareEnabled_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_GetPropertyWwanHardwareEnabled_Call) RunAndReturn(run func() (bool, error)) *MockNetworkManager_GetPropertyWwanHardwareEnabled_Call { - _c.Call.Return(run) - return _c -} - -// MarshalJSON provides a mock function with no fields -func (_m *MockNetworkManager) MarshalJSON() ([]byte, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for MarshalJSON") - } - - var r0 []byte - var r1 error - if rf, ok := ret.Get(0).(func() ([]byte, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []byte); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]byte) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_MarshalJSON_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'MarshalJSON' -type MockNetworkManager_MarshalJSON_Call struct { - *mock.Call -} - -// MarshalJSON is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) MarshalJSON() *MockNetworkManager_MarshalJSON_Call { - return &MockNetworkManager_MarshalJSON_Call{Call: _e.mock.On("MarshalJSON")} -} - -func (_c *MockNetworkManager_MarshalJSON_Call) Run(run func()) *MockNetworkManager_MarshalJSON_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_MarshalJSON_Call) Return(_a0 []byte, _a1 error) *MockNetworkManager_MarshalJSON_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_MarshalJSON_Call) RunAndReturn(run func() ([]byte, error)) *MockNetworkManager_MarshalJSON_Call { - _c.Call.Return(run) - return _c -} - -// Reload provides a mock function with given fields: flags -func (_m *MockNetworkManager) Reload(flags uint32) error { - ret := _m.Called(flags) - - if len(ret) == 0 { - panic("no return value specified for Reload") - } - - var r0 error - if rf, ok := ret.Get(0).(func(uint32) error); ok { - r0 = rf(flags) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockNetworkManager_Reload_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Reload' -type MockNetworkManager_Reload_Call struct { - *mock.Call -} - -// Reload is a helper method to define mock.On call -// - flags uint32 -func (_e *MockNetworkManager_Expecter) Reload(flags interface{}) *MockNetworkManager_Reload_Call { - return &MockNetworkManager_Reload_Call{Call: _e.mock.On("Reload", flags)} -} - -func (_c *MockNetworkManager_Reload_Call) Run(run func(flags uint32)) *MockNetworkManager_Reload_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(uint32)) - }) - return _c -} - -func (_c *MockNetworkManager_Reload_Call) Return(_a0 error) *MockNetworkManager_Reload_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockNetworkManager_Reload_Call) RunAndReturn(run func(uint32) error) *MockNetworkManager_Reload_Call { - _c.Call.Return(run) - return _c -} - -// SetPropertyWirelessEnabled provides a mock function with given fields: _a0 -func (_m *MockNetworkManager) SetPropertyWirelessEnabled(_a0 bool) error { - ret := _m.Called(_a0) - - if len(ret) == 0 { - panic("no return value specified for SetPropertyWirelessEnabled") - } - - var r0 error - if rf, ok := ret.Get(0).(func(bool) error); ok { - r0 = rf(_a0) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockNetworkManager_SetPropertyWirelessEnabled_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetPropertyWirelessEnabled' -type MockNetworkManager_SetPropertyWirelessEnabled_Call struct { - *mock.Call -} - -// SetPropertyWirelessEnabled is a helper method to define mock.On call -// - _a0 bool -func (_e *MockNetworkManager_Expecter) SetPropertyWirelessEnabled(_a0 interface{}) *MockNetworkManager_SetPropertyWirelessEnabled_Call { - return &MockNetworkManager_SetPropertyWirelessEnabled_Call{Call: _e.mock.On("SetPropertyWirelessEnabled", _a0)} -} - -func (_c *MockNetworkManager_SetPropertyWirelessEnabled_Call) Run(run func(_a0 bool)) *MockNetworkManager_SetPropertyWirelessEnabled_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(bool)) - }) - return _c -} - -func (_c *MockNetworkManager_SetPropertyWirelessEnabled_Call) Return(_a0 error) *MockNetworkManager_SetPropertyWirelessEnabled_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockNetworkManager_SetPropertyWirelessEnabled_Call) RunAndReturn(run func(bool) error) *MockNetworkManager_SetPropertyWirelessEnabled_Call { - _c.Call.Return(run) - return _c -} - -// Sleep provides a mock function with given fields: sleepNWake -func (_m *MockNetworkManager) Sleep(sleepNWake bool) error { - ret := _m.Called(sleepNWake) - - if len(ret) == 0 { - panic("no return value specified for Sleep") - } - - var r0 error - if rf, ok := ret.Get(0).(func(bool) error); ok { - r0 = rf(sleepNWake) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockNetworkManager_Sleep_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Sleep' -type MockNetworkManager_Sleep_Call struct { - *mock.Call -} - -// Sleep is a helper method to define mock.On call -// - sleepNWake bool -func (_e *MockNetworkManager_Expecter) Sleep(sleepNWake interface{}) *MockNetworkManager_Sleep_Call { - return &MockNetworkManager_Sleep_Call{Call: _e.mock.On("Sleep", sleepNWake)} -} - -func (_c *MockNetworkManager_Sleep_Call) Run(run func(sleepNWake bool)) *MockNetworkManager_Sleep_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(bool)) - }) - return _c -} - -func (_c *MockNetworkManager_Sleep_Call) Return(_a0 error) *MockNetworkManager_Sleep_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockNetworkManager_Sleep_Call) RunAndReturn(run func(bool) error) *MockNetworkManager_Sleep_Call { - _c.Call.Return(run) - return _c -} - -// State provides a mock function with no fields -func (_m *MockNetworkManager) State() (gonetworkmanager.NmState, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for State") - } - - var r0 gonetworkmanager.NmState - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.NmState, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.NmState); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(gonetworkmanager.NmState) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_State_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'State' -type MockNetworkManager_State_Call struct { - *mock.Call -} - -// State is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) State() *MockNetworkManager_State_Call { - return &MockNetworkManager_State_Call{Call: _e.mock.On("State")} -} - -func (_c *MockNetworkManager_State_Call) Run(run func()) *MockNetworkManager_State_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_State_Call) Return(_a0 gonetworkmanager.NmState, _a1 error) *MockNetworkManager_State_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_State_Call) RunAndReturn(run func() (gonetworkmanager.NmState, error)) *MockNetworkManager_State_Call { - _c.Call.Return(run) - return _c -} - -// Subscribe provides a mock function with no fields -func (_m *MockNetworkManager) Subscribe() <-chan *dbus.Signal { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Subscribe") - } - - var r0 <-chan *dbus.Signal - if rf, ok := ret.Get(0).(func() <-chan *dbus.Signal); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(<-chan *dbus.Signal) - } - } - - return r0 -} - -// MockNetworkManager_Subscribe_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Subscribe' -type MockNetworkManager_Subscribe_Call struct { - *mock.Call -} - -// Subscribe is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) Subscribe() *MockNetworkManager_Subscribe_Call { - return &MockNetworkManager_Subscribe_Call{Call: _e.mock.On("Subscribe")} -} - -func (_c *MockNetworkManager_Subscribe_Call) Run(run func()) *MockNetworkManager_Subscribe_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_Subscribe_Call) Return(_a0 <-chan *dbus.Signal) *MockNetworkManager_Subscribe_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockNetworkManager_Subscribe_Call) RunAndReturn(run func() <-chan *dbus.Signal) *MockNetworkManager_Subscribe_Call { - _c.Call.Return(run) - return _c -} - -// Unsubscribe provides a mock function with no fields -func (_m *MockNetworkManager) Unsubscribe() { - _m.Called() -} - -// MockNetworkManager_Unsubscribe_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Unsubscribe' -type MockNetworkManager_Unsubscribe_Call struct { - *mock.Call -} - -// Unsubscribe is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) Unsubscribe() *MockNetworkManager_Unsubscribe_Call { - return &MockNetworkManager_Unsubscribe_Call{Call: _e.mock.On("Unsubscribe")} -} - -func (_c *MockNetworkManager_Unsubscribe_Call) Run(run func()) *MockNetworkManager_Unsubscribe_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_Unsubscribe_Call) Return() *MockNetworkManager_Unsubscribe_Call { - _c.Call.Return() - return _c -} - -func (_c *MockNetworkManager_Unsubscribe_Call) RunAndReturn(run func()) *MockNetworkManager_Unsubscribe_Call { - _c.Run(run) - return _c -} - -// NewMockNetworkManager creates a new instance of MockNetworkManager. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewMockNetworkManager(t interface { - mock.TestingT - Cleanup(func()) -}) *MockNetworkManager { - mock := &MockNetworkManager{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/nix/inputs/dms-cli/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_Settings.go b/nix/inputs/dms-cli/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_Settings.go deleted file mode 100644 index 20f488e..0000000 --- a/nix/inputs/dms-cli/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_Settings.go +++ /dev/null @@ -1,467 +0,0 @@ -// Code generated by mockery v2.53.5. DO NOT EDIT. - -package gonetworkmanager - -import ( - gonetworkmanager "github.com/Wifx/gonetworkmanager/v2" - mock "github.com/stretchr/testify/mock" -) - -// MockSettings is an autogenerated mock type for the Settings type -type MockSettings struct { - mock.Mock -} - -type MockSettings_Expecter struct { - mock *mock.Mock -} - -func (_m *MockSettings) EXPECT() *MockSettings_Expecter { - return &MockSettings_Expecter{mock: &_m.Mock} -} - -// AddConnection provides a mock function with given fields: settings -func (_m *MockSettings) AddConnection(settings gonetworkmanager.ConnectionSettings) (gonetworkmanager.Connection, error) { - ret := _m.Called(settings) - - if len(ret) == 0 { - panic("no return value specified for AddConnection") - } - - var r0 gonetworkmanager.Connection - var r1 error - if rf, ok := ret.Get(0).(func(gonetworkmanager.ConnectionSettings) (gonetworkmanager.Connection, error)); ok { - return rf(settings) - } - if rf, ok := ret.Get(0).(func(gonetworkmanager.ConnectionSettings) gonetworkmanager.Connection); ok { - r0 = rf(settings) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.Connection) - } - } - - if rf, ok := ret.Get(1).(func(gonetworkmanager.ConnectionSettings) error); ok { - r1 = rf(settings) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockSettings_AddConnection_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddConnection' -type MockSettings_AddConnection_Call struct { - *mock.Call -} - -// AddConnection is a helper method to define mock.On call -// - settings gonetworkmanager.ConnectionSettings -func (_e *MockSettings_Expecter) AddConnection(settings interface{}) *MockSettings_AddConnection_Call { - return &MockSettings_AddConnection_Call{Call: _e.mock.On("AddConnection", settings)} -} - -func (_c *MockSettings_AddConnection_Call) Run(run func(settings gonetworkmanager.ConnectionSettings)) *MockSettings_AddConnection_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(gonetworkmanager.ConnectionSettings)) - }) - return _c -} - -func (_c *MockSettings_AddConnection_Call) Return(_a0 gonetworkmanager.Connection, _a1 error) *MockSettings_AddConnection_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockSettings_AddConnection_Call) RunAndReturn(run func(gonetworkmanager.ConnectionSettings) (gonetworkmanager.Connection, error)) *MockSettings_AddConnection_Call { - _c.Call.Return(run) - return _c -} - -// AddConnectionUnsaved provides a mock function with given fields: settings -func (_m *MockSettings) AddConnectionUnsaved(settings gonetworkmanager.ConnectionSettings) (gonetworkmanager.Connection, error) { - ret := _m.Called(settings) - - if len(ret) == 0 { - panic("no return value specified for AddConnectionUnsaved") - } - - var r0 gonetworkmanager.Connection - var r1 error - if rf, ok := ret.Get(0).(func(gonetworkmanager.ConnectionSettings) (gonetworkmanager.Connection, error)); ok { - return rf(settings) - } - if rf, ok := ret.Get(0).(func(gonetworkmanager.ConnectionSettings) gonetworkmanager.Connection); ok { - r0 = rf(settings) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.Connection) - } - } - - if rf, ok := ret.Get(1).(func(gonetworkmanager.ConnectionSettings) error); ok { - r1 = rf(settings) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockSettings_AddConnectionUnsaved_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddConnectionUnsaved' -type MockSettings_AddConnectionUnsaved_Call struct { - *mock.Call -} - -// AddConnectionUnsaved is a helper method to define mock.On call -// - settings gonetworkmanager.ConnectionSettings -func (_e *MockSettings_Expecter) AddConnectionUnsaved(settings interface{}) *MockSettings_AddConnectionUnsaved_Call { - return &MockSettings_AddConnectionUnsaved_Call{Call: _e.mock.On("AddConnectionUnsaved", settings)} -} - -func (_c *MockSettings_AddConnectionUnsaved_Call) Run(run func(settings gonetworkmanager.ConnectionSettings)) *MockSettings_AddConnectionUnsaved_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(gonetworkmanager.ConnectionSettings)) - }) - return _c -} - -func (_c *MockSettings_AddConnectionUnsaved_Call) Return(_a0 gonetworkmanager.Connection, _a1 error) *MockSettings_AddConnectionUnsaved_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockSettings_AddConnectionUnsaved_Call) RunAndReturn(run func(gonetworkmanager.ConnectionSettings) (gonetworkmanager.Connection, error)) *MockSettings_AddConnectionUnsaved_Call { - _c.Call.Return(run) - return _c -} - -// GetConnectionByUUID provides a mock function with given fields: uuid -func (_m *MockSettings) GetConnectionByUUID(uuid string) (gonetworkmanager.Connection, error) { - ret := _m.Called(uuid) - - if len(ret) == 0 { - panic("no return value specified for GetConnectionByUUID") - } - - var r0 gonetworkmanager.Connection - var r1 error - if rf, ok := ret.Get(0).(func(string) (gonetworkmanager.Connection, error)); ok { - return rf(uuid) - } - if rf, ok := ret.Get(0).(func(string) gonetworkmanager.Connection); ok { - r0 = rf(uuid) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.Connection) - } - } - - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(uuid) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockSettings_GetConnectionByUUID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetConnectionByUUID' -type MockSettings_GetConnectionByUUID_Call struct { - *mock.Call -} - -// GetConnectionByUUID is a helper method to define mock.On call -// - uuid string -func (_e *MockSettings_Expecter) GetConnectionByUUID(uuid interface{}) *MockSettings_GetConnectionByUUID_Call { - return &MockSettings_GetConnectionByUUID_Call{Call: _e.mock.On("GetConnectionByUUID", uuid)} -} - -func (_c *MockSettings_GetConnectionByUUID_Call) Run(run func(uuid string)) *MockSettings_GetConnectionByUUID_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *MockSettings_GetConnectionByUUID_Call) Return(_a0 gonetworkmanager.Connection, _a1 error) *MockSettings_GetConnectionByUUID_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockSettings_GetConnectionByUUID_Call) RunAndReturn(run func(string) (gonetworkmanager.Connection, error)) *MockSettings_GetConnectionByUUID_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyCanModify provides a mock function with no fields -func (_m *MockSettings) GetPropertyCanModify() (bool, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyCanModify") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func() (bool, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockSettings_GetPropertyCanModify_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyCanModify' -type MockSettings_GetPropertyCanModify_Call struct { - *mock.Call -} - -// GetPropertyCanModify is a helper method to define mock.On call -func (_e *MockSettings_Expecter) GetPropertyCanModify() *MockSettings_GetPropertyCanModify_Call { - return &MockSettings_GetPropertyCanModify_Call{Call: _e.mock.On("GetPropertyCanModify")} -} - -func (_c *MockSettings_GetPropertyCanModify_Call) Run(run func()) *MockSettings_GetPropertyCanModify_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockSettings_GetPropertyCanModify_Call) Return(_a0 bool, _a1 error) *MockSettings_GetPropertyCanModify_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockSettings_GetPropertyCanModify_Call) RunAndReturn(run func() (bool, error)) *MockSettings_GetPropertyCanModify_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyHostname provides a mock function with no fields -func (_m *MockSettings) GetPropertyHostname() (string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyHostname") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func() (string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockSettings_GetPropertyHostname_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyHostname' -type MockSettings_GetPropertyHostname_Call struct { - *mock.Call -} - -// GetPropertyHostname is a helper method to define mock.On call -func (_e *MockSettings_Expecter) GetPropertyHostname() *MockSettings_GetPropertyHostname_Call { - return &MockSettings_GetPropertyHostname_Call{Call: _e.mock.On("GetPropertyHostname")} -} - -func (_c *MockSettings_GetPropertyHostname_Call) Run(run func()) *MockSettings_GetPropertyHostname_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockSettings_GetPropertyHostname_Call) Return(_a0 string, _a1 error) *MockSettings_GetPropertyHostname_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockSettings_GetPropertyHostname_Call) RunAndReturn(run func() (string, error)) *MockSettings_GetPropertyHostname_Call { - _c.Call.Return(run) - return _c -} - -// ListConnections provides a mock function with no fields -func (_m *MockSettings) ListConnections() ([]gonetworkmanager.Connection, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for ListConnections") - } - - var r0 []gonetworkmanager.Connection - var r1 error - if rf, ok := ret.Get(0).(func() ([]gonetworkmanager.Connection, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []gonetworkmanager.Connection); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]gonetworkmanager.Connection) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockSettings_ListConnections_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListConnections' -type MockSettings_ListConnections_Call struct { - *mock.Call -} - -// ListConnections is a helper method to define mock.On call -func (_e *MockSettings_Expecter) ListConnections() *MockSettings_ListConnections_Call { - return &MockSettings_ListConnections_Call{Call: _e.mock.On("ListConnections")} -} - -func (_c *MockSettings_ListConnections_Call) Run(run func()) *MockSettings_ListConnections_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockSettings_ListConnections_Call) Return(_a0 []gonetworkmanager.Connection, _a1 error) *MockSettings_ListConnections_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockSettings_ListConnections_Call) RunAndReturn(run func() ([]gonetworkmanager.Connection, error)) *MockSettings_ListConnections_Call { - _c.Call.Return(run) - return _c -} - -// ReloadConnections provides a mock function with no fields -func (_m *MockSettings) ReloadConnections() error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for ReloadConnections") - } - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockSettings_ReloadConnections_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ReloadConnections' -type MockSettings_ReloadConnections_Call struct { - *mock.Call -} - -// ReloadConnections is a helper method to define mock.On call -func (_e *MockSettings_Expecter) ReloadConnections() *MockSettings_ReloadConnections_Call { - return &MockSettings_ReloadConnections_Call{Call: _e.mock.On("ReloadConnections")} -} - -func (_c *MockSettings_ReloadConnections_Call) Run(run func()) *MockSettings_ReloadConnections_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockSettings_ReloadConnections_Call) Return(_a0 error) *MockSettings_ReloadConnections_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockSettings_ReloadConnections_Call) RunAndReturn(run func() error) *MockSettings_ReloadConnections_Call { - _c.Call.Return(run) - return _c -} - -// SaveHostname provides a mock function with given fields: hostname -func (_m *MockSettings) SaveHostname(hostname string) error { - ret := _m.Called(hostname) - - if len(ret) == 0 { - panic("no return value specified for SaveHostname") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string) error); ok { - r0 = rf(hostname) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockSettings_SaveHostname_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SaveHostname' -type MockSettings_SaveHostname_Call struct { - *mock.Call -} - -// SaveHostname is a helper method to define mock.On call -// - hostname string -func (_e *MockSettings_Expecter) SaveHostname(hostname interface{}) *MockSettings_SaveHostname_Call { - return &MockSettings_SaveHostname_Call{Call: _e.mock.On("SaveHostname", hostname)} -} - -func (_c *MockSettings_SaveHostname_Call) Run(run func(hostname string)) *MockSettings_SaveHostname_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *MockSettings_SaveHostname_Call) Return(_a0 error) *MockSettings_SaveHostname_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockSettings_SaveHostname_Call) RunAndReturn(run func(string) error) *MockSettings_SaveHostname_Call { - _c.Call.Return(run) - return _c -} - -// NewMockSettings creates a new instance of MockSettings. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewMockSettings(t interface { - mock.TestingT - Cleanup(func()) -}) *MockSettings { - mock := &MockSettings{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/nix/inputs/dms-cli/internal/mocks/github.com/godbus/dbus/v5/mock_BusObject.go b/nix/inputs/dms-cli/internal/mocks/github.com/godbus/dbus/v5/mock_BusObject.go deleted file mode 100644 index c0d1a3f..0000000 --- a/nix/inputs/dms-cli/internal/mocks/github.com/godbus/dbus/v5/mock_BusObject.go +++ /dev/null @@ -1,649 +0,0 @@ -// Code generated by mockery v2.53.5. DO NOT EDIT. - -package dbus - -import ( - context "context" - - dbus "github.com/godbus/dbus/v5" - mock "github.com/stretchr/testify/mock" -) - -// MockBusObject is an autogenerated mock type for the BusObject type -type MockBusObject struct { - mock.Mock -} - -type MockBusObject_Expecter struct { - mock *mock.Mock -} - -func (_m *MockBusObject) EXPECT() *MockBusObject_Expecter { - return &MockBusObject_Expecter{mock: &_m.Mock} -} - -// AddMatchSignal provides a mock function with given fields: iface, member, options -func (_m *MockBusObject) AddMatchSignal(iface string, member string, options ...dbus.MatchOption) *dbus.Call { - _va := make([]interface{}, len(options)) - for _i := range options { - _va[_i] = options[_i] - } - var _ca []interface{} - _ca = append(_ca, iface, member) - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) - - if len(ret) == 0 { - panic("no return value specified for AddMatchSignal") - } - - var r0 *dbus.Call - if rf, ok := ret.Get(0).(func(string, string, ...dbus.MatchOption) *dbus.Call); ok { - r0 = rf(iface, member, options...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*dbus.Call) - } - } - - return r0 -} - -// MockBusObject_AddMatchSignal_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddMatchSignal' -type MockBusObject_AddMatchSignal_Call struct { - *mock.Call -} - -// AddMatchSignal is a helper method to define mock.On call -// - iface string -// - member string -// - options ...dbus.MatchOption -func (_e *MockBusObject_Expecter) AddMatchSignal(iface interface{}, member interface{}, options ...interface{}) *MockBusObject_AddMatchSignal_Call { - return &MockBusObject_AddMatchSignal_Call{Call: _e.mock.On("AddMatchSignal", - append([]interface{}{iface, member}, options...)...)} -} - -func (_c *MockBusObject_AddMatchSignal_Call) Run(run func(iface string, member string, options ...dbus.MatchOption)) *MockBusObject_AddMatchSignal_Call { - _c.Call.Run(func(args mock.Arguments) { - variadicArgs := make([]dbus.MatchOption, len(args)-2) - for i, a := range args[2:] { - if a != nil { - variadicArgs[i] = a.(dbus.MatchOption) - } - } - run(args[0].(string), args[1].(string), variadicArgs...) - }) - return _c -} - -func (_c *MockBusObject_AddMatchSignal_Call) Return(_a0 *dbus.Call) *MockBusObject_AddMatchSignal_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBusObject_AddMatchSignal_Call) RunAndReturn(run func(string, string, ...dbus.MatchOption) *dbus.Call) *MockBusObject_AddMatchSignal_Call { - _c.Call.Return(run) - return _c -} - -// Call provides a mock function with given fields: method, flags, args -func (_m *MockBusObject) Call(method string, flags dbus.Flags, args ...interface{}) *dbus.Call { - var _ca []interface{} - _ca = append(_ca, method, flags) - _ca = append(_ca, args...) - ret := _m.Called(_ca...) - - if len(ret) == 0 { - panic("no return value specified for Call") - } - - var r0 *dbus.Call - if rf, ok := ret.Get(0).(func(string, dbus.Flags, ...interface{}) *dbus.Call); ok { - r0 = rf(method, flags, args...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*dbus.Call) - } - } - - return r0 -} - -// MockBusObject_Call_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Call' -type MockBusObject_Call_Call struct { - *mock.Call -} - -// Call is a helper method to define mock.On call -// - method string -// - flags dbus.Flags -// - args ...interface{} -func (_e *MockBusObject_Expecter) Call(method interface{}, flags interface{}, args ...interface{}) *MockBusObject_Call_Call { - return &MockBusObject_Call_Call{Call: _e.mock.On("Call", - append([]interface{}{method, flags}, args...)...)} -} - -func (_c *MockBusObject_Call_Call) Run(run func(method string, flags dbus.Flags, args ...interface{})) *MockBusObject_Call_Call { - _c.Call.Run(func(args mock.Arguments) { - variadicArgs := make([]interface{}, len(args)-2) - for i, a := range args[2:] { - if a != nil { - variadicArgs[i] = a.(interface{}) - } - } - run(args[0].(string), args[1].(dbus.Flags), variadicArgs...) - }) - return _c -} - -func (_c *MockBusObject_Call_Call) Return(_a0 *dbus.Call) *MockBusObject_Call_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBusObject_Call_Call) RunAndReturn(run func(string, dbus.Flags, ...interface{}) *dbus.Call) *MockBusObject_Call_Call { - _c.Call.Return(run) - return _c -} - -// CallWithContext provides a mock function with given fields: ctx, method, flags, args -func (_m *MockBusObject) CallWithContext(ctx context.Context, method string, flags dbus.Flags, args ...interface{}) *dbus.Call { - var _ca []interface{} - _ca = append(_ca, ctx, method, flags) - _ca = append(_ca, args...) - ret := _m.Called(_ca...) - - if len(ret) == 0 { - panic("no return value specified for CallWithContext") - } - - var r0 *dbus.Call - if rf, ok := ret.Get(0).(func(context.Context, string, dbus.Flags, ...interface{}) *dbus.Call); ok { - r0 = rf(ctx, method, flags, args...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*dbus.Call) - } - } - - return r0 -} - -// MockBusObject_CallWithContext_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CallWithContext' -type MockBusObject_CallWithContext_Call struct { - *mock.Call -} - -// CallWithContext is a helper method to define mock.On call -// - ctx context.Context -// - method string -// - flags dbus.Flags -// - args ...interface{} -func (_e *MockBusObject_Expecter) CallWithContext(ctx interface{}, method interface{}, flags interface{}, args ...interface{}) *MockBusObject_CallWithContext_Call { - return &MockBusObject_CallWithContext_Call{Call: _e.mock.On("CallWithContext", - append([]interface{}{ctx, method, flags}, args...)...)} -} - -func (_c *MockBusObject_CallWithContext_Call) Run(run func(ctx context.Context, method string, flags dbus.Flags, args ...interface{})) *MockBusObject_CallWithContext_Call { - _c.Call.Run(func(args mock.Arguments) { - variadicArgs := make([]interface{}, len(args)-3) - for i, a := range args[3:] { - if a != nil { - variadicArgs[i] = a.(interface{}) - } - } - run(args[0].(context.Context), args[1].(string), args[2].(dbus.Flags), variadicArgs...) - }) - return _c -} - -func (_c *MockBusObject_CallWithContext_Call) Return(_a0 *dbus.Call) *MockBusObject_CallWithContext_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBusObject_CallWithContext_Call) RunAndReturn(run func(context.Context, string, dbus.Flags, ...interface{}) *dbus.Call) *MockBusObject_CallWithContext_Call { - _c.Call.Return(run) - return _c -} - -// Destination provides a mock function with no fields -func (_m *MockBusObject) Destination() string { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Destination") - } - - var r0 string - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - return r0 -} - -// MockBusObject_Destination_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Destination' -type MockBusObject_Destination_Call struct { - *mock.Call -} - -// Destination is a helper method to define mock.On call -func (_e *MockBusObject_Expecter) Destination() *MockBusObject_Destination_Call { - return &MockBusObject_Destination_Call{Call: _e.mock.On("Destination")} -} - -func (_c *MockBusObject_Destination_Call) Run(run func()) *MockBusObject_Destination_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockBusObject_Destination_Call) Return(_a0 string) *MockBusObject_Destination_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBusObject_Destination_Call) RunAndReturn(run func() string) *MockBusObject_Destination_Call { - _c.Call.Return(run) - return _c -} - -// GetProperty provides a mock function with given fields: p -func (_m *MockBusObject) GetProperty(p string) (dbus.Variant, error) { - ret := _m.Called(p) - - if len(ret) == 0 { - panic("no return value specified for GetProperty") - } - - var r0 dbus.Variant - var r1 error - if rf, ok := ret.Get(0).(func(string) (dbus.Variant, error)); ok { - return rf(p) - } - if rf, ok := ret.Get(0).(func(string) dbus.Variant); ok { - r0 = rf(p) - } else { - r0 = ret.Get(0).(dbus.Variant) - } - - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(p) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockBusObject_GetProperty_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetProperty' -type MockBusObject_GetProperty_Call struct { - *mock.Call -} - -// GetProperty is a helper method to define mock.On call -// - p string -func (_e *MockBusObject_Expecter) GetProperty(p interface{}) *MockBusObject_GetProperty_Call { - return &MockBusObject_GetProperty_Call{Call: _e.mock.On("GetProperty", p)} -} - -func (_c *MockBusObject_GetProperty_Call) Run(run func(p string)) *MockBusObject_GetProperty_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *MockBusObject_GetProperty_Call) Return(_a0 dbus.Variant, _a1 error) *MockBusObject_GetProperty_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockBusObject_GetProperty_Call) RunAndReturn(run func(string) (dbus.Variant, error)) *MockBusObject_GetProperty_Call { - _c.Call.Return(run) - return _c -} - -// Go provides a mock function with given fields: method, flags, ch, args -func (_m *MockBusObject) Go(method string, flags dbus.Flags, ch chan *dbus.Call, args ...interface{}) *dbus.Call { - var _ca []interface{} - _ca = append(_ca, method, flags, ch) - _ca = append(_ca, args...) - ret := _m.Called(_ca...) - - if len(ret) == 0 { - panic("no return value specified for Go") - } - - var r0 *dbus.Call - if rf, ok := ret.Get(0).(func(string, dbus.Flags, chan *dbus.Call, ...interface{}) *dbus.Call); ok { - r0 = rf(method, flags, ch, args...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*dbus.Call) - } - } - - return r0 -} - -// MockBusObject_Go_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Go' -type MockBusObject_Go_Call struct { - *mock.Call -} - -// Go is a helper method to define mock.On call -// - method string -// - flags dbus.Flags -// - ch chan *dbus.Call -// - args ...interface{} -func (_e *MockBusObject_Expecter) Go(method interface{}, flags interface{}, ch interface{}, args ...interface{}) *MockBusObject_Go_Call { - return &MockBusObject_Go_Call{Call: _e.mock.On("Go", - append([]interface{}{method, flags, ch}, args...)...)} -} - -func (_c *MockBusObject_Go_Call) Run(run func(method string, flags dbus.Flags, ch chan *dbus.Call, args ...interface{})) *MockBusObject_Go_Call { - _c.Call.Run(func(args mock.Arguments) { - variadicArgs := make([]interface{}, len(args)-3) - for i, a := range args[3:] { - if a != nil { - variadicArgs[i] = a.(interface{}) - } - } - run(args[0].(string), args[1].(dbus.Flags), args[2].(chan *dbus.Call), variadicArgs...) - }) - return _c -} - -func (_c *MockBusObject_Go_Call) Return(_a0 *dbus.Call) *MockBusObject_Go_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBusObject_Go_Call) RunAndReturn(run func(string, dbus.Flags, chan *dbus.Call, ...interface{}) *dbus.Call) *MockBusObject_Go_Call { - _c.Call.Return(run) - return _c -} - -// GoWithContext provides a mock function with given fields: ctx, method, flags, ch, args -func (_m *MockBusObject) GoWithContext(ctx context.Context, method string, flags dbus.Flags, ch chan *dbus.Call, args ...interface{}) *dbus.Call { - var _ca []interface{} - _ca = append(_ca, ctx, method, flags, ch) - _ca = append(_ca, args...) - ret := _m.Called(_ca...) - - if len(ret) == 0 { - panic("no return value specified for GoWithContext") - } - - var r0 *dbus.Call - if rf, ok := ret.Get(0).(func(context.Context, string, dbus.Flags, chan *dbus.Call, ...interface{}) *dbus.Call); ok { - r0 = rf(ctx, method, flags, ch, args...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*dbus.Call) - } - } - - return r0 -} - -// MockBusObject_GoWithContext_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GoWithContext' -type MockBusObject_GoWithContext_Call struct { - *mock.Call -} - -// GoWithContext is a helper method to define mock.On call -// - ctx context.Context -// - method string -// - flags dbus.Flags -// - ch chan *dbus.Call -// - args ...interface{} -func (_e *MockBusObject_Expecter) GoWithContext(ctx interface{}, method interface{}, flags interface{}, ch interface{}, args ...interface{}) *MockBusObject_GoWithContext_Call { - return &MockBusObject_GoWithContext_Call{Call: _e.mock.On("GoWithContext", - append([]interface{}{ctx, method, flags, ch}, args...)...)} -} - -func (_c *MockBusObject_GoWithContext_Call) Run(run func(ctx context.Context, method string, flags dbus.Flags, ch chan *dbus.Call, args ...interface{})) *MockBusObject_GoWithContext_Call { - _c.Call.Run(func(args mock.Arguments) { - variadicArgs := make([]interface{}, len(args)-4) - for i, a := range args[4:] { - if a != nil { - variadicArgs[i] = a.(interface{}) - } - } - run(args[0].(context.Context), args[1].(string), args[2].(dbus.Flags), args[3].(chan *dbus.Call), variadicArgs...) - }) - return _c -} - -func (_c *MockBusObject_GoWithContext_Call) Return(_a0 *dbus.Call) *MockBusObject_GoWithContext_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBusObject_GoWithContext_Call) RunAndReturn(run func(context.Context, string, dbus.Flags, chan *dbus.Call, ...interface{}) *dbus.Call) *MockBusObject_GoWithContext_Call { - _c.Call.Return(run) - return _c -} - -// Path provides a mock function with no fields -func (_m *MockBusObject) Path() dbus.ObjectPath { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Path") - } - - var r0 dbus.ObjectPath - if rf, ok := ret.Get(0).(func() dbus.ObjectPath); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(dbus.ObjectPath) - } - - return r0 -} - -// MockBusObject_Path_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Path' -type MockBusObject_Path_Call struct { - *mock.Call -} - -// Path is a helper method to define mock.On call -func (_e *MockBusObject_Expecter) Path() *MockBusObject_Path_Call { - return &MockBusObject_Path_Call{Call: _e.mock.On("Path")} -} - -func (_c *MockBusObject_Path_Call) Run(run func()) *MockBusObject_Path_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockBusObject_Path_Call) Return(_a0 dbus.ObjectPath) *MockBusObject_Path_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBusObject_Path_Call) RunAndReturn(run func() dbus.ObjectPath) *MockBusObject_Path_Call { - _c.Call.Return(run) - return _c -} - -// RemoveMatchSignal provides a mock function with given fields: iface, member, options -func (_m *MockBusObject) RemoveMatchSignal(iface string, member string, options ...dbus.MatchOption) *dbus.Call { - _va := make([]interface{}, len(options)) - for _i := range options { - _va[_i] = options[_i] - } - var _ca []interface{} - _ca = append(_ca, iface, member) - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) - - if len(ret) == 0 { - panic("no return value specified for RemoveMatchSignal") - } - - var r0 *dbus.Call - if rf, ok := ret.Get(0).(func(string, string, ...dbus.MatchOption) *dbus.Call); ok { - r0 = rf(iface, member, options...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*dbus.Call) - } - } - - return r0 -} - -// MockBusObject_RemoveMatchSignal_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RemoveMatchSignal' -type MockBusObject_RemoveMatchSignal_Call struct { - *mock.Call -} - -// RemoveMatchSignal is a helper method to define mock.On call -// - iface string -// - member string -// - options ...dbus.MatchOption -func (_e *MockBusObject_Expecter) RemoveMatchSignal(iface interface{}, member interface{}, options ...interface{}) *MockBusObject_RemoveMatchSignal_Call { - return &MockBusObject_RemoveMatchSignal_Call{Call: _e.mock.On("RemoveMatchSignal", - append([]interface{}{iface, member}, options...)...)} -} - -func (_c *MockBusObject_RemoveMatchSignal_Call) Run(run func(iface string, member string, options ...dbus.MatchOption)) *MockBusObject_RemoveMatchSignal_Call { - _c.Call.Run(func(args mock.Arguments) { - variadicArgs := make([]dbus.MatchOption, len(args)-2) - for i, a := range args[2:] { - if a != nil { - variadicArgs[i] = a.(dbus.MatchOption) - } - } - run(args[0].(string), args[1].(string), variadicArgs...) - }) - return _c -} - -func (_c *MockBusObject_RemoveMatchSignal_Call) Return(_a0 *dbus.Call) *MockBusObject_RemoveMatchSignal_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBusObject_RemoveMatchSignal_Call) RunAndReturn(run func(string, string, ...dbus.MatchOption) *dbus.Call) *MockBusObject_RemoveMatchSignal_Call { - _c.Call.Return(run) - return _c -} - -// SetProperty provides a mock function with given fields: p, v -func (_m *MockBusObject) SetProperty(p string, v interface{}) error { - ret := _m.Called(p, v) - - if len(ret) == 0 { - panic("no return value specified for SetProperty") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string, interface{}) error); ok { - r0 = rf(p, v) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockBusObject_SetProperty_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetProperty' -type MockBusObject_SetProperty_Call struct { - *mock.Call -} - -// SetProperty is a helper method to define mock.On call -// - p string -// - v interface{} -func (_e *MockBusObject_Expecter) SetProperty(p interface{}, v interface{}) *MockBusObject_SetProperty_Call { - return &MockBusObject_SetProperty_Call{Call: _e.mock.On("SetProperty", p, v)} -} - -func (_c *MockBusObject_SetProperty_Call) Run(run func(p string, v interface{})) *MockBusObject_SetProperty_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(interface{})) - }) - return _c -} - -func (_c *MockBusObject_SetProperty_Call) Return(_a0 error) *MockBusObject_SetProperty_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBusObject_SetProperty_Call) RunAndReturn(run func(string, interface{}) error) *MockBusObject_SetProperty_Call { - _c.Call.Return(run) - return _c -} - -// StoreProperty provides a mock function with given fields: p, value -func (_m *MockBusObject) StoreProperty(p string, value interface{}) error { - ret := _m.Called(p, value) - - if len(ret) == 0 { - panic("no return value specified for StoreProperty") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string, interface{}) error); ok { - r0 = rf(p, value) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockBusObject_StoreProperty_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'StoreProperty' -type MockBusObject_StoreProperty_Call struct { - *mock.Call -} - -// StoreProperty is a helper method to define mock.On call -// - p string -// - value interface{} -func (_e *MockBusObject_Expecter) StoreProperty(p interface{}, value interface{}) *MockBusObject_StoreProperty_Call { - return &MockBusObject_StoreProperty_Call{Call: _e.mock.On("StoreProperty", p, value)} -} - -func (_c *MockBusObject_StoreProperty_Call) Run(run func(p string, value interface{})) *MockBusObject_StoreProperty_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(interface{})) - }) - return _c -} - -func (_c *MockBusObject_StoreProperty_Call) Return(_a0 error) *MockBusObject_StoreProperty_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBusObject_StoreProperty_Call) RunAndReturn(run func(string, interface{}) error) *MockBusObject_StoreProperty_Call { - _c.Call.Return(run) - return _c -} - -// NewMockBusObject creates a new instance of MockBusObject. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewMockBusObject(t interface { - mock.TestingT - Cleanup(func()) -}) *MockBusObject { - mock := &MockBusObject{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/nix/inputs/dms-cli/internal/mocks/internal/plugins/mock_GitClient.go b/nix/inputs/dms-cli/internal/mocks/internal/plugins/mock_GitClient.go deleted file mode 100644 index d43afed..0000000 --- a/nix/inputs/dms-cli/internal/mocks/internal/plugins/mock_GitClient.go +++ /dev/null @@ -1,181 +0,0 @@ -// Code generated by mockery v2.53.5. DO NOT EDIT. - -package plugins - -import mock "github.com/stretchr/testify/mock" - -// MockGitClient is an autogenerated mock type for the GitClient type -type MockGitClient struct { - mock.Mock -} - -type MockGitClient_Expecter struct { - mock *mock.Mock -} - -func (_m *MockGitClient) EXPECT() *MockGitClient_Expecter { - return &MockGitClient_Expecter{mock: &_m.Mock} -} - -// HasUpdates provides a mock function with given fields: path -func (_m *MockGitClient) HasUpdates(path string) (bool, error) { - ret := _m.Called(path) - - if len(ret) == 0 { - panic("no return value specified for HasUpdates") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func(string) (bool, error)); ok { - return rf(path) - } - if rf, ok := ret.Get(0).(func(string) bool); ok { - r0 = rf(path) - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(path) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockGitClient_HasUpdates_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'HasUpdates' -type MockGitClient_HasUpdates_Call struct { - *mock.Call -} - -// HasUpdates is a helper method to define mock.On call -// - path string -func (_e *MockGitClient_Expecter) HasUpdates(path interface{}) *MockGitClient_HasUpdates_Call { - return &MockGitClient_HasUpdates_Call{Call: _e.mock.On("HasUpdates", path)} -} - -func (_c *MockGitClient_HasUpdates_Call) Run(run func(path string)) *MockGitClient_HasUpdates_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *MockGitClient_HasUpdates_Call) Return(_a0 bool, _a1 error) *MockGitClient_HasUpdates_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockGitClient_HasUpdates_Call) RunAndReturn(run func(string) (bool, error)) *MockGitClient_HasUpdates_Call { - _c.Call.Return(run) - return _c -} - -// PlainClone provides a mock function with given fields: path, url -func (_m *MockGitClient) PlainClone(path string, url string) error { - ret := _m.Called(path, url) - - if len(ret) == 0 { - panic("no return value specified for PlainClone") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string, string) error); ok { - r0 = rf(path, url) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockGitClient_PlainClone_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PlainClone' -type MockGitClient_PlainClone_Call struct { - *mock.Call -} - -// PlainClone is a helper method to define mock.On call -// - path string -// - url string -func (_e *MockGitClient_Expecter) PlainClone(path interface{}, url interface{}) *MockGitClient_PlainClone_Call { - return &MockGitClient_PlainClone_Call{Call: _e.mock.On("PlainClone", path, url)} -} - -func (_c *MockGitClient_PlainClone_Call) Run(run func(path string, url string)) *MockGitClient_PlainClone_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(string)) - }) - return _c -} - -func (_c *MockGitClient_PlainClone_Call) Return(_a0 error) *MockGitClient_PlainClone_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockGitClient_PlainClone_Call) RunAndReturn(run func(string, string) error) *MockGitClient_PlainClone_Call { - _c.Call.Return(run) - return _c -} - -// Pull provides a mock function with given fields: path -func (_m *MockGitClient) Pull(path string) error { - ret := _m.Called(path) - - if len(ret) == 0 { - panic("no return value specified for Pull") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string) error); ok { - r0 = rf(path) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockGitClient_Pull_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Pull' -type MockGitClient_Pull_Call struct { - *mock.Call -} - -// Pull is a helper method to define mock.On call -// - path string -func (_e *MockGitClient_Expecter) Pull(path interface{}) *MockGitClient_Pull_Call { - return &MockGitClient_Pull_Call{Call: _e.mock.On("Pull", path)} -} - -func (_c *MockGitClient_Pull_Call) Run(run func(path string)) *MockGitClient_Pull_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *MockGitClient_Pull_Call) Return(_a0 error) *MockGitClient_Pull_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockGitClient_Pull_Call) RunAndReturn(run func(string) error) *MockGitClient_Pull_Call { - _c.Call.Return(run) - return _c -} - -// NewMockGitClient creates a new instance of MockGitClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewMockGitClient(t interface { - mock.TestingT - Cleanup(func()) -}) *MockGitClient { - mock := &MockGitClient{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/nix/inputs/dms-cli/internal/mocks/net/mock_Conn.go b/nix/inputs/dms-cli/internal/mocks/net/mock_Conn.go deleted file mode 100644 index 248a0d8..0000000 --- a/nix/inputs/dms-cli/internal/mocks/net/mock_Conn.go +++ /dev/null @@ -1,427 +0,0 @@ -// Code generated by mockery v2.53.5. DO NOT EDIT. - -package net - -import ( - net "net" - - mock "github.com/stretchr/testify/mock" - - time "time" -) - -// MockConn is an autogenerated mock type for the Conn type -type MockConn struct { - mock.Mock -} - -type MockConn_Expecter struct { - mock *mock.Mock -} - -func (_m *MockConn) EXPECT() *MockConn_Expecter { - return &MockConn_Expecter{mock: &_m.Mock} -} - -// Close provides a mock function with no fields -func (_m *MockConn) Close() error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Close") - } - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockConn_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close' -type MockConn_Close_Call struct { - *mock.Call -} - -// Close is a helper method to define mock.On call -func (_e *MockConn_Expecter) Close() *MockConn_Close_Call { - return &MockConn_Close_Call{Call: _e.mock.On("Close")} -} - -func (_c *MockConn_Close_Call) Run(run func()) *MockConn_Close_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockConn_Close_Call) Return(_a0 error) *MockConn_Close_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockConn_Close_Call) RunAndReturn(run func() error) *MockConn_Close_Call { - _c.Call.Return(run) - return _c -} - -// LocalAddr provides a mock function with no fields -func (_m *MockConn) LocalAddr() net.Addr { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for LocalAddr") - } - - var r0 net.Addr - if rf, ok := ret.Get(0).(func() net.Addr); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(net.Addr) - } - } - - return r0 -} - -// MockConn_LocalAddr_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LocalAddr' -type MockConn_LocalAddr_Call struct { - *mock.Call -} - -// LocalAddr is a helper method to define mock.On call -func (_e *MockConn_Expecter) LocalAddr() *MockConn_LocalAddr_Call { - return &MockConn_LocalAddr_Call{Call: _e.mock.On("LocalAddr")} -} - -func (_c *MockConn_LocalAddr_Call) Run(run func()) *MockConn_LocalAddr_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockConn_LocalAddr_Call) Return(_a0 net.Addr) *MockConn_LocalAddr_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockConn_LocalAddr_Call) RunAndReturn(run func() net.Addr) *MockConn_LocalAddr_Call { - _c.Call.Return(run) - return _c -} - -// Read provides a mock function with given fields: b -func (_m *MockConn) Read(b []byte) (int, error) { - ret := _m.Called(b) - - if len(ret) == 0 { - panic("no return value specified for Read") - } - - var r0 int - var r1 error - if rf, ok := ret.Get(0).(func([]byte) (int, error)); ok { - return rf(b) - } - if rf, ok := ret.Get(0).(func([]byte) int); ok { - r0 = rf(b) - } else { - r0 = ret.Get(0).(int) - } - - if rf, ok := ret.Get(1).(func([]byte) error); ok { - r1 = rf(b) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockConn_Read_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Read' -type MockConn_Read_Call struct { - *mock.Call -} - -// Read is a helper method to define mock.On call -// - b []byte -func (_e *MockConn_Expecter) Read(b interface{}) *MockConn_Read_Call { - return &MockConn_Read_Call{Call: _e.mock.On("Read", b)} -} - -func (_c *MockConn_Read_Call) Run(run func(b []byte)) *MockConn_Read_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].([]byte)) - }) - return _c -} - -func (_c *MockConn_Read_Call) Return(n int, err error) *MockConn_Read_Call { - _c.Call.Return(n, err) - return _c -} - -func (_c *MockConn_Read_Call) RunAndReturn(run func([]byte) (int, error)) *MockConn_Read_Call { - _c.Call.Return(run) - return _c -} - -// RemoteAddr provides a mock function with no fields -func (_m *MockConn) RemoteAddr() net.Addr { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for RemoteAddr") - } - - var r0 net.Addr - if rf, ok := ret.Get(0).(func() net.Addr); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(net.Addr) - } - } - - return r0 -} - -// MockConn_RemoteAddr_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RemoteAddr' -type MockConn_RemoteAddr_Call struct { - *mock.Call -} - -// RemoteAddr is a helper method to define mock.On call -func (_e *MockConn_Expecter) RemoteAddr() *MockConn_RemoteAddr_Call { - return &MockConn_RemoteAddr_Call{Call: _e.mock.On("RemoteAddr")} -} - -func (_c *MockConn_RemoteAddr_Call) Run(run func()) *MockConn_RemoteAddr_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockConn_RemoteAddr_Call) Return(_a0 net.Addr) *MockConn_RemoteAddr_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockConn_RemoteAddr_Call) RunAndReturn(run func() net.Addr) *MockConn_RemoteAddr_Call { - _c.Call.Return(run) - return _c -} - -// SetDeadline provides a mock function with given fields: t -func (_m *MockConn) SetDeadline(t time.Time) error { - ret := _m.Called(t) - - if len(ret) == 0 { - panic("no return value specified for SetDeadline") - } - - var r0 error - if rf, ok := ret.Get(0).(func(time.Time) error); ok { - r0 = rf(t) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockConn_SetDeadline_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetDeadline' -type MockConn_SetDeadline_Call struct { - *mock.Call -} - -// SetDeadline is a helper method to define mock.On call -// - t time.Time -func (_e *MockConn_Expecter) SetDeadline(t interface{}) *MockConn_SetDeadline_Call { - return &MockConn_SetDeadline_Call{Call: _e.mock.On("SetDeadline", t)} -} - -func (_c *MockConn_SetDeadline_Call) Run(run func(t time.Time)) *MockConn_SetDeadline_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(time.Time)) - }) - return _c -} - -func (_c *MockConn_SetDeadline_Call) Return(_a0 error) *MockConn_SetDeadline_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockConn_SetDeadline_Call) RunAndReturn(run func(time.Time) error) *MockConn_SetDeadline_Call { - _c.Call.Return(run) - return _c -} - -// SetReadDeadline provides a mock function with given fields: t -func (_m *MockConn) SetReadDeadline(t time.Time) error { - ret := _m.Called(t) - - if len(ret) == 0 { - panic("no return value specified for SetReadDeadline") - } - - var r0 error - if rf, ok := ret.Get(0).(func(time.Time) error); ok { - r0 = rf(t) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockConn_SetReadDeadline_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetReadDeadline' -type MockConn_SetReadDeadline_Call struct { - *mock.Call -} - -// SetReadDeadline is a helper method to define mock.On call -// - t time.Time -func (_e *MockConn_Expecter) SetReadDeadline(t interface{}) *MockConn_SetReadDeadline_Call { - return &MockConn_SetReadDeadline_Call{Call: _e.mock.On("SetReadDeadline", t)} -} - -func (_c *MockConn_SetReadDeadline_Call) Run(run func(t time.Time)) *MockConn_SetReadDeadline_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(time.Time)) - }) - return _c -} - -func (_c *MockConn_SetReadDeadline_Call) Return(_a0 error) *MockConn_SetReadDeadline_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockConn_SetReadDeadline_Call) RunAndReturn(run func(time.Time) error) *MockConn_SetReadDeadline_Call { - _c.Call.Return(run) - return _c -} - -// SetWriteDeadline provides a mock function with given fields: t -func (_m *MockConn) SetWriteDeadline(t time.Time) error { - ret := _m.Called(t) - - if len(ret) == 0 { - panic("no return value specified for SetWriteDeadline") - } - - var r0 error - if rf, ok := ret.Get(0).(func(time.Time) error); ok { - r0 = rf(t) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockConn_SetWriteDeadline_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetWriteDeadline' -type MockConn_SetWriteDeadline_Call struct { - *mock.Call -} - -// SetWriteDeadline is a helper method to define mock.On call -// - t time.Time -func (_e *MockConn_Expecter) SetWriteDeadline(t interface{}) *MockConn_SetWriteDeadline_Call { - return &MockConn_SetWriteDeadline_Call{Call: _e.mock.On("SetWriteDeadline", t)} -} - -func (_c *MockConn_SetWriteDeadline_Call) Run(run func(t time.Time)) *MockConn_SetWriteDeadline_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(time.Time)) - }) - return _c -} - -func (_c *MockConn_SetWriteDeadline_Call) Return(_a0 error) *MockConn_SetWriteDeadline_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockConn_SetWriteDeadline_Call) RunAndReturn(run func(time.Time) error) *MockConn_SetWriteDeadline_Call { - _c.Call.Return(run) - return _c -} - -// Write provides a mock function with given fields: b -func (_m *MockConn) Write(b []byte) (int, error) { - ret := _m.Called(b) - - if len(ret) == 0 { - panic("no return value specified for Write") - } - - var r0 int - var r1 error - if rf, ok := ret.Get(0).(func([]byte) (int, error)); ok { - return rf(b) - } - if rf, ok := ret.Get(0).(func([]byte) int); ok { - r0 = rf(b) - } else { - r0 = ret.Get(0).(int) - } - - if rf, ok := ret.Get(1).(func([]byte) error); ok { - r1 = rf(b) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockConn_Write_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Write' -type MockConn_Write_Call struct { - *mock.Call -} - -// Write is a helper method to define mock.On call -// - b []byte -func (_e *MockConn_Expecter) Write(b interface{}) *MockConn_Write_Call { - return &MockConn_Write_Call{Call: _e.mock.On("Write", b)} -} - -func (_c *MockConn_Write_Call) Run(run func(b []byte)) *MockConn_Write_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].([]byte)) - }) - return _c -} - -func (_c *MockConn_Write_Call) Return(n int, err error) *MockConn_Write_Call { - _c.Call.Return(n, err) - return _c -} - -func (_c *MockConn_Write_Call) RunAndReturn(run func([]byte) (int, error)) *MockConn_Write_Call { - _c.Call.Return(run) - return _c -} - -// NewMockConn creates a new instance of MockConn. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewMockConn(t interface { - mock.TestingT - Cleanup(func()) -}) *MockConn { - mock := &MockConn{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/nix/inputs/dms-cli/internal/mocks/network/mock_Backend.go b/nix/inputs/dms-cli/internal/mocks/network/mock_Backend.go deleted file mode 100644 index 8298e24..0000000 --- a/nix/inputs/dms-cli/internal/mocks/network/mock_Backend.go +++ /dev/null @@ -1,1371 +0,0 @@ -// Code generated by mockery v2.53.5. DO NOT EDIT. - -package mocks_network - -import ( - network "github.com/AvengeMedia/danklinux/internal/server/network" - mock "github.com/stretchr/testify/mock" -) - -// MockBackend is an autogenerated mock type for the Backend type -type MockBackend struct { - mock.Mock -} - -type MockBackend_Expecter struct { - mock *mock.Mock -} - -func (_m *MockBackend) EXPECT() *MockBackend_Expecter { - return &MockBackend_Expecter{mock: &_m.Mock} -} - -// ActivateWiredConnection provides a mock function with given fields: uuid -func (_m *MockBackend) ActivateWiredConnection(uuid string) error { - ret := _m.Called(uuid) - - if len(ret) == 0 { - panic("no return value specified for ActivateWiredConnection") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string) error); ok { - r0 = rf(uuid) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockBackend_ActivateWiredConnection_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ActivateWiredConnection' -type MockBackend_ActivateWiredConnection_Call struct { - *mock.Call -} - -// ActivateWiredConnection is a helper method to define mock.On call -// - uuid string -func (_e *MockBackend_Expecter) ActivateWiredConnection(uuid interface{}) *MockBackend_ActivateWiredConnection_Call { - return &MockBackend_ActivateWiredConnection_Call{Call: _e.mock.On("ActivateWiredConnection", uuid)} -} - -func (_c *MockBackend_ActivateWiredConnection_Call) Run(run func(uuid string)) *MockBackend_ActivateWiredConnection_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *MockBackend_ActivateWiredConnection_Call) Return(_a0 error) *MockBackend_ActivateWiredConnection_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBackend_ActivateWiredConnection_Call) RunAndReturn(run func(string) error) *MockBackend_ActivateWiredConnection_Call { - _c.Call.Return(run) - return _c -} - -// CancelCredentials provides a mock function with given fields: token -func (_m *MockBackend) CancelCredentials(token string) error { - ret := _m.Called(token) - - if len(ret) == 0 { - panic("no return value specified for CancelCredentials") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string) error); ok { - r0 = rf(token) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockBackend_CancelCredentials_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CancelCredentials' -type MockBackend_CancelCredentials_Call struct { - *mock.Call -} - -// CancelCredentials is a helper method to define mock.On call -// - token string -func (_e *MockBackend_Expecter) CancelCredentials(token interface{}) *MockBackend_CancelCredentials_Call { - return &MockBackend_CancelCredentials_Call{Call: _e.mock.On("CancelCredentials", token)} -} - -func (_c *MockBackend_CancelCredentials_Call) Run(run func(token string)) *MockBackend_CancelCredentials_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *MockBackend_CancelCredentials_Call) Return(_a0 error) *MockBackend_CancelCredentials_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBackend_CancelCredentials_Call) RunAndReturn(run func(string) error) *MockBackend_CancelCredentials_Call { - _c.Call.Return(run) - return _c -} - -// ClearVPNCredentials provides a mock function with given fields: uuidOrName -func (_m *MockBackend) ClearVPNCredentials(uuidOrName string) error { - ret := _m.Called(uuidOrName) - - if len(ret) == 0 { - panic("no return value specified for ClearVPNCredentials") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string) error); ok { - r0 = rf(uuidOrName) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockBackend_ClearVPNCredentials_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ClearVPNCredentials' -type MockBackend_ClearVPNCredentials_Call struct { - *mock.Call -} - -// ClearVPNCredentials is a helper method to define mock.On call -// - uuidOrName string -func (_e *MockBackend_Expecter) ClearVPNCredentials(uuidOrName interface{}) *MockBackend_ClearVPNCredentials_Call { - return &MockBackend_ClearVPNCredentials_Call{Call: _e.mock.On("ClearVPNCredentials", uuidOrName)} -} - -func (_c *MockBackend_ClearVPNCredentials_Call) Run(run func(uuidOrName string)) *MockBackend_ClearVPNCredentials_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *MockBackend_ClearVPNCredentials_Call) Return(_a0 error) *MockBackend_ClearVPNCredentials_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBackend_ClearVPNCredentials_Call) RunAndReturn(run func(string) error) *MockBackend_ClearVPNCredentials_Call { - _c.Call.Return(run) - return _c -} - -// Close provides a mock function with no fields -func (_m *MockBackend) Close() { - _m.Called() -} - -// MockBackend_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close' -type MockBackend_Close_Call struct { - *mock.Call -} - -// Close is a helper method to define mock.On call -func (_e *MockBackend_Expecter) Close() *MockBackend_Close_Call { - return &MockBackend_Close_Call{Call: _e.mock.On("Close")} -} - -func (_c *MockBackend_Close_Call) Run(run func()) *MockBackend_Close_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockBackend_Close_Call) Return() *MockBackend_Close_Call { - _c.Call.Return() - return _c -} - -func (_c *MockBackend_Close_Call) RunAndReturn(run func()) *MockBackend_Close_Call { - _c.Run(run) - return _c -} - -// ConnectEthernet provides a mock function with no fields -func (_m *MockBackend) ConnectEthernet() error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for ConnectEthernet") - } - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockBackend_ConnectEthernet_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ConnectEthernet' -type MockBackend_ConnectEthernet_Call struct { - *mock.Call -} - -// ConnectEthernet is a helper method to define mock.On call -func (_e *MockBackend_Expecter) ConnectEthernet() *MockBackend_ConnectEthernet_Call { - return &MockBackend_ConnectEthernet_Call{Call: _e.mock.On("ConnectEthernet")} -} - -func (_c *MockBackend_ConnectEthernet_Call) Run(run func()) *MockBackend_ConnectEthernet_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockBackend_ConnectEthernet_Call) Return(_a0 error) *MockBackend_ConnectEthernet_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBackend_ConnectEthernet_Call) RunAndReturn(run func() error) *MockBackend_ConnectEthernet_Call { - _c.Call.Return(run) - return _c -} - -// ConnectVPN provides a mock function with given fields: uuidOrName, singleActive -func (_m *MockBackend) ConnectVPN(uuidOrName string, singleActive bool) error { - ret := _m.Called(uuidOrName, singleActive) - - if len(ret) == 0 { - panic("no return value specified for ConnectVPN") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string, bool) error); ok { - r0 = rf(uuidOrName, singleActive) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockBackend_ConnectVPN_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ConnectVPN' -type MockBackend_ConnectVPN_Call struct { - *mock.Call -} - -// ConnectVPN is a helper method to define mock.On call -// - uuidOrName string -// - singleActive bool -func (_e *MockBackend_Expecter) ConnectVPN(uuidOrName interface{}, singleActive interface{}) *MockBackend_ConnectVPN_Call { - return &MockBackend_ConnectVPN_Call{Call: _e.mock.On("ConnectVPN", uuidOrName, singleActive)} -} - -func (_c *MockBackend_ConnectVPN_Call) Run(run func(uuidOrName string, singleActive bool)) *MockBackend_ConnectVPN_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(bool)) - }) - return _c -} - -func (_c *MockBackend_ConnectVPN_Call) Return(_a0 error) *MockBackend_ConnectVPN_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBackend_ConnectVPN_Call) RunAndReturn(run func(string, bool) error) *MockBackend_ConnectVPN_Call { - _c.Call.Return(run) - return _c -} - -// ConnectWiFi provides a mock function with given fields: req -func (_m *MockBackend) ConnectWiFi(req network.ConnectionRequest) error { - ret := _m.Called(req) - - if len(ret) == 0 { - panic("no return value specified for ConnectWiFi") - } - - var r0 error - if rf, ok := ret.Get(0).(func(network.ConnectionRequest) error); ok { - r0 = rf(req) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockBackend_ConnectWiFi_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ConnectWiFi' -type MockBackend_ConnectWiFi_Call struct { - *mock.Call -} - -// ConnectWiFi is a helper method to define mock.On call -// - req network.ConnectionRequest -func (_e *MockBackend_Expecter) ConnectWiFi(req interface{}) *MockBackend_ConnectWiFi_Call { - return &MockBackend_ConnectWiFi_Call{Call: _e.mock.On("ConnectWiFi", req)} -} - -func (_c *MockBackend_ConnectWiFi_Call) Run(run func(req network.ConnectionRequest)) *MockBackend_ConnectWiFi_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(network.ConnectionRequest)) - }) - return _c -} - -func (_c *MockBackend_ConnectWiFi_Call) Return(_a0 error) *MockBackend_ConnectWiFi_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBackend_ConnectWiFi_Call) RunAndReturn(run func(network.ConnectionRequest) error) *MockBackend_ConnectWiFi_Call { - _c.Call.Return(run) - return _c -} - -// DisconnectAllVPN provides a mock function with no fields -func (_m *MockBackend) DisconnectAllVPN() error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for DisconnectAllVPN") - } - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockBackend_DisconnectAllVPN_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DisconnectAllVPN' -type MockBackend_DisconnectAllVPN_Call struct { - *mock.Call -} - -// DisconnectAllVPN is a helper method to define mock.On call -func (_e *MockBackend_Expecter) DisconnectAllVPN() *MockBackend_DisconnectAllVPN_Call { - return &MockBackend_DisconnectAllVPN_Call{Call: _e.mock.On("DisconnectAllVPN")} -} - -func (_c *MockBackend_DisconnectAllVPN_Call) Run(run func()) *MockBackend_DisconnectAllVPN_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockBackend_DisconnectAllVPN_Call) Return(_a0 error) *MockBackend_DisconnectAllVPN_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBackend_DisconnectAllVPN_Call) RunAndReturn(run func() error) *MockBackend_DisconnectAllVPN_Call { - _c.Call.Return(run) - return _c -} - -// DisconnectEthernet provides a mock function with no fields -func (_m *MockBackend) DisconnectEthernet() error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for DisconnectEthernet") - } - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockBackend_DisconnectEthernet_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DisconnectEthernet' -type MockBackend_DisconnectEthernet_Call struct { - *mock.Call -} - -// DisconnectEthernet is a helper method to define mock.On call -func (_e *MockBackend_Expecter) DisconnectEthernet() *MockBackend_DisconnectEthernet_Call { - return &MockBackend_DisconnectEthernet_Call{Call: _e.mock.On("DisconnectEthernet")} -} - -func (_c *MockBackend_DisconnectEthernet_Call) Run(run func()) *MockBackend_DisconnectEthernet_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockBackend_DisconnectEthernet_Call) Return(_a0 error) *MockBackend_DisconnectEthernet_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBackend_DisconnectEthernet_Call) RunAndReturn(run func() error) *MockBackend_DisconnectEthernet_Call { - _c.Call.Return(run) - return _c -} - -// DisconnectVPN provides a mock function with given fields: uuidOrName -func (_m *MockBackend) DisconnectVPN(uuidOrName string) error { - ret := _m.Called(uuidOrName) - - if len(ret) == 0 { - panic("no return value specified for DisconnectVPN") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string) error); ok { - r0 = rf(uuidOrName) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockBackend_DisconnectVPN_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DisconnectVPN' -type MockBackend_DisconnectVPN_Call struct { - *mock.Call -} - -// DisconnectVPN is a helper method to define mock.On call -// - uuidOrName string -func (_e *MockBackend_Expecter) DisconnectVPN(uuidOrName interface{}) *MockBackend_DisconnectVPN_Call { - return &MockBackend_DisconnectVPN_Call{Call: _e.mock.On("DisconnectVPN", uuidOrName)} -} - -func (_c *MockBackend_DisconnectVPN_Call) Run(run func(uuidOrName string)) *MockBackend_DisconnectVPN_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *MockBackend_DisconnectVPN_Call) Return(_a0 error) *MockBackend_DisconnectVPN_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBackend_DisconnectVPN_Call) RunAndReturn(run func(string) error) *MockBackend_DisconnectVPN_Call { - _c.Call.Return(run) - return _c -} - -// DisconnectWiFi provides a mock function with no fields -func (_m *MockBackend) DisconnectWiFi() error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for DisconnectWiFi") - } - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockBackend_DisconnectWiFi_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DisconnectWiFi' -type MockBackend_DisconnectWiFi_Call struct { - *mock.Call -} - -// DisconnectWiFi is a helper method to define mock.On call -func (_e *MockBackend_Expecter) DisconnectWiFi() *MockBackend_DisconnectWiFi_Call { - return &MockBackend_DisconnectWiFi_Call{Call: _e.mock.On("DisconnectWiFi")} -} - -func (_c *MockBackend_DisconnectWiFi_Call) Run(run func()) *MockBackend_DisconnectWiFi_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockBackend_DisconnectWiFi_Call) Return(_a0 error) *MockBackend_DisconnectWiFi_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBackend_DisconnectWiFi_Call) RunAndReturn(run func() error) *MockBackend_DisconnectWiFi_Call { - _c.Call.Return(run) - return _c -} - -// ForgetWiFiNetwork provides a mock function with given fields: ssid -func (_m *MockBackend) ForgetWiFiNetwork(ssid string) error { - ret := _m.Called(ssid) - - if len(ret) == 0 { - panic("no return value specified for ForgetWiFiNetwork") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string) error); ok { - r0 = rf(ssid) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockBackend_ForgetWiFiNetwork_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ForgetWiFiNetwork' -type MockBackend_ForgetWiFiNetwork_Call struct { - *mock.Call -} - -// ForgetWiFiNetwork is a helper method to define mock.On call -// - ssid string -func (_e *MockBackend_Expecter) ForgetWiFiNetwork(ssid interface{}) *MockBackend_ForgetWiFiNetwork_Call { - return &MockBackend_ForgetWiFiNetwork_Call{Call: _e.mock.On("ForgetWiFiNetwork", ssid)} -} - -func (_c *MockBackend_ForgetWiFiNetwork_Call) Run(run func(ssid string)) *MockBackend_ForgetWiFiNetwork_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *MockBackend_ForgetWiFiNetwork_Call) Return(_a0 error) *MockBackend_ForgetWiFiNetwork_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBackend_ForgetWiFiNetwork_Call) RunAndReturn(run func(string) error) *MockBackend_ForgetWiFiNetwork_Call { - _c.Call.Return(run) - return _c -} - -// GetCurrentState provides a mock function with no fields -func (_m *MockBackend) GetCurrentState() (*network.BackendState, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetCurrentState") - } - - var r0 *network.BackendState - var r1 error - if rf, ok := ret.Get(0).(func() (*network.BackendState, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() *network.BackendState); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*network.BackendState) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockBackend_GetCurrentState_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetCurrentState' -type MockBackend_GetCurrentState_Call struct { - *mock.Call -} - -// GetCurrentState is a helper method to define mock.On call -func (_e *MockBackend_Expecter) GetCurrentState() *MockBackend_GetCurrentState_Call { - return &MockBackend_GetCurrentState_Call{Call: _e.mock.On("GetCurrentState")} -} - -func (_c *MockBackend_GetCurrentState_Call) Run(run func()) *MockBackend_GetCurrentState_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockBackend_GetCurrentState_Call) Return(_a0 *network.BackendState, _a1 error) *MockBackend_GetCurrentState_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockBackend_GetCurrentState_Call) RunAndReturn(run func() (*network.BackendState, error)) *MockBackend_GetCurrentState_Call { - _c.Call.Return(run) - return _c -} - -// GetPromptBroker provides a mock function with no fields -func (_m *MockBackend) GetPromptBroker() network.PromptBroker { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPromptBroker") - } - - var r0 network.PromptBroker - if rf, ok := ret.Get(0).(func() network.PromptBroker); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(network.PromptBroker) - } - } - - return r0 -} - -// MockBackend_GetPromptBroker_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPromptBroker' -type MockBackend_GetPromptBroker_Call struct { - *mock.Call -} - -// GetPromptBroker is a helper method to define mock.On call -func (_e *MockBackend_Expecter) GetPromptBroker() *MockBackend_GetPromptBroker_Call { - return &MockBackend_GetPromptBroker_Call{Call: _e.mock.On("GetPromptBroker")} -} - -func (_c *MockBackend_GetPromptBroker_Call) Run(run func()) *MockBackend_GetPromptBroker_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockBackend_GetPromptBroker_Call) Return(_a0 network.PromptBroker) *MockBackend_GetPromptBroker_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBackend_GetPromptBroker_Call) RunAndReturn(run func() network.PromptBroker) *MockBackend_GetPromptBroker_Call { - _c.Call.Return(run) - return _c -} - -// GetWiFiEnabled provides a mock function with no fields -func (_m *MockBackend) GetWiFiEnabled() (bool, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetWiFiEnabled") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func() (bool, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockBackend_GetWiFiEnabled_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetWiFiEnabled' -type MockBackend_GetWiFiEnabled_Call struct { - *mock.Call -} - -// GetWiFiEnabled is a helper method to define mock.On call -func (_e *MockBackend_Expecter) GetWiFiEnabled() *MockBackend_GetWiFiEnabled_Call { - return &MockBackend_GetWiFiEnabled_Call{Call: _e.mock.On("GetWiFiEnabled")} -} - -func (_c *MockBackend_GetWiFiEnabled_Call) Run(run func()) *MockBackend_GetWiFiEnabled_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockBackend_GetWiFiEnabled_Call) Return(_a0 bool, _a1 error) *MockBackend_GetWiFiEnabled_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockBackend_GetWiFiEnabled_Call) RunAndReturn(run func() (bool, error)) *MockBackend_GetWiFiEnabled_Call { - _c.Call.Return(run) - return _c -} - -// GetWiFiNetworkDetails provides a mock function with given fields: ssid -func (_m *MockBackend) GetWiFiNetworkDetails(ssid string) (*network.NetworkInfoResponse, error) { - ret := _m.Called(ssid) - - if len(ret) == 0 { - panic("no return value specified for GetWiFiNetworkDetails") - } - - var r0 *network.NetworkInfoResponse - var r1 error - if rf, ok := ret.Get(0).(func(string) (*network.NetworkInfoResponse, error)); ok { - return rf(ssid) - } - if rf, ok := ret.Get(0).(func(string) *network.NetworkInfoResponse); ok { - r0 = rf(ssid) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*network.NetworkInfoResponse) - } - } - - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(ssid) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockBackend_GetWiFiNetworkDetails_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetWiFiNetworkDetails' -type MockBackend_GetWiFiNetworkDetails_Call struct { - *mock.Call -} - -// GetWiFiNetworkDetails is a helper method to define mock.On call -// - ssid string -func (_e *MockBackend_Expecter) GetWiFiNetworkDetails(ssid interface{}) *MockBackend_GetWiFiNetworkDetails_Call { - return &MockBackend_GetWiFiNetworkDetails_Call{Call: _e.mock.On("GetWiFiNetworkDetails", ssid)} -} - -func (_c *MockBackend_GetWiFiNetworkDetails_Call) Run(run func(ssid string)) *MockBackend_GetWiFiNetworkDetails_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *MockBackend_GetWiFiNetworkDetails_Call) Return(_a0 *network.NetworkInfoResponse, _a1 error) *MockBackend_GetWiFiNetworkDetails_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockBackend_GetWiFiNetworkDetails_Call) RunAndReturn(run func(string) (*network.NetworkInfoResponse, error)) *MockBackend_GetWiFiNetworkDetails_Call { - _c.Call.Return(run) - return _c -} - -// GetWiredConnections provides a mock function with no fields -func (_m *MockBackend) GetWiredConnections() ([]network.WiredConnection, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetWiredConnections") - } - - var r0 []network.WiredConnection - var r1 error - if rf, ok := ret.Get(0).(func() ([]network.WiredConnection, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []network.WiredConnection); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]network.WiredConnection) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockBackend_GetWiredConnections_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetWiredConnections' -type MockBackend_GetWiredConnections_Call struct { - *mock.Call -} - -// GetWiredConnections is a helper method to define mock.On call -func (_e *MockBackend_Expecter) GetWiredConnections() *MockBackend_GetWiredConnections_Call { - return &MockBackend_GetWiredConnections_Call{Call: _e.mock.On("GetWiredConnections")} -} - -func (_c *MockBackend_GetWiredConnections_Call) Run(run func()) *MockBackend_GetWiredConnections_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockBackend_GetWiredConnections_Call) Return(_a0 []network.WiredConnection, _a1 error) *MockBackend_GetWiredConnections_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockBackend_GetWiredConnections_Call) RunAndReturn(run func() ([]network.WiredConnection, error)) *MockBackend_GetWiredConnections_Call { - _c.Call.Return(run) - return _c -} - -// GetWiredNetworkDetails provides a mock function with given fields: uuid -func (_m *MockBackend) GetWiredNetworkDetails(uuid string) (*network.WiredNetworkInfoResponse, error) { - ret := _m.Called(uuid) - - if len(ret) == 0 { - panic("no return value specified for GetWiredNetworkDetails") - } - - var r0 *network.WiredNetworkInfoResponse - var r1 error - if rf, ok := ret.Get(0).(func(string) (*network.WiredNetworkInfoResponse, error)); ok { - return rf(uuid) - } - if rf, ok := ret.Get(0).(func(string) *network.WiredNetworkInfoResponse); ok { - r0 = rf(uuid) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*network.WiredNetworkInfoResponse) - } - } - - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(uuid) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockBackend_GetWiredNetworkDetails_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetWiredNetworkDetails' -type MockBackend_GetWiredNetworkDetails_Call struct { - *mock.Call -} - -// GetWiredNetworkDetails is a helper method to define mock.On call -// - uuid string -func (_e *MockBackend_Expecter) GetWiredNetworkDetails(uuid interface{}) *MockBackend_GetWiredNetworkDetails_Call { - return &MockBackend_GetWiredNetworkDetails_Call{Call: _e.mock.On("GetWiredNetworkDetails", uuid)} -} - -func (_c *MockBackend_GetWiredNetworkDetails_Call) Run(run func(uuid string)) *MockBackend_GetWiredNetworkDetails_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *MockBackend_GetWiredNetworkDetails_Call) Return(_a0 *network.WiredNetworkInfoResponse, _a1 error) *MockBackend_GetWiredNetworkDetails_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockBackend_GetWiredNetworkDetails_Call) RunAndReturn(run func(string) (*network.WiredNetworkInfoResponse, error)) *MockBackend_GetWiredNetworkDetails_Call { - _c.Call.Return(run) - return _c -} - -// Initialize provides a mock function with no fields -func (_m *MockBackend) Initialize() error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Initialize") - } - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockBackend_Initialize_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Initialize' -type MockBackend_Initialize_Call struct { - *mock.Call -} - -// Initialize is a helper method to define mock.On call -func (_e *MockBackend_Expecter) Initialize() *MockBackend_Initialize_Call { - return &MockBackend_Initialize_Call{Call: _e.mock.On("Initialize")} -} - -func (_c *MockBackend_Initialize_Call) Run(run func()) *MockBackend_Initialize_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockBackend_Initialize_Call) Return(_a0 error) *MockBackend_Initialize_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBackend_Initialize_Call) RunAndReturn(run func() error) *MockBackend_Initialize_Call { - _c.Call.Return(run) - return _c -} - -// ListActiveVPN provides a mock function with no fields -func (_m *MockBackend) ListActiveVPN() ([]network.VPNActive, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for ListActiveVPN") - } - - var r0 []network.VPNActive - var r1 error - if rf, ok := ret.Get(0).(func() ([]network.VPNActive, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []network.VPNActive); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]network.VPNActive) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockBackend_ListActiveVPN_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListActiveVPN' -type MockBackend_ListActiveVPN_Call struct { - *mock.Call -} - -// ListActiveVPN is a helper method to define mock.On call -func (_e *MockBackend_Expecter) ListActiveVPN() *MockBackend_ListActiveVPN_Call { - return &MockBackend_ListActiveVPN_Call{Call: _e.mock.On("ListActiveVPN")} -} - -func (_c *MockBackend_ListActiveVPN_Call) Run(run func()) *MockBackend_ListActiveVPN_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockBackend_ListActiveVPN_Call) Return(_a0 []network.VPNActive, _a1 error) *MockBackend_ListActiveVPN_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockBackend_ListActiveVPN_Call) RunAndReturn(run func() ([]network.VPNActive, error)) *MockBackend_ListActiveVPN_Call { - _c.Call.Return(run) - return _c -} - -// ListVPNProfiles provides a mock function with no fields -func (_m *MockBackend) ListVPNProfiles() ([]network.VPNProfile, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for ListVPNProfiles") - } - - var r0 []network.VPNProfile - var r1 error - if rf, ok := ret.Get(0).(func() ([]network.VPNProfile, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []network.VPNProfile); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]network.VPNProfile) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockBackend_ListVPNProfiles_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListVPNProfiles' -type MockBackend_ListVPNProfiles_Call struct { - *mock.Call -} - -// ListVPNProfiles is a helper method to define mock.On call -func (_e *MockBackend_Expecter) ListVPNProfiles() *MockBackend_ListVPNProfiles_Call { - return &MockBackend_ListVPNProfiles_Call{Call: _e.mock.On("ListVPNProfiles")} -} - -func (_c *MockBackend_ListVPNProfiles_Call) Run(run func()) *MockBackend_ListVPNProfiles_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockBackend_ListVPNProfiles_Call) Return(_a0 []network.VPNProfile, _a1 error) *MockBackend_ListVPNProfiles_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockBackend_ListVPNProfiles_Call) RunAndReturn(run func() ([]network.VPNProfile, error)) *MockBackend_ListVPNProfiles_Call { - _c.Call.Return(run) - return _c -} - -// ScanWiFi provides a mock function with no fields -func (_m *MockBackend) ScanWiFi() error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for ScanWiFi") - } - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockBackend_ScanWiFi_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ScanWiFi' -type MockBackend_ScanWiFi_Call struct { - *mock.Call -} - -// ScanWiFi is a helper method to define mock.On call -func (_e *MockBackend_Expecter) ScanWiFi() *MockBackend_ScanWiFi_Call { - return &MockBackend_ScanWiFi_Call{Call: _e.mock.On("ScanWiFi")} -} - -func (_c *MockBackend_ScanWiFi_Call) Run(run func()) *MockBackend_ScanWiFi_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockBackend_ScanWiFi_Call) Return(_a0 error) *MockBackend_ScanWiFi_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBackend_ScanWiFi_Call) RunAndReturn(run func() error) *MockBackend_ScanWiFi_Call { - _c.Call.Return(run) - return _c -} - -// SetPromptBroker provides a mock function with given fields: broker -func (_m *MockBackend) SetPromptBroker(broker network.PromptBroker) error { - ret := _m.Called(broker) - - if len(ret) == 0 { - panic("no return value specified for SetPromptBroker") - } - - var r0 error - if rf, ok := ret.Get(0).(func(network.PromptBroker) error); ok { - r0 = rf(broker) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockBackend_SetPromptBroker_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetPromptBroker' -type MockBackend_SetPromptBroker_Call struct { - *mock.Call -} - -// SetPromptBroker is a helper method to define mock.On call -// - broker network.PromptBroker -func (_e *MockBackend_Expecter) SetPromptBroker(broker interface{}) *MockBackend_SetPromptBroker_Call { - return &MockBackend_SetPromptBroker_Call{Call: _e.mock.On("SetPromptBroker", broker)} -} - -func (_c *MockBackend_SetPromptBroker_Call) Run(run func(broker network.PromptBroker)) *MockBackend_SetPromptBroker_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(network.PromptBroker)) - }) - return _c -} - -func (_c *MockBackend_SetPromptBroker_Call) Return(_a0 error) *MockBackend_SetPromptBroker_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBackend_SetPromptBroker_Call) RunAndReturn(run func(network.PromptBroker) error) *MockBackend_SetPromptBroker_Call { - _c.Call.Return(run) - return _c -} - -// SetWiFiAutoconnect provides a mock function with given fields: ssid, autoconnect -func (_m *MockBackend) SetWiFiAutoconnect(ssid string, autoconnect bool) error { - ret := _m.Called(ssid, autoconnect) - - if len(ret) == 0 { - panic("no return value specified for SetWiFiAutoconnect") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string, bool) error); ok { - r0 = rf(ssid, autoconnect) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockBackend_SetWiFiAutoconnect_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetWiFiAutoconnect' -type MockBackend_SetWiFiAutoconnect_Call struct { - *mock.Call -} - -// SetWiFiAutoconnect is a helper method to define mock.On call -// - ssid string -// - autoconnect bool -func (_e *MockBackend_Expecter) SetWiFiAutoconnect(ssid interface{}, autoconnect interface{}) *MockBackend_SetWiFiAutoconnect_Call { - return &MockBackend_SetWiFiAutoconnect_Call{Call: _e.mock.On("SetWiFiAutoconnect", ssid, autoconnect)} -} - -func (_c *MockBackend_SetWiFiAutoconnect_Call) Run(run func(ssid string, autoconnect bool)) *MockBackend_SetWiFiAutoconnect_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(bool)) - }) - return _c -} - -func (_c *MockBackend_SetWiFiAutoconnect_Call) Return(_a0 error) *MockBackend_SetWiFiAutoconnect_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBackend_SetWiFiAutoconnect_Call) RunAndReturn(run func(string, bool) error) *MockBackend_SetWiFiAutoconnect_Call { - _c.Call.Return(run) - return _c -} - -// SetWiFiEnabled provides a mock function with given fields: enabled -func (_m *MockBackend) SetWiFiEnabled(enabled bool) error { - ret := _m.Called(enabled) - - if len(ret) == 0 { - panic("no return value specified for SetWiFiEnabled") - } - - var r0 error - if rf, ok := ret.Get(0).(func(bool) error); ok { - r0 = rf(enabled) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockBackend_SetWiFiEnabled_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetWiFiEnabled' -type MockBackend_SetWiFiEnabled_Call struct { - *mock.Call -} - -// SetWiFiEnabled is a helper method to define mock.On call -// - enabled bool -func (_e *MockBackend_Expecter) SetWiFiEnabled(enabled interface{}) *MockBackend_SetWiFiEnabled_Call { - return &MockBackend_SetWiFiEnabled_Call{Call: _e.mock.On("SetWiFiEnabled", enabled)} -} - -func (_c *MockBackend_SetWiFiEnabled_Call) Run(run func(enabled bool)) *MockBackend_SetWiFiEnabled_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(bool)) - }) - return _c -} - -func (_c *MockBackend_SetWiFiEnabled_Call) Return(_a0 error) *MockBackend_SetWiFiEnabled_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBackend_SetWiFiEnabled_Call) RunAndReturn(run func(bool) error) *MockBackend_SetWiFiEnabled_Call { - _c.Call.Return(run) - return _c -} - -// StartMonitoring provides a mock function with given fields: onStateChange -func (_m *MockBackend) StartMonitoring(onStateChange func()) error { - ret := _m.Called(onStateChange) - - if len(ret) == 0 { - panic("no return value specified for StartMonitoring") - } - - var r0 error - if rf, ok := ret.Get(0).(func(func()) error); ok { - r0 = rf(onStateChange) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockBackend_StartMonitoring_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'StartMonitoring' -type MockBackend_StartMonitoring_Call struct { - *mock.Call -} - -// StartMonitoring is a helper method to define mock.On call -// - onStateChange func() -func (_e *MockBackend_Expecter) StartMonitoring(onStateChange interface{}) *MockBackend_StartMonitoring_Call { - return &MockBackend_StartMonitoring_Call{Call: _e.mock.On("StartMonitoring", onStateChange)} -} - -func (_c *MockBackend_StartMonitoring_Call) Run(run func(onStateChange func())) *MockBackend_StartMonitoring_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(func())) - }) - return _c -} - -func (_c *MockBackend_StartMonitoring_Call) Return(_a0 error) *MockBackend_StartMonitoring_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBackend_StartMonitoring_Call) RunAndReturn(run func(func()) error) *MockBackend_StartMonitoring_Call { - _c.Call.Return(run) - return _c -} - -// StopMonitoring provides a mock function with no fields -func (_m *MockBackend) StopMonitoring() { - _m.Called() -} - -// MockBackend_StopMonitoring_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'StopMonitoring' -type MockBackend_StopMonitoring_Call struct { - *mock.Call -} - -// StopMonitoring is a helper method to define mock.On call -func (_e *MockBackend_Expecter) StopMonitoring() *MockBackend_StopMonitoring_Call { - return &MockBackend_StopMonitoring_Call{Call: _e.mock.On("StopMonitoring")} -} - -func (_c *MockBackend_StopMonitoring_Call) Run(run func()) *MockBackend_StopMonitoring_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockBackend_StopMonitoring_Call) Return() *MockBackend_StopMonitoring_Call { - _c.Call.Return() - return _c -} - -func (_c *MockBackend_StopMonitoring_Call) RunAndReturn(run func()) *MockBackend_StopMonitoring_Call { - _c.Run(run) - return _c -} - -// SubmitCredentials provides a mock function with given fields: token, secrets, save -func (_m *MockBackend) SubmitCredentials(token string, secrets map[string]string, save bool) error { - ret := _m.Called(token, secrets, save) - - if len(ret) == 0 { - panic("no return value specified for SubmitCredentials") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string, map[string]string, bool) error); ok { - r0 = rf(token, secrets, save) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockBackend_SubmitCredentials_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SubmitCredentials' -type MockBackend_SubmitCredentials_Call struct { - *mock.Call -} - -// SubmitCredentials is a helper method to define mock.On call -// - token string -// - secrets map[string]string -// - save bool -func (_e *MockBackend_Expecter) SubmitCredentials(token interface{}, secrets interface{}, save interface{}) *MockBackend_SubmitCredentials_Call { - return &MockBackend_SubmitCredentials_Call{Call: _e.mock.On("SubmitCredentials", token, secrets, save)} -} - -func (_c *MockBackend_SubmitCredentials_Call) Run(run func(token string, secrets map[string]string, save bool)) *MockBackend_SubmitCredentials_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(map[string]string), args[2].(bool)) - }) - return _c -} - -func (_c *MockBackend_SubmitCredentials_Call) Return(_a0 error) *MockBackend_SubmitCredentials_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBackend_SubmitCredentials_Call) RunAndReturn(run func(string, map[string]string, bool) error) *MockBackend_SubmitCredentials_Call { - _c.Call.Return(run) - return _c -} - -// NewMockBackend creates a new instance of MockBackend. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewMockBackend(t interface { - mock.TestingT - Cleanup(func()) -}) *MockBackend { - mock := &MockBackend{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/nix/inputs/dms-cli/internal/plugins/manager.go b/nix/inputs/dms-cli/internal/plugins/manager.go deleted file mode 100644 index 0c277eb..0000000 --- a/nix/inputs/dms-cli/internal/plugins/manager.go +++ /dev/null @@ -1,430 +0,0 @@ -package plugins - -import ( - "crypto/sha256" - "encoding/hex" - "encoding/json" - "fmt" - "os" - "path/filepath" - "strings" - - "github.com/spf13/afero" -) - -type Manager struct { - fs afero.Fs - pluginsDir string - gitClient GitClient -} - -func NewManager() (*Manager, error) { - return NewManagerWithFs(afero.NewOsFs()) -} - -func NewManagerWithFs(fs afero.Fs) (*Manager, error) { - pluginsDir := getPluginsDir() - return &Manager{ - fs: fs, - pluginsDir: pluginsDir, - gitClient: &realGitClient{}, - }, nil -} - -func getPluginsDir() string { - configHome := os.Getenv("XDG_CONFIG_HOME") - if configHome == "" { - homeDir, err := os.UserHomeDir() - if err != nil { - return filepath.Join(os.TempDir(), "DankMaterialShell", "plugins") - } - configHome = filepath.Join(homeDir, ".config") - } - return filepath.Join(configHome, "DankMaterialShell", "plugins") -} - -func (m *Manager) IsInstalled(plugin Plugin) (bool, error) { - pluginPath := filepath.Join(m.pluginsDir, plugin.ID) - exists, err := afero.DirExists(m.fs, pluginPath) - if err != nil { - return false, err - } - if exists { - return true, nil - } - - systemPluginPath := filepath.Join("/etc/xdg/quickshell/dms-plugins", plugin.ID) - systemExists, err := afero.DirExists(m.fs, systemPluginPath) - if err != nil { - return false, err - } - return systemExists, nil -} - -func (m *Manager) Install(plugin Plugin) error { - pluginPath := filepath.Join(m.pluginsDir, plugin.ID) - - exists, err := afero.DirExists(m.fs, pluginPath) - if err != nil { - return fmt.Errorf("failed to check if plugin exists: %w", err) - } - - if exists { - return fmt.Errorf("plugin already installed: %s", plugin.Name) - } - - if err := m.fs.MkdirAll(m.pluginsDir, 0755); err != nil { - return fmt.Errorf("failed to create plugins directory: %w", err) - } - - reposDir := filepath.Join(m.pluginsDir, ".repos") - if err := m.fs.MkdirAll(reposDir, 0755); err != nil { - return fmt.Errorf("failed to create repos directory: %w", err) - } - - if plugin.Path != "" { - repoName := m.getRepoName(plugin.Repo) - repoPath := filepath.Join(reposDir, repoName) - - repoExists, err := afero.DirExists(m.fs, repoPath) - if err != nil { - return fmt.Errorf("failed to check if repo exists: %w", err) - } - - if !repoExists { - if err := m.gitClient.PlainClone(repoPath, plugin.Repo); err != nil { - m.fs.RemoveAll(repoPath) - return fmt.Errorf("failed to clone repository: %w", err) - } - } else { - // Pull latest changes if repo already exists - if err := m.gitClient.Pull(repoPath); err != nil { - // If pull fails (e.g., corrupted shallow clone), delete and re-clone - if err := m.fs.RemoveAll(repoPath); err != nil { - return fmt.Errorf("failed to remove corrupted repository: %w", err) - } - - if err := m.gitClient.PlainClone(repoPath, plugin.Repo); err != nil { - return fmt.Errorf("failed to re-clone repository: %w", err) - } - } - } - - sourcePath := filepath.Join(repoPath, plugin.Path) - sourceExists, err := afero.DirExists(m.fs, sourcePath) - if err != nil { - return fmt.Errorf("failed to check plugin path: %w", err) - } - if !sourceExists { - return fmt.Errorf("plugin path does not exist in repository: %s", plugin.Path) - } - - if err := m.createSymlink(sourcePath, pluginPath); err != nil { - return fmt.Errorf("failed to create symlink: %w", err) - } - - metaPath := pluginPath + ".meta" - metaContent := fmt.Sprintf("repo=%s\npath=%s\nrepodir=%s", plugin.Repo, plugin.Path, repoName) - if err := afero.WriteFile(m.fs, metaPath, []byte(metaContent), 0644); err != nil { - return fmt.Errorf("failed to write metadata: %w", err) - } - } else { - if err := m.gitClient.PlainClone(pluginPath, plugin.Repo); err != nil { - m.fs.RemoveAll(pluginPath) - return fmt.Errorf("failed to clone plugin: %w", err) - } - } - - return nil -} - -func (m *Manager) getRepoName(repoURL string) string { - hash := sha256.Sum256([]byte(repoURL)) - return hex.EncodeToString(hash[:])[:16] -} - -func (m *Manager) createSymlink(source, dest string) error { - if symlinkFs, ok := m.fs.(afero.Symlinker); ok { - return symlinkFs.SymlinkIfPossible(source, dest) - } - return os.Symlink(source, dest) -} - -func (m *Manager) Update(plugin Plugin) error { - pluginPath := filepath.Join(m.pluginsDir, plugin.ID) - - exists, err := afero.DirExists(m.fs, pluginPath) - if err != nil { - return fmt.Errorf("failed to check if plugin exists: %w", err) - } - - if !exists { - systemPluginPath := filepath.Join("/etc/xdg/quickshell/dms-plugins", plugin.ID) - systemExists, err := afero.DirExists(m.fs, systemPluginPath) - if err != nil { - return fmt.Errorf("failed to check if plugin exists: %w", err) - } - if systemExists { - return fmt.Errorf("cannot update system plugin: %s", plugin.Name) - } - return fmt.Errorf("plugin not installed: %s", plugin.Name) - } - - metaPath := pluginPath + ".meta" - metaExists, err := afero.Exists(m.fs, metaPath) - if err != nil { - return fmt.Errorf("failed to check metadata: %w", err) - } - - if metaExists { - reposDir := filepath.Join(m.pluginsDir, ".repos") - repoName := m.getRepoName(plugin.Repo) - repoPath := filepath.Join(reposDir, repoName) - - // Try to pull, if it fails (e.g., shallow clone corruption), delete and re-clone - if err := m.gitClient.Pull(repoPath); err != nil { - // Repository is likely corrupted or has issues, delete and re-clone - if err := m.fs.RemoveAll(repoPath); err != nil { - return fmt.Errorf("failed to remove corrupted repository: %w", err) - } - - if err := m.gitClient.PlainClone(repoPath, plugin.Repo); err != nil { - return fmt.Errorf("failed to re-clone repository: %w", err) - } - } - } else { - // Try to pull, if it fails, delete and re-clone - if err := m.gitClient.Pull(pluginPath); err != nil { - if err := m.fs.RemoveAll(pluginPath); err != nil { - return fmt.Errorf("failed to remove corrupted plugin: %w", err) - } - - if err := m.gitClient.PlainClone(pluginPath, plugin.Repo); err != nil { - return fmt.Errorf("failed to re-clone plugin: %w", err) - } - } - } - - return nil -} - -func (m *Manager) Uninstall(plugin Plugin) error { - pluginPath := filepath.Join(m.pluginsDir, plugin.ID) - - exists, err := afero.DirExists(m.fs, pluginPath) - if err != nil { - return fmt.Errorf("failed to check if plugin exists: %w", err) - } - - if !exists { - systemPluginPath := filepath.Join("/etc/xdg/quickshell/dms-plugins", plugin.ID) - systemExists, err := afero.DirExists(m.fs, systemPluginPath) - if err != nil { - return fmt.Errorf("failed to check if plugin exists: %w", err) - } - if systemExists { - return fmt.Errorf("cannot uninstall system plugin: %s", plugin.Name) - } - return fmt.Errorf("plugin not installed: %s", plugin.Name) - } - - metaPath := pluginPath + ".meta" - metaExists, err := afero.Exists(m.fs, metaPath) - if err != nil { - return fmt.Errorf("failed to check metadata: %w", err) - } - - if metaExists { - reposDir := filepath.Join(m.pluginsDir, ".repos") - repoName := m.getRepoName(plugin.Repo) - repoPath := filepath.Join(reposDir, repoName) - - shouldCleanup, err := m.shouldCleanupRepo(repoPath, plugin.Repo, plugin.ID) - if err != nil { - return fmt.Errorf("failed to check repo cleanup: %w", err) - } - - if err := m.fs.Remove(pluginPath); err != nil { - return fmt.Errorf("failed to remove symlink: %w", err) - } - - if err := m.fs.Remove(metaPath); err != nil { - return fmt.Errorf("failed to remove metadata: %w", err) - } - - if shouldCleanup { - if err := m.fs.RemoveAll(repoPath); err != nil { - return fmt.Errorf("failed to cleanup repository: %w", err) - } - } - } else { - if err := m.fs.RemoveAll(pluginPath); err != nil { - return fmt.Errorf("failed to remove plugin: %w", err) - } - } - - return nil -} - -func (m *Manager) shouldCleanupRepo(repoPath, repoURL, excludePlugin string) (bool, error) { - installed, err := m.ListInstalled() - if err != nil { - return false, err - } - - registry, err := NewRegistry() - if err != nil { - return false, err - } - - allPlugins, err := registry.List() - if err != nil { - return false, err - } - - for _, id := range installed { - if id == excludePlugin { - continue - } - - for _, p := range allPlugins { - if p.ID == id && p.Repo == repoURL && p.Path != "" { - return false, nil - } - } - } - - return true, nil -} - -func (m *Manager) ListInstalled() ([]string, error) { - installedMap := make(map[string]bool) - - exists, err := afero.DirExists(m.fs, m.pluginsDir) - if err != nil { - return nil, err - } - - if exists { - entries, err := afero.ReadDir(m.fs, m.pluginsDir) - if err != nil { - return nil, fmt.Errorf("failed to read plugins directory: %w", err) - } - - for _, entry := range entries { - name := entry.Name() - if name == ".repos" || strings.HasSuffix(name, ".meta") { - continue - } - - fullPath := filepath.Join(m.pluginsDir, name) - isPlugin := false - - if entry.IsDir() { - isPlugin = true - } else if entry.Mode()&os.ModeSymlink != 0 { - isPlugin = true - } else { - info, err := m.fs.Stat(fullPath) - if err == nil && info.IsDir() { - isPlugin = true - } - } - - if isPlugin { - // Read plugin.json to get the actual plugin ID - pluginID := m.getPluginID(fullPath) - if pluginID != "" { - installedMap[pluginID] = true - } - } - } - } - - systemPluginsDir := "/etc/xdg/quickshell/dms-plugins" - systemExists, err := afero.DirExists(m.fs, systemPluginsDir) - if err == nil && systemExists { - entries, err := afero.ReadDir(m.fs, systemPluginsDir) - if err == nil { - for _, entry := range entries { - if entry.IsDir() { - fullPath := filepath.Join(systemPluginsDir, entry.Name()) - // Read plugin.json to get the actual plugin ID - pluginID := m.getPluginID(fullPath) - if pluginID != "" { - installedMap[pluginID] = true - } - } - } - } - } - - var installed []string - for name := range installedMap { - installed = append(installed, name) - } - - return installed, nil -} - -// getPluginID reads the plugin.json file and returns the plugin ID -func (m *Manager) getPluginID(pluginPath string) string { - manifestPath := filepath.Join(pluginPath, "plugin.json") - data, err := afero.ReadFile(m.fs, manifestPath) - if err != nil { - return "" - } - - var manifest struct { - ID string `json:"id"` - } - if err := json.Unmarshal(data, &manifest); err != nil { - return "" - } - - return manifest.ID -} - -func (m *Manager) GetPluginsDir() string { - return m.pluginsDir -} - -func (m *Manager) HasUpdates(pluginID string, plugin Plugin) (bool, error) { - pluginPath := filepath.Join(m.pluginsDir, pluginID) - - exists, err := afero.DirExists(m.fs, pluginPath) - if err != nil { - return false, fmt.Errorf("failed to check if plugin exists: %w", err) - } - - if !exists { - systemPluginPath := filepath.Join("/etc/xdg/quickshell/dms-plugins", pluginID) - systemExists, err := afero.DirExists(m.fs, systemPluginPath) - if err != nil { - return false, fmt.Errorf("failed to check system plugin: %w", err) - } - if systemExists { - return false, nil - } - return false, fmt.Errorf("plugin not installed: %s", pluginID) - } - - // Check if there's a .meta file (plugin installed from a monorepo) - metaPath := pluginPath + ".meta" - metaExists, err := afero.Exists(m.fs, metaPath) - if err != nil { - return false, fmt.Errorf("failed to check metadata: %w", err) - } - - if metaExists { - // Plugin is from a monorepo, check the repo directory - reposDir := filepath.Join(m.pluginsDir, ".repos") - repoName := m.getRepoName(plugin.Repo) - repoPath := filepath.Join(reposDir, repoName) - - return m.gitClient.HasUpdates(repoPath) - } - - // Plugin is a standalone repo - return m.gitClient.HasUpdates(pluginPath) -} diff --git a/nix/inputs/dms-cli/internal/plugins/manager_test.go b/nix/inputs/dms-cli/internal/plugins/manager_test.go deleted file mode 100644 index 5dd9d01..0000000 --- a/nix/inputs/dms-cli/internal/plugins/manager_test.go +++ /dev/null @@ -1,247 +0,0 @@ -package plugins - -import ( - "os" - "path/filepath" - "testing" - - "github.com/spf13/afero" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func setupTestManager(t *testing.T) (*Manager, afero.Fs, string) { - fs := afero.NewMemMapFs() - pluginsDir := "/test-plugins" - manager := &Manager{ - fs: fs, - pluginsDir: pluginsDir, - gitClient: &mockGitClient{}, - } - return manager, fs, pluginsDir -} - -func TestNewManager(t *testing.T) { - manager, err := NewManager() - assert.NoError(t, err) - assert.NotNil(t, manager) - assert.NotEmpty(t, manager.pluginsDir) -} - -func TestGetPluginsDir(t *testing.T) { - t.Run("uses XDG_CONFIG_HOME when set", func(t *testing.T) { - oldConfig := os.Getenv("XDG_CONFIG_HOME") - defer func() { - if oldConfig != "" { - os.Setenv("XDG_CONFIG_HOME", oldConfig) - } else { - os.Unsetenv("XDG_CONFIG_HOME") - } - }() - - os.Setenv("XDG_CONFIG_HOME", "/tmp/test-config") - dir := getPluginsDir() - assert.Equal(t, "/tmp/test-config/DankMaterialShell/plugins", dir) - }) - - t.Run("falls back to home directory", func(t *testing.T) { - oldConfig := os.Getenv("XDG_CONFIG_HOME") - defer func() { - if oldConfig != "" { - os.Setenv("XDG_CONFIG_HOME", oldConfig) - } else { - os.Unsetenv("XDG_CONFIG_HOME") - } - }() - - os.Unsetenv("XDG_CONFIG_HOME") - dir := getPluginsDir() - assert.Contains(t, dir, ".config/DankMaterialShell/plugins") - }) -} - -func TestIsInstalled(t *testing.T) { - t.Run("returns true when plugin is installed", func(t *testing.T) { - manager, fs, pluginsDir := setupTestManager(t) - - plugin := Plugin{ID: "test-plugin", Name: "TestPlugin"} - pluginPath := filepath.Join(pluginsDir, plugin.ID) - err := fs.MkdirAll(pluginPath, 0755) - require.NoError(t, err) - - installed, err := manager.IsInstalled(plugin) - assert.NoError(t, err) - assert.True(t, installed) - }) - - t.Run("returns false when plugin is not installed", func(t *testing.T) { - manager, _, _ := setupTestManager(t) - - plugin := Plugin{ID: "non-existent", Name: "NonExistent"} - installed, err := manager.IsInstalled(plugin) - assert.NoError(t, err) - assert.False(t, installed) - }) -} - -func TestInstall(t *testing.T) { - t.Run("installs plugin successfully", func(t *testing.T) { - manager, fs, pluginsDir := setupTestManager(t) - - plugin := Plugin{ - ID: "test-plugin", - Name: "TestPlugin", - Repo: "https://github.com/test/plugin", - } - - cloneCalled := false - mockGit := &mockGitClient{ - cloneFunc: func(path string, url string) error { - cloneCalled = true - assert.Equal(t, filepath.Join(pluginsDir, plugin.ID), path) - assert.Equal(t, plugin.Repo, url) - return fs.MkdirAll(path, 0755) - }, - } - manager.gitClient = mockGit - - err := manager.Install(plugin) - assert.NoError(t, err) - assert.True(t, cloneCalled) - - exists, _ := afero.DirExists(fs, filepath.Join(pluginsDir, plugin.ID)) - assert.True(t, exists) - }) - - t.Run("returns error when plugin already installed", func(t *testing.T) { - manager, fs, pluginsDir := setupTestManager(t) - - plugin := Plugin{ID: "test-plugin", Name: "TestPlugin"} - pluginPath := filepath.Join(pluginsDir, plugin.ID) - err := fs.MkdirAll(pluginPath, 0755) - require.NoError(t, err) - - err = manager.Install(plugin) - assert.Error(t, err) - assert.Contains(t, err.Error(), "already installed") - }) - - t.Run("installs monorepo plugin with symlink", func(t *testing.T) { - t.Skip("Skipping symlink test as MemMapFs doesn't support symlinks") - }) -} - -func TestManagerUpdate(t *testing.T) { - t.Run("updates plugin successfully", func(t *testing.T) { - manager, fs, pluginsDir := setupTestManager(t) - - plugin := Plugin{ID: "test-plugin", Name: "TestPlugin"} - pluginPath := filepath.Join(pluginsDir, plugin.ID) - err := fs.MkdirAll(pluginPath, 0755) - require.NoError(t, err) - - pullCalled := false - mockGit := &mockGitClient{ - pullFunc: func(path string) error { - pullCalled = true - assert.Equal(t, pluginPath, path) - return nil - }, - } - manager.gitClient = mockGit - - err = manager.Update(plugin) - assert.NoError(t, err) - assert.True(t, pullCalled) - }) - - t.Run("returns error when plugin not installed", func(t *testing.T) { - manager, _, _ := setupTestManager(t) - - plugin := Plugin{ID: "non-existent", Name: "NonExistent"} - err := manager.Update(plugin) - assert.Error(t, err) - assert.Contains(t, err.Error(), "not installed") - }) -} - -func TestUninstall(t *testing.T) { - t.Run("uninstalls plugin successfully", func(t *testing.T) { - manager, fs, pluginsDir := setupTestManager(t) - - plugin := Plugin{ID: "test-plugin", Name: "TestPlugin"} - pluginPath := filepath.Join(pluginsDir, plugin.ID) - err := fs.MkdirAll(pluginPath, 0755) - require.NoError(t, err) - - err = manager.Uninstall(plugin) - assert.NoError(t, err) - - exists, _ := afero.DirExists(fs, pluginPath) - assert.False(t, exists) - }) - - t.Run("returns error when plugin not installed", func(t *testing.T) { - manager, _, _ := setupTestManager(t) - - plugin := Plugin{ID: "non-existent", Name: "NonExistent"} - err := manager.Uninstall(plugin) - assert.Error(t, err) - assert.Contains(t, err.Error(), "not installed") - }) -} - -func TestListInstalled(t *testing.T) { - t.Run("lists installed plugins", func(t *testing.T) { - manager, fs, pluginsDir := setupTestManager(t) - - err := fs.MkdirAll(filepath.Join(pluginsDir, "Plugin1"), 0755) - require.NoError(t, err) - err = afero.WriteFile(fs, filepath.Join(pluginsDir, "Plugin1", "plugin.json"), []byte(`{"id":"Plugin1"}`), 0644) - require.NoError(t, err) - - err = fs.MkdirAll(filepath.Join(pluginsDir, "Plugin2"), 0755) - require.NoError(t, err) - err = afero.WriteFile(fs, filepath.Join(pluginsDir, "Plugin2", "plugin.json"), []byte(`{"id":"Plugin2"}`), 0644) - require.NoError(t, err) - - installed, err := manager.ListInstalled() - assert.NoError(t, err) - assert.Len(t, installed, 2) - assert.Contains(t, installed, "Plugin1") - assert.Contains(t, installed, "Plugin2") - }) - - t.Run("returns empty list when no plugins installed", func(t *testing.T) { - manager, _, _ := setupTestManager(t) - - installed, err := manager.ListInstalled() - assert.NoError(t, err) - assert.Empty(t, installed) - }) - - t.Run("ignores files and .repos directory", func(t *testing.T) { - manager, fs, pluginsDir := setupTestManager(t) - - err := fs.MkdirAll(pluginsDir, 0755) - require.NoError(t, err) - err = fs.MkdirAll(filepath.Join(pluginsDir, "Plugin1"), 0755) - require.NoError(t, err) - err = afero.WriteFile(fs, filepath.Join(pluginsDir, "Plugin1", "plugin.json"), []byte(`{"id":"Plugin1"}`), 0644) - require.NoError(t, err) - err = fs.MkdirAll(filepath.Join(pluginsDir, ".repos"), 0755) - require.NoError(t, err) - err = afero.WriteFile(fs, filepath.Join(pluginsDir, "README.md"), []byte("test"), 0644) - require.NoError(t, err) - - installed, err := manager.ListInstalled() - assert.NoError(t, err) - assert.Len(t, installed, 1) - assert.Equal(t, "Plugin1", installed[0]) - }) -} - -func TestManagerGetPluginsDir(t *testing.T) { - manager, _, pluginsDir := setupTestManager(t) - assert.Equal(t, pluginsDir, manager.GetPluginsDir()) -} diff --git a/nix/inputs/dms-cli/internal/plugins/registry.go b/nix/inputs/dms-cli/internal/plugins/registry.go deleted file mode 100644 index f2e626d..0000000 --- a/nix/inputs/dms-cli/internal/plugins/registry.go +++ /dev/null @@ -1,256 +0,0 @@ -package plugins - -import ( - "encoding/json" - "fmt" - "os" - "path/filepath" - "strings" - - "github.com/go-git/go-git/v6" - "github.com/spf13/afero" -) - -const registryRepo = "https://github.com/AvengeMedia/dms-plugin-registry.git" - -type Plugin struct { - ID string `json:"id"` - Name string `json:"name"` - Capabilities []string `json:"capabilities"` - Category string `json:"category"` - Repo string `json:"repo"` - Path string `json:"path,omitempty"` - Author string `json:"author"` - Description string `json:"description"` - Dependencies []string `json:"dependencies,omitempty"` - Compositors []string `json:"compositors"` - Distro []string `json:"distro"` - Screenshot string `json:"screenshot,omitempty"` -} - -type GitClient interface { - PlainClone(path string, url string) error - Pull(path string) error - HasUpdates(path string) (bool, error) -} - -type realGitClient struct{} - -func (g *realGitClient) PlainClone(path string, url string) error { - _, err := git.PlainClone(path, &git.CloneOptions{ - URL: url, - Progress: os.Stdout, - }) - return err -} - -func (g *realGitClient) Pull(path string) error { - repo, err := git.PlainOpen(path) - if err != nil { - return err - } - - worktree, err := repo.Worktree() - if err != nil { - return err - } - - err = worktree.Pull(&git.PullOptions{}) - if err != nil && err.Error() != "already up-to-date" { - return err - } - - return nil -} - -func (g *realGitClient) HasUpdates(path string) (bool, error) { - repo, err := git.PlainOpen(path) - if err != nil { - return false, err - } - - // Fetch remote changes - err = repo.Fetch(&git.FetchOptions{}) - if err != nil && err.Error() != "already up-to-date" { - // If fetch fails, we can't determine if there are updates - // Return false and the error - return false, err - } - - // Get the HEAD reference - head, err := repo.Head() - if err != nil { - return false, err - } - - // Get the remote HEAD reference (typically origin/HEAD or origin/main or origin/master) - remote, err := repo.Remote("origin") - if err != nil { - return false, err - } - - refs, err := remote.List(&git.ListOptions{}) - if err != nil { - return false, err - } - - // Find the default branch remote ref - var remoteHead string - for _, ref := range refs { - if ref.Name().IsBranch() { - // Try common branch names - if ref.Name().Short() == "main" || ref.Name().Short() == "master" { - remoteHead = ref.Hash().String() - break - } - } - } - - // If we couldn't find a remote HEAD, assume no updates - if remoteHead == "" { - return false, nil - } - - // Compare local HEAD with remote HEAD - return head.Hash().String() != remoteHead, nil -} - -type Registry struct { - fs afero.Fs - cacheDir string - plugins []Plugin - git GitClient -} - -func NewRegistry() (*Registry, error) { - return NewRegistryWithFs(afero.NewOsFs()) -} - -func NewRegistryWithFs(fs afero.Fs) (*Registry, error) { - cacheDir := getCacheDir() - return &Registry{ - fs: fs, - cacheDir: cacheDir, - git: &realGitClient{}, - }, nil -} - -func getCacheDir() string { - return filepath.Join(os.TempDir(), "dankdots-plugin-registry") -} - -func (r *Registry) Update() error { - exists, err := afero.DirExists(r.fs, r.cacheDir) - if err != nil { - return fmt.Errorf("failed to check cache directory: %w", err) - } - - if !exists { - if err := r.fs.MkdirAll(filepath.Dir(r.cacheDir), 0755); err != nil { - return fmt.Errorf("failed to create cache directory: %w", err) - } - - if err := r.git.PlainClone(r.cacheDir, registryRepo); err != nil { - return fmt.Errorf("failed to clone registry: %w", err) - } - } else { - // Try to pull, if it fails (e.g., shallow clone corruption), delete and re-clone - if err := r.git.Pull(r.cacheDir); err != nil { - // Repository is likely corrupted or has issues, delete and re-clone - if err := r.fs.RemoveAll(r.cacheDir); err != nil { - return fmt.Errorf("failed to remove corrupted registry: %w", err) - } - - if err := r.fs.MkdirAll(filepath.Dir(r.cacheDir), 0755); err != nil { - return fmt.Errorf("failed to create cache directory: %w", err) - } - - if err := r.git.PlainClone(r.cacheDir, registryRepo); err != nil { - return fmt.Errorf("failed to re-clone registry: %w", err) - } - } - } - - return r.loadPlugins() -} - -func (r *Registry) loadPlugins() error { - pluginsDir := filepath.Join(r.cacheDir, "plugins") - - entries, err := afero.ReadDir(r.fs, pluginsDir) - if err != nil { - return fmt.Errorf("failed to read plugins directory: %w", err) - } - - r.plugins = []Plugin{} - - for _, entry := range entries { - if entry.IsDir() || filepath.Ext(entry.Name()) != ".json" { - continue - } - - data, err := afero.ReadFile(r.fs, filepath.Join(pluginsDir, entry.Name())) - if err != nil { - continue - } - - var plugin Plugin - if err := json.Unmarshal(data, &plugin); err != nil { - continue - } - - if plugin.ID == "" { - plugin.ID = strings.TrimSuffix(entry.Name(), ".json") - } - - r.plugins = append(r.plugins, plugin) - } - - return nil -} - -func (r *Registry) List() ([]Plugin, error) { - if len(r.plugins) == 0 { - if err := r.Update(); err != nil { - return nil, err - } - } - - return SortByFirstParty(r.plugins), nil -} - -func (r *Registry) Search(query string) ([]Plugin, error) { - allPlugins, err := r.List() - if err != nil { - return nil, err - } - - if query == "" { - return allPlugins, nil - } - - return SortByFirstParty(FuzzySearch(query, allPlugins)), nil -} - -func (r *Registry) Get(idOrName string) (*Plugin, error) { - plugins, err := r.List() - if err != nil { - return nil, err - } - - // First, try to find by ID (preferred method) - for _, p := range plugins { - if p.ID == idOrName { - return &p, nil - } - } - - // Fallback to name for backward compatibility - for _, p := range plugins { - if p.Name == idOrName { - return &p, nil - } - } - - return nil, fmt.Errorf("plugin not found: %s", idOrName) -} diff --git a/nix/inputs/dms-cli/internal/plugins/registry_test.go b/nix/inputs/dms-cli/internal/plugins/registry_test.go deleted file mode 100644 index 0abcacb..0000000 --- a/nix/inputs/dms-cli/internal/plugins/registry_test.go +++ /dev/null @@ -1,326 +0,0 @@ -package plugins - -import ( - "encoding/json" - "path/filepath" - "testing" - - "github.com/spf13/afero" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -type mockGitClient struct { - cloneFunc func(path string, url string) error - pullFunc func(path string) error - hasUpdatesFunc func(path string) (bool, error) -} - -func (m *mockGitClient) PlainClone(path string, url string) error { - if m.cloneFunc != nil { - return m.cloneFunc(path, url) - } - return nil -} - -func (m *mockGitClient) Pull(path string) error { - if m.pullFunc != nil { - return m.pullFunc(path) - } - return nil -} - -func (m *mockGitClient) HasUpdates(path string) (bool, error) { - if m.hasUpdatesFunc != nil { - return m.hasUpdatesFunc(path) - } - return false, nil -} - -func TestNewRegistry(t *testing.T) { - registry, err := NewRegistry() - assert.NoError(t, err) - assert.NotNil(t, registry) - assert.NotEmpty(t, registry.cacheDir) -} - -func TestGetCacheDir(t *testing.T) { - cacheDir := getCacheDir() - assert.Contains(t, cacheDir, "/tmp/dankdots-plugin-registry") -} - -func setupTestRegistry(t *testing.T) (*Registry, afero.Fs, string) { - fs := afero.NewMemMapFs() - tmpDir := "/test-cache" - registry := &Registry{ - fs: fs, - cacheDir: tmpDir, - plugins: []Plugin{}, - git: &mockGitClient{}, - } - return registry, fs, tmpDir -} - -func createTestPlugin(t *testing.T, fs afero.Fs, dir string, filename string, plugin Plugin) { - pluginsDir := filepath.Join(dir, "plugins") - err := fs.MkdirAll(pluginsDir, 0755) - require.NoError(t, err) - - data, err := json.Marshal(plugin) - require.NoError(t, err) - - err = afero.WriteFile(fs, filepath.Join(pluginsDir, filename), data, 0644) - require.NoError(t, err) -} - -func TestLoadPlugins(t *testing.T) { - t.Run("loads valid plugin files", func(t *testing.T) { - registry, fs, tmpDir := setupTestRegistry(t) - - plugin1 := Plugin{ - Name: "TestPlugin1", - Capabilities: []string{"dankbar-widget"}, - Category: "monitoring", - Repo: "https://github.com/test/plugin1", - Author: "Test Author", - Description: "Test plugin 1", - Compositors: []string{"niri"}, - Distro: []string{"any"}, - } - - plugin2 := Plugin{ - Name: "TestPlugin2", - Capabilities: []string{"system-tray"}, - Category: "utilities", - Repo: "https://github.com/test/plugin2", - Author: "Another Author", - Description: "Test plugin 2", - Dependencies: []string{"dep1", "dep2"}, - Compositors: []string{"hyprland", "niri"}, - Distro: []string{"arch"}, - Screenshot: "https://example.com/screenshot.png", - } - - createTestPlugin(t, fs, tmpDir, "plugin1.json", plugin1) - createTestPlugin(t, fs, tmpDir, "plugin2.json", plugin2) - - err := registry.loadPlugins() - assert.NoError(t, err) - assert.Len(t, registry.plugins, 2) - - assert.Equal(t, "TestPlugin1", registry.plugins[0].Name) - assert.Equal(t, "TestPlugin2", registry.plugins[1].Name) - assert.Equal(t, []string{"dankbar-widget"}, registry.plugins[0].Capabilities) - assert.Equal(t, []string{"dep1", "dep2"}, registry.plugins[1].Dependencies) - }) - - t.Run("skips non-json files", func(t *testing.T) { - registry, fs, tmpDir := setupTestRegistry(t) - - pluginsDir := filepath.Join(tmpDir, "plugins") - err := fs.MkdirAll(pluginsDir, 0755) - require.NoError(t, err) - - err = afero.WriteFile(fs, filepath.Join(pluginsDir, "README.md"), []byte("# Test"), 0644) - require.NoError(t, err) - - plugin := Plugin{ - Name: "ValidPlugin", - Capabilities: []string{"test"}, - Category: "test", - Repo: "https://github.com/test/test", - Author: "Test", - Description: "Test", - Compositors: []string{"niri"}, - Distro: []string{"any"}, - } - createTestPlugin(t, fs, tmpDir, "valid.json", plugin) - - err = registry.loadPlugins() - assert.NoError(t, err) - assert.Len(t, registry.plugins, 1) - assert.Equal(t, "ValidPlugin", registry.plugins[0].Name) - }) - - t.Run("skips directories", func(t *testing.T) { - registry, fs, tmpDir := setupTestRegistry(t) - - pluginsDir := filepath.Join(tmpDir, "plugins") - err := fs.MkdirAll(filepath.Join(pluginsDir, "subdir"), 0755) - require.NoError(t, err) - - plugin := Plugin{ - Name: "ValidPlugin", - Capabilities: []string{"test"}, - Category: "test", - Repo: "https://github.com/test/test", - Author: "Test", - Description: "Test", - Compositors: []string{"niri"}, - Distro: []string{"any"}, - } - createTestPlugin(t, fs, tmpDir, "valid.json", plugin) - - err = registry.loadPlugins() - assert.NoError(t, err) - assert.Len(t, registry.plugins, 1) - }) - - t.Run("skips invalid json files", func(t *testing.T) { - registry, fs, tmpDir := setupTestRegistry(t) - - pluginsDir := filepath.Join(tmpDir, "plugins") - err := fs.MkdirAll(pluginsDir, 0755) - require.NoError(t, err) - - err = afero.WriteFile(fs, filepath.Join(pluginsDir, "invalid.json"), []byte("{invalid json}"), 0644) - require.NoError(t, err) - - plugin := Plugin{ - Name: "ValidPlugin", - Capabilities: []string{"test"}, - Category: "test", - Repo: "https://github.com/test/test", - Author: "Test", - Description: "Test", - Compositors: []string{"niri"}, - Distro: []string{"any"}, - } - createTestPlugin(t, fs, tmpDir, "valid.json", plugin) - - err = registry.loadPlugins() - assert.NoError(t, err) - assert.Len(t, registry.plugins, 1) - assert.Equal(t, "ValidPlugin", registry.plugins[0].Name) - }) - - t.Run("returns error when plugins directory missing", func(t *testing.T) { - registry, _, _ := setupTestRegistry(t) - - err := registry.loadPlugins() - assert.Error(t, err) - assert.Contains(t, err.Error(), "failed to read plugins directory") - }) -} - -func TestList(t *testing.T) { - t.Run("returns cached plugins if available", func(t *testing.T) { - registry, _, _ := setupTestRegistry(t) - - plugin := Plugin{ - Name: "CachedPlugin", - Capabilities: []string{"test"}, - Category: "test", - Repo: "https://github.com/test/test", - Author: "Test", - Description: "Test", - Compositors: []string{"niri"}, - Distro: []string{"any"}, - } - - registry.plugins = []Plugin{plugin} - - plugins, err := registry.List() - assert.NoError(t, err) - assert.Len(t, plugins, 1) - assert.Equal(t, "CachedPlugin", plugins[0].Name) - }) - - t.Run("updates and loads plugins when cache is empty", func(t *testing.T) { - registry, fs, _ := setupTestRegistry(t) - - plugin := Plugin{ - Name: "NewPlugin", - Capabilities: []string{"test"}, - Category: "test", - Repo: "https://github.com/test/test", - Author: "Test", - Description: "Test", - Compositors: []string{"niri"}, - Distro: []string{"any"}, - } - - mockGit := &mockGitClient{ - cloneFunc: func(path string, url string) error { - createTestPlugin(t, fs, path, "plugin.json", plugin) - return nil - }, - } - registry.git = mockGit - - plugins, err := registry.List() - assert.NoError(t, err) - assert.Len(t, plugins, 1) - assert.Equal(t, "NewPlugin", plugins[0].Name) - }) -} - -func TestUpdate(t *testing.T) { - t.Run("clones repository when cache doesn't exist", func(t *testing.T) { - registry, fs, tmpDir := setupTestRegistry(t) - - plugin := Plugin{ - Name: "RepoPlugin", - Capabilities: []string{"test"}, - Category: "test", - Repo: "https://github.com/test/test", - Author: "Test", - Description: "Test", - Compositors: []string{"niri"}, - Distro: []string{"any"}, - } - - cloneCalled := false - mockGit := &mockGitClient{ - cloneFunc: func(path string, url string) error { - cloneCalled = true - assert.Equal(t, registryRepo, url) - assert.Equal(t, tmpDir, path) - createTestPlugin(t, fs, path, "plugin.json", plugin) - return nil - }, - } - registry.git = mockGit - - err := registry.Update() - assert.NoError(t, err) - assert.True(t, cloneCalled) - assert.Len(t, registry.plugins, 1) - assert.Equal(t, "RepoPlugin", registry.plugins[0].Name) - }) - - t.Run("pulls updates when cache exists", func(t *testing.T) { - registry, fs, tmpDir := setupTestRegistry(t) - - plugin := Plugin{ - Name: "UpdatedPlugin", - Capabilities: []string{"test"}, - Category: "test", - Repo: "https://github.com/test/test", - Author: "Test", - Description: "Test", - Compositors: []string{"niri"}, - Distro: []string{"any"}, - } - - err := fs.MkdirAll(tmpDir, 0755) - require.NoError(t, err) - - pullCalled := false - mockGit := &mockGitClient{ - pullFunc: func(path string) error { - pullCalled = true - assert.Equal(t, tmpDir, path) - createTestPlugin(t, fs, path, "plugin.json", plugin) - return nil - }, - } - registry.git = mockGit - - err = registry.Update() - assert.NoError(t, err) - assert.True(t, pullCalled) - assert.Len(t, registry.plugins, 1) - assert.Equal(t, "UpdatedPlugin", registry.plugins[0].Name) - }) -} diff --git a/nix/inputs/dms-cli/internal/plugins/search.go b/nix/inputs/dms-cli/internal/plugins/search.go deleted file mode 100644 index 0505180..0000000 --- a/nix/inputs/dms-cli/internal/plugins/search.go +++ /dev/null @@ -1,105 +0,0 @@ -package plugins - -import ( - "sort" - "strings" -) - -func FuzzySearch(query string, plugins []Plugin) []Plugin { - if query == "" { - return plugins - } - - queryLower := strings.ToLower(query) - var results []Plugin - - for _, plugin := range plugins { - if fuzzyMatch(queryLower, strings.ToLower(plugin.Name)) || - fuzzyMatch(queryLower, strings.ToLower(plugin.Category)) || - fuzzyMatch(queryLower, strings.ToLower(plugin.Description)) || - fuzzyMatch(queryLower, strings.ToLower(plugin.Author)) { - results = append(results, plugin) - } - } - - return results -} - -func fuzzyMatch(query, text string) bool { - queryIdx := 0 - for _, char := range text { - if queryIdx < len(query) && char == rune(query[queryIdx]) { - queryIdx++ - } - } - return queryIdx == len(query) -} - -func FilterByCategory(category string, plugins []Plugin) []Plugin { - if category == "" { - return plugins - } - - var results []Plugin - categoryLower := strings.ToLower(category) - - for _, plugin := range plugins { - if strings.ToLower(plugin.Category) == categoryLower { - results = append(results, plugin) - } - } - - return results -} - -func FilterByCompositor(compositor string, plugins []Plugin) []Plugin { - if compositor == "" { - return plugins - } - - var results []Plugin - compositorLower := strings.ToLower(compositor) - - for _, plugin := range plugins { - for _, comp := range plugin.Compositors { - if strings.ToLower(comp) == compositorLower { - results = append(results, plugin) - break - } - } - } - - return results -} - -func FilterByCapability(capability string, plugins []Plugin) []Plugin { - if capability == "" { - return plugins - } - - var results []Plugin - capabilityLower := strings.ToLower(capability) - - for _, plugin := range plugins { - for _, cap := range plugin.Capabilities { - if strings.ToLower(cap) == capabilityLower { - results = append(results, plugin) - break - } - } - } - - return results -} - -func SortByFirstParty(plugins []Plugin) []Plugin { - sort.SliceStable(plugins, func(i, j int) bool { - isFirstPartyI := strings.HasPrefix(plugins[i].Repo, "https://github.com/AvengeMedia") - isFirstPartyJ := strings.HasPrefix(plugins[j].Repo, "https://github.com/AvengeMedia") - if isFirstPartyI != isFirstPartyJ { - return isFirstPartyI - } - return false - }) - return plugins -} diff --git a/nix/inputs/dms-cli/internal/proto/dwl_ipc/dwl_ipc.go b/nix/inputs/dms-cli/internal/proto/dwl_ipc/dwl_ipc.go deleted file mode 100644 index 65b590d..0000000 --- a/nix/inputs/dms-cli/internal/proto/dwl_ipc/dwl_ipc.go +++ /dev/null @@ -1,491 +0,0 @@ -// Generated by go-wayland-scanner -// https://github.com/yaslama/go-wayland/cmd/go-wayland-scanner -// XML file : internal/proto/xml/dwl-ipc-unstable-v2.xml -// -// dwl_ipc_unstable_v2 Protocol Copyright: - -package dwl_ipc - -import "github.com/yaslama/go-wayland/wayland/client" - -// ZdwlIpcManagerV2InterfaceName is the name of the interface as it appears in the [client.Registry]. -// It can be used to match the [client.RegistryGlobalEvent.Interface] in the -// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. -const ZdwlIpcManagerV2InterfaceName = "zdwl_ipc_manager_v2" - -// ZdwlIpcManagerV2 : manage dwl state -// -// This interface is exposed as a global in wl_registry. -// -// Clients can use this interface to get a dwl_ipc_output. -// After binding the client will recieve the dwl_ipc_manager.tags and dwl_ipc_manager.layout events. -// The dwl_ipc_manager.tags and dwl_ipc_manager.layout events expose tags and layouts to the client. -type ZdwlIpcManagerV2 struct { - client.BaseProxy - tagsHandler ZdwlIpcManagerV2TagsHandlerFunc - layoutHandler ZdwlIpcManagerV2LayoutHandlerFunc -} - -// NewZdwlIpcManagerV2 : manage dwl state -// -// This interface is exposed as a global in wl_registry. -// -// Clients can use this interface to get a dwl_ipc_output. -// After binding the client will recieve the dwl_ipc_manager.tags and dwl_ipc_manager.layout events. -// The dwl_ipc_manager.tags and dwl_ipc_manager.layout events expose tags and layouts to the client. -func NewZdwlIpcManagerV2(ctx *client.Context) *ZdwlIpcManagerV2 { - zdwlIpcManagerV2 := &ZdwlIpcManagerV2{} - ctx.Register(zdwlIpcManagerV2) - return zdwlIpcManagerV2 -} - -// Release : release dwl_ipc_manager -// -// Indicates that the client will not the dwl_ipc_manager object anymore. -// Objects created through this instance are not affected. -func (i *ZdwlIpcManagerV2) Release() error { - defer i.Context().Unregister(i) - const opcode = 0 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// GetOutput : get a dwl_ipc_outout for a wl_output -// -// Get a dwl_ipc_outout for the specified wl_output. -func (i *ZdwlIpcManagerV2) GetOutput(output *client.Output) (*ZdwlIpcOutputV2, error) { - id := NewZdwlIpcOutputV2(i.Context()) - const opcode = 1 - const _reqBufLen = 8 + 4 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], id.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], output.ID()) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return id, err -} - -// ZdwlIpcManagerV2TagsEvent : Announces tag amount -// -// This event is sent after binding. -// A roundtrip after binding guarantees the client recieved all tags. -type ZdwlIpcManagerV2TagsEvent struct { - Amount uint32 -} -type ZdwlIpcManagerV2TagsHandlerFunc func(ZdwlIpcManagerV2TagsEvent) - -// SetTagsHandler : sets handler for ZdwlIpcManagerV2TagsEvent -func (i *ZdwlIpcManagerV2) SetTagsHandler(f ZdwlIpcManagerV2TagsHandlerFunc) { - i.tagsHandler = f -} - -// ZdwlIpcManagerV2LayoutEvent : Announces a layout -// -// This event is sent after binding. -// A roundtrip after binding guarantees the client recieved all layouts. -type ZdwlIpcManagerV2LayoutEvent struct { - Name string -} -type ZdwlIpcManagerV2LayoutHandlerFunc func(ZdwlIpcManagerV2LayoutEvent) - -// SetLayoutHandler : sets handler for ZdwlIpcManagerV2LayoutEvent -func (i *ZdwlIpcManagerV2) SetLayoutHandler(f ZdwlIpcManagerV2LayoutHandlerFunc) { - i.layoutHandler = f -} - -func (i *ZdwlIpcManagerV2) Dispatch(opcode uint32, fd int, data []byte) { - switch opcode { - case 0: - if i.tagsHandler == nil { - return - } - var e ZdwlIpcManagerV2TagsEvent - l := 0 - e.Amount = client.Uint32(data[l : l+4]) - l += 4 - - i.tagsHandler(e) - case 1: - if i.layoutHandler == nil { - return - } - var e ZdwlIpcManagerV2LayoutEvent - l := 0 - nameLen := client.PaddedLen(int(client.Uint32(data[l : l+4]))) - l += 4 - e.Name = client.String(data[l : l+nameLen]) - l += nameLen - - i.layoutHandler(e) - } -} - -// ZdwlIpcOutputV2InterfaceName is the name of the interface as it appears in the [client.Registry]. -// It can be used to match the [client.RegistryGlobalEvent.Interface] in the -// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. -const ZdwlIpcOutputV2InterfaceName = "zdwl_ipc_output_v2" - -// ZdwlIpcOutputV2 : control dwl output -// -// Observe and control a dwl output. -// -// Events are double-buffered: -// Clients should cache events and redraw when a dwl_ipc_output.frame event is sent. -// -// Request are not double-buffered: -// The compositor will update immediately upon request. -type ZdwlIpcOutputV2 struct { - client.BaseProxy - toggleVisibilityHandler ZdwlIpcOutputV2ToggleVisibilityHandlerFunc - activeHandler ZdwlIpcOutputV2ActiveHandlerFunc - tagHandler ZdwlIpcOutputV2TagHandlerFunc - layoutHandler ZdwlIpcOutputV2LayoutHandlerFunc - titleHandler ZdwlIpcOutputV2TitleHandlerFunc - appidHandler ZdwlIpcOutputV2AppidHandlerFunc - layoutSymbolHandler ZdwlIpcOutputV2LayoutSymbolHandlerFunc - frameHandler ZdwlIpcOutputV2FrameHandlerFunc -} - -// NewZdwlIpcOutputV2 : control dwl output -// -// Observe and control a dwl output. -// -// Events are double-buffered: -// Clients should cache events and redraw when a dwl_ipc_output.frame event is sent. -// -// Request are not double-buffered: -// The compositor will update immediately upon request. -func NewZdwlIpcOutputV2(ctx *client.Context) *ZdwlIpcOutputV2 { - zdwlIpcOutputV2 := &ZdwlIpcOutputV2{} - ctx.Register(zdwlIpcOutputV2) - return zdwlIpcOutputV2 -} - -// Release : release dwl_ipc_outout -// -// Indicates to that the client no longer needs this dwl_ipc_output. -func (i *ZdwlIpcOutputV2) Release() error { - defer i.Context().Unregister(i) - const opcode = 0 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// SetTags : Set the active tags of this output -// -// tagmask: bitmask of the tags that should be set. -// toggleTagset: toggle the selected tagset, zero for invalid, nonzero for valid. -func (i *ZdwlIpcOutputV2) SetTags(tagmask, toggleTagset uint32) error { - const opcode = 1 - const _reqBufLen = 8 + 4 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(tagmask)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(toggleTagset)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// SetClientTags : Set the tags of the focused client. -// -// The tags are updated as follows: -// new_tags = (current_tags AND and_tags) XOR xor_tags -func (i *ZdwlIpcOutputV2) SetClientTags(andTags, xorTags uint32) error { - const opcode = 2 - const _reqBufLen = 8 + 4 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(andTags)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(xorTags)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// SetLayout : Set the layout of this output -// -// index: index of a layout recieved by dwl_ipc_manager.layout -func (i *ZdwlIpcOutputV2) SetLayout(index uint32) error { - const opcode = 3 - const _reqBufLen = 8 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(index)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -type ZdwlIpcOutputV2TagState uint32 - -// ZdwlIpcOutputV2TagState : -const ( - // ZdwlIpcOutputV2TagStateNone : no state - ZdwlIpcOutputV2TagStateNone ZdwlIpcOutputV2TagState = 0 - // ZdwlIpcOutputV2TagStateActive : tag is active - ZdwlIpcOutputV2TagStateActive ZdwlIpcOutputV2TagState = 1 - // ZdwlIpcOutputV2TagStateUrgent : tag has at least one urgent client - ZdwlIpcOutputV2TagStateUrgent ZdwlIpcOutputV2TagState = 2 -) - -func (e ZdwlIpcOutputV2TagState) Name() string { - switch e { - case ZdwlIpcOutputV2TagStateNone: - return "none" - case ZdwlIpcOutputV2TagStateActive: - return "active" - case ZdwlIpcOutputV2TagStateUrgent: - return "urgent" - default: - return "" - } -} - -func (e ZdwlIpcOutputV2TagState) Value() string { - switch e { - case ZdwlIpcOutputV2TagStateNone: - return "0" - case ZdwlIpcOutputV2TagStateActive: - return "1" - case ZdwlIpcOutputV2TagStateUrgent: - return "2" - default: - return "" - } -} - -func (e ZdwlIpcOutputV2TagState) String() string { - return e.Name() + "=" + e.Value() -} - -// ZdwlIpcOutputV2ToggleVisibilityEvent : Toggle client visibilty -// -// Indicates the client should hide or show themselves. -// If the client is visible then hide, if hidden then show. -type ZdwlIpcOutputV2ToggleVisibilityEvent struct{} -type ZdwlIpcOutputV2ToggleVisibilityHandlerFunc func(ZdwlIpcOutputV2ToggleVisibilityEvent) - -// SetToggleVisibilityHandler : sets handler for ZdwlIpcOutputV2ToggleVisibilityEvent -func (i *ZdwlIpcOutputV2) SetToggleVisibilityHandler(f ZdwlIpcOutputV2ToggleVisibilityHandlerFunc) { - i.toggleVisibilityHandler = f -} - -// ZdwlIpcOutputV2ActiveEvent : Update the selected output. -// -// Indicates if the output is active. Zero is invalid, nonzero is valid. -type ZdwlIpcOutputV2ActiveEvent struct { - Active uint32 -} -type ZdwlIpcOutputV2ActiveHandlerFunc func(ZdwlIpcOutputV2ActiveEvent) - -// SetActiveHandler : sets handler for ZdwlIpcOutputV2ActiveEvent -func (i *ZdwlIpcOutputV2) SetActiveHandler(f ZdwlIpcOutputV2ActiveHandlerFunc) { - i.activeHandler = f -} - -// ZdwlIpcOutputV2TagEvent : Update the state of a tag. -// -// Indicates that a tag has been updated. -type ZdwlIpcOutputV2TagEvent struct { - Tag uint32 - State uint32 - Clients uint32 - Focused uint32 -} -type ZdwlIpcOutputV2TagHandlerFunc func(ZdwlIpcOutputV2TagEvent) - -// SetTagHandler : sets handler for ZdwlIpcOutputV2TagEvent -func (i *ZdwlIpcOutputV2) SetTagHandler(f ZdwlIpcOutputV2TagHandlerFunc) { - i.tagHandler = f -} - -// ZdwlIpcOutputV2LayoutEvent : Update the layout. -// -// Indicates a new layout is selected. -type ZdwlIpcOutputV2LayoutEvent struct { - Layout uint32 -} -type ZdwlIpcOutputV2LayoutHandlerFunc func(ZdwlIpcOutputV2LayoutEvent) - -// SetLayoutHandler : sets handler for ZdwlIpcOutputV2LayoutEvent -func (i *ZdwlIpcOutputV2) SetLayoutHandler(f ZdwlIpcOutputV2LayoutHandlerFunc) { - i.layoutHandler = f -} - -// ZdwlIpcOutputV2TitleEvent : Update the title. -// -// Indicates the title has changed. -type ZdwlIpcOutputV2TitleEvent struct { - Title string -} -type ZdwlIpcOutputV2TitleHandlerFunc func(ZdwlIpcOutputV2TitleEvent) - -// SetTitleHandler : sets handler for ZdwlIpcOutputV2TitleEvent -func (i *ZdwlIpcOutputV2) SetTitleHandler(f ZdwlIpcOutputV2TitleHandlerFunc) { - i.titleHandler = f -} - -// ZdwlIpcOutputV2AppidEvent : Update the appid. -// -// Indicates the appid has changed. -type ZdwlIpcOutputV2AppidEvent struct { - Appid string -} -type ZdwlIpcOutputV2AppidHandlerFunc func(ZdwlIpcOutputV2AppidEvent) - -// SetAppidHandler : sets handler for ZdwlIpcOutputV2AppidEvent -func (i *ZdwlIpcOutputV2) SetAppidHandler(f ZdwlIpcOutputV2AppidHandlerFunc) { - i.appidHandler = f -} - -// ZdwlIpcOutputV2LayoutSymbolEvent : Update the current layout symbol -// -// Indicates the layout has changed. Since layout symbols are dynamic. -// As opposed to the zdwl_ipc_manager.layout event, this should take precendence when displaying. -// You can ignore the zdwl_ipc_output.layout event. -type ZdwlIpcOutputV2LayoutSymbolEvent struct { - Layout string -} -type ZdwlIpcOutputV2LayoutSymbolHandlerFunc func(ZdwlIpcOutputV2LayoutSymbolEvent) - -// SetLayoutSymbolHandler : sets handler for ZdwlIpcOutputV2LayoutSymbolEvent -func (i *ZdwlIpcOutputV2) SetLayoutSymbolHandler(f ZdwlIpcOutputV2LayoutSymbolHandlerFunc) { - i.layoutSymbolHandler = f -} - -// ZdwlIpcOutputV2FrameEvent : The update sequence is done. -// -// Indicates that a sequence of status updates have finished and the client should redraw. -type ZdwlIpcOutputV2FrameEvent struct{} -type ZdwlIpcOutputV2FrameHandlerFunc func(ZdwlIpcOutputV2FrameEvent) - -// SetFrameHandler : sets handler for ZdwlIpcOutputV2FrameEvent -func (i *ZdwlIpcOutputV2) SetFrameHandler(f ZdwlIpcOutputV2FrameHandlerFunc) { - i.frameHandler = f -} - -func (i *ZdwlIpcOutputV2) Dispatch(opcode uint32, fd int, data []byte) { - switch opcode { - case 0: - if i.toggleVisibilityHandler == nil { - return - } - var e ZdwlIpcOutputV2ToggleVisibilityEvent - - i.toggleVisibilityHandler(e) - case 1: - if i.activeHandler == nil { - return - } - var e ZdwlIpcOutputV2ActiveEvent - l := 0 - e.Active = client.Uint32(data[l : l+4]) - l += 4 - - i.activeHandler(e) - case 2: - if i.tagHandler == nil { - return - } - var e ZdwlIpcOutputV2TagEvent - l := 0 - e.Tag = client.Uint32(data[l : l+4]) - l += 4 - e.State = client.Uint32(data[l : l+4]) - l += 4 - e.Clients = client.Uint32(data[l : l+4]) - l += 4 - e.Focused = client.Uint32(data[l : l+4]) - l += 4 - - i.tagHandler(e) - case 3: - if i.layoutHandler == nil { - return - } - var e ZdwlIpcOutputV2LayoutEvent - l := 0 - e.Layout = client.Uint32(data[l : l+4]) - l += 4 - - i.layoutHandler(e) - case 4: - if i.titleHandler == nil { - return - } - var e ZdwlIpcOutputV2TitleEvent - l := 0 - titleLen := client.PaddedLen(int(client.Uint32(data[l : l+4]))) - l += 4 - e.Title = client.String(data[l : l+titleLen]) - l += titleLen - - i.titleHandler(e) - case 5: - if i.appidHandler == nil { - return - } - var e ZdwlIpcOutputV2AppidEvent - l := 0 - appidLen := client.PaddedLen(int(client.Uint32(data[l : l+4]))) - l += 4 - e.Appid = client.String(data[l : l+appidLen]) - l += appidLen - - i.appidHandler(e) - case 6: - if i.layoutSymbolHandler == nil { - return - } - var e ZdwlIpcOutputV2LayoutSymbolEvent - l := 0 - layoutLen := client.PaddedLen(int(client.Uint32(data[l : l+4]))) - l += 4 - e.Layout = client.String(data[l : l+layoutLen]) - l += layoutLen - - i.layoutSymbolHandler(e) - case 7: - if i.frameHandler == nil { - return - } - var e ZdwlIpcOutputV2FrameEvent - - i.frameHandler(e) - } -} diff --git a/nix/inputs/dms-cli/internal/proto/wlr_gamma_control/gamma_control.go b/nix/inputs/dms-cli/internal/proto/wlr_gamma_control/gamma_control.go deleted file mode 100644 index 63e1b6a..0000000 --- a/nix/inputs/dms-cli/internal/proto/wlr_gamma_control/gamma_control.go +++ /dev/null @@ -1,268 +0,0 @@ -// Generated by go-wayland-scanner -// https://github.com/yaslama/go-wayland/cmd/go-wayland-scanner -// XML file : wayland-protocols/wlr-gamma-control-unstable-v1.xml -// -// wlr_gamma_control_unstable_v1 Protocol Copyright: -// -// Copyright © 2015 Giulio camuffo -// Copyright © 2018 Simon Ser -// -// Permission to use, copy, modify, distribute, and sell this -// software and its documentation for any purpose is hereby granted -// without fee, provided that the above copyright notice appear in -// all copies and that both that copyright notice and this permission -// notice appear in supporting documentation, and that the name of -// the copyright holders not be used in advertising or publicity -// pertaining to distribution of the software without specific, -// written prior permission. The copyright holders make no -// representations about the suitability of this software for any -// purpose. It is provided "as is" without express or implied -// warranty. -// -// THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS -// SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND -// FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY -// SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN -// AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, -// ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF -// THIS SOFTWARE. - -package wlr_gamma_control - -import ( - "github.com/yaslama/go-wayland/wayland/client" - "golang.org/x/sys/unix" -) - -// ZwlrGammaControlManagerV1InterfaceName is the name of the interface as it appears in the [client.Registry]. -// It can be used to match the [client.RegistryGlobalEvent.Interface] in the -// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. -const ZwlrGammaControlManagerV1InterfaceName = "zwlr_gamma_control_manager_v1" - -// ZwlrGammaControlManagerV1 : manager to create per-output gamma controls -// -// This interface is a manager that allows creating per-output gamma -// controls. -type ZwlrGammaControlManagerV1 struct { - client.BaseProxy -} - -// NewZwlrGammaControlManagerV1 : manager to create per-output gamma controls -// -// This interface is a manager that allows creating per-output gamma -// controls. -func NewZwlrGammaControlManagerV1(ctx *client.Context) *ZwlrGammaControlManagerV1 { - zwlrGammaControlManagerV1 := &ZwlrGammaControlManagerV1{} - ctx.Register(zwlrGammaControlManagerV1) - return zwlrGammaControlManagerV1 -} - -// GetGammaControl : get a gamma control for an output -// -// Create a gamma control that can be used to adjust gamma tables for the -// provided output. -func (i *ZwlrGammaControlManagerV1) GetGammaControl(output *client.Output) (*ZwlrGammaControlV1, error) { - id := NewZwlrGammaControlV1(i.Context()) - const opcode = 0 - const _reqBufLen = 8 + 4 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], id.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], output.ID()) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return id, err -} - -// Destroy : destroy the manager -// -// All objects created by the manager will still remain valid, until their -// appropriate destroy request has been called. -func (i *ZwlrGammaControlManagerV1) Destroy() error { - defer i.Context().Unregister(i) - const opcode = 1 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// ZwlrGammaControlV1InterfaceName is the name of the interface as it appears in the [client.Registry]. -// It can be used to match the [client.RegistryGlobalEvent.Interface] in the -// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. -const ZwlrGammaControlV1InterfaceName = "zwlr_gamma_control_v1" - -// ZwlrGammaControlV1 : adjust gamma tables for an output -// -// This interface allows a client to adjust gamma tables for a particular -// output. -// -// The client will receive the gamma size, and will then be able to set gamma -// tables. At any time the compositor can send a failed event indicating that -// this object is no longer valid. -// -// There can only be at most one gamma control object per output, which -// has exclusive access to this particular output. When the gamma control -// object is destroyed, the gamma table is restored to its original value. -type ZwlrGammaControlV1 struct { - client.BaseProxy - gammaSizeHandler ZwlrGammaControlV1GammaSizeHandlerFunc - failedHandler ZwlrGammaControlV1FailedHandlerFunc -} - -// NewZwlrGammaControlV1 : adjust gamma tables for an output -// -// This interface allows a client to adjust gamma tables for a particular -// output. -// -// The client will receive the gamma size, and will then be able to set gamma -// tables. At any time the compositor can send a failed event indicating that -// this object is no longer valid. -// -// There can only be at most one gamma control object per output, which -// has exclusive access to this particular output. When the gamma control -// object is destroyed, the gamma table is restored to its original value. -func NewZwlrGammaControlV1(ctx *client.Context) *ZwlrGammaControlV1 { - zwlrGammaControlV1 := &ZwlrGammaControlV1{} - ctx.Register(zwlrGammaControlV1) - return zwlrGammaControlV1 -} - -// SetGamma : set the gamma table -// -// Set the gamma table. The file descriptor can be memory-mapped to provide -// the raw gamma table, which contains successive gamma ramps for the red, -// green and blue channels. Each gamma ramp is an array of 16-byte unsigned -// integers which has the same length as the gamma size. -// -// The file descriptor data must have the same length as three times the -// gamma size. -// -// fd: gamma table file descriptor -func (i *ZwlrGammaControlV1) SetGamma(fd int) error { - const opcode = 0 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - oob := unix.UnixRights(int(fd)) - err := i.Context().WriteMsg(_reqBuf[:], oob) - return err -} - -// Destroy : destroy this control -// -// Destroys the gamma control object. If the object is still valid, this -// restores the original gamma tables. -func (i *ZwlrGammaControlV1) Destroy() error { - defer i.Context().Unregister(i) - const opcode = 1 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -type ZwlrGammaControlV1Error uint32 - -// ZwlrGammaControlV1Error : -const ( - // ZwlrGammaControlV1ErrorInvalidGamma : invalid gamma tables - ZwlrGammaControlV1ErrorInvalidGamma ZwlrGammaControlV1Error = 1 -) - -func (e ZwlrGammaControlV1Error) Name() string { - switch e { - case ZwlrGammaControlV1ErrorInvalidGamma: - return "invalid_gamma" - default: - return "" - } -} - -func (e ZwlrGammaControlV1Error) Value() string { - switch e { - case ZwlrGammaControlV1ErrorInvalidGamma: - return "1" - default: - return "" - } -} - -func (e ZwlrGammaControlV1Error) String() string { - return e.Name() + "=" + e.Value() -} - -// ZwlrGammaControlV1GammaSizeEvent : size of gamma ramps -// -// Advertise the size of each gamma ramp. -// -// This event is sent immediately when the gamma control object is created. -type ZwlrGammaControlV1GammaSizeEvent struct { - Size uint32 -} -type ZwlrGammaControlV1GammaSizeHandlerFunc func(ZwlrGammaControlV1GammaSizeEvent) - -// SetGammaSizeHandler : sets handler for ZwlrGammaControlV1GammaSizeEvent -func (i *ZwlrGammaControlV1) SetGammaSizeHandler(f ZwlrGammaControlV1GammaSizeHandlerFunc) { - i.gammaSizeHandler = f -} - -// ZwlrGammaControlV1FailedEvent : object no longer valid -// -// This event indicates that the gamma control is no longer valid. This -// can happen for a number of reasons, including: -// - The output doesn't support gamma tables -// - Setting the gamma tables failed -// - Another client already has exclusive gamma control for this output -// - The compositor has transferred gamma control to another client -// -// Upon receiving this event, the client should destroy this object. -type ZwlrGammaControlV1FailedEvent struct{} -type ZwlrGammaControlV1FailedHandlerFunc func(ZwlrGammaControlV1FailedEvent) - -// SetFailedHandler : sets handler for ZwlrGammaControlV1FailedEvent -func (i *ZwlrGammaControlV1) SetFailedHandler(f ZwlrGammaControlV1FailedHandlerFunc) { - i.failedHandler = f -} - -func (i *ZwlrGammaControlV1) Dispatch(opcode uint32, fd int, data []byte) { - switch opcode { - case 0: - if i.gammaSizeHandler == nil { - return - } - var e ZwlrGammaControlV1GammaSizeEvent - l := 0 - e.Size = client.Uint32(data[l : l+4]) - l += 4 - - i.gammaSizeHandler(e) - case 1: - if i.failedHandler == nil { - return - } - var e ZwlrGammaControlV1FailedEvent - - i.failedHandler(e) - } -} diff --git a/nix/inputs/dms-cli/internal/proto/xml/dwl-ipc-unstable-v2.xml b/nix/inputs/dms-cli/internal/proto/xml/dwl-ipc-unstable-v2.xml deleted file mode 100644 index 74a212f..0000000 --- a/nix/inputs/dms-cli/internal/proto/xml/dwl-ipc-unstable-v2.xml +++ /dev/null @@ -1,166 +0,0 @@ - - - - - This protocol allows clients to update and get updates from dwl. - - Warning! The protocol described in this file is experimental and - backward incompatible changes may be made. Backward compatible - changes may be added together with the corresponding interface - version bump. - Backward incompatible changes are done by bumping the version - number in the protocol and interface names and resetting the - interface version. Once the protocol is to be declared stable, - the 'z' prefix and the version number in the protocol and - interface names are removed and the interface version number is - reset. - - - - - This interface is exposed as a global in wl_registry. - - Clients can use this interface to get a dwl_ipc_output. - After binding the client will recieve the dwl_ipc_manager.tags and dwl_ipc_manager.layout events. - The dwl_ipc_manager.tags and dwl_ipc_manager.layout events expose tags and layouts to the client. - - - - - Indicates that the client will not the dwl_ipc_manager object anymore. - Objects created through this instance are not affected. - - - - - - Get a dwl_ipc_outout for the specified wl_output. - - - - - - - - This event is sent after binding. - A roundtrip after binding guarantees the client recieved all tags. - - - - - - - This event is sent after binding. - A roundtrip after binding guarantees the client recieved all layouts. - - - - - - - - Observe and control a dwl output. - - Events are double-buffered: - Clients should cache events and redraw when a dwl_ipc_output.frame event is sent. - - Request are not double-buffered: - The compositor will update immediately upon request. - - - - - - - - - - - Indicates to that the client no longer needs this dwl_ipc_output. - - - - - - Indicates the client should hide or show themselves. - If the client is visible then hide, if hidden then show. - - - - - - Indicates if the output is active. Zero is invalid, nonzero is valid. - - - - - - - Indicates that a tag has been updated. - - - - - - - - - - Indicates a new layout is selected. - - - - - - - Indicates the title has changed. - - - - - - - Indicates the appid has changed. - - - - - - - Indicates the layout has changed. Since layout symbols are dynamic. - As opposed to the zdwl_ipc_manager.layout event, this should take precendence when displaying. - You can ignore the zdwl_ipc_output.layout event. - - - - - - - Indicates that a sequence of status updates have finished and the client should redraw. - - - - - - - - - - - - The tags are updated as follows: - new_tags = (current_tags AND and_tags) XOR xor_tags - - - - - - - - - - - diff --git a/nix/inputs/dms-cli/internal/proto/xml/wlr-gamma-control-unstable-v1.xml b/nix/inputs/dms-cli/internal/proto/xml/wlr-gamma-control-unstable-v1.xml deleted file mode 100644 index 16e0be8..0000000 --- a/nix/inputs/dms-cli/internal/proto/xml/wlr-gamma-control-unstable-v1.xml +++ /dev/null @@ -1,126 +0,0 @@ - - - - Copyright © 2015 Giulio camuffo - Copyright © 2018 Simon Ser - - Permission to use, copy, modify, distribute, and sell this - software and its documentation for any purpose is hereby granted - without fee, provided that the above copyright notice appear in - all copies and that both that copyright notice and this permission - notice appear in supporting documentation, and that the name of - the copyright holders not be used in advertising or publicity - pertaining to distribution of the software without specific, - written prior permission. The copyright holders make no - representations about the suitability of this software for any - purpose. It is provided "as is" without express or implied - warranty. - - THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS - SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND - FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN - AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, - ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF - THIS SOFTWARE. - - - - This protocol allows a privileged client to set the gamma tables for - outputs. - - Warning! The protocol described in this file is experimental and - backward incompatible changes may be made. Backward compatible changes - may be added together with the corresponding interface version bump. - Backward incompatible changes are done by bumping the version number in - the protocol and interface names and resetting the interface version. - Once the protocol is to be declared stable, the 'z' prefix and the - version number in the protocol and interface names are removed and the - interface version number is reset. - - - - - This interface is a manager that allows creating per-output gamma - controls. - - - - - Create a gamma control that can be used to adjust gamma tables for the - provided output. - - - - - - - - All objects created by the manager will still remain valid, until their - appropriate destroy request has been called. - - - - - - - This interface allows a client to adjust gamma tables for a particular - output. - - The client will receive the gamma size, and will then be able to set gamma - tables. At any time the compositor can send a failed event indicating that - this object is no longer valid. - - There can only be at most one gamma control object per output, which - has exclusive access to this particular output. When the gamma control - object is destroyed, the gamma table is restored to its original value. - - - - - Advertise the size of each gamma ramp. - - This event is sent immediately when the gamma control object is created. - - - - - - - - - - - Set the gamma table. The file descriptor can be memory-mapped to provide - the raw gamma table, which contains successive gamma ramps for the red, - green and blue channels. Each gamma ramp is an array of 16-byte unsigned - integers which has the same length as the gamma size. - - The file descriptor data must have the same length as three times the - gamma size. - - - - - - - This event indicates that the gamma control is no longer valid. This - can happen for a number of reasons, including: - - The output doesn't support gamma tables - - Setting the gamma tables failed - - Another client already has exclusive gamma control for this output - - The compositor has transferred gamma control to another client - - Upon receiving this event, the client should destroy this object. - - - - - - Destroys the gamma control object. If the object is still valid, this - restores the original gamma tables. - - - - diff --git a/nix/inputs/dms-cli/internal/server/bluez/agent.go b/nix/inputs/dms-cli/internal/server/bluez/agent.go deleted file mode 100644 index 591e820..0000000 --- a/nix/inputs/dms-cli/internal/server/bluez/agent.go +++ /dev/null @@ -1,341 +0,0 @@ -package bluez - -import ( - "context" - "errors" - "fmt" - "strconv" - "time" - - "github.com/AvengeMedia/danklinux/internal/errdefs" - "github.com/AvengeMedia/danklinux/internal/log" - "github.com/godbus/dbus/v5" -) - -const ( - bluezService = "org.bluez" - agentManagerPath = "/org/bluez" - agentManagerIface = "org.bluez.AgentManager1" - agent1Iface = "org.bluez.Agent1" - device1Iface = "org.bluez.Device1" - agentPath = "/com/danklinux/bluez/agent" - agentCapability = "KeyboardDisplay" -) - -const introspectXML = ` - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -` - -type BluezAgent struct { - conn *dbus.Conn - broker PromptBroker -} - -func NewBluezAgent(broker PromptBroker) (*BluezAgent, error) { - conn, err := dbus.ConnectSystemBus() - if err != nil { - return nil, fmt.Errorf("system bus connection failed: %w", err) - } - - agent := &BluezAgent{ - conn: conn, - broker: broker, - } - - if err := conn.Export(agent, dbus.ObjectPath(agentPath), agent1Iface); err != nil { - conn.Close() - return nil, fmt.Errorf("agent export failed: %w", err) - } - - if err := conn.Export(agent, dbus.ObjectPath(agentPath), "org.freedesktop.DBus.Introspectable"); err != nil { - conn.Close() - return nil, fmt.Errorf("introspection export failed: %w", err) - } - - mgr := conn.Object(bluezService, dbus.ObjectPath(agentManagerPath)) - if err := mgr.Call(agentManagerIface+".RegisterAgent", 0, dbus.ObjectPath(agentPath), agentCapability).Err; err != nil { - conn.Close() - return nil, fmt.Errorf("agent registration failed: %w", err) - } - - if err := mgr.Call(agentManagerIface+".RequestDefaultAgent", 0, dbus.ObjectPath(agentPath)).Err; err != nil { - log.Debugf("[BluezAgent] not default agent: %v", err) - } - - log.Infof("[BluezAgent] registered at %s with capability %s", agentPath, agentCapability) - return agent, nil -} - -func (a *BluezAgent) Close() { - if a.conn == nil { - return - } - mgr := a.conn.Object(bluezService, dbus.ObjectPath(agentManagerPath)) - mgr.Call(agentManagerIface+".UnregisterAgent", 0, dbus.ObjectPath(agentPath)) - a.conn.Close() -} - -func (a *BluezAgent) Release() *dbus.Error { - log.Infof("[BluezAgent] Release called") - return nil -} - -func (a *BluezAgent) RequestPinCode(device dbus.ObjectPath) (string, *dbus.Error) { - log.Infof("[BluezAgent] RequestPinCode: device=%s", device) - - secrets, err := a.promptFor(device, "pin", []string{"pin"}, nil) - if err != nil { - log.Warnf("[BluezAgent] RequestPinCode failed: %v", err) - return "", a.errorFrom(err) - } - - pin := secrets["pin"] - log.Infof("[BluezAgent] RequestPinCode returning PIN (len=%d)", len(pin)) - return pin, nil -} - -func (a *BluezAgent) RequestPasskey(device dbus.ObjectPath) (uint32, *dbus.Error) { - log.Infof("[BluezAgent] RequestPasskey: device=%s", device) - - secrets, err := a.promptFor(device, "passkey", []string{"passkey"}, nil) - if err != nil { - log.Warnf("[BluezAgent] RequestPasskey failed: %v", err) - return 0, a.errorFrom(err) - } - - passkey, err := strconv.ParseUint(secrets["passkey"], 10, 32) - if err != nil { - log.Warnf("[BluezAgent] invalid passkey format: %v", err) - return 0, dbus.MakeFailedError(fmt.Errorf("invalid passkey: %w", err)) - } - - log.Infof("[BluezAgent] RequestPasskey returning: %d", passkey) - return uint32(passkey), nil -} - -func (a *BluezAgent) DisplayPinCode(device dbus.ObjectPath, pincode string) *dbus.Error { - log.Infof("[BluezAgent] DisplayPinCode: device=%s, pin=%s", device, pincode) - - _, err := a.promptFor(device, "display-pin", []string{}, &pincode) - if err != nil { - log.Warnf("[BluezAgent] DisplayPinCode acknowledgment failed: %v", err) - } - - return nil -} - -func (a *BluezAgent) DisplayPasskey(device dbus.ObjectPath, passkey uint32, entered uint16) *dbus.Error { - log.Infof("[BluezAgent] DisplayPasskey: device=%s, passkey=%06d, entered=%d", device, passkey, entered) - - if entered == 0 { - pk := passkey - _, err := a.promptFor(device, "display-passkey", []string{}, nil) - if err != nil { - log.Warnf("[BluezAgent] DisplayPasskey acknowledgment failed: %v", err) - } - _ = pk - } - - return nil -} - -func (a *BluezAgent) RequestConfirmation(device dbus.ObjectPath, passkey uint32) *dbus.Error { - log.Infof("[BluezAgent] RequestConfirmation: device=%s, passkey=%06d", device, passkey) - - secrets, err := a.promptFor(device, "confirm", []string{"decision"}, nil) - if err != nil { - log.Warnf("[BluezAgent] RequestConfirmation failed: %v", err) - return a.errorFrom(err) - } - - if secrets["decision"] != "yes" && secrets["decision"] != "accept" { - log.Debugf("[BluezAgent] RequestConfirmation rejected by user") - return dbus.NewError("org.bluez.Error.Rejected", nil) - } - - log.Infof("[BluezAgent] RequestConfirmation accepted") - return nil -} - -func (a *BluezAgent) RequestAuthorization(device dbus.ObjectPath) *dbus.Error { - log.Infof("[BluezAgent] RequestAuthorization: device=%s", device) - - secrets, err := a.promptFor(device, "authorize", []string{"decision"}, nil) - if err != nil { - log.Warnf("[BluezAgent] RequestAuthorization failed: %v", err) - return a.errorFrom(err) - } - - if secrets["decision"] != "yes" && secrets["decision"] != "accept" { - log.Debugf("[BluezAgent] RequestAuthorization rejected by user") - return dbus.NewError("org.bluez.Error.Rejected", nil) - } - - log.Infof("[BluezAgent] RequestAuthorization accepted") - return nil -} - -func (a *BluezAgent) AuthorizeService(device dbus.ObjectPath, uuid string) *dbus.Error { - log.Infof("[BluezAgent] AuthorizeService: device=%s, uuid=%s", device, uuid) - - secrets, err := a.promptFor(device, "authorize-service:"+uuid, []string{"decision"}, nil) - if err != nil { - log.Warnf("[BluezAgent] AuthorizeService failed: %v", err) - return a.errorFrom(err) - } - - if secrets["decision"] != "yes" && secrets["decision"] != "accept" { - log.Debugf("[BluezAgent] AuthorizeService rejected by user") - return dbus.NewError("org.bluez.Error.Rejected", nil) - } - - log.Infof("[BluezAgent] AuthorizeService accepted") - return nil -} - -func (a *BluezAgent) Cancel() *dbus.Error { - log.Infof("[BluezAgent] Cancel called") - return nil -} - -func (a *BluezAgent) Introspect() (string, *dbus.Error) { - return introspectXML, nil -} - -func (a *BluezAgent) promptFor(device dbus.ObjectPath, requestType string, fields []string, displayValue *string) (map[string]string, error) { - if a.broker == nil { - return nil, fmt.Errorf("broker not initialized") - } - - deviceName, deviceAddr := a.getDeviceInfo(device) - hints := []string{} - if displayValue != nil { - hints = append(hints, *displayValue) - } - - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) - defer cancel() - - var passkey *uint32 - if requestType == "confirm" || requestType == "display-passkey" { - if displayValue != nil { - if pk, err := strconv.ParseUint(*displayValue, 10, 32); err == nil { - pk32 := uint32(pk) - passkey = &pk32 - } - } - } - - token, err := a.broker.Ask(ctx, PromptRequest{ - DevicePath: string(device), - DeviceName: deviceName, - DeviceAddr: deviceAddr, - RequestType: requestType, - Fields: fields, - Hints: hints, - Passkey: passkey, - }) - if err != nil { - return nil, fmt.Errorf("prompt creation failed: %w", err) - } - - log.Infof("[BluezAgent] waiting for user response (token=%s)", token) - reply, err := a.broker.Wait(ctx, token) - if err != nil { - if errors.Is(err, errdefs.ErrSecretPromptTimeout) { - return nil, err - } - if reply.Cancel || errors.Is(err, errdefs.ErrSecretPromptCancelled) { - return nil, errdefs.ErrSecretPromptCancelled - } - return nil, err - } - - if !reply.Accept && len(fields) > 0 { - return nil, errdefs.ErrSecretPromptCancelled - } - - return reply.Secrets, nil -} - -func (a *BluezAgent) getDeviceInfo(device dbus.ObjectPath) (string, string) { - obj := a.conn.Object(bluezService, device) - - var name, alias, addr string - - nameVar, err := obj.GetProperty(device1Iface + ".Name") - if err == nil { - if n, ok := nameVar.Value().(string); ok { - name = n - } - } - - aliasVar, err := obj.GetProperty(device1Iface + ".Alias") - if err == nil { - if a, ok := aliasVar.Value().(string); ok { - alias = a - } - } - - addrVar, err := obj.GetProperty(device1Iface + ".Address") - if err == nil { - if a, ok := addrVar.Value().(string); ok { - addr = a - } - } - - if alias != "" { - return alias, addr - } - if name != "" { - return name, addr - } - return addr, addr -} - -func (a *BluezAgent) errorFrom(err error) *dbus.Error { - if errors.Is(err, errdefs.ErrSecretPromptTimeout) { - return dbus.NewError("org.bluez.Error.Canceled", nil) - } - if errors.Is(err, errdefs.ErrSecretPromptCancelled) { - return dbus.NewError("org.bluez.Error.Canceled", nil) - } - return dbus.MakeFailedError(err) -} diff --git a/nix/inputs/dms-cli/internal/server/bluez/broker.go b/nix/inputs/dms-cli/internal/server/bluez/broker.go deleted file mode 100644 index f817707..0000000 --- a/nix/inputs/dms-cli/internal/server/bluez/broker.go +++ /dev/null @@ -1,21 +0,0 @@ -package bluez - -import ( - "context" - "crypto/rand" - "encoding/hex" -) - -type PromptBroker interface { - Ask(ctx context.Context, req PromptRequest) (token string, err error) - Wait(ctx context.Context, token string) (PromptReply, error) - Resolve(token string, reply PromptReply) error -} - -func generateToken() (string, error) { - bytes := make([]byte, 16) - if _, err := rand.Read(bytes); err != nil { - return "", err - } - return hex.EncodeToString(bytes), nil -} diff --git a/nix/inputs/dms-cli/internal/server/bluez/broker_test.go b/nix/inputs/dms-cli/internal/server/bluez/broker_test.go deleted file mode 100644 index 9eebe61..0000000 --- a/nix/inputs/dms-cli/internal/server/bluez/broker_test.go +++ /dev/null @@ -1,220 +0,0 @@ -package bluez - -import ( - "context" - "testing" - "time" -) - -func TestSubscriptionBrokerAskWait(t *testing.T) { - promptReceived := false - broker := NewSubscriptionBroker(func(p PairingPrompt) { - promptReceived = true - if p.Token == "" { - t.Error("expected token to be non-empty") - } - if p.DeviceName != "TestDevice" { - t.Errorf("expected DeviceName=TestDevice, got %s", p.DeviceName) - } - }) - - ctx := context.Background() - req := PromptRequest{ - DevicePath: "/org/bluez/test", - DeviceName: "TestDevice", - DeviceAddr: "AA:BB:CC:DD:EE:FF", - RequestType: "pin", - Fields: []string{"pin"}, - } - - token, err := broker.Ask(ctx, req) - if err != nil { - t.Fatalf("Ask failed: %v", err) - } - - if token == "" { - t.Fatal("expected non-empty token") - } - - if !promptReceived { - t.Fatal("expected prompt broadcast to be called") - } - - go func() { - time.Sleep(50 * time.Millisecond) - broker.Resolve(token, PromptReply{ - Secrets: map[string]string{"pin": "1234"}, - Accept: true, - }) - }() - - reply, err := broker.Wait(ctx, token) - if err != nil { - t.Fatalf("Wait failed: %v", err) - } - - if reply.Secrets["pin"] != "1234" { - t.Errorf("expected pin=1234, got %s", reply.Secrets["pin"]) - } - - if !reply.Accept { - t.Error("expected Accept=true") - } -} - -func TestSubscriptionBrokerTimeout(t *testing.T) { - broker := NewSubscriptionBroker(nil) - - ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) - defer cancel() - - req := PromptRequest{ - DevicePath: "/org/bluez/test", - DeviceName: "TestDevice", - RequestType: "passkey", - Fields: []string{"passkey"}, - } - - token, err := broker.Ask(ctx, req) - if err != nil { - t.Fatalf("Ask failed: %v", err) - } - - _, err = broker.Wait(ctx, token) - if err == nil { - t.Fatal("expected timeout error") - } -} - -func TestSubscriptionBrokerCancel(t *testing.T) { - broker := NewSubscriptionBroker(nil) - - ctx := context.Background() - req := PromptRequest{ - DevicePath: "/org/bluez/test", - DeviceName: "TestDevice", - RequestType: "confirm", - Fields: []string{"decision"}, - } - - token, err := broker.Ask(ctx, req) - if err != nil { - t.Fatalf("Ask failed: %v", err) - } - - go func() { - time.Sleep(50 * time.Millisecond) - broker.Resolve(token, PromptReply{ - Cancel: true, - }) - }() - - _, err = broker.Wait(ctx, token) - if err == nil { - t.Fatal("expected cancelled error") - } -} - -func TestSubscriptionBrokerUnknownToken(t *testing.T) { - broker := NewSubscriptionBroker(nil) - - ctx := context.Background() - _, err := broker.Wait(ctx, "invalid-token") - if err == nil { - t.Fatal("expected error for unknown token") - } -} - -func TestGenerateToken(t *testing.T) { - token1, err := generateToken() - if err != nil { - t.Fatalf("generateToken failed: %v", err) - } - - token2, err := generateToken() - if err != nil { - t.Fatalf("generateToken failed: %v", err) - } - - if token1 == token2 { - t.Error("expected unique tokens") - } - - if len(token1) != 32 { - t.Errorf("expected token length 32, got %d", len(token1)) - } -} - -func TestSubscriptionBrokerResolveUnknownToken(t *testing.T) { - broker := NewSubscriptionBroker(nil) - - err := broker.Resolve("unknown-token", PromptReply{ - Secrets: map[string]string{"test": "value"}, - }) - if err == nil { - t.Fatal("expected error for unknown token") - } -} - -func TestSubscriptionBrokerMultipleRequests(t *testing.T) { - broker := NewSubscriptionBroker(nil) - ctx := context.Background() - - req1 := PromptRequest{ - DevicePath: "/org/bluez/test1", - DeviceName: "Device1", - RequestType: "pin", - Fields: []string{"pin"}, - } - - req2 := PromptRequest{ - DevicePath: "/org/bluez/test2", - DeviceName: "Device2", - RequestType: "passkey", - Fields: []string{"passkey"}, - } - - token1, err := broker.Ask(ctx, req1) - if err != nil { - t.Fatalf("Ask1 failed: %v", err) - } - - token2, err := broker.Ask(ctx, req2) - if err != nil { - t.Fatalf("Ask2 failed: %v", err) - } - - if token1 == token2 { - t.Error("expected different tokens") - } - - go func() { - time.Sleep(50 * time.Millisecond) - broker.Resolve(token1, PromptReply{ - Secrets: map[string]string{"pin": "1234"}, - Accept: true, - }) - broker.Resolve(token2, PromptReply{ - Secrets: map[string]string{"passkey": "567890"}, - Accept: true, - }) - }() - - reply1, err := broker.Wait(ctx, token1) - if err != nil { - t.Fatalf("Wait1 failed: %v", err) - } - - reply2, err := broker.Wait(ctx, token2) - if err != nil { - t.Fatalf("Wait2 failed: %v", err) - } - - if reply1.Secrets["pin"] != "1234" { - t.Errorf("expected pin=1234, got %s", reply1.Secrets["pin"]) - } - - if reply2.Secrets["passkey"] != "567890" { - t.Errorf("expected passkey=567890, got %s", reply2.Secrets["passkey"]) - } -} diff --git a/nix/inputs/dms-cli/internal/server/bluez/handlers.go b/nix/inputs/dms-cli/internal/server/bluez/handlers.go deleted file mode 100644 index 4421a79..0000000 --- a/nix/inputs/dms-cli/internal/server/bluez/handlers.go +++ /dev/null @@ -1,260 +0,0 @@ -package bluez - -import ( - "encoding/json" - "fmt" - "net" - - "github.com/AvengeMedia/danklinux/internal/server/models" -) - -type Request struct { - ID int `json:"id,omitempty"` - Method string `json:"method"` - Params map[string]interface{} `json:"params,omitempty"` -} - -type SuccessResult struct { - Success bool `json:"success"` - Message string `json:"message"` -} - -type BluetoothEvent struct { - Type string `json:"type"` - Data BluetoothState `json:"data"` -} - -func HandleRequest(conn net.Conn, req Request, manager *Manager) { - switch req.Method { - case "bluetooth.getState": - handleGetState(conn, req, manager) - case "bluetooth.startDiscovery": - handleStartDiscovery(conn, req, manager) - case "bluetooth.stopDiscovery": - handleStopDiscovery(conn, req, manager) - case "bluetooth.setPowered": - handleSetPowered(conn, req, manager) - case "bluetooth.pair": - handlePairDevice(conn, req, manager) - case "bluetooth.connect": - handleConnectDevice(conn, req, manager) - case "bluetooth.disconnect": - handleDisconnectDevice(conn, req, manager) - case "bluetooth.remove": - handleRemoveDevice(conn, req, manager) - case "bluetooth.trust": - handleTrustDevice(conn, req, manager) - case "bluetooth.untrust": - handleUntrustDevice(conn, req, manager) - case "bluetooth.subscribe": - handleSubscribe(conn, req, manager) - case "bluetooth.pairing.submit": - handlePairingSubmit(conn, req, manager) - case "bluetooth.pairing.cancel": - handlePairingCancel(conn, req, manager) - default: - models.RespondError(conn, req.ID, fmt.Sprintf("unknown method: %s", req.Method)) - } -} - -func handleGetState(conn net.Conn, req Request, manager *Manager) { - state := manager.GetState() - models.Respond(conn, req.ID, state) -} - -func handleStartDiscovery(conn net.Conn, req Request, manager *Manager) { - if err := manager.StartDiscovery(); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "discovery started"}) -} - -func handleStopDiscovery(conn net.Conn, req Request, manager *Manager) { - if err := manager.StopDiscovery(); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "discovery stopped"}) -} - -func handleSetPowered(conn net.Conn, req Request, manager *Manager) { - powered, ok := req.Params["powered"].(bool) - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'powered' parameter") - return - } - - if err := manager.SetPowered(powered); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "powered state updated"}) -} - -func handlePairDevice(conn net.Conn, req Request, manager *Manager) { - devicePath, ok := req.Params["device"].(string) - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'device' parameter") - return - } - - if err := manager.PairDevice(devicePath); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "pairing initiated"}) -} - -func handleConnectDevice(conn net.Conn, req Request, manager *Manager) { - devicePath, ok := req.Params["device"].(string) - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'device' parameter") - return - } - - if err := manager.ConnectDevice(devicePath); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "connecting"}) -} - -func handleDisconnectDevice(conn net.Conn, req Request, manager *Manager) { - devicePath, ok := req.Params["device"].(string) - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'device' parameter") - return - } - - if err := manager.DisconnectDevice(devicePath); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "disconnected"}) -} - -func handleRemoveDevice(conn net.Conn, req Request, manager *Manager) { - devicePath, ok := req.Params["device"].(string) - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'device' parameter") - return - } - - if err := manager.RemoveDevice(devicePath); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "device removed"}) -} - -func handleTrustDevice(conn net.Conn, req Request, manager *Manager) { - devicePath, ok := req.Params["device"].(string) - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'device' parameter") - return - } - - if err := manager.TrustDevice(devicePath, true); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "device trusted"}) -} - -func handleUntrustDevice(conn net.Conn, req Request, manager *Manager) { - devicePath, ok := req.Params["device"].(string) - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'device' parameter") - return - } - - if err := manager.TrustDevice(devicePath, false); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "device untrusted"}) -} - -func handlePairingSubmit(conn net.Conn, req Request, manager *Manager) { - token, ok := req.Params["token"].(string) - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'token' parameter") - return - } - - secretsRaw, ok := req.Params["secrets"].(map[string]interface{}) - secrets := make(map[string]string) - if ok { - for k, v := range secretsRaw { - if str, ok := v.(string); ok { - secrets[k] = str - } - } - } - - accept := false - if acceptParam, ok := req.Params["accept"].(bool); ok { - accept = acceptParam - } - - if err := manager.SubmitPairing(token, secrets, accept); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "pairing response submitted"}) -} - -func handlePairingCancel(conn net.Conn, req Request, manager *Manager) { - token, ok := req.Params["token"].(string) - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'token' parameter") - return - } - - if err := manager.CancelPairing(token); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "pairing cancelled"}) -} - -func handleSubscribe(conn net.Conn, req Request, manager *Manager) { - clientID := fmt.Sprintf("client-%p", conn) - stateChan := manager.Subscribe(clientID) - defer manager.Unsubscribe(clientID) - - initialState := manager.GetState() - event := BluetoothEvent{ - Type: "state_changed", - Data: initialState, - } - - if err := json.NewEncoder(conn).Encode(models.Response[BluetoothEvent]{ - ID: req.ID, - Result: &event, - }); err != nil { - return - } - - for state := range stateChan { - event := BluetoothEvent{ - Type: "state_changed", - Data: state, - } - if err := json.NewEncoder(conn).Encode(models.Response[BluetoothEvent]{ - Result: &event, - }); err != nil { - return - } - } -} diff --git a/nix/inputs/dms-cli/internal/server/bluez/handlers_test.go b/nix/inputs/dms-cli/internal/server/bluez/handlers_test.go deleted file mode 100644 index 1846eb0..0000000 --- a/nix/inputs/dms-cli/internal/server/bluez/handlers_test.go +++ /dev/null @@ -1,41 +0,0 @@ -package bluez - -import ( - "context" - "testing" - "time" -) - -func TestBrokerIntegration(t *testing.T) { - broker := NewSubscriptionBroker(nil) - ctx := context.Background() - - req := PromptRequest{ - DevicePath: "/org/bluez/test", - DeviceName: "TestDevice", - RequestType: "pin", - Fields: []string{"pin"}, - } - - token, err := broker.Ask(ctx, req) - if err != nil { - t.Fatalf("Ask failed: %v", err) - } - - go func() { - time.Sleep(50 * time.Millisecond) - broker.Resolve(token, PromptReply{ - Secrets: map[string]string{"pin": "1234"}, - Accept: true, - }) - }() - - reply, err := broker.Wait(ctx, token) - if err != nil { - t.Fatalf("Wait failed: %v", err) - } - - if reply.Secrets["pin"] != "1234" { - t.Errorf("expected pin=1234, got %s", reply.Secrets["pin"]) - } -} diff --git a/nix/inputs/dms-cli/internal/server/bluez/manager.go b/nix/inputs/dms-cli/internal/server/bluez/manager.go deleted file mode 100644 index 309c492..0000000 --- a/nix/inputs/dms-cli/internal/server/bluez/manager.go +++ /dev/null @@ -1,668 +0,0 @@ -package bluez - -import ( - "fmt" - "strings" - "sync" - "time" - - "github.com/AvengeMedia/danklinux/internal/log" - "github.com/godbus/dbus/v5" -) - -const ( - adapter1Iface = "org.bluez.Adapter1" - objectMgrIface = "org.freedesktop.DBus.ObjectManager" - propertiesIface = "org.freedesktop.DBus.Properties" -) - -func NewManager() (*Manager, error) { - conn, err := dbus.ConnectSystemBus() - if err != nil { - return nil, fmt.Errorf("system bus connection failed: %w", err) - } - - m := &Manager{ - state: &BluetoothState{ - Powered: false, - Discovering: false, - Devices: []Device{}, - PairedDevices: []Device{}, - ConnectedDevices: []Device{}, - }, - stateMutex: sync.RWMutex{}, - subscribers: make(map[string]chan BluetoothState), - subMutex: sync.RWMutex{}, - stopChan: make(chan struct{}), - dbusConn: conn, - signals: make(chan *dbus.Signal, 256), - pairingSubscribers: make(map[string]chan PairingPrompt), - pairingSubMutex: sync.RWMutex{}, - dirty: make(chan struct{}, 1), - pendingPairings: make(map[string]bool), - eventQueue: make(chan func(), 32), - } - - broker := NewSubscriptionBroker(m.broadcastPairingPrompt) - m.promptBroker = broker - - adapter, err := m.findAdapter() - if err != nil { - conn.Close() - return nil, fmt.Errorf("no bluetooth adapter found: %w", err) - } - m.adapterPath = adapter - - if err := m.initialize(); err != nil { - conn.Close() - return nil, err - } - - if err := m.startAgent(); err != nil { - conn.Close() - return nil, fmt.Errorf("agent start failed: %w", err) - } - - if err := m.startSignalPump(); err != nil { - m.Close() - return nil, err - } - - m.notifierWg.Add(1) - go m.notifier() - - m.eventWg.Add(1) - go m.eventWorker() - - return m, nil -} - -func (m *Manager) findAdapter() (dbus.ObjectPath, error) { - obj := m.dbusConn.Object(bluezService, dbus.ObjectPath("/")) - var objects map[dbus.ObjectPath]map[string]map[string]dbus.Variant - - if err := obj.Call(objectMgrIface+".GetManagedObjects", 0).Store(&objects); err != nil { - return "", err - } - - for path, interfaces := range objects { - if _, ok := interfaces[adapter1Iface]; ok { - log.Infof("[BluezManager] found adapter: %s", path) - return path, nil - } - } - - return "", fmt.Errorf("no adapter found") -} - -func (m *Manager) initialize() error { - if err := m.updateAdapterState(); err != nil { - return err - } - - if err := m.updateDevices(); err != nil { - return err - } - - return nil -} - -func (m *Manager) updateAdapterState() error { - obj := m.dbusConn.Object(bluezService, m.adapterPath) - - poweredVar, err := obj.GetProperty(adapter1Iface + ".Powered") - if err != nil { - return err - } - powered, _ := poweredVar.Value().(bool) - - discoveringVar, err := obj.GetProperty(adapter1Iface + ".Discovering") - if err != nil { - return err - } - discovering, _ := discoveringVar.Value().(bool) - - m.stateMutex.Lock() - m.state.Powered = powered - m.state.Discovering = discovering - m.stateMutex.Unlock() - - return nil -} - -func (m *Manager) updateDevices() error { - obj := m.dbusConn.Object(bluezService, dbus.ObjectPath("/")) - var objects map[dbus.ObjectPath]map[string]map[string]dbus.Variant - - if err := obj.Call(objectMgrIface+".GetManagedObjects", 0).Store(&objects); err != nil { - return err - } - - devices := []Device{} - paired := []Device{} - connected := []Device{} - - for path, interfaces := range objects { - devProps, ok := interfaces[device1Iface] - if !ok { - continue - } - - if !strings.HasPrefix(string(path), string(m.adapterPath)+"/") { - continue - } - - dev := m.deviceFromProps(string(path), devProps) - devices = append(devices, dev) - - if dev.Paired { - paired = append(paired, dev) - } - if dev.Connected { - connected = append(connected, dev) - } - } - - m.stateMutex.Lock() - m.state.Devices = devices - m.state.PairedDevices = paired - m.state.ConnectedDevices = connected - m.stateMutex.Unlock() - - return nil -} - -func (m *Manager) deviceFromProps(path string, props map[string]dbus.Variant) Device { - dev := Device{Path: path} - - if v, ok := props["Address"]; ok { - if addr, ok := v.Value().(string); ok { - dev.Address = addr - } - } - if v, ok := props["Name"]; ok { - if name, ok := v.Value().(string); ok { - dev.Name = name - } - } - if v, ok := props["Alias"]; ok { - if alias, ok := v.Value().(string); ok { - dev.Alias = alias - } - } - if v, ok := props["Paired"]; ok { - if paired, ok := v.Value().(bool); ok { - dev.Paired = paired - } - } - if v, ok := props["Trusted"]; ok { - if trusted, ok := v.Value().(bool); ok { - dev.Trusted = trusted - } - } - if v, ok := props["Blocked"]; ok { - if blocked, ok := v.Value().(bool); ok { - dev.Blocked = blocked - } - } - if v, ok := props["Connected"]; ok { - if connected, ok := v.Value().(bool); ok { - dev.Connected = connected - } - } - if v, ok := props["Class"]; ok { - if class, ok := v.Value().(uint32); ok { - dev.Class = class - } - } - if v, ok := props["Icon"]; ok { - if icon, ok := v.Value().(string); ok { - dev.Icon = icon - } - } - if v, ok := props["RSSI"]; ok { - if rssi, ok := v.Value().(int16); ok { - dev.RSSI = rssi - } - } - if v, ok := props["LegacyPairing"]; ok { - if legacy, ok := v.Value().(bool); ok { - dev.LegacyPairing = legacy - } - } - - return dev -} - -func (m *Manager) startAgent() error { - if m.promptBroker == nil { - return fmt.Errorf("prompt broker not initialized") - } - - agent, err := NewBluezAgent(m.promptBroker) - if err != nil { - return err - } - - m.agent = agent - return nil -} - -func (m *Manager) startSignalPump() error { - m.dbusConn.Signal(m.signals) - - if err := m.dbusConn.AddMatchSignal( - dbus.WithMatchInterface(propertiesIface), - dbus.WithMatchMember("PropertiesChanged"), - ); err != nil { - return err - } - - if err := m.dbusConn.AddMatchSignal( - dbus.WithMatchInterface(objectMgrIface), - dbus.WithMatchMember("InterfacesAdded"), - ); err != nil { - return err - } - - if err := m.dbusConn.AddMatchSignal( - dbus.WithMatchInterface(objectMgrIface), - dbus.WithMatchMember("InterfacesRemoved"), - ); err != nil { - return err - } - - m.sigWG.Add(1) - go func() { - defer m.sigWG.Done() - for { - select { - case <-m.stopChan: - return - case sig, ok := <-m.signals: - if !ok { - return - } - if sig == nil { - continue - } - m.handleSignal(sig) - } - } - }() - - return nil -} - -func (m *Manager) handleSignal(sig *dbus.Signal) { - switch sig.Name { - case propertiesIface + ".PropertiesChanged": - if len(sig.Body) < 2 { - return - } - - iface, ok := sig.Body[0].(string) - if !ok { - return - } - - changed, ok := sig.Body[1].(map[string]dbus.Variant) - if !ok { - return - } - - switch iface { - case adapter1Iface: - if strings.HasPrefix(string(sig.Path), string(m.adapterPath)) { - m.handleAdapterPropertiesChanged(changed) - } - case device1Iface: - m.handleDevicePropertiesChanged(sig.Path, changed) - } - - case objectMgrIface + ".InterfacesAdded": - m.notifySubscribers() - - case objectMgrIface + ".InterfacesRemoved": - m.notifySubscribers() - } -} - -func (m *Manager) handleAdapterPropertiesChanged(changed map[string]dbus.Variant) { - m.stateMutex.Lock() - dirty := false - - if v, ok := changed["Powered"]; ok { - if powered, ok := v.Value().(bool); ok { - m.state.Powered = powered - dirty = true - } - } - if v, ok := changed["Discovering"]; ok { - if discovering, ok := v.Value().(bool); ok { - m.state.Discovering = discovering - dirty = true - } - } - - m.stateMutex.Unlock() - - if dirty { - m.notifySubscribers() - } -} - -func (m *Manager) handleDevicePropertiesChanged(path dbus.ObjectPath, changed map[string]dbus.Variant) { - pairedVar, hasPaired := changed["Paired"] - _, hasConnected := changed["Connected"] - _, hasTrusted := changed["Trusted"] - - if hasPaired { - if paired, ok := pairedVar.Value().(bool); ok && paired { - devicePath := string(path) - m.pendingPairingsMux.Lock() - wasPending := m.pendingPairings[devicePath] - if wasPending { - delete(m.pendingPairings, devicePath) - } - m.pendingPairingsMux.Unlock() - - if wasPending { - select { - case m.eventQueue <- func() { - time.Sleep(300 * time.Millisecond) - log.Infof("[Bluetooth] Auto-connecting newly paired device: %s", devicePath) - if err := m.ConnectDevice(devicePath); err != nil { - log.Warnf("[Bluetooth] Auto-connect failed: %v", err) - } - }: - default: - } - } - } - } - - if hasPaired || hasConnected || hasTrusted { - select { - case m.eventQueue <- func() { - time.Sleep(100 * time.Millisecond) - m.updateDevices() - m.notifySubscribers() - }: - default: - } - } -} - -func (m *Manager) eventWorker() { - defer m.eventWg.Done() - for { - select { - case <-m.stopChan: - return - case event := <-m.eventQueue: - event() - } - } -} - -func (m *Manager) notifier() { - defer m.notifierWg.Done() - const minGap = 200 * time.Millisecond - timer := time.NewTimer(minGap) - timer.Stop() - var pending bool - - for { - select { - case <-m.stopChan: - timer.Stop() - return - case <-m.dirty: - if pending { - continue - } - pending = true - timer.Reset(minGap) - case <-timer.C: - if !pending { - continue - } - m.updateDevices() - - m.subMutex.RLock() - if len(m.subscribers) == 0 { - m.subMutex.RUnlock() - pending = false - continue - } - - currentState := m.snapshotState() - - if m.lastNotifiedState != nil && !stateChanged(m.lastNotifiedState, ¤tState) { - m.subMutex.RUnlock() - pending = false - continue - } - - for _, ch := range m.subscribers { - select { - case ch <- currentState: - default: - } - } - m.subMutex.RUnlock() - - stateCopy := currentState - m.lastNotifiedState = &stateCopy - pending = false - } - } -} - -func (m *Manager) notifySubscribers() { - select { - case m.dirty <- struct{}{}: - default: - } -} - -func (m *Manager) GetState() BluetoothState { - return m.snapshotState() -} - -func (m *Manager) snapshotState() BluetoothState { - m.stateMutex.RLock() - defer m.stateMutex.RUnlock() - - s := *m.state - s.Devices = append([]Device(nil), m.state.Devices...) - s.PairedDevices = append([]Device(nil), m.state.PairedDevices...) - s.ConnectedDevices = append([]Device(nil), m.state.ConnectedDevices...) - return s -} - -func (m *Manager) Subscribe(id string) chan BluetoothState { - ch := make(chan BluetoothState, 64) - m.subMutex.Lock() - m.subscribers[id] = ch - m.subMutex.Unlock() - return ch -} - -func (m *Manager) Unsubscribe(id string) { - m.subMutex.Lock() - if ch, ok := m.subscribers[id]; ok { - close(ch) - delete(m.subscribers, id) - } - m.subMutex.Unlock() -} - -func (m *Manager) SubscribePairing(id string) chan PairingPrompt { - ch := make(chan PairingPrompt, 16) - m.pairingSubMutex.Lock() - m.pairingSubscribers[id] = ch - m.pairingSubMutex.Unlock() - return ch -} - -func (m *Manager) UnsubscribePairing(id string) { - m.pairingSubMutex.Lock() - if ch, ok := m.pairingSubscribers[id]; ok { - close(ch) - delete(m.pairingSubscribers, id) - } - m.pairingSubMutex.Unlock() -} - -func (m *Manager) broadcastPairingPrompt(prompt PairingPrompt) { - m.pairingSubMutex.RLock() - defer m.pairingSubMutex.RUnlock() - - for _, ch := range m.pairingSubscribers { - select { - case ch <- prompt: - default: - } - } -} - -func (m *Manager) SubmitPairing(token string, secrets map[string]string, accept bool) error { - if m.promptBroker == nil { - return fmt.Errorf("prompt broker not initialized") - } - - return m.promptBroker.Resolve(token, PromptReply{ - Secrets: secrets, - Accept: accept, - Cancel: false, - }) -} - -func (m *Manager) CancelPairing(token string) error { - if m.promptBroker == nil { - return fmt.Errorf("prompt broker not initialized") - } - - return m.promptBroker.Resolve(token, PromptReply{ - Cancel: true, - }) -} - -func (m *Manager) StartDiscovery() error { - obj := m.dbusConn.Object(bluezService, m.adapterPath) - return obj.Call(adapter1Iface+".StartDiscovery", 0).Err -} - -func (m *Manager) StopDiscovery() error { - obj := m.dbusConn.Object(bluezService, m.adapterPath) - return obj.Call(adapter1Iface+".StopDiscovery", 0).Err -} - -func (m *Manager) SetPowered(powered bool) error { - obj := m.dbusConn.Object(bluezService, m.adapterPath) - return obj.Call(propertiesIface+".Set", 0, adapter1Iface, "Powered", dbus.MakeVariant(powered)).Err -} - -func (m *Manager) PairDevice(devicePath string) error { - m.pendingPairingsMux.Lock() - m.pendingPairings[devicePath] = true - m.pendingPairingsMux.Unlock() - - obj := m.dbusConn.Object(bluezService, dbus.ObjectPath(devicePath)) - err := obj.Call(device1Iface+".Pair", 0).Err - - if err != nil { - m.pendingPairingsMux.Lock() - delete(m.pendingPairings, devicePath) - m.pendingPairingsMux.Unlock() - } - - return err -} - -func (m *Manager) ConnectDevice(devicePath string) error { - obj := m.dbusConn.Object(bluezService, dbus.ObjectPath(devicePath)) - return obj.Call(device1Iface+".Connect", 0).Err -} - -func (m *Manager) DisconnectDevice(devicePath string) error { - obj := m.dbusConn.Object(bluezService, dbus.ObjectPath(devicePath)) - return obj.Call(device1Iface+".Disconnect", 0).Err -} - -func (m *Manager) RemoveDevice(devicePath string) error { - obj := m.dbusConn.Object(bluezService, m.adapterPath) - return obj.Call(adapter1Iface+".RemoveDevice", 0, dbus.ObjectPath(devicePath)).Err -} - -func (m *Manager) TrustDevice(devicePath string, trusted bool) error { - obj := m.dbusConn.Object(bluezService, dbus.ObjectPath(devicePath)) - return obj.Call(propertiesIface+".Set", 0, device1Iface, "Trusted", dbus.MakeVariant(trusted)).Err -} - -func (m *Manager) Close() { - close(m.stopChan) - m.notifierWg.Wait() - m.eventWg.Wait() - - m.sigWG.Wait() - - if m.signals != nil { - m.dbusConn.RemoveSignal(m.signals) - close(m.signals) - } - - if m.agent != nil { - m.agent.Close() - } - - m.subMutex.Lock() - for _, ch := range m.subscribers { - close(ch) - } - m.subscribers = make(map[string]chan BluetoothState) - m.subMutex.Unlock() - - m.pairingSubMutex.Lock() - for _, ch := range m.pairingSubscribers { - close(ch) - } - m.pairingSubscribers = make(map[string]chan PairingPrompt) - m.pairingSubMutex.Unlock() - - if m.dbusConn != nil { - m.dbusConn.Close() - } -} - -func stateChanged(old, new *BluetoothState) bool { - if old.Powered != new.Powered { - return true - } - if old.Discovering != new.Discovering { - return true - } - if len(old.Devices) != len(new.Devices) { - return true - } - if len(old.PairedDevices) != len(new.PairedDevices) { - return true - } - if len(old.ConnectedDevices) != len(new.ConnectedDevices) { - return true - } - for i := range old.Devices { - if old.Devices[i].Path != new.Devices[i].Path { - return true - } - if old.Devices[i].Paired != new.Devices[i].Paired { - return true - } - if old.Devices[i].Connected != new.Devices[i].Connected { - return true - } - } - return false -} diff --git a/nix/inputs/dms-cli/internal/server/bluez/subscription_broker.go b/nix/inputs/dms-cli/internal/server/bluez/subscription_broker.go deleted file mode 100644 index e466866..0000000 --- a/nix/inputs/dms-cli/internal/server/bluez/subscription_broker.go +++ /dev/null @@ -1,99 +0,0 @@ -package bluez - -import ( - "context" - "fmt" - "sync" - - "github.com/AvengeMedia/danklinux/internal/errdefs" -) - -type SubscriptionBroker struct { - mu sync.RWMutex - pending map[string]chan PromptReply - requests map[string]PromptRequest - broadcastPrompt func(PairingPrompt) -} - -func NewSubscriptionBroker(broadcastPrompt func(PairingPrompt)) PromptBroker { - return &SubscriptionBroker{ - pending: make(map[string]chan PromptReply), - requests: make(map[string]PromptRequest), - broadcastPrompt: broadcastPrompt, - } -} - -func (b *SubscriptionBroker) Ask(ctx context.Context, req PromptRequest) (string, error) { - token, err := generateToken() - if err != nil { - return "", err - } - - replyChan := make(chan PromptReply, 1) - b.mu.Lock() - b.pending[token] = replyChan - b.requests[token] = req - b.mu.Unlock() - - if b.broadcastPrompt != nil { - prompt := PairingPrompt{ - Token: token, - DevicePath: req.DevicePath, - DeviceName: req.DeviceName, - DeviceAddr: req.DeviceAddr, - RequestType: req.RequestType, - Fields: req.Fields, - Hints: req.Hints, - Passkey: req.Passkey, - } - b.broadcastPrompt(prompt) - } - - return token, nil -} - -func (b *SubscriptionBroker) Wait(ctx context.Context, token string) (PromptReply, error) { - b.mu.RLock() - replyChan, exists := b.pending[token] - b.mu.RUnlock() - - if !exists { - return PromptReply{}, fmt.Errorf("unknown token: %s", token) - } - - select { - case <-ctx.Done(): - b.cleanup(token) - return PromptReply{}, errdefs.ErrSecretPromptTimeout - case reply := <-replyChan: - b.cleanup(token) - if reply.Cancel { - return reply, errdefs.ErrSecretPromptCancelled - } - return reply, nil - } -} - -func (b *SubscriptionBroker) Resolve(token string, reply PromptReply) error { - b.mu.RLock() - replyChan, exists := b.pending[token] - b.mu.RUnlock() - - if !exists { - return fmt.Errorf("unknown or expired token: %s", token) - } - - select { - case replyChan <- reply: - return nil - default: - return fmt.Errorf("failed to deliver reply for token: %s", token) - } -} - -func (b *SubscriptionBroker) cleanup(token string) { - b.mu.Lock() - delete(b.pending, token) - delete(b.requests, token) - b.mu.Unlock() -} diff --git a/nix/inputs/dms-cli/internal/server/bluez/types.go b/nix/inputs/dms-cli/internal/server/bluez/types.go deleted file mode 100644 index 6375587..0000000 --- a/nix/inputs/dms-cli/internal/server/bluez/types.go +++ /dev/null @@ -1,80 +0,0 @@ -package bluez - -import ( - "sync" - - "github.com/godbus/dbus/v5" -) - -type BluetoothState struct { - Powered bool `json:"powered"` - Discovering bool `json:"discovering"` - Devices []Device `json:"devices"` - PairedDevices []Device `json:"pairedDevices"` - ConnectedDevices []Device `json:"connectedDevices"` -} - -type Device struct { - Path string `json:"path"` - Address string `json:"address"` - Name string `json:"name"` - Alias string `json:"alias"` - Paired bool `json:"paired"` - Trusted bool `json:"trusted"` - Blocked bool `json:"blocked"` - Connected bool `json:"connected"` - Class uint32 `json:"class"` - Icon string `json:"icon"` - RSSI int16 `json:"rssi"` - LegacyPairing bool `json:"legacyPairing"` -} - -type PromptRequest struct { - DevicePath string `json:"devicePath"` - DeviceName string `json:"deviceName"` - DeviceAddr string `json:"deviceAddr"` - RequestType string `json:"requestType"` - Fields []string `json:"fields"` - Hints []string `json:"hints"` - Passkey *uint32 `json:"passkey,omitempty"` -} - -type PromptReply struct { - Secrets map[string]string `json:"secrets"` - Accept bool `json:"accept"` - Cancel bool `json:"cancel"` -} - -type PairingPrompt struct { - Token string `json:"token"` - DevicePath string `json:"devicePath"` - DeviceName string `json:"deviceName"` - DeviceAddr string `json:"deviceAddr"` - RequestType string `json:"requestType"` - Fields []string `json:"fields"` - Hints []string `json:"hints"` - Passkey *uint32 `json:"passkey,omitempty"` -} - -type Manager struct { - state *BluetoothState - stateMutex sync.RWMutex - subscribers map[string]chan BluetoothState - subMutex sync.RWMutex - stopChan chan struct{} - dbusConn *dbus.Conn - signals chan *dbus.Signal - sigWG sync.WaitGroup - agent *BluezAgent - promptBroker PromptBroker - pairingSubscribers map[string]chan PairingPrompt - pairingSubMutex sync.RWMutex - dirty chan struct{} - notifierWg sync.WaitGroup - lastNotifiedState *BluetoothState - adapterPath dbus.ObjectPath - pendingPairings map[string]bool - pendingPairingsMux sync.Mutex - eventQueue chan func() - eventWg sync.WaitGroup -} diff --git a/nix/inputs/dms-cli/internal/server/bluez/types_test.go b/nix/inputs/dms-cli/internal/server/bluez/types_test.go deleted file mode 100644 index ab5b857..0000000 --- a/nix/inputs/dms-cli/internal/server/bluez/types_test.go +++ /dev/null @@ -1,210 +0,0 @@ -package bluez - -import ( - "encoding/json" - "testing" -) - -func TestBluetoothStateJSON(t *testing.T) { - state := BluetoothState{ - Powered: true, - Discovering: false, - Devices: []Device{ - { - Path: "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF", - Address: "AA:BB:CC:DD:EE:FF", - Name: "TestDevice", - Alias: "My Device", - Paired: true, - Trusted: false, - Connected: true, - Class: 0x240418, - Icon: "audio-headset", - RSSI: -50, - }, - }, - PairedDevices: []Device{ - { - Path: "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF", - Address: "AA:BB:CC:DD:EE:FF", - Paired: true, - }, - }, - ConnectedDevices: []Device{ - { - Path: "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF", - Address: "AA:BB:CC:DD:EE:FF", - Connected: true, - }, - }, - } - - data, err := json.Marshal(state) - if err != nil { - t.Fatalf("failed to marshal state: %v", err) - } - - var decoded BluetoothState - if err := json.Unmarshal(data, &decoded); err != nil { - t.Fatalf("failed to unmarshal state: %v", err) - } - - if decoded.Powered != state.Powered { - t.Errorf("expected Powered=%v, got %v", state.Powered, decoded.Powered) - } - - if len(decoded.Devices) != 1 { - t.Fatalf("expected 1 device, got %d", len(decoded.Devices)) - } - - if decoded.Devices[0].Address != "AA:BB:CC:DD:EE:FF" { - t.Errorf("expected address AA:BB:CC:DD:EE:FF, got %s", decoded.Devices[0].Address) - } -} - -func TestDeviceJSON(t *testing.T) { - device := Device{ - Path: "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF", - Address: "AA:BB:CC:DD:EE:FF", - Name: "TestDevice", - Alias: "My Device", - Paired: true, - Trusted: true, - Blocked: false, - Connected: true, - Class: 0x240418, - Icon: "audio-headset", - RSSI: -50, - LegacyPairing: false, - } - - data, err := json.Marshal(device) - if err != nil { - t.Fatalf("failed to marshal device: %v", err) - } - - var decoded Device - if err := json.Unmarshal(data, &decoded); err != nil { - t.Fatalf("failed to unmarshal device: %v", err) - } - - if decoded.Address != device.Address { - t.Errorf("expected Address=%s, got %s", device.Address, decoded.Address) - } - - if decoded.Name != device.Name { - t.Errorf("expected Name=%s, got %s", device.Name, decoded.Name) - } - - if decoded.Paired != device.Paired { - t.Errorf("expected Paired=%v, got %v", device.Paired, decoded.Paired) - } - - if decoded.RSSI != device.RSSI { - t.Errorf("expected RSSI=%d, got %d", device.RSSI, decoded.RSSI) - } -} - -func TestPairingPromptJSON(t *testing.T) { - passkey := uint32(123456) - prompt := PairingPrompt{ - Token: "test-token", - DevicePath: "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF", - DeviceName: "TestDevice", - DeviceAddr: "AA:BB:CC:DD:EE:FF", - RequestType: "confirm", - Fields: []string{"decision"}, - Hints: []string{}, - Passkey: &passkey, - } - - data, err := json.Marshal(prompt) - if err != nil { - t.Fatalf("failed to marshal prompt: %v", err) - } - - var decoded PairingPrompt - if err := json.Unmarshal(data, &decoded); err != nil { - t.Fatalf("failed to unmarshal prompt: %v", err) - } - - if decoded.Token != prompt.Token { - t.Errorf("expected Token=%s, got %s", prompt.Token, decoded.Token) - } - - if decoded.DeviceName != prompt.DeviceName { - t.Errorf("expected DeviceName=%s, got %s", prompt.DeviceName, decoded.DeviceName) - } - - if decoded.Passkey == nil { - t.Fatal("expected non-nil Passkey") - } - - if *decoded.Passkey != *prompt.Passkey { - t.Errorf("expected Passkey=%d, got %d", *prompt.Passkey, *decoded.Passkey) - } -} - -func TestPromptReplyJSON(t *testing.T) { - reply := PromptReply{ - Secrets: map[string]string{ - "pin": "1234", - "passkey": "567890", - }, - Accept: true, - Cancel: false, - } - - data, err := json.Marshal(reply) - if err != nil { - t.Fatalf("failed to marshal reply: %v", err) - } - - var decoded PromptReply - if err := json.Unmarshal(data, &decoded); err != nil { - t.Fatalf("failed to unmarshal reply: %v", err) - } - - if decoded.Secrets["pin"] != reply.Secrets["pin"] { - t.Errorf("expected pin=%s, got %s", reply.Secrets["pin"], decoded.Secrets["pin"]) - } - - if decoded.Accept != reply.Accept { - t.Errorf("expected Accept=%v, got %v", reply.Accept, decoded.Accept) - } -} - -func TestPromptRequestJSON(t *testing.T) { - passkey := uint32(123456) - req := PromptRequest{ - DevicePath: "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF", - DeviceName: "TestDevice", - DeviceAddr: "AA:BB:CC:DD:EE:FF", - RequestType: "confirm", - Fields: []string{"decision"}, - Hints: []string{"hint1", "hint2"}, - Passkey: &passkey, - } - - data, err := json.Marshal(req) - if err != nil { - t.Fatalf("failed to marshal request: %v", err) - } - - var decoded PromptRequest - if err := json.Unmarshal(data, &decoded); err != nil { - t.Fatalf("failed to unmarshal request: %v", err) - } - - if decoded.DevicePath != req.DevicePath { - t.Errorf("expected DevicePath=%s, got %s", req.DevicePath, decoded.DevicePath) - } - - if decoded.RequestType != req.RequestType { - t.Errorf("expected RequestType=%s, got %s", req.RequestType, decoded.RequestType) - } - - if len(decoded.Fields) != len(req.Fields) { - t.Errorf("expected %d fields, got %d", len(req.Fields), len(decoded.Fields)) - } -} diff --git a/nix/inputs/dms-cli/internal/server/brightness/ddc.go b/nix/inputs/dms-cli/internal/server/brightness/ddc.go deleted file mode 100644 index 726d107..0000000 --- a/nix/inputs/dms-cli/internal/server/brightness/ddc.go +++ /dev/null @@ -1,485 +0,0 @@ -package brightness - -import ( - "encoding/binary" - "fmt" - "math" - "os" - "path/filepath" - "strings" - "syscall" - "time" - "unsafe" - - "github.com/AvengeMedia/danklinux/internal/log" - "golang.org/x/sys/unix" -) - -const ( - I2C_SLAVE = 0x0703 - DDCCI_ADDR = 0x37 - DDCCI_VCP_GET = 0x01 - DDCCI_VCP_SET = 0x03 - VCP_BRIGHTNESS = 0x10 - DDC_SOURCE_ADDR = 0x51 -) - -func NewDDCBackend() (*DDCBackend, error) { - b := &DDCBackend{ - devices: make(map[string]*ddcDevice), - scanInterval: 30 * time.Second, - debounceTimers: make(map[string]*time.Timer), - debouncePending: make(map[string]ddcPendingSet), - } - - if err := b.scanI2CDevices(); err != nil { - return nil, err - } - - return b, nil -} - -func (b *DDCBackend) scanI2CDevices() error { - b.scanMutex.Lock() - lastScan := b.lastScan - b.scanMutex.Unlock() - - if time.Since(lastScan) < b.scanInterval { - return nil - } - - b.scanMutex.Lock() - defer b.scanMutex.Unlock() - - if time.Since(b.lastScan) < b.scanInterval { - return nil - } - - b.devicesMutex.Lock() - defer b.devicesMutex.Unlock() - - b.devices = make(map[string]*ddcDevice) - - for i := 0; i < 32; i++ { - busPath := fmt.Sprintf("/dev/i2c-%d", i) - if _, err := os.Stat(busPath); os.IsNotExist(err) { - continue - } - - // Skip SMBus, GPU internal buses (e.g. AMDGPU SMU) to prevent GPU hangs - if isIgnorableI2CBus(i) { - log.Debugf("Skipping ignorable i2c-%d", i) - continue - } - - dev, err := b.probeDDCDevice(i) - if err != nil || dev == nil { - continue - } - - id := fmt.Sprintf("ddc:i2c-%d", i) - dev.id = id - b.devices[id] = dev - log.Debugf("found DDC device on i2c-%d", i) - } - - b.lastScan = time.Now() - - return nil -} - -func (b *DDCBackend) probeDDCDevice(bus int) (*ddcDevice, error) { - busPath := fmt.Sprintf("/dev/i2c-%d", bus) - - fd, err := syscall.Open(busPath, syscall.O_RDWR, 0) - if err != nil { - return nil, err - } - defer syscall.Close(fd) - - if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), I2C_SLAVE, uintptr(DDCCI_ADDR)); errno != 0 { - return nil, errno - } - - dummy := make([]byte, 32) - syscall.Read(fd, dummy) - - writebuf := []byte{0x00} - n, err := syscall.Write(fd, writebuf) - if err == nil && n == len(writebuf) { - name := b.getDDCName(bus) - dev := &ddcDevice{ - bus: bus, - addr: DDCCI_ADDR, - name: name, - } - b.readInitialBrightness(fd, dev) - return dev, nil - } - - readbuf := make([]byte, 4) - n, err = syscall.Read(fd, readbuf) - if err != nil || n == 0 { - return nil, fmt.Errorf("x37 unresponsive") - } - - name := b.getDDCName(bus) - - dev := &ddcDevice{ - bus: bus, - addr: DDCCI_ADDR, - name: name, - } - b.readInitialBrightness(fd, dev) - return dev, nil -} - -func (b *DDCBackend) getDDCName(bus int) string { - sysfsPath := fmt.Sprintf("/sys/class/i2c-adapter/i2c-%d/name", bus) - data, err := os.ReadFile(sysfsPath) - if err != nil { - return fmt.Sprintf("I2C-%d", bus) - } - - name := strings.TrimSpace(string(data)) - if name == "" { - name = fmt.Sprintf("I2C-%d", bus) - } - - return name -} - -func (b *DDCBackend) readInitialBrightness(fd int, dev *ddcDevice) { - cap, err := b.getVCPFeature(fd, VCP_BRIGHTNESS) - if err != nil { - log.Debugf("failed to read initial brightness for %s: %v", dev.name, err) - return - } - - dev.max = cap.max - dev.lastBrightness = cap.current - log.Debugf("initialized %s with brightness %d/%d", dev.name, cap.current, cap.max) -} - -func (b *DDCBackend) GetDevices() ([]Device, error) { - if err := b.scanI2CDevices(); err != nil { - log.Debugf("DDC scan error: %v", err) - } - - b.devicesMutex.Lock() - defer b.devicesMutex.Unlock() - - devices := make([]Device, 0, len(b.devices)) - - for id, dev := range b.devices { - devices = append(devices, Device{ - Class: ClassDDC, - ID: id, - Name: dev.name, - Current: dev.lastBrightness, - Max: dev.max, - CurrentPercent: dev.lastBrightness, - Backend: "ddc", - }) - } - - return devices, nil -} - -func (b *DDCBackend) SetBrightness(id string, value int, exponential bool, callback func()) error { - return b.SetBrightnessWithExponent(id, value, exponential, 1.2, callback) -} - -func (b *DDCBackend) SetBrightnessWithExponent(id string, value int, exponential bool, exponent float64, callback func()) error { - b.devicesMutex.RLock() - _, ok := b.devices[id] - b.devicesMutex.RUnlock() - - if !ok { - return fmt.Errorf("device not found: %s", id) - } - - if value < 0 || value > 100 { - return fmt.Errorf("value out of range: %d", value) - } - - b.debounceMutex.Lock() - defer b.debounceMutex.Unlock() - - b.debouncePending[id] = ddcPendingSet{ - percent: value, - callback: callback, - } - - if timer, exists := b.debounceTimers[id]; exists { - timer.Reset(200 * time.Millisecond) - } else { - b.debounceTimers[id] = time.AfterFunc(200*time.Millisecond, func() { - b.debounceMutex.Lock() - pending, exists := b.debouncePending[id] - if exists { - delete(b.debouncePending, id) - } - b.debounceMutex.Unlock() - - if !exists { - return - } - - err := b.setBrightnessImmediateWithExponent(id, pending.percent, exponential, exponent) - if err != nil { - log.Debugf("Failed to set brightness for %s: %v", id, err) - } - - if pending.callback != nil { - pending.callback() - } - }) - } - - return nil -} - -func (b *DDCBackend) setBrightnessImmediate(id string, value int, exponential bool) error { - return b.setBrightnessImmediateWithExponent(id, value, exponential, 1.2) -} - -func (b *DDCBackend) setBrightnessImmediateWithExponent(id string, value int, exponential bool, exponent float64) error { - b.devicesMutex.RLock() - dev, ok := b.devices[id] - b.devicesMutex.RUnlock() - - if !ok { - return fmt.Errorf("device not found: %s", id) - } - - busPath := fmt.Sprintf("/dev/i2c-%d", dev.bus) - - fd, err := syscall.Open(busPath, syscall.O_RDWR, 0) - if err != nil { - return fmt.Errorf("open i2c device: %w", err) - } - defer syscall.Close(fd) - - if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), I2C_SLAVE, uintptr(dev.addr)); errno != 0 { - return fmt.Errorf("set i2c slave addr: %w", errno) - } - - max := dev.max - if max == 0 { - cap, err := b.getVCPFeature(fd, VCP_BRIGHTNESS) - if err != nil { - return fmt.Errorf("get current capability: %w", err) - } - max = cap.max - b.devicesMutex.Lock() - dev.max = max - b.devicesMutex.Unlock() - } - - if err := b.setVCPFeature(fd, VCP_BRIGHTNESS, value); err != nil { - return fmt.Errorf("set vcp feature: %w", err) - } - - log.Debugf("set %s to %d/%d", id, value, max) - - b.devicesMutex.Lock() - dev.max = max - dev.lastBrightness = value - b.devicesMutex.Unlock() - - return nil -} - -func (b *DDCBackend) getVCPFeature(fd int, vcp byte) (*ddcCapability, error) { - for flushTry := 0; flushTry < 3; flushTry++ { - dummy := make([]byte, 32) - n, _ := syscall.Read(fd, dummy) - if n == 0 { - break - } - time.Sleep(20 * time.Millisecond) - } - - data := []byte{ - DDCCI_VCP_GET, - vcp, - } - - payload := []byte{ - DDC_SOURCE_ADDR, - byte(len(data)) | 0x80, - } - payload = append(payload, data...) - payload = append(payload, ddcciChecksum(payload)) - - n, err := syscall.Write(fd, payload) - if err != nil || n != len(payload) { - return nil, fmt.Errorf("write i2c: %w", err) - } - - time.Sleep(50 * time.Millisecond) - - pollFds := []unix.PollFd{ - { - Fd: int32(fd), - Events: unix.POLLIN, - }, - } - - pollTimeout := 200 - pollResult, err := unix.Poll(pollFds, pollTimeout) - if err != nil { - return nil, fmt.Errorf("poll i2c: %w", err) - } - if pollResult == 0 { - return nil, fmt.Errorf("poll timeout after %dms", pollTimeout) - } - if pollFds[0].Revents&unix.POLLIN == 0 { - return nil, fmt.Errorf("poll returned but POLLIN not set") - } - - response := make([]byte, 12) - n, err = syscall.Read(fd, response) - if err != nil || n < 8 { - return nil, fmt.Errorf("read i2c: %w", err) - } - - if response[0] != 0x6E || response[2] != 0x02 { - return nil, fmt.Errorf("invalid ddc response") - } - - resultCode := response[3] - if resultCode != 0x00 { - return nil, fmt.Errorf("vcp feature not supported") - } - - responseVCP := response[4] - if responseVCP != vcp { - return nil, fmt.Errorf("vcp mismatch: wanted 0x%02x, got 0x%02x", vcp, responseVCP) - } - - maxHigh := response[6] - maxLow := response[7] - currentHigh := response[8] - currentLow := response[9] - - max := int(binary.BigEndian.Uint16([]byte{maxHigh, maxLow})) - current := int(binary.BigEndian.Uint16([]byte{currentHigh, currentLow})) - - return &ddcCapability{ - vcp: vcp, - max: max, - current: current, - }, nil -} - -func ddcciChecksum(payload []byte) byte { - sum := byte(0x6E) - for _, b := range payload { - sum ^= b - } - return sum -} - -func (b *DDCBackend) setVCPFeature(fd int, vcp byte, value int) error { - data := []byte{ - DDCCI_VCP_SET, - vcp, - byte(value >> 8), - byte(value & 0xFF), - } - - payload := []byte{ - DDC_SOURCE_ADDR, - byte(len(data)) | 0x80, - } - payload = append(payload, data...) - payload = append(payload, ddcciChecksum(payload)) - - if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), I2C_SLAVE, uintptr(DDCCI_ADDR)); errno != 0 { - return fmt.Errorf("set i2c slave for write: %w", errno) - } - - n, err := syscall.Write(fd, payload) - if err != nil || n != len(payload) { - return fmt.Errorf("write i2c: wrote %d/%d: %w", n, len(payload), err) - } - - time.Sleep(50 * time.Millisecond) - - return nil -} - -func (b *DDCBackend) percentToValue(percent int, max int, exponential bool) int { - const minValue = 1 - - if percent == 0 { - return minValue - } - - usableRange := max - minValue - var value int - - if exponential { - const exponent = 2.0 - normalizedPercent := float64(percent) / 100.0 - hardwarePercent := math.Pow(normalizedPercent, 1.0/exponent) - value = minValue + int(math.Round(hardwarePercent*float64(usableRange))) - } else { - value = minValue + ((percent - 1) * usableRange / 99) - } - - if value < minValue { - value = minValue - } - if value > max { - value = max - } - - return value -} - -func (b *DDCBackend) valueToPercent(value int, max int, exponential bool) int { - const minValue = 1 - - if max == 0 { - return 0 - } - - if value <= minValue { - return 1 - } - - usableRange := max - minValue - if usableRange == 0 { - return 100 - } - - var percent int - - if exponential { - const exponent = 2.0 - linearPercent := 1 + ((value - minValue) * 99 / usableRange) - normalizedLinear := float64(linearPercent) / 100.0 - expPercent := math.Pow(normalizedLinear, exponent) - percent = int(math.Round(expPercent * 100.0)) - } else { - percent = 1 + ((value - minValue) * 99 / usableRange) - } - - if percent > 100 { - percent = 100 - } - if percent < 1 { - percent = 1 - } - - return percent -} - -func (b *DDCBackend) Close() { -} - -var _ = unsafe.Sizeof(0) -var _ = filepath.Join diff --git a/nix/inputs/dms-cli/internal/server/brightness/ddc_filter.go b/nix/inputs/dms-cli/internal/server/brightness/ddc_filter.go deleted file mode 100644 index 32f3dd2..0000000 --- a/nix/inputs/dms-cli/internal/server/brightness/ddc_filter.go +++ /dev/null @@ -1,135 +0,0 @@ -package brightness - -import ( - "fmt" - "os" - "path/filepath" - "strconv" - "strings" - - "github.com/AvengeMedia/danklinux/internal/log" -) - -// isIgnorableI2CBus checks if an I2C bus should be skipped during DDC probing. -// Based on ddcutil's sysfs_is_ignorable_i2c_device() (sysfs_base.c:1441) -func isIgnorableI2CBus(busno int) bool { - name := getI2CDeviceSysfsName(busno) - driver := getI2CSysfsDriver(busno) - - if name != "" && isIgnorableI2CDeviceName(name, driver) { - log.Debugf("i2c-%d: ignoring '%s' (driver: %s)", busno, name, driver) - return true - } - - // Only probe display adapters (0x03xxxx) and docking stations (0x0axxxx) - class := getI2CDeviceSysfsClass(busno) - if class != 0 { - classHigh := class & 0xFFFF0000 - ignorable := (classHigh != 0x030000 && classHigh != 0x0A0000) - if ignorable { - log.Debugf("i2c-%d: ignoring class 0x%08x", busno, class) - } - return ignorable - } - - return false -} - -// Based on ddcutil's ignorable_i2c_device_sysfs_name() (sysfs_base.c:1408) -func isIgnorableI2CDeviceName(name, driver string) bool { - ignorablePrefixes := []string{ - "SMBus", - "Synopsys DesignWare", - "soc:i2cdsi", - "smu", - "mac-io", - "u4", - "AMDGPU SMU", // AMD Navi2+ - probing hangs GPU - } - - for _, prefix := range ignorablePrefixes { - if strings.HasPrefix(name, prefix) { - return true - } - } - - // nouveau driver: only nvkm-* buses are valid - if driver == "nouveau" && !strings.HasPrefix(name, "nvkm-") { - return true - } - - return false -} - -// Based on ddcutil's get_i2c_device_sysfs_name() (sysfs_base.c:1175) -func getI2CDeviceSysfsName(busno int) string { - path := fmt.Sprintf("/sys/bus/i2c/devices/i2c-%d/name", busno) - data, err := os.ReadFile(path) - if err != nil { - return "" - } - return strings.TrimSpace(string(data)) -} - -// Based on ddcutil's get_i2c_device_sysfs_class() (sysfs_base.c:1380) -func getI2CDeviceSysfsClass(busno int) uint32 { - classPath := fmt.Sprintf("/sys/bus/i2c/devices/i2c-%d/device/class", busno) - data, err := os.ReadFile(classPath) - if err != nil { - classPath = fmt.Sprintf("/sys/bus/i2c/devices/i2c-%d/device/device/device/class", busno) - data, err = os.ReadFile(classPath) - if err != nil { - return 0 - } - } - - classStr := strings.TrimSpace(string(data)) - classStr = strings.TrimPrefix(classStr, "0x") - - class, err := strconv.ParseUint(classStr, 16, 32) - if err != nil { - return 0 - } - - return uint32(class) -} - -// Based on ddcutil's get_i2c_sysfs_driver_by_busno() (sysfs_base.c:1284) -func getI2CSysfsDriver(busno int) string { - devicePath := fmt.Sprintf("/sys/bus/i2c/devices/i2c-%d", busno) - adapterPath, err := findI2CAdapter(devicePath) - if err != nil { - return "" - } - - driverLink := filepath.Join(adapterPath, "driver") - target, err := os.Readlink(driverLink) - if err != nil { - return "" - } - - return filepath.Base(target) -} - -func findI2CAdapter(devicePath string) (string, error) { - currentPath := devicePath - - for depth := 0; depth < 10; depth++ { - if _, err := os.Stat(filepath.Join(currentPath, "name")); err == nil { - return currentPath, nil - } - - deviceLink := filepath.Join(currentPath, "device") - target, err := os.Readlink(deviceLink) - if err != nil { - break - } - - if !filepath.IsAbs(target) { - target = filepath.Join(filepath.Dir(currentPath), target) - } - currentPath = filepath.Clean(target) - } - - return "", fmt.Errorf("could not find adapter for %s", devicePath) -} diff --git a/nix/inputs/dms-cli/internal/server/brightness/ddc_filter_test.go b/nix/inputs/dms-cli/internal/server/brightness/ddc_filter_test.go deleted file mode 100644 index 5f365c6..0000000 --- a/nix/inputs/dms-cli/internal/server/brightness/ddc_filter_test.go +++ /dev/null @@ -1,123 +0,0 @@ -package brightness - -import ( - "testing" -) - -func TestIsIgnorableI2CDeviceName(t *testing.T) { - tests := []struct { - name string - deviceName string - driver string - want bool - }{ - { - name: "AMDGPU SMU should be ignored", - deviceName: "AMDGPU SMU", - driver: "amdgpu", - want: true, - }, - { - name: "SMBus should be ignored", - deviceName: "SMBus I801 adapter", - driver: "", - want: true, - }, - { - name: "Synopsys DesignWare should be ignored", - deviceName: "Synopsys DesignWare I2C adapter", - driver: "", - want: true, - }, - { - name: "smu prefix should be ignored (Mac G5)", - deviceName: "smu-i2c-controller", - driver: "", - want: true, - }, - { - name: "Regular NVIDIA DDC should not be ignored", - deviceName: "NVIDIA i2c adapter 1", - driver: "nvidia", - want: false, - }, - { - name: "nouveau nvkm bus should not be ignored", - deviceName: "nvkm-0000:01:00.0-bus-0000", - driver: "nouveau", - want: false, - }, - { - name: "nouveau non-nvkm bus should be ignored", - deviceName: "nouveau-other-bus", - driver: "nouveau", - want: true, - }, - { - name: "Regular AMD display adapter should not be ignored", - deviceName: "AMDGPU DM i2c hw bus 0", - driver: "amdgpu", - want: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := isIgnorableI2CDeviceName(tt.deviceName, tt.driver) - if got != tt.want { - t.Errorf("isIgnorableI2CDeviceName(%q, %q) = %v, want %v", - tt.deviceName, tt.driver, got, tt.want) - } - }) - } -} - -func TestClassFiltering(t *testing.T) { - tests := []struct { - name string - class uint32 - want bool - }{ - { - name: "Display adapter class should not be ignored", - class: 0x030000, - want: false, - }, - { - name: "Docking station class should not be ignored", - class: 0x0a0000, - want: false, - }, - { - name: "Display adapter with subclass should not be ignored", - class: 0x030001, - want: false, - }, - { - name: "SMBus class should be ignored", - class: 0x0c0500, - want: true, - }, - { - name: "Bridge class should be ignored", - class: 0x060400, - want: true, - }, - { - name: "Generic system peripheral should be ignored", - class: 0x088000, - want: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - // Test the class filtering logic directly - classHigh := tt.class & 0xFFFF0000 - ignorable := (classHigh != 0x030000 && classHigh != 0x0A0000) - if ignorable != tt.want { - t.Errorf("class 0x%08x: ignorable = %v, want %v", tt.class, ignorable, tt.want) - } - }) - } -} diff --git a/nix/inputs/dms-cli/internal/server/brightness/ddc_test.go b/nix/inputs/dms-cli/internal/server/brightness/ddc_test.go deleted file mode 100644 index 7f85510..0000000 --- a/nix/inputs/dms-cli/internal/server/brightness/ddc_test.go +++ /dev/null @@ -1,135 +0,0 @@ -package brightness - -import ( - "testing" -) - -func TestDDCBackend_PercentConversions(t *testing.T) { - tests := []struct { - name string - max int - percent int - wantValue int - }{ - { - name: "0% should map to minValue=1", - max: 100, - percent: 0, - wantValue: 1, - }, - { - name: "1% should be 1", - max: 100, - percent: 1, - wantValue: 1, - }, - { - name: "50% should be ~50", - max: 100, - percent: 50, - wantValue: 50, - }, - { - name: "100% should be max", - max: 100, - percent: 100, - wantValue: 100, - }, - } - - b := &DDCBackend{} - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := b.percentToValue(tt.percent, tt.max, false) - diff := got - tt.wantValue - if diff < 0 { - diff = -diff - } - if diff > 1 { - t.Errorf("percentToValue() = %v, want %v (±1)", got, tt.wantValue) - } - }) - } -} - -func TestDDCBackend_ValueToPercent(t *testing.T) { - tests := []struct { - name string - max int - value int - wantPercent int - tolerance int - }{ - { - name: "zero value should be 1%", - max: 100, - value: 0, - wantPercent: 1, - tolerance: 0, - }, - { - name: "min value should be 1%", - max: 100, - value: 1, - wantPercent: 1, - tolerance: 0, - }, - { - name: "mid value should be ~50%", - max: 100, - value: 50, - wantPercent: 50, - tolerance: 2, - }, - { - name: "max value should be 100%", - max: 100, - value: 100, - wantPercent: 100, - tolerance: 0, - }, - } - - b := &DDCBackend{} - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := b.valueToPercent(tt.value, tt.max, false) - diff := got - tt.wantPercent - if diff < 0 { - diff = -diff - } - if diff > tt.tolerance { - t.Errorf("valueToPercent() = %v, want %v (±%d)", got, tt.wantPercent, tt.tolerance) - } - }) - } -} - -func TestDDCBackend_RoundTrip(t *testing.T) { - b := &DDCBackend{} - - tests := []struct { - name string - max int - percent int - }{ - {"1%", 100, 1}, - {"25%", 100, 25}, - {"50%", 100, 50}, - {"75%", 100, 75}, - {"100%", 100, 100}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - value := b.percentToValue(tt.percent, tt.max, false) - gotPercent := b.valueToPercent(value, tt.max, false) - - if diff := tt.percent - gotPercent; diff < -1 || diff > 1 { - t.Errorf("round trip failed: wanted %d%%, got %d%% (value=%d)", tt.percent, gotPercent, value) - } - }) - } -} diff --git a/nix/inputs/dms-cli/internal/server/brightness/handlers.go b/nix/inputs/dms-cli/internal/server/brightness/handlers.go deleted file mode 100644 index 7a1e35d..0000000 --- a/nix/inputs/dms-cli/internal/server/brightness/handlers.go +++ /dev/null @@ -1,163 +0,0 @@ -package brightness - -import ( - "encoding/json" - "net" - - "github.com/AvengeMedia/danklinux/internal/server/models" -) - -func HandleRequest(conn net.Conn, req Request, m *Manager) { - switch req.Method { - case "brightness.getState": - handleGetState(conn, req, m) - case "brightness.setBrightness": - handleSetBrightness(conn, req, m) - case "brightness.increment": - handleIncrement(conn, req, m) - case "brightness.decrement": - handleDecrement(conn, req, m) - case "brightness.rescan": - handleRescan(conn, req, m) - case "brightness.subscribe": - handleSubscribe(conn, req, m) - default: - models.RespondError(conn, req.ID.(int), "unknown method: "+req.Method) - } -} - -func handleGetState(conn net.Conn, req Request, m *Manager) { - state := m.GetState() - models.Respond(conn, req.ID.(int), state) -} - -func handleSetBrightness(conn net.Conn, req Request, m *Manager) { - var params SetBrightnessParams - - device, ok := req.Params["device"].(string) - if !ok { - models.RespondError(conn, req.ID.(int), "missing or invalid device parameter") - return - } - params.Device = device - - percentFloat, ok := req.Params["percent"].(float64) - if !ok { - models.RespondError(conn, req.ID.(int), "missing or invalid percent parameter") - return - } - params.Percent = int(percentFloat) - - if exponential, ok := req.Params["exponential"].(bool); ok { - params.Exponential = exponential - } - - exponent := 1.2 - if exponentFloat, ok := req.Params["exponent"].(float64); ok { - params.Exponent = exponentFloat - exponent = exponentFloat - } - - if err := m.SetBrightnessWithExponent(params.Device, params.Percent, params.Exponential, exponent); err != nil { - models.RespondError(conn, req.ID.(int), err.Error()) - return - } - - state := m.GetState() - models.Respond(conn, req.ID.(int), state) -} - -func handleIncrement(conn net.Conn, req Request, m *Manager) { - device, ok := req.Params["device"].(string) - if !ok { - models.RespondError(conn, req.ID.(int), "missing or invalid device parameter") - return - } - - step := 10 - if stepFloat, ok := req.Params["step"].(float64); ok { - step = int(stepFloat) - } - - exponential := false - if expBool, ok := req.Params["exponential"].(bool); ok { - exponential = expBool - } - - exponent := 1.2 - if exponentFloat, ok := req.Params["exponent"].(float64); ok { - exponent = exponentFloat - } - - if err := m.IncrementBrightnessWithExponent(device, step, exponential, exponent); err != nil { - models.RespondError(conn, req.ID.(int), err.Error()) - return - } - - state := m.GetState() - models.Respond(conn, req.ID.(int), state) -} - -func handleDecrement(conn net.Conn, req Request, m *Manager) { - device, ok := req.Params["device"].(string) - if !ok { - models.RespondError(conn, req.ID.(int), "missing or invalid device parameter") - return - } - - step := 10 - if stepFloat, ok := req.Params["step"].(float64); ok { - step = int(stepFloat) - } - - exponential := false - if expBool, ok := req.Params["exponential"].(bool); ok { - exponential = expBool - } - - exponent := 1.2 - if exponentFloat, ok := req.Params["exponent"].(float64); ok { - exponent = exponentFloat - } - - if err := m.IncrementBrightnessWithExponent(device, -step, exponential, exponent); err != nil { - models.RespondError(conn, req.ID.(int), err.Error()) - return - } - - state := m.GetState() - models.Respond(conn, req.ID.(int), state) -} - -func handleRescan(conn net.Conn, req Request, m *Manager) { - m.Rescan() - state := m.GetState() - models.Respond(conn, req.ID.(int), state) -} - -func handleSubscribe(conn net.Conn, req Request, m *Manager) { - clientID := "brightness-subscriber" - if idStr, ok := req.ID.(string); ok && idStr != "" { - clientID = idStr - } - - ch := m.Subscribe(clientID) - defer m.Unsubscribe(clientID) - - initialState := m.GetState() - if err := json.NewEncoder(conn).Encode(models.Response[State]{ - ID: req.ID.(int), - Result: &initialState, - }); err != nil { - return - } - - for state := range ch { - if err := json.NewEncoder(conn).Encode(models.Response[State]{ - ID: req.ID.(int), - Result: &state, - }); err != nil { - return - } - } -} diff --git a/nix/inputs/dms-cli/internal/server/brightness/logind.go b/nix/inputs/dms-cli/internal/server/brightness/logind.go deleted file mode 100644 index 57c6b39..0000000 --- a/nix/inputs/dms-cli/internal/server/brightness/logind.go +++ /dev/null @@ -1,67 +0,0 @@ -package brightness - -import ( - "fmt" - - "github.com/AvengeMedia/danklinux/internal/log" - "github.com/godbus/dbus/v5" -) - -type DBusConn interface { - Object(dest string, path dbus.ObjectPath) dbus.BusObject - Close() error -} - -type LogindBackend struct { - conn DBusConn - connOnce bool -} - -func NewLogindBackend() (*LogindBackend, error) { - conn, err := dbus.ConnectSystemBus() - if err != nil { - return nil, fmt.Errorf("connect to system bus: %w", err) - } - - obj := conn.Object("org.freedesktop.login1", "/org/freedesktop/login1/session/auto") - call := obj.Call("org.freedesktop.DBus.Peer.Ping", 0) - if call.Err != nil { - conn.Close() - return nil, fmt.Errorf("logind not available: %w", call.Err) - } - - conn.Close() - - return &LogindBackend{}, nil -} - -func NewLogindBackendWithConn(conn DBusConn) *LogindBackend { - return &LogindBackend{ - conn: conn, - } -} - -func (b *LogindBackend) SetBrightness(subsystem, name string, brightness uint32) error { - if b.conn == nil { - conn, err := dbus.ConnectSystemBus() - if err != nil { - return fmt.Errorf("connect to system bus: %w", err) - } - b.conn = conn - } - - obj := b.conn.Object("org.freedesktop.login1", "/org/freedesktop/login1/session/auto") - call := obj.Call("org.freedesktop.login1.Session.SetBrightness", 0, subsystem, name, brightness) - if call.Err != nil { - return fmt.Errorf("dbus call failed: %w", call.Err) - } - - log.Debugf("logind: set %s/%s to %d", subsystem, name, brightness) - return nil -} - -func (b *LogindBackend) Close() { - if b.conn != nil { - b.conn.Close() - } -} diff --git a/nix/inputs/dms-cli/internal/server/brightness/logind_test.go b/nix/inputs/dms-cli/internal/server/brightness/logind_test.go deleted file mode 100644 index 74ecb9d..0000000 --- a/nix/inputs/dms-cli/internal/server/brightness/logind_test.go +++ /dev/null @@ -1,95 +0,0 @@ -package brightness - -import ( - "errors" - "testing" - - mocks_brightness "github.com/AvengeMedia/danklinux/internal/mocks/brightness" - mock_dbus "github.com/AvengeMedia/danklinux/internal/mocks/github.com/godbus/dbus/v5" - "github.com/godbus/dbus/v5" - "github.com/stretchr/testify/mock" -) - -func TestLogindBackend_SetBrightness_Success(t *testing.T) { - mockConn := mocks_brightness.NewMockDBusConn(t) - mockObj := mock_dbus.NewMockBusObject(t) - - backend := NewLogindBackendWithConn(mockConn) - - mockConn.EXPECT(). - Object("org.freedesktop.login1", dbus.ObjectPath("/org/freedesktop/login1/session/auto")). - Return(mockObj). - Once() - - mockObj.EXPECT(). - Call("org.freedesktop.login1.Session.SetBrightness", dbus.Flags(0), "backlight", "nvidia_0", uint32(75)). - Return(&dbus.Call{Err: nil}). - Once() - - err := backend.SetBrightness("backlight", "nvidia_0", 75) - if err != nil { - t.Errorf("SetBrightness() error = %v, want nil", err) - } -} - -func TestLogindBackend_SetBrightness_DBusError(t *testing.T) { - mockConn := mocks_brightness.NewMockDBusConn(t) - mockObj := mock_dbus.NewMockBusObject(t) - - backend := NewLogindBackendWithConn(mockConn) - - mockConn.EXPECT(). - Object("org.freedesktop.login1", dbus.ObjectPath("/org/freedesktop/login1/session/auto")). - Return(mockObj). - Once() - - dbusErr := errors.New("permission denied") - mockObj.EXPECT(). - Call("org.freedesktop.login1.Session.SetBrightness", mock.Anything, mock.Anything, mock.Anything, mock.Anything). - Return(&dbus.Call{Err: dbusErr}). - Once() - - err := backend.SetBrightness("backlight", "test_device", 50) - if err == nil { - t.Error("SetBrightness() error = nil, want error") - } -} - -func TestLogindBackend_SetBrightness_LEDDevice(t *testing.T) { - mockConn := mocks_brightness.NewMockDBusConn(t) - mockObj := mock_dbus.NewMockBusObject(t) - - backend := NewLogindBackendWithConn(mockConn) - - mockConn.EXPECT(). - Object("org.freedesktop.login1", dbus.ObjectPath("/org/freedesktop/login1/session/auto")). - Return(mockObj). - Once() - - mockObj.EXPECT(). - Call("org.freedesktop.login1.Session.SetBrightness", dbus.Flags(0), "leds", "test_led", uint32(128)). - Return(&dbus.Call{Err: nil}). - Once() - - err := backend.SetBrightness("leds", "test_led", 128) - if err != nil { - t.Errorf("SetBrightness() error = %v, want nil", err) - } -} - -func TestLogindBackend_Close(t *testing.T) { - mockConn := mocks_brightness.NewMockDBusConn(t) - backend := NewLogindBackendWithConn(mockConn) - - mockConn.EXPECT(). - Close(). - Return(nil). - Once() - - backend.Close() -} - -func TestLogindBackend_Close_NilConn(t *testing.T) { - backend := &LogindBackend{conn: nil} - backend.Close() -} diff --git a/nix/inputs/dms-cli/internal/server/brightness/manager.go b/nix/inputs/dms-cli/internal/server/brightness/manager.go deleted file mode 100644 index 96351fc..0000000 --- a/nix/inputs/dms-cli/internal/server/brightness/manager.go +++ /dev/null @@ -1,383 +0,0 @@ -package brightness - -import ( - "fmt" - "sort" - "strings" - "time" - - "github.com/AvengeMedia/danklinux/internal/log" -) - -func NewManager() (*Manager, error) { - return NewManagerWithOptions(false) -} - -func NewManagerWithOptions(exponential bool) (*Manager, error) { - m := &Manager{ - subscribers: make(map[string]chan State), - updateSubscribers: make(map[string]chan DeviceUpdate), - stopChan: make(chan struct{}), - exponential: exponential, - } - - go m.initLogind() - go m.initSysfs() - go m.initDDC() - - return m, nil -} - -func (m *Manager) initLogind() { - log.Debug("Initializing logind backend...") - logind, err := NewLogindBackend() - if err != nil { - log.Infof("Logind backend not available: %v", err) - log.Info("Will use direct sysfs access for brightness control") - return - } - - m.logindBackend = logind - m.logindReady = true - log.Info("Logind backend initialized - will use for brightness control") -} - -func (m *Manager) initSysfs() { - log.Debug("Initializing sysfs backend...") - sysfs, err := NewSysfsBackend() - if err != nil { - log.Warnf("Failed to initialize sysfs backend: %v", err) - return - } - - devices, err := sysfs.GetDevices() - if err != nil { - log.Warnf("Failed to get initial sysfs devices: %v", err) - m.sysfsBackend = sysfs - m.sysfsReady = true - m.updateState() - return - } - - log.Infof("Sysfs backend initialized with %d devices", len(devices)) - for _, d := range devices { - log.Debugf(" - %s: %s (%d%%)", d.ID, d.Name, d.CurrentPercent) - } - - m.sysfsBackend = sysfs - m.sysfsReady = true - m.updateState() -} - -func (m *Manager) initDDC() { - ddc, err := NewDDCBackend() - if err != nil { - log.Debugf("Failed to initialize DDC backend: %v", err) - return - } - - m.ddcBackend = ddc - m.ddcReady = true - log.Info("DDC backend initialized") - - m.updateState() -} - -func (m *Manager) Rescan() { - log.Debug("Rescanning brightness devices...") - m.updateState() -} - -func sortDevices(devices []Device) { - sort.Slice(devices, func(i, j int) bool { - classOrder := map[DeviceClass]int{ - ClassBacklight: 0, - ClassDDC: 1, - ClassLED: 2, - } - - orderI := classOrder[devices[i].Class] - orderJ := classOrder[devices[j].Class] - - if orderI != orderJ { - return orderI < orderJ - } - - return devices[i].Name < devices[j].Name - }) -} - -func stateChanged(old, new State) bool { - if len(old.Devices) != len(new.Devices) { - return true - } - - oldMap := make(map[string]Device) - for _, d := range old.Devices { - oldMap[d.ID] = d - } - - for _, newDev := range new.Devices { - oldDev, exists := oldMap[newDev.ID] - if !exists { - return true - } - if oldDev.Current != newDev.Current || oldDev.Max != newDev.Max { - return true - } - } - - return false -} - -func (m *Manager) updateState() { - allDevices := make([]Device, 0) - - if m.sysfsReady && m.sysfsBackend != nil { - devices, err := m.sysfsBackend.GetDevices() - if err != nil { - log.Debugf("Failed to get sysfs devices: %v", err) - } - if err == nil { - allDevices = append(allDevices, devices...) - } - } - - if m.ddcReady && m.ddcBackend != nil { - devices, err := m.ddcBackend.GetDevices() - if err != nil { - log.Debugf("Failed to get DDC devices: %v", err) - } - if err == nil { - allDevices = append(allDevices, devices...) - } - } - - sortDevices(allDevices) - - m.stateMutex.Lock() - oldState := m.state - newState := State{Devices: allDevices} - - if !stateChanged(oldState, newState) { - m.stateMutex.Unlock() - return - } - - m.state = newState - m.stateMutex.Unlock() - log.Debugf("State changed, notifying subscribers") - m.NotifySubscribers() -} - -func (m *Manager) SetBrightness(deviceID string, percent int) error { - return m.SetBrightnessWithMode(deviceID, percent, m.exponential) -} - -func (m *Manager) SetBrightnessWithMode(deviceID string, percent int, exponential bool) error { - return m.SetBrightnessWithExponent(deviceID, percent, exponential, 1.2) -} - -func (m *Manager) SetBrightnessWithExponent(deviceID string, percent int, exponential bool, exponent float64) error { - if percent < 0 || percent > 100 { - return fmt.Errorf("percent out of range: %d", percent) - } - - log.Debugf("SetBrightness: %s to %d%%", deviceID, percent) - - m.stateMutex.Lock() - currentState := m.state - var found bool - var deviceClass DeviceClass - var deviceIndex int - - log.Debugf("Current state has %d devices", len(currentState.Devices)) - - for i, dev := range currentState.Devices { - if dev.ID == deviceID { - found = true - deviceClass = dev.Class - deviceIndex = i - break - } - } - - if !found { - m.stateMutex.Unlock() - log.Debugf("Device not found in state: %s", deviceID) - return fmt.Errorf("device not found: %s", deviceID) - } - - newDevices := make([]Device, len(currentState.Devices)) - copy(newDevices, currentState.Devices) - newDevices[deviceIndex].CurrentPercent = percent - m.state = State{Devices: newDevices} - m.stateMutex.Unlock() - - var err error - if deviceClass == ClassDDC { - log.Debugf("Calling DDC backend for %s", deviceID) - err = m.ddcBackend.SetBrightnessWithExponent(deviceID, percent, exponential, exponent, func() { - m.updateState() - m.debouncedBroadcast(deviceID) - }) - } else if m.logindReady && m.logindBackend != nil { - log.Debugf("Calling logind backend for %s", deviceID) - err = m.setViaSysfsWithLogindWithExponent(deviceID, percent, exponential, exponent) - } else { - log.Debugf("Calling sysfs backend for %s", deviceID) - err = m.sysfsBackend.SetBrightnessWithExponent(deviceID, percent, exponential, exponent) - } - - if err != nil { - m.updateState() - return fmt.Errorf("failed to set brightness: %w", err) - } - - if deviceClass != ClassDDC { - log.Debugf("Queueing broadcast for %s", deviceID) - m.debouncedBroadcast(deviceID) - } - return nil -} - -func (m *Manager) IncrementBrightness(deviceID string, step int) error { - return m.IncrementBrightnessWithMode(deviceID, step, m.exponential) -} - -func (m *Manager) IncrementBrightnessWithMode(deviceID string, step int, exponential bool) error { - return m.IncrementBrightnessWithExponent(deviceID, step, exponential, 1.2) -} - -func (m *Manager) IncrementBrightnessWithExponent(deviceID string, step int, exponential bool, exponent float64) error { - m.stateMutex.RLock() - currentState := m.state - m.stateMutex.RUnlock() - - var currentPercent int - var found bool - - for _, dev := range currentState.Devices { - if dev.ID == deviceID { - currentPercent = dev.CurrentPercent - found = true - break - } - } - - if !found { - return fmt.Errorf("device not found: %s", deviceID) - } - - newPercent := currentPercent + step - if newPercent > 100 { - newPercent = 100 - } - if newPercent < 0 { - newPercent = 0 - } - - return m.SetBrightnessWithExponent(deviceID, newPercent, exponential, exponent) -} - -func (m *Manager) DecrementBrightness(deviceID string, step int) error { - return m.IncrementBrightness(deviceID, -step) -} - -func (m *Manager) setViaSysfsWithLogind(deviceID string, percent int, exponential bool) error { - return m.setViaSysfsWithLogindWithExponent(deviceID, percent, exponential, 1.2) -} - -func (m *Manager) setViaSysfsWithLogindWithExponent(deviceID string, percent int, exponential bool, exponent float64) error { - parts := strings.SplitN(deviceID, ":", 2) - if len(parts) != 2 { - return fmt.Errorf("invalid device id: %s", deviceID) - } - - subsystem := parts[0] - name := parts[1] - - dev, err := m.sysfsBackend.GetDevice(deviceID) - if err != nil { - return err - } - - value := m.sysfsBackend.PercentToValueWithExponent(percent, dev, exponential, exponent) - - if m.logindBackend == nil { - return m.sysfsBackend.SetBrightnessWithExponent(deviceID, percent, exponential, exponent) - } - - err = m.logindBackend.SetBrightness(subsystem, name, uint32(value)) - if err != nil { - log.Debugf("logind SetBrightness failed, falling back to direct sysfs: %v", err) - return m.sysfsBackend.SetBrightnessWithExponent(deviceID, percent, exponential, exponent) - } - - log.Debugf("set %s to %d%% (%d/%d) via logind", deviceID, percent, value, dev.maxBrightness) - return nil -} - -func (m *Manager) debouncedBroadcast(deviceID string) { - m.broadcastMutex.Lock() - defer m.broadcastMutex.Unlock() - - m.broadcastPending = true - m.pendingDeviceID = deviceID - - if m.broadcastTimer == nil { - m.broadcastTimer = time.AfterFunc(150*time.Millisecond, func() { - m.broadcastMutex.Lock() - pending := m.broadcastPending - deviceID := m.pendingDeviceID - m.broadcastPending = false - m.pendingDeviceID = "" - m.broadcastMutex.Unlock() - - if !pending || deviceID == "" { - return - } - - m.broadcastDeviceUpdate(deviceID) - }) - } else { - m.broadcastTimer.Reset(150 * time.Millisecond) - } -} - -func (m *Manager) broadcastDeviceUpdate(deviceID string) { - m.stateMutex.RLock() - var targetDevice *Device - for _, dev := range m.state.Devices { - if dev.ID == deviceID { - devCopy := dev - targetDevice = &devCopy - break - } - } - m.stateMutex.RUnlock() - - if targetDevice == nil { - log.Debugf("Device not found for broadcast: %s", deviceID) - return - } - - update := DeviceUpdate{Device: *targetDevice} - - m.subMutex.RLock() - defer m.subMutex.RUnlock() - - if len(m.updateSubscribers) == 0 { - log.Debugf("No update subscribers for device: %s", deviceID) - return - } - - log.Debugf("Broadcasting device update: %s at %d%%", deviceID, targetDevice.CurrentPercent) - - for _, ch := range m.updateSubscribers { - select { - case ch <- update: - default: - } - } -} diff --git a/nix/inputs/dms-cli/internal/server/brightness/manager_test.go b/nix/inputs/dms-cli/internal/server/brightness/manager_test.go deleted file mode 100644 index a8ae0f8..0000000 --- a/nix/inputs/dms-cli/internal/server/brightness/manager_test.go +++ /dev/null @@ -1,11 +0,0 @@ -package brightness - -import ( - "testing" -) - -// Manager tests can be added here as needed -func TestManager_Placeholder(t *testing.T) { - // Placeholder test to keep the test file valid - t.Skip("No tests implemented yet") -} diff --git a/nix/inputs/dms-cli/internal/server/brightness/sysfs.go b/nix/inputs/dms-cli/internal/server/brightness/sysfs.go deleted file mode 100644 index 21a04da..0000000 --- a/nix/inputs/dms-cli/internal/server/brightness/sysfs.go +++ /dev/null @@ -1,272 +0,0 @@ -package brightness - -import ( - "fmt" - "math" - "os" - "path/filepath" - "strconv" - "strings" - - "github.com/AvengeMedia/danklinux/internal/log" -) - -func NewSysfsBackend() (*SysfsBackend, error) { - b := &SysfsBackend{ - basePath: "/sys/class", - classes: []string{"backlight", "leds"}, - deviceCache: make(map[string]*sysfsDevice), - } - - if err := b.scanDevices(); err != nil { - return nil, err - } - - return b, nil -} - -func (b *SysfsBackend) scanDevices() error { - b.deviceCacheMutex.Lock() - defer b.deviceCacheMutex.Unlock() - - for _, class := range b.classes { - classPath := filepath.Join(b.basePath, class) - entries, err := os.ReadDir(classPath) - if err != nil { - if os.IsNotExist(err) { - continue - } - return fmt.Errorf("read %s: %w", classPath, err) - } - - for _, entry := range entries { - devicePath := filepath.Join(classPath, entry.Name()) - - stat, err := os.Stat(devicePath) - if err != nil || !stat.IsDir() { - continue - } - maxPath := filepath.Join(devicePath, "max_brightness") - - maxData, err := os.ReadFile(maxPath) - if err != nil { - log.Debugf("skip %s/%s: no max_brightness", class, entry.Name()) - continue - } - - maxBrightness, err := strconv.Atoi(strings.TrimSpace(string(maxData))) - if err != nil || maxBrightness <= 0 { - log.Debugf("skip %s/%s: invalid max_brightness", class, entry.Name()) - continue - } - - deviceClass := ClassBacklight - minValue := 1 - if class == "leds" { - deviceClass = ClassLED - minValue = 0 - } - - deviceID := fmt.Sprintf("%s:%s", class, entry.Name()) - b.deviceCache[deviceID] = &sysfsDevice{ - class: deviceClass, - id: deviceID, - name: entry.Name(), - maxBrightness: maxBrightness, - minValue: minValue, - } - - log.Debugf("found %s device: %s (max=%d)", class, entry.Name(), maxBrightness) - } - } - - return nil -} - -func shouldSuppressDevice(name string) bool { - if strings.HasSuffix(name, "::lan") { - return true - } - - keyboardLEDs := []string{ - "::scrolllock", - "::capslock", - "::numlock", - "::kana", - "::compose", - } - - for _, suffix := range keyboardLEDs { - if strings.HasSuffix(name, suffix) { - return true - } - } - - return false -} - -func (b *SysfsBackend) GetDevices() ([]Device, error) { - b.deviceCacheMutex.RLock() - defer b.deviceCacheMutex.RUnlock() - - devices := make([]Device, 0, len(b.deviceCache)) - - for _, dev := range b.deviceCache { - if shouldSuppressDevice(dev.name) { - continue - } - - parts := strings.SplitN(dev.id, ":", 2) - if len(parts) != 2 { - continue - } - - class := parts[0] - name := parts[1] - - devicePath := filepath.Join(b.basePath, class, name) - brightnessPath := filepath.Join(devicePath, "brightness") - - brightnessData, err := os.ReadFile(brightnessPath) - if err != nil { - log.Debugf("failed to read brightness for %s: %v", dev.id, err) - continue - } - - current, err := strconv.Atoi(strings.TrimSpace(string(brightnessData))) - if err != nil { - log.Debugf("failed to parse brightness for %s: %v", dev.id, err) - continue - } - - percent := b.ValueToPercent(current, dev, false) - - devices = append(devices, Device{ - Class: dev.class, - ID: dev.id, - Name: dev.name, - Current: current, - Max: dev.maxBrightness, - CurrentPercent: percent, - Backend: "sysfs", - }) - } - - return devices, nil -} - -func (b *SysfsBackend) GetDevice(id string) (*sysfsDevice, error) { - b.deviceCacheMutex.RLock() - defer b.deviceCacheMutex.RUnlock() - - dev, ok := b.deviceCache[id] - if !ok { - return nil, fmt.Errorf("device not found: %s", id) - } - - return dev, nil -} - -func (b *SysfsBackend) SetBrightness(id string, percent int, exponential bool) error { - return b.SetBrightnessWithExponent(id, percent, exponential, 1.2) -} - -func (b *SysfsBackend) SetBrightnessWithExponent(id string, percent int, exponential bool, exponent float64) error { - dev, err := b.GetDevice(id) - if err != nil { - return err - } - - if percent < 0 || percent > 100 { - return fmt.Errorf("percent out of range: %d", percent) - } - - value := b.PercentToValueWithExponent(percent, dev, exponential, exponent) - - parts := strings.SplitN(id, ":", 2) - if len(parts) != 2 { - return fmt.Errorf("invalid device id: %s", id) - } - - class := parts[0] - name := parts[1] - - devicePath := filepath.Join(b.basePath, class, name) - brightnessPath := filepath.Join(devicePath, "brightness") - - data := []byte(fmt.Sprintf("%d", value)) - if err := os.WriteFile(brightnessPath, data, 0644); err != nil { - return fmt.Errorf("write brightness: %w", err) - } - - log.Debugf("set %s to %d%% (%d/%d) via direct sysfs", id, percent, value, dev.maxBrightness) - - return nil -} - -func (b *SysfsBackend) PercentToValue(percent int, dev *sysfsDevice, exponential bool) int { - return b.PercentToValueWithExponent(percent, dev, exponential, 1.2) -} - -func (b *SysfsBackend) PercentToValueWithExponent(percent int, dev *sysfsDevice, exponential bool, exponent float64) int { - if percent == 0 { - return dev.minValue - } - - usableRange := dev.maxBrightness - dev.minValue - var value int - - if exponential { - normalizedPercent := float64(percent) / 100.0 - hardwarePercent := math.Pow(normalizedPercent, exponent) - value = dev.minValue + int(math.Round(hardwarePercent*float64(usableRange))) - } else { - value = dev.minValue + ((percent - 1) * usableRange / 99) - } - - if value < dev.minValue { - value = dev.minValue - } - if value > dev.maxBrightness { - value = dev.maxBrightness - } - - return value -} - -func (b *SysfsBackend) ValueToPercent(value int, dev *sysfsDevice, exponential bool) int { - return b.ValueToPercentWithExponent(value, dev, exponential, 1.2) -} - -func (b *SysfsBackend) ValueToPercentWithExponent(value int, dev *sysfsDevice, exponential bool, exponent float64) int { - if value <= dev.minValue { - if dev.minValue == 0 && value == 0 { - return 0 - } - return 1 - } - - usableRange := dev.maxBrightness - dev.minValue - if usableRange == 0 { - return 100 - } - - var percent int - - if exponential { - hardwarePercent := float64(value-dev.minValue) / float64(usableRange) - normalizedPercent := math.Pow(hardwarePercent, 1.0/exponent) - percent = int(math.Round(normalizedPercent * 100.0)) - } else { - percent = 1 + ((value - dev.minValue) * 99 / usableRange) - } - - if percent > 100 { - percent = 100 - } - if percent < 1 { - percent = 1 - } - - return percent -} diff --git a/nix/inputs/dms-cli/internal/server/brightness/sysfs_logind_test.go b/nix/inputs/dms-cli/internal/server/brightness/sysfs_logind_test.go deleted file mode 100644 index 58dd40e..0000000 --- a/nix/inputs/dms-cli/internal/server/brightness/sysfs_logind_test.go +++ /dev/null @@ -1,290 +0,0 @@ -package brightness - -import ( - "os" - "path/filepath" - "testing" - - mocks_brightness "github.com/AvengeMedia/danklinux/internal/mocks/brightness" - mock_dbus "github.com/AvengeMedia/danklinux/internal/mocks/github.com/godbus/dbus/v5" - "github.com/godbus/dbus/v5" - "github.com/stretchr/testify/mock" -) - -func TestManager_SetBrightness_LogindSuccess(t *testing.T) { - tmpDir := t.TempDir() - - backlightDir := filepath.Join(tmpDir, "backlight", "test_backlight") - if err := os.MkdirAll(backlightDir, 0755); err != nil { - t.Fatal(err) - } - if err := os.WriteFile(filepath.Join(backlightDir, "max_brightness"), []byte("100\n"), 0644); err != nil { - t.Fatal(err) - } - if err := os.WriteFile(filepath.Join(backlightDir, "brightness"), []byte("50\n"), 0644); err != nil { - t.Fatal(err) - } - - mockConn := mocks_brightness.NewMockDBusConn(t) - mockObj := mock_dbus.NewMockBusObject(t) - - mockLogind := NewLogindBackendWithConn(mockConn) - - sysfs := &SysfsBackend{ - basePath: tmpDir, - classes: []string{"backlight"}, - deviceCache: make(map[string]*sysfsDevice), - } - - if err := sysfs.scanDevices(); err != nil { - t.Fatal(err) - } - - m := &Manager{ - logindBackend: mockLogind, - sysfsBackend: sysfs, - logindReady: true, - sysfsReady: true, - subscribers: make(map[string]chan State), - updateSubscribers: make(map[string]chan DeviceUpdate), - stopChan: make(chan struct{}), - } - - m.state = State{ - Devices: []Device{ - { - Class: ClassBacklight, - ID: "backlight:test_backlight", - Name: "test_backlight", - Current: 50, - Max: 100, - CurrentPercent: 50, - Backend: "sysfs", - }, - }, - } - - mockConn.EXPECT(). - Object("org.freedesktop.login1", dbus.ObjectPath("/org/freedesktop/login1/session/auto")). - Return(mockObj). - Once() - - mockObj.EXPECT(). - Call("org.freedesktop.login1.Session.SetBrightness", mock.Anything, "backlight", "test_backlight", uint32(75)). - Return(&dbus.Call{Err: nil}). - Once() - - err := m.SetBrightness("backlight:test_backlight", 75) - if err != nil { - t.Errorf("SetBrightness() with logind error = %v, want nil", err) - } - - data, _ := os.ReadFile(filepath.Join(backlightDir, "brightness")) - if string(data) == "75\n" { - t.Error("Direct sysfs write occurred when logind should have been used") - } -} - -func TestManager_SetBrightness_LogindFailsFallbackToSysfs(t *testing.T) { - tmpDir := t.TempDir() - - backlightDir := filepath.Join(tmpDir, "backlight", "test_backlight") - if err := os.MkdirAll(backlightDir, 0755); err != nil { - t.Fatal(err) - } - if err := os.WriteFile(filepath.Join(backlightDir, "max_brightness"), []byte("100\n"), 0644); err != nil { - t.Fatal(err) - } - if err := os.WriteFile(filepath.Join(backlightDir, "brightness"), []byte("50\n"), 0644); err != nil { - t.Fatal(err) - } - - mockConn := mocks_brightness.NewMockDBusConn(t) - mockObj := mock_dbus.NewMockBusObject(t) - - mockLogind := NewLogindBackendWithConn(mockConn) - - sysfs := &SysfsBackend{ - basePath: tmpDir, - classes: []string{"backlight"}, - deviceCache: make(map[string]*sysfsDevice), - } - - if err := sysfs.scanDevices(); err != nil { - t.Fatal(err) - } - - m := &Manager{ - logindBackend: mockLogind, - sysfsBackend: sysfs, - logindReady: true, - sysfsReady: true, - subscribers: make(map[string]chan State), - updateSubscribers: make(map[string]chan DeviceUpdate), - stopChan: make(chan struct{}), - } - - m.state = State{ - Devices: []Device{ - { - Class: ClassBacklight, - ID: "backlight:test_backlight", - Name: "test_backlight", - Current: 50, - Max: 100, - CurrentPercent: 50, - Backend: "sysfs", - }, - }, - } - - mockConn.EXPECT(). - Object("org.freedesktop.login1", dbus.ObjectPath("/org/freedesktop/login1/session/auto")). - Return(mockObj). - Once() - - mockObj.EXPECT(). - Call("org.freedesktop.login1.Session.SetBrightness", mock.Anything, "backlight", "test_backlight", mock.Anything). - Return(&dbus.Call{Err: dbus.ErrMsgNoObject}). - Once() - - err := m.SetBrightness("backlight:test_backlight", 75) - if err != nil { - t.Errorf("SetBrightness() with fallback error = %v, want nil", err) - } - - data, _ := os.ReadFile(filepath.Join(backlightDir, "brightness")) - brightness := string(data) - if brightness != "75" { - t.Errorf("Fallback sysfs write did not occur, got brightness = %q, want %q", brightness, "75") - } -} - -func TestManager_SetBrightness_NoLogind(t *testing.T) { - tmpDir := t.TempDir() - - backlightDir := filepath.Join(tmpDir, "backlight", "test_backlight") - if err := os.MkdirAll(backlightDir, 0755); err != nil { - t.Fatal(err) - } - if err := os.WriteFile(filepath.Join(backlightDir, "max_brightness"), []byte("100\n"), 0644); err != nil { - t.Fatal(err) - } - if err := os.WriteFile(filepath.Join(backlightDir, "brightness"), []byte("50\n"), 0644); err != nil { - t.Fatal(err) - } - - sysfs := &SysfsBackend{ - basePath: tmpDir, - classes: []string{"backlight"}, - deviceCache: make(map[string]*sysfsDevice), - } - - if err := sysfs.scanDevices(); err != nil { - t.Fatal(err) - } - - m := &Manager{ - logindBackend: nil, - sysfsBackend: sysfs, - logindReady: false, - sysfsReady: true, - subscribers: make(map[string]chan State), - updateSubscribers: make(map[string]chan DeviceUpdate), - stopChan: make(chan struct{}), - } - - m.state = State{ - Devices: []Device{ - { - Class: ClassBacklight, - ID: "backlight:test_backlight", - Name: "test_backlight", - Current: 50, - Max: 100, - CurrentPercent: 50, - Backend: "sysfs", - }, - }, - } - - err := m.SetBrightness("backlight:test_backlight", 75) - if err != nil { - t.Errorf("SetBrightness() without logind error = %v, want nil", err) - } - - data, _ := os.ReadFile(filepath.Join(backlightDir, "brightness")) - brightness := string(data) - if brightness != "75" { - t.Errorf("Direct sysfs write = %q, want %q", brightness, "75") - } -} - -func TestManager_SetBrightness_LEDWithLogind(t *testing.T) { - tmpDir := t.TempDir() - - ledsDir := filepath.Join(tmpDir, "leds", "test_led") - if err := os.MkdirAll(ledsDir, 0755); err != nil { - t.Fatal(err) - } - if err := os.WriteFile(filepath.Join(ledsDir, "max_brightness"), []byte("255\n"), 0644); err != nil { - t.Fatal(err) - } - if err := os.WriteFile(filepath.Join(ledsDir, "brightness"), []byte("128\n"), 0644); err != nil { - t.Fatal(err) - } - - mockConn := mocks_brightness.NewMockDBusConn(t) - mockObj := mock_dbus.NewMockBusObject(t) - - mockLogind := NewLogindBackendWithConn(mockConn) - - sysfs := &SysfsBackend{ - basePath: tmpDir, - classes: []string{"leds"}, - deviceCache: make(map[string]*sysfsDevice), - } - - if err := sysfs.scanDevices(); err != nil { - t.Fatal(err) - } - - m := &Manager{ - logindBackend: mockLogind, - sysfsBackend: sysfs, - logindReady: true, - sysfsReady: true, - subscribers: make(map[string]chan State), - updateSubscribers: make(map[string]chan DeviceUpdate), - stopChan: make(chan struct{}), - } - - m.state = State{ - Devices: []Device{ - { - Class: ClassLED, - ID: "leds:test_led", - Name: "test_led", - Current: 128, - Max: 255, - CurrentPercent: 50, - Backend: "sysfs", - }, - }, - } - - mockConn.EXPECT(). - Object("org.freedesktop.login1", dbus.ObjectPath("/org/freedesktop/login1/session/auto")). - Return(mockObj). - Once() - - mockObj.EXPECT(). - Call("org.freedesktop.login1.Session.SetBrightness", mock.Anything, "leds", "test_led", uint32(0)). - Return(&dbus.Call{Err: nil}). - Once() - - err := m.SetBrightness("leds:test_led", 0) - if err != nil { - t.Errorf("SetBrightness() LED with logind error = %v, want nil", err) - } -} diff --git a/nix/inputs/dms-cli/internal/server/brightness/sysfs_test.go b/nix/inputs/dms-cli/internal/server/brightness/sysfs_test.go deleted file mode 100644 index b31db63..0000000 --- a/nix/inputs/dms-cli/internal/server/brightness/sysfs_test.go +++ /dev/null @@ -1,185 +0,0 @@ -package brightness - -import ( - "os" - "path/filepath" - "testing" -) - -func TestSysfsBackend_PercentConversions(t *testing.T) { - tests := []struct { - name string - device *sysfsDevice - percent int - wantValue int - tolerance int - }{ - { - name: "backlight 0% should be minValue=1", - device: &sysfsDevice{maxBrightness: 100, minValue: 1, class: ClassBacklight}, - percent: 0, - wantValue: 1, - tolerance: 0, - }, - { - name: "backlight 1% should be minValue=1", - device: &sysfsDevice{maxBrightness: 100, minValue: 1, class: ClassBacklight}, - percent: 1, - wantValue: 1, - tolerance: 0, - }, - { - name: "backlight 50% should be ~50", - device: &sysfsDevice{maxBrightness: 100, minValue: 1, class: ClassBacklight}, - percent: 50, - wantValue: 50, - tolerance: 1, - }, - { - name: "backlight 100% should be max", - device: &sysfsDevice{maxBrightness: 100, minValue: 1, class: ClassBacklight}, - percent: 100, - wantValue: 100, - tolerance: 0, - }, - { - name: "led 0% should be 0", - device: &sysfsDevice{maxBrightness: 255, minValue: 0, class: ClassLED}, - percent: 0, - wantValue: 0, - tolerance: 0, - }, - { - name: "led 1% should be ~2-3", - device: &sysfsDevice{maxBrightness: 255, minValue: 0, class: ClassLED}, - percent: 1, - wantValue: 2, - tolerance: 3, - }, - { - name: "led 50% should be ~127", - device: &sysfsDevice{maxBrightness: 255, minValue: 0, class: ClassLED}, - percent: 50, - wantValue: 127, - tolerance: 2, - }, - { - name: "led 100% should be max", - device: &sysfsDevice{maxBrightness: 255, minValue: 0, class: ClassLED}, - percent: 100, - wantValue: 255, - tolerance: 0, - }, - } - - b := &SysfsBackend{} - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := b.PercentToValue(tt.percent, tt.device, false) - diff := got - tt.wantValue - if diff < 0 { - diff = -diff - } - if diff > tt.tolerance { - t.Errorf("percentToValue() = %v, want %v (±%d)", got, tt.wantValue, tt.tolerance) - } - - gotPercent := b.ValueToPercent(got, tt.device, false) - if tt.percent > 1 && gotPercent == 0 { - t.Errorf("valueToPercent() returned 0 for non-zero input (percent=%d, got value=%d)", tt.percent, got) - } - }) - } -} - -func TestSysfsBackend_ValueToPercent(t *testing.T) { - tests := []struct { - name string - device *sysfsDevice - value int - wantPercent int - }{ - { - name: "backlight min value", - device: &sysfsDevice{maxBrightness: 100, minValue: 1, class: ClassBacklight}, - value: 1, - wantPercent: 1, - }, - { - name: "backlight max value", - device: &sysfsDevice{maxBrightness: 100, minValue: 1, class: ClassBacklight}, - value: 100, - wantPercent: 100, - }, - { - name: "led zero", - device: &sysfsDevice{maxBrightness: 255, minValue: 0, class: ClassLED}, - value: 0, - wantPercent: 0, - }, - } - - b := &SysfsBackend{} - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := b.ValueToPercent(tt.value, tt.device, false) - if got != tt.wantPercent { - t.Errorf("valueToPercent() = %v, want %v", got, tt.wantPercent) - } - }) - } -} - -func TestSysfsBackend_ScanDevices(t *testing.T) { - tmpDir := t.TempDir() - - backlightDir := filepath.Join(tmpDir, "backlight", "test_backlight") - if err := os.MkdirAll(backlightDir, 0755); err != nil { - t.Fatal(err) - } - - if err := os.WriteFile(filepath.Join(backlightDir, "max_brightness"), []byte("100\n"), 0644); err != nil { - t.Fatal(err) - } - if err := os.WriteFile(filepath.Join(backlightDir, "brightness"), []byte("50\n"), 0644); err != nil { - t.Fatal(err) - } - - ledsDir := filepath.Join(tmpDir, "leds", "test_led") - if err := os.MkdirAll(ledsDir, 0755); err != nil { - t.Fatal(err) - } - - if err := os.WriteFile(filepath.Join(ledsDir, "max_brightness"), []byte("255\n"), 0644); err != nil { - t.Fatal(err) - } - if err := os.WriteFile(filepath.Join(ledsDir, "brightness"), []byte("128\n"), 0644); err != nil { - t.Fatal(err) - } - - b := &SysfsBackend{ - basePath: tmpDir, - classes: []string{"backlight", "leds"}, - deviceCache: make(map[string]*sysfsDevice), - } - - if err := b.scanDevices(); err != nil { - t.Fatalf("scanDevices() error = %v", err) - } - - if len(b.deviceCache) != 2 { - t.Errorf("expected 2 devices, got %d", len(b.deviceCache)) - } - - backlightID := "backlight:test_backlight" - if _, ok := b.deviceCache[backlightID]; !ok { - t.Errorf("backlight device not found") - } - - ledID := "leds:test_led" - if _, ok := b.deviceCache[ledID]; !ok { - t.Errorf("LED device not found") - } -} diff --git a/nix/inputs/dms-cli/internal/server/brightness/types.go b/nix/inputs/dms-cli/internal/server/brightness/types.go deleted file mode 100644 index a1e83b9..0000000 --- a/nix/inputs/dms-cli/internal/server/brightness/types.go +++ /dev/null @@ -1,199 +0,0 @@ -package brightness - -import ( - "sync" - "time" -) - -type DeviceClass string - -const ( - ClassBacklight DeviceClass = "backlight" - ClassLED DeviceClass = "leds" - ClassDDC DeviceClass = "ddc" -) - -type Device struct { - Class DeviceClass `json:"class"` - ID string `json:"id"` - Name string `json:"name"` - Current int `json:"current"` - Max int `json:"max"` - CurrentPercent int `json:"currentPercent"` - Backend string `json:"backend"` -} - -type State struct { - Devices []Device `json:"devices"` -} - -type DeviceUpdate struct { - Device Device `json:"device"` -} - -type Request struct { - ID interface{} `json:"id"` - Method string `json:"method"` - Params map[string]interface{} `json:"params"` -} - -type Manager struct { - logindBackend *LogindBackend - sysfsBackend *SysfsBackend - ddcBackend *DDCBackend - - logindReady bool - sysfsReady bool - ddcReady bool - - exponential bool - - stateMutex sync.RWMutex - state State - - subscribers map[string]chan State - updateSubscribers map[string]chan DeviceUpdate - subMutex sync.RWMutex - - broadcastMutex sync.Mutex - broadcastTimer *time.Timer - broadcastPending bool - pendingDeviceID string - - stopChan chan struct{} -} - -type SysfsBackend struct { - basePath string - classes []string - - deviceCache map[string]*sysfsDevice - deviceCacheMutex sync.RWMutex -} - -type sysfsDevice struct { - class DeviceClass - id string - name string - maxBrightness int - minValue int -} - -type DDCBackend struct { - devices map[string]*ddcDevice - devicesMutex sync.RWMutex - - scanMutex sync.Mutex - lastScan time.Time - scanInterval time.Duration - - debounceMutex sync.Mutex - debounceTimers map[string]*time.Timer - debouncePending map[string]ddcPendingSet -} - -type ddcPendingSet struct { - percent int - callback func() -} - -type ddcDevice struct { - bus int - addr int - id string - name string - max int - lastBrightness int -} - -type ddcCapability struct { - vcp byte - max int - current int -} - -type SetBrightnessParams struct { - Device string `json:"device"` - Percent int `json:"percent"` - Exponential bool `json:"exponential,omitempty"` - Exponent float64 `json:"exponent,omitempty"` -} - -func (m *Manager) Subscribe(id string) chan State { - ch := make(chan State, 16) - m.subMutex.Lock() - m.subscribers[id] = ch - m.subMutex.Unlock() - return ch -} - -func (m *Manager) Unsubscribe(id string) { - m.subMutex.Lock() - if ch, ok := m.subscribers[id]; ok { - close(ch) - delete(m.subscribers, id) - } - m.subMutex.Unlock() -} - -func (m *Manager) SubscribeUpdates(id string) chan DeviceUpdate { - ch := make(chan DeviceUpdate, 16) - m.subMutex.Lock() - m.updateSubscribers[id] = ch - m.subMutex.Unlock() - return ch -} - -func (m *Manager) UnsubscribeUpdates(id string) { - m.subMutex.Lock() - if ch, ok := m.updateSubscribers[id]; ok { - close(ch) - delete(m.updateSubscribers, id) - } - m.subMutex.Unlock() -} - -func (m *Manager) NotifySubscribers() { - m.stateMutex.RLock() - state := m.state - m.stateMutex.RUnlock() - - m.subMutex.RLock() - defer m.subMutex.RUnlock() - - for _, ch := range m.subscribers { - select { - case ch <- state: - default: - } - } -} - -func (m *Manager) GetState() State { - m.stateMutex.RLock() - defer m.stateMutex.RUnlock() - return m.state -} - -func (m *Manager) Close() { - close(m.stopChan) - - m.subMutex.Lock() - for _, ch := range m.subscribers { - close(ch) - } - m.subscribers = make(map[string]chan State) - for _, ch := range m.updateSubscribers { - close(ch) - } - m.updateSubscribers = make(map[string]chan DeviceUpdate) - m.subMutex.Unlock() - - if m.logindBackend != nil { - m.logindBackend.Close() - } - - if m.ddcBackend != nil { - m.ddcBackend.Close() - } -} diff --git a/nix/inputs/dms-cli/internal/server/cups/actions.go b/nix/inputs/dms-cli/internal/server/cups/actions.go deleted file mode 100644 index 4ba7457..0000000 --- a/nix/inputs/dms-cli/internal/server/cups/actions.go +++ /dev/null @@ -1,107 +0,0 @@ -package cups - -import ( - "strings" - "time" - - "github.com/AvengeMedia/danklinux/pkg/ipp" -) - -func (m *Manager) GetPrinters() ([]Printer, error) { - attributes := []string{ - ipp.AttributePrinterName, - ipp.AttributePrinterUriSupported, - ipp.AttributePrinterState, - ipp.AttributePrinterStateReasons, - ipp.AttributePrinterLocation, - ipp.AttributePrinterInfo, - ipp.AttributePrinterMakeAndModel, - ipp.AttributePrinterIsAcceptingJobs, - } - - printerAttrs, err := m.client.GetPrinters(attributes) - if err != nil { - return nil, err - } - - printers := make([]Printer, 0, len(printerAttrs)) - for _, attrs := range printerAttrs { - printer := Printer{ - Name: getStringAttr(attrs, ipp.AttributePrinterName), - URI: getStringAttr(attrs, ipp.AttributePrinterUriSupported), - State: parsePrinterState(attrs), - StateReason: getStringAttr(attrs, ipp.AttributePrinterStateReasons), - Location: getStringAttr(attrs, ipp.AttributePrinterLocation), - Info: getStringAttr(attrs, ipp.AttributePrinterInfo), - MakeModel: getStringAttr(attrs, ipp.AttributePrinterMakeAndModel), - Accepting: getBoolAttr(attrs, ipp.AttributePrinterIsAcceptingJobs), - } - - if printer.Name != "" { - printers = append(printers, printer) - } - } - - return printers, nil -} - -func (m *Manager) GetJobs(printerName string, whichJobs string) ([]Job, error) { - attributes := []string{ - ipp.AttributeJobID, - ipp.AttributeJobName, - ipp.AttributeJobState, - ipp.AttributeJobPrinterURI, - ipp.AttributeJobOriginatingUserName, - ipp.AttributeJobKilobyteOctets, - "time-at-creation", - } - - jobAttrs, err := m.client.GetJobs(printerName, "", whichJobs, false, 0, 0, attributes) - if err != nil { - return nil, err - } - - jobs := make([]Job, 0, len(jobAttrs)) - for _, attrs := range jobAttrs { - job := Job{ - ID: getIntAttr(attrs, ipp.AttributeJobID), - Name: getStringAttr(attrs, ipp.AttributeJobName), - State: parseJobState(attrs), - User: getStringAttr(attrs, ipp.AttributeJobOriginatingUserName), - Size: getIntAttr(attrs, ipp.AttributeJobKilobyteOctets) * 1024, - } - - if uri := getStringAttr(attrs, ipp.AttributeJobPrinterURI); uri != "" { - parts := strings.Split(uri, "/") - if len(parts) > 0 { - job.Printer = parts[len(parts)-1] - } - } - - if ts := getIntAttr(attrs, "time-at-creation"); ts > 0 { - job.TimeCreated = time.Unix(int64(ts), 0) - } - - if job.ID != 0 { - jobs = append(jobs, job) - } - } - - return jobs, nil -} - -func (m *Manager) CancelJob(jobID int) error { - return m.client.CancelJob(jobID, false) -} - -func (m *Manager) PausePrinter(printerName string) error { - return m.client.PausePrinter(printerName) -} - -func (m *Manager) ResumePrinter(printerName string) error { - return m.client.ResumePrinter(printerName) -} - -func (m *Manager) PurgeJobs(printerName string) error { - return m.client.CancelAllJob(printerName, true) -} diff --git a/nix/inputs/dms-cli/internal/server/cups/actions_test.go b/nix/inputs/dms-cli/internal/server/cups/actions_test.go deleted file mode 100644 index 9ff548c..0000000 --- a/nix/inputs/dms-cli/internal/server/cups/actions_test.go +++ /dev/null @@ -1,285 +0,0 @@ -package cups - -import ( - "errors" - "testing" - "time" - - mocks_cups "github.com/AvengeMedia/danklinux/internal/mocks/cups" - "github.com/AvengeMedia/danklinux/pkg/ipp" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" -) - -func TestManager_GetPrinters(t *testing.T) { - tests := []struct { - name string - mockRet map[string]ipp.Attributes - mockErr error - want int - wantErr bool - }{ - { - name: "success", - mockRet: map[string]ipp.Attributes{ - "printer1": { - ipp.AttributePrinterName: []ipp.Attribute{{Value: "printer1"}}, - ipp.AttributePrinterUriSupported: []ipp.Attribute{{Value: "ipp://localhost/printers/printer1"}}, - ipp.AttributePrinterState: []ipp.Attribute{{Value: 3}}, - ipp.AttributePrinterStateReasons: []ipp.Attribute{{Value: "none"}}, - ipp.AttributePrinterLocation: []ipp.Attribute{{Value: "Office"}}, - ipp.AttributePrinterInfo: []ipp.Attribute{{Value: "Test Printer"}}, - ipp.AttributePrinterMakeAndModel: []ipp.Attribute{{Value: "Generic"}}, - ipp.AttributePrinterIsAcceptingJobs: []ipp.Attribute{{Value: true}}, - }, - }, - mockErr: nil, - want: 1, - wantErr: false, - }, - { - name: "error", - mockRet: nil, - mockErr: errors.New("test error"), - want: 0, - wantErr: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(tt.mockRet, tt.mockErr) - - m := &Manager{ - client: mockClient, - } - - got, err := m.GetPrinters() - if tt.wantErr { - assert.Error(t, err) - } else { - assert.NoError(t, err) - assert.Equal(t, tt.want, len(got)) - if len(got) > 0 { - assert.Equal(t, "printer1", got[0].Name) - assert.Equal(t, "idle", got[0].State) - assert.Equal(t, "Office", got[0].Location) - assert.True(t, got[0].Accepting) - } - } - }) - } -} - -func TestManager_GetJobs(t *testing.T) { - tests := []struct { - name string - mockRet map[int]ipp.Attributes - mockErr error - want int - wantErr bool - }{ - { - name: "success", - mockRet: map[int]ipp.Attributes{ - 1: { - ipp.AttributeJobID: []ipp.Attribute{{Value: 1}}, - ipp.AttributeJobName: []ipp.Attribute{{Value: "test-job"}}, - ipp.AttributeJobState: []ipp.Attribute{{Value: 5}}, - ipp.AttributeJobPrinterURI: []ipp.Attribute{{Value: "ipp://localhost/printers/printer1"}}, - ipp.AttributeJobOriginatingUserName: []ipp.Attribute{{Value: "testuser"}}, - ipp.AttributeJobKilobyteOctets: []ipp.Attribute{{Value: 10}}, - "time-at-creation": []ipp.Attribute{{Value: 1609459200}}, - }, - }, - mockErr: nil, - want: 1, - wantErr: false, - }, - { - name: "error", - mockRet: nil, - mockErr: errors.New("test error"), - want: 0, - wantErr: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().GetJobs("printer1", "", "not-completed", false, 0, 0, mock.Anything). - Return(tt.mockRet, tt.mockErr) - - m := &Manager{ - client: mockClient, - } - - got, err := m.GetJobs("printer1", "not-completed") - if tt.wantErr { - assert.Error(t, err) - } else { - assert.NoError(t, err) - assert.Equal(t, tt.want, len(got)) - if len(got) > 0 { - assert.Equal(t, 1, got[0].ID) - assert.Equal(t, "test-job", got[0].Name) - assert.Equal(t, "processing", got[0].State) - assert.Equal(t, "testuser", got[0].User) - assert.Equal(t, "printer1", got[0].Printer) - assert.Equal(t, 10240, got[0].Size) - assert.Equal(t, time.Unix(1609459200, 0), got[0].TimeCreated) - } - } - }) - } -} - -func TestManager_CancelJob(t *testing.T) { - tests := []struct { - name string - mockErr error - wantErr bool - }{ - { - name: "success", - mockErr: nil, - wantErr: false, - }, - { - name: "error", - mockErr: errors.New("test error"), - wantErr: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().CancelJob(1, false).Return(tt.mockErr) - - m := &Manager{ - client: mockClient, - } - - err := m.CancelJob(1) - if tt.wantErr { - assert.Error(t, err) - } else { - assert.NoError(t, err) - } - }) - } -} - -func TestManager_PausePrinter(t *testing.T) { - tests := []struct { - name string - mockErr error - wantErr bool - }{ - { - name: "success", - mockErr: nil, - wantErr: false, - }, - { - name: "error", - mockErr: errors.New("test error"), - wantErr: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().PausePrinter("printer1").Return(tt.mockErr) - - m := &Manager{ - client: mockClient, - } - - err := m.PausePrinter("printer1") - if tt.wantErr { - assert.Error(t, err) - } else { - assert.NoError(t, err) - } - }) - } -} - -func TestManager_ResumePrinter(t *testing.T) { - tests := []struct { - name string - mockErr error - wantErr bool - }{ - { - name: "success", - mockErr: nil, - wantErr: false, - }, - { - name: "error", - mockErr: errors.New("test error"), - wantErr: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().ResumePrinter("printer1").Return(tt.mockErr) - - m := &Manager{ - client: mockClient, - } - - err := m.ResumePrinter("printer1") - if tt.wantErr { - assert.Error(t, err) - } else { - assert.NoError(t, err) - } - }) - } -} - -func TestManager_PurgeJobs(t *testing.T) { - tests := []struct { - name string - mockErr error - wantErr bool - }{ - { - name: "success", - mockErr: nil, - wantErr: false, - }, - { - name: "error", - mockErr: errors.New("test error"), - wantErr: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().CancelAllJob("printer1", true).Return(tt.mockErr) - - m := &Manager{ - client: mockClient, - } - - err := m.PurgeJobs("printer1") - if tt.wantErr { - assert.Error(t, err) - } else { - assert.NoError(t, err) - } - }) - } -} diff --git a/nix/inputs/dms-cli/internal/server/cups/handlers.go b/nix/inputs/dms-cli/internal/server/cups/handlers.go deleted file mode 100644 index ff5c2e7..0000000 --- a/nix/inputs/dms-cli/internal/server/cups/handlers.go +++ /dev/null @@ -1,160 +0,0 @@ -package cups - -import ( - "encoding/json" - "fmt" - "net" - - "github.com/AvengeMedia/danklinux/internal/server/models" -) - -type Request struct { - ID int `json:"id,omitempty"` - Method string `json:"method"` - Params map[string]interface{} `json:"params,omitempty"` -} - -type SuccessResult struct { - Success bool `json:"success"` - Message string `json:"message"` -} - -type CUPSEvent struct { - Type string `json:"type"` - Data CUPSState `json:"data"` -} - -func HandleRequest(conn net.Conn, req Request, manager *Manager) { - switch req.Method { - case "cups.subscribe": - handleSubscribe(conn, req, manager) - case "cups.getPrinters": - handleGetPrinters(conn, req, manager) - case "cups.getJobs": - handleGetJobs(conn, req, manager) - case "cups.pausePrinter": - handlePausePrinter(conn, req, manager) - case "cups.resumePrinter": - handleResumePrinter(conn, req, manager) - case "cups.cancelJob": - handleCancelJob(conn, req, manager) - case "cups.purgeJobs": - handlePurgeJobs(conn, req, manager) - default: - models.RespondError(conn, req.ID, fmt.Sprintf("unknown method: %s", req.Method)) - } -} - -func handleGetPrinters(conn net.Conn, req Request, manager *Manager) { - printers, err := manager.GetPrinters() - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, printers) -} - -func handleGetJobs(conn net.Conn, req Request, manager *Manager) { - printerName, ok := req.Params["printerName"].(string) - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'printerName' parameter") - return - } - - jobs, err := manager.GetJobs(printerName, "not-completed") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, jobs) -} - -func handlePausePrinter(conn net.Conn, req Request, manager *Manager) { - printerName, ok := req.Params["printerName"].(string) - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'printerName' parameter") - return - } - - if err := manager.PausePrinter(printerName); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "paused"}) -} - -func handleResumePrinter(conn net.Conn, req Request, manager *Manager) { - printerName, ok := req.Params["printerName"].(string) - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'printerName' parameter") - return - } - - if err := manager.ResumePrinter(printerName); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "resumed"}) -} - -func handleCancelJob(conn net.Conn, req Request, manager *Manager) { - jobIDFloat, ok := req.Params["jobID"].(float64) - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'jobid' parameter") - return - } - jobID := int(jobIDFloat) - - if err := manager.CancelJob(jobID); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "job canceled"}) -} - -func handlePurgeJobs(conn net.Conn, req Request, manager *Manager) { - printerName, ok := req.Params["printerName"].(string) - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'printerName' parameter") - return - } - - if err := manager.PurgeJobs(printerName); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "jobs canceled"}) -} - -func handleSubscribe(conn net.Conn, req Request, manager *Manager) { - clientID := fmt.Sprintf("client-%p", conn) - stateChan := manager.Subscribe(clientID) - defer manager.Unsubscribe(clientID) - - initialState := manager.GetState() - event := CUPSEvent{ - Type: "state_changed", - Data: initialState, - } - - if err := json.NewEncoder(conn).Encode(models.Response[CUPSEvent]{ - ID: req.ID, - Result: &event, - }); err != nil { - return - } - - for state := range stateChan { - event := CUPSEvent{ - Type: "state_changed", - Data: state, - } - if err := json.NewEncoder(conn).Encode(models.Response[CUPSEvent]{ - Result: &event, - }); err != nil { - return - } - } -} diff --git a/nix/inputs/dms-cli/internal/server/cups/handlers_test.go b/nix/inputs/dms-cli/internal/server/cups/handlers_test.go deleted file mode 100644 index 655d385..0000000 --- a/nix/inputs/dms-cli/internal/server/cups/handlers_test.go +++ /dev/null @@ -1,279 +0,0 @@ -package cups - -import ( - "bytes" - "encoding/json" - "errors" - "net" - "testing" - "time" - - mocks_cups "github.com/AvengeMedia/danklinux/internal/mocks/cups" - "github.com/AvengeMedia/danklinux/internal/server/models" - "github.com/AvengeMedia/danklinux/pkg/ipp" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" -) - -type mockConn struct { - *bytes.Buffer -} - -func (m *mockConn) Close() error { return nil } -func (m *mockConn) LocalAddr() net.Addr { return nil } -func (m *mockConn) RemoteAddr() net.Addr { return nil } -func (m *mockConn) SetDeadline(t time.Time) error { return nil } -func (m *mockConn) SetReadDeadline(t time.Time) error { return nil } -func (m *mockConn) SetWriteDeadline(t time.Time) error { return nil } - -func TestHandleGetPrinters(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{ - "printer1": { - ipp.AttributePrinterName: []ipp.Attribute{{Value: "printer1"}}, - ipp.AttributePrinterState: []ipp.Attribute{{Value: 3}}, - ipp.AttributePrinterUriSupported: []ipp.Attribute{{Value: "ipp://localhost/printers/printer1"}}, - }, - }, nil) - - m := &Manager{ - client: mockClient, - } - - buf := &bytes.Buffer{} - conn := &mockConn{Buffer: buf} - - req := Request{ - ID: 1, - Method: "cups.getPrinters", - } - - handleGetPrinters(conn, req, m) - - var resp models.Response[[]Printer] - err := json.NewDecoder(buf).Decode(&resp) - assert.NoError(t, err) - assert.NotNil(t, resp.Result) - assert.Equal(t, 1, len(*resp.Result)) -} - -func TestHandleGetPrinters_Error(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(nil, errors.New("test error")) - - m := &Manager{ - client: mockClient, - } - - buf := &bytes.Buffer{} - conn := &mockConn{Buffer: buf} - - req := Request{ - ID: 1, - Method: "cups.getPrinters", - } - - handleGetPrinters(conn, req, m) - - var resp models.Response[interface{}] - err := json.NewDecoder(buf).Decode(&resp) - assert.NoError(t, err) - assert.Nil(t, resp.Result) - assert.NotNil(t, resp.Error) -} - -func TestHandleGetJobs(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().GetJobs("printer1", "", "not-completed", false, 0, 0, mock.Anything). - Return(map[int]ipp.Attributes{ - 1: { - ipp.AttributeJobID: []ipp.Attribute{{Value: 1}}, - ipp.AttributeJobName: []ipp.Attribute{{Value: "job1"}}, - ipp.AttributeJobState: []ipp.Attribute{{Value: 5}}, - }, - }, nil) - - m := &Manager{ - client: mockClient, - } - - buf := &bytes.Buffer{} - conn := &mockConn{Buffer: buf} - - req := Request{ - ID: 1, - Method: "cups.getJobs", - Params: map[string]interface{}{ - "printerName": "printer1", - }, - } - - handleGetJobs(conn, req, m) - - var resp models.Response[[]Job] - err := json.NewDecoder(buf).Decode(&resp) - assert.NoError(t, err) - assert.NotNil(t, resp.Result) - assert.Equal(t, 1, len(*resp.Result)) -} - -func TestHandleGetJobs_MissingParam(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - - m := &Manager{ - client: mockClient, - } - - buf := &bytes.Buffer{} - conn := &mockConn{Buffer: buf} - - req := Request{ - ID: 1, - Method: "cups.getJobs", - Params: map[string]interface{}{}, - } - - handleGetJobs(conn, req, m) - - var resp models.Response[interface{}] - err := json.NewDecoder(buf).Decode(&resp) - assert.NoError(t, err) - assert.Nil(t, resp.Result) - assert.NotNil(t, resp.Error) -} - -func TestHandlePausePrinter(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().PausePrinter("printer1").Return(nil) - - m := &Manager{ - client: mockClient, - } - - buf := &bytes.Buffer{} - conn := &mockConn{Buffer: buf} - - req := Request{ - ID: 1, - Method: "cups.pausePrinter", - Params: map[string]interface{}{ - "printerName": "printer1", - }, - } - - handlePausePrinter(conn, req, m) - - var resp models.Response[SuccessResult] - err := json.NewDecoder(buf).Decode(&resp) - assert.NoError(t, err) - assert.NotNil(t, resp.Result) - assert.True(t, resp.Result.Success) -} - -func TestHandleResumePrinter(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().ResumePrinter("printer1").Return(nil) - - m := &Manager{ - client: mockClient, - } - - buf := &bytes.Buffer{} - conn := &mockConn{Buffer: buf} - - req := Request{ - ID: 1, - Method: "cups.resumePrinter", - Params: map[string]interface{}{ - "printerName": "printer1", - }, - } - - handleResumePrinter(conn, req, m) - - var resp models.Response[SuccessResult] - err := json.NewDecoder(buf).Decode(&resp) - assert.NoError(t, err) - assert.NotNil(t, resp.Result) - assert.True(t, resp.Result.Success) -} - -func TestHandleCancelJob(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().CancelJob(1, false).Return(nil) - - m := &Manager{ - client: mockClient, - } - - buf := &bytes.Buffer{} - conn := &mockConn{Buffer: buf} - - req := Request{ - ID: 1, - Method: "cups.cancelJob", - Params: map[string]interface{}{ - "jobID": float64(1), - }, - } - - handleCancelJob(conn, req, m) - - var resp models.Response[SuccessResult] - err := json.NewDecoder(buf).Decode(&resp) - assert.NoError(t, err) - assert.NotNil(t, resp.Result) - assert.True(t, resp.Result.Success) -} - -func TestHandlePurgeJobs(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().CancelAllJob("printer1", true).Return(nil) - - m := &Manager{ - client: mockClient, - } - - buf := &bytes.Buffer{} - conn := &mockConn{Buffer: buf} - - req := Request{ - ID: 1, - Method: "cups.purgeJobs", - Params: map[string]interface{}{ - "printerName": "printer1", - }, - } - - handlePurgeJobs(conn, req, m) - - var resp models.Response[SuccessResult] - err := json.NewDecoder(buf).Decode(&resp) - assert.NoError(t, err) - assert.NotNil(t, resp.Result) - assert.True(t, resp.Result.Success) -} - -func TestHandleRequest_UnknownMethod(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - - m := &Manager{ - client: mockClient, - } - - buf := &bytes.Buffer{} - conn := &mockConn{Buffer: buf} - - req := Request{ - ID: 1, - Method: "cups.unknownMethod", - } - - HandleRequest(conn, req, m) - - var resp models.Response[interface{}] - err := json.NewDecoder(buf).Decode(&resp) - assert.NoError(t, err) - assert.Nil(t, resp.Result) - assert.NotNil(t, resp.Error) -} diff --git a/nix/inputs/dms-cli/internal/server/cups/manager.go b/nix/inputs/dms-cli/internal/server/cups/manager.go deleted file mode 100644 index b7cae27..0000000 --- a/nix/inputs/dms-cli/internal/server/cups/manager.go +++ /dev/null @@ -1,340 +0,0 @@ -package cups - -import ( - "fmt" - "os" - "strconv" - "sync" - "time" - - "github.com/AvengeMedia/danklinux/internal/log" - "github.com/AvengeMedia/danklinux/pkg/ipp" -) - -func NewManager() (*Manager, error) { - host := os.Getenv("DMS_IPP_HOST") - if host == "" { - host = "localhost" - } - - portStr := os.Getenv("DMS_IPP_PORT") - port := 631 - if portStr != "" { - if p, err := strconv.Atoi(portStr); err == nil { - port = p - } - } - - username := os.Getenv("DMS_IPP_USERNAME") - password := os.Getenv("DMS_IPP_PASSWORD") - - client := ipp.NewCUPSClient(host, port, username, password, false) - baseURL := fmt.Sprintf("http://%s:%d", host, port) - - m := &Manager{ - state: &CUPSState{ - Printers: make(map[string]*Printer), - }, - client: client, - baseURL: baseURL, - stateMutex: sync.RWMutex{}, - stopChan: make(chan struct{}), - dirty: make(chan struct{}, 1), - subscribers: make(map[string]chan CUPSState), - subMutex: sync.RWMutex{}, - } - - if err := m.updateState(); err != nil { - return nil, err - } - - if isLocalCUPS(host) { - m.subscription = NewDBusSubscriptionManager(client, baseURL) - log.Infof("[CUPS] Using D-Bus notifications for local CUPS") - } else { - m.subscription = NewSubscriptionManager(client, baseURL) - log.Infof("[CUPS] Using IPPGET notifications for remote CUPS") - } - - m.notifierWg.Add(1) - go m.notifier() - - return m, nil -} - -func isLocalCUPS(host string) bool { - switch host { - case "localhost", "127.0.0.1", "::1", "": - return true - } - return false -} - -func (m *Manager) eventHandler() { - defer m.eventWG.Done() - - if m.subscription == nil { - return - } - - for { - select { - case <-m.stopChan: - return - case event, ok := <-m.subscription.Events(): - if !ok { - return - } - log.Debugf("[CUPS] Received event: %s (printer: %s, job: %d)", - event.EventName, event.PrinterName, event.JobID) - - if err := m.updateState(); err != nil { - log.Warnf("[CUPS] Failed to update state after event: %v", err) - } else { - m.notifySubscribers() - } - } - } -} - -func (m *Manager) updateState() error { - printers, err := m.GetPrinters() - if err != nil { - return err - } - - printerMap := make(map[string]*Printer, len(printers)) - for _, printer := range printers { - jobs, err := m.GetJobs(printer.Name, "not-completed") - if err != nil { - return err - } - - printer.Jobs = jobs - printerMap[printer.Name] = &printer - } - - m.stateMutex.Lock() - m.state.Printers = printerMap - m.stateMutex.Unlock() - - return nil -} - -func (m *Manager) notifier() { - defer m.notifierWg.Done() - const minGap = 100 * time.Millisecond - timer := time.NewTimer(minGap) - timer.Stop() - var pending bool - for { - select { - case <-m.stopChan: - timer.Stop() - return - case <-m.dirty: - if pending { - continue - } - pending = true - timer.Reset(minGap) - case <-timer.C: - if !pending { - continue - } - m.subMutex.RLock() - if len(m.subscribers) == 0 { - m.subMutex.RUnlock() - pending = false - continue - } - - currentState := m.snapshotState() - - if m.lastNotifiedState != nil && !stateChanged(m.lastNotifiedState, ¤tState) { - m.subMutex.RUnlock() - pending = false - continue - } - - for _, ch := range m.subscribers { - select { - case ch <- currentState: - default: - } - } - m.subMutex.RUnlock() - - stateCopy := currentState - m.lastNotifiedState = &stateCopy - pending = false - } - } -} - -func (m *Manager) notifySubscribers() { - select { - case m.dirty <- struct{}{}: - default: - } -} - -func (m *Manager) GetState() CUPSState { - return m.snapshotState() -} - -func (m *Manager) snapshotState() CUPSState { - m.stateMutex.RLock() - defer m.stateMutex.RUnlock() - - s := CUPSState{ - Printers: make(map[string]*Printer, len(m.state.Printers)), - } - for name, printer := range m.state.Printers { - printerCopy := *printer - s.Printers[name] = &printerCopy - } - return s -} - -func (m *Manager) Subscribe(id string) chan CUPSState { - ch := make(chan CUPSState, 64) - m.subMutex.Lock() - wasEmpty := len(m.subscribers) == 0 - m.subscribers[id] = ch - m.subMutex.Unlock() - - if wasEmpty && m.subscription != nil { - if err := m.subscription.Start(); err != nil { - log.Warnf("[CUPS] Failed to start subscription manager: %v", err) - } else { - m.eventWG.Add(1) - go m.eventHandler() - } - } - - return ch -} - -func (m *Manager) Unsubscribe(id string) { - m.subMutex.Lock() - if ch, ok := m.subscribers[id]; ok { - close(ch) - delete(m.subscribers, id) - } - isEmpty := len(m.subscribers) == 0 - m.subMutex.Unlock() - - if isEmpty && m.subscription != nil { - m.subscription.Stop() - m.eventWG.Wait() - } -} - -func (m *Manager) Close() { - close(m.stopChan) - - if m.subscription != nil { - m.subscription.Stop() - } - - m.eventWG.Wait() - m.notifierWg.Wait() - - m.subMutex.Lock() - for _, ch := range m.subscribers { - close(ch) - } - m.subscribers = make(map[string]chan CUPSState) - m.subMutex.Unlock() -} - -func stateChanged(old, new *CUPSState) bool { - if len(old.Printers) != len(new.Printers) { - return true - } - for name, oldPrinter := range old.Printers { - newPrinter, exists := new.Printers[name] - if !exists { - return true - } - if oldPrinter.State != newPrinter.State || - oldPrinter.StateReason != newPrinter.StateReason || - len(oldPrinter.Jobs) != len(newPrinter.Jobs) { - return true - } - } - return false -} - -func parsePrinterState(attrs ipp.Attributes) string { - if stateAttr, ok := attrs[ipp.AttributePrinterState]; ok && len(stateAttr) > 0 { - if state, ok := stateAttr[0].Value.(int); ok { - switch state { - case 3: - return "idle" - case 4: - return "processing" - case 5: - return "stopped" - default: - return fmt.Sprintf("%d", state) - } - } - } - return "unknown" -} - -func parseJobState(attrs ipp.Attributes) string { - if stateAttr, ok := attrs[ipp.AttributeJobState]; ok && len(stateAttr) > 0 { - if state, ok := stateAttr[0].Value.(int); ok { - switch state { - case 3: - return "pending" - case 4: - return "pending-held" - case 5: - return "processing" - case 6: - return "processing-stopped" - case 7: - return "canceled" - case 8: - return "aborted" - case 9: - return "completed" - default: - return fmt.Sprintf("%d", state) - } - } - } - return "unknown" -} - -func getStringAttr(attrs ipp.Attributes, key string) string { - if attr, ok := attrs[key]; ok && len(attr) > 0 { - if val, ok := attr[0].Value.(string); ok { - return val - } - return fmt.Sprintf("%v", attr[0].Value) - } - return "" -} - -func getIntAttr(attrs ipp.Attributes, key string) int { - if attr, ok := attrs[key]; ok && len(attr) > 0 { - if val, ok := attr[0].Value.(int); ok { - return val - } - } - return 0 -} - -func getBoolAttr(attrs ipp.Attributes, key string) bool { - if attr, ok := attrs[key]; ok && len(attr) > 0 { - if val, ok := attr[0].Value.(bool); ok { - return val - } - } - return false -} diff --git a/nix/inputs/dms-cli/internal/server/cups/manager_test.go b/nix/inputs/dms-cli/internal/server/cups/manager_test.go deleted file mode 100644 index 53541e1..0000000 --- a/nix/inputs/dms-cli/internal/server/cups/manager_test.go +++ /dev/null @@ -1,351 +0,0 @@ -package cups - -import ( - "testing" - - mocks_cups "github.com/AvengeMedia/danklinux/internal/mocks/cups" - "github.com/AvengeMedia/danklinux/pkg/ipp" - "github.com/stretchr/testify/assert" -) - -func TestNewManager(t *testing.T) { - m := &Manager{ - state: &CUPSState{ - Printers: make(map[string]*Printer), - }, - client: nil, - stopChan: make(chan struct{}), - dirty: make(chan struct{}, 1), - subscribers: make(map[string]chan CUPSState), - } - - assert.NotNil(t, m) - assert.NotNil(t, m.state) -} - -func TestManager_GetState(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - - m := &Manager{ - state: &CUPSState{ - Printers: map[string]*Printer{ - "test-printer": { - Name: "test-printer", - State: "idle", - }, - }, - }, - client: mockClient, - stopChan: make(chan struct{}), - dirty: make(chan struct{}, 1), - subscribers: make(map[string]chan CUPSState), - } - - state := m.GetState() - assert.Equal(t, 1, len(state.Printers)) - assert.Equal(t, "test-printer", state.Printers["test-printer"].Name) -} - -func TestManager_Subscribe(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - - m := &Manager{ - state: &CUPSState{ - Printers: make(map[string]*Printer), - }, - client: mockClient, - stopChan: make(chan struct{}), - dirty: make(chan struct{}, 1), - subscribers: make(map[string]chan CUPSState), - } - - ch := m.Subscribe("test-client") - assert.NotNil(t, ch) - assert.Equal(t, 1, len(m.subscribers)) - - m.Unsubscribe("test-client") - assert.Equal(t, 0, len(m.subscribers)) -} - -func TestManager_Close(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - - m := &Manager{ - state: &CUPSState{ - Printers: make(map[string]*Printer), - }, - client: mockClient, - stopChan: make(chan struct{}), - dirty: make(chan struct{}, 1), - subscribers: make(map[string]chan CUPSState), - } - - m.eventWG.Add(1) - go func() { - defer m.eventWG.Done() - <-m.stopChan - }() - - m.notifierWg.Add(1) - go func() { - defer m.notifierWg.Done() - <-m.stopChan - }() - - m.Close() - assert.Equal(t, 0, len(m.subscribers)) -} - -func TestStateChanged(t *testing.T) { - tests := []struct { - name string - oldState *CUPSState - newState *CUPSState - want bool - }{ - { - name: "no change", - oldState: &CUPSState{ - Printers: map[string]*Printer{ - "p1": {Name: "p1", State: "idle"}, - }, - }, - newState: &CUPSState{ - Printers: map[string]*Printer{ - "p1": {Name: "p1", State: "idle"}, - }, - }, - want: false, - }, - { - name: "state changed", - oldState: &CUPSState{ - Printers: map[string]*Printer{ - "p1": {Name: "p1", State: "idle"}, - }, - }, - newState: &CUPSState{ - Printers: map[string]*Printer{ - "p1": {Name: "p1", State: "processing"}, - }, - }, - want: true, - }, - { - name: "printer added", - oldState: &CUPSState{ - Printers: map[string]*Printer{}, - }, - newState: &CUPSState{ - Printers: map[string]*Printer{ - "p1": {Name: "p1", State: "idle"}, - }, - }, - want: true, - }, - { - name: "printer removed", - oldState: &CUPSState{ - Printers: map[string]*Printer{ - "p1": {Name: "p1", State: "idle"}, - }, - }, - newState: &CUPSState{ - Printers: map[string]*Printer{}, - }, - want: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := stateChanged(tt.oldState, tt.newState) - assert.Equal(t, tt.want, got) - }) - } -} - -func TestParsePrinterState(t *testing.T) { - tests := []struct { - name string - attrs ipp.Attributes - want string - }{ - { - name: "idle", - attrs: ipp.Attributes{ - ipp.AttributePrinterState: []ipp.Attribute{{Value: 3}}, - }, - want: "idle", - }, - { - name: "processing", - attrs: ipp.Attributes{ - ipp.AttributePrinterState: []ipp.Attribute{{Value: 4}}, - }, - want: "processing", - }, - { - name: "stopped", - attrs: ipp.Attributes{ - ipp.AttributePrinterState: []ipp.Attribute{{Value: 5}}, - }, - want: "stopped", - }, - { - name: "unknown", - attrs: ipp.Attributes{}, - want: "unknown", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := parsePrinterState(tt.attrs) - assert.Equal(t, tt.want, got) - }) - } -} - -func TestParseJobState(t *testing.T) { - tests := []struct { - name string - attrs ipp.Attributes - want string - }{ - { - name: "pending", - attrs: ipp.Attributes{ - ipp.AttributeJobState: []ipp.Attribute{{Value: 3}}, - }, - want: "pending", - }, - { - name: "processing", - attrs: ipp.Attributes{ - ipp.AttributeJobState: []ipp.Attribute{{Value: 5}}, - }, - want: "processing", - }, - { - name: "completed", - attrs: ipp.Attributes{ - ipp.AttributeJobState: []ipp.Attribute{{Value: 9}}, - }, - want: "completed", - }, - { - name: "unknown", - attrs: ipp.Attributes{}, - want: "unknown", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := parseJobState(tt.attrs) - assert.Equal(t, tt.want, got) - }) - } -} - -func TestGetStringAttr(t *testing.T) { - tests := []struct { - name string - attrs ipp.Attributes - key string - want string - }{ - { - name: "string value", - attrs: ipp.Attributes{ - "test-key": []ipp.Attribute{{Value: "test-value"}}, - }, - key: "test-key", - want: "test-value", - }, - { - name: "missing key", - attrs: ipp.Attributes{}, - key: "missing", - want: "", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := getStringAttr(tt.attrs, tt.key) - assert.Equal(t, tt.want, got) - }) - } -} - -func TestGetIntAttr(t *testing.T) { - tests := []struct { - name string - attrs ipp.Attributes - key string - want int - }{ - { - name: "int value", - attrs: ipp.Attributes{ - "test-key": []ipp.Attribute{{Value: 42}}, - }, - key: "test-key", - want: 42, - }, - { - name: "missing key", - attrs: ipp.Attributes{}, - key: "missing", - want: 0, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := getIntAttr(tt.attrs, tt.key) - assert.Equal(t, tt.want, got) - }) - } -} - -func TestGetBoolAttr(t *testing.T) { - tests := []struct { - name string - attrs ipp.Attributes - key string - want bool - }{ - { - name: "true value", - attrs: ipp.Attributes{ - "test-key": []ipp.Attribute{{Value: true}}, - }, - key: "test-key", - want: true, - }, - { - name: "false value", - attrs: ipp.Attributes{ - "test-key": []ipp.Attribute{{Value: false}}, - }, - key: "test-key", - want: false, - }, - { - name: "missing key", - attrs: ipp.Attributes{}, - key: "missing", - want: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := getBoolAttr(tt.attrs, tt.key) - assert.Equal(t, tt.want, got) - }) - } -} diff --git a/nix/inputs/dms-cli/internal/server/cups/subscription.go b/nix/inputs/dms-cli/internal/server/cups/subscription.go deleted file mode 100644 index 9b35f5f..0000000 --- a/nix/inputs/dms-cli/internal/server/cups/subscription.go +++ /dev/null @@ -1,245 +0,0 @@ -package cups - -import ( - "fmt" - "sync" - "time" - - "github.com/AvengeMedia/danklinux/internal/log" - "github.com/AvengeMedia/danklinux/pkg/ipp" -) - -type SubscriptionManager struct { - client CUPSClientInterface - subscriptionID int - sequenceNumber int - eventChan chan SubscriptionEvent - stopChan chan struct{} - wg sync.WaitGroup - baseURL string - running bool - mu sync.Mutex -} - -func NewSubscriptionManager(client CUPSClientInterface, baseURL string) *SubscriptionManager { - return &SubscriptionManager{ - client: client, - eventChan: make(chan SubscriptionEvent, 100), - stopChan: make(chan struct{}), - baseURL: baseURL, - } -} - -func (sm *SubscriptionManager) Start() error { - sm.mu.Lock() - if sm.running { - sm.mu.Unlock() - return fmt.Errorf("subscription manager already running") - } - sm.running = true - sm.mu.Unlock() - - subID, err := sm.createSubscription() - if err != nil { - sm.mu.Lock() - sm.running = false - sm.mu.Unlock() - return fmt.Errorf("failed to create subscription: %w", err) - } - - sm.subscriptionID = subID - log.Infof("[CUPS] Created IPP subscription with ID %d", subID) - - sm.wg.Add(1) - go sm.notificationLoop() - - return nil -} - -func (sm *SubscriptionManager) createSubscription() (int, error) { - req := ipp.NewRequest(ipp.OperationCreatePrinterSubscriptions, 1) - req.OperationAttributes[ipp.AttributePrinterURI] = fmt.Sprintf("%s/", sm.baseURL) - req.OperationAttributes[ipp.AttributeRequestingUserName] = "dms" - - // Subscription attributes go in SubscriptionAttributes (subscription-attributes-tag in IPP) - req.SubscriptionAttributes = map[string]interface{}{ - "notify-events": []string{ - "printer-state-changed", - "printer-added", - "printer-deleted", - "job-created", - "job-completed", - "job-state-changed", - }, - "notify-pull-method": "ippget", - "notify-lease-duration": 0, - } - - // Send to root IPP endpoint - resp, err := sm.client.SendRequest(fmt.Sprintf("%s/", sm.baseURL), req, nil) - if err != nil { - return 0, fmt.Errorf("SendRequest failed: %w", err) - } - - // Check for IPP errors - if err := resp.CheckForErrors(); err != nil { - return 0, fmt.Errorf("IPP error: %w", err) - } - - // Subscription ID comes back in SubscriptionAttributes - if len(resp.SubscriptionAttributes) > 0 { - if idAttr, ok := resp.SubscriptionAttributes[0]["notify-subscription-id"]; ok && len(idAttr) > 0 { - if val, ok := idAttr[0].Value.(int); ok { - return val, nil - } - } - } - - return 0, fmt.Errorf("no subscription ID returned") -} - -func (sm *SubscriptionManager) notificationLoop() { - defer sm.wg.Done() - - backoff := 1 * time.Second - - for { - select { - case <-sm.stopChan: - return - default: - } - - gotAny, err := sm.fetchNotificationsWithWait() - if err != nil { - log.Warnf("[CUPS] Error fetching notifications: %v", err) - jitter := time.Duration(50+(time.Now().UnixNano()%200)) * time.Millisecond - sleepTime := backoff + jitter - if sleepTime > 30*time.Second { - sleepTime = 30 * time.Second - } - select { - case <-sm.stopChan: - return - case <-time.After(sleepTime): - } - if backoff < 30*time.Second { - backoff *= 2 - } - continue - } - - backoff = 1 * time.Second - - if gotAny { - continue - } - - select { - case <-sm.stopChan: - return - case <-time.After(2 * time.Second): - } - } -} - -func (sm *SubscriptionManager) fetchNotificationsWithWait() (bool, error) { - req := ipp.NewRequest(ipp.OperationGetNotifications, 1) - req.OperationAttributes[ipp.AttributePrinterURI] = fmt.Sprintf("%s/", sm.baseURL) - req.OperationAttributes[ipp.AttributeRequestingUserName] = "dms" - req.OperationAttributes["notify-subscription-ids"] = sm.subscriptionID - if sm.sequenceNumber > 0 { - req.OperationAttributes["notify-sequence-numbers"] = sm.sequenceNumber - } - - resp, err := sm.client.SendRequest(fmt.Sprintf("%s/", sm.baseURL), req, nil) - if err != nil { - return false, err - } - - gotAny := false - for _, eventGroup := range resp.SubscriptionAttributes { - if seqAttr, ok := eventGroup["notify-sequence-number"]; ok && len(seqAttr) > 0 { - if seqNum, ok := seqAttr[0].Value.(int); ok { - sm.sequenceNumber = seqNum + 1 - } - } - - event := sm.parseEvent(eventGroup) - gotAny = true - select { - case sm.eventChan <- event: - case <-sm.stopChan: - return gotAny, nil - default: - log.Warn("[CUPS] Event channel full, dropping event") - } - } - - return gotAny, nil -} - -func (sm *SubscriptionManager) parseEvent(attrs ipp.Attributes) SubscriptionEvent { - event := SubscriptionEvent{ - SubscribedAt: time.Now(), - } - - if attr, ok := attrs["notify-subscribed-event"]; ok && len(attr) > 0 { - if val, ok := attr[0].Value.(string); ok { - event.EventName = val - } - } - - if attr, ok := attrs["printer-name"]; ok && len(attr) > 0 { - if val, ok := attr[0].Value.(string); ok { - event.PrinterName = val - } - } - - if attr, ok := attrs["notify-job-id"]; ok && len(attr) > 0 { - if val, ok := attr[0].Value.(int); ok { - event.JobID = val - } - } - - return event -} - -func (sm *SubscriptionManager) Events() <-chan SubscriptionEvent { - return sm.eventChan -} - -func (sm *SubscriptionManager) Stop() { - sm.mu.Lock() - if !sm.running { - sm.mu.Unlock() - return - } - sm.running = false - sm.mu.Unlock() - - close(sm.stopChan) - sm.wg.Wait() - - if sm.subscriptionID != 0 { - sm.cancelSubscription() - sm.subscriptionID = 0 - sm.sequenceNumber = 0 - } - - sm.stopChan = make(chan struct{}) -} - -func (sm *SubscriptionManager) cancelSubscription() { - req := ipp.NewRequest(ipp.OperationCancelSubscription, 1) - req.OperationAttributes[ipp.AttributePrinterURI] = fmt.Sprintf("%s/", sm.baseURL) - req.OperationAttributes[ipp.AttributeRequestingUserName] = "dms" - req.OperationAttributes["notify-subscription-id"] = sm.subscriptionID - - _, err := sm.client.SendRequest(fmt.Sprintf("%s/", sm.baseURL), req, nil) - if err != nil { - log.Warnf("[CUPS] Failed to cancel subscription %d: %v", sm.subscriptionID, err) - } else { - log.Infof("[CUPS] Cancelled subscription %d", sm.subscriptionID) - } -} diff --git a/nix/inputs/dms-cli/internal/server/cups/subscription_dbus.go b/nix/inputs/dms-cli/internal/server/cups/subscription_dbus.go deleted file mode 100644 index e438318..0000000 --- a/nix/inputs/dms-cli/internal/server/cups/subscription_dbus.go +++ /dev/null @@ -1,295 +0,0 @@ -package cups - -import ( - "fmt" - "strings" - "sync" - - "github.com/AvengeMedia/danklinux/internal/log" - "github.com/AvengeMedia/danklinux/pkg/ipp" - "github.com/godbus/dbus/v5" -) - -type DBusSubscriptionManager struct { - client CUPSClientInterface - subscriptionID int - eventChan chan SubscriptionEvent - stopChan chan struct{} - wg sync.WaitGroup - baseURL string - running bool - mu sync.Mutex - conn *dbus.Conn -} - -func NewDBusSubscriptionManager(client CUPSClientInterface, baseURL string) *DBusSubscriptionManager { - return &DBusSubscriptionManager{ - client: client, - eventChan: make(chan SubscriptionEvent, 100), - stopChan: make(chan struct{}), - baseURL: baseURL, - } -} - -func (sm *DBusSubscriptionManager) Start() error { - sm.mu.Lock() - if sm.running { - sm.mu.Unlock() - return fmt.Errorf("subscription manager already running") - } - sm.running = true - sm.mu.Unlock() - - conn, err := dbus.ConnectSystemBus() - if err != nil { - sm.mu.Lock() - sm.running = false - sm.mu.Unlock() - return fmt.Errorf("connect to system bus: %w", err) - } - sm.conn = conn - - subID, err := sm.createDBusSubscription() - if err != nil { - sm.conn.Close() - sm.mu.Lock() - sm.running = false - sm.mu.Unlock() - return fmt.Errorf("failed to create D-Bus subscription: %w", err) - } - - sm.subscriptionID = subID - log.Infof("[CUPS] Created D-Bus subscription with ID %d", subID) - - if err := sm.conn.AddMatchSignal( - dbus.WithMatchInterface("org.cups.cupsd.Notifier"), - ); err != nil { - sm.cancelSubscription() - sm.conn.Close() - sm.mu.Lock() - sm.running = false - sm.mu.Unlock() - return fmt.Errorf("failed to add D-Bus match: %w", err) - } - - sm.wg.Add(1) - go sm.dbusListenerLoop() - - return nil -} - -func (sm *DBusSubscriptionManager) createDBusSubscription() (int, error) { - req := ipp.NewRequest(ipp.OperationCreatePrinterSubscriptions, 2) - req.OperationAttributes[ipp.AttributePrinterURI] = fmt.Sprintf("%s/", sm.baseURL) - req.OperationAttributes[ipp.AttributeRequestingUserName] = "dms" - - req.SubscriptionAttributes = map[string]interface{}{ - "notify-events": []string{ - "printer-state-changed", - "printer-added", - "printer-deleted", - "job-created", - "job-completed", - "job-state-changed", - }, - "notify-recipient-uri": "dbus:/", - "notify-lease-duration": 86400, - } - - resp, err := sm.client.SendRequest(fmt.Sprintf("%s/", sm.baseURL), req, nil) - if err != nil { - return 0, fmt.Errorf("SendRequest failed: %w", err) - } - - if err := resp.CheckForErrors(); err != nil { - return 0, fmt.Errorf("IPP error: %w", err) - } - - if len(resp.SubscriptionAttributes) > 0 { - if idAttr, ok := resp.SubscriptionAttributes[0]["notify-subscription-id"]; ok && len(idAttr) > 0 { - if val, ok := idAttr[0].Value.(int); ok { - return val, nil - } - } - } - - return 0, fmt.Errorf("no subscription ID returned") -} - -func (sm *DBusSubscriptionManager) dbusListenerLoop() { - defer sm.wg.Done() - - signalChan := make(chan *dbus.Signal, 10) - sm.conn.Signal(signalChan) - defer sm.conn.RemoveSignal(signalChan) - - for { - select { - case <-sm.stopChan: - return - case sig := <-signalChan: - if sig == nil { - continue - } - - event := sm.parseDBusSignal(sig) - if event.EventName == "" { - continue - } - - select { - case sm.eventChan <- event: - case <-sm.stopChan: - return - default: - log.Warn("[CUPS] Event channel full, dropping event") - } - } - } -} - -func (sm *DBusSubscriptionManager) parseDBusSignal(sig *dbus.Signal) SubscriptionEvent { - event := SubscriptionEvent{} - - switch sig.Name { - case "org.cups.cupsd.Notifier.JobStateChanged": - if len(sig.Body) >= 6 { - if text, ok := sig.Body[0].(string); ok { - event.EventName = "job-state-changed" - parts := strings.Split(text, " ") - if len(parts) >= 2 { - event.PrinterName = parts[0] - } - } - if printerURI, ok := sig.Body[1].(string); ok && event.PrinterName == "" { - if idx := strings.LastIndex(printerURI, "/"); idx != -1 { - event.PrinterName = printerURI[idx+1:] - } - } - if jobID, ok := sig.Body[3].(uint32); ok { - event.JobID = int(jobID) - } - } - - case "org.cups.cupsd.Notifier.JobCreated": - if len(sig.Body) >= 6 { - if text, ok := sig.Body[0].(string); ok { - event.EventName = "job-created" - parts := strings.Split(text, " ") - if len(parts) >= 2 { - event.PrinterName = parts[0] - } - } - if printerURI, ok := sig.Body[1].(string); ok && event.PrinterName == "" { - if idx := strings.LastIndex(printerURI, "/"); idx != -1 { - event.PrinterName = printerURI[idx+1:] - } - } - if jobID, ok := sig.Body[3].(uint32); ok { - event.JobID = int(jobID) - } - } - - case "org.cups.cupsd.Notifier.JobCompleted": - if len(sig.Body) >= 6 { - if text, ok := sig.Body[0].(string); ok { - event.EventName = "job-completed" - parts := strings.Split(text, " ") - if len(parts) >= 2 { - event.PrinterName = parts[0] - } - } - if printerURI, ok := sig.Body[1].(string); ok && event.PrinterName == "" { - if idx := strings.LastIndex(printerURI, "/"); idx != -1 { - event.PrinterName = printerURI[idx+1:] - } - } - if jobID, ok := sig.Body[3].(uint32); ok { - event.JobID = int(jobID) - } - } - - case "org.cups.cupsd.Notifier.PrinterStateChanged": - if len(sig.Body) >= 6 { - if text, ok := sig.Body[0].(string); ok { - event.EventName = "printer-state-changed" - parts := strings.Split(text, " ") - if len(parts) >= 2 { - event.PrinterName = parts[0] - } - } - if printerURI, ok := sig.Body[1].(string); ok && event.PrinterName == "" { - if idx := strings.LastIndex(printerURI, "/"); idx != -1 { - event.PrinterName = printerURI[idx+1:] - } - } - } - - case "org.cups.cupsd.Notifier.PrinterAdded": - if len(sig.Body) >= 6 { - if text, ok := sig.Body[0].(string); ok { - event.EventName = "printer-added" - parts := strings.Split(text, " ") - if len(parts) >= 2 { - event.PrinterName = parts[0] - } - } - } - - case "org.cups.cupsd.Notifier.PrinterDeleted": - if len(sig.Body) >= 6 { - if text, ok := sig.Body[0].(string); ok { - event.EventName = "printer-deleted" - parts := strings.Split(text, " ") - if len(parts) >= 2 { - event.PrinterName = parts[0] - } - } - } - } - - return event -} - -func (sm *DBusSubscriptionManager) Events() <-chan SubscriptionEvent { - return sm.eventChan -} - -func (sm *DBusSubscriptionManager) Stop() { - sm.mu.Lock() - if !sm.running { - sm.mu.Unlock() - return - } - sm.running = false - sm.mu.Unlock() - - close(sm.stopChan) - sm.wg.Wait() - - if sm.subscriptionID != 0 { - sm.cancelSubscription() - sm.subscriptionID = 0 - } - - if sm.conn != nil { - sm.conn.Close() - sm.conn = nil - } - - sm.stopChan = make(chan struct{}) -} - -func (sm *DBusSubscriptionManager) cancelSubscription() { - req := ipp.NewRequest(ipp.OperationCancelSubscription, 1) - req.OperationAttributes[ipp.AttributePrinterURI] = fmt.Sprintf("%s/", sm.baseURL) - req.OperationAttributes[ipp.AttributeRequestingUserName] = "dms" - req.OperationAttributes["notify-subscription-id"] = sm.subscriptionID - - _, err := sm.client.SendRequest(fmt.Sprintf("%s/", sm.baseURL), req, nil) - if err != nil { - log.Warnf("[CUPS] Failed to cancel subscription %d: %v", sm.subscriptionID, err) - } else { - log.Infof("[CUPS] Cancelled subscription %d", sm.subscriptionID) - } -} diff --git a/nix/inputs/dms-cli/internal/server/cups/types.go b/nix/inputs/dms-cli/internal/server/cups/types.go deleted file mode 100644 index 9151331..0000000 --- a/nix/inputs/dms-cli/internal/server/cups/types.go +++ /dev/null @@ -1,73 +0,0 @@ -package cups - -import ( - "io" - "sync" - "time" - - "github.com/AvengeMedia/danklinux/pkg/ipp" -) - -type CUPSState struct { - Printers map[string]*Printer `json:"printers"` -} - -type Printer struct { - Name string `json:"name"` - URI string `json:"uri"` - State string `json:"state"` - StateReason string `json:"stateReason"` - Location string `json:"location"` - Info string `json:"info"` - MakeModel string `json:"makeModel"` - Accepting bool `json:"accepting"` - Jobs []Job `json:"jobs"` -} - -type Job struct { - ID int `json:"id"` - Name string `json:"name"` - State string `json:"state"` - Printer string `json:"printer"` - User string `json:"user"` - Size int `json:"size"` - TimeCreated time.Time `json:"timeCreated"` -} - -type Manager struct { - state *CUPSState - client CUPSClientInterface - subscription SubscriptionManagerInterface - stateMutex sync.RWMutex - subscribers map[string]chan CUPSState - subMutex sync.RWMutex - stopChan chan struct{} - eventWG sync.WaitGroup - dirty chan struct{} - notifierWg sync.WaitGroup - lastNotifiedState *CUPSState - baseURL string -} - -type SubscriptionManagerInterface interface { - Start() error - Stop() - Events() <-chan SubscriptionEvent -} - -type CUPSClientInterface interface { - GetPrinters(attributes []string) (map[string]ipp.Attributes, error) - GetJobs(printer, class string, whichJobs string, myJobs bool, firstJobId, limit int, attributes []string) (map[int]ipp.Attributes, error) - CancelJob(jobID int, purge bool) error - PausePrinter(printer string) error - ResumePrinter(printer string) error - CancelAllJob(printer string, purge bool) error - SendRequest(url string, req *ipp.Request, additionalResponseData io.Writer) (*ipp.Response, error) -} - -type SubscriptionEvent struct { - EventName string - PrinterName string - JobID int - SubscribedAt time.Time -} diff --git a/nix/inputs/dms-cli/internal/server/dwl/handlers.go b/nix/inputs/dms-cli/internal/server/dwl/handlers.go deleted file mode 100644 index 5bac463..0000000 --- a/nix/inputs/dms-cli/internal/server/dwl/handlers.go +++ /dev/null @@ -1,144 +0,0 @@ -package dwl - -import ( - "encoding/json" - "fmt" - "net" - - "github.com/AvengeMedia/danklinux/internal/server/models" -) - -type Request struct { - ID int `json:"id,omitempty"` - Method string `json:"method"` - Params map[string]interface{} `json:"params,omitempty"` -} - -type SuccessResult struct { - Success bool `json:"success"` - Message string `json:"message"` -} - -func HandleRequest(conn net.Conn, req Request, manager *Manager) { - if manager == nil { - models.RespondError(conn, req.ID, "dwl manager not initialized") - return - } - - switch req.Method { - case "dwl.getState": - handleGetState(conn, req, manager) - case "dwl.setTags": - handleSetTags(conn, req, manager) - case "dwl.setClientTags": - handleSetClientTags(conn, req, manager) - case "dwl.setLayout": - handleSetLayout(conn, req, manager) - case "dwl.subscribe": - handleSubscribe(conn, req, manager) - default: - models.RespondError(conn, req.ID, fmt.Sprintf("unknown method: %s", req.Method)) - } -} - -func handleGetState(conn net.Conn, req Request, manager *Manager) { - state := manager.GetState() - models.Respond(conn, req.ID, state) -} - -func handleSetTags(conn net.Conn, req Request, manager *Manager) { - output, ok := req.Params["output"].(string) - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'output' parameter") - return - } - - tagmask, ok := req.Params["tagmask"].(float64) - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'tagmask' parameter") - return - } - - toggleTagset, ok := req.Params["toggleTagset"].(float64) - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'toggleTagset' parameter") - return - } - - if err := manager.SetTags(output, uint32(tagmask), uint32(toggleTagset)); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "tags set"}) -} - -func handleSetClientTags(conn net.Conn, req Request, manager *Manager) { - output, ok := req.Params["output"].(string) - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'output' parameter") - return - } - - andTags, ok := req.Params["andTags"].(float64) - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'andTags' parameter") - return - } - - xorTags, ok := req.Params["xorTags"].(float64) - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'xorTags' parameter") - return - } - - if err := manager.SetClientTags(output, uint32(andTags), uint32(xorTags)); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "client tags set"}) -} - -func handleSetLayout(conn net.Conn, req Request, manager *Manager) { - output, ok := req.Params["output"].(string) - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'output' parameter") - return - } - - index, ok := req.Params["index"].(float64) - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'index' parameter") - return - } - - if err := manager.SetLayout(output, uint32(index)); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "layout set"}) -} - -func handleSubscribe(conn net.Conn, req Request, manager *Manager) { - clientID := fmt.Sprintf("client-%p", conn) - stateChan := manager.Subscribe(clientID) - defer manager.Unsubscribe(clientID) - - initialState := manager.GetState() - if err := json.NewEncoder(conn).Encode(models.Response[State]{ - ID: req.ID, - Result: &initialState, - }); err != nil { - return - } - - for state := range stateChan { - if err := json.NewEncoder(conn).Encode(models.Response[State]{ - Result: &state, - }); err != nil { - return - } - } -} diff --git a/nix/inputs/dms-cli/internal/server/dwl/manager.go b/nix/inputs/dms-cli/internal/server/dwl/manager.go deleted file mode 100644 index 4bdf6e7..0000000 --- a/nix/inputs/dms-cli/internal/server/dwl/manager.go +++ /dev/null @@ -1,539 +0,0 @@ -package dwl - -import ( - "fmt" - "time" - - wlclient "github.com/yaslama/go-wayland/wayland/client" - - "github.com/AvengeMedia/danklinux/internal/log" - "github.com/AvengeMedia/danklinux/internal/proto/dwl_ipc" -) - -func NewManager(display *wlclient.Display) (*Manager, error) { - m := &Manager{ - display: display, - outputs: make(map[uint32]*outputState), - cmdq: make(chan cmd, 128), - outputSetupReq: make(chan uint32, 16), - stopChan: make(chan struct{}), - subscribers: make(map[string]chan State), - dirty: make(chan struct{}, 1), - layouts: make([]string, 0), - } - - if err := m.setupRegistry(); err != nil { - return nil, err - } - - m.updateState() - - m.notifierWg.Add(1) - go m.notifier() - - m.wg.Add(1) - go m.waylandActor() - - return m, nil -} - -func (m *Manager) post(fn func()) { - select { - case m.cmdq <- cmd{fn: fn}: - default: - log.Warn("DWL actor command queue full, dropping command") - } -} - -func (m *Manager) waylandActor() { - defer m.wg.Done() - - for { - select { - case <-m.stopChan: - return - case c := <-m.cmdq: - c.fn() - case outputID := <-m.outputSetupReq: - m.outputsMutex.RLock() - out, exists := m.outputs[outputID] - m.outputsMutex.RUnlock() - - if !exists { - log.Warnf("DWL: Output %d no longer exists, skipping setup", outputID) - continue - } - - if out.ipcOutput != nil { - continue - } - - mgr, ok := m.manager.(*dwl_ipc.ZdwlIpcManagerV2) - if !ok || mgr == nil { - log.Errorf("DWL: Manager not available for output %d setup", outputID) - continue - } - - log.Infof("DWL: Setting up ipcOutput for dynamically added output %d", outputID) - if err := m.setupOutput(mgr, out.output); err != nil { - log.Errorf("DWL: Failed to setup output %d: %v", outputID, err) - } else { - m.updateState() - } - } - } -} - -func (m *Manager) setupRegistry() error { - log.Info("DWL: starting registry setup") - ctx := m.display.Context() - - registry, err := m.display.GetRegistry() - if err != nil { - return fmt.Errorf("failed to get registry: %w", err) - } - m.registry = registry - - outputs := make([]*wlclient.Output, 0) - outputRegNames := make(map[uint32]uint32) - var dwlMgr *dwl_ipc.ZdwlIpcManagerV2 - - registry.SetGlobalHandler(func(e wlclient.RegistryGlobalEvent) { - switch e.Interface { - case dwl_ipc.ZdwlIpcManagerV2InterfaceName: - log.Infof("DWL: found %s", dwl_ipc.ZdwlIpcManagerV2InterfaceName) - manager := dwl_ipc.NewZdwlIpcManagerV2(ctx) - version := e.Version - if version > 1 { - version = 1 - } - if err := registry.Bind(e.Name, e.Interface, version, manager); err == nil { - dwlMgr = manager - log.Info("DWL: manager bound successfully") - } else { - log.Errorf("DWL: failed to bind manager: %v", err) - } - case "wl_output": - log.Debugf("DWL: found wl_output (name=%d)", e.Name) - output := wlclient.NewOutput(ctx) - - outState := &outputState{ - registryName: e.Name, - output: output, - tags: make([]TagState, 0), - } - - output.SetNameHandler(func(ev wlclient.OutputNameEvent) { - log.Debugf("DWL: Output name: %s (registry=%d)", ev.Name, e.Name) - outState.name = ev.Name - }) - - output.SetDescriptionHandler(func(ev wlclient.OutputDescriptionEvent) { - log.Debugf("DWL: Output description: %s", ev.Description) - }) - - version := e.Version - if version > 4 { - version = 4 - } - if err := registry.Bind(e.Name, e.Interface, version, output); err == nil { - outputID := output.ID() - outState.id = outputID - log.Infof("DWL: Bound wl_output id=%d registry_name=%d", outputID, e.Name) - outputs = append(outputs, output) - outputRegNames[outputID] = e.Name - - m.outputsMutex.Lock() - m.outputs[outputID] = outState - m.outputsMutex.Unlock() - - if m.manager != nil { - select { - case m.outputSetupReq <- outputID: - log.Debugf("DWL: Queued setup for output %d", outputID) - default: - log.Warnf("DWL: Setup queue full, output %d will not be initialized", outputID) - } - } - } else { - log.Errorf("DWL: Failed to bind wl_output: %v", err) - } - } - }) - - registry.SetGlobalRemoveHandler(func(e wlclient.RegistryGlobalRemoveEvent) { - m.post(func() { - m.outputsMutex.Lock() - var outToRelease *outputState - for id, out := range m.outputs { - if out.registryName == e.Name { - log.Infof("DWL: Output %d removed", id) - outToRelease = out - delete(m.outputs, id) - break - } - } - m.outputsMutex.Unlock() - - if outToRelease != nil { - if ipcOut, ok := outToRelease.ipcOutput.(*dwl_ipc.ZdwlIpcOutputV2); ok && ipcOut != nil { - m.wlMutex.Lock() - ipcOut.Release() - m.wlMutex.Unlock() - log.Debugf("DWL: Released ipcOutput for removed output %d", outToRelease.id) - } - m.updateState() - } - }) - }) - - if err := m.display.Roundtrip(); err != nil { - return fmt.Errorf("first roundtrip failed: %w", err) - } - if err := m.display.Roundtrip(); err != nil { - return fmt.Errorf("second roundtrip failed: %w", err) - } - - if dwlMgr == nil { - log.Info("DWL: manager not found in registry") - return fmt.Errorf("dwl_ipc_manager_v2 not available") - } - - dwlMgr.SetTagsHandler(func(e dwl_ipc.ZdwlIpcManagerV2TagsEvent) { - log.Infof("DWL: Tags count: %d", e.Amount) - m.tagCount = e.Amount - m.updateState() - }) - - dwlMgr.SetLayoutHandler(func(e dwl_ipc.ZdwlIpcManagerV2LayoutEvent) { - log.Infof("DWL: Layout: %s", e.Name) - m.layouts = append(m.layouts, e.Name) - m.updateState() - }) - - m.manager = dwlMgr - - for _, output := range outputs { - if err := m.setupOutput(dwlMgr, output); err != nil { - log.Warnf("DWL: Failed to setup output %d: %v", output.ID(), err) - } - } - - if err := m.display.Roundtrip(); err != nil { - return fmt.Errorf("final roundtrip failed: %w", err) - } - - log.Info("DWL: registry setup complete") - return nil -} - -func (m *Manager) setupOutput(manager *dwl_ipc.ZdwlIpcManagerV2, output *wlclient.Output) error { - m.wlMutex.Lock() - ipcOutput, err := manager.GetOutput(output) - m.wlMutex.Unlock() - if err != nil { - return fmt.Errorf("failed to get dwl output: %w", err) - } - - m.outputsMutex.Lock() - outState, exists := m.outputs[output.ID()] - if !exists { - m.outputsMutex.Unlock() - return fmt.Errorf("output state not found for id %d", output.ID()) - } - outState.ipcOutput = ipcOutput - m.outputsMutex.Unlock() - - ipcOutput.SetActiveHandler(func(e dwl_ipc.ZdwlIpcOutputV2ActiveEvent) { - outState.active = e.Active - }) - - ipcOutput.SetTagHandler(func(e dwl_ipc.ZdwlIpcOutputV2TagEvent) { - updated := false - for i, tag := range outState.tags { - if tag.Tag == e.Tag { - outState.tags[i] = TagState{ - Tag: e.Tag, - State: e.State, - Clients: e.Clients, - Focused: e.Focused, - } - updated = true - break - } - } - - if !updated { - outState.tags = append(outState.tags, TagState{ - Tag: e.Tag, - State: e.State, - Clients: e.Clients, - Focused: e.Focused, - }) - } - - m.updateState() - }) - - ipcOutput.SetLayoutHandler(func(e dwl_ipc.ZdwlIpcOutputV2LayoutEvent) { - outState.layout = e.Layout - }) - - ipcOutput.SetTitleHandler(func(e dwl_ipc.ZdwlIpcOutputV2TitleEvent) { - outState.title = e.Title - }) - - ipcOutput.SetAppidHandler(func(e dwl_ipc.ZdwlIpcOutputV2AppidEvent) { - outState.appID = e.Appid - }) - - ipcOutput.SetLayoutSymbolHandler(func(e dwl_ipc.ZdwlIpcOutputV2LayoutSymbolEvent) { - outState.layoutSymbol = e.Layout - }) - - ipcOutput.SetFrameHandler(func(e dwl_ipc.ZdwlIpcOutputV2FrameEvent) { - m.updateState() - }) - - return nil -} - -func (m *Manager) updateState() { - m.outputsMutex.RLock() - outputs := make(map[string]*OutputState) - activeOutput := "" - - for _, out := range m.outputs { - name := out.name - if name == "" { - name = fmt.Sprintf("output-%d", out.id) - } - - tagsCopy := make([]TagState, len(out.tags)) - copy(tagsCopy, out.tags) - - outputs[name] = &OutputState{ - Name: name, - Active: out.active, - Tags: tagsCopy, - Layout: out.layout, - LayoutSymbol: out.layoutSymbol, - Title: out.title, - AppID: out.appID, - } - - if out.active != 0 { - activeOutput = name - } - } - m.outputsMutex.RUnlock() - - newState := State{ - Outputs: outputs, - TagCount: m.tagCount, - Layouts: m.layouts, - ActiveOutput: activeOutput, - } - - m.stateMutex.Lock() - m.state = &newState - m.stateMutex.Unlock() - - m.notifySubscribers() -} - -func (m *Manager) notifier() { - defer m.notifierWg.Done() - const minGap = 100 * time.Millisecond - timer := time.NewTimer(minGap) - timer.Stop() - var pending bool - - for { - select { - case <-m.stopChan: - timer.Stop() - return - case <-m.dirty: - if pending { - continue - } - pending = true - timer.Reset(minGap) - case <-timer.C: - if !pending { - continue - } - m.subMutex.RLock() - subCount := len(m.subscribers) - m.subMutex.RUnlock() - - if subCount == 0 { - pending = false - continue - } - - currentState := m.GetState() - - if m.lastNotified != nil && !stateChanged(m.lastNotified, ¤tState) { - pending = false - continue - } - - m.subMutex.RLock() - for _, ch := range m.subscribers { - select { - case ch <- currentState: - default: - log.Warn("DWL: subscriber channel full, dropping update") - } - } - m.subMutex.RUnlock() - - stateCopy := currentState - m.lastNotified = &stateCopy - pending = false - } - } -} - -func (m *Manager) ensureOutputSetup(out *outputState) error { - if out.ipcOutput != nil { - return nil - } - - return fmt.Errorf("output not yet initialized - setup in progress, retry in a moment") -} - -func (m *Manager) SetTags(outputName string, tagmask uint32, toggleTagset uint32) error { - m.outputsMutex.RLock() - - availableOutputs := make([]string, 0, len(m.outputs)) - var targetOut *outputState - for _, out := range m.outputs { - name := out.name - if name == "" { - name = fmt.Sprintf("output-%d", out.id) - } - availableOutputs = append(availableOutputs, name) - if name == outputName { - targetOut = out - break - } - } - m.outputsMutex.RUnlock() - - if targetOut == nil { - return fmt.Errorf("output not found: %s (available: %v)", outputName, availableOutputs) - } - - if err := m.ensureOutputSetup(targetOut); err != nil { - return fmt.Errorf("failed to setup output %s: %w", outputName, err) - } - - ipcOut, ok := targetOut.ipcOutput.(*dwl_ipc.ZdwlIpcOutputV2) - if !ok { - return fmt.Errorf("output %s has invalid ipcOutput type", outputName) - } - - m.wlMutex.Lock() - err := ipcOut.SetTags(tagmask, toggleTagset) - m.wlMutex.Unlock() - return err -} - -func (m *Manager) SetClientTags(outputName string, andTags uint32, xorTags uint32) error { - m.outputsMutex.RLock() - - var targetOut *outputState - for _, out := range m.outputs { - name := out.name - if name == "" { - name = fmt.Sprintf("output-%d", out.id) - } - if name == outputName { - targetOut = out - break - } - } - m.outputsMutex.RUnlock() - - if targetOut == nil { - return fmt.Errorf("output not found: %s", outputName) - } - - if err := m.ensureOutputSetup(targetOut); err != nil { - return fmt.Errorf("failed to setup output %s: %w", outputName, err) - } - - ipcOut, ok := targetOut.ipcOutput.(*dwl_ipc.ZdwlIpcOutputV2) - if !ok { - return fmt.Errorf("output %s has invalid ipcOutput type", outputName) - } - - m.wlMutex.Lock() - err := ipcOut.SetClientTags(andTags, xorTags) - m.wlMutex.Unlock() - return err -} - -func (m *Manager) SetLayout(outputName string, index uint32) error { - m.outputsMutex.RLock() - - var targetOut *outputState - for _, out := range m.outputs { - name := out.name - if name == "" { - name = fmt.Sprintf("output-%d", out.id) - } - if name == outputName { - targetOut = out - break - } - } - m.outputsMutex.RUnlock() - - if targetOut == nil { - return fmt.Errorf("output not found: %s", outputName) - } - - if err := m.ensureOutputSetup(targetOut); err != nil { - return fmt.Errorf("failed to setup output %s: %w", outputName, err) - } - - ipcOut, ok := targetOut.ipcOutput.(*dwl_ipc.ZdwlIpcOutputV2) - if !ok { - return fmt.Errorf("output %s has invalid ipcOutput type", outputName) - } - - m.wlMutex.Lock() - err := ipcOut.SetLayout(index) - m.wlMutex.Unlock() - return err -} - -func (m *Manager) Close() { - close(m.stopChan) - m.wg.Wait() - m.notifierWg.Wait() - - m.subMutex.Lock() - for _, ch := range m.subscribers { - close(ch) - } - m.subscribers = make(map[string]chan State) - m.subMutex.Unlock() - - m.outputsMutex.Lock() - for _, out := range m.outputs { - if ipcOut, ok := out.ipcOutput.(*dwl_ipc.ZdwlIpcOutputV2); ok { - ipcOut.Release() - } - } - m.outputs = make(map[uint32]*outputState) - m.outputsMutex.Unlock() - - if mgr, ok := m.manager.(*dwl_ipc.ZdwlIpcManagerV2); ok { - mgr.Release() - } -} diff --git a/nix/inputs/dms-cli/internal/server/dwl/types.go b/nix/inputs/dms-cli/internal/server/dwl/types.go deleted file mode 100644 index a974df5..0000000 --- a/nix/inputs/dms-cli/internal/server/dwl/types.go +++ /dev/null @@ -1,169 +0,0 @@ -package dwl - -import ( - "sync" - - wlclient "github.com/yaslama/go-wayland/wayland/client" -) - -type TagState struct { - Tag uint32 `json:"tag"` - State uint32 `json:"state"` - Clients uint32 `json:"clients"` - Focused uint32 `json:"focused"` -} - -type OutputState struct { - Name string `json:"name"` - Active uint32 `json:"active"` - Tags []TagState `json:"tags"` - Layout uint32 `json:"layout"` - LayoutSymbol string `json:"layoutSymbol"` - Title string `json:"title"` - AppID string `json:"appId"` -} - -type State struct { - Outputs map[string]*OutputState `json:"outputs"` - TagCount uint32 `json:"tagCount"` - Layouts []string `json:"layouts"` - ActiveOutput string `json:"activeOutput"` -} - -type cmd struct { - fn func() -} - -type Manager struct { - display *wlclient.Display - registry *wlclient.Registry - manager interface{} - - outputs map[uint32]*outputState - outputsMutex sync.RWMutex - - tagCount uint32 - layouts []string - - wlMutex sync.Mutex - cmdq chan cmd - outputSetupReq chan uint32 - stopChan chan struct{} - wg sync.WaitGroup - - subscribers map[string]chan State - subMutex sync.RWMutex - dirty chan struct{} - notifierWg sync.WaitGroup - lastNotified *State - - stateMutex sync.RWMutex - state *State -} - -type outputState struct { - id uint32 - registryName uint32 - output *wlclient.Output - ipcOutput interface{} - name string - active uint32 - tags []TagState - layout uint32 - layoutSymbol string - title string - appID string -} - -func (m *Manager) GetState() State { - m.stateMutex.RLock() - defer m.stateMutex.RUnlock() - if m.state == nil { - return State{ - Outputs: make(map[string]*OutputState), - Layouts: []string{}, - TagCount: 0, - } - } - stateCopy := *m.state - return stateCopy -} - -func (m *Manager) Subscribe(id string) chan State { - ch := make(chan State, 64) - m.subMutex.Lock() - m.subscribers[id] = ch - m.subMutex.Unlock() - return ch -} - -func (m *Manager) Unsubscribe(id string) { - m.subMutex.Lock() - if ch, ok := m.subscribers[id]; ok { - close(ch) - delete(m.subscribers, id) - } - m.subMutex.Unlock() -} - -func (m *Manager) notifySubscribers() { - select { - case m.dirty <- struct{}{}: - default: - } -} - -func stateChanged(old, new *State) bool { - if old == nil || new == nil { - return true - } - if old.TagCount != new.TagCount { - return true - } - if len(old.Layouts) != len(new.Layouts) { - return true - } - if old.ActiveOutput != new.ActiveOutput { - return true - } - if len(old.Outputs) != len(new.Outputs) { - return true - } - - for name, newOut := range new.Outputs { - oldOut, exists := old.Outputs[name] - if !exists { - return true - } - if oldOut.Active != newOut.Active { - return true - } - if oldOut.Layout != newOut.Layout { - return true - } - if oldOut.LayoutSymbol != newOut.LayoutSymbol { - return true - } - if oldOut.Title != newOut.Title { - return true - } - if oldOut.AppID != newOut.AppID { - return true - } - if len(oldOut.Tags) != len(newOut.Tags) { - return true - } - for i, newTag := range newOut.Tags { - if i >= len(oldOut.Tags) { - return true - } - oldTag := oldOut.Tags[i] - if oldTag.Tag != newTag.Tag || oldTag.State != newTag.State || - oldTag.Clients != newTag.Clients || oldTag.Focused != newTag.Focused { - return true - } - } - } - - return false -} diff --git a/nix/inputs/dms-cli/internal/server/freedesktop/actions.go b/nix/inputs/dms-cli/internal/server/freedesktop/actions.go deleted file mode 100644 index c187e2e..0000000 --- a/nix/inputs/dms-cli/internal/server/freedesktop/actions.go +++ /dev/null @@ -1,128 +0,0 @@ -package freedesktop - -import ( - "context" - "fmt" - "os/exec" - "time" - - "github.com/godbus/dbus/v5" -) - -func (m *Manager) SetIconFile(iconPath string) error { - if !m.state.Accounts.Available || m.accountsObj == nil { - return fmt.Errorf("accounts service not available") - } - - err := m.accountsObj.Call(dbusAccountsUserInterface+".SetIconFile", 0, iconPath).Err - if err != nil { - return fmt.Errorf("failed to set icon file: %w", err) - } - - m.updateAccountsState() - return nil -} - -func (m *Manager) SetRealName(name string) error { - if !m.state.Accounts.Available || m.accountsObj == nil { - return fmt.Errorf("accounts service not available") - } - - err := m.accountsObj.Call(dbusAccountsUserInterface+".SetRealName", 0, name).Err - if err != nil { - return fmt.Errorf("failed to set real name: %w", err) - } - - m.updateAccountsState() - return nil -} - -func (m *Manager) SetEmail(email string) error { - if !m.state.Accounts.Available || m.accountsObj == nil { - return fmt.Errorf("accounts service not available") - } - - err := m.accountsObj.Call(dbusAccountsUserInterface+".SetEmail", 0, email).Err - if err != nil { - return fmt.Errorf("failed to set email: %w", err) - } - - m.updateAccountsState() - return nil -} - -func (m *Manager) SetLanguage(language string) error { - if !m.state.Accounts.Available || m.accountsObj == nil { - return fmt.Errorf("accounts service not available") - } - - err := m.accountsObj.Call(dbusAccountsUserInterface+".SetLanguage", 0, language).Err - if err != nil { - return fmt.Errorf("failed to set language: %w", err) - } - - m.updateAccountsState() - return nil -} - -func (m *Manager) SetLocation(location string) error { - if !m.state.Accounts.Available || m.accountsObj == nil { - return fmt.Errorf("accounts service not available") - } - - err := m.accountsObj.Call(dbusAccountsUserInterface+".SetLocation", 0, location).Err - if err != nil { - return fmt.Errorf("failed to set location: %w", err) - } - - m.updateAccountsState() - return nil -} - -func (m *Manager) GetUserIconFile(username string) (string, error) { - if m.systemConn == nil { - return "", fmt.Errorf("accounts service not available") - } - - accountsManager := m.systemConn.Object(dbusAccountsDest, dbus.ObjectPath(dbusAccountsPath)) - - var userPath dbus.ObjectPath - err := accountsManager.Call(dbusAccountsInterface+".FindUserByName", 0, username).Store(&userPath) - if err != nil { - return "", fmt.Errorf("user not found: %w", err) - } - - userObj := m.systemConn.Object(dbusAccountsDest, userPath) - variant, err := userObj.GetProperty(dbusAccountsUserInterface + ".IconFile") - if err != nil { - return "", err - } - - var iconFile string - if err := variant.Store(&iconFile); err != nil { - return "", err - } - - return iconFile, nil -} - -func (m *Manager) SetIconTheme(iconTheme string) error { - ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) - defer cancel() - - check := exec.CommandContext(ctx, "gsettings", "writable", "org.gnome.desktop.interface", "icon-theme") - if err := check.Run(); err == nil { - cmd := exec.CommandContext(ctx, "gsettings", "set", "org.gnome.desktop.interface", "icon-theme", iconTheme) - if err := cmd.Run(); err != nil { - return fmt.Errorf("gsettings set failed: %w", err) - } - return nil - } - - checkDconf := exec.CommandContext(ctx, "dconf", "write", "/org/gnome/desktop/interface/icon-theme", fmt.Sprintf("'%s'", iconTheme)) - if err := checkDconf.Run(); err != nil { - return fmt.Errorf("both gsettings and dconf unavailable or failed: %w", err) - } - - return nil -} diff --git a/nix/inputs/dms-cli/internal/server/freedesktop/actions_test.go b/nix/inputs/dms-cli/internal/server/freedesktop/actions_test.go deleted file mode 100644 index e2c21e8..0000000 --- a/nix/inputs/dms-cli/internal/server/freedesktop/actions_test.go +++ /dev/null @@ -1,145 +0,0 @@ -package freedesktop - -import ( - "sync" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestManager_SetIconFile(t *testing.T) { - t.Run("accounts not available", func(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{ - Accounts: AccountsState{ - Available: false, - }, - }, - stateMutex: sync.RWMutex{}, - } - - err := manager.SetIconFile("/path/to/icon.png") - assert.Error(t, err) - assert.Contains(t, err.Error(), "accounts service not available") - }) -} - -func TestManager_SetRealName(t *testing.T) { - t.Run("accounts not available", func(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{ - Accounts: AccountsState{ - Available: false, - }, - }, - stateMutex: sync.RWMutex{}, - } - - err := manager.SetRealName("New Name") - assert.Error(t, err) - assert.Contains(t, err.Error(), "accounts service not available") - }) -} - -func TestManager_SetEmail(t *testing.T) { - t.Run("accounts not available", func(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{ - Accounts: AccountsState{ - Available: false, - }, - }, - stateMutex: sync.RWMutex{}, - } - - err := manager.SetEmail("test@example.com") - assert.Error(t, err) - assert.Contains(t, err.Error(), "accounts service not available") - }) -} - -func TestManager_SetLanguage(t *testing.T) { - t.Run("accounts not available", func(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{ - Accounts: AccountsState{ - Available: false, - }, - }, - stateMutex: sync.RWMutex{}, - } - - err := manager.SetLanguage("en_US.UTF-8") - assert.Error(t, err) - assert.Contains(t, err.Error(), "accounts service not available") - }) -} - -func TestManager_SetLocation(t *testing.T) { - t.Run("accounts not available", func(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{ - Accounts: AccountsState{ - Available: false, - }, - }, - stateMutex: sync.RWMutex{}, - } - - err := manager.SetLocation("Test Location") - assert.Error(t, err) - assert.Contains(t, err.Error(), "accounts service not available") - }) -} - -func TestManager_GetUserIconFile(t *testing.T) { - t.Run("accounts not available", func(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{ - Accounts: AccountsState{ - Available: false, - }, - }, - stateMutex: sync.RWMutex{}, - } - - iconFile, err := manager.GetUserIconFile("testuser") - assert.Error(t, err) - assert.Contains(t, err.Error(), "accounts service not available") - assert.Empty(t, iconFile) - }) -} - -func TestManager_UpdateAccountsState(t *testing.T) { - t.Run("accounts not available", func(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{ - Accounts: AccountsState{ - Available: false, - }, - }, - stateMutex: sync.RWMutex{}, - } - - err := manager.updateAccountsState() - assert.Error(t, err) - assert.Contains(t, err.Error(), "accounts service not available") - }) -} - -func TestManager_UpdateSettingsState(t *testing.T) { - t.Run("settings not available", func(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{ - Settings: SettingsState{ - Available: false, - }, - }, - stateMutex: sync.RWMutex{}, - } - - err := manager.updateSettingsState() - assert.Error(t, err) - assert.Contains(t, err.Error(), "settings portal not available") - }) -} diff --git a/nix/inputs/dms-cli/internal/server/freedesktop/constants.go b/nix/inputs/dms-cli/internal/server/freedesktop/constants.go deleted file mode 100644 index e777ee2..0000000 --- a/nix/inputs/dms-cli/internal/server/freedesktop/constants.go +++ /dev/null @@ -1,14 +0,0 @@ -package freedesktop - -const ( - dbusAccountsDest = "org.freedesktop.Accounts" - dbusAccountsPath = "/org/freedesktop/Accounts" - dbusAccountsInterface = "org.freedesktop.Accounts" - dbusAccountsUserInterface = "org.freedesktop.Accounts.User" - - dbusPortalDest = "org.freedesktop.portal.Desktop" - dbusPortalPath = "/org/freedesktop/portal/desktop" - dbusPortalSettingsInterface = "org.freedesktop.portal.Settings" - - dbusPropsInterface = "org.freedesktop.DBus.Properties" -) diff --git a/nix/inputs/dms-cli/internal/server/freedesktop/handlers.go b/nix/inputs/dms-cli/internal/server/freedesktop/handlers.go deleted file mode 100644 index b6a54c8..0000000 --- a/nix/inputs/dms-cli/internal/server/freedesktop/handlers.go +++ /dev/null @@ -1,166 +0,0 @@ -package freedesktop - -import ( - "fmt" - "net" - - "github.com/AvengeMedia/danklinux/internal/server/models" -) - -type Request struct { - ID int `json:"id,omitempty"` - Method string `json:"method"` - Params map[string]interface{} `json:"params,omitempty"` -} - -type SuccessResult struct { - Success bool `json:"success"` - Message string `json:"message"` - Value string `json:"value,omitempty"` -} - -func HandleRequest(conn net.Conn, req Request, manager *Manager) { - switch req.Method { - case "freedesktop.getState": - handleGetState(conn, req, manager) - case "freedesktop.accounts.setIconFile": - handleSetIconFile(conn, req, manager) - case "freedesktop.accounts.setRealName": - handleSetRealName(conn, req, manager) - case "freedesktop.accounts.setEmail": - handleSetEmail(conn, req, manager) - case "freedesktop.accounts.setLanguage": - handleSetLanguage(conn, req, manager) - case "freedesktop.accounts.setLocation": - handleSetLocation(conn, req, manager) - case "freedesktop.accounts.getUserIconFile": - handleGetUserIconFile(conn, req, manager) - case "freedesktop.settings.getColorScheme": - handleGetColorScheme(conn, req, manager) - case "freedesktop.settings.setIconTheme": - handleSetIconTheme(conn, req, manager) - default: - models.RespondError(conn, req.ID, fmt.Sprintf("unknown method: %s", req.Method)) - } -} - -func handleGetState(conn net.Conn, req Request, manager *Manager) { - state := manager.GetState() - models.Respond(conn, req.ID, state) -} - -func handleSetIconFile(conn net.Conn, req Request, manager *Manager) { - iconPath, ok := req.Params["path"].(string) - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'path' parameter") - return - } - - if err := manager.SetIconFile(iconPath); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "icon file set"}) -} - -func handleSetRealName(conn net.Conn, req Request, manager *Manager) { - name, ok := req.Params["name"].(string) - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'name' parameter") - return - } - - if err := manager.SetRealName(name); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "real name set"}) -} - -func handleSetEmail(conn net.Conn, req Request, manager *Manager) { - email, ok := req.Params["email"].(string) - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'email' parameter") - return - } - - if err := manager.SetEmail(email); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "email set"}) -} - -func handleSetLanguage(conn net.Conn, req Request, manager *Manager) { - language, ok := req.Params["language"].(string) - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'language' parameter") - return - } - - if err := manager.SetLanguage(language); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "language set"}) -} - -func handleSetLocation(conn net.Conn, req Request, manager *Manager) { - location, ok := req.Params["location"].(string) - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'location' parameter") - return - } - - if err := manager.SetLocation(location); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "location set"}) -} - -func handleGetUserIconFile(conn net.Conn, req Request, manager *Manager) { - username, ok := req.Params["username"].(string) - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'username' parameter") - return - } - - iconFile, err := manager.GetUserIconFile(username) - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, SuccessResult{Success: true, Value: iconFile}) -} - -func handleGetColorScheme(conn net.Conn, req Request, manager *Manager) { - if err := manager.updateSettingsState(); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - state := manager.GetState() - models.Respond(conn, req.ID, map[string]uint32{"colorScheme": state.Settings.ColorScheme}) -} - -func handleSetIconTheme(conn net.Conn, req Request, manager *Manager) { - iconTheme, ok := req.Params["iconTheme"].(string) - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'iconTheme' parameter") - return - } - - if err := manager.SetIconTheme(iconTheme); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "icon theme set"}) -} diff --git a/nix/inputs/dms-cli/internal/server/freedesktop/handlers_test.go b/nix/inputs/dms-cli/internal/server/freedesktop/handlers_test.go deleted file mode 100644 index c72beb9..0000000 --- a/nix/inputs/dms-cli/internal/server/freedesktop/handlers_test.go +++ /dev/null @@ -1,581 +0,0 @@ -package freedesktop - -import ( - "bytes" - "encoding/json" - "net" - "sync" - "testing" - - mockdbus "github.com/AvengeMedia/danklinux/internal/mocks/github.com/godbus/dbus/v5" - "github.com/AvengeMedia/danklinux/internal/server/models" - "github.com/godbus/dbus/v5" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" -) - -type mockNetConn struct { - net.Conn - readBuf *bytes.Buffer - writeBuf *bytes.Buffer - closed bool -} - -func newMockNetConn() *mockNetConn { - return &mockNetConn{ - readBuf: &bytes.Buffer{}, - writeBuf: &bytes.Buffer{}, - } -} - -func (m *mockNetConn) Read(b []byte) (n int, err error) { - return m.readBuf.Read(b) -} - -func (m *mockNetConn) Write(b []byte) (n int, err error) { - return m.writeBuf.Write(b) -} - -func (m *mockNetConn) Close() error { - m.closed = true - return nil -} - -func mockGetAllAccountsProperties() *dbus.Call { - props := map[string]dbus.Variant{ - "IconFile": dbus.MakeVariant("/path/to/icon.png"), - "RealName": dbus.MakeVariant("Test"), - "UserName": dbus.MakeVariant("test"), - "AccountType": dbus.MakeVariant(int32(0)), - "HomeDirectory": dbus.MakeVariant("/home/test"), - "Shell": dbus.MakeVariant("/bin/bash"), - "Email": dbus.MakeVariant(""), - "Language": dbus.MakeVariant(""), - "Location": dbus.MakeVariant(""), - "Locked": dbus.MakeVariant(false), - "PasswordMode": dbus.MakeVariant(int32(1)), - } - return &dbus.Call{Err: nil, Body: []interface{}{props}} -} - -func TestRespondError_Freedesktop(t *testing.T) { - conn := newMockNetConn() - models.RespondError(conn, 123, "test error") - - var resp models.Response[any] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Equal(t, "test error", resp.Error) - assert.Nil(t, resp.Result) -} - -func TestRespond_Freedesktop(t *testing.T) { - conn := newMockNetConn() - result := SuccessResult{Success: true, Message: "test"} - models.Respond(conn, 123, result) - - var resp models.Response[SuccessResult] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Empty(t, resp.Error) - require.NotNil(t, resp.Result) - assert.True(t, resp.Result.Success) - assert.Equal(t, "test", resp.Result.Message) -} - -func TestHandleGetState(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{ - Accounts: AccountsState{ - Available: true, - UserName: "testuser", - RealName: "Test User", - UID: 1000, - }, - Settings: SettingsState{ - Available: true, - ColorScheme: 1, - }, - }, - stateMutex: sync.RWMutex{}, - } - - conn := newMockNetConn() - req := Request{ID: 123, Method: "freedesktop.getState"} - - handleGetState(conn, req, manager) - - var resp models.Response[FreedeskState] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Empty(t, resp.Error) - require.NotNil(t, resp.Result) - assert.True(t, resp.Result.Accounts.Available) - assert.Equal(t, "testuser", resp.Result.Accounts.UserName) - assert.True(t, resp.Result.Settings.Available) - assert.Equal(t, uint32(1), resp.Result.Settings.ColorScheme) -} - -func TestHandleSetIconFile(t *testing.T) { - t.Run("missing path parameter", func(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{}, - stateMutex: sync.RWMutex{}, - } - - conn := newMockNetConn() - req := Request{ - ID: 123, - Method: "freedesktop.accounts.setIconFile", - Params: map[string]interface{}{}, - } - - handleSetIconFile(conn, req, manager) - - var resp models.Response[any] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Contains(t, resp.Error, "missing or invalid 'path' parameter") - }) - - t.Run("successful set icon file", func(t *testing.T) { - mockAccountsObj := mockdbus.NewMockBusObject(t) - mockCall := &dbus.Call{Err: nil} - mockAccountsObj.EXPECT().Call("org.freedesktop.Accounts.User.SetIconFile", dbus.Flags(0), "/path/to/icon.png").Return(mockCall) - mockAccountsObj.EXPECT().CallWithContext(mock.Anything, "org.freedesktop.DBus.Properties.GetAll", dbus.Flags(0), "org.freedesktop.Accounts.User").Return(mockGetAllAccountsProperties()) - - manager := &Manager{ - state: &FreedeskState{ - Accounts: AccountsState{ - Available: true, - }, - }, - stateMutex: sync.RWMutex{}, - accountsObj: mockAccountsObj, - } - - conn := newMockNetConn() - req := Request{ - ID: 123, - Method: "freedesktop.accounts.setIconFile", - Params: map[string]interface{}{ - "path": "/path/to/icon.png", - }, - } - - handleSetIconFile(conn, req, manager) - - var resp models.Response[SuccessResult] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Empty(t, resp.Error) - require.NotNil(t, resp.Result) - assert.True(t, resp.Result.Success) - assert.Equal(t, "icon file set", resp.Result.Message) - }) - - t.Run("accounts not available", func(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{ - Accounts: AccountsState{ - Available: false, - }, - }, - stateMutex: sync.RWMutex{}, - } - - conn := newMockNetConn() - req := Request{ - ID: 123, - Method: "freedesktop.accounts.setIconFile", - Params: map[string]interface{}{ - "path": "/path/to/icon.png", - }, - } - - handleSetIconFile(conn, req, manager) - - var resp models.Response[SuccessResult] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Contains(t, resp.Error, "accounts service not available") - }) -} - -func TestHandleSetRealName(t *testing.T) { - t.Run("missing name parameter", func(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{}, - stateMutex: sync.RWMutex{}, - } - - conn := newMockNetConn() - req := Request{ - ID: 123, - Method: "freedesktop.accounts.setRealName", - Params: map[string]interface{}{}, - } - - handleSetRealName(conn, req, manager) - - var resp models.Response[any] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Contains(t, resp.Error, "missing or invalid 'name' parameter") - }) - - t.Run("successful set real name", func(t *testing.T) { - mockAccountsObj := mockdbus.NewMockBusObject(t) - mockCall := &dbus.Call{Err: nil} - mockAccountsObj.EXPECT().Call("org.freedesktop.Accounts.User.SetRealName", dbus.Flags(0), "New Name").Return(mockCall) - mockAccountsObj.EXPECT().CallWithContext(mock.Anything, "org.freedesktop.DBus.Properties.GetAll", dbus.Flags(0), "org.freedesktop.Accounts.User").Return(mockGetAllAccountsProperties()) - - manager := &Manager{ - state: &FreedeskState{ - Accounts: AccountsState{ - Available: true, - }, - }, - stateMutex: sync.RWMutex{}, - accountsObj: mockAccountsObj, - } - - conn := newMockNetConn() - req := Request{ - ID: 123, - Method: "freedesktop.accounts.setRealName", - Params: map[string]interface{}{ - "name": "New Name", - }, - } - - handleSetRealName(conn, req, manager) - - var resp models.Response[SuccessResult] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Empty(t, resp.Error) - require.NotNil(t, resp.Result) - assert.True(t, resp.Result.Success) - assert.Equal(t, "real name set", resp.Result.Message) - }) -} - -func TestHandleSetEmail(t *testing.T) { - t.Run("missing email parameter", func(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{}, - stateMutex: sync.RWMutex{}, - } - - conn := newMockNetConn() - req := Request{ - ID: 123, - Method: "freedesktop.accounts.setEmail", - Params: map[string]interface{}{}, - } - - handleSetEmail(conn, req, manager) - - var resp models.Response[any] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Contains(t, resp.Error, "missing or invalid 'email' parameter") - }) - - t.Run("successful set email", func(t *testing.T) { - mockAccountsObj := mockdbus.NewMockBusObject(t) - mockCall := &dbus.Call{Err: nil} - mockAccountsObj.EXPECT().Call("org.freedesktop.Accounts.User.SetEmail", dbus.Flags(0), "test@example.com").Return(mockCall) - mockAccountsObj.EXPECT().CallWithContext(mock.Anything, "org.freedesktop.DBus.Properties.GetAll", dbus.Flags(0), "org.freedesktop.Accounts.User").Return(mockGetAllAccountsProperties()) - - manager := &Manager{ - state: &FreedeskState{ - Accounts: AccountsState{ - Available: true, - }, - }, - stateMutex: sync.RWMutex{}, - accountsObj: mockAccountsObj, - } - - conn := newMockNetConn() - req := Request{ - ID: 123, - Method: "freedesktop.accounts.setEmail", - Params: map[string]interface{}{ - "email": "test@example.com", - }, - } - - handleSetEmail(conn, req, manager) - - var resp models.Response[SuccessResult] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Empty(t, resp.Error) - require.NotNil(t, resp.Result) - assert.True(t, resp.Result.Success) - assert.Equal(t, "email set", resp.Result.Message) - }) -} - -func TestHandleSetLanguage(t *testing.T) { - t.Run("missing language parameter", func(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{}, - stateMutex: sync.RWMutex{}, - } - - conn := newMockNetConn() - req := Request{ - ID: 123, - Method: "freedesktop.accounts.setLanguage", - Params: map[string]interface{}{}, - } - - handleSetLanguage(conn, req, manager) - - var resp models.Response[any] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Contains(t, resp.Error, "missing or invalid 'language' parameter") - }) -} - -func TestHandleSetLocation(t *testing.T) { - t.Run("missing location parameter", func(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{}, - stateMutex: sync.RWMutex{}, - } - - conn := newMockNetConn() - req := Request{ - ID: 123, - Method: "freedesktop.accounts.setLocation", - Params: map[string]interface{}{}, - } - - handleSetLocation(conn, req, manager) - - var resp models.Response[any] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Contains(t, resp.Error, "missing or invalid 'location' parameter") - }) -} - -func TestHandleGetUserIconFile(t *testing.T) { - t.Run("missing username parameter", func(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{}, - stateMutex: sync.RWMutex{}, - } - - conn := newMockNetConn() - req := Request{ - ID: 123, - Method: "freedesktop.accounts.getUserIconFile", - Params: map[string]interface{}{}, - } - - handleGetUserIconFile(conn, req, manager) - - var resp models.Response[any] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Contains(t, resp.Error, "missing or invalid 'username' parameter") - }) - - t.Run("accounts not available", func(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{ - Accounts: AccountsState{ - Available: false, - }, - }, - stateMutex: sync.RWMutex{}, - } - - conn := newMockNetConn() - req := Request{ - ID: 123, - Method: "freedesktop.accounts.getUserIconFile", - Params: map[string]interface{}{ - "username": "testuser", - }, - } - - handleGetUserIconFile(conn, req, manager) - - var resp models.Response[SuccessResult] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Contains(t, resp.Error, "accounts service not available") - }) -} - -func TestHandleGetColorScheme(t *testing.T) { - t.Run("settings not available", func(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{ - Settings: SettingsState{ - Available: false, - }, - }, - stateMutex: sync.RWMutex{}, - } - - conn := newMockNetConn() - req := Request{ID: 123, Method: "freedesktop.settings.getColorScheme"} - - handleGetColorScheme(conn, req, manager) - - var resp models.Response[map[string]uint32] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Contains(t, resp.Error, "settings portal not available") - }) - - t.Run("successful get color scheme", func(t *testing.T) { - mockSettingsObj := mockdbus.NewMockBusObject(t) - mockCall := &dbus.Call{ - Err: nil, - Body: []interface{}{dbus.MakeVariant(uint32(1))}, - } - mockSettingsObj.EXPECT().Call("org.freedesktop.portal.Settings.ReadOne", dbus.Flags(0), "org.freedesktop.appearance", "color-scheme").Return(mockCall) - - manager := &Manager{ - state: &FreedeskState{ - Settings: SettingsState{ - Available: true, - }, - }, - stateMutex: sync.RWMutex{}, - settingsObj: mockSettingsObj, - } - - conn := newMockNetConn() - req := Request{ID: 123, Method: "freedesktop.settings.getColorScheme"} - - handleGetColorScheme(conn, req, manager) - - var resp models.Response[map[string]uint32] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Empty(t, resp.Error) - require.NotNil(t, resp.Result) - assert.Equal(t, uint32(1), (*resp.Result)["colorScheme"]) - }) -} - -func TestHandleRequest(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{ - Accounts: AccountsState{ - Available: true, - UserName: "testuser", - }, - }, - stateMutex: sync.RWMutex{}, - } - - t.Run("unknown method", func(t *testing.T) { - conn := newMockNetConn() - req := Request{ - ID: 123, - Method: "freedesktop.unknown", - } - - HandleRequest(conn, req, manager) - - var resp models.Response[any] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Contains(t, resp.Error, "unknown method") - }) - - t.Run("valid method - getState", func(t *testing.T) { - conn := newMockNetConn() - req := Request{ - ID: 123, - Method: "freedesktop.getState", - } - - HandleRequest(conn, req, manager) - - var resp models.Response[FreedeskState] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Empty(t, resp.Error) - }) - - t.Run("all method routes", func(t *testing.T) { - tests := []string{ - "freedesktop.accounts.setIconFile", - "freedesktop.accounts.setRealName", - "freedesktop.accounts.setEmail", - "freedesktop.accounts.setLanguage", - "freedesktop.accounts.setLocation", - "freedesktop.accounts.getUserIconFile", - "freedesktop.settings.getColorScheme", - } - - for _, method := range tests { - conn := newMockNetConn() - req := Request{ - ID: 123, - Method: method, - Params: map[string]interface{}{}, - } - - HandleRequest(conn, req, manager) - - var resp models.Response[any] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - // Will have errors due to missing params or service unavailable - // but the method routing should work - } - }) -} diff --git a/nix/inputs/dms-cli/internal/server/freedesktop/manager.go b/nix/inputs/dms-cli/internal/server/freedesktop/manager.go deleted file mode 100644 index ef85b96..0000000 --- a/nix/inputs/dms-cli/internal/server/freedesktop/manager.go +++ /dev/null @@ -1,251 +0,0 @@ -package freedesktop - -import ( - "context" - "fmt" - "os" - "sync" - - "github.com/godbus/dbus/v5" -) - -func NewManager() (*Manager, error) { - systemConn, err := dbus.ConnectSystemBus() - if err != nil { - return nil, fmt.Errorf("failed to connect to system bus: %w", err) - } - - sessionConn, err := dbus.ConnectSessionBus() - if err != nil { - sessionConn = nil - } - - m := &Manager{ - state: &FreedeskState{ - Accounts: AccountsState{}, - Settings: SettingsState{}, - }, - stateMutex: sync.RWMutex{}, - systemConn: systemConn, - sessionConn: sessionConn, - currentUID: uint64(os.Getuid()), - subscribers: make(map[string]chan FreedeskState), - subMutex: sync.RWMutex{}, - } - - m.initializeAccounts() - m.initializeSettings() - - return m, nil -} - -func (m *Manager) initializeAccounts() error { - accountsManager := m.systemConn.Object(dbusAccountsDest, dbus.ObjectPath(dbusAccountsPath)) - - var userPath dbus.ObjectPath - err := accountsManager.Call(dbusAccountsInterface+".FindUserById", 0, int64(m.currentUID)).Store(&userPath) - if err != nil { - m.stateMutex.Lock() - m.state.Accounts.Available = false - m.stateMutex.Unlock() - return err - } - - m.accountsObj = m.systemConn.Object(dbusAccountsDest, userPath) - - m.stateMutex.Lock() - m.state.Accounts.Available = true - m.state.Accounts.UserPath = string(userPath) - m.state.Accounts.UID = m.currentUID - m.stateMutex.Unlock() - - if err := m.updateAccountsState(); err != nil { - return fmt.Errorf("failed to update accounts state: %w", err) - } - - return nil -} - -func (m *Manager) initializeSettings() error { - if m.sessionConn == nil { - m.stateMutex.Lock() - m.state.Settings.Available = false - m.stateMutex.Unlock() - return fmt.Errorf("no session bus connection") - } - - m.settingsObj = m.sessionConn.Object(dbusPortalDest, dbus.ObjectPath(dbusPortalPath)) - - var variant dbus.Variant - err := m.settingsObj.Call(dbusPortalSettingsInterface+".ReadOne", 0, "org.freedesktop.appearance", "color-scheme").Store(&variant) - if err != nil { - m.stateMutex.Lock() - m.state.Settings.Available = false - m.stateMutex.Unlock() - return err - } - - m.stateMutex.Lock() - m.state.Settings.Available = true - m.stateMutex.Unlock() - - if err := m.updateSettingsState(); err != nil { - return fmt.Errorf("failed to update settings state: %w", err) - } - - return nil -} - -func (m *Manager) updateAccountsState() error { - if !m.state.Accounts.Available || m.accountsObj == nil { - return fmt.Errorf("accounts service not available") - } - - ctx := context.Background() - props, err := m.getAccountProperties(ctx) - if err != nil { - return err - } - - m.stateMutex.Lock() - defer m.stateMutex.Unlock() - - if v, ok := props["IconFile"]; ok { - if val, ok := v.Value().(string); ok { - m.state.Accounts.IconFile = val - } - } - if v, ok := props["RealName"]; ok { - if val, ok := v.Value().(string); ok { - m.state.Accounts.RealName = val - } - } - if v, ok := props["UserName"]; ok { - if val, ok := v.Value().(string); ok { - m.state.Accounts.UserName = val - } - } - if v, ok := props["AccountType"]; ok { - if val, ok := v.Value().(int32); ok { - m.state.Accounts.AccountType = val - } - } - if v, ok := props["HomeDirectory"]; ok { - if val, ok := v.Value().(string); ok { - m.state.Accounts.HomeDirectory = val - } - } - if v, ok := props["Shell"]; ok { - if val, ok := v.Value().(string); ok { - m.state.Accounts.Shell = val - } - } - if v, ok := props["Email"]; ok { - if val, ok := v.Value().(string); ok { - m.state.Accounts.Email = val - } - } - if v, ok := props["Language"]; ok { - if val, ok := v.Value().(string); ok { - m.state.Accounts.Language = val - } - } - if v, ok := props["Location"]; ok { - if val, ok := v.Value().(string); ok { - m.state.Accounts.Location = val - } - } - if v, ok := props["Locked"]; ok { - if val, ok := v.Value().(bool); ok { - m.state.Accounts.Locked = val - } - } - if v, ok := props["PasswordMode"]; ok { - if val, ok := v.Value().(int32); ok { - m.state.Accounts.PasswordMode = val - } - } - - return nil -} - -func (m *Manager) updateSettingsState() error { - if !m.state.Settings.Available || m.settingsObj == nil { - return fmt.Errorf("settings portal not available") - } - - var variant dbus.Variant - err := m.settingsObj.Call(dbusPortalSettingsInterface+".ReadOne", 0, "org.freedesktop.appearance", "color-scheme").Store(&variant) - if err != nil { - return err - } - - if colorScheme, ok := variant.Value().(uint32); ok { - m.stateMutex.Lock() - m.state.Settings.ColorScheme = colorScheme - m.stateMutex.Unlock() - } - - return nil -} - -func (m *Manager) getAccountProperties(ctx context.Context) (map[string]dbus.Variant, error) { - var props map[string]dbus.Variant - err := m.accountsObj.CallWithContext(ctx, dbusPropsInterface+".GetAll", 0, dbusAccountsUserInterface).Store(&props) - if err != nil { - return nil, err - } - return props, nil -} - -func (m *Manager) GetState() FreedeskState { - m.stateMutex.RLock() - defer m.stateMutex.RUnlock() - return *m.state -} - -func (m *Manager) Subscribe(id string) chan FreedeskState { - ch := make(chan FreedeskState, 64) - m.subMutex.Lock() - m.subscribers[id] = ch - m.subMutex.Unlock() - return ch -} - -func (m *Manager) Unsubscribe(id string) { - m.subMutex.Lock() - if ch, ok := m.subscribers[id]; ok { - close(ch) - delete(m.subscribers, id) - } - m.subMutex.Unlock() -} - -func (m *Manager) NotifySubscribers() { - m.subMutex.RLock() - defer m.subMutex.RUnlock() - - state := m.GetState() - for _, ch := range m.subscribers { - select { - case ch <- state: - default: - } - } -} - -func (m *Manager) Close() { - m.subMutex.Lock() - for id, ch := range m.subscribers { - close(ch) - delete(m.subscribers, id) - } - m.subMutex.Unlock() - - if m.systemConn != nil { - m.systemConn.Close() - } - if m.sessionConn != nil { - m.sessionConn.Close() - } -} diff --git a/nix/inputs/dms-cli/internal/server/freedesktop/manager_test.go b/nix/inputs/dms-cli/internal/server/freedesktop/manager_test.go deleted file mode 100644 index 85b9b07..0000000 --- a/nix/inputs/dms-cli/internal/server/freedesktop/manager_test.go +++ /dev/null @@ -1,154 +0,0 @@ -package freedesktop - -import ( - "sync" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestManager_GetState(t *testing.T) { - state := &FreedeskState{ - Accounts: AccountsState{ - Available: true, - UserName: "testuser", - RealName: "Test User", - UID: 1000, - }, - Settings: SettingsState{ - Available: true, - ColorScheme: 1, - }, - } - - manager := &Manager{ - state: state, - stateMutex: sync.RWMutex{}, - } - - result := manager.GetState() - assert.True(t, result.Accounts.Available) - assert.Equal(t, "testuser", result.Accounts.UserName) - assert.Equal(t, "Test User", result.Accounts.RealName) - assert.Equal(t, uint64(1000), result.Accounts.UID) - assert.True(t, result.Settings.Available) - assert.Equal(t, uint32(1), result.Settings.ColorScheme) -} - -func TestManager_GetState_ThreadSafe(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{ - Accounts: AccountsState{ - Available: true, - UserName: "testuser", - }, - Settings: SettingsState{ - Available: true, - ColorScheme: 1, - }, - }, - stateMutex: sync.RWMutex{}, - } - - // Test concurrent reads - done := make(chan bool) - for i := 0; i < 10; i++ { - go func() { - state := manager.GetState() - assert.True(t, state.Accounts.Available) - assert.Equal(t, "testuser", state.Accounts.UserName) - done <- true - }() - } - - // Wait for all goroutines to complete - for i := 0; i < 10; i++ { - <-done - } -} - -func TestManager_Close(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{}, - stateMutex: sync.RWMutex{}, - systemConn: nil, // Would be set in real scenario - sessionConn: nil, - } - - // Should not panic even with nil connections - assert.NotPanics(t, func() { - manager.Close() - }) -} - -func TestNewManager(t *testing.T) { - // This test will fail in environments without freedesktop D-Bus services - // It's primarily for local testing with proper desktop environment - t.Run("attempts to create manager", func(t *testing.T) { - manager, err := NewManager() - if err != nil { - // Expected in test environments without freedesktop services - assert.Nil(t, manager) - } else { - assert.NotNil(t, manager) - assert.NotNil(t, manager.state) - assert.NotNil(t, manager.systemConn) - - // Clean up - manager.Close() - } - }) -} - -func TestManager_GetState_EmptyState(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{}, - stateMutex: sync.RWMutex{}, - } - - result := manager.GetState() - assert.False(t, result.Accounts.Available) - assert.Empty(t, result.Accounts.UserName) - assert.False(t, result.Settings.Available) - assert.Equal(t, uint32(0), result.Settings.ColorScheme) -} - -func TestManager_AccountsState_Modification(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{ - Accounts: AccountsState{ - Available: true, - UserName: "testuser", - }, - }, - stateMutex: sync.RWMutex{}, - } - - // Get state and modify it - state := manager.GetState() - state.Accounts.UserName = "modifieduser" - - // Original state should remain unchanged (copy semantics) - original := manager.GetState() - assert.Equal(t, "testuser", original.Accounts.UserName) -} - -func TestManager_SettingsState_Modification(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{ - Settings: SettingsState{ - Available: true, - ColorScheme: 0, - }, - }, - stateMutex: sync.RWMutex{}, - } - - // Get state and modify it - state := manager.GetState() - state.Settings.ColorScheme = 1 - - // Original state should remain unchanged (copy semantics) - original := manager.GetState() - assert.Equal(t, uint32(0), original.Settings.ColorScheme) -} diff --git a/nix/inputs/dms-cli/internal/server/freedesktop/types.go b/nix/inputs/dms-cli/internal/server/freedesktop/types.go deleted file mode 100644 index e46a0e8..0000000 --- a/nix/inputs/dms-cli/internal/server/freedesktop/types.go +++ /dev/null @@ -1,46 +0,0 @@ -package freedesktop - -import ( - "sync" - - "github.com/godbus/dbus/v5" -) - -type AccountsState struct { - Available bool `json:"available"` - UserPath string `json:"userPath"` - IconFile string `json:"iconFile"` - RealName string `json:"realName"` - UserName string `json:"userName"` - AccountType int32 `json:"accountType"` - HomeDirectory string `json:"homeDirectory"` - Shell string `json:"shell"` - Email string `json:"email"` - Language string `json:"language"` - Location string `json:"location"` - Locked bool `json:"locked"` - PasswordMode int32 `json:"passwordMode"` - UID uint64 `json:"uid"` -} - -type SettingsState struct { - Available bool `json:"available"` - ColorScheme uint32 `json:"colorScheme"` -} - -type FreedeskState struct { - Accounts AccountsState `json:"accounts"` - Settings SettingsState `json:"settings"` -} - -type Manager struct { - state *FreedeskState - stateMutex sync.RWMutex - systemConn *dbus.Conn - sessionConn *dbus.Conn - accountsObj dbus.BusObject - settingsObj dbus.BusObject - currentUID uint64 - subscribers map[string]chan FreedeskState - subMutex sync.RWMutex -} diff --git a/nix/inputs/dms-cli/internal/server/freedesktop/types_test.go b/nix/inputs/dms-cli/internal/server/freedesktop/types_test.go deleted file mode 100644 index deb0327..0000000 --- a/nix/inputs/dms-cli/internal/server/freedesktop/types_test.go +++ /dev/null @@ -1,70 +0,0 @@ -package freedesktop - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestAccountsState_Struct(t *testing.T) { - state := AccountsState{ - Available: true, - UserPath: "/org/freedesktop/Accounts/User1000", - RealName: "Test User", - UserName: "testuser", - Locked: false, - UID: 1000, - } - - assert.True(t, state.Available) - assert.Equal(t, "/org/freedesktop/Accounts/User1000", state.UserPath) - assert.Equal(t, "Test User", state.RealName) - assert.Equal(t, "testuser", state.UserName) - assert.Equal(t, uint64(1000), state.UID) - assert.False(t, state.Locked) -} - -func TestSettingsState_Struct(t *testing.T) { - state := SettingsState{ - Available: true, - ColorScheme: 1, // Dark mode - } - - assert.True(t, state.Available) - assert.Equal(t, uint32(1), state.ColorScheme) -} - -func TestFreedeskState_Struct(t *testing.T) { - state := FreedeskState{ - Accounts: AccountsState{ - Available: true, - UserName: "testuser", - UID: 1000, - }, - Settings: SettingsState{ - Available: true, - ColorScheme: 0, // Light mode - }, - } - - assert.True(t, state.Accounts.Available) - assert.Equal(t, "testuser", state.Accounts.UserName) - assert.True(t, state.Settings.Available) - assert.Equal(t, uint32(0), state.Settings.ColorScheme) -} - -func TestAccountsState_DefaultValues(t *testing.T) { - state := AccountsState{} - - assert.False(t, state.Available) - assert.Empty(t, state.UserPath) - assert.Empty(t, state.UserName) - assert.Equal(t, uint64(0), state.UID) -} - -func TestSettingsState_DefaultValues(t *testing.T) { - state := SettingsState{} - - assert.False(t, state.Available) - assert.Equal(t, uint32(0), state.ColorScheme) -} diff --git a/nix/inputs/dms-cli/internal/server/loginctl/actions.go b/nix/inputs/dms-cli/internal/server/loginctl/actions.go deleted file mode 100644 index 858248e..0000000 --- a/nix/inputs/dms-cli/internal/server/loginctl/actions.go +++ /dev/null @@ -1,88 +0,0 @@ -package loginctl - -import ( - "fmt" -) - -func (m *Manager) Lock() error { - if m.sessionObj == nil { - return fmt.Errorf("session object not available") - } - err := m.sessionObj.Call(dbusSessionInterface+".Lock", 0).Err - if err != nil { - if refreshErr := m.refreshSessionBinding(); refreshErr == nil { - err = m.sessionObj.Call(dbusSessionInterface+".Lock", 0).Err - } - if err != nil { - return fmt.Errorf("failed to lock session: %w", err) - } - } - return nil -} - -func (m *Manager) Unlock() error { - err := m.sessionObj.Call(dbusSessionInterface+".Unlock", 0).Err - if err != nil { - if refreshErr := m.refreshSessionBinding(); refreshErr == nil { - err = m.sessionObj.Call(dbusSessionInterface+".Unlock", 0).Err - } - if err != nil { - return fmt.Errorf("failed to unlock session: %w", err) - } - } - return nil -} - -func (m *Manager) Activate() error { - err := m.sessionObj.Call(dbusSessionInterface+".Activate", 0).Err - if err != nil { - if refreshErr := m.refreshSessionBinding(); refreshErr == nil { - err = m.sessionObj.Call(dbusSessionInterface+".Activate", 0).Err - } - if err != nil { - return fmt.Errorf("failed to activate session: %w", err) - } - } - return nil -} - -func (m *Manager) SetIdleHint(idle bool) error { - err := m.sessionObj.Call(dbusSessionInterface+".SetIdleHint", 0, idle).Err - if err != nil { - if refreshErr := m.refreshSessionBinding(); refreshErr == nil { - err = m.sessionObj.Call(dbusSessionInterface+".SetIdleHint", 0, idle).Err - } - if err != nil { - return fmt.Errorf("failed to set idle hint: %w", err) - } - } - return nil -} - -func (m *Manager) Terminate() error { - err := m.sessionObj.Call(dbusSessionInterface+".Terminate", 0).Err - if err != nil { - if refreshErr := m.refreshSessionBinding(); refreshErr == nil { - err = m.sessionObj.Call(dbusSessionInterface+".Terminate", 0).Err - } - if err != nil { - return fmt.Errorf("failed to terminate session: %w", err) - } - } - return nil -} - -func (m *Manager) SetLockBeforeSuspend(enabled bool) { - m.lockBeforeSuspend.Store(enabled) -} - -func (m *Manager) SetSleepInhibitorEnabled(enabled bool) { - m.sleepInhibitorEnabled.Store(enabled) - if enabled { - // Re-acquire inhibitor if enabled - m.acquireSleepInhibitor() - } else { - // Release inhibitor if disabled - m.releaseSleepInhibitor() - } -} diff --git a/nix/inputs/dms-cli/internal/server/loginctl/constants.go b/nix/inputs/dms-cli/internal/server/loginctl/constants.go deleted file mode 100644 index f93f8d6..0000000 --- a/nix/inputs/dms-cli/internal/server/loginctl/constants.go +++ /dev/null @@ -1,9 +0,0 @@ -package loginctl - -const ( - dbusDest = "org.freedesktop.login1" - dbusPath = "/org/freedesktop/login1" - dbusManagerInterface = "org.freedesktop.login1.Manager" - dbusSessionInterface = "org.freedesktop.login1.Session" - dbusPropsInterface = "org.freedesktop.DBus.Properties" -) diff --git a/nix/inputs/dms-cli/internal/server/loginctl/handlers.go b/nix/inputs/dms-cli/internal/server/loginctl/handlers.go deleted file mode 100644 index 1d495c4..0000000 --- a/nix/inputs/dms-cli/internal/server/loginctl/handlers.go +++ /dev/null @@ -1,167 +0,0 @@ -package loginctl - -import ( - "encoding/json" - "fmt" - "net" - - "github.com/AvengeMedia/danklinux/internal/server/models" -) - -type Request struct { - ID int `json:"id,omitempty"` - Method string `json:"method"` - Params map[string]interface{} `json:"params,omitempty"` -} - -type SuccessResult struct { - Success bool `json:"success"` - Message string `json:"message"` -} - -func HandleRequest(conn net.Conn, req Request, manager *Manager) { - switch req.Method { - case "loginctl.getState": - handleGetState(conn, req, manager) - case "loginctl.lock": - handleLock(conn, req, manager) - case "loginctl.unlock": - handleUnlock(conn, req, manager) - case "loginctl.activate": - handleActivate(conn, req, manager) - case "loginctl.setIdleHint": - handleSetIdleHint(conn, req, manager) - case "loginctl.setLockBeforeSuspend": - handleSetLockBeforeSuspend(conn, req, manager) - case "loginctl.setSleepInhibitorEnabled": - handleSetSleepInhibitorEnabled(conn, req, manager) - case "loginctl.lockerReady": - handleLockerReady(conn, req, manager) - case "loginctl.terminate": - handleTerminate(conn, req, manager) - case "loginctl.subscribe": - handleSubscribe(conn, req, manager) - default: - models.RespondError(conn, req.ID, fmt.Sprintf("unknown method: %s", req.Method)) - } -} - -func handleGetState(conn net.Conn, req Request, manager *Manager) { - state := manager.GetState() - models.Respond(conn, req.ID, state) -} - -func handleLock(conn net.Conn, req Request, manager *Manager) { - if err := manager.Lock(); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "locked"}) -} - -func handleUnlock(conn net.Conn, req Request, manager *Manager) { - if err := manager.Unlock(); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "unlocked"}) -} - -func handleActivate(conn net.Conn, req Request, manager *Manager) { - if err := manager.Activate(); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "activated"}) -} - -func handleSetIdleHint(conn net.Conn, req Request, manager *Manager) { - idle, ok := req.Params["idle"].(bool) - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'idle' parameter") - return - } - - if err := manager.SetIdleHint(idle); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "idle hint set"}) -} - -func handleSetLockBeforeSuspend(conn net.Conn, req Request, manager *Manager) { - enabled, ok := req.Params["enabled"].(bool) - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'enabled' parameter") - return - } - - manager.SetLockBeforeSuspend(enabled) - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "lock before suspend set"}) -} - -func handleSetSleepInhibitorEnabled(conn net.Conn, req Request, manager *Manager) { - enabled, ok := req.Params["enabled"].(bool) - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'enabled' parameter") - return - } - - manager.SetSleepInhibitorEnabled(enabled) - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "sleep inhibitor setting updated"}) -} - -func handleLockerReady(conn net.Conn, req Request, manager *Manager) { - manager.lockTimerMu.Lock() - if manager.lockTimer != nil { - manager.lockTimer.Stop() - manager.lockTimer = nil - } - manager.lockTimerMu.Unlock() - - id := manager.sleepCycleID.Load() - manager.releaseForCycle(id) - - if manager.inSleepCycle.Load() { - manager.signalLockerReady() - } - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "ok"}) -} - -func handleTerminate(conn net.Conn, req Request, manager *Manager) { - if err := manager.Terminate(); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "terminated"}) -} - -func handleSubscribe(conn net.Conn, req Request, manager *Manager) { - clientID := fmt.Sprintf("client-%p", conn) - stateChan := manager.Subscribe(clientID) - defer manager.Unsubscribe(clientID) - - initialState := manager.GetState() - event := SessionEvent{ - Type: EventStateChanged, - Data: initialState, - } - if err := json.NewEncoder(conn).Encode(models.Response[SessionEvent]{ - ID: req.ID, - Result: &event, - }); err != nil { - return - } - - for state := range stateChan { - event := SessionEvent{ - Type: EventStateChanged, - Data: state, - } - if err := json.NewEncoder(conn).Encode(models.Response[SessionEvent]{ - Result: &event, - }); err != nil { - return - } - } -} diff --git a/nix/inputs/dms-cli/internal/server/loginctl/handlers_test.go b/nix/inputs/dms-cli/internal/server/loginctl/handlers_test.go deleted file mode 100644 index 97e6108..0000000 --- a/nix/inputs/dms-cli/internal/server/loginctl/handlers_test.go +++ /dev/null @@ -1,508 +0,0 @@ -package loginctl - -import ( - "bytes" - "encoding/json" - "net" - "sync" - "testing" - "time" - - mockdbus "github.com/AvengeMedia/danklinux/internal/mocks/github.com/godbus/dbus/v5" - "github.com/AvengeMedia/danklinux/internal/server/models" - "github.com/godbus/dbus/v5" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" -) - -type mockNetConn struct { - net.Conn - readBuf *bytes.Buffer - writeBuf *bytes.Buffer - closed bool -} - -func newMockNetConn() *mockNetConn { - return &mockNetConn{ - readBuf: &bytes.Buffer{}, - writeBuf: &bytes.Buffer{}, - } -} - -func (m *mockNetConn) Read(b []byte) (n int, err error) { - return m.readBuf.Read(b) -} - -func (m *mockNetConn) Write(b []byte) (n int, err error) { - return m.writeBuf.Write(b) -} - -func (m *mockNetConn) Close() error { - m.closed = true - return nil -} - -func TestRespondError_Loginctl(t *testing.T) { - conn := newMockNetConn() - models.RespondError(conn, 123, "test error") - - var resp models.Response[any] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Equal(t, "test error", resp.Error) - assert.Nil(t, resp.Result) -} - -func TestRespond_Loginctl(t *testing.T) { - conn := newMockNetConn() - result := SuccessResult{Success: true, Message: "test"} - models.Respond(conn, 123, result) - - var resp models.Response[SuccessResult] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Empty(t, resp.Error) - require.NotNil(t, resp.Result) - assert.True(t, resp.Result.Success) - assert.Equal(t, "test", resp.Result.Message) -} - -func TestHandleGetState(t *testing.T) { - manager := &Manager{ - state: &SessionState{ - SessionID: "1", - Locked: false, - Active: true, - SessionType: "wayland", - SessionClass: "user", - UserName: "testuser", - }, - stateMutex: sync.RWMutex{}, - } - - conn := newMockNetConn() - req := Request{ID: 123, Method: "loginctl.getState"} - - handleGetState(conn, req, manager) - - var resp models.Response[SessionState] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Empty(t, resp.Error) - require.NotNil(t, resp.Result) - assert.Equal(t, "1", resp.Result.SessionID) - assert.False(t, resp.Result.Locked) - assert.True(t, resp.Result.Active) -} - -func TestHandleLock(t *testing.T) { - t.Run("successful lock", func(t *testing.T) { - mockSessionObj := mockdbus.NewMockBusObject(t) - mockCall := &dbus.Call{Err: nil} - mockSessionObj.EXPECT().Call("org.freedesktop.login1.Session.Lock", dbus.Flags(0)).Return(mockCall) - - manager := &Manager{ - state: &SessionState{}, - stateMutex: sync.RWMutex{}, - sessionObj: mockSessionObj, - } - - conn := newMockNetConn() - req := Request{ID: 123, Method: "loginctl.lock"} - handleLock(conn, req, manager) - - var resp models.Response[SuccessResult] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Empty(t, resp.Error) - require.NotNil(t, resp.Result) - assert.True(t, resp.Result.Success) - assert.Equal(t, "locked", resp.Result.Message) - }) - - t.Run("lock fails", func(t *testing.T) { - mockSessionObj := mockdbus.NewMockBusObject(t) - mockCall := &dbus.Call{Err: assert.AnError} - mockSessionObj.EXPECT().Call("org.freedesktop.login1.Session.Lock", dbus.Flags(0)).Return(mockCall) - - manager := &Manager{ - state: &SessionState{}, - stateMutex: sync.RWMutex{}, - sessionObj: mockSessionObj, - } - - conn := newMockNetConn() - req := Request{ID: 123, Method: "loginctl.lock"} - handleLock(conn, req, manager) - - var resp models.Response[SuccessResult] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Contains(t, resp.Error, "failed to lock session") - }) -} - -func TestHandleUnlock(t *testing.T) { - t.Run("successful unlock", func(t *testing.T) { - mockSessionObj := mockdbus.NewMockBusObject(t) - mockCall := &dbus.Call{Err: nil} - mockSessionObj.EXPECT().Call("org.freedesktop.login1.Session.Unlock", dbus.Flags(0)).Return(mockCall) - - manager := &Manager{ - state: &SessionState{}, - stateMutex: sync.RWMutex{}, - sessionObj: mockSessionObj, - } - - conn := newMockNetConn() - req := Request{ID: 123, Method: "loginctl.unlock"} - handleUnlock(conn, req, manager) - - var resp models.Response[SuccessResult] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Empty(t, resp.Error) - require.NotNil(t, resp.Result) - assert.True(t, resp.Result.Success) - assert.Equal(t, "unlocked", resp.Result.Message) - }) - - t.Run("unlock fails", func(t *testing.T) { - mockSessionObj := mockdbus.NewMockBusObject(t) - mockCall := &dbus.Call{Err: assert.AnError} - mockSessionObj.EXPECT().Call("org.freedesktop.login1.Session.Unlock", dbus.Flags(0)).Return(mockCall) - - manager := &Manager{ - state: &SessionState{}, - stateMutex: sync.RWMutex{}, - sessionObj: mockSessionObj, - } - - conn := newMockNetConn() - req := Request{ID: 123, Method: "loginctl.unlock"} - handleUnlock(conn, req, manager) - - var resp models.Response[SuccessResult] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Contains(t, resp.Error, "failed to unlock session") - }) -} - -func TestHandleActivate(t *testing.T) { - t.Run("successful activate", func(t *testing.T) { - mockSessionObj := mockdbus.NewMockBusObject(t) - mockCall := &dbus.Call{Err: nil} - mockSessionObj.EXPECT().Call("org.freedesktop.login1.Session.Activate", dbus.Flags(0)).Return(mockCall) - - manager := &Manager{ - state: &SessionState{}, - stateMutex: sync.RWMutex{}, - sessionObj: mockSessionObj, - } - - conn := newMockNetConn() - req := Request{ID: 123, Method: "loginctl.activate"} - handleActivate(conn, req, manager) - - var resp models.Response[SuccessResult] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Empty(t, resp.Error) - require.NotNil(t, resp.Result) - assert.True(t, resp.Result.Success) - assert.Equal(t, "activated", resp.Result.Message) - }) - - t.Run("activate fails", func(t *testing.T) { - mockSessionObj := mockdbus.NewMockBusObject(t) - mockCall := &dbus.Call{Err: assert.AnError} - mockSessionObj.EXPECT().Call("org.freedesktop.login1.Session.Activate", dbus.Flags(0)).Return(mockCall) - - manager := &Manager{ - state: &SessionState{}, - stateMutex: sync.RWMutex{}, - sessionObj: mockSessionObj, - } - - conn := newMockNetConn() - req := Request{ID: 123, Method: "loginctl.activate"} - handleActivate(conn, req, manager) - - var resp models.Response[SuccessResult] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Contains(t, resp.Error, "failed to activate session") - }) -} - -func TestHandleSetIdleHint(t *testing.T) { - t.Run("missing idle parameter", func(t *testing.T) { - manager := &Manager{ - state: &SessionState{}, - stateMutex: sync.RWMutex{}, - } - - conn := newMockNetConn() - req := Request{ - ID: 123, - Method: "loginctl.setIdleHint", - Params: map[string]interface{}{}, - } - - handleSetIdleHint(conn, req, manager) - - var resp models.Response[any] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Contains(t, resp.Error, "missing or invalid 'idle' parameter") - }) - - t.Run("successful set idle hint true", func(t *testing.T) { - mockSessionObj := mockdbus.NewMockBusObject(t) - mockCall := &dbus.Call{Err: nil} - mockSessionObj.EXPECT().Call("org.freedesktop.login1.Session.SetIdleHint", dbus.Flags(0), true).Return(mockCall) - - manager := &Manager{ - state: &SessionState{}, - stateMutex: sync.RWMutex{}, - sessionObj: mockSessionObj, - } - - conn := newMockNetConn() - req := Request{ - ID: 123, - Method: "loginctl.setIdleHint", - Params: map[string]interface{}{ - "idle": true, - }, - } - - handleSetIdleHint(conn, req, manager) - - var resp models.Response[SuccessResult] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Empty(t, resp.Error) - require.NotNil(t, resp.Result) - assert.True(t, resp.Result.Success) - assert.Equal(t, "idle hint set", resp.Result.Message) - }) - - t.Run("set idle hint fails", func(t *testing.T) { - mockSessionObj := mockdbus.NewMockBusObject(t) - mockCall := &dbus.Call{Err: assert.AnError} - mockSessionObj.EXPECT().Call("org.freedesktop.login1.Session.SetIdleHint", dbus.Flags(0), false).Return(mockCall) - - manager := &Manager{ - state: &SessionState{}, - stateMutex: sync.RWMutex{}, - sessionObj: mockSessionObj, - } - - conn := newMockNetConn() - req := Request{ - ID: 123, - Method: "loginctl.setIdleHint", - Params: map[string]interface{}{ - "idle": false, - }, - } - - handleSetIdleHint(conn, req, manager) - - var resp models.Response[SuccessResult] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Contains(t, resp.Error, "failed to set idle hint") - }) -} - -func TestHandleTerminate(t *testing.T) { - t.Run("successful terminate", func(t *testing.T) { - mockSessionObj := mockdbus.NewMockBusObject(t) - mockCall := &dbus.Call{Err: nil} - mockSessionObj.EXPECT().Call("org.freedesktop.login1.Session.Terminate", dbus.Flags(0)).Return(mockCall) - - manager := &Manager{ - state: &SessionState{}, - stateMutex: sync.RWMutex{}, - sessionObj: mockSessionObj, - } - - conn := newMockNetConn() - req := Request{ID: 123, Method: "loginctl.terminate"} - handleTerminate(conn, req, manager) - - var resp models.Response[SuccessResult] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Empty(t, resp.Error) - require.NotNil(t, resp.Result) - assert.True(t, resp.Result.Success) - assert.Equal(t, "terminated", resp.Result.Message) - }) - - t.Run("terminate fails", func(t *testing.T) { - mockSessionObj := mockdbus.NewMockBusObject(t) - mockCall := &dbus.Call{Err: assert.AnError} - mockSessionObj.EXPECT().Call("org.freedesktop.login1.Session.Terminate", dbus.Flags(0)).Return(mockCall) - - manager := &Manager{ - state: &SessionState{}, - stateMutex: sync.RWMutex{}, - sessionObj: mockSessionObj, - } - - conn := newMockNetConn() - req := Request{ID: 123, Method: "loginctl.terminate"} - handleTerminate(conn, req, manager) - - var resp models.Response[SuccessResult] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Contains(t, resp.Error, "failed to terminate session") - }) -} - -func TestHandleRequest(t *testing.T) { - manager := &Manager{ - state: &SessionState{ - SessionID: "1", - Locked: false, - }, - stateMutex: sync.RWMutex{}, - } - - t.Run("unknown method", func(t *testing.T) { - conn := newMockNetConn() - req := Request{ - ID: 123, - Method: "loginctl.unknown", - } - - HandleRequest(conn, req, manager) - - var resp models.Response[any] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Contains(t, resp.Error, "unknown method") - }) - - t.Run("valid method - getState", func(t *testing.T) { - conn := newMockNetConn() - req := Request{ - ID: 123, - Method: "loginctl.getState", - } - - HandleRequest(conn, req, manager) - - var resp models.Response[SessionState] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Empty(t, resp.Error) - }) - - t.Run("lock method", func(t *testing.T) { - mockSessionObj := mockdbus.NewMockBusObject(t) - mockCall := &dbus.Call{Err: nil} - mockSessionObj.EXPECT().Call("org.freedesktop.login1.Session.Lock", mock.Anything).Return(mockCall) - - manager.sessionObj = mockSessionObj - - conn := newMockNetConn() - req := Request{ - ID: 123, - Method: "loginctl.lock", - } - - HandleRequest(conn, req, manager) - - var resp models.Response[any] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - }) -} - -func TestHandleSubscribe(t *testing.T) { - // Subscription requires long-running connection - just test initial response - manager := &Manager{ - state: &SessionState{ - SessionID: "1", - Locked: false, - }, - stateMutex: sync.RWMutex{}, - subscribers: make(map[string]chan SessionState), - subMutex: sync.RWMutex{}, - } - - conn := newMockNetConn() - req := Request{ID: 123, Method: "loginctl.subscribe"} - - done := make(chan bool) - // Run handleSubscribe in goroutine since it blocks - go func() { - handleSubscribe(conn, req, manager) - done <- true - }() - - // Give it a moment to send initial state - time.Sleep(50 * time.Millisecond) - - // Close connection to stop the subscription - conn.Close() - - // Try to decode the initial response - if conn.writeBuf.Len() > 0 { - var resp models.Response[SessionEvent] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - if err == nil { - assert.Equal(t, 123, resp.ID) - require.NotNil(t, resp.Result) - assert.Equal(t, EventStateChanged, resp.Result.Type) - assert.Equal(t, "1", resp.Result.Data.SessionID) - } - } - - // Wait for goroutine to finish or timeout - select { - case <-done: - case <-time.After(100 * time.Millisecond): - } -} diff --git a/nix/inputs/dms-cli/internal/server/loginctl/manager.go b/nix/inputs/dms-cli/internal/server/loginctl/manager.go deleted file mode 100644 index 58b091c..0000000 --- a/nix/inputs/dms-cli/internal/server/loginctl/manager.go +++ /dev/null @@ -1,597 +0,0 @@ -package loginctl - -import ( - "context" - "fmt" - "os" - "sync" - "time" - - "github.com/godbus/dbus/v5" -) - -func NewManager() (*Manager, error) { - conn, err := dbus.ConnectSystemBus() - if err != nil { - return nil, fmt.Errorf("failed to connect to system bus: %w", err) - } - - sessionID := os.Getenv("XDG_SESSION_ID") - if sessionID == "" { - sessionID = "self" - } - - m := &Manager{ - state: &SessionState{ - SessionID: sessionID, - }, - stateMutex: sync.RWMutex{}, - subscribers: make(map[string]chan SessionState), - subMutex: sync.RWMutex{}, - stopChan: make(chan struct{}), - conn: conn, - dirty: make(chan struct{}, 1), - signals: make(chan *dbus.Signal, 256), - } - m.sleepInhibitorEnabled.Store(true) - - if err := m.initialize(); err != nil { - conn.Close() - return nil, err - } - - if err := m.acquireSleepInhibitor(); err != nil { - fmt.Fprintf(os.Stderr, "sleep inhibitor unavailable: %v\n", err) - } - - m.notifierWg.Add(1) - go m.notifier() - - if err := m.startSignalPump(); err != nil { - m.Close() - return nil, err - } - - return m, nil -} - -func (m *Manager) initialize() error { - m.managerObj = m.conn.Object(dbusDest, dbus.ObjectPath(dbusPath)) - - m.initializeFallbackDelay() - - sessionPath, err := m.getSession(m.state.SessionID) - if err != nil { - return fmt.Errorf("failed to get session path: %w", err) - } - - m.stateMutex.Lock() - m.state.SessionPath = string(sessionPath) - m.sessionPath = sessionPath - m.stateMutex.Unlock() - - m.sessionObj = m.conn.Object(dbusDest, sessionPath) - - if err := m.updateSessionState(); err != nil { - return err - } - - return nil -} - -func (m *Manager) getSession(id string) (dbus.ObjectPath, error) { - var out dbus.ObjectPath - err := m.managerObj.Call(dbusManagerInterface+".GetSession", 0, id).Store(&out) - if err != nil { - return "", err - } - return out, nil -} - -func (m *Manager) refreshSessionBinding() error { - if m.managerObj == nil || m.conn == nil { - return fmt.Errorf("manager not fully initialized") - } - - sessionPath, err := m.getSession(m.state.SessionID) - if err != nil { - return fmt.Errorf("failed to get session path: %w", err) - } - - m.stateMutex.RLock() - currentPath := m.sessionPath - m.stateMutex.RUnlock() - - if sessionPath == currentPath { - return nil - } - - m.stopSignalPump() - - m.stateMutex.Lock() - m.state.SessionPath = string(sessionPath) - m.sessionPath = sessionPath - m.stateMutex.Unlock() - - m.sessionObj = m.conn.Object(dbusDest, sessionPath) - - if err := m.updateSessionState(); err != nil { - return err - } - - m.signals = make(chan *dbus.Signal, 256) - return m.startSignalPump() -} - -func (m *Manager) updateSessionState() error { - ctx := context.Background() - props, err := m.getSessionProperties(ctx) - if err != nil { - return err - } - - m.stateMutex.Lock() - defer m.stateMutex.Unlock() - - if v, ok := props["Active"]; ok { - if val, ok := v.Value().(bool); ok { - m.state.Active = val - } - } - if v, ok := props["IdleHint"]; ok { - if val, ok := v.Value().(bool); ok { - m.state.IdleHint = val - } - } - if v, ok := props["IdleSinceHint"]; ok { - if val, ok := v.Value().(uint64); ok { - m.state.IdleSinceHint = val - } - } - if v, ok := props["LockedHint"]; ok { - if val, ok := v.Value().(bool); ok { - m.state.LockedHint = val - m.state.Locked = val - } - } - if v, ok := props["Type"]; ok { - if val, ok := v.Value().(string); ok { - m.state.SessionType = val - } - } - if v, ok := props["Class"]; ok { - if val, ok := v.Value().(string); ok { - m.state.SessionClass = val - } - } - if v, ok := props["User"]; ok { - if userArr, ok := v.Value().([]interface{}); ok && len(userArr) >= 1 { - if uid, ok := userArr[0].(uint32); ok { - m.state.User = uid - } - } - } - if v, ok := props["Name"]; ok { - if val, ok := v.Value().(string); ok { - m.state.UserName = val - } - } - if v, ok := props["RemoteHost"]; ok { - if val, ok := v.Value().(string); ok { - m.state.RemoteHost = val - } - } - if v, ok := props["Service"]; ok { - if val, ok := v.Value().(string); ok { - m.state.Service = val - } - } - if v, ok := props["TTY"]; ok { - if val, ok := v.Value().(string); ok { - m.state.TTY = val - } - } - if v, ok := props["Display"]; ok { - if val, ok := v.Value().(string); ok { - m.state.Display = val - } - } - if v, ok := props["Remote"]; ok { - if val, ok := v.Value().(bool); ok { - m.state.Remote = val - } - } - if v, ok := props["Seat"]; ok { - if seatArr, ok := v.Value().([]interface{}); ok && len(seatArr) >= 1 { - if seatID, ok := seatArr[0].(string); ok { - m.state.Seat = seatID - } - } - } - if v, ok := props["VTNr"]; ok { - if val, ok := v.Value().(uint32); ok { - m.state.VTNr = val - } - } - - return nil -} - -func (m *Manager) getSessionProperties(ctx context.Context) (map[string]dbus.Variant, error) { - var props map[string]dbus.Variant - err := m.sessionObj.CallWithContext(ctx, dbusPropsInterface+".GetAll", 0, dbusSessionInterface).Store(&props) - if err != nil { - return nil, err - } - return props, nil -} - -func (m *Manager) acquireSleepInhibitor() error { - if !m.sleepInhibitorEnabled.Load() { - return nil - } - - m.inhibitMu.Lock() - defer m.inhibitMu.Unlock() - - if m.inhibitFile != nil { - return nil - } - - if m.managerObj == nil { - return fmt.Errorf("manager object not available") - } - - file, err := m.inhibit("sleep", "DankMaterialShell", "Lock before suspend", "delay") - if err != nil { - return err - } - - m.inhibitFile = file - return nil -} - -func (m *Manager) inhibit(what, who, why, mode string) (*os.File, error) { - var fd dbus.UnixFD - err := m.managerObj.Call(dbusManagerInterface+".Inhibit", 0, what, who, why, mode).Store(&fd) - if err != nil { - return nil, err - } - return os.NewFile(uintptr(fd), "inhibit"), nil -} - -func (m *Manager) releaseSleepInhibitor() { - m.inhibitMu.Lock() - f := m.inhibitFile - m.inhibitFile = nil - m.inhibitMu.Unlock() - if f != nil { - f.Close() - } -} - -func (m *Manager) releaseForCycle(id uint64) { - if !m.inSleepCycle.Load() || m.sleepCycleID.Load() != id { - return - } - m.releaseSleepInhibitor() -} - -func (m *Manager) initializeFallbackDelay() { - var maxDelayUSec uint64 - err := m.managerObj.Call( - dbusPropsInterface+".Get", - 0, - dbusManagerInterface, - "InhibitDelayMaxUSec", - ).Store(&maxDelayUSec) - - if err != nil { - m.fallbackDelay = 2 * time.Second - return - } - - maxDelay := time.Duration(maxDelayUSec) * time.Microsecond - computed := (maxDelay * 8) / 10 - - if computed < 2*time.Second { - m.fallbackDelay = 2 * time.Second - } else if computed > 4*time.Second { - m.fallbackDelay = 4 * time.Second - } else { - m.fallbackDelay = computed - } -} - -func (m *Manager) newLockerReadyCh() chan struct{} { - m.lockerReadyChMu.Lock() - defer m.lockerReadyChMu.Unlock() - m.lockerReadyCh = make(chan struct{}) - return m.lockerReadyCh -} - -func (m *Manager) signalLockerReady() { - m.lockerReadyChMu.Lock() - ch := m.lockerReadyCh - if ch != nil { - close(ch) - m.lockerReadyCh = nil - } - m.lockerReadyChMu.Unlock() -} - -func (m *Manager) snapshotState() SessionState { - m.stateMutex.RLock() - defer m.stateMutex.RUnlock() - return *m.state -} - -func stateChangedMeaningfully(old, new *SessionState) bool { - if old.Locked != new.Locked { - return true - } - if old.LockedHint != new.LockedHint { - return true - } - if old.Active != new.Active { - return true - } - if old.IdleHint != new.IdleHint { - return true - } - if old.PreparingForSleep != new.PreparingForSleep { - return true - } - return false -} - -func (m *Manager) GetState() SessionState { - return m.snapshotState() -} - -func (m *Manager) Subscribe(id string) chan SessionState { - ch := make(chan SessionState, 64) - m.subMutex.Lock() - m.subscribers[id] = ch - m.subMutex.Unlock() - return ch -} - -func (m *Manager) Unsubscribe(id string) { - m.subMutex.Lock() - if ch, ok := m.subscribers[id]; ok { - close(ch) - delete(m.subscribers, id) - } - m.subMutex.Unlock() -} - -func (m *Manager) notifier() { - defer m.notifierWg.Done() - const minGap = 100 * time.Millisecond - timer := time.NewTimer(minGap) - timer.Stop() - var pending bool - for { - select { - case <-m.stopChan: - timer.Stop() - return - case <-m.dirty: - if pending { - continue - } - pending = true - timer.Reset(minGap) - case <-timer.C: - if !pending { - continue - } - m.subMutex.RLock() - if len(m.subscribers) == 0 { - m.subMutex.RUnlock() - pending = false - continue - } - - currentState := m.snapshotState() - - if m.lastNotifiedState != nil && !stateChangedMeaningfully(m.lastNotifiedState, ¤tState) { - m.subMutex.RUnlock() - pending = false - continue - } - - for _, ch := range m.subscribers { - select { - case ch <- currentState: - default: - } - } - m.subMutex.RUnlock() - - stateCopy := currentState - m.lastNotifiedState = &stateCopy - pending = false - } - } -} - -func (m *Manager) notifySubscribers() { - select { - case m.dirty <- struct{}{}: - default: - } -} - -func (m *Manager) startSignalPump() error { - m.conn.Signal(m.signals) - - if err := m.conn.AddMatchSignal( - dbus.WithMatchObjectPath(m.sessionPath), - dbus.WithMatchInterface(dbusPropsInterface), - dbus.WithMatchMember("PropertiesChanged"), - ); err != nil { - m.conn.RemoveSignal(m.signals) - return err - } - if err := m.conn.AddMatchSignal( - dbus.WithMatchObjectPath(m.sessionPath), - dbus.WithMatchInterface(dbusSessionInterface), - dbus.WithMatchMember("Lock"), - ); err != nil { - m.conn.RemoveMatchSignal( - dbus.WithMatchObjectPath(m.sessionPath), - dbus.WithMatchInterface(dbusPropsInterface), - dbus.WithMatchMember("PropertiesChanged"), - ) - m.conn.RemoveSignal(m.signals) - return err - } - if err := m.conn.AddMatchSignal( - dbus.WithMatchObjectPath(m.sessionPath), - dbus.WithMatchInterface(dbusSessionInterface), - dbus.WithMatchMember("Unlock"), - ); err != nil { - m.conn.RemoveMatchSignal( - dbus.WithMatchObjectPath(m.sessionPath), - dbus.WithMatchInterface(dbusPropsInterface), - dbus.WithMatchMember("PropertiesChanged"), - ) - m.conn.RemoveMatchSignal( - dbus.WithMatchObjectPath(m.sessionPath), - dbus.WithMatchInterface(dbusSessionInterface), - dbus.WithMatchMember("Lock"), - ) - m.conn.RemoveSignal(m.signals) - return err - } - if err := m.conn.AddMatchSignal( - dbus.WithMatchObjectPath(dbus.ObjectPath(dbusPath)), - dbus.WithMatchInterface(dbusManagerInterface), - dbus.WithMatchMember("PrepareForSleep"), - ); err != nil { - m.conn.RemoveMatchSignal( - dbus.WithMatchObjectPath(m.sessionPath), - dbus.WithMatchInterface(dbusPropsInterface), - dbus.WithMatchMember("PropertiesChanged"), - ) - m.conn.RemoveMatchSignal( - dbus.WithMatchObjectPath(m.sessionPath), - dbus.WithMatchInterface(dbusSessionInterface), - dbus.WithMatchMember("Lock"), - ) - m.conn.RemoveMatchSignal( - dbus.WithMatchObjectPath(m.sessionPath), - dbus.WithMatchInterface(dbusSessionInterface), - dbus.WithMatchMember("Unlock"), - ) - m.conn.RemoveSignal(m.signals) - return err - } - - if err := m.conn.AddMatchSignal( - dbus.WithMatchObjectPath("/org/freedesktop/DBus"), - dbus.WithMatchInterface("org.freedesktop.DBus"), - dbus.WithMatchMember("NameOwnerChanged"), - ); err != nil { - m.conn.RemoveMatchSignal( - dbus.WithMatchObjectPath(m.sessionPath), - dbus.WithMatchInterface(dbusPropsInterface), - dbus.WithMatchMember("PropertiesChanged"), - ) - m.conn.RemoveMatchSignal( - dbus.WithMatchObjectPath(m.sessionPath), - dbus.WithMatchInterface(dbusSessionInterface), - dbus.WithMatchMember("Lock"), - ) - m.conn.RemoveMatchSignal( - dbus.WithMatchObjectPath(m.sessionPath), - dbus.WithMatchInterface(dbusSessionInterface), - dbus.WithMatchMember("Unlock"), - ) - m.conn.RemoveMatchSignal( - dbus.WithMatchObjectPath(dbus.ObjectPath(dbusPath)), - dbus.WithMatchInterface(dbusManagerInterface), - dbus.WithMatchMember("PrepareForSleep"), - ) - m.conn.RemoveSignal(m.signals) - return err - } - - m.sigWG.Add(1) - go func() { - defer m.sigWG.Done() - for { - select { - case <-m.stopChan: - return - case sig, ok := <-m.signals: - if !ok { - return - } - if sig == nil { - continue - } - m.handleDBusSignal(sig) - } - } - }() - return nil -} - -func (m *Manager) stopSignalPump() { - if m.conn == nil { - return - } - m.conn.RemoveMatchSignal( - dbus.WithMatchObjectPath(m.sessionPath), - dbus.WithMatchInterface(dbusPropsInterface), - dbus.WithMatchMember("PropertiesChanged"), - ) - m.conn.RemoveMatchSignal( - dbus.WithMatchObjectPath(m.sessionPath), - dbus.WithMatchInterface(dbusSessionInterface), - dbus.WithMatchMember("Lock"), - ) - m.conn.RemoveMatchSignal( - dbus.WithMatchObjectPath(m.sessionPath), - dbus.WithMatchInterface(dbusSessionInterface), - dbus.WithMatchMember("Unlock"), - ) - m.conn.RemoveMatchSignal( - dbus.WithMatchObjectPath(dbus.ObjectPath(dbusPath)), - dbus.WithMatchInterface(dbusManagerInterface), - dbus.WithMatchMember("PrepareForSleep"), - ) - m.conn.RemoveMatchSignal( - dbus.WithMatchObjectPath("/org/freedesktop/DBus"), - dbus.WithMatchInterface("org.freedesktop.DBus"), - dbus.WithMatchMember("NameOwnerChanged"), - ) - - m.conn.RemoveSignal(m.signals) - close(m.signals) - - m.sigWG.Wait() -} - -func (m *Manager) Close() { - close(m.stopChan) - m.notifierWg.Wait() - - m.stopSignalPump() - - m.releaseSleepInhibitor() - - m.subMutex.Lock() - for _, ch := range m.subscribers { - close(ch) - } - m.subscribers = make(map[string]chan SessionState) - m.subMutex.Unlock() - - if m.conn != nil { - m.conn.Close() - } -} diff --git a/nix/inputs/dms-cli/internal/server/loginctl/manager_test.go b/nix/inputs/dms-cli/internal/server/loginctl/manager_test.go deleted file mode 100644 index 30e7246..0000000 --- a/nix/inputs/dms-cli/internal/server/loginctl/manager_test.go +++ /dev/null @@ -1,336 +0,0 @@ -package loginctl - -import ( - "sync" - "testing" - "time" - - "github.com/stretchr/testify/assert" -) - -func TestManager_GetState(t *testing.T) { - state := &SessionState{ - SessionID: "1", - Locked: false, - Active: true, - IdleHint: false, - SessionType: "wayland", - SessionClass: "user", - UserName: "testuser", - } - - manager := &Manager{ - state: state, - stateMutex: sync.RWMutex{}, - } - - result := manager.GetState() - assert.Equal(t, "1", result.SessionID) - assert.False(t, result.Locked) - assert.True(t, result.Active) - assert.Equal(t, "wayland", result.SessionType) - assert.Equal(t, "testuser", result.UserName) -} - -func TestManager_Subscribe(t *testing.T) { - manager := &Manager{ - state: &SessionState{}, - subscribers: make(map[string]chan SessionState), - subMutex: sync.RWMutex{}, - } - - ch := manager.Subscribe("test-client") - assert.NotNil(t, ch) - assert.Equal(t, 64, cap(ch)) - - manager.subMutex.RLock() - _, exists := manager.subscribers["test-client"] - manager.subMutex.RUnlock() - assert.True(t, exists) -} - -func TestManager_Unsubscribe(t *testing.T) { - manager := &Manager{ - state: &SessionState{}, - subscribers: make(map[string]chan SessionState), - subMutex: sync.RWMutex{}, - } - - // Subscribe first - ch := manager.Subscribe("test-client") - - // Unsubscribe - manager.Unsubscribe("test-client") - - // Check channel is closed - _, ok := <-ch - assert.False(t, ok) - - // Check client is removed - manager.subMutex.RLock() - _, exists := manager.subscribers["test-client"] - manager.subMutex.RUnlock() - assert.False(t, exists) -} - -func TestManager_Unsubscribe_NonExistent(t *testing.T) { - manager := &Manager{ - state: &SessionState{}, - subscribers: make(map[string]chan SessionState), - subMutex: sync.RWMutex{}, - } - - // Unsubscribe a non-existent client should not panic - assert.NotPanics(t, func() { - manager.Unsubscribe("non-existent") - }) -} - -func TestManager_NotifySubscribers(t *testing.T) { - manager := &Manager{ - state: &SessionState{ - SessionID: "1", - Locked: false, - }, - stateMutex: sync.RWMutex{}, - subscribers: make(map[string]chan SessionState), - subMutex: sync.RWMutex{}, - stopChan: make(chan struct{}), - dirty: make(chan struct{}, 1), - } - manager.notifierWg.Add(1) - go manager.notifier() - - // Subscribe a client - ch := make(chan SessionState, 10) - manager.subMutex.Lock() - manager.subscribers["test-client"] = ch - manager.subMutex.Unlock() - - // Notify subscribers - manager.notifySubscribers() - - // Check that state was sent (wait for debounce timer + some slack) - select { - case state := <-ch: - assert.Equal(t, "1", state.SessionID) - assert.False(t, state.Locked) - case <-time.After(200 * time.Millisecond): - t.Fatal("did not receive state update") - } - - close(manager.stopChan) - manager.notifierWg.Wait() -} - -func TestManager_NotifySubscribers_Debounce(t *testing.T) { - manager := &Manager{ - state: &SessionState{ - SessionID: "1", - Locked: false, - }, - stateMutex: sync.RWMutex{}, - subscribers: make(map[string]chan SessionState), - subMutex: sync.RWMutex{}, - stopChan: make(chan struct{}), - dirty: make(chan struct{}, 1), - } - manager.notifierWg.Add(1) - go manager.notifier() - - // Subscribe a client - ch := make(chan SessionState, 10) - manager.subMutex.Lock() - manager.subscribers["test-client"] = ch - manager.subMutex.Unlock() - - // Send multiple rapid notifications - manager.notifySubscribers() - manager.notifySubscribers() - manager.notifySubscribers() - - // Should only receive one state update due to debouncing - receivedCount := 0 - timeout := time.After(200 * time.Millisecond) - for { - select { - case <-ch: - receivedCount++ - case <-timeout: - assert.Equal(t, 1, receivedCount, "should receive exactly one debounced update") - close(manager.stopChan) - manager.notifierWg.Wait() - return - } - } -} - -func TestManager_Close(t *testing.T) { - manager := &Manager{ - state: &SessionState{}, - stateMutex: sync.RWMutex{}, - subscribers: make(map[string]chan SessionState), - subMutex: sync.RWMutex{}, - stopChan: make(chan struct{}), - } - - // Add subscribers - ch1 := make(chan SessionState, 1) - ch2 := make(chan SessionState, 1) - manager.subMutex.Lock() - manager.subscribers["client1"] = ch1 - manager.subscribers["client2"] = ch2 - manager.subMutex.Unlock() - - // Close manager - manager.Close() - - // Check that stopChan is closed - select { - case <-manager.stopChan: - // Expected - case <-time.After(100 * time.Millisecond): - t.Fatal("stopChan not closed") - } - - // Check that subscriber channels are closed - _, ok1 := <-ch1 - _, ok2 := <-ch2 - assert.False(t, ok1, "ch1 should be closed") - assert.False(t, ok2, "ch2 should be closed") - - // Check that subscribers map is reset - assert.Len(t, manager.subscribers, 0) -} - -func TestManager_GetState_ThreadSafe(t *testing.T) { - manager := &Manager{ - state: &SessionState{ - SessionID: "1", - Locked: false, - Active: true, - }, - stateMutex: sync.RWMutex{}, - } - - // Test concurrent reads - done := make(chan bool) - for i := 0; i < 10; i++ { - go func() { - state := manager.GetState() - assert.Equal(t, "1", state.SessionID) - assert.True(t, state.Active) - done <- true - }() - } - - // Wait for all goroutines to complete - for i := 0; i < 10; i++ { - select { - case <-done: - case <-time.After(1 * time.Second): - t.Fatal("timeout waiting for goroutines") - } - } -} - -func TestStateChangedMeaningfully(t *testing.T) { - tests := []struct { - name string - old *SessionState - new *SessionState - expected bool - }{ - { - name: "no change", - old: &SessionState{Locked: false, Active: true, IdleHint: false}, - new: &SessionState{Locked: false, Active: true, IdleHint: false}, - expected: false, - }, - { - name: "locked changed", - old: &SessionState{Locked: false, Active: true, IdleHint: false}, - new: &SessionState{Locked: true, Active: true, IdleHint: false}, - expected: true, - }, - { - name: "active changed", - old: &SessionState{Locked: false, Active: true, IdleHint: false}, - new: &SessionState{Locked: false, Active: false, IdleHint: false}, - expected: true, - }, - { - name: "idle hint changed", - old: &SessionState{Locked: false, Active: true, IdleHint: false}, - new: &SessionState{Locked: false, Active: true, IdleHint: true}, - expected: true, - }, - { - name: "locked hint changed", - old: &SessionState{Locked: false, Active: true, LockedHint: false}, - new: &SessionState{Locked: false, Active: true, LockedHint: true}, - expected: true, - }, - { - name: "preparing for sleep changed", - old: &SessionState{Locked: false, Active: true, PreparingForSleep: false}, - new: &SessionState{Locked: false, Active: true, PreparingForSleep: true}, - expected: true, - }, - { - name: "non-meaningful change (username)", - old: &SessionState{Locked: false, Active: true, UserName: "user1"}, - new: &SessionState{Locked: false, Active: true, UserName: "user2"}, - expected: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := stateChangedMeaningfully(tt.old, tt.new) - assert.Equal(t, tt.expected, result) - }) - } -} - -func TestManager_SnapshotState(t *testing.T) { - manager := &Manager{ - state: &SessionState{ - SessionID: "1", - Locked: false, - Active: true, - UserName: "testuser", - }, - stateMutex: sync.RWMutex{}, - } - - snapshot := manager.snapshotState() - assert.Equal(t, "1", snapshot.SessionID) - assert.False(t, snapshot.Locked) - assert.True(t, snapshot.Active) - assert.Equal(t, "testuser", snapshot.UserName) - - // Modifying snapshot should not affect manager's state - snapshot.Locked = true - assert.False(t, manager.state.Locked) -} - -func TestNewManager(t *testing.T) { - // This test will fail in environments without systemd/login1 D-Bus - // It's primarily for local testing with systemd - t.Run("attempts to create manager", func(t *testing.T) { - manager, err := NewManager() - if err != nil { - // Expected in test environments without systemd - assert.Nil(t, manager) - } else { - assert.NotNil(t, manager) - assert.NotNil(t, manager.state) - assert.NotNil(t, manager.subscribers) - assert.NotNil(t, manager.stopChan) - - // Clean up - manager.Close() - } - }) -} diff --git a/nix/inputs/dms-cli/internal/server/loginctl/monitor.go b/nix/inputs/dms-cli/internal/server/loginctl/monitor.go deleted file mode 100644 index c430105..0000000 --- a/nix/inputs/dms-cli/internal/server/loginctl/monitor.go +++ /dev/null @@ -1,157 +0,0 @@ -package loginctl - -import ( - "time" - - "github.com/godbus/dbus/v5" -) - -func (m *Manager) handleDBusSignal(sig *dbus.Signal) { - switch sig.Name { - case dbusSessionInterface + ".Lock": - m.stateMutex.Lock() - m.state.Locked = true - m.state.LockedHint = true - m.stateMutex.Unlock() - m.notifySubscribers() - - if m.sleepInhibitorEnabled.Load() && m.inSleepCycle.Load() { - id := m.sleepCycleID.Load() - m.lockTimerMu.Lock() - if m.lockTimer != nil { - m.lockTimer.Stop() - } - m.lockTimer = time.AfterFunc(m.fallbackDelay, func() { - m.releaseForCycle(id) - }) - m.lockTimerMu.Unlock() - } - - case dbusSessionInterface + ".Unlock": - m.stateMutex.Lock() - m.state.Locked = false - m.state.LockedHint = false - m.stateMutex.Unlock() - m.notifySubscribers() - - // Cancel the lock timer if it's still running - m.lockTimerMu.Lock() - if m.lockTimer != nil { - m.lockTimer.Stop() - m.lockTimer = nil - } - m.lockTimerMu.Unlock() - - // Re-acquire the sleep inhibitor (acquireSleepInhibitor checks the enabled flag) - m.acquireSleepInhibitor() - - case dbusManagerInterface + ".PrepareForSleep": - if len(sig.Body) == 0 { - return - } - preparing, _ := sig.Body[0].(bool) - - if preparing { - cycleID := m.sleepCycleID.Add(1) - m.inSleepCycle.Store(true) - - if m.lockBeforeSuspend.Load() { - m.Lock() - } - - readyCh := m.newLockerReadyCh() - go func(id uint64, ch <-chan struct{}) { - <-ch - if m.inSleepCycle.Load() && m.sleepCycleID.Load() == id { - m.releaseSleepInhibitor() - } - }(cycleID, readyCh) - } else { - m.inSleepCycle.Store(false) - m.signalLockerReady() - m.refreshSessionBinding() - m.acquireSleepInhibitor() - } - - m.stateMutex.Lock() - m.state.PreparingForSleep = preparing - m.stateMutex.Unlock() - m.notifySubscribers() - - case dbusPropsInterface + ".PropertiesChanged": - m.handlePropertiesChanged(sig) - - case "org.freedesktop.DBus.NameOwnerChanged": - if len(sig.Body) == 3 { - name, _ := sig.Body[0].(string) - oldOwner, _ := sig.Body[1].(string) - newOwner, _ := sig.Body[2].(string) - if name == dbusDest && oldOwner != "" && newOwner != "" { - m.updateSessionState() - if !m.inSleepCycle.Load() { - m.acquireSleepInhibitor() - } - m.notifySubscribers() - } - } - } -} - -func (m *Manager) handlePropertiesChanged(sig *dbus.Signal) { - if len(sig.Body) < 2 { - return - } - - iface, ok := sig.Body[0].(string) - if !ok || iface != dbusSessionInterface { - return - } - - changes, ok := sig.Body[1].(map[string]dbus.Variant) - if !ok { - return - } - - var needsUpdate bool - - for key, variant := range changes { - switch key { - case "Active": - if val, ok := variant.Value().(bool); ok { - m.stateMutex.Lock() - m.state.Active = val - m.stateMutex.Unlock() - needsUpdate = true - } - - case "IdleHint": - if val, ok := variant.Value().(bool); ok { - m.stateMutex.Lock() - m.state.IdleHint = val - m.stateMutex.Unlock() - needsUpdate = true - } - - case "IdleSinceHint": - if val, ok := variant.Value().(uint64); ok { - m.stateMutex.Lock() - m.state.IdleSinceHint = val - m.stateMutex.Unlock() - needsUpdate = true - } - - case "LockedHint": - if val, ok := variant.Value().(bool); ok { - m.stateMutex.Lock() - m.state.LockedHint = val - m.state.Locked = val - m.stateMutex.Unlock() - needsUpdate = true - } - } - } - - if needsUpdate { - m.notifySubscribers() - } -} diff --git a/nix/inputs/dms-cli/internal/server/loginctl/monitor_test.go b/nix/inputs/dms-cli/internal/server/loginctl/monitor_test.go deleted file mode 100644 index e348622..0000000 --- a/nix/inputs/dms-cli/internal/server/loginctl/monitor_test.go +++ /dev/null @@ -1,325 +0,0 @@ -package loginctl - -import ( - "sync" - "testing" - - "github.com/godbus/dbus/v5" - "github.com/stretchr/testify/assert" -) - -func TestManager_HandleDBusSignal_Lock(t *testing.T) { - manager := &Manager{ - state: &SessionState{ - Locked: false, - LockedHint: false, - }, - stateMutex: sync.RWMutex{}, - subscribers: make(map[string]chan SessionState), - subMutex: sync.RWMutex{}, - dirty: make(chan struct{}, 1), - } - - sig := &dbus.Signal{ - Name: "org.freedesktop.login1.Session.Lock", - } - - manager.handleDBusSignal(sig) - - manager.stateMutex.RLock() - defer manager.stateMutex.RUnlock() - assert.True(t, manager.state.Locked) - assert.True(t, manager.state.LockedHint) -} - -func TestManager_HandleDBusSignal_Unlock(t *testing.T) { - manager := &Manager{ - state: &SessionState{ - Locked: true, - LockedHint: true, - }, - stateMutex: sync.RWMutex{}, - subscribers: make(map[string]chan SessionState), - subMutex: sync.RWMutex{}, - dirty: make(chan struct{}, 1), - } - - sig := &dbus.Signal{ - Name: "org.freedesktop.login1.Session.Unlock", - } - - manager.handleDBusSignal(sig) - - manager.stateMutex.RLock() - defer manager.stateMutex.RUnlock() - assert.False(t, manager.state.Locked) - assert.False(t, manager.state.LockedHint) -} - -func TestManager_HandleDBusSignal_PrepareForSleep(t *testing.T) { - t.Run("preparing for sleep - true", func(t *testing.T) { - manager := &Manager{ - state: &SessionState{ - PreparingForSleep: false, - }, - stateMutex: sync.RWMutex{}, - subscribers: make(map[string]chan SessionState), - subMutex: sync.RWMutex{}, - dirty: make(chan struct{}, 1), - } - - sig := &dbus.Signal{ - Name: "org.freedesktop.login1.Manager.PrepareForSleep", - Body: []interface{}{true}, - } - - manager.handleDBusSignal(sig) - - manager.stateMutex.RLock() - defer manager.stateMutex.RUnlock() - assert.True(t, manager.state.PreparingForSleep) - }) - - t.Run("preparing for sleep - false", func(t *testing.T) { - manager := &Manager{ - state: &SessionState{ - PreparingForSleep: true, - }, - stateMutex: sync.RWMutex{}, - subscribers: make(map[string]chan SessionState), - subMutex: sync.RWMutex{}, - dirty: make(chan struct{}, 1), - } - - sig := &dbus.Signal{ - Name: "org.freedesktop.login1.Manager.PrepareForSleep", - Body: []interface{}{false}, - } - - manager.handleDBusSignal(sig) - - manager.stateMutex.RLock() - defer manager.stateMutex.RUnlock() - assert.False(t, manager.state.PreparingForSleep) - }) - - t.Run("empty body", func(t *testing.T) { - manager := &Manager{ - state: &SessionState{ - PreparingForSleep: false, - }, - stateMutex: sync.RWMutex{}, - subscribers: make(map[string]chan SessionState), - subMutex: sync.RWMutex{}, - dirty: make(chan struct{}, 1), - } - - sig := &dbus.Signal{ - Name: "org.freedesktop.login1.Manager.PrepareForSleep", - Body: []interface{}{}, - } - - manager.handleDBusSignal(sig) - - // State should remain unchanged - manager.stateMutex.RLock() - defer manager.stateMutex.RUnlock() - assert.False(t, manager.state.PreparingForSleep) - }) -} - -func TestManager_HandlePropertiesChanged(t *testing.T) { - t.Run("active property changed", func(t *testing.T) { - manager := &Manager{ - state: &SessionState{ - Active: false, - }, - stateMutex: sync.RWMutex{}, - subscribers: make(map[string]chan SessionState), - subMutex: sync.RWMutex{}, - dirty: make(chan struct{}, 1), - } - - sig := &dbus.Signal{ - Name: "org.freedesktop.DBus.Properties.PropertiesChanged", - Body: []interface{}{ - "org.freedesktop.login1.Session", - map[string]dbus.Variant{ - "Active": dbus.MakeVariant(true), - }, - }, - } - - manager.handlePropertiesChanged(sig) - - manager.stateMutex.RLock() - defer manager.stateMutex.RUnlock() - assert.True(t, manager.state.Active) - }) - - t.Run("idle hint property changed", func(t *testing.T) { - manager := &Manager{ - state: &SessionState{ - IdleHint: false, - }, - stateMutex: sync.RWMutex{}, - subscribers: make(map[string]chan SessionState), - subMutex: sync.RWMutex{}, - dirty: make(chan struct{}, 1), - } - - sig := &dbus.Signal{ - Name: "org.freedesktop.DBus.Properties.PropertiesChanged", - Body: []interface{}{ - "org.freedesktop.login1.Session", - map[string]dbus.Variant{ - "IdleHint": dbus.MakeVariant(true), - }, - }, - } - - manager.handlePropertiesChanged(sig) - - manager.stateMutex.RLock() - defer manager.stateMutex.RUnlock() - assert.True(t, manager.state.IdleHint) - }) - - t.Run("idle since hint property changed", func(t *testing.T) { - manager := &Manager{ - state: &SessionState{ - IdleSinceHint: 0, - }, - stateMutex: sync.RWMutex{}, - subscribers: make(map[string]chan SessionState), - subMutex: sync.RWMutex{}, - dirty: make(chan struct{}, 1), - } - - sig := &dbus.Signal{ - Name: "org.freedesktop.DBus.Properties.PropertiesChanged", - Body: []interface{}{ - "org.freedesktop.login1.Session", - map[string]dbus.Variant{ - "IdleSinceHint": dbus.MakeVariant(uint64(123456789)), - }, - }, - } - - manager.handlePropertiesChanged(sig) - - manager.stateMutex.RLock() - defer manager.stateMutex.RUnlock() - assert.Equal(t, uint64(123456789), manager.state.IdleSinceHint) - }) - - t.Run("locked hint property changed", func(t *testing.T) { - manager := &Manager{ - state: &SessionState{ - LockedHint: false, - Locked: false, - }, - stateMutex: sync.RWMutex{}, - subscribers: make(map[string]chan SessionState), - subMutex: sync.RWMutex{}, - dirty: make(chan struct{}, 1), - } - - sig := &dbus.Signal{ - Name: "org.freedesktop.DBus.Properties.PropertiesChanged", - Body: []interface{}{ - "org.freedesktop.login1.Session", - map[string]dbus.Variant{ - "LockedHint": dbus.MakeVariant(true), - }, - }, - } - - manager.handlePropertiesChanged(sig) - - manager.stateMutex.RLock() - defer manager.stateMutex.RUnlock() - assert.True(t, manager.state.LockedHint) - assert.True(t, manager.state.Locked) - }) - - t.Run("wrong interface", func(t *testing.T) { - manager := &Manager{ - state: &SessionState{ - Active: false, - }, - stateMutex: sync.RWMutex{}, - subscribers: make(map[string]chan SessionState), - subMutex: sync.RWMutex{}, - dirty: make(chan struct{}, 1), - } - - sig := &dbus.Signal{ - Name: "org.freedesktop.DBus.Properties.PropertiesChanged", - Body: []interface{}{ - "org.freedesktop.SomeOtherInterface", - map[string]dbus.Variant{ - "Active": dbus.MakeVariant(true), - }, - }, - } - - manager.handlePropertiesChanged(sig) - - // State should remain unchanged - manager.stateMutex.RLock() - defer manager.stateMutex.RUnlock() - assert.False(t, manager.state.Active) - }) - - t.Run("empty body", func(t *testing.T) { - manager := &Manager{ - state: &SessionState{}, - stateMutex: sync.RWMutex{}, - subscribers: make(map[string]chan SessionState), - subMutex: sync.RWMutex{}, - dirty: make(chan struct{}, 1), - } - - sig := &dbus.Signal{ - Name: "org.freedesktop.DBus.Properties.PropertiesChanged", - Body: []interface{}{}, - } - - // Should not panic - assert.NotPanics(t, func() { - manager.handlePropertiesChanged(sig) - }) - }) - - t.Run("multiple properties changed", func(t *testing.T) { - manager := &Manager{ - state: &SessionState{ - Active: false, - IdleHint: false, - }, - stateMutex: sync.RWMutex{}, - subscribers: make(map[string]chan SessionState), - subMutex: sync.RWMutex{}, - dirty: make(chan struct{}, 1), - } - - sig := &dbus.Signal{ - Name: "org.freedesktop.DBus.Properties.PropertiesChanged", - Body: []interface{}{ - "org.freedesktop.login1.Session", - map[string]dbus.Variant{ - "Active": dbus.MakeVariant(true), - "IdleHint": dbus.MakeVariant(true), - }, - }, - } - - manager.handlePropertiesChanged(sig) - - manager.stateMutex.RLock() - defer manager.stateMutex.RUnlock() - assert.True(t, manager.state.Active) - assert.True(t, manager.state.IdleHint) - }) -} diff --git a/nix/inputs/dms-cli/internal/server/loginctl/types.go b/nix/inputs/dms-cli/internal/server/loginctl/types.go deleted file mode 100644 index 559396b..0000000 --- a/nix/inputs/dms-cli/internal/server/loginctl/types.go +++ /dev/null @@ -1,76 +0,0 @@ -package loginctl - -import ( - "os" - "sync" - "sync/atomic" - "time" - - "github.com/godbus/dbus/v5" -) - -type SessionState struct { - SessionID string `json:"sessionId"` - SessionPath string `json:"sessionPath"` - Locked bool `json:"locked"` - Active bool `json:"active"` - IdleHint bool `json:"idleHint"` - IdleSinceHint uint64 `json:"idleSinceHint"` - LockedHint bool `json:"lockedHint"` - SessionType string `json:"sessionType"` - SessionClass string `json:"sessionClass"` - User uint32 `json:"user"` - UserName string `json:"userName"` - RemoteHost string `json:"remoteHost"` - Service string `json:"service"` - TTY string `json:"tty"` - Display string `json:"display"` - Remote bool `json:"remote"` - Seat string `json:"seat"` - VTNr uint32 `json:"vtnr"` - PreparingForSleep bool `json:"preparingForSleep"` -} - -type EventType string - -const ( - EventStateChanged EventType = "state_changed" - EventLock EventType = "lock" - EventUnlock EventType = "unlock" - EventPrepareForSleep EventType = "prepare_for_sleep" - EventIdleHintChanged EventType = "idle_hint_changed" - EventLockedHintChanged EventType = "locked_hint_changed" -) - -type SessionEvent struct { - Type EventType `json:"type"` - Data SessionState `json:"data"` -} - -type Manager struct { - state *SessionState - stateMutex sync.RWMutex - subscribers map[string]chan SessionState - subMutex sync.RWMutex - stopChan chan struct{} - conn *dbus.Conn - sessionPath dbus.ObjectPath - managerObj dbus.BusObject - sessionObj dbus.BusObject - dirty chan struct{} - notifierWg sync.WaitGroup - lastNotifiedState *SessionState - signals chan *dbus.Signal - sigWG sync.WaitGroup - inhibitMu sync.Mutex - inhibitFile *os.File - lockBeforeSuspend atomic.Bool - inSleepCycle atomic.Bool - sleepCycleID atomic.Uint64 - lockerReadyChMu sync.Mutex - lockerReadyCh chan struct{} - lockTimerMu sync.Mutex - lockTimer *time.Timer - sleepInhibitorEnabled atomic.Bool - fallbackDelay time.Duration -} diff --git a/nix/inputs/dms-cli/internal/server/loginctl/types_test.go b/nix/inputs/dms-cli/internal/server/loginctl/types_test.go deleted file mode 100644 index d5063ea..0000000 --- a/nix/inputs/dms-cli/internal/server/loginctl/types_test.go +++ /dev/null @@ -1,63 +0,0 @@ -package loginctl - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestEventType_Constants(t *testing.T) { - assert.Equal(t, EventType("state_changed"), EventStateChanged) - assert.Equal(t, EventType("lock"), EventLock) - assert.Equal(t, EventType("unlock"), EventUnlock) - assert.Equal(t, EventType("prepare_for_sleep"), EventPrepareForSleep) - assert.Equal(t, EventType("idle_hint_changed"), EventIdleHintChanged) - assert.Equal(t, EventType("locked_hint_changed"), EventLockedHintChanged) -} - -func TestSessionState_Struct(t *testing.T) { - state := SessionState{ - SessionID: "1", - SessionPath: "/org/freedesktop/login1/session/_31", - Locked: false, - Active: true, - IdleHint: false, - IdleSinceHint: 0, - LockedHint: false, - SessionType: "wayland", - SessionClass: "user", - User: 1000, - UserName: "testuser", - RemoteHost: "", - Service: "gdm-password", - TTY: "tty2", - Display: ":1", - Remote: false, - Seat: "seat0", - VTNr: 2, - PreparingForSleep: false, - } - - assert.Equal(t, "1", state.SessionID) - assert.True(t, state.Active) - assert.False(t, state.Locked) - assert.Equal(t, "wayland", state.SessionType) - assert.Equal(t, uint32(1000), state.User) - assert.Equal(t, "testuser", state.UserName) -} - -func TestSessionEvent_Struct(t *testing.T) { - state := SessionState{ - SessionID: "1", - Locked: true, - } - - event := SessionEvent{ - Type: EventLock, - Data: state, - } - - assert.Equal(t, EventLock, event.Type) - assert.Equal(t, "1", event.Data.SessionID) - assert.True(t, event.Data.Locked) -} diff --git a/nix/inputs/dms-cli/internal/server/models/types.go b/nix/inputs/dms-cli/internal/server/models/types.go deleted file mode 100644 index 583c36e..0000000 --- a/nix/inputs/dms-cli/internal/server/models/types.go +++ /dev/null @@ -1,31 +0,0 @@ -package models - -import ( - "encoding/json" - "net" - - "github.com/AvengeMedia/danklinux/internal/log" -) - -type Request struct { - ID int `json:"id,omitempty"` - Method string `json:"method"` - Params map[string]interface{} `json:"params,omitempty"` -} - -type Response[T any] struct { - ID int `json:"id,omitempty"` - Result *T `json:"result,omitempty"` - Error string `json:"error,omitempty"` -} - -func RespondError(conn net.Conn, id int, errMsg string) { - log.Errorf("DMS API Error: id=%d error=%s", id, errMsg) - resp := Response[any]{ID: id, Error: errMsg} - json.NewEncoder(conn).Encode(resp) -} - -func Respond[T any](conn net.Conn, id int, result T) { - resp := Response[T]{ID: id, Result: &result} - json.NewEncoder(conn).Encode(resp) -} diff --git a/nix/inputs/dms-cli/internal/server/network/API.md b/nix/inputs/dms-cli/internal/server/network/API.md deleted file mode 100644 index 0163389..0000000 --- a/nix/inputs/dms-cli/internal/server/network/API.md +++ /dev/null @@ -1,552 +0,0 @@ -# NetworkManager API Documentation - -## Overview - -The network manager API provides methods for managing WiFi connections, monitoring network state, and handling credential prompts through NetworkManager. Communication occurs over a message-based protocol (websocket, IPC, etc.) with event subscriptions for state updates. - -## API Methods - -### network.wifi.connect - -Initiate a WiFi connection. - -**Request:** -```json -{ - "method": "network.wifi.connect", - "params": { - "ssid": "NetworkName", - "password": "optional-password", - "interactive": true - } -} -``` - -**Parameters:** -- `ssid` (string, required): Network SSID -- `password` (string, optional): Pre-shared key for WPA/WPA2/WPA3 networks -- `interactive` (boolean, optional): Enable credential prompting if authentication fails or password is missing. Automatically set to `true` when connecting to secured networks without providing a password. - -**Response:** -```json -{ - "success": true, - "message": "connecting" -} -``` - -**Behavior:** -- Returns immediately; connection happens asynchronously -- State updates delivered via `network` service subscription -- Credential prompts delivered via `network.credentials` service subscription - -### network.credentials.submit - -Submit credentials in response to a prompt. - -**Request:** -```json -{ - "method": "network.credentials.submit", - "params": { - "token": "correlation-token", - "secrets": { - "psk": "password" - }, - "save": true - } -} -``` - -**Parameters:** -- `token` (string, required): Token from credential prompt -- `secrets` (object, required): Key-value map of credential fields -- `save` (boolean, optional): Whether to persist credentials (default: false) - -**Common secret fields:** -- `psk`: Pre-shared key for WPA2/WPA3 personal networks -- `identity`: Username for 802.1X enterprise networks -- `password`: Password for 802.1X enterprise networks - -### network.credentials.cancel - -Cancel a credential prompt. - -**Request:** -```json -{ - "method": "network.credentials.cancel", - "params": { - "token": "correlation-token" - } -} -``` - -## Event Subscriptions - -### Subscribing to Events - -Subscribe to receive network state updates and credential prompts: - -```json -{ - "method": "subscribe", - "params": { - "services": ["network", "network.credentials"] - } -} -``` - -Both services are required for full connection handling. Missing `network.credentials` means credential prompts won't be received. - -### network Service Events - -State updates are sent whenever network configuration changes: - -```json -{ - "service": "network", - "data": { - "networkStatus": "wifi", - "isConnecting": false, - "connectingSSID": "", - "wifiConnected": true, - "wifiSSID": "MyNetwork", - "wifiIP": "192.168.1.100", - "lastError": "" - } -} -``` - -**State fields:** -- `networkStatus`: Current connection type (`wifi`, `ethernet`, `disconnected`) -- `isConnecting`: Whether a connection attempt is in progress -- `connectingSSID`: SSID being connected to (empty when idle) -- `wifiConnected`: Whether associated with an access point -- `wifiSSID`: Currently connected network name -- `wifiIP`: Assigned IP address (empty until DHCP completes) -- `lastError`: Error message from last failed connection attempt - -### network.credentials Service Events - -Credential prompts are sent when authentication is required: - -```json -{ - "service": "network.credentials", - "data": { - "token": "unique-prompt-id", - "ssid": "NetworkName", - "setting": "802-11-wireless-security", - "fields": ["psk"], - "hints": ["wpa3", "sae"], - "reason": "Credentials required" - } -} -``` - -**Prompt fields:** -- `token`: Unique identifier for this prompt (use in submit/cancel) -- `ssid`: Network requesting credentials -- `setting`: Authentication type (`802-11-wireless-security` for personal WiFi, `802-1x` for enterprise) -- `fields`: Array of required credential field names -- `hints`: Additional context about the network type -- `reason`: Human-readable explanation (e.g., "Previous password was incorrect") - -## Connection Flow - -### Typical Timeline - -``` -T+0ms Call network.wifi.connect -T+10ms Receive {"success": true, "message": "connecting"} -T+100ms State update: isConnecting=true, connectingSSID="Network" -T+500ms Credential prompt (if needed) -T+1000ms Submit credentials -T+3000ms State update: wifiConnected=true, wifiIP="192.168.x.x" -``` - -### State Machine - -``` -IDLE - | - | network.wifi.connect - v -CONNECTING (isConnecting=true, connectingSSID set) - | - +-- Needs credentials - | | - | v - | PROMPTING (credential prompt event) - | | - | | network.credentials.submit - | v - | back to CONNECTING - | - +-- Success - | | - | v - | CONNECTED (wifiConnected=true, wifiIP set, isConnecting=false) - | - +-- Failure - | - v - ERROR (isConnecting=false, !wifiConnected, lastError set) -``` - -## Connection Success Detection - -A connection is successful when all of the following are true: - -1. `wifiConnected` is `true` -2. `wifiIP` is set and non-empty -3. `wifiSSID` matches the target network -4. `isConnecting` is `false` - -Do not rely on `wifiConnected` alone - the device may be associated with an access point but not have an IP address yet. - -**Example:** -```javascript -function isConnectionComplete(state, targetSSID) { - return state.wifiConnected && - state.wifiIP && - state.wifiIP !== "" && - state.wifiSSID === targetSSID && - !state.isConnecting; -} -``` - -## Error Handling - -### Error Detection - -Errors occur when a connection attempt stops without success: - -```javascript -function checkForFailure(state, wasConnecting, targetSSID) { - // Was connecting, now idle, but not connected - if (wasConnecting && - !state.isConnecting && - state.connectingSSID === "" && - !state.wifiConnected) { - return state.lastError || "Connection failed"; - } - return null; -} -``` - -### Common Error Scenarios - -#### Wrong Password - -**Detection methods:** - -1. Quick failure (< 3 seconds from start) -2. `lastError` contains "password", "auth", or "secrets" -3. Second credential prompt with `reason: "Previous password was incorrect"` - -**Handling:** -```javascript -if (prompt.reason === "Previous password was incorrect") { - // Show error, clear password field, re-focus input -} -``` - -#### Network Out of Range - -**Detection:** -- `lastError` contains "not-found" or "connection-attempt-failed" - -#### Connection Timeout - -**Detection:** -- `isConnecting` remains true for > 30 seconds - -**Implementation:** -```javascript -let timeout = setTimeout(() => { - if (currentState.isConnecting) { - handleTimeout(); - } -}, 30000); -``` - -#### DHCP Failure - -**Detection:** -- `wifiConnected` is true -- `wifiIP` is empty after 15+ seconds - -### Error Message Translation - -Map technical errors to user-friendly messages: - -| lastError value | Meaning | User message | -|----------------|---------|--------------| -| `secrets-required` | Password needed | "Please enter password" | -| `authentication-failed` | Wrong password | "Incorrect password" | -| `connection-removed` | Profile deleted | "Network configuration removed" | -| `connection-attempt-failed` | Generic failure | "Failed to connect" | -| `network-not-found` | Out of range | "Network not found" | -| `(timeout)` | Timeout | "Connection timed out" | - -## Credential Handling - -### Secret Agent Architecture - -The credential system uses a broker pattern: - -``` -NetworkManager -> SecretAgent -> PromptBroker -> UI -> User - ^ - | - User Response - | -NetworkManager <- SecretAgent <- PromptBroker <- UI -``` - -### Implementing a Broker - -```go -type CustomBroker struct { - ui UIInterface - pending map[string]chan network.PromptReply -} - -func (b *CustomBroker) Ask(ctx context.Context, req network.PromptRequest) (string, error) { - token := generateToken() - b.pending[token] = make(chan network.PromptReply, 1) - - // Send to UI - b.ui.ShowCredentialPrompt(token, req) - - return token, nil -} - -func (b *CustomBroker) Wait(ctx context.Context, token string) (network.PromptReply, error) { - select { - case <-ctx.Done(): - return network.PromptReply{}, errors.New("timeout") - case reply := <-b.pending[token]: - return reply, nil - } -} - -func (b *CustomBroker) Resolve(token string, reply network.PromptReply) error { - if ch, ok := b.pending[token]; ok { - ch <- reply - close(ch) - delete(b.pending, token) - } - return nil -} -``` - -### Credential Field Types - -**Personal WiFi (802-11-wireless-security):** -- Fields: `["psk"]` -- UI: Single password input - -**Enterprise WiFi (802-1x):** -- Fields: `["identity", "password"]` -- UI: Username and password inputs - -### Building Secrets Object - -```javascript -function buildSecrets(setting, fields, formData) { - let secrets = {}; - - if (setting === "802-11-wireless-security") { - secrets.psk = formData.password; - } else if (setting === "802-1x") { - secrets.identity = formData.username; - secrets.password = formData.password; - } - - return secrets; -} -``` - -## Best Practices - -### Track Target Network - -Always store which network you're connecting to: - -```javascript -let targetSSID = null; - -function connect(ssid) { - targetSSID = ssid; - // send request -} - -function onStateUpdate(state) { - if (!targetSSID) return; - - if (state.wifiSSID === targetSSID && state.wifiConnected && state.wifiIP) { - // Success for the network we care about - targetSSID = null; - } -} -``` - -### Implement Timeouts - -Never wait indefinitely for a connection: - -```javascript -const CONNECTION_TIMEOUT = 30000; // 30 seconds -const DHCP_TIMEOUT = 15000; // 15 seconds - -let timer = setTimeout(() => { - if (stillConnecting) { - handleTimeout(); - } -}, CONNECTION_TIMEOUT); -``` - -### Handle Credential Re-prompts - -Wrong passwords trigger a second prompt: - -```javascript -function onCredentialPrompt(prompt) { - if (prompt.reason.includes("incorrect")) { - // Show error, but keep dialog open - showError("Wrong password"); - clearPasswordField(); - } else { - // First time prompt - showDialog(prompt); - } -} -``` - -### Clean Up State - -Reset tracking variables on success, failure, or cancellation: - -```javascript -function cleanup() { - clearTimeout(timer); - targetSSID = null; - closeDialogs(); -} -``` - -### Subscribe to Both Services - -Missing `network.credentials` means prompts won't arrive: - -```javascript -// Correct -services: ["network", "network.credentials"] - -// Wrong - will miss credential prompts -services: ["network"] -``` - -## Testing - -### Connection Test Checklist - -- [ ] Connect to open network -- [ ] Connect to WPA2 network with password provided -- [ ] Connect to WPA2 network without password (triggers prompt) -- [ ] Enter wrong password (verify error and re-prompt) -- [ ] Cancel credential prompt -- [ ] Connection timeout after 30 seconds -- [ ] DHCP timeout detection -- [ ] Network out of range -- [ ] Reconnect to already-configured network - -### Verifying Secret Agent Setup - -Check connection profile flags: -```bash -nmcli connection show "NetworkName" | grep flags -# Should show: 802-11-wireless-security.psk-flags: 1 (agent-owned) -``` - -Check agent registration in logs: -``` -INFO: Registered with NetworkManager as secret agent -``` - -## Security - -- Never log credential values (passwords, PSKs) -- Clear password fields when dialogs close -- Implement prompt timeouts (default: 2 minutes) -- Validate user input before submission -- Use secure channels for credential transmission - -## Troubleshooting - -### Credential prompt doesn't appear - -**Check:** -- Subscribed to both `network` and `network.credentials` -- Connection has `interactive: true` -- Secret flags set to AGENT_OWNED (value: 1) -- Broker registered successfully - -### Connection succeeds without prompting - -**Cause:** NetworkManager found saved credentials - -**Solution:** Delete existing connection first, or use different credentials - -### State updates seem delayed - -**Expected behavior:** State changes occur in rapid succession during connection - -**Solution:** Debounce UI updates; only act on final state - -### Multiple rapid credential prompts - -**Cause:** Connection profile has incorrect flags or conflicting agents - -**Solution:** -- Check only one agent is running -- Verify psk-flags value -- Check NetworkManager logs for agent conflicts - -## Data Structures Reference - -### PromptRequest -```go -type PromptRequest struct { - SSID string `json:"ssid"` - SettingName string `json:"setting"` - Fields []string `json:"fields"` - Hints []string `json:"hints"` - Reason string `json:"reason"` -} -``` - -### PromptReply -```go -type PromptReply struct { - Secrets map[string]string `json:"secrets"` - Save bool `json:"save"` - Cancel bool `json:"cancel"` -} -``` - -### NetworkState -```go -type NetworkState struct { - NetworkStatus string `json:"networkStatus"` - IsConnecting bool `json:"isConnecting"` - ConnectingSSID string `json:"connectingSSID"` - WifiConnected bool `json:"wifiConnected"` - WifiSSID string `json:"wifiSSID"` - WifiIP string `json:"wifiIP"` - LastError string `json:"lastError"` -} -``` diff --git a/nix/inputs/dms-cli/internal/server/network/agent_iwd.go b/nix/inputs/dms-cli/internal/server/network/agent_iwd.go deleted file mode 100644 index 99be64b..0000000 --- a/nix/inputs/dms-cli/internal/server/network/agent_iwd.go +++ /dev/null @@ -1,311 +0,0 @@ -package network - -import ( - "context" - "errors" - "fmt" - "time" - - "github.com/AvengeMedia/danklinux/internal/errdefs" - "github.com/godbus/dbus/v5" -) - -const ( - iwdAgentManagerPath = "/net/connman/iwd" - iwdAgentManagerIface = "net.connman.iwd.AgentManager" - iwdAgentInterface = "net.connman.iwd.Agent" - iwdAgentObjectPath = "/com/danklinux/iwdagent" -) - -type ConnectionStateChecker interface { - IsConnectingTo(ssid string) bool -} - -type IWDAgent struct { - conn *dbus.Conn - objPath dbus.ObjectPath - prompts PromptBroker - onUserCanceled func() - onPromptRetry func(ssid string) - lastRequestSSID string - stateChecker ConnectionStateChecker -} - -const iwdAgentIntrospectXML = ` - - - - - - - - - - - - - - - - - - - - - - - - - - - - -` - -func NewIWDAgent(prompts PromptBroker) (*IWDAgent, error) { - c, err := dbus.ConnectSystemBus() - if err != nil { - return nil, fmt.Errorf("failed to connect to system bus: %w", err) - } - - agent := &IWDAgent{ - conn: c, - objPath: dbus.ObjectPath(iwdAgentObjectPath), - prompts: prompts, - } - - if err := c.Export(agent, agent.objPath, iwdAgentInterface); err != nil { - c.Close() - return nil, fmt.Errorf("failed to export IWD agent: %w", err) - } - - if err := c.Export(agent, agent.objPath, "org.freedesktop.DBus.Introspectable"); err != nil { - c.Close() - return nil, fmt.Errorf("failed to export introspection: %w", err) - } - - mgr := c.Object("net.connman.iwd", dbus.ObjectPath(iwdAgentManagerPath)) - call := mgr.Call(iwdAgentManagerIface+".RegisterAgent", 0, agent.objPath) - if call.Err != nil { - c.Close() - return nil, fmt.Errorf("failed to register agent with iwd: %w", call.Err) - } - - return agent, nil -} - -func (a *IWDAgent) Close() { - if a.conn != nil { - mgr := a.conn.Object("net.connman.iwd", dbus.ObjectPath(iwdAgentManagerPath)) - mgr.Call(iwdAgentManagerIface+".UnregisterAgent", 0, a.objPath) - a.conn.Close() - } -} - -func (a *IWDAgent) SetStateChecker(checker ConnectionStateChecker) { - a.stateChecker = checker -} - -func (a *IWDAgent) getNetworkName(networkPath dbus.ObjectPath) string { - netObj := a.conn.Object("net.connman.iwd", networkPath) - nameVar, err := netObj.GetProperty("net.connman.iwd.Network.Name") - if err == nil { - if name, ok := nameVar.Value().(string); ok { - return name - } - } - return string(networkPath) -} - -func (a *IWDAgent) RequestPassphrase(network dbus.ObjectPath) (string, *dbus.Error) { - ssid := a.getNetworkName(network) - - if a.stateChecker != nil && !a.stateChecker.IsConnectingTo(ssid) { - return "", dbus.NewError("net.connman.iwd.Agent.Error.Canceled", nil) - } - - if a.prompts == nil { - if a.onUserCanceled != nil { - a.onUserCanceled() - } - return "", dbus.NewError("net.connman.iwd.Agent.Error.Canceled", nil) - } - - if a.lastRequestSSID == ssid { - if a.onPromptRetry != nil { - a.onPromptRetry(ssid) - } - } - a.lastRequestSSID = ssid - - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) - defer cancel() - - token, err := a.prompts.Ask(ctx, PromptRequest{ - SSID: ssid, - Fields: []string{"psk"}, - }) - if err != nil { - if a.onUserCanceled != nil { - a.onUserCanceled() - } - return "", dbus.NewError("net.connman.iwd.Agent.Error.Canceled", nil) - } - - reply, err := a.prompts.Wait(ctx, token) - if err != nil { - if reply.Cancel || errors.Is(err, errdefs.ErrSecretPromptCancelled) { - if a.onUserCanceled != nil { - a.onUserCanceled() - } - } - return "", dbus.NewError("net.connman.iwd.Agent.Error.Canceled", nil) - } - - if passphrase, ok := reply.Secrets["psk"]; ok { - return passphrase, nil - } - - if a.onUserCanceled != nil { - a.onUserCanceled() - } - return "", dbus.NewError("net.connman.iwd.Agent.Error.Canceled", nil) -} - -func (a *IWDAgent) RequestPrivateKeyPassphrase(network dbus.ObjectPath) (string, *dbus.Error) { - ssid := a.getNetworkName(network) - - if a.stateChecker != nil && !a.stateChecker.IsConnectingTo(ssid) { - return "", dbus.NewError("net.connman.iwd.Agent.Error.Canceled", nil) - } - - if a.prompts == nil { - return "", dbus.NewError("net.connman.iwd.Agent.Error.Canceled", nil) - } - - if a.lastRequestSSID == ssid { - if a.onPromptRetry != nil { - a.onPromptRetry(ssid) - } - } - a.lastRequestSSID = ssid - - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) - defer cancel() - - token, err := a.prompts.Ask(ctx, PromptRequest{ - SSID: ssid, - Fields: []string{"private-key-password"}, - }) - if err != nil { - return "", dbus.NewError("net.connman.iwd.Agent.Error.Canceled", nil) - } - - reply, err := a.prompts.Wait(ctx, token) - if err != nil || reply.Cancel { - return "", dbus.NewError("net.connman.iwd.Agent.Error.Canceled", nil) - } - - if passphrase, ok := reply.Secrets["private-key-password"]; ok { - return passphrase, nil - } - - return "", dbus.NewError("net.connman.iwd.Agent.Error.Canceled", nil) -} - -func (a *IWDAgent) RequestUserNameAndPassword(network dbus.ObjectPath) (string, string, *dbus.Error) { - ssid := a.getNetworkName(network) - - if a.stateChecker != nil && !a.stateChecker.IsConnectingTo(ssid) { - return "", "", dbus.NewError("net.connman.iwd.Agent.Error.Canceled", nil) - } - - if a.prompts == nil { - return "", "", dbus.NewError("net.connman.iwd.Agent.Error.Canceled", nil) - } - - if a.lastRequestSSID == ssid { - if a.onPromptRetry != nil { - a.onPromptRetry(ssid) - } - } - a.lastRequestSSID = ssid - - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) - defer cancel() - - token, err := a.prompts.Ask(ctx, PromptRequest{ - SSID: ssid, - Fields: []string{"identity", "password"}, - }) - if err != nil { - return "", "", dbus.NewError("net.connman.iwd.Agent.Error.Canceled", nil) - } - - reply, err := a.prompts.Wait(ctx, token) - if err != nil || reply.Cancel { - return "", "", dbus.NewError("net.connman.iwd.Agent.Error.Canceled", nil) - } - - username, hasUser := reply.Secrets["identity"] - password, hasPass := reply.Secrets["password"] - - if hasUser && hasPass { - return username, password, nil - } - - return "", "", dbus.NewError("net.connman.iwd.Agent.Error.Canceled", nil) -} - -func (a *IWDAgent) RequestUserPassword(network dbus.ObjectPath, user string) (string, *dbus.Error) { - ssid := a.getNetworkName(network) - - if a.stateChecker != nil && !a.stateChecker.IsConnectingTo(ssid) { - return "", dbus.NewError("net.connman.iwd.Agent.Error.Canceled", nil) - } - - if a.prompts == nil { - return "", dbus.NewError("net.connman.iwd.Agent.Error.Canceled", nil) - } - - if a.lastRequestSSID == ssid { - if a.onPromptRetry != nil { - a.onPromptRetry(ssid) - } - } - a.lastRequestSSID = ssid - - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) - defer cancel() - - token, err := a.prompts.Ask(ctx, PromptRequest{ - SSID: ssid, - Fields: []string{"password"}, - }) - if err != nil { - return "", dbus.NewError("net.connman.iwd.Agent.Error.Canceled", nil) - } - - reply, err := a.prompts.Wait(ctx, token) - if err != nil || reply.Cancel { - return "", dbus.NewError("net.connman.iwd.Agent.Error.Canceled", nil) - } - - if password, ok := reply.Secrets["password"]; ok { - return password, nil - } - - return "", dbus.NewError("net.connman.iwd.Agent.Error.Canceled", nil) -} - -func (a *IWDAgent) Cancel(reason string) *dbus.Error { - return nil -} - -func (a *IWDAgent) Release() *dbus.Error { - return nil -} - -func (a *IWDAgent) Introspect() (string, *dbus.Error) { - return iwdAgentIntrospectXML, nil -} diff --git a/nix/inputs/dms-cli/internal/server/network/agent_networkmanager.go b/nix/inputs/dms-cli/internal/server/network/agent_networkmanager.go deleted file mode 100644 index 3b8fae5..0000000 --- a/nix/inputs/dms-cli/internal/server/network/agent_networkmanager.go +++ /dev/null @@ -1,499 +0,0 @@ -package network - -import ( - "context" - "errors" - "fmt" - "time" - - "github.com/AvengeMedia/danklinux/internal/errdefs" - "github.com/AvengeMedia/danklinux/internal/log" - "github.com/godbus/dbus/v5" -) - -const ( - nmAgentManagerPath = "/org/freedesktop/NetworkManager/AgentManager" - nmAgentManagerIface = "org.freedesktop.NetworkManager.AgentManager" - nmSecretAgentIface = "org.freedesktop.NetworkManager.SecretAgent" - agentObjectPath = "/org/freedesktop/NetworkManager/SecretAgent" - agentIdentifier = "com.danklinux.NMAgent" -) - -type SecretAgent struct { - conn *dbus.Conn - objPath dbus.ObjectPath - id string - prompts PromptBroker - manager *Manager - backend *NetworkManagerBackend -} - -type nmVariantMap map[string]dbus.Variant -type nmSettingMap map[string]nmVariantMap - -const introspectXML = ` - - - - - - - - - - - - - - - - - - - - - - - - - - - - -` - -func NewSecretAgent(prompts PromptBroker, manager *Manager, backend *NetworkManagerBackend) (*SecretAgent, error) { - c, err := dbus.ConnectSystemBus() - if err != nil { - return nil, fmt.Errorf("failed to connect to system bus: %w", err) - } - - sa := &SecretAgent{ - conn: c, - objPath: dbus.ObjectPath(agentObjectPath), - id: agentIdentifier, - prompts: prompts, - manager: manager, - backend: backend, - } - - if err := c.Export(sa, sa.objPath, nmSecretAgentIface); err != nil { - c.Close() - return nil, fmt.Errorf("failed to export secret agent: %w", err) - } - - if err := c.Export(sa, sa.objPath, "org.freedesktop.DBus.Introspectable"); err != nil { - c.Close() - return nil, fmt.Errorf("failed to export introspection: %w", err) - } - - mgr := c.Object("org.freedesktop.NetworkManager", dbus.ObjectPath(nmAgentManagerPath)) - call := mgr.Call(nmAgentManagerIface+".Register", 0, sa.id) - if call.Err != nil { - c.Close() - return nil, fmt.Errorf("failed to register agent with NetworkManager: %w", call.Err) - } - - log.Infof("[SecretAgent] Registered with NetworkManager (id=%s, unique name=%s, fixed path=%s)", sa.id, c.Names()[0], sa.objPath) - return sa, nil -} - -func (a *SecretAgent) Close() { - if a.conn != nil { - mgr := a.conn.Object("org.freedesktop.NetworkManager", dbus.ObjectPath(nmAgentManagerPath)) - mgr.Call(nmAgentManagerIface+".Unregister", 0, a.id) - a.conn.Close() - } -} - -func (a *SecretAgent) GetSecrets( - conn map[string]nmVariantMap, - path dbus.ObjectPath, - settingName string, - hints []string, - flags uint32, -) (nmSettingMap, *dbus.Error) { - log.Infof("[SecretAgent] GetSecrets called: path=%s, setting=%s, hints=%v, flags=%d", - path, settingName, hints, flags) - - const ( - NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION = 0x1 - NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW = 0x2 - NM_SECRET_AGENT_GET_SECRETS_FLAG_USER_REQUESTED = 0x4 - ) - - connType, displayName, vpnSvc := readConnTypeAndName(conn) - ssid := readSSID(conn) - fields := fieldsNeeded(settingName, hints) - - log.Infof("[SecretAgent] connType=%s, name=%s, vpnSvc=%s, fields=%v, flags=%d", connType, displayName, vpnSvc, fields, flags) - - if a.backend != nil { - a.backend.stateMutex.RLock() - isConnecting := a.backend.state.IsConnecting - connectingSSID := a.backend.state.ConnectingSSID - isConnectingVPN := a.backend.state.IsConnectingVPN - connectingVPNUUID := a.backend.state.ConnectingVPNUUID - a.backend.stateMutex.RUnlock() - - switch connType { - case "802-11-wireless": - // If we're connecting to a WiFi network, only respond if it's the one we're connecting to - if isConnecting && connectingSSID != ssid { - log.Infof("[SecretAgent] Ignoring WiFi request for SSID '%s' - we're connecting to '%s'", ssid, connectingSSID) - return nil, dbus.NewError("org.freedesktop.NetworkManager.SecretAgent.Error.NoSecrets", nil) - } - case "vpn", "wireguard": - var connUuid string - if c, ok := conn["connection"]; ok { - if v, ok := c["uuid"]; ok { - if s, ok2 := v.Value().(string); ok2 { - connUuid = s - } - } - } - - // If we're connecting to a VPN, only respond if it's the one we're connecting to - // This prevents interfering with nmcli/other tools when our app isn't connecting - if isConnectingVPN && connUuid != connectingVPNUUID { - log.Infof("[SecretAgent] Ignoring VPN request for UUID '%s' - we're connecting to '%s'", connUuid, connectingVPNUUID) - return nil, dbus.NewError("org.freedesktop.NetworkManager.SecretAgent.Error.NoSecrets", nil) - } - } - } - - if len(fields) == 0 { - // For VPN connections with no hints, we can't provide a proper UI. - // Defer to other agents (like nm-applet or VPN-specific auth dialogs) - // that can handle the VPN type properly (e.g., OpenConnect with SAML, etc.) - if settingName == "vpn" { - log.Infof("[SecretAgent] VPN with empty hints - deferring to other agents for %s", vpnSvc) - return nil, dbus.NewError("org.freedesktop.NetworkManager.SecretAgent.Error.NoSecrets", nil) - } - - const ( - NM_SETTING_SECRET_FLAG_NONE = 0 - NM_SETTING_SECRET_FLAG_AGENT_OWNED = 1 - NM_SETTING_SECRET_FLAG_NOT_SAVED = 2 - NM_SETTING_SECRET_FLAG_NOT_REQUIRED = 4 - ) - - var passwordFlags uint32 = 0xFFFF - switch settingName { - case "802-11-wireless-security": - if wifiSecSettings, ok := conn["802-11-wireless-security"]; ok { - if flagsVariant, ok := wifiSecSettings["psk-flags"]; ok { - if pwdFlags, ok := flagsVariant.Value().(uint32); ok { - passwordFlags = pwdFlags - } - } - } - case "802-1x": - if dot1xSettings, ok := conn["802-1x"]; ok { - if flagsVariant, ok := dot1xSettings["password-flags"]; ok { - if pwdFlags, ok := flagsVariant.Value().(uint32); ok { - passwordFlags = pwdFlags - } - } - } - } - - if passwordFlags == 0xFFFF { - log.Warnf("[SecretAgent] Could not determine password-flags for empty hints - returning NoSecrets error") - return nil, dbus.NewError("org.freedesktop.NetworkManager.SecretAgent.Error.NoSecrets", nil) - } else if passwordFlags&NM_SETTING_SECRET_FLAG_NOT_REQUIRED != 0 { - log.Infof("[SecretAgent] Secrets not required (flags=%d)", passwordFlags) - out := nmSettingMap{} - out[settingName] = nmVariantMap{} - return out, nil - } else if passwordFlags&NM_SETTING_SECRET_FLAG_AGENT_OWNED != 0 { - log.Warnf("[SecretAgent] Secrets are agent-owned but we don't store secrets (flags=%d) - returning NoSecrets error", passwordFlags) - return nil, dbus.NewError("org.freedesktop.NetworkManager.SecretAgent.Error.NoSecrets", nil) - } else { - log.Infof("[SecretAgent] No secrets needed, using system stored secrets (flags=%d)", passwordFlags) - out := nmSettingMap{} - out[settingName] = nmVariantMap{} - return out, nil - } - } - - reason := reasonFromFlags(flags) - if a.manager != nil && connType == "802-11-wireless" && a.manager.WasRecentlyFailed(ssid) { - reason = "wrong-password" - } - - var connId, connUuid string - if c, ok := conn["connection"]; ok { - if v, ok := c["id"]; ok { - if s, ok2 := v.Value().(string); ok2 { - connId = s - } - } - if v, ok := c["uuid"]; ok { - if s, ok2 := v.Value().(string); ok2 { - connUuid = s - } - } - } - - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) - defer cancel() - - token, err := a.prompts.Ask(ctx, PromptRequest{ - Name: displayName, - SSID: ssid, - ConnType: connType, - VpnService: vpnSvc, - SettingName: settingName, - Fields: fields, - Hints: hints, - Reason: reason, - ConnectionId: connId, - ConnectionUuid: connUuid, - ConnectionPath: string(path), - }) - if err != nil { - log.Warnf("[SecretAgent] Failed to create prompt: %v", err) - return nil, dbus.MakeFailedError(err) - } - - log.Infof("[SecretAgent] Waiting for user input (token=%s)", token) - reply, err := a.prompts.Wait(ctx, token) - if err != nil { - log.Warnf("[SecretAgent] Prompt failed or cancelled: %v", err) - - if reply.Cancel || errors.Is(err, errdefs.ErrSecretPromptCancelled) { - return nil, dbus.NewError("org.freedesktop.NetworkManager.SecretAgent.Error.UserCanceled", nil) - } - - if errors.Is(err, errdefs.ErrSecretPromptTimeout) { - return nil, dbus.NewError("org.freedesktop.NetworkManager.SecretAgent.Error.Failed", nil) - } - return nil, dbus.NewError("org.freedesktop.NetworkManager.SecretAgent.Error.Failed", nil) - } - - log.Infof("[SecretAgent] User provided secrets, save=%v", reply.Save) - - out := nmSettingMap{} - sec := nmVariantMap{} - for k, v := range reply.Secrets { - sec[k] = dbus.MakeVariant(v) - } - out[settingName] = sec - - switch settingName { - case "802-1x": - log.Infof("[SecretAgent] Returning 802-1x enterprise secrets with %d fields", len(sec)) - case "vpn": - log.Infof("[SecretAgent] Returning VPN secrets with %d fields for %s", len(sec), vpnSvc) - } - - // If save=true, persist secrets in background after returning to NetworkManager - // This MUST happen after we return secrets, in a goroutine - if reply.Save { - go func() { - log.Infof("[SecretAgent] Persisting secrets with Update2: path=%s, setting=%s", path, settingName) - - // Get existing connection settings - connObj := a.conn.Object("org.freedesktop.NetworkManager", path) - var existingSettings map[string]map[string]dbus.Variant - if err := connObj.Call("org.freedesktop.NetworkManager.Settings.Connection.GetSettings", 0).Store(&existingSettings); err != nil { - log.Warnf("[SecretAgent] GetSettings failed: %v", err) - return - } - - // Build minimal settings with ONLY the section we're updating - // This avoids D-Bus type serialization issues with complex types like IPv6 addresses - settings := make(map[string]map[string]dbus.Variant) - - // Copy connection section (required for Update2) - if connSection, ok := existingSettings["connection"]; ok { - settings["connection"] = connSection - } - - // Update settings based on type - switch settingName { - case "vpn": - // Set password-flags=0 and add secrets to vpn section - vpn, ok := existingSettings["vpn"] - if !ok { - vpn = make(map[string]dbus.Variant) - } - - // Get existing data map (vpn.data is string->string) - var data map[string]string - if dataVariant, ok := vpn["data"]; ok { - if dm, ok := dataVariant.Value().(map[string]string); ok { - data = make(map[string]string) - for k, v := range dm { - data[k] = v - } - } else { - data = make(map[string]string) - } - } else { - data = make(map[string]string) - } - - // Update password-flags to 0 (system-stored) - data["password-flags"] = "0" - vpn["data"] = dbus.MakeVariant(data) - - // Add secrets (vpn.secrets is string->string) - secs := make(map[string]string) - for k, v := range reply.Secrets { - secs[k] = v - } - vpn["secrets"] = dbus.MakeVariant(secs) - settings["vpn"] = vpn - - log.Infof("[SecretAgent] Updated VPN settings: password-flags=0, secrets with %d fields", len(secs)) - - case "802-11-wireless-security": - // Set psk-flags=0 for WiFi - wifiSec, ok := existingSettings["802-11-wireless-security"] - if !ok { - wifiSec = make(map[string]dbus.Variant) - } - wifiSec["psk-flags"] = dbus.MakeVariant(uint32(0)) - - // Add PSK secret - if psk, ok := reply.Secrets["psk"]; ok { - wifiSec["psk"] = dbus.MakeVariant(psk) - log.Infof("[SecretAgent] Updated WiFi settings: psk-flags=0") - } - settings["802-11-wireless-security"] = wifiSec - - case "802-1x": - // Set password-flags=0 for 802.1x - dot1x, ok := existingSettings["802-1x"] - if !ok { - dot1x = make(map[string]dbus.Variant) - } - dot1x["password-flags"] = dbus.MakeVariant(uint32(0)) - - // Add password secret - if password, ok := reply.Secrets["password"]; ok { - dot1x["password"] = dbus.MakeVariant(password) - log.Infof("[SecretAgent] Updated 802.1x settings: password-flags=0") - } - settings["802-1x"] = dot1x - } - - // Call Update2 with correct signature: - // Update2(IN settings, IN flags, IN args) -> OUT result - // flags: 0x1 = to-disk - var result map[string]dbus.Variant - err := connObj.Call("org.freedesktop.NetworkManager.Settings.Connection.Update2", 0, - settings, uint32(0x1), map[string]dbus.Variant{}).Store(&result) - if err != nil { - log.Warnf("[SecretAgent] Update2(to-disk) failed: %v", err) - } else { - log.Infof("[SecretAgent] Successfully persisted secrets to disk for %s", settingName) - } - }() - } - - return out, nil -} - -func (a *SecretAgent) DeleteSecrets(conn map[string]nmVariantMap, path dbus.ObjectPath) *dbus.Error { - ssid := readSSID(conn) - log.Infof("[SecretAgent] DeleteSecrets called: path=%s, SSID=%s", path, ssid) - return nil -} - -func (a *SecretAgent) DeleteSecrets2(path dbus.ObjectPath, setting string) *dbus.Error { - log.Infof("[SecretAgent] DeleteSecrets2 (alternate) called: path=%s, setting=%s", path, setting) - return nil -} - -func (a *SecretAgent) CancelGetSecrets(path dbus.ObjectPath, settingName string) *dbus.Error { - log.Infof("[SecretAgent] CancelGetSecrets called: path=%s, setting=%s", path, settingName) - - if a.prompts != nil { - if err := a.prompts.Cancel(string(path), settingName); err != nil { - log.Warnf("[SecretAgent] Failed to cancel prompt: %v", err) - } - } - - return nil -} - -func (a *SecretAgent) Introspect() (string, *dbus.Error) { - return introspectXML, nil -} - -func readSSID(conn map[string]nmVariantMap) string { - if w, ok := conn["802-11-wireless"]; ok { - if v, ok := w["ssid"]; ok { - if b, ok := v.Value().([]byte); ok { - return string(b) - } - if s, ok := v.Value().(string); ok { - return s - } - } - } - return "" -} - -func readConnTypeAndName(conn map[string]nmVariantMap) (string, string, string) { - var connType, name, svc string - if c, ok := conn["connection"]; ok { - if v, ok := c["type"]; ok { - if s, ok2 := v.Value().(string); ok2 { - connType = s - } - } - if v, ok := c["id"]; ok { - if s, ok2 := v.Value().(string); ok2 { - name = s - } - } - } - if vpn, ok := conn["vpn"]; ok { - if v, ok := vpn["service-type"]; ok { - if s, ok2 := v.Value().(string); ok2 { - svc = s - } - } - } - if name == "" && connType == "802-11-wireless" { - name = readSSID(conn) - } - return connType, name, svc -} - -func fieldsNeeded(setting string, hints []string) []string { - switch setting { - case "802-11-wireless-security": - return []string{"psk"} - case "802-1x": - return []string{"identity", "password"} - case "vpn": - return hints - default: - return []string{} - } -} - -func reasonFromFlags(flags uint32) string { - const ( - NM_SECRET_AGENT_GET_SECRETS_FLAG_NONE = 0x0 - NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION = 0x1 - NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW = 0x2 - NM_SECRET_AGENT_GET_SECRETS_FLAG_USER_REQUESTED = 0x4 - NM_SECRET_AGENT_GET_SECRETS_FLAG_WPS_PBC_ACTIVE = 0x8 - NM_SECRET_AGENT_GET_SECRETS_FLAG_ONLY_SYSTEM = 0x80000000 - NM_SECRET_AGENT_GET_SECRETS_FLAG_NO_ERRORS = 0x40000000 - ) - - if flags&NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW != 0 { - return "wrong-password" - } - if flags&NM_SECRET_AGENT_GET_SECRETS_FLAG_USER_REQUESTED != 0 { - return "user-requested" - } - return "required" -} diff --git a/nix/inputs/dms-cli/internal/server/network/backend.go b/nix/inputs/dms-cli/internal/server/network/backend.go deleted file mode 100644 index 3925e2a..0000000 --- a/nix/inputs/dms-cli/internal/server/network/backend.go +++ /dev/null @@ -1,65 +0,0 @@ -package network - -type Backend interface { - Initialize() error - Close() - - GetWiFiEnabled() (bool, error) - SetWiFiEnabled(enabled bool) error - - ScanWiFi() error - GetWiFiNetworkDetails(ssid string) (*NetworkInfoResponse, error) - - ConnectWiFi(req ConnectionRequest) error - DisconnectWiFi() error - ForgetWiFiNetwork(ssid string) error - SetWiFiAutoconnect(ssid string, autoconnect bool) error - - GetWiredConnections() ([]WiredConnection, error) - GetWiredNetworkDetails(uuid string) (*WiredNetworkInfoResponse, error) - ConnectEthernet() error - DisconnectEthernet() error - ActivateWiredConnection(uuid string) error - - ListVPNProfiles() ([]VPNProfile, error) - ListActiveVPN() ([]VPNActive, error) - ConnectVPN(uuidOrName string, singleActive bool) error - DisconnectVPN(uuidOrName string) error - DisconnectAllVPN() error - ClearVPNCredentials(uuidOrName string) error - - GetCurrentState() (*BackendState, error) - - StartMonitoring(onStateChange func()) error - StopMonitoring() - - GetPromptBroker() PromptBroker - SetPromptBroker(broker PromptBroker) error - SubmitCredentials(token string, secrets map[string]string, save bool) error - CancelCredentials(token string) error -} - -type BackendState struct { - Backend string - NetworkStatus NetworkStatus - EthernetIP string - EthernetDevice string - EthernetConnected bool - EthernetConnectionUuid string - WiFiIP string - WiFiDevice string - WiFiConnected bool - WiFiEnabled bool - WiFiSSID string - WiFiBSSID string - WiFiSignal uint8 - WiFiNetworks []WiFiNetwork - WiredConnections []WiredConnection - VPNProfiles []VPNProfile - VPNActive []VPNActive - IsConnecting bool - ConnectingSSID string - IsConnectingVPN bool - ConnectingVPNUUID string - LastError string -} diff --git a/nix/inputs/dms-cli/internal/server/network/backend_hybrid_iwd_networkd.go b/nix/inputs/dms-cli/internal/server/network/backend_hybrid_iwd_networkd.go deleted file mode 100644 index 1df3a9a..0000000 --- a/nix/inputs/dms-cli/internal/server/network/backend_hybrid_iwd_networkd.go +++ /dev/null @@ -1,198 +0,0 @@ -package network - -import ( - "fmt" - "sync" -) - -type HybridIwdNetworkdBackend struct { - wifi *IWDBackend - l3 *SystemdNetworkdBackend - onStateChange func() - stateMutex sync.RWMutex -} - -func NewHybridIwdNetworkdBackend(w *IWDBackend, n *SystemdNetworkdBackend) (*HybridIwdNetworkdBackend, error) { - return &HybridIwdNetworkdBackend{ - wifi: w, - l3: n, - }, nil -} - -func (b *HybridIwdNetworkdBackend) Initialize() error { - if err := b.wifi.Initialize(); err != nil { - return fmt.Errorf("iwd init: %w", err) - } - if err := b.l3.Initialize(); err != nil { - return fmt.Errorf("networkd init: %w", err) - } - return nil -} - -func (b *HybridIwdNetworkdBackend) Close() { - b.wifi.Close() - b.l3.Close() -} - -func (b *HybridIwdNetworkdBackend) StartMonitoring(onStateChange func()) error { - b.onStateChange = onStateChange - - mergedCallback := func() { - ws, _ := b.wifi.GetCurrentState() - ls, _ := b.l3.GetCurrentState() - - if ws != nil && ls != nil && ws.WiFiDevice != "" && ls.WiFiIP != "" { - b.wifi.MarkIPConfigSeen() - } - - if b.onStateChange != nil { - b.onStateChange() - } - } - - if err := b.wifi.StartMonitoring(mergedCallback); err != nil { - return fmt.Errorf("wifi monitoring: %w", err) - } - if err := b.l3.StartMonitoring(mergedCallback); err != nil { - return fmt.Errorf("l3 monitoring: %w", err) - } - - return nil -} - -func (b *HybridIwdNetworkdBackend) StopMonitoring() { - b.wifi.StopMonitoring() - b.l3.StopMonitoring() -} - -func (b *HybridIwdNetworkdBackend) GetCurrentState() (*BackendState, error) { - ws, err := b.wifi.GetCurrentState() - if err != nil { - return nil, err - } - ls, err := b.l3.GetCurrentState() - if err != nil { - return nil, err - } - - merged := *ws - merged.Backend = "iwd+networkd" - - merged.WiFiIP = ls.WiFiIP - merged.EthernetConnected = ls.EthernetConnected - merged.EthernetIP = ls.EthernetIP - merged.EthernetDevice = ls.EthernetDevice - merged.EthernetConnectionUuid = ls.EthernetConnectionUuid - merged.WiredConnections = ls.WiredConnections - - if ls.EthernetConnected && ls.EthernetIP != "" { - merged.NetworkStatus = StatusEthernet - } else if ws.WiFiConnected && ls.WiFiIP != "" { - merged.NetworkStatus = StatusWiFi - } else { - merged.NetworkStatus = StatusDisconnected - } - - return &merged, nil -} - -func (b *HybridIwdNetworkdBackend) GetWiFiEnabled() (bool, error) { - return b.wifi.GetWiFiEnabled() -} - -func (b *HybridIwdNetworkdBackend) SetWiFiEnabled(enabled bool) error { - return b.wifi.SetWiFiEnabled(enabled) -} - -func (b *HybridIwdNetworkdBackend) ScanWiFi() error { - return b.wifi.ScanWiFi() -} - -func (b *HybridIwdNetworkdBackend) GetWiFiNetworkDetails(ssid string) (*NetworkInfoResponse, error) { - return b.wifi.GetWiFiNetworkDetails(ssid) -} - -func (b *HybridIwdNetworkdBackend) ConnectWiFi(req ConnectionRequest) error { - if err := b.wifi.ConnectWiFi(req); err != nil { - return err - } - - ws, err := b.wifi.GetCurrentState() - if err == nil && ws.WiFiDevice != "" { - b.l3.EnsureDhcpUp(ws.WiFiDevice) - } - - return nil -} - -func (b *HybridIwdNetworkdBackend) DisconnectWiFi() error { - return b.wifi.DisconnectWiFi() -} - -func (b *HybridIwdNetworkdBackend) ForgetWiFiNetwork(ssid string) error { - return b.wifi.ForgetWiFiNetwork(ssid) -} - -func (b *HybridIwdNetworkdBackend) GetWiredConnections() ([]WiredConnection, error) { - return b.l3.GetWiredConnections() -} - -func (b *HybridIwdNetworkdBackend) GetWiredNetworkDetails(uuid string) (*WiredNetworkInfoResponse, error) { - return b.l3.GetWiredNetworkDetails(uuid) -} - -func (b *HybridIwdNetworkdBackend) ConnectEthernet() error { - return b.l3.ConnectEthernet() -} - -func (b *HybridIwdNetworkdBackend) DisconnectEthernet() error { - return b.l3.DisconnectEthernet() -} - -func (b *HybridIwdNetworkdBackend) ActivateWiredConnection(uuid string) error { - return b.l3.ActivateWiredConnection(uuid) -} - -func (b *HybridIwdNetworkdBackend) ListVPNProfiles() ([]VPNProfile, error) { - return []VPNProfile{}, nil -} - -func (b *HybridIwdNetworkdBackend) ListActiveVPN() ([]VPNActive, error) { - return []VPNActive{}, nil -} - -func (b *HybridIwdNetworkdBackend) ConnectVPN(uuidOrName string, singleActive bool) error { - return fmt.Errorf("VPN not supported in hybrid mode") -} - -func (b *HybridIwdNetworkdBackend) DisconnectVPN(uuidOrName string) error { - return fmt.Errorf("VPN not supported in hybrid mode") -} - -func (b *HybridIwdNetworkdBackend) DisconnectAllVPN() error { - return fmt.Errorf("VPN not supported in hybrid mode") -} - -func (b *HybridIwdNetworkdBackend) ClearVPNCredentials(uuidOrName string) error { - return fmt.Errorf("VPN not supported in hybrid mode") -} - -func (b *HybridIwdNetworkdBackend) GetPromptBroker() PromptBroker { - return b.wifi.GetPromptBroker() -} - -func (b *HybridIwdNetworkdBackend) SetPromptBroker(broker PromptBroker) error { - return b.wifi.SetPromptBroker(broker) -} - -func (b *HybridIwdNetworkdBackend) SubmitCredentials(token string, secrets map[string]string, save bool) error { - return b.wifi.SubmitCredentials(token, secrets, save) -} - -func (b *HybridIwdNetworkdBackend) CancelCredentials(token string) error { - return b.wifi.CancelCredentials(token) -} - -func (b *HybridIwdNetworkdBackend) SetWiFiAutoconnect(ssid string, autoconnect bool) error { - return b.wifi.SetWiFiAutoconnect(ssid, autoconnect) -} diff --git a/nix/inputs/dms-cli/internal/server/network/backend_hybrid_test.go b/nix/inputs/dms-cli/internal/server/network/backend_hybrid_test.go deleted file mode 100644 index cd061ed..0000000 --- a/nix/inputs/dms-cli/internal/server/network/backend_hybrid_test.go +++ /dev/null @@ -1,135 +0,0 @@ -package network - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestHybridIwdNetworkdBackend_New(t *testing.T) { - wifi, _ := NewIWDBackend() - l3, _ := NewSystemdNetworkdBackend() - - hybrid, err := NewHybridIwdNetworkdBackend(wifi, l3) - assert.NoError(t, err) - assert.NotNil(t, hybrid) - assert.NotNil(t, hybrid.wifi) - assert.NotNil(t, hybrid.l3) -} - -func TestHybridIwdNetworkdBackend_GetCurrentState_MergesState(t *testing.T) { - wifi, _ := NewIWDBackend() - l3, _ := NewSystemdNetworkdBackend() - hybrid, _ := NewHybridIwdNetworkdBackend(wifi, l3) - - wifi.state.WiFiConnected = true - wifi.state.WiFiSSID = "TestNetwork" - wifi.state.WiFiBSSID = "00:11:22:33:44:55" - wifi.state.WiFiSignal = 75 - wifi.state.WiFiDevice = "wlan0" - - l3.state.WiFiIP = "192.168.1.100" - l3.state.EthernetConnected = false - - state, err := hybrid.GetCurrentState() - assert.NoError(t, err) - assert.NotNil(t, state) - assert.Equal(t, "iwd+networkd", state.Backend) - assert.Equal(t, "TestNetwork", state.WiFiSSID) - assert.Equal(t, "00:11:22:33:44:55", state.WiFiBSSID) - assert.Equal(t, uint8(75), state.WiFiSignal) - assert.Equal(t, "192.168.1.100", state.WiFiIP) - assert.True(t, state.WiFiConnected) - assert.False(t, state.EthernetConnected) - assert.Equal(t, StatusWiFi, state.NetworkStatus) -} - -func TestHybridIwdNetworkdBackend_GetCurrentState_EthernetPriority(t *testing.T) { - wifi, _ := NewIWDBackend() - l3, _ := NewSystemdNetworkdBackend() - hybrid, _ := NewHybridIwdNetworkdBackend(wifi, l3) - - wifi.state.WiFiConnected = true - wifi.state.WiFiSSID = "TestNetwork" - - l3.state.WiFiIP = "192.168.1.100" - l3.state.EthernetConnected = true - l3.state.EthernetIP = "192.168.1.50" - l3.state.EthernetDevice = "eth0" - - state, err := hybrid.GetCurrentState() - assert.NoError(t, err) - assert.Equal(t, StatusEthernet, state.NetworkStatus) - assert.Equal(t, "192.168.1.50", state.EthernetIP) - assert.Equal(t, "eth0", state.EthernetDevice) -} - -func TestHybridIwdNetworkdBackend_GetCurrentState_WiFiNoIP(t *testing.T) { - wifi, _ := NewIWDBackend() - l3, _ := NewSystemdNetworkdBackend() - hybrid, _ := NewHybridIwdNetworkdBackend(wifi, l3) - - wifi.state.WiFiConnected = true - wifi.state.WiFiSSID = "TestNetwork" - - l3.state.WiFiIP = "" - l3.state.EthernetConnected = false - - state, err := hybrid.GetCurrentState() - assert.NoError(t, err) - assert.Equal(t, StatusDisconnected, state.NetworkStatus) - assert.True(t, state.WiFiConnected) - assert.Empty(t, state.WiFiIP) -} - -func TestHybridIwdNetworkdBackend_WiFiDelegation(t *testing.T) { - wifi, _ := NewIWDBackend() - l3, _ := NewSystemdNetworkdBackend() - hybrid, _ := NewHybridIwdNetworkdBackend(wifi, l3) - - enabled, err := hybrid.GetWiFiEnabled() - assert.NoError(t, err) - assert.True(t, enabled) - - state, err := hybrid.GetCurrentState() - assert.NoError(t, err) - assert.NotNil(t, state) - assert.Equal(t, "iwd+networkd", state.Backend) -} - -func TestHybridIwdNetworkdBackend_WiredDelegation(t *testing.T) { - wifi, _ := NewIWDBackend() - l3, _ := NewSystemdNetworkdBackend() - hybrid, _ := NewHybridIwdNetworkdBackend(wifi, l3) - - conns, err := hybrid.GetWiredConnections() - assert.NoError(t, err) - assert.Empty(t, conns) -} - -func TestHybridIwdNetworkdBackend_VPNNotSupported(t *testing.T) { - wifi, _ := NewIWDBackend() - l3, _ := NewSystemdNetworkdBackend() - hybrid, _ := NewHybridIwdNetworkdBackend(wifi, l3) - - profiles, err := hybrid.ListVPNProfiles() - assert.NoError(t, err) - assert.Empty(t, profiles) - - active, err := hybrid.ListActiveVPN() - assert.NoError(t, err) - assert.Empty(t, active) - - err = hybrid.ConnectVPN("test", false) - assert.Error(t, err) - assert.Contains(t, err.Error(), "not supported") -} - -func TestHybridIwdNetworkdBackend_PromptBrokerDelegation(t *testing.T) { - wifi, _ := NewIWDBackend() - l3, _ := NewSystemdNetworkdBackend() - hybrid, _ := NewHybridIwdNetworkdBackend(wifi, l3) - - broker := hybrid.GetPromptBroker() - assert.Nil(t, broker) -} diff --git a/nix/inputs/dms-cli/internal/server/network/backend_iwd.go b/nix/inputs/dms-cli/internal/server/network/backend_iwd.go deleted file mode 100644 index 0a7d611..0000000 --- a/nix/inputs/dms-cli/internal/server/network/backend_iwd.go +++ /dev/null @@ -1,222 +0,0 @@ -package network - -import ( - "fmt" - "sync" - "time" - - "github.com/godbus/dbus/v5" -) - -const ( - iwdBusName = "net.connman.iwd" - iwdObjectPath = "/" - iwdAdapterInterface = "net.connman.iwd.Adapter" - iwdDeviceInterface = "net.connman.iwd.Device" - iwdStationInterface = "net.connman.iwd.Station" - iwdNetworkInterface = "net.connman.iwd.Network" - iwdKnownNetworkInterface = "net.connman.iwd.KnownNetwork" - dbusObjectManager = "org.freedesktop.DBus.ObjectManager" - dbusPropertiesInterface = "org.freedesktop.DBus.Properties" -) - -type connectAttempt struct { - ssid string - netPath dbus.ObjectPath - start time.Time - deadline time.Time - sawAuthish bool - connectedAt time.Time - sawIPConfig bool - sawPromptRetry bool - finalized bool - mu sync.Mutex -} - -type IWDBackend struct { - conn *dbus.Conn - state *BackendState - stateMutex sync.RWMutex - promptBroker PromptBroker - onStateChange func() - - devicePath dbus.ObjectPath - stationPath dbus.ObjectPath - adapterPath dbus.ObjectPath - - iwdAgent *IWDAgent - - stopChan chan struct{} - sigWG sync.WaitGroup - curAttempt *connectAttempt - attemptMutex sync.RWMutex - recentScans map[string]time.Time - recentScansMu sync.Mutex -} - -func NewIWDBackend() (*IWDBackend, error) { - backend := &IWDBackend{ - state: &BackendState{ - Backend: "iwd", - WiFiEnabled: true, - }, - stopChan: make(chan struct{}), - recentScans: make(map[string]time.Time), - } - - return backend, nil -} - -func (b *IWDBackend) Initialize() error { - conn, err := dbus.ConnectSystemBus() - if err != nil { - return fmt.Errorf("failed to connect to system bus: %w", err) - } - b.conn = conn - - if err := b.discoverDevices(); err != nil { - conn.Close() - return fmt.Errorf("failed to discover iwd devices: %w", err) - } - - if err := b.updateState(); err != nil { - conn.Close() - return fmt.Errorf("failed to get initial state: %w", err) - } - - return nil -} - -func (b *IWDBackend) Close() { - close(b.stopChan) - b.sigWG.Wait() - - if b.iwdAgent != nil { - b.iwdAgent.Close() - } - - if b.conn != nil { - b.conn.Close() - } -} - -func (b *IWDBackend) discoverDevices() error { - obj := b.conn.Object(iwdBusName, iwdObjectPath) - - var objects map[dbus.ObjectPath]map[string]map[string]dbus.Variant - err := obj.Call(dbusObjectManager+".GetManagedObjects", 0).Store(&objects) - if err != nil { - return fmt.Errorf("failed to get managed objects: %w", err) - } - - for path, interfaces := range objects { - if _, hasStation := interfaces[iwdStationInterface]; hasStation { - b.stationPath = path - } - if _, hasDevice := interfaces[iwdDeviceInterface]; hasDevice { - b.devicePath = path - - if devProps, ok := interfaces[iwdDeviceInterface]; ok { - if nameVar, ok := devProps["Name"]; ok { - if name, ok := nameVar.Value().(string); ok { - b.stateMutex.Lock() - b.state.WiFiDevice = name - b.stateMutex.Unlock() - } - } - } - } - if _, hasAdapter := interfaces[iwdAdapterInterface]; hasAdapter { - b.adapterPath = path - } - } - - if b.stationPath == "" || b.devicePath == "" { - return fmt.Errorf("no WiFi device found") - } - - return nil -} - -func (b *IWDBackend) GetCurrentState() (*BackendState, error) { - state := *b.state - state.WiFiNetworks = append([]WiFiNetwork(nil), b.state.WiFiNetworks...) - state.WiredConnections = append([]WiredConnection(nil), b.state.WiredConnections...) - - return &state, nil -} - -func (b *IWDBackend) OnUserCanceledPrompt() { - b.setConnectError("user-canceled") - if b.onStateChange != nil { - b.onStateChange() - } -} - -func (b *IWDBackend) OnPromptRetry(ssid string) { - b.attemptMutex.RLock() - att := b.curAttempt - b.attemptMutex.RUnlock() - - if att != nil && att.ssid == ssid { - att.mu.Lock() - att.sawPromptRetry = true - att.mu.Unlock() - } -} - -func (b *IWDBackend) MarkIPConfigSeen() { - b.attemptMutex.RLock() - att := b.curAttempt - b.attemptMutex.RUnlock() - if att != nil { - att.mu.Lock() - att.sawIPConfig = true - att.mu.Unlock() - } -} - -func (b *IWDBackend) GetPromptBroker() PromptBroker { - return b.promptBroker -} - -func (b *IWDBackend) SetPromptBroker(broker PromptBroker) error { - if broker == nil { - return fmt.Errorf("broker cannot be nil") - } - - b.promptBroker = broker - return nil -} - -func (b *IWDBackend) SubmitCredentials(token string, secrets map[string]string, save bool) error { - if b.promptBroker == nil { - return fmt.Errorf("prompt broker not initialized") - } - - return b.promptBroker.Resolve(token, PromptReply{ - Secrets: secrets, - Save: save, - Cancel: false, - }) -} - -func (b *IWDBackend) CancelCredentials(token string) error { - if b.promptBroker == nil { - return fmt.Errorf("prompt broker not initialized") - } - - return b.promptBroker.Resolve(token, PromptReply{ - Cancel: true, - }) -} - -func (b *IWDBackend) StopMonitoring() { - select { - case <-b.stopChan: - return - default: - close(b.stopChan) - } - b.sigWG.Wait() -} diff --git a/nix/inputs/dms-cli/internal/server/network/backend_iwd_signals.go b/nix/inputs/dms-cli/internal/server/network/backend_iwd_signals.go deleted file mode 100644 index 62b4d5b..0000000 --- a/nix/inputs/dms-cli/internal/server/network/backend_iwd_signals.go +++ /dev/null @@ -1,355 +0,0 @@ -package network - -import ( - "fmt" - "time" - - "github.com/godbus/dbus/v5" -) - -func (b *IWDBackend) StartMonitoring(onStateChange func()) error { - b.onStateChange = onStateChange - - if b.promptBroker != nil { - agent, err := NewIWDAgent(b.promptBroker) - if err != nil { - return fmt.Errorf("failed to start IWD agent: %w", err) - } - agent.onUserCanceled = b.OnUserCanceledPrompt - agent.onPromptRetry = b.OnPromptRetry - b.iwdAgent = agent - } - - sigChan := make(chan *dbus.Signal, 100) - b.conn.Signal(sigChan) - - if b.devicePath != "" { - err := b.conn.AddMatchSignal( - dbus.WithMatchObjectPath(b.devicePath), - dbus.WithMatchInterface(dbusPropertiesInterface), - dbus.WithMatchMember("PropertiesChanged"), - ) - if err != nil { - return fmt.Errorf("failed to add device signal match: %w", err) - } - } - - if b.stationPath != "" { - err := b.conn.AddMatchSignal( - dbus.WithMatchObjectPath(b.stationPath), - dbus.WithMatchInterface(dbusPropertiesInterface), - dbus.WithMatchMember("PropertiesChanged"), - ) - if err != nil { - return fmt.Errorf("failed to add station signal match: %w", err) - } - } - - b.sigWG.Add(1) - go b.signalHandler(sigChan) - - return nil -} - -func (b *IWDBackend) signalHandler(sigChan chan *dbus.Signal) { - defer b.sigWG.Done() - - for { - select { - case <-b.stopChan: - b.conn.RemoveSignal(sigChan) - close(sigChan) - return - - case sig := <-sigChan: - if sig == nil { - return - } - - if sig.Name != dbusPropertiesInterface+".PropertiesChanged" { - continue - } - - if len(sig.Body) < 2 { - continue - } - - iface, ok := sig.Body[0].(string) - if !ok { - continue - } - - changed, ok := sig.Body[1].(map[string]dbus.Variant) - if !ok { - continue - } - - stateChanged := false - - switch iface { - case iwdDeviceInterface: - if sig.Path == b.devicePath { - if poweredVar, ok := changed["Powered"]; ok { - if powered, ok := poweredVar.Value().(bool); ok { - b.stateMutex.Lock() - if b.state.WiFiEnabled != powered { - b.state.WiFiEnabled = powered - stateChanged = true - } - b.stateMutex.Unlock() - } - } - } - - case iwdStationInterface: - if sig.Path == b.stationPath { - if scanningVar, ok := changed["Scanning"]; ok { - if scanning, ok := scanningVar.Value().(bool); ok && !scanning { - networks, err := b.updateWiFiNetworks() - if err == nil { - b.stateMutex.Lock() - b.state.WiFiNetworks = networks - b.stateMutex.Unlock() - stateChanged = true - } - - b.stateMutex.RLock() - wifiConnected := b.state.WiFiConnected - b.stateMutex.RUnlock() - - if wifiConnected { - stationObj := b.conn.Object(iwdBusName, b.stationPath) - connNetVar, err := stationObj.GetProperty(iwdStationInterface + ".ConnectedNetwork") - if err == nil && connNetVar.Value() != nil { - if netPath, ok := connNetVar.Value().(dbus.ObjectPath); ok && netPath != "/" { - var orderedNetworks [][]dbus.Variant - err = stationObj.Call(iwdStationInterface+".GetOrderedNetworks", 0).Store(&orderedNetworks) - if err == nil { - for _, netData := range orderedNetworks { - if len(netData) < 2 { - continue - } - currentNetPath, ok := netData[0].Value().(dbus.ObjectPath) - if !ok || currentNetPath != netPath { - continue - } - signalStrength, ok := netData[1].Value().(int16) - if !ok { - continue - } - signalDbm := signalStrength / 100 - signal := uint8(signalDbm + 100) - if signalDbm > 0 { - signal = 100 - } else if signalDbm < -100 { - signal = 0 - } - b.stateMutex.Lock() - if b.state.WiFiSignal != signal { - b.state.WiFiSignal = signal - stateChanged = true - } - b.stateMutex.Unlock() - break - } - } - } - } - } - } - } - - if stateVar, ok := changed["State"]; ok { - if state, ok := stateVar.Value().(string); ok { - b.attemptMutex.RLock() - att := b.curAttempt - b.attemptMutex.RUnlock() - - var connPath dbus.ObjectPath - if v, ok := changed["ConnectedNetwork"]; ok { - if v.Value() != nil { - if p, ok := v.Value().(dbus.ObjectPath); ok { - connPath = p - } - } - } - if connPath == "" { - station := b.conn.Object(iwdBusName, b.stationPath) - if cnVar, err := station.GetProperty(iwdStationInterface + ".ConnectedNetwork"); err == nil && cnVar.Value() != nil { - cnVar.Store(&connPath) - } - } - - b.stateMutex.RLock() - prevConnected := b.state.WiFiConnected - prevSSID := b.state.WiFiSSID - b.stateMutex.RUnlock() - - targetPath := dbus.ObjectPath("") - if att != nil { - targetPath = att.netPath - } - - isTarget := att != nil && targetPath != "" && connPath == targetPath - - if att != nil { - switch state { - case "authenticating", "associating", "associated", "roaming": - att.mu.Lock() - att.sawAuthish = true - att.mu.Unlock() - } - } - - if att != nil && state == "connected" && isTarget { - att.mu.Lock() - if att.connectedAt.IsZero() { - att.connectedAt = time.Now() - } - att.mu.Unlock() - } - - if att != nil && state == "configuring" { - att.mu.Lock() - att.sawIPConfig = true - att.mu.Unlock() - } - - switch state { - case "connected": - b.stateMutex.Lock() - b.state.WiFiConnected = true - b.state.NetworkStatus = StatusWiFi - b.state.IsConnecting = false - b.state.ConnectingSSID = "" - b.state.LastError = "" - b.stateMutex.Unlock() - - if connPath != "" && connPath != "/" { - netObj := b.conn.Object(iwdBusName, connPath) - if nameVar, err := netObj.GetProperty(iwdNetworkInterface + ".Name"); err == nil { - if name, ok := nameVar.Value().(string); ok { - b.stateMutex.Lock() - b.state.WiFiSSID = name - b.stateMutex.Unlock() - } - } - } - - stateChanged = true - - if att != nil && isTarget { - go func(attLocal *connectAttempt, tgt dbus.ObjectPath) { - time.Sleep(3 * time.Second) - station := b.conn.Object(iwdBusName, b.stationPath) - var nowState string - if stVar, err := station.GetProperty(iwdStationInterface + ".State"); err == nil { - stVar.Store(&nowState) - } - var nowConn dbus.ObjectPath - if cnVar, err := station.GetProperty(iwdStationInterface + ".ConnectedNetwork"); err == nil && cnVar.Value() != nil { - cnVar.Store(&nowConn) - } - - if nowState == "connected" && nowConn == tgt { - b.finalizeAttempt(attLocal, "") - b.attemptMutex.Lock() - if b.curAttempt == attLocal { - b.curAttempt = nil - } - b.attemptMutex.Unlock() - } - }(att, targetPath) - } - - case "disconnecting", "disconnected": - if att != nil { - wasConnectedToTarget := prevConnected && prevSSID == att.ssid - if wasConnectedToTarget || isTarget { - code := b.classifyAttempt(att) - b.finalizeAttempt(att, code) - b.attemptMutex.Lock() - if b.curAttempt == att { - b.curAttempt = nil - } - b.attemptMutex.Unlock() - } - } - - b.stateMutex.Lock() - b.state.WiFiConnected = false - if state == "disconnected" { - b.state.NetworkStatus = StatusDisconnected - } - b.stateMutex.Unlock() - stateChanged = true - } - } - } - - if connNetVar, ok := changed["ConnectedNetwork"]; ok { - if netPath, ok := connNetVar.Value().(dbus.ObjectPath); ok && netPath != "/" { - netObj := b.conn.Object(iwdBusName, netPath) - nameVar, err := netObj.GetProperty(iwdNetworkInterface + ".Name") - if err == nil { - if name, ok := nameVar.Value().(string); ok { - b.stateMutex.Lock() - if b.state.WiFiSSID != name { - b.state.WiFiSSID = name - stateChanged = true - } - b.stateMutex.Unlock() - } - } - - stationObj := b.conn.Object(iwdBusName, b.stationPath) - var orderedNetworks [][]dbus.Variant - err = stationObj.Call(iwdStationInterface+".GetOrderedNetworks", 0).Store(&orderedNetworks) - if err == nil { - for _, netData := range orderedNetworks { - if len(netData) < 2 { - continue - } - currentNetPath, ok := netData[0].Value().(dbus.ObjectPath) - if !ok || currentNetPath != netPath { - continue - } - signalStrength, ok := netData[1].Value().(int16) - if !ok { - continue - } - signalDbm := signalStrength / 100 - signal := uint8(signalDbm + 100) - if signalDbm > 0 { - signal = 100 - } else if signalDbm < -100 { - signal = 0 - } - b.stateMutex.Lock() - if b.state.WiFiSignal != signal { - b.state.WiFiSignal = signal - stateChanged = true - } - b.stateMutex.Unlock() - break - } - } - } else { - b.stateMutex.Lock() - if b.state.WiFiSSID != "" { - b.state.WiFiSSID = "" - b.state.WiFiSignal = 0 - stateChanged = true - } - b.stateMutex.Unlock() - } - } - } - } - - if stateChanged && b.onStateChange != nil { - b.onStateChange() - } - } - } -} diff --git a/nix/inputs/dms-cli/internal/server/network/backend_iwd_test.go b/nix/inputs/dms-cli/internal/server/network/backend_iwd_test.go deleted file mode 100644 index 829e413..0000000 --- a/nix/inputs/dms-cli/internal/server/network/backend_iwd_test.go +++ /dev/null @@ -1,212 +0,0 @@ -package network - -import ( - "testing" - "time" - - "github.com/stretchr/testify/assert" -) - -func TestIWDBackend_MarkIPConfigSeen(t *testing.T) { - backend, _ := NewIWDBackend() - - att := &connectAttempt{ - ssid: "TestNetwork", - netPath: "/net/connman/iwd/0/1/test", - start: time.Now(), - deadline: time.Now().Add(15 * time.Second), - } - - backend.attemptMutex.Lock() - backend.curAttempt = att - backend.attemptMutex.Unlock() - - backend.MarkIPConfigSeen() - - att.mu.Lock() - assert.True(t, att.sawIPConfig, "sawIPConfig should be true after MarkIPConfigSeen") - att.mu.Unlock() -} - -func TestIWDBackend_MarkIPConfigSeen_NoAttempt(t *testing.T) { - backend, _ := NewIWDBackend() - - backend.attemptMutex.Lock() - backend.curAttempt = nil - backend.attemptMutex.Unlock() - - backend.MarkIPConfigSeen() -} - -func TestIWDBackend_OnPromptRetry(t *testing.T) { - backend, _ := NewIWDBackend() - - att := &connectAttempt{ - ssid: "TestNetwork", - netPath: "/net/connman/iwd/0/1/test", - start: time.Now(), - deadline: time.Now().Add(15 * time.Second), - } - - backend.attemptMutex.Lock() - backend.curAttempt = att - backend.attemptMutex.Unlock() - - backend.OnPromptRetry("TestNetwork") - - att.mu.Lock() - assert.True(t, att.sawPromptRetry, "sawPromptRetry should be true after OnPromptRetry") - att.mu.Unlock() -} - -func TestIWDBackend_OnPromptRetry_WrongSSID(t *testing.T) { - backend, _ := NewIWDBackend() - - att := &connectAttempt{ - ssid: "TestNetwork", - netPath: "/net/connman/iwd/0/1/test", - start: time.Now(), - deadline: time.Now().Add(15 * time.Second), - } - - backend.attemptMutex.Lock() - backend.curAttempt = att - backend.attemptMutex.Unlock() - - backend.OnPromptRetry("DifferentNetwork") - - att.mu.Lock() - assert.False(t, att.sawPromptRetry, "sawPromptRetry should remain false for different SSID") - att.mu.Unlock() -} - -func TestIWDBackend_ClassifyAttempt_BadCredentials_PromptRetry(t *testing.T) { - backend, _ := NewIWDBackend() - - att := &connectAttempt{ - ssid: "TestNetwork", - netPath: "/test", - start: time.Now().Add(-5 * time.Second), - deadline: time.Now().Add(10 * time.Second), - sawPromptRetry: true, - } - - code := backend.classifyAttempt(att) - assert.Equal(t, "bad-credentials", code) -} - -func TestIWDBackend_ClassifyAttempt_DhcpTimeout(t *testing.T) { - backend, _ := NewIWDBackend() - - att := &connectAttempt{ - ssid: "TestNetwork", - netPath: "/test", - start: time.Now().Add(-13 * time.Second), - deadline: time.Now().Add(2 * time.Second), - sawAuthish: true, - sawIPConfig: false, - } - - code := backend.classifyAttempt(att) - assert.Equal(t, "dhcp-timeout", code) -} - -func TestIWDBackend_ClassifyAttempt_AssocTimeout(t *testing.T) { - backend, _ := NewIWDBackend() - - att := &connectAttempt{ - ssid: "TestNetwork", - netPath: "/test", - start: time.Now().Add(-5 * time.Second), - deadline: time.Now().Add(10 * time.Second), - } - - backend.recentScansMu.Lock() - backend.recentScans["TestNetwork"] = time.Now() - backend.recentScansMu.Unlock() - - code := backend.classifyAttempt(att) - assert.Equal(t, "assoc-timeout", code) -} - -func TestIWDBackend_ClassifyAttempt_NoSuchSSID(t *testing.T) { - backend, _ := NewIWDBackend() - - att := &connectAttempt{ - ssid: "TestNetwork", - netPath: "/test", - start: time.Now().Add(-5 * time.Second), - deadline: time.Now().Add(10 * time.Second), - } - - code := backend.classifyAttempt(att) - assert.Equal(t, "no-such-ssid", code) -} - -func TestIWDBackend_MapIwdDBusError(t *testing.T) { - backend, _ := NewIWDBackend() - - testCases := []struct { - name string - expected string - }{ - {"net.connman.iwd.Error.AlreadyConnected", "already-connected"}, - {"net.connman.iwd.Error.AuthenticationFailed", "bad-credentials"}, - {"net.connman.iwd.Error.InvalidKey", "bad-credentials"}, - {"net.connman.iwd.Error.IncorrectPassphrase", "bad-credentials"}, - {"net.connman.iwd.Error.NotFound", "no-such-ssid"}, - {"net.connman.iwd.Error.NotSupported", "connection-failed"}, - {"net.connman.iwd.Agent.Error.Canceled", "user-canceled"}, - {"net.connman.iwd.Error.Unknown", "connection-failed"}, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - code := backend.mapIwdDBusError(tc.name) - assert.Equal(t, tc.expected, code) - }) - } -} - -func TestConnectAttempt_Finalization(t *testing.T) { - backend, _ := NewIWDBackend() - backend.state = &BackendState{} - - att := &connectAttempt{ - ssid: "TestNetwork", - netPath: "/test", - start: time.Now(), - deadline: time.Now().Add(15 * time.Second), - } - - backend.finalizeAttempt(att, "bad-credentials") - - att.mu.Lock() - assert.True(t, att.finalized) - att.mu.Unlock() - - backend.stateMutex.RLock() - assert.False(t, backend.state.IsConnecting) - assert.Empty(t, backend.state.ConnectingSSID) - assert.Equal(t, "bad-credentials", backend.state.LastError) - backend.stateMutex.RUnlock() -} - -func TestConnectAttempt_DoubleFinalization(t *testing.T) { - backend, _ := NewIWDBackend() - backend.state = &BackendState{} - - att := &connectAttempt{ - ssid: "TestNetwork", - netPath: "/test", - start: time.Now(), - deadline: time.Now().Add(15 * time.Second), - } - - backend.finalizeAttempt(att, "bad-credentials") - backend.finalizeAttempt(att, "dhcp-timeout") - - backend.stateMutex.RLock() - assert.Equal(t, "bad-credentials", backend.state.LastError) - backend.stateMutex.RUnlock() -} diff --git a/nix/inputs/dms-cli/internal/server/network/backend_iwd_unimplemented.go b/nix/inputs/dms-cli/internal/server/network/backend_iwd_unimplemented.go deleted file mode 100644 index 374d0bf..0000000 --- a/nix/inputs/dms-cli/internal/server/network/backend_iwd_unimplemented.go +++ /dev/null @@ -1,47 +0,0 @@ -package network - -import "fmt" - -func (b *IWDBackend) GetWiredConnections() ([]WiredConnection, error) { - return nil, fmt.Errorf("wired connections not supported by iwd") -} - -func (b *IWDBackend) GetWiredNetworkDetails(uuid string) (*WiredNetworkInfoResponse, error) { - return nil, fmt.Errorf("wired connections not supported by iwd") -} - -func (b *IWDBackend) ConnectEthernet() error { - return fmt.Errorf("wired connections not supported by iwd") -} - -func (b *IWDBackend) DisconnectEthernet() error { - return fmt.Errorf("wired connections not supported by iwd") -} - -func (b *IWDBackend) ActivateWiredConnection(uuid string) error { - return fmt.Errorf("wired connections not supported by iwd") -} - -func (b *IWDBackend) ListVPNProfiles() ([]VPNProfile, error) { - return nil, fmt.Errorf("VPN not supported by iwd backend") -} - -func (b *IWDBackend) ListActiveVPN() ([]VPNActive, error) { - return nil, fmt.Errorf("VPN not supported by iwd backend") -} - -func (b *IWDBackend) ConnectVPN(uuidOrName string, singleActive bool) error { - return fmt.Errorf("VPN not supported by iwd backend") -} - -func (b *IWDBackend) DisconnectVPN(uuidOrName string) error { - return fmt.Errorf("VPN not supported by iwd backend") -} - -func (b *IWDBackend) DisconnectAllVPN() error { - return fmt.Errorf("VPN not supported by iwd backend") -} - -func (b *IWDBackend) ClearVPNCredentials(uuidOrName string) error { - return fmt.Errorf("VPN not supported by iwd backend") -} diff --git a/nix/inputs/dms-cli/internal/server/network/backend_iwd_wifi.go b/nix/inputs/dms-cli/internal/server/network/backend_iwd_wifi.go deleted file mode 100644 index a56ed06..0000000 --- a/nix/inputs/dms-cli/internal/server/network/backend_iwd_wifi.go +++ /dev/null @@ -1,662 +0,0 @@ -package network - -import ( - "fmt" - "time" - - "github.com/AvengeMedia/danklinux/internal/errdefs" - "github.com/godbus/dbus/v5" -) - -func (b *IWDBackend) updateState() error { - if b.devicePath == "" { - return nil - } - - obj := b.conn.Object(iwdBusName, b.devicePath) - - poweredVar, err := obj.GetProperty(iwdDeviceInterface + ".Powered") - if err == nil { - if powered, ok := poweredVar.Value().(bool); ok { - b.stateMutex.Lock() - b.state.WiFiEnabled = powered - b.stateMutex.Unlock() - } - } - - if b.stationPath == "" { - return nil - } - - stationObj := b.conn.Object(iwdBusName, b.stationPath) - - stateVar, err := stationObj.GetProperty(iwdStationInterface + ".State") - if err == nil { - if state, ok := stateVar.Value().(string); ok { - b.stateMutex.Lock() - b.state.WiFiConnected = (state == "connected") - if state == "connected" { - b.state.NetworkStatus = StatusWiFi - } else { - b.state.NetworkStatus = StatusDisconnected - } - b.stateMutex.Unlock() - } - } - - connNetVar, err := stationObj.GetProperty(iwdStationInterface + ".ConnectedNetwork") - if err == nil && connNetVar.Value() != nil { - if netPath, ok := connNetVar.Value().(dbus.ObjectPath); ok && netPath != "/" { - netObj := b.conn.Object(iwdBusName, netPath) - - nameVar, err := netObj.GetProperty(iwdNetworkInterface + ".Name") - if err == nil { - if name, ok := nameVar.Value().(string); ok { - b.stateMutex.Lock() - b.state.WiFiSSID = name - b.stateMutex.Unlock() - } - } - - var orderedNetworks [][]dbus.Variant - err = stationObj.Call(iwdStationInterface+".GetOrderedNetworks", 0).Store(&orderedNetworks) - if err == nil { - for _, netData := range orderedNetworks { - if len(netData) < 2 { - continue - } - currentNetPath, ok := netData[0].Value().(dbus.ObjectPath) - if !ok || currentNetPath != netPath { - continue - } - signalStrength, ok := netData[1].Value().(int16) - if !ok { - continue - } - signalDbm := signalStrength / 100 - signal := uint8(signalDbm + 100) - if signalDbm > 0 { - signal = 100 - } else if signalDbm < -100 { - signal = 0 - } - b.stateMutex.Lock() - b.state.WiFiSignal = signal - b.stateMutex.Unlock() - break - } - } - } - } - - networks, err := b.updateWiFiNetworks() - if err == nil { - b.stateMutex.Lock() - b.state.WiFiNetworks = networks - b.stateMutex.Unlock() - } - - return nil -} - -func (b *IWDBackend) GetWiFiEnabled() (bool, error) { - b.stateMutex.RLock() - defer b.stateMutex.RUnlock() - return b.state.WiFiEnabled, nil -} - -func (b *IWDBackend) SetWiFiEnabled(enabled bool) error { - if b.devicePath == "" { - return fmt.Errorf("no WiFi device available") - } - - obj := b.conn.Object(iwdBusName, b.devicePath) - call := obj.Call(dbusPropertiesInterface+".Set", 0, iwdDeviceInterface, "Powered", dbus.MakeVariant(enabled)) - if call.Err != nil { - return fmt.Errorf("failed to set WiFi enabled: %w", call.Err) - } - - b.stateMutex.Lock() - b.state.WiFiEnabled = enabled - b.stateMutex.Unlock() - - if b.onStateChange != nil { - b.onStateChange() - } - - return nil -} - -func (b *IWDBackend) ScanWiFi() error { - if b.stationPath == "" { - return fmt.Errorf("no WiFi device available") - } - - obj := b.conn.Object(iwdBusName, b.stationPath) - - scanningVar, err := obj.GetProperty(iwdStationInterface + ".Scanning") - if err != nil { - return fmt.Errorf("failed to check scanning state: %w", err) - } - - if scanning, ok := scanningVar.Value().(bool); ok && scanning { - return fmt.Errorf("scan already in progress") - } - - call := obj.Call(iwdStationInterface+".Scan", 0) - if call.Err != nil { - return fmt.Errorf("scan request failed: %w", call.Err) - } - - return nil -} - -func (b *IWDBackend) updateWiFiNetworks() ([]WiFiNetwork, error) { - if b.stationPath == "" { - return nil, fmt.Errorf("no WiFi device available") - } - - obj := b.conn.Object(iwdBusName, b.stationPath) - - var orderedNetworks [][]dbus.Variant - err := obj.Call(iwdStationInterface+".GetOrderedNetworks", 0).Store(&orderedNetworks) - if err != nil { - return nil, fmt.Errorf("failed to get networks: %w", err) - } - - knownNetworks, err := b.getKnownNetworks() - if err != nil { - knownNetworks = make(map[string]bool) - } - - autoconnectMap, err := b.getAutoconnectSettings() - if err != nil { - autoconnectMap = make(map[string]bool) - } - - b.stateMutex.RLock() - currentSSID := b.state.WiFiSSID - wifiConnected := b.state.WiFiConnected - b.stateMutex.RUnlock() - - networks := make([]WiFiNetwork, 0, len(orderedNetworks)) - for _, netData := range orderedNetworks { - if len(netData) < 2 { - continue - } - - networkPath, ok := netData[0].Value().(dbus.ObjectPath) - if !ok { - continue - } - - signalStrength, ok := netData[1].Value().(int16) - if !ok { - continue - } - - netObj := b.conn.Object(iwdBusName, networkPath) - - nameVar, err := netObj.GetProperty(iwdNetworkInterface + ".Name") - if err != nil { - continue - } - name, ok := nameVar.Value().(string) - if !ok { - continue - } - - typeVar, err := netObj.GetProperty(iwdNetworkInterface + ".Type") - if err != nil { - continue - } - netType, ok := typeVar.Value().(string) - if !ok { - continue - } - - signalDbm := signalStrength / 100 - signal := uint8(signalDbm + 100) - if signalDbm > 0 { - signal = 100 - } else if signalDbm < -100 { - signal = 0 - } - - secured := netType != "open" - - network := WiFiNetwork{ - SSID: name, - Signal: signal, - Secured: secured, - Connected: wifiConnected && name == currentSSID, - Saved: knownNetworks[name], - Autoconnect: autoconnectMap[name], - Enterprise: netType == "8021x", - } - - networks = append(networks, network) - } - - sortWiFiNetworks(networks) - - b.stateMutex.Lock() - b.state.WiFiNetworks = networks - b.stateMutex.Unlock() - - now := time.Now() - b.recentScansMu.Lock() - for _, net := range networks { - b.recentScans[net.SSID] = now - } - b.recentScansMu.Unlock() - - return networks, nil -} - -func (b *IWDBackend) getKnownNetworks() (map[string]bool, error) { - obj := b.conn.Object(iwdBusName, iwdObjectPath) - - var objects map[dbus.ObjectPath]map[string]map[string]dbus.Variant - err := obj.Call(dbusObjectManager+".GetManagedObjects", 0).Store(&objects) - if err != nil { - return nil, err - } - - known := make(map[string]bool) - for _, interfaces := range objects { - if knownProps, ok := interfaces[iwdKnownNetworkInterface]; ok { - if nameVar, ok := knownProps["Name"]; ok { - if name, ok := nameVar.Value().(string); ok { - known[name] = true - } - } - } - } - - return known, nil -} - -func (b *IWDBackend) getAutoconnectSettings() (map[string]bool, error) { - obj := b.conn.Object(iwdBusName, iwdObjectPath) - - var objects map[dbus.ObjectPath]map[string]map[string]dbus.Variant - err := obj.Call(dbusObjectManager+".GetManagedObjects", 0).Store(&objects) - if err != nil { - return nil, err - } - - autoconnectMap := make(map[string]bool) - for _, interfaces := range objects { - if knownProps, ok := interfaces[iwdKnownNetworkInterface]; ok { - if nameVar, ok := knownProps["Name"]; ok { - if name, ok := nameVar.Value().(string); ok { - autoconnect := true - if acVar, ok := knownProps["AutoConnect"]; ok { - if ac, ok := acVar.Value().(bool); ok { - autoconnect = ac - } - } - autoconnectMap[name] = autoconnect - } - } - } - } - - return autoconnectMap, nil -} - -func (b *IWDBackend) GetWiFiNetworkDetails(ssid string) (*NetworkInfoResponse, error) { - b.stateMutex.RLock() - networks := b.state.WiFiNetworks - b.stateMutex.RUnlock() - - var found *WiFiNetwork - for i := range networks { - if networks[i].SSID == ssid { - found = &networks[i] - break - } - } - - if found == nil { - return nil, fmt.Errorf("network not found: %s", ssid) - } - - return &NetworkInfoResponse{ - SSID: ssid, - Bands: []WiFiNetwork{*found}, - }, nil -} - -func (b *IWDBackend) setConnectError(code string) { - b.stateMutex.Lock() - b.state.IsConnecting = false - b.state.ConnectingSSID = "" - b.state.LastError = code - b.stateMutex.Unlock() -} - -func (b *IWDBackend) seenInRecentScan(ssid string) bool { - b.recentScansMu.Lock() - defer b.recentScansMu.Unlock() - lastSeen, ok := b.recentScans[ssid] - return ok && time.Since(lastSeen) < 30*time.Second -} - -func (b *IWDBackend) classifyAttempt(att *connectAttempt) string { - att.mu.Lock() - defer att.mu.Unlock() - - if att.sawPromptRetry { - return errdefs.ErrBadCredentials - } - - if !att.connectedAt.IsZero() && !att.sawIPConfig { - connDuration := time.Since(att.connectedAt) - if connDuration > 500*time.Millisecond && connDuration < 3*time.Second { - return errdefs.ErrBadCredentials - } - } - - if (att.sawAuthish || !att.connectedAt.IsZero()) && !att.sawIPConfig { - if time.Since(att.start) > 12*time.Second { - return errdefs.ErrDhcpTimeout - } - } - - if !att.sawAuthish && att.connectedAt.IsZero() { - if !b.seenInRecentScan(att.ssid) { - return errdefs.ErrNoSuchSSID - } - return errdefs.ErrAssocTimeout - } - - return errdefs.ErrAssocTimeout -} - -func (b *IWDBackend) finalizeAttempt(att *connectAttempt, code string) { - att.mu.Lock() - if att.finalized { - att.mu.Unlock() - return - } - att.finalized = true - att.mu.Unlock() - - b.stateMutex.Lock() - b.state.IsConnecting = false - b.state.ConnectingSSID = "" - b.state.LastError = code - b.stateMutex.Unlock() - - b.updateState() - - if b.onStateChange != nil { - b.onStateChange() - } -} - -func (b *IWDBackend) startAttemptWatchdog(att *connectAttempt) { - b.sigWG.Add(1) - go func() { - defer b.sigWG.Done() - - ticker := time.NewTicker(250 * time.Millisecond) - defer ticker.Stop() - - for { - select { - case <-ticker.C: - att.mu.Lock() - finalized := att.finalized - att.mu.Unlock() - - if finalized || time.Now().After(att.deadline) { - if !finalized { - b.finalizeAttempt(att, b.classifyAttempt(att)) - } - return - } - - station := b.conn.Object(iwdBusName, b.stationPath) - stVar, err := station.GetProperty(iwdStationInterface + ".State") - if err != nil { - continue - } - state, _ := stVar.Value().(string) - - cnVar, err := station.GetProperty(iwdStationInterface + ".ConnectedNetwork") - if err != nil { - continue - } - var connPath dbus.ObjectPath - if cnVar.Value() != nil { - connPath, _ = cnVar.Value().(dbus.ObjectPath) - } - - att.mu.Lock() - if connPath == att.netPath && state == "connected" && att.connectedAt.IsZero() { - att.connectedAt = time.Now() - } - if state == "configuring" { - att.sawIPConfig = true - } - att.mu.Unlock() - - case <-b.stopChan: - return - } - } - }() -} - -func (b *IWDBackend) mapIwdDBusError(name string) string { - switch name { - case "net.connman.iwd.Error.AlreadyConnected": - return errdefs.ErrAlreadyConnected - case "net.connman.iwd.Error.AuthenticationFailed", - "net.connman.iwd.Error.InvalidKey", - "net.connman.iwd.Error.IncorrectPassphrase": - return errdefs.ErrBadCredentials - case "net.connman.iwd.Error.NotFound": - return errdefs.ErrNoSuchSSID - case "net.connman.iwd.Error.NotSupported": - return errdefs.ErrConnectionFailed - case "net.connman.iwd.Agent.Error.Canceled": - return errdefs.ErrUserCanceled - default: - return errdefs.ErrConnectionFailed - } -} - -func (b *IWDBackend) ConnectWiFi(req ConnectionRequest) error { - if b.stationPath == "" { - b.setConnectError(errdefs.ErrWifiDisabled) - if b.onStateChange != nil { - b.onStateChange() - } - return fmt.Errorf("no WiFi device available") - } - - networkPath, err := b.findNetworkPath(req.SSID) - if err != nil { - b.setConnectError(errdefs.ErrNoSuchSSID) - if b.onStateChange != nil { - b.onStateChange() - } - return fmt.Errorf("network not found: %w", err) - } - - att := &connectAttempt{ - ssid: req.SSID, - netPath: networkPath, - start: time.Now(), - deadline: time.Now().Add(15 * time.Second), - } - - b.attemptMutex.Lock() - b.curAttempt = att - b.attemptMutex.Unlock() - - b.stateMutex.Lock() - b.state.IsConnecting = true - b.state.ConnectingSSID = req.SSID - b.state.LastError = "" - b.stateMutex.Unlock() - - if b.onStateChange != nil { - b.onStateChange() - } - - netObj := b.conn.Object(iwdBusName, networkPath) - go func() { - call := netObj.Call(iwdNetworkInterface+".Connect", 0) - if call.Err != nil { - var code string - if dbusErr, ok := call.Err.(dbus.Error); ok { - code = b.mapIwdDBusError(dbusErr.Name) - } else if dbusErrPtr, ok := call.Err.(*dbus.Error); ok { - code = b.mapIwdDBusError(dbusErrPtr.Name) - } else { - code = errdefs.ErrConnectionFailed - } - - att.mu.Lock() - if att.sawPromptRetry { - code = errdefs.ErrBadCredentials - } - att.mu.Unlock() - - b.finalizeAttempt(att, code) - return - } - - b.startAttemptWatchdog(att) - }() - - return nil -} - -func (b *IWDBackend) findNetworkPath(ssid string) (dbus.ObjectPath, error) { - obj := b.conn.Object(iwdBusName, iwdObjectPath) - - var objects map[dbus.ObjectPath]map[string]map[string]dbus.Variant - err := obj.Call(dbusObjectManager+".GetManagedObjects", 0).Store(&objects) - if err != nil { - return "", err - } - - for path, interfaces := range objects { - if netProps, ok := interfaces[iwdNetworkInterface]; ok { - if nameVar, ok := netProps["Name"]; ok { - if name, ok := nameVar.Value().(string); ok && name == ssid { - return path, nil - } - } - } - } - - return "", fmt.Errorf("network not found") -} - -func (b *IWDBackend) DisconnectWiFi() error { - if b.stationPath == "" { - return fmt.Errorf("no WiFi device available") - } - - obj := b.conn.Object(iwdBusName, b.stationPath) - call := obj.Call(iwdStationInterface+".Disconnect", 0) - if call.Err != nil { - return fmt.Errorf("failed to disconnect: %w", call.Err) - } - - b.updateState() - - if b.onStateChange != nil { - b.onStateChange() - } - - return nil -} - -func (b *IWDBackend) ForgetWiFiNetwork(ssid string) error { - b.stateMutex.RLock() - currentSSID := b.state.WiFiSSID - isConnected := b.state.WiFiConnected - b.stateMutex.RUnlock() - - obj := b.conn.Object(iwdBusName, iwdObjectPath) - - var objects map[dbus.ObjectPath]map[string]map[string]dbus.Variant - err := obj.Call(dbusObjectManager+".GetManagedObjects", 0).Store(&objects) - if err != nil { - return err - } - - for path, interfaces := range objects { - if knownProps, ok := interfaces[iwdKnownNetworkInterface]; ok { - if nameVar, ok := knownProps["Name"]; ok { - if name, ok := nameVar.Value().(string); ok && name == ssid { - knownObj := b.conn.Object(iwdBusName, path) - call := knownObj.Call(iwdKnownNetworkInterface+".Forget", 0) - if call.Err != nil { - return fmt.Errorf("failed to forget network: %w", call.Err) - } - - if isConnected && currentSSID == ssid { - b.stateMutex.Lock() - b.state.WiFiConnected = false - b.state.WiFiSSID = "" - b.state.WiFiSignal = 0 - b.state.WiFiIP = "" - b.state.NetworkStatus = StatusDisconnected - b.stateMutex.Unlock() - } - - if b.onStateChange != nil { - b.onStateChange() - } - - return nil - } - } - } - } - - return fmt.Errorf("network not found") -} - -func (b *IWDBackend) SetWiFiAutoconnect(ssid string, autoconnect bool) error { - obj := b.conn.Object(iwdBusName, iwdObjectPath) - - var objects map[dbus.ObjectPath]map[string]map[string]dbus.Variant - err := obj.Call(dbusObjectManager+".GetManagedObjects", 0).Store(&objects) - if err != nil { - return err - } - - for path, interfaces := range objects { - if knownProps, ok := interfaces[iwdKnownNetworkInterface]; ok { - if nameVar, ok := knownProps["Name"]; ok { - if name, ok := nameVar.Value().(string); ok && name == ssid { - knownObj := b.conn.Object(iwdBusName, path) - call := knownObj.Call(dbusPropertiesInterface+".Set", 0, iwdKnownNetworkInterface, "AutoConnect", dbus.MakeVariant(autoconnect)) - if call.Err != nil { - return fmt.Errorf("failed to set autoconnect: %w", call.Err) - } - - b.updateState() - - if b.onStateChange != nil { - b.onStateChange() - } - - return nil - } - } - } - } - - return fmt.Errorf("network not found") -} diff --git a/nix/inputs/dms-cli/internal/server/network/backend_networkd.go b/nix/inputs/dms-cli/internal/server/network/backend_networkd.go deleted file mode 100644 index 2aabaca..0000000 --- a/nix/inputs/dms-cli/internal/server/network/backend_networkd.go +++ /dev/null @@ -1,268 +0,0 @@ -package network - -import ( - "fmt" - "net" - "strings" - "sync" - - "github.com/AvengeMedia/danklinux/internal/log" - "github.com/godbus/dbus/v5" -) - -const ( - networkdBusName = "org.freedesktop.network1" - networkdManagerPath = "/org/freedesktop/network1" - networkdManagerIface = "org.freedesktop.network1.Manager" - networkdLinkIface = "org.freedesktop.network1.Link" -) - -type linkInfo struct { - ifindex int32 - name string - path dbus.ObjectPath - opState string -} - -type SystemdNetworkdBackend struct { - conn *dbus.Conn - managerPath dbus.ObjectPath - links map[string]*linkInfo - linksMutex sync.RWMutex - state *BackendState - stateMutex sync.RWMutex - onStateChange func() - stopChan chan struct{} - signals chan *dbus.Signal - sigWG sync.WaitGroup -} - -func NewSystemdNetworkdBackend() (*SystemdNetworkdBackend, error) { - return &SystemdNetworkdBackend{ - managerPath: networkdManagerPath, - links: make(map[string]*linkInfo), - state: &BackendState{ - Backend: "networkd", - WiFiNetworks: []WiFiNetwork{}, - }, - stopChan: make(chan struct{}), - }, nil -} - -func (b *SystemdNetworkdBackend) Initialize() error { - c, err := dbus.ConnectSystemBus() - if err != nil { - return fmt.Errorf("connect bus: %w", err) - } - b.conn = c - - if err := b.enumerateLinks(); err != nil { - c.Close() - return fmt.Errorf("enumerate links: %w", err) - } - - if err := b.updateState(); err != nil { - c.Close() - return fmt.Errorf("update initial state: %w", err) - } - - return nil -} - -func (b *SystemdNetworkdBackend) Close() { - close(b.stopChan) - b.StopMonitoring() - - if b.conn != nil { - b.conn.Close() - } -} - -func (b *SystemdNetworkdBackend) enumerateLinks() error { - obj := b.conn.Object(networkdBusName, b.managerPath) - - var links []struct { - Ifindex int32 - Name string - Path dbus.ObjectPath - } - err := obj.Call(networkdManagerIface+".ListLinks", 0).Store(&links) - if err != nil { - return fmt.Errorf("ListLinks: %w", err) - } - - b.linksMutex.Lock() - defer b.linksMutex.Unlock() - - for _, l := range links { - b.links[l.Name] = &linkInfo{ - ifindex: l.Ifindex, - name: l.Name, - path: l.Path, - } - log.Debugf("networkd: enumerated link %s (ifindex=%d, path=%s)", l.Name, l.Ifindex, l.Path) - } - - return nil -} - -func (b *SystemdNetworkdBackend) updateState() error { - b.linksMutex.RLock() - defer b.linksMutex.RUnlock() - - var wiredIface *linkInfo - var wifiIface *linkInfo - - for name, link := range b.links { - if b.isVirtualInterface(name) { - continue - } - - linkObj := b.conn.Object(networkdBusName, link.path) - opStateVar, err := linkObj.GetProperty(networkdLinkIface + ".OperationalState") - if err == nil { - if opState, ok := opStateVar.Value().(string); ok { - link.opState = opState - } - } - - if strings.HasPrefix(name, "wlan") || strings.HasPrefix(name, "wlp") { - if wifiIface == nil || link.opState == "routable" || link.opState == "carrier" { - wifiIface = link - } - } else if !b.isVirtualInterface(name) { - if wiredIface == nil || link.opState == "routable" || link.opState == "carrier" { - wiredIface = link - } - } - } - - var wiredConns []WiredConnection - for name, link := range b.links { - if b.isVirtualInterface(name) || strings.HasPrefix(name, "wlan") || strings.HasPrefix(name, "wlp") { - continue - } - - active := link.opState == "routable" || link.opState == "carrier" - wiredConns = append(wiredConns, WiredConnection{ - Path: link.path, - ID: name, - UUID: "wired:" + name, - Type: "ethernet", - IsActive: active, - }) - } - - b.stateMutex.Lock() - defer b.stateMutex.Unlock() - - b.state.NetworkStatus = StatusDisconnected - b.state.EthernetConnected = false - b.state.EthernetIP = "" - b.state.WiFiConnected = false - b.state.WiFiIP = "" - b.state.WiredConnections = wiredConns - - if wiredIface != nil { - b.state.EthernetDevice = wiredIface.name - log.Debugf("networkd: wired interface %s opState=%s", wiredIface.name, wiredIface.opState) - if wiredIface.opState == "routable" || wiredIface.opState == "carrier" { - b.state.EthernetConnected = true - b.state.NetworkStatus = StatusEthernet - - if addrs := b.getAddresses(wiredIface.name); len(addrs) > 0 { - b.state.EthernetIP = addrs[0] - log.Debugf("networkd: ethernet IP %s on %s", addrs[0], wiredIface.name) - } - } - } - - if wifiIface != nil { - b.state.WiFiDevice = wifiIface.name - log.Debugf("networkd: wifi interface %s opState=%s", wifiIface.name, wifiIface.opState) - if wifiIface.opState == "routable" || wifiIface.opState == "carrier" { - b.state.WiFiConnected = true - - if addrs := b.getAddresses(wifiIface.name); len(addrs) > 0 { - b.state.WiFiIP = addrs[0] - log.Debugf("networkd: wifi IP %s on %s", addrs[0], wifiIface.name) - if b.state.NetworkStatus == StatusDisconnected { - b.state.NetworkStatus = StatusWiFi - } - } - } - } - - return nil -} - -func (b *SystemdNetworkdBackend) isVirtualInterface(name string) bool { - virtualPrefixes := []string{ - "lo", "docker", "veth", "virbr", "br-", "vnet", "tun", "tap", - "vboxnet", "vmnet", "kube", "cni", "flannel", "cali", - } - for _, prefix := range virtualPrefixes { - if strings.HasPrefix(name, prefix) { - return true - } - } - return false -} - -func (b *SystemdNetworkdBackend) getAddresses(ifname string) []string { - iface, err := net.InterfaceByName(ifname) - if err != nil { - return nil - } - - addrs, err := iface.Addrs() - if err != nil { - return nil - } - - var result []string - for _, addr := range addrs { - if ipnet, ok := addr.(*net.IPNet); ok { - if ipv4 := ipnet.IP.To4(); ipv4 != nil { - result = append(result, ipv4.String()) - } - } - } - return result -} - -func (b *SystemdNetworkdBackend) GetCurrentState() (*BackendState, error) { - b.stateMutex.RLock() - defer b.stateMutex.RUnlock() - s := *b.state - return &s, nil -} - -func (b *SystemdNetworkdBackend) GetPromptBroker() PromptBroker { - return nil -} - -func (b *SystemdNetworkdBackend) SetPromptBroker(broker PromptBroker) error { - return nil -} - -func (b *SystemdNetworkdBackend) SubmitCredentials(token string, secrets map[string]string, save bool) error { - return fmt.Errorf("credentials not needed by networkd backend") -} - -func (b *SystemdNetworkdBackend) CancelCredentials(token string) error { - return fmt.Errorf("credentials not needed by networkd backend") -} - -func (b *SystemdNetworkdBackend) EnsureDhcpUp(ifname string) error { - b.linksMutex.RLock() - link, exists := b.links[ifname] - b.linksMutex.RUnlock() - - if !exists { - return fmt.Errorf("interface %s not found", ifname) - } - - linkObj := b.conn.Object(networkdBusName, link.path) - return linkObj.Call(networkdLinkIface+".Reconfigure", 0).Err -} diff --git a/nix/inputs/dms-cli/internal/server/network/backend_networkd_ethernet.go b/nix/inputs/dms-cli/internal/server/network/backend_networkd_ethernet.go deleted file mode 100644 index d0c9b94..0000000 --- a/nix/inputs/dms-cli/internal/server/network/backend_networkd_ethernet.go +++ /dev/null @@ -1,110 +0,0 @@ -package network - -import ( - "fmt" - "net" - "strings" -) - -func (b *SystemdNetworkdBackend) GetWiredConnections() ([]WiredConnection, error) { - b.linksMutex.RLock() - defer b.linksMutex.RUnlock() - - var conns []WiredConnection - for name, link := range b.links { - if b.isVirtualInterface(name) || strings.HasPrefix(name, "wlan") || strings.HasPrefix(name, "wlp") { - continue - } - - active := link.opState == "routable" || link.opState == "carrier" - conns = append(conns, WiredConnection{ - Path: link.path, - ID: name, - UUID: "wired:" + name, - Type: "ethernet", - IsActive: active, - }) - } - - return conns, nil -} - -func (b *SystemdNetworkdBackend) GetWiredNetworkDetails(id string) (*WiredNetworkInfoResponse, error) { - ifname := strings.TrimPrefix(id, "wired:") - - b.linksMutex.RLock() - _, exists := b.links[ifname] - b.linksMutex.RUnlock() - - if !exists { - return nil, fmt.Errorf("interface %s not found", ifname) - } - - iface, err := net.InterfaceByName(ifname) - if err != nil { - return nil, fmt.Errorf("get interface: %w", err) - } - - addrs, _ := iface.Addrs() - var ipv4s, ipv6s []string - for _, addr := range addrs { - if ipnet, ok := addr.(*net.IPNet); ok { - if ipv4 := ipnet.IP.To4(); ipv4 != nil { - ipv4s = append(ipv4s, ipnet.String()) - } else if ipv6 := ipnet.IP.To16(); ipv6 != nil { - ipv6s = append(ipv6s, ipnet.String()) - } - } - } - - return &WiredNetworkInfoResponse{ - UUID: id, - IFace: ifname, - HwAddr: iface.HardwareAddr.String(), - IPv4: WiredIPConfig{ - IPs: ipv4s, - }, - IPv6: WiredIPConfig{ - IPs: ipv6s, - }, - }, nil -} - -func (b *SystemdNetworkdBackend) ConnectEthernet() error { - b.linksMutex.RLock() - var primaryWired *linkInfo - for name, l := range b.links { - if strings.HasPrefix(name, "lo") || strings.HasPrefix(name, "wlan") || strings.HasPrefix(name, "wlp") { - continue - } - primaryWired = l - break - } - b.linksMutex.RUnlock() - - if primaryWired == nil { - return fmt.Errorf("no wired interface found") - } - - linkObj := b.conn.Object(networkdBusName, primaryWired.path) - return linkObj.Call(networkdLinkIface+".Reconfigure", 0).Err -} - -func (b *SystemdNetworkdBackend) DisconnectEthernet() error { - return fmt.Errorf("not supported by networkd backend") -} - -func (b *SystemdNetworkdBackend) ActivateWiredConnection(id string) error { - ifname := strings.TrimPrefix(id, "wired:") - - b.linksMutex.RLock() - link, exists := b.links[ifname] - b.linksMutex.RUnlock() - - if !exists { - return fmt.Errorf("interface %s not found", ifname) - } - - linkObj := b.conn.Object(networkdBusName, link.path) - return linkObj.Call(networkdLinkIface+".Reconfigure", 0).Err -} diff --git a/nix/inputs/dms-cli/internal/server/network/backend_networkd_signals.go b/nix/inputs/dms-cli/internal/server/network/backend_networkd_signals.go deleted file mode 100644 index a022ee6..0000000 --- a/nix/inputs/dms-cli/internal/server/network/backend_networkd_signals.go +++ /dev/null @@ -1,68 +0,0 @@ -package network - -import ( - "fmt" - - "github.com/AvengeMedia/danklinux/internal/log" - "github.com/godbus/dbus/v5" -) - -func (b *SystemdNetworkdBackend) StartMonitoring(onStateChange func()) error { - b.onStateChange = onStateChange - - b.signals = make(chan *dbus.Signal, 64) - b.conn.Signal(b.signals) - - matchRules := []string{ - "type='signal',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged',path_namespace='/org/freedesktop/network1'", - "type='signal',interface='org.freedesktop.network1.Manager'", - } - - for _, rule := range matchRules { - if err := b.conn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, rule).Err; err != nil { - return fmt.Errorf("add match %q: %w", rule, err) - } - } - - b.sigWG.Add(1) - go b.signalLoop() - - return nil -} - -func (b *SystemdNetworkdBackend) StopMonitoring() { - b.sigWG.Wait() -} - -func (b *SystemdNetworkdBackend) signalLoop() { - defer b.sigWG.Done() - - for { - select { - case <-b.stopChan: - return - case sig := <-b.signals: - if sig == nil { - continue - } - - if sig.Name == "org.freedesktop.DBus.Properties.PropertiesChanged" { - if len(sig.Body) < 2 { - continue - } - iface, ok := sig.Body[0].(string) - if !ok || iface != networkdLinkIface { - continue - } - - b.enumerateLinks() - if err := b.updateState(); err != nil { - log.Warnf("networkd state update failed: %v", err) - } - if b.onStateChange != nil { - b.onStateChange() - } - } - } - } -} diff --git a/nix/inputs/dms-cli/internal/server/network/backend_networkd_test.go b/nix/inputs/dms-cli/internal/server/network/backend_networkd_test.go deleted file mode 100644 index 383c775..0000000 --- a/nix/inputs/dms-cli/internal/server/network/backend_networkd_test.go +++ /dev/null @@ -1,125 +0,0 @@ -package network - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestSystemdNetworkdBackend_New(t *testing.T) { - backend, err := NewSystemdNetworkdBackend() - assert.NoError(t, err) - assert.NotNil(t, backend) - assert.Equal(t, "networkd", backend.state.Backend) - assert.NotNil(t, backend.links) - assert.NotNil(t, backend.stopChan) -} - -func TestSystemdNetworkdBackend_GetCurrentState(t *testing.T) { - backend, _ := NewSystemdNetworkdBackend() - backend.state.NetworkStatus = StatusEthernet - backend.state.EthernetConnected = true - backend.state.EthernetIP = "192.168.1.100" - - state, err := backend.GetCurrentState() - assert.NoError(t, err) - assert.NotNil(t, state) - assert.Equal(t, StatusEthernet, state.NetworkStatus) - assert.True(t, state.EthernetConnected) - assert.Equal(t, "192.168.1.100", state.EthernetIP) -} - -func TestSystemdNetworkdBackend_WiFiNotSupported(t *testing.T) { - backend, _ := NewSystemdNetworkdBackend() - - err := backend.ScanWiFi() - assert.Error(t, err) - assert.Contains(t, err.Error(), "not supported") - - req := ConnectionRequest{SSID: "test"} - err = backend.ConnectWiFi(req) - assert.Error(t, err) - assert.Contains(t, err.Error(), "not supported") - - err = backend.DisconnectWiFi() - assert.Error(t, err) - assert.Contains(t, err.Error(), "not supported") - - err = backend.ForgetWiFiNetwork("test") - assert.Error(t, err) - assert.Contains(t, err.Error(), "not supported") - - _, err = backend.GetWiFiNetworkDetails("test") - assert.Error(t, err) - assert.Contains(t, err.Error(), "not supported") -} - -func TestSystemdNetworkdBackend_VPNNotSupported(t *testing.T) { - backend, _ := NewSystemdNetworkdBackend() - - profiles, err := backend.ListVPNProfiles() - assert.NoError(t, err) - assert.Empty(t, profiles) - - active, err := backend.ListActiveVPN() - assert.NoError(t, err) - assert.Empty(t, active) - - err = backend.ConnectVPN("test", false) - assert.Error(t, err) - assert.Contains(t, err.Error(), "not supported") - - err = backend.DisconnectVPN("test") - assert.Error(t, err) - assert.Contains(t, err.Error(), "not supported") - - err = backend.DisconnectAllVPN() - assert.Error(t, err) - assert.Contains(t, err.Error(), "not supported") - - err = backend.ClearVPNCredentials("test") - assert.Error(t, err) - assert.Contains(t, err.Error(), "not supported") -} - -func TestSystemdNetworkdBackend_PromptBroker(t *testing.T) { - backend, _ := NewSystemdNetworkdBackend() - - broker := backend.GetPromptBroker() - assert.Nil(t, broker) - - err := backend.SetPromptBroker(nil) - assert.NoError(t, err) - - err = backend.SubmitCredentials("token", nil, false) - assert.Error(t, err) - assert.Contains(t, err.Error(), "not needed") - - err = backend.CancelCredentials("token") - assert.Error(t, err) - assert.Contains(t, err.Error(), "not needed") -} - -func TestSystemdNetworkdBackend_GetWiFiEnabled(t *testing.T) { - backend, _ := NewSystemdNetworkdBackend() - - enabled, err := backend.GetWiFiEnabled() - assert.NoError(t, err) - assert.True(t, enabled) -} - -func TestSystemdNetworkdBackend_SetWiFiEnabled(t *testing.T) { - backend, _ := NewSystemdNetworkdBackend() - - err := backend.SetWiFiEnabled(false) - assert.Error(t, err) - assert.Contains(t, err.Error(), "not supported") -} - -func TestSystemdNetworkdBackend_DisconnectEthernet(t *testing.T) { - backend, _ := NewSystemdNetworkdBackend() - - err := backend.DisconnectEthernet() - assert.Error(t, err) - assert.Contains(t, err.Error(), "not supported") -} diff --git a/nix/inputs/dms-cli/internal/server/network/backend_networkd_unimplemented.go b/nix/inputs/dms-cli/internal/server/network/backend_networkd_unimplemented.go deleted file mode 100644 index 1f44752..0000000 --- a/nix/inputs/dms-cli/internal/server/network/backend_networkd_unimplemented.go +++ /dev/null @@ -1,59 +0,0 @@ -package network - -import "fmt" - -func (b *SystemdNetworkdBackend) GetWiFiEnabled() (bool, error) { - return true, nil -} - -func (b *SystemdNetworkdBackend) SetWiFiEnabled(enabled bool) error { - return fmt.Errorf("WiFi control not supported by networkd backend") -} - -func (b *SystemdNetworkdBackend) ScanWiFi() error { - return fmt.Errorf("WiFi scan not supported by networkd backend") -} - -func (b *SystemdNetworkdBackend) GetWiFiNetworkDetails(ssid string) (*NetworkInfoResponse, error) { - return nil, fmt.Errorf("WiFi details not supported by networkd backend") -} - -func (b *SystemdNetworkdBackend) ConnectWiFi(req ConnectionRequest) error { - return fmt.Errorf("WiFi connect not supported by networkd backend") -} - -func (b *SystemdNetworkdBackend) DisconnectWiFi() error { - return fmt.Errorf("WiFi disconnect not supported by networkd backend") -} - -func (b *SystemdNetworkdBackend) ForgetWiFiNetwork(ssid string) error { - return fmt.Errorf("WiFi forget not supported by networkd backend") -} - -func (b *SystemdNetworkdBackend) ListVPNProfiles() ([]VPNProfile, error) { - return []VPNProfile{}, nil -} - -func (b *SystemdNetworkdBackend) ListActiveVPN() ([]VPNActive, error) { - return []VPNActive{}, nil -} - -func (b *SystemdNetworkdBackend) ConnectVPN(uuidOrName string, singleActive bool) error { - return fmt.Errorf("VPN not supported by networkd backend") -} - -func (b *SystemdNetworkdBackend) DisconnectVPN(uuidOrName string) error { - return fmt.Errorf("VPN not supported by networkd backend") -} - -func (b *SystemdNetworkdBackend) DisconnectAllVPN() error { - return fmt.Errorf("VPN not supported by networkd backend") -} - -func (b *SystemdNetworkdBackend) ClearVPNCredentials(uuidOrName string) error { - return fmt.Errorf("VPN not supported by networkd backend") -} - -func (b *SystemdNetworkdBackend) SetWiFiAutoconnect(ssid string, autoconnect bool) error { - return fmt.Errorf("WiFi autoconnect not supported by networkd backend") -} diff --git a/nix/inputs/dms-cli/internal/server/network/backend_networkmanager.go b/nix/inputs/dms-cli/internal/server/network/backend_networkmanager.go deleted file mode 100644 index 2119408..0000000 --- a/nix/inputs/dms-cli/internal/server/network/backend_networkmanager.go +++ /dev/null @@ -1,307 +0,0 @@ -package network - -import ( - "fmt" - "sync" - - "github.com/AvengeMedia/danklinux/internal/log" - "github.com/Wifx/gonetworkmanager/v2" - "github.com/godbus/dbus/v5" -) - -const ( - dbusNMPath = "/org/freedesktop/NetworkManager" - dbusNMInterface = "org.freedesktop.NetworkManager" - dbusNMDeviceInterface = "org.freedesktop.NetworkManager.Device" - dbusNMWirelessInterface = "org.freedesktop.NetworkManager.Device.Wireless" - dbusNMAccessPointInterface = "org.freedesktop.NetworkManager.AccessPoint" - dbusPropsInterface = "org.freedesktop.DBus.Properties" - - NmDeviceStateReasonWrongPassword = 8 - NmDeviceStateReasonSupplicantTimeout = 24 - NmDeviceStateReasonSupplicantFailed = 25 - NmDeviceStateReasonSecretsRequired = 7 - NmDeviceStateReasonNoSecrets = 6 - NmDeviceStateReasonNoSsid = 10 - NmDeviceStateReasonDhcpClientFailed = 14 - NmDeviceStateReasonIpConfigUnavailable = 18 - NmDeviceStateReasonSupplicantDisconnect = 23 - NmDeviceStateReasonCarrier = 40 - NmDeviceStateReasonNewActivation = 60 -) - -type NetworkManagerBackend struct { - nmConn interface{} - ethernetDevice interface{} - wifiDevice interface{} - settings interface{} - wifiDev interface{} - - dbusConn *dbus.Conn - signals chan *dbus.Signal - sigWG sync.WaitGroup - stopChan chan struct{} - - secretAgent *SecretAgent - promptBroker PromptBroker - - state *BackendState - stateMutex sync.RWMutex - - lastFailedSSID string - lastFailedTime int64 - failedMutex sync.RWMutex - - onStateChange func() -} - -func NewNetworkManagerBackend(nmConn ...gonetworkmanager.NetworkManager) (*NetworkManagerBackend, error) { - var nm gonetworkmanager.NetworkManager - var err error - - if len(nmConn) > 0 && nmConn[0] != nil { - // Use injected connection (for testing) - nm = nmConn[0] - } else { - // Create real connection - nm, err = gonetworkmanager.NewNetworkManager() - if err != nil { - return nil, fmt.Errorf("failed to connect to NetworkManager: %w", err) - } - } - - backend := &NetworkManagerBackend{ - nmConn: nm, - stopChan: make(chan struct{}), - state: &BackendState{ - Backend: "networkmanager", - }, - } - - return backend, nil -} - -func (b *NetworkManagerBackend) Initialize() error { - nm := b.nmConn.(gonetworkmanager.NetworkManager) - - if s, err := gonetworkmanager.NewSettings(); err == nil { - b.settings = s - } - - devices, err := nm.GetDevices() - if err != nil { - return fmt.Errorf("failed to get devices: %w", err) - } - - for _, dev := range devices { - devType, err := dev.GetPropertyDeviceType() - if err != nil { - continue - } - - switch devType { - case gonetworkmanager.NmDeviceTypeEthernet: - if managed, _ := dev.GetPropertyManaged(); !managed { - continue - } - b.ethernetDevice = dev - if err := b.updateEthernetState(); err != nil { - continue - } - _, err := b.listEthernetConnections() - if err != nil { - return fmt.Errorf("failed to get wired configurations: %w", err) - } - - case gonetworkmanager.NmDeviceTypeWifi: - b.wifiDevice = dev - if w, err := gonetworkmanager.NewDeviceWireless(dev.GetPath()); err == nil { - b.wifiDev = w - } - wifiEnabled, err := nm.GetPropertyWirelessEnabled() - if err == nil { - b.stateMutex.Lock() - b.state.WiFiEnabled = wifiEnabled - b.stateMutex.Unlock() - } - if err := b.updateWiFiState(); err != nil { - continue - } - if wifiEnabled { - if _, err := b.updateWiFiNetworks(); err != nil { - log.Warnf("Failed to get initial networks: %v", err) - } - } - } - } - - if err := b.updatePrimaryConnection(); err != nil { - return err - } - - if _, err := b.ListVPNProfiles(); err != nil { - log.Warnf("Failed to get initial VPN profiles: %v", err) - } - - if _, err := b.ListActiveVPN(); err != nil { - log.Warnf("Failed to get initial active VPNs: %v", err) - } - - return nil -} - -func (b *NetworkManagerBackend) Close() { - close(b.stopChan) - b.StopMonitoring() - - if b.secretAgent != nil { - b.secretAgent.Close() - } -} - -func (b *NetworkManagerBackend) GetCurrentState() (*BackendState, error) { - b.stateMutex.RLock() - defer b.stateMutex.RUnlock() - - state := *b.state - state.WiFiNetworks = append([]WiFiNetwork(nil), b.state.WiFiNetworks...) - state.WiredConnections = append([]WiredConnection(nil), b.state.WiredConnections...) - state.VPNProfiles = append([]VPNProfile(nil), b.state.VPNProfiles...) - state.VPNActive = append([]VPNActive(nil), b.state.VPNActive...) - - return &state, nil -} - -func (b *NetworkManagerBackend) StartMonitoring(onStateChange func()) error { - b.onStateChange = onStateChange - - if err := b.startSecretAgent(); err != nil { - return fmt.Errorf("failed to start secret agent: %w", err) - } - - if err := b.startSignalPump(); err != nil { - return err - } - - return nil -} - -func (b *NetworkManagerBackend) StopMonitoring() { - b.stopSignalPump() -} - -func (b *NetworkManagerBackend) GetPromptBroker() PromptBroker { - return b.promptBroker -} - -func (b *NetworkManagerBackend) SetPromptBroker(broker PromptBroker) error { - if broker == nil { - return fmt.Errorf("broker cannot be nil") - } - - hadAgent := b.secretAgent != nil - - b.promptBroker = broker - - if b.secretAgent != nil { - b.secretAgent.Close() - b.secretAgent = nil - } - - if hadAgent { - return b.startSecretAgent() - } - - return nil -} - -func (b *NetworkManagerBackend) SubmitCredentials(token string, secrets map[string]string, save bool) error { - if b.promptBroker == nil { - return fmt.Errorf("prompt broker not initialized") - } - - return b.promptBroker.Resolve(token, PromptReply{ - Secrets: secrets, - Save: save, - Cancel: false, - }) -} - -func (b *NetworkManagerBackend) CancelCredentials(token string) error { - if b.promptBroker == nil { - return fmt.Errorf("prompt broker not initialized") - } - - return b.promptBroker.Resolve(token, PromptReply{ - Cancel: true, - }) -} - -func (b *NetworkManagerBackend) ensureWiFiDevice() error { - if b.wifiDev != nil { - return nil - } - - if b.wifiDevice == nil { - return fmt.Errorf("no WiFi device available") - } - - dev := b.wifiDevice.(gonetworkmanager.Device) - wifiDev, err := gonetworkmanager.NewDeviceWireless(dev.GetPath()) - if err != nil { - return fmt.Errorf("failed to get wireless device: %w", err) - } - b.wifiDev = wifiDev - return nil -} - -func (b *NetworkManagerBackend) startSecretAgent() error { - if b.promptBroker == nil { - return fmt.Errorf("prompt broker not set") - } - - agent, err := NewSecretAgent(b.promptBroker, nil, b) - if err != nil { - return err - } - - b.secretAgent = agent - return nil -} - -func (b *NetworkManagerBackend) getActiveConnections() (map[string]bool, error) { - nm := b.nmConn.(gonetworkmanager.NetworkManager) - - activeUUIDs := make(map[string]bool) - - activeConns, err := nm.GetPropertyActiveConnections() - if err != nil { - return activeUUIDs, fmt.Errorf("failed to get active connections: %w", err) - } - - for _, activeConn := range activeConns { - connType, err := activeConn.GetPropertyType() - if err != nil { - continue - } - - if connType != "802-3-ethernet" { - continue - } - - state, err := activeConn.GetPropertyState() - if err != nil { - continue - } - if state < 1 || state > 2 { - continue - } - - uuid, err := activeConn.GetPropertyUUID() - if err != nil { - continue - } - activeUUIDs[uuid] = true - } - return activeUUIDs, nil -} diff --git a/nix/inputs/dms-cli/internal/server/network/backend_networkmanager_ethernet.go b/nix/inputs/dms-cli/internal/server/network/backend_networkmanager_ethernet.go deleted file mode 100644 index 965565e..0000000 --- a/nix/inputs/dms-cli/internal/server/network/backend_networkmanager_ethernet.go +++ /dev/null @@ -1,317 +0,0 @@ -package network - -import ( - "fmt" - "net" - "strconv" - "strings" - - "github.com/AvengeMedia/danklinux/internal/log" - "github.com/Wifx/gonetworkmanager/v2" -) - -func (b *NetworkManagerBackend) GetWiredConnections() ([]WiredConnection, error) { - return b.listEthernetConnections() -} - -func (b *NetworkManagerBackend) GetWiredNetworkDetails(uuid string) (*WiredNetworkInfoResponse, error) { - if b.ethernetDevice == nil { - return nil, fmt.Errorf("no ethernet device available") - } - - dev := b.ethernetDevice.(gonetworkmanager.Device) - - iface, _ := dev.GetPropertyInterface() - driver, _ := dev.GetPropertyDriver() - - hwAddr := "Not available" - var speed uint32 = 0 - wiredDevice, err := gonetworkmanager.NewDeviceWired(dev.GetPath()) - if err == nil { - hwAddr, _ = wiredDevice.GetPropertyHwAddress() - speed, _ = wiredDevice.GetPropertySpeed() - } - var ipv4Config WiredIPConfig - var ipv6Config WiredIPConfig - - activeConn, err := dev.GetPropertyActiveConnection() - if err == nil && activeConn != nil { - ip4Config, err := activeConn.GetPropertyIP4Config() - if err == nil && ip4Config != nil { - var ips []string - addresses, err := ip4Config.GetPropertyAddressData() - if err == nil && len(addresses) > 0 { - for _, addr := range addresses { - ips = append(ips, fmt.Sprintf("%s/%s", addr.Address, strconv.Itoa(int(addr.Prefix)))) - } - } - - gateway, _ := ip4Config.GetPropertyGateway() - dnsAddrs := "" - dns, err := ip4Config.GetPropertyNameserverData() - if err == nil && len(dns) > 0 { - for _, d := range dns { - if len(dnsAddrs) > 0 { - dnsAddrs = strings.Join([]string{dnsAddrs, d.Address}, "; ") - } else { - dnsAddrs = d.Address - } - } - } - - ipv4Config = WiredIPConfig{ - IPs: ips, - Gateway: gateway, - DNS: dnsAddrs, - } - } - - ip6Config, err := activeConn.GetPropertyIP6Config() - if err == nil && ip6Config != nil { - var ips []string - addresses, err := ip6Config.GetPropertyAddressData() - if err == nil && len(addresses) > 0 { - for _, addr := range addresses { - ips = append(ips, fmt.Sprintf("%s/%s", addr.Address, strconv.Itoa(int(addr.Prefix)))) - } - } - - gateway, _ := ip6Config.GetPropertyGateway() - dnsAddrs := "" - dns, err := ip6Config.GetPropertyNameservers() - if err == nil && len(dns) > 0 { - for _, d := range dns { - if len(d) == 16 { - ip := net.IP(d) - if len(dnsAddrs) > 0 { - dnsAddrs = strings.Join([]string{dnsAddrs, ip.String()}, "; ") - } else { - dnsAddrs = ip.String() - } - } - } - } - - ipv6Config = WiredIPConfig{ - IPs: ips, - Gateway: gateway, - DNS: dnsAddrs, - } - } - } - - return &WiredNetworkInfoResponse{ - UUID: uuid, - IFace: iface, - Driver: driver, - HwAddr: hwAddr, - Speed: strconv.Itoa(int(speed)), - IPv4: ipv4Config, - IPv6: ipv6Config, - }, nil -} - -func (b *NetworkManagerBackend) ConnectEthernet() error { - if b.ethernetDevice == nil { - return fmt.Errorf("no ethernet device available") - } - - nm := b.nmConn.(gonetworkmanager.NetworkManager) - dev := b.ethernetDevice.(gonetworkmanager.Device) - - settingsMgr, err := gonetworkmanager.NewSettings() - if err != nil { - return fmt.Errorf("failed to get settings: %w", err) - } - - connections, err := settingsMgr.ListConnections() - if err != nil { - return fmt.Errorf("failed to get connections: %w", err) - } - - for _, conn := range connections { - connSettings, err := conn.GetSettings() - if err != nil { - continue - } - - if connMeta, ok := connSettings["connection"]; ok { - if connType, ok := connMeta["type"].(string); ok && connType == "802-3-ethernet" { - _, err := nm.ActivateConnection(conn, dev, nil) - if err != nil { - return fmt.Errorf("failed to activate ethernet: %w", err) - } - - b.updateEthernetState() - b.listEthernetConnections() - b.updatePrimaryConnection() - - if b.onStateChange != nil { - b.onStateChange() - } - - return nil - } - } - } - - settings := make(map[string]map[string]interface{}) - settings["connection"] = map[string]interface{}{ - "id": "Wired connection", - "type": "802-3-ethernet", - } - - _, err = nm.AddAndActivateConnection(settings, dev) - if err != nil { - return fmt.Errorf("failed to create and activate ethernet: %w", err) - } - - b.updateEthernetState() - b.listEthernetConnections() - b.updatePrimaryConnection() - - if b.onStateChange != nil { - b.onStateChange() - } - - return nil -} - -func (b *NetworkManagerBackend) DisconnectEthernet() error { - if b.ethernetDevice == nil { - return fmt.Errorf("no ethernet device available") - } - - dev := b.ethernetDevice.(gonetworkmanager.Device) - - err := dev.Disconnect() - if err != nil { - return fmt.Errorf("failed to disconnect: %w", err) - } - - b.updateEthernetState() - b.listEthernetConnections() - b.updatePrimaryConnection() - - if b.onStateChange != nil { - b.onStateChange() - } - - return nil -} - -func (b *NetworkManagerBackend) ActivateWiredConnection(uuid string) error { - if b.ethernetDevice == nil { - return fmt.Errorf("no ethernet device available") - } - - nm := b.nmConn.(gonetworkmanager.NetworkManager) - dev := b.ethernetDevice.(gonetworkmanager.Device) - - settingsMgr, err := gonetworkmanager.NewSettings() - if err != nil { - return fmt.Errorf("failed to get settings: %w", err) - } - - connections, err := settingsMgr.ListConnections() - if err != nil { - return fmt.Errorf("failed to get connections: %w", err) - } - - var targetConnection gonetworkmanager.Connection - for _, conn := range connections { - settings, err := conn.GetSettings() - if err != nil { - continue - } - - if connectionSettings, ok := settings["connection"]; ok { - if connUUID, ok := connectionSettings["uuid"].(string); ok && connUUID == uuid { - targetConnection = conn - break - } - } - } - - if targetConnection == nil { - return fmt.Errorf("connection with UUID %s not found", uuid) - } - - _, err = nm.ActivateConnection(targetConnection, dev, nil) - if err != nil { - return fmt.Errorf("error activation connection: %w", err) - } - - b.updateEthernetState() - b.listEthernetConnections() - b.updatePrimaryConnection() - - if b.onStateChange != nil { - b.onStateChange() - } - - return nil -} - -func (b *NetworkManagerBackend) listEthernetConnections() ([]WiredConnection, error) { - if b.ethernetDevice == nil { - return nil, fmt.Errorf("no ethernet device available") - } - - s := b.settings - if s == nil { - s, err := gonetworkmanager.NewSettings() - if err != nil { - return nil, fmt.Errorf("failed to get settings: %w", err) - } - b.settings = s - } - - settingsMgr := s.(gonetworkmanager.Settings) - connections, err := settingsMgr.ListConnections() - if err != nil { - return nil, fmt.Errorf("failed to get connections: %w", err) - } - - wiredConfigs := make([]WiredConnection, 0) - activeUUIDs, err := b.getActiveConnections() - - if err != nil { - return nil, fmt.Errorf("failed to get active wired connections: %w", err) - } - - currentUuid := "" - for _, connection := range connections { - path := connection.GetPath() - settings, err := connection.GetSettings() - if err != nil { - log.Errorf("unable to get settings for %s: %v", path, err) - continue - } - - connectionSettings := settings["connection"] - connType, _ := connectionSettings["type"].(string) - connID, _ := connectionSettings["id"].(string) - connUUID, _ := connectionSettings["uuid"].(string) - - if connType == "802-3-ethernet" { - wiredConfigs = append(wiredConfigs, WiredConnection{ - Path: path, - ID: connID, - UUID: connUUID, - Type: connType, - IsActive: activeUUIDs[connUUID], - }) - if activeUUIDs[connUUID] { - currentUuid = connUUID - } - } - } - - b.stateMutex.Lock() - b.state.EthernetConnectionUuid = currentUuid - b.state.WiredConnections = wiredConfigs - b.stateMutex.Unlock() - - return wiredConfigs, nil -} diff --git a/nix/inputs/dms-cli/internal/server/network/backend_networkmanager_ethernet_test.go b/nix/inputs/dms-cli/internal/server/network/backend_networkmanager_ethernet_test.go deleted file mode 100644 index 601306a..0000000 --- a/nix/inputs/dms-cli/internal/server/network/backend_networkmanager_ethernet_test.go +++ /dev/null @@ -1,94 +0,0 @@ -package network - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestNetworkManagerBackend_GetWiredConnections_NoDevice(t *testing.T) { - backend, err := NewNetworkManagerBackend() - if err != nil { - t.Skipf("NetworkManager not available: %v", err) - } - - backend.ethernetDevice = nil - _, err = backend.GetWiredConnections() - assert.Error(t, err) - assert.Contains(t, err.Error(), "no ethernet device available") -} - -func TestNetworkManagerBackend_GetWiredNetworkDetails_NoDevice(t *testing.T) { - backend, err := NewNetworkManagerBackend() - if err != nil { - t.Skipf("NetworkManager not available: %v", err) - } - - backend.ethernetDevice = nil - _, err = backend.GetWiredNetworkDetails("test-uuid") - assert.Error(t, err) - assert.Contains(t, err.Error(), "no ethernet device available") -} - -func TestNetworkManagerBackend_ConnectEthernet_NoDevice(t *testing.T) { - backend, err := NewNetworkManagerBackend() - if err != nil { - t.Skipf("NetworkManager not available: %v", err) - } - - backend.ethernetDevice = nil - err = backend.ConnectEthernet() - assert.Error(t, err) - assert.Contains(t, err.Error(), "no ethernet device available") -} - -func TestNetworkManagerBackend_DisconnectEthernet_NoDevice(t *testing.T) { - backend, err := NewNetworkManagerBackend() - if err != nil { - t.Skipf("NetworkManager not available: %v", err) - } - - backend.ethernetDevice = nil - err = backend.DisconnectEthernet() - assert.Error(t, err) - assert.Contains(t, err.Error(), "no ethernet device available") -} - -func TestNetworkManagerBackend_ActivateWiredConnection_NoDevice(t *testing.T) { - backend, err := NewNetworkManagerBackend() - if err != nil { - t.Skipf("NetworkManager not available: %v", err) - } - - backend.ethernetDevice = nil - err = backend.ActivateWiredConnection("test-uuid") - assert.Error(t, err) - assert.Contains(t, err.Error(), "no ethernet device available") -} - -func TestNetworkManagerBackend_ActivateWiredConnection_NotFound(t *testing.T) { - backend, err := NewNetworkManagerBackend() - if err != nil { - t.Skipf("NetworkManager not available: %v", err) - } - - if backend.ethernetDevice == nil { - t.Skip("No ethernet device available") - } - - err = backend.ActivateWiredConnection("non-existent-uuid-12345") - assert.Error(t, err) - assert.Contains(t, err.Error(), "not found") -} - -func TestNetworkManagerBackend_ListEthernetConnections_NoDevice(t *testing.T) { - backend, err := NewNetworkManagerBackend() - if err != nil { - t.Skipf("NetworkManager not available: %v", err) - } - - backend.ethernetDevice = nil - _, err = backend.listEthernetConnections() - assert.Error(t, err) - assert.Contains(t, err.Error(), "no ethernet device available") -} diff --git a/nix/inputs/dms-cli/internal/server/network/backend_networkmanager_signals.go b/nix/inputs/dms-cli/internal/server/network/backend_networkmanager_signals.go deleted file mode 100644 index 153a9d8..0000000 --- a/nix/inputs/dms-cli/internal/server/network/backend_networkmanager_signals.go +++ /dev/null @@ -1,321 +0,0 @@ -package network - -import ( - "github.com/Wifx/gonetworkmanager/v2" - "github.com/godbus/dbus/v5" -) - -func (b *NetworkManagerBackend) startSignalPump() error { - conn, err := dbus.ConnectSystemBus() - if err != nil { - return err - } - b.dbusConn = conn - - signals := make(chan *dbus.Signal, 256) - b.signals = signals - conn.Signal(signals) - - if err := conn.AddMatchSignal( - dbus.WithMatchObjectPath(dbus.ObjectPath(dbusNMPath)), - dbus.WithMatchInterface(dbusPropsInterface), - dbus.WithMatchMember("PropertiesChanged"), - ); err != nil { - conn.RemoveSignal(signals) - conn.Close() - return err - } - - if err := conn.AddMatchSignal( - dbus.WithMatchObjectPath(dbus.ObjectPath("/org/freedesktop/NetworkManager/Settings")), - dbus.WithMatchInterface("org.freedesktop.NetworkManager.Settings"), - dbus.WithMatchMember("NewConnection"), - ); err != nil { - conn.RemoveMatchSignal( - dbus.WithMatchObjectPath(dbus.ObjectPath(dbusNMPath)), - dbus.WithMatchInterface(dbusPropsInterface), - dbus.WithMatchMember("PropertiesChanged"), - ) - conn.RemoveSignal(signals) - conn.Close() - return err - } - - if err := conn.AddMatchSignal( - dbus.WithMatchObjectPath(dbus.ObjectPath("/org/freedesktop/NetworkManager/Settings")), - dbus.WithMatchInterface("org.freedesktop.NetworkManager.Settings"), - dbus.WithMatchMember("ConnectionRemoved"), - ); err != nil { - conn.RemoveMatchSignal( - dbus.WithMatchObjectPath(dbus.ObjectPath(dbusNMPath)), - dbus.WithMatchInterface(dbusPropsInterface), - dbus.WithMatchMember("PropertiesChanged"), - ) - conn.RemoveMatchSignal( - dbus.WithMatchObjectPath(dbus.ObjectPath("/org/freedesktop/NetworkManager/Settings")), - dbus.WithMatchInterface("org.freedesktop.NetworkManager.Settings"), - dbus.WithMatchMember("NewConnection"), - ) - conn.RemoveSignal(signals) - conn.Close() - return err - } - - if b.wifiDevice != nil { - dev := b.wifiDevice.(gonetworkmanager.Device) - if err := conn.AddMatchSignal( - dbus.WithMatchObjectPath(dbus.ObjectPath(dev.GetPath())), - dbus.WithMatchInterface(dbusPropsInterface), - dbus.WithMatchMember("PropertiesChanged"), - ); err != nil { - conn.RemoveMatchSignal( - dbus.WithMatchObjectPath(dbus.ObjectPath(dbusNMPath)), - dbus.WithMatchInterface(dbusPropsInterface), - dbus.WithMatchMember("PropertiesChanged"), - ) - conn.RemoveSignal(signals) - conn.Close() - return err - } - } - - if b.ethernetDevice != nil { - dev := b.ethernetDevice.(gonetworkmanager.Device) - if err := conn.AddMatchSignal( - dbus.WithMatchObjectPath(dbus.ObjectPath(dev.GetPath())), - dbus.WithMatchInterface(dbusPropsInterface), - dbus.WithMatchMember("PropertiesChanged"), - ); err != nil { - conn.RemoveMatchSignal( - dbus.WithMatchObjectPath(dbus.ObjectPath(dbusNMPath)), - dbus.WithMatchInterface(dbusPropsInterface), - dbus.WithMatchMember("PropertiesChanged"), - ) - if b.wifiDevice != nil { - dev := b.wifiDevice.(gonetworkmanager.Device) - conn.RemoveMatchSignal( - dbus.WithMatchObjectPath(dbus.ObjectPath(dev.GetPath())), - dbus.WithMatchInterface(dbusPropsInterface), - dbus.WithMatchMember("PropertiesChanged"), - ) - } - conn.RemoveSignal(signals) - conn.Close() - return err - } - } - - b.sigWG.Add(1) - go func() { - defer b.sigWG.Done() - for { - select { - case <-b.stopChan: - return - case sig, ok := <-signals: - if !ok { - return - } - if sig == nil { - continue - } - b.handleDBusSignal(sig) - } - } - }() - return nil -} - -func (b *NetworkManagerBackend) stopSignalPump() { - if b.dbusConn == nil { - return - } - - b.dbusConn.RemoveMatchSignal( - dbus.WithMatchObjectPath(dbus.ObjectPath(dbusNMPath)), - dbus.WithMatchInterface(dbusPropsInterface), - dbus.WithMatchMember("PropertiesChanged"), - ) - - if b.wifiDevice != nil { - dev := b.wifiDevice.(gonetworkmanager.Device) - b.dbusConn.RemoveMatchSignal( - dbus.WithMatchObjectPath(dbus.ObjectPath(dev.GetPath())), - dbus.WithMatchInterface(dbusPropsInterface), - dbus.WithMatchMember("PropertiesChanged"), - ) - } - - if b.ethernetDevice != nil { - dev := b.ethernetDevice.(gonetworkmanager.Device) - b.dbusConn.RemoveMatchSignal( - dbus.WithMatchObjectPath(dbus.ObjectPath(dev.GetPath())), - dbus.WithMatchInterface(dbusPropsInterface), - dbus.WithMatchMember("PropertiesChanged"), - ) - } - - if b.signals != nil { - b.dbusConn.RemoveSignal(b.signals) - close(b.signals) - } - - b.sigWG.Wait() - - b.dbusConn.Close() -} - -func (b *NetworkManagerBackend) handleDBusSignal(sig *dbus.Signal) { - if sig.Name == "org.freedesktop.NetworkManager.Settings.NewConnection" || - sig.Name == "org.freedesktop.NetworkManager.Settings.ConnectionRemoved" { - b.ListVPNProfiles() - if b.onStateChange != nil { - b.onStateChange() - } - return - } - - if len(sig.Body) < 2 { - return - } - - iface, ok := sig.Body[0].(string) - if !ok { - return - } - - changes, ok := sig.Body[1].(map[string]dbus.Variant) - if !ok { - return - } - - switch iface { - case dbusNMInterface: - b.handleNetworkManagerChange(changes) - - case dbusNMDeviceInterface: - b.handleDeviceChange(changes) - - case dbusNMWirelessInterface: - b.handleWiFiChange(changes) - - case dbusNMAccessPointInterface: - b.handleAccessPointChange(changes) - } -} - -func (b *NetworkManagerBackend) handleNetworkManagerChange(changes map[string]dbus.Variant) { - var needsUpdate bool - - for key := range changes { - switch key { - case "PrimaryConnection", "State", "ActiveConnections": - needsUpdate = true - case "WirelessEnabled": - nm := b.nmConn.(gonetworkmanager.NetworkManager) - if enabled, err := nm.GetPropertyWirelessEnabled(); err == nil { - b.stateMutex.Lock() - b.state.WiFiEnabled = enabled - b.stateMutex.Unlock() - needsUpdate = true - } - default: - continue - } - } - - if needsUpdate { - b.updatePrimaryConnection() - if _, exists := changes["State"]; exists { - b.updateEthernetState() - b.updateWiFiState() - } - if _, exists := changes["ActiveConnections"]; exists { - b.updateVPNConnectionState() - b.ListActiveVPN() - } - if b.onStateChange != nil { - b.onStateChange() - } - } -} - -func (b *NetworkManagerBackend) handleDeviceChange(changes map[string]dbus.Variant) { - var needsUpdate bool - var stateChanged bool - - for key := range changes { - switch key { - case "State": - stateChanged = true - needsUpdate = true - case "Ip4Config": - needsUpdate = true - default: - continue - } - } - - if needsUpdate { - b.updateEthernetState() - b.updateWiFiState() - if stateChanged { - b.updatePrimaryConnection() - } - if b.onStateChange != nil { - b.onStateChange() - } - } -} - -func (b *NetworkManagerBackend) handleWiFiChange(changes map[string]dbus.Variant) { - var needsStateUpdate bool - var needsNetworkUpdate bool - - for key := range changes { - switch key { - case "ActiveAccessPoint": - needsStateUpdate = true - needsNetworkUpdate = true - case "AccessPoints": - needsNetworkUpdate = true - default: - continue - } - } - - if needsStateUpdate { - b.updateWiFiState() - } - if needsNetworkUpdate { - b.updateWiFiNetworks() - } - if needsStateUpdate || needsNetworkUpdate { - if b.onStateChange != nil { - b.onStateChange() - } - } -} - -func (b *NetworkManagerBackend) handleAccessPointChange(changes map[string]dbus.Variant) { - _, hasStrength := changes["Strength"] - if !hasStrength { - return - } - - b.stateMutex.RLock() - oldSignal := b.state.WiFiSignal - b.stateMutex.RUnlock() - - b.updateWiFiState() - - b.stateMutex.RLock() - newSignal := b.state.WiFiSignal - b.stateMutex.RUnlock() - - if signalChangeSignificant(oldSignal, newSignal) { - if b.onStateChange != nil { - b.onStateChange() - } - } -} diff --git a/nix/inputs/dms-cli/internal/server/network/backend_networkmanager_signals_test.go b/nix/inputs/dms-cli/internal/server/network/backend_networkmanager_signals_test.go deleted file mode 100644 index aa90a80..0000000 --- a/nix/inputs/dms-cli/internal/server/network/backend_networkmanager_signals_test.go +++ /dev/null @@ -1,240 +0,0 @@ -package network - -import ( - "testing" - - "github.com/godbus/dbus/v5" - "github.com/stretchr/testify/assert" -) - -func TestNetworkManagerBackend_HandleDBusSignal_NewConnection(t *testing.T) { - backend, err := NewNetworkManagerBackend() - if err != nil { - t.Skipf("NetworkManager not available: %v", err) - } - - sig := &dbus.Signal{ - Name: "org.freedesktop.NetworkManager.Settings.NewConnection", - Body: []interface{}{"/org/freedesktop/NetworkManager/Settings/1"}, - } - - assert.NotPanics(t, func() { - backend.handleDBusSignal(sig) - }) -} - -func TestNetworkManagerBackend_HandleDBusSignal_ConnectionRemoved(t *testing.T) { - backend, err := NewNetworkManagerBackend() - if err != nil { - t.Skipf("NetworkManager not available: %v", err) - } - - sig := &dbus.Signal{ - Name: "org.freedesktop.NetworkManager.Settings.ConnectionRemoved", - Body: []interface{}{"/org/freedesktop/NetworkManager/Settings/1"}, - } - - assert.NotPanics(t, func() { - backend.handleDBusSignal(sig) - }) -} - -func TestNetworkManagerBackend_HandleDBusSignal_InvalidBody(t *testing.T) { - backend, err := NewNetworkManagerBackend() - if err != nil { - t.Skipf("NetworkManager not available: %v", err) - } - - sig := &dbus.Signal{ - Name: "org.freedesktop.DBus.Properties.PropertiesChanged", - Body: []interface{}{"only-one-element"}, - } - - assert.NotPanics(t, func() { - backend.handleDBusSignal(sig) - }) -} - -func TestNetworkManagerBackend_HandleDBusSignal_InvalidInterface(t *testing.T) { - backend, err := NewNetworkManagerBackend() - if err != nil { - t.Skipf("NetworkManager not available: %v", err) - } - - sig := &dbus.Signal{ - Name: "org.freedesktop.DBus.Properties.PropertiesChanged", - Body: []interface{}{123, map[string]dbus.Variant{}}, - } - - assert.NotPanics(t, func() { - backend.handleDBusSignal(sig) - }) -} - -func TestNetworkManagerBackend_HandleDBusSignal_InvalidChanges(t *testing.T) { - backend, err := NewNetworkManagerBackend() - if err != nil { - t.Skipf("NetworkManager not available: %v", err) - } - - sig := &dbus.Signal{ - Name: "org.freedesktop.DBus.Properties.PropertiesChanged", - Body: []interface{}{dbusNMInterface, "not-a-map"}, - } - - assert.NotPanics(t, func() { - backend.handleDBusSignal(sig) - }) -} - -func TestNetworkManagerBackend_HandleNetworkManagerChange(t *testing.T) { - backend, err := NewNetworkManagerBackend() - if err != nil { - t.Skipf("NetworkManager not available: %v", err) - } - - changes := map[string]dbus.Variant{ - "PrimaryConnection": dbus.MakeVariant("/"), - "State": dbus.MakeVariant(uint32(70)), - } - - assert.NotPanics(t, func() { - backend.handleNetworkManagerChange(changes) - }) -} - -func TestNetworkManagerBackend_HandleNetworkManagerChange_WirelessEnabled(t *testing.T) { - backend, err := NewNetworkManagerBackend() - if err != nil { - t.Skipf("NetworkManager not available: %v", err) - } - - changes := map[string]dbus.Variant{ - "WirelessEnabled": dbus.MakeVariant(true), - } - - assert.NotPanics(t, func() { - backend.handleNetworkManagerChange(changes) - }) -} - -func TestNetworkManagerBackend_HandleNetworkManagerChange_ActiveConnections(t *testing.T) { - backend, err := NewNetworkManagerBackend() - if err != nil { - t.Skipf("NetworkManager not available: %v", err) - } - - changes := map[string]dbus.Variant{ - "ActiveConnections": dbus.MakeVariant([]interface{}{}), - } - - assert.NotPanics(t, func() { - backend.handleNetworkManagerChange(changes) - }) -} - -func TestNetworkManagerBackend_HandleDeviceChange(t *testing.T) { - backend, err := NewNetworkManagerBackend() - if err != nil { - t.Skipf("NetworkManager not available: %v", err) - } - - changes := map[string]dbus.Variant{ - "State": dbus.MakeVariant(uint32(100)), - } - - assert.NotPanics(t, func() { - backend.handleDeviceChange(changes) - }) -} - -func TestNetworkManagerBackend_HandleDeviceChange_Ip4Config(t *testing.T) { - backend, err := NewNetworkManagerBackend() - if err != nil { - t.Skipf("NetworkManager not available: %v", err) - } - - changes := map[string]dbus.Variant{ - "Ip4Config": dbus.MakeVariant("/"), - } - - assert.NotPanics(t, func() { - backend.handleDeviceChange(changes) - }) -} - -func TestNetworkManagerBackend_HandleWiFiChange_ActiveAccessPoint(t *testing.T) { - backend, err := NewNetworkManagerBackend() - if err != nil { - t.Skipf("NetworkManager not available: %v", err) - } - - changes := map[string]dbus.Variant{ - "ActiveAccessPoint": dbus.MakeVariant("/"), - } - - assert.NotPanics(t, func() { - backend.handleWiFiChange(changes) - }) -} - -func TestNetworkManagerBackend_HandleWiFiChange_AccessPoints(t *testing.T) { - backend, err := NewNetworkManagerBackend() - if err != nil { - t.Skipf("NetworkManager not available: %v", err) - } - - changes := map[string]dbus.Variant{ - "AccessPoints": dbus.MakeVariant([]interface{}{}), - } - - assert.NotPanics(t, func() { - backend.handleWiFiChange(changes) - }) -} - -func TestNetworkManagerBackend_HandleAccessPointChange_NoStrength(t *testing.T) { - backend, err := NewNetworkManagerBackend() - if err != nil { - t.Skipf("NetworkManager not available: %v", err) - } - - changes := map[string]dbus.Variant{ - "SomeOtherProperty": dbus.MakeVariant("value"), - } - - assert.NotPanics(t, func() { - backend.handleAccessPointChange(changes) - }) -} - -func TestNetworkManagerBackend_HandleAccessPointChange_WithStrength(t *testing.T) { - backend, err := NewNetworkManagerBackend() - if err != nil { - t.Skipf("NetworkManager not available: %v", err) - } - - backend.stateMutex.Lock() - backend.state.WiFiSignal = 50 - backend.stateMutex.Unlock() - - changes := map[string]dbus.Variant{ - "Strength": dbus.MakeVariant(uint8(80)), - } - - assert.NotPanics(t, func() { - backend.handleAccessPointChange(changes) - }) -} - -func TestNetworkManagerBackend_StopSignalPump_NoConnection(t *testing.T) { - backend, err := NewNetworkManagerBackend() - if err != nil { - t.Skipf("NetworkManager not available: %v", err) - } - - backend.dbusConn = nil - assert.NotPanics(t, func() { - backend.stopSignalPump() - }) -} diff --git a/nix/inputs/dms-cli/internal/server/network/backend_networkmanager_state.go b/nix/inputs/dms-cli/internal/server/network/backend_networkmanager_state.go deleted file mode 100644 index 72c2268..0000000 --- a/nix/inputs/dms-cli/internal/server/network/backend_networkmanager_state.go +++ /dev/null @@ -1,261 +0,0 @@ -package network - -import ( - "time" - - "github.com/AvengeMedia/danklinux/internal/errdefs" - "github.com/AvengeMedia/danklinux/internal/log" - "github.com/Wifx/gonetworkmanager/v2" -) - -func (b *NetworkManagerBackend) updatePrimaryConnection() error { - nm := b.nmConn.(gonetworkmanager.NetworkManager) - - activeConns, err := nm.GetPropertyActiveConnections() - if err != nil { - return err - } - - hasActiveVPN := false - for _, activeConn := range activeConns { - connType, err := activeConn.GetPropertyType() - if err != nil { - continue - } - if connType == "vpn" || connType == "wireguard" { - state, _ := activeConn.GetPropertyState() - if state == 2 { - hasActiveVPN = true - break - } - } - } - - if hasActiveVPN { - b.stateMutex.Lock() - b.state.NetworkStatus = StatusVPN - b.stateMutex.Unlock() - return nil - } - - primaryConn, err := nm.GetPropertyPrimaryConnection() - if err != nil { - return err - } - - if primaryConn == nil || primaryConn.GetPath() == "/" { - b.stateMutex.Lock() - b.state.NetworkStatus = StatusDisconnected - b.stateMutex.Unlock() - return nil - } - - connType, err := primaryConn.GetPropertyType() - if err != nil { - return err - } - - b.stateMutex.Lock() - switch connType { - case "802-3-ethernet": - b.state.NetworkStatus = StatusEthernet - case "802-11-wireless": - b.state.NetworkStatus = StatusWiFi - case "vpn", "wireguard": - b.state.NetworkStatus = StatusVPN - default: - b.state.NetworkStatus = StatusDisconnected - } - b.stateMutex.Unlock() - - return nil -} - -func (b *NetworkManagerBackend) updateEthernetState() error { - if b.ethernetDevice == nil { - return nil - } - - dev := b.ethernetDevice.(gonetworkmanager.Device) - - iface, err := dev.GetPropertyInterface() - if err != nil { - return err - } - - state, err := dev.GetPropertyState() - if err != nil { - return err - } - - connected := state == gonetworkmanager.NmDeviceStateActivated - - var ip string - if connected { - ip = b.getDeviceIP(dev) - } - - b.stateMutex.Lock() - b.state.EthernetDevice = iface - b.state.EthernetConnected = connected - b.state.EthernetIP = ip - b.stateMutex.Unlock() - - return nil -} - -func (b *NetworkManagerBackend) getDeviceStateReason(dev gonetworkmanager.Device) uint32 { - path := dev.GetPath() - obj := b.dbusConn.Object("org.freedesktop.NetworkManager", path) - - variant, err := obj.GetProperty(dbusNMDeviceInterface + ".StateReason") - if err != nil { - return 0 - } - - if stateReasonStruct, ok := variant.Value().([]interface{}); ok && len(stateReasonStruct) >= 2 { - if reason, ok := stateReasonStruct[1].(uint32); ok { - return reason - } - } - - return 0 -} - -func (b *NetworkManagerBackend) classifyNMStateReason(reason uint32) string { - switch reason { - case NmDeviceStateReasonWrongPassword, - NmDeviceStateReasonSupplicantTimeout, - NmDeviceStateReasonSupplicantFailed, - NmDeviceStateReasonSecretsRequired: - return errdefs.ErrBadCredentials - case NmDeviceStateReasonNoSecrets: - return errdefs.ErrUserCanceled - case NmDeviceStateReasonNoSsid: - return errdefs.ErrNoSuchSSID - case NmDeviceStateReasonDhcpClientFailed, - NmDeviceStateReasonIpConfigUnavailable: - return errdefs.ErrDhcpTimeout - case NmDeviceStateReasonSupplicantDisconnect, - NmDeviceStateReasonCarrier: - return errdefs.ErrAssocTimeout - default: - return errdefs.ErrConnectionFailed - } -} - -func (b *NetworkManagerBackend) updateWiFiState() error { - if b.wifiDevice == nil { - return nil - } - - dev := b.wifiDevice.(gonetworkmanager.Device) - - iface, err := dev.GetPropertyInterface() - if err != nil { - return err - } - - state, err := dev.GetPropertyState() - if err != nil { - return err - } - - connected := state == gonetworkmanager.NmDeviceStateActivated - failed := state == gonetworkmanager.NmDeviceStateFailed - disconnected := state == gonetworkmanager.NmDeviceStateDisconnected - - var ip, ssid, bssid string - var signal uint8 - - if connected { - if err := b.ensureWiFiDevice(); err == nil && b.wifiDev != nil { - w := b.wifiDev.(gonetworkmanager.DeviceWireless) - activeAP, err := w.GetPropertyActiveAccessPoint() - if err == nil && activeAP != nil && activeAP.GetPath() != "/" { - ssid, _ = activeAP.GetPropertySSID() - signal, _ = activeAP.GetPropertyStrength() - bssid, _ = activeAP.GetPropertyHWAddress() - } - } - - ip = b.getDeviceIP(dev) - } - - b.stateMutex.RLock() - wasConnecting := b.state.IsConnecting - connectingSSID := b.state.ConnectingSSID - b.stateMutex.RUnlock() - - var reasonCode string - if wasConnecting && connectingSSID != "" && (failed || (disconnected && !connected)) { - reason := b.getDeviceStateReason(dev) - - if reason == NmDeviceStateReasonNewActivation || reason == 0 { - return nil - } - - log.Warnf("[updateWiFiState] Connection failed: SSID=%s, state=%d, reason=%d", connectingSSID, state, reason) - - reasonCode = b.classifyNMStateReason(reason) - - if reasonCode == errdefs.ErrConnectionFailed { - b.failedMutex.RLock() - if b.lastFailedSSID == connectingSSID { - elapsed := time.Now().Unix() - b.lastFailedTime - if elapsed < 5 { - reasonCode = errdefs.ErrBadCredentials - } - } - b.failedMutex.RUnlock() - } - } - - b.stateMutex.Lock() - defer b.stateMutex.Unlock() - - wasConnecting = b.state.IsConnecting - connectingSSID = b.state.ConnectingSSID - - if wasConnecting && connectingSSID != "" { - if connected && ssid == connectingSSID { - log.Infof("[updateWiFiState] Connection successful: %s", ssid) - b.state.IsConnecting = false - b.state.ConnectingSSID = "" - b.state.LastError = "" - } else if failed || (disconnected && !connected) { - log.Warnf("[updateWiFiState] Connection failed: SSID=%s, state=%d", connectingSSID, state) - b.state.IsConnecting = false - b.state.ConnectingSSID = "" - b.state.LastError = reasonCode - - b.failedMutex.Lock() - b.lastFailedSSID = connectingSSID - b.lastFailedTime = time.Now().Unix() - b.failedMutex.Unlock() - } - } - - b.state.WiFiDevice = iface - b.state.WiFiConnected = connected - b.state.WiFiIP = ip - b.state.WiFiSSID = ssid - b.state.WiFiBSSID = bssid - b.state.WiFiSignal = signal - - return nil -} - -func (b *NetworkManagerBackend) getDeviceIP(dev gonetworkmanager.Device) string { - ip4Config, err := dev.GetPropertyIP4Config() - if err != nil || ip4Config == nil { - return "" - } - - addresses, err := ip4Config.GetPropertyAddressData() - if err != nil || len(addresses) == 0 { - return "" - } - - return addresses[0].Address -} diff --git a/nix/inputs/dms-cli/internal/server/network/backend_networkmanager_state_test.go b/nix/inputs/dms-cli/internal/server/network/backend_networkmanager_state_test.go deleted file mode 100644 index ef9e1b8..0000000 --- a/nix/inputs/dms-cli/internal/server/network/backend_networkmanager_state_test.go +++ /dev/null @@ -1,82 +0,0 @@ -package network - -import ( - "testing" - - "github.com/AvengeMedia/danklinux/internal/errdefs" - mock_gonetworkmanager "github.com/AvengeMedia/danklinux/internal/mocks/github.com/Wifx/gonetworkmanager/v2" - "github.com/Wifx/gonetworkmanager/v2" - "github.com/stretchr/testify/assert" -) - -func TestNetworkManagerBackend_UpdatePrimaryConnection(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - mockNM.EXPECT().GetPropertyActiveConnections().Return([]gonetworkmanager.ActiveConnection{}, nil) - mockNM.EXPECT().GetPropertyPrimaryConnection().Return(nil, nil) - - err = backend.updatePrimaryConnection() - assert.NoError(t, err) -} - -func TestNetworkManagerBackend_UpdateEthernetState_NoDevice(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - backend.ethernetDevice = nil - err = backend.updateEthernetState() - assert.NoError(t, err) -} - -func TestNetworkManagerBackend_UpdateWiFiState_NoDevice(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - backend.wifiDevice = nil - err = backend.updateWiFiState() - assert.NoError(t, err) -} - -func TestNetworkManagerBackend_ClassifyNMStateReason(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - testCases := []struct { - reason uint32 - expected string - }{ - {NmDeviceStateReasonWrongPassword, errdefs.ErrBadCredentials}, - {NmDeviceStateReasonNoSecrets, errdefs.ErrUserCanceled}, - {NmDeviceStateReasonSupplicantTimeout, errdefs.ErrBadCredentials}, - {NmDeviceStateReasonDhcpClientFailed, errdefs.ErrDhcpTimeout}, - {NmDeviceStateReasonNoSsid, errdefs.ErrNoSuchSSID}, - {999, errdefs.ErrConnectionFailed}, - } - - for _, tc := range testCases { - result := backend.classifyNMStateReason(tc.reason) - assert.Equal(t, tc.expected, result) - } -} - -func TestNetworkManagerBackend_GetDeviceIP_NoConfig(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - mockDevice := mock_gonetworkmanager.NewMockDevice(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - mockDevice.EXPECT().GetPropertyIP4Config().Return(nil, nil) - - ip := backend.getDeviceIP(mockDevice) - assert.Empty(t, ip) -} diff --git a/nix/inputs/dms-cli/internal/server/network/backend_networkmanager_test.go b/nix/inputs/dms-cli/internal/server/network/backend_networkmanager_test.go deleted file mode 100644 index 4d9ba23..0000000 --- a/nix/inputs/dms-cli/internal/server/network/backend_networkmanager_test.go +++ /dev/null @@ -1,154 +0,0 @@ -package network - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestNetworkManagerBackend_New(t *testing.T) { - backend, err := NewNetworkManagerBackend() - if err != nil { - t.Skipf("NetworkManager not available: %v", err) - } - - assert.NotNil(t, backend) - assert.Equal(t, "networkmanager", backend.state.Backend) - assert.NotNil(t, backend.stopChan) - assert.NotNil(t, backend.state) -} - -func TestNetworkManagerBackend_GetCurrentState(t *testing.T) { - backend, err := NewNetworkManagerBackend() - if err != nil { - t.Skipf("NetworkManager not available: %v", err) - } - - backend.state.NetworkStatus = StatusWiFi - backend.state.WiFiConnected = true - backend.state.WiFiSSID = "TestNetwork" - backend.state.WiFiIP = "192.168.1.100" - backend.state.WiFiNetworks = []WiFiNetwork{ - {SSID: "TestNetwork", Signal: 80, Connected: true}, - } - backend.state.WiredConnections = []WiredConnection{ - {ID: "Wired connection 1", UUID: "test-uuid"}, - } - - state, err := backend.GetCurrentState() - assert.NoError(t, err) - assert.NotNil(t, state) - assert.Equal(t, StatusWiFi, state.NetworkStatus) - assert.True(t, state.WiFiConnected) - assert.Equal(t, "TestNetwork", state.WiFiSSID) - assert.Len(t, state.WiFiNetworks, 1) - assert.Len(t, state.WiredConnections, 1) - - assert.NotSame(t, &backend.state.WiFiNetworks, &state.WiFiNetworks) - assert.NotSame(t, &backend.state.WiredConnections, &state.WiredConnections) -} - -func TestNetworkManagerBackend_SetPromptBroker_Nil(t *testing.T) { - backend, err := NewNetworkManagerBackend() - if err != nil { - t.Skipf("NetworkManager not available: %v", err) - } - - err = backend.SetPromptBroker(nil) - assert.Error(t, err) - assert.Contains(t, err.Error(), "cannot be nil") -} - -func TestNetworkManagerBackend_SubmitCredentials_NoBroker(t *testing.T) { - backend, err := NewNetworkManagerBackend() - if err != nil { - t.Skipf("NetworkManager not available: %v", err) - } - - backend.promptBroker = nil - err = backend.SubmitCredentials("token", map[string]string{"password": "test"}, false) - assert.Error(t, err) - assert.Contains(t, err.Error(), "not initialized") -} - -func TestNetworkManagerBackend_CancelCredentials_NoBroker(t *testing.T) { - backend, err := NewNetworkManagerBackend() - if err != nil { - t.Skipf("NetworkManager not available: %v", err) - } - - backend.promptBroker = nil - err = backend.CancelCredentials("token") - assert.Error(t, err) - assert.Contains(t, err.Error(), "not initialized") -} - -func TestNetworkManagerBackend_EnsureWiFiDevice_NoDevice(t *testing.T) { - backend, err := NewNetworkManagerBackend() - if err != nil { - t.Skipf("NetworkManager not available: %v", err) - } - - backend.wifiDevice = nil - backend.wifiDev = nil - - err = backend.ensureWiFiDevice() - assert.Error(t, err) - assert.Contains(t, err.Error(), "no WiFi device available") -} - -func TestNetworkManagerBackend_EnsureWiFiDevice_AlreadySet(t *testing.T) { - backend, err := NewNetworkManagerBackend() - if err != nil { - t.Skipf("NetworkManager not available: %v", err) - } - - backend.wifiDev = "dummy-device" - - err = backend.ensureWiFiDevice() - assert.NoError(t, err) -} - -func TestNetworkManagerBackend_StartSecretAgent_NoBroker(t *testing.T) { - backend, err := NewNetworkManagerBackend() - if err != nil { - t.Skipf("NetworkManager not available: %v", err) - } - - backend.promptBroker = nil - err = backend.startSecretAgent() - assert.Error(t, err) - assert.Contains(t, err.Error(), "prompt broker not set") -} - -func TestNetworkManagerBackend_Close(t *testing.T) { - backend, err := NewNetworkManagerBackend() - if err != nil { - t.Skipf("NetworkManager not available: %v", err) - } - - assert.NotPanics(t, func() { - backend.Close() - }) -} - -func TestNetworkManagerBackend_GetPromptBroker(t *testing.T) { - backend, err := NewNetworkManagerBackend() - if err != nil { - t.Skipf("NetworkManager not available: %v", err) - } - - broker := backend.GetPromptBroker() - assert.Nil(t, broker) -} - -func TestNetworkManagerBackend_StopMonitoring_NoSignals(t *testing.T) { - backend, err := NewNetworkManagerBackend() - if err != nil { - t.Skipf("NetworkManager not available: %v", err) - } - - assert.NotPanics(t, func() { - backend.StopMonitoring() - }) -} diff --git a/nix/inputs/dms-cli/internal/server/network/backend_networkmanager_vpn.go b/nix/inputs/dms-cli/internal/server/network/backend_networkmanager_vpn.go deleted file mode 100644 index c5ffee0..0000000 --- a/nix/inputs/dms-cli/internal/server/network/backend_networkmanager_vpn.go +++ /dev/null @@ -1,527 +0,0 @@ -package network - -import ( - "fmt" - "sort" - "strings" - "time" - - "github.com/AvengeMedia/danklinux/internal/log" - "github.com/Wifx/gonetworkmanager/v2" -) - -func (b *NetworkManagerBackend) ListVPNProfiles() ([]VPNProfile, error) { - s := b.settings - if s == nil { - var err error - s, err = gonetworkmanager.NewSettings() - if err != nil { - return nil, fmt.Errorf("failed to get settings: %w", err) - } - b.settings = s - } - - settingsMgr := s.(gonetworkmanager.Settings) - connections, err := settingsMgr.ListConnections() - if err != nil { - return nil, fmt.Errorf("failed to get connections: %w", err) - } - - var profiles []VPNProfile - for _, conn := range connections { - settings, err := conn.GetSettings() - if err != nil { - continue - } - - connMeta, ok := settings["connection"] - if !ok { - continue - } - - connType, _ := connMeta["type"].(string) - if connType != "vpn" && connType != "wireguard" { - continue - } - - connID, _ := connMeta["id"].(string) - connUUID, _ := connMeta["uuid"].(string) - - profile := VPNProfile{ - Name: connID, - UUID: connUUID, - Type: connType, - } - - if connType == "vpn" { - if vpnSettings, ok := settings["vpn"]; ok { - if svcType, ok := vpnSettings["service-type"].(string); ok { - profile.ServiceType = svcType - } - } - } - - profiles = append(profiles, profile) - } - - sort.Slice(profiles, func(i, j int) bool { - return strings.ToLower(profiles[i].Name) < strings.ToLower(profiles[j].Name) - }) - - b.stateMutex.Lock() - b.state.VPNProfiles = profiles - b.stateMutex.Unlock() - - return profiles, nil -} - -func (b *NetworkManagerBackend) ListActiveVPN() ([]VPNActive, error) { - nm := b.nmConn.(gonetworkmanager.NetworkManager) - - activeConns, err := nm.GetPropertyActiveConnections() - if err != nil { - return nil, fmt.Errorf("failed to get active connections: %w", err) - } - - var active []VPNActive - for _, activeConn := range activeConns { - connType, err := activeConn.GetPropertyType() - if err != nil { - continue - } - - if connType != "vpn" && connType != "wireguard" { - continue - } - - uuid, _ := activeConn.GetPropertyUUID() - id, _ := activeConn.GetPropertyID() - state, _ := activeConn.GetPropertyState() - - var stateStr string - switch state { - case 0: - stateStr = "unknown" - case 1: - stateStr = "activating" - case 2: - stateStr = "activated" - case 3: - stateStr = "deactivating" - case 4: - stateStr = "deactivated" - } - - vpnActive := VPNActive{ - Name: id, - UUID: uuid, - State: stateStr, - Type: connType, - Plugin: "", - } - - if connType == "vpn" { - conn, _ := activeConn.GetPropertyConnection() - if conn != nil { - connSettings, err := conn.GetSettings() - if err == nil { - if vpnSettings, ok := connSettings["vpn"]; ok { - if svcType, ok := vpnSettings["service-type"].(string); ok { - vpnActive.Plugin = svcType - } - } - } - } - } - - active = append(active, vpnActive) - } - - b.stateMutex.Lock() - b.state.VPNActive = active - b.stateMutex.Unlock() - - return active, nil -} - -func (b *NetworkManagerBackend) ConnectVPN(uuidOrName string, singleActive bool) error { - if singleActive { - active, err := b.ListActiveVPN() - if err == nil && len(active) > 0 { - alreadyConnected := false - for _, vpn := range active { - if vpn.UUID == uuidOrName || vpn.Name == uuidOrName { - alreadyConnected = true - break - } - } - - if !alreadyConnected { - if err := b.DisconnectAllVPN(); err != nil { - log.Warnf("Failed to disconnect existing VPNs: %v", err) - } - time.Sleep(500 * time.Millisecond) - } else { - return nil - } - } - } - - s := b.settings - if s == nil { - var err error - s, err = gonetworkmanager.NewSettings() - if err != nil { - return fmt.Errorf("failed to get settings: %w", err) - } - b.settings = s - } - - settingsMgr := s.(gonetworkmanager.Settings) - connections, err := settingsMgr.ListConnections() - if err != nil { - return fmt.Errorf("failed to get connections: %w", err) - } - - var targetConn gonetworkmanager.Connection - for _, conn := range connections { - settings, err := conn.GetSettings() - if err != nil { - continue - } - - connMeta, ok := settings["connection"] - if !ok { - continue - } - - connType, _ := connMeta["type"].(string) - if connType != "vpn" && connType != "wireguard" { - continue - } - - connID, _ := connMeta["id"].(string) - connUUID, _ := connMeta["uuid"].(string) - - if connUUID == uuidOrName || connID == uuidOrName { - targetConn = conn - break - } - } - - if targetConn == nil { - return fmt.Errorf("VPN connection not found: %s", uuidOrName) - } - - targetSettings, err := targetConn.GetSettings() - if err != nil { - return fmt.Errorf("failed to get connection settings: %w", err) - } - - var targetUUID string - if connMeta, ok := targetSettings["connection"]; ok { - if uuid, ok := connMeta["uuid"].(string); ok { - targetUUID = uuid - } - } - - b.stateMutex.Lock() - b.state.IsConnectingVPN = true - b.state.ConnectingVPNUUID = targetUUID - b.stateMutex.Unlock() - - if b.onStateChange != nil { - b.onStateChange() - } - - nm := b.nmConn.(gonetworkmanager.NetworkManager) - activeConn, err := nm.ActivateConnection(targetConn, nil, nil) - if err != nil { - b.stateMutex.Lock() - b.state.IsConnectingVPN = false - b.state.ConnectingVPNUUID = "" - b.stateMutex.Unlock() - - if b.onStateChange != nil { - b.onStateChange() - } - - return fmt.Errorf("failed to activate VPN: %w", err) - } - - if activeConn != nil { - state, _ := activeConn.GetPropertyState() - if state == 2 { - b.stateMutex.Lock() - b.state.IsConnectingVPN = false - b.state.ConnectingVPNUUID = "" - b.stateMutex.Unlock() - b.ListActiveVPN() - if b.onStateChange != nil { - b.onStateChange() - } - } - } - - return nil -} - -func (b *NetworkManagerBackend) DisconnectVPN(uuidOrName string) error { - nm := b.nmConn.(gonetworkmanager.NetworkManager) - - activeConns, err := nm.GetPropertyActiveConnections() - if err != nil { - return fmt.Errorf("failed to get active connections: %w", err) - } - - log.Debugf("[DisconnectVPN] Looking for VPN: %s", uuidOrName) - - for _, activeConn := range activeConns { - connType, err := activeConn.GetPropertyType() - if err != nil { - continue - } - - if connType != "vpn" && connType != "wireguard" { - continue - } - - uuid, _ := activeConn.GetPropertyUUID() - id, _ := activeConn.GetPropertyID() - state, _ := activeConn.GetPropertyState() - - log.Debugf("[DisconnectVPN] Found active VPN: uuid=%s id=%s state=%d", uuid, id, state) - - if uuid == uuidOrName || id == uuidOrName { - log.Infof("[DisconnectVPN] Deactivating VPN: %s (state=%d)", id, state) - if err := nm.DeactivateConnection(activeConn); err != nil { - return fmt.Errorf("failed to deactivate VPN: %w", err) - } - b.ListActiveVPN() - if b.onStateChange != nil { - b.onStateChange() - } - return nil - } - } - - log.Warnf("[DisconnectVPN] VPN not found in active connections: %s", uuidOrName) - - s := b.settings - if s == nil { - var err error - s, err = gonetworkmanager.NewSettings() - if err != nil { - return fmt.Errorf("VPN connection not active and cannot access settings: %w", err) - } - b.settings = s - } - - settingsMgr := s.(gonetworkmanager.Settings) - connections, err := settingsMgr.ListConnections() - if err != nil { - return fmt.Errorf("VPN connection not active: %s", uuidOrName) - } - - for _, conn := range connections { - settings, err := conn.GetSettings() - if err != nil { - continue - } - - connMeta, ok := settings["connection"] - if !ok { - continue - } - - connType, _ := connMeta["type"].(string) - if connType != "vpn" && connType != "wireguard" { - continue - } - - connID, _ := connMeta["id"].(string) - connUUID, _ := connMeta["uuid"].(string) - - if connUUID == uuidOrName || connID == uuidOrName { - log.Infof("[DisconnectVPN] VPN connection exists but not active: %s", connID) - return nil - } - } - - return fmt.Errorf("VPN connection not found: %s", uuidOrName) -} - -func (b *NetworkManagerBackend) DisconnectAllVPN() error { - nm := b.nmConn.(gonetworkmanager.NetworkManager) - - activeConns, err := nm.GetPropertyActiveConnections() - if err != nil { - return fmt.Errorf("failed to get active connections: %w", err) - } - - var lastErr error - var disconnected bool - for _, activeConn := range activeConns { - connType, err := activeConn.GetPropertyType() - if err != nil { - continue - } - - if connType != "vpn" && connType != "wireguard" { - continue - } - - if err := nm.DeactivateConnection(activeConn); err != nil { - lastErr = err - log.Warnf("Failed to deactivate VPN connection: %v", err) - } else { - disconnected = true - } - } - - if disconnected { - b.ListActiveVPN() - if b.onStateChange != nil { - b.onStateChange() - } - } - - return lastErr -} - -func (b *NetworkManagerBackend) ClearVPNCredentials(uuidOrName string) error { - s := b.settings - if s == nil { - var err error - s, err = gonetworkmanager.NewSettings() - if err != nil { - return fmt.Errorf("failed to get settings: %w", err) - } - b.settings = s - } - - settingsMgr := s.(gonetworkmanager.Settings) - connections, err := settingsMgr.ListConnections() - if err != nil { - return fmt.Errorf("failed to get connections: %w", err) - } - - for _, conn := range connections { - settings, err := conn.GetSettings() - if err != nil { - continue - } - - connMeta, ok := settings["connection"] - if !ok { - continue - } - - connType, _ := connMeta["type"].(string) - if connType != "vpn" && connType != "wireguard" { - continue - } - - connID, _ := connMeta["id"].(string) - connUUID, _ := connMeta["uuid"].(string) - - if connUUID == uuidOrName || connID == uuidOrName { - if connType == "vpn" { - if vpnSettings, ok := settings["vpn"]; ok { - delete(vpnSettings, "secrets") - - if dataMap, ok := vpnSettings["data"].(map[string]string); ok { - dataMap["password-flags"] = "1" - vpnSettings["data"] = dataMap - } - - vpnSettings["password-flags"] = uint32(1) - } - - settings["vpn-secrets"] = make(map[string]interface{}) - } - - if err := conn.Update(settings); err != nil { - return fmt.Errorf("failed to update connection: %w", err) - } - - if err := conn.ClearSecrets(); err != nil { - log.Warnf("ClearSecrets call failed (may not be critical): %v", err) - } - - log.Infof("Cleared credentials for VPN: %s", connID) - return nil - } - } - - return fmt.Errorf("VPN connection not found: %s", uuidOrName) -} - -func (b *NetworkManagerBackend) updateVPNConnectionState() { - b.stateMutex.RLock() - isConnectingVPN := b.state.IsConnectingVPN - connectingVPNUUID := b.state.ConnectingVPNUUID - b.stateMutex.RUnlock() - - if !isConnectingVPN || connectingVPNUUID == "" { - return - } - - nm := b.nmConn.(gonetworkmanager.NetworkManager) - activeConns, err := nm.GetPropertyActiveConnections() - if err != nil { - return - } - - foundConnection := false - for _, activeConn := range activeConns { - connType, err := activeConn.GetPropertyType() - if err != nil { - continue - } - - if connType != "vpn" && connType != "wireguard" { - continue - } - - uuid, err := activeConn.GetPropertyUUID() - if err != nil { - continue - } - - state, _ := activeConn.GetPropertyState() - stateReason, _ := activeConn.GetPropertyStateFlags() - - if uuid == connectingVPNUUID { - foundConnection = true - - switch state { - case 2: - log.Infof("[updateVPNConnectionState] VPN connection successful: %s", uuid) - b.stateMutex.Lock() - b.state.IsConnectingVPN = false - b.state.ConnectingVPNUUID = "" - b.state.LastError = "" - b.stateMutex.Unlock() - return - case 4: - log.Warnf("[updateVPNConnectionState] VPN connection failed/deactivated: %s (state=%d, flags=%d)", uuid, state, stateReason) - b.stateMutex.Lock() - b.state.IsConnectingVPN = false - b.state.ConnectingVPNUUID = "" - b.state.LastError = "VPN connection failed" - b.stateMutex.Unlock() - return - } - } - } - - if !foundConnection { - log.Warnf("[updateVPNConnectionState] VPN connection no longer exists: %s", connectingVPNUUID) - b.stateMutex.Lock() - b.state.IsConnectingVPN = false - b.state.ConnectingVPNUUID = "" - b.state.LastError = "VPN connection failed" - b.stateMutex.Unlock() - } -} diff --git a/nix/inputs/dms-cli/internal/server/network/backend_networkmanager_vpn_test.go b/nix/inputs/dms-cli/internal/server/network/backend_networkmanager_vpn_test.go deleted file mode 100644 index 52defdd..0000000 --- a/nix/inputs/dms-cli/internal/server/network/backend_networkmanager_vpn_test.go +++ /dev/null @@ -1,138 +0,0 @@ -package network - -import ( - "testing" - - mock_gonetworkmanager "github.com/AvengeMedia/danklinux/internal/mocks/github.com/Wifx/gonetworkmanager/v2" - "github.com/Wifx/gonetworkmanager/v2" - "github.com/stretchr/testify/assert" -) - -func TestNetworkManagerBackend_ListVPNProfiles(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - mockSettings := mock_gonetworkmanager.NewMockSettings(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - backend.settings = mockSettings - - mockSettings.EXPECT().ListConnections().Return([]gonetworkmanager.Connection{}, nil) - - profiles, err := backend.ListVPNProfiles() - assert.NoError(t, err) - assert.Empty(t, profiles) -} - -func TestNetworkManagerBackend_ListActiveVPN(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - mockNM.EXPECT().GetPropertyActiveConnections().Return([]gonetworkmanager.ActiveConnection{}, nil) - - active, err := backend.ListActiveVPN() - assert.NoError(t, err) - assert.Empty(t, active) -} - -func TestNetworkManagerBackend_ConnectVPN_NotFound(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - mockSettings := mock_gonetworkmanager.NewMockSettings(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - backend.settings = mockSettings - - mockSettings.EXPECT().ListConnections().Return([]gonetworkmanager.Connection{}, nil) - - err = backend.ConnectVPN("non-existent-vpn-12345", false) - assert.Error(t, err) - assert.Contains(t, err.Error(), "not found") -} - -func TestNetworkManagerBackend_ConnectVPN_SingleActive_NoActiveVPN(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - mockSettings := mock_gonetworkmanager.NewMockSettings(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - backend.settings = mockSettings - - mockSettings.EXPECT().ListConnections().Return([]gonetworkmanager.Connection{}, nil) - mockNM.EXPECT().GetPropertyActiveConnections().Return([]gonetworkmanager.ActiveConnection{}, nil) - - err = backend.ConnectVPN("non-existent-vpn-12345", true) - assert.Error(t, err) -} - -func TestNetworkManagerBackend_DisconnectVPN_NotActive(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - mockNM.EXPECT().GetPropertyActiveConnections().Return([]gonetworkmanager.ActiveConnection{}, nil) - - err = backend.DisconnectVPN("non-existent-vpn-12345") - assert.Error(t, err) -} - -func TestNetworkManagerBackend_DisconnectAllVPN(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - mockNM.EXPECT().GetPropertyActiveConnections().Return([]gonetworkmanager.ActiveConnection{}, nil) - - err = backend.DisconnectAllVPN() - assert.NoError(t, err) -} - -func TestNetworkManagerBackend_ClearVPNCredentials_NotFound(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - mockSettings := mock_gonetworkmanager.NewMockSettings(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - backend.settings = mockSettings - - mockSettings.EXPECT().ListConnections().Return([]gonetworkmanager.Connection{}, nil) - - err = backend.ClearVPNCredentials("non-existent-vpn-12345") - assert.Error(t, err) - assert.Contains(t, err.Error(), "not found") -} - -func TestNetworkManagerBackend_UpdateVPNConnectionState_NotConnecting(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - backend.stateMutex.Lock() - backend.state.IsConnectingVPN = false - backend.state.ConnectingVPNUUID = "" - backend.stateMutex.Unlock() - - assert.NotPanics(t, func() { - backend.updateVPNConnectionState() - }) -} - -func TestNetworkManagerBackend_UpdateVPNConnectionState_EmptyUUID(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - backend.stateMutex.Lock() - backend.state.IsConnectingVPN = true - backend.state.ConnectingVPNUUID = "" - backend.stateMutex.Unlock() - - assert.NotPanics(t, func() { - backend.updateVPNConnectionState() - }) -} diff --git a/nix/inputs/dms-cli/internal/server/network/backend_networkmanager_wifi.go b/nix/inputs/dms-cli/internal/server/network/backend_networkmanager_wifi.go deleted file mode 100644 index 49fe925..0000000 --- a/nix/inputs/dms-cli/internal/server/network/backend_networkmanager_wifi.go +++ /dev/null @@ -1,718 +0,0 @@ -package network - -import ( - "bytes" - "fmt" - "sort" - - "github.com/AvengeMedia/danklinux/internal/log" - "github.com/Wifx/gonetworkmanager/v2" -) - -func (b *NetworkManagerBackend) GetWiFiEnabled() (bool, error) { - nm := b.nmConn.(gonetworkmanager.NetworkManager) - return nm.GetPropertyWirelessEnabled() -} - -func (b *NetworkManagerBackend) SetWiFiEnabled(enabled bool) error { - nm := b.nmConn.(gonetworkmanager.NetworkManager) - err := nm.SetPropertyWirelessEnabled(enabled) - if err != nil { - return fmt.Errorf("failed to set WiFi enabled: %w", err) - } - - b.stateMutex.Lock() - b.state.WiFiEnabled = enabled - b.stateMutex.Unlock() - - if b.onStateChange != nil { - b.onStateChange() - } - - return nil -} - -func (b *NetworkManagerBackend) ScanWiFi() error { - if b.wifiDevice == nil { - return fmt.Errorf("no WiFi device available") - } - - b.stateMutex.RLock() - enabled := b.state.WiFiEnabled - b.stateMutex.RUnlock() - - if !enabled { - return fmt.Errorf("WiFi is disabled") - } - - if err := b.ensureWiFiDevice(); err != nil { - return err - } - - w := b.wifiDev.(gonetworkmanager.DeviceWireless) - err := w.RequestScan() - if err != nil { - return fmt.Errorf("scan request failed: %w", err) - } - - _, err = b.updateWiFiNetworks() - return err -} - -func (b *NetworkManagerBackend) GetWiFiNetworkDetails(ssid string) (*NetworkInfoResponse, error) { - if b.wifiDevice == nil { - return nil, fmt.Errorf("no WiFi device available") - } - - if err := b.ensureWiFiDevice(); err != nil { - return nil, err - } - wifiDev := b.wifiDev - - w := wifiDev.(gonetworkmanager.DeviceWireless) - apPaths, err := w.GetAccessPoints() - if err != nil { - return nil, fmt.Errorf("failed to get access points: %w", err) - } - - s := b.settings - if s == nil { - s, err = gonetworkmanager.NewSettings() - if err != nil { - return nil, fmt.Errorf("failed to get settings: %w", err) - } - b.settings = s - } - - settingsMgr := s.(gonetworkmanager.Settings) - connections, err := settingsMgr.ListConnections() - if err != nil { - return nil, fmt.Errorf("failed to get connections: %w", err) - } - - savedSSIDs := make(map[string]bool) - autoconnectMap := make(map[string]bool) - for _, conn := range connections { - connSettings, err := conn.GetSettings() - if err != nil { - continue - } - - if connMeta, ok := connSettings["connection"]; ok { - if connType, ok := connMeta["type"].(string); ok && connType == "802-11-wireless" { - if wifiSettings, ok := connSettings["802-11-wireless"]; ok { - if ssidBytes, ok := wifiSettings["ssid"].([]byte); ok { - savedSSID := string(ssidBytes) - savedSSIDs[savedSSID] = true - autoconnect := true - if ac, ok := connMeta["autoconnect"].(bool); ok { - autoconnect = ac - } - autoconnectMap[savedSSID] = autoconnect - } - } - } - } - } - - b.stateMutex.RLock() - currentSSID := b.state.WiFiSSID - currentBSSID := b.state.WiFiBSSID - b.stateMutex.RUnlock() - - var bands []WiFiNetwork - - for _, ap := range apPaths { - apSSID, err := ap.GetPropertySSID() - if err != nil || apSSID != ssid { - continue - } - - strength, _ := ap.GetPropertyStrength() - flags, _ := ap.GetPropertyFlags() - wpaFlags, _ := ap.GetPropertyWPAFlags() - rsnFlags, _ := ap.GetPropertyRSNFlags() - freq, _ := ap.GetPropertyFrequency() - maxBitrate, _ := ap.GetPropertyMaxBitrate() - bssid, _ := ap.GetPropertyHWAddress() - mode, _ := ap.GetPropertyMode() - - secured := flags != uint32(gonetworkmanager.Nm80211APFlagsNone) || - wpaFlags != uint32(gonetworkmanager.Nm80211APSecNone) || - rsnFlags != uint32(gonetworkmanager.Nm80211APSecNone) - - enterprise := (rsnFlags&uint32(gonetworkmanager.Nm80211APSecKeyMgmt8021X) != 0) || - (wpaFlags&uint32(gonetworkmanager.Nm80211APSecKeyMgmt8021X) != 0) - - var modeStr string - switch mode { - case gonetworkmanager.Nm80211ModeAdhoc: - modeStr = "adhoc" - case gonetworkmanager.Nm80211ModeInfra: - modeStr = "infrastructure" - case gonetworkmanager.Nm80211ModeAp: - modeStr = "ap" - default: - modeStr = "unknown" - } - - channel := frequencyToChannel(freq) - - network := WiFiNetwork{ - SSID: ssid, - BSSID: bssid, - Signal: strength, - Secured: secured, - Enterprise: enterprise, - Connected: ssid == currentSSID && bssid == currentBSSID, - Saved: savedSSIDs[ssid], - Autoconnect: autoconnectMap[ssid], - Frequency: freq, - Mode: modeStr, - Rate: maxBitrate / 1000, - Channel: channel, - } - - bands = append(bands, network) - } - - if len(bands) == 0 { - return nil, fmt.Errorf("network not found: %s", ssid) - } - - sort.Slice(bands, func(i, j int) bool { - if bands[i].Connected && !bands[j].Connected { - return true - } - if !bands[i].Connected && bands[j].Connected { - return false - } - return bands[i].Signal > bands[j].Signal - }) - - return &NetworkInfoResponse{ - SSID: ssid, - Bands: bands, - }, nil -} - -func (b *NetworkManagerBackend) ConnectWiFi(req ConnectionRequest) error { - if b.wifiDevice == nil { - return fmt.Errorf("no WiFi device available") - } - - b.stateMutex.RLock() - alreadyConnected := b.state.WiFiConnected && b.state.WiFiSSID == req.SSID - b.stateMutex.RUnlock() - - if alreadyConnected && !req.Interactive { - return nil - } - - b.stateMutex.Lock() - b.state.IsConnecting = true - b.state.ConnectingSSID = req.SSID - b.state.LastError = "" - b.stateMutex.Unlock() - - if b.onStateChange != nil { - b.onStateChange() - } - - nm := b.nmConn.(gonetworkmanager.NetworkManager) - - existingConn, err := b.findConnection(req.SSID) - if err == nil && existingConn != nil { - dev := b.wifiDevice.(gonetworkmanager.Device) - - _, err := nm.ActivateConnection(existingConn, dev, nil) - if err != nil { - log.Warnf("[ConnectWiFi] Failed to activate existing connection: %v", err) - b.stateMutex.Lock() - b.state.IsConnecting = false - b.state.ConnectingSSID = "" - b.state.LastError = fmt.Sprintf("failed to activate connection: %v", err) - b.stateMutex.Unlock() - if b.onStateChange != nil { - b.onStateChange() - } - return fmt.Errorf("failed to activate connection: %w", err) - } - - return nil - } - - if err := b.createAndConnectWiFi(req); err != nil { - log.Warnf("[ConnectWiFi] Failed to create and connect: %v", err) - b.stateMutex.Lock() - b.state.IsConnecting = false - b.state.ConnectingSSID = "" - b.state.LastError = err.Error() - b.stateMutex.Unlock() - if b.onStateChange != nil { - b.onStateChange() - } - return err - } - - return nil -} - -func (b *NetworkManagerBackend) DisconnectWiFi() error { - if b.wifiDevice == nil { - return fmt.Errorf("no WiFi device available") - } - - dev := b.wifiDevice.(gonetworkmanager.Device) - - err := dev.Disconnect() - if err != nil { - return fmt.Errorf("failed to disconnect: %w", err) - } - - b.updateWiFiState() - b.updatePrimaryConnection() - - if b.onStateChange != nil { - b.onStateChange() - } - - return nil -} - -func (b *NetworkManagerBackend) ForgetWiFiNetwork(ssid string) error { - conn, err := b.findConnection(ssid) - if err != nil { - return fmt.Errorf("connection not found: %w", err) - } - - b.stateMutex.RLock() - currentSSID := b.state.WiFiSSID - isConnected := b.state.WiFiConnected - b.stateMutex.RUnlock() - - err = conn.Delete() - if err != nil { - return fmt.Errorf("failed to delete connection: %w", err) - } - - if isConnected && currentSSID == ssid { - b.stateMutex.Lock() - b.state.WiFiConnected = false - b.state.WiFiSSID = "" - b.state.WiFiBSSID = "" - b.state.WiFiSignal = 0 - b.state.WiFiIP = "" - b.state.NetworkStatus = StatusDisconnected - b.stateMutex.Unlock() - } - - b.updateWiFiNetworks() - - if b.onStateChange != nil { - b.onStateChange() - } - - return nil -} - -func (b *NetworkManagerBackend) IsConnectingTo(ssid string) bool { - b.stateMutex.RLock() - defer b.stateMutex.RUnlock() - return b.state.IsConnecting && b.state.ConnectingSSID == ssid -} - -func (b *NetworkManagerBackend) updateWiFiNetworks() ([]WiFiNetwork, error) { - if b.wifiDevice == nil { - return nil, fmt.Errorf("no WiFi device available") - } - - if err := b.ensureWiFiDevice(); err != nil { - return nil, err - } - wifiDev := b.wifiDev - - w := wifiDev.(gonetworkmanager.DeviceWireless) - apPaths, err := w.GetAccessPoints() - if err != nil { - return nil, fmt.Errorf("failed to get access points: %w", err) - } - - s := b.settings - if s == nil { - s, err = gonetworkmanager.NewSettings() - if err != nil { - return nil, fmt.Errorf("failed to get settings: %w", err) - } - b.settings = s - } - - settingsMgr := s.(gonetworkmanager.Settings) - connections, err := settingsMgr.ListConnections() - if err != nil { - return nil, fmt.Errorf("failed to get connections: %w", err) - } - - savedSSIDs := make(map[string]bool) - autoconnectMap := make(map[string]bool) - for _, conn := range connections { - connSettings, err := conn.GetSettings() - if err != nil { - continue - } - - if connMeta, ok := connSettings["connection"]; ok { - if connType, ok := connMeta["type"].(string); ok && connType == "802-11-wireless" { - if wifiSettings, ok := connSettings["802-11-wireless"]; ok { - if ssidBytes, ok := wifiSettings["ssid"].([]byte); ok { - ssid := string(ssidBytes) - savedSSIDs[ssid] = true - autoconnect := true - if ac, ok := connMeta["autoconnect"].(bool); ok { - autoconnect = ac - } - autoconnectMap[ssid] = autoconnect - } - } - } - } - } - - b.stateMutex.RLock() - currentSSID := b.state.WiFiSSID - b.stateMutex.RUnlock() - - seenSSIDs := make(map[string]*WiFiNetwork) - networks := []WiFiNetwork{} - - for _, ap := range apPaths { - ssid, err := ap.GetPropertySSID() - if err != nil || ssid == "" { - continue - } - - if existing, exists := seenSSIDs[ssid]; exists { - strength, _ := ap.GetPropertyStrength() - if strength > existing.Signal { - existing.Signal = strength - freq, _ := ap.GetPropertyFrequency() - existing.Frequency = freq - bssid, _ := ap.GetPropertyHWAddress() - existing.BSSID = bssid - } - continue - } - - strength, _ := ap.GetPropertyStrength() - flags, _ := ap.GetPropertyFlags() - wpaFlags, _ := ap.GetPropertyWPAFlags() - rsnFlags, _ := ap.GetPropertyRSNFlags() - freq, _ := ap.GetPropertyFrequency() - maxBitrate, _ := ap.GetPropertyMaxBitrate() - bssid, _ := ap.GetPropertyHWAddress() - mode, _ := ap.GetPropertyMode() - - secured := flags != uint32(gonetworkmanager.Nm80211APFlagsNone) || - wpaFlags != uint32(gonetworkmanager.Nm80211APSecNone) || - rsnFlags != uint32(gonetworkmanager.Nm80211APSecNone) - - enterprise := (rsnFlags&uint32(gonetworkmanager.Nm80211APSecKeyMgmt8021X) != 0) || - (wpaFlags&uint32(gonetworkmanager.Nm80211APSecKeyMgmt8021X) != 0) - - var modeStr string - switch mode { - case gonetworkmanager.Nm80211ModeAdhoc: - modeStr = "adhoc" - case gonetworkmanager.Nm80211ModeInfra: - modeStr = "infrastructure" - case gonetworkmanager.Nm80211ModeAp: - modeStr = "ap" - default: - modeStr = "unknown" - } - - channel := frequencyToChannel(freq) - - network := WiFiNetwork{ - SSID: ssid, - BSSID: bssid, - Signal: strength, - Secured: secured, - Enterprise: enterprise, - Connected: ssid == currentSSID, - Saved: savedSSIDs[ssid], - Autoconnect: autoconnectMap[ssid], - Frequency: freq, - Mode: modeStr, - Rate: maxBitrate / 1000, - Channel: channel, - } - - seenSSIDs[ssid] = &network - networks = append(networks, network) - } - - sortWiFiNetworks(networks) - - b.stateMutex.Lock() - b.state.WiFiNetworks = networks - b.stateMutex.Unlock() - - return networks, nil -} - -func (b *NetworkManagerBackend) findConnection(ssid string) (gonetworkmanager.Connection, error) { - s := b.settings - if s == nil { - var err error - s, err = gonetworkmanager.NewSettings() - if err != nil { - return nil, err - } - b.settings = s - } - - settings := s.(gonetworkmanager.Settings) - connections, err := settings.ListConnections() - if err != nil { - return nil, err - } - - ssidBytes := []byte(ssid) - for _, conn := range connections { - connSettings, err := conn.GetSettings() - if err != nil { - continue - } - - if connMeta, ok := connSettings["connection"]; ok { - if connType, ok := connMeta["type"].(string); ok && connType == "802-11-wireless" { - if wifiSettings, ok := connSettings["802-11-wireless"]; ok { - if candidateSSID, ok := wifiSettings["ssid"].([]byte); ok { - if bytes.Equal(candidateSSID, ssidBytes) { - return conn, nil - } - } - } - } - } - } - - return nil, fmt.Errorf("connection not found") -} - -func (b *NetworkManagerBackend) createAndConnectWiFi(req ConnectionRequest) error { - if b.wifiDevice == nil { - return fmt.Errorf("no WiFi device available") - } - - nm := b.nmConn.(gonetworkmanager.NetworkManager) - dev := b.wifiDevice.(gonetworkmanager.Device) - - if err := b.ensureWiFiDevice(); err != nil { - return err - } - wifiDev := b.wifiDev - - w := wifiDev.(gonetworkmanager.DeviceWireless) - apPaths, err := w.GetAccessPoints() - if err != nil { - return fmt.Errorf("failed to get access points: %w", err) - } - - var targetAP gonetworkmanager.AccessPoint - for _, ap := range apPaths { - ssid, err := ap.GetPropertySSID() - if err != nil || ssid != req.SSID { - continue - } - targetAP = ap - break - } - - if targetAP == nil { - return fmt.Errorf("access point not found: %s", req.SSID) - } - - flags, _ := targetAP.GetPropertyFlags() - wpaFlags, _ := targetAP.GetPropertyWPAFlags() - rsnFlags, _ := targetAP.GetPropertyRSNFlags() - - const KeyMgmt8021x = uint32(512) - const KeyMgmtPsk = uint32(256) - const KeyMgmtSae = uint32(1024) - - isEnterprise := (wpaFlags&KeyMgmt8021x) != 0 || (rsnFlags&KeyMgmt8021x) != 0 - isPsk := (wpaFlags&KeyMgmtPsk) != 0 || (rsnFlags&KeyMgmtPsk) != 0 - isSae := (wpaFlags&KeyMgmtSae) != 0 || (rsnFlags&KeyMgmtSae) != 0 - - secured := flags != uint32(gonetworkmanager.Nm80211APFlagsNone) || - wpaFlags != uint32(gonetworkmanager.Nm80211APSecNone) || - rsnFlags != uint32(gonetworkmanager.Nm80211APSecNone) - - if isEnterprise { - log.Infof("[createAndConnectWiFi] Enterprise network detected (802.1x) - SSID: %s, interactive: %v", - req.SSID, req.Interactive) - } - - settings := make(map[string]map[string]interface{}) - - settings["connection"] = map[string]interface{}{ - "id": req.SSID, - "type": "802-11-wireless", - "autoconnect": true, - } - - settings["ipv4"] = map[string]interface{}{"method": "auto"} - settings["ipv6"] = map[string]interface{}{"method": "auto"} - - if secured { - settings["802-11-wireless"] = map[string]interface{}{ - "ssid": []byte(req.SSID), - "mode": "infrastructure", - "security": "802-11-wireless-security", - } - - switch { - case isEnterprise || req.Username != "": - settings["802-11-wireless-security"] = map[string]interface{}{ - "key-mgmt": "wpa-eap", - } - - x := map[string]interface{}{ - "eap": []string{"peap"}, - "phase2-auth": "mschapv2", - "system-ca-certs": false, - "password-flags": uint32(0), - } - - if req.Username != "" { - x["identity"] = req.Username - } - if req.Password != "" { - x["password"] = req.Password - } - - if req.AnonymousIdentity != "" { - x["anonymous-identity"] = req.AnonymousIdentity - } - if req.DomainSuffixMatch != "" { - x["domain-suffix-match"] = req.DomainSuffixMatch - } - - settings["802-1x"] = x - - log.Infof("[createAndConnectWiFi] WPA-EAP settings: eap=peap, phase2-auth=mschapv2, identity=%s, interactive=%v, system-ca-certs=%v, domain-suffix-match=%q", - req.Username, req.Interactive, x["system-ca-certs"], req.DomainSuffixMatch) - - case isPsk: - sec := map[string]interface{}{ - "key-mgmt": "wpa-psk", - "psk-flags": uint32(0), - } - if !req.Interactive { - sec["psk"] = req.Password - } - settings["802-11-wireless-security"] = sec - - case isSae: - sec := map[string]interface{}{ - "key-mgmt": "sae", - "pmf": int32(3), - "psk-flags": uint32(0), - } - if !req.Interactive { - sec["psk"] = req.Password - } - settings["802-11-wireless-security"] = sec - - default: - return fmt.Errorf("secured network but not SAE/PSK/802.1X (rsn=0x%x wpa=0x%x)", rsnFlags, wpaFlags) - } - } else { - settings["802-11-wireless"] = map[string]interface{}{ - "ssid": []byte(req.SSID), - "mode": "infrastructure", - } - } - - if req.Interactive { - s := b.settings - if s == nil { - var settingsErr error - s, settingsErr = gonetworkmanager.NewSettings() - if settingsErr != nil { - return fmt.Errorf("failed to get settings manager: %w", settingsErr) - } - b.settings = s - } - - settingsMgr := s.(gonetworkmanager.Settings) - conn, err := settingsMgr.AddConnection(settings) - if err != nil { - return fmt.Errorf("failed to add connection: %w", err) - } - - if isEnterprise { - log.Infof("[createAndConnectWiFi] Enterprise connection added, activating (secret agent will be called)") - } - - _, err = nm.ActivateWirelessConnection(conn, dev, targetAP) - if err != nil { - return fmt.Errorf("failed to activate connection: %w", err) - } - - log.Infof("[createAndConnectWiFi] Connection activation initiated, waiting for NetworkManager state changes...") - } else { - _, err = nm.AddAndActivateWirelessConnection(settings, dev, targetAP) - if err != nil { - return fmt.Errorf("failed to connect: %w", err) - } - log.Infof("[createAndConnectWiFi] Connection activation initiated, waiting for NetworkManager state changes...") - } - - return nil -} - -func (b *NetworkManagerBackend) SetWiFiAutoconnect(ssid string, autoconnect bool) error { - conn, err := b.findConnection(ssid) - if err != nil { - return fmt.Errorf("connection not found: %w", err) - } - - settings, err := conn.GetSettings() - if err != nil { - return fmt.Errorf("failed to get connection settings: %w", err) - } - - if connMeta, ok := settings["connection"]; ok { - connMeta["autoconnect"] = autoconnect - } else { - return fmt.Errorf("connection metadata not found") - } - - if ipv4, ok := settings["ipv4"]; ok { - delete(ipv4, "addresses") - delete(ipv4, "routes") - delete(ipv4, "dns") - } - - if ipv6, ok := settings["ipv6"]; ok { - delete(ipv6, "addresses") - delete(ipv6, "routes") - delete(ipv6, "dns") - } - - err = conn.Update(settings) - if err != nil { - return fmt.Errorf("failed to update connection: %w", err) - } - - b.updateWiFiNetworks() - - if b.onStateChange != nil { - b.onStateChange() - } - - return nil -} diff --git a/nix/inputs/dms-cli/internal/server/network/backend_networkmanager_wifi_test.go b/nix/inputs/dms-cli/internal/server/network/backend_networkmanager_wifi_test.go deleted file mode 100644 index 09f439a..0000000 --- a/nix/inputs/dms-cli/internal/server/network/backend_networkmanager_wifi_test.go +++ /dev/null @@ -1,198 +0,0 @@ -package network - -import ( - "testing" - - mock_gonetworkmanager "github.com/AvengeMedia/danklinux/internal/mocks/github.com/Wifx/gonetworkmanager/v2" - "github.com/stretchr/testify/assert" -) - -func TestNetworkManagerBackend_GetWiFiEnabled(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - mockNM.EXPECT().GetPropertyWirelessEnabled().Return(true, nil) - - enabled, err := backend.GetWiFiEnabled() - assert.NoError(t, err) - assert.True(t, enabled) -} - -func TestNetworkManagerBackend_SetWiFiEnabled(t *testing.T) { - backend, err := NewNetworkManagerBackend() - if err != nil { - t.Skipf("NetworkManager not available: %v", err) - } - - originalState, err := backend.GetWiFiEnabled() - if err != nil { - t.Skipf("Cannot get WiFi state: %v", err) - } - - defer func() { - backend.SetWiFiEnabled(originalState) - }() - - err = backend.SetWiFiEnabled(!originalState) - assert.NoError(t, err) - - backend.stateMutex.RLock() - assert.Equal(t, !originalState, backend.state.WiFiEnabled) - backend.stateMutex.RUnlock() -} - -func TestNetworkManagerBackend_ScanWiFi_NoDevice(t *testing.T) { - backend, err := NewNetworkManagerBackend() - if err != nil { - t.Skipf("NetworkManager not available: %v", err) - } - - backend.wifiDevice = nil - err = backend.ScanWiFi() - assert.Error(t, err) - assert.Contains(t, err.Error(), "no WiFi device available") -} - -func TestNetworkManagerBackend_ScanWiFi_Disabled(t *testing.T) { - backend, err := NewNetworkManagerBackend() - if err != nil { - t.Skipf("NetworkManager not available: %v", err) - } - - if backend.wifiDevice == nil { - t.Skip("No WiFi device available") - } - - backend.stateMutex.Lock() - backend.state.WiFiEnabled = false - backend.stateMutex.Unlock() - - err = backend.ScanWiFi() - assert.Error(t, err) - assert.Contains(t, err.Error(), "WiFi is disabled") -} - -func TestNetworkManagerBackend_GetWiFiNetworkDetails_NoDevice(t *testing.T) { - backend, err := NewNetworkManagerBackend() - if err != nil { - t.Skipf("NetworkManager not available: %v", err) - } - - backend.wifiDevice = nil - _, err = backend.GetWiFiNetworkDetails("TestNetwork") - assert.Error(t, err) - assert.Contains(t, err.Error(), "no WiFi device available") -} - -func TestNetworkManagerBackend_ConnectWiFi_NoDevice(t *testing.T) { - backend, err := NewNetworkManagerBackend() - if err != nil { - t.Skipf("NetworkManager not available: %v", err) - } - - backend.wifiDevice = nil - req := ConnectionRequest{SSID: "TestNetwork", Password: "password"} - err = backend.ConnectWiFi(req) - assert.Error(t, err) - assert.Contains(t, err.Error(), "no WiFi device available") -} - -func TestNetworkManagerBackend_ConnectWiFi_AlreadyConnected(t *testing.T) { - backend, err := NewNetworkManagerBackend() - if err != nil { - t.Skipf("NetworkManager not available: %v", err) - } - - if backend.wifiDevice == nil { - t.Skip("No WiFi device available") - } - - backend.stateMutex.Lock() - backend.state.WiFiConnected = true - backend.state.WiFiSSID = "TestNetwork" - backend.stateMutex.Unlock() - - req := ConnectionRequest{SSID: "TestNetwork", Password: "password"} - err = backend.ConnectWiFi(req) - assert.NoError(t, err) -} - -func TestNetworkManagerBackend_DisconnectWiFi_NoDevice(t *testing.T) { - backend, err := NewNetworkManagerBackend() - if err != nil { - t.Skipf("NetworkManager not available: %v", err) - } - - backend.wifiDevice = nil - err = backend.DisconnectWiFi() - assert.Error(t, err) - assert.Contains(t, err.Error(), "no WiFi device available") -} - -func TestNetworkManagerBackend_IsConnectingTo(t *testing.T) { - backend, err := NewNetworkManagerBackend() - if err != nil { - t.Skipf("NetworkManager not available: %v", err) - } - - backend.stateMutex.Lock() - backend.state.IsConnecting = true - backend.state.ConnectingSSID = "TestNetwork" - backend.stateMutex.Unlock() - - assert.True(t, backend.IsConnectingTo("TestNetwork")) - assert.False(t, backend.IsConnectingTo("OtherNetwork")) -} - -func TestNetworkManagerBackend_IsConnectingTo_NotConnecting(t *testing.T) { - backend, err := NewNetworkManagerBackend() - if err != nil { - t.Skipf("NetworkManager not available: %v", err) - } - - backend.stateMutex.Lock() - backend.state.IsConnecting = false - backend.state.ConnectingSSID = "" - backend.stateMutex.Unlock() - - assert.False(t, backend.IsConnectingTo("TestNetwork")) -} - -func TestNetworkManagerBackend_UpdateWiFiNetworks_NoDevice(t *testing.T) { - backend, err := NewNetworkManagerBackend() - if err != nil { - t.Skipf("NetworkManager not available: %v", err) - } - - backend.wifiDevice = nil - _, err = backend.updateWiFiNetworks() - assert.Error(t, err) - assert.Contains(t, err.Error(), "no WiFi device available") -} - -func TestNetworkManagerBackend_FindConnection_NoSettings(t *testing.T) { - backend, err := NewNetworkManagerBackend() - if err != nil { - t.Skipf("NetworkManager not available: %v", err) - } - - backend.settings = nil - _, err = backend.findConnection("NonExistentNetwork") - assert.Error(t, err) -} - -func TestNetworkManagerBackend_CreateAndConnectWiFi_NoDevice(t *testing.T) { - backend, err := NewNetworkManagerBackend() - if err != nil { - t.Skipf("NetworkManager not available: %v", err) - } - - backend.wifiDevice = nil - backend.wifiDev = nil - req := ConnectionRequest{SSID: "TestNetwork", Password: "password"} - err = backend.createAndConnectWiFi(req) - assert.Error(t, err) - assert.Contains(t, err.Error(), "no WiFi device available") -} diff --git a/nix/inputs/dms-cli/internal/server/network/broker.go b/nix/inputs/dms-cli/internal/server/network/broker.go deleted file mode 100644 index 5290fb5..0000000 --- a/nix/inputs/dms-cli/internal/server/network/broker.go +++ /dev/null @@ -1,22 +0,0 @@ -package network - -import ( - "context" - "crypto/rand" - "encoding/hex" -) - -type PromptBroker interface { - Ask(ctx context.Context, req PromptRequest) (token string, err error) - Wait(ctx context.Context, token string) (PromptReply, error) - Resolve(token string, reply PromptReply) error - Cancel(path string, setting string) error -} - -func generateToken() (string, error) { - bytes := make([]byte, 16) - if _, err := rand.Read(bytes); err != nil { - return "", err - } - return hex.EncodeToString(bytes), nil -} diff --git a/nix/inputs/dms-cli/internal/server/network/connection_test.go b/nix/inputs/dms-cli/internal/server/network/connection_test.go deleted file mode 100644 index 1f4a8a7..0000000 --- a/nix/inputs/dms-cli/internal/server/network/connection_test.go +++ /dev/null @@ -1,109 +0,0 @@ -package network_test - -import ( - "errors" - "testing" - - mocks_network "github.com/AvengeMedia/danklinux/internal/mocks/network" - "github.com/AvengeMedia/danklinux/internal/server/network" - "github.com/stretchr/testify/assert" -) - -func TestConnectionRequest_Validation(t *testing.T) { - t.Run("basic WiFi connection", func(t *testing.T) { - req := network.ConnectionRequest{ - SSID: "TestNetwork", - Password: "testpass123", - } - - assert.NotEmpty(t, req.SSID) - assert.NotEmpty(t, req.Password) - assert.Empty(t, req.Username) - }) - - t.Run("enterprise WiFi connection", func(t *testing.T) { - req := network.ConnectionRequest{ - SSID: "EnterpriseNetwork", - Password: "testpass123", - Username: "testuser", - } - - assert.NotEmpty(t, req.SSID) - assert.NotEmpty(t, req.Password) - assert.NotEmpty(t, req.Username) - }) - - t.Run("open WiFi connection", func(t *testing.T) { - req := network.ConnectionRequest{ - SSID: "OpenNetwork", - } - - assert.NotEmpty(t, req.SSID) - assert.Empty(t, req.Password) - assert.Empty(t, req.Username) - }) -} - -func TestManager_ConnectWiFi_NoDevice(t *testing.T) { - backend := mocks_network.NewMockBackend(t) - req := network.ConnectionRequest{ - SSID: "TestNetwork", - Password: "testpass123", - } - backend.EXPECT().ConnectWiFi(req).Return(errors.New("no WiFi device available")) - - manager := network.NewTestManager(backend, &network.NetworkState{}) - - err := manager.ConnectWiFi(req) - assert.Error(t, err) - assert.Contains(t, err.Error(), "no WiFi device available") -} - -func TestManager_DisconnectWiFi_NoDevice(t *testing.T) { - backend := mocks_network.NewMockBackend(t) - backend.EXPECT().DisconnectWiFi().Return(errors.New("no WiFi device available")) - - manager := network.NewTestManager(backend, &network.NetworkState{}) - - err := manager.DisconnectWiFi() - assert.Error(t, err) - assert.Contains(t, err.Error(), "no WiFi device available") -} - -func TestManager_ForgetWiFiNetwork_NotFound(t *testing.T) { - backend := mocks_network.NewMockBackend(t) - backend.EXPECT().ForgetWiFiNetwork("NonExistentNetwork").Return(errors.New("connection not found")) - - manager := network.NewTestManager(backend, &network.NetworkState{}) - - err := manager.ForgetWiFiNetwork("NonExistentNetwork") - assert.Error(t, err) - assert.Contains(t, err.Error(), "connection not found") -} - -func TestManager_ConnectEthernet_NoDevice(t *testing.T) { - backend := mocks_network.NewMockBackend(t) - backend.EXPECT().ConnectEthernet().Return(errors.New("no ethernet device available")) - - manager := network.NewTestManager(backend, &network.NetworkState{}) - - err := manager.ConnectEthernet() - assert.Error(t, err) - assert.Contains(t, err.Error(), "no ethernet device available") -} - -func TestManager_DisconnectEthernet_NoDevice(t *testing.T) { - backend := mocks_network.NewMockBackend(t) - backend.EXPECT().DisconnectEthernet().Return(errors.New("no ethernet device available")) - - manager := network.NewTestManager(backend, &network.NetworkState{}) - - err := manager.DisconnectEthernet() - assert.Error(t, err) - assert.Contains(t, err.Error(), "no ethernet device available") -} - -// Note: More comprehensive tests for connection operations would require -// mocking the NetworkManager D-Bus interfaces, which is beyond the scope -// of these unit tests. The tests above cover the basic error cases and -// validation logic. Integration tests would be needed for full coverage. diff --git a/nix/inputs/dms-cli/internal/server/network/detect.go b/nix/inputs/dms-cli/internal/server/network/detect.go deleted file mode 100644 index 6031e42..0000000 --- a/nix/inputs/dms-cli/internal/server/network/detect.go +++ /dev/null @@ -1,89 +0,0 @@ -package network - -import ( - "fmt" - - "github.com/godbus/dbus/v5" -) - -type BackendType int - -const ( - BackendNone BackendType = iota - BackendNetworkManager - BackendIwd - BackendConnMan - BackendNetworkd -) - -func nameHasOwner(bus *dbus.Conn, name string) (bool, error) { - obj := bus.Object("org.freedesktop.DBus", "/org/freedesktop/DBus") - var owned bool - if err := obj.Call("org.freedesktop.DBus.NameHasOwner", 0, name).Store(&owned); err != nil { - return false, err - } - return owned, nil -} - -type DetectResult struct { - Backend BackendType - HasNM bool - HasIwd bool - HasConnMan bool - HasWpaSupp bool - HasNetworkd bool - ChosenReason string -} - -func DetectNetworkStack() (*DetectResult, error) { - bus, err := dbus.ConnectSystemBus() - if err != nil { - return nil, fmt.Errorf("connect system bus: %w", err) - } - defer bus.Close() - - hasNM, _ := nameHasOwner(bus, "org.freedesktop.NetworkManager") - hasIwd, _ := nameHasOwner(bus, "net.connman.iwd") - hasConn, _ := nameHasOwner(bus, "net.connman") - hasWpa, _ := nameHasOwner(bus, "fi.w1.wpa_supplicant1") - hasNetworkd, _ := nameHasOwner(bus, "org.freedesktop.network1") - - res := &DetectResult{ - HasNM: hasNM, - HasIwd: hasIwd, - HasConnMan: hasConn, - HasWpaSupp: hasWpa, - HasNetworkd: hasNetworkd, - } - - switch { - case hasNM: - res.Backend = BackendNetworkManager - if hasIwd { - res.ChosenReason = "NetworkManager present; iwd also running (likely NM's Wi-Fi backend). Using NM API." - } else { - res.ChosenReason = "NetworkManager present. Using NM API." - } - case hasConn && hasIwd: - res.Backend = BackendConnMan - res.ChosenReason = "ConnMan + iwd detected. Use ConnMan API (iwd is its Wi-Fi daemon)." - case hasIwd && hasNetworkd: - res.Backend = BackendNetworkd - res.ChosenReason = "iwd + systemd-networkd detected. Using iwd for Wi-Fi association and networkd for IP/DHCP." - case hasIwd: - res.Backend = BackendIwd - res.ChosenReason = "iwd detected without NM/ConnMan. Using iwd API." - case hasNetworkd: - res.Backend = BackendNetworkd - res.ChosenReason = "systemd-networkd detected (no NM/ConnMan). Using networkd for L3 and wired." - default: - res.Backend = BackendNone - if hasWpa { - res.ChosenReason = "No NM/ConnMan/iwd; wpa_supplicant present. Consider a wpa_supplicant path." - } else { - res.ChosenReason = "No known network manager bus names found." - } - } - - return res, nil -} diff --git a/nix/inputs/dms-cli/internal/server/network/detect_test.go b/nix/inputs/dms-cli/internal/server/network/detect_test.go deleted file mode 100644 index 5678860..0000000 --- a/nix/inputs/dms-cli/internal/server/network/detect_test.go +++ /dev/null @@ -1,34 +0,0 @@ -package network - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestBackendType_Constants(t *testing.T) { - assert.Equal(t, BackendType(0), BackendNone) - assert.Equal(t, BackendType(1), BackendNetworkManager) - assert.Equal(t, BackendType(2), BackendIwd) - assert.Equal(t, BackendType(3), BackendConnMan) - assert.Equal(t, BackendType(4), BackendNetworkd) -} - -func TestDetectResult_HasNetworkdField(t *testing.T) { - result := &DetectResult{ - Backend: BackendNetworkd, - HasNetworkd: true, - HasIwd: true, - } - - assert.True(t, result.HasNetworkd) - assert.True(t, result.HasIwd) - assert.Equal(t, BackendNetworkd, result.Backend) -} - -func TestDetectNetworkStack_Integration(t *testing.T) { - result, err := DetectNetworkStack() - assert.NoError(t, err) - assert.NotNil(t, result) - assert.NotEmpty(t, result.ChosenReason) -} diff --git a/nix/inputs/dms-cli/internal/server/network/handlers.go b/nix/inputs/dms-cli/internal/server/network/handlers.go deleted file mode 100644 index 9af367e..0000000 --- a/nix/inputs/dms-cli/internal/server/network/handlers.go +++ /dev/null @@ -1,487 +0,0 @@ -package network - -import ( - "encoding/json" - "fmt" - "net" - - "github.com/AvengeMedia/danklinux/internal/log" - "github.com/AvengeMedia/danklinux/internal/server/models" -) - -type Request struct { - ID int `json:"id,omitempty"` - Method string `json:"method"` - Params map[string]interface{} `json:"params,omitempty"` -} - -type SuccessResult struct { - Success bool `json:"success"` - Message string `json:"message"` -} - -func HandleRequest(conn net.Conn, req Request, manager *Manager) { - switch req.Method { - case "network.getState": - handleGetState(conn, req, manager) - case "network.wifi.scan": - handleScanWiFi(conn, req, manager) - case "network.wifi.networks": - handleGetWiFiNetworks(conn, req, manager) - case "network.wifi.connect": - handleConnectWiFi(conn, req, manager) - case "network.wifi.disconnect": - handleDisconnectWiFi(conn, req, manager) - case "network.wifi.forget": - handleForgetWiFi(conn, req, manager) - case "network.wifi.toggle": - handleToggleWiFi(conn, req, manager) - case "network.wifi.enable": - handleEnableWiFi(conn, req, manager) - case "network.wifi.disable": - handleDisableWiFi(conn, req, manager) - case "network.ethernet.connect.config": - handleConnectEthernetSpecificConfig(conn, req, manager) - case "network.ethernet.connect": - handleConnectEthernet(conn, req, manager) - case "network.ethernet.disconnect": - handleDisconnectEthernet(conn, req, manager) - case "network.preference.set": - handleSetPreference(conn, req, manager) - case "network.info": - handleGetNetworkInfo(conn, req, manager) - case "network.ethernet.info": - handleGetWiredNetworkInfo(conn, req, manager) - case "network.subscribe": - handleSubscribe(conn, req, manager) - case "network.credentials.submit": - handleCredentialsSubmit(conn, req, manager) - case "network.credentials.cancel": - handleCredentialsCancel(conn, req, manager) - case "network.vpn.profiles": - handleListVPNProfiles(conn, req, manager) - case "network.vpn.active": - handleListActiveVPN(conn, req, manager) - case "network.vpn.connect": - handleConnectVPN(conn, req, manager) - case "network.vpn.disconnect": - handleDisconnectVPN(conn, req, manager) - case "network.vpn.disconnectAll": - handleDisconnectAllVPN(conn, req, manager) - case "network.vpn.clearCredentials": - handleClearVPNCredentials(conn, req, manager) - case "network.wifi.setAutoconnect": - handleSetWiFiAutoconnect(conn, req, manager) - default: - models.RespondError(conn, req.ID, fmt.Sprintf("unknown method: %s", req.Method)) - } -} - -func handleCredentialsSubmit(conn net.Conn, req Request, manager *Manager) { - token, ok := req.Params["token"].(string) - if !ok { - log.Warnf("handleCredentialsSubmit: missing or invalid token parameter") - models.RespondError(conn, req.ID, "missing or invalid 'token' parameter") - return - } - - secretsRaw, ok := req.Params["secrets"].(map[string]interface{}) - if !ok { - log.Warnf("handleCredentialsSubmit: missing or invalid secrets parameter") - models.RespondError(conn, req.ID, "missing or invalid 'secrets' parameter") - return - } - - secrets := make(map[string]string) - for k, v := range secretsRaw { - if str, ok := v.(string); ok { - secrets[k] = str - } - } - - save := true - if saveParam, ok := req.Params["save"].(bool); ok { - save = saveParam - } - - if err := manager.SubmitCredentials(token, secrets, save); err != nil { - log.Warnf("handleCredentialsSubmit: failed to submit credentials: %v", err) - models.RespondError(conn, req.ID, err.Error()) - return - } - - log.Infof("handleCredentialsSubmit: credentials submitted successfully") - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "credentials submitted"}) -} - -func handleCredentialsCancel(conn net.Conn, req Request, manager *Manager) { - token, ok := req.Params["token"].(string) - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'token' parameter") - return - } - - if err := manager.CancelCredentials(token); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "credentials cancelled"}) -} - -func handleGetState(conn net.Conn, req Request, manager *Manager) { - state := manager.GetState() - models.Respond(conn, req.ID, state) -} - -func handleScanWiFi(conn net.Conn, req Request, manager *Manager) { - if err := manager.ScanWiFi(); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "scanning"}) -} - -func handleGetWiFiNetworks(conn net.Conn, req Request, manager *Manager) { - networks := manager.GetWiFiNetworks() - models.Respond(conn, req.ID, networks) -} - -func handleConnectWiFi(conn net.Conn, req Request, manager *Manager) { - ssid, ok := req.Params["ssid"].(string) - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'ssid' parameter") - return - } - - var connReq ConnectionRequest - connReq.SSID = ssid - - if password, ok := req.Params["password"].(string); ok { - connReq.Password = password - } - if username, ok := req.Params["username"].(string); ok { - connReq.Username = username - } - - if interactive, ok := req.Params["interactive"].(bool); ok { - connReq.Interactive = interactive - } else { - state := manager.GetState() - alreadyConnected := state.WiFiConnected && state.WiFiSSID == ssid - - if alreadyConnected { - connReq.Interactive = false - } else { - networkInfo, err := manager.GetNetworkInfo(ssid) - isSaved := err == nil && networkInfo.Saved - - if isSaved { - connReq.Interactive = false - } else if err == nil && networkInfo.Secured && connReq.Password == "" && connReq.Username == "" { - connReq.Interactive = true - } - } - } - - if anonymousIdentity, ok := req.Params["anonymousIdentity"].(string); ok { - connReq.AnonymousIdentity = anonymousIdentity - } - if domainSuffixMatch, ok := req.Params["domainSuffixMatch"].(string); ok { - connReq.DomainSuffixMatch = domainSuffixMatch - } - - if err := manager.ConnectWiFi(connReq); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "connecting"}) -} - -func handleDisconnectWiFi(conn net.Conn, req Request, manager *Manager) { - if err := manager.DisconnectWiFi(); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "disconnected"}) -} - -func handleForgetWiFi(conn net.Conn, req Request, manager *Manager) { - ssid, ok := req.Params["ssid"].(string) - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'ssid' parameter") - return - } - - if err := manager.ForgetWiFiNetwork(ssid); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "forgotten"}) -} - -func handleToggleWiFi(conn net.Conn, req Request, manager *Manager) { - if err := manager.ToggleWiFi(); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - state := manager.GetState() - models.Respond(conn, req.ID, map[string]bool{"enabled": state.WiFiEnabled}) -} - -func handleEnableWiFi(conn net.Conn, req Request, manager *Manager) { - if err := manager.EnableWiFi(); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - models.Respond(conn, req.ID, map[string]bool{"enabled": true}) -} - -func handleDisableWiFi(conn net.Conn, req Request, manager *Manager) { - if err := manager.DisableWiFi(); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - models.Respond(conn, req.ID, map[string]bool{"enabled": false}) -} - -func handleConnectEthernetSpecificConfig(conn net.Conn, req Request, manager *Manager) { - uuid, ok := req.Params["uuid"].(string) - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'uuid' parameter") - return - } - if err := manager.activateConnection(uuid); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "connecting"}) -} - -func handleConnectEthernet(conn net.Conn, req Request, manager *Manager) { - if err := manager.ConnectEthernet(); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "connecting"}) -} - -func handleDisconnectEthernet(conn net.Conn, req Request, manager *Manager) { - if err := manager.DisconnectEthernet(); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "disconnected"}) -} - -func handleSetPreference(conn net.Conn, req Request, manager *Manager) { - preference, ok := req.Params["preference"].(string) - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'preference' parameter") - return - } - - if err := manager.SetConnectionPreference(ConnectionPreference(preference)); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, map[string]string{"preference": preference}) -} - -func handleGetNetworkInfo(conn net.Conn, req Request, manager *Manager) { - ssid, ok := req.Params["ssid"].(string) - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'ssid' parameter") - return - } - - network, err := manager.GetNetworkInfoDetailed(ssid) - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, network) -} - -func handleGetWiredNetworkInfo(conn net.Conn, req Request, manager *Manager) { - uuid, ok := req.Params["uuid"].(string) - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'uuid' parameter") - return - } - - network, err := manager.GetWiredNetworkInfoDetailed(uuid) - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, network) -} - -func handleSubscribe(conn net.Conn, req Request, manager *Manager) { - clientID := fmt.Sprintf("client-%p", conn) - stateChan := manager.Subscribe(clientID) - defer manager.Unsubscribe(clientID) - - initialState := manager.GetState() - event := NetworkEvent{ - Type: EventStateChanged, - Data: initialState, - } - if err := json.NewEncoder(conn).Encode(models.Response[NetworkEvent]{ - ID: req.ID, - Result: &event, - }); err != nil { - return - } - - for state := range stateChan { - event := NetworkEvent{ - Type: EventStateChanged, - Data: state, - } - if err := json.NewEncoder(conn).Encode(models.Response[NetworkEvent]{ - Result: &event, - }); err != nil { - return - } - } -} - -func handleListVPNProfiles(conn net.Conn, req Request, manager *Manager) { - profiles, err := manager.ListVPNProfiles() - if err != nil { - log.Warnf("handleListVPNProfiles: failed to list profiles: %v", err) - models.RespondError(conn, req.ID, fmt.Sprintf("failed to list VPN profiles: %v", err)) - return - } - - models.Respond(conn, req.ID, profiles) -} - -func handleListActiveVPN(conn net.Conn, req Request, manager *Manager) { - active, err := manager.ListActiveVPN() - if err != nil { - log.Warnf("handleListActiveVPN: failed to list active VPNs: %v", err) - models.RespondError(conn, req.ID, fmt.Sprintf("failed to list active VPNs: %v", err)) - return - } - - models.Respond(conn, req.ID, active) -} - -func handleConnectVPN(conn net.Conn, req Request, manager *Manager) { - uuidOrName, ok := req.Params["uuidOrName"].(string) - if !ok { - name, nameOk := req.Params["name"].(string) - uuid, uuidOk := req.Params["uuid"].(string) - if nameOk { - uuidOrName = name - } else if uuidOk { - uuidOrName = uuid - } else { - log.Warnf("handleConnectVPN: missing uuidOrName/name/uuid parameter") - models.RespondError(conn, req.ID, "missing 'uuidOrName', 'name', or 'uuid' parameter") - return - } - } - - // Default to true - only allow one VPN connection at a time - singleActive := true - if sa, ok := req.Params["singleActive"].(bool); ok { - singleActive = sa - } - - if err := manager.ConnectVPN(uuidOrName, singleActive); err != nil { - log.Warnf("handleConnectVPN: failed to connect: %v", err) - models.RespondError(conn, req.ID, fmt.Sprintf("failed to connect VPN: %v", err)) - return - } - - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "VPN connection initiated"}) -} - -func handleDisconnectVPN(conn net.Conn, req Request, manager *Manager) { - uuidOrName, ok := req.Params["uuidOrName"].(string) - if !ok { - name, nameOk := req.Params["name"].(string) - uuid, uuidOk := req.Params["uuid"].(string) - if nameOk { - uuidOrName = name - } else if uuidOk { - uuidOrName = uuid - } else { - log.Warnf("handleDisconnectVPN: missing uuidOrName/name/uuid parameter") - models.RespondError(conn, req.ID, "missing 'uuidOrName', 'name', or 'uuid' parameter") - return - } - } - - if err := manager.DisconnectVPN(uuidOrName); err != nil { - log.Warnf("handleDisconnectVPN: failed to disconnect: %v", err) - models.RespondError(conn, req.ID, fmt.Sprintf("failed to disconnect VPN: %v", err)) - return - } - - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "VPN disconnected"}) -} - -func handleDisconnectAllVPN(conn net.Conn, req Request, manager *Manager) { - if err := manager.DisconnectAllVPN(); err != nil { - log.Warnf("handleDisconnectAllVPN: failed: %v", err) - models.RespondError(conn, req.ID, fmt.Sprintf("failed to disconnect all VPNs: %v", err)) - return - } - - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "All VPNs disconnected"}) -} - -func handleClearVPNCredentials(conn net.Conn, req Request, manager *Manager) { - uuidOrName, ok := req.Params["uuid"].(string) - if !ok { - uuidOrName, ok = req.Params["name"].(string) - } - if !ok { - uuidOrName, ok = req.Params["uuidOrName"].(string) - } - if !ok { - log.Warnf("handleClearVPNCredentials: missing uuidOrName/name/uuid parameter") - models.RespondError(conn, req.ID, "missing uuidOrName/name/uuid parameter") - return - } - - if err := manager.ClearVPNCredentials(uuidOrName); err != nil { - log.Warnf("handleClearVPNCredentials: failed: %v", err) - models.RespondError(conn, req.ID, fmt.Sprintf("failed to clear VPN credentials: %v", err)) - return - } - - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "VPN credentials cleared"}) -} - -func handleSetWiFiAutoconnect(conn net.Conn, req Request, manager *Manager) { - ssid, ok := req.Params["ssid"].(string) - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'ssid' parameter") - return - } - - autoconnect, ok := req.Params["autoconnect"].(bool) - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'autoconnect' parameter") - return - } - - if err := manager.SetWiFiAutoconnect(ssid, autoconnect); err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to set autoconnect: %v", err)) - return - } - - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "autoconnect updated"}) -} diff --git a/nix/inputs/dms-cli/internal/server/network/handlers_test.go b/nix/inputs/dms-cli/internal/server/network/handlers_test.go deleted file mode 100644 index 00126da..0000000 --- a/nix/inputs/dms-cli/internal/server/network/handlers_test.go +++ /dev/null @@ -1,263 +0,0 @@ -package network - -import ( - "bytes" - "encoding/json" - "net" - "testing" - - "github.com/AvengeMedia/danklinux/internal/server/models" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -type mockNetConn struct { - net.Conn - readBuf *bytes.Buffer - writeBuf *bytes.Buffer - closed bool -} - -func newMockNetConn() *mockNetConn { - return &mockNetConn{ - readBuf: &bytes.Buffer{}, - writeBuf: &bytes.Buffer{}, - } -} - -func (m *mockNetConn) Read(b []byte) (n int, err error) { - return m.readBuf.Read(b) -} - -func (m *mockNetConn) Write(b []byte) (n int, err error) { - return m.writeBuf.Write(b) -} - -func (m *mockNetConn) Close() error { - m.closed = true - return nil -} - -func TestRespondError_Network(t *testing.T) { - conn := newMockNetConn() - models.RespondError(conn, 123, "test error") - - var resp models.Response[any] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Equal(t, "test error", resp.Error) - assert.Nil(t, resp.Result) -} - -func TestRespond_Network(t *testing.T) { - conn := newMockNetConn() - result := SuccessResult{Success: true, Message: "test"} - models.Respond(conn, 123, result) - - var resp models.Response[SuccessResult] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Empty(t, resp.Error) - require.NotNil(t, resp.Result) - assert.True(t, resp.Result.Success) - assert.Equal(t, "test", resp.Result.Message) -} - -func TestHandleGetState(t *testing.T) { - manager := &Manager{ - state: &NetworkState{ - NetworkStatus: StatusWiFi, - WiFiSSID: "TestNetwork", - WiFiConnected: true, - }, - } - - conn := newMockNetConn() - req := Request{ID: 123, Method: "network.getState"} - - handleGetState(conn, req, manager) - - var resp models.Response[NetworkState] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Empty(t, resp.Error) - require.NotNil(t, resp.Result) - assert.Equal(t, StatusWiFi, resp.Result.NetworkStatus) - assert.Equal(t, "TestNetwork", resp.Result.WiFiSSID) -} - -func TestHandleGetWiFiNetworks(t *testing.T) { - manager := &Manager{ - state: &NetworkState{ - WiFiNetworks: []WiFiNetwork{ - {SSID: "Network1", Signal: 90}, - {SSID: "Network2", Signal: 80}, - }, - }, - } - - conn := newMockNetConn() - req := Request{ID: 123, Method: "network.wifi.networks"} - - handleGetWiFiNetworks(conn, req, manager) - - var resp models.Response[[]WiFiNetwork] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Empty(t, resp.Error) - require.NotNil(t, resp.Result) - assert.Len(t, *resp.Result, 2) - assert.Equal(t, "Network1", (*resp.Result)[0].SSID) -} - -func TestHandleConnectWiFi(t *testing.T) { - t.Run("missing ssid parameter", func(t *testing.T) { - manager := &Manager{ - state: &NetworkState{}, - } - - conn := newMockNetConn() - req := Request{ - ID: 123, - Method: "network.wifi.connect", - Params: map[string]interface{}{}, - } - - handleConnectWiFi(conn, req, manager) - - var resp models.Response[any] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Contains(t, resp.Error, "missing or invalid 'ssid' parameter") - }) -} - -func TestHandleSetPreference(t *testing.T) { - t.Run("missing preference parameter", func(t *testing.T) { - manager := &Manager{ - state: &NetworkState{}, - } - - conn := newMockNetConn() - req := Request{ - ID: 123, - Method: "network.preference.set", - Params: map[string]interface{}{}, - } - - handleSetPreference(conn, req, manager) - - var resp models.Response[any] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Contains(t, resp.Error, "missing or invalid 'preference' parameter") - }) -} - -func TestHandleGetNetworkInfo(t *testing.T) { - t.Run("missing ssid parameter", func(t *testing.T) { - manager := &Manager{ - state: &NetworkState{}, - } - - conn := newMockNetConn() - req := Request{ - ID: 123, - Method: "network.info", - Params: map[string]interface{}{}, - } - - handleGetNetworkInfo(conn, req, manager) - - var resp models.Response[any] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Contains(t, resp.Error, "missing or invalid 'ssid' parameter") - }) -} - -func TestHandleRequest(t *testing.T) { - manager := &Manager{ - state: &NetworkState{ - NetworkStatus: StatusWiFi, - }, - } - - t.Run("unknown method", func(t *testing.T) { - conn := newMockNetConn() - req := Request{ - ID: 123, - Method: "network.unknown", - } - - HandleRequest(conn, req, manager) - - var resp models.Response[any] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Contains(t, resp.Error, "unknown method") - }) - - t.Run("valid method - getState", func(t *testing.T) { - conn := newMockNetConn() - req := Request{ - ID: 123, - Method: "network.getState", - } - - HandleRequest(conn, req, manager) - - var resp models.Response[NetworkState] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Empty(t, resp.Error) - }) -} - -func TestHandleSubscribe(t *testing.T) { - // This test is complex due to the streaming nature of subscriptions - // Better suited as an integration test - t.Skip("Subscription test requires connection lifecycle management - integration test needed") -} - -func TestManager_Subscribe_Unsubscribe(t *testing.T) { - manager := &Manager{ - state: &NetworkState{}, - subscribers: make(map[string]chan NetworkState), - } - - t.Run("subscribe creates channel", func(t *testing.T) { - ch := manager.Subscribe("client1") - assert.NotNil(t, ch) - assert.Len(t, manager.subscribers, 1) - }) - - t.Run("unsubscribe removes channel", func(t *testing.T) { - manager.Unsubscribe("client1") - assert.Len(t, manager.subscribers, 0) - }) - - t.Run("unsubscribe non-existent client is safe", func(t *testing.T) { - assert.NotPanics(t, func() { - manager.Unsubscribe("non-existent") - }) - }) -} diff --git a/nix/inputs/dms-cli/internal/server/network/helpers.go b/nix/inputs/dms-cli/internal/server/network/helpers.go deleted file mode 100644 index 4669f52..0000000 --- a/nix/inputs/dms-cli/internal/server/network/helpers.go +++ /dev/null @@ -1,53 +0,0 @@ -package network - -import "sort" - -func frequencyToChannel(freq uint32) uint32 { - if freq >= 2412 && freq <= 2484 { - if freq == 2484 { - return 14 - } - return (freq-2412)/5 + 1 - } - - if freq >= 5170 && freq <= 5825 { - return (freq-5170)/5 + 34 - } - - if freq >= 5955 && freq <= 7115 { - return (freq-5955)/5 + 1 - } - - return 0 -} - -func sortWiFiNetworks(networks []WiFiNetwork) { - sort.Slice(networks, func(i, j int) bool { - if networks[i].Connected && !networks[j].Connected { - return true - } - if !networks[i].Connected && networks[j].Connected { - return false - } - - if networks[i].Saved && !networks[j].Saved { - return true - } - if !networks[i].Saved && networks[j].Saved { - return false - } - - if !networks[i].Secured && networks[j].Secured { - if networks[i].Signal >= 50 { - return true - } - } - if networks[i].Secured && !networks[j].Secured { - if networks[j].Signal >= 50 { - return false - } - } - - return networks[i].Signal > networks[j].Signal - }) -} diff --git a/nix/inputs/dms-cli/internal/server/network/manager.go b/nix/inputs/dms-cli/internal/server/network/manager.go deleted file mode 100644 index c1c18c1..0000000 --- a/nix/inputs/dms-cli/internal/server/network/manager.go +++ /dev/null @@ -1,530 +0,0 @@ -package network - -import ( - "fmt" - "sync" - "time" - - "github.com/AvengeMedia/danklinux/internal/log" -) - -func NewManager() (*Manager, error) { - detection, err := DetectNetworkStack() - if err != nil { - return nil, fmt.Errorf("failed to detect network stack: %w", err) - } - - log.Infof("Network backend detection: %s", detection.ChosenReason) - - var backend Backend - switch detection.Backend { - case BackendNetworkManager: - nm, err := NewNetworkManagerBackend() - if err != nil { - return nil, fmt.Errorf("failed to create NetworkManager backend: %w", err) - } - backend = nm - - case BackendIwd: - iwd, err := NewIWDBackend() - if err != nil { - return nil, fmt.Errorf("failed to create iwd backend: %w", err) - } - backend = iwd - - case BackendNetworkd: - if detection.HasIwd && !detection.HasNM { - wifi, err := NewIWDBackend() - if err != nil { - return nil, fmt.Errorf("failed to create iwd backend: %w", err) - } - l3, err := NewSystemdNetworkdBackend() - if err != nil { - return nil, fmt.Errorf("failed to create networkd backend: %w", err) - } - hybrid, err := NewHybridIwdNetworkdBackend(wifi, l3) - if err != nil { - return nil, fmt.Errorf("failed to create hybrid backend: %w", err) - } - backend = hybrid - } else { - nd, err := NewSystemdNetworkdBackend() - if err != nil { - return nil, fmt.Errorf("failed to create networkd backend: %w", err) - } - backend = nd - } - - default: - return nil, fmt.Errorf("no supported network backend found: %s", detection.ChosenReason) - } - - m := &Manager{ - backend: backend, - state: &NetworkState{ - NetworkStatus: StatusDisconnected, - Preference: PreferenceAuto, - WiFiNetworks: []WiFiNetwork{}, - }, - stateMutex: sync.RWMutex{}, - subscribers: make(map[string]chan NetworkState), - subMutex: sync.RWMutex{}, - stopChan: make(chan struct{}), - dirty: make(chan struct{}, 1), - credentialSubscribers: make(map[string]chan CredentialPrompt), - credSubMutex: sync.RWMutex{}, - } - - broker := NewSubscriptionBroker(m.broadcastCredentialPrompt) - if err := backend.SetPromptBroker(broker); err != nil { - return nil, fmt.Errorf("failed to set prompt broker: %w", err) - } - - if err := backend.Initialize(); err != nil { - return nil, fmt.Errorf("failed to initialize backend: %w", err) - } - - if err := m.syncStateFromBackend(); err != nil { - return nil, fmt.Errorf("failed to sync initial state: %w", err) - } - - m.notifierWg.Add(1) - go m.notifier() - - if err := backend.StartMonitoring(m.onBackendStateChange); err != nil { - m.Close() - return nil, fmt.Errorf("failed to start monitoring: %w", err) - } - - return m, nil -} - -func (m *Manager) syncStateFromBackend() error { - backendState, err := m.backend.GetCurrentState() - if err != nil { - return err - } - - m.stateMutex.Lock() - m.state.Backend = backendState.Backend - m.state.NetworkStatus = backendState.NetworkStatus - m.state.EthernetIP = backendState.EthernetIP - m.state.EthernetDevice = backendState.EthernetDevice - m.state.EthernetConnected = backendState.EthernetConnected - m.state.EthernetConnectionUuid = backendState.EthernetConnectionUuid - m.state.WiFiIP = backendState.WiFiIP - m.state.WiFiDevice = backendState.WiFiDevice - m.state.WiFiConnected = backendState.WiFiConnected - m.state.WiFiEnabled = backendState.WiFiEnabled - m.state.WiFiSSID = backendState.WiFiSSID - m.state.WiFiBSSID = backendState.WiFiBSSID - m.state.WiFiSignal = backendState.WiFiSignal - m.state.WiFiNetworks = backendState.WiFiNetworks - m.state.WiredConnections = backendState.WiredConnections - m.state.VPNProfiles = backendState.VPNProfiles - m.state.VPNActive = backendState.VPNActive - m.state.IsConnecting = backendState.IsConnecting - m.state.ConnectingSSID = backendState.ConnectingSSID - m.state.LastError = backendState.LastError - m.stateMutex.Unlock() - - return nil -} - -func (m *Manager) onBackendStateChange() { - if err := m.syncStateFromBackend(); err != nil { - log.Errorf("failed to sync state from backend: %v", err) - } - m.notifySubscribers() -} - -func signalChangeSignificant(old, new uint8) bool { - if old == 0 || new == 0 { - return true - } - diff := int(new) - int(old) - if diff < 0 { - diff = -diff - } - return diff >= 5 -} - -func (m *Manager) snapshotState() NetworkState { - m.stateMutex.RLock() - defer m.stateMutex.RUnlock() - s := *m.state - s.WiFiNetworks = append([]WiFiNetwork(nil), m.state.WiFiNetworks...) - s.WiredConnections = append([]WiredConnection(nil), m.state.WiredConnections...) - s.VPNProfiles = append([]VPNProfile(nil), m.state.VPNProfiles...) - s.VPNActive = append([]VPNActive(nil), m.state.VPNActive...) - return s -} - -func stateChangedMeaningfully(old, new *NetworkState) bool { - if old.NetworkStatus != new.NetworkStatus { - return true - } - if old.Preference != new.Preference { - return true - } - if old.EthernetConnected != new.EthernetConnected { - return true - } - if old.EthernetIP != new.EthernetIP { - return true - } - if old.WiFiConnected != new.WiFiConnected { - return true - } - if old.WiFiEnabled != new.WiFiEnabled { - return true - } - if old.WiFiSSID != new.WiFiSSID { - return true - } - if old.WiFiBSSID != new.WiFiBSSID { - return true - } - if old.WiFiIP != new.WiFiIP { - return true - } - if !signalChangeSignificant(old.WiFiSignal, new.WiFiSignal) { - if old.WiFiSignal != new.WiFiSignal { - return false - } - } else if old.WiFiSignal != new.WiFiSignal { - return true - } - if old.IsConnecting != new.IsConnecting { - return true - } - if old.ConnectingSSID != new.ConnectingSSID { - return true - } - if old.LastError != new.LastError { - return true - } - if len(old.WiFiNetworks) != len(new.WiFiNetworks) { - return true - } - if len(old.WiredConnections) != len(new.WiredConnections) { - return true - } - - for i := range old.WiFiNetworks { - oldNet := &old.WiFiNetworks[i] - newNet := &new.WiFiNetworks[i] - if oldNet.SSID != newNet.SSID { - return true - } - if oldNet.Connected != newNet.Connected { - return true - } - if oldNet.Saved != newNet.Saved { - return true - } - if oldNet.Autoconnect != newNet.Autoconnect { - return true - } - } - - for i := range old.WiredConnections { - oldNet := &old.WiredConnections[i] - newNet := &new.WiredConnections[i] - if oldNet.ID != newNet.ID { - return true - } - if oldNet.IsActive != newNet.IsActive { - return true - } - } - - // Check VPN profiles count - if len(old.VPNProfiles) != len(new.VPNProfiles) { - return true - } - - // Check active VPN connections count or state - if len(old.VPNActive) != len(new.VPNActive) { - return true - } - - // Check if any active VPN changed - for i := range old.VPNActive { - oldVPN := &old.VPNActive[i] - newVPN := &new.VPNActive[i] - if oldVPN.UUID != newVPN.UUID { - return true - } - if oldVPN.State != newVPN.State { - return true - } - } - - return false -} - -func (m *Manager) GetState() NetworkState { - return m.snapshotState() -} - -func (m *Manager) Subscribe(id string) chan NetworkState { - ch := make(chan NetworkState, 64) - m.subMutex.Lock() - m.subscribers[id] = ch - m.subMutex.Unlock() - return ch -} - -func (m *Manager) Unsubscribe(id string) { - m.subMutex.Lock() - if ch, ok := m.subscribers[id]; ok { - close(ch) - delete(m.subscribers, id) - } - m.subMutex.Unlock() -} - -func (m *Manager) SubscribeCredentials(id string) chan CredentialPrompt { - ch := make(chan CredentialPrompt, 16) - m.credSubMutex.Lock() - m.credentialSubscribers[id] = ch - m.credSubMutex.Unlock() - return ch -} - -func (m *Manager) UnsubscribeCredentials(id string) { - m.credSubMutex.Lock() - if ch, ok := m.credentialSubscribers[id]; ok { - close(ch) - delete(m.credentialSubscribers, id) - } - m.credSubMutex.Unlock() -} - -func (m *Manager) broadcastCredentialPrompt(prompt CredentialPrompt) { - m.credSubMutex.RLock() - defer m.credSubMutex.RUnlock() - - for _, ch := range m.credentialSubscribers { - select { - case ch <- prompt: - default: - } - } -} - -func (m *Manager) notifier() { - defer m.notifierWg.Done() - const minGap = 100 * time.Millisecond - timer := time.NewTimer(minGap) - timer.Stop() - var pending bool - for { - select { - case <-m.stopChan: - timer.Stop() - return - case <-m.dirty: - if pending { - continue - } - pending = true - timer.Reset(minGap) - case <-timer.C: - if !pending { - continue - } - m.subMutex.RLock() - if len(m.subscribers) == 0 { - m.subMutex.RUnlock() - pending = false - continue - } - - currentState := m.snapshotState() - - if m.lastNotifiedState != nil && !stateChangedMeaningfully(m.lastNotifiedState, ¤tState) { - m.subMutex.RUnlock() - pending = false - continue - } - - for _, ch := range m.subscribers { - select { - case ch <- currentState: - default: - } - } - m.subMutex.RUnlock() - - stateCopy := currentState - m.lastNotifiedState = &stateCopy - pending = false - } - } -} - -func (m *Manager) notifySubscribers() { - select { - case m.dirty <- struct{}{}: - default: - } -} - -func (m *Manager) SetPromptBroker(broker PromptBroker) error { - return m.backend.SetPromptBroker(broker) -} - -func (m *Manager) SubmitCredentials(token string, secrets map[string]string, save bool) error { - return m.backend.SubmitCredentials(token, secrets, save) -} - -func (m *Manager) CancelCredentials(token string) error { - return m.backend.CancelCredentials(token) -} - -func (m *Manager) GetPromptBroker() PromptBroker { - return m.backend.GetPromptBroker() -} - -func (m *Manager) Close() { - close(m.stopChan) - m.notifierWg.Wait() - - if m.backend != nil { - m.backend.Close() - } - - m.subMutex.Lock() - for _, ch := range m.subscribers { - close(ch) - } - m.subscribers = make(map[string]chan NetworkState) - m.subMutex.Unlock() -} - -func (m *Manager) ScanWiFi() error { - return m.backend.ScanWiFi() -} - -func (m *Manager) GetWiFiNetworks() []WiFiNetwork { - m.stateMutex.RLock() - defer m.stateMutex.RUnlock() - networks := make([]WiFiNetwork, len(m.state.WiFiNetworks)) - copy(networks, m.state.WiFiNetworks) - return networks -} - -func (m *Manager) GetNetworkInfo(ssid string) (*WiFiNetwork, error) { - m.stateMutex.RLock() - defer m.stateMutex.RUnlock() - - for _, network := range m.state.WiFiNetworks { - if network.SSID == ssid { - return &network, nil - } - } - - return nil, fmt.Errorf("network not found: %s", ssid) -} - -func (m *Manager) GetNetworkInfoDetailed(ssid string) (*NetworkInfoResponse, error) { - return m.backend.GetWiFiNetworkDetails(ssid) -} - -func (m *Manager) ToggleWiFi() error { - enabled, err := m.backend.GetWiFiEnabled() - if err != nil { - return fmt.Errorf("failed to get WiFi state: %w", err) - } - - err = m.backend.SetWiFiEnabled(!enabled) - if err != nil { - return fmt.Errorf("failed to toggle WiFi: %w", err) - } - - return nil -} - -func (m *Manager) EnableWiFi() error { - err := m.backend.SetWiFiEnabled(true) - if err != nil { - return fmt.Errorf("failed to enable WiFi: %w", err) - } - - return nil -} - -func (m *Manager) DisableWiFi() error { - err := m.backend.SetWiFiEnabled(false) - if err != nil { - return fmt.Errorf("failed to disable WiFi: %w", err) - } - - return nil -} - -func (m *Manager) ConnectWiFi(req ConnectionRequest) error { - return m.backend.ConnectWiFi(req) -} - -func (m *Manager) DisconnectWiFi() error { - return m.backend.DisconnectWiFi() -} - -func (m *Manager) ForgetWiFiNetwork(ssid string) error { - return m.backend.ForgetWiFiNetwork(ssid) -} - -func (m *Manager) GetWiredConfigs() []WiredConnection { - m.stateMutex.RLock() - defer m.stateMutex.RUnlock() - configs := make([]WiredConnection, len(m.state.WiredConnections)) - copy(configs, m.state.WiredConnections) - return configs -} - -func (m *Manager) GetWiredNetworkInfoDetailed(uuid string) (*WiredNetworkInfoResponse, error) { - return m.backend.GetWiredNetworkDetails(uuid) -} - -func (m *Manager) ConnectEthernet() error { - return m.backend.ConnectEthernet() -} - -func (m *Manager) DisconnectEthernet() error { - return m.backend.DisconnectEthernet() -} - -func (m *Manager) activateConnection(uuid string) error { - return m.backend.ActivateWiredConnection(uuid) -} - -func (m *Manager) ListVPNProfiles() ([]VPNProfile, error) { - return m.backend.ListVPNProfiles() -} - -func (m *Manager) ListActiveVPN() ([]VPNActive, error) { - return m.backend.ListActiveVPN() -} - -func (m *Manager) ConnectVPN(uuidOrName string, singleActive bool) error { - return m.backend.ConnectVPN(uuidOrName, singleActive) -} - -func (m *Manager) DisconnectVPN(uuidOrName string) error { - return m.backend.DisconnectVPN(uuidOrName) -} - -func (m *Manager) DisconnectAllVPN() error { - return m.backend.DisconnectAllVPN() -} - -func (m *Manager) ClearVPNCredentials(uuidOrName string) error { - return m.backend.ClearVPNCredentials(uuidOrName) -} - -func (m *Manager) SetWiFiAutoconnect(ssid string, autoconnect bool) error { - return m.backend.SetWiFiAutoconnect(ssid, autoconnect) -} diff --git a/nix/inputs/dms-cli/internal/server/network/manager_test.go b/nix/inputs/dms-cli/internal/server/network/manager_test.go deleted file mode 100644 index f6dbf80..0000000 --- a/nix/inputs/dms-cli/internal/server/network/manager_test.go +++ /dev/null @@ -1,231 +0,0 @@ -package network - -import ( - "sync" - "testing" - "time" - - "github.com/stretchr/testify/assert" -) - -func TestManager_GetState(t *testing.T) { - state := &NetworkState{ - NetworkStatus: StatusWiFi, - WiFiSSID: "TestNetwork", - WiFiConnected: true, - } - - manager := &Manager{ - state: state, - stateMutex: sync.RWMutex{}, - } - - result := manager.GetState() - assert.Equal(t, StatusWiFi, result.NetworkStatus) - assert.Equal(t, "TestNetwork", result.WiFiSSID) - assert.True(t, result.WiFiConnected) -} - -func TestManager_NotifySubscribers(t *testing.T) { - manager := &Manager{ - state: &NetworkState{ - NetworkStatus: StatusWiFi, - }, - stateMutex: sync.RWMutex{}, - subscribers: make(map[string]chan NetworkState), - subMutex: sync.RWMutex{}, - stopChan: make(chan struct{}), - dirty: make(chan struct{}, 1), - } - manager.notifierWg.Add(1) - go manager.notifier() - - // Subscribe a client - ch := make(chan NetworkState, 10) - manager.subMutex.Lock() - manager.subscribers["test-client"] = ch - manager.subMutex.Unlock() - - // Notify subscribers - manager.notifySubscribers() - - // Check that state was sent (wait for debounce timer + some slack) - select { - case state := <-ch: - assert.Equal(t, StatusWiFi, state.NetworkStatus) - case <-time.After(200 * time.Millisecond): - t.Fatal("did not receive state update") - } - - close(manager.stopChan) - manager.notifierWg.Wait() -} - -func TestManager_NotifySubscribers_Debounce(t *testing.T) { - manager := &Manager{ - state: &NetworkState{ - NetworkStatus: StatusWiFi, - }, - stateMutex: sync.RWMutex{}, - subscribers: make(map[string]chan NetworkState), - subMutex: sync.RWMutex{}, - stopChan: make(chan struct{}), - dirty: make(chan struct{}, 1), - } - manager.notifierWg.Add(1) - go manager.notifier() - - // Subscribe a client - ch := make(chan NetworkState, 10) - manager.subMutex.Lock() - manager.subscribers["test-client"] = ch - manager.subMutex.Unlock() - - // Send multiple rapid notifications - manager.notifySubscribers() - manager.notifySubscribers() - manager.notifySubscribers() - - // Should only receive one state update due to debouncing - receivedCount := 0 - timeout := time.After(200 * time.Millisecond) - for { - select { - case <-ch: - receivedCount++ - case <-timeout: - assert.Equal(t, 1, receivedCount, "should receive exactly one debounced update") - close(manager.stopChan) - manager.notifierWg.Wait() - return - } - } -} - -func TestManager_Close(t *testing.T) { - manager := &Manager{ - state: &NetworkState{}, - stateMutex: sync.RWMutex{}, - subscribers: make(map[string]chan NetworkState), - subMutex: sync.RWMutex{}, - stopChan: make(chan struct{}), - } - - // Add subscribers - ch1 := make(chan NetworkState, 1) - ch2 := make(chan NetworkState, 1) - manager.subMutex.Lock() - manager.subscribers["client1"] = ch1 - manager.subscribers["client2"] = ch2 - manager.subMutex.Unlock() - - // Close manager - manager.Close() - - // Check that stopChan is closed - select { - case <-manager.stopChan: - // Expected - case <-time.After(100 * time.Millisecond): - t.Fatal("stopChan not closed") - } - - // Check that subscriber channels are closed - _, ok1 := <-ch1 - _, ok2 := <-ch2 - assert.False(t, ok1, "ch1 should be closed") - assert.False(t, ok2, "ch2 should be closed") - - // Check that subscribers map is reset - assert.Len(t, manager.subscribers, 0) -} - -func TestManager_Subscribe(t *testing.T) { - manager := &Manager{ - state: &NetworkState{}, - subscribers: make(map[string]chan NetworkState), - subMutex: sync.RWMutex{}, - } - - ch := manager.Subscribe("test-client") - assert.NotNil(t, ch) - assert.Equal(t, 64, cap(ch)) - - manager.subMutex.RLock() - _, exists := manager.subscribers["test-client"] - manager.subMutex.RUnlock() - assert.True(t, exists) -} - -func TestManager_Unsubscribe(t *testing.T) { - manager := &Manager{ - state: &NetworkState{}, - subscribers: make(map[string]chan NetworkState), - subMutex: sync.RWMutex{}, - } - - // Subscribe first - ch := manager.Subscribe("test-client") - - // Unsubscribe - manager.Unsubscribe("test-client") - - // Check channel is closed - _, ok := <-ch - assert.False(t, ok) - - // Check client is removed - manager.subMutex.RLock() - _, exists := manager.subscribers["test-client"] - manager.subMutex.RUnlock() - assert.False(t, exists) -} - -func TestNewManager(t *testing.T) { - // This test will fail in environments without NetworkManager D-Bus - // It's primarily for local testing - t.Run("attempts to create manager", func(t *testing.T) { - manager, err := NewManager() - if err != nil { - // Expected in test environments - assert.Nil(t, manager) - } else { - assert.NotNil(t, manager) - assert.NotNil(t, manager.state) - assert.NotNil(t, manager.subscribers) - assert.NotNil(t, manager.stopChan) - - // Clean up - manager.Close() - } - }) -} - -func TestManager_GetState_ThreadSafe(t *testing.T) { - manager := &Manager{ - state: &NetworkState{ - NetworkStatus: StatusWiFi, - WiFiSSID: "TestNetwork", - }, - stateMutex: sync.RWMutex{}, - } - - // Test concurrent reads - done := make(chan bool) - for i := 0; i < 10; i++ { - go func() { - state := manager.GetState() - assert.Equal(t, StatusWiFi, state.NetworkStatus) - done <- true - }() - } - - // Wait for all goroutines to complete - for i := 0; i < 10; i++ { - select { - case <-done: - case <-time.After(1 * time.Second): - t.Fatal("timeout waiting for goroutines") - } - } -} diff --git a/nix/inputs/dms-cli/internal/server/network/monitor.go b/nix/inputs/dms-cli/internal/server/network/monitor.go deleted file mode 100644 index 1ae2e9d..0000000 --- a/nix/inputs/dms-cli/internal/server/network/monitor.go +++ /dev/null @@ -1 +0,0 @@ -package network diff --git a/nix/inputs/dms-cli/internal/server/network/priority.go b/nix/inputs/dms-cli/internal/server/network/priority.go deleted file mode 100644 index d047bcc..0000000 --- a/nix/inputs/dms-cli/internal/server/network/priority.go +++ /dev/null @@ -1,138 +0,0 @@ -package network - -import ( - "fmt" - "time" - - "github.com/Wifx/gonetworkmanager/v2" -) - -func (m *Manager) SetConnectionPreference(pref ConnectionPreference) error { - switch pref { - case PreferenceWiFi, PreferenceEthernet, PreferenceAuto: - default: - return fmt.Errorf("invalid preference: %s", pref) - } - - m.stateMutex.Lock() - m.state.Preference = pref - m.stateMutex.Unlock() - - if _, ok := m.backend.(*NetworkManagerBackend); !ok { - m.notifySubscribers() - return nil - } - - switch pref { - case PreferenceWiFi: - return m.prioritizeWiFi() - case PreferenceEthernet: - return m.prioritizeEthernet() - case PreferenceAuto: - return m.balancePriorities() - } - - return nil -} - -func (m *Manager) prioritizeWiFi() error { - if err := m.setConnectionMetrics("802-11-wireless", 50); err != nil { - return err - } - - if err := m.setConnectionMetrics("802-3-ethernet", 100); err != nil { - return err - } - - m.notifySubscribers() - return nil -} - -func (m *Manager) prioritizeEthernet() error { - if err := m.setConnectionMetrics("802-3-ethernet", 50); err != nil { - return err - } - - if err := m.setConnectionMetrics("802-11-wireless", 100); err != nil { - return err - } - - m.notifySubscribers() - return nil -} - -func (m *Manager) balancePriorities() error { - if err := m.setConnectionMetrics("802-3-ethernet", 50); err != nil { - return err - } - - if err := m.setConnectionMetrics("802-11-wireless", 50); err != nil { - return err - } - - m.notifySubscribers() - return nil -} - -func (m *Manager) setConnectionMetrics(connType string, metric uint32) error { - settingsMgr, err := gonetworkmanager.NewSettings() - if err != nil { - return fmt.Errorf("failed to get settings: %w", err) - } - - connections, err := settingsMgr.ListConnections() - if err != nil { - return fmt.Errorf("failed to get connections: %w", err) - } - - for _, conn := range connections { - connSettings, err := conn.GetSettings() - if err != nil { - continue - } - - if connMeta, ok := connSettings["connection"]; ok { - if cType, ok := connMeta["type"].(string); ok && cType == connType { - if connSettings["ipv4"] == nil { - connSettings["ipv4"] = make(map[string]interface{}) - } - if ipv4Map := connSettings["ipv4"]; ipv4Map != nil { - ipv4Map["route-metric"] = int64(metric) - } - - if connSettings["ipv6"] == nil { - connSettings["ipv6"] = make(map[string]interface{}) - } - if ipv6Map := connSettings["ipv6"]; ipv6Map != nil { - ipv6Map["route-metric"] = int64(metric) - } - - err = conn.Update(connSettings) - if err != nil { - continue - } - } - } - } - - return nil -} - -func (m *Manager) GetConnectionPreference() ConnectionPreference { - m.stateMutex.RLock() - defer m.stateMutex.RUnlock() - return m.state.Preference -} - -func (m *Manager) WasRecentlyFailed(ssid string) bool { - if nm, ok := m.backend.(*NetworkManagerBackend); ok { - nm.failedMutex.RLock() - defer nm.failedMutex.RUnlock() - - if nm.lastFailedSSID == ssid { - elapsed := time.Now().Unix() - nm.lastFailedTime - return elapsed < 10 - } - } - return false -} diff --git a/nix/inputs/dms-cli/internal/server/network/priority_test.go b/nix/inputs/dms-cli/internal/server/network/priority_test.go deleted file mode 100644 index c0c65e3..0000000 --- a/nix/inputs/dms-cli/internal/server/network/priority_test.go +++ /dev/null @@ -1,50 +0,0 @@ -package network - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestManager_SetConnectionPreference(t *testing.T) { - t.Run("invalid preference", func(t *testing.T) { - manager := &Manager{ - state: &NetworkState{ - Preference: PreferenceAuto, - }, - } - - err := manager.SetConnectionPreference(ConnectionPreference("invalid")) - assert.Error(t, err) - assert.Contains(t, err.Error(), "invalid preference") - }) -} - -func TestManager_GetConnectionPreference(t *testing.T) { - tests := []struct { - name string - preference ConnectionPreference - }{ - {"auto", PreferenceAuto}, - {"wifi", PreferenceWiFi}, - {"ethernet", PreferenceEthernet}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - manager := &Manager{ - state: &NetworkState{ - Preference: tt.preference, - }, - } - - result := manager.GetConnectionPreference() - assert.Equal(t, tt.preference, result) - }) - } -} - -// Note: Full testing of priority operations would require mocking NetworkManager -// D-Bus interfaces. The tests above cover the basic logic and error handling. -// Integration tests would be needed for complete coverage of network connection -// priority updates and reactivation. diff --git a/nix/inputs/dms-cli/internal/server/network/subscription_broker.go b/nix/inputs/dms-cli/internal/server/network/subscription_broker.go deleted file mode 100644 index 3697751..0000000 --- a/nix/inputs/dms-cli/internal/server/network/subscription_broker.go +++ /dev/null @@ -1,146 +0,0 @@ -package network - -import ( - "context" - "fmt" - "sync" - - "github.com/AvengeMedia/danklinux/internal/errdefs" - "github.com/AvengeMedia/danklinux/internal/log" -) - -type SubscriptionBroker struct { - mu sync.RWMutex - pending map[string]chan PromptReply - requests map[string]PromptRequest - pathSettingToToken map[string]string - broadcastPrompt func(CredentialPrompt) -} - -func NewSubscriptionBroker(broadcastPrompt func(CredentialPrompt)) PromptBroker { - return &SubscriptionBroker{ - pending: make(map[string]chan PromptReply), - requests: make(map[string]PromptRequest), - pathSettingToToken: make(map[string]string), - broadcastPrompt: broadcastPrompt, - } -} - -func (b *SubscriptionBroker) Ask(ctx context.Context, req PromptRequest) (string, error) { - pathSettingKey := fmt.Sprintf("%s:%s", req.ConnectionPath, req.SettingName) - - b.mu.Lock() - existingToken, alreadyPending := b.pathSettingToToken[pathSettingKey] - b.mu.Unlock() - - if alreadyPending { - log.Infof("[SubscriptionBroker] Duplicate prompt for %s, returning existing token", pathSettingKey) - return existingToken, nil - } - - token, err := generateToken() - if err != nil { - return "", err - } - - replyChan := make(chan PromptReply, 1) - b.mu.Lock() - b.pending[token] = replyChan - b.requests[token] = req - b.pathSettingToToken[pathSettingKey] = token - b.mu.Unlock() - - if b.broadcastPrompt != nil { - prompt := CredentialPrompt{ - Token: token, - Name: req.Name, - SSID: req.SSID, - ConnType: req.ConnType, - VpnService: req.VpnService, - Setting: req.SettingName, - Fields: req.Fields, - Hints: req.Hints, - Reason: req.Reason, - ConnectionId: req.ConnectionId, - ConnectionUuid: req.ConnectionUuid, - } - b.broadcastPrompt(prompt) - } - - return token, nil -} - -func (b *SubscriptionBroker) Wait(ctx context.Context, token string) (PromptReply, error) { - b.mu.RLock() - replyChan, exists := b.pending[token] - b.mu.RUnlock() - - if !exists { - return PromptReply{}, fmt.Errorf("unknown token: %s", token) - } - - select { - case <-ctx.Done(): - b.cleanup(token) - return PromptReply{}, errdefs.ErrSecretPromptTimeout - case reply := <-replyChan: - b.cleanup(token) - if reply.Cancel { - return reply, errdefs.ErrSecretPromptCancelled - } - return reply, nil - } -} - -func (b *SubscriptionBroker) Resolve(token string, reply PromptReply) error { - b.mu.RLock() - replyChan, exists := b.pending[token] - b.mu.RUnlock() - - if !exists { - log.Warnf("[SubscriptionBroker] Resolve: unknown or expired token: %s", token) - return fmt.Errorf("unknown or expired token: %s", token) - } - - select { - case replyChan <- reply: - return nil - default: - log.Warnf("[SubscriptionBroker] Resolve: failed to deliver reply for token %s (channel full or closed)", token) - return fmt.Errorf("failed to deliver reply for token: %s", token) - } -} - -func (b *SubscriptionBroker) cleanup(token string) { - b.mu.Lock() - defer b.mu.Unlock() - - if req, exists := b.requests[token]; exists { - pathSettingKey := fmt.Sprintf("%s:%s", req.ConnectionPath, req.SettingName) - delete(b.pathSettingToToken, pathSettingKey) - } - - delete(b.pending, token) - delete(b.requests, token) -} - -func (b *SubscriptionBroker) Cancel(path string, setting string) error { - pathSettingKey := fmt.Sprintf("%s:%s", path, setting) - - b.mu.Lock() - token, exists := b.pathSettingToToken[pathSettingKey] - b.mu.Unlock() - - if !exists { - log.Infof("[SubscriptionBroker] Cancel: no pending prompt for %s", pathSettingKey) - return nil - } - - log.Infof("[SubscriptionBroker] Cancelling prompt for %s (token=%s)", pathSettingKey, token) - - reply := PromptReply{ - Cancel: true, - } - - return b.Resolve(token, reply) -} diff --git a/nix/inputs/dms-cli/internal/server/network/testing.go b/nix/inputs/dms-cli/internal/server/network/testing.go deleted file mode 100644 index f4ff197..0000000 --- a/nix/inputs/dms-cli/internal/server/network/testing.go +++ /dev/null @@ -1,15 +0,0 @@ -package network - -// NewTestManager creates a Manager for testing with a provided backend -func NewTestManager(backend Backend, state *NetworkState) *Manager { - if state == nil { - state = &NetworkState{} - } - return &Manager{ - backend: backend, - state: state, - subscribers: make(map[string]chan NetworkState), - stopChan: make(chan struct{}), - dirty: make(chan struct{}, 1), - } -} diff --git a/nix/inputs/dms-cli/internal/server/network/types.go b/nix/inputs/dms-cli/internal/server/network/types.go deleted file mode 100644 index a46a916..0000000 --- a/nix/inputs/dms-cli/internal/server/network/types.go +++ /dev/null @@ -1,190 +0,0 @@ -package network - -import ( - "sync" - - "github.com/godbus/dbus/v5" -) - -type NetworkStatus string - -const ( - StatusDisconnected NetworkStatus = "disconnected" - StatusEthernet NetworkStatus = "ethernet" - StatusWiFi NetworkStatus = "wifi" - StatusVPN NetworkStatus = "vpn" -) - -type ConnectionPreference string - -const ( - PreferenceAuto ConnectionPreference = "auto" - PreferenceWiFi ConnectionPreference = "wifi" - PreferenceEthernet ConnectionPreference = "ethernet" -) - -type WiFiNetwork struct { - SSID string `json:"ssid"` - BSSID string `json:"bssid"` - Signal uint8 `json:"signal"` - Secured bool `json:"secured"` - Enterprise bool `json:"enterprise"` - Connected bool `json:"connected"` - Saved bool `json:"saved"` - Autoconnect bool `json:"autoconnect"` - Frequency uint32 `json:"frequency"` - Mode string `json:"mode"` - Rate uint32 `json:"rate"` - Channel uint32 `json:"channel"` -} - -type VPNProfile struct { - Name string `json:"name"` - UUID string `json:"uuid"` - Type string `json:"type"` - ServiceType string `json:"serviceType"` -} - -type VPNActive struct { - Name string `json:"name"` - UUID string `json:"uuid"` - Device string `json:"device,omitempty"` - State string `json:"state,omitempty"` - Type string `json:"type"` - Plugin string `json:"serviceType"` -} - -type VPNState struct { - Profiles []VPNProfile `json:"profiles"` - Active []VPNActive `json:"activeConnections"` -} - -type NetworkState struct { - Backend string `json:"backend"` - NetworkStatus NetworkStatus `json:"networkStatus"` - Preference ConnectionPreference `json:"preference"` - EthernetIP string `json:"ethernetIP"` - EthernetDevice string `json:"ethernetDevice"` - EthernetConnected bool `json:"ethernetConnected"` - EthernetConnectionUuid string `json:"ethernetConnectionUuid"` - WiFiIP string `json:"wifiIP"` - WiFiDevice string `json:"wifiDevice"` - WiFiConnected bool `json:"wifiConnected"` - WiFiEnabled bool `json:"wifiEnabled"` - WiFiSSID string `json:"wifiSSID"` - WiFiBSSID string `json:"wifiBSSID"` - WiFiSignal uint8 `json:"wifiSignal"` - WiFiNetworks []WiFiNetwork `json:"wifiNetworks"` - WiredConnections []WiredConnection `json:"wiredConnections"` - VPNProfiles []VPNProfile `json:"vpnProfiles"` - VPNActive []VPNActive `json:"vpnActive"` - IsConnecting bool `json:"isConnecting"` - ConnectingSSID string `json:"connectingSSID"` - LastError string `json:"lastError"` -} - -type ConnectionRequest struct { - SSID string `json:"ssid"` - Password string `json:"password,omitempty"` - Username string `json:"username,omitempty"` - AnonymousIdentity string `json:"anonymousIdentity,omitempty"` - DomainSuffixMatch string `json:"domainSuffixMatch,omitempty"` - Interactive bool `json:"interactive,omitempty"` -} - -type WiredConnection struct { - Path dbus.ObjectPath `json:"path"` - ID string `json:"id"` - UUID string `json:"uuid"` - Type string `json:"type"` - IsActive bool `json:"isActive"` -} - -type PriorityUpdate struct { - Preference ConnectionPreference `json:"preference"` -} - -type Manager struct { - backend Backend - state *NetworkState - stateMutex sync.RWMutex - subscribers map[string]chan NetworkState - subMutex sync.RWMutex - stopChan chan struct{} - dirty chan struct{} - notifierWg sync.WaitGroup - lastNotifiedState *NetworkState - credentialSubscribers map[string]chan CredentialPrompt - credSubMutex sync.RWMutex -} - -type EventType string - -const ( - EventStateChanged EventType = "state_changed" - EventNetworksUpdated EventType = "networks_updated" - EventConnecting EventType = "connecting" - EventConnected EventType = "connected" - EventDisconnected EventType = "disconnected" - EventError EventType = "error" -) - -type NetworkEvent struct { - Type EventType `json:"type"` - Data NetworkState `json:"data"` -} - -type PromptRequest struct { - Name string `json:"name"` - SSID string `json:"ssid"` - ConnType string `json:"connType"` - VpnService string `json:"vpnService"` - SettingName string `json:"setting"` - Fields []string `json:"fields"` - Hints []string `json:"hints"` - Reason string `json:"reason"` - ConnectionId string `json:"connectionId"` - ConnectionUuid string `json:"connectionUuid"` - ConnectionPath string `json:"connectionPath"` -} - -type PromptReply struct { - Secrets map[string]string `json:"secrets"` - Save bool `json:"save"` - Cancel bool `json:"cancel"` -} - -type CredentialPrompt struct { - Token string `json:"token"` - Name string `json:"name"` - SSID string `json:"ssid"` - ConnType string `json:"connType"` - VpnService string `json:"vpnService"` - Setting string `json:"setting"` - Fields []string `json:"fields"` - Hints []string `json:"hints"` - Reason string `json:"reason"` - ConnectionId string `json:"connectionId"` - ConnectionUuid string `json:"connectionUuid"` -} - -type NetworkInfoResponse struct { - SSID string `json:"ssid"` - Bands []WiFiNetwork `json:"bands"` -} - -type WiredNetworkInfoResponse struct { - UUID string `json:"uuid"` - IFace string `json:"iface"` - Driver string `json:"driver"` - HwAddr string `json:"hwAddr"` - Speed string `json:"speed"` - IPv4 WiredIPConfig `json:"IPv4s"` - IPv6 WiredIPConfig `json:"IPv6s"` -} - -type WiredIPConfig struct { - IPs []string `json:"ips"` - Gateway string `json:"gateway"` - DNS string `json:"dns"` -} diff --git a/nix/inputs/dms-cli/internal/server/network/types_test.go b/nix/inputs/dms-cli/internal/server/network/types_test.go deleted file mode 100644 index 1c3e0bf..0000000 --- a/nix/inputs/dms-cli/internal/server/network/types_test.go +++ /dev/null @@ -1,178 +0,0 @@ -package network - -import ( - "encoding/json" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestNetworkStatus_Constants(t *testing.T) { - assert.Equal(t, NetworkStatus("disconnected"), StatusDisconnected) - assert.Equal(t, NetworkStatus("ethernet"), StatusEthernet) - assert.Equal(t, NetworkStatus("wifi"), StatusWiFi) -} - -func TestConnectionPreference_Constants(t *testing.T) { - assert.Equal(t, ConnectionPreference("auto"), PreferenceAuto) - assert.Equal(t, ConnectionPreference("wifi"), PreferenceWiFi) - assert.Equal(t, ConnectionPreference("ethernet"), PreferenceEthernet) -} - -func TestEventType_Constants(t *testing.T) { - assert.Equal(t, EventType("state_changed"), EventStateChanged) - assert.Equal(t, EventType("networks_updated"), EventNetworksUpdated) - assert.Equal(t, EventType("connecting"), EventConnecting) - assert.Equal(t, EventType("connected"), EventConnected) - assert.Equal(t, EventType("disconnected"), EventDisconnected) - assert.Equal(t, EventType("error"), EventError) -} - -func TestWiFiNetwork_JSON(t *testing.T) { - network := WiFiNetwork{ - SSID: "TestNetwork", - BSSID: "00:11:22:33:44:55", - Signal: 85, - Secured: true, - Enterprise: false, - Connected: true, - Saved: true, - Frequency: 2437, - Mode: "infrastructure", - Rate: 300, - Channel: 6, - } - - data, err := json.Marshal(network) - require.NoError(t, err) - - var decoded WiFiNetwork - err = json.Unmarshal(data, &decoded) - require.NoError(t, err) - - assert.Equal(t, network.SSID, decoded.SSID) - assert.Equal(t, network.BSSID, decoded.BSSID) - assert.Equal(t, network.Signal, decoded.Signal) - assert.Equal(t, network.Secured, decoded.Secured) - assert.Equal(t, network.Enterprise, decoded.Enterprise) - assert.Equal(t, network.Connected, decoded.Connected) - assert.Equal(t, network.Saved, decoded.Saved) - assert.Equal(t, network.Frequency, decoded.Frequency) - assert.Equal(t, network.Mode, decoded.Mode) - assert.Equal(t, network.Rate, decoded.Rate) - assert.Equal(t, network.Channel, decoded.Channel) -} - -func TestNetworkState_JSON(t *testing.T) { - state := NetworkState{ - NetworkStatus: StatusWiFi, - Preference: PreferenceAuto, - EthernetIP: "192.168.1.100", - EthernetDevice: "eth0", - EthernetConnected: false, - WiFiIP: "192.168.1.101", - WiFiDevice: "wlan0", - WiFiConnected: true, - WiFiEnabled: true, - WiFiSSID: "TestNetwork", - WiFiBSSID: "00:11:22:33:44:55", - WiFiSignal: 85, - WiFiNetworks: []WiFiNetwork{ - {SSID: "Network1", Signal: 90}, - {SSID: "Network2", Signal: 60}, - }, - IsConnecting: false, - ConnectingSSID: "", - LastError: "", - } - - data, err := json.Marshal(state) - require.NoError(t, err) - - var decoded NetworkState - err = json.Unmarshal(data, &decoded) - require.NoError(t, err) - - assert.Equal(t, state.NetworkStatus, decoded.NetworkStatus) - assert.Equal(t, state.Preference, decoded.Preference) - assert.Equal(t, state.WiFiIP, decoded.WiFiIP) - assert.Equal(t, state.WiFiSSID, decoded.WiFiSSID) - assert.Equal(t, len(state.WiFiNetworks), len(decoded.WiFiNetworks)) -} - -func TestConnectionRequest_JSON(t *testing.T) { - t.Run("with password", func(t *testing.T) { - req := ConnectionRequest{ - SSID: "TestNetwork", - Password: "testpass123", - } - - data, err := json.Marshal(req) - require.NoError(t, err) - - var decoded ConnectionRequest - err = json.Unmarshal(data, &decoded) - require.NoError(t, err) - - assert.Equal(t, req.SSID, decoded.SSID) - assert.Equal(t, req.Password, decoded.Password) - assert.Empty(t, decoded.Username) - }) - - t.Run("with username and password (enterprise)", func(t *testing.T) { - req := ConnectionRequest{ - SSID: "EnterpriseNetwork", - Password: "testpass123", - Username: "testuser", - } - - data, err := json.Marshal(req) - require.NoError(t, err) - - var decoded ConnectionRequest - err = json.Unmarshal(data, &decoded) - require.NoError(t, err) - - assert.Equal(t, req.SSID, decoded.SSID) - assert.Equal(t, req.Password, decoded.Password) - assert.Equal(t, req.Username, decoded.Username) - }) -} - -func TestPriorityUpdate_JSON(t *testing.T) { - update := PriorityUpdate{ - Preference: PreferenceWiFi, - } - - data, err := json.Marshal(update) - require.NoError(t, err) - - var decoded PriorityUpdate - err = json.Unmarshal(data, &decoded) - require.NoError(t, err) - - assert.Equal(t, update.Preference, decoded.Preference) -} - -func TestNetworkEvent_JSON(t *testing.T) { - event := NetworkEvent{ - Type: EventStateChanged, - Data: NetworkState{ - NetworkStatus: StatusWiFi, - WiFiSSID: "TestNetwork", - WiFiConnected: true, - }, - } - - data, err := json.Marshal(event) - require.NoError(t, err) - - var decoded NetworkEvent - err = json.Unmarshal(data, &decoded) - require.NoError(t, err) - - assert.Equal(t, event.Type, decoded.Type) - assert.Equal(t, event.Data.NetworkStatus, decoded.Data.NetworkStatus) - assert.Equal(t, event.Data.WiFiSSID, decoded.Data.WiFiSSID) -} diff --git a/nix/inputs/dms-cli/internal/server/network/wifi_test.go b/nix/inputs/dms-cli/internal/server/network/wifi_test.go deleted file mode 100644 index c771e75..0000000 --- a/nix/inputs/dms-cli/internal/server/network/wifi_test.go +++ /dev/null @@ -1,152 +0,0 @@ -package network - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestFrequencyToChannel(t *testing.T) { - tests := []struct { - name string - frequency uint32 - channel uint32 - }{ - {"2.4 GHz channel 1", 2412, 1}, - {"2.4 GHz channel 6", 2437, 6}, - {"2.4 GHz channel 11", 2462, 11}, - {"2.4 GHz channel 14", 2484, 14}, - {"5 GHz channel 36", 5180, 36}, - {"5 GHz channel 40", 5200, 40}, - {"5 GHz channel 165", 5825, 165}, - {"6 GHz channel 1", 5955, 1}, - {"6 GHz channel 233", 7115, 233}, - {"Unknown frequency", 1000, 0}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := frequencyToChannel(tt.frequency) - assert.Equal(t, tt.channel, result) - }) - } -} - -func TestSortWiFiNetworks(t *testing.T) { - t.Run("connected network comes first", func(t *testing.T) { - networks := []WiFiNetwork{ - {SSID: "Network1", Signal: 90, Connected: false}, - {SSID: "Network2", Signal: 80, Connected: true}, - {SSID: "Network3", Signal: 70, Connected: false}, - } - - sortWiFiNetworks(networks) - - assert.Equal(t, "Network2", networks[0].SSID) - assert.True(t, networks[0].Connected) - }) - - t.Run("sorts by signal strength", func(t *testing.T) { - networks := []WiFiNetwork{ - {SSID: "Weak", Signal: 40, Secured: true}, - {SSID: "Strong", Signal: 90, Secured: true}, - {SSID: "Medium", Signal: 60, Secured: true}, - } - - sortWiFiNetworks(networks) - - assert.Equal(t, "Strong", networks[0].SSID) - assert.Equal(t, "Medium", networks[1].SSID) - assert.Equal(t, "Weak", networks[2].SSID) - }) - - t.Run("prioritizes open networks with good signal", func(t *testing.T) { - networks := []WiFiNetwork{ - {SSID: "SecureWeak", Signal: 40, Secured: true}, - {SSID: "OpenStrong", Signal: 60, Secured: false}, - {SSID: "SecureStrong", Signal: 90, Secured: true}, - } - - sortWiFiNetworks(networks) - - // The sorting gives priority to open networks with good signal (>= 50) - // OpenStrong (60 signal, open) should come before SecureWeak (40 signal, secured) - assert.Equal(t, "OpenStrong", networks[0].SSID) - - // Verify open network comes before weak secure network - openIdx := -1 - weakSecureIdx := -1 - for i, n := range networks { - if n.SSID == "OpenStrong" { - openIdx = i - } - if n.SSID == "SecureWeak" { - weakSecureIdx = i - } - } - assert.Less(t, openIdx, weakSecureIdx, "OpenStrong should come before SecureWeak") - }) - - t.Run("prioritizes saved networks after connected", func(t *testing.T) { - networks := []WiFiNetwork{ - {SSID: "UnsavedStrong", Signal: 95, Saved: false}, - {SSID: "SavedMedium", Signal: 60, Saved: true}, - {SSID: "SavedWeak", Signal: 50, Saved: true}, - {SSID: "UnsavedMedium", Signal: 70, Saved: false}, - } - - sortWiFiNetworks(networks) - - assert.Equal(t, "SavedMedium", networks[0].SSID) - assert.Equal(t, "SavedWeak", networks[1].SSID) - assert.Equal(t, "UnsavedStrong", networks[2].SSID) - assert.Equal(t, "UnsavedMedium", networks[3].SSID) - }) -} - -func TestManager_GetWiFiNetworks(t *testing.T) { - manager := &Manager{ - state: &NetworkState{ - WiFiNetworks: []WiFiNetwork{ - {SSID: "Network1", Signal: 90}, - {SSID: "Network2", Signal: 80}, - }, - }, - } - - networks := manager.GetWiFiNetworks() - - assert.Len(t, networks, 2) - assert.Equal(t, "Network1", networks[0].SSID) - assert.Equal(t, "Network2", networks[1].SSID) - - // Verify it's a copy, not the original - networks[0].SSID = "Modified" - assert.Equal(t, "Network1", manager.state.WiFiNetworks[0].SSID) -} - -func TestManager_GetNetworkInfo(t *testing.T) { - manager := &Manager{ - state: &NetworkState{ - WiFiNetworks: []WiFiNetwork{ - {SSID: "Network1", Signal: 90, BSSID: "00:11:22:33:44:55"}, - {SSID: "Network2", Signal: 80, BSSID: "AA:BB:CC:DD:EE:FF"}, - }, - }, - } - - t.Run("finds existing network", func(t *testing.T) { - network, err := manager.GetNetworkInfo("Network1") - assert.NoError(t, err) - assert.NotNil(t, network) - assert.Equal(t, "Network1", network.SSID) - assert.Equal(t, uint8(90), network.Signal) - }) - - t.Run("returns error for non-existent network", func(t *testing.T) { - network, err := manager.GetNetworkInfo("NonExistent") - assert.Error(t, err) - assert.Nil(t, network) - assert.Contains(t, err.Error(), "network not found") - }) -} diff --git a/nix/inputs/dms-cli/internal/server/network/wired_test.go b/nix/inputs/dms-cli/internal/server/network/wired_test.go deleted file mode 100644 index 175b343..0000000 --- a/nix/inputs/dms-cli/internal/server/network/wired_test.go +++ /dev/null @@ -1,23 +0,0 @@ -package network - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestManager_GetWiredConfigs(t *testing.T) { - manager := &Manager{ - state: &NetworkState{ - EthernetConnected: true, - WiredConnections: []WiredConnection{ - {ID: "Test", IsActive: true}, - }, - }, - } - - configs := manager.GetWiredConfigs() - - assert.Len(t, configs, 1) - assert.Equal(t, "Test", configs[0].ID) -} diff --git a/nix/inputs/dms-cli/internal/server/plugins/handlers.go b/nix/inputs/dms-cli/internal/server/plugins/handlers.go deleted file mode 100644 index 0035049..0000000 --- a/nix/inputs/dms-cli/internal/server/plugins/handlers.go +++ /dev/null @@ -1,27 +0,0 @@ -package plugins - -import ( - "fmt" - "net" - - "github.com/AvengeMedia/danklinux/internal/server/models" -) - -func HandleRequest(conn net.Conn, req models.Request) { - switch req.Method { - case "plugins.list": - HandleList(conn, req) - case "plugins.listInstalled": - HandleListInstalled(conn, req) - case "plugins.install": - HandleInstall(conn, req) - case "plugins.uninstall": - HandleUninstall(conn, req) - case "plugins.update": - HandleUpdate(conn, req) - case "plugins.search": - HandleSearch(conn, req) - default: - models.RespondError(conn, req.ID, fmt.Sprintf("unknown method: %s", req.Method)) - } -} diff --git a/nix/inputs/dms-cli/internal/server/plugins/handlers_test.go b/nix/inputs/dms-cli/internal/server/plugins/handlers_test.go deleted file mode 100644 index e21d052..0000000 --- a/nix/inputs/dms-cli/internal/server/plugins/handlers_test.go +++ /dev/null @@ -1,196 +0,0 @@ -package plugins - -import ( - "encoding/json" - "testing" - - "github.com/AvengeMedia/danklinux/internal/mocks/net" - "github.com/AvengeMedia/danklinux/internal/server/models" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" -) - -func TestHandleList(t *testing.T) { - conn := net.NewMockConn(t) - conn.EXPECT().Write(mock.Anything).Return(0, nil).Maybe() - - req := models.Request{ - ID: 123, - Method: "plugins.list", - Params: map[string]interface{}{}, - } - - HandleList(conn, req) -} - -func TestHandleListInstalled(t *testing.T) { - conn := net.NewMockConn(t) - conn.EXPECT().Write(mock.Anything).Return(0, nil).Maybe() - - req := models.Request{ - ID: 123, - Method: "plugins.listInstalled", - Params: map[string]interface{}{}, - } - - HandleListInstalled(conn, req) -} - -func TestHandleInstallMissingName(t *testing.T) { - conn := net.NewMockConn(t) - var written []byte - conn.EXPECT().Write(mock.Anything).RunAndReturn(func(b []byte) (int, error) { - written = b - return len(b), nil - }).Maybe() - - req := models.Request{ - ID: 123, - Method: "plugins.install", - Params: map[string]interface{}{}, - } - - HandleInstall(conn, req) - - var resp models.Response[SuccessResult] - err := json.Unmarshal(written, &resp) - assert.NoError(t, err) - assert.NotEmpty(t, resp.Error) - assert.Contains(t, resp.Error, "missing or invalid 'name' parameter") -} - -func TestHandleInstallInvalidName(t *testing.T) { - conn := net.NewMockConn(t) - var written []byte - conn.EXPECT().Write(mock.Anything).RunAndReturn(func(b []byte) (int, error) { - written = b - return len(b), nil - }).Maybe() - - req := models.Request{ - ID: 123, - Method: "plugins.install", - Params: map[string]interface{}{ - "name": 123, - }, - } - - HandleInstall(conn, req) - - var resp models.Response[SuccessResult] - err := json.Unmarshal(written, &resp) - assert.NoError(t, err) - assert.NotEmpty(t, resp.Error) -} - -func TestHandleUninstallMissingName(t *testing.T) { - conn := net.NewMockConn(t) - var written []byte - conn.EXPECT().Write(mock.Anything).RunAndReturn(func(b []byte) (int, error) { - written = b - return len(b), nil - }).Maybe() - - req := models.Request{ - ID: 123, - Method: "plugins.uninstall", - Params: map[string]interface{}{}, - } - - HandleUninstall(conn, req) - - var resp models.Response[SuccessResult] - err := json.Unmarshal(written, &resp) - assert.NoError(t, err) - assert.NotEmpty(t, resp.Error) -} - -func TestHandleUpdateMissingName(t *testing.T) { - conn := net.NewMockConn(t) - var written []byte - conn.EXPECT().Write(mock.Anything).RunAndReturn(func(b []byte) (int, error) { - written = b - return len(b), nil - }).Maybe() - - req := models.Request{ - ID: 123, - Method: "plugins.update", - Params: map[string]interface{}{}, - } - - HandleUpdate(conn, req) - - var resp models.Response[SuccessResult] - err := json.Unmarshal(written, &resp) - assert.NoError(t, err) - assert.NotEmpty(t, resp.Error) -} - -func TestHandleSearchMissingQuery(t *testing.T) { - conn := net.NewMockConn(t) - var written []byte - conn.EXPECT().Write(mock.Anything).RunAndReturn(func(b []byte) (int, error) { - written = b - return len(b), nil - }).Maybe() - - req := models.Request{ - ID: 123, - Method: "plugins.search", - Params: map[string]interface{}{}, - } - - HandleSearch(conn, req) - - var resp models.Response[[]PluginInfo] - err := json.Unmarshal(written, &resp) - assert.NoError(t, err) - assert.NotEmpty(t, resp.Error) -} - -func TestSortPluginInfoByFirstParty(t *testing.T) { - plugins := []PluginInfo{ - {Name: "third-party", Repo: "https://github.com/other/test"}, - {Name: "first-party", Repo: "https://github.com/AvengeMedia/test"}, - } - - SortPluginInfoByFirstParty(plugins) - - assert.Equal(t, "first-party", plugins[0].Name) - assert.Equal(t, "third-party", plugins[1].Name) -} - -func TestPluginInfoJSON(t *testing.T) { - info := PluginInfo{ - Name: "test", - Description: "test description", - Installed: true, - FirstParty: true, - } - - data, err := json.Marshal(info) - assert.NoError(t, err) - - var unmarshaled PluginInfo - err = json.Unmarshal(data, &unmarshaled) - assert.NoError(t, err) - assert.Equal(t, info.Name, unmarshaled.Name) - assert.Equal(t, info.Installed, unmarshaled.Installed) -} - -func TestSuccessResult(t *testing.T) { - result := SuccessResult{ - Success: true, - Message: "test message", - } - - data, err := json.Marshal(result) - assert.NoError(t, err) - - var unmarshaled SuccessResult - err = json.Unmarshal(data, &unmarshaled) - assert.NoError(t, err) - assert.True(t, unmarshaled.Success) - assert.Equal(t, "test message", unmarshaled.Message) -} diff --git a/nix/inputs/dms-cli/internal/server/plugins/install.go b/nix/inputs/dms-cli/internal/server/plugins/install.go deleted file mode 100644 index 2ef6a8b..0000000 --- a/nix/inputs/dms-cli/internal/server/plugins/install.go +++ /dev/null @@ -1,69 +0,0 @@ -package plugins - -import ( - "fmt" - "net" - - "github.com/AvengeMedia/danklinux/internal/plugins" - "github.com/AvengeMedia/danklinux/internal/server/models" -) - -func HandleInstall(conn net.Conn, req models.Request) { - idOrName, ok := req.Params["name"].(string) - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'name' parameter") - return - } - - registry, err := plugins.NewRegistry() - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to create registry: %v", err)) - return - } - - pluginList, err := registry.List() - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to list plugins: %v", err)) - return - } - - // First, try to find by ID (preferred method) - var plugin *plugins.Plugin - for _, p := range pluginList { - if p.ID == idOrName { - plugin = &p - break - } - } - - // Fallback to name for backward compatibility - if plugin == nil { - for _, p := range pluginList { - if p.Name == idOrName { - plugin = &p - break - } - } - } - - if plugin == nil { - models.RespondError(conn, req.ID, fmt.Sprintf("plugin not found: %s", idOrName)) - return - } - - manager, err := plugins.NewManager() - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to create manager: %v", err)) - return - } - - if err := manager.Install(*plugin); err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to install plugin: %v", err)) - return - } - - models.Respond(conn, req.ID, SuccessResult{ - Success: true, - Message: fmt.Sprintf("plugin installed: %s", plugin.Name), - }) -} diff --git a/nix/inputs/dms-cli/internal/server/plugins/list.go b/nix/inputs/dms-cli/internal/server/plugins/list.go deleted file mode 100644 index 4421608..0000000 --- a/nix/inputs/dms-cli/internal/server/plugins/list.go +++ /dev/null @@ -1,51 +0,0 @@ -package plugins - -import ( - "fmt" - "net" - "strings" - - "github.com/AvengeMedia/danklinux/internal/plugins" - "github.com/AvengeMedia/danklinux/internal/server/models" -) - -func HandleList(conn net.Conn, req models.Request) { - registry, err := plugins.NewRegistry() - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to create registry: %v", err)) - return - } - - pluginList, err := registry.List() - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to list plugins: %v", err)) - return - } - - manager, err := plugins.NewManager() - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to create manager: %v", err)) - return - } - - result := make([]PluginInfo, len(pluginList)) - for i, p := range pluginList { - installed, _ := manager.IsInstalled(p) - result[i] = PluginInfo{ - ID: p.ID, - Name: p.Name, - Category: p.Category, - Author: p.Author, - Description: p.Description, - Repo: p.Repo, - Path: p.Path, - Capabilities: p.Capabilities, - Compositors: p.Compositors, - Dependencies: p.Dependencies, - Installed: installed, - FirstParty: strings.HasPrefix(p.Repo, "https://github.com/AvengeMedia"), - } - } - - models.Respond(conn, req.ID, result) -} diff --git a/nix/inputs/dms-cli/internal/server/plugins/list_installed.go b/nix/inputs/dms-cli/internal/server/plugins/list_installed.go deleted file mode 100644 index dfb9211..0000000 --- a/nix/inputs/dms-cli/internal/server/plugins/list_installed.go +++ /dev/null @@ -1,76 +0,0 @@ -package plugins - -import ( - "fmt" - "net" - "strings" - - "github.com/AvengeMedia/danklinux/internal/plugins" - "github.com/AvengeMedia/danklinux/internal/server/models" -) - -func HandleListInstalled(conn net.Conn, req models.Request) { - manager, err := plugins.NewManager() - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to create manager: %v", err)) - return - } - - installedNames, err := manager.ListInstalled() - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to list installed plugins: %v", err)) - return - } - - registry, err := plugins.NewRegistry() - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to create registry: %v", err)) - return - } - - allPlugins, err := registry.List() - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to list plugins: %v", err)) - return - } - - pluginMap := make(map[string]plugins.Plugin) - for _, p := range allPlugins { - pluginMap[p.ID] = p - } - - result := make([]PluginInfo, 0, len(installedNames)) - for _, id := range installedNames { - if plugin, ok := pluginMap[id]; ok { - hasUpdate := false - if hasUpdates, err := manager.HasUpdates(id, plugin); err == nil { - hasUpdate = hasUpdates - } - - result = append(result, PluginInfo{ - ID: plugin.ID, - Name: plugin.Name, - Category: plugin.Category, - Author: plugin.Author, - Description: plugin.Description, - Repo: plugin.Repo, - Path: plugin.Path, - Capabilities: plugin.Capabilities, - Compositors: plugin.Compositors, - Dependencies: plugin.Dependencies, - FirstParty: strings.HasPrefix(plugin.Repo, "https://github.com/AvengeMedia"), - HasUpdate: hasUpdate, - }) - } else { - result = append(result, PluginInfo{ - ID: id, - Name: id, - Note: "not in registry", - }) - } - } - - SortPluginInfoByFirstParty(result) - - models.Respond(conn, req.ID, result) -} diff --git a/nix/inputs/dms-cli/internal/server/plugins/search.go b/nix/inputs/dms-cli/internal/server/plugins/search.go deleted file mode 100644 index 2595f71..0000000 --- a/nix/inputs/dms-cli/internal/server/plugins/search.go +++ /dev/null @@ -1,73 +0,0 @@ -package plugins - -import ( - "fmt" - "net" - "strings" - - "github.com/AvengeMedia/danklinux/internal/plugins" - "github.com/AvengeMedia/danklinux/internal/server/models" -) - -func HandleSearch(conn net.Conn, req models.Request) { - query, ok := req.Params["query"].(string) - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'query' parameter") - return - } - - registry, err := plugins.NewRegistry() - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to create registry: %v", err)) - return - } - - pluginList, err := registry.List() - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to list plugins: %v", err)) - return - } - - searchResults := plugins.FuzzySearch(query, pluginList) - - if category, ok := req.Params["category"].(string); ok && category != "" { - searchResults = plugins.FilterByCategory(category, searchResults) - } - - if compositor, ok := req.Params["compositor"].(string); ok && compositor != "" { - searchResults = plugins.FilterByCompositor(compositor, searchResults) - } - - if capability, ok := req.Params["capability"].(string); ok && capability != "" { - searchResults = plugins.FilterByCapability(capability, searchResults) - } - - searchResults = plugins.SortByFirstParty(searchResults) - - manager, err := plugins.NewManager() - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to create manager: %v", err)) - return - } - - result := make([]PluginInfo, len(searchResults)) - for i, p := range searchResults { - installed, _ := manager.IsInstalled(p) - result[i] = PluginInfo{ - ID: p.ID, - Name: p.Name, - Category: p.Category, - Author: p.Author, - Description: p.Description, - Repo: p.Repo, - Path: p.Path, - Capabilities: p.Capabilities, - Compositors: p.Compositors, - Dependencies: p.Dependencies, - Installed: installed, - FirstParty: strings.HasPrefix(p.Repo, "https://github.com/AvengeMedia"), - } - } - - models.Respond(conn, req.ID, result) -} diff --git a/nix/inputs/dms-cli/internal/server/plugins/types.go b/nix/inputs/dms-cli/internal/server/plugins/types.go deleted file mode 100644 index 232a258..0000000 --- a/nix/inputs/dms-cli/internal/server/plugins/types.go +++ /dev/null @@ -1,23 +0,0 @@ -package plugins - -type PluginInfo struct { - ID string `json:"id"` - Name string `json:"name"` - Category string `json:"category,omitempty"` - Author string `json:"author,omitempty"` - Description string `json:"description,omitempty"` - Repo string `json:"repo,omitempty"` - Path string `json:"path,omitempty"` - Capabilities []string `json:"capabilities,omitempty"` - Compositors []string `json:"compositors,omitempty"` - Dependencies []string `json:"dependencies,omitempty"` - Installed bool `json:"installed,omitempty"` - FirstParty bool `json:"firstParty,omitempty"` - Note string `json:"note,omitempty"` - HasUpdate bool `json:"hasUpdate,omitempty"` -} - -type SuccessResult struct { - Success bool `json:"success"` - Message string `json:"message"` -} diff --git a/nix/inputs/dms-cli/internal/server/plugins/uninstall.go b/nix/inputs/dms-cli/internal/server/plugins/uninstall.go deleted file mode 100644 index 74d714a..0000000 --- a/nix/inputs/dms-cli/internal/server/plugins/uninstall.go +++ /dev/null @@ -1,69 +0,0 @@ -package plugins - -import ( - "fmt" - "net" - - "github.com/AvengeMedia/danklinux/internal/plugins" - "github.com/AvengeMedia/danklinux/internal/server/models" -) - -func HandleUninstall(conn net.Conn, req models.Request) { - name, ok := req.Params["name"].(string) - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'name' parameter") - return - } - - registry, err := plugins.NewRegistry() - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to create registry: %v", err)) - return - } - - pluginList, err := registry.List() - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to list plugins: %v", err)) - return - } - - var plugin *plugins.Plugin - for _, p := range pluginList { - if p.Name == name { - plugin = &p - break - } - } - - if plugin == nil { - models.RespondError(conn, req.ID, fmt.Sprintf("plugin not found: %s", name)) - return - } - - manager, err := plugins.NewManager() - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to create manager: %v", err)) - return - } - - installed, err := manager.IsInstalled(*plugin) - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to check if plugin is installed: %v", err)) - return - } - - if !installed { - models.RespondError(conn, req.ID, fmt.Sprintf("plugin not installed: %s", name)) - return - } - - if err := manager.Uninstall(*plugin); err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to uninstall plugin: %v", err)) - return - } - - models.Respond(conn, req.ID, SuccessResult{ - Success: true, - Message: fmt.Sprintf("plugin uninstalled: %s", name), - }) -} diff --git a/nix/inputs/dms-cli/internal/server/plugins/update.go b/nix/inputs/dms-cli/internal/server/plugins/update.go deleted file mode 100644 index 6560cb6..0000000 --- a/nix/inputs/dms-cli/internal/server/plugins/update.go +++ /dev/null @@ -1,69 +0,0 @@ -package plugins - -import ( - "fmt" - "net" - - "github.com/AvengeMedia/danklinux/internal/plugins" - "github.com/AvengeMedia/danklinux/internal/server/models" -) - -func HandleUpdate(conn net.Conn, req models.Request) { - name, ok := req.Params["name"].(string) - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'name' parameter") - return - } - - registry, err := plugins.NewRegistry() - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to create registry: %v", err)) - return - } - - pluginList, err := registry.List() - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to list plugins: %v", err)) - return - } - - var plugin *plugins.Plugin - for _, p := range pluginList { - if p.Name == name { - plugin = &p - break - } - } - - if plugin == nil { - models.RespondError(conn, req.ID, fmt.Sprintf("plugin not found: %s", name)) - return - } - - manager, err := plugins.NewManager() - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to create manager: %v", err)) - return - } - - installed, err := manager.IsInstalled(*plugin) - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to check if plugin is installed: %v", err)) - return - } - - if !installed { - models.RespondError(conn, req.ID, fmt.Sprintf("plugin not installed: %s", name)) - return - } - - if err := manager.Update(*plugin); err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to update plugin: %v", err)) - return - } - - models.Respond(conn, req.ID, SuccessResult{ - Success: true, - Message: fmt.Sprintf("plugin updated: %s", name), - }) -} diff --git a/nix/inputs/dms-cli/internal/server/plugins/utils.go b/nix/inputs/dms-cli/internal/server/plugins/utils.go deleted file mode 100644 index 8b89f44..0000000 --- a/nix/inputs/dms-cli/internal/server/plugins/utils.go +++ /dev/null @@ -1,17 +0,0 @@ -package plugins - -import ( - "sort" - "strings" -) - -func SortPluginInfoByFirstParty(pluginInfos []PluginInfo) { - sort.SliceStable(pluginInfos, func(i, j int) bool { - isFirstPartyI := strings.HasPrefix(pluginInfos[i].Repo, "https://github.com/AvengeMedia") - isFirstPartyJ := strings.HasPrefix(pluginInfos[j].Repo, "https://github.com/AvengeMedia") - if isFirstPartyI != isFirstPartyJ { - return isFirstPartyI - } - return false - }) -} diff --git a/nix/inputs/dms-cli/internal/server/router.go b/nix/inputs/dms-cli/internal/server/router.go deleted file mode 100644 index 2c0c067..0000000 --- a/nix/inputs/dms-cli/internal/server/router.go +++ /dev/null @@ -1,149 +0,0 @@ -package server - -import ( - "fmt" - "net" - "strings" - - "github.com/AvengeMedia/danklinux/internal/server/bluez" - "github.com/AvengeMedia/danklinux/internal/server/brightness" - "github.com/AvengeMedia/danklinux/internal/server/cups" - "github.com/AvengeMedia/danklinux/internal/server/dwl" - "github.com/AvengeMedia/danklinux/internal/server/freedesktop" - "github.com/AvengeMedia/danklinux/internal/server/loginctl" - "github.com/AvengeMedia/danklinux/internal/server/models" - "github.com/AvengeMedia/danklinux/internal/server/network" - serverPlugins "github.com/AvengeMedia/danklinux/internal/server/plugins" - "github.com/AvengeMedia/danklinux/internal/server/wayland" -) - -func RouteRequest(conn net.Conn, req models.Request) { - if strings.HasPrefix(req.Method, "network.") { - if networkManager == nil { - models.RespondError(conn, req.ID, "network manager not initialized") - return - } - netReq := network.Request{ - ID: req.ID, - Method: req.Method, - Params: req.Params, - } - network.HandleRequest(conn, netReq, networkManager) - return - } - - if strings.HasPrefix(req.Method, "plugins.") { - serverPlugins.HandleRequest(conn, req) - return - } - - if strings.HasPrefix(req.Method, "loginctl.") { - if loginctlManager == nil { - models.RespondError(conn, req.ID, "loginctl manager not initialized") - return - } - loginReq := loginctl.Request{ - ID: req.ID, - Method: req.Method, - Params: req.Params, - } - loginctl.HandleRequest(conn, loginReq, loginctlManager) - return - } - - if strings.HasPrefix(req.Method, "freedesktop.") { - if freedesktopManager == nil { - models.RespondError(conn, req.ID, "freedesktop manager not initialized") - return - } - freedeskReq := freedesktop.Request{ - ID: req.ID, - Method: req.Method, - Params: req.Params, - } - freedesktop.HandleRequest(conn, freedeskReq, freedesktopManager) - return - } - - if strings.HasPrefix(req.Method, "wayland.") { - if waylandManager == nil { - models.RespondError(conn, req.ID, "wayland manager not initialized") - return - } - waylandReq := wayland.Request{ - ID: req.ID, - Method: req.Method, - Params: req.Params, - } - wayland.HandleRequest(conn, waylandReq, waylandManager) - return - } - - if strings.HasPrefix(req.Method, "bluetooth.") { - if bluezManager == nil { - models.RespondError(conn, req.ID, "bluetooth manager not initialized") - return - } - bluezReq := bluez.Request{ - ID: req.ID, - Method: req.Method, - Params: req.Params, - } - bluez.HandleRequest(conn, bluezReq, bluezManager) - return - } - - if strings.HasPrefix(req.Method, "cups.") { - if cupsManager == nil { - models.RespondError(conn, req.ID, "CUPS manager not initialized") - return - } - cupsReq := cups.Request{ - ID: req.ID, - Method: req.Method, - Params: req.Params, - } - cups.HandleRequest(conn, cupsReq, cupsManager) - return - } - - if strings.HasPrefix(req.Method, "dwl.") { - if dwlManager == nil { - models.RespondError(conn, req.ID, "dwl manager not initialized") - return - } - dwlReq := dwl.Request{ - ID: req.ID, - Method: req.Method, - Params: req.Params, - } - dwl.HandleRequest(conn, dwlReq, dwlManager) - return - } - - if strings.HasPrefix(req.Method, "brightness.") { - if brightnessManager == nil { - models.RespondError(conn, req.ID, "brightness manager not initialized") - return - } - brightnessReq := brightness.Request{ - ID: req.ID, - Method: req.Method, - Params: req.Params, - } - brightness.HandleRequest(conn, brightnessReq, brightnessManager) - return - } - - switch req.Method { - case "ping": - models.Respond(conn, req.ID, "pong") - case "getServerInfo": - info := getServerInfo() - models.Respond(conn, req.ID, info) - case "subscribe": - handleSubscribe(conn, req) - default: - models.RespondError(conn, req.ID, fmt.Sprintf("unknown method: %s", req.Method)) - } -} diff --git a/nix/inputs/dms-cli/internal/server/server.go b/nix/inputs/dms-cli/internal/server/server.go deleted file mode 100644 index 307468b..0000000 --- a/nix/inputs/dms-cli/internal/server/server.go +++ /dev/null @@ -1,1050 +0,0 @@ -package server - -import ( - "bufio" - "encoding/json" - "fmt" - "net" - "os" - "path/filepath" - "strconv" - "strings" - "sync" - "syscall" - "time" - - "github.com/AvengeMedia/danklinux/internal/log" - "github.com/AvengeMedia/danklinux/internal/server/bluez" - "github.com/AvengeMedia/danklinux/internal/server/brightness" - "github.com/AvengeMedia/danklinux/internal/server/cups" - "github.com/AvengeMedia/danklinux/internal/server/dwl" - "github.com/AvengeMedia/danklinux/internal/server/freedesktop" - "github.com/AvengeMedia/danklinux/internal/server/loginctl" - "github.com/AvengeMedia/danklinux/internal/server/models" - "github.com/AvengeMedia/danklinux/internal/server/network" - "github.com/AvengeMedia/danklinux/internal/server/wayland" - "github.com/AvengeMedia/danklinux/internal/server/wlcontext" -) - -const APIVersion = 15 - -type Capabilities struct { - Capabilities []string `json:"capabilities"` -} - -type ServerInfo struct { - APIVersion int `json:"apiVersion"` - Capabilities []string `json:"capabilities"` -} - -type ServiceEvent struct { - Service string `json:"service"` - Data interface{} `json:"data"` -} - -var networkManager *network.Manager -var loginctlManager *loginctl.Manager -var freedesktopManager *freedesktop.Manager -var waylandManager *wayland.Manager -var bluezManager *bluez.Manager -var cupsManager *cups.Manager -var dwlManager *dwl.Manager -var brightnessManager *brightness.Manager -var wlContext *wlcontext.SharedContext - -var capabilitySubscribers = make(map[string]chan ServerInfo) -var capabilityMutex sync.RWMutex - -var cupsSubscribers = make(map[string]bool) -var cupsSubscribersMutex sync.Mutex - -func getSocketDir() string { - if runtime := os.Getenv("XDG_RUNTIME_DIR"); runtime != "" { - return runtime - } - - if os.Getuid() == 0 { - if _, err := os.Stat("/run"); err == nil { - return "/run/dankdots" - } - return "/var/run/dankdots" - } - - return os.TempDir() -} - -func GetSocketPath() string { - return filepath.Join(getSocketDir(), fmt.Sprintf("danklinux-%d.sock", os.Getpid())) -} - -func cleanupStaleSockets() { - dir := getSocketDir() - entries, err := os.ReadDir(dir) - if err != nil { - return - } - - for _, entry := range entries { - if !strings.HasPrefix(entry.Name(), "danklinux-") || !strings.HasSuffix(entry.Name(), ".sock") { - continue - } - - pidStr := strings.TrimPrefix(entry.Name(), "danklinux-") - pidStr = strings.TrimSuffix(pidStr, ".sock") - pid, err := strconv.Atoi(pidStr) - if err != nil { - continue - } - - process, err := os.FindProcess(pid) - if err != nil { - socketPath := filepath.Join(dir, entry.Name()) - os.Remove(socketPath) - log.Debugf("Removed stale socket: %s", socketPath) - continue - } - - err = process.Signal(syscall.Signal(0)) - if err != nil { - socketPath := filepath.Join(dir, entry.Name()) - os.Remove(socketPath) - log.Debugf("Removed stale socket: %s", socketPath) - } - } -} - -func InitializeNetworkManager() error { - manager, err := network.NewManager() - if err != nil { - log.Warnf("Failed to initialize network manager: %v", err) - return err - } - - networkManager = manager - - log.Info("Network manager initialized") - return nil -} - -func InitializeLoginctlManager() error { - manager, err := loginctl.NewManager() - if err != nil { - log.Warnf("Failed to initialize loginctl manager: %v", err) - return err - } - - loginctlManager = manager - - log.Info("Loginctl manager initialized") - return nil -} - -func InitializeFreedeskManager() error { - manager, err := freedesktop.NewManager() - if err != nil { - log.Warnf("Failed to initialize freedesktop manager: %v", err) - return err - } - - freedesktopManager = manager - - log.Info("Freedesktop manager initialized") - return nil -} - -func InitializeWaylandManager() error { - log.Info("Attempting to initialize Wayland gamma control...") - - if wlContext == nil { - ctx, err := wlcontext.New() - if err != nil { - log.Errorf("Failed to create shared Wayland context: %v", err) - return err - } - wlContext = ctx - } - - config := wayland.DefaultConfig() - manager, err := wayland.NewManager(wlContext.Display(), config) - if err != nil { - log.Errorf("Failed to initialize wayland manager: %v", err) - return err - } - - waylandManager = manager - - log.Info("Wayland gamma control initialized successfully") - return nil -} - -func InitializeBluezManager() error { - manager, err := bluez.NewManager() - if err != nil { - log.Warnf("Failed to initialize bluez manager: %v", err) - return err - } - - bluezManager = manager - - log.Info("Bluez manager initialized") - return nil -} - -func InitializeCupsManager() error { - manager, err := cups.NewManager() - if err != nil { - log.Warnf("Failed to initialize cups manager: %v", err) - return err - } - - cupsManager = manager - - log.Info("CUPS manager initialized") - return nil -} - -func InitializeDwlManager() error { - log.Info("Attempting to initialize DWL IPC...") - - if wlContext == nil { - ctx, err := wlcontext.New() - if err != nil { - log.Errorf("Failed to create shared Wayland context: %v", err) - return err - } - wlContext = ctx - } - - manager, err := dwl.NewManager(wlContext.Display()) - if err != nil { - log.Debug("Failed to initialize dwl manager: %v", err) - return err - } - - dwlManager = manager - - log.Info("DWL IPC initialized successfully") - return nil -} - -func InitializeBrightnessManager() error { - manager, err := brightness.NewManager() - if err != nil { - log.Warnf("Failed to initialize brightness manager: %v", err) - return err - } - - brightnessManager = manager - - log.Info("Brightness manager initialized") - return nil -} - -func handleConnection(conn net.Conn) { - defer conn.Close() - - caps := getCapabilities() - capsData, _ := json.Marshal(caps) - conn.Write(capsData) - conn.Write([]byte("\n")) - - scanner := bufio.NewScanner(conn) - for scanner.Scan() { - line := scanner.Bytes() - - var req models.Request - if err := json.Unmarshal(line, &req); err != nil { - log.Warnf("handleConnection: Failed to unmarshal JSON: %v, line: %s", err, string(line)) - models.RespondError(conn, 0, "invalid json") - continue - } - - go RouteRequest(conn, req) - } -} - -func getCapabilities() Capabilities { - caps := []string{"plugins"} - - if networkManager != nil { - caps = append(caps, "network") - } - - if loginctlManager != nil { - caps = append(caps, "loginctl") - } - - if freedesktopManager != nil { - caps = append(caps, "freedesktop") - } - - if waylandManager != nil { - caps = append(caps, "gamma") - } - - if bluezManager != nil { - caps = append(caps, "bluetooth") - } - - if cupsManager != nil { - caps = append(caps, "cups") - } - - if dwlManager != nil { - caps = append(caps, "dwl") - } - - if brightnessManager != nil { - caps = append(caps, "brightness") - } - - return Capabilities{Capabilities: caps} -} - -func getServerInfo() ServerInfo { - caps := []string{"plugins"} - - if networkManager != nil { - caps = append(caps, "network") - } - - if loginctlManager != nil { - caps = append(caps, "loginctl") - } - - if freedesktopManager != nil { - caps = append(caps, "freedesktop") - } - - if waylandManager != nil { - caps = append(caps, "gamma") - } - - if bluezManager != nil { - caps = append(caps, "bluetooth") - } - - if cupsManager != nil { - caps = append(caps, "cups") - } - - if dwlManager != nil { - caps = append(caps, "dwl") - } - - if brightnessManager != nil { - caps = append(caps, "brightness") - } - - return ServerInfo{ - APIVersion: APIVersion, - Capabilities: caps, - } -} - -func notifyCapabilityChange() { - capabilityMutex.RLock() - defer capabilityMutex.RUnlock() - - info := getServerInfo() - for _, ch := range capabilitySubscribers { - select { - case ch <- info: - default: - } - } -} - -func handleSubscribe(conn net.Conn, req models.Request) { - clientID := fmt.Sprintf("meta-client-%p", conn) - - var services []string - if servicesParam, ok := req.Params["services"].([]interface{}); ok { - for _, s := range servicesParam { - if str, ok := s.(string); ok { - services = append(services, str) - } - } - } - - if len(services) == 0 { - services = []string{"all"} - } - - subscribeAll := false - for _, s := range services { - if s == "all" { - subscribeAll = true - break - } - } - - var wg sync.WaitGroup - eventChan := make(chan ServiceEvent, 256) - stopChan := make(chan struct{}) - - capChan := make(chan ServerInfo, 64) - capabilityMutex.Lock() - capabilitySubscribers[clientID+"-capabilities"] = capChan - capabilityMutex.Unlock() - - wg.Add(1) - go func() { - defer wg.Done() - defer func() { - capabilityMutex.Lock() - delete(capabilitySubscribers, clientID+"-capabilities") - capabilityMutex.Unlock() - }() - - for { - select { - case info, ok := <-capChan: - if !ok { - return - } - select { - case eventChan <- ServiceEvent{Service: "server", Data: info}: - case <-stopChan: - return - } - case <-stopChan: - return - } - } - }() - - shouldSubscribe := func(service string) bool { - if subscribeAll { - return true - } - for _, s := range services { - if s == service { - return true - } - } - return false - } - - if shouldSubscribe("network") && networkManager != nil { - wg.Add(1) - netChan := networkManager.Subscribe(clientID + "-network") - go func() { - defer wg.Done() - defer networkManager.Unsubscribe(clientID + "-network") - - initialState := networkManager.GetState() - select { - case eventChan <- ServiceEvent{Service: "network", Data: initialState}: - case <-stopChan: - return - } - - for { - select { - case state, ok := <-netChan: - if !ok { - return - } - select { - case eventChan <- ServiceEvent{Service: "network", Data: state}: - case <-stopChan: - return - } - case <-stopChan: - return - } - } - }() - } - - if shouldSubscribe("network.credentials") && networkManager != nil { - wg.Add(1) - credChan := networkManager.SubscribeCredentials(clientID + "-credentials") - go func() { - defer wg.Done() - defer networkManager.UnsubscribeCredentials(clientID + "-credentials") - - for { - select { - case prompt, ok := <-credChan: - if !ok { - return - } - select { - case eventChan <- ServiceEvent{Service: "network.credentials", Data: prompt}: - case <-stopChan: - return - } - case <-stopChan: - return - } - } - }() - } - - if shouldSubscribe("loginctl") && loginctlManager != nil { - wg.Add(1) - loginChan := loginctlManager.Subscribe(clientID + "-loginctl") - go func() { - defer wg.Done() - defer loginctlManager.Unsubscribe(clientID + "-loginctl") - - initialState := loginctlManager.GetState() - select { - case eventChan <- ServiceEvent{Service: "loginctl", Data: initialState}: - case <-stopChan: - return - } - - for { - select { - case state, ok := <-loginChan: - if !ok { - return - } - select { - case eventChan <- ServiceEvent{Service: "loginctl", Data: state}: - case <-stopChan: - return - } - case <-stopChan: - return - } - } - }() - } - - if shouldSubscribe("freedesktop") && freedesktopManager != nil { - wg.Add(1) - freedesktopChan := freedesktopManager.Subscribe(clientID + "-freedesktop") - go func() { - defer wg.Done() - defer freedesktopManager.Unsubscribe(clientID + "-freedesktop") - - initialState := freedesktopManager.GetState() - select { - case eventChan <- ServiceEvent{Service: "freedesktop", Data: initialState}: - case <-stopChan: - return - } - - for { - select { - case state, ok := <-freedesktopChan: - if !ok { - return - } - select { - case eventChan <- ServiceEvent{Service: "freedesktop", Data: state}: - case <-stopChan: - return - } - case <-stopChan: - return - } - } - }() - } - - if shouldSubscribe("gamma") && waylandManager != nil { - wg.Add(1) - waylandChan := waylandManager.Subscribe(clientID + "-gamma") - go func() { - defer wg.Done() - defer waylandManager.Unsubscribe(clientID + "-gamma") - - initialState := waylandManager.GetState() - select { - case eventChan <- ServiceEvent{Service: "gamma", Data: initialState}: - case <-stopChan: - return - } - - for { - select { - case state, ok := <-waylandChan: - if !ok { - return - } - select { - case eventChan <- ServiceEvent{Service: "gamma", Data: state}: - case <-stopChan: - return - } - case <-stopChan: - return - } - } - }() - } - - if shouldSubscribe("bluetooth") && bluezManager != nil { - wg.Add(1) - bluezChan := bluezManager.Subscribe(clientID + "-bluetooth") - go func() { - defer wg.Done() - defer bluezManager.Unsubscribe(clientID + "-bluetooth") - - initialState := bluezManager.GetState() - select { - case eventChan <- ServiceEvent{Service: "bluetooth", Data: initialState}: - case <-stopChan: - return - } - - for { - select { - case state, ok := <-bluezChan: - if !ok { - return - } - select { - case eventChan <- ServiceEvent{Service: "bluetooth", Data: state}: - case <-stopChan: - return - } - case <-stopChan: - return - } - } - }() - } - - if shouldSubscribe("bluetooth.pairing") && bluezManager != nil { - wg.Add(1) - pairingChan := bluezManager.SubscribePairing(clientID + "-pairing") - go func() { - defer wg.Done() - defer bluezManager.UnsubscribePairing(clientID + "-pairing") - - for { - select { - case prompt, ok := <-pairingChan: - if !ok { - return - } - select { - case eventChan <- ServiceEvent{Service: "bluetooth.pairing", Data: prompt}: - case <-stopChan: - return - } - case <-stopChan: - return - } - } - }() - } - - if shouldSubscribe("cups") { - cupsSubscribersMutex.Lock() - wasEmpty := len(cupsSubscribers) == 0 - cupsSubscribers[clientID+"-cups"] = true - cupsSubscribersMutex.Unlock() - - if wasEmpty { - if err := InitializeCupsManager(); err != nil { - log.Warnf("Failed to initialize CUPS manager for subscription: %v", err) - } else { - notifyCapabilityChange() - } - } - - if cupsManager != nil { - wg.Add(1) - cupsChan := cupsManager.Subscribe(clientID + "-cups") - go func() { - defer wg.Done() - defer func() { - cupsManager.Unsubscribe(clientID + "-cups") - - cupsSubscribersMutex.Lock() - delete(cupsSubscribers, clientID+"-cups") - isEmpty := len(cupsSubscribers) == 0 - cupsSubscribersMutex.Unlock() - - if isEmpty { - log.Info("Last CUPS subscriber disconnected, shutting down CUPS manager") - if cupsManager != nil { - cupsManager.Close() - cupsManager = nil - notifyCapabilityChange() - } - } - }() - - initialState := cupsManager.GetState() - select { - case eventChan <- ServiceEvent{Service: "cups", Data: initialState}: - case <-stopChan: - return - } - - for { - select { - case state, ok := <-cupsChan: - if !ok { - return - } - select { - case eventChan <- ServiceEvent{Service: "cups", Data: state}: - case <-stopChan: - return - } - case <-stopChan: - return - } - } - }() - } - } - - if shouldSubscribe("dwl") && dwlManager != nil { - wg.Add(1) - dwlChan := dwlManager.Subscribe(clientID + "-dwl") - go func() { - defer wg.Done() - defer dwlManager.Unsubscribe(clientID + "-dwl") - - initialState := dwlManager.GetState() - select { - case eventChan <- ServiceEvent{Service: "dwl", Data: initialState}: - case <-stopChan: - return - } - - for { - select { - case state, ok := <-dwlChan: - if !ok { - return - } - select { - case eventChan <- ServiceEvent{Service: "dwl", Data: state}: - case <-stopChan: - return - } - case <-stopChan: - return - } - } - }() - } - - if shouldSubscribe("brightness") && brightnessManager != nil { - wg.Add(2) - brightnessStateChan := brightnessManager.Subscribe(clientID + "-brightness-state") - brightnessUpdateChan := brightnessManager.SubscribeUpdates(clientID + "-brightness-updates") - - go func() { - defer wg.Done() - defer brightnessManager.Unsubscribe(clientID + "-brightness-state") - - initialState := brightnessManager.GetState() - select { - case eventChan <- ServiceEvent{Service: "brightness", Data: initialState}: - case <-stopChan: - return - } - - for { - select { - case state, ok := <-brightnessStateChan: - if !ok { - return - } - select { - case eventChan <- ServiceEvent{Service: "brightness", Data: state}: - case <-stopChan: - return - } - case <-stopChan: - return - } - } - }() - - go func() { - defer wg.Done() - defer brightnessManager.UnsubscribeUpdates(clientID + "-brightness-updates") - - for { - select { - case update, ok := <-brightnessUpdateChan: - if !ok { - return - } - select { - case eventChan <- ServiceEvent{Service: "brightness.update", Data: update}: - case <-stopChan: - return - } - case <-stopChan: - return - } - } - }() - } - - go func() { - wg.Wait() - close(eventChan) - }() - - info := getServerInfo() - if err := json.NewEncoder(conn).Encode(models.Response[ServiceEvent]{ - ID: req.ID, - Result: &ServiceEvent{Service: "server", Data: info}, - }); err != nil { - close(stopChan) - return - } - - for event := range eventChan { - if err := json.NewEncoder(conn).Encode(models.Response[ServiceEvent]{ - ID: req.ID, - Result: &event, - }); err != nil { - close(stopChan) - return - } - } -} - -func cleanupManagers() { - if networkManager != nil { - networkManager.Close() - } - if loginctlManager != nil { - loginctlManager.Close() - } - if freedesktopManager != nil { - freedesktopManager.Close() - } - if waylandManager != nil { - waylandManager.Close() - } - if bluezManager != nil { - bluezManager.Close() - } - if cupsManager != nil { - cupsManager.Close() - } - if dwlManager != nil { - dwlManager.Close() - } - if brightnessManager != nil { - brightnessManager.Close() - } - if wlContext != nil { - wlContext.Close() - } -} - -func Start(printDocs bool) error { - cleanupStaleSockets() - - socketPath := GetSocketPath() - os.Remove(socketPath) - - listener, err := net.Listen("unix", socketPath) - if err != nil { - return err - } - defer listener.Close() - defer cleanupManagers() - - log.Infof("DMS API Server listening on: %s", socketPath) - log.Infof("API Version: %d", APIVersion) - log.Info("Protocol: JSON over Unix socket") - log.Info("Request format: {\"id\": , \"method\": \"...\", \"params\": {...}}") - log.Info("Response format: {\"id\": , \"result\": {...}} or {\"id\": , \"error\": \"...\"}") - log.Info("") - if printDocs { - log.Info("Available methods:") - log.Info(" ping - Test connection") - log.Info(" getServerInfo - Get server info (API version and capabilities)") - log.Info(" subscribe - Subscribe to multiple services (params: services [default: all])") - log.Info("Plugins:") - log.Info(" plugins.list - List all plugins") - log.Info(" plugins.listInstalled - List installed plugins") - log.Info(" plugins.install - Install plugin (params: name)") - log.Info(" plugins.uninstall - Uninstall plugin (params: name)") - log.Info(" plugins.update - Update plugin (params: name)") - log.Info(" plugins.search - Search plugins (params: query, category?, compositor?, capability?)") - log.Info("Network:") - log.Info(" network.getState - Get current network state") - log.Info(" network.wifi.scan - Scan for WiFi networks") - log.Info(" network.wifi.networks - Get WiFi network list") - log.Info(" network.wifi.connect - Connect to WiFi (params: ssid, password?, username?)") - log.Info(" network.wifi.disconnect - Disconnect WiFi") - log.Info(" network.wifi.forget - Forget network (params: ssid)") - log.Info(" network.wifi.toggle - Toggle WiFi radio") - log.Info(" network.wifi.enable - Enable WiFi") - log.Info(" network.wifi.disable - Disable WiFi") - log.Info(" network.wifi.setAutoconnect - Set network autoconnect (params: ssid, autoconnect)") - log.Info(" network.ethernet.connect - Connect Ethernet") - log.Info(" network.ethernet.connect.config - Connect Ethernet to a specific configuration") - log.Info(" network.ethernet.disconnect - Disconnect Ethernet") - log.Info(" network.vpn.profiles - List VPN profiles") - log.Info(" network.vpn.active - List active VPN connections") - log.Info(" network.vpn.connect - Connect VPN (params: uuidOrName|name|uuid, singleActive?)") - log.Info(" network.vpn.disconnect - Disconnect VPN (params: uuidOrName|name|uuid)") - log.Info(" network.vpn.disconnectAll - Disconnect all VPNs") - log.Info(" network.vpn.clearCredentials - Clear saved VPN credentials (params: uuidOrName|name|uuid)") - log.Info(" network.preference.set - Set preference (params: preference [auto|wifi|ethernet])") - log.Info(" network.info - Get network info (params: ssid)") - log.Info(" network.credentials.submit - Submit credentials for prompt (params: token, secrets, save?)") - log.Info(" network.credentials.cancel - Cancel credential prompt (params: token)") - log.Info(" network.subscribe - Subscribe to network state changes (streaming)") - log.Info("Loginctl:") - log.Info(" loginctl.getState - Get current session state") - log.Info(" loginctl.lock - Lock session") - log.Info(" loginctl.unlock - Unlock session") - log.Info(" loginctl.activate - Activate session") - log.Info(" loginctl.setIdleHint - Set idle hint (params: idle)") - log.Info(" loginctl.setLockBeforeSuspend - Set lock before suspend (params: enabled)") - log.Info(" loginctl.setSleepInhibitorEnabled - Enable/disable sleep inhibitor (params: enabled)") - log.Info(" loginctl.lockerReady - Signal locker UI is ready (releases sleep inhibitor)") - log.Info(" loginctl.terminate - Terminate session") - log.Info(" loginctl.subscribe - Subscribe to session state changes (streaming)") - log.Info("Freedesktop:") - log.Info(" freedesktop.getState - Get accounts & settings state") - log.Info(" freedesktop.accounts.setIconFile - Set profile icon (params: path)") - log.Info(" freedesktop.accounts.setRealName - Set real name (params: name)") - log.Info(" freedesktop.accounts.setEmail - Set email (params: email)") - log.Info(" freedesktop.accounts.setLanguage - Set language (params: language)") - log.Info(" freedesktop.accounts.setLocation - Set location (params: location)") - log.Info(" freedesktop.accounts.getUserIconFile - Get user icon (params: username)") - log.Info(" freedesktop.settings.getColorScheme - Get color scheme") - log.Info(" freedesktop.settings.setIconTheme - Set icon theme (params: iconTheme)") - log.Info("Wayland:") - log.Info(" wayland.gamma.getState - Get current gamma control state") - log.Info(" wayland.gamma.setTemperature - Set temperature range (params: low, high)") - log.Info(" wayland.gamma.setLocation - Set location (params: latitude, longitude)") - log.Info(" wayland.gamma.setManualTimes - Set manual times (params: sunrise, sunset)") - log.Info(" wayland.gamma.setGamma - Set gamma value (params: gamma)") - log.Info(" wayland.gamma.setEnabled - Enable/disable gamma control (params: enabled)") - log.Info(" wayland.gamma.subscribe - Subscribe to gamma state changes (streaming)") - log.Info("Bluetooth:") - log.Info(" bluetooth.getState - Get current bluetooth state") - log.Info(" bluetooth.startDiscovery - Start device discovery") - log.Info(" bluetooth.stopDiscovery - Stop device discovery") - log.Info(" bluetooth.setPowered - Set adapter power state (params: powered)") - log.Info(" bluetooth.pair - Pair with device (params: device)") - log.Info(" bluetooth.connect - Connect to device (params: device)") - log.Info(" bluetooth.disconnect - Disconnect from device (params: device)") - log.Info(" bluetooth.remove - Remove/unpair device (params: device)") - log.Info(" bluetooth.trust - Trust device (params: device)") - log.Info(" bluetooth.untrust - Untrust device (params: device)") - log.Info(" bluetooth.pairing.submit - Submit pairing response (params: token, secrets?, accept?)") - log.Info(" bluetooth.pairing.cancel - Cancel pairing prompt (params: token)") - log.Info(" bluetooth.subscribe - Subscribe to bluetooth state changes (streaming)") - log.Info("CUPS:") - log.Info(" cups.getPrinters - Get printers list") - log.Info(" cups.getJobs - Get non-completed jobs list (params: printerName)") - log.Info(" cups.pausePrinter - Pause printer (params: printerName)") - log.Info(" cups.resumePrinter - Resume printer (params: printerName)") - log.Info(" cups.cancelJob - Cancel job (params: printerName, jobID)") - log.Info(" cups.purgeJobs - Cancel all jobs (params: printerName)") - log.Info("DWL:") - log.Info(" dwl.getState - Get current dwl state (tags, windows, layouts)") - log.Info(" dwl.setTags - Set active tags (params: output, tagmask, toggleTagset)") - log.Info(" dwl.setClientTags - Set focused client tags (params: output, andTags, xorTags)") - log.Info(" dwl.setLayout - Set layout (params: output, index)") - log.Info(" dwl.subscribe - Subscribe to dwl state changes (streaming)") - log.Info("Brightness:") - log.Info(" brightness.getState - Get current brightness state for all devices") - log.Info(" brightness.setBrightness - Set device brightness (params: device, percent)") - log.Info(" brightness.increment - Increment device brightness (params: device, step?)") - log.Info(" brightness.decrement - Decrement device brightness (params: device, step?)") - log.Info(" brightness.rescan - Rescan for brightness devices (e.g., after plugging in monitor)") - log.Info(" brightness.subscribe - Subscribe to brightness state changes (streaming)") - log.Info(" Subscription events:") - log.Info(" - brightness : Full device list (on rescan, DDC discovery, device changes)") - log.Info(" - brightness.update: Single device update (on brightness change for efficiency)") - log.Info("") - } - log.Info("Initializing managers...") - log.Info("") - - go func() { - ticker := time.NewTicker(30 * time.Second) - defer ticker.Stop() - - if err := InitializeNetworkManager(); err != nil { - log.Warnf("Network manager unavailable: %v", err) - } else { - notifyCapabilityChange() - return - } - - for range ticker.C { - if networkManager != nil { - return - } - if err := InitializeNetworkManager(); err == nil { - log.Info("Network manager initialized") - notifyCapabilityChange() - return - } - } - }() - - go func() { - if err := InitializeLoginctlManager(); err != nil { - log.Warnf("Loginctl manager unavailable: %v", err) - } else { - notifyCapabilityChange() - } - }() - - go func() { - if err := InitializeFreedeskManager(); err != nil { - log.Warnf("Freedesktop manager unavailable: %v", err) - } else if freedesktopManager != nil { - freedesktopManager.NotifySubscribers() - notifyCapabilityChange() - } - }() - - if err := InitializeWaylandManager(); err != nil { - log.Warnf("Wayland manager unavailable: %v", err) - } - - go func() { - if err := InitializeBluezManager(); err != nil { - log.Warnf("Bluez manager unavailable: %v", err) - } else { - notifyCapabilityChange() - } - }() - - if err := InitializeDwlManager(); err != nil { - log.Debugf("DWL manager unavailable: %v", err) - } - - go func() { - if err := InitializeBrightnessManager(); err != nil { - log.Warnf("Brightness manager unavailable: %v", err) - } else { - notifyCapabilityChange() - } - }() - - if wlContext != nil { - wlContext.Start() - log.Info("Wayland event dispatcher started") - } - - log.Info("") - log.Infof("Ready! Capabilities: %v", getCapabilities().Capabilities) - - for { - conn, err := listener.Accept() - if err != nil { - return err - } - go handleConnection(conn) - } -} diff --git a/nix/inputs/dms-cli/internal/server/server_test.go b/nix/inputs/dms-cli/internal/server/server_test.go deleted file mode 100644 index c943c9e..0000000 --- a/nix/inputs/dms-cli/internal/server/server_test.go +++ /dev/null @@ -1,184 +0,0 @@ -package server - -import ( - "encoding/json" - "fmt" - "net" - "os" - "path/filepath" - "testing" - - "github.com/AvengeMedia/danklinux/internal/server/models" - "github.com/AvengeMedia/danklinux/internal/server/network" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestGetSocketDir(t *testing.T) { - tests := []struct { - name string - xdgRuntimeDir string - uid int - expectedSubstr string - }{ - { - name: "uses XDG_RUNTIME_DIR when set", - xdgRuntimeDir: "/run/user/1000", - expectedSubstr: "/run/user/1000", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if tt.xdgRuntimeDir != "" { - t.Setenv("XDG_RUNTIME_DIR", tt.xdgRuntimeDir) - } - - result := getSocketDir() - assert.Contains(t, result, tt.expectedSubstr) - }) - } -} - -func TestGetSocketPath(t *testing.T) { - path := GetSocketPath() - assert.Contains(t, path, "danklinux-") - assert.Contains(t, path, ".sock") - assert.Contains(t, path, fmt.Sprintf("%d", os.Getpid())) -} - -func TestGetCapabilities(t *testing.T) { - originalNetworkManager := networkManager - defer func() { networkManager = originalNetworkManager }() - - t.Run("capabilities without network manager", func(t *testing.T) { - networkManager = nil - caps := getCapabilities() - assert.Contains(t, caps.Capabilities, "plugins") - assert.NotContains(t, caps.Capabilities, "network") - }) - - t.Run("capabilities with network manager", func(t *testing.T) { - networkManager = &network.Manager{} - caps := getCapabilities() - assert.Contains(t, caps.Capabilities, "plugins") - assert.Contains(t, caps.Capabilities, "network") - }) -} - -type mockConn struct { - net.Conn - written []byte -} - -func (m *mockConn) Write(b []byte) (n int, err error) { - m.written = append(m.written, b...) - return len(b), nil -} - -func (m *mockConn) Close() error { - return nil -} - -func TestRespondError(t *testing.T) { - conn := &mockConn{} - models.RespondError(conn, 123, "test error") - - var resp models.Response[any] - err := json.Unmarshal(conn.written, &resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Equal(t, "test error", resp.Error) - assert.Nil(t, resp.Result) -} - -func TestRespond(t *testing.T) { - conn := &mockConn{} - result := map[string]string{"foo": "bar"} - models.Respond(conn, 123, result) - - var resp models.Response[map[string]string] - err := json.Unmarshal(conn.written, &resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Empty(t, resp.Error) - require.NotNil(t, resp.Result) - assert.Equal(t, "bar", (*resp.Result)["foo"]) -} - -func TestRequest_JSON(t *testing.T) { - jsonStr := `{"id":123,"method":"test.method","params":{"key":"value"}}` - var req models.Request - err := json.Unmarshal([]byte(jsonStr), &req) - require.NoError(t, err) - - assert.Equal(t, 123, req.ID) - assert.Equal(t, "test.method", req.Method) - assert.Equal(t, "value", req.Params["key"]) -} - -func TestResponse_JSON(t *testing.T) { - t.Run("success response", func(t *testing.T) { - result := "success" - resp := models.Response[string]{ - ID: 123, - Result: &result, - } - - data, err := json.Marshal(resp) - require.NoError(t, err) - - var decoded models.Response[string] - err = json.Unmarshal(data, &decoded) - require.NoError(t, err) - - assert.Equal(t, 123, decoded.ID) - assert.Equal(t, "success", *decoded.Result) - assert.Empty(t, decoded.Error) - }) - - t.Run("error response", func(t *testing.T) { - resp := models.Response[any]{ - ID: 123, - Error: "test error", - } - - data, err := json.Marshal(resp) - require.NoError(t, err) - - var decoded models.Response[any] - err = json.Unmarshal(data, &decoded) - require.NoError(t, err) - - assert.Equal(t, 123, decoded.ID) - assert.Equal(t, "test error", decoded.Error) - assert.Nil(t, decoded.Result) - }) -} - -func TestCleanupStaleSockets(t *testing.T) { - tempDir := t.TempDir() - t.Setenv("XDG_RUNTIME_DIR", tempDir) - - // Create a socket file with a non-existent PID - staleSocket := filepath.Join(tempDir, "danklinux-999999.sock") - err := os.WriteFile(staleSocket, []byte{}, 0600) - require.NoError(t, err) - - // Create a socket file with current PID (should not be deleted) - activeSocket := filepath.Join(tempDir, fmt.Sprintf("danklinux-%d.sock", os.Getpid())) - err = os.WriteFile(activeSocket, []byte{}, 0600) - require.NoError(t, err) - - cleanupStaleSockets() - - // Stale socket should be removed - _, err = os.Stat(staleSocket) - assert.True(t, os.IsNotExist(err)) - - // Active socket should still exist - _, err = os.Stat(activeSocket) - assert.NoError(t, err) -} diff --git a/nix/inputs/dms-cli/internal/server/wayland/gamma.go b/nix/inputs/dms-cli/internal/server/wayland/gamma.go deleted file mode 100644 index 07d7795..0000000 --- a/nix/inputs/dms-cli/internal/server/wayland/gamma.go +++ /dev/null @@ -1,88 +0,0 @@ -package wayland - -import ( - "math" - - "github.com/AvengeMedia/danklinux/internal/utils" -) - -type GammaRamp struct { - Red []uint16 - Green []uint16 - Blue []uint16 -} - -func GenerateGammaRamp(size uint32, temp int, gamma float64) GammaRamp { - ramp := GammaRamp{ - Red: make([]uint16, size), - Green: make([]uint16, size), - Blue: make([]uint16, size), - } - - for i := uint32(0); i < size; i++ { - val := float64(i) / float64(size-1) - - valGamma := math.Pow(val, 1.0/gamma) - - r, g, b := temperatureToRGB(temp) - - ramp.Red[i] = uint16(utils.Clamp(valGamma*r*65535.0, 0, 65535)) - ramp.Green[i] = uint16(utils.Clamp(valGamma*g*65535.0, 0, 65535)) - ramp.Blue[i] = uint16(utils.Clamp(valGamma*b*65535.0, 0, 65535)) - } - - return ramp -} - -func GenerateIdentityRamp(size uint32) GammaRamp { - ramp := GammaRamp{ - Red: make([]uint16, size), - Green: make([]uint16, size), - Blue: make([]uint16, size), - } - - for i := uint32(0); i < size; i++ { - val := uint16((float64(i) / float64(size-1)) * 65535.0) - ramp.Red[i] = val - ramp.Green[i] = val - ramp.Blue[i] = val - } - - return ramp -} - -func temperatureToRGB(temp int) (float64, float64, float64) { - tempK := float64(temp) / 100.0 - - var r, g, b float64 - - if tempK <= 66 { - r = 1.0 - } else { - r = tempK - 60 - r = 329.698727446 * math.Pow(r, -0.1332047592) - r = utils.Clamp(r, 0, 255) / 255.0 - } - - if tempK <= 66 { - g = tempK - g = 99.4708025861*math.Log(g) - 161.1195681661 - g = utils.Clamp(g, 0, 255) / 255.0 - } else { - g = tempK - 60 - g = 288.1221695283 * math.Pow(g, -0.0755148492) - g = utils.Clamp(g, 0, 255) / 255.0 - } - - if tempK >= 66 { - b = 1.0 - } else if tempK <= 19 { - b = 0.0 - } else { - b = tempK - 10 - b = 138.5177312231*math.Log(b) - 305.0447927307 - b = utils.Clamp(b, 0, 255) / 255.0 - } - - return r, g, b -} diff --git a/nix/inputs/dms-cli/internal/server/wayland/gamma_test.go b/nix/inputs/dms-cli/internal/server/wayland/gamma_test.go deleted file mode 100644 index 05f26e8..0000000 --- a/nix/inputs/dms-cli/internal/server/wayland/gamma_test.go +++ /dev/null @@ -1,120 +0,0 @@ -package wayland - -import ( - "testing" - - "github.com/AvengeMedia/danklinux/internal/utils" -) - -func TestGenerateGammaRamp(t *testing.T) { - tests := []struct { - name string - size uint32 - temp int - gamma float64 - }{ - {"small_warm", 16, 6500, 1.0}, - {"small_cool", 16, 4000, 1.0}, - {"large_warm", 256, 6500, 1.0}, - {"large_cool", 256, 4000, 1.0}, - {"custom_gamma", 64, 5500, 1.2}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - ramp := GenerateGammaRamp(tt.size, tt.temp, tt.gamma) - - if len(ramp.Red) != int(tt.size) { - t.Errorf("expected %d red values, got %d", tt.size, len(ramp.Red)) - } - if len(ramp.Green) != int(tt.size) { - t.Errorf("expected %d green values, got %d", tt.size, len(ramp.Green)) - } - if len(ramp.Blue) != int(tt.size) { - t.Errorf("expected %d blue values, got %d", tt.size, len(ramp.Blue)) - } - - if ramp.Red[0] != 0 || ramp.Green[0] != 0 || ramp.Blue[0] != 0 { - t.Errorf("first values should be 0, got R:%d G:%d B:%d", - ramp.Red[0], ramp.Green[0], ramp.Blue[0]) - } - - lastIdx := tt.size - 1 - if ramp.Red[lastIdx] == 0 || ramp.Green[lastIdx] == 0 || ramp.Blue[lastIdx] == 0 { - t.Errorf("last values should be non-zero, got R:%d G:%d B:%d", - ramp.Red[lastIdx], ramp.Green[lastIdx], ramp.Blue[lastIdx]) - } - - for i := uint32(1); i < tt.size; i++ { - if ramp.Red[i] < ramp.Red[i-1] { - t.Errorf("red ramp not monotonic at index %d", i) - } - } - }) - } -} - -func TestTemperatureToRGB(t *testing.T) { - tests := []struct { - name string - temp int - }{ - {"very_warm", 6500}, - {"neutral", 5500}, - {"cool", 4000}, - {"very_cool", 3000}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r, g, b := temperatureToRGB(tt.temp) - - if r < 0 || r > 1 { - t.Errorf("red out of range: %f", r) - } - if g < 0 || g > 1 { - t.Errorf("green out of range: %f", g) - } - if b < 0 || b > 1 { - t.Errorf("blue out of range: %f", b) - } - }) - } -} - -func TestTemperatureProgression(t *testing.T) { - temps := []int{3000, 4000, 5000, 6000, 6500} - - var prevBlue float64 - for i, temp := range temps { - _, _, b := temperatureToRGB(temp) - if i > 0 && b < prevBlue { - t.Errorf("blue should increase with temperature, %d->%d: %f->%f", - temps[i-1], temp, prevBlue, b) - } - prevBlue = b - } -} - -func TestClamp(t *testing.T) { - tests := []struct { - val float64 - min float64 - max float64 - expected float64 - }{ - {5, 0, 10, 5}, - {-5, 0, 10, 0}, - {15, 0, 10, 10}, - {0, 0, 10, 0}, - {10, 0, 10, 10}, - } - - for _, tt := range tests { - result := utils.Clamp(tt.val, tt.min, tt.max) - if result != tt.expected { - t.Errorf("clamp(%f, %f, %f) = %f, want %f", - tt.val, tt.min, tt.max, result, tt.expected) - } - } -} diff --git a/nix/inputs/dms-cli/internal/server/wayland/geolocation.go b/nix/inputs/dms-cli/internal/server/wayland/geolocation.go deleted file mode 100644 index 43a986a..0000000 --- a/nix/inputs/dms-cli/internal/server/wayland/geolocation.go +++ /dev/null @@ -1,50 +0,0 @@ -package wayland - -import ( - "encoding/json" - "fmt" - "io" - "net/http" - "time" - - "github.com/AvengeMedia/danklinux/internal/log" -) - -type ipAPIResponse struct { - Lat float64 `json:"lat"` - Lon float64 `json:"lon"` - City string `json:"city"` -} - -func FetchIPLocation() (*float64, *float64, error) { - client := &http.Client{ - Timeout: 10 * time.Second, - } - - resp, err := client.Get("http://ip-api.com/json/") - if err != nil { - return nil, nil, fmt.Errorf("failed to fetch IP location: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return nil, nil, fmt.Errorf("ip-api.com returned status %d", resp.StatusCode) - } - - body, err := io.ReadAll(resp.Body) - if err != nil { - return nil, nil, fmt.Errorf("failed to read response: %w", err) - } - - var data ipAPIResponse - if err := json.Unmarshal(body, &data); err != nil { - return nil, nil, fmt.Errorf("failed to parse response: %w", err) - } - - if data.Lat == 0 && data.Lon == 0 { - return nil, nil, fmt.Errorf("missing location data in response") - } - - log.Infof("Fetched IP-based location: %s (%.4f, %.4f)", data.City, data.Lat, data.Lon) - return &data.Lat, &data.Lon, nil -} diff --git a/nix/inputs/dms-cli/internal/server/wayland/handlers.go b/nix/inputs/dms-cli/internal/server/wayland/handlers.go deleted file mode 100644 index 8568af8..0000000 --- a/nix/inputs/dms-cli/internal/server/wayland/handlers.go +++ /dev/null @@ -1,205 +0,0 @@ -package wayland - -import ( - "encoding/json" - "fmt" - "net" - "time" - - "github.com/AvengeMedia/danklinux/internal/server/models" -) - -type Request struct { - ID int `json:"id,omitempty"` - Method string `json:"method"` - Params map[string]interface{} `json:"params,omitempty"` -} - -type SuccessResult struct { - Success bool `json:"success"` - Message string `json:"message"` -} - -func HandleRequest(conn net.Conn, req Request, manager *Manager) { - if manager == nil { - models.RespondError(conn, req.ID, "wayland manager not initialized") - return - } - - switch req.Method { - case "wayland.gamma.getState": - handleGetState(conn, req, manager) - case "wayland.gamma.setTemperature": - handleSetTemperature(conn, req, manager) - case "wayland.gamma.setLocation": - handleSetLocation(conn, req, manager) - case "wayland.gamma.setManualTimes": - handleSetManualTimes(conn, req, manager) - case "wayland.gamma.setUseIPLocation": - handleSetUseIPLocation(conn, req, manager) - case "wayland.gamma.setGamma": - handleSetGamma(conn, req, manager) - case "wayland.gamma.setEnabled": - handleSetEnabled(conn, req, manager) - case "wayland.gamma.subscribe": - handleSubscribe(conn, req, manager) - default: - models.RespondError(conn, req.ID, fmt.Sprintf("unknown method: %s", req.Method)) - } -} - -func handleGetState(conn net.Conn, req Request, manager *Manager) { - state := manager.GetState() - models.Respond(conn, req.ID, state) -} - -func handleSetTemperature(conn net.Conn, req Request, manager *Manager) { - var lowTemp, highTemp int - - if temp, ok := req.Params["temp"].(float64); ok { - lowTemp = int(temp) - highTemp = int(temp) - } else { - low, okLow := req.Params["low"].(float64) - high, okHigh := req.Params["high"].(float64) - - if !okLow || !okHigh { - models.RespondError(conn, req.ID, "missing temperature parameters (provide 'temp' or both 'low' and 'high')") - return - } - - lowTemp = int(low) - highTemp = int(high) - } - - if err := manager.SetTemperature(lowTemp, highTemp); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "temperature set"}) -} - -func handleSetLocation(conn net.Conn, req Request, manager *Manager) { - lat, ok := req.Params["latitude"].(float64) - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'latitude' parameter") - return - } - - lon, ok := req.Params["longitude"].(float64) - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'longitude' parameter") - return - } - - if err := manager.SetLocation(lat, lon); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "location set"}) -} - -func handleSetManualTimes(conn net.Conn, req Request, manager *Manager) { - sunriseParam := req.Params["sunrise"] - sunsetParam := req.Params["sunset"] - - if sunriseParam == nil || sunsetParam == nil { - manager.ClearManualTimes() - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "manual times cleared"}) - return - } - - sunriseStr, ok := sunriseParam.(string) - if !ok || sunriseStr == "" { - manager.ClearManualTimes() - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "manual times cleared"}) - return - } - - sunsetStr, ok := sunsetParam.(string) - if !ok || sunsetStr == "" { - manager.ClearManualTimes() - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "manual times cleared"}) - return - } - - sunrise, err := time.Parse("15:04", sunriseStr) - if err != nil { - models.RespondError(conn, req.ID, "invalid sunrise format (use HH:MM)") - return - } - - sunset, err := time.Parse("15:04", sunsetStr) - if err != nil { - models.RespondError(conn, req.ID, "invalid sunset format (use HH:MM)") - return - } - - if err := manager.SetManualTimes(sunrise, sunset); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "manual times set"}) -} - -func handleSetUseIPLocation(conn net.Conn, req Request, manager *Manager) { - use, ok := req.Params["use"].(bool) - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'use' parameter") - return - } - - manager.SetUseIPLocation(use) - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "IP location preference set"}) -} - -func handleSetGamma(conn net.Conn, req Request, manager *Manager) { - gamma, ok := req.Params["gamma"].(float64) - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'gamma' parameter") - return - } - - if err := manager.SetGamma(gamma); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "gamma set"}) -} - -func handleSetEnabled(conn net.Conn, req Request, manager *Manager) { - enabled, ok := req.Params["enabled"].(bool) - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'enabled' parameter") - return - } - - manager.SetEnabled(enabled) - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "enabled state set"}) -} - -func handleSubscribe(conn net.Conn, req Request, manager *Manager) { - clientID := fmt.Sprintf("client-%p", conn) - stateChan := manager.Subscribe(clientID) - defer manager.Unsubscribe(clientID) - - initialState := manager.GetState() - if err := json.NewEncoder(conn).Encode(models.Response[State]{ - ID: req.ID, - Result: &initialState, - }); err != nil { - return - } - - for state := range stateChan { - if err := json.NewEncoder(conn).Encode(models.Response[State]{ - Result: &state, - }); err != nil { - return - } - } -} diff --git a/nix/inputs/dms-cli/internal/server/wayland/manager.go b/nix/inputs/dms-cli/internal/server/wayland/manager.go deleted file mode 100644 index 5c2ffd9..0000000 --- a/nix/inputs/dms-cli/internal/server/wayland/manager.go +++ /dev/null @@ -1,1367 +0,0 @@ -package wayland - -import ( - "bytes" - "encoding/binary" - "fmt" - "os" - "syscall" - "time" - - "github.com/godbus/dbus/v5" - wlclient "github.com/yaslama/go-wayland/wayland/client" - "golang.org/x/sys/unix" - - "github.com/AvengeMedia/danklinux/internal/errdefs" - "github.com/AvengeMedia/danklinux/internal/log" - "github.com/AvengeMedia/danklinux/internal/proto/wlr_gamma_control" -) - -func NewManager(display *wlclient.Display, config Config) (*Manager, error) { - if err := config.Validate(); err != nil { - return nil, err - } - - m := &Manager{ - config: config, - display: display, - outputs: make(map[uint32]*outputState), - cmdq: make(chan cmd, 128), - stopChan: make(chan struct{}), - updateTrigger: make(chan struct{}, 1), - subscribers: make(map[string]chan State), - dirty: make(chan struct{}, 1), - dbusSignal: make(chan *dbus.Signal, 16), - transitionChan: make(chan int, 1), - } - - if err := m.setupRegistry(); err != nil { - return nil, err - } - - // Setup D-Bus monitoring for suspend/resume events - if err := m.setupDBusMonitor(); err != nil { - log.Warnf("Failed to setup D-Bus monitoring for suspend/resume: %v", err) - // Don't fail initialization if D-Bus setup fails, just continue without it - } - - // Initialize currentTemp and targetTemp before starting any goroutines - now := time.Now() - initial := m.calculateTemperature(now) - m.transitionMutex.Lock() - m.currentTemp = initial - m.targetTemp = initial - m.transitionMutex.Unlock() - - m.alive = true - m.updateState() - - m.notifierWg.Add(1) - go m.notifier() - - m.wg.Add(1) - go m.updateLoop() - - if m.dbusConn != nil { - m.wg.Add(1) - go m.dbusMonitor() - } - - m.wg.Add(1) - go m.waylandActor() - - m.wg.Add(1) - go m.transitionWorker() - - if config.Enabled { - m.post(func() { - log.Info("Gamma control enabled at startup, initializing controls") - gammaMgr := m.gammaControl.(*wlr_gamma_control.ZwlrGammaControlManagerV1) - if err := func() error { - var outputs []*wlclient.Output = m.availableOutputs - return m.setupOutputControls(outputs, gammaMgr) - }(); err != nil { - log.Errorf("Failed to initialize gamma controls: %v", err) - } else { - m.controlsInitialized = true - } - }) - } - - return m, nil -} - -func (m *Manager) post(fn func()) { - select { - case m.cmdq <- cmd{fn: fn}: - default: - log.Warn("Actor command queue full, dropping command") - } -} - -func (m *Manager) waylandActor() { - defer m.wg.Done() - - for { - select { - case <-m.stopChan: - return - case c := <-m.cmdq: - c.fn() - } - } -} - -func (m *Manager) allOutputsReady() bool { - m.outputsMutex.RLock() - defer m.outputsMutex.RUnlock() - if len(m.outputs) == 0 { - return false - } - for _, o := range m.outputs { - if o.rampSize == 0 || o.failed { - return false - } - } - return true -} - -func (m *Manager) setupDBusMonitor() error { - conn, err := dbus.ConnectSystemBus() - if err != nil { - return fmt.Errorf("failed to connect to system bus: %w", err) - } - - // Subscribe to PrepareForSleep signal - matchRule := "type='signal',interface='org.freedesktop.login1.Manager',member='PrepareForSleep',path='/org/freedesktop/login1'" - if err := conn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, matchRule).Err; err != nil { - conn.Close() - return fmt.Errorf("failed to add match rule: %w", err) - } - - conn.Signal(m.dbusSignal) - m.dbusConn = conn - - log.Info("D-Bus monitoring for suspend/resume events enabled") - return nil -} - -func (m *Manager) setupRegistry() error { - log.Info("setupRegistry: starting registry setup") - ctx := m.display.Context() - - registry, err := m.display.GetRegistry() - if err != nil { - return fmt.Errorf("failed to get registry: %w", err) - } - m.registry = registry - - outputs := make([]*wlclient.Output, 0) - outputRegNames := make(map[uint32]uint32) - outputNames := make(map[uint32]string) - var gammaMgr *wlr_gamma_control.ZwlrGammaControlManagerV1 - - registry.SetGlobalHandler(func(e wlclient.RegistryGlobalEvent) { - switch e.Interface { - case wlr_gamma_control.ZwlrGammaControlManagerV1InterfaceName: - log.Infof("setupRegistry: found %s", wlr_gamma_control.ZwlrGammaControlManagerV1InterfaceName) - manager := wlr_gamma_control.NewZwlrGammaControlManagerV1(ctx) - version := e.Version - if version > 1 { - version = 1 - } - if err := registry.Bind(e.Name, e.Interface, version, manager); err == nil { - gammaMgr = manager - log.Info("setupRegistry: gamma control manager bound successfully") - } else { - log.Errorf("setupRegistry: failed to bind gamma control: %v", err) - } - case "wl_output": - log.Debugf("Global event: found wl_output (name=%d)", e.Name) - output := wlclient.NewOutput(ctx) - version := e.Version - if version > 4 { - version = 4 - } - if err := registry.Bind(e.Name, e.Interface, version, output); err == nil { - outputID := output.ID() - log.Infof("Bound wl_output id=%d registry_name=%d", outputID, e.Name) - - output.SetNameHandler(func(ev wlclient.OutputNameEvent) { - log.Infof("Output %d name: %s", outputID, ev.Name) - outputNames[outputID] = ev.Name - isVirtual := len(ev.Name) >= 9 && ev.Name[:9] == "HEADLESS-" - if isVirtual { - log.Infof("Output %d identified as virtual", outputID) - } - }) - - if gammaMgr != nil { - outputs = append(outputs, output) - outputRegNames[outputID] = e.Name - } - - m.outputsMutex.Lock() - if m.outputRegNames != nil { - m.outputRegNames[outputID] = e.Name - } - m.outputsMutex.Unlock() - - m.configMutex.RLock() - enabled := m.config.Enabled - m.configMutex.RUnlock() - - if enabled && m.controlsInitialized { - m.post(func() { - log.Infof("New output %d added, creating gamma control", outputID) - if err := m.addOutputControl(output); err != nil { - log.Errorf("Failed to add gamma control for new output %d: %v", outputID, err) - } - }) - } else if enabled && !m.controlsInitialized { - m.post(func() { - log.Infof("Output %d added after all were removed, creating gamma control", outputID) - if err := m.addOutputControl(output); err != nil { - log.Errorf("Failed to add gamma control for output %d: %v", outputID, err) - } else { - m.controlsInitialized = true - } - }) - } - } else { - log.Errorf("Failed to bind wl_output: %v", err) - } - } - }) - - registry.SetGlobalRemoveHandler(func(e wlclient.RegistryGlobalRemoveEvent) { - m.post(func() { - m.outputsMutex.Lock() - defer m.outputsMutex.Unlock() - - for id, out := range m.outputs { - if out.registryName == e.Name { - log.Infof("Output %d (registry name %d) removed, destroying gamma control", id, e.Name) - if out.gammaControl != nil { - control := out.gammaControl.(*wlr_gamma_control.ZwlrGammaControlV1) - control.Destroy() - } - delete(m.outputs, id) - - if len(m.outputs) == 0 { - m.controlsInitialized = false - log.Info("All outputs removed, controls no longer initialized") - } - return - } - } - }) - }) - - if err := m.display.Roundtrip(); err != nil { - return fmt.Errorf("first roundtrip failed: %w", err) - } - if err := m.display.Roundtrip(); err != nil { - return fmt.Errorf("second roundtrip failed: %w", err) - } - - log.Infof("setupRegistry: discovered gamma_manager=%v, outputs=%d", gammaMgr != nil, len(outputs)) - - if gammaMgr == nil { - log.Error("setupRegistry: gamma control manager not found in registry") - return errdefs.ErrNoGammaControl - } - - if len(outputs) == 0 { - log.Error("setupRegistry: no wl_output objects found") - return fmt.Errorf("no outputs available") - } - - physicalOutputs := make([]*wlclient.Output, 0) - for _, output := range outputs { - outputID := output.ID() - name := outputNames[outputID] - if name != "" && (len(name) >= 9 && name[:9] == "HEADLESS-") { - log.Infof("Skipping virtual output %d (name=%s) for gamma control", outputID, name) - continue - } - physicalOutputs = append(physicalOutputs, output) - } - - log.Infof("setupRegistry: filtered %d physical outputs from %d total outputs", len(physicalOutputs), len(outputs)) - - m.gammaControl = gammaMgr - m.availableOutputs = physicalOutputs - m.outputRegNames = outputRegNames - - log.Info("setupRegistry: completed successfully (gamma controls will be initialized when enabled)") - return nil -} - -func (m *Manager) setupOutputControls(outputs []*wlclient.Output, manager *wlr_gamma_control.ZwlrGammaControlManagerV1) error { - log.Infof("setupOutputControls: creating gamma controls for %d outputs", len(outputs)) - - for _, output := range outputs { - control, err := manager.GetGammaControl(output) - if err != nil { - log.Warnf("Failed to get gamma control for output %d: %v", output.ID(), err) - continue - } - - outState := &outputState{ - id: output.ID(), - registryName: m.outputRegNames[output.ID()], - output: output, - gammaControl: control, - isVirtual: false, - } - - func(state *outputState) { - control.SetGammaSizeHandler(func(e wlr_gamma_control.ZwlrGammaControlV1GammaSizeEvent) { - m.outputsMutex.Lock() - if outState, exists := m.outputs[state.id]; exists { - outState.rampSize = e.Size - outState.failed = false - outState.retryCount = 0 - log.Infof("Output %d gamma_size=%d", state.id, e.Size) - } - m.outputsMutex.Unlock() - - m.transitionMutex.RLock() - currentTemp := m.currentTemp - m.transitionMutex.RUnlock() - - m.post(func() { - m.applyNowOnActor(currentTemp) - }) - }) - - control.SetFailedHandler(func(e wlr_gamma_control.ZwlrGammaControlV1FailedEvent) { - m.outputsMutex.Lock() - if outState, exists := m.outputs[state.id]; exists { - outState.failed = true - outState.rampSize = 0 - outState.retryCount++ - outState.lastFailTime = time.Now() - - retryCount := outState.retryCount - if retryCount == 1 || retryCount%5 == 0 { - log.Errorf("Gamma control failed for output %d (attempt %d)", state.id, retryCount) - } - - backoff := time.Duration(300<= 9 && ev.Name[:9] == "HEADLESS-" { - log.Infof("Detected virtual output %d (name=%s), marking for gamma control skip", outputID, ev.Name) - outState.isVirtual = true - outState.failed = true - } - } - m.outputsMutex.Unlock() - }) - - gammaMgr := m.gammaControl.(*wlr_gamma_control.ZwlrGammaControlManagerV1) - - control, err := gammaMgr.GetGammaControl(output) - if err != nil { - return fmt.Errorf("failed to get gamma control: %w", err) - } - - outState := &outputState{ - id: outputID, - name: outputName, - registryName: m.outputRegNames[outputID], - output: output, - gammaControl: control, - isVirtual: false, - } - - control.SetGammaSizeHandler(func(e wlr_gamma_control.ZwlrGammaControlV1GammaSizeEvent) { - m.outputsMutex.Lock() - if out, exists := m.outputs[outState.id]; exists { - out.rampSize = e.Size - out.failed = false - out.retryCount = 0 - log.Infof("Output %d gamma_size=%d", outState.id, e.Size) - } - m.outputsMutex.Unlock() - - m.transitionMutex.RLock() - currentTemp := m.currentTemp - m.transitionMutex.RUnlock() - - m.post(func() { - m.applyNowOnActor(currentTemp) - }) - }) - - control.SetFailedHandler(func(e wlr_gamma_control.ZwlrGammaControlV1FailedEvent) { - m.outputsMutex.Lock() - if out, exists := m.outputs[outState.id]; exists { - out.failed = true - out.rampSize = 0 - out.retryCount++ - out.lastFailTime = time.Now() - - retryCount := out.retryCount - if retryCount == 1 || retryCount%5 == 0 { - log.Errorf("Gamma control failed for output %d (attempt %d)", outState.id, retryCount) - } - - backoff := time.Duration(300< %dK over %v", currentTemp, targetTemp, dur) - - for i := 0; i <= steps; i++ { - select { - case newTarget := <-m.transitionChan: - m.transitionMutex.Lock() - m.targetTemp = newTarget - m.transitionMutex.Unlock() - log.Debugf("Transition %dK -> %dK aborted (newer transition started)", currentTemp, targetTemp) - break - default: - } - - m.transitionMutex.RLock() - if m.targetTemp != targetTemp { - m.transitionMutex.RUnlock() - break - } - m.transitionMutex.RUnlock() - - progress := float64(i) / float64(steps) - temp := currentTemp + int(float64(targetTemp-currentTemp)*progress) - - m.post(func() { m.applyNowOnActor(temp) }) - - if i < steps { - time.Sleep(stepDur) - } - } - - m.transitionMutex.RLock() - finalTarget := m.targetTemp - m.transitionMutex.RUnlock() - - if finalTarget == targetTemp { - log.Debugf("Transition complete: now at %dK", targetTemp) - - m.configMutex.RLock() - enabled := m.config.Enabled - identityTemp := m.config.HighTemp - m.configMutex.RUnlock() - - if !enabled && targetTemp == identityTemp && m.controlsInitialized { - m.post(func() { - log.Info("Destroying gamma controls after transition to identity") - m.outputsMutex.Lock() - for id, out := range m.outputs { - if out.gammaControl != nil { - control := out.gammaControl.(*wlr_gamma_control.ZwlrGammaControlV1) - control.Destroy() - log.Debugf("Destroyed gamma control for output %d", id) - } - } - m.outputs = make(map[uint32]*outputState) - m.controlsInitialized = false - m.outputsMutex.Unlock() - - m.transitionMutex.Lock() - m.currentTemp = identityTemp - m.targetTemp = identityTemp - m.transitionMutex.Unlock() - - if _, err := m.display.Sync(); err != nil { - log.Warnf("Failed to sync Wayland display after destroying controls: %v", err) - } - - log.Info("All gamma controls destroyed") - }) - } - } - } - } -} - -func (m *Manager) recreateOutputControl(out *outputState) error { - m.configMutex.RLock() - enabled := m.config.Enabled - m.configMutex.RUnlock() - - if !enabled || !m.controlsInitialized { - return nil - } - - m.outputsMutex.RLock() - _, exists := m.outputs[out.id] - m.outputsMutex.RUnlock() - - if !exists { - return nil - } - - if out.isVirtual { - return nil - } - - const maxRetries = 10 - if out.retryCount >= maxRetries { - return nil - } - - gammaMgr, ok := m.gammaControl.(*wlr_gamma_control.ZwlrGammaControlManagerV1) - if !ok || gammaMgr == nil { - return fmt.Errorf("gamma control manager not available") - } - control, err := gammaMgr.GetGammaControl(out.output) - if err != nil { - return fmt.Errorf("get gamma control: %w", err) - } - - state := out - control.SetGammaSizeHandler(func(e wlr_gamma_control.ZwlrGammaControlV1GammaSizeEvent) { - m.outputsMutex.Lock() - if outState, exists := m.outputs[state.id]; exists { - outState.rampSize = e.Size - outState.failed = false - outState.retryCount = 0 - log.Infof("Output %d gamma_size=%d (recreated)", state.id, e.Size) - } - m.outputsMutex.Unlock() - - m.transitionMutex.RLock() - currentTemp := m.currentTemp - m.transitionMutex.RUnlock() - - m.post(func() { - m.applyNowOnActor(currentTemp) - }) - }) - - control.SetFailedHandler(func(e wlr_gamma_control.ZwlrGammaControlV1FailedEvent) { - m.outputsMutex.Lock() - if outState, exists := m.outputs[state.id]; exists { - outState.failed = true - outState.rampSize = 0 - outState.retryCount++ - outState.lastFailTime = time.Now() - - retryCount := outState.retryCount - if retryCount == 1 || retryCount%5 == 0 { - log.Errorf("Gamma control failed for output %d (attempt %d)", state.id, retryCount) - } - - backoff := time.Duration(300< 1 { - return SunTimes{ - Sunrise: time.Date(year, month, day, 0, 0, 0, 0, time.UTC).In(loc), - Sunset: time.Date(year, month, day, 0, 0, 0, 0, time.UTC).In(loc), - } - } - if cosHourAngle < -1 { - return SunTimes{ - Sunrise: time.Date(year, month, day, 0, 0, 0, 0, time.UTC).In(loc), - Sunset: time.Date(year, month, day, 23, 59, 59, 0, time.UTC).In(loc), - } - } - - hourAngle := math.Acos(cosHourAngle) * radToDeg - - sunriseTime := solarNoon - hourAngle/15.0 - lon/15.0 - eqTime/60.0 - sunsetTime := solarNoon + hourAngle/15.0 - lon/15.0 - eqTime/60.0 - - sunrise := timeOfDayToTime(sunriseTime, year, month, day, time.UTC).In(loc) - sunset := timeOfDayToTime(sunsetTime, year, month, day, time.UTC).In(loc) - - return SunTimes{ - Sunrise: sunrise, - Sunset: sunset, - } -} - -func timeOfDayToTime(hours float64, year int, month time.Month, day int, loc *time.Location) time.Time { - h := int(hours) - m := int((hours - float64(h)) * 60) - s := int(((hours-float64(h))*60 - float64(m)) * 60) - - if h < 0 { - h += 24 - day-- - } - if h >= 24 { - h -= 24 - day++ - } - - return time.Date(year, month, day, h, m, s, 0, loc) -} diff --git a/nix/inputs/dms-cli/internal/server/wayland/suncalc_test.go b/nix/inputs/dms-cli/internal/server/wayland/suncalc_test.go deleted file mode 100644 index 0aaa535..0000000 --- a/nix/inputs/dms-cli/internal/server/wayland/suncalc_test.go +++ /dev/null @@ -1,378 +0,0 @@ -package wayland - -import ( - "math" - "testing" - "time" -) - -func calculateTemperature(config Config, now time.Time) int { - if !config.Enabled { - return config.HighTemp - } - - var sunrise, sunset time.Time - - if config.ManualSunrise != nil && config.ManualSunset != nil { - year, month, day := now.Date() - loc := now.Location() - - sunrise = time.Date(year, month, day, - config.ManualSunrise.Hour(), - config.ManualSunrise.Minute(), - config.ManualSunrise.Second(), 0, loc) - sunset = time.Date(year, month, day, - config.ManualSunset.Hour(), - config.ManualSunset.Minute(), - config.ManualSunset.Second(), 0, loc) - - if sunset.Before(sunrise) { - sunset = sunset.Add(24 * time.Hour) - } - } else if config.UseIPLocation { - lat, lon, err := FetchIPLocation() - if err != nil { - return config.HighTemp - } - times := CalculateSunTimes(*lat, *lon, now) - sunrise = times.Sunrise - sunset = times.Sunset - } else if config.Latitude != nil && config.Longitude != nil { - times := CalculateSunTimes(*config.Latitude, *config.Longitude, now) - sunrise = times.Sunrise - sunset = times.Sunset - } else { - return config.HighTemp - } - - if now.Before(sunrise) || now.After(sunset) { - return config.LowTemp - } - return config.HighTemp -} - -func calculateNextTransition(config Config, now time.Time) time.Time { - if !config.Enabled { - return now.Add(24 * time.Hour) - } - - var sunrise, sunset time.Time - - if config.ManualSunrise != nil && config.ManualSunset != nil { - year, month, day := now.Date() - loc := now.Location() - - sunrise = time.Date(year, month, day, - config.ManualSunrise.Hour(), - config.ManualSunrise.Minute(), - config.ManualSunrise.Second(), 0, loc) - sunset = time.Date(year, month, day, - config.ManualSunset.Hour(), - config.ManualSunset.Minute(), - config.ManualSunset.Second(), 0, loc) - - if sunset.Before(sunrise) { - sunset = sunset.Add(24 * time.Hour) - } - } else if config.UseIPLocation { - lat, lon, err := FetchIPLocation() - if err != nil { - return now.Add(24 * time.Hour) - } - times := CalculateSunTimes(*lat, *lon, now) - sunrise = times.Sunrise - sunset = times.Sunset - } else if config.Latitude != nil && config.Longitude != nil { - times := CalculateSunTimes(*config.Latitude, *config.Longitude, now) - sunrise = times.Sunrise - sunset = times.Sunset - } else { - return now.Add(24 * time.Hour) - } - - if now.Before(sunrise) { - return sunrise - } - if now.Before(sunset) { - return sunset - } - - if config.ManualSunrise != nil && config.ManualSunset != nil { - year, month, day := now.Add(24 * time.Hour).Date() - loc := now.Location() - nextSunrise := time.Date(year, month, day, - config.ManualSunrise.Hour(), - config.ManualSunrise.Minute(), - config.ManualSunrise.Second(), 0, loc) - return nextSunrise - } - - if config.UseIPLocation { - lat, lon, err := FetchIPLocation() - if err != nil { - return now.Add(24 * time.Hour) - } - nextDayTimes := CalculateSunTimes(*lat, *lon, now.Add(24*time.Hour)) - return nextDayTimes.Sunrise - } - - if config.Latitude != nil && config.Longitude != nil { - nextDayTimes := CalculateSunTimes(*config.Latitude, *config.Longitude, now.Add(24*time.Hour)) - return nextDayTimes.Sunrise - } - - return now.Add(24 * time.Hour) -} - -func TestCalculateSunTimes(t *testing.T) { - tests := []struct { - name string - lat float64 - lon float64 - date time.Time - checkFunc func(*testing.T, SunTimes) - }{ - { - name: "new_york_summer", - lat: 40.7128, - lon: -74.0060, - date: time.Date(2024, 6, 21, 12, 0, 0, 0, time.Local), - checkFunc: func(t *testing.T, times SunTimes) { - if times.Sunrise.Hour() < 4 || times.Sunrise.Hour() > 6 { - t.Logf("sunrise: %v", times.Sunrise) - } - if times.Sunset.Hour() < 19 || times.Sunset.Hour() > 21 { - t.Logf("sunset: %v", times.Sunset) - } - if !times.Sunset.After(times.Sunrise) { - t.Error("sunset should be after sunrise") - } - }, - }, - { - name: "london_winter", - lat: 51.5074, - lon: -0.1278, - date: time.Date(2024, 12, 21, 12, 0, 0, 0, time.UTC), - checkFunc: func(t *testing.T, times SunTimes) { - if times.Sunrise.Hour() < 7 || times.Sunrise.Hour() > 9 { - t.Errorf("unexpected sunrise hour: %d", times.Sunrise.Hour()) - } - if times.Sunset.Hour() < 15 || times.Sunset.Hour() > 17 { - t.Errorf("unexpected sunset hour: %d", times.Sunset.Hour()) - } - }, - }, - { - name: "equator_equinox", - lat: 0.0, - lon: 0.0, - date: time.Date(2024, 3, 20, 12, 0, 0, 0, time.UTC), - checkFunc: func(t *testing.T, times SunTimes) { - if times.Sunrise.Hour() < 5 || times.Sunrise.Hour() > 7 { - t.Errorf("unexpected sunrise hour: %d", times.Sunrise.Hour()) - } - if times.Sunset.Hour() < 17 || times.Sunset.Hour() > 19 { - t.Errorf("unexpected sunset hour: %d", times.Sunset.Hour()) - } - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - times := CalculateSunTimes(tt.lat, tt.lon, tt.date) - tt.checkFunc(t, times) - }) - } -} - -func TestCalculateTemperature(t *testing.T) { - lat := 40.7128 - lon := -74.0060 - date := time.Date(2024, 6, 21, 0, 0, 0, 0, time.Local) - - config := Config{ - LowTemp: 4000, - HighTemp: 6500, - Latitude: &lat, - Longitude: &lon, - Enabled: true, - } - - times := CalculateSunTimes(lat, lon, date) - - tests := []struct { - name string - timeFunc func() time.Time - wantTemp int - }{ - { - name: "midnight", - timeFunc: func() time.Time { return times.Sunrise.Add(-4 * time.Hour) }, - wantTemp: 4000, - }, - { - name: "sunrise", - timeFunc: func() time.Time { return times.Sunrise }, - wantTemp: 6500, - }, - { - name: "noon", - timeFunc: func() time.Time { return times.Sunrise.Add(6 * time.Hour) }, - wantTemp: 6500, - }, - { - name: "after_sunset_transition", - timeFunc: func() time.Time { return times.Sunset.Add(2 * time.Hour) }, - wantTemp: 4000, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - temp := calculateTemperature(config, tt.timeFunc()) - - if math.Abs(float64(temp-tt.wantTemp)) > 500 { - t.Errorf("temperature = %d, want approximately %d", temp, tt.wantTemp) - } - }) - } -} - -func TestCalculateTemperatureManualTimes(t *testing.T) { - sunrise := time.Date(0, 1, 1, 6, 30, 0, 0, time.Local) - sunset := time.Date(0, 1, 1, 18, 30, 0, 0, time.Local) - - config := Config{ - LowTemp: 4000, - HighTemp: 6500, - ManualSunrise: &sunrise, - ManualSunset: &sunset, - Enabled: true, - } - - tests := []struct { - name string - time time.Time - want int - }{ - {"before_sunrise", time.Date(2024, 1, 1, 3, 0, 0, 0, time.Local), 4000}, - {"at_sunrise", time.Date(2024, 1, 1, 6, 30, 0, 0, time.Local), 6500}, - {"midday", time.Date(2024, 1, 1, 12, 0, 0, 0, time.Local), 6500}, - {"at_sunset", time.Date(2024, 1, 1, 18, 30, 0, 0, time.Local), 6500}, - {"after_sunset", time.Date(2024, 1, 1, 22, 0, 0, 0, time.Local), 4000}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - temp := calculateTemperature(config, tt.time) - if math.Abs(float64(temp-tt.want)) > 500 { - t.Errorf("temperature = %d, want approximately %d", temp, tt.want) - } - }) - } -} - -func TestCalculateTemperatureDisabled(t *testing.T) { - lat := 40.7128 - lon := -74.0060 - - config := Config{ - LowTemp: 4000, - HighTemp: 6500, - Latitude: &lat, - Longitude: &lon, - Enabled: false, - } - - temp := calculateTemperature(config, time.Now()) - if temp != 6500 { - t.Errorf("disabled should return high temp, got %d", temp) - } -} - -func TestCalculateNextTransition(t *testing.T) { - lat := 40.7128 - lon := -74.0060 - date := time.Date(2024, 6, 21, 0, 0, 0, 0, time.Local) - - config := Config{ - LowTemp: 4000, - HighTemp: 6500, - Latitude: &lat, - Longitude: &lon, - Enabled: true, - } - - times := CalculateSunTimes(lat, lon, date) - - tests := []struct { - name string - now time.Time - checkFunc func(*testing.T, time.Time) - }{ - { - name: "before_sunrise", - now: times.Sunrise.Add(-2 * time.Hour), - checkFunc: func(t *testing.T, next time.Time) { - if !next.Equal(times.Sunrise) && !next.After(times.Sunrise.Add(-1*time.Minute)) { - t.Error("next transition should be at or near sunrise") - } - }, - }, - { - name: "after_sunrise", - now: times.Sunrise.Add(2 * time.Hour), - checkFunc: func(t *testing.T, next time.Time) { - if !next.After(times.Sunrise) { - t.Error("next transition should be after sunrise") - } - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - next := calculateNextTransition(config, tt.now) - tt.checkFunc(t, next) - }) - } -} - -func TestTimeOfDayToTime(t *testing.T) { - tests := []struct { - name string - hours float64 - expected time.Time - }{ - { - name: "noon", - hours: 12.0, - expected: time.Date(2024, 6, 21, 12, 0, 0, 0, time.Local), - }, - { - name: "half_past", - hours: 12.5, - expected: time.Date(2024, 6, 21, 12, 30, 0, 0, time.Local), - }, - { - name: "early_morning", - hours: 6.25, - expected: time.Date(2024, 6, 21, 6, 15, 0, 0, time.Local), - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := timeOfDayToTime(tt.hours, 2024, 6, 21, time.Local) - - if result.Hour() != tt.expected.Hour() { - t.Errorf("hour = %d, want %d", result.Hour(), tt.expected.Hour()) - } - if result.Minute() != tt.expected.Minute() { - t.Errorf("minute = %d, want %d", result.Minute(), tt.expected.Minute()) - } - }) - } -} diff --git a/nix/inputs/dms-cli/internal/server/wayland/types.go b/nix/inputs/dms-cli/internal/server/wayland/types.go deleted file mode 100644 index 02ccb07..0000000 --- a/nix/inputs/dms-cli/internal/server/wayland/types.go +++ /dev/null @@ -1,194 +0,0 @@ -package wayland - -import ( - "math" - "sync" - "time" - - "github.com/AvengeMedia/danklinux/internal/errdefs" - "github.com/godbus/dbus/v5" - wlclient "github.com/yaslama/go-wayland/wayland/client" -) - -type Config struct { - Outputs []string - LowTemp int - HighTemp int - Latitude *float64 - Longitude *float64 - UseIPLocation bool - ManualSunrise *time.Time - ManualSunset *time.Time - ManualDuration *time.Duration - Gamma float64 - Enabled bool -} - -type State struct { - Config Config `json:"config"` - CurrentTemp int `json:"currentTemp"` - NextTransition time.Time `json:"nextTransition"` - SunriseTime time.Time `json:"sunriseTime"` - SunsetTime time.Time `json:"sunsetTime"` - IsDay bool `json:"isDay"` -} - -type cmd struct { - fn func() -} - -type Manager struct { - config Config - configMutex sync.RWMutex - state *State - stateMutex sync.RWMutex - - display *wlclient.Display - registry *wlclient.Registry - gammaControl interface{} - availableOutputs []*wlclient.Output - outputRegNames map[uint32]uint32 - outputs map[uint32]*outputState - outputsMutex sync.RWMutex - controlsInitialized bool - - cmdq chan cmd - alive bool - - stopChan chan struct{} - updateTrigger chan struct{} - wg sync.WaitGroup - - currentTemp int - targetTemp int - transitionMutex sync.RWMutex - transitionChan chan int - - cachedIPLat *float64 - cachedIPLon *float64 - locationMutex sync.RWMutex - - subscribers map[string]chan State - subMutex sync.RWMutex - dirty chan struct{} - notifierWg sync.WaitGroup - lastNotified *State - - dbusConn *dbus.Conn - dbusSignal chan *dbus.Signal -} - -type outputState struct { - id uint32 - name string - registryName uint32 - output *wlclient.Output - gammaControl interface{} - rampSize uint32 - failed bool - isVirtual bool - retryCount int - lastFailTime time.Time -} - -type SunTimes struct { - Sunrise time.Time - Sunset time.Time -} - -func DefaultConfig() Config { - return Config{ - Outputs: []string{}, - LowTemp: 4000, - HighTemp: 6500, - Gamma: 1.0, - Enabled: false, - } -} - -func (c *Config) Validate() error { - if c.LowTemp < 1000 || c.LowTemp > 10000 { - return errdefs.ErrInvalidTemperature - } - if c.HighTemp < 1000 || c.HighTemp > 10000 { - return errdefs.ErrInvalidTemperature - } - if c.LowTemp > c.HighTemp { - return errdefs.ErrInvalidTemperature - } - if c.Gamma <= 0 || c.Gamma > 10 { - return errdefs.ErrInvalidGamma - } - if c.Latitude != nil && (math.Abs(*c.Latitude) > 90) { - return errdefs.ErrInvalidLocation - } - if c.Longitude != nil && (math.Abs(*c.Longitude) > 180) { - return errdefs.ErrInvalidLocation - } - if (c.Latitude != nil) != (c.Longitude != nil) { - return errdefs.ErrInvalidLocation - } - if (c.ManualSunrise != nil) != (c.ManualSunset != nil) { - return errdefs.ErrInvalidManualTimes - } - return nil -} - -func (m *Manager) GetState() State { - m.stateMutex.RLock() - defer m.stateMutex.RUnlock() - if m.state == nil { - return State{} - } - stateCopy := *m.state - return stateCopy -} - -func (m *Manager) Subscribe(id string) chan State { - ch := make(chan State, 64) - m.subMutex.Lock() - m.subscribers[id] = ch - m.subMutex.Unlock() - return ch -} - -func (m *Manager) Unsubscribe(id string) { - m.subMutex.Lock() - if ch, ok := m.subscribers[id]; ok { - close(ch) - delete(m.subscribers, id) - } - m.subMutex.Unlock() -} - -func (m *Manager) notifySubscribers() { - select { - case m.dirty <- struct{}{}: - default: - } -} - -func stateChanged(old, new *State) bool { - if old == nil || new == nil { - return true - } - if old.CurrentTemp != new.CurrentTemp { - return true - } - if old.IsDay != new.IsDay { - return true - } - if !old.NextTransition.Equal(new.NextTransition) { - return true - } - if !old.SunriseTime.Equal(new.SunriseTime) { - return true - } - if !old.SunsetTime.Equal(new.SunsetTime) { - return true - } - if old.Config.Enabled != new.Config.Enabled { - return true - } - return false -} diff --git a/nix/inputs/dms-cli/internal/server/wayland/types_test.go b/nix/inputs/dms-cli/internal/server/wayland/types_test.go deleted file mode 100644 index f5bbb42..0000000 --- a/nix/inputs/dms-cli/internal/server/wayland/types_test.go +++ /dev/null @@ -1,330 +0,0 @@ -package wayland - -import ( - "testing" - "time" -) - -func TestConfigValidate(t *testing.T) { - tests := []struct { - name string - config Config - wantErr bool - }{ - { - name: "valid_default", - config: DefaultConfig(), - wantErr: false, - }, - { - name: "valid_with_location", - config: Config{ - LowTemp: 4000, - HighTemp: 6500, - Latitude: floatPtr(40.7128), - Longitude: floatPtr(-74.0060), - Gamma: 1.0, - Enabled: true, - }, - wantErr: false, - }, - { - name: "valid_manual_times", - config: Config{ - LowTemp: 4000, - HighTemp: 6500, - ManualSunrise: timePtr(time.Date(0, 1, 1, 6, 30, 0, 0, time.Local)), - ManualSunset: timePtr(time.Date(0, 1, 1, 18, 30, 0, 0, time.Local)), - Gamma: 1.0, - Enabled: true, - }, - wantErr: false, - }, - { - name: "invalid_low_temp_too_low", - config: Config{ - LowTemp: 500, - HighTemp: 6500, - Gamma: 1.0, - }, - wantErr: true, - }, - { - name: "invalid_low_temp_too_high", - config: Config{ - LowTemp: 15000, - HighTemp: 20000, - Gamma: 1.0, - }, - wantErr: true, - }, - { - name: "invalid_high_temp_too_low", - config: Config{ - LowTemp: 4000, - HighTemp: 500, - Gamma: 1.0, - }, - wantErr: true, - }, - { - name: "valid_temps_equal", - config: Config{ - LowTemp: 5000, - HighTemp: 5000, - Gamma: 1.0, - }, - wantErr: false, - }, - { - name: "invalid_temps_reversed", - config: Config{ - LowTemp: 6500, - HighTemp: 4000, - Gamma: 1.0, - }, - wantErr: true, - }, - { - name: "invalid_gamma_zero", - config: Config{ - LowTemp: 4000, - HighTemp: 6500, - Gamma: 0, - }, - wantErr: true, - }, - { - name: "invalid_gamma_negative", - config: Config{ - LowTemp: 4000, - HighTemp: 6500, - Gamma: -1.0, - }, - wantErr: true, - }, - { - name: "invalid_gamma_too_high", - config: Config{ - LowTemp: 4000, - HighTemp: 6500, - Gamma: 15.0, - }, - wantErr: true, - }, - { - name: "invalid_latitude_too_high", - config: Config{ - LowTemp: 4000, - HighTemp: 6500, - Latitude: floatPtr(100), - Longitude: floatPtr(0), - Gamma: 1.0, - }, - wantErr: true, - }, - { - name: "invalid_latitude_too_low", - config: Config{ - LowTemp: 4000, - HighTemp: 6500, - Latitude: floatPtr(-100), - Longitude: floatPtr(0), - Gamma: 1.0, - }, - wantErr: true, - }, - { - name: "invalid_longitude_too_high", - config: Config{ - LowTemp: 4000, - HighTemp: 6500, - Latitude: floatPtr(40), - Longitude: floatPtr(200), - Gamma: 1.0, - }, - wantErr: true, - }, - { - name: "invalid_longitude_too_low", - config: Config{ - LowTemp: 4000, - HighTemp: 6500, - Latitude: floatPtr(40), - Longitude: floatPtr(-200), - Gamma: 1.0, - }, - wantErr: true, - }, - { - name: "invalid_latitude_without_longitude", - config: Config{ - LowTemp: 4000, - HighTemp: 6500, - Latitude: floatPtr(40), - Gamma: 1.0, - }, - wantErr: true, - }, - { - name: "invalid_longitude_without_latitude", - config: Config{ - LowTemp: 4000, - HighTemp: 6500, - Longitude: floatPtr(-74), - Gamma: 1.0, - }, - wantErr: true, - }, - { - name: "invalid_sunrise_without_sunset", - config: Config{ - LowTemp: 4000, - HighTemp: 6500, - ManualSunrise: timePtr(time.Date(0, 1, 1, 6, 30, 0, 0, time.Local)), - Gamma: 1.0, - }, - wantErr: true, - }, - { - name: "invalid_sunset_without_sunrise", - config: Config{ - LowTemp: 4000, - HighTemp: 6500, - ManualSunset: timePtr(time.Date(0, 1, 1, 18, 30, 0, 0, time.Local)), - Gamma: 1.0, - }, - wantErr: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - err := tt.config.Validate() - if (err != nil) != tt.wantErr { - t.Errorf("Validate() error = %v, wantErr %v", err, tt.wantErr) - } - }) - } -} - -func TestDefaultConfig(t *testing.T) { - config := DefaultConfig() - - if config.LowTemp != 4000 { - t.Errorf("default low temp = %d, want 4000", config.LowTemp) - } - if config.HighTemp != 6500 { - t.Errorf("default high temp = %d, want 6500", config.HighTemp) - } - if config.Gamma != 1.0 { - t.Errorf("default gamma = %f, want 1.0", config.Gamma) - } - if config.Enabled { - t.Error("default should be disabled") - } - if config.Latitude != nil { - t.Error("default should not have latitude") - } - if config.Longitude != nil { - t.Error("default should not have longitude") - } -} - -func TestStateChanged(t *testing.T) { - baseState := &State{ - CurrentTemp: 5000, - NextTransition: time.Now(), - SunriseTime: time.Now().Add(6 * time.Hour), - SunsetTime: time.Now().Add(18 * time.Hour), - IsDay: true, - Config: DefaultConfig(), - } - - tests := []struct { - name string - old *State - new *State - wantChanged bool - }{ - { - name: "nil_old", - old: nil, - new: baseState, - wantChanged: true, - }, - { - name: "nil_new", - old: baseState, - new: nil, - wantChanged: true, - }, - { - name: "same_state", - old: baseState, - new: baseState, - wantChanged: false, - }, - { - name: "temp_changed", - old: baseState, - new: &State{ - CurrentTemp: 6000, - NextTransition: baseState.NextTransition, - SunriseTime: baseState.SunriseTime, - SunsetTime: baseState.SunsetTime, - IsDay: baseState.IsDay, - Config: baseState.Config, - }, - wantChanged: true, - }, - { - name: "is_day_changed", - old: baseState, - new: &State{ - CurrentTemp: baseState.CurrentTemp, - NextTransition: baseState.NextTransition, - SunriseTime: baseState.SunriseTime, - SunsetTime: baseState.SunsetTime, - IsDay: false, - Config: baseState.Config, - }, - wantChanged: true, - }, - { - name: "enabled_changed", - old: baseState, - new: &State{ - CurrentTemp: baseState.CurrentTemp, - NextTransition: baseState.NextTransition, - SunriseTime: baseState.SunriseTime, - SunsetTime: baseState.SunsetTime, - IsDay: baseState.IsDay, - Config: Config{ - LowTemp: 4000, - HighTemp: 6500, - Gamma: 1.0, - Enabled: true, - }, - }, - wantChanged: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - changed := stateChanged(tt.old, tt.new) - if changed != tt.wantChanged { - t.Errorf("stateChanged() = %v, want %v", changed, tt.wantChanged) - } - }) - } -} - -func floatPtr(f float64) *float64 { - return &f -} - -func timePtr(t time.Time) *time.Time { - return &t -} diff --git a/nix/inputs/dms-cli/internal/server/wlcontext/context.go b/nix/inputs/dms-cli/internal/server/wlcontext/context.go deleted file mode 100644 index ea15973..0000000 --- a/nix/inputs/dms-cli/internal/server/wlcontext/context.go +++ /dev/null @@ -1,76 +0,0 @@ -package wlcontext - -import ( - "fmt" - "sync" - - "github.com/AvengeMedia/danklinux/internal/errdefs" - "github.com/AvengeMedia/danklinux/internal/log" - wlclient "github.com/yaslama/go-wayland/wayland/client" -) - -type SharedContext struct { - display *wlclient.Display - stopChan chan struct{} - wg sync.WaitGroup - mu sync.Mutex - started bool -} - -func New() (*SharedContext, error) { - display, err := wlclient.Connect("") - if err != nil { - return nil, fmt.Errorf("%w: %v", errdefs.ErrNoWaylandDisplay, err) - } - - sc := &SharedContext{ - display: display, - stopChan: make(chan struct{}), - started: false, - } - - return sc, nil -} - -func (sc *SharedContext) Start() { - sc.mu.Lock() - defer sc.mu.Unlock() - - if sc.started { - return - } - - sc.started = true - sc.wg.Add(1) - go sc.eventDispatcher() -} - -func (sc *SharedContext) Display() *wlclient.Display { - return sc.display -} - -func (sc *SharedContext) eventDispatcher() { - defer sc.wg.Done() - ctx := sc.display.Context() - - for { - select { - case <-sc.stopChan: - return - default: - if err := ctx.Dispatch(); err != nil { - log.Errorf("Wayland connection error: %v", err) - return - } - } - } -} - -func (sc *SharedContext) Close() { - close(sc.stopChan) - sc.wg.Wait() - - if sc.display != nil { - sc.display.Context().Close() - } -} diff --git a/nix/inputs/dms-cli/internal/tui/app.go b/nix/inputs/dms-cli/internal/tui/app.go deleted file mode 100644 index a4a2d80..0000000 --- a/nix/inputs/dms-cli/internal/tui/app.go +++ /dev/null @@ -1,214 +0,0 @@ -package tui - -import ( - "github.com/AvengeMedia/danklinux/internal/deps" - "github.com/AvengeMedia/danklinux/internal/distros" - "github.com/charmbracelet/bubbles/spinner" - "github.com/charmbracelet/bubbles/textinput" - tea "github.com/charmbracelet/bubbletea" -) - -type Model struct { - version string - state ApplicationState - - osInfo *distros.OSInfo - dependencies []deps.Dependency - err error - - spinner spinner.Model - passwordInput textinput.Model - width int - height int - isLoading bool - styles Styles - - logMessages []string - logChan chan string - packageProgressChan chan packageInstallProgressMsg - packageProgress packageInstallProgressMsg - installationLogs []string - - selectedWM int - selectedTerminal int - selectedDep int - selectedConfig int - reinstallItems map[string]bool - replaceConfigs map[string]bool - sudoPassword string - existingConfigs []ExistingConfigInfo - fingerprintFailed bool -} - -func NewModel(version string) Model { - s := spinner.New() - s.Spinner = spinner.Dot - - theme := TerminalTheme() - styles := NewStyles(theme) - s.Style = styles.SpinnerStyle - - pi := textinput.New() - pi.Placeholder = "Enter sudo password" - pi.EchoMode = textinput.EchoPassword - pi.EchoCharacter = '•' - pi.Focus() - - logChan := make(chan string, 1000) - packageProgressChan := make(chan packageInstallProgressMsg, 100) - - return Model{ - version: version, - state: StateWelcome, - spinner: s, - passwordInput: pi, - isLoading: true, - styles: styles, - - logMessages: []string{}, - logChan: logChan, - packageProgressChan: packageProgressChan, - packageProgress: packageInstallProgressMsg{ - progress: 0.0, - step: "Initializing package installation", - isComplete: false, - }, - selectedWM: 0, - selectedTerminal: 0, // Default to Ghostty - selectedDep: 0, - selectedConfig: 0, - reinstallItems: make(map[string]bool), - replaceConfigs: make(map[string]bool), - installationLogs: []string{}, - } -} - -func (m Model) Init() tea.Cmd { - return tea.Batch( - m.spinner.Tick, - m.listenForLogs(), - m.detectOS(), - ) -} - -func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { - if keyMsg, ok := msg.(tea.KeyMsg); ok { - switch keyMsg.String() { - case "ctrl+c": - return m, tea.Quit - } - } - - if tickMsg, ok := msg.(spinner.TickMsg); ok { - var cmd tea.Cmd - m.spinner, cmd = m.spinner.Update(tickMsg) - return m, tea.Batch(cmd, m.listenForLogs()) - } - - if sizeMsg, ok := msg.(tea.WindowSizeMsg); ok { - m.width = sizeMsg.Width - m.height = sizeMsg.Height - } - - if logMsg, ok := msg.(logMsg); ok { - m.logMessages = append(m.logMessages, logMsg.message) - return m, m.listenForLogs() - } - - switch m.state { - case StateWelcome: - return m.updateWelcomeState(msg) - case StateSelectWindowManager: - return m.updateSelectWindowManagerState(msg) - case StateSelectTerminal: - return m.updateSelectTerminalState(msg) - case StateMissingWMInstructions: - return m.updateMissingWMInstructionsState(msg) - case StateDetectingDeps: - return m.updateDetectingDepsState(msg) - case StateDependencyReview: - return m.updateDependencyReviewState(msg) - case StateAuthMethodChoice: - return m.updateAuthMethodChoiceState(msg) - case StateFingerprintAuth: - return m.updateFingerprintAuthState(msg) - case StatePasswordPrompt: - return m.updatePasswordPromptState(msg) - case StateInstallingPackages: - return m.updateInstallingPackagesState(msg) - case StateConfigConfirmation: - return m.updateConfigConfirmationState(msg) - case StateDeployingConfigs: - return m.updateDeployingConfigsState(msg) - case StateInstallComplete: - return m.updateInstallCompleteState(msg) - case StateError: - return m.updateErrorState(msg) - default: - return m, m.listenForLogs() - } -} - -func (m Model) View() string { - switch m.state { - case StateWelcome: - return m.viewWelcome() - case StateSelectWindowManager: - return m.viewSelectWindowManager() - case StateSelectTerminal: - return m.viewSelectTerminal() - case StateMissingWMInstructions: - return m.viewMissingWMInstructions() - case StateDetectingDeps: - return m.viewDetectingDeps() - case StateDependencyReview: - return m.viewDependencyReview() - case StateAuthMethodChoice: - return m.viewAuthMethodChoice() - case StateFingerprintAuth: - return m.viewFingerprintAuth() - case StatePasswordPrompt: - return m.viewPasswordPrompt() - case StateInstallingPackages: - return m.viewInstallingPackages() - case StateConfigConfirmation: - return m.viewConfigConfirmation() - case StateDeployingConfigs: - return m.viewDeployingConfigs() - case StateInstallComplete: - return m.viewInstallComplete() - case StateError: - return m.viewError() - default: - return m.viewWelcome() - } -} - -func (m Model) listenForLogs() tea.Cmd { - return func() tea.Msg { - select { - case msg, ok := <-m.logChan: - if !ok { - return nil - } - return logMsg{message: msg} - default: - return nil - } - } -} - -func (m Model) detectOS() tea.Cmd { - return func() tea.Msg { - info, err := distros.GetOSInfo() - osInfoMsg := &distros.OSInfo{} - if info != nil { - osInfoMsg.Distribution = info.Distribution - osInfoMsg.Version = info.Version - osInfoMsg.VersionID = info.VersionID - osInfoMsg.PrettyName = info.PrettyName - osInfoMsg.Architecture = info.Architecture - } - return osInfoCompleteMsg{info: osInfoMsg, err: err} - } -} diff --git a/nix/inputs/dms-cli/internal/tui/banner.go b/nix/inputs/dms-cli/internal/tui/banner.go deleted file mode 100644 index d0f625b..0000000 --- a/nix/inputs/dms-cli/internal/tui/banner.go +++ /dev/null @@ -1,21 +0,0 @@ -package tui - -import "github.com/charmbracelet/lipgloss" - -func (m Model) renderBanner() string { - logo := ` -██████╗ █████╗ ███╗ ██╗██╗ ██╗ -██╔══██╗██╔══██╗████╗ ██║██║ ██╔╝ -██║ ██║███████║██╔██╗ ██║█████╔╝ -██║ ██║██╔══██║██║╚██╗██║██╔═██╗ -██████╔╝██║ ██║██║ ╚████║██║ ██╗ -╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝ ` - - theme := TerminalTheme() - style := lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Primary)). - Bold(true). - MarginBottom(1) - - return style.Render(logo) -} diff --git a/nix/inputs/dms-cli/internal/tui/messages.go b/nix/inputs/dms-cli/internal/tui/messages.go deleted file mode 100644 index 3df9f1f..0000000 --- a/nix/inputs/dms-cli/internal/tui/messages.go +++ /dev/null @@ -1,39 +0,0 @@ -package tui - -import ( - "github.com/AvengeMedia/danklinux/internal/deps" - "github.com/AvengeMedia/danklinux/internal/distros" -) - -type logMsg struct { - message string -} - -type osInfoCompleteMsg struct { - info *distros.OSInfo - err error -} - -type depsDetectedMsg struct { - deps []deps.Dependency - err error -} - -type packageInstallProgressMsg struct { - progress float64 - step string - isComplete bool - needsSudo bool - commandInfo string - logOutput string - error error -} - -type packageProgressCompletedMsg struct{} - -type passwordValidMsg struct { - password string - valid bool -} - -type delayCompleteMsg struct{} diff --git a/nix/inputs/dms-cli/internal/tui/states.go b/nix/inputs/dms-cli/internal/tui/states.go deleted file mode 100644 index a0bb5c2..0000000 --- a/nix/inputs/dms-cli/internal/tui/states.go +++ /dev/null @@ -1,21 +0,0 @@ -package tui - -type ApplicationState int - -const ( - StateWelcome ApplicationState = iota - StateSelectWindowManager - StateSelectTerminal - StateMissingWMInstructions - StateDetectingDeps - StateDependencyReview - StateAuthMethodChoice - StateFingerprintAuth - StatePasswordPrompt - StateInstallingPackages - StateConfigConfirmation - StateDeployingConfigs - StateInstallComplete - StateFinalComplete - StateError -) diff --git a/nix/inputs/dms-cli/internal/tui/styles.go b/nix/inputs/dms-cli/internal/tui/styles.go deleted file mode 100644 index 45a9dae..0000000 --- a/nix/inputs/dms-cli/internal/tui/styles.go +++ /dev/null @@ -1,124 +0,0 @@ -package tui - -import ( - "github.com/charmbracelet/bubbles/progress" - "github.com/charmbracelet/lipgloss" -) - -type AppTheme struct { - Primary string - Secondary string - Accent string - Text string - Subtle string - Error string - Warning string - Success string - Background string - Surface string -} - -func TerminalTheme() AppTheme { - return AppTheme{ - Primary: "6", // #625690 - purple - Secondary: "5", // #36247a - dark purple - Accent: "12", // #7060ac - light purple - Text: "7", // #2e2e2e - dark gray - Subtle: "8", // #4a4a4a - medium gray - Error: "1", // #d83636 - red - Warning: "3", // #ffff89 - yellow - Success: "2", // #53e550 - green - Background: "15", // #1a1a1a - near black - Surface: "8", // #4a4a4a - medium gray - } -} - -func NewStyles(theme AppTheme) Styles { - return Styles{ - Title: lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Primary)). - Bold(true). - MarginLeft(1). - MarginBottom(1), - - Normal: lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Text)), - - Bold: lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Text)). - Bold(true), - - Subtle: lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Subtle)), - - Error: lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Error)), - - Warning: lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Warning)), - - StatusBar: lipgloss.NewStyle(). - Foreground(lipgloss.Color("#33275e")). - Background(lipgloss.Color(theme.Primary)). - Padding(0, 1), - - Key: lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Accent)). - Bold(true), - - SpinnerStyle: lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Primary)), - - Success: lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Success)). - Bold(true), - - HighlightButton: lipgloss.NewStyle(). - Foreground(lipgloss.Color("#33275e")). - Background(lipgloss.Color(theme.Primary)). - Padding(0, 2). - Bold(true), - - SelectedOption: lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Accent)). - Bold(true), - - CodeBlock: lipgloss.NewStyle(). - Background(lipgloss.Color(theme.Surface)). - Foreground(lipgloss.Color(theme.Text)). - Padding(1, 2). - MarginLeft(2), - } -} - -type Styles struct { - Title lipgloss.Style - Normal lipgloss.Style - Bold lipgloss.Style - Subtle lipgloss.Style - Warning lipgloss.Style - Error lipgloss.Style - StatusBar lipgloss.Style - Key lipgloss.Style - SpinnerStyle lipgloss.Style - Success lipgloss.Style - HighlightButton lipgloss.Style - SelectedOption lipgloss.Style - CodeBlock lipgloss.Style -} - -func (s Styles) NewThemedProgress(width int) progress.Model { - theme := TerminalTheme() - prog := progress.New( - progress.WithGradient(theme.Secondary, theme.Primary), - ) - - prog.Width = width - prog.ShowPercentage = true - prog.PercentFormat = "%.0f%%" - prog.PercentageStyle = lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Text)). - Bold(true) - - return prog -} diff --git a/nix/inputs/dms-cli/internal/tui/views_config.go b/nix/inputs/dms-cli/internal/tui/views_config.go deleted file mode 100644 index 5905a94..0000000 --- a/nix/inputs/dms-cli/internal/tui/views_config.go +++ /dev/null @@ -1,333 +0,0 @@ -package tui - -import ( - "context" - "fmt" - "os" - "path/filepath" - "strings" - - "github.com/AvengeMedia/danklinux/internal/config" - "github.com/AvengeMedia/danklinux/internal/deps" - tea "github.com/charmbracelet/bubbletea" -) - -type configDeploymentResult struct { - results []config.DeploymentResult - error error -} - -type ExistingConfigInfo struct { - ConfigType string - Path string - Exists bool -} - -type configCheckResult struct { - configs []ExistingConfigInfo - error error -} - -func (m Model) viewDeployingConfigs() string { - var b strings.Builder - - b.WriteString(m.renderBanner()) - b.WriteString("\n") - - title := m.styles.Title.Render("Deploying Configurations") - b.WriteString(title) - b.WriteString("\n\n") - - spinner := m.spinner.View() - status := m.styles.Normal.Render("Setting up configuration files...") - b.WriteString(fmt.Sprintf("%s %s", spinner, status)) - b.WriteString("\n\n") - - // Show progress information - info := m.styles.Subtle.Render("• Creating backups of existing configurations\n• Deploying optimized configurations\n• Detecting system paths") - b.WriteString(info) - - // Show live log output if available - if len(m.installationLogs) > 0 { - b.WriteString("\n\n") - logHeader := m.styles.Subtle.Render("Configuration Log:") - b.WriteString(logHeader) - b.WriteString("\n") - - // Show last few lines of logs - maxLines := 5 - startIdx := 0 - if len(m.installationLogs) > maxLines { - startIdx = len(m.installationLogs) - maxLines - } - - for i := startIdx; i < len(m.installationLogs); i++ { - if m.installationLogs[i] != "" { - logLine := m.styles.Subtle.Render(" " + m.installationLogs[i]) - b.WriteString(logLine) - b.WriteString("\n") - } - } - } - - return b.String() -} - -func (m Model) updateDeployingConfigsState(msg tea.Msg) (tea.Model, tea.Cmd) { - if result, ok := msg.(configDeploymentResult); ok { - if result.error != nil { - m.err = result.error - m.state = StateError - m.isLoading = false - return m, nil - } - - for _, deployResult := range result.results { - if deployResult.Deployed { - logMsg := fmt.Sprintf("✓ %s configuration deployed", deployResult.ConfigType) - if deployResult.BackupPath != "" { - logMsg += fmt.Sprintf(" (backup: %s)", deployResult.BackupPath) - } - m.installationLogs = append(m.installationLogs, logMsg) - } - } - - m.state = StateInstallComplete - m.isLoading = false - return m, nil - } - - return m, m.listenForLogs() -} - -func (m Model) deployConfigurations() tea.Cmd { - return func() tea.Msg { - // Determine the selected window manager - var wm deps.WindowManager - switch m.selectedWM { - case 0: - wm = deps.WindowManagerNiri - case 1: - wm = deps.WindowManagerHyprland - default: - wm = deps.WindowManagerNiri - } - - // Determine the selected terminal - var terminal deps.Terminal - switch m.selectedTerminal { - case 0: - terminal = deps.TerminalGhostty - case 1: - terminal = deps.TerminalKitty - default: - terminal = deps.TerminalGhostty - } - - deployer := config.NewConfigDeployer(m.logChan) - - results, err := deployer.DeployConfigurationsSelectiveWithReinstalls(context.Background(), wm, terminal, m.dependencies, m.replaceConfigs, m.reinstallItems) - - return configDeploymentResult{ - results: results, - error: err, - } - } -} - -func (m Model) viewConfigConfirmation() string { - var b strings.Builder - - b.WriteString(m.renderBanner()) - b.WriteString("\n") - - title := m.styles.Title.Render("Configuration Deployment") - b.WriteString(title) - b.WriteString("\n\n") - - if len(m.existingConfigs) == 0 { - // No existing configs, proceed directly - info := m.styles.Normal.Render("No existing configurations found. Proceeding with deployment...") - b.WriteString(info) - return b.String() - } - - // Show existing configurations with toggle options - for i, configInfo := range m.existingConfigs { - if configInfo.Exists { - var status string - var replaceMarker string - - shouldReplace := m.replaceConfigs[configInfo.ConfigType] - if _, exists := m.replaceConfigs[configInfo.ConfigType]; !exists { - shouldReplace = true - m.replaceConfigs[configInfo.ConfigType] = true - } - - if shouldReplace { - replaceMarker = "🔄 " - status = m.styles.Warning.Render("Will replace") - } else { - replaceMarker = "✓ " - status = m.styles.Success.Render("Keep existing") - } - - var line string - if i == m.selectedConfig { - line = fmt.Sprintf("▶ %s%-15s %s", replaceMarker, configInfo.ConfigType, status) - line += fmt.Sprintf("\n %s", configInfo.Path) - line = m.styles.SelectedOption.Render(line) - } else { - line = fmt.Sprintf(" %s%-15s %s", replaceMarker, configInfo.ConfigType, status) - line += fmt.Sprintf("\n %s", configInfo.Path) - line = m.styles.Normal.Render(line) - } - - b.WriteString(line) - b.WriteString("\n\n") - } - } - - backup := m.styles.Success.Render("✓ Replaced configurations will be backed up with timestamp") - b.WriteString(backup) - b.WriteString("\n\n") - - help := m.styles.Subtle.Render("↑/↓: Navigate, Space: Toggle replace/keep, Enter: Continue") - b.WriteString(help) - - return b.String() -} - -func (m Model) updateConfigConfirmationState(msg tea.Msg) (tea.Model, tea.Cmd) { - if result, ok := msg.(configCheckResult); ok { - if result.error != nil { - m.err = result.error - m.state = StateError - return m, nil - } - - m.existingConfigs = result.configs - - firstExistingSet := false - for i, config := range result.configs { - if config.Exists { - m.replaceConfigs[config.ConfigType] = true - if !firstExistingSet { - m.selectedConfig = i - firstExistingSet = true - } - } - } - - hasExisting := false - for _, config := range result.configs { - if config.Exists { - hasExisting = true - break - } - } - - if !hasExisting { - // No existing configs, proceed directly to deployment - m.state = StateDeployingConfigs - return m, m.deployConfigurations() - } - - // Show confirmation view - return m, nil - } - - if keyMsg, ok := msg.(tea.KeyMsg); ok { - switch keyMsg.String() { - case "up": - if m.selectedConfig > 0 { - for i := m.selectedConfig - 1; i >= 0; i-- { - if m.existingConfigs[i].Exists { - m.selectedConfig = i - break - } - } - } - case "down": - if m.selectedConfig < len(m.existingConfigs)-1 { - for i := m.selectedConfig + 1; i < len(m.existingConfigs); i++ { - if m.existingConfigs[i].Exists { - m.selectedConfig = i - break - } - } - } - case " ": - if len(m.existingConfigs) > 0 && m.selectedConfig < len(m.existingConfigs) { - configType := m.existingConfigs[m.selectedConfig].ConfigType - if m.existingConfigs[m.selectedConfig].Exists { - m.replaceConfigs[configType] = !m.replaceConfigs[configType] - } - } - case "enter": - m.state = StateDeployingConfigs - return m, m.deployConfigurations() - } - } - - return m, nil -} - -func (m Model) checkExistingConfigurations() tea.Cmd { - return func() tea.Msg { - var configs []ExistingConfigInfo - - if m.selectedWM == 0 { - niriPath := filepath.Join(os.Getenv("HOME"), ".config", "niri", "config.kdl") - niriExists := false - if _, err := os.Stat(niriPath); err == nil { - niriExists = true - } - configs = append(configs, ExistingConfigInfo{ - ConfigType: "Niri", - Path: niriPath, - Exists: niriExists, - }) - } else { - hyprlandPath := filepath.Join(os.Getenv("HOME"), ".config", "hypr", "hyprland.conf") - hyprlandExists := false - if _, err := os.Stat(hyprlandPath); err == nil { - hyprlandExists = true - } - configs = append(configs, ExistingConfigInfo{ - ConfigType: "Hyprland", - Path: hyprlandPath, - Exists: hyprlandExists, - }) - } - - if m.selectedTerminal == 0 { - ghosttyPath := filepath.Join(os.Getenv("HOME"), ".config", "ghostty", "config") - ghosttyExists := false - if _, err := os.Stat(ghosttyPath); err == nil { - ghosttyExists = true - } - configs = append(configs, ExistingConfigInfo{ - ConfigType: "Ghostty", - Path: ghosttyPath, - Exists: ghosttyExists, - }) - } else { - kittyPath := filepath.Join(os.Getenv("HOME"), ".config", "kitty", "kitty.conf") - kittyExists := false - if _, err := os.Stat(kittyPath); err == nil { - kittyExists = true - } - configs = append(configs, ExistingConfigInfo{ - ConfigType: "Kitty", - Path: kittyPath, - Exists: kittyExists, - }) - } - - return configCheckResult{ - configs: configs, - error: nil, - } - } -} diff --git a/nix/inputs/dms-cli/internal/tui/views_dependencies.go b/nix/inputs/dms-cli/internal/tui/views_dependencies.go deleted file mode 100644 index d92192c..0000000 --- a/nix/inputs/dms-cli/internal/tui/views_dependencies.go +++ /dev/null @@ -1,237 +0,0 @@ -package tui - -import ( - "context" - "fmt" - "strings" - - "github.com/AvengeMedia/danklinux/internal/deps" - "github.com/AvengeMedia/danklinux/internal/distros" - tea "github.com/charmbracelet/bubbletea" -) - -func (m Model) viewDetectingDeps() string { - var b strings.Builder - - b.WriteString(m.renderBanner()) - b.WriteString("\n") - - title := m.styles.Title.Render("Detecting Dependencies") - b.WriteString(title) - b.WriteString("\n\n") - - spinner := m.spinner.View() - status := m.styles.Normal.Render("Scanning system for existing packages and configurations...") - b.WriteString(fmt.Sprintf("%s %s", spinner, status)) - - return b.String() -} - -func (m Model) viewDependencyReview() string { - var b strings.Builder - - b.WriteString(m.renderBanner()) - b.WriteString("\n") - - title := m.styles.Title.Render("Dependency Review") - b.WriteString(title) - b.WriteString("\n\n") - - if len(m.dependencies) > 0 { - for i, dep := range m.dependencies { - var status string - var reinstallMarker string - var variantMarker string - - isDMS := dep.Name == "dms (DankMaterialShell)" - - if dep.CanToggle && dep.Variant == deps.VariantGit { - variantMarker = "[git] " - } - - if m.reinstallItems[dep.Name] { - reinstallMarker = "🔄 " - status = m.styles.Warning.Render("Will reinstall") - } else if isDMS { - reinstallMarker = "⚡ " - switch dep.Status { - case deps.StatusInstalled: - status = m.styles.Success.Render("✓ Required (installed)") - case deps.StatusMissing: - status = m.styles.Warning.Render("○ Required (will install)") - case deps.StatusNeedsUpdate: - status = m.styles.Warning.Render("△ Required (needs update)") - case deps.StatusNeedsReinstall: - status = m.styles.Error.Render("! Required (needs reinstall)") - } - } else { - switch dep.Status { - case deps.StatusInstalled: - status = m.styles.Success.Render("✓ Already Installed") - case deps.StatusMissing: - status = m.styles.Warning.Render("○ Will be installed") - case deps.StatusNeedsUpdate: - status = m.styles.Warning.Render("△ Needs update") - case deps.StatusNeedsReinstall: - status = m.styles.Error.Render("! Needs reinstall") - } - } - - var line string - if i == m.selectedDep { - line = fmt.Sprintf("▶ %s%s%-25s %s", reinstallMarker, variantMarker, dep.Name, status) - if dep.Version != "" { - line += fmt.Sprintf(" (%s)", dep.Version) - } - line = m.styles.SelectedOption.Render(line) - } else { - line = fmt.Sprintf(" %s%s%-25s %s", reinstallMarker, variantMarker, dep.Name, status) - if dep.Version != "" { - line += fmt.Sprintf(" (%s)", dep.Version) - } - line = m.styles.Normal.Render(line) - } - - b.WriteString(line) - b.WriteString("\n") - } - } - - b.WriteString("\n") - help := m.styles.Subtle.Render("↑/↓: Navigate, Space: Toggle reinstall, G: Toggle stable/git, Enter: Continue") - b.WriteString(help) - - return b.String() -} - -func (m Model) updateDetectingDepsState(msg tea.Msg) (tea.Model, tea.Cmd) { - if depsMsg, ok := msg.(depsDetectedMsg); ok { - m.isLoading = false - if depsMsg.err != nil { - m.err = depsMsg.err - m.state = StateError - } else { - m.dependencies = depsMsg.deps - m.state = StateDependencyReview - } - return m, m.listenForLogs() - } - return m, m.listenForLogs() -} - -func (m Model) updateDependencyReviewState(msg tea.Msg) (tea.Model, tea.Cmd) { - if keyMsg, ok := msg.(tea.KeyMsg); ok { - switch keyMsg.String() { - case "up": - if m.selectedDep > 0 { - m.selectedDep-- - } - case "down": - if m.selectedDep < len(m.dependencies)-1 { - m.selectedDep++ - } - case " ": - if len(m.dependencies) > 0 { - depName := m.dependencies[m.selectedDep].Name - - if m.dependencies[m.selectedDep].Status == deps.StatusInstalled || - m.dependencies[m.selectedDep].Status == deps.StatusNeedsReinstall { - m.reinstallItems[depName] = !m.reinstallItems[depName] - } - } - case "g", "G": - if len(m.dependencies) > 0 && m.dependencies[m.selectedDep].CanToggle { - if m.dependencies[m.selectedDep].Variant == deps.VariantStable { - m.dependencies[m.selectedDep].Variant = deps.VariantGit - } else { - m.dependencies[m.selectedDep].Variant = deps.VariantStable - } - } - case "enter": - // Check if fingerprint is enabled - if checkFingerprintEnabled() { - m.state = StateAuthMethodChoice - m.selectedConfig = 0 // Default to fingerprint - return m, nil - } else { - m.state = StatePasswordPrompt - m.passwordInput.Focus() - return m, nil - } - case "esc": - m.state = StateSelectWindowManager - return m, nil - } - } - return m, m.listenForLogs() -} - -func (m Model) installPackages() tea.Cmd { - return func() tea.Msg { - if m.osInfo == nil { - return packageInstallProgressMsg{ - progress: 0.0, - step: "Error: OS info not available", - isComplete: true, - } - } - - installer, err := distros.NewPackageInstaller(m.osInfo.Distribution.ID, m.logChan) - if err != nil { - return packageInstallProgressMsg{ - progress: 0.0, - step: fmt.Sprintf("Error: %s", err.Error()), - isComplete: true, - } - } - - // Convert TUI selection to deps enum - var wm deps.WindowManager - if m.selectedWM == 0 { - wm = deps.WindowManagerNiri - } else { - wm = deps.WindowManagerHyprland - } - - installerProgressChan := make(chan distros.InstallProgressMsg, 100) - - go func() { - defer close(installerProgressChan) - err := installer.InstallPackages(context.Background(), m.dependencies, wm, m.sudoPassword, m.reinstallItems, installerProgressChan) - if err != nil { - installerProgressChan <- distros.InstallProgressMsg{ - Progress: 0.0, - Step: fmt.Sprintf("Installation error: %s", err.Error()), - IsComplete: true, - Error: err, - } - } - }() - - // Convert installer messages to TUI messages - go func() { - for msg := range installerProgressChan { - tuiMsg := packageInstallProgressMsg{ - progress: msg.Progress, - step: msg.Step, - isComplete: msg.IsComplete, - needsSudo: msg.NeedsSudo, - commandInfo: msg.CommandInfo, - logOutput: msg.LogOutput, - error: msg.Error, - } - if msg.IsComplete { - m.logChan <- fmt.Sprintf("[DEBUG] Sending completion signal: step=%s, progress=%.2f", msg.Step, msg.Progress) - } - m.packageProgressChan <- tuiMsg - } - m.logChan <- "[DEBUG] Installer channel closed" - }() - - return packageInstallProgressMsg{ - progress: 0.05, - step: "Starting installation...", - isComplete: false, - } - } -} diff --git a/nix/inputs/dms-cli/internal/tui/views_install.go b/nix/inputs/dms-cli/internal/tui/views_install.go deleted file mode 100644 index f3af334..0000000 --- a/nix/inputs/dms-cli/internal/tui/views_install.go +++ /dev/null @@ -1,265 +0,0 @@ -package tui - -import ( - "fmt" - "strings" - - tea "github.com/charmbracelet/bubbletea" -) - -// wrapText wraps text to the specified width -func wrapText(text string, width int) string { - if len(text) <= width { - return text - } - - var result strings.Builder - words := strings.Fields(text) - currentLine := "" - - for _, word := range words { - if len(currentLine) == 0 { - currentLine = word - } else if len(currentLine)+1+len(word) <= width { - currentLine += " " + word - } else { - result.WriteString(currentLine) - result.WriteString("\n") - currentLine = word - } - } - - if len(currentLine) > 0 { - result.WriteString(currentLine) - } - - return result.String() -} - -func (m Model) viewInstallingPackages() string { - var b strings.Builder - - b.WriteString(m.renderBanner()) - b.WriteString("\n") - - title := m.styles.Title.Render("Installing Packages") - b.WriteString(title) - b.WriteString("\n\n") - - if !m.packageProgress.isComplete { - spinner := m.spinner.View() - status := m.styles.Normal.Render(m.packageProgress.step) - b.WriteString(fmt.Sprintf("%s %s", spinner, status)) - b.WriteString("\n\n") - - // Show progress bar - progressBar := fmt.Sprintf("[%s%s] %.0f%%", - strings.Repeat("█", int(m.packageProgress.progress*30)), - strings.Repeat("░", 30-int(m.packageProgress.progress*30)), - m.packageProgress.progress*100) - b.WriteString(m.styles.Normal.Render(progressBar)) - b.WriteString("\n") - - // Show command info if available - if m.packageProgress.commandInfo != "" { - cmdInfo := m.styles.Subtle.Render("$ " + m.packageProgress.commandInfo) - b.WriteString(cmdInfo) - b.WriteString("\n") - } - - // Show live log output - if len(m.installationLogs) > 0 { - b.WriteString("\n") - logHeader := m.styles.Subtle.Render("Live Output:") - b.WriteString(logHeader) - b.WriteString("\n") - - // Show last few lines of accumulated logs - maxLines := 8 - startIdx := 0 - if len(m.installationLogs) > maxLines { - startIdx = len(m.installationLogs) - maxLines - } - - for i := startIdx; i < len(m.installationLogs); i++ { - if m.installationLogs[i] != "" { - logLine := m.styles.Subtle.Render(" " + m.installationLogs[i]) - b.WriteString(logLine) - b.WriteString("\n") - } - } - } - - // Show error if any - if m.packageProgress.error != nil { - b.WriteString("\n") - wrappedErrorMsg := wrapText("Error: "+m.packageProgress.error.Error(), 80) - errorMsg := m.styles.Error.Render(wrappedErrorMsg) - b.WriteString(errorMsg) - } - - // Show sudo prompt if needed - if m.packageProgress.needsSudo { - sudoWarning := m.styles.Warning.Render("⚠ Using provided sudo password") - b.WriteString(sudoWarning) - } - } else { - if m.packageProgress.error != nil { - wrappedFailedMsg := wrapText("✗ Installation failed: "+m.packageProgress.error.Error(), 80) - errorMsg := m.styles.Error.Render(wrappedFailedMsg) - b.WriteString(errorMsg) - } else { - success := m.styles.Success.Render("✓ Installation complete!") - b.WriteString(success) - } - } - - return b.String() -} - -func (m Model) viewInstallComplete() string { - var b strings.Builder - - b.WriteString(m.renderBanner()) - b.WriteString("\n") - - title := m.styles.Success.Render("Setup Complete!") - b.WriteString(title) - b.WriteString("\n\n") - - success := m.styles.Success.Render("✓ All packages installed and configurations deployed.") - b.WriteString(success) - b.WriteString("\n\n") - - // Show what was accomplished - accomplishments := []string{ - "• Window manager and dependencies installed", - "• Terminal and development tools configured", - "• Configuration files deployed with backups", - "• System optimized for DankMaterialShell", - } - - for _, item := range accomplishments { - b.WriteString(m.styles.Subtle.Render(item)) - b.WriteString("\n") - } - - b.WriteString("\n") - info := m.styles.Normal.Render("Your system is ready! Log out and log back in to start using\nyour new desktop environment.\nIf you do not have a greeter, login with \"niri-session\" or \"Hyprland\" \n\nPress Enter to exit.") - b.WriteString(info) - - return b.String() -} - -func (m Model) viewError() string { - var b strings.Builder - - b.WriteString(m.renderBanner()) - b.WriteString("\n") - - title := m.styles.Error.Render("Installation Failed") - b.WriteString(title) - b.WriteString("\n\n") - - if m.err != nil { - wrappedError := wrapText("✗ "+m.err.Error(), 80) - error := m.styles.Error.Render(wrappedError) - b.WriteString(error) - b.WriteString("\n\n") - } - - // Show package progress error if available - if m.packageProgress.error != nil { - wrappedPackageError := wrapText("Package Installation Error: "+m.packageProgress.error.Error(), 80) - packageError := m.styles.Error.Render(wrappedPackageError) - b.WriteString(packageError) - b.WriteString("\n\n") - } - - // Show persistent installation logs - if len(m.installationLogs) > 0 { - logHeader := m.styles.Warning.Render("Installation Logs (last 15 lines):") - b.WriteString(logHeader) - b.WriteString("\n") - - maxLines := 15 - startIdx := 0 - if len(m.installationLogs) > maxLines { - startIdx = len(m.installationLogs) - maxLines - } - - for i := startIdx; i < len(m.installationLogs); i++ { - if m.installationLogs[i] != "" { - logLine := m.styles.Subtle.Render(" " + m.installationLogs[i]) - b.WriteString(logLine) - b.WriteString("\n") - } - } - b.WriteString("\n") - } - - help := m.styles.Subtle.Render("Press Enter to exit") - b.WriteString(help) - - return b.String() -} - -func (m Model) updateInstallingPackagesState(msg tea.Msg) (tea.Model, tea.Cmd) { - if progressMsg, ok := msg.(packageInstallProgressMsg); ok { - m.packageProgress = progressMsg - - // Accumulate log output - if progressMsg.logOutput != "" { - m.installationLogs = append(m.installationLogs, progressMsg.logOutput) - // Keep only last 50 lines to preserve more context for debugging - if len(m.installationLogs) > 50 { - m.installationLogs = m.installationLogs[len(m.installationLogs)-50:] - } - } - - if progressMsg.isComplete { - if progressMsg.error != nil { - m.state = StateError - m.isLoading = false - } else { - m.installationLogs = []string{} - m.state = StateConfigConfirmation - m.isLoading = true - return m, tea.Batch(m.spinner.Tick, m.checkExistingConfigurations()) - } - } - return m, m.listenForPackageProgress() - } - return m, m.listenForLogs() -} - -func (m Model) updateInstallCompleteState(msg tea.Msg) (tea.Model, tea.Cmd) { - if keyMsg, ok := msg.(tea.KeyMsg); ok { - switch keyMsg.String() { - case "enter": - return m, tea.Quit - } - } - return m, m.listenForLogs() -} - -func (m Model) updateErrorState(msg tea.Msg) (tea.Model, tea.Cmd) { - if keyMsg, ok := msg.(tea.KeyMsg); ok { - switch keyMsg.String() { - case "enter": - return m, tea.Quit - } - } - return m, m.listenForLogs() -} - -func (m Model) listenForPackageProgress() tea.Cmd { - return func() tea.Msg { - msg, ok := <-m.packageProgressChan - if !ok { - return packageProgressCompletedMsg{} - } - // Always return the message, completion will be handled in updateInstallingPackagesState - return msg - } -} diff --git a/nix/inputs/dms-cli/internal/tui/views_nixos_wm.go b/nix/inputs/dms-cli/internal/tui/views_nixos_wm.go deleted file mode 100644 index 16ecebd..0000000 --- a/nix/inputs/dms-cli/internal/tui/views_nixos_wm.go +++ /dev/null @@ -1,85 +0,0 @@ -package tui - -import ( - "strings" - - tea "github.com/charmbracelet/bubbletea" -) - -func (m Model) viewMissingWMInstructions() string { - var b strings.Builder - - b.WriteString(m.renderBanner()) - b.WriteString("\n\n") - - // Determine which WM is missing - wmName := "Niri" - installCmd := `environment.systemPackages = with pkgs; [ - niri -];` - alternateCmd := `# Or enable the module if available: -# programs.niri.enable = true;` - - if m.selectedWM == 1 { - wmName = "Hyprland" - installCmd = `programs.hyprland.enable = true;` - alternateCmd = `# Or add to systemPackages: -# environment.systemPackages = with pkgs; [ -# hyprland -# ];` - } - - // Title - title := m.styles.Title.Render("⚠️ " + wmName + " Not Installed") - b.WriteString(title) - b.WriteString("\n\n") - - // Explanation - explanation := m.styles.Normal.Render(wmName + " needs to be installed system-wide on NixOS.") - b.WriteString(explanation) - b.WriteString("\n\n") - - // Instructions - instructions := m.styles.Subtle.Render("To install " + wmName + ", add this to your /etc/nixos/configuration.nix:") - b.WriteString(instructions) - b.WriteString("\n\n") - - // Command box - cmdBox := m.styles.CodeBlock.Render(installCmd) - b.WriteString(cmdBox) - b.WriteString("\n\n") - - // Alternate command - altBox := m.styles.Subtle.Render(alternateCmd) - b.WriteString(altBox) - b.WriteString("\n\n") - - // Rebuild instruction - rebuildInstruction := m.styles.Normal.Render("Then rebuild your system:") - b.WriteString(rebuildInstruction) - b.WriteString("\n") - - rebuildCmd := m.styles.CodeBlock.Render("sudo nixos-rebuild switch") - b.WriteString(rebuildCmd) - b.WriteString("\n\n") - - // Navigation help - help := m.styles.Subtle.Render("Press Esc to go back and select a different window manager, or Ctrl+C to exit") - b.WriteString(help) - - return b.String() -} - -func (m Model) updateMissingWMInstructionsState(msg tea.Msg) (tea.Model, tea.Cmd) { - if keyMsg, ok := msg.(tea.KeyMsg); ok { - switch keyMsg.String() { - case "esc": - // Go back to window manager selection - m.state = StateSelectWindowManager - return m, m.listenForLogs() - case "ctrl+c": - return m, tea.Quit - } - } - return m, m.listenForLogs() -} diff --git a/nix/inputs/dms-cli/internal/tui/views_password.go b/nix/inputs/dms-cli/internal/tui/views_password.go deleted file mode 100644 index 5f5c817..0000000 --- a/nix/inputs/dms-cli/internal/tui/views_password.go +++ /dev/null @@ -1,347 +0,0 @@ -package tui - -import ( - "context" - "fmt" - "os" - "os/exec" - "path/filepath" - "strings" - "time" - - tea "github.com/charmbracelet/bubbletea" -) - -func (m Model) viewAuthMethodChoice() string { - var b strings.Builder - - b.WriteString(m.renderBanner()) - b.WriteString("\n") - - title := m.styles.Title.Render("Authentication Method") - b.WriteString(title) - b.WriteString("\n\n") - - message := "Fingerprint authentication is available.\nHow would you like to authenticate?" - b.WriteString(m.styles.Normal.Render(message)) - b.WriteString("\n\n") - - // Option 0: Fingerprint - if m.selectedConfig == 0 { - option := m.styles.SelectedOption.Render("▶ Use Fingerprint") - b.WriteString(option) - } else { - option := m.styles.Normal.Render(" Use Fingerprint") - b.WriteString(option) - } - b.WriteString("\n") - - // Option 1: Password - if m.selectedConfig == 1 { - option := m.styles.SelectedOption.Render("▶ Use Password") - b.WriteString(option) - } else { - option := m.styles.Normal.Render(" Use Password") - b.WriteString(option) - } - b.WriteString("\n\n") - - help := m.styles.Subtle.Render("↑/↓: Navigate, Enter: Select, Esc: Back") - b.WriteString(help) - - return b.String() -} - -func (m Model) viewFingerprintAuth() string { - var b strings.Builder - - b.WriteString(m.renderBanner()) - b.WriteString("\n") - - title := m.styles.Title.Render("Fingerprint Authentication") - b.WriteString(title) - b.WriteString("\n\n") - - if m.fingerprintFailed { - errorMsg := m.styles.Error.Render("✗ Fingerprint authentication failed") - b.WriteString(errorMsg) - b.WriteString("\n") - retryMsg := m.styles.Subtle.Render("Returning to authentication menu...") - b.WriteString(retryMsg) - } else { - message := "Please place your finger on the fingerprint reader." - b.WriteString(m.styles.Normal.Render(message)) - b.WriteString("\n\n") - - spinner := m.spinner.View() - status := m.styles.Normal.Render("Waiting for fingerprint...") - b.WriteString(fmt.Sprintf("%s %s", spinner, status)) - } - - return b.String() -} - -func (m Model) viewPasswordPrompt() string { - var b strings.Builder - - b.WriteString(m.renderBanner()) - b.WriteString("\n") - - title := m.styles.Title.Render("Password Authentication") - b.WriteString(title) - b.WriteString("\n\n") - - message := "Installation requires sudo privileges.\nPlease enter your password to continue:" - b.WriteString(m.styles.Normal.Render(message)) - b.WriteString("\n\n") - - // Password input - b.WriteString(m.passwordInput.View()) - b.WriteString("\n") - - // Show validation status - if m.packageProgress.step == "Validating sudo password..." { - spinner := m.spinner.View() - status := m.styles.Normal.Render(m.packageProgress.step) - b.WriteString(spinner + " " + status) - b.WriteString("\n") - } else if m.packageProgress.error != nil { - errorMsg := m.styles.Error.Render("✗ " + m.packageProgress.error.Error() + ". Please try again.") - b.WriteString(errorMsg) - b.WriteString("\n") - } else if m.packageProgress.step == "Password validation failed" { - errorMsg := m.styles.Error.Render("✗ Incorrect password. Please try again.") - b.WriteString(errorMsg) - b.WriteString("\n") - } - - b.WriteString("\n") - help := m.styles.Subtle.Render("Enter: Continue, Esc: Back, Ctrl+C: Cancel") - b.WriteString(help) - - return b.String() -} - -func (m Model) updateAuthMethodChoiceState(msg tea.Msg) (tea.Model, tea.Cmd) { - m.fingerprintFailed = false - - if keyMsg, ok := msg.(tea.KeyMsg); ok { - switch keyMsg.String() { - case "up": - if m.selectedConfig > 0 { - m.selectedConfig-- - } - case "down": - if m.selectedConfig < 1 { - m.selectedConfig++ - } - case "enter": - if m.selectedConfig == 0 { - m.state = StateFingerprintAuth - m.isLoading = true - return m, tea.Batch(m.spinner.Tick, m.tryFingerprint()) - } else { - m.state = StatePasswordPrompt - m.passwordInput.Focus() - return m, nil - } - case "esc": - m.state = StateDependencyReview - return m, nil - } - } - return m, nil -} - -func (m Model) updateFingerprintAuthState(msg tea.Msg) (tea.Model, tea.Cmd) { - if validMsg, ok := msg.(passwordValidMsg); ok { - if validMsg.valid { - m.sudoPassword = "" - m.packageProgress = packageInstallProgressMsg{} - m.state = StateInstallingPackages - m.isLoading = true - return m, tea.Batch(m.spinner.Tick, m.installPackages()) - } else { - m.fingerprintFailed = true - return m, m.delayThenReturn() - } - } - - if _, ok := msg.(delayCompleteMsg); ok { - m.fingerprintFailed = false - m.selectedConfig = 0 - m.state = StateAuthMethodChoice - return m, nil - } - - return m, m.listenForLogs() -} - -func (m Model) updatePasswordPromptState(msg tea.Msg) (tea.Model, tea.Cmd) { - var cmd tea.Cmd - - if validMsg, ok := msg.(passwordValidMsg); ok { - if validMsg.valid { - // Password is valid, proceed with installation - m.sudoPassword = validMsg.password - m.passwordInput.SetValue("") // Clear password input - // Clear any error state - m.packageProgress = packageInstallProgressMsg{} - m.state = StateInstallingPackages - m.isLoading = true - return m, tea.Batch(m.spinner.Tick, m.installPackages()) - } else { - // Password is invalid, show error and stay on password prompt - m.packageProgress = packageInstallProgressMsg{ - progress: 0.0, - step: "Password validation failed", - error: fmt.Errorf("incorrect password"), - logOutput: "Authentication failed", - } - m.passwordInput.SetValue("") - m.passwordInput.Focus() - return m, nil - } - } - - if keyMsg, ok := msg.(tea.KeyMsg); ok { - switch keyMsg.String() { - case "enter": - // Don't allow multiple validation attempts while one is in progress - if m.packageProgress.step == "Validating sudo password..." { - return m, nil - } - - // Validate password first - password := m.passwordInput.Value() - if password == "" { - return m, nil // Don't proceed with empty password - } - - // Clear any previous error and show validation in progress - m.packageProgress = packageInstallProgressMsg{ - progress: 0.01, - step: "Validating sudo password...", - isComplete: false, - logOutput: "Testing password with sudo -v", - } - return m, m.validatePassword(password) - case "esc": - // Go back to dependency review - m.passwordInput.SetValue("") - m.packageProgress = packageInstallProgressMsg{} // Clear any validation state - m.state = StateDependencyReview - return m, nil - } - } - - m.passwordInput, cmd = m.passwordInput.Update(msg) - return m, cmd -} - -func checkFingerprintEnabled() bool { - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) - defer cancel() - - // Check if pam_fprintd.so is in PAM config - cmd := exec.CommandContext(ctx, "grep", "-q", "pam_fprintd.so", "/etc/pam.d/system-auth") - if err := cmd.Run(); err != nil { - return false - } - - // Check if fprintd-list exists and user has enrolled fingerprints - user := os.Getenv("USER") - if user == "" { - return false - } - - listCmd := exec.CommandContext(ctx, "fprintd-list", user) - output, err := listCmd.CombinedOutput() - if err != nil { - return false - } - - // If output contains "finger:" or similar, fingerprints are enrolled - return strings.Contains(string(output), "finger") -} - -func checkSudoCached() bool { - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) - defer cancel() - - cmd := exec.CommandContext(ctx, "sudo", "-n", "true") - err := cmd.Run() - return err == nil -} - -func (m Model) delayThenReturn() tea.Cmd { - return func() tea.Msg { - time.Sleep(2 * time.Second) - return delayCompleteMsg{} - } -} - -func (m Model) tryFingerprint() tea.Cmd { - return func() tea.Msg { - clearCmd := exec.Command("sudo", "-k") - clearCmd.Run() - - tmpDir := os.TempDir() - askpassScript := filepath.Join(tmpDir, fmt.Sprintf("danklinux-fp-%d.sh", time.Now().UnixNano())) - - scriptContent := "#!/bin/sh\nexit 1\n" - if err := os.WriteFile(askpassScript, []byte(scriptContent), 0700); err != nil { - return passwordValidMsg{password: "", valid: false} - } - defer os.Remove(askpassScript) - - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - defer cancel() - - cmd := exec.CommandContext(ctx, "sudo", "-A", "-v") - cmd.Env = append(os.Environ(), fmt.Sprintf("SUDO_ASKPASS=%s", askpassScript)) - - err := cmd.Run() - - if err != nil { - return passwordValidMsg{password: "", valid: false} - } - - return passwordValidMsg{password: "", valid: true} - } -} - -func (m Model) validatePassword(password string) tea.Cmd { - return func() tea.Msg { - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - defer cancel() - - cmd := exec.CommandContext(ctx, "sudo", "-S", "-v") - - stdin, err := cmd.StdinPipe() - if err != nil { - return passwordValidMsg{password: "", valid: false} - } - - if err := cmd.Start(); err != nil { - return passwordValidMsg{password: "", valid: false} - } - - _, err = fmt.Fprintf(stdin, "%s\n", password) - stdin.Close() - if err != nil { - return passwordValidMsg{password: "", valid: false} - } - - err = cmd.Wait() - - if err != nil { - if ctx.Err() == context.DeadlineExceeded { - return passwordValidMsg{password: "", valid: false} - } - return passwordValidMsg{password: "", valid: false} - } - - return passwordValidMsg{password: password, valid: true} - } -} diff --git a/nix/inputs/dms-cli/internal/tui/views_selection.go b/nix/inputs/dms-cli/internal/tui/views_selection.go deleted file mode 100644 index 1f75df0..0000000 --- a/nix/inputs/dms-cli/internal/tui/views_selection.go +++ /dev/null @@ -1,216 +0,0 @@ -package tui - -import ( - "context" - "fmt" - "os/exec" - "strings" - - "github.com/AvengeMedia/danklinux/internal/deps" - "github.com/AvengeMedia/danklinux/internal/distros" - tea "github.com/charmbracelet/bubbletea" -) - -func (m Model) viewSelectWindowManager() string { - var b strings.Builder - - b.WriteString(m.renderBanner()) - b.WriteString("\n") - - title := m.styles.Title.Render("Choose Window Manager") - b.WriteString(title) - b.WriteString("\n\n") - - options := []struct { - name string - description string - }{ - {"niri", "Scrollable-tiling Wayland compositor."}, - } - - if m.osInfo == nil || m.osInfo.Distribution.ID != "debian" { - options = append(options, struct { - name string - description string - }{"Hyprland", "Dynamic tiling Wayland compositor."}) - } - - for i, option := range options { - if i == m.selectedWM { - selected := m.styles.SelectedOption.Render("▶ " + option.name) - b.WriteString(selected) - b.WriteString("\n") - desc := m.styles.Subtle.Render(" " + option.description) - b.WriteString(desc) - } else { - normal := m.styles.Normal.Render(" " + option.name) - b.WriteString(normal) - b.WriteString("\n") - desc := m.styles.Subtle.Render(" " + option.description) - b.WriteString(desc) - } - b.WriteString("\n") - if i < len(options)-1 { - b.WriteString("\n") - } - } - - b.WriteString("\n") - help := m.styles.Subtle.Render("Use ↑/↓ to navigate, Enter to select, Esc to go back") - b.WriteString(help) - - return b.String() -} - -func (m Model) viewSelectTerminal() string { - var b strings.Builder - - b.WriteString(m.renderBanner()) - b.WriteString("\n") - - title := m.styles.Title.Render("Choose Terminal Emulator") - b.WriteString(title) - b.WriteString("\n\n") - - options := []struct { - name string - description string - }{ - {"ghostty", "A fast, native terminal emulator built in Zig."}, - {"kitty", "A feature-rich, customizable terminal emulator."}, - {"alacritty", "A simple terminal emulator."}, - } - - for i, option := range options { - if i == m.selectedTerminal { - selected := m.styles.SelectedOption.Render("▶ " + option.name) - b.WriteString(selected) - b.WriteString("\n") - desc := m.styles.Subtle.Render(" " + option.description) - b.WriteString(desc) - } else { - normal := m.styles.Normal.Render(" " + option.name) - b.WriteString(normal) - b.WriteString("\n") - desc := m.styles.Subtle.Render(" " + option.description) - b.WriteString(desc) - } - b.WriteString("\n") - if i < len(options)-1 { - b.WriteString("\n") - } - } - - b.WriteString("\n") - help := m.styles.Subtle.Render("Use ↑/↓ to navigate, Enter to select, Esc to go back") - b.WriteString(help) - - return b.String() -} - -func (m Model) updateSelectTerminalState(msg tea.Msg) (tea.Model, tea.Cmd) { - if keyMsg, ok := msg.(tea.KeyMsg); ok { - switch keyMsg.String() { - case "up": - if m.selectedTerminal > 0 { - m.selectedTerminal-- - } - case "down": - if m.selectedTerminal < 2 { - m.selectedTerminal++ - } - case "enter": - // On NixOS, check if the selected WM is actually installed - if m.osInfo != nil && m.osInfo.Distribution.ID == "nixos" { - var wmInstalled bool - if m.selectedWM == 0 { - wmInstalled = m.commandExists("niri") - } else { - wmInstalled = m.commandExists("hyprland") || m.commandExists("Hyprland") - } - - if !wmInstalled { - m.state = StateMissingWMInstructions - return m, m.listenForLogs() - } - } - - m.state = StateDetectingDeps - m.isLoading = true - return m, tea.Batch(m.spinner.Tick, m.detectDependencies()) - case "esc": - // Go back to window manager selection - m.state = StateSelectWindowManager - return m, m.listenForLogs() - } - } - return m, m.listenForLogs() -} - -func (m Model) updateSelectWindowManagerState(msg tea.Msg) (tea.Model, tea.Cmd) { - if keyMsg, ok := msg.(tea.KeyMsg); ok { - maxWMIndex := 1 - if m.osInfo != nil && m.osInfo.Distribution.ID == "debian" { - maxWMIndex = 0 - } - - switch keyMsg.String() { - case "up": - if m.selectedWM > 0 { - m.selectedWM-- - } - case "down": - if m.selectedWM < maxWMIndex { - m.selectedWM++ - } - case "enter": - m.state = StateSelectTerminal - return m, m.listenForLogs() - case "esc": - // Go back to welcome screen - m.state = StateWelcome - return m, m.listenForLogs() - } - } - return m, m.listenForLogs() -} - -func (m Model) commandExists(cmd string) bool { - _, err := exec.LookPath(cmd) - return err == nil -} - -func (m Model) detectDependencies() tea.Cmd { - return func() tea.Msg { - if m.osInfo == nil { - return depsDetectedMsg{deps: nil, err: fmt.Errorf("OS info not available")} - } - - detector, err := distros.NewDependencyDetector(m.osInfo.Distribution.ID, m.logChan) - if err != nil { - return depsDetectedMsg{deps: nil, err: err} - } - - // Convert TUI selection to deps enum - var wm deps.WindowManager - if m.selectedWM == 0 { - wm = deps.WindowManagerNiri // First option is Niri - } else { - wm = deps.WindowManagerHyprland // Second option is Hyprland - } - - // Convert TUI terminal selection to deps enum - var terminal deps.Terminal - switch m.selectedTerminal { - case 0: - terminal = deps.TerminalGhostty - case 1: - terminal = deps.TerminalKitty - default: - terminal = deps.TerminalAlacritty - } - - dependencies, err := detector.DetectDependenciesWithTerminal(context.Background(), wm, terminal) - return depsDetectedMsg{deps: dependencies, err: err} - } -} diff --git a/nix/inputs/dms-cli/internal/tui/views_welcome.go b/nix/inputs/dms-cli/internal/tui/views_welcome.go deleted file mode 100644 index 549ea97..0000000 --- a/nix/inputs/dms-cli/internal/tui/views_welcome.go +++ /dev/null @@ -1,216 +0,0 @@ -package tui - -import ( - "fmt" - "strings" - - "github.com/AvengeMedia/danklinux/internal/distros" - tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/lipgloss" -) - -func (m Model) viewWelcome() string { - var b strings.Builder - - b.WriteString(m.renderBanner()) - b.WriteString("\n") - - theme := TerminalTheme() - - decorator := lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Accent)). - Render("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") - - titleBox := lipgloss.NewStyle(). - Border(lipgloss.RoundedBorder()). - BorderForeground(lipgloss.Color(theme.Primary)). - Padding(0, 2). - MarginBottom(1) - - titleText := lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Primary)). - Bold(true). - Render("dankinstall") - - versionTag := lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Accent)). - Italic(true). - Render(" // Dank Linux Installer") - - subtitle := lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Subtle)). - Italic(true). - Render("Quickstart for a Dank™ Desktop") - - b.WriteString(decorator) - b.WriteString("\n") - b.WriteString(titleBox.Render(titleText + versionTag)) - b.WriteString("\n") - b.WriteString(subtitle) - b.WriteString("\n\n") - - if m.osInfo != nil { - if distros.IsUnsupportedDistro(m.osInfo.Distribution.ID, m.osInfo.VersionID) { - errorBox := lipgloss.NewStyle(). - Border(lipgloss.RoundedBorder()). - BorderForeground(lipgloss.Color("#FF6B6B")). - Padding(1, 2). - MarginBottom(1) - - errorTitle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#FF6B6B")). - Bold(true). - Render("⚠ UNSUPPORTED DISTRIBUTION") - - var errorMsg string - switch m.osInfo.Distribution.ID { - case "ubuntu": - errorMsg = fmt.Sprintf("Ubuntu %s is not supported.\n\nOnly Ubuntu 25.04+ is supported.\n\nPlease upgrade to Ubuntu 25.04 or later.", m.osInfo.VersionID) - case "debian": - errorMsg = fmt.Sprintf("Debian %s is not supported.\n\nOnly Debian 13+ (Trixie) is supported.\n\nPlease upgrade to Debian 13 or later.", m.osInfo.VersionID) - case "nixos": - errorMsg = "NixOS is currently not supported, but there is a DankMaterialShell flake available." - default: - errorMsg = fmt.Sprintf("%s is not supported.\nFeel free to request on https://github.com/AvengeMedia/danklinux", m.osInfo.PrettyName) - } - - errorMsgStyled := lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Text)). - Render(errorMsg) - - b.WriteString(errorBox.Render(errorTitle + "\n\n" + errorMsgStyled)) - b.WriteString("\n\n") - } else { - // System info box - sysBox := lipgloss.NewStyle(). - Border(lipgloss.NormalBorder()). - BorderForeground(lipgloss.Color(theme.Subtle)). - Padding(0, 1). - MarginBottom(1) - - // Style the distro name with its color - distroStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color(m.osInfo.Distribution.HexColorCode)). - Bold(true) - distroName := distroStyle.Render(m.osInfo.PrettyName) - - archStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Accent)) - - sysInfo := fmt.Sprintf("System: %s / %s", distroName, archStyle.Render(m.osInfo.Architecture)) - b.WriteString(sysBox.Render(sysInfo)) - b.WriteString("\n") - - // Feature list with better styling - featTitle := lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Primary)). - Bold(true). - Underline(true). - Render("WHAT YOU GET") - b.WriteString(featTitle + "\n\n") - - features := []string{ - "[shell] dms (DankMaterialShell)", - "[wm] niri or Hyprland", - "[term] Ghostty, kitty, or Alacritty", - "[style] All the themes, automatically.", - "[config] DANK defaults - keybindings, rules, animations, etc.", - } - - for i, feat := range features { - prefix := feat[:9] - content := feat[10:] - - prefixStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Accent)). - Bold(true) - - contentStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Text)) - - if i == len(features)-1 { - contentStyle = contentStyle.Bold(true) - } - - b.WriteString(fmt.Sprintf(" %s %s\n", - prefixStyle.Render(prefix), - contentStyle.Render(content))) - } - - b.WriteString("\n") - - noteStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Subtle)). - Italic(true) - note := noteStyle.Render("* Existing configs can be replaced (and backed up) or preserved") - b.WriteString(note) - b.WriteString("\n") - - if m.osInfo.Distribution.ID == "gentoo" { - gentooNote := noteStyle.Render("* Will set per-package USE flags and unmask testing packages as needed") - b.WriteString(gentooNote) - b.WriteString("\n") - } - - b.WriteString("\n") - } - - } else if m.isLoading { - spinner := m.spinner.View() - loading := m.styles.Normal.Render("Detecting system...") - b.WriteString(fmt.Sprintf("%s %s\n\n", spinner, loading)) - } - - // Footer with better visual separation - footerDivider := lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Subtle)). - Render("───────────────────────────────────────────────────────────") - b.WriteString(footerDivider + "\n") - - if m.osInfo != nil { - ctrlKey := lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Primary)). - Bold(true). - Render("Ctrl+C") - - if distros.IsUnsupportedDistro(m.osInfo.Distribution.ID, m.osInfo.VersionID) { - b.WriteString(m.styles.Subtle.Render("Press ") + ctrlKey + m.styles.Subtle.Render(" to quit")) - } else { - enterKey := lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Primary)). - Bold(true). - Render("Enter") - - b.WriteString(m.styles.Subtle.Render("Press ") + enterKey + m.styles.Subtle.Render(" to choose window manager, ") + ctrlKey + m.styles.Subtle.Render(" to quit")) - } - } else { - help := m.styles.Subtle.Render("Press Enter to continue, Ctrl+C to quit") - b.WriteString(help) - } - - return b.String() -} - -func (m Model) updateWelcomeState(msg tea.Msg) (tea.Model, tea.Cmd) { - if completeMsg, ok := msg.(osInfoCompleteMsg); ok { - m.isLoading = false - if completeMsg.err != nil { - m.err = completeMsg.err - m.state = StateError - } else { - m.osInfo = completeMsg.info - } - return m, m.listenForLogs() - } - - if keyMsg, ok := msg.(tea.KeyMsg); ok { - switch keyMsg.String() { - case "enter": - if m.osInfo != nil && !distros.IsUnsupportedDistro(m.osInfo.Distribution.ID, m.osInfo.VersionID) { - m.state = StateSelectWindowManager - return m, m.listenForLogs() - } - } - } - return m, m.listenForLogs() -} diff --git a/nix/inputs/dms-cli/internal/utils/math.go b/nix/inputs/dms-cli/internal/utils/math.go deleted file mode 100644 index 408af9f..0000000 --- a/nix/inputs/dms-cli/internal/utils/math.go +++ /dev/null @@ -1,13 +0,0 @@ -package utils - -import "golang.org/x/exp/constraints" - -func Clamp[T constraints.Ordered](val, min, max T) T { - if val < min { - return min - } - if val > max { - return max - } - return val -} diff --git a/nix/inputs/dms-cli/internal/version/version.go b/nix/inputs/dms-cli/internal/version/version.go deleted file mode 100644 index 831f1ff..0000000 --- a/nix/inputs/dms-cli/internal/version/version.go +++ /dev/null @@ -1,221 +0,0 @@ -package version - -import ( - "encoding/json" - "fmt" - "os" - "os/exec" - "path/filepath" - "strings" -) - -type VersionInfo struct { - Current string - Latest string - IsGit bool - IsBranch bool - IsTag bool - HasUpdate bool -} - -func GetCurrentDMSVersion() (string, error) { - homeDir, err := os.UserHomeDir() - if err != nil { - return "", fmt.Errorf("failed to get home directory: %w", err) - } - - dmsPath := filepath.Join(homeDir, ".config", "quickshell", "dms") - if _, err := os.Stat(dmsPath); os.IsNotExist(err) { - return "", fmt.Errorf("DMS not installed") - } - - originalDir, err := os.Getwd() - if err != nil { - return "", err - } - defer os.Chdir(originalDir) - - if err := os.Chdir(dmsPath); err != nil { - return "", fmt.Errorf("failed to change to DMS directory: %w", err) - } - - if _, err := os.Stat(filepath.Join(dmsPath, ".git")); err == nil { - tagCmd := exec.Command("git", "describe", "--exact-match", "--tags", "HEAD") - if tagOutput, err := tagCmd.Output(); err == nil { - return strings.TrimSpace(string(tagOutput)), nil - } - - branchCmd := exec.Command("git", "rev-parse", "--abbrev-ref", "HEAD") - if branchOutput, err := branchCmd.Output(); err == nil { - branch := strings.TrimSpace(string(branchOutput)) - revCmd := exec.Command("git", "rev-parse", "--short", "HEAD") - if revOutput, err := revCmd.Output(); err == nil { - rev := strings.TrimSpace(string(revOutput)) - return fmt.Sprintf("%s@%s", branch, rev), nil - } - return branch, nil - } - } - - cmd := exec.Command("dms", "--version") - if output, err := cmd.Output(); err == nil { - return strings.TrimSpace(string(output)), nil - } - - return "unknown", nil -} - -func GetLatestDMSVersion() (string, error) { - homeDir, err := os.UserHomeDir() - if err != nil { - return "", fmt.Errorf("failed to get home directory: %w", err) - } - - dmsPath := filepath.Join(homeDir, ".config", "quickshell", "dms") - - originalDir, err := os.Getwd() - if err != nil { - return "", err - } - defer os.Chdir(originalDir) - - if _, err := os.Stat(filepath.Join(dmsPath, ".git")); err == nil { - if err := os.Chdir(dmsPath); err != nil { - return "", fmt.Errorf("failed to change to DMS directory: %w", err) - } - - currentRefCmd := exec.Command("git", "symbolic-ref", "-q", "HEAD") - currentRefOutput, _ := currentRefCmd.Output() - onBranch := len(currentRefOutput) > 0 - - if !onBranch { - tagCmd := exec.Command("git", "describe", "--exact-match", "--tags", "HEAD") - if _, err := tagCmd.Output(); err == nil { - // Add timeout to git fetch to prevent hanging - fetchCmd := exec.Command("timeout", "5s", "git", "fetch", "origin", "--tags", "--quiet") - fetchCmd.Run() - - latestTagCmd := exec.Command("git", "tag", "-l", "v0.1.*", "--sort=-version:refname") - latestTagOutput, err := latestTagCmd.Output() - if err != nil { - return "", fmt.Errorf("failed to get latest tag: %w", err) - } - - tags := strings.Split(strings.TrimSpace(string(latestTagOutput)), "\n") - if len(tags) == 0 || tags[0] == "" { - return "", fmt.Errorf("no v0.1.* tags found") - } - return tags[0], nil - } - } else { - branchCmd := exec.Command("git", "rev-parse", "--abbrev-ref", "HEAD") - branchOutput, err := branchCmd.Output() - if err != nil { - return "", fmt.Errorf("failed to get current branch: %w", err) - } - currentBranch := strings.TrimSpace(string(branchOutput)) - - // Add timeout to git fetch to prevent hanging - fetchCmd := exec.Command("timeout", "5s", "git", "fetch", "origin", currentBranch, "--quiet") - fetchCmd.Run() - - remoteRevCmd := exec.Command("git", "rev-parse", "--short", fmt.Sprintf("origin/%s", currentBranch)) - remoteRevOutput, err := remoteRevCmd.Output() - if err != nil { - return "", fmt.Errorf("failed to get remote revision: %w", err) - } - remoteRev := strings.TrimSpace(string(remoteRevOutput)) - return fmt.Sprintf("%s@%s", currentBranch, remoteRev), nil - } - } - - // Add timeout to prevent hanging when GitHub is down - cmd := exec.Command("curl", "-s", "--max-time", "5", "https://api.github.com/repos/AvengeMedia/danklinux/releases/latest") - output, err := cmd.Output() - if err != nil { - return "", fmt.Errorf("failed to fetch latest release: %w", err) - } - - var result struct { - TagName string `json:"tag_name"` - } - if err := json.Unmarshal(output, &result); err != nil { - for _, line := range strings.Split(string(output), "\n") { - if strings.Contains(line, "\"tag_name\"") { - parts := strings.Split(line, "\"") - if len(parts) >= 4 { - return parts[3], nil - } - } - } - return "", fmt.Errorf("failed to parse latest version: %w", err) - } - - return result.TagName, nil -} - -func GetDMSVersionInfo() (*VersionInfo, error) { - current, err := GetCurrentDMSVersion() - if err != nil { - return nil, err - } - - latest, err := GetLatestDMSVersion() - if err != nil { - return nil, fmt.Errorf("failed to get latest version: %w", err) - } - - info := &VersionInfo{ - Current: current, - Latest: latest, - IsGit: strings.Contains(current, "@"), - IsBranch: strings.Contains(current, "@"), - IsTag: !strings.Contains(current, "@") && strings.HasPrefix(current, "v"), - } - - if info.IsBranch { - parts := strings.Split(current, "@") - latestParts := strings.Split(latest, "@") - if len(parts) == 2 && len(latestParts) == 2 { - info.HasUpdate = parts[1] != latestParts[1] - } - } else if info.IsTag { - info.HasUpdate = current != latest - } else { - info.HasUpdate = false - } - - return info, nil -} - -func CompareVersions(v1, v2 string) int { - v1 = strings.TrimPrefix(v1, "v") - v2 = strings.TrimPrefix(v2, "v") - - parts1 := strings.Split(v1, ".") - parts2 := strings.Split(v2, ".") - - maxLen := len(parts1) - if len(parts2) > maxLen { - maxLen = len(parts2) - } - - for i := 0; i < maxLen; i++ { - var p1, p2 int - if i < len(parts1) { - fmt.Sscanf(parts1[i], "%d", &p1) - } - if i < len(parts2) { - fmt.Sscanf(parts2[i], "%d", &p2) - } - - if p1 < p2 { - return -1 - } - if p1 > p2 { - return 1 - } - } - - return 0 -} diff --git a/nix/inputs/dms-cli/internal/version/version_test.go b/nix/inputs/dms-cli/internal/version/version_test.go deleted file mode 100644 index 08b123a..0000000 --- a/nix/inputs/dms-cli/internal/version/version_test.go +++ /dev/null @@ -1,293 +0,0 @@ -package version - -import ( - "os" - "os/exec" - "path/filepath" - "testing" -) - -func TestCompareVersions(t *testing.T) { - tests := []struct { - v1 string - v2 string - expected int - }{ - {"v0.1.0", "v0.1.0", 0}, - {"v0.1.0", "v0.1.1", -1}, - {"v0.1.1", "v0.1.0", 1}, - {"v0.1.10", "v0.1.2", 1}, - {"v0.2.0", "v0.1.9", 1}, - {"0.1.0", "0.1.0", 0}, - {"1.0.0", "v1.0.0", 0}, - {"v1.2.3", "v1.2.4", -1}, - {"v2.0.0", "v1.9.9", 1}, - } - - for _, tt := range tests { - result := CompareVersions(tt.v1, tt.v2) - if result != tt.expected { - t.Errorf("CompareVersions(%q, %q) = %d; want %d", tt.v1, tt.v2, result, tt.expected) - } - } -} - -func TestGetDMSVersionInfo_Structure(t *testing.T) { - homeDir, err := os.UserHomeDir() - if err != nil { - t.Skip("Cannot get home directory") - } - - dmsPath := filepath.Join(homeDir, ".config", "quickshell", "dms") - if _, err := os.Stat(dmsPath); os.IsNotExist(err) { - t.Skip("DMS not installed, skipping version info test") - } - - info, err := GetDMSVersionInfo() - if err != nil { - t.Fatalf("GetDMSVersionInfo() failed: %v", err) - } - - if info == nil { - t.Fatal("GetDMSVersionInfo() returned nil") - } - - if info.Current == "" { - t.Error("Current version is empty") - } - - if info.Latest == "" { - t.Error("Latest version is empty") - } - - t.Logf("Current: %s, Latest: %s, HasUpdate: %v", info.Current, info.Latest, info.HasUpdate) -} - -func TestGetCurrentDMSVersion_NotInstalled(t *testing.T) { - originalHome := os.Getenv("HOME") - defer os.Setenv("HOME", originalHome) - - tempDir := t.TempDir() - os.Setenv("HOME", tempDir) - - _, err := GetCurrentDMSVersion() - if err == nil { - t.Error("Expected error when DMS not installed, got nil") - } -} - -func TestGetCurrentDMSVersion_GitTag(t *testing.T) { - if !commandExists("git") { - t.Skip("git not available") - } - - tempDir := t.TempDir() - dmsPath := filepath.Join(tempDir, ".config", "quickshell", "dms") - os.MkdirAll(dmsPath, 0755) - - originalHome := os.Getenv("HOME") - defer os.Setenv("HOME", originalHome) - os.Setenv("HOME", tempDir) - - exec.Command("git", "init", dmsPath).Run() - exec.Command("git", "-C", dmsPath, "config", "user.email", "test@test.com").Run() - exec.Command("git", "-C", dmsPath, "config", "user.name", "Test User").Run() - - testFile := filepath.Join(dmsPath, "test.txt") - os.WriteFile(testFile, []byte("test"), 0644) - exec.Command("git", "-C", dmsPath, "add", ".").Run() - exec.Command("git", "-C", dmsPath, "commit", "-m", "initial").Run() - exec.Command("git", "-C", dmsPath, "tag", "v0.1.0").Run() - - version, err := GetCurrentDMSVersion() - if err != nil { - t.Fatalf("GetCurrentDMSVersion() failed: %v", err) - } - - if version != "v0.1.0" { - t.Errorf("Expected version v0.1.0, got %s", version) - } -} - -func TestGetCurrentDMSVersion_GitBranch(t *testing.T) { - if !commandExists("git") { - t.Skip("git not available") - } - - tempDir := t.TempDir() - dmsPath := filepath.Join(tempDir, ".config", "quickshell", "dms") - os.MkdirAll(dmsPath, 0755) - - originalHome := os.Getenv("HOME") - defer os.Setenv("HOME", originalHome) - os.Setenv("HOME", tempDir) - - exec.Command("git", "init", dmsPath).Run() - exec.Command("git", "-C", dmsPath, "config", "user.email", "test@test.com").Run() - exec.Command("git", "-C", dmsPath, "config", "user.name", "Test User").Run() - exec.Command("git", "-C", dmsPath, "checkout", "-b", "master").Run() - - testFile := filepath.Join(dmsPath, "test.txt") - os.WriteFile(testFile, []byte("test"), 0644) - exec.Command("git", "-C", dmsPath, "add", ".").Run() - exec.Command("git", "-C", dmsPath, "commit", "-m", "initial").Run() - - version, err := GetCurrentDMSVersion() - if err != nil { - t.Fatalf("GetCurrentDMSVersion() failed: %v", err) - } - - if version == "" { - t.Error("Expected non-empty version") - } - - if len(version) < 7 { - t.Errorf("Expected version with branch@commit format, got %s", version) - } -} - -func TestVersionInfo_IsGit(t *testing.T) { - tests := []struct { - current string - isGit bool - isBranch bool - isTag bool - }{ - {"v0.1.0", false, false, true}, - {"master@abc1234", true, true, false}, - {"dev@def5678", true, true, false}, - {"v0.2.0", false, false, true}, - {"unknown", false, false, false}, - } - - for _, tt := range tests { - info := &VersionInfo{ - IsGit: tt.isGit, - IsBranch: tt.isBranch, - IsTag: tt.isTag, - } - - actualIsGit := len(tt.current) > 0 && tt.current[0] != 'v' && tt.current != "unknown" - actualIsBranch := len(tt.current) > 0 && tt.current[0] != 'v' - actualIsTag := len(tt.current) > 0 && tt.current[0] == 'v' - - if tt.current == "unknown" { - actualIsGit = false - actualIsBranch = false - actualIsTag = false - } - - if info.IsGit != tt.isGit { - t.Errorf("For %s: IsGit = %v; want %v", tt.current, info.IsGit, tt.isGit) - } - if info.IsBranch != tt.isBranch { - t.Errorf("For %s: IsBranch = %v; want %v", tt.current, info.IsBranch, tt.isBranch) - } - if info.IsTag != tt.isTag { - t.Errorf("For %s: IsTag = %v; want %v", tt.current, info.IsTag, tt.isTag) - } - - _ = actualIsGit - _ = actualIsBranch - _ = actualIsTag - } -} - -func TestVersionInfo_HasUpdate_Branch(t *testing.T) { - tests := []struct { - current string - latest string - hasUpdate bool - }{ - {"master@abc1234", "master@abc1234", false}, - {"master@abc1234", "master@def5678", true}, - {"dev@abc1234", "dev@abc1234", false}, - {"dev@old1234", "dev@new5678", true}, - } - - for _, tt := range tests { - info := &VersionInfo{ - HasUpdate: tt.hasUpdate, - } - - if info.HasUpdate != tt.hasUpdate { - t.Errorf("For current=%s, latest=%s: HasUpdate = %v; want %v", - tt.current, tt.latest, info.HasUpdate, tt.hasUpdate) - } - } -} - -func TestVersionInfo_HasUpdate_Tag(t *testing.T) { - tests := []struct { - current string - latest string - hasUpdate bool - }{ - {"v0.1.0", "v0.1.0", false}, - {"v0.1.0", "v0.1.1", true}, - {"v0.1.5", "v0.1.5", false}, - {"v0.1.9", "v0.2.0", true}, - } - - for _, tt := range tests { - info := &VersionInfo{ - HasUpdate: tt.hasUpdate, - } - - if info.HasUpdate != tt.hasUpdate { - t.Errorf("For current=%s, latest=%s: HasUpdate = %v; want %v", - tt.current, tt.latest, info.HasUpdate, tt.hasUpdate) - } - } -} - -func commandExists(cmd string) bool { - _, err := exec.LookPath(cmd) - return err == nil -} - -func TestGetLatestDMSVersion_FallbackParsing(t *testing.T) { - jsonResponse := `{ - "tag_name": "v0.1.17", - "name": "Release v0.1.17" - }` - - lines := []string{ - ` "tag_name": "v0.1.17",`, - ` "name": "Release v0.1.17"`, - } - - for _, line := range lines { - if len(line) > 0 && line[0:15] == ` "tag_name": "` { - parts := []string{"", "", "", "v0.1.17"} - version := parts[3] - if version != "v0.1.17" { - t.Errorf("Failed to parse version from line: %s", line) - } - } - } - - _ = jsonResponse -} - -func TestCompareVersions_EdgeCases(t *testing.T) { - tests := []struct { - v1 string - v2 string - expected int - }{ - {"", "", 0}, - {"v1", "v1", 0}, - {"v1.0", "v1", 0}, - {"v1.0.0", "v1.0", 0}, - {"v1.0.1", "v1.0", 1}, - {"v1", "v1.0.1", -1}, - } - - for _, tt := range tests { - result := CompareVersions(tt.v1, tt.v2) - if result != tt.expected { - t.Errorf("CompareVersions(%q, %q) = %d; want %d", tt.v1, tt.v2, result, tt.expected) - } - } -} diff --git a/nix/inputs/dms-cli/pkg/ipp/CREDITS.MD b/nix/inputs/dms-cli/pkg/ipp/CREDITS.MD deleted file mode 100644 index b7d28cc..0000000 --- a/nix/inputs/dms-cli/pkg/ipp/CREDITS.MD +++ /dev/null @@ -1,5 +0,0 @@ -# Credits - -* Original socket adapter code is mostly taken from [korylprince/printer-manager-cups](https://github.com/korylprince/printer-manager-cups) -([MIT](https://github.com/korylprince/printer-manager-cups/blob/v1.0.9/LICENSE) licensed): -[conn.go](https://github.com/korylprince/printer-manager-cups/blob/v1.0.9/cups/conn.go) diff --git a/nix/inputs/dms-cli/pkg/ipp/LICENSE b/nix/inputs/dms-cli/pkg/ipp/LICENSE deleted file mode 100644 index 261eeb9..0000000 --- a/nix/inputs/dms-cli/pkg/ipp/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/nix/inputs/dms-cli/pkg/ipp/adapter-http.go b/nix/inputs/dms-cli/pkg/ipp/adapter-http.go deleted file mode 100644 index 0862808..0000000 --- a/nix/inputs/dms-cli/pkg/ipp/adapter-http.go +++ /dev/null @@ -1,132 +0,0 @@ -package ipp - -import ( - "bytes" - "crypto/tls" - "fmt" - "io" - "net" - "net/http" - "strconv" - "time" -) - -type HttpAdapter struct { - host string - port int - username string - password string - useTLS bool - client *http.Client -} - -func NewHttpAdapter(host string, port int, username, password string, useTLS bool) *HttpAdapter { - httpClient := http.Client{ - Timeout: 0, - Transport: &http.Transport{ - TLSClientConfig: &tls.Config{ - InsecureSkipVerify: true, - }, - ResponseHeaderTimeout: 90 * time.Second, - IdleConnTimeout: 120 * time.Second, - }, - } - - return &HttpAdapter{ - host: host, - port: port, - username: username, - password: password, - useTLS: useTLS, - client: &httpClient, - } -} - -func (h *HttpAdapter) SendRequest(url string, req *Request, additionalResponseData io.Writer) (*Response, error) { - payload, err := req.Encode() - if err != nil { - return nil, err - } - - size := len(payload) - var body io.Reader - if req.File != nil && req.FileSize != -1 { - size += req.FileSize - body = io.MultiReader(bytes.NewBuffer(payload), req.File) - } else { - body = bytes.NewBuffer(payload) - } - - httpReq, err := http.NewRequest("POST", url, body) - if err != nil { - return nil, err - } - - httpReq.Header.Set("Content-Length", strconv.Itoa(size)) - httpReq.Header.Set("Content-Type", ContentTypeIPP) - - if h.username != "" && h.password != "" { - httpReq.SetBasicAuth(h.username, h.password) - } - - httpResp, err := h.client.Do(httpReq) - if err != nil { - return nil, err - } - defer httpResp.Body.Close() - - if httpResp.StatusCode != 200 { - return nil, HTTPError{ - Code: httpResp.StatusCode, - } - } - - // buffer response to avoid read issues - buf := new(bytes.Buffer) - if httpResp.ContentLength > 0 { - buf.Grow(int(httpResp.ContentLength)) - } - if _, err := io.Copy(buf, httpResp.Body); err != nil { - return nil, fmt.Errorf("unable to buffer response: %w", err) - } - - ippResp, err := NewResponseDecoder(buf).Decode(additionalResponseData) - if err != nil { - return nil, err - } - - if err = ippResp.CheckForErrors(); err != nil { - return nil, fmt.Errorf("received error IPP response: %w", err) - } - - return ippResp, nil -} - -func (h *HttpAdapter) GetHttpUri(namespace string, object interface{}) string { - proto := "http" - if h.useTLS { - proto = "https" - } - - uri := fmt.Sprintf("%s://%s:%d", proto, h.host, h.port) - - if namespace != "" { - uri = fmt.Sprintf("%s/%s", uri, namespace) - } - - if object != nil { - uri = fmt.Sprintf("%s/%v", uri, object) - } - - return uri -} - -func (h *HttpAdapter) TestConnection() error { - conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", h.host, h.port)) - if err != nil { - return err - } - conn.Close() - - return nil -} diff --git a/nix/inputs/dms-cli/pkg/ipp/adapter.go b/nix/inputs/dms-cli/pkg/ipp/adapter.go deleted file mode 100644 index 15ad2d0..0000000 --- a/nix/inputs/dms-cli/pkg/ipp/adapter.go +++ /dev/null @@ -1,9 +0,0 @@ -package ipp - -import "io" - -type Adapter interface { - SendRequest(url string, req *Request, additionalResponseData io.Writer) (*Response, error) - GetHttpUri(namespace string, object interface{}) string - TestConnection() error -} diff --git a/nix/inputs/dms-cli/pkg/ipp/attribute.go b/nix/inputs/dms-cli/pkg/ipp/attribute.go deleted file mode 100644 index a9b4ba7..0000000 --- a/nix/inputs/dms-cli/pkg/ipp/attribute.go +++ /dev/null @@ -1,528 +0,0 @@ -package ipp - -import ( - "encoding/binary" - "fmt" - "io" -) - -const ( - sizeInteger = int16(4) - sizeBoolean = int16(1) -) - -// AttributeEncoder encodes attribute to a io.Writer -type AttributeEncoder struct { - writer io.Writer -} - -// NewAttributeEncoder returns a new encoder that writes to w -func NewAttributeEncoder(w io.Writer) *AttributeEncoder { - return &AttributeEncoder{w} -} - -// Encode encodes a attribute and its value to a io.Writer -// the tag is determined by the AttributeTagMapping map -func (e *AttributeEncoder) Encode(attribute string, value interface{}) error { - tag, ok := AttributeTagMapping[attribute] - if !ok { - return fmt.Errorf("cannot get tag of attribute %s", attribute) - } - - switch v := value.(type) { - case int: - if tag != TagInteger && tag != TagEnum { - return fmt.Errorf("tag for attribute %s does not match with value type", attribute) - } - - if err := e.encodeTag(tag); err != nil { - return err - } - - if err := e.encodeString(attribute); err != nil { - return err - } - - if err := e.encodeInteger(int32(v)); err != nil { - return err - } - case int16: - if tag != TagInteger && tag != TagEnum { - return fmt.Errorf("tag for attribute %s does not match with value type", attribute) - } - - if err := e.encodeTag(tag); err != nil { - return err - } - - if err := e.encodeString(attribute); err != nil { - return err - } - - if err := e.encodeInteger(int32(v)); err != nil { - return err - } - case int8: - if tag != TagInteger && tag != TagEnum { - return fmt.Errorf("tag for attribute %s does not match with value type", attribute) - } - - if err := e.encodeTag(tag); err != nil { - return err - } - - if err := e.encodeString(attribute); err != nil { - return err - } - - if err := e.encodeInteger(int32(v)); err != nil { - return err - } - case int32: - if tag != TagInteger && tag != TagEnum { - return fmt.Errorf("tag for attribute %s does not match with value type", attribute) - } - - if err := e.encodeTag(tag); err != nil { - return err - } - - if err := e.encodeString(attribute); err != nil { - return err - } - - if err := e.encodeInteger(v); err != nil { - return err - } - case int64: - if tag != TagInteger && tag != TagEnum { - return fmt.Errorf("tag for attribute %s does not match with value type", attribute) - } - - if err := e.encodeTag(tag); err != nil { - return err - } - - if err := e.encodeString(attribute); err != nil { - return err - } - - if err := e.encodeInteger(int32(v)); err != nil { - return err - } - case []int: - if tag != TagInteger && tag != TagEnum { - return fmt.Errorf("tag for attribute %s does not match with value type", attribute) - } - - for index, val := range v { - if err := e.encodeTag(tag); err != nil { - return err - } - - if index == 0 { - if err := e.encodeString(attribute); err != nil { - return err - } - } else { - if err := e.writeNullByte(); err != nil { - return err - } - } - - if err := e.encodeInteger(int32(val)); err != nil { - return err - } - } - case []int16: - if tag != TagInteger && tag != TagEnum { - return fmt.Errorf("tag for attribute %s does not match with value type", attribute) - } - - for index, val := range v { - if err := e.encodeTag(tag); err != nil { - return err - } - - if index == 0 { - if err := e.encodeString(attribute); err != nil { - return err - } - } else { - if err := e.writeNullByte(); err != nil { - return err - } - } - - if err := e.encodeInteger(int32(val)); err != nil { - return err - } - } - case []int8: - if tag != TagInteger && tag != TagEnum { - return fmt.Errorf("tag for attribute %s does not match with value type", attribute) - } - - for index, val := range v { - if err := e.encodeTag(tag); err != nil { - return err - } - - if index == 0 { - if err := e.encodeString(attribute); err != nil { - return err - } - } else { - if err := e.writeNullByte(); err != nil { - return err - } - } - - if err := e.encodeInteger(int32(val)); err != nil { - return err - } - } - case []int32: - if tag != TagInteger && tag != TagEnum { - return fmt.Errorf("tag for attribute %s does not match with value type", attribute) - } - - for index, val := range v { - if err := e.encodeTag(tag); err != nil { - return err - } - - if index == 0 { - if err := e.encodeString(attribute); err != nil { - return err - } - } else { - if err := e.writeNullByte(); err != nil { - return err - } - } - - if err := e.encodeInteger(val); err != nil { - return err - } - } - case []int64: - if tag != TagInteger && tag != TagEnum { - return fmt.Errorf("tag for attribute %s does not match with value type", attribute) - } - - for index, val := range v { - if err := e.encodeTag(tag); err != nil { - return err - } - - if index == 0 { - if err := e.encodeString(attribute); err != nil { - return err - } - } else { - if err := e.writeNullByte(); err != nil { - return err - } - } - - if err := e.encodeInteger(int32(val)); err != nil { - return err - } - } - case bool: - if tag != TagBoolean { - return fmt.Errorf("tag for attribute %s does not match with value type", attribute) - } - - if err := e.encodeTag(tag); err != nil { - return err - } - - if err := e.encodeString(attribute); err != nil { - return err - } - - if err := e.encodeBoolean(v); err != nil { - return err - } - case []bool: - if tag != TagBoolean { - return fmt.Errorf("tag for attribute %s does not match with value type", attribute) - } - - for index, val := range v { - if err := e.encodeTag(tag); err != nil { - return err - } - - if index == 0 { - if err := e.encodeString(attribute); err != nil { - return err - } - } else { - if err := e.writeNullByte(); err != nil { - return err - } - } - - if err := e.encodeBoolean(val); err != nil { - return err - } - } - case string: - if err := e.encodeTag(tag); err != nil { - return err - } - - if err := e.encodeString(attribute); err != nil { - return err - } - - if err := e.encodeString(v); err != nil { - return err - } - case []string: - for index, val := range v { - if err := e.encodeTag(tag); err != nil { - return err - } - - if index == 0 { - if err := e.encodeString(attribute); err != nil { - return err - } - } else { - if err := e.writeNullByte(); err != nil { - return err - } - } - - if err := e.encodeString(val); err != nil { - return err - } - } - default: - return fmt.Errorf("type %T is not supported", value) - } - - return nil -} - -func (e *AttributeEncoder) encodeString(s string) error { - if err := binary.Write(e.writer, binary.BigEndian, int16(len(s))); err != nil { - return err - } - - _, err := e.writer.Write([]byte(s)) - return err -} - -func (e *AttributeEncoder) encodeInteger(i int32) error { - if err := binary.Write(e.writer, binary.BigEndian, sizeInteger); err != nil { - return err - } - - return binary.Write(e.writer, binary.BigEndian, i) -} - -func (e *AttributeEncoder) encodeBoolean(b bool) error { - if err := binary.Write(e.writer, binary.BigEndian, sizeBoolean); err != nil { - return err - } - - return binary.Write(e.writer, binary.BigEndian, b) -} - -func (e *AttributeEncoder) encodeTag(t int8) error { - return binary.Write(e.writer, binary.BigEndian, t) -} - -func (e *AttributeEncoder) writeNullByte() error { - return binary.Write(e.writer, binary.BigEndian, int16(0)) -} - -// Attribute defines an ipp attribute -type Attribute struct { - Tag int8 - Name string - Value interface{} -} - -// Resolution defines the resolution attribute -type Resolution struct { - Height int32 - Width int32 - Depth int8 -} - -// AttributeDecoder reads and decodes ipp from an input stream -type AttributeDecoder struct { - reader io.Reader -} - -// NewAttributeDecoder returns a new decoder that reads from r -func NewAttributeDecoder(r io.Reader) *AttributeDecoder { - return &AttributeDecoder{r} -} - -// Decode reads the next ipp attribute into a attribute struct. the type is identified by a tag passed as an argument -func (d *AttributeDecoder) Decode(tag int8) (*Attribute, error) { - attr := Attribute{Tag: tag} - - name, err := d.decodeString() - if err != nil { - return nil, err - } - attr.Name = name - - switch attr.Tag { - case TagEnum, TagInteger: - val, err := d.decodeInteger() - if err != nil { - return nil, err - } - attr.Value = val - case TagBoolean: - val, err := d.decodeBool() - if err != nil { - return nil, err - } - attr.Value = val - case TagDate: - val, err := d.decodeDate() - if err != nil { - return nil, err - } - attr.Value = val - case TagRange: - val, err := d.decodeRange() - if err != nil { - return nil, err - } - attr.Value = val - case TagResolution: - val, err := d.decodeResolution() - if err != nil { - return nil, err - } - attr.Value = val - default: - val, err := d.decodeString() - if err != nil { - return nil, err - } - attr.Value = val - } - - return &attr, nil -} - -func (d *AttributeDecoder) decodeBool() (b bool, err error) { - if _, err = d.readValueLength(); err != nil { - return - } - - if err = binary.Read(d.reader, binary.BigEndian, &b); err != nil { - return - } - - return -} - -func (d *AttributeDecoder) decodeInteger() (i int, err error) { - if _, err = d.readValueLength(); err != nil { - return - } - - var reti int32 - if err = binary.Read(d.reader, binary.BigEndian, &reti); err != nil { - return - } - - return int(reti), nil -} - -func (d *AttributeDecoder) decodeString() (string, error) { - length, err := d.readValueLength() - if err != nil { - return "", err - } - - if length == 0 { - return "", nil - } - - bs := make([]byte, length) - if _, err := d.reader.Read(bs); err != nil { - return "", nil - } - - return string(bs), nil -} - -func (d *AttributeDecoder) decodeDate() ([]int, error) { - length, err := d.readValueLength() - if err != nil { - return nil, err - } - - is := make([]int, length) - var ti int8 - - for i := int16(0); i < length; i++ { - if err = binary.Read(d.reader, binary.BigEndian, &ti); err != nil { - return nil, err - } - is[i] = int(ti) - } - - return is, nil -} - -func (d *AttributeDecoder) decodeRange() ([]int32, error) { - length, err := d.readValueLength() - if err != nil { - return nil, err - } - - // initialize range element count (c) and range slice (r) - c := length / 4 - r := make([]int32, c) - - for i := int16(0); i < c; i++ { - var ti int32 - if err = binary.Read(d.reader, binary.BigEndian, &ti); err != nil { - return nil, err - } - r[i] = ti - } - - return r, nil -} - -func (d *AttributeDecoder) decodeResolution() (res Resolution, err error) { - _, err = d.readValueLength() - if err != nil { - return - } - - if err = binary.Read(d.reader, binary.BigEndian, &res.Height); err != nil { - return - } - - if err = binary.Read(d.reader, binary.BigEndian, &res.Width); err != nil { - return - } - - if err = binary.Read(d.reader, binary.BigEndian, &res.Depth); err != nil { - return - } - - return -} - -func (d *AttributeDecoder) readValueLength() (length int16, err error) { - err = binary.Read(d.reader, binary.BigEndian, &length) - return -} diff --git a/nix/inputs/dms-cli/pkg/ipp/constants.go b/nix/inputs/dms-cli/pkg/ipp/constants.go deleted file mode 100644 index 5c91983..0000000 --- a/nix/inputs/dms-cli/pkg/ipp/constants.go +++ /dev/null @@ -1,449 +0,0 @@ -package ipp - -// ipp status codes -const ( - StatusCupsInvalid int16 = -1 - StatusOk int16 = 0x0000 - StatusOkIgnoredOrSubstituted int16 = 0x0001 - StatusOkConflicting int16 = 0x0002 - StatusOkIgnoredSubscriptions int16 = 0x0003 - StatusOkIgnoredNotifications int16 = 0x0004 - StatusOkTooManyEvents int16 = 0x0005 - StatusOkButCancelSubscription int16 = 0x0006 - StatusOkEventsComplete int16 = 0x0007 - StatusRedirectionOtherSite int16 = 0x0200 - StatusCupsSeeOther int16 = 0x0280 - StatusErrorBadRequest int16 = 0x0400 - StatusErrorForbidden int16 = 0x0401 - StatusErrorNotAuthenticated int16 = 0x0402 - StatusErrorNotAuthorized int16 = 0x0403 - StatusErrorNotPossible int16 = 0x0404 - StatusErrorTimeout int16 = 0x0405 - StatusErrorNotFound int16 = 0x0406 - StatusErrorGone int16 = 0x0407 - StatusErrorRequestEntity int16 = 0x0408 - StatusErrorRequestValue int16 = 0x0409 - StatusErrorDocumentFormatNotSupported int16 = 0x040a - StatusErrorAttributesOrValues int16 = 0x040b - StatusErrorUriScheme int16 = 0x040c - StatusErrorCharset int16 = 0x040d - StatusErrorConflicting int16 = 0x040e - StatusErrorCompressionError int16 = 0x040f - StatusErrorDocumentFormatError int16 = 0x0410 - StatusErrorDocumentAccess int16 = 0x0411 - StatusErrorAttributesNotSettable int16 = 0x0412 - StatusErrorIgnoredAllSubscriptions int16 = 0x0413 - StatusErrorTooManySubscriptions int16 = 0x0414 - StatusErrorIgnoredAllNotifications int16 = 0x0415 - StatusErrorPrintSupportFileNotFound int16 = 0x0416 - StatusErrorDocumentPassword int16 = 0x0417 - StatusErrorDocumentPermission int16 = 0x0418 - StatusErrorDocumentSecurity int16 = 0x0419 - StatusErrorDocumentUnprintable int16 = 0x041a - StatusErrorAccountInfoNeeded int16 = 0x041b - StatusErrorAccountClosed int16 = 0x041c - StatusErrorAccountLimitReached int16 = 0x041d - StatusErrorAccountAuthorizationFailed int16 = 0x041e - StatusErrorNotFetchable int16 = 0x041f - StatusErrorCupsAccountInfoNeeded int16 = 0x049C - StatusErrorCupsAccountClosed int16 = 0x049d - StatusErrorCupsAccountLimitReached int16 = 0x049e - StatusErrorCupsAccountAuthorizationFailed int16 = 0x049f - StatusErrorInternal int16 = 0x0500 - StatusErrorOperationNotSupported int16 = 0x0501 - StatusErrorServiceUnavailable int16 = 0x0502 - StatusErrorVersionNotSupported int16 = 0x0503 - StatusErrorDevice int16 = 0x0504 - StatusErrorTemporary int16 = 0x0505 - StatusErrorNotAcceptingJobs int16 = 0x0506 - StatusErrorBusy int16 = 0x0507 - StatusErrorJobCanceled int16 = 0x0508 - StatusErrorMultipleJobsNotSupported int16 = 0x0509 - StatusErrorPrinterIsDeactivated int16 = 0x050a - StatusErrorTooManyJobs int16 = 0x050b - StatusErrorTooManyDocuments int16 = 0x050c - StatusErrorCupsAuthenticationCanceled int16 = 0x1000 - StatusErrorCupsPki int16 = 0x1001 - StatusErrorCupsUpgradeRequired int16 = 0x1002 -) - -// ipp operations -const ( - OperationCupsInvalid int16 = -0x0001 - OperationCupsNone int16 = 0x0000 - OperationPrintJob int16 = 0x0002 - OperationPrintUri int16 = 0x0003 - OperationValidateJob int16 = 0x0004 - OperationCreateJob int16 = 0x0005 - OperationSendDocument int16 = 0x0006 - OperationSendUri int16 = 0x0007 - OperationCancelJob int16 = 0x0008 - OperationGetJobAttributes int16 = 0x0009 - OperationGetJobs int16 = 0x000a - OperationGetPrinterAttributes int16 = 0x000b - OperationHoldJob int16 = 0x000c - OperationReleaseJob int16 = 0x000d - OperationRestartJob int16 = 0x000e - OperationPausePrinter int16 = 0x0010 - OperationResumePrinter int16 = 0x0011 - OperationPurgeJobs int16 = 0x0012 - OperationSetPrinterAttributes int16 = 0x0013 - OperationSetJobAttributes int16 = 0x0014 - OperationGetPrinterSupportedValues int16 = 0x0015 - OperationCreatePrinterSubscriptions int16 = 0x0016 - OperationCreateJobSubscriptions int16 = 0x0017 - OperationGetSubscriptionAttributes int16 = 0x0018 - OperationGetSubscriptions int16 = 0x0019 - OperationRenewSubscription int16 = 0x001a - OperationCancelSubscription int16 = 0x001b - OperationGetNotifications int16 = 0x001c - OperationSendNotifications int16 = 0x001d - OperationGetResourceAttributes int16 = 0x001e - OperationGetResourceData int16 = 0x001f - OperationGetResources int16 = 0x0020 - OperationGetPrintSupportFiles int16 = 0x0021 - OperationEnablePrinter int16 = 0x0022 - OperationDisablePrinter int16 = 0x0023 - OperationPausePrinterAfterCurrentJob int16 = 0x0024 - OperationHoldNewJobs int16 = 0x0025 - OperationReleaseHeldNewJobs int16 = 0x0026 - OperationDeactivatePrinter int16 = 0x0027 - OperationActivatePrinter int16 = 0x0028 - OperationRestartPrinter int16 = 0x0029 - OperationShutdownPrinter int16 = 0x002a - OperationStartupPrinter int16 = 0x002b - OperationReprocessJob int16 = 0x002c - OperationCancelCurrentJob int16 = 0x002d - OperationSuspendCurrentJob int16 = 0x002e - OperationResumeJob int16 = 0x002f - OperationOperationPromoteJob int16 = 0x0030 - OperationScheduleJobAfter int16 = 0x0031 - OperationCancelDocument int16 = 0x0033 - OperationGetDocumentAttributes int16 = 0x0034 - OperationGetDocuments int16 = 0x0035 - OperationDeleteDocument int16 = 0x0036 - OperationSetDocumentAttributes int16 = 0x0037 - OperationCancelJobs int16 = 0x0038 - OperationCancelMyJobs int16 = 0x0039 - OperationResubmitJob int16 = 0x003a - OperationCloseJob int16 = 0x003b - OperationIdentifyPrinter int16 = 0x003c - OperationValidateDocument int16 = 0x003d - OperationAddDocumentImages int16 = 0x003e - OperationAcknowledgeDocument int16 = 0x003f - OperationAcknowledgeIdentifyPrinter int16 = 0x0040 - OperationAcknowledgeJob int16 = 0x0041 - OperationFetchDocument int16 = 0x0042 - OperationFetchJob int16 = 0x0043 - OperationGetOutputDeviceAttributes int16 = 0x0044 - OperationUpdateActiveJobs int16 = 0x0045 - OperationDeregisterOutputDevice int16 = 0x0046 - OperationUpdateDocumentStatus int16 = 0x0047 - OperationUpdateJobStatus int16 = 0x0048 - OperationUpdateOutputDeviceAttributes int16 = 0x0049 - OperationGetNextDocumentData int16 = 0x004a - OperationAllocatePrinterResources int16 = 0x004b - OperationCreatePrinter int16 = 0x004c - OperationDeallocatePrinterResources int16 = 0x004d - OperationDeletePrinter int16 = 0x004e - OperationGetPrinters int16 = 0x004f - OperationShutdownOnePrinter int16 = 0x0050 - OperationStartupOnePrinter int16 = 0x0051 - OperationCancelResource int16 = 0x0052 - OperationCreateResource int16 = 0x0053 - OperationInstallResource int16 = 0x0054 - OperationSendResourceData int16 = 0x0055 - OperationSetResourceAttributes int16 = 0x0056 - OperationCreateResourceSubscriptions int16 = 0x0057 - OperationCreateSystemSubscriptions int16 = 0x0058 - OperationDisableAllPrinters int16 = 0x0059 - OperationEnableAllPrinters int16 = 0x005a - OperationGetSystemAttributes int16 = 0x005b - OperationGetSystemSupportedValues int16 = 0x005c - OperationPauseAllPrinters int16 = 0x005d - OperationPauseAllPrintersAfterCurrentJob int16 = 0x005e - OperationRegisterOutputDevice int16 = 0x005f - OperationRestartSystem int16 = 0x0060 - OperationResumeAllPrinters int16 = 0x0061 - OperationSetSystemAttributes int16 = 0x0062 - OperationShutdownAllPrinter int16 = 0x0063 - OperationStartupAllPrinters int16 = 0x0064 - OperationPrivate int16 = 0x4000 - OperationCupsGetDefault int16 = 0x4001 - OperationCupsGetPrinters int16 = 0x4002 - OperationCupsAddModifyPrinter int16 = 0x4003 - OperationCupsDeletePrinter int16 = 0x4004 - OperationCupsGetClasses int16 = 0x4005 - OperationCupsAddModifyClass int16 = 0x4006 - OperationCupsDeleteClass int16 = 0x4007 - OperationCupsAcceptJobs int16 = 0x4008 - OperationCupsRejectJobs int16 = 0x4009 - OperationCupsSetDefault int16 = 0x400a - OperationCupsGetDevices int16 = 0x400b - OperationCupsGetPPDs int16 = 0x400c - OperationCupsMoveJob int16 = 0x400d - OperationCupsAuthenticateJob int16 = 0x400e - OperationCupsGetPpd int16 = 0x400f - OperationCupsGetDocument int16 = 0x4027 - OperationCupsCreateLocalPrinter int16 = 0x4028 -) - -// ipp tags -const ( - TagCupsInvalid int8 = -1 - TagZero int8 = 0x00 - TagOperation int8 = 0x01 - TagJob int8 = 0x02 - TagEnd int8 = 0x03 - TagPrinter int8 = 0x04 - TagUnsupportedGroup int8 = 0x05 - TagSubscription int8 = 0x06 - TagEventNotification int8 = 0x07 - TagResource int8 = 0x08 - TagDocument int8 = 0x09 - TagSystem int8 = 0x0a - TagUnsupportedValue int8 = 0x10 - TagDefault int8 = 0x11 - TagUnknown int8 = 0x12 - TagNoValue int8 = 0x13 - TagNotSettable int8 = 0x15 - TagDeleteAttr int8 = 0x16 - TagAdminDefine int8 = 0x17 - TagInteger int8 = 0x21 - TagBoolean int8 = 0x22 - TagEnum int8 = 0x23 - TagString int8 = 0x30 - TagDate int8 = 0x31 - TagResolution int8 = 0x32 - TagRange int8 = 0x33 - TagBeginCollection int8 = 0x34 - TagTextLang int8 = 0x35 - TagNameLang int8 = 0x36 - TagEndCollection int8 = 0x37 - TagText int8 = 0x41 - TagName int8 = 0x42 - TagReservedString int8 = 0x43 - TagKeyword int8 = 0x44 - TagUri int8 = 0x45 - TagUriScheme int8 = 0x46 - TagCharset int8 = 0x47 - TagLanguage int8 = 0x48 - TagMimeType int8 = 0x49 - TagMemberName int8 = 0x4a - TagExtension int8 = 0x7f -) - -// job states -const ( - JobStatePending int8 = 0x03 - JobStateHeld int8 = 0x04 - JobStateProcessing int8 = 0x05 - JobStateStopped int8 = 0x06 - JobStateCanceled int8 = 0x07 - JobStateAborted int8 = 0x08 - JobStateCompleted int8 = 0x09 -) - -// document states -const ( - DocumentStatePending int8 = 0x03 - DocumentStateProcessing int8 = 0x05 - DocumentStateCanceled int8 = 0x07 - DocumentStateAborted int8 = 0x08 - DocumentStateCompleted int8 = 0x08 -) - -// printer states -const ( - PrinterStateIdle int8 = 0x0003 - PrinterStateProcessing int8 = 0x0004 - PrinterStateStopped int8 = 0x0005 -) - -// job state filter -const ( - JobStateFilterNotCompleted = "not-completed" - JobStateFilterCompleted = "completed" - JobStateFilterAll = "all" -) - -// error policies -const ( - ErrorPolicyRetryJob = "retry-job" - ErrorPolicyAbortJob = "abort-job" - ErrorPolicyRetryCurrentJob = "retry-current-job" - ErrorPolicyStopPrinter = "stop-printer" -) - -// ipp defaults -const ( - CharsetLanguage = "en-US" - Charset = "utf-8" - ProtocolVersionMajor = int8(2) - ProtocolVersionMinor = int8(0) - - DefaultJobPriority = 50 -) - -// useful mime types for ipp -const ( - MimeTypePostscript = "application/postscript" - MimeTypeOctetStream = "application/octet-stream" -) - -// ipp content types -const ( - ContentTypeIPP = "application/ipp" -) - -// known ipp attributes -const ( - AttributeCopies = "copies" - AttributeDocumentFormat = "document-format" - AttributeDocumentName = "document-name" - AttributeJobID = "job-id" - AttributeJobName = "job-name" - AttributeJobPriority = "job-priority" - AttributeJobURI = "job-uri" - AttributeLastDocument = "last-document" - AttributeMyJobs = "my-jobs" - AttributePPDName = "ppd-name" - AttributePPDMakeAndModel = "ppd-make-and-model" - AttributePrinterIsShared = "printer-is-shared" - AttributePrinterIsTemporary = "printer-is-temporary" - AttributePrinterURI = "printer-uri" - AttributePurgeJobs = "purge-jobs" - AttributeRequestedAttributes = "requested-attributes" - AttributeRequestingUserName = "requesting-user-name" - AttributeWhichJobs = "which-jobs" - AttributeFirstJobID = "first-job-id" - AttributeLimit = "limit" - AttributeStatusMessage = "status-message" - AttributeCharset = "attributes-charset" - AttributeNaturalLanguage = "attributes-natural-language" - AttributeDeviceURI = "device-uri" - AttributeHoldJobUntil = "job-hold-until" - AttributePrinterErrorPolicy = "printer-error-policy" - AttributePrinterInfo = "printer-info" - AttributePrinterLocation = "printer-location" - AttributePrinterName = "printer-name" - AttributePrinterStateReasons = "printer-state-reasons" - AttributeJobPrinterURI = "job-printer-uri" - AttributeMemberURIs = "member-uris" - AttributeDocumentNumber = "document-number" - AttributeDocumentState = "document-state" - AttributeFinishings = "finishings" - AttributeJobHoldUntil = "hold-job-until" - AttributeJobSheets = "job-sheets" - AttributeJobState = "job-state" - AttributeJobStateReason = "job-state-reason" - AttributeMedia = "media" - AttributeSides = "sides" - AttributeNumberUp = "number-up" - AttributeOrientationRequested = "orientation-requested" - AttributePrintQuality = "print-quality" - AttributePrinterIsAcceptingJobs = "printer-is-accepting-jobs" - AttributePrinterResolution = "printer-resolution" - AttributePrinterState = "printer-state" - AttributeMemberNames = "member-names" - AttributePrinterType = "printer-type" - AttributePrinterMakeAndModel = "printer-make-and-model" - AttributePrinterStateMessage = "printer-state-message" - AttributePrinterUriSupported = "printer-uri-supported" - AttributeJobMediaProgress = "job-media-progress" - AttributeJobKilobyteOctets = "job-k-octets" - AttributeNumberOfDocuments = "number-of-documents" - AttributeJobOriginatingUserName = "job-originating-user-name" - AttributeOutputOrder = "outputorder" - AttributeJobStateReasons = "job-state-reasons" - AttributeJobStateMessage = "job-state-message" - AttributeJobPrinterStateReasons = "job-printer-state-reasons" - AttributeJobPrinterStateMessage = "job-printer-state-message" - AttributeJobImpressionsCompleted = "job-impressions-completed" - AttributePrintScaling = "print-scaling" -) - -// Default attributes -var ( - DefaultClassAttributes = []string{AttributePrinterName, AttributeMemberNames} - DefaultPrinterAttributes = []string{ - AttributePrinterName, AttributePrinterType, AttributePrinterLocation, AttributePrinterInfo, - AttributePrinterMakeAndModel, AttributePrinterState, AttributePrinterStateMessage, AttributePrinterStateReasons, - AttributePrinterUriSupported, AttributeDeviceURI, AttributePrinterIsShared, - } - DefaultJobAttributes = []string{ - AttributeJobID, AttributeJobName, AttributePrinterURI, AttributeJobState, AttributeJobStateReason, - AttributeJobHoldUntil, AttributeJobMediaProgress, AttributeJobKilobyteOctets, AttributeNumberOfDocuments, AttributeCopies, - AttributeJobOriginatingUserName, - } -) - -// Attribute to tag mapping -var ( - AttributeTagMapping = map[string]int8{ - AttributeCharset: TagCharset, - AttributeNaturalLanguage: TagLanguage, - AttributeCopies: TagInteger, - AttributeDeviceURI: TagUri, - AttributeDocumentFormat: TagMimeType, - AttributeDocumentName: TagName, - AttributeDocumentNumber: TagInteger, - AttributeDocumentState: TagEnum, - AttributeFinishings: TagEnum, - AttributeJobHoldUntil: TagKeyword, - AttributeHoldJobUntil: TagKeyword, - AttributeJobID: TagInteger, - AttributeJobName: TagName, - AttributeJobPrinterURI: TagUri, - AttributeJobPriority: TagInteger, - AttributeJobSheets: TagName, - AttributeJobState: TagEnum, - AttributeJobStateReason: TagKeyword, - AttributeJobURI: TagUri, - AttributeLastDocument: TagBoolean, - AttributeMedia: TagKeyword, - AttributeSides: TagKeyword, - AttributeMemberURIs: TagUri, - AttributeMyJobs: TagBoolean, - AttributeNumberUp: TagInteger, - AttributeOrientationRequested: TagEnum, - AttributePPDName: TagName, - AttributePPDMakeAndModel: TagText, - AttributeNumberOfDocuments: TagInteger, - AttributePrintQuality: TagEnum, - AttributePrinterErrorPolicy: TagName, - AttributePrinterInfo: TagText, - AttributePrinterIsAcceptingJobs: TagBoolean, - AttributePrinterIsShared: TagBoolean, - AttributePrinterIsTemporary: TagBoolean, - AttributePrinterName: TagName, - AttributePrinterLocation: TagText, - AttributePrinterResolution: TagResolution, - AttributePrinterState: TagEnum, - AttributePrinterStateReasons: TagKeyword, - AttributePrinterURI: TagUri, - AttributePurgeJobs: TagBoolean, - AttributeRequestedAttributes: TagKeyword, - AttributeRequestingUserName: TagName, - AttributeWhichJobs: TagKeyword, - AttributeFirstJobID: TagInteger, - AttributeStatusMessage: TagText, - AttributeLimit: TagInteger, - AttributeOutputOrder: TagName, - AttributeJobStateReasons: TagString, - AttributeJobStateMessage: TagString, - AttributeJobPrinterStateReasons: TagString, - AttributeJobPrinterStateMessage: TagString, - AttributeJobImpressionsCompleted: TagInteger, - AttributePrintScaling: TagKeyword, - // IPP Subscription/Notification attributes (added for dankdots) - "notify-events": TagKeyword, - "notify-pull-method": TagKeyword, - "notify-lease-duration": TagInteger, - "notify-subscription-id": TagInteger, - "notify-subscription-ids": TagInteger, - "notify-sequence-numbers": TagInteger, - "notify-wait": TagBoolean, - "notify-recipient-uri": TagUri, - } -) diff --git a/nix/inputs/dms-cli/pkg/ipp/cups-client.go b/nix/inputs/dms-cli/pkg/ipp/cups-client.go deleted file mode 100644 index 9e2efc3..0000000 --- a/nix/inputs/dms-cli/pkg/ipp/cups-client.go +++ /dev/null @@ -1,322 +0,0 @@ -package ipp - -import ( - "bytes" - "strings" -) - -// CUPSClient implements a ipp client with specific cups operations -type CUPSClient struct { - *IPPClient -} - -// NewCUPSClient creates a new cups ipp client (used HttpAdapter internally) -func NewCUPSClient(host string, port int, username, password string, useTLS bool) *CUPSClient { - ippClient := NewIPPClient(host, port, username, password, useTLS) - return &CUPSClient{ippClient} -} - -// NewCUPSClientWithAdapter creates a new cups ipp client with given Adapter -func NewCUPSClientWithAdapter(username string, adapter Adapter) *CUPSClient { - ippClient := NewIPPClientWithAdapter(username, adapter) - return &CUPSClient{ippClient} -} - -// GetDevices returns a map of device uris and printer attributes -func (c *CUPSClient) GetDevices() (map[string]Attributes, error) { - req := NewRequest(OperationCupsGetDevices, 1) - - resp, err := c.SendRequest(c.adapter.GetHttpUri("", nil), req, nil) - if err != nil { - return nil, err - } - - printerNameMap := make(map[string]Attributes) - - for _, printerAttributes := range resp.PrinterAttributes { - printerNameMap[printerAttributes[AttributeDeviceURI][0].Value.(string)] = printerAttributes - } - - return printerNameMap, nil -} - -// MoveJob moves a job to a other printer -func (c *CUPSClient) MoveJob(jobID int, destPrinter string) error { - req := NewRequest(OperationCupsMoveJob, 1) - req.OperationAttributes[AttributeJobURI] = c.getJobUri(jobID) - req.PrinterAttributes[AttributeJobPrinterURI] = c.getPrinterUri(destPrinter) - - _, err := c.SendRequest(c.adapter.GetHttpUri("jobs", ""), req, nil) - return err -} - -// MoveAllJob moves all job from a printer to a other printer -func (c *CUPSClient) MoveAllJob(srcPrinter, destPrinter string) error { - req := NewRequest(OperationCupsMoveJob, 1) - req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(srcPrinter) - req.PrinterAttributes[AttributeJobPrinterURI] = c.getPrinterUri(destPrinter) - - _, err := c.SendRequest(c.adapter.GetHttpUri("jobs", ""), req, nil) - return err -} - -// GetPPDs returns a map of ppd names and attributes -func (c *CUPSClient) GetPPDs() (map[string]Attributes, error) { - req := NewRequest(OperationCupsGetPPDs, 1) - - resp, err := c.SendRequest(c.adapter.GetHttpUri("", nil), req, nil) - if err != nil { - return nil, err - } - - ppdNameMap := make(map[string]Attributes) - - for _, printerAttributes := range resp.PrinterAttributes { - ppdNameMap[printerAttributes[AttributePPDName][0].Value.(string)] = printerAttributes - } - - return ppdNameMap, nil -} - -// AcceptJobs lets a printer accept jobs again -func (c *CUPSClient) AcceptJobs(printer string) error { - req := NewRequest(OperationCupsAcceptJobs, 1) - req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer) - - _, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil) - return err -} - -// RejectJobs does not let a printer accept jobs -func (c *CUPSClient) RejectJobs(printer string) error { - req := NewRequest(OperationCupsRejectJobs, 1) - req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer) - - _, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil) - return err -} - -// AddPrinterToClass adds a printer to a class, if the class does not exists it will be crated -func (c *CUPSClient) AddPrinterToClass(class, printer string) error { - attributes, err := c.GetPrinterAttributes(class, []string{AttributeMemberURIs}) - if err != nil && !IsNotExistsError(err) { - return err - } - - memberURIList := make([]string, 0) - - if !IsNotExistsError(err) { - for _, member := range attributes[AttributeMemberURIs] { - memberString := strings.Split(member.Value.(string), "/") - printerName := memberString[len(memberString)-1] - - if printerName == printer { - return nil - } - - memberURIList = append(memberURIList, member.Value.(string)) - } - } - - memberURIList = append(memberURIList, c.getPrinterUri(printer)) - - req := NewRequest(OperationCupsAddModifyClass, 1) - req.OperationAttributes[AttributePrinterURI] = c.getClassUri(class) - req.PrinterAttributes[AttributeMemberURIs] = memberURIList - - _, err = c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil) - return err -} - -// DeletePrinterFromClass removes a printer from a class, if a class has no more printer it will be deleted -func (c *CUPSClient) DeletePrinterFromClass(class, printer string) error { - attributes, err := c.GetPrinterAttributes(class, []string{AttributeMemberURIs}) - if err != nil { - return err - } - - memberURIList := make([]string, 0) - - for _, member := range attributes[AttributeMemberURIs] { - memberString := strings.Split(member.Value.(string), "/") - printerName := memberString[len(memberString)-1] - - if printerName != printer { - memberURIList = append(memberURIList, member.Value.(string)) - } - } - - if len(memberURIList) == 0 { - return c.DeleteClass(class) - } - - req := NewRequest(OperationCupsAddModifyClass, 1) - req.OperationAttributes[AttributePrinterURI] = c.getClassUri(class) - req.PrinterAttributes[AttributeMemberURIs] = memberURIList - - _, err = c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil) - return err -} - -// DeleteClass deletes a class -func (c *CUPSClient) DeleteClass(class string) error { - req := NewRequest(OperationCupsDeleteClass, 1) - req.OperationAttributes[AttributePrinterURI] = c.getClassUri(class) - - _, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil) - return err -} - -// CreatePrinter creates a new printer -func (c *CUPSClient) CreatePrinter(name, deviceURI, ppd string, shared bool, errorPolicy string, information, location string) error { - req := NewRequest(OperationCupsAddModifyPrinter, 1) - req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(name) - req.OperationAttributes[AttributePPDName] = ppd - req.OperationAttributes[AttributePrinterIsShared] = shared - req.PrinterAttributes[AttributePrinterStateReasons] = "none" - req.PrinterAttributes[AttributeDeviceURI] = deviceURI - req.PrinterAttributes[AttributePrinterInfo] = information - req.PrinterAttributes[AttributePrinterLocation] = location - req.PrinterAttributes[AttributePrinterErrorPolicy] = errorPolicy - - _, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil) - return err -} - -// SetPrinterPPD sets the ppd for a printer -func (c *CUPSClient) SetPrinterPPD(printer, ppd string) error { - req := NewRequest(OperationCupsAddModifyPrinter, 1) - req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer) - req.OperationAttributes[AttributePPDName] = ppd - - _, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil) - return err -} - -// SetPrinterDeviceURI sets the device uri for a printer -func (c *CUPSClient) SetPrinterDeviceURI(printer, deviceURI string) error { - req := NewRequest(OperationCupsAddModifyPrinter, 1) - req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer) - req.PrinterAttributes[AttributeDeviceURI] = deviceURI - - _, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil) - return err -} - -// SetPrinterIsShared shares or unshares a printer in the network -func (c *CUPSClient) SetPrinterIsShared(printer string, shared bool) error { - req := NewRequest(OperationCupsAddModifyPrinter, 1) - req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer) - req.OperationAttributes[AttributePrinterIsShared] = shared - - _, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil) - return err -} - -// SetPrinterErrorPolicy sets the error policy for a printer -func (c *CUPSClient) SetPrinterErrorPolicy(printer string, errorPolicy string) error { - req := NewRequest(OperationCupsAddModifyPrinter, 1) - req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer) - req.PrinterAttributes[AttributePrinterErrorPolicy] = errorPolicy - - _, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil) - return err -} - -// SetPrinterInformation sets general printer information -func (c *CUPSClient) SetPrinterInformation(printer, information string) error { - req := NewRequest(OperationCupsAddModifyPrinter, 1) - req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer) - req.PrinterAttributes[AttributePrinterInfo] = information - - _, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil) - return err -} - -// SetPrinterLocation sets the printer location -func (c *CUPSClient) SetPrinterLocation(printer, location string) error { - req := NewRequest(OperationCupsAddModifyPrinter, 1) - req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer) - req.PrinterAttributes[AttributePrinterLocation] = location - - _, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil) - return err -} - -// DeletePrinter deletes a printer -func (c *CUPSClient) DeletePrinter(printer string) error { - req := NewRequest(OperationCupsDeletePrinter, 1) - req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer) - - _, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil) - return err -} - -// GetPrinters returns a map of printer names and attributes -func (c *CUPSClient) GetPrinters(attributes []string) (map[string]Attributes, error) { - req := NewRequest(OperationCupsGetPrinters, 1) - - if attributes == nil { - req.OperationAttributes[AttributeRequestedAttributes] = DefaultPrinterAttributes - } else { - req.OperationAttributes[AttributeRequestedAttributes] = append(attributes, AttributePrinterName) - } - - resp, err := c.SendRequest(c.adapter.GetHttpUri("", nil), req, nil) - if err != nil { - return nil, err - } - - printerNameMap := make(map[string]Attributes) - - for _, printerAttributes := range resp.PrinterAttributes { - printerNameMap[printerAttributes[AttributePrinterName][0].Value.(string)] = printerAttributes - } - - return printerNameMap, nil -} - -// GetClasses returns a map of class names and attributes -func (c *CUPSClient) GetClasses(attributes []string) (map[string]Attributes, error) { - req := NewRequest(OperationCupsGetClasses, 1) - - if attributes == nil { - req.OperationAttributes[AttributeRequestedAttributes] = DefaultClassAttributes - } else { - req.OperationAttributes[AttributeRequestedAttributes] = append(attributes, AttributePrinterName) - } - - resp, err := c.SendRequest(c.adapter.GetHttpUri("", nil), req, nil) - if err != nil { - return nil, err - } - - printerNameMap := make(map[string]Attributes) - - for _, printerAttributes := range resp.PrinterAttributes { - printerNameMap[printerAttributes[AttributePrinterName][0].Value.(string)] = printerAttributes - } - - return printerNameMap, nil -} - -// PrintTestPage prints a test page of type application/vnd.cups-pdf-banner -func (c *CUPSClient) PrintTestPage(printer string) (int, error) { - testPage := new(bytes.Buffer) - testPage.WriteString("#PDF-BANNER\n") - testPage.WriteString("Template default-testpage.pdf\n") - testPage.WriteString("Show printer-name printer-info printer-location printer-make-and-model printer-driver-name") - testPage.WriteString("printer-driver-version paper-size imageable-area job-id options time-at-creation") - testPage.WriteString("time-at-processing\n\n") - - return c.PrintDocuments([]Document{ - { - Document: testPage, - Name: "Test Page", - Size: testPage.Len(), - MimeType: MimeTypePostscript, - }, - }, printer, map[string]interface{}{ - AttributeJobName: "Test Page", - }) -} diff --git a/nix/inputs/dms-cli/pkg/ipp/error.go b/nix/inputs/dms-cli/pkg/ipp/error.go deleted file mode 100644 index e4edf5e..0000000 --- a/nix/inputs/dms-cli/pkg/ipp/error.go +++ /dev/null @@ -1,31 +0,0 @@ -package ipp - -import "fmt" - -// IsNotExistsError checks a given error whether a printer or class does not exist -func IsNotExistsError(err error) bool { - if err == nil { - return false - } - - return err.Error() == "The printer or class does not exist." -} - -// IPPError used for non ok ipp status codes -type IPPError struct { - Status int16 - Message string -} - -func (e IPPError) Error() string { - return fmt.Sprintf("ipp status: %d, message: %s", e.Status, e.Message) -} - -// HTTPError used for non 200 http codes -type HTTPError struct { - Code int -} - -func (e HTTPError) Error() string { - return fmt.Sprintf("got http code %d", e.Code) -} diff --git a/nix/inputs/dms-cli/pkg/ipp/ipp-client.go b/nix/inputs/dms-cli/pkg/ipp/ipp-client.go deleted file mode 100644 index 7a7cc53..0000000 --- a/nix/inputs/dms-cli/pkg/ipp/ipp-client.go +++ /dev/null @@ -1,329 +0,0 @@ -package ipp - -import ( - "errors" - "fmt" - "io" - "os" - "path" -) - -// Document wraps an io.Reader with more information, needed for encoding -type Document struct { - Document io.Reader - Size int - Name string - MimeType string -} - -// IPPClient implements a generic ipp client -type IPPClient struct { - username string - adapter Adapter -} - -// NewIPPClient creates a new generic ipp client (used HttpAdapter internally) -func NewIPPClient(host string, port int, username, password string, useTLS bool) *IPPClient { - adapter := NewHttpAdapter(host, port, username, password, useTLS) - - return &IPPClient{ - username: username, - adapter: adapter, - } -} - -// NewIPPClientWithAdapter creates a new generic ipp client with given Adapter -func NewIPPClientWithAdapter(username string, adapter Adapter) *IPPClient { - return &IPPClient{ - username: username, - adapter: adapter, - } -} - -func (c *IPPClient) getPrinterUri(printer string) string { - return fmt.Sprintf("ipp://localhost/printers/%s", printer) -} - -func (c *IPPClient) getJobUri(jobID int) string { - return fmt.Sprintf("ipp://localhost/jobs/%d", jobID) -} - -func (c *IPPClient) getClassUri(printer string) string { - return fmt.Sprintf("ipp://localhost/classes/%s", printer) -} - -// SendRequest sends a request to a remote uri end returns the response -func (c *IPPClient) SendRequest(url string, req *Request, additionalResponseData io.Writer) (*Response, error) { - if _, ok := req.OperationAttributes[AttributeRequestingUserName]; !ok { - req.OperationAttributes[AttributeRequestingUserName] = c.username - } - - return c.adapter.SendRequest(url, req, additionalResponseData) -} - -// PrintDocuments prints one or more documents using a Create-Job operation followed by one or more Send-Document operation(s). custom job settings can be specified via the jobAttributes parameter -func (c *IPPClient) PrintDocuments(docs []Document, printer string, jobAttributes map[string]interface{}) (int, error) { - printerURI := c.getPrinterUri(printer) - - req := NewRequest(OperationCreateJob, 1) - req.OperationAttributes[AttributePrinterURI] = printerURI - req.OperationAttributes[AttributeRequestingUserName] = c.username - - // set defaults for some attributes, may get overwritten - req.OperationAttributes[AttributeJobName] = docs[0].Name - req.OperationAttributes[AttributeCopies] = 1 - req.OperationAttributes[AttributeJobPriority] = DefaultJobPriority - - for key, value := range jobAttributes { - req.JobAttributes[key] = value - } - - resp, err := c.SendRequest(c.adapter.GetHttpUri("printers", printer), req, nil) - if err != nil { - return -1, err - } - - if len(resp.JobAttributes) == 0 { - return 0, errors.New("server doesn't returned a job id") - } - - jobID := resp.JobAttributes[0][AttributeJobID][0].Value.(int) - - documentCount := len(docs) - 1 - - for docID, doc := range docs { - req = NewRequest(OperationSendDocument, 2) - req.OperationAttributes[AttributePrinterURI] = printerURI - req.OperationAttributes[AttributeRequestingUserName] = c.username - req.OperationAttributes[AttributeJobID] = jobID - req.OperationAttributes[AttributeDocumentName] = doc.Name - req.OperationAttributes[AttributeDocumentFormat] = doc.MimeType - req.OperationAttributes[AttributeLastDocument] = docID == documentCount - req.File = doc.Document - req.FileSize = doc.Size - - _, err = c.SendRequest(c.adapter.GetHttpUri("printers", printer), req, nil) - if err != nil { - return -1, err - } - } - - return jobID, nil -} - -// PrintJob prints a document using a Print-Job operation. custom job settings can be specified via the jobAttributes parameter -func (c *IPPClient) PrintJob(doc Document, printer string, jobAttributes map[string]interface{}) (int, error) { - printerURI := c.getPrinterUri(printer) - - req := NewRequest(OperationPrintJob, 1) - req.OperationAttributes[AttributePrinterURI] = printerURI - req.OperationAttributes[AttributeRequestingUserName] = c.username - req.OperationAttributes[AttributeJobName] = doc.Name - req.OperationAttributes[AttributeDocumentFormat] = doc.MimeType - - // set defaults for some attributes, may get overwritten - req.OperationAttributes[AttributeCopies] = 1 - req.OperationAttributes[AttributeJobPriority] = DefaultJobPriority - - for key, value := range jobAttributes { - req.JobAttributes[key] = value - } - - req.File = doc.Document - req.FileSize = doc.Size - - resp, err := c.SendRequest(c.adapter.GetHttpUri("printers", printer), req, nil) - if err != nil { - return -1, err - } - - if len(resp.JobAttributes) == 0 { - return 0, errors.New("server doesn't returned a job id") - } - - jobID := resp.JobAttributes[0][AttributeJobID][0].Value.(int) - - return jobID, nil -} - -// PrintFile prints a local file on the file system. custom job settings can be specified via the jobAttributes parameter -func (c *IPPClient) PrintFile(filePath, printer string, jobAttributes map[string]interface{}) (int, error) { - fileStats, err := os.Stat(filePath) - if os.IsNotExist(err) { - return -1, err - } - - fileName := path.Base(filePath) - - document, err := os.Open(filePath) - if err != nil { - return 0, err - } - defer document.Close() - - jobAttributes[AttributeJobName] = fileName - - return c.PrintDocuments([]Document{ - { - Document: document, - Name: fileName, - Size: int(fileStats.Size()), - MimeType: MimeTypeOctetStream, - }, - }, printer, jobAttributes) -} - -// GetPrinterAttributes returns the requested attributes for the specified printer, if attributes is nil the default attributes will be used -func (c *IPPClient) GetPrinterAttributes(printer string, attributes []string) (Attributes, error) { - req := NewRequest(OperationGetPrinterAttributes, 1) - req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer) - req.OperationAttributes[AttributeRequestingUserName] = c.username - - if attributes == nil { - req.OperationAttributes[AttributeRequestedAttributes] = DefaultPrinterAttributes - } else { - req.OperationAttributes[AttributeRequestedAttributes] = attributes - } - - resp, err := c.SendRequest(c.adapter.GetHttpUri("printers", printer), req, nil) - if err != nil { - return nil, err - } - - if len(resp.PrinterAttributes) == 0 { - return nil, errors.New("server doesn't return any printer attributes") - } - - return resp.PrinterAttributes[0], nil -} - -// ResumePrinter resumes a printer -func (c *IPPClient) ResumePrinter(printer string) error { - req := NewRequest(OperationResumePrinter, 1) - req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer) - - _, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil) - return err -} - -// PausePrinter pauses a printer -func (c *IPPClient) PausePrinter(printer string) error { - req := NewRequest(OperationPausePrinter, 1) - req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer) - - _, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil) - return err -} - -// GetJobAttributes returns the requested attributes for the specified job, if attributes is nil the default job will be used -func (c *IPPClient) GetJobAttributes(jobID int, attributes []string) (Attributes, error) { - req := NewRequest(OperationGetJobAttributes, 1) - req.OperationAttributes[AttributeJobURI] = c.getJobUri(jobID) - - if attributes == nil { - req.OperationAttributes[AttributeRequestedAttributes] = DefaultJobAttributes - } else { - req.OperationAttributes[AttributeRequestedAttributes] = attributes - } - - resp, err := c.SendRequest(c.adapter.GetHttpUri("jobs", jobID), req, nil) - if err != nil { - return nil, err - } - - if len(resp.JobAttributes) == 0 { - return nil, errors.New("server doesn't return any job attributes") - } - - return resp.JobAttributes[0], nil -} - -// GetJobs returns jobs from a printer or class -func (c *IPPClient) GetJobs(printer, class string, whichJobs string, myJobs bool, firstJobId, limit int, attributes []string) (map[int]Attributes, error) { - req := NewRequest(OperationGetJobs, 1) - req.OperationAttributes[AttributeWhichJobs] = whichJobs - req.OperationAttributes[AttributeMyJobs] = myJobs - - if printer != "" { - req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer) - } else if class != "" { - req.OperationAttributes[AttributePrinterURI] = c.getClassUri(printer) - } else { - req.OperationAttributes[AttributePrinterURI] = "ipp://localhost/" - } - - if firstJobId > 0 { - req.OperationAttributes[AttributeFirstJobID] = firstJobId - } - - if limit > 0 { - req.OperationAttributes[AttributeLimit] = limit - } - - if myJobs { - req.OperationAttributes[AttributeRequestingUserName] = c.username - } - - if attributes == nil { - req.OperationAttributes[AttributeRequestedAttributes] = DefaultJobAttributes - } else { - req.OperationAttributes[AttributeRequestedAttributes] = append(attributes, AttributeJobID) - } - - resp, err := c.SendRequest(c.adapter.GetHttpUri("", nil), req, nil) - if err != nil { - return nil, err - } - - jobIDMap := make(map[int]Attributes) - - for _, jobAttributes := range resp.JobAttributes { - jobIDMap[jobAttributes[AttributeJobID][0].Value.(int)] = jobAttributes - } - - return jobIDMap, nil -} - -// CancelJob cancels a job. if purge is true, the job will also be removed -func (c *IPPClient) CancelJob(jobID int, purge bool) error { - req := NewRequest(OperationCancelJob, 1) - req.OperationAttributes[AttributeJobURI] = c.getJobUri(jobID) - req.OperationAttributes[AttributePurgeJobs] = purge - - _, err := c.SendRequest(c.adapter.GetHttpUri("jobs", ""), req, nil) - return err -} - -// CancelAllJob cancels all jobs for a specified printer. if purge is true, the jobs will also be removed -func (c *IPPClient) CancelAllJob(printer string, purge bool) error { - req := NewRequest(OperationCancelJobs, 1) - req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer) - req.OperationAttributes[AttributePurgeJobs] = purge - - _, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil) - return err -} - -// RestartJob restarts a job -func (c *IPPClient) RestartJob(jobID int) error { - req := NewRequest(OperationRestartJob, 1) - req.OperationAttributes[AttributeJobURI] = c.getJobUri(jobID) - - _, err := c.SendRequest(c.adapter.GetHttpUri("jobs", ""), req, nil) - return err -} - -// HoldJobUntil holds a job -func (c *IPPClient) HoldJobUntil(jobID int, holdUntil string) error { - req := NewRequest(OperationRestartJob, 1) - req.OperationAttributes[AttributeJobURI] = c.getJobUri(jobID) - req.JobAttributes[AttributeHoldJobUntil] = holdUntil - - _, err := c.SendRequest(c.adapter.GetHttpUri("jobs", ""), req, nil) - return err -} - -// TestConnection tests if a tcp connection to the remote server is possible -func (c *IPPClient) TestConnection() error { - return c.adapter.TestConnection() -} diff --git a/nix/inputs/dms-cli/pkg/ipp/request.go b/nix/inputs/dms-cli/pkg/ipp/request.go deleted file mode 100644 index 0c29655..0000000 --- a/nix/inputs/dms-cli/pkg/ipp/request.go +++ /dev/null @@ -1,299 +0,0 @@ -package ipp - -import ( - "bytes" - "encoding/binary" - "io" -) - -// Request defines a ipp request -type Request struct { - ProtocolVersionMajor int8 - ProtocolVersionMinor int8 - - Operation int16 - RequestId int32 - - OperationAttributes map[string]interface{} - JobAttributes map[string]interface{} - PrinterAttributes map[string]interface{} - SubscriptionAttributes map[string]interface{} // Added for subscription operations - - File io.Reader - FileSize int -} - -// NewRequest creates a new ipp request -func NewRequest(op int16, reqID int32) *Request { - return &Request{ - ProtocolVersionMajor: ProtocolVersionMajor, - ProtocolVersionMinor: ProtocolVersionMinor, - Operation: op, - RequestId: reqID, - OperationAttributes: make(map[string]interface{}), - JobAttributes: make(map[string]interface{}), - PrinterAttributes: make(map[string]interface{}), - SubscriptionAttributes: make(map[string]interface{}), - File: nil, - FileSize: -1, - } -} - -// Encode encodes the request to a byte slice -func (r *Request) Encode() ([]byte, error) { - buf := new(bytes.Buffer) - enc := NewAttributeEncoder(buf) - - if err := binary.Write(buf, binary.BigEndian, r.ProtocolVersionMajor); err != nil { - return nil, err - } - - if err := binary.Write(buf, binary.BigEndian, r.ProtocolVersionMinor); err != nil { - return nil, err - } - - if err := binary.Write(buf, binary.BigEndian, r.Operation); err != nil { - return nil, err - } - - if err := binary.Write(buf, binary.BigEndian, r.RequestId); err != nil { - return nil, err - } - - if err := binary.Write(buf, binary.BigEndian, TagOperation); err != nil { - return nil, err - } - - if r.OperationAttributes == nil { - r.OperationAttributes = make(map[string]interface{}, 2) - } - - if _, found := r.OperationAttributes[AttributeCharset]; !found { - r.OperationAttributes[AttributeCharset] = Charset - } - - if _, found := r.OperationAttributes[AttributeNaturalLanguage]; !found { - r.OperationAttributes[AttributeNaturalLanguage] = CharsetLanguage - } - - if err := r.encodeOperationAttributes(enc); err != nil { - return nil, err - } - - if len(r.JobAttributes) > 0 { - if err := binary.Write(buf, binary.BigEndian, TagJob); err != nil { - return nil, err - } - for attr, value := range r.JobAttributes { - if err := enc.Encode(attr, value); err != nil { - return nil, err - } - } - } - - if len(r.PrinterAttributes) > 0 { - if err := binary.Write(buf, binary.BigEndian, TagPrinter); err != nil { - return nil, err - } - for attr, value := range r.PrinterAttributes { - if err := enc.Encode(attr, value); err != nil { - return nil, err - } - } - } - - if len(r.SubscriptionAttributes) > 0 { - if err := binary.Write(buf, binary.BigEndian, TagSubscription); err != nil { - return nil, err - } - if err := r.encodeSubscriptionAttributes(enc); err != nil { - return nil, err - } - } - - if err := binary.Write(buf, binary.BigEndian, TagEnd); err != nil { - return nil, err - } - - return buf.Bytes(), nil -} - -func (r *Request) encodeOperationAttributes(enc *AttributeEncoder) error { - ordered := []string{ - AttributeCharset, - AttributeNaturalLanguage, - AttributePrinterURI, - AttributeJobID, - } - - for _, attr := range ordered { - if value, ok := r.OperationAttributes[attr]; ok { - delete(r.OperationAttributes, attr) - if err := enc.Encode(attr, value); err != nil { - return err - } - } - } - - for attr, value := range r.OperationAttributes { - if err := enc.Encode(attr, value); err != nil { - return err - } - } - - return nil -} - -func (r *Request) encodeSubscriptionAttributes(enc *AttributeEncoder) error { - // Encode subscription attributes in proper order - // notify-pull-method and notify-lease-duration must come before notify-events - ordered := []string{ - "notify-pull-method", - "notify-lease-duration", - "notify-events", - } - - for _, attr := range ordered { - if value, ok := r.SubscriptionAttributes[attr]; ok { - delete(r.SubscriptionAttributes, attr) - if err := enc.Encode(attr, value); err != nil { - return err - } - } - } - - // Encode any remaining subscription attributes - for attr, value := range r.SubscriptionAttributes { - if err := enc.Encode(attr, value); err != nil { - return err - } - } - - return nil -} - -// RequestDecoder reads and decodes a request from a stream -type RequestDecoder struct { - reader io.Reader -} - -// NewRequestDecoder returns a new decoder that reads from r -func NewRequestDecoder(r io.Reader) *RequestDecoder { - return &RequestDecoder{ - reader: r, - } -} - -// Decode decodes a ipp request into a request struct. additional data will be written to an io.Writer if data is not nil -func (d *RequestDecoder) Decode(data io.Writer) (*Request, error) { - req := new(Request) - - if err := binary.Read(d.reader, binary.BigEndian, &req.ProtocolVersionMajor); err != nil { - return nil, err - } - - if err := binary.Read(d.reader, binary.BigEndian, &req.ProtocolVersionMinor); err != nil { - return nil, err - } - - if err := binary.Read(d.reader, binary.BigEndian, &req.Operation); err != nil { - return nil, err - } - - if err := binary.Read(d.reader, binary.BigEndian, &req.RequestId); err != nil { - return nil, err - } - - startByteSlice := make([]byte, 1) - - tag := TagCupsInvalid - previousAttributeName := "" - tagSet := false - - attribDecoder := NewAttributeDecoder(d.reader) - - // decode attribute buffer - for { - if _, err := d.reader.Read(startByteSlice); err != nil { - // when we read from a stream, we may get an EOF if we want to read the end tag - // all data should be read and we can ignore the error - if err == io.EOF { - break - } - return nil, err - } - - startByte := int8(startByteSlice[0]) - - // check if attributes are completed - if startByte == TagEnd { - break - } - - if startByte == TagOperation { - if req.OperationAttributes == nil { - req.OperationAttributes = make(map[string]interface{}) - } - - tag = TagOperation - tagSet = true - - } - - if startByte == TagJob { - if req.JobAttributes == nil { - req.JobAttributes = make(map[string]interface{}) - } - tag = TagJob - tagSet = true - } - - if startByte == TagPrinter { - if req.PrinterAttributes == nil { - req.PrinterAttributes = make(map[string]interface{}) - } - tag = TagPrinter - tagSet = true - } - - if tagSet { - if _, err := d.reader.Read(startByteSlice); err != nil { - return nil, err - } - startByte = int8(startByteSlice[0]) - } - - attrib, err := attribDecoder.Decode(startByte) - if err != nil { - return nil, err - } - - if attrib.Name != "" { - appendAttributeToRequest(req, tag, attrib.Name, attrib.Value) - previousAttributeName = attrib.Name - } else { - appendAttributeToRequest(req, tag, previousAttributeName, attrib.Value) - } - - tagSet = false - } - - if data != nil { - if _, err := io.Copy(data, d.reader); err != nil { - return nil, err - } - } - - return req, nil -} - -func appendAttributeToRequest(req *Request, tag int8, name string, value interface{}) { - switch tag { - case TagOperation: - req.OperationAttributes[name] = value - case TagPrinter: - req.PrinterAttributes[name] = value - case TagJob: - req.JobAttributes[name] = value - } -} diff --git a/nix/inputs/dms-cli/pkg/ipp/response.go b/nix/inputs/dms-cli/pkg/ipp/response.go deleted file mode 100644 index 73e3566..0000000 --- a/nix/inputs/dms-cli/pkg/ipp/response.go +++ /dev/null @@ -1,383 +0,0 @@ -package ipp - -import ( - "bytes" - "encoding/binary" - "io" -) - -// Attributes is a wrapper for a set of attributes -type Attributes map[string][]Attribute - -// Response defines a ipp response -type Response struct { - ProtocolVersionMajor int8 - ProtocolVersionMinor int8 - - StatusCode int16 - RequestId int32 - - OperationAttributes Attributes - PrinterAttributes []Attributes - JobAttributes []Attributes - SubscriptionAttributes []Attributes // Added for subscription responses -} - -// CheckForErrors checks the status code and returns a error if it is not zero. it also returns the status message if provided by the server -func (r *Response) CheckForErrors() error { - if r.StatusCode != StatusOk { - err := IPPError{ - Status: r.StatusCode, - Message: "no status message returned", - } - - if len(r.OperationAttributes["status-message"]) > 0 { - err.Message = r.OperationAttributes["status-message"][0].Value.(string) - } - - return err - } - - return nil -} - -// NewResponse creates a new ipp response -func NewResponse(statusCode int16, reqID int32) *Response { - return &Response{ - ProtocolVersionMajor: ProtocolVersionMajor, - ProtocolVersionMinor: ProtocolVersionMinor, - StatusCode: statusCode, - RequestId: reqID, - OperationAttributes: make(Attributes), - PrinterAttributes: make([]Attributes, 0), - JobAttributes: make([]Attributes, 0), - } -} - -// Encode encodes the response to a byte slice -func (r *Response) Encode() ([]byte, error) { - buf := new(bytes.Buffer) - enc := NewAttributeEncoder(buf) - - if err := binary.Write(buf, binary.BigEndian, r.ProtocolVersionMajor); err != nil { - return nil, err - } - - if err := binary.Write(buf, binary.BigEndian, r.ProtocolVersionMinor); err != nil { - return nil, err - } - - if err := binary.Write(buf, binary.BigEndian, r.StatusCode); err != nil { - return nil, err - } - - if err := binary.Write(buf, binary.BigEndian, r.RequestId); err != nil { - return nil, err - } - - if err := binary.Write(buf, binary.BigEndian, TagOperation); err != nil { - return nil, err - } - - if r.OperationAttributes == nil { - r.OperationAttributes = make(Attributes, 0) - } - - if _, found := r.OperationAttributes[AttributeCharset]; !found { - r.OperationAttributes[AttributeCharset] = []Attribute{ - { - Value: Charset, - }, - } - } - - if _, found := r.OperationAttributes[AttributeNaturalLanguage]; !found { - r.OperationAttributes[AttributeNaturalLanguage] = []Attribute{ - { - Value: CharsetLanguage, - }, - } - } - - if err := r.encodeOperationAttributes(enc); err != nil { - return nil, err - } - - if len(r.PrinterAttributes) > 0 { - for _, printerAttr := range r.PrinterAttributes { - if err := binary.Write(buf, binary.BigEndian, TagPrinter); err != nil { - return nil, err - } - - for name, attr := range printerAttr { - if len(attr) == 0 { - continue - } - - values := make([]interface{}, len(attr)) - for i, v := range attr { - values[i] = v.Value - } - - if len(values) == 1 { - if err := enc.Encode(name, values[0]); err != nil { - return nil, err - } - } else { - if err := enc.Encode(name, values); err != nil { - return nil, err - } - } - } - } - } - - if len(r.JobAttributes) > 0 { - for _, jobAttr := range r.JobAttributes { - if err := binary.Write(buf, binary.BigEndian, TagJob); err != nil { - return nil, err - } - - for name, attr := range jobAttr { - if len(attr) == 0 { - continue - } - - values := make([]interface{}, len(attr)) - for i, v := range attr { - values[i] = v.Value - } - - if len(values) == 1 { - if err := enc.Encode(name, values[0]); err != nil { - return nil, err - } - } else { - if err := enc.Encode(name, values); err != nil { - return nil, err - } - } - } - } - } - - if err := binary.Write(buf, binary.BigEndian, TagEnd); err != nil { - return nil, err - } - - return buf.Bytes(), nil -} - -func (r *Response) encodeOperationAttributes(enc *AttributeEncoder) error { - ordered := []string{ - AttributeCharset, - AttributeNaturalLanguage, - AttributePrinterURI, - AttributeJobID, - } - - for _, name := range ordered { - if attr, ok := r.OperationAttributes[name]; ok { - delete(r.OperationAttributes, name) - if err := encodeOperationAttribute(enc, name, attr); err != nil { - return err - } - } - } - - for name, attr := range r.OperationAttributes { - if err := encodeOperationAttribute(enc, name, attr); err != nil { - return err - } - } - - return nil -} - -func encodeOperationAttribute(enc *AttributeEncoder, name string, attr []Attribute) error { - if len(attr) == 0 { - return nil - } - - values := make([]interface{}, len(attr)) - for i, v := range attr { - values[i] = v.Value - } - - if len(values) == 1 { - return enc.Encode(name, values[0]) - } - - return enc.Encode(name, values) -} - -// ResponseDecoder reads and decodes a response from a stream -type ResponseDecoder struct { - reader io.Reader -} - -// NewResponseDecoder returns a new decoder that reads from r -func NewResponseDecoder(r io.Reader) *ResponseDecoder { - return &ResponseDecoder{ - reader: r, - } -} - -// Decode decodes a ipp response into a response struct. additional data will be written to an io.Writer if data is not nil -func (d *ResponseDecoder) Decode(data io.Writer) (*Response, error) { - /* - 1 byte: Protocol Major Version - b - 1 byte: Protocol Minor Version - b - 2 byte: Status ID - h - 4 byte: Request ID - i - 1 byte: Operation Attribute Byte (\0x01) - N times: Attributes - 1 byte: Attribute End Byte (\0x03) - */ - - resp := new(Response) - - // wrap the reader so we have more functionality - // reader := bufio.NewReader(d.reader) - - if err := binary.Read(d.reader, binary.BigEndian, &resp.ProtocolVersionMajor); err != nil { - return nil, err - } - - if err := binary.Read(d.reader, binary.BigEndian, &resp.ProtocolVersionMinor); err != nil { - return nil, err - } - - if err := binary.Read(d.reader, binary.BigEndian, &resp.StatusCode); err != nil { - return nil, err - } - - if err := binary.Read(d.reader, binary.BigEndian, &resp.RequestId); err != nil { - return nil, err - } - - startByteSlice := make([]byte, 1) - - tag := TagCupsInvalid - previousAttributeName := "" - tempAttributes := make(Attributes) - tagSet := false - - attribDecoder := NewAttributeDecoder(d.reader) - - // decode attribute buffer - for { - if _, err := d.reader.Read(startByteSlice); err != nil { - // when we read from a stream, we may get an EOF if we want to read the end tag - // all data should be read and we can ignore the error - if err == io.EOF { - break - } - return nil, err - } - - startByte := int8(startByteSlice[0]) - - // check if attributes are completed - if startByte == TagEnd { - break - } - - if startByte == TagOperation { - if len(tempAttributes) > 0 && tag != TagCupsInvalid { - appendAttributeToResponse(resp, tag, tempAttributes) - tempAttributes = make(Attributes) - } - - tag = TagOperation - tagSet = true - } - - if startByte == TagJob { - if len(tempAttributes) > 0 && tag != TagCupsInvalid { - appendAttributeToResponse(resp, tag, tempAttributes) - tempAttributes = make(Attributes) - } - - tag = TagJob - tagSet = true - } - - if startByte == TagPrinter { - if len(tempAttributes) > 0 && tag != TagCupsInvalid { - appendAttributeToResponse(resp, tag, tempAttributes) - tempAttributes = make(Attributes) - } - - tag = TagPrinter - tagSet = true - } - - if startByte == TagSubscription { - if len(tempAttributes) > 0 && tag != TagCupsInvalid { - appendAttributeToResponse(resp, tag, tempAttributes) - tempAttributes = make(Attributes) - } - - tag = TagSubscription - tagSet = true - } - - if startByte == TagEventNotification { - if len(tempAttributes) > 0 && tag != TagCupsInvalid { - appendAttributeToResponse(resp, tag, tempAttributes) - tempAttributes = make(Attributes) - } - - tag = TagEventNotification - tagSet = true - } - - if tagSet { - if _, err := d.reader.Read(startByteSlice); err != nil { - return nil, err - } - startByte = int8(startByteSlice[0]) - } - - attrib, err := attribDecoder.Decode(startByte) - if err != nil { - return nil, err - } - - if attrib.Name != "" { - tempAttributes[attrib.Name] = append(tempAttributes[attrib.Name], *attrib) - previousAttributeName = attrib.Name - } else { - tempAttributes[previousAttributeName] = append(tempAttributes[previousAttributeName], *attrib) - } - - tagSet = false - } - - if len(tempAttributes) > 0 && tag != TagCupsInvalid { - appendAttributeToResponse(resp, tag, tempAttributes) - } - - if data != nil { - if _, err := io.Copy(data, d.reader); err != nil { - return nil, err - } - } - - return resp, nil -} - -func appendAttributeToResponse(resp *Response, tag int8, attr map[string][]Attribute) { - switch tag { - case TagOperation: - resp.OperationAttributes = attr - case TagPrinter: - resp.PrinterAttributes = append(resp.PrinterAttributes, attr) - case TagJob: - resp.JobAttributes = append(resp.JobAttributes, attr) - case TagSubscription, TagEventNotification: - // Both subscription and event notification attributes go to SubscriptionAttributes - resp.SubscriptionAttributes = append(resp.SubscriptionAttributes, attr) - } -} diff --git a/nix/inputs/dms-cli/pkg/ipp/utils.go b/nix/inputs/dms-cli/pkg/ipp/utils.go deleted file mode 100644 index 62689a3..0000000 --- a/nix/inputs/dms-cli/pkg/ipp/utils.go +++ /dev/null @@ -1,28 +0,0 @@ -package ipp - -import ( - "fmt" - "os" - "path" -) - -// ParseControlFile reads and decodes a cups control file into a response -func ParseControlFile(jobID int, spoolDirectory string) (*Response, error) { - if spoolDirectory == "" { - spoolDirectory = "/var/spool/cups" - } - - controlFilePath := path.Join(spoolDirectory, fmt.Sprintf("c%d", jobID)) - - if _, err := os.Stat(controlFilePath); err != nil { - return nil, err - } - - controlFile, err := os.Open(controlFilePath) - if err != nil { - return nil, err - } - defer controlFile.Close() - - return NewResponseDecoder(controlFile).Decode(nil) -} From 205d9476145bcc4ee98d49c176205d03b4dbb04d Mon Sep 17 00:00:00 2001 From: Ali Rizvi Date: Fri, 5 Dec 2025 23:21:29 -0500 Subject: [PATCH 02/10] fix: update select flake inputs I updated `nixpkgs`, `home-manager`, and `stylix`, mainly to stop the version mismatch warning. --- flake.lock | 60 +++++++++++++++++++++++++++--------------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/flake.lock b/flake.lock index a88204f..3537208 100644 --- a/flake.lock +++ b/flake.lock @@ -59,11 +59,11 @@ "base16-helix": { "flake": false, "locked": { - "lastModified": 1752979451, - "narHash": "sha256-0CQM+FkYy0fOO/sMGhOoNL80ftsAzYCg9VhIrodqusM=", + "lastModified": 1760703920, + "narHash": "sha256-m82fGUYns4uHd+ZTdoLX2vlHikzwzdu2s2rYM2bNwzw=", "owner": "tinted-theming", "repo": "base16-helix", - "rev": "27cf1e66e50abc622fb76a3019012dc07c678fac", + "rev": "d646af9b7d14bff08824538164af99d0c521b185", "type": "github" }, "original": { @@ -159,11 +159,11 @@ "firefox-gnome-theme": { "flake": false, "locked": { - "lastModified": 1758112371, - "narHash": "sha256-lizRM2pj6PHrR25yimjyFn04OS4wcdbc38DCdBVa2rk=", + "lastModified": 1764724327, + "narHash": "sha256-OkFLrD3pFR952TrjQi1+Vdj604KLcMnkpa7lkW7XskI=", "owner": "rafaelmardojai", "repo": "firefox-gnome-theme", - "rev": "0909cfe4a2af8d358ad13b20246a350e14c2473d", + "rev": "66b7c635763d8e6eb86bd766de5a1e1fbfcc1047", "type": "github" }, "original": { @@ -231,11 +231,11 @@ "flake": false, "locked": { "host": "gitlab.gnome.org", - "lastModified": 1762869044, - "narHash": "sha256-nwm/GJ2Syigf7VccLAZ66mFC8mZJFqpJmIxSGKl7+Ds=", + "lastModified": 1764524476, + "narHash": "sha256-bTmNn3Q4tMQ0J/P0O5BfTQwqEnCiQIzOGef9/aqAZvk=", "owner": "GNOME", "repo": "gnome-shell", - "rev": "680e3d195a92203f28d4bf8c6e8bb537cc3ed4ad", + "rev": "c0e1ad9f0f703fd0519033b8f46c3267aab51a22", "type": "gitlab" }, "original": { @@ -253,11 +253,11 @@ ] }, "locked": { - "lastModified": 1764636297, - "narHash": "sha256-S41K55kw+hWgDfgKmZ9/fMZ3F0BQDMvqFfE120fMHeE=", + "lastModified": 1764986695, + "narHash": "sha256-k+4uvvt3TisTVOwyH0135ztmBiPFk61bXNKebQBGkhU=", "owner": "nix-community", "repo": "home-manager", - "rev": "ff067cfc619fdf6f82d50344e7d19ff2323f0827", + "rev": "ccd22c13b2200263fb59342a34bf7119a31aa363", "type": "github" }, "original": { @@ -338,11 +338,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1764517877, - "narHash": "sha256-pp3uT4hHijIC8JUK5MEqeAWmParJrgBVzHLNfJDZxg4=", + "lastModified": 1764667669, + "narHash": "sha256-7WUCZfmqLAssbDqwg9cUDAXrSoXN79eEEq17qhTNM/Y=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "2d293cbfa5a793b4c50d17c05ef9e385b90edf6c", + "rev": "418468ac9527e799809c900eda37cbff999199b6", "type": "github" }, "original": { @@ -379,11 +379,11 @@ ] }, "locked": { - "lastModified": 1758998580, - "narHash": "sha256-VLx0z396gDCGSiowLMFz5XRO/XuNV+4EnDYjdJhHvUk=", + "lastModified": 1764773531, + "narHash": "sha256-mCBl7MD1WZ7yCG6bR9MmpPO2VydpNkWFgnslJRIT1YU=", "owner": "nix-community", "repo": "NUR", - "rev": "ba8d9c98f5f4630bcb0e815ab456afd90c930728", + "rev": "1d9616689e98beded059ad0384b9951e967a17fa", "type": "github" }, "original": { @@ -447,11 +447,11 @@ "tinted-zed": "tinted-zed" }, "locked": { - "lastModified": 1764648840, - "narHash": "sha256-hIj3aKL+G8TXrqvz0wCnUKESN5MEyrD4YxaLLARnkPs=", + "lastModified": 1764979509, + "narHash": "sha256-n68Io6VWMbUX4857RHqGOfH9MDdta7EX6OYn8e/m8sI=", "owner": "nix-community", "repo": "stylix", - "rev": "71054adbc3629404b2f02e8385aec07f87980a2f", + "rev": "3a332459f45b16c6df9d788e923f293a4c28d793", "type": "github" }, "original": { @@ -526,11 +526,11 @@ "tinted-schemes": { "flake": false, "locked": { - "lastModified": 1757716333, - "narHash": "sha256-d4km8W7w2zCUEmPAPUoLk1NlYrGODuVa3P7St+UrqkM=", + "lastModified": 1763914658, + "narHash": "sha256-Hju0WtMf3iForxtOwXqGp3Ynipo0EYx1AqMKLPp9BJw=", "owner": "tinted-theming", "repo": "schemes", - "rev": "317a5e10c35825a6c905d912e480dfe8e71c7559", + "rev": "0f6be815d258e435c9b137befe5ef4ff24bea32c", "type": "github" }, "original": { @@ -542,11 +542,11 @@ "tinted-tmux": { "flake": false, "locked": { - "lastModified": 1757811970, - "narHash": "sha256-n5ZJgmzGZXOD9pZdAl1OnBu3PIqD+X3vEBUGbTi4JiI=", + "lastModified": 1764465359, + "narHash": "sha256-lbSVPqLEk2SqMrnpvWuKYGCaAlfWFMA6MVmcOFJjdjE=", "owner": "tinted-theming", "repo": "tinted-tmux", - "rev": "d217ba31c846006e9e0ae70775b0ee0f00aa6b1e", + "rev": "edf89a780e239263cc691a987721f786ddc4f6aa", "type": "github" }, "original": { @@ -558,11 +558,11 @@ "tinted-zed": { "flake": false, "locked": { - "lastModified": 1757811247, - "narHash": "sha256-4EFOUyLj85NRL3OacHoLGEo0wjiRJzfsXtR4CZWAn6w=", + "lastModified": 1764464512, + "narHash": "sha256-rCD/pAhkMdCx6blsFwxIyvBJbPZZ1oL2sVFrH07lmqg=", "owner": "tinted-theming", "repo": "base16-zed", - "rev": "824fe0aacf82b3c26690d14e8d2cedd56e18404e", + "rev": "907dbba5fb8cf69ebfd90b00813418a412d0a29a", "type": "github" }, "original": { From 49b8a8967fc46c63bc074e5eeeef07efbb2d40d8 Mon Sep 17 00:00:00 2001 From: Ali Rizvi Date: Mon, 8 Dec 2025 23:36:02 -0500 Subject: [PATCH 03/10] fix: update flake.lock, migrate to new DMS package, update nix-benchmark DankMaterialShell changed their flake in a backwards incompatible way (again). This time, they've pinned quickshell as their own input, and also changed their package to `dms-shell`, which comprises both the Quickshell configuration as well as the CLI application (formerly known as `dankMaterialShell` and `dmsCli` respectively). --- flake.lock | 45 ++++++++++--------- flake.nix | 1 + .../home-manager/my/programs/dms/config.nix | 9 ++-- pkgs/nix-benchmark/nix/package.nix | 1 + 4 files changed, 30 insertions(+), 26 deletions(-) diff --git a/flake.lock b/flake.lock index 3537208..244c79f 100644 --- a/flake.lock +++ b/flake.lock @@ -5,14 +5,17 @@ "dgop": "dgop", "nixpkgs": [ "nixpkgs" + ], + "quickshell": [ + "quickshell" ] }, "locked": { - "lastModified": 1764650935, - "narHash": "sha256-WGNMbRgdVKaqhe2T/rP3b0GMre0IZuiRfNALiaKPznA=", + "lastModified": 1765253026, + "narHash": "sha256-w/Klh9GLQYvPlaVa7eOqmNskBG7N0fT8ukqO7/lvDn0=", "owner": "AvengeMedia", "repo": "DankMaterialShell", - "rev": "f96a2e2325a364bb347054cd2ac2de145bc417dd", + "rev": "ef9d28597b06588b7c16d277e0c3e5d13e944b98", "type": "github" }, "original": { @@ -122,11 +125,11 @@ ] }, "locked": { - "lastModified": 1762435535, - "narHash": "sha256-QhzRn7pYN35IFpKjjxJAj3GPJECuC+VLhoGem3ezycc=", + "lastModified": 1762835999, + "narHash": "sha256-UykYGrGFOFTmDpKTLNxj1wvd1gbDG4TkqLNSbV0TYwk=", "owner": "AvengeMedia", "repo": "dgop", - "rev": "6cf638dde818f9f8a2e26d0243179c43cb3458d7", + "rev": "799301991cd5dcea9b64245f9d500dcc76615653", "type": "github" }, "original": { @@ -253,11 +256,11 @@ ] }, "locked": { - "lastModified": 1764986695, - "narHash": "sha256-k+4uvvt3TisTVOwyH0135ztmBiPFk61bXNKebQBGkhU=", + "lastModified": 1765217760, + "narHash": "sha256-BVVyAodLcAD8KOtR3yCStBHSE0WAH/xQWH9f0qsxbmk=", "owner": "nix-community", "repo": "home-manager", - "rev": "ccd22c13b2200263fb59342a34bf7119a31aa363", + "rev": "e5b1f87841810fc24772bf4389f9793702000c9b", "type": "github" }, "original": { @@ -308,11 +311,11 @@ "treefmt-nix": "treefmt-nix" }, "locked": { - "lastModified": 1764645678, - "narHash": "sha256-cU6uR0UlWhzewbrcAnPMkIOQ7D09I6vEqI0vkBRwWss=", + "lastModified": 1765250566, + "narHash": "sha256-YMvogB39RKR7pFNd6SK6THiGYSAa2unoomlKMBmQiVA=", "owner": "nix-community", "repo": "nixos-facter", - "rev": "55a0bd7172c8a52960946db4504b89c58a8e8004", + "rev": "ff0fd2956fc0e760c4d75d9a7c191512159f0793", "type": "github" }, "original": { @@ -338,11 +341,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1764667669, - "narHash": "sha256-7WUCZfmqLAssbDqwg9cUDAXrSoXN79eEEq17qhTNM/Y=", + "lastModified": 1764950072, + "narHash": "sha256-BmPWzogsG2GsXZtlT+MTcAWeDK5hkbGRZTeZNW42fwA=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "418468ac9527e799809c900eda37cbff999199b6", + "rev": "f61125a668a320878494449750330ca58b78c557", "type": "github" }, "original": { @@ -399,11 +402,11 @@ ] }, "locked": { - "lastModified": 1764482797, - "narHash": "sha256-ynV90KoBrPe38YFlKAHtPFk4Ee3IANUsIFGxRaq7H/s=", + "lastModified": 1764663772, + "narHash": "sha256-sHqLmm0wAt3PC4vczJeBozI1/f4rv9yp3IjkClHDXDs=", "owner": "quickshell-mirror", "repo": "quickshell", - "rev": "d24e8e9736287d01ee73ef9d573d2bc316a62d5c", + "rev": "26531fc46ef17e9365b03770edd3fb9206fcb460", "type": "github" }, "original": { @@ -447,11 +450,11 @@ "tinted-zed": "tinted-zed" }, "locked": { - "lastModified": 1764979509, - "narHash": "sha256-n68Io6VWMbUX4857RHqGOfH9MDdta7EX6OYn8e/m8sI=", + "lastModified": 1765047449, + "narHash": "sha256-VQcqjJ2g0kT9TW4ENwA2HBQJzfbCUd5s1Wm3K+R2QZY=", "owner": "nix-community", "repo": "stylix", - "rev": "3a332459f45b16c6df9d788e923f293a4c28d793", + "rev": "bd00e01aab676aee88e6cc5c9238b4a5a7d6639a", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 42d6b83..3d12c8c 100644 --- a/flake.nix +++ b/flake.nix @@ -7,6 +7,7 @@ url = "github:AvengeMedia/DankMaterialShell"; inputs.nixpkgs.follows = "nixpkgs"; + inputs.quickshell.follows = "quickshell"; }; home-manager = { diff --git a/modules/home-manager/my/programs/dms/config.nix b/modules/home-manager/my/programs/dms/config.nix index 4db39d7..e382ef9 100644 --- a/modules/home-manager/my/programs/dms/config.nix +++ b/modules/home-manager/my/programs/dms/config.nix @@ -14,8 +14,7 @@ let system = pkgs.stdenv.hostPlatform.system; dgop = inputDms.inputs.dgop.packages.${system}.default; - dms = inputDms.packages.${system}.dankMaterialShell; - dms-cli = inputDms.packages.${system}.dmsCli; + dms-shell = inputDms.packages.${system}.dms-shell; quickshell = inputQs.packages.${system}.default; # DankMaterialShell's `wallpaperFillMode` option requires sentence casing @@ -32,7 +31,7 @@ in { config = lib.mkIf cfg.enable { home.packages = [ dgop - dms-cli + dms-shell ] ++ (with pkgs; [ # Needed for brightness functionality brightnessctl @@ -78,7 +77,7 @@ in { package = quickshell; configs = { - dms = "${dms}/etc/xdg/quickshell/dms"; + dms = "${dms-shell}/share/quickshell/dms"; }; }; @@ -105,7 +104,7 @@ in { "--dereference --no-preserve=all " + "${config.xdg.configHome}/DankMaterialShell/default-settings.json " + "${config.xdg.configHome}/DankMaterialShell/settings.json"; - ExecStart = "${lib.getExe dms-cli} run --session"; + ExecStart = "${lib.getExe dms-shell} run --session"; Restart = "on-failure"; }; diff --git a/pkgs/nix-benchmark/nix/package.nix b/pkgs/nix-benchmark/nix/package.nix index a4570c3..ffa6d8a 100644 --- a/pkgs/nix-benchmark/nix/package.nix +++ b/pkgs/nix-benchmark/nix/package.nix @@ -28,6 +28,7 @@ stdenvNoCC.mkDerivation { nixVersions.nix_2_32 nixVersions.git lixPackageSets.lix_2_93.lix + lixPackageSets.lix_2_94.lix lixPackageSets.git.lix ]); From 9fa0a1d4f1c0ca87090af4e67f9cc8abdc0fe2d7 Mon Sep 17 00:00:00 2001 From: Ali Rizvi Date: Tue, 9 Dec 2025 01:55:10 -0500 Subject: [PATCH 04/10] fix: improve Nix configuration for `nix-benchmark` Instead of attaching both possible options into one, I dynamically generate the options based on which Nix variant is being used. --- pkgs/nix-benchmark/src/nix-benchmark.sh | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/pkgs/nix-benchmark/src/nix-benchmark.sh b/pkgs/nix-benchmark/src/nix-benchmark.sh index de1b2c3..ee7c2f8 100755 --- a/pkgs/nix-benchmark/src/nix-benchmark.sh +++ b/pkgs/nix-benchmark/src/nix-benchmark.sh @@ -21,10 +21,6 @@ showUsage() { origArgs=("$@") nixBins=(@nixBins@) -# We use both `pipe-operator` and `pipe-operators` because Lix decided to be -# quirky and remove the 's' from the name, whilst every other Nix version still -# uses 'operators'. Extremly irritating change on Lix's end. -nixEvalArgs="eval --option eval-cache false --option extra-experimental-features 'pipe-operator pipe-operators' --raw" flakeRef= while [ "$#" -gt 0 ]; do @@ -56,8 +52,26 @@ fi benchmarkNixEval() { local nixBinary="$1" + local nixEvalArgs="eval --raw --option 'eval-cache' 'false' --option 'extra-experimental-features'" local name="$($nixBinary --version 2> /dev/null | head -n1)" + + # Both Nix and Lix support the pipe (`|>`) operator through an optional + # experimental feature toggle. However, they both use different names for + # the feature. Lix uses 'pipe-operator', whilst Nix uses 'pipe-operators'. + # + # Lix's reasoning is that their implementation of the operator differs from + # official Nix, and as a result should be disambiguated. In practice I have + # not actually seen much of a difference, but I'll take their word for it. + # + # In order to respect this difference, I append to the experimental feature + # depending on which version of Nix is being used for benchmarking. + if [[ "$name" == "nix (Lix, like Nix)"* ]]; then + nixEvalArgs="$nixEvalArgs 'pipe-operator'" + else + nixEvalArgs="$nixEvalArgs 'pipe-operators'" + fi + local cmd="$nixBinary $nixEvalArgs '$flakeRef.drvPath'" hyperfine --warmup 5 --runs 20 --command-name "$name" "$cmd" From cfec1984a626a7b1f4773e55110b93bf450ac20f Mon Sep 17 00:00:00 2001 From: Ali Rizvi Date: Tue, 9 Dec 2025 01:59:10 -0500 Subject: [PATCH 05/10] feat: significantly reduce the amount of runs done in `nix-benchmark` I feel like 20 runs is mildly excessive. It takes up a lot of CI time and from local testing, doesn't seem to really improve the accuracy of the final time measurement. At the very least, the accuracy is negligible compared to the runtime cost, so I'm dropping it by many factors. Warmup runs are going from 5 -> 4, and full runs are going from 20 -> 8. --- pkgs/nix-benchmark/src/nix-benchmark.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/nix-benchmark/src/nix-benchmark.sh b/pkgs/nix-benchmark/src/nix-benchmark.sh index ee7c2f8..f8fad1e 100755 --- a/pkgs/nix-benchmark/src/nix-benchmark.sh +++ b/pkgs/nix-benchmark/src/nix-benchmark.sh @@ -74,7 +74,7 @@ benchmarkNixEval() { local cmd="$nixBinary $nixEvalArgs '$flakeRef.drvPath'" - hyperfine --warmup 5 --runs 20 --command-name "$name" "$cmd" + hyperfine --warmup 4 --runs 8 --command-name "$name" "$cmd" } for bin in "${nixBins[@]}"; do From c6228fc49baae0552be26ba54b2aa69592df7f86 Mon Sep 17 00:00:00 2001 From: Ali Rizvi Date: Mon, 15 Dec 2025 12:30:38 -0500 Subject: [PATCH 06/10] feat: add Polychromatic to control my Razer devices This also adds the OpenRazer daemon, as that is the backend that Polychromatic communicates with. --- hosts/laptop/configuration.nix | 3 +++ .../hardware/openrazer/config/home-manager.nix | 15 +++++++++++++++ users/frontear/nixos/default.nix | 5 +++++ 3 files changed, 23 insertions(+) create mode 100644 modules/shared/hardware/openrazer/config/home-manager.nix diff --git a/hosts/laptop/configuration.nix b/hosts/laptop/configuration.nix index 9d28af3..39d2e76 100644 --- a/hosts/laptop/configuration.nix +++ b/hosts/laptop/configuration.nix @@ -14,6 +14,9 @@ # Use the latest xanmod kernel, mainly for the Clear Linux patches boot.kernelPackages = pkgs.linuxPackages_xanmod_latest; + # TODO: detect from `facter.json`? + hardware.openrazer.enable = true; + services = { # NTP daemon that's more suitable for laptops chrony.enable = true; diff --git a/modules/shared/hardware/openrazer/config/home-manager.nix b/modules/shared/hardware/openrazer/config/home-manager.nix new file mode 100644 index 0000000..285d4bb --- /dev/null +++ b/modules/shared/hardware/openrazer/config/home-manager.nix @@ -0,0 +1,15 @@ +{ + nixosConfig, + lib, + pkgs, + ... +}: +let + cfg = nixosConfig.hardware.openrazer; +in { + config = lib.mkIf cfg.enable { + home.packages = with pkgs; [ + polychromatic + ]; + }; +} \ No newline at end of file diff --git a/users/frontear/nixos/default.nix b/users/frontear/nixos/default.nix index 5229319..1afd6fb 100644 --- a/users/frontear/nixos/default.nix +++ b/users/frontear/nixos/default.nix @@ -16,5 +16,10 @@ extraGroups = [ "wheel" ] ++ (lib.optional config.networking.networkmanager.enable "networkmanager"); }; + + # Allow my user to control the OpenRazer daemon. + hardware.openrazer.users = [ + "frontear" + ]; }; } \ No newline at end of file From 6f47bd8973e813876240ef51ea67e847e8edde8b Mon Sep 17 00:00:00 2001 From: Ali Rizvi Date: Mon, 15 Dec 2025 12:55:15 -0500 Subject: [PATCH 07/10] fix: persist OpenRazer and Polychromatic paths The most important of these is the Polychromatic cache, and the OpenRazer config. I'm unsure if I really care much about the Polychromatic config, but for now I'm keeping it. --- .../openrazer/config/home-manager.nix | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/modules/shared/hardware/openrazer/config/home-manager.nix b/modules/shared/hardware/openrazer/config/home-manager.nix index 285d4bb..398e6d6 100644 --- a/modules/shared/hardware/openrazer/config/home-manager.nix +++ b/modules/shared/hardware/openrazer/config/home-manager.nix @@ -11,5 +11,28 @@ in { home.packages = with pkgs; [ polychromatic ]; + + # NOTE: I am not saving `~/.local/share/openrazer` because this only + # contains logs. Logs are not necessary to see unless there's a problem, + # and I can look at them during the problematic run. + my.persist.directories = [ + { + # Stores the downloaded images of Razer devices for the UI. + path = "~/.cache/polychromatic"; + unique = false; + } + { + # DPI and polling rate are saved here, in `persistence.conf`. + path = "~/.config/openrazer"; + unique = true; + } + { + # Various UI settings, and the DPI stages (I think?). + # + # TODO: is this necessary to store? + path = "~/.config/polychromatic"; + unique = true; + } + ]; }; } \ No newline at end of file From 62521625a38f33fbd99febfec527737776354ed5 Mon Sep 17 00:00:00 2001 From: Ali Rizvi Date: Mon, 15 Dec 2025 15:06:31 -0500 Subject: [PATCH 08/10] fix: disable mouse acceleration in Niri --- users/frontear/home/desktops/niri/default.nix | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/users/frontear/home/desktops/niri/default.nix b/users/frontear/home/desktops/niri/default.nix index 7e213d2..b53e2e5 100644 --- a/users/frontear/home/desktops/niri/default.nix +++ b/users/frontear/home/desktops/niri/default.nix @@ -26,6 +26,12 @@ in { repeat-rate 25 } + mouse { + accel-profile "flat" + // scroll-button 274 // found with `libinput debug-events` + // scroll-method "on-button-down" + } + touchpad { accel-profile "adaptive" click-method "clickfinger" From 7bc304864e449c3f335d8f4776bae12eaf71a6fc Mon Sep 17 00:00:00 2001 From: Ali Rizvi Date: Tue, 16 Dec 2025 21:57:52 -0500 Subject: [PATCH 09/10] fix: drop the TODO for OpenRazer detection Doesn't make much sense, since peripherals can be swapped at any time. --- hosts/laptop/configuration.nix | 1 - 1 file changed, 1 deletion(-) diff --git a/hosts/laptop/configuration.nix b/hosts/laptop/configuration.nix index 39d2e76..3d9b151 100644 --- a/hosts/laptop/configuration.nix +++ b/hosts/laptop/configuration.nix @@ -14,7 +14,6 @@ # Use the latest xanmod kernel, mainly for the Clear Linux patches boot.kernelPackages = pkgs.linuxPackages_xanmod_latest; - # TODO: detect from `facter.json`? hardware.openrazer.enable = true; services = { From ff7cfd765ed54fff5b2fdc8c76a939629bee1dd0 Mon Sep 17 00:00:00 2001 From: Ali Rizvi Date: Tue, 16 Dec 2025 22:28:29 -0500 Subject: [PATCH 10/10] feat: bump `flake.lock` --- flake.lock | 56 +++++++++++++++++++++++++++--------------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/flake.lock b/flake.lock index 244c79f..79c6345 100644 --- a/flake.lock +++ b/flake.lock @@ -11,11 +11,11 @@ ] }, "locked": { - "lastModified": 1765253026, - "narHash": "sha256-w/Klh9GLQYvPlaVa7eOqmNskBG7N0fT8ukqO7/lvDn0=", + "lastModified": 1765916864, + "narHash": "sha256-mXKYRVK5YndrvgbIKCyz4BRuLkyEqgceF/djXmA6cD8=", "owner": "AvengeMedia", "repo": "DankMaterialShell", - "rev": "ef9d28597b06588b7c16d277e0c3e5d13e944b98", + "rev": "672754b0b5efd9e61ea8080c40614ad3b4fd5dbf", "type": "github" }, "original": { @@ -45,17 +45,17 @@ "base16-fish": { "flake": false, "locked": { - "lastModified": 1754405784, - "narHash": "sha256-l9xHIy+85FN+bEo6yquq2IjD1rSg9fjfjpyGP1W8YXo=", + "lastModified": 1765809053, + "narHash": "sha256-XCUQLoLfBJ8saWms2HCIj4NEN+xNsWBlU1NrEPcQG4s=", "owner": "tomyun", "repo": "base16-fish", - "rev": "23ae20a0093dca0d7b39d76ba2401af0ccf9c561", + "rev": "86cbea4dca62e08fb7fd83a70e96472f92574782", "type": "github" }, "original": { "owner": "tomyun", "repo": "base16-fish", - "rev": "23ae20a0093dca0d7b39d76ba2401af0ccf9c561", + "rev": "86cbea4dca62e08fb7fd83a70e96472f92574782", "type": "github" } }, @@ -180,11 +180,11 @@ "nixpkgs-lib": "nixpkgs-lib" }, "locked": { - "lastModified": 1763759067, - "narHash": "sha256-LlLt2Jo/gMNYAwOgdRQBrsRoOz7BPRkzvNaI/fzXi2Q=", + "lastModified": 1765835352, + "narHash": "sha256-XswHlK/Qtjasvhd1nOa1e8MgZ8GS//jBoTqWtrS1Giw=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "2cccadc7357c0ba201788ae99c4dfa90728ef5e0", + "rev": "a34fae9c08a15ad73f295041fec82323541400a9", "type": "github" }, "original": { @@ -256,11 +256,11 @@ ] }, "locked": { - "lastModified": 1765217760, - "narHash": "sha256-BVVyAodLcAD8KOtR3yCStBHSE0WAH/xQWH9f0qsxbmk=", + "lastModified": 1765860045, + "narHash": "sha256-7Lxp/PfOy4h3QIDtmWG/EgycaswqRSkDX4DGtet14NE=", "owner": "nix-community", "repo": "home-manager", - "rev": "e5b1f87841810fc24772bf4389f9793702000c9b", + "rev": "09de9577d47d8bffb11c449b6a3d24e32ac16c99", "type": "github" }, "original": { @@ -311,11 +311,11 @@ "treefmt-nix": "treefmt-nix" }, "locked": { - "lastModified": 1765250566, - "narHash": "sha256-YMvogB39RKR7pFNd6SK6THiGYSAa2unoomlKMBmQiVA=", + "lastModified": 1765855572, + "narHash": "sha256-FJLnGdUnJWp7H3cJN9Kycv1XIUQKj+LgHFCv2EKQdN8=", "owner": "nix-community", "repo": "nixos-facter", - "rev": "ff0fd2956fc0e760c4d75d9a7c191512159f0793", + "rev": "56e45023f7daae906362496a99ad2e62e0fd127a", "type": "github" }, "original": { @@ -326,11 +326,11 @@ }, "nixos-facter-modules": { "locked": { - "lastModified": 1764252389, - "narHash": "sha256-3bbuneTKZBkYXlm0bE36kUjiDsasoIC1GWBw/UEJ9T4=", + "lastModified": 1765442039, + "narHash": "sha256-k3lYQ+A1F7aTz8HnlU++bd9t/x/NP2A4v9+x6opcVg0=", "owner": "nix-community", "repo": "nixos-facter-modules", - "rev": "5ea68886d95218646d11d3551a476d458df00778", + "rev": "9dd775ee92de63f14edd021d59416e18ac2c00f1", "type": "github" }, "original": { @@ -341,11 +341,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1764950072, - "narHash": "sha256-BmPWzogsG2GsXZtlT+MTcAWeDK5hkbGRZTeZNW42fwA=", + "lastModified": 1765779637, + "narHash": "sha256-KJ2wa/BLSrTqDjbfyNx70ov/HdgNBCBBSQP3BIzKnv4=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "f61125a668a320878494449750330ca58b78c557", + "rev": "1306659b587dc277866c7b69eb97e5f07864d8c4", "type": "github" }, "original": { @@ -357,11 +357,11 @@ }, "nixpkgs-lib": { "locked": { - "lastModified": 1761765539, - "narHash": "sha256-b0yj6kfvO8ApcSE+QmA6mUfu8IYG6/uU28OFn4PaC8M=", + "lastModified": 1765674936, + "narHash": "sha256-k00uTP4JNfmejrCLJOwdObYC9jHRrr/5M/a/8L2EIdo=", "owner": "nix-community", "repo": "nixpkgs.lib", - "rev": "719359f4562934ae99f5443f20aa06c2ffff91fc", + "rev": "2075416fcb47225d9b68ac469a5c4801a9c4dd85", "type": "github" }, "original": { @@ -450,11 +450,11 @@ "tinted-zed": "tinted-zed" }, "locked": { - "lastModified": 1765047449, - "narHash": "sha256-VQcqjJ2g0kT9TW4ENwA2HBQJzfbCUd5s1Wm3K+R2QZY=", + "lastModified": 1765897595, + "narHash": "sha256-NgTRxiEC5y96zrhdBygnY+mSzk5FWMML39PcRGVJmxg=", "owner": "nix-community", "repo": "stylix", - "rev": "bd00e01aab676aee88e6cc5c9238b4a5a7d6639a", + "rev": "e6829552d4bb659ebab00f08c61d8c62754763f3", "type": "github" }, "original": {