Advertisement
Guest User

Untitled

a guest
Oct 7th, 2017
100
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
TCL 8.58 KB | None | 0 0
  1. if 0 {
  2.   @ proptypes @
  3.     | Validate property types of a dict in an efficient manner
  4.     | while taking care to provide clear stack traces when
  5.     | errors do occur.
  6.  
  7.     > Disabling proptypes checking
  8.       Checking proptypes can become an intensive task.  By defining
  9.       the TCL_ENV environment variable, proptypes checking can be
  10.       toggled on and off.
  11.  
  12.       If TCL_ENV is set and it is not "development" then the proptypes
  13.       check will be an empty proc accepting any number of args.
  14.  
  15.       If TCL_ENV is set after the package is required, it will still
  16.       be checked and we will immediately return whenever proptypes
  17.       is called.
  18.  
  19.   @arg dict {dict<key, mixed>}
  20.     The dict to compare against
  21.   @arg args {[dict] | ...[dict]}
  22.     Either a single element or multiple elements which make up a
  23.     valid dict.  Each key should map to the desired type.
  24.  
  25.     @shape
  26.       Some types allow for nested type checking.  These values accept either
  27.       2 or 3 arguments where the second argument is the shape and the third
  28.       is the optional boolean value defined in the {@optional} section.
  29.  
  30.       @shapes {shape|list|tuple}
  31.         @shape {dict}
  32.         {
  33.           # expects [dict create foo [dict create bar $entierValue]]
  34.           proptypes $mydict {
  35.             foo {shape {
  36.               bar entier
  37.             }}
  38.           }
  39.         }
  40.         @shape {list}
  41.         {
  42.           # expects a list of dicts that contains a key "foo" with an entier value.
  43.           proptypes $myDict {
  44.             foo {list {shape {foo entier}}}
  45.           }
  46.         }
  47.         @shape {tuple}
  48.         {
  49.           # expects a tuple (an exact list) containing 3 elements of types any entier boolean
  50.           proptypes $myDict {
  51.             foo {tuple {any entier boolean}}
  52.           }
  53.         }
  54.  
  55.     @optional
  56.       If a value should be optional, the value can be a list with its
  57.       second value being a boolean indicating whether or not the value is
  58.       required.
  59.  
  60.       Optional values will be type checked only if they exist in the dict.
  61.   @example
  62.   {
  63.     proc typedProc args {
  64.       proptypes $args {
  65.         foo dict
  66.         bar entier
  67.         baz {boolean false}
  68.         qux {shape {
  69.           foo {tuple {string boolean entier}}
  70.         } false}
  71.       }
  72.       puts "Validated!"
  73.     }
  74.  
  75.     typedProc foo [dict create one two] bar 2
  76.     # OK: Validated!
  77.     typedProc foo [dict create one two] bar hi
  78.     # ERROR: invalid type for prop bar, expected entier
  79.     typedProc foo [dict create one two] baz true
  80.     # ERROR: required value bar not found
  81.     typedProc foo [dict create one two] bar 2 baz 2
  82.     # ERROR: invalid type for prop baz, expected boolean
  83.   }
  84. }
  85.  
  86.  
  87. if {[info exists ::env(TCL_ENV)]} {
  88.   if {$::env(TCL_ENV) != "development"} {
  89.     proc proptypes args {}
  90.   }
  91. }
  92.  
  93. if {![info exists ::env(TCL_ENV)] || [info command proptypes] eq {}} {
  94.   proc proptypes {dict args} {
  95.     if {[info exists ::env(TCL_ENV)] && $::env(TCL_ENV) != "development"} {
  96.       # check in case the env var was defined later
  97.       return
  98.     }
  99.     if {[llength $args] == 1} {
  100.       set args [lindex $args 0]
  101.     }
  102.     dict for {prop type} $args {
  103.       set config [lassign $type type]
  104.       if {$type in [list shape tuple list]} {
  105.         lassign $config shape required
  106.       } else {
  107.         lassign $config required
  108.       }
  109.       if {$required eq {}} {
  110.         set required true
  111.       }
  112.       if {[dict exists $dict $prop]} {
  113.         set value [dict get $dict $prop]
  114.         switch -- $type {
  115.           any - string { break }
  116.           number {
  117.             if {![string is double -strict $value]} {
  118.               tailcall return -code error -errorCode [list INVALID_PROP $prop] " invalid type for prop ${prop}, expected $type"
  119.             }
  120.           }
  121.           dict {
  122.             if {[catch {dict size $value} err]} {
  123.               tailcall return -code error -errorCode [list INVALID_PROP $prop] " invalid type for prop ${prop}, expected dict but received value with [llength $value] elements"
  124.             }
  125.           }
  126.           bool {
  127.             if {![string is boolean -strict $value]} {
  128.               tailcall return -code error -errorCode [list INVALID_PROP $prop] " invalid type for prop ${prop}, expected $type"
  129.             }
  130.           }
  131.           tuple {
  132.             # exact # of elements matching the given type
  133.             # tuple {string entier boolean}
  134.             if {[llength $shape] != [llength $value]} {
  135.               tailcall return -code error -errorCode [list INVALID_PROP $prop WRONG_NUMBER_ELEMENTS] " invalid type for prop ${prop}, expected tuple with [llength $shape] elements but received a value with [llenght $value] elements"
  136.             } else {
  137.               upvar 1 level plevel
  138.               if {![info exists plevel]} {
  139.                 set level 0
  140.                 set plevel 0
  141.               } else {
  142.                 set level [expr {$plevel + 1}]
  143.               }
  144.               set e 0
  145.               foreach tupleType $shape {
  146.                 set cname ${prop}.$e
  147.                 set cval   [dict create $cname [lindex $value $e]]
  148.                 set cshape [dict create $cname $tupleType]
  149.                 if {[catch {proptypes $cval $cshape} err]} {
  150.                   if {$level == 1} {
  151.                     tailcall return -code error -errorCode [list INVALID_PROP $cname INVALID_TUPLE] "invalid element (${e}) for tuple ${cname} \n[string repeat { } $level]$err"
  152.                   } else {
  153.                     return -code error -errorCode [list INVALID_PROP $cname INVALID_TUPLE] "invalid element (${e}) for tuple ${cname} \n[string repeat { } $level]$err"
  154.                   }
  155.                 }
  156.                 incr e
  157.               }
  158.             }
  159.           }
  160.           list {
  161.             # exact # of elements matching the given type
  162.             # tuple {string entier boolean}
  163.             if {[llength $value] == 0} {
  164.               tailcall return -code error -errorCode [list INVALID_PROP $prop WRONG_NUMBER_ELEMENTS] " invalid type for prop ${prop}, expected list with at least one element of type $shape"
  165.             } else {
  166.               upvar 1 level plevel
  167.               if {![info exists plevel]} {
  168.                 set level 0
  169.                 set plevel 0
  170.               } else {
  171.                 set level [expr {$plevel + 1}]
  172.               }
  173.               set e 0
  174.               foreach larg $value {
  175.                 set cname ${prop}.$e
  176.                 set cval   [dict create $cname [lindex $value $e]]
  177.                 set cshape [dict create $cname $shape]
  178.                 if {[catch {proptypes $cval $cshape} err]} {
  179.                   if {$level == 1} {
  180.                     tailcall return -code error -errorCode [list INVALID_PROP $cname INVALID_LIST] "invalid element (${e}) for list ${prop} \n[string repeat { } $level]$err"
  181.                   } else {
  182.                     return -code error -errorCode [list INVALID_PROP $cname INVALID_LIST] "invalid element (${e}) for list ${prop} \n[string repeat { } $level]$err"
  183.                   }
  184.                 }
  185.                 incr e
  186.               }
  187.             }
  188.           }
  189.           shape {
  190.             upvar 1 level plevel
  191.             if {![info exists plevel]} {
  192.               set level  1
  193.               set plevel 0
  194.             } else {
  195.               set level [expr {$plevel + 1}]
  196.             }
  197.             if {[catch {proptypes $value $shape} err]} {
  198.               if {$level == 1} {
  199.                 tailcall return -code error -errorCode [list INVALID_PROP $prop INVALID_SHAPE] "invalid shape for prop ${prop} \n[string repeat { } $level]$err"
  200.               } else {
  201.                 return -code error -errorCode [list INVALID_PROP $prop INVALID_SHAPE] "invalid shape for prop ${prop} \n[string repeat { } $level]$err"
  202.               }
  203.             }
  204.           }
  205.           default {
  206.             if {![string is $type -strict $value]} {
  207.               tailcall return -code error -errorCode [list INVALID_PROP $prop] " invalid type for prop ${prop}, expected $type"
  208.             }
  209.           }
  210.         }
  211.       } elseif {$required || $type eq "any"} {
  212.         tailcall return -code error -errorCode [list PROP_REQUIRED $prop] " required value $prop not found"
  213.       }
  214.     }
  215.   }
  216. }
  217.  
  218. proc test args {
  219.   # proptypes $args {
  220.   #   foo entier
  221.   #   bar {shape {
  222.   #     foo {shape {
  223.   #       foo {list }
  224.   #       bar {tuple {any entier entier}}
  225.   #     }}
  226.   #   } false}
  227.   # }
  228.   proptypes $args {
  229.     foo {list {shape {foo entier}}}
  230.   }
  231. }
  232.  
  233. # test \
  234. #   foo 1 \
  235. #   bar {
  236. #     foo {
  237. #       foo {2 2 2 hello 2}
  238. #       bar {one 2 false}
  239. #
  240. #     }
  241. #   }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement