Refactor towards modular structure

This commit is contained in:
Borys_Levytskyi
2021-01-14 20:48:19 +02:00
parent f671b32b63
commit 50d1606105
31 changed files with 221 additions and 154 deletions

View File

@@ -1,86 +0,0 @@
import * as expression from './expression/expression';
import React from 'react';
import uuid from 'uuid/v4';
import { CommandInput, CmdShell } from './core/cmd';
import AppState from './core/AppState';
import {ParsingError, IpAddress, ipAddressParser, IpAddressWithSubnetMask, ParsedIpObject} from './ipaddress/ip'
import ErrorResultView from './components/results/ErrorResultView';
import HelpResultView from './components/results/HelpResultView';
import AboutResultView from './components/results/AboutResultView';
import WhatsnewResultView from './components/results/WhatsNewResultView';
import TextResultView from './components/results/TextResultView';
import IpAddressView from './components/results/IpAddressView';
import UnknownInputResultView from './components/results/UnknownInputResultView';
import BitwiseOperationExpressionView from './components/results/expressions/BitwiseOperationExpressionView';
export default {
initialize (cmd: CmdShell, appState: AppState) {
cmd.debugMode = appState.debugMode;
appState.onChange(() => cmd.debugMode = appState.debugMode);
cmd.command("help", (c: CommandInput) => appState.addCommandResult(c.input, <HelpResultView />));
cmd.command("clear", () => appState.clearCommandResults());
cmd.command("em", () => appState.toggleEmphasizeBytes());
cmd.command("dark", () => appState.setUiTheme('dark'));
cmd.command("light", () => appState.setUiTheme('light'));
cmd.command("midnight", () => appState.setUiTheme('midnight'));
cmd.command("about", (c: CommandInput) => appState.addCommandResult(c.input, <AboutResultView />));
cmd.command("whatsnew", (c: CommandInput) => appState.addCommandResult(c.input, <WhatsnewResultView />));
cmd.command("guid", (c: CommandInput) => appState.addCommandResult(c.input, <TextResultView text={uuid()} />));
cmd.command("-notrack", () => {});
cmd.command("-debug", (c: CommandInput) => {
appState.toggleDebugMode();
appState.addCommandResult(c.input, <TextResultView text={`Debug Mode: ${appState.debugMode}`}/>);
});
// Ip Addresses
cmd.command({
canHandle: (input:string) => ipAddressParser.parse(input) != null,
handle: function(c: CommandInput) {
var result = ipAddressParser.parse(c.input);
if(result == null)
return;
if(result instanceof ParsingError) {
appState.addCommandResult(c.input, <ErrorResultView errorMessage={result.errorMessage} />);
return;
}
const ipAddresses : IpAddress[] = [];
(result as ParsedIpObject[]).forEach(r => {
if(r instanceof IpAddressWithSubnetMask)
{
ipAddresses.push(r.ipAddress);
ipAddresses.push(r.createSubnetMaskIp());
}
else if(r instanceof IpAddress) {
ipAddresses.push(r);
}
});
appState.addCommandResult(c.input, <IpAddressView ipAddresses={ipAddresses} />);
}
})
// Bitwise Expressions
cmd.command({
canHandle: (input:string) => expression.parser.canParse(input),
handle: function(c: CommandInput) {
var expr = expression.parser.parse(c.input);
appState.addCommandResult(c.input, <BitwiseOperationExpressionView expression={expr!} emphasizeBytes={appState.emphasizeBytes} />);
}
})
// Last command handler reports that input is unknown
cmd.command({
canHandle: () => true,
handle: (c: CommandInput) => appState.addCommandResult(c.input, <UnknownInputResultView input={c.input}/>)
});
cmd.onError((input: string, err: Error) => appState.addCommandResult(input, <ErrorResultView errorMessage={err.toString()} />));
}
}

View File

@@ -1,5 +1,5 @@
import React from 'react';
import cmd from '../core/cmd';
import cmd from '../../shell/cmd';
type CommandLinkProps = {
command?:string;

View File

@@ -1,5 +1,5 @@
import { NumericOperand, ListOfNumbersExpression, BitwiseOperationExpression, ExpressionOperand } from '../../../expression/expression';
import { ExpressionInputItem, ExpressionInput } from '../../../expression/expression-interfaces';
import { NumericOperand, ListOfNumbersExpression, BitwiseOperationExpression, ExpressionOperand } from '../expression';
import { ExpressionInputItem, ExpressionInput } from '../expression-interfaces';
type Config = {
emphasizeBytes: boolean;

View File

@@ -1,9 +1,9 @@
import React from 'react';
import formatter from '../../../core/formatter';
import BinaryStringView, { FlipBitEventArg } from '../BinaryString';
import formatter from '../../core/formatter';
import BinaryStringView, { FlipBitEventArg } from '../../core/components/BinaryString';
import BitwiseExpressionViewModel from './BitwiseExpressionModel';
import { ExpressionInput, ExpressionInputItem } from '../../../expression/expression-interfaces';
import { ExpressionOperand, NumericOperand } from '../../../expression/expression';
import { ExpressionInput, ExpressionInputItem } from '../expression-interfaces';
import { ExpressionOperand, NumericOperand } from '../expression';
type BitwiseOperationExpressionViewProps = {
expression: ExpressionInput;

21
src/expression/module.tsx Normal file
View File

@@ -0,0 +1,21 @@
import React from 'react';
import AppState from '../shell/AppState';
import { CmdShell, CommandInput } from '../shell/cmd';
import BitwiseOperationExpressionView from './components/BitwiseOperationExpressionView';
import {parser} from './expression';
const expressionAppModule = {
setup: function(appState: AppState, cmd: CmdShell) {
// Bitwise Expressions
cmd.command({
canHandle: (input:string) => parser.canParse(input),
handle: function(c: CommandInput) {
var expr = parser.parse(c.input);
appState.addCommandResult(c.input, <BitwiseOperationExpressionView expression={expr!} emphasizeBytes={appState.emphasizeBytes} />);
}
});
}
}
export default expressionAppModule;

View File

@@ -1,61 +1,41 @@
import AppState from './core/AppState';
import appStateStore from './core/appStateStore';
import cmd from './core/cmd';
import commands from './commands';
import AppRoot from './AppRoot';
import hash from './core/hash';
import cmd, { CommandInput } from './shell/cmd';
import AppRoot from './shell/components/AppRoot';
import log from 'loglevel';
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import networkingAppModule from './networking/module';
import expressionAppModule from './expression/module';
import shellModule from './shell/module';
import bootstrapAppData from './shell/startup';
import UnknownInputResultView from './shell/components/UnknownInputResultView';
const env = window.location.host === "bitwisecmd.com" ? 'prod' : 'stage';
setupLogger(env);
const appState = createAppState(env);
commands.initialize(cmd, appState);
const appData = bootstrapAppData();
initializeModules();
executeStartupCommands();
var root = <AppRoot appState={appState} />;
var root = <AppRoot appState={appData.appState} />;
ReactDOM.render(root, document.getElementById('root'));
log.debug("started");
function createAppState(env:string) {
var stateData = appStateStore.getPersistedData();
const appState = new AppState(stateData, env);
appStateStore.watch(appState);
log.debug("appState initialized", appState);
return appState;
}
function setupLogger(env: Env) {
if(env != 'prod'){
log.setLevel("debug");
log.debug(`Log level is set to debug. Env: ${env}`)
} else {
log.setLevel("warn");
}
}
function executeStartupCommands() {
var hashArgs = hash.getArgs(window.location.hash);
var startupCommands = ['help', '127.0.0.1 192.168.0.0/8', '1|2&6','4 0b1000000 0x80'];
if(appState.wasOldVersion) {
startupCommands = ["whatsnew"];
}
if(hashArgs.length > 0) {
startupCommands = hashArgs;
}
log.debug('Executing startup commands', startupCommands);
startupCommands.forEach(cmd.execute.bind(cmd));
log.debug("Executing startup commands", appData.startupCommands);
appData.startupCommands.forEach(cmd.execute.bind(cmd));
}
type Env = 'prod' | 'stage';
function initializeModules() {
shellModule.setup(appData.appState, cmd);
networkingAppModule.setup(appData.appState, cmd);
expressionAppModule.setup(appData.appState, cmd);
// Last command handler reports that input is unknown
cmd.command({
canHandle: () => true,
handle: (c: CommandInput) => appData.appState.addCommandResult(c.input, <UnknownInputResultView input={c.input}/>)
});
}

View File

@@ -1,7 +1,7 @@
import React from 'react';
import { IpAddress, OctetNumber, getNetworkClass } from '../../ipaddress/ip';
import { IpAddress, OctetNumber, getNetworkClass } from '../ip';
import formatter from '../../core/formatter'
import BinaryStringView from './BinaryString';
import BinaryStringView from '../../core/components/BinaryString';
import './IpAddressView.css';
type IpAddressViewProps = {
ipAddresses: IpAddress[]

47
src/networking/module.tsx Normal file
View File

@@ -0,0 +1,47 @@
import React from 'react';
import AppState from '../shell/AppState';
import { CmdShell, CommandInput } from '../shell/cmd';
import ErrorResultView from '../shell/components/ErrorResultView';
import IpAddressView from './components/IpAddressView';
import { ipAddressParser, ParsingError, IpAddress, ParsedIpObject, IpAddressWithSubnetMask } from './ip';
import log from 'loglevel';
const networkingAppModule = {
setup: function(appState: AppState, cmd: CmdShell) {
// Add Ip Address commands
cmd.command({
canHandle: (input:string) => ipAddressParser.parse(input) != null,
handle: function(c: CommandInput) {
var result = ipAddressParser.parse(c.input);
if(result == null)
return;
if(result instanceof ParsingError) {
appState.addCommandResult(c.input, <ErrorResultView errorMessage={result.errorMessage} />);
return;
}
const ipAddresses : IpAddress[] = [];
(result as ParsedIpObject[]).forEach(r => {
if(r instanceof IpAddressWithSubnetMask)
{
ipAddresses.push(r.ipAddress);
ipAddresses.push(r.createSubnetMaskIp());
}
else if(r instanceof IpAddress) {
ipAddresses.push(r);
}
});
appState.addCommandResult(c.input, <IpAddressView ipAddresses={ipAddresses} />);
}
});
log.debug();
}
}
export default networkingAppModule;

View File

@@ -1,5 +1,4 @@
import { CmdShell, ICommandHandler } from "./cmd";
import { func } from "prop-types";
describe("CmdShell", () => {
it("simple command", () => {

View File

@@ -1,4 +1,4 @@
import is from './is';
import is from '../core/is';
import log from 'loglevel';
export type CommandInput = {

View File

@@ -1,11 +1,11 @@
import React from 'react';
import InputBox from './components/InputBox';
import DisplayResultView from './components/DisplayResultView';
import AppState, { CommandResultView } from './core/AppState';
import cmd from './core/cmd';
import InputBox from './InputBox';
import DisplayResultView from './DisplayResultView';
import AppState, { CommandResultView } from '../AppState';
import cmd from '../cmd';
import log from 'loglevel';
import Indicators from './components/Indicators';
import hash from './core/hash';
import Indicators from './Indicators';
import hash from '../../core/hash';
type AppRootProps = {
appState: AppState,
@@ -33,7 +33,7 @@ export default class AppRoot extends React.Component<AppRootProps, AppRootState>
}
getResultViews() : JSX.Element[] {
log.debug('getting result views')
var results = this.state.commandResults.map((r, i) =>
<DisplayResultView key={r.key} input={r.input} inputHash={hash.encodeHash(r.input)} appState={this.props.appState}>
{r.view}

View File

@@ -1,5 +1,5 @@
import React from 'react';
import AppState from '../core/AppState';
import AppState from '../AppState';
type DisplayResultProps = {

View File

@@ -1,5 +1,5 @@
import React from 'react';
import CommandLink from '../CommandLink';
import CommandLink from '../../core/components/CommandLink';
import './HelpResultView.css';
function HelpResultView() {

View File

@@ -1,4 +1,4 @@
import AppState from "../core/AppState";
import AppState from "../AppState";
import React from "react";
type IndicatorsProps = {

View File

@@ -1,5 +1,5 @@
import React from 'react';
import CommandLink from '../CommandLink';
import CommandLink from '../../core/components/CommandLink';
import './WhatsNewResultView.css';
function WhatsnewResultView() {

8
src/shell/interfaces.ts Normal file
View File

@@ -0,0 +1,8 @@
import AppState from "./AppState";
import { CmdShell } from "./cmd";
export type Env = 'prod' | 'stage';
export type AppModule = {
setup: (appState: AppState, cmd: CmdShell) => void;
};

36
src/shell/module.tsx Normal file
View File

@@ -0,0 +1,36 @@
import React from 'react';
import uuid from 'uuid';
import AppState from './AppState';
import { CmdShell, CommandInput } from './cmd';
import AboutResultView from './components/AboutResultView';
import ErrorResultView from './components/ErrorResultView';
import HelpResultView from './components/HelpResultView';
import TextResultView from './components/TextResultView';
import WhatsnewResultView from './components/WhatsNewResultView';
const shellModule = {
setup: function(appState: AppState, cmd: CmdShell) {
cmd.debugMode = appState.debugMode;
appState.onChange(() => cmd.debugMode = appState.debugMode);
cmd.command("help", (c: CommandInput) => appState.addCommandResult(c.input, <HelpResultView />));
cmd.command("clear", () => appState.clearCommandResults());
cmd.command("em", () => appState.toggleEmphasizeBytes());
cmd.command("dark", () => appState.setUiTheme('dark'));
cmd.command("light", () => appState.setUiTheme('light'));
cmd.command("midnight", () => appState.setUiTheme('midnight'));
cmd.command("about", (c: CommandInput) => appState.addCommandResult(c.input, <AboutResultView />));
cmd.command("whatsnew", (c: CommandInput) => appState.addCommandResult(c.input, <WhatsnewResultView />));
cmd.command("guid", (c: CommandInput) => appState.addCommandResult(c.input, <TextResultView text={uuid()} />));
cmd.command("-notrack", () => {});
cmd.command("-debug", (c: CommandInput) => {
appState.toggleDebugMode();
appState.addCommandResult(c.input, <TextResultView text={`Debug Mode: ${appState.debugMode}`}/>);
});
cmd.onError((input: string, err: Error) => appState.addCommandResult(input, <ErrorResultView errorMessage={err.toString()} />));
}
}
export default shellModule;

62
src/shell/startup.ts Normal file
View File

@@ -0,0 +1,62 @@
import log from 'loglevel';
import hash from '../core/hash';
import AppState from './AppState';
import { Env } from './interfaces';
import appStateStore from './appStateStore';
export type StartupAppData = {
appState: AppState,
startupCommands: string[]
}
function bootstrapAppData() : StartupAppData {
const env = window.location.host === "bitwisecmd.com" ? 'prod' : 'stage';
setupLogger(env);
const appState = createAppState(env);
const startupCommands = getStartupCommands(appState);
return {
appState,
startupCommands
}
}
function createAppState(env:string) {
var stateData = appStateStore.getPersistedData();
const appState = new AppState(stateData, env);
appStateStore.watch(appState);
log.debug("appState initialized", appState);
return appState;
}
function getStartupCommands(appState : AppState) : string[] {
var hashArgs = hash.getArgs(window.location.hash);
var startupCommands = ['help', '127.0.0.1 192.168.0.0/8', '1|2&6','4 0b1000000 0x80'];
if(appState.wasOldVersion) {
startupCommands = ["whatsnew"];
}
if(hashArgs.length > 0) {
startupCommands = hashArgs;
}
log.debug('Executing startup commands', startupCommands);
return startupCommands;
}
function setupLogger(env: Env) {
if(env != 'prod'){
log.setLevel("debug");
log.debug(`Log level is set to debug. Env: ${env}`)
} else {
log.setLevel("warn");
}
}
export default bootstrapAppData;