+1
.envrc
+1
.envrc
···
1
+
use flake
+24
README.md
+24
README.md
···
1
+
# sheetr
2
+
3
+
Monorepo for Sheetr and it's associated libraries.
4
+
5
+
> NOTE: Libraries under the [`/alicia/`](/alicia/) directory will eventually be
6
+
pulled out into their own repo when ready. As of current they are unstable and
7
+
rely/will rely on path dependencies within this monorepo.
8
+
9
+
## Development
10
+
Sheetr provides a `flake.nix` for use with the Nix package manager. This can be
11
+
installed independently from your existing package manager as shown
12
+
[here](https://nixos.org/download/). It is very possible (and fairly easy) to
13
+
develop this project without Nix, however Nix helps ensure you are using the
14
+
exact same devenv as everyone else.
15
+
16
+
### Generating code from Lexicons
17
+
A useful helper is provided via the Nix flake.
18
+
```bash
19
+
nix run .#lexgen
20
+
21
+
# without Nix
22
+
cd shared
23
+
gleam run -m alicia/lexgen -- --dir=../lexicons
24
+
```
+32
TODO.md
+32
TODO.md
···
1
+
# alicia
2
+
3
+
## alicia_lexgen
4
+
5
+
- [ ] Codegen
6
+
- [ ] 100% spec coverage
7
+
- [x] Coverage for Sheetr lexicons
8
+
- [ ] Ability to fetch lexicon schemas from internet/git
9
+
- [ ] Possibly move parsing/gen into an `alicia_lex` or `alicia_lexicon` package
10
+
and have `alicia_lexgen` as a pure CLI package.
11
+
- [ ] Extract codegen library or use an external one.
12
+
13
+
## alicia_api
14
+
15
+
> Waiting on `lexgen`
16
+
17
+
- [ ] Generated code
18
+
- [ ] `com.atproto.*`
19
+
- [ ] `app.bsky.*`
20
+
21
+
## alicia_identity
22
+
23
+
## alicia_oauth
24
+
25
+
## xrpc
26
+
Possibly have XRPC client libraries which are thin wrappers around existing HTTP
27
+
clients like `httpc` or JavaScript's `fetch`. Could also provide helper package
28
+
for `lustre` users that provides functions that return `effect`s instead/as well.
29
+
30
+
## crypto/common/syntax/repo
31
+
All might be needed, unsure as to the exact architecture that will be used as of
32
+
current so can't say for certain.
+24
alicia/identity/README.md
+24
alicia/identity/README.md
···
1
+
# alicia_identity
2
+
3
+
[](https://hex.pm/packages/alicia_identity)
4
+
[](https://hexdocs.pm/alicia_identity/)
5
+
6
+
```sh
7
+
gleam add alicia_identity@1
8
+
```
9
+
```gleam
10
+
import alicia_identity
11
+
12
+
pub fn main() -> Nil {
13
+
// TODO: An example of the project in use
14
+
}
15
+
```
16
+
17
+
Further documentation can be found at <https://hexdocs.pm/alicia_identity>.
18
+
19
+
## Development
20
+
21
+
```sh
22
+
gleam run # Run the project
23
+
gleam test # Run the tests
24
+
```
+19
alicia/identity/gleam.toml
+19
alicia/identity/gleam.toml
···
1
+
name = "alicia_identity"
2
+
version = "1.0.0"
3
+
4
+
# Fill out these fields if you intend to generate HTML documentation or publish
5
+
# your project to the Hex package manager.
6
+
#
7
+
# description = ""
8
+
# licences = ["Apache-2.0"]
9
+
# repository = { type = "github", user = "", repo = "" }
10
+
# links = [{ title = "Website", href = "" }]
11
+
#
12
+
# For a full reference of all the available options, you can have a look at
13
+
# https://gleam.run/writing-gleam/gleam-toml/.
14
+
15
+
[dependencies]
16
+
gleam_stdlib = ">= 0.44.0 and < 2.0.0"
17
+
18
+
[dev-dependencies]
19
+
gleeunit = ">= 1.0.0 and < 2.0.0"
+11
alicia/identity/manifest.toml
+11
alicia/identity/manifest.toml
···
1
+
# This file was generated by Gleam
2
+
# You typically do not need to edit this file
3
+
4
+
packages = [
5
+
{ name = "gleam_stdlib", version = "0.67.1", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "6CE3E4189A8B8EC2F73AB61A2FBDE49F159D6C9C61C49E3B3082E439F260D3D0" },
6
+
{ name = "gleeunit", version = "1.9.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "DA9553CE58B67924B3C631F96FE3370C49EB6D6DC6B384EC4862CC4AAA718F3C" },
7
+
]
8
+
9
+
[requirements]
10
+
gleam_stdlib = { version = ">= 0.44.0 and < 2.0.0" }
11
+
gleeunit = { version = ">= 1.0.0 and < 2.0.0" }
alicia/identity/src/alicia/identity.gleam
alicia/identity/src/alicia/identity.gleam
This is a binary file and will not be displayed.
+13
alicia/identity/test/alicia_identity_test.gleam
+13
alicia/identity/test/alicia_identity_test.gleam
+24
alicia/lexgen/README.md
+24
alicia/lexgen/README.md
···
1
+
# alicia_lexgen
2
+
3
+
[](https://hex.pm/packages/alicia_lexgen)
4
+
[](https://hexdocs.pm/alicia_lexgen/)
5
+
6
+
```sh
7
+
gleam add alicia_lexgen@1
8
+
```
9
+
```gleam
10
+
import alicia_lexgen
11
+
12
+
pub fn main() -> Nil {
13
+
// TODO: An example of the project in use
14
+
}
15
+
```
16
+
17
+
Further documentation can be found at <https://hexdocs.pm/alicia_lexgen>.
18
+
19
+
## Development
20
+
21
+
```sh
22
+
gleam run # Run the project
23
+
gleam test # Run the tests
24
+
```
+25
alicia/lexgen/gleam.toml
+25
alicia/lexgen/gleam.toml
···
1
+
name = "alicia_lexgen"
2
+
version = "1.0.0"
3
+
4
+
# Fill out these fields if you intend to generate HTML documentation or publish
5
+
# your project to the Hex package manager.
6
+
#
7
+
# description = ""
8
+
# licences = ["Apache-2.0"]
9
+
# repository = { type = "github", user = "", repo = "" }
10
+
# links = [{ title = "Website", href = "" }]
11
+
#
12
+
# For a full reference of all the available options, you can have a look at
13
+
# https://gleam.run/writing-gleam/gleam-toml/.
14
+
15
+
[dependencies]
16
+
gleam_stdlib = ">= 0.44.0 and < 2.0.0"
17
+
simplifile = ">= 2.3.2 and < 3.0.0"
18
+
gleam_json = ">= 3.1.0 and < 4.0.0"
19
+
glint = ">= 1.2.1 and < 2.0.0"
20
+
argv = ">= 1.0.2 and < 2.0.0"
21
+
snag = ">= 1.2.0 and < 2.0.0"
22
+
pprint = ">= 1.0.6 and < 2.0.0"
23
+
24
+
[dev-dependencies]
25
+
gleeunit = ">= 1.0.0 and < 2.0.0"
+28
alicia/lexgen/manifest.toml
+28
alicia/lexgen/manifest.toml
···
1
+
# This file was generated by Gleam
2
+
# You typically do not need to edit this file
3
+
4
+
packages = [
5
+
{ name = "argv", version = "1.0.2", build_tools = ["gleam"], requirements = [], otp_app = "argv", source = "hex", outer_checksum = "BA1FF0929525DEBA1CE67256E5ADF77A7CDDFE729E3E3F57A5BDCAA031DED09D" },
6
+
{ name = "filepath", version = "1.1.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "filepath", source = "hex", outer_checksum = "B06A9AF0BF10E51401D64B98E4B627F1D2E48C154967DA7AF4D0914780A6D40A" },
7
+
{ name = "glam", version = "2.0.3", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "glam", source = "hex", outer_checksum = "237C2CE218A2A0A5D46D625F8EF5B78F964BC91018B78D692B17E1AB84295229" },
8
+
{ name = "gleam_community_ansi", version = "1.4.3", build_tools = ["gleam"], requirements = ["gleam_community_colour", "gleam_regexp", "gleam_stdlib"], otp_app = "gleam_community_ansi", source = "hex", outer_checksum = "8A62AE9CC6EA65BEA630D95016D6C07E4F9973565FA3D0DE68DC4200D8E0DD27" },
9
+
{ name = "gleam_community_colour", version = "2.0.2", build_tools = ["gleam"], requirements = ["gleam_json", "gleam_stdlib"], otp_app = "gleam_community_colour", source = "hex", outer_checksum = "E34DD2C896AC3792151EDA939DA435FF3B69922F33415ED3C4406C932FBE9634" },
10
+
{ name = "gleam_json", version = "3.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_json", source = "hex", outer_checksum = "44FDAA8847BE8FC48CA7A1C089706BD54BADCC4C45B237A992EDDF9F2CDB2836" },
11
+
{ name = "gleam_regexp", version = "1.1.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_regexp", source = "hex", outer_checksum = "9C215C6CA84A5B35BB934A9B61A9A306EC743153BE2B0425A0D032E477B062A9" },
12
+
{ name = "gleam_stdlib", version = "0.67.1", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "6CE3E4189A8B8EC2F73AB61A2FBDE49F159D6C9C61C49E3B3082E439F260D3D0" },
13
+
{ name = "gleeunit", version = "1.9.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "DA9553CE58B67924B3C631F96FE3370C49EB6D6DC6B384EC4862CC4AAA718F3C" },
14
+
{ name = "glint", version = "1.2.1", build_tools = ["gleam"], requirements = ["gleam_community_ansi", "gleam_community_colour", "gleam_stdlib", "snag"], otp_app = "glint", source = "hex", outer_checksum = "2214C7CEFDE457CEE62140C3D4899B964E05236DA74E4243DFADF4AF29C382BB" },
15
+
{ name = "pprint", version = "1.0.6", build_tools = ["gleam"], requirements = ["glam", "gleam_stdlib"], otp_app = "pprint", source = "hex", outer_checksum = "4E9B34AE03B2E81D60F230B9BAF1792BE1AC37AFB5564B8DEBEE56BAEC866B7D" },
16
+
{ name = "simplifile", version = "2.3.2", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "E049B4DACD4D206D87843BCF4C775A50AE0F50A52031A2FFB40C9ED07D6EC70A" },
17
+
{ name = "snag", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "snag", source = "hex", outer_checksum = "274F41D6C3ECF99F7686FDCE54183333E41D2C1CA5A3A673F9A8B2C7A4401077" },
18
+
]
19
+
20
+
[requirements]
21
+
argv = { version = ">= 1.0.2 and < 2.0.0" }
22
+
gleam_json = { version = ">= 3.1.0 and < 4.0.0" }
23
+
gleam_stdlib = { version = ">= 0.44.0 and < 2.0.0" }
24
+
gleeunit = { version = ">= 1.0.0 and < 2.0.0" }
25
+
glint = { version = ">= 1.2.1 and < 2.0.0" }
26
+
pprint = { version = ">= 1.0.6 and < 2.0.0" }
27
+
simplifile = { version = ">= 2.3.2 and < 3.0.0" }
28
+
snag = { version = ">= 1.2.0 and < 2.0.0" }
+12
alicia/lexgen/src/alicia/lexgen.gleam
+12
alicia/lexgen/src/alicia/lexgen.gleam
···
1
+
import alicia/lexgen/cli
2
+
import argv
3
+
import glint
4
+
5
+
pub fn main() {
6
+
glint.new()
7
+
|> glint.with_name("alicia/lexgen")
8
+
|> glint.pretty_help(glint.default_pretty_help())
9
+
|> glint.add(at: [], do: cli.run())
10
+
|> glint.add(at: ["check"], do: cli.check())
11
+
|> glint.run(argv.load().arguments)
12
+
}
+212
alicia/lexgen/src/alicia/lexgen/cli.gleam
+212
alicia/lexgen/src/alicia/lexgen/cli.gleam
···
1
+
import alicia/lexgen/codegen
2
+
import alicia/lexgen/lexicon
3
+
import gleam/dict
4
+
import gleam/int
5
+
import gleam/io
6
+
import gleam/list
7
+
import gleam/result
8
+
import gleam/string
9
+
import glint
10
+
import pprint
11
+
import simplifile
12
+
import snag
13
+
14
+
fn dir_flag() -> glint.Flag(String) {
15
+
glint.string_flag("dir")
16
+
|> glint.flag_default("./lexicons")
17
+
|> glint.flag_help(
18
+
"Folder that alicia/lexgen will search for lexicon definitions.",
19
+
)
20
+
}
21
+
22
+
fn verbose_flag() -> glint.Flag(Bool) {
23
+
glint.bool_flag("verbose")
24
+
|> glint.flag_default(False)
25
+
|> glint.flag_help("Whether verbose command output should be enabled.")
26
+
}
27
+
28
+
fn parse_lexicon(
29
+
contents: String,
30
+
verbose: Bool,
31
+
) -> Result(lexicon.Lexicon, snag.Snag) {
32
+
let res =
33
+
lexicon.parse(contents)
34
+
|> snag.replace_error("Unable to parse lexicon")
35
+
36
+
case verbose {
37
+
True ->
38
+
result.map(res, fn(lexicon) {
39
+
io.println("Parsed lexicon with NSID " <> lexicon.id)
40
+
lexicon
41
+
})
42
+
False -> res
43
+
}
44
+
}
45
+
46
+
fn read_lexicon(path: String, verbose: Bool) -> Result(String, snag.Snag) {
47
+
case verbose {
48
+
True -> io.println("Found " <> path)
49
+
False -> Nil
50
+
}
51
+
simplifile.read(path)
52
+
|> snag.replace_error("Unable to read lexicon " <> path)
53
+
}
54
+
55
+
fn read_lexicons(dir: String, verbose: Bool) -> Result(List(String), snag.Snag) {
56
+
use files_and_folders <- result.try(
57
+
simplifile.read_directory(dir)
58
+
|> snag.replace_error("Unable to read directory " <> dir),
59
+
)
60
+
Ok(
61
+
list.map(files_and_folders, fn(file_or_folder) {
62
+
let path = dir <> "/" <> file_or_folder
63
+
case simplifile.is_file(path) {
64
+
Ok(False) -> read_lexicons(path, verbose)
65
+
Ok(True) -> [read_lexicon(path, verbose)] |> result.all
66
+
Error(_) ->
67
+
[
68
+
Error(snag.new(
69
+
"Failed to determine whether " <> path <> " is a file or folder.",
70
+
)),
71
+
]
72
+
|> result.all
73
+
}
74
+
})
75
+
|> result.all
76
+
|> result.map(list.flatten),
77
+
)
78
+
|> result.flatten
79
+
}
80
+
81
+
fn is_upper(string: String) -> Bool {
82
+
case string {
83
+
"A"
84
+
| "B"
85
+
| "C"
86
+
| "D"
87
+
| "E"
88
+
| "F"
89
+
| "G"
90
+
| "H"
91
+
| "I"
92
+
| "J"
93
+
| "K"
94
+
| "L"
95
+
| "M"
96
+
| "N"
97
+
| "O"
98
+
| "P"
99
+
| "Q"
100
+
| "R"
101
+
| "S"
102
+
| "T"
103
+
| "U"
104
+
| "V"
105
+
| "W"
106
+
| "X"
107
+
| "Y"
108
+
| "Z" -> True
109
+
_ -> False
110
+
}
111
+
}
112
+
113
+
fn to_snake_case(string: String) -> String {
114
+
string
115
+
|> string.to_graphemes
116
+
|> list.map(fn(letter) {
117
+
case is_upper(letter) {
118
+
True -> "_" <> string.lowercase(letter)
119
+
False -> letter
120
+
}
121
+
})
122
+
|> string.concat
123
+
}
124
+
125
+
fn generate_module(
126
+
lexicon: lexicon.Lexicon,
127
+
) -> Result(#(String, codegen.Codegen), snag.Snag) {
128
+
let split_nsid =
129
+
lexicon.id
130
+
|> string.split(".")
131
+
use last <- result.try(
132
+
list.last(split_nsid)
133
+
|> snag.replace_error("Provided NSID was empty"),
134
+
)
135
+
let module_path =
136
+
string.join(
137
+
split_nsid
138
+
|> list.reverse
139
+
|> list.drop(1)
140
+
|> list.reverse
141
+
|> list.map(to_snake_case),
142
+
"/",
143
+
)
144
+
// TODO: When we have a token, create a type with the name of the preceeding
145
+
// part of the NSID with the token as a variant. For example:
146
+
// `com.example.colour.red` would still be placed at
147
+
// `com/example/colour/colour.gleam`, but would generate the code:
148
+
// ```gleam
149
+
// pub type Colour {
150
+
// Red
151
+
//}
152
+
// ```
153
+
// instead of:
154
+
// ```gleam
155
+
// pub type Red {
156
+
// Red
157
+
//}
158
+
// ```
159
+
let type_name = case last |> string.to_graphemes {
160
+
[first, ..rest] ->
161
+
case is_upper(first) {
162
+
True -> last
163
+
False -> string.uppercase(first) <> string.concat(rest)
164
+
}
165
+
[] -> ""
166
+
}
167
+
let module =
168
+
codegen.new()
169
+
|> codegen.add_type(type_name, variants: [codegen.variant(type_name, [])])
170
+
Ok(#(module_path, module))
171
+
}
172
+
173
+
fn unique_or_merge(list: List(#(a, b)), merge: fn(b, b) -> b) -> List(#(a, b)) {
174
+
list.fold(list, dict.new(), fn(acc, item) {
175
+
let #(k, v) = item
176
+
case dict.get(acc, k) {
177
+
Ok(existing) -> dict.insert(acc, k, merge(v, existing))
178
+
Error(_) -> dict.insert(acc, k, v)
179
+
}
180
+
})
181
+
|> dict.to_list
182
+
}
183
+
184
+
pub fn run() -> glint.Command(Result(Nil, snag.Snag)) {
185
+
use <- glint.command_help("Generates Gleam code from lexicon definitions")
186
+
use dir <- glint.flag(dir_flag())
187
+
use verbose <- glint.flag(verbose_flag())
188
+
use _, _, flags <- glint.command()
189
+
// dir_flag and verbose_flag supply defaults so asserting here is safe
190
+
let assert Ok(dir) = dir(flags)
191
+
let assert Ok(verbose) = verbose(flags)
192
+
io.println("Reading lexicons from " <> dir)
193
+
use lexicons <- result.try(read_lexicons(dir, verbose))
194
+
io.println("Parsing " <> int.to_string(list.length(lexicons)) <> " lexicons")
195
+
use lexicons <- result.try(
196
+
list.map(lexicons, parse_lexicon(_, verbose))
197
+
|> result.all,
198
+
)
199
+
list.map(lexicons, generate_module)
200
+
|> result.all
201
+
|> result.map(unique_or_merge(_, codegen.merge))
202
+
|> pprint.debug
203
+
Ok(Nil)
204
+
}
205
+
206
+
pub fn check() -> glint.Command(Result(Nil, snag.Snag)) {
207
+
use <- glint.command_help(
208
+
"Checks if existing Gleam code matches lexicon definitions",
209
+
)
210
+
use _, _, _ <- glint.command()
211
+
Ok(Nil)
212
+
}
+85
alicia/lexgen/src/alicia/lexgen/codegen.gleam
+85
alicia/lexgen/src/alicia/lexgen/codegen.gleam
···
1
+
import gleam/list
2
+
import gleam/string
3
+
4
+
pub opaque type Codegen {
5
+
Codegen(
6
+
module_comment: String,
7
+
imports: List(String),
8
+
types: List(String),
9
+
functions: List(String),
10
+
)
11
+
}
12
+
13
+
pub fn new() -> Codegen {
14
+
Codegen(
15
+
module_comment: "//// Module generated by alicia/lexgen. DO NOT EDIT.",
16
+
imports: [],
17
+
types: [],
18
+
functions: [],
19
+
)
20
+
}
21
+
22
+
pub fn generate(codegen c: Codegen) -> String {
23
+
c.module_comment
24
+
<> "\n\n"
25
+
<> list.fold(c.imports, "", fn(acc, i) { acc <> i <> "\n" })
26
+
<> "\n"
27
+
<> list.fold(c.types, "", fn(acc, t) { acc <> t <> "\n" })
28
+
<> "\n"
29
+
<> list.fold(c.functions, "", fn(acc, f) { acc <> f <> "\n" })
30
+
}
31
+
32
+
pub fn variant(
33
+
name name: String,
34
+
fields fields: List(#(String, String)),
35
+
) -> String {
36
+
name
37
+
<> case fields {
38
+
[] -> ""
39
+
_ ->
40
+
"("
41
+
<> list.fold(fields, "", fn(acc, field) {
42
+
acc
43
+
<> case field.0 {
44
+
"" -> field.1
45
+
label -> label <> ": " <> field.1
46
+
}
47
+
<> ", "
48
+
})
49
+
// remove last `, `
50
+
|> string.drop_end(2)
51
+
<> ")"
52
+
}
53
+
<> "\n"
54
+
}
55
+
56
+
/// types added in reverse order
57
+
pub fn add_type(
58
+
codegen c: Codegen,
59
+
name name: String,
60
+
variants variants: List(String),
61
+
) -> Codegen {
62
+
let code =
63
+
"pub type "
64
+
<> name
65
+
<> case variants {
66
+
[] -> ""
67
+
_ ->
68
+
" {\n"
69
+
<> list.fold(variants, "", fn(acc, variant) { acc <> " " <> variant })
70
+
<> "}\n"
71
+
}
72
+
Codegen(..c, types: [code, ..c.types])
73
+
}
74
+
75
+
pub fn merge(left: Codegen, right: Codegen) -> Codegen {
76
+
Codegen(
77
+
module_comment: case left.module_comment == right.module_comment {
78
+
True -> left.module_comment
79
+
False -> left.module_comment <> "\n" <> right.module_comment
80
+
},
81
+
imports: list.append(left.imports, right.imports) |> list.unique,
82
+
types: list.append(left.types, right.types) |> list.unique,
83
+
functions: list.append(left.functions, right.functions) |> list.unique,
84
+
)
85
+
}
+47
alicia/lexgen/src/alicia/lexgen/lexicon.gleam
+47
alicia/lexgen/src/alicia/lexgen/lexicon.gleam
···
1
+
import gleam/dict
2
+
import gleam/dynamic/decode
3
+
import gleam/json
4
+
import gleam/option
5
+
6
+
pub fn parse(lexicon: String) -> Result(Lexicon, json.DecodeError) {
7
+
json.parse(lexicon, lexicon_decoder())
8
+
}
9
+
10
+
pub type Lexicon {
11
+
Lexicon(version: Int, id: String, defs: dict.Dict(String, LexiconDef))
12
+
}
13
+
14
+
fn lexicon_decoder() -> decode.Decoder(Lexicon) {
15
+
use version <- decode.field("lexicon", decode.int)
16
+
use id <- decode.field("id", decode.string)
17
+
use defs <- decode.field(
18
+
"defs",
19
+
decode.dict(decode.string, lexicon_def_decoder()),
20
+
)
21
+
decode.success(Lexicon(version:, id:, defs:))
22
+
}
23
+
24
+
pub type LexiconDef {
25
+
LexiconDef(
26
+
// TODO: maybe make type(s) here rather
27
+
// than a bunch of optional fields?
28
+
type_: String,
29
+
key: option.Option(String),
30
+
description: option.Option(String),
31
+
)
32
+
}
33
+
34
+
pub fn lexicon_def_decoder() -> decode.Decoder(LexiconDef) {
35
+
use type_ <- decode.field("type", decode.string)
36
+
use key <- decode.optional_field(
37
+
"key",
38
+
option.None,
39
+
decode.optional(decode.string),
40
+
)
41
+
use description <- decode.optional_field(
42
+
"description",
43
+
option.None,
44
+
decode.optional(decode.string),
45
+
)
46
+
decode.success(LexiconDef(type_:, key:, description:))
47
+
}
+13
alicia/lexgen/test/alicia_lexgen_test.gleam
+13
alicia/lexgen/test/alicia_lexgen_test.gleam
+24
backend/README.md
+24
backend/README.md
···
1
+
# sheetr_server
2
+
3
+
[](https://hex.pm/packages/sheetr_server)
4
+
[](https://hexdocs.pm/sheetr_server/)
5
+
6
+
```sh
7
+
gleam add sheetr_server@1
8
+
```
9
+
```gleam
10
+
import sheetr_server
11
+
12
+
pub fn main() -> Nil {
13
+
// TODO: An example of the project in use
14
+
}
15
+
```
16
+
17
+
Further documentation can be found at <https://hexdocs.pm/sheetr_server>.
18
+
19
+
## Development
20
+
21
+
```sh
22
+
gleam run # Run the project
23
+
gleam test # Run the tests
24
+
```
+19
backend/gleam.toml
+19
backend/gleam.toml
···
1
+
name = "sheetr_server"
2
+
version = "1.0.0"
3
+
4
+
# Fill out these fields if you intend to generate HTML documentation or publish
5
+
# your project to the Hex package manager.
6
+
#
7
+
# description = ""
8
+
# licences = ["Apache-2.0"]
9
+
# repository = { type = "github", user = "", repo = "" }
10
+
# links = [{ title = "Website", href = "" }]
11
+
#
12
+
# For a full reference of all the available options, you can have a look at
13
+
# https://gleam.run/writing-gleam/gleam-toml/.
14
+
15
+
[dependencies]
16
+
gleam_stdlib = ">= 0.44.0 and < 2.0.0"
17
+
18
+
[dev-dependencies]
19
+
gleeunit = ">= 1.0.0 and < 2.0.0"
+5
backend/src/sheetr_server.gleam
+5
backend/src/sheetr_server.gleam
+13
backend/test/sheetr_server_test.gleam
+13
backend/test/sheetr_server_test.gleam
+27
flake.lock
+27
flake.lock
···
1
+
{
2
+
"nodes": {
3
+
"nixpkgs": {
4
+
"locked": {
5
+
"lastModified": 1767273430,
6
+
"narHash": "sha256-kDpoFwQ8GLrPiS3KL+sAwreXrph2KhdXuJzo5+vSLoo=",
7
+
"owner": "NixOS",
8
+
"repo": "nixpkgs",
9
+
"rev": "76eec3925eb9bbe193934987d3285473dbcfad50",
10
+
"type": "github"
11
+
},
12
+
"original": {
13
+
"owner": "NixOS",
14
+
"ref": "nixpkgs-unstable",
15
+
"repo": "nixpkgs",
16
+
"type": "github"
17
+
}
18
+
},
19
+
"root": {
20
+
"inputs": {
21
+
"nixpkgs": "nixpkgs"
22
+
}
23
+
}
24
+
},
25
+
"root": "root",
26
+
"version": 7
27
+
}
+73
flake.nix
+73
flake.nix
···
1
+
{
2
+
inputs = {
3
+
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
4
+
};
5
+
6
+
outputs = {nixpkgs, ...} @ inputs: let
7
+
lib = nixpkgs.lib;
8
+
supportedSystems = ["x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin"];
9
+
forEachSupportedSystem = f:
10
+
lib.genAttrs supportedSystems (system:
11
+
f {
12
+
pkgs = import nixpkgs {inherit system;};
13
+
});
14
+
in {
15
+
devShells = forEachSupportedSystem ({pkgs}: {
16
+
default = pkgs.mkShell {
17
+
packages = with pkgs; [
18
+
gleam
19
+
erlang_28
20
+
beam28Packages.rebar3
21
+
jq
22
+
];
23
+
};
24
+
});
25
+
apps = forEachSupportedSystem ({pkgs}: let
26
+
cdInto = service: ''
27
+
rootDir=$(jj --ignore-working-copy root || git rev-parse --show-toplevel) || (echo "error: can't find repo root?"; exit 1)
28
+
cd "$rootDir/${service}"
29
+
'';
30
+
runtimeInputs = with pkgs; [
31
+
gleam
32
+
erlang_28
33
+
beam28Packages.rebar3
34
+
];
35
+
in {
36
+
# TODO: make default app run both frontend and backend
37
+
frontend = {
38
+
type = "app";
39
+
program = "${(pkgs.writeShellApplication {
40
+
inherit runtimeInputs;
41
+
name = "run-frontend";
42
+
text = ''
43
+
${cdInto "frontend"}
44
+
# TODO: dev server proxy thing
45
+
${pkgs.gleam}/bin/gleam run -m lustre/dev start
46
+
'';
47
+
})}/bin/run-frontend";
48
+
};
49
+
backend = {
50
+
type = "app";
51
+
program = "${(pkgs.writeShellApplication {
52
+
inherit runtimeInputs;
53
+
name = "run-backend";
54
+
text = ''
55
+
${cdInto "backend"}
56
+
${pkgs.gleam}/bin/gleam run
57
+
'';
58
+
})}/bin/run-backend";
59
+
};
60
+
lexgen = {
61
+
type = "app";
62
+
program = "${(pkgs.writeShellApplication {
63
+
inherit runtimeInputs;
64
+
name = "run-lexgen";
65
+
text = ''
66
+
${cdInto "shared"}
67
+
${pkgs.gleam}/bin/gleam run -m alicia/lexgen -- --dir=${./lexicons}
68
+
'';
69
+
})}/bin/run-lexgen";
70
+
};
71
+
});
72
+
};
73
+
}
+24
frontend/README.md
+24
frontend/README.md
···
1
+
# sheetr_client
2
+
3
+
[](https://hex.pm/packages/sheetr_client)
4
+
[](https://hexdocs.pm/sheetr_client/)
5
+
6
+
```sh
7
+
gleam add sheetr_client@1
8
+
```
9
+
```gleam
10
+
import sheetr_client
11
+
12
+
pub fn main() -> Nil {
13
+
// TODO: An example of the project in use
14
+
}
15
+
```
16
+
17
+
Further documentation can be found at <https://hexdocs.pm/sheetr_client>.
18
+
19
+
## Development
20
+
21
+
```sh
22
+
gleam run # Run the project
23
+
gleam test # Run the tests
24
+
```
+19
frontend/gleam.toml
+19
frontend/gleam.toml
···
1
+
name = "sheetr_client"
2
+
version = "1.0.0"
3
+
4
+
# Fill out these fields if you intend to generate HTML documentation or publish
5
+
# your project to the Hex package manager.
6
+
#
7
+
# description = ""
8
+
# licences = ["Apache-2.0"]
9
+
# repository = { type = "github", user = "", repo = "" }
10
+
# links = [{ title = "Website", href = "" }]
11
+
#
12
+
# For a full reference of all the available options, you can have a look at
13
+
# https://gleam.run/writing-gleam/gleam-toml/.
14
+
15
+
[dependencies]
16
+
gleam_stdlib = ">= 0.44.0 and < 2.0.0"
17
+
18
+
[dev-dependencies]
19
+
gleeunit = ">= 1.0.0 and < 2.0.0"
+5
frontend/src/sheetr_client.gleam
+5
frontend/src/sheetr_client.gleam
+13
frontend/test/sheetr_client_test.gleam
+13
frontend/test/sheetr_client_test.gleam
+10
lexicons/system/dnd5e/alignment/chaoticEvil.json
+10
lexicons/system/dnd5e/alignment/chaoticEvil.json
+10
lexicons/system/dnd5e/alignment/chaoticGood.json
+10
lexicons/system/dnd5e/alignment/chaoticGood.json
+10
lexicons/system/dnd5e/alignment/chaoticNeutral.json
+10
lexicons/system/dnd5e/alignment/chaoticNeutral.json
+10
lexicons/system/dnd5e/alignment/lawfulEvil.json
+10
lexicons/system/dnd5e/alignment/lawfulEvil.json
+10
lexicons/system/dnd5e/alignment/lawfulGood.json
+10
lexicons/system/dnd5e/alignment/lawfulGood.json
+10
lexicons/system/dnd5e/alignment/lawfulNeutral.json
+10
lexicons/system/dnd5e/alignment/lawfulNeutral.json
+10
lexicons/system/dnd5e/alignment/neutralEvil.json
+10
lexicons/system/dnd5e/alignment/neutralEvil.json
+10
lexicons/system/dnd5e/alignment/neutralGood.json
+10
lexicons/system/dnd5e/alignment/neutralGood.json
+10
lexicons/system/dnd5e/alignment/neutralNeutral.json
+10
lexicons/system/dnd5e/alignment/neutralNeutral.json
+34
lexicons/system/dnd5e/description.json
+34
lexicons/system/dnd5e/description.json
···
1
+
{
2
+
"lexicon": 1,
3
+
"id": "app.sheetr.system.dnd5e.description",
4
+
"defs": {
5
+
"main": {
6
+
"type": "record",
7
+
"key": "tid",
8
+
"description": "D&D 5e Character description.",
9
+
"record": {
10
+
"type": "object",
11
+
"required": [],
12
+
"properties": {
13
+
"name": {
14
+
"type": "string"
15
+
},
16
+
"alignment": {
17
+
"type": "string",
18
+
"knownValues": [
19
+
"app.sheetr.system.dnd5e.alignment.lawfulGood",
20
+
"app.sheetr.system.dnd5e.alignment.lawfulNeutral",
21
+
"app.sheetr.system.dnd5e.alignment.lawfulEvil",
22
+
"app.sheetr.system.dnd5e.alignment.neutralGood",
23
+
"app.sheetr.system.dnd5e.alignment.neutralNeutral",
24
+
"app.sheetr.system.dnd5e.alignment.neutralEvil",
25
+
"app.sheetr.system.dnd5e.alignment.chaoticGood",
26
+
"app.sheetr.system.dnd5e.alignment.chaoticNeutral",
27
+
"app.sheetr.system.dnd5e.alignment.chaoticEvil"
28
+
]
29
+
}
30
+
}
31
+
}
32
+
}
33
+
}
34
+
}
+21
lexicons/system/dnd5e/system.json
+21
lexicons/system/dnd5e/system.json
···
1
+
{
2
+
"lexicon": 1,
3
+
"id": "app.sheetr.system.dnd5e",
4
+
"defs": {
5
+
"main": {
6
+
"type": "record",
7
+
"key": "tid",
8
+
"description": "A system for Sheetr to support D&D 5th edition.",
9
+
"record": {
10
+
"type": "object",
11
+
"required": [],
12
+
"properties": {
13
+
"description": {
14
+
"type": "string",
15
+
"format": "at-uri"
16
+
}
17
+
}
18
+
}
19
+
}
20
+
}
21
+
}