# Source code for debsbom.graph.walker

    # Copyright (C) 2026 Siemens
    #
    # SPDX-License-Identifier: MIT

    from abc import abstractmethod
    from collections.abc import Iterable
    from dataclasses import dataclass
    from pathlib import Path
    from io import IOBase

    from packageurl import PackageURL

    from ..bomreader.bomreader import BomReader
    from ..util.sbom_processor import SbomProcessor
    from ..sbom import SBOMType

    class NoRootNodeError(RuntimeError):
        def __init__(self):
            super().__init__("SBOM does not contain any root node")

    @dataclass
    class PackageRepr:
        """
        Simple package representation that covers meta, source and binary packages.
        """

        name: str
        ref: str
        version: str | None = None
        maintainer: str | None = None
        purl: str | None = None

        def __str__(self):
            NOA = "NoAssertion"
            return f"{self.name}\t{self.version or NOA}\t{self.purl or NOA}\t{self.maintainer or NOA}"

        def __hash__(self):
            """Trivial hash function based on the unique ref field."""
            return hash(self.ref)

    class GraphWalker(SbomProcessor):
        """
        Base class of graph walkers
        """

        @classmethod
        def create(
            cls,
            filename: Path,
            bomtype: SBOMType | None = None,
        ) -> "GraphWalker":
            """
            Factory to create a GraphWalker for the given SBOM type (based on the filename extension).
            """
            reader = BomReader.create(filename, bomtype)
            return cls.from_document(reader.read(), reader.sbom_type())

        @classmethod
        def from_stream(cls, stream: IOBase, bomtype: SBOMType) -> "GraphWalker":
            """
            Factory to create a GraphWalker for the given SBOM type that takes the SBOM as stream.
            """
            reader = BomReader.from_stream(stream, bomtype)
            return cls.from_document(reader.read(), reader.sbom_type())

        @classmethod
        def from_json(cls, json_obj: IOBase, bomtype: SBOMType) -> "GraphWalker":
            """
            Factory to create a GraphWalker for the given SBOM type that takes a json object.
            """
            reader = BomReader.from_json(json_obj, bomtype)
            return cls.from_document(reader.read(), reader.sbom_type())

        @classmethod
        def from_document(cls, document, sbom_type: SBOMType) -> "GraphWalker":
            """
            Factory to create a GraphWalker from an SBOM document instance.
            """
            if sbom_type == SBOMType.SPDX:
                from .spdx import SpdxGraphWalker

                return SpdxGraphWalker(document)
            else:
                from .cdx import CdxGraphWalker

                return CdxGraphWalker(document)

        @abstractmethod
        def shortest(self, source: PackageURL) -> list[PackageRepr]:
            """Return shortest path from source to root. Abstract method."""
            raise NotImplementedError()

        @abstractmethod
        def all_shortest(self, source: PackageURL) -> Iterable[list[PackageRepr]]:
            """Return all shortest path from source to root. Abstract method."""
            raise NotImplementedError()

        @abstractmethod
        def all_simple(self, source: PackageURL) -> Iterable[list[PackageRepr]]:
            """Return all non-cyclic path from source to root. Abstract method."""
            raise NotImplementedError()

        @abstractmethod
        def descendants(self, source: PackageURL) -> Iterable[PackageRepr]:
            """Return all descendants of source. Abstract method."""
            raise NotImplementedError()