Skip to content

Commit

Permalink
Improve invalid message more readable in 'valid' command
Browse files Browse the repository at this point in the history
  • Loading branch information
namhyun-gu committed May 2, 2021
1 parent e58590b commit 8e866cd
Show file tree
Hide file tree
Showing 2 changed files with 145 additions and 99 deletions.
59 changes: 30 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,12 +171,13 @@ Download [latest release](https://github.com/namhyun-gu/brick/releases)
- Examples
```bash
$ ./brick valid test.yaml
invalid: require 'source' field
invalid dependency (index: 0) in group (index: 0): require 'content' field
invalid group (index: 1): require 'java' or 'kotlin' field
invalid group (index: 2): require 'document' field
invalid group (index: 4): require 'name' field
Error: 5 issues found
invalid: require 'source' field in (root)
invalid: require 'name' field in (root)
invalid: require 'content' field in dagger-hilt (java) (line: 6, col: 9)
invalid: require 'java' or 'kotlin' field in dagger2 (line: 16, col: 5)
invalid: require 'document' field in koin (line: 19, col: 5)
invalid: require 'name' field in (line: 27, col: 5)
Error: 6 issues found
```

### `bucket`
Expand All @@ -187,37 +188,37 @@ Download [latest release](https://github.com/namhyun-gu/brick/releases)

> Add new bucket
- Argument
`{owner}:{repo}@{branch}`
- Argument
`{owner}:{repo}@{branch}`

e.g namhyun-gu:brick@main
- Options
- `-p`, `--path`: Bucket default path, defaults `""`
- Example
```bash
$ ./brick bucket add namhyun-gu:brick@main --path data/
Added namhyun-gu:brick to bucket.
```
e.g namhyun-gu:brick@main
- Options
- `-p`, `--path`: Bucket default path, defaults `""`
- Example
```bash
$ ./brick bucket add namhyun-gu:brick@main --path data/
Added namhyun-gu:brick to bucket.
```

- `remove`

> Remove bucket
- Argument
`{owner}:{repo}`
- Example
```bash
$ ./brick bucket remove namhyun-gu:brick
Removed namhyun-gu:brick to bucket.
```
- Argument
`{owner}:{repo}`
- Example
```bash
$ ./brick bucket remove namhyun-gu:brick
Removed namhyun-gu:brick to bucket.
```

- `list`

> Print bucket list
- Example
```bash
$ ./brick bucket list
namhyun-gu:brick@main data/
Found 1 buckets.
```
- Example
```bash
$ ./brick bucket list
namhyun-gu:brick@main data/
Found 1 buckets.
```
185 changes: 115 additions & 70 deletions pkg/cmd/valid/valid.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,34 @@ import (
"path/filepath"
)

type Lang string

const (
Java Lang = "java"
Kotlin = "kotlin"
)

type Locator struct {
Node *yaml.Node
Group string
}

func (loc *Locator) string() string {
if loc.Group == "" {
if loc.Node != nil {
return fmt.Sprintf("(line: %d, col: %d)", loc.Node.Line, loc.Node.Column)
} else {
return "(root)"
}
} else {
if loc.Node != nil {
return fmt.Sprintf("%s (line: %d, col: %d)", loc.Group, loc.Node.Line, loc.Node.Column)
} else {
return fmt.Sprintf("'%s'", loc.Group)
}
}
}

var IssueCount = 0

func NewCmdValid() *cobra.Command {
Expand All @@ -31,7 +59,7 @@ func NewCmdValid() *cobra.Command {
return err
}

err = validFile(cmd, bytes)
err = validFile(bytes)
if err != nil {
return err
}
Expand All @@ -47,101 +75,118 @@ func NewCmdValid() *cobra.Command {
return cmd
}

func validFile(cmd *cobra.Command, content []byte) error {
m := make(map[string]interface{})
err := yaml.Unmarshal(content, &m)
type Root struct {
Name yaml.Node `yaml:"name"`
Source yaml.Node `yaml:"source"`
Content []yaml.Node `yaml:"content"`
}

type Group struct {
Name yaml.Node `yaml:"name"`
Document yaml.Node `yaml:"document"`
Java []yaml.Node `yaml:"java"`
Kotlin []yaml.Node `yaml:"kotlin"`
}

type Dependency struct {
Type yaml.Node `yaml:"type"`
Content yaml.Node `yaml:"content"`
}

func validFile(content []byte) error {
var root Root
err := yaml.Unmarshal(content, &root)

if err != nil {
return err
}
return validRoot(root)
}

checkSectionInfo(cmd, m)
func validRoot(root Root) error {
if root.Source.Value == "" {
invalid("require 'source' field", Locator{})
}

if !isContain(m, "content") {
IssueCount++
cmd.Println("invalid: require 'content' field")
} else {
checkGroups(cmd, m["content"].([]interface{}))
if root.Name.Value == "" {
invalid("require 'name' field", Locator{})
}
return nil
}

func checkSectionInfo(cmd *cobra.Command, m map[string]interface{}) {
if !isContain(m, "name") {
IssueCount++
cmd.Println("invalid: require 'name' field")
if root.Content == nil {
invalid("require 'content' field", Locator{})
}

if !isContain(m, "source") {
IssueCount++
cmd.Println("invalid: require 'source' field")
err := validContents(root.Content)
if err != nil {
return err
}
return nil
}

func checkGroups(cmd *cobra.Command, contents []interface{}) {
for idx, v := range contents {
checkGroup(cmd, idx, v)
func validContents(contents []yaml.Node) error {
for _, content := range contents {
var group Group
err := content.Decode(&group)
if err != nil {
return err
}
err = validGroup(content, group)
if err != nil {
return err
}
}
return nil
}

func checkGroup(cmd *cobra.Command, idx int, v interface{}) {
m := v.(map[string]interface{})

if !isContain(m, "name") {
IssueCount++
cmd.Printf("invalid group (index: %d): require 'name' field\n", idx)
func validGroup(node yaml.Node, group Group) error {
groupName := group.Name.Value
if group.Name.Value == "" {
invalid("require 'name' field", Locator{Group: groupName, Node: &node})
}

if !isContain(m, "document") {
IssueCount++
cmd.Printf("invalid group (index: %d): require 'document' field\n", idx)
if group.Document.Value == "" {
invalid("require 'document' field", Locator{Group: groupName, Node: &node})
}
if group.Java == nil && group.Kotlin == nil {
invalid("require 'java' or 'kotlin' field", Locator{Group: groupName, Node: &node})
}
if group.Java != nil {
return validDependencies(Java, groupName, group.Java)
}
if group.Kotlin != nil {
return validDependencies(Kotlin, groupName, group.Kotlin)
}
return nil
}

containJava := isContain(m, "java")
containKotlin := isContain(m, "kotlin")

if !containJava && !containKotlin {
IssueCount++
cmd.Printf("invalid group (index: %d): require 'java' or 'kotlin' field\n", idx)
} else {
if isContain(m, "java") {
javaDeps := m["java"].([]interface{})
for depIdx, dep := range javaDeps {
checkDependency(cmd, idx, depIdx, dep)
func validDependencies(lang Lang, group string, depNodes []yaml.Node) error {
group = fmt.Sprintf("%s (%s)", group, lang)
for _, depNode := range depNodes {
if depNode.Kind == yaml.ScalarNode {
if depNode.Value == "" {
invalid("require content value", Locator{Group: group, Node: &depNode})
}
}
if isContain(m, "kotlin") {
kotlinDeps := m["kotlin"].([]interface{})
for depIdx, dep := range kotlinDeps {
checkDependency(cmd, idx, depIdx, dep)
} else {
var dep Dependency
err := depNode.Decode(&dep)
if err != nil {
return err
}
validDependency(group, depNode, dep)
}
}
return nil
}

func checkDependency(cmd *cobra.Command, groupIdx int, idx int, dep interface{}) {
switch dep.(type) {
case string:
// Ignore
case map[string]interface{}:
m := dep.(map[string]interface{})

if !isContain(m, "type") {
IssueCount++
cmd.Printf("invalid dependency (index: %d) in group (index: %d): require 'type' field\n", groupIdx, idx)
}

if !isContain(m, "content") {
IssueCount++
cmd.Printf("invalid dependency (index: %d) in group (index: %d): require 'content' field\n", groupIdx, idx)
}
default:
IssueCount++
cmd.Printf("invalid dependency (index: %s) in group (index: %d): Invalid format\n", groupIdx, idx)
func validDependency(group string, node yaml.Node, dep Dependency) {
if dep.Type.Value == "" {
invalid("require 'type' field", Locator{Group: group, Node: &node})
}
if dep.Content.Value == "" {
invalid("require 'content' field", Locator{Group: group, Node: &node})
}
}

func isContain(m map[string]interface{}, key string) bool {
_, contain := m[key]
return contain
func invalid(message string, locator Locator) {
IssueCount++
fmt.Printf("invalid: %s in %s\n", message, locator.string())
}

0 comments on commit 8e866cd

Please sign in to comment.