Commit
+129 -82 +/-6 browse
1 | diff --git a/pkg/cmd/app.go b/pkg/cmd/app.go |
2 | index 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 |
61 | index 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 |
173 | index 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 |
214 | deleted file mode 100644 |
215 | index 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 |
250 | index 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 |
268 | new file mode 100644 |
269 | index 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 | + } |