@startuml Reader_Class

' ── Skin ─────────────────────────────────────────────────────────────────────

skinparam linetype ortho
skinparam nodesep 35
skinparam ranksep 45

skinparam ClassBackgroundColor        #F8F7FF
skinparam ClassBorderColor            #AFA9EC
skinparam ClassBorderThickness        1.5
skinparam ClassFontName               Helvetica
skinparam ClassFontSize               11
skinparam ClassFontColor              #26215C
skinparam ClassHeaderBackgroundColor  #EDE9F7
skinparam ClassArrowColor             #7F77DD
skinparam ClassArrowThickness         1.5

skinparam InterfaceBackgroundColor    #EDE9F7
skinparam InterfaceBorderColor        #7F77DD
skinparam InterfaceFontColor          #26215C
skinparam InterfaceFontName           Helvetica
skinparam InterfaceFontSize           11

skinparam PackageBackgroundColor      #F9F8FC
skinparam PackageBorderColor          #C5C1EB
skinparam PackageFontColor            #534AB7
skinparam PackageFontSize             11
skinparam PackageFontStyle            bold

skinparam NoteBackgroundColor         #FAEEDA
skinparam NoteBorderColor             #EF9F27
skinparam ShadowingEnabled            false
skinparam RoundCorner                 8

skinparam ClassBackgroundColor<<adapter>>  #EDE9F7
skinparam ClassBorderColor<<adapter>>      #7F77DD
skinparam ClassBackgroundColor<<usecase>>  #EBF3FC
skinparam ClassBorderColor<<usecase>>      #9EB8E6
skinparam ClassBackgroundColor<<fn>>       #F0F7EE
skinparam ClassBorderColor<<fn>>           #8CC87A

' ── Component ────────────────────────────────────────────────────────────────

package "Reader" {

  ' ── Inbound layer ──────────────────────────────────────────────────────────

  class Reader <<adapter>> {
    - use_case : Box<ReaderRunPort>
  }

  interface ReaderRunPort <<inbound port>> {
    + get_contract_context(path, comments) : Result<ContractContext>
    + get_fuzz_output(path) : Result<Option<String>>
    + get_coverage_context(path) : Result<Option<CoverageContext>>
    + get_existing_bodies(path) : Result<Option<BodiesJson>>
    + get_existing_config(path) : Result<Option<FuzzerConfigArtifact>>
  }

  class ReadUseCase <<usecase>> {
    - contract_reader : Arc<ContractReaderPort>
    - fs_reader : Arc<FileSystemReader>
  }

  class parse_lcov <<fn>> {
    (lcov_content : &str)
    → Result<CoverageContext>
  }

  ' ── Outbound ports ─────────────────────────────────────────────────────────

  together {
    interface ContractReaderPort <<outbound port>> {
      + get_contract_context(path, comments) : Result<String>
    }
    interface CoverageReaderPort <<outbound port>> {
      + read_lcov(path) : Result<Option<String>>
    }
  }

  ' ── Outbound adapters ──────────────────────────────────────────────────────

  together {
    class SolidityContractReader <<adapter>> {
      - reader : Arc<FileSystemReader>
    }
    class FoundryCoverageReader <<adapter>> {
      - reader : Arc<FileSystemReader>
    }
  }

  class FileSystemReader <<adapter>> {
    - base_path : PathBuf
    + read_file(path) : Result<String>
    + read_file_optional(path) : Result<Option<String>>
  }

}

' ── Shared ───────────────────────────────────────────────────────────────────

package "Shared" #F1EFE8 {

  interface ReaderPort {
    + get_contract_context(path, comments) : Result<ContractContext>
    + get_fuzz_output(path) : Result<Option<String>>
    + get_coverage_context(path) : Result<Option<CoverageContext>>
    + get_existing_bodies(path) : Result<Option<BodiesJson>>
    + get_existing_config(path) : Result<Option<FuzzerConfigArtifact>>
  }

  together {
    class ContractContext <<model>> {
      + source_code : String
    }
    class CoverageContext <<model>> {
      + gaps : Vec<CoverageGap>
      + line_found : u32
      + line_hit : u32
      + branch_found : u32
      + branch_hit : u32
    }
  }

  together {
    class BodiesJson <<model>> {
      + meta : BodiesMeta
      + handler : HandlerBodies
      + invariant_test : InvariantTestBodies
    }
    enum FuzzerConfigArtifact <<model>> {
      Foundry(FoundryConfig)
    }
  }

}

' ── Wiring ───────────────────────────────────────────────────────────────────

' ① Inbound: shared port → adapter → internal port → use case
Reader            ..|>  ReaderPort            : implements
Reader            *-->  ReaderRunPort
ReadUseCase       ..|>  ReaderRunPort          : implements

' ② Use case owns both outbound ports and the base file reader
ReadUseCase       *-->  ContractReaderPort
ReadUseCase       *-->  FileSystemReader

' ③ Outbound adapters implement outbound ports
SolidityContractReader ..|> ContractReaderPort : implements
FoundryCoverageReader  ..|> CoverageReaderPort : implements

' ④ Both adapters delegate raw I/O to FileSystemReader
SolidityContractReader *-->  FileSystemReader  : delegates to
FoundryCoverageReader  *-->  FileSystemReader  : delegates to

' ⑤ Use case calls parse_lcov helper
ReadUseCase       ..>   parse_lcov            : calls
parse_lcov        ..>   CoverageContext        : produces

' ⑥ Data flow
ReadUseCase       ..>   ContractContext        : returns
ReadUseCase       ..>   CoverageContext        : returns
ReadUseCase       ..>   BodiesJson             : returns
ReadUseCase       ..>   FuzzerConfigArtifact   : returns

legend bottom left
  **Legend**
  ----
  <color:#7F77DD>- - ▷</color>  **..|>**  implements / realizes interface
  <color:#7F77DD>◆ ──▶</color>  ***-->**  composition — struct owns a port or field
  <color:#7F77DD>- - ▶</color>  **..>**   calls · receives · produces
endlegend

@enduml
