Coverage for src/secchi/workspace/state.py: 100%

24 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-08-04 22:15 +0000

1"""Pure state transitions for workspace navigation and lazy loading.""" 

2 

3from __future__ import annotations 

4 

5from dataclasses import dataclass, field 

6 

7from secchi.models import PackageRef 

8 

9 

10@dataclass 

11class WorkspaceState: 

12 """Track selection and per-project loading without depending on Textual.""" 

13 

14 selected_ref: PackageRef | None = None 

15 loaded_projects: set[str] = field(default_factory=set) 

16 loading_projects: set[str] = field(default_factory=set) 

17 

18 def select(self, ref: PackageRef) -> None: 

19 self.selected_ref = ref 

20 

21 def should_load(self, project_name: str, *, force: bool = False) -> bool: 

22 """Return whether a project fetch may start now.""" 

23 if project_name in self.loading_projects: 

24 return False 

25 return force or project_name not in self.loaded_projects 

26 

27 def begin_load(self, project_name: str, *, force: bool = False) -> bool: 

28 """Mark a project as loading, returning false when it should be skipped.""" 

29 if not self.should_load(project_name, force=force): 

30 return False 

31 self.loading_projects.add(project_name) 

32 return True 

33 

34 def finish_load(self, project_name: str) -> None: 

35 """Mark a project as loaded after a successful or partial fetch.""" 

36 self.loading_projects.discard(project_name) 

37 self.loaded_projects.add(project_name) 

38 

39 def cancel_load(self, project_name: str) -> None: 

40 """Clear an in-flight marker when a fetch is cancelled or fails to start.""" 

41 self.loading_projects.discard(project_name)