package app.git import app.TempFilesManager import app.extensions.systemSeparator import dagger.assisted.Assisted import dagger.assisted.AssistedInject import org.eclipse.jgit.diff.ContentSource import org.eclipse.jgit.diff.DiffEntry import org.eclipse.jgit.diff.RawText import org.eclipse.jgit.errors.BinaryBlobException import org.eclipse.jgit.lib.* import org.eclipse.jgit.storage.pack.PackConfig import org.eclipse.jgit.treewalk.AbstractTreeIterator import org.eclipse.jgit.treewalk.WorkingTreeIterator import org.eclipse.jgit.util.LfsFactory import java.io.FileOutputStream import java.nio.file.Path import kotlin.io.path.createTempFile private const val DEFAULT_BINARY_FILE_THRESHOLD = PackConfig.DEFAULT_BIG_FILE_THRESHOLD class RawFileManager @AssistedInject constructor( @Assisted private val repository: Repository, private val tempFilesManager: TempFilesManager, ) : AutoCloseable { private var reader: ObjectReader = repository.newObjectReader() private var source: ContentSource.Pair private val imageFormatsSupported = listOf( "png", "jpg", "jpeg", "webp", ) init { val cs = ContentSource.create(reader) source = ContentSource.Pair(cs, cs) } fun scan(oldTreeIterator: AbstractTreeIterator, newTreeIterator: AbstractTreeIterator) { source = ContentSource.Pair(source(oldTreeIterator), source(newTreeIterator)) } private fun source(iterator: AbstractTreeIterator): ContentSource { return if (iterator is WorkingTreeIterator) ContentSource.create(iterator) else ContentSource.create(reader) } fun getRawContent(side: DiffEntry.Side, entry: DiffEntry): EntryContent { if (entry.getMode(side) === FileMode.MISSING) return EntryContent.Missing if (entry.getMode(side).objectType != Constants.OBJ_BLOB) return EntryContent.InvalidObjectBlob val ldr = LfsFactory.getInstance().applySmudgeFilter( repository, source.open(side, entry), entry.diffAttribute ) return try { EntryContent.Text(RawText.load(ldr, DEFAULT_BINARY_FILE_THRESHOLD)) } catch (ex: BinaryBlobException) { if (isImage(entry)) { generateImageBinary(ldr, entry, side) } else EntryContent.Binary } } private fun generateImageBinary( ldr: ObjectLoader, entry: DiffEntry, side: DiffEntry.Side ): EntryContent.ImageBinary { val tempDir = tempFilesManager.tempDir val tempFile = createTempFile(tempDir, prefix = "${entry.newPath.replace(systemSeparator, "_")}_${side.name}") tempFile.toFile().deleteOnExit() val out = FileOutputStream(tempFile.toFile()) out.use { ldr.copyTo(out) } return EntryContent.ImageBinary(tempFile) } // todo check if it's an image checking the binary format, checking the extension is a temporary workaround private fun isImage(entry: DiffEntry): Boolean { val path = entry.newPath val fileExtension = path.split(".").lastOrNull() ?: return false return imageFormatsSupported.contains(fileExtension.lowercase()) } override fun close() { reader.close() } } sealed class EntryContent { object Missing : EntryContent() object InvalidObjectBlob : EntryContent() data class Text(val rawText: RawText) : EntryContent() sealed class BinaryContent : EntryContent() data class ImageBinary(val tempFilePath: Path) : BinaryContent() object Binary : BinaryContent() object TooLargeEntry : EntryContent() }