From 5ebf5b6189aabb729a79d1396132abea963ee269 Mon Sep 17 00:00:00 2001 From: Qubasa Date: Thu, 10 Jul 2025 14:19:19 +0700 Subject: [PATCH] clan-app: Implement open_clan_folder api request --- pkgs/clan-app/clan_app/api/file_gtk.py | 47 +++++++++++++++++++++++++ pkgs/clan-cli/clan_lib/api/directory.py | 11 ++++++ pkgs/clan-cli/clan_lib/clan/check.py | 37 +++++++++++++++++++ 3 files changed, 95 insertions(+) create mode 100644 pkgs/clan-cli/clan_lib/clan/check.py diff --git a/pkgs/clan-app/clan_app/api/file_gtk.py b/pkgs/clan-app/clan_app/api/file_gtk.py index 73f458e50..c7a77bc25 100644 --- a/pkgs/clan-app/clan_app/api/file_gtk.py +++ b/pkgs/clan-app/clan_app/api/file_gtk.py @@ -9,6 +9,8 @@ gi.require_version("Gtk", "4.0") from clan_lib.api import ApiError, ErrorDataClass, SuccessDataClass from clan_lib.api.directory import FileRequest +from clan_lib.clan.check import check_clan_valid +from clan_lib.flake import Flake from gi.repository import Gio, GLib, Gtk gi.require_version("Gtk", "4.0") @@ -22,6 +24,51 @@ def remove_none(_list: list) -> list: RESULT: dict[str, SuccessDataClass[list[str] | None] | ErrorDataClass] = {} +def open_clan_folder(*, op_key: str) -> SuccessDataClass[Flake] | ErrorDataClass: + """ + Opens the clan folder using the GTK file dialog. + Returns the path to the clan folder or an error if it fails. + """ + file_request = FileRequest( + mode="select_folder", + title="Select Clan Folder", + initial_folder=str(Path.home()), + ) + response = open_file(file_request, op_key=op_key) + + if isinstance(response, ErrorDataClass): + return response + + if not response.data or len(response.data) == 0: + return ErrorDataClass( + op_key=op_key, + status="error", + errors=[ + ApiError( + message="No folder selected", + description="You must select a folder to open.", + location=["open_clan_folder"], + ) + ], + ) + + clan_folder = Flake(response.data[0]) + if not check_clan_valid(clan_folder): + return ErrorDataClass( + op_key=op_key, + status="error", + errors=[ + ApiError( + message="Invalid clan folder", + description=f"The selected folder '{clan_folder}' is not a valid clan folder.", + location=["open_clan_folder"], + ) + ], + ) + + return SuccessDataClass(op_key=op_key, data=clan_folder, status="success") + + def open_file( file_request: FileRequest, *, op_key: str ) -> SuccessDataClass[list[str] | None] | ErrorDataClass: diff --git a/pkgs/clan-cli/clan_lib/api/directory.py b/pkgs/clan-cli/clan_lib/api/directory.py index 8097bb906..bcbbb57af 100644 --- a/pkgs/clan-cli/clan_lib/api/directory.py +++ b/pkgs/clan-cli/clan_lib/api/directory.py @@ -3,6 +3,7 @@ from dataclasses import dataclass, field from typing import Any, Literal from clan_lib.cmd import RunOpts, run +from clan_lib.flake import Flake from clan_lib.nix import nix_shell from . import API @@ -38,6 +39,16 @@ def open_file(file_request: FileRequest) -> list[str] | None: raise NotImplementedError(msg) +@API.register_abstract +def open_clan_folder() -> Flake: + """ + Abstract api method to open the clan folder. + It must return the path to the clan folder. + """ + msg = "open_clan_folder() is not implemented" + raise NotImplementedError(msg) + + @dataclass class BlkInfo: name: str diff --git a/pkgs/clan-cli/clan_lib/clan/check.py b/pkgs/clan-cli/clan_lib/clan/check.py new file mode 100644 index 000000000..26f074054 --- /dev/null +++ b/pkgs/clan-cli/clan_lib/clan/check.py @@ -0,0 +1,37 @@ +import logging + +from clan_lib.api import API +from clan_lib.errors import ClanError +from clan_lib.flake import Flake + +log = logging.getLogger(__name__) + + +@API.register +def check_clan_valid(flake: Flake) -> bool: + """Check if a clan is valid by verifying if it has the clanInternals attribute. + Args: + flake: The Flake instance representing the clan. + Returns: + bool: True if the clan exists, False otherwise. + """ + try: + flake.prefetch() + except ClanError as e: + msg = f"Flake {flake} is not valid: {e}" + log.info(msg) + return False + + if flake.is_local and not flake.path.exists(): + msg = f"Path {flake} does not exist" + log.info(msg) + return False + + try: + flake.select("clanInternals.inventoryClass.directory") + except ClanError as e: + msg = f"Flake {flake} is not a valid clan directory: {e}" + log.info(msg) + return False + + return True