Statocyst Development Part 3
Refactoring runCommand
From our last post, we are going to modify our runCommand function to allow us to process the output. Our original function looks like this:
Her's our original
export const runCommand = async (...args: ConstructorParameters<typeof Deno.Command>): Promise<Result<string>> => {
const cmd = new Deno.Command(...args);
try {
const result = await cmd.output();
const textDecoder = new TextDecoder();
if (result.stderr.length > 0) {
return createFail(createErrorString(textDecoder.decode(result.stderr)));
}
return createSuccess(textDecoder.decode(result.stdout));
} catch (error) {
return createFail(createErrorString(error));
}
}
and instead of modifying the original function we'll create a new function call runCommandAndProcessOutput that will take in generic type T and a processor function that will take in the output of our command and return T. We'll use Dark Matter's pipeAsync function to pipe theoutput of the runCommand function through our processor function :
import { createErrorString, createFail, createSuccess, isSuccess, pipeAsync, Result } from "@joyautomation/dark-matter";
export const runCommand = async (...args: ConstructorParameters<typeof Deno.Command>): Promise<Result<string>> => {
const cmd = new Deno.Command(...args);
try {
const result = await cmd.output();
const textDecoder = new TextDecoder();
if (result.stderr.length > 0) {
return createFail(createErrorString(textDecoder.decode(result.stderr)));
}
return createSuccess(textDecoder.decode(result.stdout));
} catch (error) {
return createFail(createErrorString(error));
}
}
export const runCommandAndProcessOutput = <T>(
processor:(output:string) => T = (output:string) => output as T,
...args: ConstructorParameters<typeof Deno.Command>) =>
pipeAsync(runCommand(...args),
(result) => {
if(isSuccess(result)) {
return createSuccess(processor(result.output))
} else {
return result
}
}
)
This turns changes our isNftInstalled and installNft from this:
export const isNftInstalled = ():Promise<Result<boolean>> =>
runCommand( 'which', { args: ['nft'] })
.then((result) => {
if(isSuccess(result)) {
return createSuccess(result.output !== '')
} else {
return result
}
})
export const installNft = (): Promise<Result<void>> =>
runCommand('sudo', { args: ['apt', 'install', 'nftables', '-y'] })
.then((result) => {
if (isSuccess(result)) {
return createSuccess(void 0)
} else {
return result
}
})
to this:
export const isNftInstalled = (): Promise<Result<boolean>> =>
runCommandAndProcessOutput<boolean>((output) => output !== '', 'which', {
args: ['nft'],
})
export const installNft = (): Promise<Result<void>> =>
runCommandAndProcessOutput<void>(undefined, 'sudo', {
args: ['apt', 'install', 'nftables', '-y'],
})
which is much more concise and a lot less boiler plate!
Now we can continue by creating an installIfNftMissing function!