1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
use clap::{Parser, Subcommand};
use gen_models::db::OperationsConnection;
pub mod cli_context;
pub mod export;
pub mod graph_operations;
pub mod import;
pub mod remote;
pub mod update;
#[derive(Subcommand)]
#[allow(clippy::large_enum_variant)]
pub enum Commands {
Init {},
/// Commands for importing
Import(import::Command),
/// Commands for updating
Update(update::Command),
/// Commands for exporting
Export(export::Command),
/// Commands for transforming file types for input to Gen.
#[command(arg_required_else_help(true))]
Transform {
/// For update-gaf, this transforms the csv to a fasta for use in alignments
#[arg(long)]
format_csv_for_gaf: Option<String>,
},
/// Translate coordinates of standard bioinformatic file formats.
#[command(arg_required_else_help(true))]
Translate {
/// Transform coordinates of a BED to graph nodes
#[arg(long)]
bed: Option<String>,
/// Transform coordinates of a GFF to graph nodes
#[arg(long)]
gff: Option<String>,
/// The name of the collection to map sequences against
#[arg(short, long)]
collection: Option<String>,
/// The sample name whose graph coordinates are mapped against
#[arg(short, long)]
sample: Option<String>,
},
/// Show a visual representation of a graph in the terminal
#[command()]
View {
/// The name of the graph to view
#[clap(index = 1)]
graph: Option<String>,
/// View the graph for a specific sample
#[arg(short, long)]
sample: Option<String>,
/// Look for the sample in a specific collection
#[arg(short, long)]
collection: Option<String>,
/// Position as "node id:coordinate" to center the graph on
#[arg(short, long)]
position: Option<String>,
/// Show the full TUI explorer instead of the inline preview. Includes sidebar explorer and additional interactive features.
#[arg(short, long)]
full: bool,
},
/// Show a diff of operations and render the consolidated graph
#[command(name = "view-diff", arg_required_else_help(true))]
ViewDiff {
/// The base ref (operation hash/prefix, branch name, or HEAD shorthand) to diff from
#[clap(index = 1)]
from: String,
/// The target ref to diff to (defaults to the currently checked out operation)
#[clap(index = 2)]
to: Option<String>,
},
/// Export a set of operations to a patch file
#[command(name = "patch-create", arg_required_else_help(true))]
PatchCreate {
/// To create a patch against a non-checked out branch.
#[arg(short, long)]
branch: Option<String>,
/// The patch name
#[arg(short, long)]
name: String,
/// The operation(s) to create a patch from. For a range, use start..end and for multiple
/// or discontinuous ranges, use commas. HEAD and HEAD~<number> syntax is supported.
#[clap(index = 1)]
operation: String,
},
/// Apply changes from a patch file
#[command(name = "patch-apply", arg_required_else_help(true))]
PatchApply {
/// The patch file
#[clap(index = 1)]
patch: String,
},
/// View a patch in dot format
#[command(name = "patch-view", arg_required_else_help(true))]
PatchView {
/// The prefix to use in the output filenames. One dot file is created for each operation and graph,
/// following the pattern {prefix}_{operation}_{graph_id}.dot. Defaults to patch filename.
#[arg(long, short)]
prefix: Option<String>,
/// The patch file
#[clap(index = 1)]
patch: String,
},
/// Manage and create branches
#[command(arg_required_else_help(true))]
Branch {
/// Create a branch with the given name
#[arg(long, action)]
create: bool,
/// Delete a given branch
#[arg(short, long, action)]
delete: bool,
/// Checkout a given branch
#[arg(long, action)]
checkout: bool,
/// List all branches
#[arg(short, long, action)]
list: bool,
#[arg(short, long, action)]
merge: bool,
/// Set the remote for the current branch
#[arg(long)]
set_remote: Option<String>,
/// The branch name
#[clap(index = 1)]
branch_name: Option<String>,
},
/// Merge branches
#[command(arg_required_else_help(true))]
Merge {
/// The branch name to merge
#[clap(index = 1)]
branch_name: Option<String>,
},
/// Migrate a database to a given operation
#[command(arg_required_else_help(true))]
Checkout {
/// Create and checkout a new branch.
#[arg(short, long)]
branch: Option<String>,
/// The operation hash to move to
#[clap(index = 1)]
hash: Option<String>,
},
/// Reset a branch to a previous operation
#[command(arg_required_else_help(true))]
Reset {
/// The operation hash to reset to
#[clap(index = 1)]
hash: String,
},
/// View operations carried out against a database
#[command()]
Operations {
/// Edit operation messages
#[arg(short, long)]
interactive: bool,
/// The branch to list operations for
#[arg(short, long)]
branch: Option<String>,
},
/// Apply an operation to a branch
#[command(arg_required_else_help(true))]
Apply {
/// The operation hash to apply
#[clap(index = 1)]
hash: String,
},
/// Configure default options
#[command(arg_required_else_help(true))]
Defaults {
/// The default database to use
#[arg(short, long)]
database: Option<String>,
/// The default collection to use
#[arg(short, long)]
collection: Option<String>,
},
/// Manage remote repositories
#[command(subcommand)]
Remote(remote::RemoteCommand),
/// Push the local repo to the remote
#[command()]
Push {
/// The remote to push to
#[arg(short, long)]
remote: Option<String>,
},
#[command()]
Pull {
/// The remote to pull from
#[arg(short, long)]
remote: Option<String>,
},
/// Convert annotation coordinates between two samples
#[command(arg_required_else_help(true))]
PropagateAnnotations {
/// The name of the collection to annotate
#[arg(short, long)]
name: Option<String>,
/// The name of the sample the annotations are referenced to (if not provided, the default)
#[arg(short, long)]
from_sample: Option<String>,
/// The name of the sample to annotate
#[arg(short, long)]
to_sample: String,
/// The name of the annotation file to propagate
#[arg(short, long)]
gff: String,
/// The name of the output file
#[arg(short, long)]
output_gff: String,
},
/// Add an annotation and accession for a region
#[command(name = "add-annotation", arg_required_else_help(true))]
AddAnnotation {
/// The annotation name
#[arg(short, long)]
name: String,
/// The annotation group name
#[arg(short, long)]
group: Option<String>,
/// The sample name to annotate (defaults to the collection's default sample)
#[arg(short, long)]
sample: Option<String>,
/// The region to annotate (region:start-end)
#[clap(index = 1)]
region: String,
},
/// Add an annotation file without importing its intervals
#[command(name = "add-annotation-file", arg_required_else_help(true))]
AddAnnotationFile {
/// The annotation file path
#[clap(index = 1)]
path: String,
/// The annotation file format (gff3, bed, genbank). If omitted, infer from the file extension.
#[arg(short, long)]
format: Option<String>,
/// Optional tabix index file path for the annotation file
#[arg(long)]
index: Option<String>,
/// Optional annotation file name
#[arg(short, long)]
name: Option<String>,
/// Optional operation summary message
#[arg(short, long)]
message: Option<String>,
},
/// List all samples in the current collection
ListSamples {},
#[command()]
/// List all regions/contigs in the current collection and given sample
ListGraphs {
/// The name of the collection to list graphs for
#[arg(short, long)]
name: Option<String>,
/// The name of the sample to list graphs for
#[arg(short, long)]
sample: Option<String>,
},
/// Extract a sequence from a graph
#[command(arg_required_else_help(true))]
GetSequence {
/// The name of the collection containing the sequence
#[arg(short, long)]
name: Option<String>,
/// The name of the sample containing the sequence
#[arg(short, long)]
sample: Option<String>,
/// The name of the graph to get the sequence for
#[arg(short, long)]
graph: Option<String>,
/// The start coordinate of the sequence
#[arg(long)]
start: Option<i64>,
/// The end coordinate of the sequence
#[arg(long)]
end: Option<i64>,
/// The region (name:start-end format) of the sequence
#[arg(long)]
region: Option<String>,
},
/// Output a file representing the "diff" between two samples
Diff {
/// The name of the collection to diff
#[arg(short, long)]
name: Option<String>,
/// The name of the first sample to diff
#[arg(long)]
sample1: Option<String>,
/// The name of the second sample to diff
#[arg(long)]
sample2: Option<String>,
/// The name of the output GFA file
#[arg(long)]
gfa: String,
},
/// Replace a sequence graph with a subgraph in the range of the specified coordinates
DeriveSubgraph {
/// The name of the collection to derive the subgraph from
#[arg(short, long)]
name: Option<String>,
/// The name of the parent sample
#[arg(short, long)]
sample: Option<String>,
/// The name of the new sample
#[arg(long)]
new_sample: String,
/// The name of the region to derive the subgraph from
#[arg(short, long)]
region: String,
/// Name of alternate path (not current) to use
#[arg(long)]
backbone: Option<String>,
},
/// Replace a sequence graph with subgraphs in the ranges of the specified coordinates
DeriveChunks {
/// The name of the collection to derive the subgraph from
#[arg(short, long)]
name: Option<String>,
/// The name of the parent sample
#[arg(short, long)]
sample: Option<String>,
/// The name of the new sample
#[arg(long)]
new_sample: String,
/// The name of the region to derive the subgraph from
#[arg(short, long)]
region: String,
/// Name of alternate path (not current) to use
#[arg(long)]
backbone: Option<String>,
/// Breakpoints to derive chunks from
#[arg(long)]
breakpoints: Option<String>,
/// The size of the chunks to derive
#[arg(long)]
chunk_size: Option<i64>,
},
#[command(
verbatim_doc_comment,
long_about = "Combine multiple sequence graphs into one. Example:
gen make-stitch --sample parent_sample --new-sample my_child_sample --regions chr1.2,chr1.3 --new-region spliced_chr1"
)]
MakeStitch {
/// The name of the collection to derive the subgraph from
#[arg(short, long)]
name: Option<String>,
/// The name of the parent sample
#[arg(short, long)]
sample: Option<String>,
/// The name of the new sample
#[arg(long)]
new_sample: String,
/// The names of the regions to combine
#[arg(long)]
regions: String,
/// The name of the new region
#[arg(long)]
new_region: String,
},
}
#[derive(Parser)]
#[command(version, about, long_about = None, arg_required_else_help(true))]
pub struct Cli {
/// The path to the database you wish to utilize
#[arg(short, long)]
pub db: Option<String>,
#[command(subcommand)]
pub command: Option<Commands>,
}
pub fn get_default_collection(conn: &OperationsConnection) -> String {
let mut stmt = conn
.prepare("select collection_name from defaults where id = 1")
.unwrap();
stmt.query_row((), |row| row.get(0))
.unwrap_or("default".to_string())
}