Files
capa/web/explorer/src/components/FunctionCapabilities.vue
2024-08-08 07:08:46 +02:00

124 lines
4.1 KiB
Vue

<template>
<DataTable
:value="tableData"
rowGroupMode="rowspan"
groupRowsBy="address"
removableSort
size="small"
:filters="filters"
:filterMode="filterMode"
:globalFilterFields="['funcaddr', 'ruleName', 'namespace']"
>
<template #header>
<IconField>
<InputIcon class="pi pi-search" />
<InputText v-model="filters['global'].value" placeholder="Global search" />
</IconField>
</template>
<Column field="address" sortable header="Function Address" :rowspan="3" class="w-min">
<template #body="{ data }">
<span class="font-monospace">{{ data.address }}</span>
<span v-if="data.matchCount > 1" class="font-italic">
({{ data.matchCount }} match{{ data.matchCount > 1 ? "es" : "" }})
</span>
</template>
</Column>
<Column field="rule" sortable header="Matches" class="w-min">
<template #body="{ data }">
{{ data.rule }}
<LibraryTag v-if="data.lib" />
</template>
</Column>
<Column field="namespace" sortable header="Namespace"></Column>
</DataTable>
<Dialog v-model:visible="sourceDialogVisible" :style="{ width: '50vw' }">
<highlightjs lang="yml" :code="currentSource" class="bg-white" />
</Dialog>
</template>
<script setup>
import { ref, computed, onMounted } from "vue";
import DataTable from "primevue/datatable";
import Column from "primevue/column";
import Dialog from "primevue/dialog";
import IconField from "primevue/iconfield";
import InputIcon from "primevue/inputicon";
import InputText from "primevue/inputtext";
import LibraryTag from "@/components/misc/LibraryTag.vue";
import { parseFunctionCapabilities } from "@/utils/rdocParser";
const props = defineProps({
data: {
type: Object,
required: true
},
showLibraryRules: {
type: Boolean,
default: false
}
});
const filters = ref({ global: { value: null, matchMode: "contains" } });
const filterMode = ref("lenient");
const sourceDialogVisible = ref(false);
const currentSource = ref("");
const functionCapabilities = ref([]);
onMounted(() => {
const cacheKey = "functionCapabilities";
let cachedData = sessionStorage.getItem(cacheKey);
if (cachedData) {
// If the data is already in sessionStorage, parse it and use it
functionCapabilities.value = JSON.parse(cachedData);
} else {
// Parse function capabilities and cache the result in sessionStorage
functionCapabilities.value = parseFunctionCapabilities(props.data);
try {
sessionStorage.setItem(cacheKey, JSON.stringify(functionCapabilities.value));
} catch (e) {
console.warn("Failed to store parsed data in sessionStorage:", e);
// If storing fails (e.g., due to storage limits), we can still continue with the parsed data
}
}
});
/*
* tableData is the data passed to the DataTable component
* it is a computed property (that is because it gets re-executed everytime props.showLibraryRules changes)
* it is an array of objects, where each object represents a row in the table
* it also converts the output of parseFunctionCapabilities into a format that can be used by the DataTable component
*/
const tableData = computed(() => {
const data = [];
for (const fcaps of functionCapabilities.value) {
const capabilities = fcaps.capabilities;
for (const capability of capabilities) {
if (capability.lib && !props.showLibraryRules) continue;
data.push({
address: fcaps.address,
matchCount: capabilities.length,
rule: capability.name,
namespace: capability.namespace,
lib: capability.lib
});
}
}
return data;
});
</script>
<style scoped>
/* tighten up the spacing between rows */
:deep(.p-datatable.p-datatable-sm .p-datatable-tbody > tr > td) {
padding: 0.2rem 0.5rem !important;
}
</style>