github.com/mattdotmatt/gauge@v0.3.2-0.20160421115137-425a4cdccb62/formatter/formatter.go (about)

     1  // Copyright 2015 ThoughtWorks, Inc.
     2  
     3  // This file is part of Gauge.
     4  
     5  // Gauge is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  
    10  // Gauge is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13  // GNU General Public License for more details.
    14  
    15  // You should have received a copy of the GNU General Public License
    16  // along with Gauge.  If not, see <http://www.gnu.org/licenses/>.
    17  
    18  package formatter
    19  
    20  import (
    21  	"bytes"
    22  	"fmt"
    23  	"sort"
    24  	"strings"
    25  
    26  	"github.com/getgauge/common"
    27  	"github.com/getgauge/gauge/gauge"
    28  	"github.com/getgauge/gauge/gauge_messages"
    29  	"github.com/getgauge/gauge/parser"
    30  	"github.com/getgauge/gauge/util"
    31  )
    32  
    33  const (
    34  	tableLeftSpacing = 5
    35  )
    36  
    37  func FormatSpecFiles(specFiles ...string) []*parser.ParseResult {
    38  	specs, results := parser.ParseSpecFiles(specFiles, &gauge.ConceptDictionary{})
    39  	for i, spec := range specs {
    40  		if err := formatAndSave(spec); err != nil {
    41  			results[i].ParseError = &parser.ParseError{Message: err.Error()}
    42  		}
    43  	}
    44  	return results
    45  }
    46  
    47  func FormatSpecHeading(specHeading string) string {
    48  	return FormatHeading(specHeading, "=")
    49  }
    50  
    51  func FormatScenarioHeading(scenarioHeading string) string {
    52  	return fmt.Sprintf("%s", FormatHeading(scenarioHeading, "-"))
    53  }
    54  
    55  func FormatStep(step *gauge.Step) string {
    56  	text := step.Value
    57  	paramCount := strings.Count(text, gauge.ParameterPlaceholder)
    58  	for i := 0; i < paramCount; i++ {
    59  		argument := step.Args[i]
    60  		formattedArg := ""
    61  		if argument.ArgType == gauge.TableArg {
    62  			formattedTable := FormatTable(&argument.Table)
    63  			formattedArg = fmt.Sprintf("\n%s", formattedTable)
    64  		} else if argument.ArgType == gauge.Dynamic {
    65  			formattedArg = fmt.Sprintf("<%s>", parser.GetUnescapedString(argument.Value))
    66  		} else if argument.ArgType == gauge.SpecialString || argument.ArgType == gauge.SpecialTable {
    67  			formattedArg = fmt.Sprintf("<%s>", parser.GetUnescapedString(argument.Name))
    68  		} else {
    69  			formattedArg = fmt.Sprintf("\"%s\"", parser.GetUnescapedString(argument.Value))
    70  		}
    71  		text = strings.Replace(text, gauge.ParameterPlaceholder, formattedArg, 1)
    72  	}
    73  	stepText := ""
    74  	if strings.HasSuffix(text, "\n") {
    75  		stepText = fmt.Sprintf("* %s", text)
    76  	} else {
    77  		stepText = fmt.Sprintf("* %s\n", text)
    78  	}
    79  	return stepText
    80  }
    81  
    82  func FormatConcept(protoConcept *gauge_messages.ProtoConcept) string {
    83  	conceptText := "* "
    84  	for _, fragment := range protoConcept.ConceptStep.GetFragments() {
    85  		if fragment.GetFragmentType() == gauge_messages.Fragment_Text {
    86  			conceptText = conceptText + fragment.GetText()
    87  		} else if fragment.GetFragmentType() == gauge_messages.Fragment_Parameter {
    88  			if fragment.GetParameter().GetParameterType() == (gauge_messages.Parameter_Table | gauge_messages.Parameter_Special_Table) {
    89  				conceptText += "\n" + FormatTable(parser.TableFrom(fragment.GetParameter().GetTable()))
    90  			} else {
    91  				conceptText = conceptText + "\"" + fragment.GetParameter().GetValue() + "\""
    92  			}
    93  		}
    94  	}
    95  	return conceptText + "\n"
    96  }
    97  
    98  func FormatHeading(heading, headingChar string) string {
    99  	trimmedHeading := strings.TrimSpace(heading)
   100  	length := len(trimmedHeading)
   101  	return fmt.Sprintf("%s\n%s\n", trimmedHeading, getRepeatedChars(headingChar, length))
   102  }
   103  
   104  func FormatTable(table *gauge.Table) string {
   105  	columnToWidthMap := make(map[int]int)
   106  	for i, header := range table.Headers {
   107  		//table.get(header) returns a list of cells in that particular column
   108  		cells := table.Get(header)
   109  		columnToWidthMap[i] = findLongestCellWidth(cells, len(header))
   110  	}
   111  
   112  	var tableStringBuffer bytes.Buffer
   113  	tableStringBuffer.WriteString(fmt.Sprintf("%s|", getRepeatedChars(" ", tableLeftSpacing)))
   114  	for i, header := range table.Headers {
   115  		width := columnToWidthMap[i]
   116  		tableStringBuffer.WriteString(fmt.Sprintf("%s|", addPaddingToCell(header, width)))
   117  	}
   118  
   119  	tableStringBuffer.WriteString("\n")
   120  	tableStringBuffer.WriteString(fmt.Sprintf("%s|", getRepeatedChars(" ", tableLeftSpacing)))
   121  	for i := range table.Headers {
   122  		width := columnToWidthMap[i]
   123  		cell := getRepeatedChars("-", width)
   124  		tableStringBuffer.WriteString(fmt.Sprintf("%s|", addPaddingToCell(cell, width)))
   125  	}
   126  
   127  	tableStringBuffer.WriteString("\n")
   128  	for _, row := range table.Rows() {
   129  		tableStringBuffer.WriteString(fmt.Sprintf("%s|", getRepeatedChars(" ", tableLeftSpacing)))
   130  		for i, cell := range row {
   131  			width := columnToWidthMap[i]
   132  			tableStringBuffer.WriteString(fmt.Sprintf("%s|", addPaddingToCell(cell, width)))
   133  		}
   134  		tableStringBuffer.WriteString("\n")
   135  	}
   136  
   137  	return string(tableStringBuffer.Bytes())
   138  }
   139  
   140  func addPaddingToCell(cellValue string, width int) string {
   141  	padding := getRepeatedChars(" ", width-len(cellValue))
   142  	return fmt.Sprintf("%s%s", cellValue, padding)
   143  }
   144  
   145  func findLongestCellWidth(columnCells []gauge.TableCell, minValue int) int {
   146  	longestLength := minValue
   147  	for _, cellValue := range columnCells {
   148  		cellValueLen := len(cellValue.GetValue())
   149  		if cellValueLen > longestLength {
   150  			longestLength = cellValueLen
   151  		}
   152  	}
   153  	return longestLength
   154  }
   155  
   156  func FormatComment(comment *gauge.Comment) string {
   157  	if comment.Value == "\n" {
   158  		return comment.Value
   159  	}
   160  	return fmt.Sprintf("%s\n", comment.Value)
   161  }
   162  
   163  func FormatTags(tags *gauge.Tags) string {
   164  	if tags == nil || len(tags.Values) == 0 {
   165  		return ""
   166  	}
   167  	var b bytes.Buffer
   168  	b.WriteString("tags: ")
   169  	for i, tag := range tags.Values {
   170  		b.WriteString(tag)
   171  		if (i + 1) != len(tags.Values) {
   172  			b.WriteString(", ")
   173  		}
   174  	}
   175  	b.WriteString("\n")
   176  	return string(b.Bytes())
   177  }
   178  
   179  func FormatExternalDataTable(dataTable *gauge.DataTable) string {
   180  	if dataTable == nil || len(dataTable.Value) == 0 {
   181  		return ""
   182  	}
   183  	var b bytes.Buffer
   184  	b.WriteString(dataTable.Value)
   185  	b.WriteString("\n")
   186  	return string(b.Bytes())
   187  }
   188  
   189  func formatAndSave(spec *gauge.Specification) error {
   190  	formatted := FormatSpecification(spec)
   191  	if err := common.SaveFile(spec.FileName, formatted, true); err != nil {
   192  		return err
   193  	}
   194  	return nil
   195  }
   196  
   197  func FormatSpecification(specification *gauge.Specification) string {
   198  	var formattedSpec bytes.Buffer
   199  	formatter := &formatter{buffer: formattedSpec}
   200  	specification.Traverse(formatter)
   201  	return string(formatter.buffer.Bytes())
   202  }
   203  
   204  func sortConcepts(conceptDictionary *gauge.ConceptDictionary, conceptMap map[string]string) []*gauge.Concept {
   205  	var concepts []*gauge.Concept
   206  	for _, concept := range conceptDictionary.ConceptsMap {
   207  		conceptMap[concept.FileName] = ""
   208  		concepts = append(concepts, concept)
   209  	}
   210  	sort.Sort(gauge.ByLineNo(concepts))
   211  	return concepts
   212  }
   213  
   214  func formatConceptSteps(conceptMap map[string]string, concept *gauge.Concept) {
   215  	conceptMap[concept.FileName] += strings.TrimSpace(strings.Replace(FormatStep(concept.ConceptStep), "*", "#", 1)) + "\n"
   216  	for i := 1; i < len(concept.ConceptStep.Items); i++ {
   217  		conceptMap[concept.FileName] += formatItem(concept.ConceptStep.Items[i])
   218  	}
   219  }
   220  
   221  func FormatConcepts(conceptDictionary *gauge.ConceptDictionary) map[string]string {
   222  	conceptMap := make(map[string]string)
   223  	for _, concept := range sortConcepts(conceptDictionary, conceptMap) {
   224  		for _, comment := range concept.ConceptStep.PreComments {
   225  			conceptMap[concept.FileName] += FormatComment(comment)
   226  		}
   227  		formatConceptSteps(conceptMap, concept)
   228  	}
   229  	return conceptMap
   230  }
   231  
   232  func formatItem(item gauge.Item) string {
   233  	switch item.Kind() {
   234  	case gauge.CommentKind:
   235  		comment := item.(*gauge.Comment)
   236  		if comment.Value == "\n" {
   237  			return comment.Value
   238  		}
   239  		return fmt.Sprintf("%s\n", comment.Value)
   240  	case gauge.StepKind:
   241  		step := item.(*gauge.Step)
   242  		return FormatStep(step)
   243  	case gauge.DataTableKind:
   244  		dataTable := item.(*gauge.DataTable)
   245  		return FormatTable(&dataTable.Table)
   246  	case gauge.TagKind:
   247  		tags := item.(*gauge.Tags)
   248  		return FormatTags(tags)
   249  	}
   250  	return ""
   251  }
   252  
   253  func getRepeatedChars(character string, repeatCount int) string {
   254  	formatted := ""
   255  	for i := 0; i < repeatCount; i++ {
   256  		formatted = fmt.Sprintf("%s%s", formatted, character)
   257  	}
   258  	return formatted
   259  }
   260  
   261  func FormatSpecFilesIn(filesLocation string) {
   262  	specFiles := util.GetSpecFiles(filesLocation)
   263  	parseResults := FormatSpecFiles(specFiles...)
   264  	parser.HandleParseResult(parseResults...)
   265  }