Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

**Added**
- Add `--summary-only` flag.
- Add `--byte-unit` option.

**Fixed**
- Significantly improve `.jar` diff performance.
Expand Down
10 changes: 8 additions & 2 deletions diffuse/src/main/kotlin/com/jakewharton/diffuse/diffuse.kt
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import com.jakewharton.diffuse.info.AarInfo
import com.jakewharton.diffuse.info.ApkInfo
import com.jakewharton.diffuse.info.DexInfo
import com.jakewharton.diffuse.info.JarInfo
import com.jakewharton.diffuse.io.ByteUnit
import com.jakewharton.diffuse.io.Input
import com.jakewharton.diffuse.io.Input.Companion.asInput
import com.jakewharton.diffuse.report.Report
Expand Down Expand Up @@ -134,9 +135,14 @@ private class OutputOptions(outputFs: FileSystem, private val output: PrintStrea
)
.flag()

private val byteUnit by
option("--byte-unit", help = "Byte unit to use in reports. Default is 'binary'.")
.choice("binary" to ByteUnit.Binary, "decimal" to ByteUnit.Decimal)
.default(ByteUnit.Binary)

fun write(reportFactory: Report.Factory) {
val textReport by lazy(NONE) { reportFactory.toTextReport(summaryOnly).toString() }
val htmlReport by lazy(NONE) { reportFactory.toHtmlReport(summaryOnly).toString() }
val textReport by lazy(NONE) { reportFactory.toTextReport(summaryOnly, byteUnit).toString() }
val htmlReport by lazy(NONE) { reportFactory.toHtmlReport(summaryOnly, byteUnit).toString() }

text?.writeText(textReport)
html?.writeText(htmlReport)
Expand Down
10 changes: 10 additions & 0 deletions io/api/io.api
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
public final class com/jakewharton/diffuse/io/ByteUnit : java/lang/Enum {
public static final field Binary Lcom/jakewharton/diffuse/io/ByteUnit;
public static final field Decimal Lcom/jakewharton/diffuse/io/ByteUnit;
public static fun getEntries ()Lkotlin/enums/EnumEntries;
public static fun valueOf (Ljava/lang/String;)Lcom/jakewharton/diffuse/io/ByteUnit;
public static fun values ()[Lcom/jakewharton/diffuse/io/ByteUnit;
}

public final class com/jakewharton/diffuse/io/BytesInput : com/jakewharton/diffuse/io/Input {
public final fun getBytes ()Lokio/ByteString;
public fun getName ()Ljava/lang/String;
Expand Down Expand Up @@ -62,6 +70,8 @@ public final class com/jakewharton/diffuse/io/Size : java/lang/Comparable {
public static final fun plus-2SwpbTA (JJ)J
public fun toString ()Ljava/lang/String;
public static fun toString-impl (J)Ljava/lang/String;
public static final fun toString-impl (JLcom/jakewharton/diffuse/io/ByteUnit;)Ljava/lang/String;
public static synthetic fun toString-impl$default (JLcom/jakewharton/diffuse/io/ByteUnit;ILjava/lang/Object;)Ljava/lang/String;
public static final fun unaryMinus-kab5oJg (J)J
public final synthetic fun unbox-impl ()J
}
Expand Down
19 changes: 13 additions & 6 deletions io/src/main/kotlin/com/jakewharton/diffuse/io/Size.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,22 @@ package com.jakewharton.diffuse.io

import kotlin.math.absoluteValue
import me.saket.bytesize.binaryBytes
import me.saket.bytesize.decimalBytes

enum class ByteUnit {
Binary,
Decimal,
}

@JvmInline
value class Size(val bytes: Long) : Comparable<Size> {
override fun toString(): String =
if (bytes >= 0) {
bytes.binaryBytes.toString()
} else {
"-" + (-bytes).binaryBytes.toString()
}
override fun toString(): String = toString(ByteUnit.Binary)

fun toString(format: ByteUnit = ByteUnit.Binary): String =
when (format) {
ByteUnit.Binary -> bytes.binaryBytes
ByteUnit.Decimal -> bytes.decimalBytes
}.toString()

override fun compareTo(other: Size) = bytes.compareTo(other.bytes)

Expand Down
14 changes: 14 additions & 0 deletions io/src/test/kotlin/com/jakewharton/diffuse/io/SizeTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.jakewharton.diffuse.io

import assertk.assertThat
import assertk.assertions.hasToString
import assertk.assertions.isEqualTo
import org.junit.Test

class SizeTest {
Expand All @@ -17,4 +18,17 @@ class SizeTest {
assertThat(Size(1024L * 1024 * 1024)).hasToString("1 GiB")
assertThat(Size(-(1024L * 1024 * 1024))).hasToString("-1 GiB")
}

@Test
fun toStringDecimalFormatsBytes() {
assertThat(Size(0).toString(ByteUnit.Decimal)).isEqualTo("0 B")
assertThat(Size(1).toString(ByteUnit.Decimal)).isEqualTo("1 B")
assertThat(Size(-1).toString(ByteUnit.Decimal)).isEqualTo("-1 B")
assertThat(Size(1000).toString(ByteUnit.Decimal)).isEqualTo("1 KB")
assertThat(Size(-1000).toString(ByteUnit.Decimal)).isEqualTo("-1 KB")
assertThat(Size(1000L * 1000).toString(ByteUnit.Decimal)).isEqualTo("1 MB")
assertThat(Size(-(1000L * 1000)).toString(ByteUnit.Decimal)).isEqualTo("-1 MB")
assertThat(Size(1000L * 1000 * 1000).toString(ByteUnit.Decimal)).isEqualTo("1 GB")
assertThat(Size(-(1000L * 1000 * 1000)).toString(ByteUnit.Decimal)).isEqualTo("-1 GB")
}
}
38 changes: 22 additions & 16 deletions reports/api/reports.api
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ public abstract interface class com/jakewharton/diffuse/diff/BinaryDiff : com/ja
public static fun ofApk (Lcom/jakewharton/diffuse/format/Apk;Lcom/jakewharton/diffuse/format/ApiMapping;Lcom/jakewharton/diffuse/format/Apk;Lcom/jakewharton/diffuse/format/ApiMapping;)Lcom/jakewharton/diffuse/diff/BinaryDiff;
public static fun ofDex (Lcom/jakewharton/diffuse/format/Dex;Lcom/jakewharton/diffuse/format/ApiMapping;Lcom/jakewharton/diffuse/format/Dex;Lcom/jakewharton/diffuse/format/ApiMapping;)Lcom/jakewharton/diffuse/diff/BinaryDiff;
public static fun ofJar (Lcom/jakewharton/diffuse/format/Jar;Lcom/jakewharton/diffuse/format/ApiMapping;Lcom/jakewharton/diffuse/format/Jar;Lcom/jakewharton/diffuse/format/ApiMapping;)Lcom/jakewharton/diffuse/diff/BinaryDiff;
public abstract fun toTextReport (ZLcom/jakewharton/diffuse/io/ByteUnit;)Lcom/jakewharton/diffuse/report/Report;
}

public final class com/jakewharton/diffuse/diff/BinaryDiff$Companion {
Expand All @@ -20,25 +21,25 @@ public final class com/jakewharton/diffuse/diff/BinaryDiff$Companion {
}

public final class com/jakewharton/diffuse/diff/BinaryDiff$DefaultImpls {
public static fun toHtmlReport (Lcom/jakewharton/diffuse/diff/BinaryDiff;Z)Lcom/jakewharton/diffuse/report/Report;
public static fun toHtmlReport (Lcom/jakewharton/diffuse/diff/BinaryDiff;ZLcom/jakewharton/diffuse/io/ByteUnit;)Lcom/jakewharton/diffuse/report/Report;
}

public final class com/jakewharton/diffuse/info/AabInfo : com/jakewharton/diffuse/diff/BinaryDiff {
public fun <init> (Lcom/jakewharton/diffuse/format/Aab;)V
public fun toHtmlReport (Z)Lcom/jakewharton/diffuse/report/Report;
public fun toTextReport (Z)Lcom/jakewharton/diffuse/report/Report;
public fun toHtmlReport (ZLcom/jakewharton/diffuse/io/ByteUnit;)Lcom/jakewharton/diffuse/report/Report;
public fun toTextReport (ZLcom/jakewharton/diffuse/io/ByteUnit;)Lcom/jakewharton/diffuse/report/Report;
}

public final class com/jakewharton/diffuse/info/AarInfo : com/jakewharton/diffuse/info/BinaryInfo {
public fun <init> (Lcom/jakewharton/diffuse/format/Aar;)V
public fun toHtmlReport (Z)Lcom/jakewharton/diffuse/report/Report;
public fun toTextReport (Z)Lcom/jakewharton/diffuse/report/Report;
public fun toHtmlReport (ZLcom/jakewharton/diffuse/io/ByteUnit;)Lcom/jakewharton/diffuse/report/Report;
public fun toTextReport (ZLcom/jakewharton/diffuse/io/ByteUnit;)Lcom/jakewharton/diffuse/report/Report;
}

public final class com/jakewharton/diffuse/info/ApkInfo : com/jakewharton/diffuse/info/BinaryInfo {
public fun <init> (Lcom/jakewharton/diffuse/format/Apk;)V
public fun toHtmlReport (Z)Lcom/jakewharton/diffuse/report/Report;
public fun toTextReport (Z)Lcom/jakewharton/diffuse/report/Report;
public fun toHtmlReport (ZLcom/jakewharton/diffuse/io/ByteUnit;)Lcom/jakewharton/diffuse/report/Report;
public fun toTextReport (ZLcom/jakewharton/diffuse/io/ByteUnit;)Lcom/jakewharton/diffuse/report/Report;
}

public abstract interface class com/jakewharton/diffuse/info/BinaryInfo : com/jakewharton/diffuse/report/Report$Factory {
Expand All @@ -51,36 +52,41 @@ public final class com/jakewharton/diffuse/info/BinaryInfo$Companion {
}

public final class com/jakewharton/diffuse/info/BinaryInfo$DefaultImpls {
public static fun toHtmlReport (Lcom/jakewharton/diffuse/info/BinaryInfo;Z)Lcom/jakewharton/diffuse/report/Report;
public static fun toHtmlReport (Lcom/jakewharton/diffuse/info/BinaryInfo;ZLcom/jakewharton/diffuse/io/ByteUnit;)Lcom/jakewharton/diffuse/report/Report;
}

public final class com/jakewharton/diffuse/info/DexInfo : com/jakewharton/diffuse/info/BinaryInfo {
public fun <init> (Lcom/jakewharton/diffuse/format/Dex;)V
public fun toHtmlReport (Z)Lcom/jakewharton/diffuse/report/Report;
public fun toTextReport (Z)Lcom/jakewharton/diffuse/report/Report;
public fun toHtmlReport (ZLcom/jakewharton/diffuse/io/ByteUnit;)Lcom/jakewharton/diffuse/report/Report;
public fun toTextReport (ZLcom/jakewharton/diffuse/io/ByteUnit;)Lcom/jakewharton/diffuse/report/Report;
}

public final class com/jakewharton/diffuse/info/JarInfo : com/jakewharton/diffuse/info/BinaryInfo {
public fun <init> (Lcom/jakewharton/diffuse/format/Jar;)V
public fun toHtmlReport (Z)Lcom/jakewharton/diffuse/report/Report;
public fun toTextReport (Z)Lcom/jakewharton/diffuse/report/Report;
public fun toHtmlReport (ZLcom/jakewharton/diffuse/io/ByteUnit;)Lcom/jakewharton/diffuse/report/Report;
public fun toTextReport (ZLcom/jakewharton/diffuse/io/ByteUnit;)Lcom/jakewharton/diffuse/report/Report;
}

public abstract interface class com/jakewharton/diffuse/report/Report {
public abstract fun write (Ljava/lang/Appendable;)V
}

public abstract interface class com/jakewharton/diffuse/report/Report$Factory {
public fun toHtmlReport (Z)Lcom/jakewharton/diffuse/report/Report;
public abstract fun toTextReport (Z)Lcom/jakewharton/diffuse/report/Report;
public fun toHtmlReport (ZLcom/jakewharton/diffuse/io/ByteUnit;)Lcom/jakewharton/diffuse/report/Report;
public static synthetic fun toHtmlReport$default (Lcom/jakewharton/diffuse/report/Report$Factory;ZLcom/jakewharton/diffuse/io/ByteUnit;ILjava/lang/Object;)Lcom/jakewharton/diffuse/report/Report;
public abstract fun toTextReport (ZLcom/jakewharton/diffuse/io/ByteUnit;)Lcom/jakewharton/diffuse/report/Report;
public static synthetic fun toTextReport$default (Lcom/jakewharton/diffuse/report/Report$Factory;ZLcom/jakewharton/diffuse/io/ByteUnit;ILjava/lang/Object;)Lcom/jakewharton/diffuse/report/Report;
}

public final class com/jakewharton/diffuse/report/Report$Factory$DefaultImpls {
public static fun toHtmlReport (Lcom/jakewharton/diffuse/report/Report$Factory;Z)Lcom/jakewharton/diffuse/report/Report;
public static fun toHtmlReport (Lcom/jakewharton/diffuse/report/Report$Factory;ZLcom/jakewharton/diffuse/io/ByteUnit;)Lcom/jakewharton/diffuse/report/Report;
public static synthetic fun toHtmlReport$default (Lcom/jakewharton/diffuse/report/Report$Factory;ZLcom/jakewharton/diffuse/io/ByteUnit;ILjava/lang/Object;)Lcom/jakewharton/diffuse/report/Report;
public static synthetic fun toTextReport$default (Lcom/jakewharton/diffuse/report/Report$Factory;ZLcom/jakewharton/diffuse/io/ByteUnit;ILjava/lang/Object;)Lcom/jakewharton/diffuse/report/Report;
}

public final class com/jakewharton/diffuse/report/text/ApkInfoTextReport : com/jakewharton/diffuse/report/Report {
public fun <init> (Lcom/jakewharton/diffuse/format/Apk;)V
public fun <init> (Lcom/jakewharton/diffuse/format/Apk;Lcom/jakewharton/diffuse/io/ByteUnit;)V
public synthetic fun <init> (Lcom/jakewharton/diffuse/format/Apk;Lcom/jakewharton/diffuse/io/ByteUnit;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun toString ()Ljava/lang/String;
public fun write (Ljava/lang/Appendable;)V
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.jakewharton.diffuse.diff

import com.jakewharton.diffuse.format.Aab
import com.jakewharton.diffuse.io.ByteUnit
import com.jakewharton.diffuse.report.Report
import com.jakewharton.diffuse.report.text.AabDiffTextReport

Expand Down Expand Up @@ -29,5 +30,6 @@ internal class AabDiff(val oldAab: Aab, val newAab: Aab) : BinaryDiff {
ModuleDiff(oldModule, newAab.featureModules.getValue(name))
}

override fun toTextReport(summaryOnly: Boolean): Report = AabDiffTextReport(this, summaryOnly)
override fun toTextReport(summaryOnly: Boolean, byteUnit: ByteUnit): Report =
AabDiffTextReport(this, summaryOnly, byteUnit)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.jakewharton.diffuse.diff

import com.jakewharton.diffuse.format.Aar
import com.jakewharton.diffuse.format.ApiMapping
import com.jakewharton.diffuse.io.ByteUnit
import com.jakewharton.diffuse.report.Report
import com.jakewharton.diffuse.report.text.AarDiffTextReport

Expand All @@ -15,5 +16,6 @@ internal class AarDiff(
val jars = JarsDiff(oldAar.jars, oldMapping, newAar.jars, newMapping)
val manifest = ManifestDiff(oldAar.manifest, newAar.manifest)

override fun toTextReport(summaryOnly: Boolean): Report = AarDiffTextReport(this, summaryOnly)
override fun toTextReport(summaryOnly: Boolean, byteUnit: ByteUnit): Report =
AarDiffTextReport(this, summaryOnly, byteUnit)
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.jakewharton.diffuse.diff
import com.jakewharton.diffuse.diff.lint.resourcesArscCompression
import com.jakewharton.diffuse.format.ApiMapping
import com.jakewharton.diffuse.format.Apk
import com.jakewharton.diffuse.io.ByteUnit
import com.jakewharton.diffuse.report.Report
import com.jakewharton.diffuse.report.text.ApkDiffTextReport

Expand All @@ -24,5 +25,6 @@ internal class ApkDiff(

val lintMessages = listOfNotNull(archive.resourcesArscCompression())

override fun toTextReport(summaryOnly: Boolean): Report = ApkDiffTextReport(this, summaryOnly)
override fun toTextReport(summaryOnly: Boolean, byteUnit: ByteUnit): Report =
ApkDiffTextReport(this, summaryOnly, byteUnit)
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import com.jakewharton.diffuse.diff.ArchiveFilesDiff.Change
import com.jakewharton.diffuse.diffuseTable
import com.jakewharton.diffuse.format.ArchiveFile.Type
import com.jakewharton.diffuse.format.ArchiveFiles
import com.jakewharton.diffuse.io.ByteUnit
import com.jakewharton.diffuse.io.Size
import com.jakewharton.diffuse.report.toDiffString
import com.jakewharton.picnic.TableSectionDsl
Expand Down Expand Up @@ -98,6 +99,7 @@ internal fun ArchiveFilesDiff.toSummaryTable(
name: String,
displayTypes: List<Type>,
skipIfEmptyTypes: Set<Type> = emptySet(),
byteUnit: ByteUnit = ByteUnit.Binary,
) =
diffuseTable {
header {
Expand Down Expand Up @@ -137,19 +139,24 @@ internal fun ArchiveFilesDiff.toSummaryTable(
val newUncompressedSize =
new.values.fold(Size.ZERO) { acc, file -> acc + file.uncompressedSize }
if (oldSize != Size.ZERO || newSize != Size.ZERO || type !in skipIfEmptyTypes) {
val uncompressedDiff = (newUncompressedSize - oldUncompressedSize).toDiffString()
val uncompressedDiff = (newUncompressedSize - oldUncompressedSize).toDiffString(byteUnit)
if (includeCompressed) {
row(
name,
oldSize,
newSize,
(newSize - oldSize).toDiffString(),
oldUncompressedSize,
newUncompressedSize,
oldSize.toString(byteUnit),
newSize.toString(byteUnit),
(newSize - oldSize).toDiffString(byteUnit),
oldUncompressedSize.toString(byteUnit),
newUncompressedSize.toString(byteUnit),
uncompressedDiff,
)
} else {
row(name, oldUncompressedSize, newUncompressedSize, uncompressedDiff)
row(
name,
oldUncompressedSize.toString(byteUnit),
newUncompressedSize.toString(byteUnit),
uncompressedDiff,
)
}
}
}
Expand All @@ -168,7 +175,7 @@ internal fun ArchiveFilesDiff.toSummaryTable(
}
.renderText()

internal fun ArchiveFilesDiff.toDetailReport() = buildString {
internal fun ArchiveFilesDiff.toDetailReport(byteUnit: ByteUnit = ByteUnit.Binary) = buildString {
appendLine()
appendLine(
diffuseTable {
Expand Down Expand Up @@ -202,15 +209,15 @@ internal fun ArchiveFilesDiff.toDetailReport() = buildString {
if (includeCompressed) {
val totalSize = changes.fold(Size.ZERO) { acc, change -> acc + change.size }
val totalDiff = changes.fold(Size.ZERO) { acc, change -> acc + change.sizeDiff }
cell(totalSize) { alignment = MiddleRight }
cell(totalDiff.toDiffString()) { alignment = MiddleRight }
cell(totalSize.toString(byteUnit)) { alignment = MiddleRight }
cell(totalDiff.toDiffString(byteUnit)) { alignment = MiddleRight }
}
val totalUncompressedSize =
changes.fold(Size.ZERO) { acc, change -> acc + change.uncompressedSize }
val totalUncompressedDiff =
changes.fold(Size.ZERO) { acc, change -> acc + change.uncompressedSizeDiff }
cell(totalUncompressedSize) { alignment = MiddleRight }
cell(totalUncompressedDiff.toDiffString()) { alignment = MiddleRight }
cell(totalUncompressedSize.toString(byteUnit)) { alignment = MiddleRight }
cell(totalUncompressedDiff.toDiffString(byteUnit)) { alignment = MiddleRight }
cell("(total)")
}
}
Expand All @@ -223,13 +230,15 @@ internal fun ArchiveFilesDiff.toDetailReport() = buildString {
}
row {
if (includeCompressed) {
cell(if (type != Change.Type.Removed) size else "") { alignment = MiddleRight }
cell(sizeDiff.toDiffString()) { alignment = MiddleRight }
cell(if (type != Change.Type.Removed) size.toString(byteUnit) else "") {
alignment = MiddleRight
}
cell(sizeDiff.toDiffString(byteUnit)) { alignment = MiddleRight }
}
cell(if (type != Change.Type.Removed) uncompressedSize else "") {
cell(if (type != Change.Type.Removed) uncompressedSize.toString(byteUnit) else "") {
alignment = MiddleRight
}
cell(uncompressedSizeDiff.toDiffString()) { alignment = MiddleRight }
cell(uncompressedSizeDiff.toDiffString(byteUnit)) { alignment = MiddleRight }
cell("$typeChar $path")
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@ import com.jakewharton.diffuse.format.ApiMapping
import com.jakewharton.diffuse.format.Apk
import com.jakewharton.diffuse.format.Dex
import com.jakewharton.diffuse.format.Jar
import com.jakewharton.diffuse.io.ByteUnit
import com.jakewharton.diffuse.report.Report

interface BinaryDiff : Report.Factory {
override fun toTextReport(summaryOnly: Boolean, byteUnit: ByteUnit): Report

companion object {
@JvmStatic
fun ofApk(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import com.jakewharton.diffuse.diffuseTable
import com.jakewharton.diffuse.format.Dex
import com.jakewharton.diffuse.format.Field
import com.jakewharton.diffuse.format.Method
import com.jakewharton.diffuse.io.ByteUnit
import com.jakewharton.diffuse.report.Report
import com.jakewharton.diffuse.report.text.DexDiffTextReport
import com.jakewharton.diffuse.report.toDiffString
Expand Down Expand Up @@ -32,7 +33,8 @@ internal class DexDiff(val oldDexes: List<Dex>, val newDexes: List<Dex>) : Binar

val changed = strings.changed || types.changed || methods.changed || fields.changed

override fun toTextReport(summaryOnly: Boolean): Report = DexDiffTextReport(this, summaryOnly)
override fun toTextReport(summaryOnly: Boolean, byteUnit: ByteUnit): Report =
DexDiffTextReport(this, summaryOnly, byteUnit)
}

internal fun DexDiff.toSummaryTable() =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.jakewharton.diffuse.diff

import com.jakewharton.diffuse.format.ApiMapping
import com.jakewharton.diffuse.format.Jar
import com.jakewharton.diffuse.io.ByteUnit
import com.jakewharton.diffuse.report.Report
import com.jakewharton.diffuse.report.text.JarDiffTextReport

Expand All @@ -16,5 +17,6 @@ internal class JarDiff(

val changed = jars.changed || archive.changed

override fun toTextReport(summaryOnly: Boolean): Report = JarDiffTextReport(this, summaryOnly)
override fun toTextReport(summaryOnly: Boolean, byteUnit: ByteUnit): Report =
JarDiffTextReport(this, summaryOnly, byteUnit)
}
Loading