Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify the frontend actions #1013

Merged
merged 6 commits into from
Dec 2, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ui/frontend/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -67,6 +67,7 @@ module.exports = {
'Header.tsx',
'PopButton.tsx',
'Stdin.tsx',
'actions.ts',
'api.ts',
'compileActions.ts',
'editor/AceEditor.tsx',
@@ -96,6 +97,7 @@ module.exports = {
'reducers/selection.ts',
'reducers/versions.ts',
'reducers/websocket.ts',
'types.ts',
'websocketActions.ts',
'websocketMiddleware.ts',
],
2 changes: 2 additions & 0 deletions ui/frontend/.prettierignore
Original file line number Diff line number Diff line change
@@ -18,6 +18,7 @@ node_modules
!Header.tsx
!PopButton.tsx
!Stdin.tsx
!actions.ts
!api.ts
!compileActions.ts
!editor/AceEditor.tsx
@@ -47,5 +48,6 @@ node_modules
!reducers/selection.ts
!reducers/versions.ts
!reducers/websocket.ts
!types.ts
!websocketActions.ts
!websocketMiddleware.ts
3 changes: 2 additions & 1 deletion ui/frontend/Router.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from 'react';
import { AnyAction } from '@reduxjs/toolkit';

import { createBrowserHistory as createHistory, Path, Location } from 'history';
import { createRouter, PlainOrThunk } from './uss-router';
@@ -64,7 +65,7 @@ const stateToLocation = ({ page, configuration, output }: Substate): Partial<Pat
}
};

const locationToAction = (location: Location): PlainOrThunk<State, actions.Action> | null => {
const locationToAction = (location: Location): PlainOrThunk<State, AnyAction> | null => {
const matchedHelp = helpRoute.match(location.pathname);

if (matchedHelp) {
181 changes: 67 additions & 114 deletions ui/frontend/actions.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
import { ThunkAction as ReduxThunkAction, AnyAction } from '@reduxjs/toolkit';
import { AnyAction, ThunkAction as ReduxThunkAction } from '@reduxjs/toolkit';

import { addCrateType, editCode } from './reducers/code';
import {
getCrateType,
runAsTest,
wasmLikelyToWork,
} from './selectors';
changeBacktrace,
changeChannel,
changeEditionRaw,
changeMode,
changePrimaryAction,
} from './reducers/configuration';
import { performCompileToAssemblyOnly } from './reducers/output/assembly';
import { performCommonExecute } from './reducers/output/execute';
import { performGistLoad } from './reducers/output/gist';
import { performCompileToHirOnly } from './reducers/output/hir';
import { performCompileToLlvmIrOnly } from './reducers/output/llvmIr';
import { performCompileToMirOnly } from './reducers/output/mir';
import { performCompileToWasmOnly } from './reducers/output/wasm';
import { navigateToHelp, navigateToIndex } from './reducers/page';
import { getCrateType, runAsTest, wasmLikelyToWork } from './selectors';
import State from './state';
import {
Backtrace,
@@ -14,47 +26,20 @@ import {
PrimaryAction,
PrimaryActionAuto,
PrimaryActionCore,
parseChannel,
parseEdition,
parseMode,
} from './types';

import { performCommonExecute, wsExecuteRequest } from './reducers/output/execute';
import { performGistLoad } from './reducers/output/gist';
import { performCompileToAssemblyOnly } from './reducers/output/assembly';
import { performCompileToHirOnly } from './reducers/output/hir';
import { performCompileToLlvmIrOnly } from './reducers/output/llvmIr';
import { performCompileToMirOnly } from './reducers/output/mir';
import { performCompileToWasmOnly } from './reducers/output/wasm';
import { navigateToHelp, navigateToIndex } from './reducers/page';
import { addCrateType, editCode } from './reducers/code';
import {
changeBacktrace,
changeChannel,
changeEditionRaw,
changeMode,
changePrimaryAction,
} from './reducers/configuration';

export type ThunkAction<T = void> = ReduxThunkAction<T, State, {}, Action>;
export type SimpleThunkAction<T = void> = ReduxThunkAction<T, State, {}, AnyAction>;

const createAction = <T extends string, P extends {}>(type: T, props?: P) => (
Object.assign({ type }, props)
);

export enum ActionType {
InitializeApplication = 'INITIALIZE_APPLICATION',
}

export const initializeApplication = () => createAction(ActionType.InitializeApplication);
export type ThunkAction<T = void> = ReduxThunkAction<T, State, {}, AnyAction>;


export const reExecuteWithBacktrace = (): ThunkAction => dispatch => {
export const reExecuteWithBacktrace = (): ThunkAction => (dispatch) => {
dispatch(changeBacktrace(Backtrace.Enabled));
dispatch(performExecuteOnly());
};


function performAutoOnly(): ThunkAction {
return function(dispatch, getState) {
return function (dispatch, getState) {
const state = getState();
const crateType = getCrateType(state);
const tests = runAsTest(state);
@@ -71,7 +56,7 @@ const performTestOnly = (): ThunkAction => (dispatch, getState) => {
return dispatch(performCommonExecute(crateType, true));
};

const performCompileToNightlyHirOnly = (): ThunkAction => dispatch => {
const performCompileToNightlyHirOnly = (): ThunkAction => (dispatch) => {
dispatch(changeChannel(Channel.Nightly));
dispatch(performCompileToHirOnly());
};
@@ -103,75 +88,56 @@ export const performPrimaryAction = (): ThunkAction => (dispatch, getState) => {
dispatch(primaryAction());
};

const performAndSwitchPrimaryAction = (inner: () => ThunkAction, id: PrimaryAction) => (): ThunkAction => dispatch => {
dispatch(changePrimaryAction(id));
dispatch(inner());
};

export const performExecute =
performAndSwitchPrimaryAction(performExecuteOnly, PrimaryActionCore.Execute);
export const performCompile =
performAndSwitchPrimaryAction(performCompileOnly, PrimaryActionCore.Compile);
export const performTest =
performAndSwitchPrimaryAction(performTestOnly, PrimaryActionCore.Test);
export const performCompileToAssembly =
performAndSwitchPrimaryAction(performCompileToAssemblyOnly, PrimaryActionCore.Asm);
export const performCompileToLLVM =
performAndSwitchPrimaryAction(performCompileToLlvmIrOnly, PrimaryActionCore.LlvmIr);
export const performCompileToMir =
performAndSwitchPrimaryAction(performCompileToMirOnly, PrimaryActionCore.Mir);
export const performCompileToNightlyHir =
performAndSwitchPrimaryAction(performCompileToNightlyHirOnly, PrimaryActionCore.Hir);
export const performCompileToWasm =
performAndSwitchPrimaryAction(performCompileToCdylibWasmOnly, PrimaryActionCore.Wasm);

function parseChannel(s?: string): Channel | null {
switch (s) {
case 'stable':
return Channel.Stable;
case 'beta':
return Channel.Beta;
case 'nightly':
return Channel.Nightly;
default:
return null;
}
}

function parseMode(s?: string): Mode | null {
switch (s) {
case 'debug':
return Mode.Debug;
case 'release':
return Mode.Release;
default:
return null;
}
}
const performAndSwitchPrimaryAction =
(inner: () => ThunkAction, id: PrimaryAction) => (): ThunkAction => (dispatch) => {
dispatch(changePrimaryAction(id));
dispatch(inner());
};

function parseEdition(s?: string): Edition | null {
switch (s) {
case '2015':
return Edition.Rust2015;
case '2018':
return Edition.Rust2018;
case '2021':
return Edition.Rust2021;
case '2024':
return Edition.Rust2024;
default:
return null;
}
}
export const performExecute = performAndSwitchPrimaryAction(
performExecuteOnly,
PrimaryActionCore.Execute,
);
export const performCompile = performAndSwitchPrimaryAction(
performCompileOnly,
PrimaryActionCore.Compile,
);
export const performTest = performAndSwitchPrimaryAction(performTestOnly, PrimaryActionCore.Test);
export const performCompileToAssembly = performAndSwitchPrimaryAction(
performCompileToAssemblyOnly,
PrimaryActionCore.Asm,
);
export const performCompileToLLVM = performAndSwitchPrimaryAction(
performCompileToLlvmIrOnly,
PrimaryActionCore.LlvmIr,
);
export const performCompileToMir = performAndSwitchPrimaryAction(
performCompileToMirOnly,
PrimaryActionCore.Mir,
);
export const performCompileToNightlyHir = performAndSwitchPrimaryAction(
performCompileToNightlyHirOnly,
PrimaryActionCore.Hir,
);
export const performCompileToWasm = performAndSwitchPrimaryAction(
performCompileToCdylibWasmOnly,
PrimaryActionCore.Wasm,
);

export function indexPageLoad({
code,
gist,
version,
mode: modeString,
edition: editionString,
}: { code?: string, gist?: string, version?: string, mode?: string, edition?: string }): ThunkAction {
return function(dispatch) {
}: {
code?: string;
gist?: string;
version?: string;
mode?: string;
edition?: string;
}): ThunkAction {
return function (dispatch) {
const channel = parseChannel(version) || Channel.Stable;
const mode = parseMode(modeString) || Mode.Debug;
let maybeEdition = parseEdition(editionString);
@@ -204,21 +170,8 @@ export function indexPageLoad({
export const helpPageLoad = navigateToHelp;

export function showExample(code: string): ThunkAction {
return function(dispatch) {
return function (dispatch) {
dispatch(navigateToIndex());
dispatch(editCode(code));
};
}

export type Action =
| ReturnType<typeof initializeApplication>
| ReturnType<typeof changeBacktrace>
| ReturnType<typeof changeChannel>
| ReturnType<typeof changeEditionRaw>
| ReturnType<typeof changeMode>
| ReturnType<typeof changePrimaryAction>
| ReturnType<typeof editCode>
| ReturnType<typeof addCrateType>
| ReturnType<typeof navigateToIndex>
| ReturnType<typeof wsExecuteRequest>
;
6 changes: 3 additions & 3 deletions ui/frontend/compileActions.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { AsyncThunk, createAsyncThunk } from '@reduxjs/toolkit';
import * as z from 'zod';

import { SimpleThunkAction } from './actions';
import { ThunkAction } from './actions';
import { jsonPost, routes } from './api';
import { compileRequestPayloadSelector } from './selectors';

@@ -33,7 +33,7 @@ interface Props {

interface CompileActions {
action: AsyncThunk<CompileResponseBody, CompileRequestBody, {}>;
performCompile: () => SimpleThunkAction;
performCompile: () => ThunkAction;
}

export const makeCompileActions = ({ sliceName, target }: Props): CompileActions => {
@@ -42,7 +42,7 @@ export const makeCompileActions = ({ sliceName, target }: Props): CompileActions
return CompileResponseBody.parseAsync(d);
});

const performCompile = (): SimpleThunkAction => (dispatch, getState) => {
const performCompile = (): ThunkAction => (dispatch, getState) => {
const state = getState();
const body = compileRequestPayloadSelector(state, { target });
dispatch(action(body));
3 changes: 1 addition & 2 deletions ui/frontend/configureStore.ts
Original file line number Diff line number Diff line change
@@ -4,7 +4,6 @@ import { configureStore as reduxConfigureStore } from '@reduxjs/toolkit';
import { produce } from 'immer';
import type {} from 'redux-thunk/extend-redux';

import { initializeApplication } from './actions';
import initializeLocalStorage from './local_storage';
import initializeSessionStorage from './session_storage';
import { websocketMiddleware } from './websocketMiddleware';
@@ -19,7 +18,7 @@ export default function configureStore(window: Window) {
baseUrl,
},
};
const initialAppState = reducer(undefined, initializeApplication());
const initialAppState = reducer(undefined, { type: 'initializeForStore' });

const localStorage = initializeLocalStorage();
const sessionStorage = initializeSessionStorage();
2 changes: 1 addition & 1 deletion ui/frontend/reducers/browser.ts
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@ const initialState: State = {
isSmall: true,
};

export type State = {
type State = {
isSmall: boolean;
};

6 changes: 3 additions & 3 deletions ui/frontend/reducers/configuration.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { PayloadAction, createSlice } from '@reduxjs/toolkit';

import { SimpleThunkAction } from '../actions';
import { ThunkAction } from '../actions';
import {
AssemblyFlavor,
Backtrace,
@@ -16,7 +16,7 @@ import {
ProcessAssembly,
} from '../types';

export interface State {
interface State {
editor: Editor;
ace: {
keybinding: string;
@@ -138,7 +138,7 @@ export const {
} = slice.actions;

export const changeEdition =
(edition: Edition): SimpleThunkAction =>
(edition: Edition): ThunkAction =>
(dispatch) => {
if (edition === Edition.Rust2024) {
dispatch(changeChannel(Channel.Nightly));
2 changes: 1 addition & 1 deletion ui/frontend/reducers/crates.ts
Original file line number Diff line number Diff line change
@@ -9,7 +9,7 @@ const sliceName = 'crates';

const initialState: State = [];

export type State = Crate[];
type State = Crate[];

const CratesResponse = z.object({
crates: Crate.array(),
12 changes: 6 additions & 6 deletions ui/frontend/reducers/output/execute.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { AnyAction, Draft, createAsyncThunk, createSlice } from '@reduxjs/toolkit';
import * as z from 'zod';

import { SimpleThunkAction } from '../../actions';
import { ThunkAction } from '../../actions';
import { jsonPost, routes } from '../../api';
import { executeRequestPayloadSelector, executeViaWebsocketSelector } from '../../selectors';
import { Channel, Edition, Mode } from '../../types';
@@ -194,7 +194,7 @@ const slice = createSlice({
export const { wsExecuteRequest } = slice.actions;

export const performCommonExecute =
(crateType: string, tests: boolean): SimpleThunkAction =>
(crateType: string, tests: boolean): ThunkAction =>
(dispatch, getState) => {
const state = getState();
const body = executeRequestPayloadSelector(state, { crateType, tests });
@@ -208,7 +208,7 @@ export const performCommonExecute =
};

const dispatchWhenSequenceNumber =
<A extends AnyAction>(cb: (sequenceNumber: number) => A): SimpleThunkAction =>
<A extends AnyAction>(cb: (sequenceNumber: number) => A): ThunkAction =>
(dispatch, getState) => {
const state = getState();
const { sequenceNumber } = state.output.execute;
@@ -218,17 +218,17 @@ const dispatchWhenSequenceNumber =
}
};

export const wsExecuteStdin = (payload: string): SimpleThunkAction =>
export const wsExecuteStdin = (payload: string): ThunkAction =>
dispatchWhenSequenceNumber((sequenceNumber) =>
slice.actions.wsExecuteStdin(payload, sequenceNumber),
);

export const wsExecuteStdinClose = (): SimpleThunkAction =>
export const wsExecuteStdinClose = (): ThunkAction =>
dispatchWhenSequenceNumber((sequenceNumber) =>
slice.actions.wsExecuteStdinClose(undefined, sequenceNumber),
);

export const wsExecuteKill = (): SimpleThunkAction =>
export const wsExecuteKill = (): ThunkAction =>
dispatchWhenSequenceNumber((sequenceNumber) =>
slice.actions.wsExecuteKill(undefined, sequenceNumber),
);
2 changes: 0 additions & 2 deletions ui/frontend/reducers/output/index.ts
Original file line number Diff line number Diff line change
@@ -28,6 +28,4 @@ const output = combineReducers({
gist,
});

export type State = ReturnType<typeof output>;

export default output;
2 changes: 1 addition & 1 deletion ui/frontend/reducers/websocket.ts
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@ import z from 'zod';

import { createWebsocketResponseSchema, makeWebSocketMeta } from '../websocketActions';

export type State = {
type State = {
connected: boolean;
error?: string;
};
27 changes: 25 additions & 2 deletions ui/frontend/types.ts
Original file line number Diff line number Diff line change
@@ -7,8 +7,10 @@ export interface Position {
column: number;
}

export const makePosition = (line: string | number, column: string | number): Position =>
({ line: +line, column: +column });
export const makePosition = (line: string | number, column: string | number): Position => ({
line: +line,
column: +column,
});

export interface Selection {
start?: Position;
@@ -104,18 +106,39 @@ export enum Channel {
Nightly = 'nightly',
}

const ChannelEnum = z.nativeEnum(Channel);

export function parseChannel(s?: string): Channel | null {
const p = ChannelEnum.safeParse(s);
return p.success ? p.data : null;
}

export enum Mode {
Debug = 'debug',
Release = 'release',
}

const ModeEnum = z.nativeEnum(Mode);

export function parseMode(s?: string): Mode | null {
const p = ModeEnum.safeParse(s);
return p.success ? p.data : null;
}

export enum Edition {
Rust2015 = '2015',
Rust2018 = '2018',
Rust2021 = '2021',
Rust2024 = '2024',
}

const EditionEnum = z.nativeEnum(Edition);

export function parseEdition(s?: string): Edition | null {
const p = EditionEnum.safeParse(s);
return p.success ? p.data : null;
}

export enum Backtrace {
Disabled = 'disabled',
Enabled = 'enabled',