Skip to content

Commit

Permalink
feat: implement json, xml, and csv (#1)
Browse files Browse the repository at this point in the history
* feat: implemented json & xml

* chore: gh actions on push only for main

* chore: tweaks

* chore: go mod tidy

* chore: use go 1.23

* feat: implemented csv

* chore: fix linter
  • Loading branch information
costinmrr authored Nov 11, 2024
1 parent fd98223 commit cc9f4fd
Show file tree
Hide file tree
Showing 21 changed files with 526 additions and 0 deletions.
19 changes: 19 additions & 0 deletions .github/workflows/govulncheck.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: govulncheck

on:
push:
branches: [main]
pull_request:

jobs:
scan:
name: govulncheck
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: ./go.mod
- name: govulncheck
continue-on-error: false
run: make govulncheck
18 changes: 18 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: lint

on:
push:
branches: [main]
pull_request:

jobs:
golangci:
name: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: ./go.mod
- name: golangci-lint
uses: golangci/golangci-lint-action@v6
18 changes: 18 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: test

on:
push:
branches: [main]
pull_request:

jobs:
test:
name: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: ./go.mod
- name: test
run: make test
22 changes: 22 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
linters-settings:
revive:
rules:
- name: line-length-limit
arguments: [120]

issues:
exclude-rules:
- path: _test\.go
linters:
- revive
text: "line-length-limit:"

linters:
enable:
- thelper
- gofumpt
- tparallel
- unconvert
- unparam
- wastedassign
- revive
8 changes: 8 additions & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions .idea/gontenttype.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 20 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
export GO111MODULE=on

tidy:
go mod tidy

build:
go build

test:
go test -v ./...

fmt:
go fmt ./...

lint:
golangci-lint run ./...

govulncheck:
@go get golang.org/x/vuln/cmd/govulncheck
@go run golang.org/x/vuln/cmd/govulncheck ./...
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module github.com/costinmrr/gontenttype

go 1.23
Empty file added go.sum
Empty file.
26 changes: 26 additions & 0 deletions gontenttype.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package gontenttype

import (
"github.com/costinmrr/gontenttype/types/csv"
"github.com/costinmrr/gontenttype/types/json"
"github.com/costinmrr/gontenttype/types/xml"
)

func GetContentType(content string) ContentType {
err := json.IsJSON(content)
if err == nil {
return JSON
}

err = xml.IsXML(content)
if err == nil {
return XML
}

err = csv.IsCSV(content)
if err == nil {
return CSV
}

return Unsupported
}
10 changes: 10 additions & 0 deletions types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package gontenttype

type ContentType string

const (
Unsupported ContentType = ""
JSON ContentType = "application/json"
XML ContentType = "application/xml"
CSV ContentType = "text/csv"
)
20 changes: 20 additions & 0 deletions types/csv/csv.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package csv

import (
"encoding/csv"
"strings"
)

// IsCSV returns true if the content is a CSV.
func IsCSV(content string) error {
if content == "" {
return ErrEmptyContent
}
reader := csv.NewReader(strings.NewReader(content))
_, err := reader.ReadAll()
if err != nil {
return err
}

return nil
}
77 changes: 77 additions & 0 deletions types/csv/csv_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package csv

import "testing"

func TestIsCSV(t *testing.T) {
type args struct {
content string
}
tests := []struct {
name string
args args
wantErr bool
}{
{
name: "empty",
args: args{content: ""},
wantErr: true,
},
{
name: "simple string is csv",
args: args{content: "foo"},
wantErr: false,
},
{
name: "csv comma separated",
args: args{content: "foo,bar\nbaz,qux"},
wantErr: false,
},
{
name: "csv semicolon separated",
args: args{content: "foo;bar\nbaz;qux"},
wantErr: false,
},
{
name: "csv tab separated",
args: args{content: "foo\tbar\nbaz\tqux"},
wantErr: false,
},
{
name: "csv pipe separated",
args: args{content: "foo|bar\nbaz|qux"},
wantErr: false,
},
{
name: "csv with quotes",
args: args{content: "\"foo\",\"bar\"\n\"baz\",\"qux\""},
wantErr: false,
},
{
name: "csv with quotes and commas",
args: args{content: "\"foo,bar\",\"baz,qux\"\n\"foo,bar\",\"baz,qux\""},
wantErr: false,
},
{
name: "different number of columns per line",
args: args{content: "foo,bar\nbaz"},
wantErr: true,
},
{
name: "empty lines",
args: args{content: "foo,bar\n\nbaz,qux"},
wantErr: false,
},
{
name: "different separators per line",
args: args{content: "foo,bar\nbaz;qux"},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := IsCSV(tt.args.content); (err != nil) != tt.wantErr {
t.Errorf("IsCSV() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
5 changes: 5 additions & 0 deletions types/csv/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package csv

import "errors"

var ErrEmptyContent = errors.New("empty content")
13 changes: 13 additions & 0 deletions types/json/json.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package json

import "encoding/json"

// IsJSON returns true if the content is a JSON.
func IsJSON(content string) error {
err := json.Unmarshal([]byte(content), new(interface{}))
if err != nil {
return err
}

return nil
}
76 changes: 76 additions & 0 deletions types/json/json_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package json

import "testing"

func TestIsJSON(t *testing.T) {
type args struct {
content string
}
tests := []struct {
name string
args args
wantErr bool
}{
{
name: "empty",
args: args{content: ""},
wantErr: true,
},
{
name: "string not json",
args: args{content: "foo"},
wantErr: true,
},
{
name: "json",
args: args{content: `{"foo": "bar"}`},
wantErr: false,
},
{
name: "json with spaces",
args: args{content: ` { "foo": "bar" } `},
wantErr: false,
},
{
name: "json with newlines",
args: args{content: `{
"foo": "bar"
}`},
wantErr: false,
},
{
name: "json with tabs",
args: args{content: `{
"foo": "bar"
}`},
wantErr: false,
},
{
name: "json with other chars at the end",
args: args{content: `{"foo": "bar"}!`},
wantErr: true,
},
{
name: "json with other chars at the end 2",
args: args{content: `{"foo": "bar"}>>`},
wantErr: true,
},
{
name: "json with other chars at the end 3",
args: args{content: `{"foo": "bar"}}}`},
wantErr: true,
},
{
name: "json with other chars at the end 4",
args: args{content: `{"foo": "bar"}{"baz": "qux"}`},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := IsJSON(tt.args.content); (err != nil) != tt.wantErr {
t.Errorf("IsJSON() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
10 changes: 10 additions & 0 deletions types/xml/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package xml

import "errors"

var (
ErrEmptyContent = errors.New("empty content")
ErrSecondRootFound = errors.New("found a second root element")
ErrContentAfterRoot = errors.New("found content after root element was closed")
ErrRootNotFound = errors.New("root element not found")
)
Loading

0 comments on commit cc9f4fd

Please sign in to comment.