diff --git a/flake.lock b/flake.lock index bd5ce21..79c6345 100644 --- a/flake.lock +++ b/flake.lock @@ -3,19 +3,19 @@ "DankMaterialShell": { "inputs": { "dgop": "dgop", - "dms-cli": [ - "dms-cli" - ], "nixpkgs": [ "nixpkgs" + ], + "quickshell": [ + "quickshell" ] }, "locked": { - "lastModified": 1762812757, - "narHash": "sha256-VPmzq5tYJIwIV9LQyn+wCiNRHiVHO8wrqoM7pz6LVPs=", + "lastModified": 1765916864, + "narHash": "sha256-mXKYRVK5YndrvgbIKCyz4BRuLkyEqgceF/djXmA6cD8=", "owner": "AvengeMedia", "repo": "DankMaterialShell", - "rev": "37a10bd453da057fb4e69cf600c413eb4467bd72", + "rev": "672754b0b5efd9e61ea8080c40614ad3b4fd5dbf", "type": "github" }, "original": { @@ -45,28 +45,28 @@ "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" } }, "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": { @@ -104,11 +104,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": { @@ -125,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": { @@ -159,31 +159,14 @@ "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": { - "lastModified": 1758112371, - "narHash": "sha256-lizRM2pj6PHrR25yimjyFn04OS4wcdbc38DCdBVa2rk=", + "lastModified": 1764724327, + "narHash": "sha256-OkFLrD3pFR952TrjQi1+Vdj604KLcMnkpa7lkW7XskI=", "owner": "rafaelmardojai", "repo": "firefox-gnome-theme", - "rev": "0909cfe4a2af8d358ad13b20246a350e14c2473d", + "rev": "66b7c635763d8e6eb86bd766de5a1e1fbfcc1047", "type": "github" }, "original": { @@ -197,11 +180,11 @@ "nixpkgs-lib": "nixpkgs-lib" }, "locked": { - "lastModified": 1762440070, - "narHash": "sha256-xxdepIcb39UJ94+YydGP221rjnpkDZUlykKuF54PsqI=", + "lastModified": 1765835352, + "narHash": "sha256-XswHlK/Qtjasvhd1nOa1e8MgZ8GS//jBoTqWtrS1Giw=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "26d05891e14c88eb4a5d5bee659c0db5afb609d8", + "rev": "a34fae9c08a15ad73f295041fec82323541400a9", "type": "github" }, "original": { @@ -250,18 +233,20 @@ "gnome-shell": { "flake": false, "locked": { - "lastModified": 1748186689, - "narHash": "sha256-UaD7Y9f8iuLBMGHXeJlRu6U1Ggw5B9JnkFs3enZlap0=", + "host": "gitlab.gnome.org", + "lastModified": 1764524476, + "narHash": "sha256-bTmNn3Q4tMQ0J/P0O5BfTQwqEnCiQIzOGef9/aqAZvk=", "owner": "GNOME", "repo": "gnome-shell", - "rev": "8c88f917db0f1f0d80fa55206c863d3746fa18d0", - "type": "github" + "rev": "c0e1ad9f0f703fd0519033b8f46c3267aab51a22", + "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 +256,11 @@ ] }, "locked": { - "lastModified": 1762787259, - "narHash": "sha256-t2U/GLLXHa2+kJkwnFNRVc2fEJ/lUfyZXBE5iKzJdcs=", + "lastModified": 1765860045, + "narHash": "sha256-7Lxp/PfOy4h3QIDtmWG/EgycaswqRSkDX4DGtet14NE=", "owner": "nix-community", "repo": "home-manager", - "rev": "37a3d97f2873e0f68711117c34d04b7c7ead8f4e", + "rev": "09de9577d47d8bffb11c449b6a3d24e32ac16c99", "type": "github" }, "original": { @@ -326,11 +311,11 @@ "treefmt-nix": "treefmt-nix" }, "locked": { - "lastModified": 1762744937, - "narHash": "sha256-37twpZntKS1NOPDSULAyaUPMCyrvw2vhcyYd1bMT40E=", + "lastModified": 1765855572, + "narHash": "sha256-FJLnGdUnJWp7H3cJN9Kycv1XIUQKj+LgHFCv2EKQdN8=", "owner": "nix-community", "repo": "nixos-facter", - "rev": "bfc460a1df6056f97152252bc45c3668b584e068", + "rev": "56e45023f7daae906362496a99ad2e62e0fd127a", "type": "github" }, "original": { @@ -341,11 +326,11 @@ }, "nixos-facter-modules": { "locked": { - "lastModified": 1762264948, - "narHash": "sha256-iaRf6n0KPl9hndnIft3blm1YTAyxSREV1oX0MFZ6Tk4=", + "lastModified": 1765442039, + "narHash": "sha256-k3lYQ+A1F7aTz8HnlU++bd9t/x/NP2A4v9+x6opcVg0=", "owner": "nix-community", "repo": "nixos-facter-modules", - "rev": "fa695bff9ec37fd5bbd7ee3181dbeb5f97f53c96", + "rev": "9dd775ee92de63f14edd021d59416e18ac2c00f1", "type": "github" }, "original": { @@ -356,11 +341,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1762596750, - "narHash": "sha256-rXXuz51Bq7DHBlfIjN7jO8Bu3du5TV+3DSADBX7/9YQ=", + "lastModified": 1765779637, + "narHash": "sha256-KJ2wa/BLSrTqDjbfyNx70ov/HdgNBCBBSQP3BIzKnv4=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "b6a8526db03f735b89dd5ff348f53f752e7ddc8e", + "rev": "1306659b587dc277866c7b69eb97e5f07864d8c4", "type": "github" }, "original": { @@ -372,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": { @@ -397,11 +382,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": { @@ -417,11 +402,11 @@ ] }, "locked": { - "lastModified": 1761897390, - "narHash": "sha256-er4gYrIoThYLjlsOMTysoRfn67d1Gci+ZpqDrtQxrA0=", + "lastModified": 1764663772, + "narHash": "sha256-sHqLmm0wAt3PC4vczJeBozI1/f4rv9yp3IjkClHDXDs=", "owner": "quickshell-mirror", "repo": "quickshell", - "rev": "fc704e6b5d445899a1565955268c91942a4f263f", + "rev": "26531fc46ef17e9365b03770edd3fb9206fcb460", "type": "github" }, "original": { @@ -433,7 +418,6 @@ "root": { "inputs": { "DankMaterialShell": "DankMaterialShell", - "dms-cli": "dms-cli", "flake-parts": "flake-parts", "home-manager": "home-manager", "nixos-facter": "nixos-facter", @@ -466,11 +450,11 @@ "tinted-zed": "tinted-zed" }, "locked": { - "lastModified": 1762264356, - "narHash": "sha256-QVfC53Ri+8n3e7Ujx9kq6all3+TLBRRPRnc6No5qY5w=", + "lastModified": 1765897595, + "narHash": "sha256-NgTRxiEC5y96zrhdBygnY+mSzk5FWMML39PcRGVJmxg=", "owner": "nix-community", "repo": "stylix", - "rev": "647bb8dd96a206a1b79c4fd714affc88b409e10b", + "rev": "e6829552d4bb659ebab00f08c61d8c62754763f3", "type": "github" }, "original": { @@ -545,11 +529,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": { @@ -561,11 +545,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": { @@ -577,11 +561,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": { @@ -598,11 +582,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..3d12c8c 100644 --- a/flake.nix +++ b/flake.nix @@ -6,16 +6,8 @@ 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"; + inputs.quickshell.follows = "quickshell"; }; home-manager = { diff --git a/hosts/laptop/configuration.nix b/hosts/laptop/configuration.nix index 9d28af3..3d9b151 100644 --- a/hosts/laptop/configuration.nix +++ b/hosts/laptop/configuration.nix @@ -14,6 +14,8 @@ # Use the latest xanmod kernel, mainly for the Clear Linux patches boot.kernelPackages = pkgs.linuxPackages_xanmod_latest; + hardware.openrazer.enable = true; + services = { # NTP daemon that's more suitable for laptops chrony.enable = true; diff --git a/modules/home-manager/my/programs/dms/config.nix b/modules/home-manager/my/programs/dms/config.nix index efd9b8a..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}.default; - dms-cli = inputDms.inputs.dms-cli.packages.${system}.default; + 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"; + ExecStart = "${lib.getExe dms-shell} run --session"; Restart = "on-failure"; }; 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..398e6d6 --- /dev/null +++ b/modules/shared/hardware/openrazer/config/home-manager.nix @@ -0,0 +1,38 @@ +{ + nixosConfig, + lib, + pkgs, + ... +}: +let + cfg = nixosConfig.hardware.openrazer; +in { + config = lib.mkIf cfg.enable { + 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 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) -} 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 ]); diff --git a/pkgs/nix-benchmark/src/nix-benchmark.sh b/pkgs/nix-benchmark/src/nix-benchmark.sh index de1b2c3..f8fad1e 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,11 +52,29 @@ 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" + hyperfine --warmup 4 --runs 8 --command-name "$name" "$cmd" } for bin in "${nixBins[@]}"; do 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" 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