Skip to content
This repository has been archived by the owner on Sep 5, 2023. It is now read-only.

Konstantin8105/Go2ErrorTree

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

13 Commits
 
 
 
 

Repository files navigation

Go2ErrorTree

Proposal is not clear for case if we have error tree.

Error-tree created in according to repository.

Error-tree help to grouping the errors.

Description of new language element errorhandling:

Go code

errorhandling(et interface(){ Add(error) }){ 
	val1, et = function1()
	val2, et = function2(val1)
	val3, val4, et = function3()
}

Internal interpretation errorhandling:

{ var errLocal error; val1, errLocal = function1(); et.Add(errLocal) }
{ var errLocal error; val2, errLocal = function2(val1); et.Add(errLocal) }
{ var errLocal error; val3, val4, errLocal = function3(); et.Add(errLocal) }

Let's suppose on example:

func printSum(a, b string) error {
  x, err := strconv.Atoi(a)
  if err != nil {
    return err
  }
  y, err := strconv.Atoi(b)
  if err != nil {
    return err
  }
  fmt.Println("result:", x + y)
  return nil
}

Add exist error-tree.

func printSum(a, b string) error {
  // create error tree
  et := errors.New("check input data")
  x, err := strconv.Atoi(a)
  if err != nil {
    et.Add(err)
  }
  y, err := strconv.Atoi(b)
  if err != nil {
    et.Add(err)
  }
  // is error-tree have errors
  if et.IsError() {
    return et
  }
  fmt.Println("result:", x + y)
  return nil
}

And writing it as:

func printSum(a, b string) error {
  // create error tree
  et := errors.New("check input data")
  errorhandling(et interface{Add(error)}){ // add type just for clearification
    x, et := strconv.Atoi(a)
    y, et := strconv.Atoi(b)
  }
  // is error-tree have errors
  if et.IsError() {
    return et
  }
  fmt.Println("result:", x + y)
  return nil
}

Now, we take next example:

func CopyFile(src, dst string) error {
	r, err := os.Open(src)
	if err != nil {
		return fmt.Errorf("copy %s %s: %v", src, dst, err)
	}
	defer r.Close()

	w, err := os.Create(dst)
	if err != nil {
		return fmt.Errorf("copy %s %s: %v", src, dst, err)
	}

	if _, err := io.Copy(w, r); err != nil {
		w.Close()
		os.Remove(dst)
		return fmt.Errorf("copy %s %s: %v", src, dst, err)
	}

	if err := w.Close(); err != nil {
		os.Remove(dst)
		return fmt.Errorf("copy %s %s: %v", src, dst, err)
	}
  
  return nil
}

Rewrite in according to error-tree approach:

func CopyFile(src, dst string) (err error) {
	// create error tree
	et := errors.New("CopyFile")
	defer func() {
		if et.IsError() {
			er.Add(fmt.Errorf("copy %s %s", src, dst))
		} else {
			err = nil
		}
	}()
	r, err := os.Open(src)
	if err != nil {
		et.Add(err)
		return et
	}
	defer func() {
		et.Add(r.Close())
	}()

	w, err := os.Create(dst)
	if err != nil {
		et.Add(err)
		return et
	}

	if _, err := io.Copy(w, r); err != nil {
		et.Add(err)
		et.Add(w.Close())
		et.Add(os.Remove(dst))
		return et
	}

	if err := w.Close(); err != nil {
		et.Add(err)
		et.Add(os.Remove(dst))
		return et
	}

	return nil
}

Rewrite with errorhandling:

func CopyFile(src, dst string) (err error) {
	// create error tree
	et := errors.New("CopyFile")
	defer func() {
		if et.IsError() {
			er.Add(fmt.Errorf("copy %s %s", src, dst))
		} else {
			err = nil
		}
	}()
	r, err := os.Open(src)
	if err != nil {
		et.Add(err)
		return et
	}
	defer func() {
		et.Add(r.Close())
	}()

	w, err := os.Create(dst)
	if err != nil {
		et.Add(err)
		return et
	}

	if _, err := io.Copy(w, r); err != nil {
		errorhandling(et) {
			et = err
			et = w.Close()
			et = os.Remove(dst)
		}
		return et
	}

	if err := w.Close(); err != nil {
		errorhandling(et) {
			et = err
			et = os.Remove(dst)
		}
		return et
	}

	return nil
}

If we have a tree, then we have to add function for moving by tree as go.ast.Walk.

Example of Walking by error-tree taked from git:

type ErrorValue struct {
	ValueName string
	Reason    error
}

func (e ErrorValue) Error() string {
	return fmt.Sprintf("Value `%s`: %v", e.ValueName, e.Reason)
}

func Example() {
	// some input data
	f := math.NaN()
	i := -32
	var s string

	// checking
	var et Tree
	et.Name = "Check input data"
	if math.IsNaN(f) {
		et.Add(ErrorValue{
			ValueName: "f",
			Reason:    fmt.Errorf("is NaN"),
		})
	}
	if f < 0 {
		et.Add(fmt.Errorf("Parameter `f` is negative"))
	}
	if i < 0 {
		et.Add(fmt.Errorf("Parameter `i` is less zero"))
	}
	if s == "" {
		et.Add(fmt.Errorf("Parameter `s` is empty"))
	}

	if et.IsError() {
		fmt.Println(et.Error())
	}

	// walk
	Walk(&et, func(e error) {
		fmt.Fprintf(os.Stdout, "%-25s %v\n", fmt.Sprintf("%T", e), e)
	})

	// Output:
	// Check input data
	// ├──Value `f`: is NaN
	// ├──Parameter `i` is less zero
	// └──Parameter `s` is empty
	//
	// errors.ErrorValue         Value `f`: is NaN
	// *errors.errorString       Parameter `i` is less zero
	// *errors.errorString       Parameter `s` is empty
}

Releases

No releases published

Packages

No packages published