domain/architecture/system/release/ops.bend

near-v-framework@0.7.0.0 · 163 lines · 0 definitions

1import Base
2import near-v-framework@0.7.0.0/domain/architecture/system/release/type.bend as Rl
3
4# ---- the hub's rules: a name is 12 to 64 characters of a-z, 0-9 and -; a version is four numbers ----
5def digits_only(cs: List<&2, Char>) -> Bool:
6  match cs:
7    case []:
8      True{}
9    case c <> rest:
10      Char.is_digit(c) && digits_only(rest)
11
12def all_numbers(xs: List<&2, String>) -> Bool:
13  match xs:
14    case []:
15      True{}
16    case +x <> rest:
17      Bool.not(String.is_empty(x)) && digits_only(String.to_list(x)) && all_numbers(rest)
18
19def name_chars(cs: List<&2, Char>) -> Bool:
20  match cs:
21    case []:
22      True{}
23    case +c <> rest:
24      (Char.is_lower(c) || Char.is_digit(c) || Char.is_eq(c, '-')) && name_chars(rest)
25
26def package_ok(p: Rl.Package) -> Bool:
27  match p:
28    case Rl.Package{+n, +v, _, _}:
29      Nat.is_ge(String.length(n), 12n) && Nat.is_le(String.length(n), 64n) && name_chars(String.to_list(n)) &&
30      Nat.is_eq(List.length(&2, String, String.split(v, '.')), 4n) && all_numbers(String.split(v, '.'))
31
32def packages_ok(ps: List<&2, Rl.Package>) -> Bool:
33  match ps:
34    case []:
35      True{}
36    case p <> rest:
37      package_ok(p) && packages_ok(rest)
38
39def packages(r: Rl.Release) -> List<&2, Rl.Package>:
40  match r:
41    case Rl.Release{_, ps, _}:
42      ps
43
44def plugins(r: Rl.Release) -> List<&2, Rl.Plugin>:
45  match r:
46    case Rl.Release{_, _, gs}:
47      gs
48
49def repository(r: Rl.Release) -> Rl.Repository:
50  match r:
51    case Rl.Release{repo, _, _}:
52      repo
53
54def is_public(r: Rl.Release) -> Bool:
55  match r:
56    case Rl.Release{repo, _, _}:
57      match repo:
58        case Rl.Repository{_, p}:
59          p
60
61# Every component any package ships.
62def shipped_components(ps: List<&2, Rl.Package>) -> List<&2, String>:
63  match ps:
64    case []:
65      []
66    case p <> rest:
67      match p:
68        case Rl.Package{_, _, _, cs}:
69          List.append(&2, String, cs, shipped_components(rest))
70
71# Every package's entry file.
72def entries(ps: List<&2, Rl.Package>) -> List<&2, String>:
73  match ps:
74    case []:
75      []
76    case p <> rest:
77      match p:
78        case Rl.Package{_, _, e, _}:
79          e <> entries(rest)
80
81# Every plugin's entry file.
82def plugin_entries(gs: List<&2, Rl.Plugin>) -> List<&2, String>:
83  match gs:
84    case []:
85      []
86    case g <> rest:
87      match g:
88        case Rl.Plugin{_, _, e, _, _}:
89          e <> plugin_entries(rest)
90
91# Every component any plugin ships.
92def plugin_components(gs: List<&2, Rl.Plugin>) -> List<&2, String>:
93  match gs:
94    case []:
95      []
96    case g <> rest:
97      match g:
98        case Rl.Plugin{_, _, _, cs, _}:
99          List.append(&2, String, cs, plugin_components(rest))
100
101# ---- the repository: Bend only, and no dead code ----
102# Bend, or a foreign effect in C or JavaScript (Bend's two lanes).
103def is_source(+f: String) -> Bool:
104  String.ends_with(f, ".bend") || String.ends_with(f, ".c") || String.ends_with(f, ".js")
105
106def base_name(f: String) -> String:
107  Maybe.default(&2, String, List.head(&2, String, List.reverse(&2, String, String.split(f, '/'))), "")
108
109# Every source file is reachable from an entry: a package's, or the system's own.
110def no_dead_code(fs: List<&2, String>, +reached: List<&2, String>) -> Bool:
111  match fs:
112    case []:
113      True{}
114    case +f <> rest:
115      (Bool.not(is_source(f)) || List.contains(~String, ~String.eq, reached, f)) && no_dead_code(rest, reached)
116
117# The first source file nothing reaches, "" when there is none.
118def dead(fs: List<&2, String>, +reached: List<&2, String>) -> String:
119  match fs:
120    case []:
121      ""
122    case +f <> rest:
123      Bool.pick(String, is_source(f) && Bool.not(List.contains(~String, ~String.eq, reached, f)), f, dead(rest, reached))
124
125# The first file that is not Bend, "" when there is none.
126# ---- the hub's path rule: every segment is [A-Za-z_][A-Za-z0-9_]* (the file name also ends in .bend, .c or
127# .js, or is a LICENSE), under 256 characters ----
128def name_char(+c: Char, first: Bool) -> Bool:
129  Char.is_lower(c) || Char.is_upper(c) || Char.is_eq(c, '_') || (Bool.not(first) && Char.is_digit(c))
130
131def name_chars_ok(cs: List<&2, Char>, first: Bool) -> Bool:
132  match cs:
133    case []:
134      Bool.not(first)
135    case +c <> rest:
136      name_char(c, first) && name_chars_ok(rest, False{})
137
138def stem(+f: String) -> String:
139  Bool.pick(String, String.ends_with(f, ".bend"), String.take(f, Nat.sub(String.length(f), 5n)),
140    Bool.pick(String, String.ends_with(f, ".js"), String.take(f, Nat.sub(String.length(f), 3n)),
141      Bool.pick(String, String.ends_with(f, ".c"), String.take(f, Nat.sub(String.length(f), 2n)), f)))
142
143def segments_ok(ss: List<&2, String>) -> Bool:
144  match ss:
145    case []:
146      True{}
147    case +s <> rest:
148      match rest:
149        case []:
150          String.eq(s, "LICENSE") || (is_source(s) && name_chars_ok(String.to_list(stem(s)), True{}))
151        case _ <> _:
152          name_chars_ok(String.to_list(s), True{}) && segments_ok(rest)
153
154def hub_path_ok(+f: String) -> Bool:
155  Nat.is_lt(String.length(f), 256n) && segments_ok(String.split(f, '/'))
156
157# The first file the hub would refuse, "" when there is none.
158def hub_refused(fs: List<&2, String>) -> String:
159  match fs:
160    case []:
161      ""
162    case +f <> rest:
163      Bool.pick(String, hub_path_ok(f), hub_refused(rest), f)