Skip to content

Commit

Permalink
enable watching file stats
Browse files Browse the repository at this point in the history
  • Loading branch information
xryu committed Dec 25, 2024
1 parent d31abbb commit 789e0d7
Show file tree
Hide file tree
Showing 4 changed files with 178 additions and 19 deletions.
97 changes: 96 additions & 1 deletion src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ tauri-build = { version = "2.0.0-beta", features = [] }
[dependencies]
tauri = { version = "2.0.0-beta", features = [] }
tauri-plugin-dialog = "2.0.0-beta.7"
tauri-plugin-fs = "2.0.0-beta.7"
tauri-plugin-fs = { version = "2.0.0-beta.7", features = [ "watch" ] }
tauri-plugin-os = "2.0.0-beta"
tauri-plugin-process = "2.0.0-beta.4"
tauri-plugin-shell = "2.0.0-beta.4"
Expand Down
35 changes: 28 additions & 7 deletions src/components/sections/PartitionView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -89,26 +89,33 @@
<field-base>{{ index + 1 }}</field-base>
</template>
<template #column-name="{ data }">
<field-base selectable :buttons="[
<field-base selectable :buttons="data.file.existed ? [
{
icon: Search16Regular,
title: '定位文件',
onClick: () => revealFile(data.file.containerPath ?? data.file.path),
onClick: () => revealFile(data.file.containerPath ?? data.file.path)
},
]">{{ data.file.name }}</field-base>
]:[]">
<n-popover>
<template #trigger>
{{ data.file.name }}
</template>
<span>{{ data.file.path }}</span>
</n-popover>
</field-base>
</template>
<template #column-addr="{ index }">
<field-addr v-model:value="state![index].addr" :placeholder="toHex(image.partitions[index].addr)"
:disabled="props.busy" @blur="() => handleAddrInputBlur(index)" />
</template>
<template #column-modified-at="{ data }">
<field-base>
<format-time :datetime="data.file.modifiedAt" />
<format-time v-if="data.file.existed" :datetime="data.file.mtime!.getTime()" />
</field-base>
</template>
<template #column-size="{ data }">
<field-base>
<file-size :size="data.file.size" />
<file-size v-if="data.file.existed" :size="data.file.size" />
</field-base>
</template>
<template #column-progress="{ index }">
Expand All @@ -126,6 +133,9 @@
<span>{{ props.errors[index] }}</span>
</n-popover>
</template>
<template v-else-if="!image.partitions[index].file.existed">
<n-text type="error">已移除</n-text>
</template>
<template v-else-if="props.progress.perPartition?.[index]?.status == FlashStatus.STOPPED">
<n-text type="error">已停止</n-text>
</template>
Expand All @@ -145,11 +155,11 @@
<n-text>未开始</n-text>
</template>
</template>
<template #column-actions="{ index }">
<template #column-actions="{ index, data }">
<n-space>
<n-tooltip>
<template #trigger>
<n-button quaternary circle size="small" @click="() => handlerRevealPart(index)">
<n-button :disabled="!data.file.existed" quaternary circle size="small" @click="() => handlerRevealPart(index)">
<template #icon>
<n-icon>
<folder-open-16-regular />
Expand Down Expand Up @@ -223,6 +233,12 @@ export interface IPartitionState {
addr: string;
}
export interface IFileStats {
size?: number;
mtime?: Date;
removed: boolean;
}
const image = defineModel<IFlashImage | null>('image');
const props = defineProps<{
Expand Down Expand Up @@ -257,6 +273,11 @@ async function handleFiles(files: string[]) {
const state = ref<IPartitionState[] | null>(null);
watch(image, (image) => {
if (image?.format == 'bin') {
image.partitions.forEach((part) => {
part.file.startWatch();
});
}
state.value = image?.format == 'bin' ? image.partitions.map((part) => ({
addr: toHex(part.addr),
})) : null;
Expand Down
63 changes: 53 additions & 10 deletions src/utils/file.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { appCacheDir, basename, join } from '@tauri-apps/api/path';
import { mkdir, readFile, remove, stat, writeFile } from '@tauri-apps/plugin-fs';
import { mkdir, readFile, remove, stat, writeFile, watch as watchFile, type WatchEvent, type DebouncedWatchOptions, type UnwatchFn, exists } from '@tauri-apps/plugin-fs';

const TEMP_DIR = 'unpacked';

Expand Down Expand Up @@ -30,6 +30,21 @@ export interface IFileRef {
*/
size: number;

/**
* Timestamp of the last modification of the file.
*/
mtime: Date;

/**
* Whether the file exists or not.
*/
existed: boolean;

/**
* Watch the file for changes.
*/
startWatch(): Promise<UnwatchFileFn>;

/**
* Read the content of the file.
*/
Expand Down Expand Up @@ -60,31 +75,59 @@ function generateTmpFileName(): string {
return `${Math.random().toString(36).substring(2)}.bin`;
}

export type UnwatchFileFn = () => void;

class BaseFile implements IFileRef {
private unwatchFn?: UnwatchFn;
mtime: Date;
size: number;
existed = true;

constructor(
readonly path: string,
readonly name: string,
readonly size: number,
readonly modifiedAt: number) {
size: number,
mtime: Date) {
this.mtime = mtime;
this.size = size;
}

async startWatch(): Promise<UnwatchFileFn> {
if (!(await exists(this.path))) {
throw new Error(`File ${this.path} does not exist`);
}
this.unwatchFn?.();
this.unwatchFn = await watchFile(this.path, async (ev) => {
console.log('into watch callback')
if ('modify' in (ev.type as any) || 'create' in (ev.type as any)) {
const newStat = await stat(this.path);
this.mtime = newStat.mtime!;
this.size = newStat.size;
this.existed = true;
} else if ('remove' in (ev.type as any)) {
this.existed = false;
}
});
return this.unwatchFn;
}

async content(): Promise<Uint8Array> {
return await readFile(this.path);
}

async free(): Promise<void> {
// do nothing
this.unwatchFn?.();
}
}

export class LocalFile extends BaseFile {
static async from(path: string): Promise<LocalFile> {
const name = await basename(path);
const { size, mtime } = await stat(path);
return new LocalFile(path, name, size, mtime!.getTime());
return new LocalFile(path, name, size, mtime!);
}

private constructor(path: string, name: string, size: number, mtime: number) {
private constructor(path: string, name: string, size: number, mtime: Date) {
super(path, name, size, mtime);
}
}
Expand All @@ -99,16 +142,16 @@ export class TmpFile extends BaseFile {
await writeFile(path, content);
const { mtime } = await stat(path);

return new TmpFile(path, name, content.length, mtime!.getTime(), containerPath);
return new TmpFile(path, name, content.length, mtime!, containerPath);
}

static clone(base: TmpFile): TmpFile {
return new TmpFile(base.path, base.name, base.size, base.modifiedAt, base.containerPath);
return new TmpFile(base.path, base.name, base.size, base.mtime, base.containerPath);
}

private constructor(path: string, name: string, size: number, readonly modifiedAt: number,
private constructor(path: string, name: string, size: number, readonly mtime: Date,
readonly containerPath?: string) {
super(path, name, size, modifiedAt);
super(path, name, size, mtime);
}

async free(): Promise<void> {
Expand Down

0 comments on commit 789e0d7

Please sign in to comment.