package main

import (
	"context"
	"errors"
	"fmt"
	"io/fs"
	"strconv"
)

var ErrPositive = errors.New("must be positive")
var ErrStopped = errors.New("operator stopped task")

type FieldError struct {
	Field string
	Err   error
}

func (e *FieldError) Error() string { return fmt.Sprintf("field %s: %v", e.Field, e.Err) }
func (e *FieldError) Unwrap() error { return e.Err }
func parseLimit(s string) (int, error) {
	n, err := strconv.Atoi(s)
	if err != nil {
		return 0, &FieldError{Field: "limit", Err: err}
	}
	if n <= 0 {
		return 0, &FieldError{Field: "limit", Err: ErrPositive}
	}
	return n, nil
}
func loadLimit(s string) (int, error) {
	n, err := parseLimit(s)
	if err != nil {
		return 0, fmt.Errorf("load config: %w", err)
	}
	return n, nil
}
func finish(work, close func() error) (err error) {
	defer func() { err = errors.Join(err, close()) }()
	return work()
}
func boundary(task func()) (err error) {
	defer func() {
		if p := recover(); p != nil {
			if cause, ok := p.(error); ok {
				err = fmt.Errorf("task panicked: %w", cause)
			} else {
				err = fmt.Errorf("task panicked: %v", p)
			}
		}
	}()
	task()
	return nil
}
func main() {
	n, err := loadLimit("8")
	fmt.Println("success:", n, err)
	_, err = loadLimit("0")
	fmt.Println("error:", err)
	fmt.Println("sentinel:", err == ErrPositive, errors.Is(err, ErrPositive))
	fmt.Println("same text:", errors.Is(err, errors.New("must be positive")))
	var fe *FieldError
	fmt.Println("As:", errors.As(err, &fe), fe.Field)
	fe2, ok := errors.AsType[*FieldError](err)
	fmt.Println("AsType:", ok, fe2.Field)
	_, direct := err.(*FieldError)
	fmt.Println("direct assertion:", direct)
	textOnly := fmt.Errorf("load config: %v", ErrPositive)
	fmt.Println("text-only Is:", errors.Is(textOnly, ErrPositive))
	left := &FieldError{Field: "limit", Err: ErrPositive}
	right := fmt.Errorf("read config: %w", fs.ErrPermission)
	joined := errors.Join(left, nil, right)
	tree := fmt.Errorf("startup: %w", joined)
	fmt.Printf("joined text: %q\n", joined.Error())
	fmt.Println("tree Is:", errors.Is(tree, ErrPositive), errors.Is(tree, fs.ErrPermission))
	fmt.Println("Unwrap join:", errors.Unwrap(joined) == nil)
	fmt.Println("all nil:", errors.Join(nil, nil) == nil)
	multi := fmt.Errorf("both: %w; %w", ErrPositive, fs.ErrPermission)
	fmt.Println("multiple %%w:", errors.Is(multi, ErrPositive), errors.Is(multi, fs.ErrPermission))
	combined := finish(func() error { return ErrPositive }, func() error { return fs.ErrPermission })
	fmt.Println("work and close:", errors.Is(combined, ErrPositive), errors.Is(combined, fs.ErrPermission))
	var p *FieldError
	var typedNil error = p
	fmt.Println("typed nil:", p == nil, typedNil == nil)
	ctx, cancel := context.WithCancelCause(context.Background())
	cancel(ErrStopped)
	fmt.Println("context:", ctx.Err(), context.Cause(ctx))
	fmt.Println("cause Is:", errors.Is(ctx.Err(), ErrStopped), errors.Is(context.Cause(ctx), ErrStopped))
	panicErr := boundary(func() { panic(ErrPositive) })
	fmt.Println("recovered:", panicErr, errors.Is(panicErr, ErrPositive))
}
