mirror of
https://github.com/dnote/dnote
synced 2026-03-14 14:35:50 +01:00
Remove ls and cat commands
This commit is contained in:
parent
5c416e3a32
commit
04091f7ddf
13 changed files with 425 additions and 197 deletions
|
|
@ -131,7 +131,7 @@ func newRun(ctx context.DnoteCtx) infra.RunEFunc {
|
|||
return err
|
||||
}
|
||||
|
||||
output.NoteInfo(info)
|
||||
output.NoteInfo(os.Stdout, info)
|
||||
|
||||
if err := upgrade.Check(ctx); err != nil {
|
||||
log.Error(errors.Wrap(err, "automatically checking updates").Error())
|
||||
|
|
|
|||
|
|
@ -1,95 +0,0 @@
|
|||
/* Copyright 2025 Dnote Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cat
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/dnote/dnote/pkg/cli/context"
|
||||
"github.com/dnote/dnote/pkg/cli/database"
|
||||
"github.com/dnote/dnote/pkg/cli/infra"
|
||||
"github.com/dnote/dnote/pkg/cli/log"
|
||||
"github.com/dnote/dnote/pkg/cli/output"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var example = `
|
||||
* See the notes with index 2 from a book 'javascript'
|
||||
dnote cat javascript 2
|
||||
`
|
||||
|
||||
var deprecationWarning = `and "view" will replace it in the future version.
|
||||
|
||||
Run "dnote view --help" for more information.
|
||||
`
|
||||
|
||||
func preRun(cmd *cobra.Command, args []string) error {
|
||||
if len(args) != 2 {
|
||||
return errors.New("Incorrect number of arguments")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewCmd returns a new cat command
|
||||
func NewCmd(ctx context.DnoteCtx) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "cat <book name> <note index>",
|
||||
Aliases: []string{"c"},
|
||||
Short: "See a note",
|
||||
Example: example,
|
||||
RunE: NewRun(ctx, false),
|
||||
PreRunE: preRun,
|
||||
Deprecated: deprecationWarning,
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// NewRun returns a new run function
|
||||
func NewRun(ctx context.DnoteCtx, contentOnly bool) infra.RunEFunc {
|
||||
return func(cmd *cobra.Command, args []string) error {
|
||||
var noteRowIDArg string
|
||||
|
||||
if len(args) == 2 {
|
||||
log.Plain(log.ColorYellow.Sprintf("DEPRECATED: you no longer need to pass book name to the view command. e.g. `dnote view 123`.\n\n"))
|
||||
|
||||
noteRowIDArg = args[1]
|
||||
} else {
|
||||
noteRowIDArg = args[0]
|
||||
}
|
||||
|
||||
noteRowID, err := strconv.Atoi(noteRowIDArg)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "invalid rowid")
|
||||
}
|
||||
|
||||
db := ctx.DB
|
||||
info, err := database.GetNoteInfo(db, noteRowID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if contentOnly {
|
||||
output.NoteContent(info)
|
||||
} else {
|
||||
output.NoteInfo(info)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
@ -166,7 +166,7 @@ func runNote(ctx context.DnoteCtx, rowIDArg string) error {
|
|||
}
|
||||
|
||||
log.Success("edited the note\n")
|
||||
output.NoteInfo(noteInfo)
|
||||
output.NoteInfo(os.Stdout, noteInfo)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ package remove
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/dnote/dnote/pkg/cli/context"
|
||||
|
|
@ -129,7 +130,7 @@ func runNote(ctx context.DnoteCtx, rowIDArg string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
output.NoteInfo(noteInfo)
|
||||
output.NoteInfo(os.Stdout, noteInfo)
|
||||
|
||||
ok, err := maybeConfirm("remove this note?", false)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -13,76 +13,19 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package ls
|
||||
package view
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/dnote/dnote/pkg/cli/context"
|
||||
"github.com/dnote/dnote/pkg/cli/infra"
|
||||
"github.com/dnote/dnote/pkg/cli/log"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var example = `
|
||||
* List all books
|
||||
dnote ls
|
||||
|
||||
* List notes in a book
|
||||
dnote ls javascript
|
||||
`
|
||||
|
||||
var deprecationWarning = `and "view" will replace it in the future version.
|
||||
|
||||
Run "dnote view --help" for more information.
|
||||
`
|
||||
|
||||
func preRun(cmd *cobra.Command, args []string) error {
|
||||
if len(args) > 1 {
|
||||
return errors.New("Incorrect number of argument")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewCmd returns a new ls command
|
||||
func NewCmd(ctx context.DnoteCtx) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "ls <book name?>",
|
||||
Aliases: []string{"l", "notes"},
|
||||
Short: "List all notes",
|
||||
Example: example,
|
||||
RunE: NewRun(ctx, false),
|
||||
PreRunE: preRun,
|
||||
Deprecated: deprecationWarning,
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// NewRun returns a new run function for ls
|
||||
func NewRun(ctx context.DnoteCtx, nameOnly bool) infra.RunEFunc {
|
||||
return func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) == 0 {
|
||||
if err := printBooks(ctx, nameOnly); err != nil {
|
||||
return errors.Wrap(err, "viewing books")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
bookName := args[0]
|
||||
if err := printNotes(ctx, bookName); err != nil {
|
||||
return errors.Wrapf(err, "viewing book '%s'", bookName)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// bookInfo is an information about the book to be printed on screen
|
||||
type bookInfo struct {
|
||||
BookLabel string
|
||||
|
|
@ -97,15 +40,13 @@ type noteInfo struct {
|
|||
|
||||
// getNewlineIdx returns the index of newline character in a string
|
||||
func getNewlineIdx(str string) int {
|
||||
var ret int
|
||||
|
||||
ret = strings.Index(str, "\n")
|
||||
|
||||
if ret == -1 {
|
||||
ret = strings.Index(str, "\r\n")
|
||||
// Check for \r\n first
|
||||
if idx := strings.Index(str, "\r\n"); idx != -1 {
|
||||
return idx
|
||||
}
|
||||
|
||||
return ret
|
||||
// Then check for \n
|
||||
return strings.Index(str, "\n")
|
||||
}
|
||||
|
||||
// formatBody returns an excerpt of the given raw note content and a boolean
|
||||
|
|
@ -123,15 +64,15 @@ func formatBody(noteBody string) (string, bool) {
|
|||
return strings.Trim(trimmed, " "), false
|
||||
}
|
||||
|
||||
func printBookLine(info bookInfo, nameOnly bool) {
|
||||
func printBookLine(w io.Writer, info bookInfo, nameOnly bool) {
|
||||
if nameOnly {
|
||||
fmt.Println(info.BookLabel)
|
||||
fmt.Fprintln(w, info.BookLabel)
|
||||
} else {
|
||||
log.Printf("%s %s\n", info.BookLabel, log.ColorYellow.Sprintf("(%d)", info.NoteCount))
|
||||
fmt.Fprintf(w, "%s %s\n", info.BookLabel, log.ColorYellow.Sprintf("(%d)", info.NoteCount))
|
||||
}
|
||||
}
|
||||
|
||||
func printBooks(ctx context.DnoteCtx, nameOnly bool) error {
|
||||
func listBooks(ctx context.DnoteCtx, w io.Writer, nameOnly bool) error {
|
||||
db := ctx.DB
|
||||
|
||||
rows, err := db.Query(`SELECT books.label, count(notes.uuid) note_count
|
||||
|
|
@ -157,13 +98,13 @@ func printBooks(ctx context.DnoteCtx, nameOnly bool) error {
|
|||
}
|
||||
|
||||
for _, info := range infos {
|
||||
printBookLine(info, nameOnly)
|
||||
printBookLine(w, info, nameOnly)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func printNotes(ctx context.DnoteCtx, bookName string) error {
|
||||
func listNotes(ctx context.DnoteCtx, w io.Writer, bookName string) error {
|
||||
db := ctx.DB
|
||||
|
||||
var bookUUID string
|
||||
|
|
@ -191,7 +132,7 @@ func printNotes(ctx context.DnoteCtx, bookName string) error {
|
|||
infos = append(infos, info)
|
||||
}
|
||||
|
||||
log.Infof("on book %s\n", bookName)
|
||||
fmt.Fprintf(w, "on book %s\n", bookName)
|
||||
|
||||
for _, info := range infos {
|
||||
body, isExcerpt := formatBody(info.Body)
|
||||
|
|
@ -201,7 +142,7 @@ func printNotes(ctx context.DnoteCtx, bookName string) error {
|
|||
body = fmt.Sprintf("%s %s", body, log.ColorYellow.Sprintf("[---More---]"))
|
||||
}
|
||||
|
||||
log.Plainf("%s %s\n", rowid, body)
|
||||
fmt.Fprintf(w, "%s %s\n", rowid, body)
|
||||
}
|
||||
|
||||
return nil
|
||||
184
pkg/cli/cmd/view/book_test.go
Normal file
184
pkg/cli/cmd/view/book_test.go
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
/* Copyright 2025 Dnote Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package view
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/dnote/dnote/pkg/assert"
|
||||
"github.com/dnote/dnote/pkg/cli/context"
|
||||
"github.com/dnote/dnote/pkg/cli/database"
|
||||
)
|
||||
|
||||
func TestGetNewlineIdx(t *testing.T) {
|
||||
testCases := []struct {
|
||||
input string
|
||||
expected int
|
||||
}{
|
||||
{
|
||||
input: "hello\nworld",
|
||||
expected: 5,
|
||||
},
|
||||
{
|
||||
input: "hello\r\nworld",
|
||||
expected: 5,
|
||||
},
|
||||
{
|
||||
input: "no newline here",
|
||||
expected: -1,
|
||||
},
|
||||
{
|
||||
input: "",
|
||||
expected: -1,
|
||||
},
|
||||
{
|
||||
input: "\n",
|
||||
expected: 0,
|
||||
},
|
||||
{
|
||||
input: "\r\n",
|
||||
expected: 0,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(fmt.Sprintf("input: %q", tc.input), func(t *testing.T) {
|
||||
got := getNewlineIdx(tc.input)
|
||||
assert.Equal(t, got, tc.expected, "newline index mismatch")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatBody(t *testing.T) {
|
||||
testCases := []struct {
|
||||
input string
|
||||
expectedBody string
|
||||
expectedExcerpt bool
|
||||
}{
|
||||
{
|
||||
input: "single line",
|
||||
expectedBody: "single line",
|
||||
expectedExcerpt: false,
|
||||
},
|
||||
{
|
||||
input: "first line\nsecond line",
|
||||
expectedBody: "first line",
|
||||
expectedExcerpt: true,
|
||||
},
|
||||
{
|
||||
input: "first line\r\nsecond line",
|
||||
expectedBody: "first line",
|
||||
expectedExcerpt: true,
|
||||
},
|
||||
{
|
||||
input: " spaced line ",
|
||||
expectedBody: "spaced line",
|
||||
expectedExcerpt: false,
|
||||
},
|
||||
{
|
||||
input: " first line \nsecond line",
|
||||
expectedBody: "first line",
|
||||
expectedExcerpt: true,
|
||||
},
|
||||
{
|
||||
input: "",
|
||||
expectedBody: "",
|
||||
expectedExcerpt: false,
|
||||
},
|
||||
{
|
||||
input: "line with trailing newline\n",
|
||||
expectedBody: "line with trailing newline",
|
||||
expectedExcerpt: false,
|
||||
},
|
||||
{
|
||||
input: "line with trailing newlines\n\n",
|
||||
expectedBody: "line with trailing newlines",
|
||||
expectedExcerpt: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(fmt.Sprintf("input: %q", tc.input), func(t *testing.T) {
|
||||
gotBody, gotExcerpt := formatBody(tc.input)
|
||||
assert.Equal(t, gotBody, tc.expectedBody, "formatted body mismatch")
|
||||
assert.Equal(t, gotExcerpt, tc.expectedExcerpt, "excerpt flag mismatch")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestListNotes(t *testing.T) {
|
||||
// Setup
|
||||
db := database.InitTestMemoryDB(t)
|
||||
defer db.Close()
|
||||
|
||||
bookUUID := "js-book-uuid"
|
||||
database.MustExec(t, "inserting book", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", bookUUID, "javascript")
|
||||
database.MustExec(t, "inserting note 1", db, "INSERT INTO notes (uuid, book_uuid, body, added_on) VALUES (?, ?, ?, ?)", "note-1", bookUUID, "first note", 1515199943)
|
||||
database.MustExec(t, "inserting note 2", db, "INSERT INTO notes (uuid, book_uuid, body, added_on) VALUES (?, ?, ?, ?)", "note-2", bookUUID, "multiline note\nwith second line", 1515199945)
|
||||
|
||||
ctx := context.DnoteCtx{DB: db}
|
||||
var buf bytes.Buffer
|
||||
|
||||
// Execute
|
||||
err := listNotes(ctx, &buf, "javascript")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
got := buf.String()
|
||||
|
||||
// Verify output
|
||||
assert.Equal(t, strings.Contains(got, "on book javascript"), true, "should show book name")
|
||||
assert.Equal(t, strings.Contains(got, "first note"), true, "should contain first note")
|
||||
assert.Equal(t, strings.Contains(got, "multiline note"), true, "should show first line of multiline note")
|
||||
assert.Equal(t, strings.Contains(got, "[---More---]"), true, "should show more indicator for multiline note")
|
||||
assert.Equal(t, strings.Contains(got, "with second line"), false, "should not show second line of multiline note")
|
||||
}
|
||||
|
||||
func TestListBooks(t *testing.T) {
|
||||
// Setup
|
||||
db := database.InitTestMemoryDB(t)
|
||||
defer db.Close()
|
||||
|
||||
b1UUID := "js-book-uuid"
|
||||
b2UUID := "linux-book-uuid"
|
||||
|
||||
database.MustExec(t, "inserting book 1", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b1UUID, "javascript")
|
||||
database.MustExec(t, "inserting book 2", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b2UUID, "linux")
|
||||
|
||||
// Add notes to test count
|
||||
database.MustExec(t, "inserting note 1", db, "INSERT INTO notes (uuid, book_uuid, body, added_on) VALUES (?, ?, ?, ?)", "note-1", b1UUID, "note body 1", 1515199943)
|
||||
database.MustExec(t, "inserting note 2", db, "INSERT INTO notes (uuid, book_uuid, body, added_on) VALUES (?, ?, ?, ?)", "note-2", b1UUID, "note body 2", 1515199944)
|
||||
|
||||
ctx := context.DnoteCtx{DB: db}
|
||||
var buf bytes.Buffer
|
||||
|
||||
// Execute
|
||||
err := listBooks(ctx, &buf, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
got := buf.String()
|
||||
|
||||
// Verify output
|
||||
assert.Equal(t, strings.Contains(got, "javascript"), true, "should contain javascript book")
|
||||
assert.Equal(t, strings.Contains(got, "linux"), true, "should contain linux book")
|
||||
assert.Equal(t, strings.Contains(got, "(2)"), true, "should show 2 notes for javascript")
|
||||
}
|
||||
47
pkg/cli/cmd/view/note.go
Normal file
47
pkg/cli/cmd/view/note.go
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/* Copyright 2025 Dnote Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package view
|
||||
|
||||
import (
|
||||
"io"
|
||||
"strconv"
|
||||
|
||||
"github.com/dnote/dnote/pkg/cli/context"
|
||||
"github.com/dnote/dnote/pkg/cli/database"
|
||||
"github.com/dnote/dnote/pkg/cli/output"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func viewNote(ctx context.DnoteCtx, w io.Writer, noteRowIDArg string, contentOnly bool) error {
|
||||
noteRowID, err := strconv.Atoi(noteRowIDArg)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "invalid rowid")
|
||||
}
|
||||
|
||||
db := ctx.DB
|
||||
info, err := database.GetNoteInfo(db, noteRowID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if contentOnly {
|
||||
output.NoteContent(w, info)
|
||||
} else {
|
||||
output.NoteInfo(w, info)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
90
pkg/cli/cmd/view/note_test.go
Normal file
90
pkg/cli/cmd/view/note_test.go
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
/* Copyright 2025 Dnote Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package view
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/dnote/dnote/pkg/assert"
|
||||
"github.com/dnote/dnote/pkg/cli/context"
|
||||
"github.com/dnote/dnote/pkg/cli/database"
|
||||
)
|
||||
|
||||
func TestViewNote(t *testing.T) {
|
||||
db := database.InitTestMemoryDB(t)
|
||||
defer db.Close()
|
||||
|
||||
bookUUID := "test-book-uuid"
|
||||
database.MustExec(t, "inserting book", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", bookUUID, "golang")
|
||||
database.MustExec(t, "inserting note", db, "INSERT INTO notes (uuid, book_uuid, body, added_on) VALUES (?, ?, ?, ?)",
|
||||
"note-uuid", bookUUID, "test note content", 1515199943000000000)
|
||||
|
||||
ctx := context.DnoteCtx{DB: db}
|
||||
var buf bytes.Buffer
|
||||
|
||||
err := viewNote(ctx, &buf, "1", false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
got := buf.String()
|
||||
assert.Equal(t, strings.Contains(got, "test note content"), true, "should contain note content")
|
||||
}
|
||||
|
||||
func TestViewNoteContentOnly(t *testing.T) {
|
||||
db := database.InitTestMemoryDB(t)
|
||||
defer db.Close()
|
||||
|
||||
bookUUID := "test-book-uuid"
|
||||
database.MustExec(t, "inserting book", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", bookUUID, "golang")
|
||||
database.MustExec(t, "inserting note", db, "INSERT INTO notes (uuid, book_uuid, body, added_on) VALUES (?, ?, ?, ?)",
|
||||
"note-uuid", bookUUID, "test note content", 1515199943000000000)
|
||||
|
||||
ctx := context.DnoteCtx{DB: db}
|
||||
var buf bytes.Buffer
|
||||
|
||||
err := viewNote(ctx, &buf, "1", true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
got := buf.String()
|
||||
assert.Equal(t, got, "test note content", "should contain only note content")
|
||||
}
|
||||
|
||||
func TestViewNoteInvalidRowID(t *testing.T) {
|
||||
db := database.InitTestMemoryDB(t)
|
||||
defer db.Close()
|
||||
|
||||
ctx := context.DnoteCtx{DB: db}
|
||||
var buf bytes.Buffer
|
||||
|
||||
err := viewNote(ctx, &buf, "not-a-number", false)
|
||||
assert.NotEqual(t, err, nil, "should return error for invalid rowid")
|
||||
}
|
||||
|
||||
func TestViewNoteNotFound(t *testing.T) {
|
||||
db := database.InitTestMemoryDB(t)
|
||||
defer db.Close()
|
||||
|
||||
ctx := context.DnoteCtx{DB: db}
|
||||
var buf bytes.Buffer
|
||||
|
||||
err := viewNote(ctx, &buf, "999", false)
|
||||
assert.NotEqual(t, err, nil, "should return error for non-existent note")
|
||||
}
|
||||
|
|
@ -16,14 +16,13 @@
|
|||
package view
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/dnote/dnote/pkg/cli/context"
|
||||
"github.com/dnote/dnote/pkg/cli/infra"
|
||||
"github.com/dnote/dnote/pkg/cli/utils"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/dnote/dnote/pkg/cli/cmd/cat"
|
||||
"github.com/dnote/dnote/pkg/cli/cmd/ls"
|
||||
"github.com/dnote/dnote/pkg/cli/utils"
|
||||
)
|
||||
|
||||
var example = `
|
||||
|
|
@ -68,27 +67,26 @@ func NewCmd(ctx context.DnoteCtx) *cobra.Command {
|
|||
|
||||
func newRun(ctx context.DnoteCtx) infra.RunEFunc {
|
||||
return func(cmd *cobra.Command, args []string) error {
|
||||
var run infra.RunEFunc
|
||||
|
||||
if len(args) == 0 {
|
||||
run = ls.NewRun(ctx, nameOnly)
|
||||
// List all books
|
||||
return listBooks(ctx, os.Stdout, nameOnly)
|
||||
} else if len(args) == 1 {
|
||||
if nameOnly {
|
||||
return errors.New("--name-only flag is only valid when viewing books")
|
||||
}
|
||||
|
||||
if utils.IsNumber(args[0]) {
|
||||
run = cat.NewRun(ctx, contentOnly)
|
||||
// View a note by index
|
||||
return viewNote(ctx, os.Stdout, args[0], contentOnly)
|
||||
} else {
|
||||
run = ls.NewRun(ctx, false)
|
||||
// List notes in a book
|
||||
return listNotes(ctx, os.Stdout, args[0])
|
||||
}
|
||||
} else if len(args) == 2 {
|
||||
// DEPRECATED: passing book name to view command is deprecated
|
||||
run = cat.NewRun(ctx, false)
|
||||
} else {
|
||||
return errors.New("Incorrect number of arguments")
|
||||
// View a note in a book (book name + note index)
|
||||
return viewNote(ctx, os.Stdout, args[1], contentOnly)
|
||||
}
|
||||
|
||||
return run(cmd, args)
|
||||
return errors.New("Incorrect number of arguments")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,12 +26,10 @@ import (
|
|||
|
||||
// commands
|
||||
"github.com/dnote/dnote/pkg/cli/cmd/add"
|
||||
"github.com/dnote/dnote/pkg/cli/cmd/cat"
|
||||
"github.com/dnote/dnote/pkg/cli/cmd/edit"
|
||||
"github.com/dnote/dnote/pkg/cli/cmd/find"
|
||||
"github.com/dnote/dnote/pkg/cli/cmd/login"
|
||||
"github.com/dnote/dnote/pkg/cli/cmd/logout"
|
||||
"github.com/dnote/dnote/pkg/cli/cmd/ls"
|
||||
"github.com/dnote/dnote/pkg/cli/cmd/remove"
|
||||
"github.com/dnote/dnote/pkg/cli/cmd/root"
|
||||
"github.com/dnote/dnote/pkg/cli/cmd/sync"
|
||||
|
|
@ -79,10 +77,8 @@ func main() {
|
|||
root.Register(login.NewCmd(*ctx))
|
||||
root.Register(logout.NewCmd(*ctx))
|
||||
root.Register(add.NewCmd(*ctx))
|
||||
root.Register(ls.NewCmd(*ctx))
|
||||
root.Register(sync.NewCmd(*ctx))
|
||||
root.Register(version.NewCmd(*ctx))
|
||||
root.Register(cat.NewCmd(*ctx))
|
||||
root.Register(view.NewCmd(*ctx))
|
||||
root.Register(find.NewCmd(*ctx))
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import (
|
|||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/dnote/dnote/pkg/assert"
|
||||
|
|
@ -568,3 +569,65 @@ func TestDBPathFlag(t *testing.T) {
|
|||
db2.QueryRow("SELECT count(*) FROM books WHERE label = ?", "db1-book").Scan(&db2HasDB1Book)
|
||||
assert.Equal(t, db2HasDB1Book, 0, "db2 should not have db1's book")
|
||||
}
|
||||
|
||||
func TestView(t *testing.T) {
|
||||
t.Run("view note by rowid", func(t *testing.T) {
|
||||
_, opts := setupTestEnv(t)
|
||||
|
||||
db, dbPath := database.InitTestFileDB(t)
|
||||
testutils.Setup4(t, db)
|
||||
|
||||
output := testutils.RunDnoteCmd(t, opts, binaryName, "--dbPath", dbPath, "view", "1")
|
||||
|
||||
assert.Equal(t, strings.Contains(output, "Booleans have toString()"), true, "should contain note content")
|
||||
assert.Equal(t, strings.Contains(output, "book name"), true, "should show metadata")
|
||||
})
|
||||
|
||||
t.Run("view note content only", func(t *testing.T) {
|
||||
_, opts := setupTestEnv(t)
|
||||
|
||||
db, dbPath := database.InitTestFileDB(t)
|
||||
testutils.Setup4(t, db)
|
||||
|
||||
output := testutils.RunDnoteCmd(t, opts, binaryName, "--dbPath", dbPath, "view", "1", "--content-only")
|
||||
|
||||
assert.Equal(t, strings.Contains(output, "Booleans have toString()"), true, "should contain note content")
|
||||
assert.Equal(t, strings.Contains(output, "book name"), false, "should not show metadata")
|
||||
})
|
||||
|
||||
t.Run("list books", func(t *testing.T) {
|
||||
_, opts := setupTestEnv(t)
|
||||
|
||||
db, dbPath := database.InitTestFileDB(t)
|
||||
testutils.Setup1(t, db)
|
||||
|
||||
output := testutils.RunDnoteCmd(t, opts, binaryName, "--dbPath", dbPath, "view")
|
||||
|
||||
assert.Equal(t, strings.Contains(output, "js"), true, "should list js book")
|
||||
assert.Equal(t, strings.Contains(output, "linux"), true, "should list linux book")
|
||||
})
|
||||
|
||||
t.Run("list notes in book", func(t *testing.T) {
|
||||
_, opts := setupTestEnv(t)
|
||||
|
||||
db, dbPath := database.InitTestFileDB(t)
|
||||
testutils.Setup2(t, db)
|
||||
|
||||
output := testutils.RunDnoteCmd(t, opts, binaryName, "--dbPath", dbPath, "view", "js")
|
||||
|
||||
assert.Equal(t, strings.Contains(output, "n1 body"), true, "should list note 1")
|
||||
assert.Equal(t, strings.Contains(output, "n2 body"), true, "should list note 2")
|
||||
})
|
||||
|
||||
t.Run("view note by book name and rowid", func(t *testing.T) {
|
||||
_, opts := setupTestEnv(t)
|
||||
|
||||
db, dbPath := database.InitTestFileDB(t)
|
||||
testutils.Setup4(t, db)
|
||||
|
||||
output := testutils.RunDnoteCmd(t, opts, binaryName, "--dbPath", dbPath, "view", "js", "2")
|
||||
|
||||
assert.Equal(t, strings.Contains(output, "Date object implements mathematical comparisons"), true, "should contain note content")
|
||||
assert.Equal(t, strings.Contains(output, "book name"), true, "should show metadata")
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ package output
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/dnote/dnote/pkg/cli/database"
|
||||
|
|
@ -26,7 +27,7 @@ import (
|
|||
)
|
||||
|
||||
// NoteInfo prints a note information
|
||||
func NoteInfo(info database.NoteInfo) {
|
||||
func NoteInfo(w io.Writer, info database.NoteInfo) {
|
||||
log.Infof("book name: %s\n", info.BookLabel)
|
||||
log.Infof("created at: %s\n", time.Unix(0, info.AddedOn).Format("Jan 2, 2006 3:04pm (MST)"))
|
||||
if info.EditedOn != 0 {
|
||||
|
|
@ -35,13 +36,13 @@ func NoteInfo(info database.NoteInfo) {
|
|||
log.Infof("note id: %d\n", info.RowID)
|
||||
log.Infof("note uuid: %s\n", info.UUID)
|
||||
|
||||
fmt.Printf("\n------------------------content------------------------\n")
|
||||
fmt.Printf("%s", info.Content)
|
||||
fmt.Printf("\n-------------------------------------------------------\n")
|
||||
fmt.Fprintf(w, "\n------------------------content------------------------\n")
|
||||
fmt.Fprintf(w, "%s", info.Content)
|
||||
fmt.Fprintf(w, "\n-------------------------------------------------------\n")
|
||||
}
|
||||
|
||||
func NoteContent(info database.NoteInfo) {
|
||||
fmt.Printf("%s", info.Content)
|
||||
func NoteContent(w io.Writer, info database.NoteInfo) {
|
||||
fmt.Fprintf(w, "%s", info.Content)
|
||||
}
|
||||
|
||||
// BookInfo prints a note information
|
||||
|
|
|
|||
|
|
@ -144,7 +144,7 @@ type RunDnoteCmdOptions struct {
|
|||
}
|
||||
|
||||
// RunDnoteCmd runs a dnote command
|
||||
func RunDnoteCmd(t *testing.T, opts RunDnoteCmdOptions, binaryName string, arg ...string) {
|
||||
func RunDnoteCmd(t *testing.T, opts RunDnoteCmdOptions, binaryName string, arg ...string) string {
|
||||
t.Logf("running: %s %s", binaryName, strings.Join(arg, " "))
|
||||
|
||||
cmd, stderr, stdout, err := NewDnoteCmd(opts, binaryName, arg...)
|
||||
|
|
@ -162,6 +162,8 @@ func RunDnoteCmd(t *testing.T, opts RunDnoteCmdOptions, binaryName string, arg .
|
|||
|
||||
// Print stdout if and only if test fails later
|
||||
t.Logf("\n%s", stdout)
|
||||
|
||||
return stdout.String()
|
||||
}
|
||||
|
||||
// WaitDnoteCmd runs a dnote command and passes stdout to the callback.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue