├── .travis.yml ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── VERSION ├── all-shpecs ├── lib └── y2s.bash └── shpec ├── shpec-helper.bash └── y2s_shpec.bash /.travis.yml: -------------------------------------------------------------------------------- 1 | language: sh 2 | dist: trusty 3 | sudo: false 4 | install: 5 | - git clone git://github.com/rylnd/shpec --branch 0.2.2 --depth 1 "$HOME"/shpec 6 | - git clone git://github.com/binaryphile/kaizen --branch v0.5.3 --depth 1 "$TRAVIS_BUILD_DIR"/../kaizen 7 | - git clone git://github.com/binaryphile/sorta --branch v1.0.4 --depth 1 "$TRAVIS_BUILD_DIR"/../sorta 8 | - git clone git://github.com/binaryphile/nano --branch v0.1.3 --depth 1 "$TRAVIS_BUILD_DIR"/../nano 9 | - export PATH=$HOME/shpec/bin:$TRAVIS_BUILD_DIR/lib:$TRAVIS_BUILD_DIR/../nano/lib:$TRAVIS_BUILD_DIR/../kaizen/lib:$TRAVIS_BUILD_DIR/../sorta/lib:$PATH 10 | script: ./all-shpecs 11 | git: 12 | depth: 1 13 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Change Log 2 | ========== 3 | 4 | The format is based on [Keep a Changelog] and this project adheres to 5 | [Semantic Versioning]. 6 | 7 | Latest Changes 8 | ============== 9 | 10 | [v0.0.3] - 2017-05-12 11 | ------------ 12 | 13 | ### Added 14 | 15 | - compatibfility with bash 4.4-p12 16 | 17 | [v0.0.2] - 2017-05-11 18 | --------------------- 19 | 20 | ### Changed 21 | 22 | - reverted to normal versioning 23 | 24 | - tests now employ shpec-helper's `stop_on_error` 25 | 26 | - tests use sorta's `import.bash` to only import defined functions 27 | from shpec-helper 28 | 29 | - prefer (()) to [let] 30 | 31 | ### Added 32 | 33 | - explanation in readme that keys must meet bash variable name 34 | requirements 35 | 36 | ### Fixed 37 | 38 | - shpecs failing in strict mode due to uninitialized variables and bad 39 | "read"s 40 | 41 | [v0.0.1] - 2017-03-01 42 | --------------------- 43 | 44 | ### Changed 45 | 46 | - refactored expression generation 47 | 48 | - clarified readme 49 | 50 | - updated to shpec-helper from kaizen 51 | 52 | Older Changes 53 | ============= 54 | 55 | [v0.0.0] - 2017-02-20 56 | --------------------- 57 | 58 | ### Added 59 | 60 | - initial version 61 | 62 | - supports arrays, hashes, various single-line forms of scalars 63 | 64 | - supports nesting 65 | 66 | - supports lookup 67 | 68 | - working test suite 69 | 70 | [Keep a Changelog]: http://keepachangelog.com/ 71 | [Semantic Versioning]: http://semver.org/ 72 | [v0.0.3]: https://github.com/binaryphile/y2s/compare/v0.0.2...v0.0.3 73 | [v0.0.2]: https://github.com/binaryphile/y2s/compare/v0.0.1...v0.0.2 74 | [let]: http://wiki.bash-hackers.org/commands/builtin/let 75 | [v0.0.1]: https://github.com/binaryphile/y2s/compare/v0.0.0...v0.0.1 76 | [v0.0.0]: https://github.com/binaryphile/y2s/tree/v0.0.0 77 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2017 Thomas E Lilley 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | y2s [![Build Status](https://travis-ci.org/binaryphile/y2s.svg?branch=master)](https://travis-ci.org/binaryphile/y2s) 2 | === 3 | 4 | y2s stands for "YAML2struct". It is a handful of bash functions for 5 | parsing a limited subset of YAML into a makeshift bash nested data 6 | structure. 7 | 8 | The structure, or "struct" for short, is a hash (a.k.a. associative 9 | array) which contains keys for all of the leaf elements in the YAML 10 | source document, along with a slightly custom syntax for storing copies 11 | of all of the intermediate levels of the structure as well. 12 | 13 | For example, consider the following `demo.yml` file: 14 | 15 | myhash: 16 | one: 1 17 | two: 2 18 | myarray: 19 | - zero 20 | - one 21 | 22 | Using the function `yml2struct` to store the result in a hash called 23 | `hash` looks like this: 24 | 25 | $ source y2s.bash 26 | $ declare -A hash 27 | $ yml2struct hash demo.yml 28 | 29 | The resulting `hash` looks like so: 30 | 31 | hash[myhash]='([one]="1" [two]="2" )' 32 | hash[myhash.one]="1" 33 | hash[myhash.two]="2" 34 | hash[myarray]='([0]="zero" [1]="one" )' 35 | hash[myarray.0]="zero" 36 | hash[myarray.1]="one" 37 | 38 | Notice that the scalar values are available via the keys which specify 39 | their full paths (although this is not the recommended usage pattern, 40 | instead see below): 41 | 42 | $ echo "${hash[myarray.0]}" 43 | zero 44 | 45 | The non-scalar values are still stored as strings, however they are 46 | serialized as the right-hand side of an assignment statement. For 47 | example, you could do this (again, not the recommended usage pattern): 48 | 49 | $ eval "array=${hash[myarray]}" 50 | $ echo "${array[0]}" 51 | zero 52 | 53 | Since these methods of accessing the values are somewhat clunky, there 54 | is a convenience function, called `lookup`, which allows you to use 55 | dotted notation to access values and substructures. 56 | 57 | If it is given the name of a variable as its second argument, it stores 58 | the value there, otherwise it echoes the value: 59 | 60 | $ lookup hash.myhash.one 61 | 1 62 | 63 | $ unset -v array 64 | $ declare -a array 65 | $ lookup hash.myarray array 66 | $ echo "${array[0]}" 67 | zero 68 | 69 | $ declare -A myhash 70 | $ lookup hash.myhash myhash 71 | $ echo "${myhash[one]}" 72 | 1 73 | 74 | Using `lookup` to instantiate a hash with nested structures also 75 | preserves the embedded structures, so the result is itself a struct. 76 | This means you can instantiate and then use subportions of the data 77 | structure with the `lookup` function as well. 78 | 79 | Instantiating an array with nested elements would lose the embedded 80 | structure data, since the array is not capable of using complex 81 | string-based keys. For that reason, `lookup` doesn't support doing so 82 | with arbitrary subtrees. 83 | 84 | Only flat array structures can be instantiated into a bash array. 85 | 86 | However, you may instead use a hash as the variable into which an array 87 | structure would be stored, and it will act the same as an array does 88 | (for the most part) but will also still be a struct. 89 | 90 | Installation 91 | ------------ 92 | 93 | Requires Bash 4.3 or higher. 94 | 95 | y2s depends on the [nano] library. Follow its installation instructions, 96 | then clone y2s and put its `lib` directory on your PATH. 97 | 98 | Then you may source it in your scripts with `source y2s.bash`. 99 | 100 | Limitations 101 | ----------- 102 | 103 | y2s: 104 | 105 | - understands hashes and arrays, in addition to scalar values. 106 | 107 | - only allows alphanumerics and underscores in hash keys, so they can 108 | be instantiated as bash variables 109 | 110 | - supports nesting arrays in hashes and vice-versa. 111 | 112 | - understands plain (unquoted), single- and double-quoted scalars 113 | 114 | - it strives for compatibility with Ruby's syck implementation as 115 | a guide 116 | 117 | - only accepts indents of two space characters per level 118 | 119 | - only allows printable characters in values (including whitespace) 120 | 121 | - only supports a very limited subset of YAML 122 | 123 | - values are limited to a single line 124 | 125 | - double-quoted values do, however, allow the use of escaped 126 | characters such as `\n` 127 | 128 | - does not understand the JSON forms of keys, hashes or arrays 129 | 130 | - does not support converting structs to yml, nor writing yml files 131 | 132 | - has no feedback on errors which occur during parsing (at the moment) 133 | 134 | - the best thing to try is to pass the same input through a real 135 | parser 136 | 137 | Also, because y2s denormalizes the yml data, copying the same value into 138 | multiple locations, it is only suitable for reading data. Writing data 139 | into a struct would require updating all of the locations in which that 140 | data appears. There are no plans for implementing such support. 141 | 142 | Shouts-Out 143 | ---------- 144 | 145 | y2s is inspired by [YAY] and the Stack Overflow articles cited by YAY. 146 | 147 | [nano]: https://github.com/binaryphile/nano 148 | [YAY]: https://github.com/johnlane/random-toolbox/blob/master/usr/lib/yay 149 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 0.0.3 2 | -------------------------------------------------------------------------------- /all-shpecs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | find . -name '*_shpec.*sh' -print0 | xargs -0I filename bash -c 'echo "shpec filename"; shpec filename' 4 | -------------------------------------------------------------------------------- /lib/y2s.bash: -------------------------------------------------------------------------------- 1 | [[ -n ${_y2s:-} ]] && return 2 | readonly _y2s=loaded 3 | 4 | source nano.bash 5 | 6 | _expand_expression () { 7 | local expression=$1 8 | local ref=$2 9 | local indent='( *)' 10 | local key='([[:alnum:]_]+)' 11 | local space='[[:space:]]*' 12 | 13 | expression=${expression// /$space} 14 | expression=${expression//$key} 15 | expression=${expression//$indent} 16 | printf -v expression '^%s$' "$expression" 17 | 18 | local "$ref" || return 19 | _ret "$ref" "$expression" 20 | } 21 | 22 | lookup () { 23 | local _key=$1 24 | local _ref=${2:-} 25 | local _assignment 26 | local _refu 27 | local _struct=${_key%%.*} 28 | 29 | _key=${_key#*.} 30 | _refu=$_struct[$_key] 31 | [[ -z $_ref ]] && { _puts "${!_refu}"; return ;} 32 | [[ ${!_refu} != '('*')' ]] && { 33 | printf -v "$_ref" "${!_refu}" 34 | return 35 | } 36 | printf -v _assignment "%s=%s" "$_ref" "${!_refu}" 37 | eval "$_assignment" 38 | } 39 | 40 | _parse () { 41 | local datatype=$1 42 | local style=$2 43 | 44 | [[ -z ${curdatatype:-} || $curdatatype == "$datatype" ]] || return 45 | [[ -z ${curdatatype:-} && $datatype == 'array' ]] && key=0 46 | curdatatype=$datatype 47 | case $datatype in 48 | 'array' ) _parse_array_values ;; 49 | 'hash' ) _parse_hash_values ;; 50 | esac 51 | case $style in 52 | 'double' ) _parse_double_quoted_line ;; 53 | 'plain' ) _parse_plain_line ;; 54 | 'single' ) _parse_single_quoted_line ;; 55 | esac 56 | } 57 | 58 | _parse_array_values () { 59 | indent=$(( ${#BASH_REMATCH[1]}/2 )) 60 | value=${BASH_REMATCH[2]} 61 | } 62 | 63 | _parse_double_quoted_line () { 64 | ! [[ $value =~ ([^\\]+|^)(\\\\)*\" ]] || return 65 | value=${value//\\\"/\"} 66 | printf -v value '%b' "$value" 67 | } 68 | 69 | _parse_hash_values () { 70 | indent=$(( ${#BASH_REMATCH[1]}/2 )) 71 | key=${BASH_REMATCH[2]} 72 | value=${BASH_REMATCH[3]} 73 | } 74 | 75 | _parse_line () { 76 | [[ $line =~ $hash_double_quoted_expression ]] && { _parse hash double ; return ;} 77 | [[ $line =~ $hash_single_quoted_expression ]] && { _parse hash single ; return ;} 78 | [[ $line =~ $hash_start_and_plain_expression ]] && { _parse hash plain ; return ;} 79 | [[ $line =~ $array_double_quoted_expression ]] && { _parse array double ; return ;} 80 | [[ $line =~ $array_single_quoted_expression ]] && { _parse array single ; return ;} 81 | [[ $line =~ $array_plain_expression ]] && { _parse array plain ; return ;} 82 | return 1 83 | } 84 | 85 | _parse_plain_line () { 86 | ! [[ $value == '"' || $value == "'" ]] || return 87 | } 88 | 89 | _parse_single_quoted_line () { 90 | ! [[ $value =~ ([^\']+|^)(\'\')*\' ]] || return 91 | value=${value//\'\'/\'} 92 | } 93 | 94 | _setup_expressions () { 95 | array_double_quoted_expression='- "[value]" ' 96 | array_plain_expression='- [value] ' 97 | array_single_quoted_expression="- '[value]' " 98 | 99 | array_double_quoted_expression=${array_double_quoted_expression/\[value]/$quoted_value} 100 | array_plain_expression=${array_plain_expression/\[value]/$plain_value} 101 | array_single_quoted_expression=${array_single_quoted_expression/\[value]/$quoted_value} 102 | 103 | _expand_expression "$array_double_quoted_expression" array_double_quoted_expression 104 | _expand_expression "$array_plain_expression" array_plain_expression 105 | _expand_expression "$array_single_quoted_expression" array_single_quoted_expression 106 | 107 | hash_double_quoted_expression=' : "[value]" ' 108 | hash_single_quoted_expression=" : '[value]' " 109 | hash_start_and_plain_expression=' : [value] ' 110 | 111 | hash_double_quoted_expression=${hash_double_quoted_expression/\[value]/$quoted_value} 112 | hash_single_quoted_expression=${hash_single_quoted_expression/\[value]/$quoted_value} 113 | hash_start_and_plain_expression=${hash_start_and_plain_expression/\[value]/$plain_value} 114 | 115 | _expand_expression "$hash_double_quoted_expression" hash_double_quoted_expression 116 | _expand_expression "$hash_start_and_plain_expression" hash_start_and_plain_expression 117 | _expand_expression "$hash_single_quoted_expression" hash_single_quoted_expression 118 | } 119 | 120 | _value_of () { 121 | local _source=$1 122 | local _ref=$2 123 | local _value 124 | 125 | _value=$(declare -p "$_source" 2>/dev/null) || return 126 | _value=${_value#*=\'} 127 | _value=${_value%\'} 128 | printf -v "$_ref" '%s' "$_value" 129 | } 130 | 131 | _y2s_rec () { 132 | local ref=$1 133 | local -A resulth 134 | local -A hash 135 | local curindent=$curindent 136 | local curdatatype 137 | local indent 138 | local key 139 | local value 140 | 141 | while [[ $wait == 'true' ]] || IFS= read -r line; do 142 | wait=false 143 | _parse_line || return 144 | if (( indent == curindent )); then 145 | if [[ -n $value ]]; then 146 | hash[$key]=$value 147 | else 148 | (( curindent += 1 )) ||: 149 | _y2s_rec resulth || return 150 | (( curindent -= 1 )) ||: 151 | _value_of resulth hash[$key] 152 | for subkey in "${!resulth[@]}"; do 153 | printf -v hash[$key.$subkey] '%s' "${resulth[$subkey]}" 154 | done 155 | fi 156 | elif (( indent < curindent )); then 157 | wait=true 158 | break 159 | else 160 | return 1 161 | fi 162 | [[ $curdatatype == 'array' ]] && (( key += 1 )) 163 | done 164 | 165 | local "$ref" || return 166 | _ret "$ref" hash 167 | } 168 | 169 | yml2struct () { 170 | local ref=$1 171 | local filename=${2:-/dev/stdin} 172 | local -A resulth 173 | local array_double_quoted_expression 174 | local array_plain_expression 175 | local array_single_quoted_expression 176 | local curindent=0 177 | local hash_double_quoted_expression 178 | local hash_single_quoted_expression 179 | local hash_start_and_plain_expression 180 | local plain_value='([[:graph:]]*|[[:print:]]+[[:graph:]])' 181 | local quoted_value='([[:print:]]*)' 182 | local line 183 | local wait=false 184 | 185 | _setup_expressions 186 | _y2s_rec resulth <"$filename" || return 187 | 188 | local "$ref" || return 189 | _ret "$ref" resulth 190 | } 191 | -------------------------------------------------------------------------------- /shpec/shpec-helper.bash: -------------------------------------------------------------------------------- 1 | ../../kaizen/lib/shpec-helper.bash -------------------------------------------------------------------------------- /shpec/y2s_shpec.bash: -------------------------------------------------------------------------------- 1 | source import.bash 2 | 3 | shpec_helper_imports=( 4 | initialize_shpec_helper 5 | shpec_cwd 6 | stop_on_error 7 | ) 8 | eval "$(importa shpec-helper shpec_helper_imports)" 9 | initialize_shpec_helper 10 | stop_on_error=true 11 | stop_on_error 12 | 13 | source "$(shpec_cwd)"/../lib/y2s.bash 14 | 15 | describe lookup 16 | it "returns a scalar by name from a lookup" 17 | declare -A sampleh=([one]="1") 18 | result='' 19 | expected='declare -- result="1"' 20 | lookup sampleh.one result 21 | assert equal "$expected" "$(declare -p result)" 22 | end 23 | 24 | it "returns an array by name from a lookup" 25 | declare -A sampleh=([ones]="( 1 )") 26 | results=() 27 | expected='declare -a results='\''([0]="1")'\' 28 | lookup sampleh.ones results 29 | assert equal "$expected" "$(declare -p results)" 30 | end 31 | 32 | it "returns an array nested in an array" 33 | declare -A sampleh=([ones.0]="( 2 )") 34 | results=() 35 | expected='declare -a results='\''([0]="2")'\' 36 | lookup sampleh.ones.0 results 37 | assert equal "$expected" "$(declare -p results)" 38 | end 39 | 40 | it "returns an array nested in a hash" 41 | declare -A sampleh=([ones.twos]="( 2 )") 42 | results=() 43 | expected='declare -a results='\''([0]="2")'\' 44 | lookup sampleh.ones.twos results 45 | assert equal "$expected" "$(declare -p results)" 46 | end 47 | 48 | it "returns a hash by name from a lookup" 49 | declare -A sampleh=([oneh]="( [one]=1 )") 50 | declare -A resulth=() 51 | expected='declare -A resulth='\''([one]="1" )'\' 52 | lookup sampleh.oneh resulth 53 | assert equal "$expected" "$(declare -p resulth)" 54 | end 55 | 56 | it "returns a hash nested in an array" 57 | declare -A sampleh=([oneh.0]="( [one]=1 )") 58 | declare -A resulth=() 59 | expected='declare -A resulth='\''([one]="1" )'\' 60 | lookup sampleh.oneh.0 resulth 61 | assert equal "$expected" "$(declare -p resulth)" 62 | end 63 | 64 | it "returns a hash nested in a hash" 65 | declare -A sampleh=([oneh.twoh]="( [one]=1 )") 66 | declare -A resulth=() 67 | expected='declare -A resulth='\''([one]="1" )'\' 68 | lookup sampleh.oneh.twoh resulth 69 | assert equal "$expected" "$(declare -p resulth)" 70 | end 71 | end 72 | 73 | describe _expand_expression 74 | it "transforms an expression" 75 | expected='^( *)([[:alnum:]_]+)[[:space:]]*:[[:space:]]*[value][[:space:]]*$' 76 | result='' 77 | _expand_expression ' : [value] ' result 78 | assert equal "$expected" "$result" 79 | end 80 | end 81 | 82 | describe yml2struct 83 | it "parses a plain scalar" 84 | sample='one: 1' 85 | declare -A resulth=() 86 | expected='declare -A resulth='\''([one]="1" )'\' 87 | yml2struct resulth <<<"$sample" 88 | assert equal "$expected" "$(declare -p resulth)" 89 | end 90 | 91 | it "parses a double-quoted scalar" 92 | sample='one: "1"' 93 | declare -A resulth=() 94 | expected='declare -A resulth='\''([one]="1" )'\' 95 | yml2struct resulth <<<"$sample" 96 | assert equal "$expected" "$(declare -p resulth)" 97 | end 98 | 99 | it "parses a single-quoted scalar" 100 | sample="one: '1'" 101 | declare -A resulth=() 102 | expected='declare -A resulth='\''([one]="1" )'\' 103 | yml2struct resulth <<<"$sample" 104 | assert equal "$expected" "$(declare -p resulth)" 105 | end 106 | 107 | it "errors on a non-doubled single-quote in a single-quoted scalar" 108 | sample="one: '''" 109 | declare -A resulth=() 110 | stop_on_error off 111 | yml2struct resulth <<<"$sample" 112 | assert unequal $? 0 113 | stop_on_error 114 | end 115 | 116 | it "errors on a plain single-quote scalar" 117 | sample="one: '" 118 | declare -A resulth=() 119 | stop_on_error off 120 | yml2struct resulth <<<"$sample" 121 | assert unequal 0 $? 122 | stop_on_error 123 | end 124 | 125 | it "errors on a plain double-quote scalar" 126 | sample='one: "' 127 | declare -A resulth=() 128 | stop_on_error off 129 | yml2struct resulth <<<"$sample" 130 | assert unequal 0 $? 131 | stop_on_error 132 | end 133 | 134 | it "errors on a non-escaped double-quote in a double-quoted scalar" 135 | sample='one: """' 136 | declare -A resulth=() 137 | stop_on_error off 138 | yml2struct resulth <<<"$sample" 139 | assert unequal 0 $? 140 | stop_on_error 141 | end 142 | 143 | it "parses a plain escaped double-quote scalar" 144 | sample='one: \"' 145 | declare -A resulth=() 146 | yml2struct resulth <<<"$sample" 147 | expected='declare -A resulth='\''([one]="\\\"" )'\' 148 | assert equal "$expected" "$(declare -p resulth)" 149 | end 150 | 151 | it "parses a double-quoted escaped double-quote scalar" 152 | sample='one: "\""' 153 | declare -A resulth=() 154 | yml2struct resulth <<<"$sample" 155 | expected='declare -A resulth='\''([one]="\"" )'\' 156 | assert equal "$expected" "$(declare -p resulth)" 157 | end 158 | 159 | it "errors on a double-quoted double-quote with a leading escaped backslash scalar" 160 | sample='one: "\\""' 161 | declare -A resulth=() 162 | stop_on_error off 163 | yml2struct resulth <<<"$sample" 164 | assert unequal $? 0 165 | stop_on_error 166 | end 167 | 168 | it "doesn't expand shell variables" 169 | one=hello 170 | sample='one: $one' 171 | declare -A resulth=() 172 | yml2struct resulth <<<"$sample" 173 | expected='declare -A resulth='\''([one]="\$one" )'\' 174 | assert equal "$expected" "$(declare -p resulth)" 175 | end 176 | 177 | it "parses a plain scalar with a space inside" 178 | sample='one: 1 1' 179 | declare -A resulth=() 180 | expected='declare -A resulth='\''([one]="1 1" )'\' 181 | yml2struct resulth <<<"$sample" 182 | assert equal "$expected" "$(declare -p resulth)" 183 | end 184 | 185 | it "parses a double-quoted scalar with a leading space" 186 | sample='one: " 1"' 187 | declare -A resulth=() 188 | expected='declare -A resulth='\''([one]=" 1" )'\' 189 | yml2struct resulth <<<"$sample" 190 | assert equal "$expected" "$(declare -p resulth)" 191 | end 192 | 193 | it "parses a double-quoted scalar with a trailing space" 194 | sample='one: "1 "' 195 | declare -A resulth=() 196 | expected='declare -A resulth='\''([one]="1 " )'\' 197 | yml2struct resulth <<<"$sample" 198 | assert equal "$expected" "$(declare -p resulth)" 199 | end 200 | 201 | it "parses a hash nested in a hash" 202 | read -rd '' sample <<'EOS' ||: 203 | oneh: 204 | two: 2 205 | EOS 206 | declare -A resulth=() 207 | expected='declare -A resulth='\''([oneh.two]="2" [oneh]="([two]=\\"2\\" )" )'\' 208 | yml2struct resulth <<<"$sample" 209 | assert equal "$expected" "$(declare -p resulth)" 210 | end 211 | 212 | it "parses an array nested in a hash" 213 | read -rd '' sample <<'EOS' ||: 214 | oneh: 215 | - zero 216 | EOS 217 | declare -A resulth=() 218 | expected='declare -A resulth='\''([oneh]="([0]=\\"zero\\" )" [oneh.0]="zero" )'\' 219 | yml2struct resulth <<<"$sample" 220 | assert equal "$expected" "$(declare -p resulth)" 221 | end 222 | 223 | it "continues parsing a hash after a nested element" 224 | read -rd '' sample <<'EOS' ||: 225 | oneh: 226 | two: 2 227 | threeh: 228 | four: 4 229 | EOS 230 | declare -A resulth=() 231 | expected='declare -A resulth='\''([threeh]="([four]=\"4\" )" [oneh.two]="2" [oneh]="([two]=\"2\" )" [threeh.four]="4" )'\' 232 | yml2struct resulth <<<"$sample" 233 | assert equal "$expected" "$(declare -p resulth)" 234 | end 235 | 236 | it "parses a nested hash two levels deep" 237 | read -rd '' sample <<'EOS' ||: 238 | oneh: 239 | twoh: 240 | three: 3 241 | EOS 242 | declare -A resulth=() 243 | expected='declare -A resulth='\''([oneh.twoh.three]="3" [oneh]="([twoh.three]=\"3\" [twoh]=\"([three]=\\\"3\\\" )\" )" [oneh.twoh]="([three]=\"3\" )" )'\' 244 | yml2struct resulth <<<"$sample" 245 | assert equal "$expected" "$(declare -p resulth)" 246 | end 247 | 248 | it "parses an array" 249 | sample='- one' 250 | declare -A resulth=() 251 | expected='declare -A resulth='\''([0]="one" )'\' 252 | yml2struct resulth <<<"$sample" 253 | assert equal "$expected" "$(declare -p resulth)" 254 | end 255 | 256 | it "parses an array in an array" 257 | read -rd '' sample <<'EOS' ||: 258 | - 259 | - zero 260 | EOS 261 | declare -A resulth=() 262 | expected='declare -A resulth='\''([0.0]="zero" [0]="([0]=\"zero\" )" )'\' 263 | yml2struct resulth <<<"$sample" 264 | assert equal "$expected" "$(declare -p resulth)" 265 | end 266 | end 267 | --------------------------------------------------------------------------------