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
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
use std::path::PathBuf;
use clap::{Args, Parser, Subcommand, ValueEnum};
use sbol::{ExternalValidationMode, RdfFormat, Severity};
#[derive(Parser)]
#[command(
name = "sbol",
version = env!("SBOL_VERSION_FULL"),
about = "Command-line tool for SBOL 3 documents",
propagate_version = true
)]
pub(crate) struct Cli {
/// When to colorize output. `auto` colorizes the streams that are
/// TTYs and `NO_COLOR` is unset.
#[arg(long, value_enum, default_value_t = ColorMode::Auto, global = true)]
pub(crate) color: ColorMode,
#[command(subcommand)]
pub(crate) command: Command,
}
#[derive(Subcommand)]
pub(crate) enum Command {
/// Validate an SBOL 3 document against the spec.
Validate(ValidateArgs),
/// Convert an SBOL 3 document between RDF serializations.
Convert(ConvertArgs),
/// Upgrade an SBOL 2 RDF document to SBOL 3.
Upgrade(UpgradeArgs),
/// Downgrade an SBOL 3 RDF document to SBOL 2.
Downgrade(DowngradeArgs),
/// Import a GenBank file (.gb / .gbk) into SBOL 3.
ImportGenbank(ImportGenbankArgs),
/// Import a FASTA file (.fasta / .fa / .fna / .faa) into SBOL 3.
ImportFasta(ImportFastaArgs),
/// Inspect the built-in validation rule catalog.
#[command(subcommand)]
Rules(RulesCommand),
/// Manage cached extension ontologies (NCIT and others).
#[command(subcommand)]
Ontology(OntologyCommand),
}
#[derive(Subcommand)]
pub(crate) enum RulesCommand {
/// List validation rules, their implementation status, and spec section.
List(RulesListArgs),
}
#[derive(Subcommand)]
pub(crate) enum OntologyCommand {
/// Download and build a named ontology extension into the cache.
Install(OntologyInstallArgs),
/// List installed ontology extensions.
List,
/// Print the cache directory path.
Path,
/// Remove an installed ontology extension.
Remove(OntologyRemoveArgs),
/// Re-hash an installed extension's TSV and compare against its
/// manifest. Errors if the extension is missing or tampered with.
Verify(OntologyVerifyArgs),
}
#[derive(Args)]
pub(crate) struct OntologyInstallArgs {
/// Built-in ontology to install. Currently: `ncit`.
pub(crate) name: String,
/// Re-download and rebuild even if already installed.
#[arg(long)]
pub(crate) force: bool,
}
#[derive(Args)]
pub(crate) struct OntologyRemoveArgs {
/// Cache entry name (e.g. `ncit`).
pub(crate) name: String,
}
#[derive(Args)]
pub(crate) struct OntologyVerifyArgs {
/// Cache entry name to verify. If omitted, every installed
/// extension is verified.
pub(crate) name: Option<String>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, ValueEnum)]
pub(crate) enum OutputFormat {
Text,
Json,
#[cfg(feature = "sarif")]
Sarif,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, ValueEnum)]
pub(crate) enum RulesFormat {
Text,
Json,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, ValueEnum)]
pub(crate) enum ColorMode {
Auto,
Always,
Never,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, ValueEnum)]
pub(crate) enum RuleStatusFilter {
Error,
Warning,
Configurable,
MachineUncheckable,
Unimplemented,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, ValueEnum)]
pub(crate) enum RdfFormatArg {
Turtle,
Rdfxml,
Jsonld,
Ntriples,
}
impl From<RdfFormatArg> for RdfFormat {
fn from(value: RdfFormatArg) -> Self {
match value {
RdfFormatArg::Turtle => RdfFormat::Turtle,
RdfFormatArg::Rdfxml => RdfFormat::RdfXml,
RdfFormatArg::Jsonld => RdfFormat::JsonLd,
RdfFormatArg::Ntriples => RdfFormat::NTriples,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, ValueEnum)]
pub(crate) enum SeverityArg {
Warning,
Error,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, ValueEnum)]
pub(crate) enum ExternalModeArg {
Off,
Provided,
Allowed,
}
impl From<ExternalModeArg> for ExternalValidationMode {
fn from(value: ExternalModeArg) -> Self {
match value {
ExternalModeArg::Off => ExternalValidationMode::Off,
ExternalModeArg::Provided => ExternalValidationMode::ProvidedOnly,
ExternalModeArg::Allowed => ExternalValidationMode::ExternalAllowed,
}
}
}
impl From<SeverityArg> for Severity {
fn from(value: SeverityArg) -> Self {
match value {
SeverityArg::Warning => Severity::Warning,
SeverityArg::Error => Severity::Error,
}
}
}
#[derive(Args)]
pub(crate) struct ValidateArgs {
/// Path to an SBOL 3 document. Format is inferred from the extension —
/// `.ttl` (Turtle), `.rdf` (RDF/XML), `.jsonld` (JSON-LD), or `.nt`
/// (N-Triples).
pub(crate) path: PathBuf,
/// Output format.
#[arg(long, value_enum, default_value_t = OutputFormat::Text)]
pub(crate) format: OutputFormat,
/// Destination for output. Use `-` for stdout.
#[arg(long, default_value = "-")]
pub(crate) output: String,
/// Suppress diagnostics for these rule IDs (e.g. `--allow sbol3-10502`).
#[arg(long = "allow", value_name = "RULE_ID")]
pub(crate) allow: Vec<String>,
/// Promote these rule IDs to error severity.
#[arg(long = "deny", value_name = "RULE_ID")]
pub(crate) deny: Vec<String>,
/// Demote these rule IDs to warning severity.
#[arg(long = "warn", value_name = "RULE_ID")]
pub(crate) warn: Vec<String>,
/// Floor on the severity of any emitted issue.
#[arg(long, value_enum)]
pub(crate) severity_floor: Option<SeverityArg>,
/// Ceiling on the severity of any emitted issue.
#[arg(long, value_enum)]
pub(crate) severity_ceiling: Option<SeverityArg>,
/// Treat warnings as errors (alias for `--severity-floor error`).
#[arg(long)]
pub(crate) treat_warnings_as_errors: bool,
/// Use `Document::check_complete` semantics: any rule with partial
/// coverage causes exit code 3.
#[arg(long)]
pub(crate) treat_partial_as_errors: bool,
/// In text output, print a coverage summary after the issues.
#[arg(long)]
pub(crate) show_coverage: bool,
/// Whether to resolve external documents and content.
#[arg(long, value_enum, default_value_t = ExternalModeArg::Off)]
pub(crate) external_mode: ExternalModeArg,
/// Filesystem roots from which external Attachment / Model / TopLevel
/// references may be resolved.
#[arg(long = "resolve-documents", value_name = "DIR")]
pub(crate) resolve_documents: Vec<PathBuf>,
/// Filesystem roots for Attachment / Model byte content.
#[arg(long = "resolve-content", value_name = "DIR")]
pub(crate) resolve_content: Vec<PathBuf>,
/// Cache directory required by `--external-mode allowed` when the
/// `http-resolver` feature is built in.
#[arg(long)]
pub(crate) cache_dir: Option<PathBuf>,
/// Layer an installed runtime ontology extension on top of the bundled
/// facts for this validation run. Pass the cache entry name (e.g.
/// `--ontology ncit`). Repeatable; later extensions override earlier
/// ones on conflict.
#[arg(long = "ontology", value_name = "NAME")]
pub(crate) ontology: Vec<String>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, ValueEnum)]
pub(crate) enum UpgradeReportFormat {
None,
Text,
Json,
}
#[derive(Args)]
pub(crate) struct UpgradeArgs {
/// Path to an SBOL 2 RDF document. Input format is inferred from the
/// extension — `.ttl` (Turtle), `.rdf` / `.xml` (RDF/XML), `.jsonld`
/// (JSON-LD), or `.nt` (N-Triples). Use `--from` to override.
pub(crate) path: PathBuf,
/// Override the input format inference. Useful for SBOL 2 files
/// distributed with non-standard extensions.
#[arg(long, value_enum, value_name = "FORMAT")]
pub(crate) from: Option<RdfFormatArg>,
/// Target SBOL 3 RDF serialization. If omitted, inferred from
/// `--output`'s extension.
#[arg(long, value_enum, value_name = "FORMAT")]
pub(crate) to: Option<RdfFormatArg>,
/// Destination path for the SBOL 3 output. Use `-` (the default) for
/// stdout; in that case `--to` is required.
#[arg(long, short = 'o', default_value = "-")]
pub(crate) output: String,
/// Default `hasNamespace` value for top-level objects whose namespace
/// cannot be derived from the input. Without this flag, such objects
/// fall back to the URL scheme+host or, failing that, omit
/// `hasNamespace` entirely.
#[arg(long, value_name = "IRI")]
pub(crate) namespace: Option<String>,
/// Where to write the conversion report.
#[arg(long, value_enum, default_value_t = UpgradeReportFormat::None)]
pub(crate) report: UpgradeReportFormat,
/// Exit with status 1 if any conversion warnings were produced.
#[arg(long)]
pub(crate) strict: bool,
/// Run SBOL 3 validation on the converted document and fold the result
/// into the exit code: code 1 if validation finds errors.
#[arg(long)]
pub(crate) validate: bool,
}
#[derive(Args)]
pub(crate) struct DowngradeArgs {
/// Path to an SBOL 3 RDF document. Input format is inferred from
/// the extension (`.ttl`, `.rdf` / `.xml`, `.jsonld`, `.nt`).
pub(crate) path: PathBuf,
/// Override the input format inference. Useful for SBOL 3 RDF files
/// distributed with non-standard extensions.
#[arg(long, value_enum, value_name = "FORMAT")]
pub(crate) from: Option<RdfFormatArg>,
/// Target SBOL 2 RDF serialization. If omitted, inferred from
/// `--output`'s extension.
#[arg(long, value_enum, value_name = "FORMAT")]
pub(crate) to: Option<RdfFormatArg>,
/// Destination path. Use `-` (the default) for stdout; in that
/// case `--to` is required.
#[arg(long, short = 'o', default_value = "-")]
pub(crate) output: String,
/// Version string assigned to top-level objects whose source
/// document didn't carry `backport:sbol2version`. Omit to leave
/// such subjects unversioned (SBOL 2 makes `sbol2:version`
/// optional); pass `--default-version 1` to match the libSBOLj /
/// SynBioHub convention of always emitting one.
#[arg(long, value_name = "VERSION")]
pub(crate) default_version: Option<String>,
/// Validate the downgrade by round-tripping the produced SBOL 2
/// back up through `sbol::upgrade` and running SBOL 3 validation
/// on the result. There is no native SBOL 2 validator in this
/// workspace, so this round-trip is the proxy for structural
/// correctness. Exit code 1 on validation errors.
#[arg(long)]
pub(crate) validate: bool,
/// Exit with status 1 if any downgrade warnings were produced.
#[arg(long)]
pub(crate) strict: bool,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, ValueEnum)]
pub(crate) enum FastaAlphabetArg {
Dna,
Rna,
Protein,
}
impl From<FastaAlphabetArg> for sbol_fasta::Alphabet {
fn from(value: FastaAlphabetArg) -> Self {
match value {
FastaAlphabetArg::Dna => sbol_fasta::Alphabet::Dna,
FastaAlphabetArg::Rna => sbol_fasta::Alphabet::Rna,
FastaAlphabetArg::Protein => sbol_fasta::Alphabet::Protein,
}
}
}
#[derive(Args)]
pub(crate) struct ImportFastaArgs {
/// Path to a FASTA file (`.fasta` / `.fa` / `.fna` / `.faa`).
pub(crate) path: PathBuf,
/// Namespace IRI under which the resulting SBOL 3 top-level
/// objects will be rooted. Required because FASTA carries no
/// namespace concept.
#[arg(long, short = 'n', value_name = "IRI")]
pub(crate) namespace: String,
/// Override alphabet auto-detection. Pass when the sequence text
/// is ambiguous (e.g. a short peptide composed only of A/C/G/T
/// letters that would otherwise be misclassified as DNA).
#[arg(long, value_enum, value_name = "ALPHABET")]
pub(crate) alphabet: Option<FastaAlphabetArg>,
/// Target SBOL 3 RDF serialization. If omitted, inferred from
/// `--output`'s extension.
#[arg(long, value_enum, value_name = "FORMAT")]
pub(crate) to: Option<RdfFormatArg>,
/// Destination path. Use `-` (the default) for stdout; in that
/// case `--to` is required.
#[arg(long, short = 'o', default_value = "-")]
pub(crate) output: String,
/// Run SBOL 3 validation on the converted document and fold the
/// result into the exit code: code 1 if validation finds errors.
#[arg(long)]
pub(crate) validate: bool,
/// Exit with status 1 if any import warnings were produced.
#[arg(long)]
pub(crate) strict: bool,
}
#[derive(Args)]
pub(crate) struct ImportGenbankArgs {
/// Path to a GenBank flat-file (`.gb` / `.gbk`). Mixed-case month
/// names in the LOCUS line (as emitted by SynBioHub) are tolerated.
pub(crate) path: PathBuf,
/// Namespace IRI under which the resulting SBOL 3 top-level
/// objects will be rooted. Required because GenBank carries no
/// namespace concept.
#[arg(long, short = 'n', value_name = "IRI")]
pub(crate) namespace: String,
/// Target SBOL 3 RDF serialization. If omitted, inferred from
/// `--output`'s extension.
#[arg(long, value_enum, value_name = "FORMAT")]
pub(crate) to: Option<RdfFormatArg>,
/// Destination path. Use `-` (the default) for stdout; in that
/// case `--to` is required.
#[arg(long, short = 'o', default_value = "-")]
pub(crate) output: String,
/// Run SBOL 3 validation on the converted document and fold the
/// result into the exit code: code 1 if validation finds errors.
#[arg(long)]
pub(crate) validate: bool,
/// Exit with status 1 if any import warnings were produced.
#[arg(long)]
pub(crate) strict: bool,
}
#[derive(Args)]
pub(crate) struct ConvertArgs {
/// Path to an SBOL 3 document. Input format is inferred from the
/// extension — `.ttl` (Turtle), `.rdf` (RDF/XML), `.jsonld` (JSON-LD),
/// or `.nt` (N-Triples).
pub(crate) path: PathBuf,
/// Target serialization. If omitted, inferred from `--output`'s
/// extension.
#[arg(long, value_enum, value_name = "FORMAT")]
pub(crate) to: Option<RdfFormatArg>,
/// Destination path. Use `-` (the default) for stdout; in that case
/// `--to` is required.
#[arg(long, short = 'o', default_value = "-")]
pub(crate) output: String,
}
#[derive(Args)]
pub(crate) struct RulesListArgs {
/// Output format.
#[arg(long, value_enum, default_value_t = RulesFormat::Text)]
pub(crate) format: RulesFormat,
/// Only show rules with this implementation status.
#[arg(long, value_enum, value_name = "STATUS")]
pub(crate) status: Option<RuleStatusFilter>,
/// Show full notes instead of truncating to fit one line per rule.
#[arg(long)]
pub(crate) full: bool,
}