system/output: implement MessageError
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				Test / Hakurei (push) Successful in 43s
				
			
		
			
				
	
				Test / Create distribution (push) Successful in 26s
				
			
		
			
				
	
				Test / Sandbox (push) Successful in 1m40s
				
			
		
			
				
	
				Test / Hpkg (push) Successful in 3m35s
				
			
		
			
				
	
				Test / Sandbox (race detector) (push) Successful in 4m24s
				
			
		
			
				
	
				Test / Hakurei (race detector) (push) Successful in 5m20s
				
			
		
			
				
	
				Test / Flake checks (push) Successful in 1m37s
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	Test / Hakurei (push) Successful in 43s
				
			Test / Create distribution (push) Successful in 26s
				
			Test / Sandbox (push) Successful in 1m40s
				
			Test / Hpkg (push) Successful in 3m35s
				
			Test / Sandbox (race detector) (push) Successful in 4m24s
				
			Test / Hakurei (race detector) (push) Successful in 5m20s
				
			Test / Flake checks (push) Successful in 1m37s
				
			This error is also formatted differently based on state. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
		
							parent
							
								
									780e3e5465
								
							
						
					
					
						commit
						b489a3bba1
					
				| @ -20,16 +20,16 @@ func SetOutput(v container.Msg) { | ||||
| 
 | ||||
| // OpError is returned by [I.Commit] and [I.Revert]. | ||||
| type OpError struct { | ||||
| 	Op      string | ||||
| 	Err     error | ||||
| 	Message string | ||||
| 	Revert  bool | ||||
| 	Op     string | ||||
| 	Err    error | ||||
| 	Msg    string | ||||
| 	Revert bool | ||||
| } | ||||
| 
 | ||||
| func (e *OpError) Unwrap() error { return e.Err } | ||||
| func (e *OpError) Error() string { | ||||
| 	if e.Message != "" { | ||||
| 		return e.Message | ||||
| 	if e.Msg != "" { | ||||
| 		return e.Msg | ||||
| 	} | ||||
| 
 | ||||
| 	switch { | ||||
| @ -47,6 +47,16 @@ func (e *OpError) Error() string { | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (e *OpError) Message() string { | ||||
| 	switch { | ||||
| 	case e.Msg != "": | ||||
| 		return e.Error() | ||||
| 
 | ||||
| 	default: | ||||
| 		return "cannot " + e.Error() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // newOpError returns an [OpError] without a message string. | ||||
| func newOpError(op string, err error, revert bool) error { | ||||
| 	if err == nil { | ||||
| @ -69,10 +79,18 @@ func printJoinedError(println func(v ...any), fallback string, err error) { | ||||
| 		error | ||||
| 	} | ||||
| 	if !errors.As(err, &joinErr) { | ||||
| 		println(fallback, err) | ||||
| 		if m, ok := container.GetErrorMessage(err); ok { | ||||
| 			println(m) | ||||
| 		} else { | ||||
| 			println(fallback, err) | ||||
| 		} | ||||
| 	} else { | ||||
| 		for _, err = range joinErr.Unwrap() { | ||||
| 			println(err.Error()) | ||||
| 			if m, ok := container.GetErrorMessage(err); ok { | ||||
| 				println(m) | ||||
| 			} else { | ||||
| 				println(err.Error()) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -19,27 +19,33 @@ func TestOpError(t *testing.T) { | ||||
| 		s    string | ||||
| 		is   error | ||||
| 		isF  error | ||||
| 		msg  string | ||||
| 	}{ | ||||
| 		{"message", newOpErrorMessage("dbus", ErrDBusConfig, | ||||
| 			"attempted to create message bus proxy args without session bus config", false), | ||||
| 			"attempted to create message bus proxy args without session bus config", | ||||
| 			ErrDBusConfig, syscall.ENOTRECOVERABLE}, | ||||
| 			ErrDBusConfig, syscall.ENOTRECOVERABLE, | ||||
| 			"attempted to create message bus proxy args without session bus config"}, | ||||
| 
 | ||||
| 		{"apply", newOpError("tmpfile", syscall.EBADE, false), | ||||
| 			"apply tmpfile: invalid exchange", | ||||
| 			syscall.EBADE, syscall.EBADF}, | ||||
| 			syscall.EBADE, syscall.EBADF, | ||||
| 			"cannot apply tmpfile: invalid exchange"}, | ||||
| 
 | ||||
| 		{"revert", newOpError("wayland", syscall.EBADF, true), | ||||
| 			"revert wayland: bad file descriptor", | ||||
| 			syscall.EBADF, syscall.EBADE}, | ||||
| 			syscall.EBADF, syscall.EBADE, | ||||
| 			"cannot revert wayland: bad file descriptor"}, | ||||
| 
 | ||||
| 		{"path", newOpError("tmpfile", &os.PathError{Op: "stat", Path: "/run/dbus", Err: syscall.EISDIR}, false), | ||||
| 			"stat /run/dbus: is a directory", | ||||
| 			syscall.EISDIR, syscall.ENOTDIR}, | ||||
| 			syscall.EISDIR, syscall.ENOTDIR, | ||||
| 			"cannot stat /run/dbus: is a directory"}, | ||||
| 
 | ||||
| 		{"net", newOpError("wayland", &net.OpError{Op: "dial", Net: "unix", Addr: &net.UnixAddr{Name: "/run/user/1000/wayland-1", Net: "unix"}, Err: syscall.ENOENT}, false), | ||||
| 			"dial unix /run/user/1000/wayland-1: no such file or directory", | ||||
| 			syscall.ENOENT, syscall.EPERM}, | ||||
| 			syscall.ENOENT, syscall.EPERM, | ||||
| 			"cannot dial unix /run/user/1000/wayland-1: no such file or directory"}, | ||||
| 	} | ||||
| 	for _, tc := range testCases { | ||||
| 		t.Run(tc.name, func(t *testing.T) { | ||||
| @ -48,6 +54,7 @@ func TestOpError(t *testing.T) { | ||||
| 					t.Errorf("Error: %q, want %q", got, tc.s) | ||||
| 				} | ||||
| 			}) | ||||
| 
 | ||||
| 			t.Run("is", func(t *testing.T) { | ||||
| 				if !errors.Is(tc.err, tc.is) { | ||||
| 					t.Error("Is: unexpected false") | ||||
| @ -56,6 +63,17 @@ func TestOpError(t *testing.T) { | ||||
| 					t.Error("Is: unexpected true") | ||||
| 				} | ||||
| 			}) | ||||
| 
 | ||||
| 			t.Run("msg", func(t *testing.T) { | ||||
| 				if got, ok := container.GetErrorMessage(tc.err); !ok { | ||||
| 					if tc.msg != "" { | ||||
| 						t.Errorf("GetErrorMessage: err does not implement MessageError") | ||||
| 					} | ||||
| 					return | ||||
| 				} else if got != tc.msg { | ||||
| 					t.Errorf("GetErrorMessage: %q, want %q", got, tc.msg) | ||||
| 				} | ||||
| 			}) | ||||
| 		}) | ||||
| 	} | ||||
| 
 | ||||
| @ -103,14 +121,40 @@ func TestPrintJoinedError(t *testing.T) { | ||||
| 		want [][]any | ||||
| 	}{ | ||||
| 		{"nil", nil, [][]any{{"not a joined error:", nil}}}, | ||||
| 		{"unwrapped", syscall.EINVAL, [][]any{{"not a joined error:", syscall.EINVAL}}}, | ||||
| 		{"single", errors.Join(syscall.EINVAL), [][]any{{"invalid argument"}}}, | ||||
| 
 | ||||
| 		{"unwrapped", syscall.EINVAL, [][]any{{"not a joined error:", syscall.EINVAL}}}, | ||||
| 		{"unwrapped message", &OpError{ | ||||
| 			Op:  "meow", | ||||
| 			Err: syscall.EBADFD, | ||||
| 		}, [][]any{ | ||||
| 			{"cannot apply meow: file descriptor in bad state"}, | ||||
| 		}}, | ||||
| 
 | ||||
| 		{"many", errors.Join(syscall.ENOTRECOVERABLE, syscall.ETIMEDOUT, syscall.EBADFD), [][]any{ | ||||
| 			{"state not recoverable"}, | ||||
| 			{"connection timed out"}, | ||||
| 			{"file descriptor in bad state"}, | ||||
| 		}}, | ||||
| 		{"many message", errors.Join( | ||||
| 			&container.StartError{ | ||||
| 				Step: "meow", | ||||
| 				Err:  syscall.ENOMEM, | ||||
| 			}, | ||||
| 			&os.PathError{ | ||||
| 				Op:   "meow", | ||||
| 				Path: "/proc/nonexistent", | ||||
| 				Err:  syscall.ENOSYS, | ||||
| 			}, | ||||
| 			&OpError{ | ||||
| 				Op:     "meow", | ||||
| 				Err:    syscall.ENODEV, | ||||
| 				Revert: true, | ||||
| 			}), [][]any{ | ||||
| 			{"cannot meow: cannot allocate memory"}, | ||||
| 			{"meow /proc/nonexistent: function not implemented"}, | ||||
| 			{"cannot revert meow: no such device"}, | ||||
| 		}}, | ||||
| 	} | ||||
| 	for _, tc := range testCases { | ||||
| 		t.Run(tc.name, func(t *testing.T) { | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user