diff --git a/src/launchpad/analyzers/apple.py b/src/launchpad/analyzers/apple.py index 0b4c01c0..ef90bfd9 100644 --- a/src/launchpad/analyzers/apple.py +++ b/src/launchpad/analyzers/apple.py @@ -11,7 +11,8 @@ import lief from ..artifacts import AppleArtifact, ZippedXCArchive -from ..insights.common import DuplicateFilesInsight, InsightsInput +from ..insights.common import DuplicateFilesInsight +from ..insights.insight import InsightsInput from ..models import AppleAnalysisResults, AppleAppInfo, FileAnalysis, FileInfo, MachOBinaryAnalysis from ..models.apple import AppleInsightResults from ..models.treemap import FILE_TYPE_TO_TREEMAP_TYPE, TreemapType @@ -128,7 +129,7 @@ def analyze(self, artifact: AppleArtifact) -> AppleAnalysisResults: treemap=treemap, ) insights = AppleInsightResults( - duplicate_files=DuplicateFilesInsight().__call__(insights_input), + duplicate_files=DuplicateFilesInsight().generate(insights_input), ) results = AppleAnalysisResults( diff --git a/src/launchpad/insights/common.py b/src/launchpad/insights/common.py index 6a2c5044..379071d7 100644 --- a/src/launchpad/insights/common.py +++ b/src/launchpad/insights/common.py @@ -3,57 +3,16 @@ from __future__ import annotations from collections import defaultdict -from dataclasses import dataclass -from typing import Dict, List, Protocol, TypeVar +from typing import Dict, List -from launchpad.models.common import FileAnalysis, FileInfo +from launchpad.insights.insight import Insight, InsightsInput +from launchpad.models.common import FileInfo from launchpad.models.insights import DuplicateFilesInsightResult -from launchpad.models.treemap import TreemapResults - -from ..models.apple import AppleAppInfo, MachOBinaryAnalysis - -T_co = TypeVar("T_co", covariant=True) - - -@dataclass -class InsightsInput: - app_info: AppleAppInfo - file_analysis: FileAnalysis - treemap: TreemapResults | None - binary_analysis: List[MachOBinaryAnalysis] - - -class Insight(Protocol[T_co]): - """Protocol for insight functions. - - Insights are functions that take analysis results and return typed insight results. - All data needed for the insight must be collected during the main analysis phase. - """ - - def __call__(self, input: InsightsInput) -> T_co: - """Generate insights from analysis results. - - Args: - results: The analysis results to generate insights from - - Returns: - Typed insight results - """ - ... class DuplicateFilesInsight(Insight[DuplicateFilesInsightResult]): - """Insight for duplicate files analysis.""" - - def __call__(self, input: InsightsInput) -> DuplicateFilesInsightResult: - """Generate insights about duplicate files. - - Args: - results: The analysis results to generate insights from - Returns: - Duplicate files insight results - """ + def generate(self, input: InsightsInput) -> DuplicateFilesInsightResult: # Group files by hash files_by_hash: Dict[str, List[FileInfo]] = defaultdict(list) for file in input.file_analysis.files: diff --git a/src/launchpad/insights/insight.py b/src/launchpad/insights/insight.py new file mode 100644 index 00000000..58ba53c1 --- /dev/null +++ b/src/launchpad/insights/insight.py @@ -0,0 +1,37 @@ +from abc import abstractmethod +from dataclasses import dataclass +from typing import Protocol, TypeVar + +from ..models.apple import AppleAppInfo, MachOBinaryAnalysis +from ..models.common import FileAnalysis +from ..models.treemap import TreemapResults + +T_co = TypeVar("T_co", covariant=True) + + +@dataclass +class InsightsInput: + app_info: AppleAppInfo + file_analysis: FileAnalysis + treemap: TreemapResults | None + binary_analysis: list[MachOBinaryAnalysis] + + +class Insight(Protocol[T_co]): + """Protocol for insight functions. + + Insights are functions that take analysis results and return typed insight results. + All data needed for the insight must be collected during the main analysis phase. + """ + + @abstractmethod + def generate(self, input: InsightsInput) -> T_co: + """Generate insights from analysis results. + + Args: + results: The analysis results to generate insights from + + Returns: + Typed insight results + """ + raise NotImplementedError("Not implemented")