| 
									
										
										
										
											2015-12-07 19:57:01 +01:00
										 |  |  | // Copyright 2015 The Hugo Authors. All rights reserved.
 | 
					
						
							| 
									
										
										
										
											2014-05-01 13:19:51 -04:00
										 |  |  | //
 | 
					
						
							| 
									
										
										
										
											2015-11-23 22:16:36 -05:00
										 |  |  | // Licensed under the Apache License, Version 2.0 (the "License");
 | 
					
						
							| 
									
										
										
										
											2014-05-01 13:19:51 -04:00
										 |  |  | // you may not use this file except in compliance with the License.
 | 
					
						
							|  |  |  | // You may obtain a copy of the License at
 | 
					
						
							| 
									
										
										
										
											2015-11-23 22:16:36 -05:00
										 |  |  | // http://www.apache.org/licenses/LICENSE-2.0
 | 
					
						
							| 
									
										
										
										
											2014-05-01 13:19:51 -04:00
										 |  |  | //
 | 
					
						
							|  |  |  | // Unless required by applicable law or agreed to in writing, software
 | 
					
						
							|  |  |  | // distributed under the License is distributed on an "AS IS" BASIS,
 | 
					
						
							|  |  |  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
					
						
							|  |  |  | // See the License for the specific language governing permissions and
 | 
					
						
							|  |  |  | // limitations under the License.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | package parser
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-18 19:06:28 +02:00
										 |  |  | // TODO(bep) archetype remove unused from this package.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-01 13:19:51 -04:00
										 |  |  | import (
 | 
					
						
							|  |  |  | 	"bytes"
 | 
					
						
							|  |  |  | 	"encoding/json"
 | 
					
						
							| 
									
										
										
										
											2016-11-18 22:54:57 +01:00
										 |  |  | 	"errors"
 | 
					
						
							| 
									
										
										
										
											2016-12-26 15:23:20 -06:00
										 |  |  | 	"io"
 | 
					
						
							| 
									
										
										
										
											2014-05-02 01:01:44 -04:00
										 |  |  | 	"strings"
 | 
					
						
							| 
									
										
										
										
											2014-05-01 13:19:51 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-31 13:10:46 +03:00
										 |  |  | 	"github.com/BurntSushi/toml"
 | 
					
						
							| 
									
										
										
										
											2017-02-20 23:46:03 -08:00
										 |  |  | 	"github.com/chaseadamsio/goorgeous"
 | 
					
						
							| 
									
										
										
										
											2016-08-20 20:28:38 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-24 04:30:00 -07:00
										 |  |  | 	"gopkg.in/yaml.v2"
 | 
					
						
							| 
									
										
										
										
											2014-05-01 13:19:51 -04:00
										 |  |  | )
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-26 15:23:20 -06:00
										 |  |  | // FrontmatterType represents a type of frontmatter.
 | 
					
						
							|  |  |  | type FrontmatterType struct {
 | 
					
						
							|  |  |  | 	// Parse decodes content into a Go interface.
 | 
					
						
							|  |  |  | 	Parse func([]byte) (interface{}, error)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	markstart, markend []byte // starting and ending delimiters
 | 
					
						
							|  |  |  | 	includeMark        bool   // include start and end mark in output
 | 
					
						
							| 
									
										
										
										
											2014-05-01 13:19:51 -04:00
										 |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-26 15:23:20 -06:00
										 |  |  | // InterfaceToConfig encodes a given input based upon the mark and writes to w.
 | 
					
						
							|  |  |  | func InterfaceToConfig(in interface{}, mark rune, w io.Writer) error {
 | 
					
						
							| 
									
										
										
										
											2014-05-08 18:30:11 -04:00
										 |  |  | 	if in == nil {
 | 
					
						
							| 
									
										
										
										
											2016-12-26 15:23:20 -06:00
										 |  |  | 		return errors.New("input was nil")
 | 
					
						
							| 
									
										
										
										
											2014-05-08 18:30:11 -04:00
										 |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch mark {
 | 
					
						
							| 
									
										
										
										
											2016-03-23 14:51:16 +01:00
										 |  |  | 	case rune(YAMLLead[0]):
 | 
					
						
							| 
									
										
										
										
											2016-12-26 15:23:20 -06:00
										 |  |  | 		b, err := yaml.Marshal(in)
 | 
					
						
							| 
									
										
										
										
											2014-05-08 18:30:11 -04:00
										 |  |  | 		if err != nil {
 | 
					
						
							| 
									
										
										
										
											2016-12-26 15:23:20 -06:00
										 |  |  | 			return err
 | 
					
						
							| 
									
										
										
										
											2014-05-08 18:30:11 -04:00
										 |  |  | 		}
 | 
					
						
							| 
									
										
										
										
											2016-12-26 15:23:20 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		_, err = w.Write(b)
 | 
					
						
							|  |  |  | 		return err
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-23 14:51:16 +01:00
										 |  |  | 	case rune(TOMLLead[0]):
 | 
					
						
							| 
									
										
										
										
											2017-05-31 13:10:46 +03:00
										 |  |  | 		return toml.NewEncoder(w).Encode(in)
 | 
					
						
							| 
									
										
										
										
											2016-03-23 14:51:16 +01:00
										 |  |  | 	case rune(JSONLead[0]):
 | 
					
						
							| 
									
										
										
										
											2016-12-26 15:23:20 -06:00
										 |  |  | 		b, err := json.MarshalIndent(in, "", "   ")
 | 
					
						
							| 
									
										
										
										
											2014-05-08 18:30:11 -04:00
										 |  |  | 		if err != nil {
 | 
					
						
							| 
									
										
										
										
											2016-12-26 15:23:20 -06:00
										 |  |  | 			return err
 | 
					
						
							| 
									
										
										
										
											2014-05-08 18:30:11 -04:00
										 |  |  | 		}
 | 
					
						
							| 
									
										
										
										
											2016-12-26 15:23:20 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		_, err = w.Write(b)
 | 
					
						
							| 
									
										
										
										
											2014-05-08 18:30:11 -04:00
										 |  |  | 		if err != nil {
 | 
					
						
							| 
									
										
										
										
											2016-12-26 15:23:20 -06:00
										 |  |  | 			return err
 | 
					
						
							| 
									
										
										
										
											2014-05-08 18:30:11 -04:00
										 |  |  | 		}
 | 
					
						
							| 
									
										
										
										
											2016-12-26 15:23:20 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		_, err = w.Write([]byte{'\n'})
 | 
					
						
							|  |  |  | 		return err
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-08 18:30:11 -04:00
										 |  |  | 	default:
 | 
					
						
							| 
									
										
										
										
											2016-12-26 15:23:20 -06:00
										 |  |  | 		return errors.New("Unsupported Format provided")
 | 
					
						
							| 
									
										
										
										
											2014-05-08 18:30:11 -04:00
										 |  |  | 	}
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-26 15:23:20 -06:00
										 |  |  | // InterfaceToFrontMatter encodes a given input into a frontmatter
 | 
					
						
							|  |  |  | // representation based upon the mark with the appropriate front matter delimiters
 | 
					
						
							|  |  |  | // surrounding the output, which is written to w.
 | 
					
						
							|  |  |  | func InterfaceToFrontMatter(in interface{}, mark rune, w io.Writer) error {
 | 
					
						
							| 
									
										
										
										
											2014-05-01 13:19:51 -04:00
										 |  |  | 	if in == nil {
 | 
					
						
							| 
									
										
										
										
											2016-12-26 15:23:20 -06:00
										 |  |  | 		return errors.New("input was nil")
 | 
					
						
							| 
									
										
										
										
											2014-05-01 13:19:51 -04:00
										 |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch mark {
 | 
					
						
							| 
									
										
										
										
											2016-03-23 14:51:16 +01:00
										 |  |  | 	case rune(YAMLLead[0]):
 | 
					
						
							| 
									
										
										
										
											2016-12-26 15:23:20 -06:00
										 |  |  | 		_, err := w.Write([]byte(YAMLDelimUnix))
 | 
					
						
							| 
									
										
										
										
											2014-05-01 13:19:51 -04:00
										 |  |  | 		if err != nil {
 | 
					
						
							| 
									
										
										
										
											2016-12-26 15:23:20 -06:00
										 |  |  | 			return err
 | 
					
						
							| 
									
										
										
										
											2014-05-01 13:19:51 -04:00
										 |  |  | 		}
 | 
					
						
							| 
									
										
										
										
											2016-12-26 15:23:20 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		err = InterfaceToConfig(in, mark, w)
 | 
					
						
							| 
									
										
										
										
											2014-05-01 13:19:51 -04:00
										 |  |  | 		if err != nil {
 | 
					
						
							| 
									
										
										
										
											2016-12-26 15:23:20 -06:00
										 |  |  | 			return err
 | 
					
						
							| 
									
										
										
										
											2014-05-01 13:19:51 -04:00
										 |  |  | 		}
 | 
					
						
							| 
									
										
										
										
											2016-12-26 15:23:20 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		_, err = w.Write([]byte(YAMLDelimUnix))
 | 
					
						
							|  |  |  | 		return err
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-23 14:51:16 +01:00
										 |  |  | 	case rune(TOMLLead[0]):
 | 
					
						
							| 
									
										
										
										
											2016-12-26 15:23:20 -06:00
										 |  |  | 		_, err := w.Write([]byte(TOMLDelimUnix))
 | 
					
						
							| 
									
										
										
										
											2014-05-01 13:19:51 -04:00
										 |  |  | 		if err != nil {
 | 
					
						
							| 
									
										
										
										
											2016-12-26 15:23:20 -06:00
										 |  |  | 			return err
 | 
					
						
							| 
									
										
										
										
											2014-05-01 13:19:51 -04:00
										 |  |  | 		}
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-26 15:23:20 -06:00
										 |  |  | 		err = InterfaceToConfig(in, mark, w)
 | 
					
						
							| 
									
										
										
										
											2017-03-15 08:04:52 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-01 13:19:51 -04:00
										 |  |  | 		if err != nil {
 | 
					
						
							| 
									
										
										
										
											2016-12-26 15:23:20 -06:00
										 |  |  | 			return err
 | 
					
						
							| 
									
										
										
										
											2014-05-01 13:19:51 -04:00
										 |  |  | 		}
 | 
					
						
							| 
									
										
										
										
											2016-12-26 15:23:20 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		_, err = w.Write([]byte("\n" + TOMLDelimUnix))
 | 
					
						
							|  |  |  | 		return err
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-01 13:19:51 -04:00
										 |  |  | 	default:
 | 
					
						
							| 
									
										
										
										
											2016-12-26 15:23:20 -06:00
										 |  |  | 		return InterfaceToConfig(in, mark, w)
 | 
					
						
							| 
									
										
										
										
											2014-05-01 13:19:51 -04:00
										 |  |  | 	}
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-26 15:23:20 -06:00
										 |  |  | // FormatToLeadRune takes a given format kind and return the leading front
 | 
					
						
							|  |  |  | // matter delimiter.
 | 
					
						
							| 
									
										
										
										
											2014-05-02 01:01:44 -04:00
										 |  |  | func FormatToLeadRune(kind string) rune {
 | 
					
						
							| 
									
										
										
										
											2014-05-08 18:30:11 -04:00
										 |  |  | 	switch FormatSanitize(kind) {
 | 
					
						
							| 
									
										
										
										
											2014-05-02 01:01:44 -04:00
										 |  |  | 	case "yaml":
 | 
					
						
							| 
									
										
										
										
											2016-03-23 14:51:16 +01:00
										 |  |  | 		return rune([]byte(YAMLLead)[0])
 | 
					
						
							| 
									
										
										
										
											2014-05-02 01:01:44 -04:00
										 |  |  | 	case "json":
 | 
					
						
							| 
									
										
										
										
											2016-03-23 14:51:16 +01:00
										 |  |  | 		return rune([]byte(JSONLead)[0])
 | 
					
						
							| 
									
										
										
										
											2016-12-26 15:23:20 -06:00
										 |  |  | 	case "org":
 | 
					
						
							|  |  |  | 		return '#'
 | 
					
						
							| 
									
										
										
										
											2014-05-02 01:01:44 -04:00
										 |  |  | 	default:
 | 
					
						
							| 
									
										
										
										
											2016-03-23 14:51:16 +01:00
										 |  |  | 		return rune([]byte(TOMLLead)[0])
 | 
					
						
							| 
									
										
										
										
											2014-05-02 01:01:44 -04:00
										 |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2014-05-08 18:30:11 -04:00
										 |  |  | }
 | 
					
						
							| 
									
										
										
										
											2014-05-02 01:01:44 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-26 15:23:20 -06:00
										 |  |  | // FormatSanitize returns the canonical format name for a given kind.
 | 
					
						
							|  |  |  | //
 | 
					
						
							| 
									
										
										
										
											2016-03-14 17:52:11 +01:00
										 |  |  | // TODO(bep) move to helpers
 | 
					
						
							| 
									
										
										
										
											2014-05-08 18:30:11 -04:00
										 |  |  | func FormatSanitize(kind string) string {
 | 
					
						
							|  |  |  | 	switch strings.ToLower(kind) {
 | 
					
						
							|  |  |  | 	case "yaml", "yml":
 | 
					
						
							|  |  |  | 		return "yaml"
 | 
					
						
							|  |  |  | 	case "toml", "tml":
 | 
					
						
							|  |  |  | 		return "toml"
 | 
					
						
							|  |  |  | 	case "json", "js":
 | 
					
						
							|  |  |  | 		return "json"
 | 
					
						
							| 
									
										
										
										
											2016-12-26 15:23:20 -06:00
										 |  |  | 	case "org":
 | 
					
						
							|  |  |  | 		return kind
 | 
					
						
							| 
									
										
										
										
											2014-05-08 18:30:11 -04:00
										 |  |  | 	default:
 | 
					
						
							|  |  |  | 		return "toml"
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2014-05-02 01:01:44 -04:00
										 |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-13 22:53:06 +01:00
										 |  |  | // DetectFrontMatter detects the type of frontmatter analysing its first character.
 | 
					
						
							| 
									
										
										
										
											2016-12-26 15:23:20 -06:00
										 |  |  | func DetectFrontMatter(mark rune) (f *FrontmatterType) {
 | 
					
						
							| 
									
										
										
										
											2014-05-01 13:19:51 -04:00
										 |  |  | 	switch mark {
 | 
					
						
							|  |  |  | 	case '-':
 | 
					
						
							| 
									
										
										
										
											2016-12-26 15:23:20 -06:00
										 |  |  | 		return &FrontmatterType{HandleYAMLMetaData, []byte(YAMLDelim), []byte(YAMLDelim), false}
 | 
					
						
							| 
									
										
										
										
											2014-05-01 13:19:51 -04:00
										 |  |  | 	case '+':
 | 
					
						
							| 
									
										
										
										
											2016-12-26 15:23:20 -06:00
										 |  |  | 		return &FrontmatterType{HandleTOMLMetaData, []byte(TOMLDelim), []byte(TOMLDelim), false}
 | 
					
						
							| 
									
										
										
										
											2014-05-01 13:19:51 -04:00
										 |  |  | 	case '{':
 | 
					
						
							| 
									
										
										
										
											2016-12-26 15:23:20 -06:00
										 |  |  | 		return &FrontmatterType{HandleJSONMetaData, []byte{'{'}, []byte{'}'}, true}
 | 
					
						
							| 
									
										
										
										
											2017-02-20 23:46:03 -08:00
										 |  |  | 	case '#':
 | 
					
						
							| 
									
										
										
										
											2016-12-26 15:23:20 -06:00
										 |  |  | 		return &FrontmatterType{HandleOrgMetaData, []byte("#+"), []byte("\n"), false}
 | 
					
						
							| 
									
										
										
										
											2014-05-01 13:19:51 -04:00
										 |  |  | 	default:
 | 
					
						
							|  |  |  | 		return nil
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-26 15:23:20 -06:00
										 |  |  | // HandleTOMLMetaData unmarshals TOML-encoded datum and returns a Go interface
 | 
					
						
							|  |  |  | // representing the encoded data structure.
 | 
					
						
							| 
									
										
										
										
											2015-03-11 11:34:57 -06:00
										 |  |  | func HandleTOMLMetaData(datum []byte) (interface{}, error) {
 | 
					
						
							| 
									
										
										
										
											2014-05-01 13:19:51 -04:00
										 |  |  | 	m := map[string]interface{}{}
 | 
					
						
							| 
									
										
										
										
											2015-03-11 11:34:57 -06:00
										 |  |  | 	datum = removeTOMLIdentifier(datum)
 | 
					
						
							| 
									
										
										
										
											2016-08-20 20:28:38 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-31 13:10:46 +03:00
										 |  |  | 	_, err := toml.Decode(string(datum), &m)
 | 
					
						
							| 
									
										
										
										
											2016-08-20 20:28:38 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-31 13:10:46 +03:00
										 |  |  | 	return m, err
 | 
					
						
							| 
									
										
										
										
											2016-08-20 20:28:38 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-01 13:19:51 -04:00
										 |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-26 15:23:20 -06:00
										 |  |  | // removeTOMLIdentifier removes, if necessary, beginning and ending TOML
 | 
					
						
							|  |  |  | // frontmatter delimiters from a byte slice.
 | 
					
						
							| 
									
										
										
										
											2015-03-11 11:34:57 -06:00
										 |  |  | func removeTOMLIdentifier(datum []byte) []byte {
 | 
					
						
							| 
									
										
										
										
											2016-12-26 15:23:20 -06:00
										 |  |  | 	ld := len(datum)
 | 
					
						
							|  |  |  | 	if ld < 8 {
 | 
					
						
							|  |  |  | 		return datum
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	b := bytes.TrimPrefix(datum, []byte(TOMLDelim))
 | 
					
						
							|  |  |  | 	if ld-len(b) != 3 {
 | 
					
						
							|  |  |  | 		// No TOML prefix trimmed, so bail out
 | 
					
						
							|  |  |  | 		return datum
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	b = bytes.Trim(b, "\r\n")
 | 
					
						
							|  |  |  | 	return bytes.TrimSuffix(b, []byte(TOMLDelim))
 | 
					
						
							| 
									
										
										
										
											2014-05-01 13:19:51 -04:00
										 |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-26 15:23:20 -06:00
										 |  |  | // HandleYAMLMetaData unmarshals YAML-encoded datum and returns a Go interface
 | 
					
						
							|  |  |  | // representing the encoded data structure.
 | 
					
						
							| 
									
										
										
										
											2015-03-11 11:34:57 -06:00
										 |  |  | func HandleYAMLMetaData(datum []byte) (interface{}, error) {
 | 
					
						
							| 
									
										
										
										
											2014-05-01 13:19:51 -04:00
										 |  |  | 	m := map[string]interface{}{}
 | 
					
						
							| 
									
										
										
										
											2016-08-30 22:29:48 +05:00
										 |  |  | 	err := yaml.Unmarshal(datum, &m)
 | 
					
						
							|  |  |  | 	return m, err
 | 
					
						
							| 
									
										
										
										
											2014-05-01 13:19:51 -04:00
										 |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-26 15:23:20 -06:00
										 |  |  | // HandleJSONMetaData unmarshals JSON-encoded datum and returns a Go interface
 | 
					
						
							|  |  |  | // representing the encoded data structure.
 | 
					
						
							| 
									
										
										
										
											2015-03-11 11:34:57 -06:00
										 |  |  | func HandleJSONMetaData(datum []byte) (interface{}, error) {
 | 
					
						
							| 
									
										
										
										
											2016-12-26 15:23:20 -06:00
										 |  |  | 	if datum == nil {
 | 
					
						
							|  |  |  | 		// Package json returns on error on nil input.
 | 
					
						
							|  |  |  | 		// Return an empty map to be consistent with our other supported
 | 
					
						
							|  |  |  | 		// formats.
 | 
					
						
							|  |  |  | 		return make(map[string]interface{}), nil
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-01 13:19:51 -04:00
										 |  |  | 	var f interface{}
 | 
					
						
							| 
									
										
										
										
											2016-08-30 22:29:48 +05:00
										 |  |  | 	err := json.Unmarshal(datum, &f)
 | 
					
						
							|  |  |  | 	return f, err
 | 
					
						
							| 
									
										
										
										
											2014-05-01 13:19:51 -04:00
										 |  |  | }
 | 
					
						
							| 
									
										
										
										
											2017-02-20 23:46:03 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-26 15:23:20 -06:00
										 |  |  | // HandleOrgMetaData unmarshals org-mode encoded datum and returns a Go
 | 
					
						
							|  |  |  | // interface representing the encoded data structure.
 | 
					
						
							| 
									
										
										
										
											2017-02-20 23:46:03 -08:00
										 |  |  | func HandleOrgMetaData(datum []byte) (interface{}, error) {
 | 
					
						
							|  |  |  | 	return goorgeous.OrgHeaders(datum)
 | 
					
						
							|  |  |  | }
 |