package org.iwakura.larcjb import com.intellij.execution.configurations.GeneralCommandLine import com.intellij.execution.process.CapturingProcessHandler import com.intellij.execution.process.ProcessOutput import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.project.Project import com.intellij.openapi.vfs.VirtualFile import java.io.File import java.nio.charset.StandardCharsets /** * Wrapper for larc CLI commands. * Similar to how Git4Idea calls git binary. */ class LarcExecutable(private val project: Project) { private val log = Logger.getInstance(LarcExecutable::class.java) fun getExecutablePath(): String { /* TODO(kroot): make configurable via settings */ return LarcVcsSettings.getInstance(project).executablePath.ifEmpty { "larc" } } /** * Execute larc command and return output */ fun execute( workingDir: VirtualFile, vararg args: String ): LarcCommandResult { return execute(File(workingDir.path), *args) } fun execute( workingDir: File, vararg args: String ): LarcCommandResult { val commandLine = GeneralCommandLine() .withExePath(getExecutablePath()) .withWorkDirectory(workingDir) .withCharset(StandardCharsets.UTF_8) .withParameters(*args) log.debug("Executing: ${commandLine.commandLineString}") return try { val handler = CapturingProcessHandler(commandLine) val output = handler.runProcess(TIMEOUT_MS) LarcCommandResult( exitCode = output.exitCode, stdout = output.stdout.trim(), stderr = output.stderr.trim(), isTimeout = output.isTimeout ) } catch (e: Exception) { log.error("Failed to execute larc command", e) LarcCommandResult( exitCode = -1, stdout = "", stderr = e.message ?: "Unknown error", isTimeout = false ) } } /* * Convenience methods for common commands */ fun status(workingDir: VirtualFile): LarcCommandResult { return execute(workingDir, "status") } fun add(workingDir: VirtualFile, vararg files: String): LarcCommandResult { return execute(workingDir, "add", *files) } fun commit( workingDir: VirtualFile, message: String, author: String ): LarcCommandResult { return execute(workingDir, "commit", "-m", message, "-a", author) } fun log(workingDir: VirtualFile, limit: Int = 10): LarcCommandResult { return execute(workingDir, "log", "-n", limit.toString()) } fun branch(workingDir: VirtualFile): LarcCommandResult { return execute(workingDir, "branch") } fun currentBranch(workingDir: VirtualFile): String? { val result = branch(workingDir) if (result.isSuccess) { /* parse branch output to find current branch (marked with *) */ return result.stdout.lines() .firstOrNull { it.trimStart().startsWith("*") } ?.substringAfter("*") ?.trim() } return null } fun version(): LarcCommandResult { return execute(File("."), "version") } companion object { private const val TIMEOUT_MS = 30_000 } } data class LarcCommandResult( val exitCode: Int, val stdout: String, val stderr: String, val isTimeout: Boolean ) { val isSuccess: Boolean get() = exitCode == 0 && !isTimeout fun getOutputOrError(): String = if (isSuccess) stdout else stderr }