AlignmentCli.bend

bend-edit-distance@0.2.0.0 · 104 lines · 0 definitions

1import Base
2import bend-edit-distance@0.2.0.0/Cli.bend as CLI
3import bend-edit-distance@0.2.0.0/EdlibTypes.bend as E
4
5def read_mode(value: String) -> Maybe<&2, E.Mode>:
6  match value:
7    case "NW":
8      Some{E.NW{}}
9    case "SHW":
10      Some{E.SHW{}}
11    case "HW":
12      Some{E.HW{}}
13    case _:
14      None{}
15
16def read_task(value: String) -> Maybe<&2, E.Task>:
17  match value:
18    case "DISTANCE":
19      Some{E.Distance{}}
20    case "LOC":
21      Some{E.Locations{}}
22    case "PATH":
23      Some{E.Path{}}
24    case _:
25      None{}
26
27def read_format(value: String) -> Maybe<&2, E.CigarFormat>:
28  match value:
29    case "STANDARD":
30      Some{E.Standard{}}
31    case "EXTENDED":
32      Some{E.Extended{}}
33    case _:
34      None{}
35
36def bounded_threshold(value: Nat, valid: Bool) -> Maybe<&2, E.Threshold>:
37  match valid:
38    case True{}:
39      Some{E.MaxDistance{value}}
40    case False{}:
41      None{}
42
43def negative_threshold(magnitude: Nat, valid: Bool) -> Maybe<&2, E.Threshold>:
44  match magnitude valid:
45    case _ False{}:
46      None{}
47    case 0n True{}:
48      Some{E.MaxDistance{0n}}
49    case _ True{}:
50      Some{E.Unlimited{}}
51
52def read_threshold(value: String) -> Maybe<&2, E.Threshold>:
53  match value:
54    case SCon{'-', digits}:
55      do Maybe<&2, E.Threshold>:
56        +magnitude : Nat <- Nat.read(digits)
57        negative_threshold(magnitude, Nat.is_le(magnitude, 2147483648n))
58    case _:
59      do Maybe<&2, E.Threshold>:
60        +maximum : Nat <- Nat.read(value)
61        bounded_threshold(maximum, Nat.is_le(maximum, 2147483647n))
62
63def read_pairs(value: String) -> Maybe<&2, List<&2, E.EqualityPair>>:
64  match value:
65    case SNil{}:
66      Some{Nil{}}
67    case SCon{_, SNil{}}:
68      None{}
69    case SCon{first, SCon{second, tail}}:
70      do Maybe<&2, List<&2, E.EqualityPair>>:
71        pairs : List<&2, E.EqualityPair> <- read_pairs(tail)
72        return E.EqualityPair{first, second} <> pairs
73
74def parse(mode: String, task: String, threshold: String, pairs: String,
75          format: String) -> Maybe<&2, E.AlignConfig>:
76  do Maybe<&2, E.AlignConfig>:
77    m : E.Mode <- read_mode(mode)
78    t : E.Task <- read_task(task)
79    k : E.Threshold <- read_threshold(threshold)
80    eq : List<&2, E.EqualityPair> <- read_pairs(pairs)
81    c : E.CigarFormat <- read_format(format)
82    return E.AlignConfig{m, t, k, eq, c}
83
84def respond(~align: String -> String -> E.AlignConfig -> E.AlignResult,
85            query: String, target: String, config: Maybe<&2, E.AlignConfig>,
86            status: CLI.InputStatus) -> IO(Unit):
87  match config status:
88    case None{} _:
89      IO.die(Unit, 2, "Invalid alignment configuration")
90    case Some{value} CLI.Supported{}:
91      IO.print(E.result_show(align(query, target, value)))
92    case _ CLI.NonAscii{}:
93      IO.die(Unit, 2, "Sequences and equality pairs must contain only non-NUL ASCII characters")
94    case _ CLI.TooLong{}:
95      IO.die(Unit, 2, "Sequence or equality-pair length exceeds maximum of 4096 ASCII characters")
96
97def run(~align: String -> String -> E.AlignConfig -> E.AlignResult,
98        args: List<String>) -> IO(Unit):
99  match args:
100    case _ <> "--config" <> mode <> task <> threshold <> +pairs <> format <> +query <> +target <> Nil{}:
101      respond(~align, query, target, parse(mode, task, threshold, pairs, format),
102        CLI.combine(CLI.validate(query, target), CLI.scan(pairs, CLI.max_sequence_length())))
103    case _:
104      IO.die(Unit, 2, "Expected QUERY TARGET or --config MODE TASK K PAIRS CIGAR QUERY TARGET")