CLI interface to medialist (fossil mirror)
Révision | 7645bcaf4f5391ffb2694b8e15240ae12dff85fc (tree) |
---|---|
l'heure | 2021-11-21 19:09:36 |
Auteur | mio <stigma@disr...> |
Commiter | mio |
Share the code for adding missing headers between add.d and update.d (now located in util.d). These are the only places where the code should be required.
FossilOrigin-Name: 46ca890bccd96877f1e48144414e435bf8ea446de1e032f133b38df6312fb958
@@ -59,16 +59,16 @@ import util; | ||
59 | 59 | _log("list '" ~ listName ~ "' doesn't exist.", true); |
60 | 60 | return false; |
61 | 61 | } |
62 | - | |
62 | + | |
63 | 63 | // no status or progress. |
64 | 64 | if (args.length < 3) |
65 | 65 | { |
66 | 66 | return addItem(listFilePath, title); |
67 | 67 | } |
68 | - | |
68 | + | |
69 | 69 | string status = "UNKNOWN"; |
70 | 70 | string progress = "??/??"; |
71 | - | |
71 | + | |
72 | 72 | getopt(args, |
73 | 73 | "status|s", "Set the initial status for the new item", &status, |
74 | 74 | "progress|p", "Set the initial progress for the new item", &progress); |
@@ -78,7 +78,7 @@ import util; | ||
78 | 78 | writefln("Added %s to %s (status: %s, progress: %s)", title, listName, status, progress); |
79 | 79 | return true; |
80 | 80 | } |
81 | - | |
81 | + | |
82 | 82 | return false; |
83 | 83 | } |
84 | 84 |
@@ -86,115 +86,14 @@ private bool addItem(string listFilePath, string itemName, | ||
86 | 86 | string status = "UNKNOWN", string progress = "??/??") |
87 | 87 | { |
88 | 88 | File listFile = File(listFilePath, "a+"); |
89 | - | |
90 | - string[] headers = retrieveListHeaders(&listFile); | |
91 | - if (headers is null) | |
92 | - { | |
93 | - return false; | |
94 | - } | |
95 | - addMissingHeaders(headers, &listFile); | |
96 | - auto headersAndPositions = parseMLHeader(headers); | |
97 | - writeNewItem(&listFile, headersAndPositions, itemName, status, progress); | |
98 | - | |
99 | - return true; | |
100 | -} | |
101 | - | |
102 | -private string[] retrieveListHeaders(File* listFile) | |
103 | -{ | |
104 | - string line; | |
105 | - | |
106 | - while ((line = listFile.readln()) !is null) | |
107 | - { | |
108 | - if ('#' != line[0]) | |
109 | - { | |
110 | - return line.strip().split('\t'); | |
111 | - } | |
112 | - } | |
113 | - | |
114 | - return null; | |
115 | -} | |
116 | - | |
117 | -private void addMissingHeaders(ref string[] fileHeaders, File* listFile) | |
118 | -{ | |
119 | - import std.algorithm.searching : canFind; | |
120 | - | |
121 | - if (false == canFind(fileHeaders, "start_date")) | |
122 | - { | |
123 | - addHeader(listFile, "start_date"); | |
124 | - fileHeaders ~= "start_date"; | |
125 | - } | |
126 | - | |
127 | - if (false == canFind(fileHeaders, "end_date")) | |
128 | - { | |
129 | - addHeader(listFile, "end_date"); | |
130 | - fileHeaders ~= "end_date"; | |
131 | - } | |
132 | - | |
133 | - if (false == canFind(fileHeaders, "last_updated")) | |
134 | - { | |
135 | - addHeader(listFile, "last_updated"); | |
136 | - fileHeaders ~= "last_updated"; | |
137 | - } | |
138 | -} | |
139 | - | |
140 | -private | |
141 | -void addHeader(File* f, string newHeader) @trusted | |
142 | -in | |
143 | -{ | |
144 | - assert(f.isOpen == true); | |
145 | - assert(newHeader !is null); | |
146 | -} | |
147 | -out | |
148 | -{ | |
149 | - assert(f.isOpen == true); | |
150 | -} | |
151 | -do | |
152 | -{ | |
153 | - import std.file : rename, remove, tempDir; | |
154 | - import std.path : buildPath, baseName; | |
155 | - | |
156 | - immutable origPath = f.name; | |
157 | - immutable tempPath = buildPath(tempDir(), baseName(origPath)); | |
158 | - | |
159 | - File tempFile = File(tempPath, "w+"); | |
160 | - | |
161 | - string line = ""; | |
162 | - bool pastHeader = false; | |
163 | - | |
164 | - f.rewind(); | |
165 | - | |
166 | - while ((line = f.readln()) !is null) | |
167 | - { | |
168 | - if (false == pastHeader && '#' != line[0]) | |
169 | - { | |
170 | - pastHeader = true; | |
171 | - tempFile.writefln("%s\t%s", line.strip, newHeader); | |
172 | - } | |
173 | - else | |
174 | - { | |
175 | - /* line may not have a newline character */ | |
176 | - tempFile.writeln(line.strip); | |
177 | - } | |
178 | - } | |
179 | - | |
180 | - f.close(); | |
181 | - | |
182 | - remove(origPath); | |
183 | 89 | |
184 | - /* some operating systems don't like to copy from the tempdir? */ | |
185 | - f.open(origPath, "a+"); | |
186 | - tempFile.rewind(); | |
90 | + string[] headers = retrieveListHeaders (&listFile); | |
91 | + addMissingHeaders (headers, &listFile); | |
187 | 92 | |
188 | - while ((line = tempFile.readln()) !is null) | |
189 | - { | |
190 | - f.write(line); | |
191 | - } | |
192 | - | |
193 | - /* rewind because we need to re-read the headers */ | |
194 | - f.rewind(); | |
93 | + auto headersAndPositions = parseMLHeader(headers); | |
94 | + writeNewItem(&listFile, headersAndPositions, itemName, status, progress); | |
195 | 95 | |
196 | - tempFile.close(); | |
197 | - remove(tempPath); | |
96 | + return true; | |
198 | 97 | } |
199 | 98 | |
200 | 99 | private void writeNewItem(File* listFile, HeaderPairType[NUMBER_OF_ML_HEADERS] headersAndPositions, |
@@ -203,9 +102,9 @@ private void writeNewItem(File* listFile, HeaderPairType[NUMBER_OF_ML_HEADERS] h | ||
203 | 102 | import std.datetime.date : Date; |
204 | 103 | import std.datetime.systime : Clock; |
205 | 104 | import std.string : toLower; |
206 | - | |
105 | + | |
207 | 106 | size_t previousIndent = 0; |
208 | - | |
107 | + | |
209 | 108 | immutable currentDT = Clock.currTime(); |
210 | 109 | immutable currentDateString = Date(currentDT.year, currentDT.month, |
211 | 110 | currentDT.day).toISOExtString(); |
@@ -215,7 +114,7 @@ private void writeNewItem(File* listFile, HeaderPairType[NUMBER_OF_ML_HEADERS] h | ||
215 | 114 | /* unset header -- no more headers possible */ |
216 | 115 | if (headerPair[0] == 0 && headerPair[1] is null) |
217 | 116 | break; |
218 | - | |
117 | + | |
219 | 118 | listFile.replicateIndent(headerPair, &previousIndent); |
220 | 119 | |
221 | 120 | switch (headerPair[1]) |
@@ -262,7 +161,7 @@ private void replicateIndent(File* listFile, HeaderPairType headerPair, size_t* | ||
262 | 161 | import core.stdc.stdio : fprintf; |
263 | 162 | import core.stdc.stdio : cstderr = stderr; |
264 | 163 | import core.stdc.stdlib : malloc, free; |
265 | - | |
164 | + | |
266 | 165 | // Can't add NULL byte to D string, so allocate a temp string and free it. |
267 | 166 | char* progname = cast(char*)malloc(char.sizeof * programName.length); |
268 | 167 | scope(exit) free(progname); |
@@ -279,3 +178,29 @@ options: | ||
279 | 178 | -p, --progress the initial progress of the new item (default: ??/??) |
280 | 179 | -s, --status the initial status of the new item (default: UNKNOWN)\n", progname); |
281 | 180 | } |
181 | + | |
182 | + | |
183 | +/* Testing pre-0.2 files with the addHeaders */ | |
184 | +unittest | |
185 | +{ | |
186 | + import std.file : remove; | |
187 | + import std.stdio : File; | |
188 | + | |
189 | + enum listName = "unittest-add-pre-0.2"; | |
190 | + string line; | |
191 | + | |
192 | + auto listFile = File (listName ~ ".tsv", "w+"); | |
193 | + scope (exit) remove(listName ~ ".tsv"); | |
194 | + | |
195 | + listFile.writeln("TITLE\tPROGRESS\tSTATUS"); | |
196 | + listFile.close(); | |
197 | + | |
198 | + /* Shouldn't crash */ | |
199 | + handle_add (listName, [listName, "Item 1", "-p", "??/??", "-s", "PLAN-TO-READ"], "."); | |
200 | + | |
201 | + listFile.open(listName ~ ".tsv", "r"); | |
202 | + listFile.rewind(); | |
203 | + line = listFile.readln(); | |
204 | + assert ("TITLE\tPROGRESS\tSTATUS\tstart_date\tend_date\tlast_updated\n" == line, "'" ~ line ~ "'"); | |
205 | +} | |
206 | + |
@@ -110,6 +110,13 @@ handle_update(string program_name, string[] args, string data_dir) | ||
110 | 110 | bool read_header = false; |
111 | 111 | Tuple!(size_t, string)[NUMBER_OF_ML_HEADERS] headers; |
112 | 112 | |
113 | + /* Add missing headers */ | |
114 | + { | |
115 | + File listFile = File(filename, "a+"); | |
116 | + string[] headers_ = retrieveListHeaders(&listFile); | |
117 | + addMissingHeaders(headers_, &listFile); | |
118 | + } | |
119 | + | |
113 | 120 | /* |
114 | 121 | * line_number represents the line number in file. |
115 | 122 | * index initially represents a matching ID number |
@@ -18,6 +18,7 @@ | ||
18 | 18 | */ |
19 | 19 | module util; |
20 | 20 | |
21 | +import std.stdio : File; | |
21 | 22 | import std.typecons : Tuple, tuple; |
22 | 23 | |
23 | 24 | alias HeaderPairType = Tuple!(size_t, string); |
@@ -183,3 +184,118 @@ parseMLHeader(string[] headerSections) | ||
183 | 184 | |
184 | 185 | return sections; |
185 | 186 | } |
187 | + | |
188 | +public string[] | |
189 | +retrieveListHeaders (File* listFile) | |
190 | +{ | |
191 | + import std.string : strip, split; | |
192 | + | |
193 | + string line = null; | |
194 | + string[] fileHeaders; | |
195 | + | |
196 | + listFile.rewind (); | |
197 | + | |
198 | + while ((line = listFile.readln()) !is null) | |
199 | + { | |
200 | + if ('#' != line[0]) | |
201 | + { | |
202 | + fileHeaders = line.strip().split('\t'); | |
203 | + break; | |
204 | + } | |
205 | + } | |
206 | + | |
207 | + return fileHeaders; | |
208 | +} | |
209 | + | |
210 | +public void | |
211 | +addMissingHeaders(ref string[] fileHeaders, File* listFile) | |
212 | +{ | |
213 | + import core.stdc.stdio : SEEK_END; | |
214 | + import std.algorithm.searching : canFind; | |
215 | + | |
216 | + /* Version 0.2 */ | |
217 | + if (false == canFind(fileHeaders, "start_date")) | |
218 | + { | |
219 | + addHeader(listFile, "start_date"); | |
220 | + fileHeaders ~= "start_date"; | |
221 | + } | |
222 | + | |
223 | + if (false == canFind(fileHeaders, "end_date")) | |
224 | + { | |
225 | + addHeader(listFile, "end_date"); | |
226 | + fileHeaders ~= "end_date"; | |
227 | + } | |
228 | + | |
229 | + if (false == canFind(fileHeaders, "last_updated")) | |
230 | + { | |
231 | + addHeader(listFile, "last_updated"); | |
232 | + fileHeaders ~= "last_updated"; | |
233 | + } | |
234 | + | |
235 | + listFile.seek(0, SEEK_END); | |
236 | +} | |
237 | + | |
238 | +@trusted private void | |
239 | +addHeader(File* f, string newHeader) | |
240 | +in | |
241 | +{ | |
242 | + assert (f.isOpen == true, "f.isOpen == true (in)"); | |
243 | + assert (newHeader !is null, "newHeader !is null"); | |
244 | +} | |
245 | +out | |
246 | +{ | |
247 | + assert (f.isOpen == true, "f.isOpen == true (out)"); | |
248 | +} | |
249 | +do | |
250 | +{ | |
251 | + import std.file : remove, tempDir; | |
252 | + import std.path : buildPath, baseName; | |
253 | + import std.string : strip; | |
254 | + | |
255 | + immutable originalPath = f.name; | |
256 | + immutable tempPath = buildPath(tempDir, baseName(originalPath)); | |
257 | + | |
258 | + File tempFile = File(tempPath, "w+"); | |
259 | + | |
260 | + string line = ""; | |
261 | + bool pastHeader = false; | |
262 | + | |
263 | + /* Make sure we're at the start of the file. */ | |
264 | + f.rewind (); | |
265 | + | |
266 | + while ((line = f.readln()) !is null) | |
267 | + { | |
268 | + if (false == pastHeader && '#' != line[0]) | |
269 | + { | |
270 | + pastHeader = true; | |
271 | + tempFile.writefln ("%s\t%s", strip (line), newHeader); | |
272 | + } | |
273 | + else | |
274 | + { | |
275 | + /* strip + writeln since the previous one may not have had the newline */ | |
276 | + tempFile.writeln (strip (line)); | |
277 | + } | |
278 | + } | |
279 | + | |
280 | + f.close (); | |
281 | + | |
282 | + /* | |
283 | + * Since the temporary directory *could* be mounted differently (e.g. remotely) | |
284 | + * some systems don't like to copy from the temporary directory. | |
285 | + */ | |
286 | + remove (originalPath); | |
287 | + | |
288 | + f.open (originalPath, "w+"); | |
289 | + tempFile.rewind (); | |
290 | + | |
291 | + while ((line = tempFile.readln()) !is null) | |
292 | + { | |
293 | + f.write (line); | |
294 | + } | |
295 | + | |
296 | + /* Rewind since we need to re-parse the headers */ | |
297 | + f.rewind (); | |
298 | + | |
299 | + tempFile.close(); | |
300 | + remove (tempPath); | |
301 | +} |