Add parsing for binary files

This commit is contained in:
Chooooooo 2024-10-10 19:04:36 +09:00
parent 857cf7816c
commit 3c9eb56a37
No known key found for this signature in database
GPG Key ID: 89810C752A7F1DF0
6 changed files with 124 additions and 11 deletions

1
Cargo.lock generated
View File

@ -2494,6 +2494,7 @@ name = "gitbutler-repo"
version = "0.0.0"
dependencies = [
"anyhow",
"base64 0.22.1",
"bstr",
"git2",
"gitbutler-branch",

View File

@ -1,7 +1,10 @@
<script lang="ts">
import { invoke } from '$lib/backend/ipc';
import { Project } from '$lib/backend/projects';
import HunkViewer from '$lib/hunk/HunkViewer.svelte';
import InfoMessage from '$lib/shared/InfoMessage.svelte';
import LargeDiffMessage from '$lib/shared/LargeDiffMessage.svelte';
import { getContext } from '$lib/utils/context';
import { computeAddedRemovedByHunk } from '$lib/utils/metrics';
import { getLocalCommits, getLocalAndRemoteCommits } from '$lib/vbranches/contexts';
import { getLockText } from '$lib/vbranches/tooltip';
@ -32,6 +35,7 @@
}: Props = $props();
let alwaysShow = $state(false);
const project = getContext(Project);
const localCommits = isFileLocked ? getLocalCommits() : undefined;
const remoteCommits = isFileLocked ? getLocalAndRemoteCommits() : undefined;
@ -49,11 +53,56 @@
}
const maxLineNumber = $derived(sections.at(-1)?.maxLineNumber);
const minWidth = $derived(getGutterMinWidth(maxLineNumber));
const KB = 1024;
const MB = 1024 ** 2;
const GB = 1024 ** 3;
function formatFileSize(bytes: number): string {
if (bytes < KB) return bytes + ' B';
else if (bytes < MB) return (bytes / KB).toFixed(1) + ' KB';
else if (bytes < GB) return (bytes / MB).toFixed(1) + ' MB';
else return (bytes / GB).toFixed(1) + ' GB';
}
let blobSize: number = $state(0);
let blobName: string | undefined = $state(undefined);
let blobContent: string | undefined = $state(undefined);
let imageMimeType: string | undefined = $state(undefined);
let errorMessage: string | undefined = $state(undefined);
async function fetchBlobInfo() {
try {
const blobInfo: Record<string, string> = await invoke('get_blob_info', {
relativePath: filePath,
projectId: project.id
});
blobContent = blobInfo.content;
blobName = blobInfo.name;
imageMimeType = blobInfo.mimeType;
blobSize = parseInt(blobInfo.size || '0', 10);
errorMessage = undefined;
} catch (error) {
console.error(error);
errorMessage = "untracked file";
}
}
$effect(() => {
fetchBlobInfo();
});
</script>
<div class="hunks">
{#if isBinary}
Binary content not shown
{#if errorMessage}
<p>{errorMessage}</p>
{:else if isBinary}
{#if imageMimeType && blobContent}
<img src="data:{imageMimeType};base64,{blobContent}" alt={blobName} />
{/if}
<p>Size: {formatFileSize(blobSize)}</p>
{:else if isLarge}
Diff too large to be shown
{:else if sections.length > 50 && !alwaysShow}
@ -125,4 +174,13 @@
align-items: center;
gap: 2px;
}
img {
max-width: 100%;
height: auto;
}
p {
display: flex;
flex-direction: column;
align-items: center;
}
</style>

View File

@ -40,6 +40,7 @@ gitbutler-stack.workspace = true
uuid.workspace = true
itertools = "0.13"
toml.workspace = true
base64 = "0.22.1"
[[test]]
name = "repo"

View File

@ -1,17 +1,18 @@
use crate::{Config, RepositoryExt};
use anyhow::Result;
use base64::{engine::general_purpose, Engine as _};
use gitbutler_command_context::CommandContext;
use gitbutler_project::Project;
use std::collections::HashMap;
use std::path::Path;
use crate::{Config, RepositoryExt};
pub trait RepoCommands {
fn add_remote(&self, name: &str, url: &str) -> Result<()>;
fn remotes(&self) -> Result<Vec<String>>;
fn get_local_config(&self, key: &str) -> Result<Option<String>>;
fn set_local_config(&self, key: &str, value: &str) -> Result<()>;
fn check_signing_settings(&self) -> Result<bool>;
fn read_file_from_workspace(&self, relative_path: &Path) -> Result<String>;
fn read_file_from_workspace(&self, relative_path: &Path) -> Result<HashMap<String, String>>;
}
impl RepoCommands for Project {
@ -47,7 +48,7 @@ impl RepoCommands for Project {
Ok(())
}
fn read_file_from_workspace(&self, relative_path: &Path) -> Result<String> {
fn read_file_from_workspace(&self, relative_path: &Path) -> Result<HashMap<String, String>> {
let ctx = CommandContext::open(self)?;
if self
.path
@ -60,12 +61,47 @@ impl RepoCommands for Project {
let entry = tree.get_path(relative_path)?;
let blob = ctx.repository().find_blob(entry.id())?;
let mut file_info = HashMap::new();
if !blob.is_binary() {
let content = std::str::from_utf8(blob.content())?;
Ok(content.to_string())
let content = std::str::from_utf8(blob.content())?.to_string();
file_info.insert("content".to_string(), content);
} else {
anyhow::bail!("File is binary");
let file_name = relative_path
.file_name()
.unwrap_or_default()
.to_string_lossy()
.into_owned();
let extension = relative_path
.extension()
.unwrap_or_default()
.to_string_lossy()
.to_lowercase();
file_info.insert("name".to_string(), file_name);
file_info.insert("size".to_string(), blob.size().to_string());
let image_types = [
("jpg", "image/jpeg"),
("jpeg", "image/jpeg"),
("png", "image/png"),
("gif", "image/gif"),
("svg", "image/svg+xml"),
("webp", "image/webp"),
("bmp", "image/bmp"),
];
if let Some(&(_, mime_type)) =
image_types.iter().find(|&&(ext, _)| ext == extension)
{
let binary_content = blob.content();
let encoded_content = general_purpose::STANDARD.encode(binary_content);
file_info.insert("content".to_string(), encoded_content);
file_info.insert("mimeType".to_string(), mime_type.to_string());
}
}
Ok(file_info)
} else {
anyhow::bail!("Invalid workspace file");
}

View File

@ -152,6 +152,7 @@ fn main() {
repo::commands::git_clone_repository,
repo::commands::get_uncommited_files,
repo::commands::get_pr_template_contents,
repo::commands::get_blob_info,
virtual_branches::commands::list_virtual_branches,
virtual_branches::commands::create_virtual_branch,
virtual_branches::commands::delete_local_branch,

View File

@ -1,16 +1,16 @@
pub mod commands {
use crate::error::{Error, UnmarkedError};
use anyhow::Result;
use gitbutler_branch_actions::RemoteBranchFile;
use gitbutler_project as projects;
use gitbutler_project::ProjectId;
use gitbutler_repo::RepoCommands;
use std::collections::HashMap;
use std::path::Path;
use std::sync::atomic::AtomicBool;
use tauri::State;
use tracing::instrument;
use crate::error::{Error, UnmarkedError};
#[tauri::command(async)]
#[instrument(skip(projects), err(Debug))]
pub fn git_get_local_config(
@ -77,6 +77,22 @@ pub mod commands {
) -> Result<String, Error> {
let project = projects.get(project_id)?;
Ok(project
.read_file_from_workspace(relative_path)?
.get("content")
.unwrap()
.to_string())
}
#[tauri::command(async)]
#[instrument(skip(projects))]
pub fn get_blob_info(
projects: State<'_, projects::Controller>,
project_id: ProjectId,
relative_path: &Path,
) -> Result<HashMap<String, String>, Error> {
let project = projects.get(project_id)?;
Ok(project.read_file_from_workspace(relative_path)?)
}
}