Skip to content
This repository has been archived by the owner on Dec 11, 2024. It is now read-only.

Commit

Permalink
PLF-102 Remove generator, NewClassM, module id in xyerror
Browse files Browse the repository at this point in the history
  • Loading branch information
huykingsofm committed Sep 2, 2022
1 parent 9d70b23 commit 159bb44
Show file tree
Hide file tree
Showing 13 changed files with 197 additions and 258 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# Developing

- Make it simple by removing Generator.

# v0.0.1 (Sep 02, 2022)

- Migrated from the [origin project](https://github.com/xybor/xyplatform).
143 changes: 57 additions & 86 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ The error is inspired by the idea of Python `Exception`.

## Python Exception Idea

Xyerror defined an error type used to create other errors called `Class`, it is
equivalent to `Exception` class in Python.
Package xyerror defined an error type used to create other errors called
`Class`, it is equivalent to `Exception` class in Python.

`Class` creates `XyError` objects by using `New` method. It looks like
`Exception` class creates `Exception` instances. Example:
Expand Down Expand Up @@ -96,112 +96,83 @@ def bar():
print(e)
```

## Error Number

Every `Class` has its own unique number called `errno`. This number is used to
compare a `XyError` with a `Class`. All `XyError` instances created by the same
`Class` have the same `errno`.

## Module-oriented Error

Xyerror is tended to create module-oriented errors. `Errno` of all `Class`
instances in a module need to be the same prefix.

For example, `ValueError` and `TypeError` are in `Default` module. So their
`errno` should be 100001 and 100002, respectively. In this case, 100000 is the
prefix number of `Default` module.

Every module needs to create an object called `Generator` to create its error
`Class` instances. Prefix of module is a self-defined value, it must be
divisible by 100000 and not equal to other modules' prefix.

```golang
var egen = xyerror.Register("XyExample", 200000)
var (
FooError = egen.NewClass("FooError")
BarError = egen.NewClass("BarError")
)
```

# Example

```golang
package xyerror_test

import (
"errors"
"fmt"
"errors"
"fmt"

"github.com/xybor-x/xyerror"
"github.com/xybor-x/xyerror"
)

var exampleGen = xyerror.Register("example", 400000)

func ExampleClass() {
// To create a root Class, call Generator.NewClass with the name of Class.
var RootError = exampleGen.NewClass("RootError")
// To create a root Class, call xyerror.NewClass with the its name.
var RootError = xyerror.NewClass("RootError")

// You can create a class from another one.
var ChildError = RootError.NewClass("ChildError")
// You can create a class by inheriting from another one.
var ChildError = RootError.NewClass("ChildError")

fmt.Println(RootError)
fmt.Println(ChildError)
fmt.Println(RootError)
fmt.Println(ChildError)

// Output:
// [400001] RootError
// [400002] ChildError
// Output:
// RootError
// ChildError
}

func ExampleXyError() {
// You can compare a XyError with an Class by using the built-in method
// errors.Is.
var NegativeIndexError = xyerror.IndexError.NewClass("NegativeIndexError")

var err1 = xyerror.ValueError.New("some value error")
if errors.Is(err1, xyerror.ValueError) {
fmt.Println("err1 is a ValueError")
}
if !errors.Is(err1, NegativeIndexError) {
fmt.Println("err1 is not a NegativeIndexError")
}

var err2 = NegativeIndexError.Newf("some negative index error %d", -1)
if errors.Is(err2, NegativeIndexError) {
fmt.Println("err2 is a NegativeIndexError")
}
if errors.Is(err2, xyerror.IndexError) {
fmt.Println("err2 is a IndexError")
}
if !errors.Is(err2, xyerror.ValueError) {
fmt.Println("err2 is not a ValueError")
}

// Output:
// err1 is a ValueError
// err1 is not a NegativeIndexError
// err2 is a NegativeIndexError
// err2 is a IndexError
// err2 is not a ValueError
// You can compare a XyError with an Class by using the built-in method
// errors.Is.
var NegativeIndexError = xyerror.IndexError.NewClass("NegativeIndexError")

var err1 = xyerror.ValueError.New("some value error")
if errors.Is(err1, xyerror.ValueError) {
fmt.Println("err1 is a ValueError")
}
if !errors.Is(err1, NegativeIndexError) {
fmt.Println("err1 is not a NegativeIndexError")
}

var err2 = NegativeIndexError.Newf("some negative index error %d", -1)
if errors.Is(err2, NegativeIndexError) {
fmt.Println("err2 is a NegativeIndexError")
}
if errors.Is(err2, xyerror.IndexError) {
fmt.Println("err2 is a IndexError")
}
if !errors.Is(err2, xyerror.ValueError) {
fmt.Println("err2 is not a ValueError")
}

// Output:
// err1 is a ValueError
// err1 is not a NegativeIndexError
// err2 is a NegativeIndexError
// err2 is a IndexError
// err2 is not a ValueError
}

func ExampleGroup() {
// Group allows you to create a class with multiparents.
var KeyValueError = xyerror.
Combine(xyerror.KeyError, xyerror.ValueError).
NewClass(exampleGen, "KeyValueError")
// Group allows you to create a class with multiparents.
var KeyValueError = xyerror.
Combine(xyerror.KeyError, xyerror.ValueError).
NewClass("KeyValueError")

var err = KeyValueError.New("something is wrong")
var err = KeyValueError.New("something is wrong")

if errors.Is(err, xyerror.KeyError) {
fmt.Println("err is a KeyError")
}
if errors.Is(err, xyerror.KeyError) {
fmt.Println("err is a KeyError")
}

if errors.Is(err, xyerror.ValueError) {
fmt.Println("err is a ValueError")
}
if errors.Is(err, xyerror.ValueError) {
fmt.Println("err is a ValueError")
}

// Output:
// err is a KeyError
// err is a ValueError
// Output:
// err is a KeyError
// err is a ValueError
}
```
54 changes: 26 additions & 28 deletions class.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,15 @@ package xyerror

import (
"fmt"

"github.com/xybor-x/xylock"
)

// Class is a special error with error number and error name. Error number is a
// unique number of Class and helps to determine which module the error Class
// belongs to.
//
// The main purpose of error Class is creating a XyError, so that it should not
// be used for returning.
//
// A Class can be created by one or many parent Classes. The Class without
// parent is called root Class..
var counter = 0
var classes []string
var lock = xylock.Lock{}

// Class is the generic type for XyError.
type Class struct {
// The unique number of each Class.
errno int
Expand All @@ -24,29 +22,29 @@ type Class struct {
parent []Class
}

// NewClass creates a root Class with error number will be determined by
// module's id in Generator.
func (gen Generator) NewClass(name string, args ...any) Class {
manager[gen].count++
// NewClass creates a root Class.
func NewClass(name string) Class {
lock.Lock()
defer lock.Unlock()

for i := range classes {
if classes[i] == name {
panic("Do not use an existed error class name: " + name)
}
}
counter++
classes = append(classes, name)

return Class{
errno: manager[gen].count + gen.id,
name: fmt.Sprintf(name, args...),
errno: counter,
name: name,
parent: nil,
}
}

// NewClass creates a new Class with called Class as parent.
func (c Class) NewClass(name string, args ...any) Class {
var gen = getGenerator(c.errno)
var class = gen.NewClass(name, args...)
class.parent = []Class{c}
return class
}

// NewClassM creates a new error class with this class as parent. It has another
// errorid and the same name.
func (c Class) NewClassM(gen Generator) Class {
var class = gen.NewClass(c.name)
// NewClass creates a new Class by inheriting the called Class.
func (c Class) NewClass(name string) Class {
var class = NewClass(name)
class.parent = []Class{c}
return class
}
Expand Down Expand Up @@ -79,5 +77,5 @@ func (c Class) belongsTo(t Class) bool {

// Error is the method to treat Class as an error.
func (c Class) Error() string {
return fmt.Sprintf("[%d] %s", c.errno, c.name)
return c.name
}
19 changes: 4 additions & 15 deletions class_test.go
Original file line number Diff line number Diff line change
@@ -1,27 +1,16 @@
package xyerror_test

import (
"strings"
"testing"

"github.com/xybor-x/xycond"
"github.com/xybor-x/xyerror"
)

func TestClassNewClass(t *testing.T) {
var id = nextid()
var egen = xyerror.Register("gen", id)
var c1 = egen.NewClass("class1")
var c1 = xyerror.NewClass("class1")
var c2 = c1.NewClass("class2")
xycond.ExpectEqual(c1.Error(), classmsg(id+1, "class1")).Test(t)
xycond.ExpectEqual(c2.Error(), classmsg(id+2, "class2")).Test(t)
}

func TestClassNewClassM(t *testing.T) {
var id1 = nextid()
var id2 = nextid()
var egen1 = xyerror.Register("gen", id1)
var egen2 = xyerror.Register("gen", id2)
var c1 = egen1.NewClass("class")
var c2 = c1.NewClassM(egen2)
xycond.ExpectEqual(c2.Error(), classmsg(id2+1, "class")).Test(t)
xycond.ExpectTrue(strings.Contains(c1.Error(), "class1")).Test(t)
xycond.ExpectTrue(strings.Contains(c2.Error(), "class2")).Test(t)
}
23 changes: 10 additions & 13 deletions default.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
package xyerror

// Default is the default error generator.
var Default = Register("default", 100000)

// Default predefined errors.
var (
Error = Default.NewClass("Error")
IOError = Default.NewClass("IOError")
FloatingPointError = Default.NewClass("FloatingPointError")
IndexError = Default.NewClass("IndexError")
KeyError = Default.NewClass("KeyError")
NotImplementedError = Default.NewClass("NotImplementedError")
ValueError = Default.NewClass("ValueError")
ParameterError = Default.NewClass("ParameterError")
TypeError = Default.NewClass("TypeError")
AssertionError = Default.NewClass("AssertionError")
Error = NewClass("Error")
IOError = NewClass("IOError")
FloatingPointError = NewClass("FloatingPointError")
IndexError = NewClass("IndexError")
KeyError = NewClass("KeyError")
NotImplementedError = NewClass("NotImplementedError")
ValueError = NewClass("ValueError")
ParameterError = NewClass("ParameterError")
TypeError = NewClass("TypeError")
AssertionError = NewClass("AssertionError")
)
6 changes: 1 addition & 5 deletions error.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,7 @@ import (
"fmt"
)

// XyError is an error supporting to check if an error belongs to a class or
// not.
//
// errors.Is(err, cls) returns true if err is created by cls itself or cls's
// child class.
// XyError is a special error belongs to a generic error class.
type XyError struct {
// error class
c Class
Expand Down
13 changes: 5 additions & 8 deletions error_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,12 @@ import (
)

func TestXyError(t *testing.T) {
var id = nextid()
var egen = xyerror.Register("", id)
var c = egen.NewClass("class")
var c = xyerror.NewClass("XError")
var xerr1 = c.Newf("error-%d", 1)
var xerr2 = c.New("error-2")

xycond.ExpectEqual(xerr1.Error(), "class: error-1").Test(t)
xycond.ExpectEqual(xerr2.Error(), "class: error-2").Test(t)
xycond.ExpectEqual(xerr1.Error(), "XError: error-1").Test(t)
xycond.ExpectEqual(xerr2.Error(), "XError: error-2").Test(t)
}

func TestXyErrorIs(t *testing.T) {
Expand All @@ -41,10 +39,9 @@ func TestOr(t *testing.T) {
}

func TestCombine(t *testing.T) {
var gen = xyerror.Register("", nextid())
var c = xyerror.Combine(xyerror.ValueError, xyerror.TypeError).
NewClass(gen, "class")
var xerr = c.New("error")
NewClass("ValueTypeError")
var xerr = c.New("foo")

xycond.ExpectError(xerr, xyerror.ValueError).Test(t)
xycond.ExpectError(xerr, xyerror.TypeError).Test(t)
Expand Down
Loading

0 comments on commit 159bb44

Please sign in to comment.