Skip to content

Commit

Permalink
Update result error
Browse files Browse the repository at this point in the history
  • Loading branch information
bkiac committed Nov 9, 2023
1 parent d462c20 commit 28698ec
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 75 deletions.
4 changes: 0 additions & 4 deletions src/error/panic.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
import {inspectSymbol} from "../util"

// function formatErrorString(name: string, message = "") {
// return name + (message ? ": " + message : "")
// }

export class Panic extends Error {
readonly origin?: unknown
override readonly name: string = "Panic"
Expand Down
131 changes: 81 additions & 50 deletions src/error/result_error.test.ts
Original file line number Diff line number Diff line change
@@ -1,57 +1,88 @@
import {describe, expect, it} from "vitest"
import {ResultError, StdError} from "../internal"
import {describe, expect, it, test} from "vitest"
import {ResultError, StdError, inspectSymbol} from "../internal"

describe.concurrent("ResultError", () => {
class MyResultError extends ResultError {
readonly tag = "MyResultError"
class MyResultError extends ResultError<Error> {
static _tag = "MyResultError" as const
readonly tag = MyResultError._tag

constructor() {
super(new Error())
constructor(message?: string, origin?: Error) {
super(message, origin)
}
}

it("returns instance with no args", () => {
const error = new MyResultError()

expect(error).toBeInstanceOf(ResultError)
expect(error).toBeInstanceOf(StdError)

expect(error.tag).toEqual("StdError")

expect(error.message).toEqual("")
expect(error.stack).toContain("StdError: ")
})

it("returns instance with message", () => {
const msg = "msg"
const error = new StdError(msg)

expect(error).toBeInstanceOf(ResultError)
expect(error).toBeInstanceOf(StdError)

expect(error.tag).toEqual("StdError")

expect(error.message).toEqual(msg)
expect(error.stack).toContain(`StdError: ${msg}`)
})

it("returns instance with error", () => {
let origin = new Error("msg")
let error = new StdError(origin)

expect(error).toBeInstanceOf(ResultError)
expect(error).toBeInstanceOf(StdError)

expect(error.tag).toEqual("StdError")

expect(error.origin).toEqual(origin)
expect(error.message).toEqual(origin.message)
expect(error.stack).toContain(`StdError: ${origin.message}`)

origin = new Error("msg")
origin.name = "MyError"
error = new StdError(origin)
expect(error.stack).toContain(`StdError from MyError: ${origin.message}`)
describe("returns instance", () => {
test("without params", () => {
const error = new MyResultError()

expect(error).toBeInstanceOf(ResultError)
expect(error).toBeInstanceOf(MyResultError)

expect(error.tag).toEqual(MyResultError._tag)
expect(error.name).toEqual(error.tag)
expect(error.stack).toBeDefined()

expect(error.message).toEqual("")
expect(error.origin).toBeNull()
expect(error.toString()).toEqual(error.name)
expect(error[inspectSymbol]()).toEqual(error.stack)
})

test("with message", () => {
const msg = "msg"
const error = new MyResultError(msg)

expect(error).toBeInstanceOf(ResultError)
expect(error).toBeInstanceOf(MyResultError)

expect(error.tag).toEqual(MyResultError._tag)
expect(error.name).toEqual(error.tag)
expect(error.stack).toBeDefined()

expect(error.message).toEqual(msg)
expect(error.origin).toBeNull()
expect(error.toString()).toEqual(`${error.name}: ${error.message}`)
expect(error[inspectSymbol]()).toEqual(error.stack)
})

test("with origin", () => {
let origin = new Error("msg")
let error = new MyResultError("", origin)

expect(error).toBeInstanceOf(ResultError)
expect(error).toBeInstanceOf(MyResultError)

expect(error.tag).toEqual(MyResultError._tag)
expect(error.name).toEqual(error.tag)
expect(error.stack).toBeDefined()

expect(error.message).toEqual("")
expect(error.origin).toEqual(origin)
expect(error.toString()).toEqual(
`${error.name}\nCaused by: ${error.origin?.toString()}`,
)
expect(error[inspectSymbol]()).toEqual(error.stack + `\nCaused by: ${origin.stack}`)
})

test("with message and origin", () => {
const msg = "panic message"
let origin = new Error("error message")
let error = new MyResultError(msg, origin)

expect(error).toBeInstanceOf(ResultError)
expect(error).toBeInstanceOf(MyResultError)

expect(error.tag).toEqual(MyResultError._tag)
expect(error.name).toEqual(error.tag)
expect(error.stack).toBeDefined()

expect(error.message).toEqual(msg)
expect(error.origin).toEqual(origin)
expect(error.toString()).toEqual(
`${error.name}: ${error.message}\nCaused by: ${error.origin?.toString()}`,
)
expect(error[inspectSymbol]()).toEqual(error.stack + `\nCaused by: ${origin.stack}`)
})
})
})

Expand All @@ -69,8 +100,8 @@ describe.concurrent("StdError", () => {
for (const value of values) {
const stdError = new StdError(value)
expect(stdError).toBeInstanceOf(StdError)
expect(stdError.origin).toBeInstanceOf(Error)
expect(stdError.origin.message).toContain("Unexpected error type")
expect(stdError.origin).toBeInstanceOf(TypeError)
expect(stdError.origin.message).toEqual(`Unexpected error type: "${String(value)}"`)
expect(stdError.originRaw).toEqual(value)
}
})
Expand Down
52 changes: 33 additions & 19 deletions src/error/result_error.ts
Original file line number Diff line number Diff line change
@@ -1,44 +1,58 @@
import {inspectSymbol} from "../util"
import {replaceName, replaceStack} from "./util"
import {formatErrorString} from "./util"

export abstract class ResultError<T extends Error = Error> implements Error {
export abstract class ResultError<T extends Error | null = null> implements Error {
abstract readonly tag: string

readonly message: string
readonly origin: T

constructor(origin: T) {
this.message = origin.message
this.origin = origin
readonly stack?: string
readonly origin: T | null

constructor(message?: string, origin?: T) {
this.message = message ?? ""
this.origin = origin ?? null

if (Error.captureStackTrace) {
Error.captureStackTrace(this, this.constructor)
} else {
this.stack = new Error().stack
}
}

get name() {
return replaceName(this.tag, this.origin.name)
}

get stack() {
return replaceStack(this.name, this.origin.name, this.origin.stack)
return this.tag
}

toString() {
return `${this.name}${this.message ? `: ${this.message}` : ""}`
let str = formatErrorString(this.name, this.message)
if (this.origin) {
str += `\nCaused by: ${this.origin.toString()}`
}
return str
}

[inspectSymbol]() {
return this.stack
let str = this.stack
if (this.origin) {
str += `\nCaused by: ${this.origin.stack}`
}
return str
}
}

export class StdError<T = unknown> extends ResultError {
export class StdError<T = unknown> extends ResultError<Error> {
readonly tag = "StdError"

override readonly origin: Error
readonly originRaw: T

constructor(origin: T) {
const error =
constructor(origin: T, message?: string) {
const o =
origin instanceof Error
? origin
: new Error(`Unexpected error type: "${String(origin)}"`)
super(error)
: new TypeError(`Unexpected error type: "${String(origin)}"`)
super(message)
this.origin = o
this.originRaw = origin
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/error/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ export function replaceStack(name: string, originName: string, stack?: string) {
return stack?.replace(r, name)
}

export function replaceName(name: string, originName?: string) {
return originName ? `${name} from ${originName}` : name
export function formatErrorString(name: string, message = "") {
return name + (message ? ": " + message : "")
}

0 comments on commit 28698ec

Please sign in to comment.