Skip to content

Commit f3b6baf

Browse files
committed
start some tests
1 parent 1333cc3 commit f3b6baf

File tree

16 files changed

+206
-26
lines changed

16 files changed

+206
-26
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,5 @@ next-env.d.ts
3838
.env
3939

4040
/host
41+
42+
/test/hold

.npmignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,4 @@ tsconfig.json
1717
.dockerignore
1818
Dockerfile*
1919
.gitattributes
20+
/test/hold

code/cli/error.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import Kink, { KinkList } from '@termsurf/kink'
2-
import { logWithSpace } from '../shared/logger'
2+
import { logWithSpace } from '../shared/logger.js'
33
import { makeBaseKinkText, makeKinkText } from '@termsurf/kink-text'
44

55
export function logError(kink) {

code/cli/logging.ts

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
import stripAnsi from 'strip-ansi'
22
import tint, { Tint } from '@termsurf/tint-text'
33
import ora from 'ora'
4-
import chalk from 'chalk'
5-
import { log, logWithSpace } from '../shared/logger'
4+
import { log, logWithSpace } from '../shared/logger.js'
65

76
const M: Tint = { tone: 'magenta' }
87
const C: Tint = { tone: 'cyan' }
9-
const B: Tint = { tone: 'gray' }
8+
const B: Tint = { tone: 'white' }
109
const G: Tint = { tone: 'green' }
1110
const R: Tint = { tone: 'red' }
1211

@@ -26,7 +25,8 @@ export function logStart(text: string, color = true) {
2625
}
2726

2827
export function logSpinnerWithSpace(text: string) {
29-
return ora({ prefixText: '\n ', suffixText: '\n' }).start(text)
28+
// return ora({ prefixText: '\n ', suffixText: '\n' }).start(text)
29+
return ora().start(text)
3030
}
3131

3232
export function logSpinner(text: string) {
@@ -47,6 +47,20 @@ export function logOutput(text: string, color = true) {
4747
}
4848
}
4949

50+
export function logOutputError(text: string, color = true) {
51+
if (!text) {
52+
return
53+
}
54+
55+
if (color) {
56+
logWithSpace(
57+
` ${tint('link <', B)}${tint(text, R)}${tint('>', B)}`,
58+
)
59+
} else {
60+
log(text)
61+
}
62+
}
63+
5064
export function renderProgress(text: string, color = true) {
5165
if (color) {
5266
return `${tint('note <', B)}${tint(text, C)}${tint('>', B)}`

code/cli/parse.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Form, FormLink } from '@termsurf/form'
22
import { Input, Link, Value } from './type'
3+
import _ from 'lodash'
34

45
export function transferInput(source, map: Record<string, Link>) {
56
const out: Record<string, Value | Array<Value>> = {}

code/cli/task.ts

Lines changed: 56 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ import {
99
} from '../node/document.js'
1010
import { convertFontWithFontForge } from '../node/font.js'
1111
import { convertVideoWithFfmpeg } from '../node/video.js'
12-
import { convertImageWithImageMagick } from '../node/image.js'
12+
import {
13+
convertImageWithImageMagick,
14+
verifyImageWithImageMagick,
15+
} from '../node/image.js'
1316
import {
1417
build_command_to_convert_document_with_calibre,
1518
build_command_to_convert_document_with_libre_office,
@@ -19,7 +22,12 @@ import {
1922
build_command_to_process_image,
2023
} from '../shared/type/source/index.js'
2124
import { Form } from '@termsurf/form'
22-
import { BuildFormatInputOutputModel, Task } from '../shared/index.js'
25+
import {
26+
BuildFormatInputOutputModel,
27+
IMAGE_MAGICK_FORMAT,
28+
ImageMagickFormat,
29+
Task,
30+
} from '../shared/index.js'
2331
import kink from '../shared/kink.js'
2432
import {
2533
useConvertDocumentWithCalibre,
@@ -29,7 +37,12 @@ import {
2937
useConvertImageWithImageMagick,
3038
useConvertVideoWithFfmpeg,
3139
} from '../node/call/convert.js'
32-
import { logOutput, logStart, renderProgress } from './logging.js'
40+
import {
41+
logOutput,
42+
logOutputError,
43+
logStart,
44+
renderProgress,
45+
} from './logging.js'
3346
import {
3447
buildCliOptions,
3548
buildInputMapping,
@@ -48,6 +61,15 @@ export const CONVERT_KEY: Record<string, Link> = {
4861
// -l color -l plain
4962
}
5063

64+
export const INPUT_KEY: Record<string, Link> = {
65+
i: { line: ['input', 'file', 'path'] },
66+
I: { line: ['input', 'format'], need: false },
67+
b: { line: ['input', 'directory', 'path'], need: false },
68+
h: { line: ['help'], need: false, like: 'boolean' },
69+
l: { line: ['log'], need: false, like: 'string' }, // -l json to output json
70+
// -l color -l plain
71+
}
72+
5173
export async function call(task: Task, source) {
5274
switch (task) {
5375
case 'convert': {
@@ -127,12 +149,19 @@ export async function call(task: Task, source) {
127149
if (source.help) {
128150
return showHelpForConvert(form)
129151
}
130-
const spinner = logStart(`Converting image...`, isColor)
131-
const input = transferInput(source, buildInputMapping(form))
132-
const out = await convertImageWithImageMagick(input)
133-
spinner?.stop()
134-
logOutput(out, isColor)
135-
return
152+
let spinner
153+
154+
try {
155+
spinner = logStart(`Converting image...`, isColor)
156+
const input = transferInput(source, buildInputMapping(form))
157+
const out = await convertImageWithImageMagick(input)
158+
spinner?.stop()
159+
logOutput(out, isColor)
160+
return
161+
} catch (e) {
162+
spinner?.stop()
163+
throw e
164+
}
136165
}
137166

138167
if (
@@ -181,6 +210,24 @@ export async function call(task: Task, source) {
181210
case 'decompress': {
182211
break
183212
}
213+
case 'verify': {
214+
const base = transferInput(source, INPUT_KEY)
215+
if (
216+
'input' in base &&
217+
typeof base.input === 'object' &&
218+
'format' in base.input &&
219+
IMAGE_MAGICK_FORMAT.includes(
220+
base.input.format as ImageMagickFormat,
221+
)
222+
) {
223+
if (await verifyImageWithImageMagick(base)) {
224+
logOutput(`Image is a ${base.input.format}.`)
225+
} else {
226+
logOutputError(`Image is not ${base.input.format}.`)
227+
}
228+
return
229+
}
230+
}
184231
}
185232

186233
throw kink('task_not_implemented', {

code/node/command.ts

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,21 @@
11
import bytes from 'bytes'
22
import child_process from 'child_process'
3-
import { Command, CommandCall, CommandName } from '../shared/index.js'
3+
import {
4+
Command,
5+
CommandCall,
6+
CommandName,
7+
IMAGE_MAGICK_FORMAT,
8+
} from '../shared/index.js'
49
import kink from '../shared/kink.js'
510
import { ChildProcessError, Message, exec } from './process.js'
611
import Kink from '@termsurf/kink'
12+
import _ from 'lodash'
713

814
export type CommandHandle = (cmd: CommandCall) => Promise<string | void>
915

1016
export const COMMAND_HANDLE: Record<CommandName, CommandHandle> = {
17+
patool: handlePatoolCommand,
18+
identify: handleIdentifyCommand,
1119
ffmpeg: handleFfmpegCommand,
1220
black: handleBlackCommand,
1321
asmfmt: handleAsmfmtCommand,
@@ -86,6 +94,10 @@ export async function handleGifsicleCommand(cmd: CommandCall) {
8694
return await exec(cmd.join(' '))
8795
}
8896

97+
export async function handlePatoolCommand(cmd: CommandCall) {
98+
return await exec(cmd.join(' '))
99+
}
100+
89101
export async function handleBlackCommand(cmd: CommandCall) {
90102
return await exec(cmd.join(' '))
91103
}
@@ -106,7 +118,7 @@ export async function handleConvertCommand(cmd: CommandCall) {
106118
if (e.data.stderr) {
107119
if (e.data.stderr.match(/^convert: unable to open image/i)) {
108120
// throw new Kink
109-
throw new Error(`Cannot process image`)
121+
throw new Error(`Cannot process image.`)
110122
}
111123
}
112124
} else {
@@ -140,6 +152,16 @@ export async function handleCommand(cmd: Command) {
140152
return await handle(cmd.list)
141153
}
142154

155+
export async function handleIdentifyCommand(cmd: CommandCall) {
156+
const text = await exec(cmd.join(' '))
157+
const pattern = new RegExp(`^([^\\s]+)\\s+(\\w+)`, 'i')
158+
text.match(pattern)
159+
return {
160+
path: RegExp.$1,
161+
format: _.snakeCase(RegExp.$2),
162+
}
163+
}
164+
143165
export async function handleJavaCommand(cmd: CommandCall) {
144166
return await exec(cmd.join(' '))
145167
}

code/node/image.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@ import _ from 'lodash'
22
import {
33
BuildCommandToConvertAiToSvgWithInkscapeModel,
44
BuildCommandToProcessImageModel,
5+
IMAGE_MAGICK_FORMAT,
56
IMAGE_MAGICK_INPUT_FORMAT,
67
IMAGE_MAGICK_OUTPUT_FORMAT,
78
INKSCAPE_EXPORT_FORMAT,
89
} from '../shared/type/index.js'
910
import {
1011
buildCommandToConvertAIToSVGWithInkscape,
1112
buildCommandToProcessImage,
13+
buildCommandToVerifyImageWithImageMagick,
1214
} from '../shared/index.js'
1315
import {
1416
buildUseConvert,
@@ -70,3 +72,37 @@ export async function convertImageWithInkscape(source) {
7072
}
7173
return input.output.file.path
7274
}
75+
76+
export async function verifyImageWithImageMagick(source) {
77+
const [cmd] = await buildCommandToVerifyImageWithImageMagick(source)
78+
if (cmd) {
79+
const data = await handleCommand(cmd)
80+
if (
81+
IMAGE_MAGICK_FORMAT.includes(data.format) &&
82+
isFormatMatch(data.format, source.input.format)
83+
) {
84+
return true
85+
}
86+
}
87+
return false
88+
}
89+
90+
const IMAGEMAGICK_FORMAT_VARIANT_NAME: Record<string, Array<string>> = {
91+
jpeg: ['jpg'],
92+
jpg: ['jpeg'],
93+
}
94+
95+
function isFormatMatch(a: string, b: string) {
96+
if (a === b) {
97+
return true
98+
}
99+
const v = IMAGEMAGICK_FORMAT_VARIANT_NAME[a]
100+
if (v) {
101+
for (const x of v) {
102+
if (x === b) {
103+
return true
104+
}
105+
}
106+
}
107+
return false
108+
}

code/shared/command.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ export const COMMAND_NAME = [
3636
'unoconv',
3737
'gifsicle',
3838
'patool',
39+
'identify',
3940
] as const
4041

4142
export type CommandName = (typeof COMMAND_NAME)[number]
@@ -65,6 +66,7 @@ export const COMMAND: Record<CommandName, Array<string> | undefined> = {
6566
rustc: ['rustc'],
6667
rustfmt: ['rustfmt'],
6768
gifsicle: ['gifsicle'],
69+
identify: ['identify'],
6870
rubocop: ['rubocop'],
6971
shfmt: ['shfmt'],
7072
zip: ['zip'],

code/shared/image.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,14 @@ export async function buildCommandToConvertAIToSVGWithInkscape(
277277
return [cmd]
278278
}
279279

280+
export async function buildCommandToVerifyImageWithImageMagick(input) {
281+
const cmd = getCommand(`identify`)
282+
283+
cmd.list.push(`"${input.input.file.path}"`)
284+
285+
return [cmd]
286+
}
287+
280288
export type ExifMetadata = { name: string; bond: string | number }
281289

282290
export function parseImageMetadataFromExifTool(lines: Array<string>) {

0 commit comments

Comments
 (0)