diff --git a/CHANGELOG.md b/CHANGELOG.md index 37cad08..7a6b2a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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). diff --git a/README.md b/README.md index e52a57d..cfa12b5 100644 --- a/README.md +++ b/README.md @@ -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: @@ -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 } ``` diff --git a/class.go b/class.go index 914b222..f27494c 100644 --- a/class.go +++ b/class.go @@ -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 @@ -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 } @@ -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 } diff --git a/class_test.go b/class_test.go index 1d5526b..06cde73 100644 --- a/class_test.go +++ b/class_test.go @@ -1,6 +1,7 @@ package xyerror_test import ( + "strings" "testing" "github.com/xybor-x/xycond" @@ -8,20 +9,8 @@ import ( ) 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) } diff --git a/default.go b/default.go index 8b8b3c4..8f92996 100644 --- a/default.go +++ b/default.go @@ -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") ) diff --git a/error.go b/error.go index c540343..57ff8a4 100644 --- a/error.go +++ b/error.go @@ -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 diff --git a/error_test.go b/error_test.go index 2484d88..eee3934 100644 --- a/error_test.go +++ b/error_test.go @@ -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) { @@ -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) diff --git a/example_test.go b/example_test.go new file mode 100644 index 0000000..99679ad --- /dev/null +++ b/example_test.go @@ -0,0 +1,76 @@ +package xyerror_test + +import ( + "errors" + "fmt" + + "github.com/xybor-x/xyerror" +) + +func ExampleClass() { + // To create a root Class, call xyerror.NewClass with the its name. + var RootError = xyerror.NewClass("RootError") + + // You can create a class by inheriting from another one. + var ChildError = RootError.NewClass("ChildError") + + fmt.Println(RootError) + fmt.Println(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 +} + +func ExampleGroup() { + // 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") + + if errors.Is(err, xyerror.KeyError) { + fmt.Println("err is a KeyError") + } + + if errors.Is(err, xyerror.ValueError) { + fmt.Println("err is a ValueError") + } + + // Output: + // err is a KeyError + // err is a ValueError +} diff --git a/go.mod b/go.mod index 75a81c3..2e27684 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,9 @@ module github.com/xybor-x/xyerror go 1.18 -require github.com/xybor-x/xycond v0.0.1 +require ( + github.com/xybor-x/xycond v0.0.1 + github.com/xybor-x/xylock v0.0.1 +) + +require golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde // indirect diff --git a/go.sum b/go.sum index effe0db..cca673b 100644 --- a/go.sum +++ b/go.sum @@ -1,2 +1,6 @@ -github.com/xybor-x/xycond v0.0.0-20220901043621-c68af596b26f h1:ScbaR2k7EZZOb3yoADpQJkEv9bb67L8syq4DbqkKLCY= -github.com/xybor-x/xycond v0.0.0-20220901043621-c68af596b26f/go.mod h1:PuDr8bAjLq9M2/rgAo5o+IBa+gYZryWhop9XtGiHVOo= +github.com/xybor-x/xycond v0.0.1 h1:9NWWLNHfAEjJTEuprpLN7vTw3aXbGw2EElJ00L6v6Js= +github.com/xybor-x/xycond v0.0.1/go.mod h1:PuDr8bAjLq9M2/rgAo5o+IBa+gYZryWhop9XtGiHVOo= +github.com/xybor-x/xylock v0.0.1 h1:/LhbJb6WJkUW+xE4/bBpIC76BKrbgnayiId6LdouqUw= +github.com/xybor-x/xylock v0.0.1/go.mod h1:IQvixGhBrgKdh0HcCJAqzYzk482SGyYOzezBxK6UdRc= +golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde h1:ejfdSekXMDxDLbRrJMwUk6KnSLZ2McaUCVcIKM+N6jc= +golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/group.go b/group.go index d57e412..32effa8 100644 --- a/group.go +++ b/group.go @@ -1,9 +1,5 @@ package xyerror -import ( - "fmt" -) - // Group is an array of class. It supports to creates a Class inherited from // many parents. type Group []Class @@ -15,8 +11,8 @@ func Combine(cs ...Class) Group { } // NewClass creates a Class with multiparents. -func (g Group) NewClass(gen Generator, name string, a ...any) Class { - var child = gen.NewClass(fmt.Sprintf(name, a...)) +func (g Group) NewClass(name string) Class { + var child = NewClass(name) child.parent = g return child } diff --git a/manage.go b/manage.go deleted file mode 100644 index e7dbd76..0000000 --- a/manage.go +++ /dev/null @@ -1,56 +0,0 @@ -// Package xyerror supports to define error types conveniently. -package xyerror - -import ( - "log" -) - -// Generator is used to generate root Class for every module. It is determined -// by the identifier of module. -type Generator struct { - // The identifier of module. - id int -} - -// erroinfo includes the name and the number of created errors of an error id. -type errorinfo struct { - name string - count int -} - -// The minimum and default id of module -var minid = 100000 - -// manager is a map of errorid as key and errorinfo as value. -var manager = make(map[Generator]*errorinfo) - -// getGenerator returns the Generator with the given errno. -func getGenerator(errno int) Generator { - for gen := range manager { - var d = errno - gen.id - if d < 0 || d > gen.id { - continue - } - - if d < minid { - return gen - } - } - - return Generator{0} -} - -// Register adds a Module with its identifier to managing pool for creating new -// Classes. -func Register(name string, id int) Generator { - if id%minid != 0 { - log.Panicf("Cannot register, %d is not divisible by %d", id, minid) - } - var gen = Generator{id} - if _, ok := manager[gen]; ok { - log.Panicf("id %d had already registered", id) - } - - manager[gen] = &errorinfo{name: name, count: 0} - return gen -} diff --git a/manage_test.go b/manage_test.go deleted file mode 100644 index 0bdeb78..0000000 --- a/manage_test.go +++ /dev/null @@ -1,38 +0,0 @@ -package xyerror_test - -import ( - "fmt" - "testing" - - "github.com/xybor-x/xycond" - "github.com/xybor-x/xyerror" -) - -var autoid = 1000000 - -func nextid() int { - autoid += 100000 - return autoid -} - -func classmsg(id int, msg string) string { - return fmt.Sprintf("[%d] %s", id, msg) -} - -func TestInitiateGeneratorClassDirectly(t *testing.T) { - var errorGen = xyerror.Generator{} - xycond.ExpectPanic(func() { errorGen.NewClass("foo") }).Test(t) -} - -func TestRegister(t *testing.T) { - var id = nextid() - var egen = xyerror.Register(t.Name(), id) - var c = egen.NewClass("bar") - xycond.ExpectEqual(c.Error(), classmsg(id+1, "bar")).Test(t) -} - -func TestRegisterDuplicate(t *testing.T) { - var id = nextid() - xyerror.Register(t.Name(), id) - xycond.ExpectPanic(func() { xyerror.Register("foobar", id) }).Test(t) -}