Skip to content

Commit

Permalink
feat: implement rust for wasm
Browse files Browse the repository at this point in the history
  • Loading branch information
Gumichocopengin8 committed Aug 12, 2023
1 parent 45140a5 commit a0c01f2
Show file tree
Hide file tree
Showing 12 changed files with 573 additions and 94 deletions.
53 changes: 49 additions & 4 deletions web/src/components/[crate_names]/DownloadChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@
* LICENSE file in the root directory of this source tree.
*/

import { useMemo } from 'react';
import { useMemo, useState } from 'react';
import { useRecoilValue } from 'recoil';
import { css } from '@emotion/react';
import dayjs from 'dayjs';
import { crateDownloadDataResultsState } from 'recoil/atoms';
import { Typography } from '@mui/material';
import ReactECharts from 'components/echarts/ReactEChart';
import type { EChartsOption } from 'echarts';
import { uniform_data } from 'web_assembly/pkg/web_assembly';
import { wasmInitSelector } from 'recoil/selectors';

interface Props {
crateNames: string[];
Expand All @@ -29,8 +31,12 @@ interface ChartData {

const DownloadChart = ({ crateNames }: Props): JSX.Element => {
const crateDownloadDataResults = useRecoilValue(crateDownloadDataResultsState(crateNames));
const isWasmLoaded = useRecoilValue(wasmInitSelector);
const [perfJs, setPerfJs] = useState<number>(0);
const [perfRs, setPerfRs] = useState<number>(0);

const uniformedData: ChartData = useMemo(() => {
const t1 = performance.now();
const dates: string[] = [];
let start = dayjs().subtract(89, 'day'); // for 90 days
const end = dayjs();
Expand All @@ -51,14 +57,26 @@ const DownloadChart = ({ crateNames }: Props): JSX.Element => {
}
map.set(crateNames[index], vMap);
});

return {
const data = {
dates: dates,
data: crateNames.map((name) => {
const m = map.get(name);
return { name, downloads: map.has(name) ? Array.from(m.values()) : [] };
}),
};
const t2 = performance.now();
setPerfJs(t2 - t1);
console.log('uniformedData', t2 - t1);
return data;
}, [crateNames, crateDownloadDataResults]);

const uniformedData2: ChartData = useMemo(() => {
const t1 = performance.now();
const data: ChartData = JSON.parse(uniform_data(crateNames, JSON.stringify(crateDownloadDataResults)));
const t2 = performance.now();
setPerfRs(t2 - t1);
console.log('uniformedData2', t2 - t1);
return data;
}, [crateNames, crateDownloadDataResults]);

const option: EChartsOption = {
Expand All @@ -83,13 +101,40 @@ const DownloadChart = ({ crateNames }: Props): JSX.Element => {
series: uniformedData.data.map((d) => ({ data: d.downloads, name: d.name, type: 'line' })),
};

const option2: EChartsOption = {
animation: false,
dataZoom: [
{ realtime: true, show: true, type: 'slider' },
{ realtime: true, show: true, type: 'inside', zoomLock: true },
],
tooltip: { trigger: 'axis' },
legend: { data: crateNames, type: 'scroll', padding: [8, 200, 0, 0], left: '10%' },
toolbox: {
feature: {
dataZoom: { yAxisIndex: 'none' },
dataView: { readOnly: true },
magicType: { type: ['line', 'bar'] },
saveAsImage: {},
},
},
grid: { left: 80, right: 80 },
xAxis: { type: 'category', boundaryGap: true, data: uniformedData2.dates },
yAxis: { type: 'value' },
series: uniformedData2.data.map((d) => ({ data: d.downloads, name: d.name, type: 'line' })),
};

return (
<section>
<Typography variant="h6" component="h3" gutterBottom>
Recent Daily Downloads (90days)
</Typography>
<div>JS: {perfJs} ms</div>
<div>Rust: {perfRs} ms</div>
<div css={CrateDownloadChart}>
<ReactECharts option={option} />
<>
{isWasmLoaded && <ReactECharts option={option} />}
<ReactECharts option={option2} />
</>
</div>
</section>
);
Expand Down
8 changes: 7 additions & 1 deletion web/src/components/shared/CratesView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,14 @@ const CratesCompare = (): JSX.Element => {
const router = useRouter();
const { crate_names } = router.query;

const [isClient, setIsClient] = useState<boolean>(false);
const [crateNames, setCrateNames] = useState<string[]>([]);

useEffect(() => {
// https://nextjs.org/docs/messages/react-hydration-error
setIsClient(true);
}, []);

useEffect(() => {
if (!crate_names) return;
const crateNameList = Array.from(new Set(crate_names.toString().split('+'))); // unique!
Expand All @@ -37,7 +43,7 @@ const CratesCompare = (): JSX.Element => {
</Head>
<InputForm />
<Suspense fallback={<ChartSkelton />}>
<DownloadChart crateNames={crateNames} />
{isClient ? <DownloadChart crateNames={crateNames} /> : <ChartSkelton />}
</Suspense>
<Suspense fallback={<TableSkelton crateNames={crateNames} />}>
<CratesTable crateNames={crateNames} />
Expand Down
25 changes: 1 addition & 24 deletions web/src/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,36 +6,14 @@
*/

import React, { useState, useEffect } from 'react';
import { RecoilRoot, useSetRecoilState } from 'recoil';
import { RecoilRoot } from 'recoil';
import { AppProps } from 'next/app';
import { useRouter } from 'next/router';
import Head from 'next/head';
import { CircularProgress } from '@mui/material';
import { css } from '@emotion/react';
import Header from 'components/shared/Header';
import Footer from 'components/shared/Footer';
import init from 'web_assembly/pkg';
import { isWasmLoadedState } from 'recoil/atoms';

const HiddenComponent = () => {
// hidden compont to load wasm
// This component should be under <RecoilRoot>
const setIsWasmLoaded = useSetRecoilState(isWasmLoadedState);

useEffect(() => {
(async () => {
await init()
.then(() => {
setIsWasmLoaded(true);
})
.catch((err) => {
console.error('wasm error', err);
});
})();
}, [setIsWasmLoaded]);

return <></>;
};

const MyApp = (props: AppProps): JSX.Element => {
const { Component, pageProps } = props;
Expand All @@ -59,7 +37,6 @@ const MyApp = (props: AppProps): JSX.Element => {
return (
<React.StrictMode>
<RecoilRoot>
<HiddenComponent />
<div css={Wrapper}>
<Head>
<link rel="icon" href="/favicon.png" type="image/png" sizes="32x32" />
Expand Down
7 changes: 1 addition & 6 deletions web/src/recoil/atoms.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { atomFamily, atom } from 'recoil';
import { atomFamily } from 'recoil';
import { crateDataResultsQuery, crateDownloadDataResultsQuery } from 'recoil/selectors';
import { CrateResponse } from 'interfaces/crate';
import { Downloads } from 'interfaces/downloads';
Expand All @@ -12,8 +12,3 @@ export const crateDownloadDataResultsState = atomFamily<Downloads[], string[]>({
key: 'crateDownloadDataResultsState',
default: (crateNames) => crateDownloadDataResultsQuery(crateNames),
});

export const isWasmLoadedState = atom<boolean>({
key: 'isWasmLoaded',
default: false,
});
15 changes: 14 additions & 1 deletion web/src/recoil/selectors.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { selectorFamily, waitForAll } from 'recoil';
import { selector, selectorFamily, waitForAll } from 'recoil';
import { CrateResponse } from 'interfaces/crate';
import { Downloads } from 'interfaces/downloads';
import { fetchCrateDataUsingGET, fetchDownloadDataUsingGET } from 'api/index';
import init from 'web_assembly/pkg';

const crateDataResultQuery = selectorFamily<CrateResponse, string>({
key: 'crateDataResultQuery',
Expand Down Expand Up @@ -38,3 +39,15 @@ export const crateDownloadDataResultsQuery = selectorFamily<Downloads[], string[
return crateDataResults.filter((d) => d);
},
});

export const wasmInitSelector = selector<boolean>({
key: 'wasmInitSelector',
get: async () => {
return await init()
.then(() => true)
.catch(() => {
console.error('failed to load web assembly');
return false;
});
},
});
Loading

0 comments on commit a0c01f2

Please sign in to comment.