Coverage Summary for Class: HousesSectionParser (com.galarzaa.tibiakt.core.section.community.house.parser)

Class Class, % Method, % Branch, % Line, % Instruction, %
HousesSectionParser 100% (1/1) 100% (3/3) 66.7% (24/36) 91.5% (43/47) 85.7% (367/428)


 /*
  * Copyright © 2025 Allan Galarza
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
  *     http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 package com.galarzaa.tibiakt.core.section.community.house.parser
 
 import com.galarzaa.tibiakt.core.collections.getContaining
 import com.galarzaa.tibiakt.core.collections.offsetStart
 import com.galarzaa.tibiakt.core.enums.StringEnum
 import com.galarzaa.tibiakt.core.exceptions.ParsingException
 import com.galarzaa.tibiakt.core.html.cells
 import com.galarzaa.tibiakt.core.html.cleanText
 import com.galarzaa.tibiakt.core.html.formData
 import com.galarzaa.tibiakt.core.html.parseTablesMap
 import com.galarzaa.tibiakt.core.html.rows
 import com.galarzaa.tibiakt.core.parser.Parser
 import com.galarzaa.tibiakt.core.section.community.house.builder.housesSection
 import com.galarzaa.tibiakt.core.section.community.house.model.HouseOrder
 import com.galarzaa.tibiakt.core.section.community.house.model.HouseStatus
 import com.galarzaa.tibiakt.core.section.community.house.model.HousesSection
 import com.galarzaa.tibiakt.core.text.parseInteger
 import com.galarzaa.tibiakt.core.text.parseThousandSuffix
 import com.galarzaa.tibiakt.core.text.remove
 import kotlin.time.Duration
 import kotlin.time.Duration.Companion.days
 import kotlin.time.Duration.Companion.hours
 
 /** Parses content from the houses section. */
 public object HousesSectionParser : Parser<HousesSection?> {
     private val auctionInfoRegex = Regex("""\((?<bid>\d+) gold; (?<timeLeft>\w)+ (?<timeUnit>day|hour)s? left\)""")
 
     override fun fromContent(content: String): HousesSection? {
         val boxContent = boxContent(content)
         return housesSection {
             val tables = boxContent.parseTablesMap()
             tables["House Search"]?.apply {
                 val form = boxContent.selectFirst("div.BoxContent > form")?.formData()
                     ?: throw ParsingException("could not find house form")
                 world = form.values["world"] ?: throw ParsingException("could not find world value in form")
                 town = form.values["town"] ?: throw ParsingException("could not find town value in form")
                 status = StringEnum.fromValue(form.values["state"])
                 type = StringEnum.fromValue(form.values["type"])
                     ?: throw ParsingException("could not find type value in form")
                 StringEnum.fromValue<HouseOrder>(form.values["order"])?.also {
                     order = it
                 }
             } ?: throw ParsingException("House Search table not found")
             tables.getContaining("Available")?.apply {
                 for (row in rows().offsetStart(1)) {
                     val columns = row.cells()
                     if (columns.size != 5) break
                     val statusText = columns[3].cleanText()
                     var timeLeft: Duration? = null
                     var highestBid: Int? = null
                     auctionInfoRegex.find(statusText)?.apply {
                         highestBid = groups["bid"]!!.value.toInt()
                         val timeUnit = groups["timeUnit"]!!.value
                         timeLeft = groups["timeLeft"]!!.value.toLong().let {
                             when (timeUnit) {
                                 "hour" -> it.hours
                                 "day" -> it.days
                                 else -> throw ParsingException("unknown time unit $timeUnit")
                             }
                         }
                     }
                     addEntry {
                         name = columns[0].cleanText()
                         size = columns[1].cleanText().remove("sqm").parseInteger()
                         rent = columns[2].cleanText().remove("gold").parseThousandSuffix()
                         status = if (statusText.contains("auctioned")) HouseStatus.AUCTIONED else HouseStatus.RENTED
                         houseId = columns[4].selectFirst("input[name=houseid]")?.`val`()?.toInt()
                             ?: throw ParsingException("could not find view button for house")
                         this.timeLeft = timeLeft
                         this.highestBid = highestBid
                         town = this@housesSection.town
                         world = this@housesSection.world
                         type = this@housesSection.type
                     }
                 }
             } ?: return null
         }
     }
 }