0.2.0 • Published 10 months ago

painless-error v0.2.0

Weekly downloads
-
License
AGPL-3.0-or-later
Repository
-
Last release
10 months ago

Painless Error

Easily create multiple types of Error and match them. You can create error in different type in a simple function call dynamically without defining a new class.

import {PainlessError} from './index.js'
const Perr = PainlessError

let n = Math.random()
try {
  if (n < 0.5) throw new Perr(`too-small: n ${n} too small`)
  else throw new Perr(`too-big: n ${n} too big`)
}
catch (err) {
  if (err.is('too-small')) n *= 2
  else if (err.is('too-big')) n /= 2
  else throw err
}

api

new

new PainlessError(name, [message, [cause]])
new PainlessError(`${name}: ${message}`, [cause])

name should be invariant. message is for human reading. cause can be any js value.

is (match)

test if an error match a name.

const err = new PainlessError('test-error/test-match: a error to test match')

err.is('test-error') == true
err.is('test-error/test-match') == true
err.is('test-match') == false
err.is('a error to test match') == false

it would be similar to the inherit error pattern, though it use the is method.

class TestError extends Error {}
class TestMatchError extends TestError {}
let tmerr = new TestMatchError()
(tmerr instanceof TestError) == true
(tmerr instanceof TestMatchError) == true

match with callback

match will execute the function whose name match the err. if nothing match and default is defined, it is executed. the longest name is matched first, and the tag is match in order.

const ctx = err.match({
  'test-error/test-match'(ctx) {
    ctx.error == err
    ctx.tag == 'test-other'
    ctx.goReturn = true
    return 'ret'
  },
  'test-other'(ctx) {
    ctx.tag == 'test-other'
    ctx.goReturn = true
  },
  default(ctx) {
    ctx.tag == 'default'
  }
})

match return a context object, which is pass to the executed callback too. you can pass state to outside by setting its properties. the matched callback's return value is stored in the ctx.result after the match return.

context would be useful if you want to return the outer function in some callback.

ctx.result == 'ret'
if (ctx.goReturn) return ctx.result

you can throw inside the callback too, thought it will introduce some more call stack.

since if ... else if ... is verbose, i write this method.

tag

you can tag a name with another name, and the error with this name will match the tag.

Perr.tagName(`${name}: ${tag1} ${tag2}`)
err = new Perr(`${name}: some message`)
err.is(tag1) == true
err.is(tag2) == true

it should be useful if you want some errors in different names could be catch with a single term.

Perr.tagName('file-miss: file-picker')
Perr.tagName('file-null: file-picker')
new Perr('file-miss').is('file-picker') == true
new Perr('file-null').is('file-picker') == true

wrap

wrap a object to a PainlessError, copy its {name, message}, and the object is store in the cause porperty.

let erro = new Error('some message')
err = Perr.wrap(erro)

err.match({
  Error() {
    console.log(`this will match since ${erro.name == 'Error'}`)
    err.name == erro.name
    err.message == erro.message
    err.cause == erro
  }
})

return the original object if the object is already a instance of PainlessError, except the second argument is true.

Perr.wrap(err) == err
Perr.wrap(err, true) != err

unwrap

return the original error if err.cause is a error, or return null.

erro = err.unwrap()
erro == err.cause

install

install from tarball, npm, or just import the index.js.

naming convension

a name should be non-space characters. i like kebab case so i use it. you can use camel case anyway, but the name always match the whole string or at the slash separator.

todo

  • wrap change the call stack
  • allow no readme msg
  • tag syntax test-error/example-error|tag1|tag2: a test error with tag
0.2.0

10 months ago

0.1.3

10 months ago

0.1.2

10 months ago

0.1.1

10 months ago

0.1.0

10 months ago