REBOL[
	File: %form-error.r
	Date: 20-Dec-2006
	Title: {Trapping and displaying an error}
	Purpose: {Beginner's example for trapping errors.}
	Author: "Tim Johnson"
	email: tim@johnsons-web.com
	NOTE: {See comments for some code illustrations}
	Library: [
		level: 'Intermediate
		platform: 'all
		type: [Function how-to]
		domain: [debug testing]
		tested-under: "Linux, CGI"
		support: ["Tim Johnson" tim@johnsons-web.com]
		license: none
		see-also: none
		]
	]

form-error: func[{Compose a message representation of an error object} 
	error[error!] "The error" 
	markup[block! string! none!] 
	{Use to make more readable. markup 1 & 4 'wrap' the message.
     markup 2 & 3 for 'near and 'where attributes respectively.
     All four elements of 'markup are optional}
	/local arg1 arg2 arg3 message format
	][
	error: disarm error  ;; convert to object
	format: compose[(any[markup ["" "" "" ""]])]
	all[4 > length? format
		append format array/initial (4 - length? format) ""
		]
 	set [arg1 arg2 arg3][error/arg1 error/arg2 error/arg3]
 	message: get in get in system/error error/type error/id
 	if block? message[bind message 'arg1]
	message: reform reduce message
	if error/near [append message rejoin[format/2 " Near: " error/near]]
	if error/where [append message rejoin[format/3 " Where: " error/near]]
	rejoin[format/1 message format/4]
	]

;;; Explanations:
;;	1) If you do not fully understand the 'magic' of setting arg2 and arg3
;;     it would be useful to probe system/error. The 3 arguments are used
;;     to extract a human-readable message from the system/error object
;;     look for :arg1, :arg2, :arg3 etc.
;;  2) Although I was an experienced 'C' programmer when I started using rebol,
;;    thinking in 'C'-style imperative logic could be an obstacle.
;;    EXAMPLE: Convert a data-type - the C programmer might want to do the following:
;;    if not markup[format: ["" "" "" ""]] ;; put in default empty strings
;;    if string? markup[format: reduce[markup]] ;; convert 'markup to a block
;;    Thinking about this in more 'rebol-esque' terms, we actually use:
;;    format: compose[(any[markup ["" "" "" ""]])]  ;; combining the two 
;;    ;; lines above and using 'any instead of 'if
;;  3) If 'markup doesn't have the necessary 4 strings, the imperative approach
;;     would be something like this:
;;     if (length? markup) < 4[loop 4 - (length? markup)[append markup ""]]
;;     ;; filling in the block with empty strings, and reallocating memory in each
;;     ;; loop
;;    	all[4 > length? format
;;		append format array/initial (4 - length? format) ""
;;		]  ;; HINT: in your console, >> help all
;;         ;;                        >> help array

;; Usage: combine with the 'try construct, checking for an error. 
;; Example in comments. Think of a python
;; try:
;; except:
;; finally:
comment{
  either error? res: try[ ;; attempted code follows
  	do-some-code
	do-some-more-code
	1 / 0  ;; YIKES! Don't touch me there!
  	][ ;; 'res is an error and we catch it here
  	print "

Error executing code. Explanation follows:

" print form-error res ["

" "
  " "
  " "

"] ][ ;; Finish up here. in python this would be the finally section finish-code ] }