Compare commits

...

2 Commits

Author SHA1 Message Date
baab2e75d9 Progress on canvas module 2025-05-27 17:42:24 -05:00
4f1847efa8 Add Cursor error type 2025-05-27 17:42:09 -05:00
2 changed files with 55 additions and 13 deletions

View File

@ -1,4 +1,6 @@
import { type CursorPos } from 'node:readline' import { type CursorPos } from 'node:readline'
import { type Either, fold, type Left, left, right } from '../util/basic/either'
import { PaintCursorError } from './errors'
// Cursor stuffs // Cursor stuffs
interface CursorPosX { interface CursorPosX {
@ -11,14 +13,35 @@ interface CursorPosY {
readonly value: number readonly value: number
} }
const cursorPosX = (x: number): CursorPosX => ({_tag: 'X', value: x}) type CursorPosVal = (str: 'X' | 'Y') => (x: number) => CursorPosX | CursorPosY
const cursorPosVal: CursorPosVal = str => x => ({_tag: str, value: x})
const cursorPosY = (x: number): CursorPosY => ({_tag: 'Y', value: x}) type IsCursorPos = (x: 'X' | 'Y') => (y: CursorPosX | CursorPosY) => boolean
type IsCursorPos =
(x: 'X' | 'Y') => (y: CursorPosX | CursorPosY) => boolean
const isCursorPos: IsCursorPos = x => y => y._tag === x const isCursorPos: IsCursorPos = x => y => y._tag === x
const cursorPos = (x: number) => (y: number): CursorPos => ({rows: x, cols: y})
// Cursor Position
type SafeCursorPos =
(x: CursorPosX | CursorPosY) =>
(y: CursorPosX | CursorPosY) => Either<CursorPos, Error>
const safeCursorPos: SafeCursorPos = (x) => y => {
return isCursorPos('X')(x) && isCursorPos('Y')(y)
? right(cursorPos(x.value)(y.value))
: isCursorPos('Y')(x) && isCursorPos('X')(y)
? right(cursorPos(y.value)(x.value))
: left(
new PaintCursorError(
`Both arguments provided were of the same axis, or otherwise invalid.`,
{x, y},
),
)
}
console.log(
safeCursorPos(cursorPosVal('X')(5))(cursorPosVal('X')(7)),
)
// Tagged types // Tagged types
interface CanvasOrigin { interface CanvasOrigin {
readonly _tag: 'Origin' readonly _tag: 'Origin'
@ -30,15 +53,23 @@ interface CanvasEnd {
readonly value: CursorPos readonly value: CursorPos
} }
type CanvasOriginConstructor = (x: SafeCursorPos) => Either<CanvasOrigin, E>
const canvasOrigin = (x: CursorPos): CanvasOrigin => ({
_tag: 'Origin',
value: x,
})
const canvasEnd = (x: CursorPos): CanvasEnd => ({
_tag: 'End',
value: x,
})
// Canvas type using tagged positions // Canvas type using tagged positions
type Canvas = { type Canvas = {
origin: CanvasOrigin, origin: CanvasOrigin,
end: CanvasEnd, end: CanvasEnd,
} }
// Cursor Position
// const cursorPos: CursorPos = ()
// Constructor helpers // Constructor helpers
const origin = (pos: CursorPos): CanvasOrigin => ({ const origin = (pos: CursorPos): CanvasOrigin => ({
_tag: 'Origin', _tag: 'Origin',
@ -51,11 +82,10 @@ const end = (pos: CursorPos): CanvasEnd => ({
}) })
// Curried canvas creator with type safety // Curried canvas creator with type safety
const canvas = const canvas = (origin: CanvasOrigin) => (end: CanvasEnd): Canvas => ({
(origin: CanvasOrigin) => (end: CanvasEnd): Canvas => ({ origin,
origin, end,
end, })
})
// Usage with proper type safety // Usage with proper type safety
const myCanvas = canvas(origin({rows: 3, cols: 1}))( const myCanvas = canvas(origin({rows: 3, cols: 1}))(

12
src/lib/painter/errors.ts Normal file
View File

@ -0,0 +1,12 @@
export class PaintCursorError extends Error {
cursorX
cursorY
constructor(message: string, {x, y}: {x: unknown, y: unknown}) {
super(message)
this.name = this.constructor.name
this.cursorX = !x ? 'No data' : x
this.cursorY = !y ? 'No data' : y
}
}