Author: Kevin Schoon [me@kevinschoon.com]
Hash: 1e9aa15a1e62c51fda0728d37cddc1495b1f9add
Timestamp: Fri, 03 Jun 2022 20:53:30 +0000 (2 years ago)

+129 -82 +/-6 browse
various updates
1diff --git a/pkg/cmd/app.go b/pkg/cmd/app.go
2index e48869a..a669c41 100644
3--- a/pkg/cmd/app.go
4+++ b/pkg/cmd/app.go
5 @@ -63,11 +63,6 @@ data structure. It uses SQLite to store note content and metadata.
6 Aliases: []string{"e"},
7 Flags: []cli.Flag{
8 &cli.BoolFlag{
9- Name: "parents",
10- Aliases: []string{"p"},
11- Usage: "create parent notes if they don't already exist",
12- },
13- &cli.BoolFlag{
14 Name: "stdin",
15 Aliases: []string{"r"},
16 Usage: "read content from standard input",
17 @@ -79,7 +74,18 @@ data structure. It uses SQLite to store note content and metadata.
18 },
19 },
20 Action: func(ctx *cli.Context) error {
21- return hierarchy.Edit(*cfg, ctx.Args().First())
22+ path := ctx.Args().First()
23+ if path == "" {
24+ return fmt.Errorf("no path specified")
25+ }
26+ err := hierarchy.Create(*cfg, path)
27+ if err != nil {
28+ return err
29+ }
30+ if ctx.Bool("stdin") {
31+ return hierarchy.EditFromStdout(*cfg, path, ctx.Bool("append"))
32+ }
33+ return hierarchy.Edit(*cfg, path)
34 },
35 },
36 {
37 @@ -95,11 +101,20 @@ data structure. It uses SQLite to store note content and metadata.
38 },
39 },
40 Action: func(ctx *cli.Context) error {
41- notes, err := hierarchy.LoadNotes(*cfg)
42+ path := ctx.Args().First()
43+ if path == "" {
44+ notes, err := hierarchy.LoadNotes(*cfg)
45+ if err != nil {
46+ return err
47+ }
48+ return json.NewEncoder(os.Stdout).Encode(notes)
49+ }
50+
51+ note, err := hierarchy.Find(*cfg, path)
52 if err != nil {
53 return err
54 }
55- return json.NewEncoder(os.Stdout).Encode(notes)
56+ return json.NewEncoder(os.Stdout).Encode(note)
57 },
58 },
59 {
60 diff --git a/pkg/hierarchy/database.go b/pkg/hierarchy/database.go
61index 53579c5..540aaef 100644
62--- a/pkg/hierarchy/database.go
63+++ b/pkg/hierarchy/database.go
64 @@ -3,8 +3,7 @@ package hierarchy
65 import (
66 "database/sql"
67 _ "embed"
68- "io/ioutil"
69- "os"
70+ "fmt"
71
72 _ "github.com/mattn/go-sqlite3"
73 "kevinschoon.com/hierarchy/pkg/config"
74 @@ -42,13 +41,6 @@ func With(path string, fn func(*sql.Tx) error) error {
75 return tx.Commit()
76 }
77
78- type dbNote struct {
79- ID int64
80- Name string
81- Content string
82- Parent int64
83- }
84-
85 func resolve(seed *Note, tx *sql.Tx) error {
86 rows, err := tx.Query(`
87 WITH RECURSIVE
88 @@ -103,9 +95,9 @@ func Find(cfg config.Config, notePath string) (*Note, error) {
89 note := new(Note)
90 return note, With(cfg.Database, func(tx *sql.Tx) error {
91 row := tx.QueryRow(`
92- SELECT (
93+ SELECT
94 id, name, content
95- ) FROM notes
96+ FROM notes
97 WHERE
98 name = ?
99 `, np.String())
100 @@ -117,33 +109,6 @@ WHERE
101 })
102 }
103
104- func Edit(cfg config.Config, notePath string) error {
105- np := ReadPath(notePath)
106- return With(cfg.Database, func(tx *sql.Tx) error {
107- raw, err := ioutil.ReadAll(os.Stdin)
108- if err != nil {
109- return err
110- }
111- var parent *int64
112- if np.Parent() != "" {
113- parent = new(int64)
114- row := tx.QueryRow(`
115- SELECT id FROM notes
116- WHERE name = ?
117- `, np.Parent())
118- err = row.Scan(&parent)
119- if err != nil {
120- return err
121- }
122- }
123- _, err = tx.Exec(`
124- INSERT INTO notes (name, parent, content)
125- VALUES (?, ?, ?)
126- `, np.Name(), parent, string(raw))
127- return err
128- })
129- }
130-
131 func LoadNotes(cfg config.Config) (notes []*Note, err error) {
132 return notes, With(cfg.Database, func(tx *sql.Tx) error {
133 rows, err := tx.Query(`
134 @@ -170,3 +135,37 @@ WHERE parent IS NULL
135 return nil
136 })
137 }
138+
139+ func Create(cfg config.Config, path string) error {
140+ np := ReadPath(path)
141+ return With(cfg.Database, func(tx *sql.Tx) error {
142+ for _, path := range np.Paths() {
143+ _, err := tx.Exec(`
144+ INSERT INTO notes
145+ (name, parent)
146+ VALUES
147+ (?, (select id from notes where name = ?))
148+ ON CONFLICT DO NOTHING
149+ `, path.String(), path.Parent())
150+ if err != nil {
151+ return err
152+ }
153+ }
154+ return nil
155+ })
156+ }
157+
158+ func Save(cfg config.Config, path, content string) error {
159+ np := ReadPath(path)
160+ fmt.Println(content, np.String())
161+ return With(cfg.Database, func(tx *sql.Tx) error {
162+ _, err := tx.Exec(`
163+ UPDATE notes
164+ set content = ?,
165+ updated_at = CURRENT_TIMESTAMP
166+ WHERE
167+ name = ?
168+ `, content, np.String())
169+ return err
170+ })
171+ }
172 diff --git a/pkg/hierarchy/editor.go b/pkg/hierarchy/editor.go
173index aadda55..13a6ee2 100644
174--- a/pkg/hierarchy/editor.go
175+++ b/pkg/hierarchy/editor.go
176 @@ -9,7 +9,11 @@ import (
177 "kevinschoon.com/hierarchy/pkg/config"
178 )
179
180- func OpenWithEditor(cfg config.Config, note *Note, cb func() error) error {
181+ func Edit(cfg config.Config, path string) error {
182+ note, err := Find(cfg, path)
183+ if err != nil {
184+ return err
185+ }
186 editor := cfg.Editor
187 if editor == "" {
188 editor = os.Getenv("EDITOR")
189 @@ -40,8 +44,20 @@ func OpenWithEditor(cfg config.Config, note *Note, cb func() error) error {
190 if err != nil {
191 return err
192 }
193- if string(raw) != note.Content {
194+ return Save(cfg, path, string(raw))
195+ }
196+
197+ func EditFromStdout(cfg config.Config, path string, doAppend bool) error {
198+ raw, err := ioutil.ReadAll(os.Stdin)
199+ if err != nil {
200+ return err
201+ }
202+ if doAppend {
203+ note, err := Find(cfg, path)
204+ if err != nil {
205+ return err
206+ }
207+ return Save(cfg, path, note.Content+string(raw))
208 }
209- // TODO
210- return nil
211+ return Save(cfg, path, string(raw))
212 }
213 diff --git a/pkg/hierarchy/helpers.go b/pkg/hierarchy/helpers.go
214deleted file mode 100644
215index 4bca583..0000000
216--- a/pkg/hierarchy/helpers.go
217+++ /dev/null
218 @@ -1,30 +0,0 @@
219- package hierarchy
220-
221- import (
222- "path"
223- "strings"
224- )
225-
226- type NotePath [2]string
227-
228- func (np NotePath) String() string {
229- return path.Join(np[0], np[1])
230- }
231-
232- func (np NotePath) Parent() string {
233- return np[0]
234- }
235-
236- func (np NotePath) Name() string {
237- return np[1]
238- }
239-
240- func ReadPath(input string) NotePath {
241- base, name := path.Split(input)
242- if base == "/" {
243- base = ""
244- }
245- base = strings.TrimLeft(base, "/")
246- base = strings.TrimRight(base, "/")
247- return [2]string{base, name}
248- }
249 diff --git a/pkg/hierarchy/migrate/init.sql b/pkg/hierarchy/migrate/init.sql
250index 176943d..716bd23 100644
251--- a/pkg/hierarchy/migrate/init.sql
252+++ b/pkg/hierarchy/migrate/init.sql
253 @@ -3,10 +3,11 @@ PRAGMA foreign_keys = on;
254 -- main note table
255 CREATE TABLE IF NOT EXISTS notes (
256 id INTEGER PRIMARY KEY,
257- created_at INTEGER,
258+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
259+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
260 parent INTEGER,
261 name VARCHAR NOT NULL UNIQUE,
262- content VARCHAR,
263+ content VARCHAR DEFAULT '',
264 FOREIGN KEY(parent) REFERENCES notes(id)
265 );
266
267 diff --git a/pkg/hierarchy/path.go b/pkg/hierarchy/path.go
268new file mode 100644
269index 0000000..fc22c45
270--- /dev/null
271+++ b/pkg/hierarchy/path.go
272 @@ -0,0 +1,46 @@
273+ package hierarchy
274+
275+ import (
276+ "path"
277+ "strings"
278+ )
279+
280+ type NotePath [2]string
281+
282+ func (np NotePath) String() string {
283+ return path.Join(np[0], np[1])
284+ }
285+
286+ func (np NotePath) AtRoot() bool {
287+ return np[0] == ""
288+ }
289+
290+ func (np NotePath) Paths() (paths []NotePath) {
291+ if np.AtRoot() {
292+ paths = append(paths, np)
293+ return paths
294+ }
295+ split := strings.Split(np.String(), "/")
296+ for i := 0; i < len(split); i++ {
297+ paths = append(paths, ReadPath(path.Join(split[0:i+1]...)))
298+ }
299+ return paths
300+ }
301+
302+ func (np NotePath) Parent() string {
303+ return np[0]
304+ }
305+
306+ func (np NotePath) Name() string {
307+ return np[1]
308+ }
309+
310+ func ReadPath(input string) NotePath {
311+ base, name := path.Split(input)
312+ if base == "/" {
313+ base = ""
314+ }
315+ base = strings.TrimLeft(base, "/")
316+ base = strings.TrimRight(base, "/")
317+ return [2]string{base, name}
318+ }