diff --git a/app/src/commonMain/kotlin/com/codebutler/farebot/shared/App.kt b/app/src/commonMain/kotlin/com/codebutler/farebot/shared/App.kt index 394209249..d6e500eea 100644 --- a/app/src/commonMain/kotlin/com/codebutler/farebot/shared/App.kt +++ b/app/src/commonMain/kotlin/com/codebutler/farebot/shared/App.kt @@ -227,27 +227,24 @@ fun FareBotApp( var count = 0 for (cardInfo in supportedCards) { val fileName = cardInfo.sampleDumpFile ?: continue - try { - val bytes = Res.readBytes("files/samples/$fileName") - val result = - if (fileName.endsWith(".mfc")) { - cardImporter.importMfcDump(bytes) - } else { - cardImporter.importCards(bytes.decodeToString()) - } - if (result is ImportResult.Success) { - for (rawCard in result.cards) { - cardPersister.insertCard( - SavedCard( - type = rawCard.cardType(), - serial = rawCard.tagId().hex(), - data = cardSerializer.serialize(rawCard), - ), - ) - count++ - } + val bytes = Res.readBytes("files/samples/$fileName") + val result = + if (fileName.endsWith(".mfc")) { + cardImporter.importMfcDump(bytes) + } else { + cardImporter.importCards(bytes.decodeToString()) + } + if (result is ImportResult.Success) { + for (rawCard in result.cards) { + cardPersister.insertCard( + SavedCard( + type = rawCard.cardType(), + serial = rawCard.tagId().hex(), + data = cardSerializer.serialize(rawCard), + ), + ) + count++ } - } catch (_: Exception) { } } historyViewModel.loadCards() diff --git a/app/src/commonTest/kotlin/com/codebutler/farebot/test/ClipperTransitTest.kt b/app/src/commonTest/kotlin/com/codebutler/farebot/test/ClipperTransitTest.kt index 4cbd97187..b7311c68d 100644 --- a/app/src/commonTest/kotlin/com/codebutler/farebot/test/ClipperTransitTest.kt +++ b/app/src/commonTest/kotlin/com/codebutler/farebot/test/ClipperTransitTest.kt @@ -33,7 +33,6 @@ import com.codebutler.farebot.test.CardTestHelper.standardFile import com.codebutler.farebot.transit.TransitCurrency import com.codebutler.farebot.transit.Trip import com.codebutler.farebot.transit.clipper.ClipperTransitFactory -import com.codebutler.farebot.transit.clipper.ClipperTransitInfo import com.codebutler.farebot.transit.clipper.ClipperTrip import kotlinx.coroutines.test.runTest import kotlin.math.abs @@ -195,7 +194,7 @@ class ClipperTransitTest { agency = 0x04, fareValue = 350, ) - val fareStr = trip.fare?.formatCurrencyString() ?: "" + val fareStr = trip.fare.formatCurrencyString() // Should format as USD assertTrue( fareStr.contains("3.50") || fareStr.contains("3,50"), @@ -229,7 +228,6 @@ class ClipperTransitTest { assertEquals("572691763", identity.serialNumber) val info = factory.parseInfo(card) - assertTrue(info is ClipperTransitInfo, "TransitData must be instance of ClipperTransitInfo") assertEquals("572691763", info.serialNumber) assertEquals("Clipper", info.cardName.resolveAsync()) diff --git a/app/src/commonTest/kotlin/com/codebutler/farebot/test/CompassTransitTest.kt b/app/src/commonTest/kotlin/com/codebutler/farebot/test/CompassTransitTest.kt index 963119781..b48214ce9 100644 --- a/app/src/commonTest/kotlin/com/codebutler/farebot/test/CompassTransitTest.kt +++ b/app/src/commonTest/kotlin/com/codebutler/farebot/test/CompassTransitTest.kt @@ -83,8 +83,6 @@ class CompassTransitTest { // Test transit info parsing val info = factory.parseInfo(card) - assertNotNull(info, "Transit info should not be null") - assertTrue(info is CompassUltralightTransitInfo, "Info should be CompassUltralightTransitInfo") assertEquals(cardData[0], info.serialNumber, "Serial number should match") // Test identity parsing diff --git a/app/src/commonTest/kotlin/com/codebutler/farebot/test/FlipperIntegrationTest.kt b/app/src/commonTest/kotlin/com/codebutler/farebot/test/FlipperIntegrationTest.kt index 8c6cfee23..7798c4f8f 100644 --- a/app/src/commonTest/kotlin/com/codebutler/farebot/test/FlipperIntegrationTest.kt +++ b/app/src/commonTest/kotlin/com/codebutler/farebot/test/FlipperIntegrationTest.kt @@ -28,11 +28,8 @@ import com.codebutler.farebot.shared.serialize.FlipperNfcParser import com.codebutler.farebot.transit.TransitCurrency import com.codebutler.farebot.transit.Trip import com.codebutler.farebot.transit.clipper.ClipperTransitFactory -import com.codebutler.farebot.transit.clipper.ClipperTransitInfo import com.codebutler.farebot.transit.orca.OrcaTransitFactory -import com.codebutler.farebot.transit.orca.OrcaTransitInfo import com.codebutler.farebot.transit.suica.SuicaTransitFactory -import com.codebutler.farebot.transit.suica.SuicaTransitInfo import kotlinx.coroutines.test.runTest import kotlin.test.Test import kotlin.test.assertEquals @@ -74,8 +71,6 @@ class FlipperIntegrationTest { assertEquals("10043012", identity.serialNumber) val info = factory.parseInfo(card) - assertNotNull(info, "Failed to parse ORCA transit info") - assertTrue(info is OrcaTransitInfo) // Balance: $26.25 USD val balances = info.balances @@ -111,8 +106,6 @@ class FlipperIntegrationTest { assertEquals("1205019883", identity.serialNumber) val info = factory.parseInfo(card) - assertNotNull(info, "Failed to parse Clipper transit info") - assertTrue(info is ClipperTransitInfo) // Balance: $2.25 USD val balances = info.balances @@ -288,8 +281,6 @@ class FlipperIntegrationTest { assertNull(identity.serialNumber) val info = factory.parseInfo(card) - assertNotNull(info, "Failed to parse Suica transit info") - assertTrue(info is SuicaTransitInfo) // Balance: 870 JPY val balances = info.balances @@ -527,9 +518,6 @@ class FlipperIntegrationTest { assertNull(identity.serialNumber) val info = factory.parseInfo(card) - assertNotNull(info, "Failed to parse PASMO transit info") - assertTrue(info is SuicaTransitInfo) - // Balance: 500 JPY val balances = info.balances assertNotNull(balances) @@ -674,8 +662,6 @@ class FlipperIntegrationTest { assertNull(identity.serialNumber) val info = factory.parseInfo(card) - assertNotNull(info, "Failed to parse ICOCA transit info") - assertTrue(info is SuicaTransitInfo) // Balance: 827 JPY val balances = info.balances diff --git a/app/src/commonTest/kotlin/com/codebutler/farebot/test/MetrodroidDumpIntegrationTest.kt b/app/src/commonTest/kotlin/com/codebutler/farebot/test/MetrodroidDumpIntegrationTest.kt index 900a0c9f3..a1007efbf 100644 --- a/app/src/commonTest/kotlin/com/codebutler/farebot/test/MetrodroidDumpIntegrationTest.kt +++ b/app/src/commonTest/kotlin/com/codebutler/farebot/test/MetrodroidDumpIntegrationTest.kt @@ -26,12 +26,10 @@ import com.codebutler.farebot.card.classic.ClassicCard import com.codebutler.farebot.card.classic.raw.RawClassicBlock import com.codebutler.farebot.card.classic.raw.RawClassicCard import com.codebutler.farebot.card.classic.raw.RawClassicSector -import com.codebutler.farebot.card.ultralight.UltralightCard import com.codebutler.farebot.card.ultralight.UltralightPage import com.codebutler.farebot.card.ultralight.raw.RawUltralightCard import com.codebutler.farebot.transit.TransitCurrency import com.codebutler.farebot.transit.troika.TroikaTransitFactory -import com.codebutler.farebot.transit.troika.TroikaTransitInfo import com.codebutler.farebot.transit.ventra.VentraUltralightTransitInfo import kotlinx.coroutines.test.runTest import kotlin.test.Test @@ -93,7 +91,6 @@ class MetrodroidDumpIntegrationTest { ) val card = rawCard.parse() - assertTrue(card is UltralightCard, "Expected UltralightCard") val factory = VentraUltralightTransitInfo.FACTORY assertTrue(factory.check(card), "Ventra factory should recognize this card") @@ -103,8 +100,6 @@ class MetrodroidDumpIntegrationTest { assertNotNull(identity.serialNumber, "Should have a serial number") val info = factory.parseInfo(card) - assertNotNull(info, "Failed to parse Ventra transit info") - assertTrue(info is VentraUltralightTransitInfo) // Balance: $8.44 USD (844 cents) val balances = info.balances @@ -141,8 +136,6 @@ class MetrodroidDumpIntegrationTest { assertNotNull(identity.serialNumber, "Should have serial number") val info = factory.parseInfo(card) - assertNotNull(info, "Failed to parse Troika transit info") - assertTrue(info is TroikaTransitInfo) // Balance: 0.00 RUB (0 kopeks) val balances = info.balances @@ -174,8 +167,6 @@ class MetrodroidDumpIntegrationTest { assertNotNull(identity.serialNumber, "Should have serial number") val info = factory.parseInfo(card) - assertNotNull(info, "Failed to parse Troika transit info") - assertTrue(info is TroikaTransitInfo) // Balance: 50.00 RUB (5000 kopeks) val balances = info.balances diff --git a/app/src/commonTest/kotlin/com/codebutler/farebot/test/MykiTransitTest.kt b/app/src/commonTest/kotlin/com/codebutler/farebot/test/MykiTransitTest.kt index 0222fe321..db8d1b40b 100644 --- a/app/src/commonTest/kotlin/com/codebutler/farebot/test/MykiTransitTest.kt +++ b/app/src/commonTest/kotlin/com/codebutler/farebot/test/MykiTransitTest.kt @@ -73,7 +73,6 @@ class MykiTransitTest { // Test TransitData val info = factory.parseInfo(card) - assertTrue(info is MykiTransitInfo, "TransitData must be instance of MykiTransitInfo") assertEquals("308425123456780", info.serialNumber) } } diff --git a/app/src/commonTest/kotlin/com/codebutler/farebot/test/NextfareTransitTest.kt b/app/src/commonTest/kotlin/com/codebutler/farebot/test/NextfareTransitTest.kt index fa1e8681c..8c9098373 100644 --- a/app/src/commonTest/kotlin/com/codebutler/farebot/test/NextfareTransitTest.kt +++ b/app/src/commonTest/kotlin/com/codebutler/farebot/test/NextfareTransitTest.kt @@ -26,12 +26,9 @@ import com.codebutler.farebot.card.classic.ClassicCard import com.codebutler.farebot.card.classic.DataClassicSector import com.codebutler.farebot.test.CardTestHelper.hexToBytes import com.codebutler.farebot.transit.laxtap.LaxTapTransitFactory -import com.codebutler.farebot.transit.laxtap.LaxTapTransitInfo import com.codebutler.farebot.transit.mspgoto.MspGotoTransitFactory -import com.codebutler.farebot.transit.mspgoto.MspGotoTransitInfo import com.codebutler.farebot.transit.nextfare.NextfareTransitInfo import com.codebutler.farebot.transit.seqgo.SeqGoTransitFactory -import com.codebutler.farebot.transit.seqgo.SeqGoTransitInfo import kotlinx.datetime.TimeZone import kotlin.test.Test import kotlin.test.assertEquals @@ -175,7 +172,6 @@ class NextfareTransitTest { val seqGoFactory = SeqGoTransitFactory() assertTrue(seqGoFactory.check(c1), "Card is seqgo") val d1 = seqGoFactory.parseInfo(c1) - assertTrue(d1 is SeqGoTransitInfo, "Card is SeqGoTransitInfo") assertEquals("0160 0012 3456 7893", d1.serialNumber) val balances1 = d1.balances assertNotNull(balances1) @@ -190,7 +186,6 @@ class NextfareTransitTest { ) assertTrue(seqGoFactory.check(c2), "Card is seqgo") val d2 = seqGoFactory.parseInfo(c2) - assertTrue(d2 is SeqGoTransitInfo, "Card is SeqGoTransitInfo") assertEquals("0160 0098 7654 3213", d2.serialNumber) val balances2 = d2.balances assertNotNull(balances2) @@ -211,7 +206,6 @@ class NextfareTransitTest { val laxTapFactory = LaxTapTransitFactory() assertTrue(laxTapFactory.check(c), "Card is laxtap") val d = laxTapFactory.parseInfo(c) - assertTrue(d is LaxTapTransitInfo, "Card is LaxTapTransitInfo") assertEquals("0160 0323 4663 8769", d.serialNumber) val balances = d.balances assertNotNull(balances) @@ -231,7 +225,6 @@ class NextfareTransitTest { val mspGotoFactory = MspGotoTransitFactory() assertTrue(mspGotoFactory.check(c), "Card is mspgoto") val d = mspGotoFactory.parseInfo(c) - assertTrue(d is MspGotoTransitInfo, "Card is MspGotoTransitInfo") assertEquals("0160 0112 3581 3212", d.serialNumber) val balances = d.balances assertNotNull(balances) diff --git a/app/src/commonTest/kotlin/com/codebutler/farebot/test/OctopusTransitTest.kt b/app/src/commonTest/kotlin/com/codebutler/farebot/test/OctopusTransitTest.kt index 6a8ef0680..bbae7bdf7 100644 --- a/app/src/commonTest/kotlin/com/codebutler/farebot/test/OctopusTransitTest.kt +++ b/app/src/commonTest/kotlin/com/codebutler/farebot/test/OctopusTransitTest.kt @@ -91,7 +91,6 @@ class OctopusTransitTest { // Test TransitData val info = factory.parseInfo(card) - assertTrue(info is OctopusTransitInfo, "TransitData must be instance of OctopusTransitInfo") assertNotNull(info.balances) assertTrue(info.balances!!.isNotEmpty()) diff --git a/app/src/commonTest/kotlin/com/codebutler/farebot/test/OpalTransitTest.kt b/app/src/commonTest/kotlin/com/codebutler/farebot/test/OpalTransitTest.kt index 514111ff8..8f481c898 100644 --- a/app/src/commonTest/kotlin/com/codebutler/farebot/test/OpalTransitTest.kt +++ b/app/src/commonTest/kotlin/com/codebutler/farebot/test/OpalTransitTest.kt @@ -151,7 +151,6 @@ class OpalTransitTest { // Test TransitInfo val info = factory.parseInfo(card) - assertTrue(info is OpalTransitInfo, "TransitData must be instance of OpalTransitInfo") assertEquals("3085 2200 1234 5670", info.serialNumber) assertEquals(TransitCurrency.AUD(336), info.balances?.first()?.balance) @@ -191,7 +190,7 @@ class OpalTransitTest { // 2018-03-31 09:00 AEDT (UTC+11) // = 2018-03-30 22:00 UTC var card = createOpalCard(hexToBytes("85D25E07230520A70044DA380419FFFF")) - var info = factory.parseInfo(card) as OpalTransitInfo + var info = factory.parseInfo(card) var expectedTime = Instant.parse("2018-03-30T22:00:00Z") assertEquals( expectedTime, @@ -204,7 +203,7 @@ class OpalTransitTest { // 2018-04-01 09:00 AEST (UTC+10) // = 2018-03-31 23:00 UTC card = createOpalCard(hexToBytes("85D25E07430520A70048DA380419FFFF")) - info = factory.parseInfo(card) as OpalTransitInfo + info = factory.parseInfo(card) expectedTime = Instant.parse("2018-03-31T23:00:00Z") assertEquals( expectedTime, diff --git a/app/src/commonTest/kotlin/com/codebutler/farebot/test/OrcaTransitTest.kt b/app/src/commonTest/kotlin/com/codebutler/farebot/test/OrcaTransitTest.kt index 0d3f16958..b28737917 100644 --- a/app/src/commonTest/kotlin/com/codebutler/farebot/test/OrcaTransitTest.kt +++ b/app/src/commonTest/kotlin/com/codebutler/farebot/test/OrcaTransitTest.kt @@ -29,7 +29,6 @@ import com.codebutler.farebot.test.CardTestHelper.standardFile import com.codebutler.farebot.transit.TransitCurrency import com.codebutler.farebot.transit.Trip import com.codebutler.farebot.transit.orca.OrcaTransitFactory -import com.codebutler.farebot.transit.orca.OrcaTransitInfo import kotlinx.coroutines.test.runTest import kotlin.math.abs import kotlin.test.Test @@ -94,7 +93,6 @@ class OrcaTransitTest { // Test TransitInfo val info = factory.parseInfo(card) - assertTrue(info is OrcaTransitInfo, "TransitData must be instance of OrcaTransitInfo") assertEquals("12030625", info.serialNumber) assertEquals("ORCA", info.cardName.resolveAsync()) assertEquals(TransitCurrency.USD(23432), info.balances?.firstOrNull()?.balance) diff --git a/app/src/commonTest/kotlin/com/codebutler/farebot/test/SampleDumpIntegrationTest.kt b/app/src/commonTest/kotlin/com/codebutler/farebot/test/SampleDumpIntegrationTest.kt index f8b7f005f..824f6388e 100644 --- a/app/src/commonTest/kotlin/com/codebutler/farebot/test/SampleDumpIntegrationTest.kt +++ b/app/src/commonTest/kotlin/com/codebutler/farebot/test/SampleDumpIntegrationTest.kt @@ -37,7 +37,6 @@ import com.codebutler.farebot.transit.bilheteunico.BilheteUnicoSPTransitFactory import com.codebutler.farebot.transit.bilheteunico.BilheteUnicoSPTransitInfo import com.codebutler.farebot.transit.calypso.mobib.MobibTransitInfo import com.codebutler.farebot.transit.easycard.EasyCardTransitFactory -import com.codebutler.farebot.transit.easycard.EasyCardTransitInfo import com.codebutler.farebot.transit.ezlink.EZLinkTransitFactory import com.codebutler.farebot.transit.ezlink.EZLinkTransitInfo import com.codebutler.farebot.transit.hsl.HSLTransitFactory @@ -58,7 +57,6 @@ import com.codebutler.farebot.transit.seqgo.SeqGoTransitFactory import com.codebutler.farebot.transit.seqgo.SeqGoTransitInfo import com.codebutler.farebot.transit.serialonly.HoloTransitFactory import com.codebutler.farebot.transit.serialonly.HoloTransitInfo -import com.codebutler.farebot.transit.serialonly.SerialOnlyTransitInfo import com.codebutler.farebot.transit.serialonly.TrimetHopTransitFactory import com.codebutler.farebot.transit.serialonly.TrimetHopTransitInfo import com.codebutler.farebot.transit.tmoney.TMoneyTransitFactory @@ -320,7 +318,6 @@ class SampleDumpIntegrationTest : CardDumpTest() { assertNotNull(identity.serialNumber) // Serial-only card: no balance, no trips, but has emptyStateMessage - assertTrue(info is SerialOnlyTransitInfo) assertNotNull(info.emptyStateMessage, "Serial-only card should have an emptyStateMessage") assertNull(info.trips, "Serial-only card should have null trips") assertTrue(info.balances.isNullOrEmpty(), "Serial-only card should have no balances") @@ -377,7 +374,7 @@ class SampleDumpIntegrationTest : CardDumpTest() { val result = importer.importMfcDump(bytes) assertTrue(result is ImportResult.Success, "Failed to import MFC dump: $result") - val rawCard = (result as ImportResult.Success).cards.first() + val rawCard = result.cards.first() val card = rawCard.parse() as ClassicCard val factory = EasyCardTransitFactory() @@ -387,8 +384,6 @@ class SampleDumpIntegrationTest : CardDumpTest() { assertNotNull(identity.name) val info = factory.parseInfo(card) - assertNotNull(info, "Failed to parse EasyCard transit info") - assertTrue(info is EasyCardTransitInfo) // Balance: 245 TWD val balances = info.balances @@ -518,7 +513,6 @@ class SampleDumpIntegrationTest : CardDumpTest() { assertEquals("308425123456780", identity.serialNumber) // Serial-only card: has emptyStateMessage, no trips - assertTrue(info is SerialOnlyTransitInfo) assertNotNull(info.emptyStateMessage, "Serial-only card should have an emptyStateMessage") assertNull(info.trips, "Serial-only card should have null trips") } @@ -567,11 +561,9 @@ class SampleDumpIntegrationTest : CardDumpTest() { assertEquals("Hop Fastpass", identity.name.resolveAsync()) assertEquals("01-001-12345678-RA", identity.serialNumber) - assertTrue(info is TrimetHopTransitInfo) assertEquals("01-001-12345678-RA", info.serialNumber) // Serial-only card: has emptyStateMessage, no trips - assertTrue(info is SerialOnlyTransitInfo) assertNotNull(info.emptyStateMessage, "Serial-only card should have an emptyStateMessage") assertNull(info.trips, "Serial-only card should have null trips") } diff --git a/app/src/commonTest/kotlin/com/codebutler/farebot/test/TestAssetLoader.kt b/app/src/commonTest/kotlin/com/codebutler/farebot/test/TestAssetLoader.kt index 498a87d1c..bd583ff93 100644 --- a/app/src/commonTest/kotlin/com/codebutler/farebot/test/TestAssetLoader.kt +++ b/app/src/commonTest/kotlin/com/codebutler/farebot/test/TestAssetLoader.kt @@ -124,7 +124,7 @@ object TestAssetLoader { val importer = CardImporter.create(KotlinxCardSerializer(json)) val result = importer.importCards(jsonString) assertTrue(result is ImportResult.Success, "Failed to import card from $resourcePath: $result") - return (result as ImportResult.Success).cards.first() + return result.cards.first() } /** @@ -247,8 +247,6 @@ abstract class CardDumpTest { val card = rawCard.parse() assertTrue(factory.check(card), "Card did not match factory: ${factory::class.simpleName}") val transitInfo = factory.parseInfo(card) - assertNotNull(transitInfo, "Failed to parse transit info") - assertTrue(transitInfo is T, "Transit info is not of expected type") return transitInfo } @@ -273,8 +271,6 @@ abstract class CardDumpTest { val card = rawCard.parse() as C assertTrue(factory.check(card), "Card did not match factory: ${factory::class.simpleName}") val transitInfo = factory.parseInfo(card) - assertNotNull(transitInfo, "Failed to parse transit info") - assertTrue(transitInfo is T, "Transit info is not of expected type") return transitInfo } @@ -291,8 +287,6 @@ abstract class CardDumpTest { val card = rawCard.parse() as C assertTrue(factory.check(card), "Card did not match factory: ${factory::class.simpleName}") val transitInfo = factory.parseInfo(card) - assertNotNull(transitInfo, "Failed to parse transit info") - assertTrue(transitInfo is T, "Transit info is not of expected type") return Pair(card, transitInfo) } } diff --git a/app/web/build.gradle.kts b/app/web/build.gradle.kts index e4eaa38b4..c56e9ed66 100644 --- a/app/web/build.gradle.kts +++ b/app/web/build.gradle.kts @@ -1,3 +1,5 @@ +@file:OptIn(org.jetbrains.kotlin.gradle.ExperimentalWasmDsl::class) + plugins { alias(libs.plugins.kotlin.multiplatform) alias(libs.plugins.compose.multiplatform) diff --git a/base/src/commonMain/kotlin/com/codebutler/farebot/base/util/ByteUtils.kt b/base/src/commonMain/kotlin/com/codebutler/farebot/base/util/ByteUtils.kt index b68f7cdf9..ef9a3835d 100644 --- a/base/src/commonMain/kotlin/com/codebutler/farebot/base/util/ByteUtils.kt +++ b/base/src/commonMain/kotlin/com/codebutler/farebot/base/util/ByteUtils.kt @@ -13,13 +13,8 @@ object ByteUtils { fun getHexString( b: ByteArray, - defaultResult: String, - ): String = - try { - getHexString(b) - } catch (ex: Exception) { - defaultResult - } + @Suppress("UNUSED_PARAMETER") defaultResult: String, + ): String = getHexString(b) fun hexStringToByteArray(s: String): ByteArray { if (s.length % 2 != 0) { diff --git a/card/desfire/src/commonMain/kotlin/com/codebutler/farebot/card/desfire/DesfireCardReader.kt b/card/desfire/src/commonMain/kotlin/com/codebutler/farebot/card/desfire/DesfireCardReader.kt index 213187244..33db1135a 100644 --- a/card/desfire/src/commonMain/kotlin/com/codebutler/farebot/card/desfire/DesfireCardReader.kt +++ b/card/desfire/src/commonMain/kotlin/com/codebutler/farebot/card/desfire/DesfireCardReader.kt @@ -130,7 +130,7 @@ object DesfireCardReader { val fileData = readFileData(desfireProtocol, fileId, fileSettings) RawDesfireFile.create(fileId, fileSettings, fileData) } catch (ex: UnauthorizedException) { - RawDesfireFile.createUnauthorized(fileId, fileSettings, ex.message ?: "Access denied") + RawDesfireFile.createUnauthorized(fileId, fileSettings, ex.message) } catch (ex: Exception) { RawDesfireFile.createInvalid(fileId, fileSettings, ex.toString()) } @@ -169,7 +169,7 @@ object DesfireCardReader { // All commands failed return if (lastException is UnauthorizedException) { - RawDesfireFile.createUnauthorized(fileId, null, lastException.message ?: "Access denied") + RawDesfireFile.createUnauthorized(fileId, null, lastException.message) } else { RawDesfireFile.createInvalid(fileId, null, lastException.toString()) } diff --git a/card/desfire/src/commonTest/kotlin/com/codebutler/farebot/card/desfire/DesfireProtocolTest.kt b/card/desfire/src/commonTest/kotlin/com/codebutler/farebot/card/desfire/DesfireProtocolTest.kt index efa063aa9..3f579b8f9 100644 --- a/card/desfire/src/commonTest/kotlin/com/codebutler/farebot/card/desfire/DesfireProtocolTest.kt +++ b/card/desfire/src/commonTest/kotlin/com/codebutler/farebot/card/desfire/DesfireProtocolTest.kt @@ -115,7 +115,7 @@ class DesfireProtocolTest { assertFailsWith { protocol.getFileList() } - assertTrue(ex.message!!.contains("Invalid response")) + assertEquals(ex.message?.contains("Invalid response"), true) } // -- Status code: OPERATION_OK -- @@ -185,7 +185,7 @@ class DesfireProtocolTest { assertFailsWith { protocol.getFileList() } - assertTrue(ex.message!!.contains("Permission denied")) + assertTrue(ex.message.contains("Permission denied")) } // -- Status code: AUTHENTICATION_ERROR (0xAE) -- @@ -210,7 +210,7 @@ class DesfireProtocolTest { assertFailsWith { protocol.getFileList() } - assertTrue(ex.message!!.contains("Authentication error")) + assertTrue(ex.message.contains("Authentication error")) } // -- Status code: AID_NOT_FOUND (0xA0) -- @@ -235,7 +235,7 @@ class DesfireProtocolTest { assertFailsWith { protocol.selectApp(0x000001) } - assertTrue(ex.message!!.contains("AID not found")) + assertTrue(ex.message.contains("AID not found")) } // -- Status code: FILE_NOT_FOUND (0xF0) -- @@ -260,7 +260,7 @@ class DesfireProtocolTest { assertFailsWith { protocol.readFile(1) } - assertTrue(ex.message!!.contains("File not found")) + assertTrue(ex.message.contains("File not found")) } // -- Status code: unknown -- @@ -275,8 +275,8 @@ class DesfireProtocolTest { assertFailsWith { protocol.getFileList() } - assertTrue(ex.message!!.contains("Unknown status code")) - assertTrue(ex.message!!.contains("bb")) + assertEquals(ex.message?.contains("Unknown status code"), true) + assertEquals(ex.message?.contains("bb"), true) } @Test diff --git a/migrate_strings.py b/migrate_strings.py deleted file mode 100644 index 503b48252..000000000 --- a/migrate_strings.py +++ /dev/null @@ -1,153 +0,0 @@ -#!/usr/bin/env python3 -""" -Mechanical migration script: Replace getStringBlocking/getPluralStringBlocking/stringResource.getString -with FormattedString equivalents throughout the codebase. - -This handles: -1. getStringBlocking(... ) -> FormattedString(... ) -2. getPluralStringBlocking(... ) -> FormattedString.plural(... ) -3. stringResource.getString(... ) -> FormattedString(... ) -4. Import replacements -5. Property type fixups: override val cardName: String -> override val cardName: FormattedString -6. Remove stringResource constructor parameters and fields -7. Update override val signatures for Trip/Subscription/TransitInfo properties -""" - -import os -import re -import sys - -WORKTREE = os.path.dirname(os.path.abspath(__file__)) - -# Files to skip (already manually updated or infrastructure files to delete) -SKIP_FILES = { - 'base/src/commonMain/kotlin/com/codebutler/farebot/base/ui/ListItem.kt', - 'base/src/commonMain/kotlin/com/codebutler/farebot/base/ui/HeaderListItem.kt', - 'base/src/commonMain/kotlin/com/codebutler/farebot/base/ui/ListItemInterface.kt', - 'base/src/commonMain/kotlin/com/codebutler/farebot/base/ui/FareBotUiTree.kt', - 'base/src/commonMain/kotlin/com/codebutler/farebot/base/ui/UiTreeBuilder.kt', - 'base/src/commonMain/kotlin/com/codebutler/farebot/base/util/FormattedString.kt', - 'base/src/commonMain/kotlin/com/codebutler/farebot/base/util/GetStringBlocking.kt', - 'base/src/commonMain/kotlin/com/codebutler/farebot/base/util/DefaultStringResource.kt', - 'base/src/commonMain/kotlin/com/codebutler/farebot/base/util/StringResource.kt', - 'base/src/androidMain/kotlin/com/codebutler/farebot/base/util/GetStringBlocking.kt', - 'base/src/iosMain/kotlin/com/codebutler/farebot/base/util/GetStringBlocking.kt', - 'base/src/jvmMain/kotlin/com/codebutler/farebot/base/util/GetStringBlocking.kt', - 'base/src/wasmJsMain/kotlin/com/codebutler/farebot/base/util/GetStringBlocking.kt', - 'transit/src/commonMain/kotlin/com/codebutler/farebot/transit/TransitInfo.kt', - 'transit/src/commonMain/kotlin/com/codebutler/farebot/transit/TransitIdentity.kt', - 'transit/src/commonMain/kotlin/com/codebutler/farebot/transit/Trip.kt', - 'transit/src/commonMain/kotlin/com/codebutler/farebot/transit/Subscription.kt', - 'transit/src/commonMain/kotlin/com/codebutler/farebot/transit/Station.kt', - 'app/src/commonTest/kotlin/com/codebutler/farebot/test/TestStringResource.kt', - # Skip non-Kotlin files - 'CLAUDE.md', - 'migrate_strings.py', -} - -# Properties that changed from String to FormattedString -FORMATTEDSTRING_OVERRIDE_PROPS = [ - 'cardName', - 'routeName', - 'agencyName', - 'shortAgencyName', - 'fareString', - 'subscriptionName', - 'saleAgencyName', - 'warning', - 'emptyStateMessage', -] - -def should_skip(filepath): - rel = os.path.relpath(filepath, WORKTREE) - return rel in SKIP_FILES - -def process_file(filepath): - with open(filepath, 'r') as f: - content = f.read() - - original = content - modified = False - - # 1. Replace getStringBlocking( -> FormattedString( - if 'getStringBlocking(' in content: - content = content.replace('getStringBlocking(', 'FormattedString(') - modified = True - - # 2. Replace getPluralStringBlocking( -> FormattedString.plural( - if 'getPluralStringBlocking(' in content: - content = content.replace('getPluralStringBlocking(', 'FormattedString.plural(') - modified = True - - # 3. Replace stringResource.getString( -> FormattedString( - if 'stringResource.getString(' in content: - content = content.replace('stringResource.getString(', 'FormattedString(') - modified = True - - # 4. Fix override val properties that changed type from String to FormattedString - for prop in FORMATTEDSTRING_OVERRIDE_PROPS: - # override val propName: String -> override val propName: FormattedString - pattern = rf'(override\s+val\s+{prop}\s*:\s*)String(\b)' - replacement = rf'\1FormattedString\2' - new_content = re.sub(pattern, replacement, content) - if new_content != content: - content = new_content - modified = True - - # 5. Fix "val description: String get() = getStringBlocking" -> already handled by step 1 - # But need to fix the return type - # PaymentMethod.description is handled in Subscription.kt (manually updated) - - # 6. Update imports - if modified: - # Remove old imports - old_imports = [ - 'import com.codebutler.farebot.base.util.getStringBlocking', - 'import com.codebutler.farebot.base.util.getPluralStringBlocking', - ] - for old_import in old_imports: - if old_import in content: - content = content.replace(old_import + '\n', '') - - # Add FormattedString import if not already present and file uses it - if 'FormattedString(' in content or 'FormattedString.plural(' in content: - fs_import = 'import com.codebutler.farebot.base.util.FormattedString' - if fs_import not in content: - # Find the last import line and add after it - lines = content.split('\n') - last_import_idx = -1 - for i, line in enumerate(lines): - if line.startswith('import '): - last_import_idx = i - if last_import_idx >= 0: - lines.insert(last_import_idx + 1, fs_import) - content = '\n'.join(lines) - - if content != original: - with open(filepath, 'w') as f: - f.write(content) - return True - return False - -def main(): - count = 0 - for root, dirs, files in os.walk(WORKTREE): - # Skip build directories, .git, metrodroid, etc. - dirs[:] = [d for d in dirs if d not in { - 'build', '.git', '.gradle', 'metrodroid', 'kotlin-js-store', - 'docs', '.devcontainer', 'config', '.claude', '.idea', - }] - for fname in files: - if not fname.endswith('.kt'): - continue - filepath = os.path.join(root, fname) - if should_skip(filepath): - continue - if process_file(filepath): - rel = os.path.relpath(filepath, WORKTREE) - print(f' Modified: {rel}') - count += 1 - print(f'\nTotal files modified: {count}') - -if __name__ == '__main__': - main() diff --git a/remove_string_resource.py b/remove_string_resource.py deleted file mode 100644 index 55c7ad8b3..000000000 --- a/remove_string_resource.py +++ /dev/null @@ -1,243 +0,0 @@ -#!/usr/bin/env python3 -""" -Remove stringResource: StringResource parameter/field from all transit factories, -transit info classes, trip classes, subscription classes, card classes, and DI graphs. - -This handles: -1. Constructor/function parameter: `stringResource: StringResource` (with various modifiers) -2. Constructor argument passing: `stringResource = stringResource,` or `stringResource,` -3. Import removal: StringResource, DefaultStringResource -4. Property declarations: `val stringResource = DefaultStringResource()` -5. getAdvancedUi(stringResource: StringResource) -> getAdvancedUi() -6. getAdvancedUi(stringResource) -> getAdvancedUi() -7. Refill.getAgencyName(stringResource) patterns -""" - -import os -import re -import sys - -WORKTREE = os.path.dirname(os.path.abspath(__file__)) - -# Files to skip -SKIP_FILES = { - 'base/src/commonMain/kotlin/com/codebutler/farebot/base/util/FormattedString.kt', - 'base/src/commonMain/kotlin/com/codebutler/farebot/base/util/StringResource.kt', - 'base/src/commonMain/kotlin/com/codebutler/farebot/base/util/DefaultStringResource.kt', - 'base/src/commonMain/kotlin/com/codebutler/farebot/base/util/GetStringBlocking.kt', - 'base/src/androidMain/kotlin/com/codebutler/farebot/base/util/GetStringBlocking.kt', - 'base/src/iosMain/kotlin/com/codebutler/farebot/base/util/GetStringBlocking.kt', - 'base/src/jvmMain/kotlin/com/codebutler/farebot/base/util/GetStringBlocking.kt', - 'base/src/wasmJsMain/kotlin/com/codebutler/farebot/base/util/GetStringBlocking.kt', - 'CLAUDE.md', - 'migrate_strings.py', - 'remove_string_resource.py', -} - -def should_skip(filepath): - rel = os.path.relpath(filepath, WORKTREE) - return rel in SKIP_FILES - -def process_file(filepath): - with open(filepath, 'r') as f: - content = f.read() - - original = content - - # 1. Remove imports - imports_to_remove = [ - 'import com.codebutler.farebot.base.util.StringResource', - 'import com.codebutler.farebot.base.util.DefaultStringResource', - 'import com.codebutler.farebot.base.util.getStringBlocking', - 'import com.codebutler.farebot.base.util.getPluralStringBlocking', - ] - for imp in imports_to_remove: - content = content.replace(imp + '\n', '') - - # 2. Remove stringResource parameter lines from constructors/functions - # Patterns like: - # private val stringResource: StringResource, - # val stringResource: StringResource, - # override val stringResource: StringResource, - # stringResource: StringResource, - # stringResource: StringResource = DefaultStringResource(), - # private val stringResource: StringResource = DefaultStringResource(), - lines = content.split('\n') - new_lines = [] - i = 0 - while i < len(lines): - line = lines[i] - stripped = line.strip() - - # Match stringResource parameter/field declarations - if re.match(r'^(private\s+val\s+|val\s+|override\s+val\s+)?stringResource\s*:\s*StringResource(\s*=\s*DefaultStringResource\(\))?\s*,?\s*$', stripped): - # If the line ends with comma, just remove the line - # If not, check if previous line ends with comma that needs removing - if not stripped.endswith(',') and new_lines: - # Remove trailing comma from previous non-blank line - for j in range(len(new_lines) - 1, -1, -1): - if new_lines[j].strip(): - new_lines[j] = new_lines[j].rstrip().rstrip(',') - break - i += 1 - continue - - new_lines.append(line) - i += 1 - - content = '\n'.join(new_lines) - - # 3. Remove stringResource being passed as constructor argument - # Patterns: - # stringResource = stringResource, - # stringResource = this.stringResource, - # stringResource = stringResource - # stringResource, (standalone on a line, as positional arg) - lines = content.split('\n') - new_lines = [] - i = 0 - while i < len(lines): - line = lines[i] - stripped = line.strip() - - # Match: stringResource = stringResource, or stringResource = this.stringResource, - if re.match(r'^stringResource\s*=\s*(this\.)?stringResource\s*,?\s*$', stripped): - if not stripped.endswith(',') and new_lines: - for j in range(len(new_lines) - 1, -1, -1): - if new_lines[j].strip(): - new_lines[j] = new_lines[j].rstrip().rstrip(',') - break - i += 1 - continue - - # Match standalone: stringResource = DefaultStringResource(), (as arg) - if re.match(r'^stringResource\s*=\s*DefaultStringResource\(\)\s*,?\s*$', stripped): - if not stripped.endswith(',') and new_lines: - for j in range(len(new_lines) - 1, -1, -1): - if new_lines[j].strip(): - new_lines[j] = new_lines[j].rstrip().rstrip(',') - break - i += 1 - continue - - new_lines.append(line) - i += 1 - - content = '\n'.join(new_lines) - - # 4. Remove inline stringResource parameter from function signatures - # e.g., fun getAdvancedUi(stringResource: StringResource): ... - # -> fun getAdvancedUi(): ... - content = re.sub( - r'(\bgetAdvancedUi\s*)\(\s*stringResource\s*:\s*StringResource\s*\)', - r'\1()', - content - ) - - # 5. Remove stringResource argument from getAdvancedUi calls - # e.g., getAdvancedUi(stringResource) -> getAdvancedUi() - # e.g., getAdvancedUi(this.stringResource) -> getAdvancedUi() - content = re.sub( - r'(\bgetAdvancedUi\s*)\(\s*(this\.)?stringResource\s*\)', - r'\1()', - content - ) - - # 6. Remove stringResource from inline constructor params (single line) - # e.g., SuicaTrip(stringResource, ...) -> SuicaTrip(...) - # e.g., Foo(bar, stringResource, baz) -> Foo(bar, baz) - # e.g., Foo(stringResource = stringResource, bar = baz) -> Foo(bar = baz) - - # Pattern: remove "stringResource, " or ", stringResource" from argument lists - # Be careful not to match inside strings - # Handle named arg: stringResource = stringResource, - content = re.sub(r'stringResource\s*=\s*(this\.)?stringResource\s*,\s*', '', content) - # Handle named arg at end: , stringResource = stringResource) - content = re.sub(r',\s*stringResource\s*=\s*(this\.)?stringResource\s*(?=\))', '', content) - # Handle named arg as only arg: (stringResource = stringResource) - content = re.sub(r'\(\s*stringResource\s*=\s*(this\.)?stringResource\s*\)', '()', content) - - # Handle positional: stringResource, (at start of args) - content = re.sub(r'(?<=\()(\s*)stringResource\s*,\s*', r'\1', content) - # Handle positional: , stringResource (at end of args) - content = re.sub(r',\s*stringResource\s*(?=\s*\))', '', content) - # Handle positional as only arg: (stringResource) but NOT (stringResource: Type) - content = re.sub(r'\(\s*stringResource\s*\)(?!\s*[:{])', '()', content) - - # 7. Remove stringResource = DefaultStringResource() as arg - content = re.sub(r'stringResource\s*=\s*DefaultStringResource\(\)\s*,\s*', '', content) - content = re.sub(r',\s*stringResource\s*=\s*DefaultStringResource\(\)\s*(?=\))', '', content) - content = re.sub(r'\(\s*stringResource\s*=\s*DefaultStringResource\(\)\s*\)', '()', content) - - # 8. Remove standalone property assignments - # val stringResource = DefaultStringResource() - # private val stringResource = DefaultStringResource() - content = re.sub(r'\n\s*(private\s+)?val\s+stringResource\s*=\s*DefaultStringResource\(\)\s*\n', '\n', content) - content = re.sub(r'\n\s*(private\s+)?val\s+stringResource\s*:\s*StringResource\s*=\s*DefaultStringResource\(\)\s*\n', '\n', content) - - # 9. Handle Refill abstract methods that take stringResource - # fun getAgencyName(stringResource: StringResource): String? -> fun getAgencyName(): FormattedString? - # These should have been handled by the mechanical migration, but just in case - content = re.sub( - r'(\bgetAgencyName\s*)\(\s*stringResource\s*:\s*StringResource\s*\)', - r'\1()', - content - ) - content = re.sub( - r'(\bgetShortAgencyName\s*)\(\s*stringResource\s*:\s*StringResource\s*\)', - r'\1()', - content - ) - content = re.sub( - r'(\bgetAmountString\s*)\(\s*stringResource\s*:\s*StringResource\s*\)', - r'\1()', - content - ) - - # Also fix call sites - content = re.sub(r'(\bgetAgencyName\s*)\(\s*(this\.)?stringResource\s*\)', r'\1()', content) - content = re.sub(r'(\bgetShortAgencyName\s*)\(\s*(this\.)?stringResource\s*\)', r'\1()', content) - content = re.sub(r'(\bgetAmountString\s*)\(\s*(this\.)?stringResource\s*\)', r'\1()', content) - - # 10. Remove stringResource from inline function params - # e.g., fun foo(stringResource: StringResource, bar: Bar) -> fun foo(bar: Bar) - content = re.sub(r'stringResource\s*:\s*StringResource\s*,\s*', '', content) - content = re.sub(r',\s*stringResource\s*:\s*StringResource\s*(?=\))', '', content) - content = re.sub(r'\(\s*stringResource\s*:\s*StringResource\s*\)', '()', content) - - # Also with default value - content = re.sub(r'stringResource\s*:\s*StringResource\s*=\s*DefaultStringResource\(\)\s*,\s*', '', content) - content = re.sub(r',\s*stringResource\s*:\s*StringResource\s*=\s*DefaultStringResource\(\)\s*(?=\))', '', content) - content = re.sub(r'\(\s*stringResource\s*:\s*StringResource\s*=\s*DefaultStringResource\(\)\s*\)', '()', content) - - # 11. Clean up double blank lines that may have been created - while '\n\n\n' in content: - content = content.replace('\n\n\n', '\n\n') - - if content != original: - with open(filepath, 'w') as f: - f.write(content) - return True - return False - -def main(): - count = 0 - for root, dirs, files in os.walk(WORKTREE): - dirs[:] = [d for d in dirs if d not in { - 'build', '.git', '.gradle', 'metrodroid', 'kotlin-js-store', - 'docs', '.devcontainer', 'config', '.claude', '.idea', 'worktrees', - }] - for fname in files: - if not fname.endswith('.kt'): - continue - filepath = os.path.join(root, fname) - if should_skip(filepath): - continue - if process_file(filepath): - rel = os.path.relpath(filepath, WORKTREE) - print(f' Modified: {rel}') - count += 1 - print(f'\nTotal files modified: {count}') - -if __name__ == '__main__': - main() diff --git a/tools/mdst/build.gradle.kts b/tools/mdst/build.gradle.kts index 34d37e2cc..d72dd079d 100644 --- a/tools/mdst/build.gradle.kts +++ b/tools/mdst/build.gradle.kts @@ -1,3 +1,5 @@ +@file:OptIn(org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi::class) + plugins { alias(libs.plugins.kotlin.multiplatform) alias(libs.plugins.kotlin.serialization) diff --git a/transit/en1545/src/commonMain/kotlin/com/codebutler/farebot/transit/en1545/En1545Bitmap.kt b/transit/en1545/src/commonMain/kotlin/com/codebutler/farebot/transit/en1545/En1545Bitmap.kt index cccfc161d..741c97231 100644 --- a/transit/en1545/src/commonMain/kotlin/com/codebutler/farebot/transit/en1545/En1545Bitmap.kt +++ b/transit/en1545/src/commonMain/kotlin/com/codebutler/farebot/transit/en1545/En1545Bitmap.kt @@ -49,12 +49,8 @@ class En1545Bitmap private constructor( bitParser: En1545Bits, ): Int { var off = off - val bitmask: Int - try { - bitmask = bitParser(b, off, fields.size) - } catch (_: Exception) { - return off + fields.size - } + if (off + fields.size > b.size * 8) return off + fields.size + val bitmask = bitParser(b, off, fields.size) off += fields.size if (infix != null) { diff --git a/transit/en1545/src/commonMain/kotlin/com/codebutler/farebot/transit/en1545/En1545FixedHex.kt b/transit/en1545/src/commonMain/kotlin/com/codebutler/farebot/transit/en1545/En1545FixedHex.kt index 287c39e9f..82a119d69 100644 --- a/transit/en1545/src/commonMain/kotlin/com/codebutler/farebot/transit/en1545/En1545FixedHex.kt +++ b/transit/en1545/src/commonMain/kotlin/com/codebutler/farebot/transit/en1545/En1545FixedHex.kt @@ -33,8 +33,8 @@ class En1545FixedHex( holder: En1545Parsed, bitParser: En1545Bits, ): Int { - var res = "" - try { + if (off + len <= b.size * 8) { + var res = "" var i = len while (i > 0) { if (i >= 8) { @@ -53,7 +53,6 @@ class En1545FixedHex( break } holder.insertString(name, path, res) - } catch (_: Exception) { } return off + len } diff --git a/transit/en1545/src/commonMain/kotlin/com/codebutler/farebot/transit/en1545/En1545FixedInteger.kt b/transit/en1545/src/commonMain/kotlin/com/codebutler/farebot/transit/en1545/En1545FixedInteger.kt index d64f2553e..e8ea517fe 100644 --- a/transit/en1545/src/commonMain/kotlin/com/codebutler/farebot/transit/en1545/En1545FixedInteger.kt +++ b/transit/en1545/src/commonMain/kotlin/com/codebutler/farebot/transit/en1545/En1545FixedInteger.kt @@ -43,9 +43,8 @@ class En1545FixedInteger( holder: En1545Parsed, bitParser: En1545Bits, ): Int { - try { + if (off + len <= b.size * 8) { holder.insertInt(name, path, bitParser(b, off, len)) - } catch (_: Exception) { } return off + len } diff --git a/transit/en1545/src/commonMain/kotlin/com/codebutler/farebot/transit/en1545/En1545FixedString.kt b/transit/en1545/src/commonMain/kotlin/com/codebutler/farebot/transit/en1545/En1545FixedString.kt index 63754011c..995510a66 100644 --- a/transit/en1545/src/commonMain/kotlin/com/codebutler/farebot/transit/en1545/En1545FixedString.kt +++ b/transit/en1545/src/commonMain/kotlin/com/codebutler/farebot/transit/en1545/En1545FixedString.kt @@ -51,12 +51,7 @@ class En1545FixedString( var lastNonSpace = 0 val ret = StringBuilder() while (i + 4 < start + length && i + 4 < bin.size * 8) { - val bl: Int - try { - bl = bitParser(bin, i, 5) - } catch (_: Exception) { - return null - } + val bl = bitParser(bin, i, 5) if (bl == 0 || bl == 31) { if (j != 0) { @@ -70,10 +65,10 @@ class En1545FixedString( } i += 5 } - return try { - ret.substring(0, lastNonSpace + 1) - } catch (_: Exception) { + return if (ret.isEmpty()) { ret.toString() + } else { + ret.substring(0, lastNonSpace + 1) } } } diff --git a/transit/en1545/src/commonMain/kotlin/com/codebutler/farebot/transit/en1545/En1545Repeat.kt b/transit/en1545/src/commonMain/kotlin/com/codebutler/farebot/transit/en1545/En1545Repeat.kt index 72a31ca53..b7432a313 100644 --- a/transit/en1545/src/commonMain/kotlin/com/codebutler/farebot/transit/en1545/En1545Repeat.kt +++ b/transit/en1545/src/commonMain/kotlin/com/codebutler/farebot/transit/en1545/En1545Repeat.kt @@ -41,12 +41,8 @@ class En1545Repeat( bitParser: En1545Bits, ): Int { var off = off - val ctr: Int - try { - ctr = bitParser(b, off, ctrLen) - } catch (_: Exception) { - return off + ctrLen - } + if (off + ctrLen > b.size * 8) return off + ctrLen + val ctr = bitParser(b, off, ctrLen) off += ctrLen for (i in 0 until ctr) { diff --git a/transit/oyster/src/commonMain/kotlin/com/codebutler/farebot/transit/oyster/OysterRefill.kt b/transit/oyster/src/commonMain/kotlin/com/codebutler/farebot/transit/oyster/OysterRefill.kt index 14ec8de21..253d4c965 100644 --- a/transit/oyster/src/commonMain/kotlin/com/codebutler/farebot/transit/oyster/OysterRefill.kt +++ b/transit/oyster/src/commonMain/kotlin/com/codebutler/farebot/transit/oyster/OysterRefill.kt @@ -44,17 +44,15 @@ class OysterRefill( val result = mutableListOf() val sector5 = card.getSector(5) as? DataClassicSector ?: return result for (block in 0..2) { - try { - val data = sector5.getBlock(block).data - result.add( - OysterRefill( - startTimestamp = OysterUtils.parseTimestamp(data), - // estimate: max top-up requires 14 bits - amount = data.getBitsFromBufferLeBits(74, 14), - ), - ) - } catch (_: Exception) { - } + if (block >= sector5.blocks.size) continue + val data = sector5.getBlock(block).data + result.add( + OysterRefill( + startTimestamp = OysterUtils.parseTimestamp(data), + // estimate: max top-up requires 14 bits + amount = data.getBitsFromBufferLeBits(74, 14), + ), + ) } return result } diff --git a/transit/oyster/src/commonMain/kotlin/com/codebutler/farebot/transit/oyster/OysterTransaction.kt b/transit/oyster/src/commonMain/kotlin/com/codebutler/farebot/transit/oyster/OysterTransaction.kt index 4c8f8a8e4..22016d236 100644 --- a/transit/oyster/src/commonMain/kotlin/com/codebutler/farebot/transit/oyster/OysterTransaction.kt +++ b/transit/oyster/src/commonMain/kotlin/com/codebutler/farebot/transit/oyster/OysterTransaction.kt @@ -54,14 +54,12 @@ class OysterTransaction( for (block in 0..2) { // invalid if (block == 0 && sector == 9) continue - try { - result.add( - OysterTransaction( - OysterUtils.parseTimestamp(sec.getBlock(block).data, 6), - ), - ) - } catch (_: Exception) { - } + if (block >= sec.blocks.size) continue + result.add( + OysterTransaction( + OysterUtils.parseTimestamp(sec.getBlock(block).data, 6), + ), + ) } } return result diff --git a/transit/oyster/src/commonMain/kotlin/com/codebutler/farebot/transit/oyster/OysterTravelPass.kt b/transit/oyster/src/commonMain/kotlin/com/codebutler/farebot/transit/oyster/OysterTravelPass.kt index ffa1a8181..a811f128f 100644 --- a/transit/oyster/src/commonMain/kotlin/com/codebutler/farebot/transit/oyster/OysterTravelPass.kt +++ b/transit/oyster/src/commonMain/kotlin/com/codebutler/farebot/transit/oyster/OysterTravelPass.kt @@ -47,33 +47,27 @@ class OysterTravelPass( internal fun parseAll(card: ClassicCard): List { val result = mutableListOf() for (block in 0..2) { - try { - val sec7 = - (card.getSector(7) as? DataClassicSector) - ?.getBlock(block) - ?.data ?: continue + val sector7 = card.getSector(7) as? DataClassicSector ?: continue + val sector8 = card.getSector(8) as? DataClassicSector ?: continue + if (block >= sector7.blocks.size || block >= sector8.blocks.size) continue - // Don't know what a blank card looks like, so try to skip if it doesn't look - // like there is any expiry date on a pass. - if (sec7.sliceOffLen(9, 4).isAllZero()) { - // invalid date? - continue - } + val sec7 = sector7.getBlock(block).data + val sec8 = sector8.getBlock(block).data - val sec8 = - (card.getSector(8) as? DataClassicSector) - ?.getBlock(block) - ?.data ?: continue - - result.add( - OysterTravelPass( - validFrom = OysterUtils.parseTimestamp(sec8, 78), - validTo = OysterUtils.parseTimestamp(sec7, 33), - cost = TransitCurrency.GBP(sec8.byteArrayToIntReversed(0, 2)), - ), - ) - } catch (_: Exception) { + // Don't know what a blank card looks like, so try to skip if it doesn't look + // like there is any expiry date on a pass. + if (sec7.sliceOffLen(9, 4).isAllZero()) { + // invalid date? + continue } + + result.add( + OysterTravelPass( + validFrom = OysterUtils.parseTimestamp(sec8, 78), + validTo = OysterUtils.parseTimestamp(sec7, 33), + cost = TransitCurrency.GBP(sec8.byteArrayToIntReversed(0, 2)), + ), + ) } return result } diff --git a/transit/smartrider/src/commonMain/kotlin/com/codebutler/farebot/transit/smartrider/SmartRiderTagRecord.kt b/transit/smartrider/src/commonMain/kotlin/com/codebutler/farebot/transit/smartrider/SmartRiderTagRecord.kt index 924367282..fc826bcec 100644 --- a/transit/smartrider/src/commonMain/kotlin/com/codebutler/farebot/transit/smartrider/SmartRiderTagRecord.kt +++ b/transit/smartrider/src/commonMain/kotlin/com/codebutler/farebot/transit/smartrider/SmartRiderTagRecord.kt @@ -135,13 +135,11 @@ class SmartRiderTagRecord( companion object { private fun routeName(input: ByteArray): String { val cleaned = input.filter { it != 0.toByte() }.toByteArray() - try { - if (cleaned.isASCII()) { - return cleaned.readASCII() - } - } catch (_: Exception) { + return if (cleaned.isASCII()) { + cleaned.readASCII() + } else { + cleaned.hex() } - return cleaned.hex() } /**