-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat:support remove tag, login by id and refactor db
- Loading branch information
Showing
12 changed files
with
354 additions
and
56 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,25 +1,41 @@ | ||
package cmd | ||
|
||
import ( | ||
"github.com/pkg/errors" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
func newTagCmd() *cobra.Command { | ||
var ( | ||
tags []string | ||
id int | ||
appendtTags []string | ||
deleteTags []string | ||
id int | ||
) | ||
cmd := &cobra.Command{ | ||
Use: "tag", | ||
Short: "tag an entry by id", | ||
Example: "ssx tag -i 1 -t tag1 [-t tag2]", | ||
Short: "add or delete tag for entry by id", | ||
Example: "ssx tag -i <ENTRY_ID> [-t TAG1 [-t TAG2 ...]] [-d TAG3 [-d TAG4 ...]]", | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
return ssxInst.AppendTagByID(id, tags...) | ||
if len(appendtTags) == 0 && len(deleteTags) == 0 { | ||
return errors.New("no tag is spicified") | ||
} | ||
if len(deleteTags) > 0 { | ||
if err := ssxInst.DeleteTagByID(id, deleteTags...); err != nil { | ||
return err | ||
} | ||
} | ||
if len(appendtTags) > 0 { | ||
if err := ssxInst.AppendTagByID(id, appendtTags...); err != nil { | ||
return err | ||
} | ||
} | ||
return nil | ||
}, | ||
} | ||
|
||
cmd.Flags().StringSliceVarP(&tags, "tag", "t", nil, "tag name") | ||
cmd.Flags().IntVarP(&id, "id", "i", 0, "entry id") | ||
cmd.MarkFlagsRequiredTogether("id", "tag") | ||
cmd.Flags().StringSliceVarP(&appendtTags, "tag", "t", nil, "tag name to add") | ||
cmd.Flags().StringSliceVarP(&deleteTags, "delete", "d", nil, "tag name to delete") | ||
_ = cmd.MarkFlagRequired("id") | ||
return cmd | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
package slice | ||
|
||
// Distinct returns the unique vals of a slice | ||
func Distinct[T comparable](arrs []T) []T { | ||
m := make(map[T]int) | ||
order := 0 | ||
for idx := range arrs { | ||
if _, exist := m[arrs[idx]]; !exist { | ||
m[arrs[idx]] = order | ||
order++ | ||
} | ||
} | ||
res := make([]T, len(m)) | ||
for k, v := range m { | ||
res[v] = k | ||
} | ||
return res | ||
} | ||
|
||
// Union returns a slice that contains the unique values of all the input slices | ||
func Union[T comparable](arrs ...[]T) []T { | ||
m := make(map[T]int) | ||
order := 0 | ||
for idx1 := range arrs { | ||
for idx2 := range arrs[idx1] { | ||
if _, exist := m[arrs[idx1][idx2]]; !exist { | ||
m[arrs[idx1][idx2]] = order | ||
order++ | ||
} | ||
} | ||
} | ||
|
||
ret := make([]T, len(m)) | ||
for k, v := range m { | ||
ret[v] = k | ||
} | ||
|
||
return ret | ||
} | ||
|
||
// Intersect returns a slice of values that are present in all the input slices | ||
func Intersect[T comparable](arrs ...[]T) []T { | ||
m := make(map[T]int) | ||
var order []T | ||
for idx1 := range arrs { | ||
tmpArr := Distinct(arrs[idx1]) | ||
for idx2 := range tmpArr { | ||
count, ok := m[tmpArr[idx2]] | ||
if !ok { | ||
order = append(order, tmpArr[idx2]) | ||
m[tmpArr[idx2]] = 1 | ||
} else { | ||
m[tmpArr[idx2]] = count + 1 | ||
} | ||
} | ||
} | ||
|
||
var ( | ||
ret []T | ||
lenArrs = len(arrs) | ||
) | ||
for idx := range order { | ||
if m[order[idx]] == lenArrs { | ||
ret = append(ret, order[idx]) | ||
} | ||
} | ||
|
||
return ret | ||
} | ||
|
||
// Difference returns a slice of values that are only present in one of the input slices | ||
func Difference[T comparable](arrs ...[]T) []T { | ||
m := make(map[T]int) | ||
var order []T | ||
for idx1 := range arrs { | ||
tmpArr := Distinct(arrs[idx1]) | ||
for idx2 := range tmpArr { | ||
count, ok := m[tmpArr[idx2]] | ||
if !ok { | ||
order = append(order, tmpArr[idx2]) | ||
m[tmpArr[idx2]] = 1 | ||
} else { | ||
m[tmpArr[idx2]] = count + 1 | ||
} | ||
} | ||
} | ||
|
||
var ( | ||
ret []T | ||
) | ||
for idx := range order { | ||
if m[order[idx]] == 1 { | ||
ret = append(ret, order[idx]) | ||
} | ||
} | ||
|
||
return ret | ||
} | ||
|
||
// Delete deletes the element from the slice | ||
func Delete[T comparable](slice []T, elems ...T) []T { | ||
for _, val := range elems { | ||
for idx, elem := range slice { | ||
if val == elem { | ||
slice = append(slice[:idx], slice[idx+1:]...) | ||
} | ||
} | ||
} | ||
return slice | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
package slice | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
type typ struct{ value int } | ||
|
||
func (a typ) Compare(b typ) int { | ||
if a.value == b.value { | ||
return 0 | ||
} | ||
return 1 | ||
} | ||
|
||
func TestDistinct(t *testing.T) { | ||
t.Run("string", func(t *testing.T) { | ||
actual := Distinct([]string{"a", "a", "b", "b"}) | ||
assert.Equal(t, []string{"a", "b"}, actual) | ||
}) | ||
|
||
t.Run("integer", func(t *testing.T) { | ||
actual := Distinct([]int{1, 1, 3, 3, 2}) | ||
assert.Equal(t, []int{1, 3, 2}, actual) | ||
}) | ||
|
||
t.Run("object", func(t *testing.T) { | ||
actual := Distinct([]typ{{1}, {1}, {2}}) | ||
assert.Equal(t, []typ{{1}, {2}}, actual) | ||
}) | ||
} | ||
|
||
func TestUnion(t *testing.T) { | ||
t.Run("string", func(t *testing.T) { | ||
actual := Union([]string{"a", "a", "b"}, []string{"b", "c"}) | ||
assert.Equal(t, []string{"a", "b", "c"}, actual) | ||
}) | ||
|
||
t.Run("integer", func(t *testing.T) { | ||
actual := Union([]int{1, 1, 2, 3}, []int{2, 2, 3, 4}, []int{3, 4, 5}) | ||
assert.Equal(t, []int{1, 2, 3, 4, 5}, actual) | ||
}) | ||
|
||
t.Run("integer_order", func(t *testing.T) { | ||
actual := Union([]int{1, 2, 2, 3}, []int{10, 10, 3, 6}, []int{4, 2, 8}) | ||
assert.Equal(t, []int{1, 2, 3, 10, 6, 4, 8}, actual) | ||
}) | ||
|
||
t.Run("object", func(t *testing.T) { | ||
actual := Union([]typ{{1}, {1}, {2}}, []typ{{1}, {3}}) | ||
assert.Equal(t, []typ{{1}, {2}, {3}}, actual) | ||
}) | ||
} | ||
|
||
func TestIntersect(t *testing.T) { | ||
t.Run("string", func(t *testing.T) { | ||
actual := Intersect([]string{"a", "a", "b"}, []string{"b", "c"}) | ||
assert.Equal(t, []string{"b"}, actual) | ||
}) | ||
|
||
t.Run("integer", func(t *testing.T) { | ||
actual := Intersect([]int{1, 1, 3, 2}, []int{2, 10, 3, 4}, []int{2, 3, 4, 5}) | ||
assert.Equal(t, []int{3, 2}, actual) | ||
}) | ||
|
||
t.Run("object", func(t *testing.T) { | ||
actual := Intersect([]typ{{1}, {1}, {2}}, []typ{{1}, {3}}) | ||
assert.Equal(t, []typ{{1}}, actual) | ||
}) | ||
} | ||
|
||
func TestDifference(t *testing.T) { | ||
t.Run("string", func(t *testing.T) { | ||
actual := Difference([]string{"a", "a", "b"}, []string{"b", "c"}) | ||
assert.Equal(t, []string{"a", "c"}, actual) | ||
}) | ||
|
||
t.Run("integer", func(t *testing.T) { | ||
actual := Difference([]int{1, 1, 3, 2}, []int{2, 10, 3, 4}, []int{2, 3, 4, 5}) | ||
assert.Equal(t, []int{1, 10, 5}, actual) | ||
}) | ||
|
||
t.Run("object", func(t *testing.T) { | ||
actual := Difference([]typ{{1}, {1}, {2}}, []typ{{1}, {3}}) | ||
assert.Equal(t, []typ{{2}, {3}}, actual) | ||
}) | ||
} | ||
|
||
func TestDelete(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
slice []int | ||
remove []int | ||
expect []int | ||
}{ | ||
{"common", []int{1, 2, 3, 4, 5}, []int{2, 4}, []int{1, 3, 5}}, | ||
{"partial-non-exist", []int{1, 2, 3, 4, 5}, []int{2, 6}, []int{1, 3, 4, 5}}, | ||
{"all-non-exist", []int{1, 2, 3}, []int{6}, []int{1, 2, 3}}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
actual := Delete(tt.slice, tt.remove...) | ||
assert.Equal(t, tt.expect, actual) | ||
}) | ||
} | ||
} |
Oops, something went wrong.