Repository: segmentio/terraform-docs
Pull request #136 (2019-12-21 02:06:50)

Move doc.Doc to tfconf.Module
### Prerequisites

Put an `x` into the box(es) that apply:

- [ ] This pull request fixes a bug.
- [ ] This pull request adds a feature.
- [x] This pull request enhances existing functionality.
- [ ] This pull request introduces breaking change.

For more information, see the [Contributing Guide](https://github.com/segmentio/terraform-docs/tree/master/CONTRIBUTING.md).

### Description

This PR refactors the confusing `doc.Doc` to `tfconf.Module` for better readability and also adjusts sorting and references with pointers for better performance.

### Issues Resolved

List any existing issues this pull request resolves.

### Checklist

Put an `x` into all boxes that apply:

- [ ] I have read the [Contributing Guidelines](https://github.com/segmentio/terraform-docs/tree/master/CONTRIBUTING.md).

#### Tests

- [ ] I have added tests to cover my changes.
- [x] All tests pass when I run `make test`.

#### Documentation

- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.

#### Code Style

- [ ] My code follows the code style of this project.



Patch:
diff --git a/cmd/json.go b/cmd/json.go
index 9409d9f..307af9d 100644
--- a/cmd/json.go
+++ b/cmd/json.go
@@ -1,8 +1,8 @@
 package cmd
 
 import (
-	"github.com/segmentio/terraform-docs/internal/pkg/doc"
 	"github.com/segmentio/terraform-docs/internal/pkg/print/json"
+	"github.com/segmentio/terraform-docs/internal/pkg/tfconf"
 	"github.com/spf13/cobra"
 )
 
@@ -11,8 +11,8 @@ var jsonCmd = &cobra.Command{
 	Use:   "json [PATH...]",
 	Short: "Generate a JSON of inputs and outputs",
 	Run: func(cmd *cobra.Command, args []string) {
-		doPrint(args, func(doc *doc.Doc) (string, error) {
-			return json.Print(doc, settings)
+		doPrint(args, func(module *tfconf.Module) (string, error) {
+			return json.Print(module, settings)
 		})
 	},
 }
diff --git a/cmd/markdown.go b/cmd/markdown.go
index d795de2..ca8040d 100644
--- a/cmd/markdown.go
+++ b/cmd/markdown.go
@@ -1,9 +1,9 @@
 package cmd
 
 import (
-	"github.com/segmentio/terraform-docs/internal/pkg/doc"
 	"github.com/segmentio/terraform-docs/internal/pkg/print/markdown/document"
 	"github.com/segmentio/terraform-docs/internal/pkg/print/markdown/table"
+	"github.com/segmentio/terraform-docs/internal/pkg/tfconf"
 	"github.com/spf13/cobra"
 )
 
@@ -13,8 +13,8 @@ var markdownCmd = &cobra.Command{
 	Aliases: []string{"md"},
 	Short:   "Generate Markdown of inputs and outputs",
 	Run: func(cmd *cobra.Command, args []string) {
-		doPrint(args, func(doc *doc.Doc) (string, error) {
-			return table.Print(doc, settings)
+		doPrint(args, func(module *tfconf.Module) (string, error) {
+			return table.Print(module, settings)
 		})
 	},
 }
@@ -25,8 +25,8 @@ var mdTableCmd = &cobra.Command{
 	Aliases: []string{"tbl"},
 	Short:   "Generate Markdown tables of inputs and outputs",
 	Run: func(cmd *cobra.Command, args []string) {
-		doPrint(args, func(doc *doc.Doc) (string, error) {
-			return table.Print(doc, settings)
+		doPrint(args, func(module *tfconf.Module) (string, error) {
+			return table.Print(module, settings)
 		})
 	},
 }
@@ -37,8 +37,8 @@ var mdDocumentCmd = &cobra.Command{
 	Aliases: []string{"doc"},
 	Short:   "Generate Markdown document of inputs and outputs",
 	Run: func(cmd *cobra.Command, args []string) {
-		doPrint(args, func(doc *doc.Doc) (string, error) {
-			return document.Print(doc, settings)
+		doPrint(args, func(module *tfconf.Module) (string, error) {
+			return document.Print(module, settings)
 		})
 	},
 }
diff --git a/cmd/pretty.go b/cmd/pretty.go
index a10cf72..863b7f6 100644
--- a/cmd/pretty.go
+++ b/cmd/pretty.go
@@ -1,8 +1,8 @@
 package cmd
 
 import (
-	"github.com/segmentio/terraform-docs/internal/pkg/doc"
 	"github.com/segmentio/terraform-docs/internal/pkg/print/pretty"
+	"github.com/segmentio/terraform-docs/internal/pkg/tfconf"
 	"github.com/spf13/cobra"
 )
 
@@ -11,8 +11,8 @@ var prettyCmd = &cobra.Command{
 	Use:   "pretty [PATH...]",
 	Short: "Generate a colorized pretty of inputs and outputs",
 	Run: func(cmd *cobra.Command, args []string) {
-		doPrint(args, func(doc *doc.Doc) (string, error) {
-			return pretty.Print(doc, settings)
+		doPrint(args, func(module *tfconf.Module) (string, error) {
+			return pretty.Print(module, settings)
 		})
 	},
 }
diff --git a/cmd/root.go b/cmd/root.go
index 8bb14b8..247e5b8 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -4,9 +4,8 @@ import (
 	"fmt"
 	"log"
 
-	"github.com/hashicorp/terraform-config-inspect/tfconfig"
-	"github.com/segmentio/terraform-docs/internal/pkg/doc"
 	"github.com/segmentio/terraform-docs/internal/pkg/print"
+	"github.com/segmentio/terraform-docs/internal/pkg/tfconf"
 	"github.com/segmentio/terraform-docs/internal/pkg/version"
 	"github.com/spf13/cobra"
 )
@@ -47,18 +46,13 @@ func Execute() error {
 	return rootCmd.Execute()
 }
 
-func doPrint(paths []string, fn func(*doc.Doc) (string, error)) {
-	module, diag := tfconfig.LoadModule(paths[0])
-	if diag != nil && diag.HasErrors() {
-		log.Fatal(diag)
-	}
-
-	document, err := doc.Create(module)
+func doPrint(paths []string, fn func(*tfconf.Module) (string, error)) {
+	module, err := tfconf.CreateModule(paths[0])
 	if err != nil {
 		log.Fatal(err)
 	}
 
-	output, err := fn(document)
+	output, err := fn(module)
 
 	if err != nil {
 		log.Fatal(err)
diff --git a/internal/pkg/doc/doc.go b/internal/pkg/doc/doc.go
deleted file mode 100644
index 9bb39f3..0000000
--- a/internal/pkg/doc/doc.go
+++ /dev/null
@@ -1,94 +0,0 @@
-package doc
-
-import (
-	"encoding/json"
-	"fmt"
-
-	"github.com/hashicorp/terraform-config-inspect/tfconfig"
-)
-
-// Doc represents a Terraform module.
-type Doc struct {
-	Inputs         []Input  `json:"inputs"`
-	Outputs        []Output `json:"outputs"`
-	RequiredInputs []Input  `json:"-"`
-	OptionalInputs []Input  `json:"-"`
-}
-
-// HasInputs indicates if the document has inputs.
-func (d *Doc) HasInputs() bool {
-	return len(d.Inputs) > 0
-}
-
-// HasOutputs indicates if the document has outputs.
-func (d *Doc) HasOutputs() bool {
-	return len(d.Outputs) > 0
-}
-
-// Create TODO
-func Create(module *tfconfig.Module) (*Doc, error) {
-	var inputs = make([]Input, 0, len(module.Variables))
-	var requiredInputs = make([]Input, 0, len(module.Variables))
-	var optionalInputs = make([]Input, 0, len(module.Variables))
-
-	for _, input := range module.Variables {
-		inputType := input.Type
-		if input.Type == "" {
-			inputType = "any"
-		}
-
-		var defaultValue string
-		if input.Default != nil {
-			marshaled, err := json.MarshalIndent(input.Default, "", "  ")
-			if err != nil {
-				return nil, err
-			}
-			defaultValue = string(marshaled)
-
-			if inputType == "any" {
-				switch xType := fmt.Sprintf("%T", input.Default); xType {
-				case "string":
-					inputType = "string"
-				case "int", "int8", "int16", "int32", "int64", "float32", "float64":
-					inputType = "number"
-				case "bool":
-					inputType = "bool"
-				case "[]interface {}":
-					inputType = "list"
-				case "map[string]interface {}":
-					inputType = "map"
-				}
-			}
-		}
-
-		i := Input{
-			Name:        input.Name,
-			Type:        inputType,
-			Description: input.Description,
-			Default:     defaultValue,
-		}
-
-		inputs = append(inputs, i)
-		if i.HasDefault() {
-			optionalInputs = append(optionalInputs, i)
-		} else {
-			requiredInputs = append(requiredInputs, i)
-		}
-	}
-
-	var outputs = make([]Output, 0, len(module.Outputs))
-	for _, output := range module.Outputs {
-		outputs = append(outputs, Output{
-			Name:        output.Name,
-			Description: output.Description,
-		})
-	}
-
-	doc := &Doc{
-		Inputs:         inputs,
-		Outputs:        outputs,
-		RequiredInputs: requiredInputs,
-		OptionalInputs: optionalInputs,
-	}
-	return doc, nil
-}
diff --git a/internal/pkg/print/json/json.go b/internal/pkg/print/json/json.go
index 0246b9f..a2f6693 100644
--- a/internal/pkg/print/json/json.go
+++ b/internal/pkg/print/json/json.go
@@ -3,8 +3,8 @@ package json
 import (
 	"encoding/json"
 
-	"github.com/segmentio/terraform-docs/internal/pkg/doc"
 	"github.com/segmentio/terraform-docs/internal/pkg/print"
+	"github.com/segmentio/terraform-docs/internal/pkg/tfconf"
 )
 
 const (
@@ -13,20 +13,10 @@ const (
 )
 
 // Print prints a document as json.
-func Print(document *doc.Doc, settings *print.Settings) (string, error) {
-	if settings.SortByName {
-		if settings.SortInputsByRequired {
-			doc.SortInputsByRequired(document.Inputs)
-		} else {
-			doc.SortInputsByName(document.Inputs)
-		}
-	}
-
-	if settings.SortByName {
-		doc.SortOutputsByName(document.Outputs)
-	}
+func Print(module *tfconf.Module, settings *print.Settings) (string, error) {
+	module.Sort(settings)
 
-	buffer, err := json.MarshalIndent(document, prefix, indent)
+	buffer, err := json.MarshalIndent(module, prefix, indent)
 	if err != nil {
 		return "", err
 	}
diff --git a/internal/pkg/print/markdown/document/document.go b/internal/pkg/print/markdown/document/document.go
index d545c1b..74fdda8 100644
--- a/internal/pkg/print/markdown/document/document.go
+++ b/internal/pkg/print/markdown/document/document.go
@@ -5,33 +5,19 @@ import (
 	"fmt"
 	"strings"
 
-	"github.com/segmentio/terraform-docs/internal/pkg/doc"
 	"github.com/segmentio/terraform-docs/internal/pkg/print"
 	"github.com/segmentio/terraform-docs/internal/pkg/print/markdown"
+	"github.com/segmentio/terraform-docs/internal/pkg/tfconf"
 )
 
 // Print prints a document as Markdown document.
-func Print(document *doc.Doc, settings *print.Settings) (string, error) {
+func Print(module *tfconf.Module, settings *print.Settings) (string, error) {
 	var buffer bytes.Buffer
 
-	if settings.SortByName {
-		if settings.SortInputsByRequired {
-			doc.SortInputsByRequired(document.Inputs)
-			doc.SortInputsByRequired(document.RequiredInputs)
-			doc.SortInputsByRequired(document.OptionalInputs)
-		} else {
-			doc.SortInputsByName(document.Inputs)
-			doc.SortInputsByName(document.RequiredInputs)
-			doc.SortInputsByName(document.OptionalInputs)
-		}
-	}
-
-	if settings.SortByName {
-		doc.SortOutputsByName(document.Outputs)
-	}
+	module.Sort(settings)
 
-	printInputs(&buffer, document, settings)
-	printOutputs(&buffer, document.Outputs, settings)
+	printInputs(&buffer, module, settings)
+	printOutputs(&buffer, module.Outputs, settings)
 
 	out := strings.Replace(buffer.String(), "<br>```<br>", "\n```\n", -1)
 
@@ -58,7 +44,7 @@ func Print(document *doc.Doc, settings *print.Settings) (string, error) {
 	return strings.Replace(buf.String(), " \n\n", "\n\n", -1), nil
 }
 
-func getInputType(input *doc.Input) string {
+func getInputType(input *tfconf.Input) string {
 	var result = ""
 	var extraline = false
 
@@ -68,7 +54,7 @@ func getInputType(input *doc.Input) string {
 	return result
 }
 
-func getInputValue(input *doc.Input) string {
+func getInputValue(input *tfconf.Input) string {
 	var result = "n/a\n"
 	var extraline = false
 
@@ -80,19 +66,19 @@ func getInputValue(input *doc.Input) string {
 	return result
 }
 
-func printInput(buffer *bytes.Buffer, input doc.Input, settings *print.Settings) {
+func printInput(buffer *bytes.Buffer, input *tfconf.Input, settings *print.Settings) {
 	buffer.WriteString("\n")
 	buffer.WriteString(fmt.Sprintf("%s %s\n\n", markdown.GenerateIndentation(1, settings), markdown.SanitizeName(input.Name, settings)))
 	buffer.WriteString(fmt.Sprintf("Description: %s\n\n", markdown.SanitizeItemForDocument(input.Description, settings)))
-	buffer.WriteString(fmt.Sprintf("Type: %s", getInputType(&input)))
+	buffer.WriteString(fmt.Sprintf("Type: %s", getInputType(input)))
 
 	// Don't print defaults for required inputs when we're already explicit about it being required
 	if input.HasDefault() || !settings.ShowRequired {
-		buffer.WriteString(fmt.Sprintf("\nDefault: %s", getInputValue(&input)))
+		buffer.WriteString(fmt.Sprintf("\nDefault: %s", getInputValue(input)))
 	}
 }
 
-func printInputsRequired(buffer *bytes.Buffer, inputs []doc.Input, settings *print.Settings) {
+func printInputsRequired(buffer *bytes.Buffer, inputs []*tfconf.Input, settings *print.Settings) {
 	buffer.WriteString(fmt.Sprintf("%s Required Inputs\n\n", markdown.GenerateIndentation(0, settings)))
 
 	if len(inputs) == 0 {
@@ -106,7 +92,7 @@ func printInputsRequired(buffer *bytes.Buffer, inputs []doc.Input, settings *pri
 	}
 }
 
-func printInputsOptional(buffer *bytes.Buffer, inputs []doc.Input, settings *print.Settings) {
+func printInputsOptional(buffer *bytes.Buffer, inputs []*tfconf.Input, settings *print.Settings) {
 	buffer.WriteString("\n")
 	buffer.WriteString(fmt.Sprintf("%s Optional Inputs\n\n", markdown.GenerateIndentation(0, settings)))
 
@@ -121,7 +107,7 @@ func printInputsOptional(buffer *bytes.Buffer, inputs []doc.Input, settings *pri
 	}
 }
 
-func printInputsAll(buffer *bytes.Buffer, inputs []doc.Input, settings *print.Settings) {
+func printInputsAll(buffer *bytes.Buffer, inputs []*tfconf.Input, settings *print.Settings) {
 	buffer.WriteString(fmt.Sprintf("%s Inputs\n\n", markdown.GenerateIndentation(0, settings)))
 
 	if len(inputs) == 0 {
@@ -136,16 +122,16 @@ func printInputsAll(buffer *bytes.Buffer, inputs []doc.Input, settings *print.Se
 	}
 }
 
-func printInputs(buffer *bytes.Buffer, document *doc.Doc, settings *print.Settings) {
+func printInputs(buffer *bytes.Buffer, module *tfconf.Module, settings *print.Settings) {
 	if settings.ShowRequired {
-		printInputsRequired(buffer, document.RequiredInputs, settings)
-		printInputsOptional(buffer, document.OptionalInputs, settings)
+		printInputsRequired(buffer, module.RequiredInputs, settings)
+		printInputsOptional(buffer, module.OptionalInputs, settings)
 	} else {
-		printInputsAll(buffer, document.Inputs, settings)
+		printInputsAll(buffer, module.Inputs, settings)
 	}
 }
 
-func printOutputs(buffer *bytes.Buffer, outputs []doc.Output, settings *print.Settings) {
+func printOutputs(buffer *bytes.Buffer, outputs []*tfconf.Output, settings *print.Settings) {
 	buffer.WriteString(fmt.Sprintf("\n%s Outputs\n\n", markdown.GenerateIndentation(0, settings)))
 
 	if len(outputs) == 0 {
diff --git a/internal/pkg/print/markdown/table/table.go b/internal/pkg/print/markdown/table/table.go
index a2a2b17..f72488a 100644
--- a/internal/pkg/print/markdown/table/table.go
+++ b/internal/pkg/print/markdown/table/table.go
@@ -4,43 +4,29 @@ import (
 	"bytes"
 	"fmt"
 
-	"github.com/segmentio/terraform-docs/internal/pkg/doc"
 	"github.com/segmentio/terraform-docs/internal/pkg/print"
 	"github.com/segmentio/terraform-docs/internal/pkg/print/markdown"
+	"github.com/segmentio/terraform-docs/internal/pkg/tfconf"
 )
 
 // Print prints a document as Markdown tables.
-func Print(document *doc.Doc, settings *print.Settings) (string, error) {
+func Print(module *tfconf.Module, settings *print.Settings) (string, error) {
 	var buffer bytes.Buffer
 
-	if settings.SortByName {
-		if settings.SortInputsByRequired {
-			doc.SortInputsByRequired(document.Inputs)
-			// doc.SortInputsByRequired(document.RequiredInputs)
-			// doc.SortInputsByRequired(document.OptionalInputs)
-		} else {
-			doc.SortInputsByName(document.Inputs)
-			// doc.SortInputsByName(document.RequiredInputs)
-			// doc.SortInputsByName(document.OptionalInputs)
-		}
-	}
-
-	if settings.SortByName {
-		doc.SortOutputsByName(document.Outputs)
-	}
+	module.Sort(settings)
 
-	printInputs(&buffer, document.Inputs, settings)
-	printOutputs(&buffer, document.Outputs, settings)
+	printInputs(&buffer, module.Inputs, settings)
+	printOutputs(&buffer, module.Outputs, settings)
 
 	return markdown.Sanitize(buffer.String()), nil
 }
 
-func getInputType(input *doc.Input) string {
+func getInputType(input *tfconf.Input) string {
 	inputType, _ := markdown.PrintFencedCodeBlock(input.Type, "")
 	return inputType
 }
 
-func getInputValue(input *doc.Input) string {
+func getInputValue(input *tfconf.Input) string {
 	var result = "n/a"
 
 	if input.HasDefault() {
@@ -49,14 +35,14 @@ func getInputValue(input *doc.Input) string {
 	return result
 }
 
-func printIsInputRequired(input *doc.Input) string {
+func printIsInputRequired(input *tfconf.Input) string {
 	if !input.HasDefault() {
 		return "yes"
 	}
 	return "no"
 }
 
-func printInputs(buffer *bytes.Buffer, inputs []doc.Input, settings *print.Settings) {
+func printInputs(buffer *bytes.Buffer, inputs []*tfconf.Input, settings *print.Settings) {
 	buffer.WriteString(fmt.Sprintf("%s Inputs\n\n", markdown.GenerateIndentation(0, settings)))
 
 	if len(inputs) == 0 {
@@ -86,13 +72,13 @@ func printInputs(buffer *bytes.Buffer, inputs []doc.Input, settings *print.Setti
 				"| %s | %s | %s | %s |",
 				markdown.SanitizeName(input.Name, settings),
 				markdown.SanitizeItemForTable(input.Description, settings),
-				markdown.SanitizeItemForTable(getInputType(&input), settings),
-				markdown.SanitizeItemForTable(getInputValue(&input), settings),
+				markdown.SanitizeItemForTable(getInputType(input), settings),
+				markdown.SanitizeItemForTable(getInputValue(input), settings),
 			),
 		)
 
 		if settings.ShowRequired {
-			buffer.WriteString(fmt.Sprintf(" %v |\n", printIsInputRequired(&input)))
+			buffer.WriteString(fmt.Sprintf(" %v |\n", printIsInputRequired(input)))
 		} else {
 			buffer.WriteString("\n")
 		}
@@ -100,7 +86,7 @@ func printInputs(buffer *bytes.Buffer, inputs []doc.Input, settings *print.Setti
 
 }
 
-func printOutputs(buffer *bytes.Buffer, outputs []doc.Output, settings *print.Settings) {
+func printOutputs(buffer *bytes.Buffer, outputs []*tfconf.Output, settings *print.Settings) {
 	buffer.WriteString(fmt.Sprintf("\n%s Outputs\n\n", markdown.GenerateIndentation(0, settings)))
 
 	if len(outputs) == 0 {
diff --git a/internal/pkg/print/pretty/pretty.go b/internal/pkg/print/pretty/pretty.go
index 1a394fa..85ee0a3 100644
--- a/internal/pkg/print/pretty/pretty.go
+++ b/internal/pkg/print/pretty/pretty.go
@@ -5,33 +5,23 @@ import (
 	"fmt"
 	"strings"
 
-	"github.com/segmentio/terraform-docs/internal/pkg/doc"
 	"github.com/segmentio/terraform-docs/internal/pkg/print"
+	"github.com/segmentio/terraform-docs/internal/pkg/tfconf"
 )
 
 // Print prints a pretty document.
-func Print(document *doc.Doc, settings *print.Settings) (string, error) {
+func Print(module *tfconf.Module, settings *print.Settings) (string, error) {
 	var buffer bytes.Buffer
 
-	if settings.SortByName {
-		if settings.SortInputsByRequired {
-			doc.SortInputsByRequired(document.Inputs)
-		} else {
-			doc.SortInputsByName(document.Inputs)
-		}
-	}
-
-	if settings.SortByName {
-		doc.SortOutputsByName(document.Outputs)
-	}
+	module.Sort(settings)
 
-	printInputs(&buffer, document.Inputs, settings)
-	printOutputs(&buffer, document.Outputs, settings)
+	printInputs(&buffer, module.Inputs, settings)
+	printOutputs(&buffer, module.Outputs, settings)
 
 	return buffer.String(), nil
 }
 
-func getInputDefaultValue(input *doc.Input, settings *print.Settings) string {
+func getInputDefaultValue(input *tfconf.Input, settings *print.Settings) string {
 	var result = "required"
 
 	if input.HasDefault() {
@@ -51,7 +41,7 @@ func getDescription(description string) string {
 	return result
 }
 
-func printInputs(buffer *bytes.Buffer, inputs []doc.Input, settings *print.Settings) {
+func printInputs(buffer *bytes.Buffer, inputs []*tfconf.Input, settings *print.Settings) {
 	buffer.WriteString("\n\n")
 
 	for _, input := range inputs {
@@ -60,7 +50,7 @@ func printInputs(buffer *bytes.Buffer, inputs []doc.Input, settings *print.Setti
 			fmt.Sprintf(
 				format,
 				input.Name,
-				getInputDefaultValue(&input, settings),
+				getInputDefaultValue(input, settings),
 				getDescription(input.Description),
 			),
 		)
@@ -69,7 +59,7 @@ func printInputs(buffer *bytes.Buffer, inputs []doc.Input, settings *print.Setti
 	buffer.WriteString("\n")
 }
 
-func printOutputs(buffer *bytes.Buffer, outputs []doc.Output, settings *print.Settings) {
+func printOutputs(buffer *bytes.Buffer, outputs []*tfconf.Output, settings *print.Settings) {
 	buffer.WriteString("\n")
 
 	for _, output := range outputs {
diff --git a/internal/pkg/doc/input.go b/internal/pkg/tfconf/input.go
similarity index 72%
rename from internal/pkg/doc/input.go
rename to internal/pkg/tfconf/input.go
index 0c7d746..2d2ff32 100644
--- a/internal/pkg/doc/input.go
+++ b/internal/pkg/tfconf/input.go
@@ -1,8 +1,4 @@
-package doc
-
-import (
-	"sort"
-)
+package tfconf
 
 // Input represents a Terraform input.
 type Input struct {
@@ -17,7 +13,7 @@ func (i *Input) HasDefault() bool {
 	return len(i.Default) > 0
 }
 
-type inputsSortedByName []Input
+type inputsSortedByName []*Input
 
 func (a inputsSortedByName) Len() int {
 	return len(a)
@@ -31,12 +27,7 @@ func (a inputsSortedByName) Less(i, j int) bool {
 	return a[i].Name < a[j].Name
 }
 
-// SortInputsByName sorts a list of inputs by name.
-func SortInputsByName(inputs []Input) {
-	sort.Sort(inputsSortedByName(inputs))
-}
-
-type inputsSortedByRequired []Input
+type inputsSortedByRequired []*Input
 
 func (a inputsSortedByRequired) Len() int {
 	return len(a)
@@ -59,8 +50,3 @@ func (a inputsSortedByRequired) Less(i, j int) bool {
 		return a[i].Name < a[j].Name
 	}
 }
-
-// SortInputsByRequired sorts a list of inputs by whether they are required
-func SortInputsByRequired(inputs []Input) {
-	sort.Sort(inputsSortedByRequired(inputs))
-}
diff --git a/internal/pkg/tfconf/module.go b/internal/pkg/tfconf/module.go
new file mode 100644
index 0000000..aefac7a
--- /dev/null
+++ b/internal/pkg/tfconf/module.go
@@ -0,0 +1,140 @@
+package tfconf
+
+import (
+	"encoding/json"
+	"fmt"
+	"log"
+	"sort"
+
+	"github.com/hashicorp/terraform-config-inspect/tfconfig"
+	"github.com/segmentio/terraform-docs/internal/pkg/print"
+)
+
+// Module represents a Terraform mod.
+type Module struct {
+	Inputs         []*Input  `json:"inputs"`
+	Outputs        []*Output `json:"outputs"`
+	RequiredInputs []*Input  `json:"-"`
+	OptionalInputs []*Input  `json:"-"`
+}
+
+// HasInputs indicates if the document has inputs.
+func (m *Module) HasInputs() bool {
+	return len(m.Inputs) > 0
+}
+
+// HasOutputs indicates if the document has outputs.
+func (m *Module) HasOutputs() bool {
+	return len(m.Outputs) > 0
+}
+
+func (m *Module) sortInputsByName() {
+	sort.Sort(inputsSortedByName(m.Inputs))
+	sort.Sort(inputsSortedByName(m.RequiredInputs))
+	sort.Sort(inputsSortedByName(m.OptionalInputs))
+}
+
+func (m *Module) sortInputsByRequired() {
+	sort.Sort(inputsSortedByRequired(m.Inputs))
+	sort.Sort(inputsSortedByRequired(m.RequiredInputs))
+	sort.Sort(inputsSortedByRequired(m.OptionalInputs))
+}
+
+func (m *Module) sortOutputsByName() {
+	sort.Sort(outputsSortedByName(m.Outputs))
+}
+
+// Sort sorts list of inputs and outputs based on provided flags (name, required, etc)
+func (m *Module) Sort(settings *print.Settings) {
+	if settings.SortByName {
+		if settings.SortInputsByRequired {
+			m.sortInputsByRequired()
+		} else {
+			m.sortInputsByName()
+		}
+	}
+
+	if settings.SortByName {
+		m.sortOutputsByName()
+	}
+
+}
+
+// CreateModule returns new instance of Module with all the inputs and
+// outputs dircoverd from provided 'path' containing Terraform config
+func CreateModule(path string) (*Module, error) {
+	mod := loadModule(path)
+
+	var inputs = make([]*Input, 0, len(mod.Variables))
+	var requiredInputs = make([]*Input, 0, len(mod.Variables))
+	var optionalInputs = make([]*Input, 0, len(mod.Variables))
+
+	for _, input := range mod.Variables {
+		inputType := input.Type
+		if input.Type == "" {
+			inputType = "any"
+		}
+
+		var defaultValue string
+		if input.Default != nil {
+			marshaled, err := json.MarshalIndent(input.Default, "", "  ")
+			if err != nil {
+				return nil, err
+			}
+			defaultValue = string(marshaled)
+
+			if inputType == "any" {
+				switch xType := fmt.Sprintf("%T", input.Default); xType {
+				case "string":
+					inputType = "string"
+				case "int", "int8", "int16", "int32", "int64", "float32", "float64":
+					inputType = "number"
+				case "bool":
+					inputType = "bool"
+				case "[]interface {}":
+					inputType = "list"
+				case "map[string]interface {}":
+					inputType = "map"
+				}
+			}
+		}
+
+		i := &Input{
+			Name:        input.Name,
+			Type:        inputType,
+			Description: input.Description,
+			Default:     defaultValue,
+		}
+
+		inputs = append(inputs, i)
+		if i.HasDefault() {
+			optionalInputs = append(optionalInputs, i)
+		} else {
+			requiredInputs = append(requiredInputs, i)
+		}
+	}
+
+	var outputs = make([]*Output, 0, len(mod.Outputs))
+	for _, output := range mod.Outputs {
+		outputs = append(outputs, &Output{
+			Name:        output.Name,
+			Description: output.Description,
+		})
+	}
+
+	module := &Module{
+		Inputs:         inputs,
+		Outputs:        outputs,
+		RequiredInputs: requiredInputs,
+		OptionalInputs: optionalInputs,
+	}
+	return module, nil
+}
+
+func loadModule(path string) *tfconfig.Module {
+	module, diag := tfconfig.LoadModule(path)
+	if diag != nil && diag.HasErrors() {
+		log.Fatal(diag)
+	}
+	return module
+}
diff --git a/internal/pkg/doc/output.go b/internal/pkg/tfconf/output.go
similarity index 64%
rename from internal/pkg/doc/output.go
rename to internal/pkg/tfconf/output.go
index 827de17..6b4fd42 100644
--- a/internal/pkg/doc/output.go
+++ b/internal/pkg/tfconf/output.go
@@ -1,8 +1,4 @@
-package doc
-
-import (
-	"sort"
-)
+package tfconf
 
 // Output represents a Terraform output.
 type Output struct {
@@ -10,7 +6,7 @@ type Output struct {
 	Description string `json:"description,omitempty"`
 }
 
-type outputsSortedByName []Output
+type outputsSortedByName []*Output
 
 func (a outputsSortedByName) Len() int {
 	return len(a)
@@ -23,8 +19,3 @@ func (a outputsSortedByName) Swap(i, j int) {
 func (a outputsSortedByName) Less(i, j int) bool {
 	return a[i].Name < a[j].Name
 }
-
-// SortOutputsByName sorts a list of outputs by name.
-func SortOutputsByName(outputs []Output) {
-	sort.Sort(outputsSortedByName(outputs))
-}


Test patch:
diff --git a/internal/pkg/print/json/json_test.go b/internal/pkg/print/json/json_test.go
index bf55ebf..f0f3b0c 100644
--- a/internal/pkg/print/json/json_test.go
+++ b/internal/pkg/print/json/json_test.go
@@ -3,10 +3,9 @@ package json_test
 import (
 	"testing"
 
-	"github.com/hashicorp/terraform-config-inspect/tfconfig"
-	"github.com/segmentio/terraform-docs/internal/pkg/doc"
 	"github.com/segmentio/terraform-docs/internal/pkg/print"
 	"github.com/segmentio/terraform-docs/internal/pkg/print/json"
+	"github.com/segmentio/terraform-docs/internal/pkg/tfconf"
 	"github.com/stretchr/testify/assert"
 )
 
@@ -16,17 +15,12 @@ func TestPrint(t *testing.T) {
 		SortByName: true,
 	}
 
-	module, diag := tfconfig.LoadModule("../../../../examples")
-	if diag != nil && diag.HasErrors() {
-		t.Fatal(diag)
-	}
-
-	document, err := doc.Create(module)
+	module, err := tfconf.CreateModule("../../../../examples")
 	if err != nil {
 		t.Fatal(err)
 	}
 
-	actual, err := json.Print(document, settings)
+	actual, err := json.Print(module, settings)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -44,17 +38,12 @@ func TestPrintWithSortByName(t *testing.T) {
 		SortByName: true,
 	}
 
-	module, diag := tfconfig.LoadModule("../../../../examples")
-	if diag != nil && diag.HasErrors() {
-		t.Fatal(diag)
-	}
-
-	document, err := doc.Create(module)
+	module, err := tfconf.CreateModule("../../../../examples")
 	if err != nil {
 		t.Fatal(err)
 	}
 
-	actual, err := json.Print(document, settings)
+	actual, err := json.Print(module, settings)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -73,17 +62,12 @@ func TestPrintWithSortInputsByRequired(t *testing.T) {
 		SortInputsByRequired: true,
 	}
 
-	module, diag := tfconfig.LoadModule("../../../../examples")
-	if diag != nil && diag.HasErrors() {
-		t.Fatal(diag)
-	}
-
-	document, err := doc.Create(module)
+	module, err := tfconf.CreateModule("../../../../examples")
 	if err != nil {
 		t.Fatal(err)
 	}
 
-	actual, err := json.Print(document, settings)
+	actual, err := json.Print(module, settings)
 	if err != nil {
 		t.Fatal(err)
 	}
diff --git a/internal/pkg/print/markdown/document/document_test.go b/internal/pkg/print/markdown/document/document_test.go
index f31bd08..92162fe 100644
--- a/internal/pkg/print/markdown/document/document_test.go
+++ b/internal/pkg/print/markdown/document/document_test.go
@@ -3,10 +3,9 @@ package document_test
 import (
 	"testing"
 
-	"github.com/hashicorp/terraform-config-inspect/tfconfig"
-	"github.com/segmentio/terraform-docs/internal/pkg/doc"
 	"github.com/segmentio/terraform-docs/internal/pkg/print"
 	"github.com/segmentio/terraform-docs/internal/pkg/print/markdown/document"
+	"github.com/segmentio/terraform-docs/internal/pkg/tfconf"
 	"github.com/stretchr/testify/assert"
 )
 
@@ -16,17 +15,12 @@ func TestPrint(t *testing.T) {
 		SortByName: true,
 	}
 
-	module, diag := tfconfig.LoadModule("../../../../../examples")
-	if diag != nil && diag.HasErrors() {
-		t.Fatal(diag)
-	}
-
-	doc, err := doc.Create(module)
+	module, err := tfconf.CreateModule("../../../../../examples")
 	if err != nil {
 		t.Fatal(err)
 	}
 
-	actual, err := document.Print(doc, settings)
+	actual, err := document.Print(module, settings)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -46,17 +40,12 @@ func TestPrintWithRequired(t *testing.T) {
 		ShowRequired: true,
 	}
 
-	module, diag := tfconfig.LoadModule("../../../../../examples")
-	if diag != nil && diag.HasErrors() {
-		t.Fatal(diag)
-	}
-
-	doc, err := doc.Create(module)
+	module, err := tfconf.CreateModule("../../../../../examples")
 	if err != nil {
 		t.Fatal(err)
 	}
 
-	actual, err := document.Print(doc, settings)
+	actual, err := document.Print(module, settings)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -74,17 +63,12 @@ func TestPrintWithSortByName(t *testing.T) {
 		SortByName: true,
 	}
 
-	module, diag := tfconfig.LoadModule("../../../../../examples")
-	if diag != nil && diag.HasErrors() {
-		t.Fatal(diag)
-	}
-
-	doc, err := doc.Create(module)
+	module, err := tfconf.CreateModule("../../../../../examples")
 	if err != nil {
 		t.Fatal(err)
 	}
 
-	actual, err := document.Print(doc, settings)
+	actual, err := document.Print(module, settings)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -103,17 +87,12 @@ func TestPrintWithSortInputsByRequired(t *testing.T) {
 		SortInputsByRequired: true,
 	}
 
-	module, diag := tfconfig.LoadModule("../../../../../examples")
-	if diag != nil && diag.HasErrors() {
-		t.Fatal(diag)
-	}
-
-	doc, err := doc.Create(module)
+	module, err := tfconf.CreateModule("../../../../../examples")
 	if err != nil {
 		t.Fatal(err)
 	}
 
-	actual, err := document.Print(doc, settings)
+	actual, err := document.Print(module, settings)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -133,17 +112,12 @@ func TestPrintWithEscapeName(t *testing.T) {
 		EscapeMarkdown: true,
 	}
 
-	module, diag := tfconfig.LoadModule("../../../../../examples")
-	if diag != nil && diag.HasErrors() {
-		t.Fatal(diag)
-	}
-
-	doc, err := doc.Create(module)
+	module, err := tfconf.CreateModule("../../../../../examples")
 	if err != nil {
 		t.Fatal(err)
 	}
 
-	actual, err := document.Print(doc, settings)
+	actual, err := document.Print(module, settings)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -163,17 +137,12 @@ func TestPrintWithIndentationBellowAllowed(t *testing.T) {
 		MarkdownIndent: 0,
 	}
 
-	module, diag := tfconfig.LoadModule("../../../../../examples")
-	if diag != nil && diag.HasErrors() {
-		t.Fatal(diag)
-	}
-
-	doc, err := doc.Create(module)
+	module, err := tfconf.CreateModule("../../../../../examples")
 	if err != nil {
 		t.Fatal(err)
 	}
 
-	actual, err := document.Print(doc, settings)
+	actual, err := document.Print(module, settings)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -193,17 +162,12 @@ func TestPrintWithIndentationAboveAllowed(t *testing.T) {
 		MarkdownIndent: 10,
 	}
 
-	module, diag := tfconfig.LoadModule("../../../../../examples")
-	if diag != nil && diag.HasErrors() {
-		t.Fatal(diag)
-	}
-
-	doc, err := doc.Create(module)
+	module, err := tfconf.CreateModule("../../../../../examples")
 	if err != nil {
 		t.Fatal(err)
 	}
 
-	actual, err := document.Print(doc, settings)
+	actual, err := document.Print(module, settings)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -223,17 +187,12 @@ func TestPrintWithIndentationOfFour(t *testing.T) {
 		MarkdownIndent: 4,
 	}
 
-	module, diag := tfconfig.LoadModule("../../../../../examples")
-	if diag != nil && diag.HasErrors() {
-		t.Fatal(diag)
-	}
-
-	doc, err := doc.Create(module)
+	module, err := tfconf.CreateModule("../../../../../examples")
 	if err != nil {
 		t.Fatal(err)
 	}
 
-	actual, err := document.Print(doc, settings)
+	actual, err := document.Print(module, settings)
 	if err != nil {
 		t.Fatal(err)
 	}
diff --git a/internal/pkg/print/markdown/table/table_test.go b/internal/pkg/print/markdown/table/table_test.go
index 9abdf51..c65265a 100644
--- a/internal/pkg/print/markdown/table/table_test.go
+++ b/internal/pkg/print/markdown/table/table_test.go
@@ -3,10 +3,9 @@ package table_test
 import (
 	"testing"
 
-	"github.com/hashicorp/terraform-config-inspect/tfconfig"
-	"github.com/segmentio/terraform-docs/internal/pkg/doc"
 	"github.com/segmentio/terraform-docs/internal/pkg/print"
 	"github.com/segmentio/terraform-docs/internal/pkg/print/markdown/table"
+	"github.com/segmentio/terraform-docs/internal/pkg/tfconf"
 	"github.com/stretchr/testify/assert"
 )
 
@@ -16,17 +15,12 @@ func TestPrint(t *testing.T) {
 		SortByName: true,
 	}
 
-	module, diag := tfconfig.LoadModule("../../../../../examples")
-	if diag != nil && diag.HasErrors() {
-		t.Fatal(diag)
-	}
-
-	document, err := doc.Create(module)
+	module, err := tfconf.CreateModule("../../../../../examples")
 	if err != nil {
 		t.Fatal(err)
 	}
 
-	actual, err := table.Print(document, settings)
+	actual, err := table.Print(module, settings)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -46,17 +40,12 @@ func TestPrintWithRequired(t *testing.T) {
 		ShowRequired: true,
 	}
 
-	module, diag := tfconfig.LoadModule("../../../../../examples")
-	if diag != nil && diag.HasErrors() {
-		t.Fatal(diag)
-	}
-
-	document, err := doc.Create(module)
+	module, err := tfconf.CreateModule("../../../../../examples")
 	if err != nil {
 		t.Fatal(err)
 	}
 
-	actual, err := table.Print(document, settings)
+	actual, err := table.Print(module, settings)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -74,17 +63,12 @@ func TestPrintWithSortByName(t *testing.T) {
 		SortByName: true,
 	}
 
-	module, diag := tfconfig.LoadModule("../../../../../examples")
-	if diag != nil && diag.HasErrors() {
-		t.Fatal(diag)
-	}
-
-	document, err := doc.Create(module)
+	module, err := tfconf.CreateModule("../../../../../examples")
 	if err != nil {
 		t.Fatal(err)
 	}
 
-	actual, err := table.Print(document, settings)
+	actual, err := table.Print(module, settings)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -103,17 +87,12 @@ func TestPrintWithSortInputsByRequired(t *testing.T) {
 		SortInputsByRequired: true,
 	}
 
-	module, diag := tfconfig.LoadModule("../../../../../examples")
-	if diag != nil && diag.HasErrors() {
-		t.Fatal(diag)
-	}
-
-	document, err := doc.Create(module)
+	module, err := tfconf.CreateModule("../../../../../examples")
 	if err != nil {
 		t.Fatal(err)
 	}
 
-	actual, err := table.Print(document, settings)
+	actual, err := table.Print(module, settings)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -133,17 +112,12 @@ func TestPrintWithEscapeName(t *testing.T) {
 		EscapeMarkdown: true,
 	}
 
-	module, diag := tfconfig.LoadModule("../../../../../examples")
-	if diag != nil && diag.HasErrors() {
-		t.Fatal(diag)
-	}
-
-	document, err := doc.Create(module)
+	module, err := tfconf.CreateModule("../../../../../examples")
 	if err != nil {
 		t.Fatal(err)
 	}
 
-	actual, err := table.Print(document, settings)
+	actual, err := table.Print(module, settings)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -163,17 +137,12 @@ func TestPrintWithIndentationBellowAllowed(t *testing.T) {
 		MarkdownIndent: 0,
 	}
 
-	module, diag := tfconfig.LoadModule("../../../../../examples")
-	if diag != nil && diag.HasErrors() {
-		t.Fatal(diag)
-	}
-
-	document, err := doc.Create(module)
+	module, err := tfconf.CreateModule("../../../../../examples")
 	if err != nil {
 		t.Fatal(err)
 	}
 
-	actual, err := table.Print(document, settings)
+	actual, err := table.Print(module, settings)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -193,17 +162,12 @@ func TestPrintWithIndentationAboveAllowed(t *testing.T) {
 		MarkdownIndent: 10,
 	}
 
-	module, diag := tfconfig.LoadModule("../../../../../examples")
-	if diag != nil && diag.HasErrors() {
-		t.Fatal(diag)
-	}
-
-	document, err := doc.Create(module)
+	module, err := tfconf.CreateModule("../../../../../examples")
 	if err != nil {
 		t.Fatal(err)
 	}
 
-	actual, err := table.Print(document, settings)
+	actual, err := table.Print(module, settings)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -223,17 +187,12 @@ func TestPrintWithIndentationOfFour(t *testing.T) {
 		MarkdownIndent: 4,
 	}
 
-	module, diag := tfconfig.LoadModule("../../../../../examples")
-	if diag != nil && diag.HasErrors() {
-		t.Fatal(diag)
-	}
-
-	document, err := doc.Create(module)
+	module, err := tfconf.CreateModule("../../../../../examples")
 	if err != nil {
 		t.Fatal(err)
 	}
 
-	actual, err := table.Print(document, settings)
+	actual, err := table.Print(module, settings)
 	if err != nil {
 		t.Fatal(err)
 	}
diff --git a/internal/pkg/print/pretty/pretty_test.go b/internal/pkg/print/pretty/pretty_test.go
index 7724fb2..17d7ab6 100644
--- a/internal/pkg/print/pretty/pretty_test.go
+++ b/internal/pkg/print/pretty/pretty_test.go
@@ -3,10 +3,9 @@ package pretty_test
 import (
 	"testing"
 
-	"github.com/hashicorp/terraform-config-inspect/tfconfig"
-	"github.com/segmentio/terraform-docs/internal/pkg/doc"
 	"github.com/segmentio/terraform-docs/internal/pkg/print"
 	"github.com/segmentio/terraform-docs/internal/pkg/print/pretty"
+	"github.com/segmentio/terraform-docs/internal/pkg/tfconf"
 	"github.com/stretchr/testify/assert"
 )
 
@@ -16,17 +15,12 @@ func TestPretty(t *testing.T) {
 		SortByName: true,
 	}
 
-	module, diag := tfconfig.LoadModule("../../../../examples")
-	if diag != nil && diag.HasErrors() {
-		t.Fatal(diag)
-	}
-
-	document, err := doc.Create(module)
+	module, err := tfconf.CreateModule("../../../../examples")
 	if err != nil {
 		t.Fatal(err)
 	}
 
-	actual, err := pretty.Print(document, settings)
+	actual, err := pretty.Print(module, settings)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -44,17 +38,12 @@ func TestPrettyWithSortByName(t *testing.T) {
 		SortByName: true,
 	}
 
-	module, diag := tfconfig.LoadModule("../../../../examples")
-	if diag != nil && diag.HasErrors() {
-		t.Fatal(diag)
-	}
-
-	document, err := doc.Create(module)
+	module, err := tfconf.CreateModule("../../../../examples")
 	if err != nil {
 		t.Fatal(err)
 	}
 
-	actual, err := pretty.Print(document, settings)
+	actual, err := pretty.Print(module, settings)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -73,17 +62,12 @@ func TestPrettyWithSortInputsByRequired(t *testing.T) {
 		SortInputsByRequired: true,
 	}
 
-	module, diag := tfconfig.LoadModule("../../../../examples")
-	if diag != nil && diag.HasErrors() {
-		t.Fatal(diag)
-	}
-
-	document, err := doc.Create(module)
+	module, err := tfconf.CreateModule("../../../../examples")
 	if err != nil {
 		t.Fatal(err)
 	}
 
-	actual, err := pretty.Print(document, settings)
+	actual, err := pretty.Print(module, settings)
 	if err != nil {
 		t.Fatal(err)
 	}