.sqlx
crates
lexicons
migrations
src
···
1
1
+
{
2
2
+
"db_name": "SQLite",
3
3
+
"query": "SELECT EXISTS(SELECT 1 FROM models WHERE uri = ?)",
4
4
+
"describe": {
5
5
+
"columns": [
6
6
+
{
7
7
+
"name": "EXISTS(SELECT 1 FROM models WHERE uri = ?)",
8
8
+
"ordinal": 0,
9
9
+
"type_info": "Integer",
10
10
+
"origin": "Expression"
11
11
+
}
12
12
+
],
13
13
+
"parameters": {
14
14
+
"Right": 1
15
15
+
},
16
16
+
"nullable": [
17
17
+
false
18
18
+
]
19
19
+
},
20
20
+
"hash": "05e2314ba1bdc25c78d9468e6a7a5115b0b3e2f063979d13cd8cb11330c7d382"
21
21
+
}
···
1
1
+
{
2
2
+
"db_name": "SQLite",
3
3
+
"query": "SELECT COUNT(*) AS count FROM thing_models WHERE model_uri = ?",
4
4
+
"describe": {
5
5
+
"columns": [
6
6
+
{
7
7
+
"name": "count",
8
8
+
"ordinal": 0,
9
9
+
"type_info": "Integer",
10
10
+
"origin": "Expression"
11
11
+
}
12
12
+
],
13
13
+
"parameters": {
14
14
+
"Right": 1
15
15
+
},
16
16
+
"nullable": [
17
17
+
false
18
18
+
]
19
19
+
},
20
20
+
"hash": "0d80344b5e78f5c0d5de9cf752b860d3dd91b5749ccaa6a6e8cd65764d720905"
21
21
+
}
···
1
1
+
{
2
2
+
"db_name": "SQLite",
3
3
+
"query": "SELECT cid, rkey FROM likes WHERE did = ? AND subject_uri = ?",
4
4
+
"describe": {
5
5
+
"columns": [
6
6
+
{
7
7
+
"name": "cid",
8
8
+
"ordinal": 0,
9
9
+
"type_info": "Text",
10
10
+
"origin": {
11
11
+
"Table": {
12
12
+
"table": "likes",
13
13
+
"name": "cid"
14
14
+
}
15
15
+
}
16
16
+
},
17
17
+
{
18
18
+
"name": "rkey",
19
19
+
"ordinal": 1,
20
20
+
"type_info": "Text",
21
21
+
"origin": {
22
22
+
"Table": {
23
23
+
"table": "likes",
24
24
+
"name": "rkey"
25
25
+
}
26
26
+
}
27
27
+
}
28
28
+
],
29
29
+
"parameters": {
30
30
+
"Right": 2
31
31
+
},
32
32
+
"nullable": [
33
33
+
false,
34
34
+
false
35
35
+
]
36
36
+
},
37
37
+
"hash": "0ed97286ed1a532f11a124fbb744081d97244d658f169b8e0bda21d40f4416dd"
38
38
+
}
···
1
1
+
{
2
2
+
"db_name": "SQLite",
3
3
+
"query": "SELECT EXISTS(SELECT 1 FROM things WHERE uri = ?)",
4
4
+
"describe": {
5
5
+
"columns": [
6
6
+
{
7
7
+
"name": "EXISTS(SELECT 1 FROM things WHERE uri = ?)",
8
8
+
"ordinal": 0,
9
9
+
"type_info": "Integer",
10
10
+
"origin": "Expression"
11
11
+
}
12
12
+
],
13
13
+
"parameters": {
14
14
+
"Right": 1
15
15
+
},
16
16
+
"nullable": [
17
17
+
false
18
18
+
]
19
19
+
},
20
20
+
"hash": "1133e593e6973e28ad03b0297f3bfd4c3f0918190f4ecd265314b213451bcf77"
21
21
+
}
···
1
1
+
{
2
2
+
"db_name": "SQLite",
3
3
+
"query": "SELECT file_json FROM upload_staging WHERE owner_did = ? AND upload_id = ? AND status = 'uploaded'",
4
4
+
"describe": {
5
5
+
"columns": [
6
6
+
{
7
7
+
"name": "file_json",
8
8
+
"ordinal": 0,
9
9
+
"type_info": "Text",
10
10
+
"origin": {
11
11
+
"Table": {
12
12
+
"table": "upload_staging",
13
13
+
"name": "file_json"
14
14
+
}
15
15
+
}
16
16
+
}
17
17
+
],
18
18
+
"parameters": {
19
19
+
"Right": 2
20
20
+
},
21
21
+
"nullable": [
22
22
+
false
23
23
+
]
24
24
+
},
25
25
+
"hash": "2094180851ce8e023590116d2bf5cc3dd7b39e688652f305b957e61e8c18b661"
26
26
+
}
···
1
1
+
{
2
2
+
"db_name": "SQLite",
3
3
+
"query": "SELECT EXISTS(SELECT 1 FROM models WHERE did = ? AND uri = ?)",
4
4
+
"describe": {
5
5
+
"columns": [
6
6
+
{
7
7
+
"name": "EXISTS(SELECT 1 FROM models WHERE did = ? AND uri = ?)",
8
8
+
"ordinal": 0,
9
9
+
"type_info": "Integer",
10
10
+
"origin": "Expression"
11
11
+
}
12
12
+
],
13
13
+
"parameters": {
14
14
+
"Right": 2
15
15
+
},
16
16
+
"nullable": [
17
17
+
false
18
18
+
]
19
19
+
},
20
20
+
"hash": "4486bc1f0e41e6cf884dddaa52c24b6695604f173e09f05445add90eee55dae2"
21
21
+
}
···
1
1
+
{
2
2
+
"db_name": "SQLite",
3
3
+
"query": "SELECT COUNT(*) AS count FROM model_parts WHERE part_uri = ?",
4
4
+
"describe": {
5
5
+
"columns": [
6
6
+
{
7
7
+
"name": "count",
8
8
+
"ordinal": 0,
9
9
+
"type_info": "Integer",
10
10
+
"origin": "Expression"
11
11
+
}
12
12
+
],
13
13
+
"parameters": {
14
14
+
"Right": 1
15
15
+
},
16
16
+
"nullable": [
17
17
+
false
18
18
+
]
19
19
+
},
20
20
+
"hash": "563c3c23ec87f048b811625df4d623d1eb6cc6e95287f5c9c8e890fd727a056e"
21
21
+
}
···
1
1
+
{
2
2
+
"db_name": "SQLite",
3
3
+
"query": "SELECT EXISTS(SELECT 1 FROM parts WHERE did = ? AND uri = ?)",
4
4
+
"describe": {
5
5
+
"columns": [
6
6
+
{
7
7
+
"name": "EXISTS(SELECT 1 FROM parts WHERE did = ? AND uri = ?)",
8
8
+
"ordinal": 0,
9
9
+
"type_info": "Integer",
10
10
+
"origin": "Expression"
11
11
+
}
12
12
+
],
13
13
+
"parameters": {
14
14
+
"Right": 2
15
15
+
},
16
16
+
"nullable": [
17
17
+
false
18
18
+
]
19
19
+
},
20
20
+
"hash": "6b48422566a6ed1221ec83e82b8253653f0b2af89db76077142a71cc7f1643d0"
21
21
+
}
···
1
1
+
{
2
2
+
"db_name": "SQLite",
3
3
+
"query": "SELECT p.did, p.rkey, p.uri FROM thing_models tm JOIN model_parts mp ON mp.model_uri = tm.model_uri JOIN parts p ON p.uri = mp.part_uri WHERE tm.thing_uri = ? ORDER BY tm.position, mp.position",
4
4
+
"describe": {
5
5
+
"columns": [
6
6
+
{
7
7
+
"name": "did",
8
8
+
"ordinal": 0,
9
9
+
"type_info": "Text",
10
10
+
"origin": {
11
11
+
"Table": {
12
12
+
"table": "parts",
13
13
+
"name": "did"
14
14
+
}
15
15
+
}
16
16
+
},
17
17
+
{
18
18
+
"name": "rkey",
19
19
+
"ordinal": 1,
20
20
+
"type_info": "Text",
21
21
+
"origin": {
22
22
+
"Table": {
23
23
+
"table": "parts",
24
24
+
"name": "rkey"
25
25
+
}
26
26
+
}
27
27
+
},
28
28
+
{
29
29
+
"name": "uri",
30
30
+
"ordinal": 2,
31
31
+
"type_info": "Text",
32
32
+
"origin": {
33
33
+
"Table": {
34
34
+
"table": "parts",
35
35
+
"name": "uri"
36
36
+
}
37
37
+
}
38
38
+
}
39
39
+
],
40
40
+
"parameters": {
41
41
+
"Right": 1
42
42
+
},
43
43
+
"nullable": [
44
44
+
false,
45
45
+
false,
46
46
+
false
47
47
+
]
48
48
+
},
49
49
+
"hash": "7bdc04619643992941c43a742f045677fbe7a38b9ed034d70d5aea7be3603b9c"
50
50
+
}
···
1
1
-
{
2
2
-
"db_name": "SQLite",
3
3
-
"query": "INSERT OR IGNORE INTO tags (did, rkey, subject_uri, tag, created_at) VALUES (?, ?, ?, ?, ?)",
4
4
-
"describe": {
5
5
-
"columns": [],
6
6
-
"parameters": {
7
7
-
"Right": 5
8
8
-
},
9
9
-
"nullable": []
10
10
-
},
11
11
-
"hash": "8b02bdc1204d6e4afd30d29aa605e34777a5ab609d8eb454836d676800ff0511"
12
12
-
}
···
1
1
+
{
2
2
+
"db_name": "SQLite",
3
3
+
"query": "SELECT cid, rkey FROM saves WHERE did = ? AND subject_uri = ?",
4
4
+
"describe": {
5
5
+
"columns": [
6
6
+
{
7
7
+
"name": "cid",
8
8
+
"ordinal": 0,
9
9
+
"type_info": "Text",
10
10
+
"origin": {
11
11
+
"Table": {
12
12
+
"table": "saves",
13
13
+
"name": "cid"
14
14
+
}
15
15
+
}
16
16
+
},
17
17
+
{
18
18
+
"name": "rkey",
19
19
+
"ordinal": 1,
20
20
+
"type_info": "Text",
21
21
+
"origin": {
22
22
+
"Table": {
23
23
+
"table": "saves",
24
24
+
"name": "rkey"
25
25
+
}
26
26
+
}
27
27
+
}
28
28
+
],
29
29
+
"parameters": {
30
30
+
"Right": 2
31
31
+
},
32
32
+
"nullable": [
33
33
+
false,
34
34
+
false
35
35
+
]
36
36
+
},
37
37
+
"hash": "993cd27061ba88a858f7021f8e86d116f2dec107de7fdba8cc8644ebbc6d0ae3"
38
38
+
}
···
1
1
-
{
2
2
-
"db_name": "SQLite",
3
3
-
"query": "INSERT OR IGNORE INTO listitems (did, rkey, list_uri, subject_uri, created_at) VALUES (?, ?, ?, ?, ?)",
4
4
-
"describe": {
5
5
-
"columns": [],
6
6
-
"parameters": {
7
7
-
"Right": 5
8
8
-
},
9
9
-
"nullable": []
10
10
-
},
11
11
-
"hash": "af25cf6fdd1ddc048069a952e8a25dad4abd7cba9b604fe2a25d77bcf3d5f1af"
12
12
-
}
···
1
1
-
{
2
2
-
"db_name": "SQLite",
3
3
-
"query": "INSERT OR IGNORE INTO saves (did, rkey, subject_uri, note, created_at) VALUES (?, ?, ?, ?, ?)",
4
4
-
"describe": {
5
5
-
"columns": [],
6
6
-
"parameters": {
7
7
-
"Right": 5
8
8
-
},
9
9
-
"nullable": []
10
10
-
},
11
11
-
"hash": "b4a4296cbfee1f54b3e025416499fbeb50c24b15248ac8516ede592e68b09523"
12
12
-
}
···
1
1
+
{
2
2
+
"db_name": "SQLite",
3
3
+
"query": "SELECT did, rkey, uri FROM things WHERE uri = ?",
4
4
+
"describe": {
5
5
+
"columns": [
6
6
+
{
7
7
+
"name": "did",
8
8
+
"ordinal": 0,
9
9
+
"type_info": "Text",
10
10
+
"origin": {
11
11
+
"Table": {
12
12
+
"table": "things",
13
13
+
"name": "did"
14
14
+
}
15
15
+
}
16
16
+
},
17
17
+
{
18
18
+
"name": "rkey",
19
19
+
"ordinal": 1,
20
20
+
"type_info": "Text",
21
21
+
"origin": {
22
22
+
"Table": {
23
23
+
"table": "things",
24
24
+
"name": "rkey"
25
25
+
}
26
26
+
}
27
27
+
},
28
28
+
{
29
29
+
"name": "uri",
30
30
+
"ordinal": 2,
31
31
+
"type_info": "Text",
32
32
+
"origin": {
33
33
+
"Table": {
34
34
+
"table": "things",
35
35
+
"name": "uri"
36
36
+
}
37
37
+
}
38
38
+
}
39
39
+
],
40
40
+
"parameters": {
41
41
+
"Right": 1
42
42
+
},
43
43
+
"nullable": [
44
44
+
false,
45
45
+
false,
46
46
+
false
47
47
+
]
48
48
+
},
49
49
+
"hash": "bc2637c2b2950f580859536b190e13b7f1192296f332354b9e174150fd5e89ac"
50
50
+
}
···
1
1
-
{
2
2
-
"db_name": "SQLite",
3
3
-
"query": "INSERT OR IGNORE INTO likes (did, rkey, subject_uri, created_at) VALUES (?, ?, ?, ?)",
4
4
-
"describe": {
5
5
-
"columns": [],
6
6
-
"parameters": {
7
7
-
"Right": 4
8
8
-
},
9
9
-
"nullable": []
10
10
-
},
11
11
-
"hash": "c9ef48617a88d2241b9959825184b59d1c25cf1024571297cbe32c3efb86c03d"
12
12
-
}
···
1
1
+
{
2
2
+
"db_name": "SQLite",
3
3
+
"query": "INSERT OR IGNORE INTO saves (did, rkey, cid, subject_uri, note, created_at) VALUES (?, ?, ?, ?, ?, ?)",
4
4
+
"describe": {
5
5
+
"columns": [],
6
6
+
"parameters": {
7
7
+
"Right": 6
8
8
+
},
9
9
+
"nullable": []
10
10
+
},
11
11
+
"hash": "cb4c8e4f4748a49049c4e942e6c1e6e1f9ae120827a7abf0d150f1a37936fd4f"
12
12
+
}
···
1
1
+
{
2
2
+
"db_name": "SQLite",
3
3
+
"query": "INSERT OR IGNORE INTO tags (did, rkey, cid, subject_uri, tag, created_at) VALUES (?, ?, ?, ?, ?, ?)",
4
4
+
"describe": {
5
5
+
"columns": [],
6
6
+
"parameters": {
7
7
+
"Right": 6
8
8
+
},
9
9
+
"nullable": []
10
10
+
},
11
11
+
"hash": "cd961657cb3d0a41d2e6b503d4038849f8350117fb65f773f9429709dd588a09"
12
12
+
}
···
1
1
+
{
2
2
+
"db_name": "SQLite",
3
3
+
"query": "SELECT EXISTS(SELECT 1 FROM parts WHERE uri = ?)",
4
4
+
"describe": {
5
5
+
"columns": [
6
6
+
{
7
7
+
"name": "EXISTS(SELECT 1 FROM parts WHERE uri = ?)",
8
8
+
"ordinal": 0,
9
9
+
"type_info": "Integer",
10
10
+
"origin": "Expression"
11
11
+
}
12
12
+
],
13
13
+
"parameters": {
14
14
+
"Right": 1
15
15
+
},
16
16
+
"nullable": [
17
17
+
false
18
18
+
]
19
19
+
},
20
20
+
"hash": "ce65448db03ba94432bb911c797b33277438ac7fd538b9e03334988dbd014fac"
21
21
+
}
···
1
1
+
{
2
2
+
"db_name": "SQLite",
3
3
+
"query": "INSERT OR IGNORE INTO listitems (did, rkey, cid, list_uri, subject_uri, created_at) VALUES (?, ?, ?, ?, ?, ?)",
4
4
+
"describe": {
5
5
+
"columns": [],
6
6
+
"parameters": {
7
7
+
"Right": 6
8
8
+
},
9
9
+
"nullable": []
10
10
+
},
11
11
+
"hash": "db731da68101b9edd9662d51f7fce22044ab9f955f59b1d57ca2a03837aa0728"
12
12
+
}
···
1
1
+
{
2
2
+
"db_name": "SQLite",
3
3
+
"query": "SELECT m.did, m.rkey, m.uri FROM thing_models tm JOIN models m ON m.uri = tm.model_uri WHERE tm.thing_uri = ? ORDER BY tm.position",
4
4
+
"describe": {
5
5
+
"columns": [
6
6
+
{
7
7
+
"name": "did",
8
8
+
"ordinal": 0,
9
9
+
"type_info": "Text",
10
10
+
"origin": {
11
11
+
"Table": {
12
12
+
"table": "models",
13
13
+
"name": "did"
14
14
+
}
15
15
+
}
16
16
+
},
17
17
+
{
18
18
+
"name": "rkey",
19
19
+
"ordinal": 1,
20
20
+
"type_info": "Text",
21
21
+
"origin": {
22
22
+
"Table": {
23
23
+
"table": "models",
24
24
+
"name": "rkey"
25
25
+
}
26
26
+
}
27
27
+
},
28
28
+
{
29
29
+
"name": "uri",
30
30
+
"ordinal": 2,
31
31
+
"type_info": "Text",
32
32
+
"origin": {
33
33
+
"Table": {
34
34
+
"table": "models",
35
35
+
"name": "uri"
36
36
+
}
37
37
+
}
38
38
+
}
39
39
+
],
40
40
+
"parameters": {
41
41
+
"Right": 1
42
42
+
},
43
43
+
"nullable": [
44
44
+
false,
45
45
+
false,
46
46
+
false
47
47
+
]
48
48
+
},
49
49
+
"hash": "e16314e2226f9cf2334ca747403b693fb60120cfed020b300efe1f55e1e9564c"
50
50
+
}
···
1
1
+
{
2
2
+
"db_name": "SQLite",
3
3
+
"query": "INSERT OR IGNORE INTO likes (did, rkey, cid, subject_uri, created_at) VALUES (?, ?, ?, ?, ?)",
4
4
+
"describe": {
5
5
+
"columns": [],
6
6
+
"parameters": {
7
7
+
"Right": 5
8
8
+
},
9
9
+
"nullable": []
10
10
+
},
11
11
+
"hash": "e9573839c7d4c31f3960e6415774d5f65ae2e0b6ac708f00bb750974412c4fb2"
12
12
+
}
···
1
1
+
{
2
2
+
"db_name": "SQLite",
3
3
+
"query": "INSERT INTO upload_staging (owner_did, upload_id, sha256, mime_type, size, filename, status, file_json, chunks_json, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, 'uploaded', ?, ?, ?, ?) ON CONFLICT(owner_did, upload_id) DO UPDATE SET sha256 = excluded.sha256, mime_type = excluded.mime_type, size = excluded.size, filename = excluded.filename, status = excluded.status, file_json = excluded.file_json, chunks_json = excluded.chunks_json, updated_at = excluded.updated_at",
4
4
+
"describe": {
5
5
+
"columns": [],
6
6
+
"parameters": {
7
7
+
"Right": 10
8
8
+
},
9
9
+
"nullable": []
10
10
+
},
11
11
+
"hash": "f727bf890c756a13373922ed6f5f948f77dd5faa65c0b75664142a4b888986da"
12
12
+
}
···
148
148
version = "1.4.2"
149
149
source = "registry+https://github.com/rust-lang/crates.io-index"
150
150
checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
151
151
+
dependencies = [
152
152
+
"derive_arbitrary",
153
153
+
]
151
154
152
155
[[package]]
153
156
name = "arc-swap"
···
1571
1574
version = "0.5.8"
1572
1575
source = "registry+https://github.com/rust-lang/crates.io-index"
1573
1576
checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c"
1577
1577
+
1578
1578
+
[[package]]
1579
1579
+
name = "derive_arbitrary"
1580
1580
+
version = "1.4.2"
1581
1581
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1582
1582
+
checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a"
1583
1583
+
dependencies = [
1584
1584
+
"proc-macro2",
1585
1585
+
"quote",
1586
1586
+
"syn 2.0.118",
1587
1587
+
]
1574
1588
1575
1589
[[package]]
1576
1590
name = "derive_more"
···
6184
6198
"dotenvy",
6185
6199
"futures",
6186
6200
"http",
6201
6201
+
"http-body-util",
6187
6202
"hydrant",
6188
6203
"jacquard",
6189
6204
"jacquard-axum",
···
6197
6212
"rustls",
6198
6213
"serde",
6199
6214
"serde_json",
6215
6215
+
"sha2 0.10.9",
6200
6216
"sqlx",
6201
6217
"stl_io",
6202
6218
"thiserror 2.0.18",
···
6211
6227
"wasm-bindgen",
6212
6228
"wasm-bindgen-futures",
6213
6229
"web-sys",
6230
6230
+
"zip",
6214
6231
]
6215
6232
6216
6233
[[package]]
···
9884
9901
"proc-macro2",
9885
9902
"quote",
9886
9903
"syn 2.0.118",
9904
9904
+
]
9905
9905
+
9906
9906
+
[[package]]
9907
9907
+
name = "zip"
9908
9908
+
version = "2.4.2"
9909
9909
+
source = "registry+https://github.com/rust-lang/crates.io-index"
9910
9910
+
checksum = "fabe6324e908f85a1c52063ce7aa26b68dcb7eb6dbc83a2d148403c9bc3eba50"
9911
9911
+
dependencies = [
9912
9912
+
"arbitrary",
9913
9913
+
"crc32fast",
9914
9914
+
"crossbeam-utils",
9915
9915
+
"displaydoc",
9916
9916
+
"indexmap",
9917
9917
+
"memchr",
9918
9918
+
"thiserror 2.0.18",
9887
9919
]
9888
9920
9889
9921
[[package]]
···
42
42
"dep:anyhow",
43
43
"dep:dotenvy",
44
44
"dep:chrono",
45
45
+
"dep:http-body-util",
45
46
"dep:tracing-subscriber",
46
47
"dep:rustls",
47
48
# OAuth (PM-27): confidential client, private cookie jar key, secret sources.
···
49
50
50
51
"dep:keyring",
51
52
"dep:base64",
53
53
+
"dep:sha2",
54
54
+
"dep:zip",
52
55
]
53
56
[dependencies]
54
57
dioxus = { version = "0.7", features = ["router", "fullstack"] }
···
65
68
three-d-asset = { version = "0.10", default-features = false }
66
69
stl_io = "0.11"
67
70
68
68
-
axum = { version = "0.8", optional = true }
71
71
+
axum = { version = "0.8", optional = true, features = ["multipart"] }
69
72
tower = { version = "0.5", optional = true }
70
73
tower-http = { version = "0.6", optional = true, features = ["trace"] }
71
74
three-d = { version = "0.19.0", default-features = false }
···
97
100
"env-filter",
98
101
] }
99
102
http = "1.4"
103
103
+
http-body-util = { version = "0.1", optional = true }
100
104
reqwest = { version = "0.12", default-features = false, features = ["json", "charset"] }
101
105
102
106
# PM-27 OAuth: private cookie-jar signing key and swappable secret sources
···
105
109
106
110
keyring = { version = "3", optional = true }
107
111
base64 = { version = "0.22", optional = true }
112
112
+
sha2 = { version = "0.10", optional = true }
113
113
+
zip = { version = "2", default-features = false, optional = true }
108
114
109
115
110
116
[target.'cfg(all(target_family = "wasm", target_os = "unknown"))'.dependencies]
···
118
124
[dev-dependencies]
119
125
anyhow = "1"
120
126
axum = "0.8"
127
127
+
axum-extra = { version = "0.10", features = ["cookie"] }
128
128
+
base64 = "0.22"
121
129
chrono = "0.4"
122
130
futures = "0.3"
123
131
hydrant = { git = "https://tangled.org/ptr.pet/hydrant", rev = "1145fa1bb194372ceea81ab48b5d68d304da34ad", default-features = false, features = ["indexer", "indexer_stream"] }
132
132
+
http-body-util = "0.1"
124
133
jacquard-axum = { workspace = true }
134
134
+
keyring = "3"
125
135
libsqlite3-sys = { version = "0.37", features = ["bundled"] }
126
136
rustls = { version = "0.23", default-features = false, features = ["aws-lc-rs", "std"] }
137
137
+
sha2 = "0.10"
127
138
sqlx = { version = "0.9", default-features = false, features = [
128
139
"runtime-tokio",
129
140
"sqlite",
···
1
1
+
{
2
2
+
"lexicon": 1,
3
3
+
"id": "space.polymodel.actor.bootstrapProfile",
4
4
+
"defs": {
5
5
+
"main": {
6
6
+
"type": "procedure",
7
7
+
"description": "One-time authenticated bootstrap of the actor's Polymodel profile from their own PDS profile records. Creates space.polymodel.actor.profile/self only when absent locally and PDS-side.",
8
8
+
"output": {
9
9
+
"encoding": "application/json",
10
10
+
"schema": {
11
11
+
"type": "object",
12
12
+
"required": [
13
13
+
"status",
14
14
+
"profile"
15
15
+
],
16
16
+
"properties": {
17
17
+
"cid": {
18
18
+
"type": "string",
19
19
+
"format": "cid"
20
20
+
},
21
21
+
"profile": {
22
22
+
"type": "ref",
23
23
+
"ref": "space.polymodel.actor.defs#profileView"
24
24
+
},
25
25
+
"status": {
26
26
+
"type": "string",
27
27
+
"knownValues": [
28
28
+
"existingLocal",
29
29
+
"existingRemote",
30
30
+
"created"
31
31
+
]
32
32
+
},
33
33
+
"uri": {
34
34
+
"type": "string",
35
35
+
"format": "at-uri"
36
36
+
}
37
37
+
}
38
38
+
}
39
39
+
}
40
40
+
}
41
41
+
}
42
42
+
}
···
1
1
+
{
2
2
+
"lexicon": 1,
3
3
+
"id": "space.polymodel.graph.createLike",
4
4
+
"defs": {
5
5
+
"main": {
6
6
+
"type": "procedure",
7
7
+
"description": "Create a like record for an indexed Polymodel subject on the authenticated actor's PDS and eagerly project viewer/count state.",
8
8
+
"input": {
9
9
+
"encoding": "application/json",
10
10
+
"schema": {
11
11
+
"type": "object",
12
12
+
"required": [
13
13
+
"subject"
14
14
+
],
15
15
+
"properties": {
16
16
+
"subject": {
17
17
+
"type": "ref",
18
18
+
"ref": "com.atproto.repo.strongRef"
19
19
+
}
20
20
+
}
21
21
+
}
22
22
+
},
23
23
+
"output": {
24
24
+
"encoding": "application/json",
25
25
+
"schema": {
26
26
+
"type": "object",
27
27
+
"required": [
28
28
+
"uri",
29
29
+
"cid"
30
30
+
],
31
31
+
"properties": {
32
32
+
"cid": {
33
33
+
"type": "string",
34
34
+
"format": "cid"
35
35
+
},
36
36
+
"created": {
37
37
+
"type": "boolean"
38
38
+
},
39
39
+
"uri": {
40
40
+
"type": "string",
41
41
+
"format": "at-uri"
42
42
+
}
43
43
+
}
44
44
+
}
45
45
+
}
46
46
+
}
47
47
+
}
48
48
+
}
···
1
1
+
{
2
2
+
"lexicon": 1,
3
3
+
"id": "space.polymodel.graph.createSave",
4
4
+
"defs": {
5
5
+
"main": {
6
6
+
"type": "procedure",
7
7
+
"description": "Create a save record for an indexed Polymodel subject on the authenticated actor's PDS and eagerly project viewer/count state.",
8
8
+
"input": {
9
9
+
"encoding": "application/json",
10
10
+
"schema": {
11
11
+
"type": "object",
12
12
+
"required": [
13
13
+
"subject"
14
14
+
],
15
15
+
"properties": {
16
16
+
"note": {
17
17
+
"type": "string",
18
18
+
"maxLength": 2000
19
19
+
},
20
20
+
"subject": {
21
21
+
"type": "ref",
22
22
+
"ref": "com.atproto.repo.strongRef"
23
23
+
}
24
24
+
}
25
25
+
}
26
26
+
},
27
27
+
"output": {
28
28
+
"encoding": "application/json",
29
29
+
"schema": {
30
30
+
"type": "object",
31
31
+
"required": [
32
32
+
"uri",
33
33
+
"cid"
34
34
+
],
35
35
+
"properties": {
36
36
+
"cid": {
37
37
+
"type": "string",
38
38
+
"format": "cid"
39
39
+
},
40
40
+
"created": {
41
41
+
"type": "boolean"
42
42
+
},
43
43
+
"uri": {
44
44
+
"type": "string",
45
45
+
"format": "at-uri"
46
46
+
}
47
47
+
}
48
48
+
}
49
49
+
}
50
50
+
}
51
51
+
}
52
52
+
}
···
1
1
+
{
2
2
+
"lexicon": 1,
3
3
+
"id": "space.polymodel.graph.createTag",
4
4
+
"defs": {
5
5
+
"main": {
6
6
+
"type": "procedure",
7
7
+
"description": "Create a tag assertion for an indexed Polymodel subject on the authenticated actor's PDS and eagerly project viewer/count state.",
8
8
+
"input": {
9
9
+
"encoding": "application/json",
10
10
+
"schema": {
11
11
+
"type": "object",
12
12
+
"required": [
13
13
+
"subject",
14
14
+
"tag"
15
15
+
],
16
16
+
"properties": {
17
17
+
"subject": {
18
18
+
"type": "ref",
19
19
+
"ref": "com.atproto.repo.strongRef"
20
20
+
},
21
21
+
"tag": {
22
22
+
"type": "string",
23
23
+
"maxLength": 64,
24
24
+
"maxGraphemes": 32
25
25
+
}
26
26
+
}
27
27
+
}
28
28
+
},
29
29
+
"output": {
30
30
+
"encoding": "application/json",
31
31
+
"schema": {
32
32
+
"type": "object",
33
33
+
"required": [
34
34
+
"uri",
35
35
+
"cid"
36
36
+
],
37
37
+
"properties": {
38
38
+
"cid": {
39
39
+
"type": "string",
40
40
+
"format": "cid"
41
41
+
},
42
42
+
"created": {
43
43
+
"type": "boolean"
44
44
+
},
45
45
+
"uri": {
46
46
+
"type": "string",
47
47
+
"format": "at-uri"
48
48
+
}
49
49
+
}
50
50
+
}
51
51
+
}
52
52
+
}
53
53
+
}
54
54
+
}
···
1
1
+
{
2
2
+
"lexicon": 1,
3
3
+
"id": "space.polymodel.graph.deleteLike",
4
4
+
"defs": {
5
5
+
"main": {
6
6
+
"type": "procedure",
7
7
+
"description": "Delete the authenticated actor's like by subject or like record URI, then eagerly project viewer/count state.",
8
8
+
"input": {
9
9
+
"encoding": "application/json",
10
10
+
"schema": {
11
11
+
"type": "object",
12
12
+
"properties": {
13
13
+
"subject": {
14
14
+
"type": "ref",
15
15
+
"ref": "com.atproto.repo.strongRef"
16
16
+
},
17
17
+
"uri": {
18
18
+
"type": "string",
19
19
+
"format": "at-uri"
20
20
+
}
21
21
+
}
22
22
+
}
23
23
+
},
24
24
+
"output": {
25
25
+
"encoding": "application/json",
26
26
+
"schema": {
27
27
+
"type": "object",
28
28
+
"required": [
29
29
+
"deleted"
30
30
+
],
31
31
+
"properties": {
32
32
+
"deleted": {
33
33
+
"type": "boolean"
34
34
+
},
35
35
+
"uri": {
36
36
+
"type": "string",
37
37
+
"format": "at-uri"
38
38
+
}
39
39
+
}
40
40
+
}
41
41
+
}
42
42
+
}
43
43
+
}
44
44
+
}
···
1
1
+
{
2
2
+
"lexicon": 1,
3
3
+
"id": "space.polymodel.graph.deleteSave",
4
4
+
"defs": {
5
5
+
"main": {
6
6
+
"type": "procedure",
7
7
+
"description": "Delete the authenticated actor's save by subject or save record URI, then eagerly project viewer/count state.",
8
8
+
"input": {
9
9
+
"encoding": "application/json",
10
10
+
"schema": {
11
11
+
"type": "object",
12
12
+
"properties": {
13
13
+
"subject": {
14
14
+
"type": "ref",
15
15
+
"ref": "com.atproto.repo.strongRef"
16
16
+
},
17
17
+
"uri": {
18
18
+
"type": "string",
19
19
+
"format": "at-uri"
20
20
+
}
21
21
+
}
22
22
+
}
23
23
+
},
24
24
+
"output": {
25
25
+
"encoding": "application/json",
26
26
+
"schema": {
27
27
+
"type": "object",
28
28
+
"required": [
29
29
+
"deleted"
30
30
+
],
31
31
+
"properties": {
32
32
+
"deleted": {
33
33
+
"type": "boolean"
34
34
+
},
35
35
+
"uri": {
36
36
+
"type": "string",
37
37
+
"format": "at-uri"
38
38
+
}
39
39
+
}
40
40
+
}
41
41
+
}
42
42
+
}
43
43
+
}
44
44
+
}
···
1
1
+
{
2
2
+
"lexicon": 1,
3
3
+
"id": "space.polymodel.graph.deleteTag",
4
4
+
"defs": {
5
5
+
"main": {
6
6
+
"type": "procedure",
7
7
+
"description": "Delete a specific tag assertion record for the authenticated actor, then eagerly project viewer/count state. Tag delete requires a concrete tag record URI because tags are rkey-idempotent, not unique by subject+tag.",
8
8
+
"input": {
9
9
+
"encoding": "application/json",
10
10
+
"schema": {
11
11
+
"type": "object",
12
12
+
"required": [
13
13
+
"uri"
14
14
+
],
15
15
+
"properties": {
16
16
+
"uri": {
17
17
+
"type": "string",
18
18
+
"format": "at-uri"
19
19
+
}
20
20
+
}
21
21
+
}
22
22
+
},
23
23
+
"output": {
24
24
+
"encoding": "application/json",
25
25
+
"schema": {
26
26
+
"type": "object",
27
27
+
"required": [
28
28
+
"deleted"
29
29
+
],
30
30
+
"properties": {
31
31
+
"deleted": {
32
32
+
"type": "boolean"
33
33
+
},
34
34
+
"uri": {
35
35
+
"type": "string",
36
36
+
"format": "at-uri"
37
37
+
}
38
38
+
}
39
39
+
}
40
40
+
}
41
41
+
}
42
42
+
}
43
43
+
}
···
1
1
+
{
2
2
+
"lexicon": 1,
3
3
+
"id": "space.polymodel.library.deleteThing",
4
4
+
"defs": {
5
5
+
"main": {
6
6
+
"type": "procedure",
7
7
+
"description": "Delete an owned thing and its owned model/part composition records from the authenticated actor's PDS, then eagerly remove local projections.",
8
8
+
"input": {
9
9
+
"encoding": "application/json",
10
10
+
"schema": {
11
11
+
"type": "object",
12
12
+
"required": [
13
13
+
"uri"
14
14
+
],
15
15
+
"properties": {
16
16
+
"uri": {
17
17
+
"type": "string",
18
18
+
"format": "at-uri"
19
19
+
}
20
20
+
}
21
21
+
}
22
22
+
},
23
23
+
"output": {
24
24
+
"encoding": "application/json",
25
25
+
"schema": {
26
26
+
"type": "object",
27
27
+
"required": [
28
28
+
"deleted"
29
29
+
],
30
30
+
"properties": {
31
31
+
"deleted": {
32
32
+
"type": "boolean"
33
33
+
},
34
34
+
"deletedUris": {
35
35
+
"type": "array",
36
36
+
"items": {
37
37
+
"type": "string",
38
38
+
"format": "at-uri"
39
39
+
}
40
40
+
}
41
41
+
}
42
42
+
}
43
43
+
}
44
44
+
}
45
45
+
}
46
46
+
}
···
1
1
+
{
2
2
+
"lexicon": 1,
3
3
+
"id": "space.polymodel.library.publishThing",
4
4
+
"defs": {
5
5
+
"main": {
6
6
+
"type": "procedure",
7
7
+
"description": "Publish a thing by creating part records first, model records second, and the thing record last on the authenticated actor's PDS, then eagerly projecting every successful record locally.",
8
8
+
"input": {
9
9
+
"encoding": "application/json",
10
10
+
"schema": {
11
11
+
"type": "object",
12
12
+
"required": [
13
13
+
"thing"
14
14
+
],
15
15
+
"properties": {
16
16
+
"thing": {
17
17
+
"type": "ref",
18
18
+
"ref": "#thingInput"
19
19
+
}
20
20
+
}
21
21
+
}
22
22
+
},
23
23
+
"output": {
24
24
+
"encoding": "application/json",
25
25
+
"schema": {
26
26
+
"type": "object",
27
27
+
"required": [
28
28
+
"thing",
29
29
+
"models"
30
30
+
],
31
31
+
"properties": {
32
32
+
"models": {
33
33
+
"type": "array",
34
34
+
"items": {
35
35
+
"type": "ref",
36
36
+
"ref": "#modelPublishResult"
37
37
+
}
38
38
+
},
39
39
+
"thing": {
40
40
+
"type": "ref",
41
41
+
"ref": "#recordRef"
42
42
+
}
43
43
+
}
44
44
+
}
45
45
+
}
46
46
+
},
47
47
+
"modelInput": {
48
48
+
"type": "object",
49
49
+
"required": [
50
50
+
"name",
51
51
+
"parts"
52
52
+
],
53
53
+
"properties": {
54
54
+
"cover": {
55
55
+
"type": "array",
56
56
+
"items": {
57
57
+
"type": "ref",
58
58
+
"ref": "space.polymodel.library.defs#image"
59
59
+
},
60
60
+
"maxLength": 8
61
61
+
},
62
62
+
"derivedFrom": {
63
63
+
"type": "ref",
64
64
+
"ref": "com.atproto.repo.strongRef"
65
65
+
},
66
66
+
"instructions": {
67
67
+
"type": "array",
68
68
+
"items": {
69
69
+
"type": "string"
70
70
+
},
71
71
+
"maxLength": 100
72
72
+
},
73
73
+
"intendedToWorkWith": {
74
74
+
"type": "array",
75
75
+
"items": {
76
76
+
"type": "ref",
77
77
+
"ref": "com.atproto.repo.strongRef"
78
78
+
},
79
79
+
"maxLength": 32
80
80
+
},
81
81
+
"license": {
82
82
+
"type": "string"
83
83
+
},
84
84
+
"name": {
85
85
+
"type": "string",
86
86
+
"maxLength": 256,
87
87
+
"maxGraphemes": 64
88
88
+
},
89
89
+
"parts": {
90
90
+
"type": "array",
91
91
+
"items": {
92
92
+
"type": "ref",
93
93
+
"ref": "#partInput"
94
94
+
},
95
95
+
"maxLength": 1024
96
96
+
},
97
97
+
"previews": {
98
98
+
"type": "array",
99
99
+
"items": {
100
100
+
"type": "ref",
101
101
+
"ref": "space.polymodel.library.defs#image"
102
102
+
},
103
103
+
"maxLength": 12
104
104
+
},
105
105
+
"summary": {
106
106
+
"type": "string",
107
107
+
"maxLength": 2000,
108
108
+
"maxGraphemes": 300
109
109
+
},
110
110
+
"tags": {
111
111
+
"type": "array",
112
112
+
"items": {
113
113
+
"type": "string",
114
114
+
"maxLength": 64,
115
115
+
"maxGraphemes": 32
116
116
+
},
117
117
+
"maxLength": 16
118
118
+
}
119
119
+
}
120
120
+
},
121
121
+
"modelPublishResult": {
122
122
+
"type": "object",
123
123
+
"required": [
124
124
+
"model",
125
125
+
"parts"
126
126
+
],
127
127
+
"properties": {
128
128
+
"model": {
129
129
+
"type": "ref",
130
130
+
"ref": "#recordRef"
131
131
+
},
132
132
+
"parts": {
133
133
+
"type": "array",
134
134
+
"items": {
135
135
+
"type": "ref",
136
136
+
"ref": "#recordRef"
137
137
+
}
138
138
+
}
139
139
+
}
140
140
+
},
141
141
+
"partInput": {
142
142
+
"type": "object",
143
143
+
"required": [
144
144
+
"name"
145
145
+
],
146
146
+
"properties": {
147
147
+
"derivedFrom": {
148
148
+
"type": "ref",
149
149
+
"ref": "com.atproto.repo.strongRef"
150
150
+
},
151
151
+
"dimensions": {
152
152
+
"type": "ref",
153
153
+
"ref": "space.polymodel.library.defs#bbox"
154
154
+
},
155
155
+
"file": {
156
156
+
"type": "ref",
157
157
+
"ref": "space.polymodel.library.defs#file"
158
158
+
},
159
159
+
"format": {
160
160
+
"type": "string"
161
161
+
},
162
162
+
"intendedToWorkWith": {
163
163
+
"type": "array",
164
164
+
"items": {
165
165
+
"type": "ref",
166
166
+
"ref": "com.atproto.repo.strongRef"
167
167
+
},
168
168
+
"maxLength": 32
169
169
+
},
170
170
+
"name": {
171
171
+
"type": "string",
172
172
+
"maxLength": 256,
173
173
+
"maxGraphemes": 64
174
174
+
},
175
175
+
"notes": {
176
176
+
"type": "string",
177
177
+
"maxLength": 2000,
178
178
+
"maxGraphemes": 300
179
179
+
},
180
180
+
"previews": {
181
181
+
"type": "array",
182
182
+
"items": {
183
183
+
"type": "ref",
184
184
+
"ref": "space.polymodel.library.defs#image"
185
185
+
},
186
186
+
"maxLength": 12
187
187
+
},
188
188
+
"printSettings": {
189
189
+
"type": "array",
190
190
+
"items": {
191
191
+
"type": "string"
192
192
+
},
193
193
+
"maxLength": 50
194
194
+
},
195
195
+
"units": {
196
196
+
"type": "string"
197
197
+
},
198
198
+
"uploadId": {
199
199
+
"type": "string"
200
200
+
}
201
201
+
}
202
202
+
},
203
203
+
"recordRef": {
204
204
+
"type": "object",
205
205
+
"required": [
206
206
+
"uri",
207
207
+
"cid"
208
208
+
],
209
209
+
"properties": {
210
210
+
"cid": {
211
211
+
"type": "string",
212
212
+
"format": "cid"
213
213
+
},
214
214
+
"uri": {
215
215
+
"type": "string",
216
216
+
"format": "at-uri"
217
217
+
}
218
218
+
}
219
219
+
},
220
220
+
"thingInput": {
221
221
+
"type": "object",
222
222
+
"required": [
223
223
+
"name",
224
224
+
"license",
225
225
+
"models"
226
226
+
],
227
227
+
"properties": {
228
228
+
"cover": {
229
229
+
"type": "array",
230
230
+
"items": {
231
231
+
"type": "ref",
232
232
+
"ref": "space.polymodel.library.defs#image"
233
233
+
},
234
234
+
"maxLength": 8
235
235
+
},
236
236
+
"derivedFrom": {
237
237
+
"type": "ref",
238
238
+
"ref": "com.atproto.repo.strongRef"
239
239
+
},
240
240
+
"instructions": {
241
241
+
"type": "array",
242
242
+
"items": {
243
243
+
"type": "string"
244
244
+
},
245
245
+
"maxLength": 100
246
246
+
},
247
247
+
"intendedToWorkWith": {
248
248
+
"type": "array",
249
249
+
"items": {
250
250
+
"type": "ref",
251
251
+
"ref": "com.atproto.repo.strongRef"
252
252
+
},
253
253
+
"maxLength": 32
254
254
+
},
255
255
+
"license": {
256
256
+
"type": "string"
257
257
+
},
258
258
+
"models": {
259
259
+
"type": "array",
260
260
+
"items": {
261
261
+
"type": "ref",
262
262
+
"ref": "#modelInput"
263
263
+
},
264
264
+
"maxLength": 256
265
265
+
},
266
266
+
"name": {
267
267
+
"type": "string",
268
268
+
"maxLength": 256,
269
269
+
"maxGraphemes": 64
270
270
+
},
271
271
+
"previews": {
272
272
+
"type": "array",
273
273
+
"items": {
274
274
+
"type": "ref",
275
275
+
"ref": "space.polymodel.library.defs#image"
276
276
+
},
277
277
+
"maxLength": 12
278
278
+
},
279
279
+
"summary": {
280
280
+
"type": "string",
281
281
+
"maxLength": 2000,
282
282
+
"maxGraphemes": 300
283
283
+
},
284
284
+
"tags": {
285
285
+
"type": "array",
286
286
+
"items": {
287
287
+
"type": "string",
288
288
+
"maxLength": 64,
289
289
+
"maxGraphemes": 32
290
290
+
},
291
291
+
"maxLength": 16
292
292
+
}
293
293
+
}
294
294
+
}
295
295
+
}
296
296
+
}
···
1
1
+
{
2
2
+
"lexicon": 1,
3
3
+
"id": "space.polymodel.library.stageFile",
4
4
+
"defs": {
5
5
+
"main": {
6
6
+
"type": "procedure",
7
7
+
"description": "Stage one raw geometry payload for publishing. The HTTP body is raw bytes, not base64 JSON; metadata is provided by headers. The server chunks and uploads blobs through the authenticated actor's PDS, stores owner-scoped staging metadata, and returns defs#file-compatible metadata.",
8
8
+
"input": {
9
9
+
"encoding": "*/*"
10
10
+
},
11
11
+
"output": {
12
12
+
"encoding": "application/json",
13
13
+
"schema": {
14
14
+
"type": "object",
15
15
+
"required": [
16
16
+
"uploadId",
17
17
+
"file"
18
18
+
],
19
19
+
"properties": {
20
20
+
"file": {
21
21
+
"type": "ref",
22
22
+
"ref": "space.polymodel.library.defs#file"
23
23
+
},
24
24
+
"filename": {
25
25
+
"type": "string"
26
26
+
},
27
27
+
"status": {
28
28
+
"type": "string",
29
29
+
"knownValues": [
30
30
+
"uploaded"
31
31
+
]
32
32
+
},
33
33
+
"uploadId": {
34
34
+
"type": "string"
35
35
+
}
36
36
+
}
37
37
+
}
38
38
+
}
39
39
+
}
40
40
+
}
41
41
+
}
···
1
1
+
{
2
2
+
"lexicon": 1,
3
3
+
"id": "space.polymodel.library.updateThing",
4
4
+
"defs": {
5
5
+
"main": {
6
6
+
"type": "procedure",
7
7
+
"description": "Update an owned thing composition with updated thing/model/part data. Existing model/part inputs with uri are updated in place; inputs without uri create new records. Removed child records are detached from the updated parent records but not deleted, so they can be restored later.",
8
8
+
"input": {
9
9
+
"encoding": "application/json",
10
10
+
"schema": {
11
11
+
"type": "object",
12
12
+
"required": [
13
13
+
"uri",
14
14
+
"thing"
15
15
+
],
16
16
+
"properties": {
17
17
+
"thing": {
18
18
+
"type": "ref",
19
19
+
"ref": "#thingInput"
20
20
+
},
21
21
+
"uri": {
22
22
+
"type": "string",
23
23
+
"format": "at-uri"
24
24
+
}
25
25
+
}
26
26
+
}
27
27
+
},
28
28
+
"output": {
29
29
+
"encoding": "application/json",
30
30
+
"schema": {
31
31
+
"type": "object",
32
32
+
"required": [
33
33
+
"thing",
34
34
+
"models"
35
35
+
],
36
36
+
"properties": {
37
37
+
"models": {
38
38
+
"type": "array",
39
39
+
"items": {
40
40
+
"type": "ref",
41
41
+
"ref": "space.polymodel.library.publishThing#modelPublishResult"
42
42
+
}
43
43
+
},
44
44
+
"thing": {
45
45
+
"type": "ref",
46
46
+
"ref": "space.polymodel.library.publishThing#recordRef"
47
47
+
}
48
48
+
}
49
49
+
}
50
50
+
}
51
51
+
},
52
52
+
"modelInput": {
53
53
+
"type": "object",
54
54
+
"required": [
55
55
+
"name",
56
56
+
"parts"
57
57
+
],
58
58
+
"properties": {
59
59
+
"cover": {
60
60
+
"type": "array",
61
61
+
"items": {
62
62
+
"type": "ref",
63
63
+
"ref": "space.polymodel.library.defs#image"
64
64
+
},
65
65
+
"maxLength": 8
66
66
+
},
67
67
+
"derivedFrom": {
68
68
+
"type": "ref",
69
69
+
"ref": "com.atproto.repo.strongRef"
70
70
+
},
71
71
+
"instructions": {
72
72
+
"type": "array",
73
73
+
"items": {
74
74
+
"type": "string"
75
75
+
},
76
76
+
"maxLength": 100
77
77
+
},
78
78
+
"intendedToWorkWith": {
79
79
+
"type": "array",
80
80
+
"items": {
81
81
+
"type": "ref",
82
82
+
"ref": "com.atproto.repo.strongRef"
83
83
+
},
84
84
+
"maxLength": 32
85
85
+
},
86
86
+
"license": {
87
87
+
"type": "string"
88
88
+
},
89
89
+
"name": {
90
90
+
"type": "string",
91
91
+
"maxLength": 256,
92
92
+
"maxGraphemes": 64
93
93
+
},
94
94
+
"parts": {
95
95
+
"type": "array",
96
96
+
"items": {
97
97
+
"type": "ref",
98
98
+
"ref": "#partInput"
99
99
+
},
100
100
+
"maxLength": 1024
101
101
+
},
102
102
+
"previews": {
103
103
+
"type": "array",
104
104
+
"items": {
105
105
+
"type": "ref",
106
106
+
"ref": "space.polymodel.library.defs#image"
107
107
+
},
108
108
+
"maxLength": 12
109
109
+
},
110
110
+
"summary": {
111
111
+
"type": "string",
112
112
+
"maxLength": 2000,
113
113
+
"maxGraphemes": 300
114
114
+
},
115
115
+
"tags": {
116
116
+
"type": "array",
117
117
+
"items": {
118
118
+
"type": "string",
119
119
+
"maxLength": 64,
120
120
+
"maxGraphemes": 32
121
121
+
},
122
122
+
"maxLength": 16
123
123
+
},
124
124
+
"uri": {
125
125
+
"type": "string",
126
126
+
"format": "at-uri"
127
127
+
}
128
128
+
}
129
129
+
},
130
130
+
"partInput": {
131
131
+
"type": "object",
132
132
+
"required": [
133
133
+
"name"
134
134
+
],
135
135
+
"properties": {
136
136
+
"derivedFrom": {
137
137
+
"type": "ref",
138
138
+
"ref": "com.atproto.repo.strongRef"
139
139
+
},
140
140
+
"dimensions": {
141
141
+
"type": "ref",
142
142
+
"ref": "space.polymodel.library.defs#bbox"
143
143
+
},
144
144
+
"file": {
145
145
+
"type": "ref",
146
146
+
"ref": "space.polymodel.library.defs#file"
147
147
+
},
148
148
+
"format": {
149
149
+
"type": "string"
150
150
+
},
151
151
+
"intendedToWorkWith": {
152
152
+
"type": "array",
153
153
+
"items": {
154
154
+
"type": "ref",
155
155
+
"ref": "com.atproto.repo.strongRef"
156
156
+
},
157
157
+
"maxLength": 32
158
158
+
},
159
159
+
"name": {
160
160
+
"type": "string",
161
161
+
"maxLength": 256,
162
162
+
"maxGraphemes": 64
163
163
+
},
164
164
+
"notes": {
165
165
+
"type": "string",
166
166
+
"maxLength": 2000,
167
167
+
"maxGraphemes": 300
168
168
+
},
169
169
+
"previews": {
170
170
+
"type": "array",
171
171
+
"items": {
172
172
+
"type": "ref",
173
173
+
"ref": "space.polymodel.library.defs#image"
174
174
+
},
175
175
+
"maxLength": 12
176
176
+
},
177
177
+
"printSettings": {
178
178
+
"type": "array",
179
179
+
"items": {
180
180
+
"type": "string"
181
181
+
},
182
182
+
"maxLength": 50
183
183
+
},
184
184
+
"units": {
185
185
+
"type": "string"
186
186
+
},
187
187
+
"uploadId": {
188
188
+
"type": "string"
189
189
+
},
190
190
+
"uri": {
191
191
+
"type": "string",
192
192
+
"format": "at-uri"
193
193
+
}
194
194
+
}
195
195
+
},
196
196
+
"thingInput": {
197
197
+
"type": "object",
198
198
+
"required": [
199
199
+
"name",
200
200
+
"license",
201
201
+
"models"
202
202
+
],
203
203
+
"properties": {
204
204
+
"cover": {
205
205
+
"type": "array",
206
206
+
"items": {
207
207
+
"type": "ref",
208
208
+
"ref": "space.polymodel.library.defs#image"
209
209
+
},
210
210
+
"maxLength": 8
211
211
+
},
212
212
+
"derivedFrom": {
213
213
+
"type": "ref",
214
214
+
"ref": "com.atproto.repo.strongRef"
215
215
+
},
216
216
+
"instructions": {
217
217
+
"type": "array",
218
218
+
"items": {
219
219
+
"type": "string"
220
220
+
},
221
221
+
"maxLength": 100
222
222
+
},
223
223
+
"intendedToWorkWith": {
224
224
+
"type": "array",
225
225
+
"items": {
226
226
+
"type": "ref",
227
227
+
"ref": "com.atproto.repo.strongRef"
228
228
+
},
229
229
+
"maxLength": 32
230
230
+
},
231
231
+
"license": {
232
232
+
"type": "string"
233
233
+
},
234
234
+
"models": {
235
235
+
"type": "array",
236
236
+
"items": {
237
237
+
"type": "ref",
238
238
+
"ref": "#modelInput"
239
239
+
},
240
240
+
"maxLength": 256
241
241
+
},
242
242
+
"name": {
243
243
+
"type": "string",
244
244
+
"maxLength": 256,
245
245
+
"maxGraphemes": 64
246
246
+
},
247
247
+
"previews": {
248
248
+
"type": "array",
249
249
+
"items": {
250
250
+
"type": "ref",
251
251
+
"ref": "space.polymodel.library.defs#image"
252
252
+
},
253
253
+
"maxLength": 12
254
254
+
},
255
255
+
"summary": {
256
256
+
"type": "string",
257
257
+
"maxLength": 2000,
258
258
+
"maxGraphemes": 300
259
259
+
},
260
260
+
"tags": {
261
261
+
"type": "array",
262
262
+
"items": {
263
263
+
"type": "string",
264
264
+
"maxLength": 64,
265
265
+
"maxGraphemes": 32
266
266
+
},
267
267
+
"maxLength": 16
268
268
+
}
269
269
+
}
270
270
+
}
271
271
+
}
272
272
+
}
···
6
6
// Any manual changes will be overwritten on the next regeneration.
7
7
8
8
//! Generated bindings for the `space.polymodel.actor` Lexicon namespace/module.
9
9
+
pub mod bootstrap_profile;
9
10
pub mod get_profile;
10
11
pub mod get_session;
11
12
pub mod profile;
···
1
1
+
// @generated by jacquard-lexicon. DO NOT EDIT.
2
2
+
//
3
3
+
// Lexicon: space.polymodel.actor.bootstrapProfile
4
4
+
//
5
5
+
// This file was automatically generated from Lexicon schemas.
6
6
+
// Any manual changes will be overwritten on the next regeneration.
7
7
+
8
8
+
#[allow(unused_imports)]
9
9
+
use alloc::collections::BTreeMap;
10
10
+
11
11
+
#[allow(unused_imports)]
12
12
+
use core::marker::PhantomData;
13
13
+
use jacquard_common::{CowStr, BosStr, DefaultStr, FromStaticStr};
14
14
+
use jacquard_common::deps::smol_str::SmolStr;
15
15
+
use jacquard_common::types::string::{AtUri, Cid};
16
16
+
use jacquard_common::types::value::Data;
17
17
+
use jacquard_derive::IntoStatic;
18
18
+
use serde::{Serialize, Deserialize};
19
19
+
use crate::space_polymodel::actor::ProfileView;
20
20
+
21
21
+
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic)]
22
22
+
#[serde(rename_all = "camelCase", bound(deserialize = "S: Deserialize<'de> + BosStr"))]
23
23
+
pub struct BootstrapProfileOutput<S: BosStr = DefaultStr> {
24
24
+
#[serde(skip_serializing_if = "Option::is_none")]
25
25
+
pub cid: Option<Cid<S>>,
26
26
+
pub profile: ProfileView<S>,
27
27
+
pub status: BootstrapProfileOutputStatus<S>,
28
28
+
#[serde(skip_serializing_if = "Option::is_none")]
29
29
+
pub uri: Option<AtUri<S>>,
30
30
+
#[serde(flatten, default, skip_serializing_if = "Option::is_none")]
31
31
+
pub extra_data: Option<BTreeMap<SmolStr, Data<S>>>,
32
32
+
}
33
33
+
34
34
+
35
35
+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
36
36
+
pub enum BootstrapProfileOutputStatus<S: BosStr = DefaultStr> {
37
37
+
ExistingLocal,
38
38
+
ExistingRemote,
39
39
+
Created,
40
40
+
Other(S),
41
41
+
}
42
42
+
43
43
+
impl<S: BosStr> BootstrapProfileOutputStatus<S> {
44
44
+
pub fn as_str(&self) -> &str {
45
45
+
match self {
46
46
+
Self::ExistingLocal => "existingLocal",
47
47
+
Self::ExistingRemote => "existingRemote",
48
48
+
Self::Created => "created",
49
49
+
Self::Other(s) => s.as_ref(),
50
50
+
}
51
51
+
}
52
52
+
/// Construct from a string-like value, matching known values.
53
53
+
pub fn from_value(s: S) -> Self {
54
54
+
match s.as_ref() {
55
55
+
"existingLocal" => Self::ExistingLocal,
56
56
+
"existingRemote" => Self::ExistingRemote,
57
57
+
"created" => Self::Created,
58
58
+
_ => Self::Other(s),
59
59
+
}
60
60
+
}
61
61
+
}
62
62
+
63
63
+
impl<S: BosStr> core::fmt::Display for BootstrapProfileOutputStatus<S> {
64
64
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
65
65
+
write!(f, "{}", self.as_str())
66
66
+
}
67
67
+
}
68
68
+
69
69
+
impl<S: BosStr> AsRef<str> for BootstrapProfileOutputStatus<S> {
70
70
+
fn as_ref(&self) -> &str {
71
71
+
self.as_str()
72
72
+
}
73
73
+
}
74
74
+
75
75
+
impl<S: BosStr> Serialize for BootstrapProfileOutputStatus<S> {
76
76
+
fn serialize<Ser>(&self, serializer: Ser) -> Result<Ser::Ok, Ser::Error>
77
77
+
where
78
78
+
Ser: serde::Serializer,
79
79
+
{
80
80
+
serializer.serialize_str(self.as_str())
81
81
+
}
82
82
+
}
83
83
+
84
84
+
impl<'de, S: Deserialize<'de> + BosStr> Deserialize<'de>
85
85
+
for BootstrapProfileOutputStatus<S> {
86
86
+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
87
87
+
where
88
88
+
D: serde::Deserializer<'de>,
89
89
+
{
90
90
+
let s = S::deserialize(deserializer)?;
91
91
+
Ok(Self::from_value(s))
92
92
+
}
93
93
+
}
94
94
+
95
95
+
impl<S: BosStr + Default> Default for BootstrapProfileOutputStatus<S> {
96
96
+
fn default() -> Self {
97
97
+
Self::Other(Default::default())
98
98
+
}
99
99
+
}
100
100
+
101
101
+
impl<S: BosStr> jacquard_common::IntoStatic for BootstrapProfileOutputStatus<S>
102
102
+
where
103
103
+
S: BosStr + jacquard_common::IntoStatic,
104
104
+
S::Output: BosStr,
105
105
+
{
106
106
+
type Output = BootstrapProfileOutputStatus<S::Output>;
107
107
+
fn into_static(self) -> Self::Output {
108
108
+
match self {
109
109
+
BootstrapProfileOutputStatus::ExistingLocal => {
110
110
+
BootstrapProfileOutputStatus::ExistingLocal
111
111
+
}
112
112
+
BootstrapProfileOutputStatus::ExistingRemote => {
113
113
+
BootstrapProfileOutputStatus::ExistingRemote
114
114
+
}
115
115
+
BootstrapProfileOutputStatus::Created => {
116
116
+
BootstrapProfileOutputStatus::Created
117
117
+
}
118
118
+
BootstrapProfileOutputStatus::Other(v) => {
119
119
+
BootstrapProfileOutputStatus::Other(v.into_static())
120
120
+
}
121
121
+
}
122
122
+
}
123
123
+
}
124
124
+
125
125
+
/** Request marker for the `space.polymodel.actor.bootstrapProfile` procedure.
126
126
+
127
127
+
This endpoint has no request parameters or input body; send this marker with `jacquard::Client`.*/
128
128
+
129
129
+
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic, Copy)]
130
130
+
pub struct BootstrapProfile;
131
131
+
/** Response marker for the `space.polymodel.actor.bootstrapProfile` procedure.
132
132
+
133
133
+
Implements `jacquard_common::xrpc::XrpcResp`; successful bodies decode as `Self::Output<S>`, which is `BootstrapProfileOutput<S>` for this endpoint.*/
134
134
+
pub struct BootstrapProfileResponse;
135
135
+
impl jacquard_common::xrpc::XrpcResp for BootstrapProfileResponse {
136
136
+
const NSID: &'static str = "space.polymodel.actor.bootstrapProfile";
137
137
+
const ENCODING: &'static str = "application/json";
138
138
+
type Output<S: BosStr> = BootstrapProfileOutput<S>;
139
139
+
type Err = jacquard_common::xrpc::GenericError;
140
140
+
}
141
141
+
142
142
+
impl jacquard_common::xrpc::XrpcRequest for BootstrapProfile {
143
143
+
const NSID: &'static str = "space.polymodel.actor.bootstrapProfile";
144
144
+
const METHOD: jacquard_common::xrpc::XrpcMethod = jacquard_common::xrpc::XrpcMethod::Procedure(
145
145
+
"application/json",
146
146
+
);
147
147
+
type Response = BootstrapProfileResponse;
148
148
+
}
149
149
+
150
150
+
/** Endpoint marker for the `space.polymodel.actor.bootstrapProfile` procedure.
151
151
+
152
152
+
Path: `/xrpc/space.polymodel.actor.bootstrapProfile`. The request payload type is `BootstrapProfile`; use this marker with lower-level `XrpcEndpoint` APIs when you need endpoint metadata.*/
153
153
+
pub struct BootstrapProfileRequest;
154
154
+
impl jacquard_common::xrpc::XrpcEndpoint for BootstrapProfileRequest {
155
155
+
const PATH: &'static str = "/xrpc/space.polymodel.actor.bootstrapProfile";
156
156
+
const METHOD: jacquard_common::xrpc::XrpcMethod = jacquard_common::xrpc::XrpcMethod::Procedure(
157
157
+
"application/json",
158
158
+
);
159
159
+
type Request<S: BosStr> = BootstrapProfile;
160
160
+
type Response = BootstrapProfileResponse;
161
161
+
}
···
4
4
// Any manual changes will be overwritten on the next regeneration.
5
5
6
6
//! Generated bindings for the `space.polymodel.graph` Lexicon namespace/module.
7
7
+
pub mod create_like;
8
8
+
pub mod create_save;
9
9
+
pub mod create_tag;
10
10
+
pub mod delete_like;
11
11
+
pub mod delete_save;
12
12
+
pub mod delete_tag;
7
13
pub mod get_list;
8
14
pub mod like;
9
15
pub mod list;
···
1
1
+
// @generated by jacquard-lexicon. DO NOT EDIT.
2
2
+
//
3
3
+
// Lexicon: space.polymodel.graph.createLike
4
4
+
//
5
5
+
// This file was automatically generated from Lexicon schemas.
6
6
+
// Any manual changes will be overwritten on the next regeneration.
7
7
+
8
8
+
#[allow(unused_imports)]
9
9
+
use alloc::collections::BTreeMap;
10
10
+
11
11
+
#[allow(unused_imports)]
12
12
+
use core::marker::PhantomData;
13
13
+
use jacquard_common::{BosStr, DefaultStr, FromStaticStr};
14
14
+
use jacquard_common::deps::smol_str::SmolStr;
15
15
+
use jacquard_common::types::string::{AtUri, Cid};
16
16
+
use jacquard_common::types::value::Data;
17
17
+
use jacquard_derive::IntoStatic;
18
18
+
use serde::{Serialize, Deserialize};
19
19
+
use crate::com_atproto::repo::strong_ref::StrongRef;
20
20
+
21
21
+
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic)]
22
22
+
#[serde(rename_all = "camelCase", bound(deserialize = "S: Deserialize<'de> + BosStr"))]
23
23
+
pub struct CreateLike<S: BosStr = DefaultStr> {
24
24
+
pub subject: StrongRef<S>,
25
25
+
#[serde(flatten, default, skip_serializing_if = "Option::is_none")]
26
26
+
pub extra_data: Option<BTreeMap<SmolStr, Data<S>>>,
27
27
+
}
28
28
+
29
29
+
30
30
+
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic)]
31
31
+
#[serde(rename_all = "camelCase", bound(deserialize = "S: Deserialize<'de> + BosStr"))]
32
32
+
pub struct CreateLikeOutput<S: BosStr = DefaultStr> {
33
33
+
pub cid: Cid<S>,
34
34
+
#[serde(skip_serializing_if = "Option::is_none")]
35
35
+
pub created: Option<bool>,
36
36
+
pub uri: AtUri<S>,
37
37
+
#[serde(flatten, default, skip_serializing_if = "Option::is_none")]
38
38
+
pub extra_data: Option<BTreeMap<SmolStr, Data<S>>>,
39
39
+
}
40
40
+
41
41
+
/** Response marker for the `space.polymodel.graph.createLike` procedure.
42
42
+
43
43
+
Implements `jacquard_common::xrpc::XrpcResp`; successful bodies decode as `Self::Output<S>`, which is `CreateLikeOutput<S>` for this endpoint.*/
44
44
+
pub struct CreateLikeResponse;
45
45
+
impl jacquard_common::xrpc::XrpcResp for CreateLikeResponse {
46
46
+
const NSID: &'static str = "space.polymodel.graph.createLike";
47
47
+
const ENCODING: &'static str = "application/json";
48
48
+
type Output<S: BosStr> = CreateLikeOutput<S>;
49
49
+
type Err = jacquard_common::xrpc::GenericError;
50
50
+
}
51
51
+
52
52
+
impl<S: BosStr> jacquard_common::xrpc::XrpcRequest for CreateLike<S> {
53
53
+
const NSID: &'static str = "space.polymodel.graph.createLike";
54
54
+
const METHOD: jacquard_common::xrpc::XrpcMethod = jacquard_common::xrpc::XrpcMethod::Procedure(
55
55
+
"application/json",
56
56
+
);
57
57
+
type Response = CreateLikeResponse;
58
58
+
}
59
59
+
60
60
+
/** Endpoint marker for the `space.polymodel.graph.createLike` procedure.
61
61
+
62
62
+
Path: `/xrpc/space.polymodel.graph.createLike`. The request payload type is `CreateLike<S>`; send that request with `jacquard::Client` or use this marker through lower-level `XrpcEndpoint` APIs.*/
63
63
+
pub struct CreateLikeRequest;
64
64
+
impl jacquard_common::xrpc::XrpcEndpoint for CreateLikeRequest {
65
65
+
const PATH: &'static str = "/xrpc/space.polymodel.graph.createLike";
66
66
+
const METHOD: jacquard_common::xrpc::XrpcMethod = jacquard_common::xrpc::XrpcMethod::Procedure(
67
67
+
"application/json",
68
68
+
);
69
69
+
type Request<S: BosStr> = CreateLike<S>;
70
70
+
type Response = CreateLikeResponse;
71
71
+
}
72
72
+
73
73
+
pub mod create_like_state {
74
74
+
75
75
+
pub use crate::builder_types::{Set, Unset, IsSet, IsUnset};
76
76
+
#[allow(unused)]
77
77
+
use ::core::marker::PhantomData;
78
78
+
mod sealed {
79
79
+
pub trait Sealed {}
80
80
+
}
81
81
+
/// State trait tracking which required fields have been set
82
82
+
pub trait State: sealed::Sealed {
83
83
+
type Subject;
84
84
+
}
85
85
+
/// Empty state - all required fields are unset
86
86
+
pub struct Empty(());
87
87
+
impl sealed::Sealed for Empty {}
88
88
+
impl State for Empty {
89
89
+
type Subject = Unset;
90
90
+
}
91
91
+
///State transition - sets the `subject` field to Set
92
92
+
pub struct SetSubject<St: State = Empty>(PhantomData<fn() -> St>);
93
93
+
impl<St: State> sealed::Sealed for SetSubject<St> {}
94
94
+
impl<St: State> State for SetSubject<St> {
95
95
+
type Subject = Set<members::subject>;
96
96
+
}
97
97
+
/// Marker types for field names
98
98
+
#[allow(non_camel_case_types)]
99
99
+
pub mod members {
100
100
+
///Marker type for the `subject` field
101
101
+
pub struct subject(());
102
102
+
}
103
103
+
}
104
104
+
105
105
+
/// Builder for constructing an instance of this type.
106
106
+
pub struct CreateLikeBuilder<St: create_like_state::State, S: BosStr = DefaultStr> {
107
107
+
_state: PhantomData<fn() -> St>,
108
108
+
_fields: (Option<StrongRef<S>>,),
109
109
+
_type: PhantomData<fn() -> S>,
110
110
+
}
111
111
+
112
112
+
impl CreateLike<DefaultStr> {
113
113
+
/// Create a new builder for this type, using the default string type (DefaultStr = SmolStr) if needed
114
114
+
pub fn new() -> CreateLikeBuilder<create_like_state::Empty, DefaultStr> {
115
115
+
CreateLikeBuilder::new()
116
116
+
}
117
117
+
}
118
118
+
119
119
+
impl<S: BosStr> CreateLike<S> {
120
120
+
/// Create a new builder for this type
121
121
+
pub fn builder() -> CreateLikeBuilder<create_like_state::Empty, S> {
122
122
+
CreateLikeBuilder::builder()
123
123
+
}
124
124
+
}
125
125
+
126
126
+
impl CreateLikeBuilder<create_like_state::Empty, DefaultStr> {
127
127
+
/// Create a new builder with all fields unset, using the default string type, if needed
128
128
+
pub fn new() -> Self {
129
129
+
CreateLikeBuilder {
130
130
+
_state: PhantomData,
131
131
+
_fields: (None,),
132
132
+
_type: PhantomData,
133
133
+
}
134
134
+
}
135
135
+
}
136
136
+
137
137
+
impl<S: BosStr> CreateLikeBuilder<create_like_state::Empty, S> {
138
138
+
/// Create a new builder with all fields unset
139
139
+
pub fn builder() -> Self {
140
140
+
CreateLikeBuilder {
141
141
+
_state: PhantomData,
142
142
+
_fields: (None,),
143
143
+
_type: PhantomData,
144
144
+
}
145
145
+
}
146
146
+
}
147
147
+
148
148
+
impl<St, S: BosStr> CreateLikeBuilder<St, S>
149
149
+
where
150
150
+
St: create_like_state::State,
151
151
+
St::Subject: create_like_state::IsUnset,
152
152
+
{
153
153
+
/// Set the `subject` field (required)
154
154
+
pub fn subject(
155
155
+
mut self,
156
156
+
value: impl Into<StrongRef<S>>,
157
157
+
) -> CreateLikeBuilder<create_like_state::SetSubject<St>, S> {
158
158
+
self._fields.0 = Option::Some(value.into());
159
159
+
CreateLikeBuilder {
160
160
+
_state: PhantomData,
161
161
+
_fields: self._fields,
162
162
+
_type: PhantomData,
163
163
+
}
164
164
+
}
165
165
+
}
166
166
+
167
167
+
impl<St, S: BosStr> CreateLikeBuilder<St, S>
168
168
+
where
169
169
+
St: create_like_state::State,
170
170
+
St::Subject: create_like_state::IsSet,
171
171
+
{
172
172
+
/// Build the final struct.
173
173
+
pub fn build(self) -> CreateLike<S> {
174
174
+
CreateLike {
175
175
+
subject: self._fields.0.unwrap(),
176
176
+
extra_data: Default::default(),
177
177
+
}
178
178
+
}
179
179
+
/// Build the final struct with custom extra_data.
180
180
+
pub fn build_with_data(
181
181
+
self,
182
182
+
extra_data: BTreeMap<SmolStr, Data<S>>,
183
183
+
) -> CreateLike<S> {
184
184
+
CreateLike {
185
185
+
subject: self._fields.0.unwrap(),
186
186
+
extra_data: Some(extra_data),
187
187
+
}
188
188
+
}
189
189
+
}
···
1
1
+
// @generated by jacquard-lexicon. DO NOT EDIT.
2
2
+
//
3
3
+
// Lexicon: space.polymodel.graph.createSave
4
4
+
//
5
5
+
// This file was automatically generated from Lexicon schemas.
6
6
+
// Any manual changes will be overwritten on the next regeneration.
7
7
+
8
8
+
#[allow(unused_imports)]
9
9
+
use alloc::collections::BTreeMap;
10
10
+
11
11
+
#[allow(unused_imports)]
12
12
+
use core::marker::PhantomData;
13
13
+
use jacquard_common::{CowStr, BosStr, DefaultStr, FromStaticStr};
14
14
+
use jacquard_common::deps::smol_str::SmolStr;
15
15
+
use jacquard_common::types::string::{AtUri, Cid};
16
16
+
use jacquard_common::types::value::Data;
17
17
+
use jacquard_derive::IntoStatic;
18
18
+
use serde::{Serialize, Deserialize};
19
19
+
use crate::com_atproto::repo::strong_ref::StrongRef;
20
20
+
21
21
+
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic)]
22
22
+
#[serde(rename_all = "camelCase", bound(deserialize = "S: Deserialize<'de> + BosStr"))]
23
23
+
pub struct CreateSave<S: BosStr = DefaultStr> {
24
24
+
#[serde(skip_serializing_if = "Option::is_none")]
25
25
+
pub note: Option<S>,
26
26
+
pub subject: StrongRef<S>,
27
27
+
#[serde(flatten, default, skip_serializing_if = "Option::is_none")]
28
28
+
pub extra_data: Option<BTreeMap<SmolStr, Data<S>>>,
29
29
+
}
30
30
+
31
31
+
32
32
+
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic)]
33
33
+
#[serde(rename_all = "camelCase", bound(deserialize = "S: Deserialize<'de> + BosStr"))]
34
34
+
pub struct CreateSaveOutput<S: BosStr = DefaultStr> {
35
35
+
pub cid: Cid<S>,
36
36
+
#[serde(skip_serializing_if = "Option::is_none")]
37
37
+
pub created: Option<bool>,
38
38
+
pub uri: AtUri<S>,
39
39
+
#[serde(flatten, default, skip_serializing_if = "Option::is_none")]
40
40
+
pub extra_data: Option<BTreeMap<SmolStr, Data<S>>>,
41
41
+
}
42
42
+
43
43
+
/** Response marker for the `space.polymodel.graph.createSave` procedure.
44
44
+
45
45
+
Implements `jacquard_common::xrpc::XrpcResp`; successful bodies decode as `Self::Output<S>`, which is `CreateSaveOutput<S>` for this endpoint.*/
46
46
+
pub struct CreateSaveResponse;
47
47
+
impl jacquard_common::xrpc::XrpcResp for CreateSaveResponse {
48
48
+
const NSID: &'static str = "space.polymodel.graph.createSave";
49
49
+
const ENCODING: &'static str = "application/json";
50
50
+
type Output<S: BosStr> = CreateSaveOutput<S>;
51
51
+
type Err = jacquard_common::xrpc::GenericError;
52
52
+
}
53
53
+
54
54
+
impl<S: BosStr> jacquard_common::xrpc::XrpcRequest for CreateSave<S> {
55
55
+
const NSID: &'static str = "space.polymodel.graph.createSave";
56
56
+
const METHOD: jacquard_common::xrpc::XrpcMethod = jacquard_common::xrpc::XrpcMethod::Procedure(
57
57
+
"application/json",
58
58
+
);
59
59
+
type Response = CreateSaveResponse;
60
60
+
}
61
61
+
62
62
+
/** Endpoint marker for the `space.polymodel.graph.createSave` procedure.
63
63
+
64
64
+
Path: `/xrpc/space.polymodel.graph.createSave`. The request payload type is `CreateSave<S>`; send that request with `jacquard::Client` or use this marker through lower-level `XrpcEndpoint` APIs.*/
65
65
+
pub struct CreateSaveRequest;
66
66
+
impl jacquard_common::xrpc::XrpcEndpoint for CreateSaveRequest {
67
67
+
const PATH: &'static str = "/xrpc/space.polymodel.graph.createSave";
68
68
+
const METHOD: jacquard_common::xrpc::XrpcMethod = jacquard_common::xrpc::XrpcMethod::Procedure(
69
69
+
"application/json",
70
70
+
);
71
71
+
type Request<S: BosStr> = CreateSave<S>;
72
72
+
type Response = CreateSaveResponse;
73
73
+
}
74
74
+
75
75
+
pub mod create_save_state {
76
76
+
77
77
+
pub use crate::builder_types::{Set, Unset, IsSet, IsUnset};
78
78
+
#[allow(unused)]
79
79
+
use ::core::marker::PhantomData;
80
80
+
mod sealed {
81
81
+
pub trait Sealed {}
82
82
+
}
83
83
+
/// State trait tracking which required fields have been set
84
84
+
pub trait State: sealed::Sealed {
85
85
+
type Subject;
86
86
+
}
87
87
+
/// Empty state - all required fields are unset
88
88
+
pub struct Empty(());
89
89
+
impl sealed::Sealed for Empty {}
90
90
+
impl State for Empty {
91
91
+
type Subject = Unset;
92
92
+
}
93
93
+
///State transition - sets the `subject` field to Set
94
94
+
pub struct SetSubject<St: State = Empty>(PhantomData<fn() -> St>);
95
95
+
impl<St: State> sealed::Sealed for SetSubject<St> {}
96
96
+
impl<St: State> State for SetSubject<St> {
97
97
+
type Subject = Set<members::subject>;
98
98
+
}
99
99
+
/// Marker types for field names
100
100
+
#[allow(non_camel_case_types)]
101
101
+
pub mod members {
102
102
+
///Marker type for the `subject` field
103
103
+
pub struct subject(());
104
104
+
}
105
105
+
}
106
106
+
107
107
+
/// Builder for constructing an instance of this type.
108
108
+
pub struct CreateSaveBuilder<St: create_save_state::State, S: BosStr = DefaultStr> {
109
109
+
_state: PhantomData<fn() -> St>,
110
110
+
_fields: (Option<S>, Option<StrongRef<S>>),
111
111
+
_type: PhantomData<fn() -> S>,
112
112
+
}
113
113
+
114
114
+
impl CreateSave<DefaultStr> {
115
115
+
/// Create a new builder for this type, using the default string type (DefaultStr = SmolStr) if needed
116
116
+
pub fn new() -> CreateSaveBuilder<create_save_state::Empty, DefaultStr> {
117
117
+
CreateSaveBuilder::new()
118
118
+
}
119
119
+
}
120
120
+
121
121
+
impl<S: BosStr> CreateSave<S> {
122
122
+
/// Create a new builder for this type
123
123
+
pub fn builder() -> CreateSaveBuilder<create_save_state::Empty, S> {
124
124
+
CreateSaveBuilder::builder()
125
125
+
}
126
126
+
}
127
127
+
128
128
+
impl CreateSaveBuilder<create_save_state::Empty, DefaultStr> {
129
129
+
/// Create a new builder with all fields unset, using the default string type, if needed
130
130
+
pub fn new() -> Self {
131
131
+
CreateSaveBuilder {
132
132
+
_state: PhantomData,
133
133
+
_fields: (None, None),
134
134
+
_type: PhantomData,
135
135
+
}
136
136
+
}
137
137
+
}
138
138
+
139
139
+
impl<S: BosStr> CreateSaveBuilder<create_save_state::Empty, S> {
140
140
+
/// Create a new builder with all fields unset
141
141
+
pub fn builder() -> Self {
142
142
+
CreateSaveBuilder {
143
143
+
_state: PhantomData,
144
144
+
_fields: (None, None),
145
145
+
_type: PhantomData,
146
146
+
}
147
147
+
}
148
148
+
}
149
149
+
150
150
+
impl<St: create_save_state::State, S: BosStr> CreateSaveBuilder<St, S> {
151
151
+
/// Set the `note` field (optional)
152
152
+
pub fn note(mut self, value: impl Into<Option<S>>) -> Self {
153
153
+
self._fields.0 = value.into();
154
154
+
self
155
155
+
}
156
156
+
/// Set the `note` field to an Option value (optional)
157
157
+
pub fn maybe_note(mut self, value: Option<S>) -> Self {
158
158
+
self._fields.0 = value;
159
159
+
self
160
160
+
}
161
161
+
}
162
162
+
163
163
+
impl<St, S: BosStr> CreateSaveBuilder<St, S>
164
164
+
where
165
165
+
St: create_save_state::State,
166
166
+
St::Subject: create_save_state::IsUnset,
167
167
+
{
168
168
+
/// Set the `subject` field (required)
169
169
+
pub fn subject(
170
170
+
mut self,
171
171
+
value: impl Into<StrongRef<S>>,
172
172
+
) -> CreateSaveBuilder<create_save_state::SetSubject<St>, S> {
173
173
+
self._fields.1 = Option::Some(value.into());
174
174
+
CreateSaveBuilder {
175
175
+
_state: PhantomData,
176
176
+
_fields: self._fields,
177
177
+
_type: PhantomData,
178
178
+
}
179
179
+
}
180
180
+
}
181
181
+
182
182
+
impl<St, S: BosStr> CreateSaveBuilder<St, S>
183
183
+
where
184
184
+
St: create_save_state::State,
185
185
+
St::Subject: create_save_state::IsSet,
186
186
+
{
187
187
+
/// Build the final struct.
188
188
+
pub fn build(self) -> CreateSave<S> {
189
189
+
CreateSave {
190
190
+
note: self._fields.0,
191
191
+
subject: self._fields.1.unwrap(),
192
192
+
extra_data: Default::default(),
193
193
+
}
194
194
+
}
195
195
+
/// Build the final struct with custom extra_data.
196
196
+
pub fn build_with_data(
197
197
+
self,
198
198
+
extra_data: BTreeMap<SmolStr, Data<S>>,
199
199
+
) -> CreateSave<S> {
200
200
+
CreateSave {
201
201
+
note: self._fields.0,
202
202
+
subject: self._fields.1.unwrap(),
203
203
+
extra_data: Some(extra_data),
204
204
+
}
205
205
+
}
206
206
+
}
···
1
1
+
// @generated by jacquard-lexicon. DO NOT EDIT.
2
2
+
//
3
3
+
// Lexicon: space.polymodel.graph.createTag
4
4
+
//
5
5
+
// This file was automatically generated from Lexicon schemas.
6
6
+
// Any manual changes will be overwritten on the next regeneration.
7
7
+
8
8
+
#[allow(unused_imports)]
9
9
+
use alloc::collections::BTreeMap;
10
10
+
11
11
+
#[allow(unused_imports)]
12
12
+
use core::marker::PhantomData;
13
13
+
use jacquard_common::{CowStr, BosStr, DefaultStr, FromStaticStr};
14
14
+
use jacquard_common::deps::smol_str::SmolStr;
15
15
+
use jacquard_common::types::string::{AtUri, Cid};
16
16
+
use jacquard_common::types::value::Data;
17
17
+
use jacquard_derive::IntoStatic;
18
18
+
use serde::{Serialize, Deserialize};
19
19
+
use crate::com_atproto::repo::strong_ref::StrongRef;
20
20
+
21
21
+
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic)]
22
22
+
#[serde(rename_all = "camelCase", bound(deserialize = "S: Deserialize<'de> + BosStr"))]
23
23
+
pub struct CreateTag<S: BosStr = DefaultStr> {
24
24
+
pub subject: StrongRef<S>,
25
25
+
pub tag: S,
26
26
+
#[serde(flatten, default, skip_serializing_if = "Option::is_none")]
27
27
+
pub extra_data: Option<BTreeMap<SmolStr, Data<S>>>,
28
28
+
}
29
29
+
30
30
+
31
31
+
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic)]
32
32
+
#[serde(rename_all = "camelCase", bound(deserialize = "S: Deserialize<'de> + BosStr"))]
33
33
+
pub struct CreateTagOutput<S: BosStr = DefaultStr> {
34
34
+
pub cid: Cid<S>,
35
35
+
#[serde(skip_serializing_if = "Option::is_none")]
36
36
+
pub created: Option<bool>,
37
37
+
pub uri: AtUri<S>,
38
38
+
#[serde(flatten, default, skip_serializing_if = "Option::is_none")]
39
39
+
pub extra_data: Option<BTreeMap<SmolStr, Data<S>>>,
40
40
+
}
41
41
+
42
42
+
/** Response marker for the `space.polymodel.graph.createTag` procedure.
43
43
+
44
44
+
Implements `jacquard_common::xrpc::XrpcResp`; successful bodies decode as `Self::Output<S>`, which is `CreateTagOutput<S>` for this endpoint.*/
45
45
+
pub struct CreateTagResponse;
46
46
+
impl jacquard_common::xrpc::XrpcResp for CreateTagResponse {
47
47
+
const NSID: &'static str = "space.polymodel.graph.createTag";
48
48
+
const ENCODING: &'static str = "application/json";
49
49
+
type Output<S: BosStr> = CreateTagOutput<S>;
50
50
+
type Err = jacquard_common::xrpc::GenericError;
51
51
+
}
52
52
+
53
53
+
impl<S: BosStr> jacquard_common::xrpc::XrpcRequest for CreateTag<S> {
54
54
+
const NSID: &'static str = "space.polymodel.graph.createTag";
55
55
+
const METHOD: jacquard_common::xrpc::XrpcMethod = jacquard_common::xrpc::XrpcMethod::Procedure(
56
56
+
"application/json",
57
57
+
);
58
58
+
type Response = CreateTagResponse;
59
59
+
}
60
60
+
61
61
+
/** Endpoint marker for the `space.polymodel.graph.createTag` procedure.
62
62
+
63
63
+
Path: `/xrpc/space.polymodel.graph.createTag`. The request payload type is `CreateTag<S>`; send that request with `jacquard::Client` or use this marker through lower-level `XrpcEndpoint` APIs.*/
64
64
+
pub struct CreateTagRequest;
65
65
+
impl jacquard_common::xrpc::XrpcEndpoint for CreateTagRequest {
66
66
+
const PATH: &'static str = "/xrpc/space.polymodel.graph.createTag";
67
67
+
const METHOD: jacquard_common::xrpc::XrpcMethod = jacquard_common::xrpc::XrpcMethod::Procedure(
68
68
+
"application/json",
69
69
+
);
70
70
+
type Request<S: BosStr> = CreateTag<S>;
71
71
+
type Response = CreateTagResponse;
72
72
+
}
73
73
+
74
74
+
pub mod create_tag_state {
75
75
+
76
76
+
pub use crate::builder_types::{Set, Unset, IsSet, IsUnset};
77
77
+
#[allow(unused)]
78
78
+
use ::core::marker::PhantomData;
79
79
+
mod sealed {
80
80
+
pub trait Sealed {}
81
81
+
}
82
82
+
/// State trait tracking which required fields have been set
83
83
+
pub trait State: sealed::Sealed {
84
84
+
type Subject;
85
85
+
type Tag;
86
86
+
}
87
87
+
/// Empty state - all required fields are unset
88
88
+
pub struct Empty(());
89
89
+
impl sealed::Sealed for Empty {}
90
90
+
impl State for Empty {
91
91
+
type Subject = Unset;
92
92
+
type Tag = Unset;
93
93
+
}
94
94
+
///State transition - sets the `subject` field to Set
95
95
+
pub struct SetSubject<St: State = Empty>(PhantomData<fn() -> St>);
96
96
+
impl<St: State> sealed::Sealed for SetSubject<St> {}
97
97
+
impl<St: State> State for SetSubject<St> {
98
98
+
type Subject = Set<members::subject>;
99
99
+
type Tag = St::Tag;
100
100
+
}
101
101
+
///State transition - sets the `tag` field to Set
102
102
+
pub struct SetTag<St: State = Empty>(PhantomData<fn() -> St>);
103
103
+
impl<St: State> sealed::Sealed for SetTag<St> {}
104
104
+
impl<St: State> State for SetTag<St> {
105
105
+
type Subject = St::Subject;
106
106
+
type Tag = Set<members::tag>;
107
107
+
}
108
108
+
/// Marker types for field names
109
109
+
#[allow(non_camel_case_types)]
110
110
+
pub mod members {
111
111
+
///Marker type for the `subject` field
112
112
+
pub struct subject(());
113
113
+
///Marker type for the `tag` field
114
114
+
pub struct tag(());
115
115
+
}
116
116
+
}
117
117
+
118
118
+
/// Builder for constructing an instance of this type.
119
119
+
pub struct CreateTagBuilder<St: create_tag_state::State, S: BosStr = DefaultStr> {
120
120
+
_state: PhantomData<fn() -> St>,
121
121
+
_fields: (Option<StrongRef<S>>, Option<S>),
122
122
+
_type: PhantomData<fn() -> S>,
123
123
+
}
124
124
+
125
125
+
impl CreateTag<DefaultStr> {
126
126
+
/// Create a new builder for this type, using the default string type (DefaultStr = SmolStr) if needed
127
127
+
pub fn new() -> CreateTagBuilder<create_tag_state::Empty, DefaultStr> {
128
128
+
CreateTagBuilder::new()
129
129
+
}
130
130
+
}
131
131
+
132
132
+
impl<S: BosStr> CreateTag<S> {
133
133
+
/// Create a new builder for this type
134
134
+
pub fn builder() -> CreateTagBuilder<create_tag_state::Empty, S> {
135
135
+
CreateTagBuilder::builder()
136
136
+
}
137
137
+
}
138
138
+
139
139
+
impl CreateTagBuilder<create_tag_state::Empty, DefaultStr> {
140
140
+
/// Create a new builder with all fields unset, using the default string type, if needed
141
141
+
pub fn new() -> Self {
142
142
+
CreateTagBuilder {
143
143
+
_state: PhantomData,
144
144
+
_fields: (None, None),
145
145
+
_type: PhantomData,
146
146
+
}
147
147
+
}
148
148
+
}
149
149
+
150
150
+
impl<S: BosStr> CreateTagBuilder<create_tag_state::Empty, S> {
151
151
+
/// Create a new builder with all fields unset
152
152
+
pub fn builder() -> Self {
153
153
+
CreateTagBuilder {
154
154
+
_state: PhantomData,
155
155
+
_fields: (None, None),
156
156
+
_type: PhantomData,
157
157
+
}
158
158
+
}
159
159
+
}
160
160
+
161
161
+
impl<St, S: BosStr> CreateTagBuilder<St, S>
162
162
+
where
163
163
+
St: create_tag_state::State,
164
164
+
St::Subject: create_tag_state::IsUnset,
165
165
+
{
166
166
+
/// Set the `subject` field (required)
167
167
+
pub fn subject(
168
168
+
mut self,
169
169
+
value: impl Into<StrongRef<S>>,
170
170
+
) -> CreateTagBuilder<create_tag_state::SetSubject<St>, S> {
171
171
+
self._fields.0 = Option::Some(value.into());
172
172
+
CreateTagBuilder {
173
173
+
_state: PhantomData,
174
174
+
_fields: self._fields,
175
175
+
_type: PhantomData,
176
176
+
}
177
177
+
}
178
178
+
}
179
179
+
180
180
+
impl<St, S: BosStr> CreateTagBuilder<St, S>
181
181
+
where
182
182
+
St: create_tag_state::State,
183
183
+
St::Tag: create_tag_state::IsUnset,
184
184
+
{
185
185
+
/// Set the `tag` field (required)
186
186
+
pub fn tag(
187
187
+
mut self,
188
188
+
value: impl Into<S>,
189
189
+
) -> CreateTagBuilder<create_tag_state::SetTag<St>, S> {
190
190
+
self._fields.1 = Option::Some(value.into());
191
191
+
CreateTagBuilder {
192
192
+
_state: PhantomData,
193
193
+
_fields: self._fields,
194
194
+
_type: PhantomData,
195
195
+
}
196
196
+
}
197
197
+
}
198
198
+
199
199
+
impl<St, S: BosStr> CreateTagBuilder<St, S>
200
200
+
where
201
201
+
St: create_tag_state::State,
202
202
+
St::Subject: create_tag_state::IsSet,
203
203
+
St::Tag: create_tag_state::IsSet,
204
204
+
{
205
205
+
/// Build the final struct.
206
206
+
pub fn build(self) -> CreateTag<S> {
207
207
+
CreateTag {
208
208
+
subject: self._fields.0.unwrap(),
209
209
+
tag: self._fields.1.unwrap(),
210
210
+
extra_data: Default::default(),
211
211
+
}
212
212
+
}
213
213
+
/// Build the final struct with custom extra_data.
214
214
+
pub fn build_with_data(
215
215
+
self,
216
216
+
extra_data: BTreeMap<SmolStr, Data<S>>,
217
217
+
) -> CreateTag<S> {
218
218
+
CreateTag {
219
219
+
subject: self._fields.0.unwrap(),
220
220
+
tag: self._fields.1.unwrap(),
221
221
+
extra_data: Some(extra_data),
222
222
+
}
223
223
+
}
224
224
+
}
···
1
1
+
// @generated by jacquard-lexicon. DO NOT EDIT.
2
2
+
//
3
3
+
// Lexicon: space.polymodel.graph.deleteLike
4
4
+
//
5
5
+
// This file was automatically generated from Lexicon schemas.
6
6
+
// Any manual changes will be overwritten on the next regeneration.
7
7
+
8
8
+
#[allow(unused_imports)]
9
9
+
use alloc::collections::BTreeMap;
10
10
+
11
11
+
#[allow(unused_imports)]
12
12
+
use core::marker::PhantomData;
13
13
+
use jacquard_common::{BosStr, DefaultStr, FromStaticStr};
14
14
+
use jacquard_common::deps::smol_str::SmolStr;
15
15
+
use jacquard_common::types::string::AtUri;
16
16
+
use jacquard_common::types::value::Data;
17
17
+
use jacquard_derive::IntoStatic;
18
18
+
use serde::{Serialize, Deserialize};
19
19
+
use crate::com_atproto::repo::strong_ref::StrongRef;
20
20
+
21
21
+
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic, Default)]
22
22
+
#[serde(rename_all = "camelCase", bound(deserialize = "S: Deserialize<'de> + BosStr"))]
23
23
+
pub struct DeleteLike<S: BosStr = DefaultStr> {
24
24
+
#[serde(skip_serializing_if = "Option::is_none")]
25
25
+
pub subject: Option<StrongRef<S>>,
26
26
+
#[serde(skip_serializing_if = "Option::is_none")]
27
27
+
pub uri: Option<AtUri<S>>,
28
28
+
#[serde(flatten, default, skip_serializing_if = "Option::is_none")]
29
29
+
pub extra_data: Option<BTreeMap<SmolStr, Data<S>>>,
30
30
+
}
31
31
+
32
32
+
33
33
+
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic)]
34
34
+
#[serde(rename_all = "camelCase", bound(deserialize = "S: Deserialize<'de> + BosStr"))]
35
35
+
pub struct DeleteLikeOutput<S: BosStr = DefaultStr> {
36
36
+
pub deleted: bool,
37
37
+
#[serde(skip_serializing_if = "Option::is_none")]
38
38
+
pub uri: Option<AtUri<S>>,
39
39
+
#[serde(flatten, default, skip_serializing_if = "Option::is_none")]
40
40
+
pub extra_data: Option<BTreeMap<SmolStr, Data<S>>>,
41
41
+
}
42
42
+
43
43
+
/** Response marker for the `space.polymodel.graph.deleteLike` procedure.
44
44
+
45
45
+
Implements `jacquard_common::xrpc::XrpcResp`; successful bodies decode as `Self::Output<S>`, which is `DeleteLikeOutput<S>` for this endpoint.*/
46
46
+
pub struct DeleteLikeResponse;
47
47
+
impl jacquard_common::xrpc::XrpcResp for DeleteLikeResponse {
48
48
+
const NSID: &'static str = "space.polymodel.graph.deleteLike";
49
49
+
const ENCODING: &'static str = "application/json";
50
50
+
type Output<S: BosStr> = DeleteLikeOutput<S>;
51
51
+
type Err = jacquard_common::xrpc::GenericError;
52
52
+
}
53
53
+
54
54
+
impl<S: BosStr> jacquard_common::xrpc::XrpcRequest for DeleteLike<S> {
55
55
+
const NSID: &'static str = "space.polymodel.graph.deleteLike";
56
56
+
const METHOD: jacquard_common::xrpc::XrpcMethod = jacquard_common::xrpc::XrpcMethod::Procedure(
57
57
+
"application/json",
58
58
+
);
59
59
+
type Response = DeleteLikeResponse;
60
60
+
}
61
61
+
62
62
+
/** Endpoint marker for the `space.polymodel.graph.deleteLike` procedure.
63
63
+
64
64
+
Path: `/xrpc/space.polymodel.graph.deleteLike`. The request payload type is `DeleteLike<S>`; send that request with `jacquard::Client` or use this marker through lower-level `XrpcEndpoint` APIs.*/
65
65
+
pub struct DeleteLikeRequest;
66
66
+
impl jacquard_common::xrpc::XrpcEndpoint for DeleteLikeRequest {
67
67
+
const PATH: &'static str = "/xrpc/space.polymodel.graph.deleteLike";
68
68
+
const METHOD: jacquard_common::xrpc::XrpcMethod = jacquard_common::xrpc::XrpcMethod::Procedure(
69
69
+
"application/json",
70
70
+
);
71
71
+
type Request<S: BosStr> = DeleteLike<S>;
72
72
+
type Response = DeleteLikeResponse;
73
73
+
}
···
1
1
+
// @generated by jacquard-lexicon. DO NOT EDIT.
2
2
+
//
3
3
+
// Lexicon: space.polymodel.graph.deleteSave
4
4
+
//
5
5
+
// This file was automatically generated from Lexicon schemas.
6
6
+
// Any manual changes will be overwritten on the next regeneration.
7
7
+
8
8
+
#[allow(unused_imports)]
9
9
+
use alloc::collections::BTreeMap;
10
10
+
11
11
+
#[allow(unused_imports)]
12
12
+
use core::marker::PhantomData;
13
13
+
use jacquard_common::{BosStr, DefaultStr, FromStaticStr};
14
14
+
use jacquard_common::deps::smol_str::SmolStr;
15
15
+
use jacquard_common::types::string::AtUri;
16
16
+
use jacquard_common::types::value::Data;
17
17
+
use jacquard_derive::IntoStatic;
18
18
+
use serde::{Serialize, Deserialize};
19
19
+
use crate::com_atproto::repo::strong_ref::StrongRef;
20
20
+
21
21
+
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic, Default)]
22
22
+
#[serde(rename_all = "camelCase", bound(deserialize = "S: Deserialize<'de> + BosStr"))]
23
23
+
pub struct DeleteSave<S: BosStr = DefaultStr> {
24
24
+
#[serde(skip_serializing_if = "Option::is_none")]
25
25
+
pub subject: Option<StrongRef<S>>,
26
26
+
#[serde(skip_serializing_if = "Option::is_none")]
27
27
+
pub uri: Option<AtUri<S>>,
28
28
+
#[serde(flatten, default, skip_serializing_if = "Option::is_none")]
29
29
+
pub extra_data: Option<BTreeMap<SmolStr, Data<S>>>,
30
30
+
}
31
31
+
32
32
+
33
33
+
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic)]
34
34
+
#[serde(rename_all = "camelCase", bound(deserialize = "S: Deserialize<'de> + BosStr"))]
35
35
+
pub struct DeleteSaveOutput<S: BosStr = DefaultStr> {
36
36
+
pub deleted: bool,
37
37
+
#[serde(skip_serializing_if = "Option::is_none")]
38
38
+
pub uri: Option<AtUri<S>>,
39
39
+
#[serde(flatten, default, skip_serializing_if = "Option::is_none")]
40
40
+
pub extra_data: Option<BTreeMap<SmolStr, Data<S>>>,
41
41
+
}
42
42
+
43
43
+
/** Response marker for the `space.polymodel.graph.deleteSave` procedure.
44
44
+
45
45
+
Implements `jacquard_common::xrpc::XrpcResp`; successful bodies decode as `Self::Output<S>`, which is `DeleteSaveOutput<S>` for this endpoint.*/
46
46
+
pub struct DeleteSaveResponse;
47
47
+
impl jacquard_common::xrpc::XrpcResp for DeleteSaveResponse {
48
48
+
const NSID: &'static str = "space.polymodel.graph.deleteSave";
49
49
+
const ENCODING: &'static str = "application/json";
50
50
+
type Output<S: BosStr> = DeleteSaveOutput<S>;
51
51
+
type Err = jacquard_common::xrpc::GenericError;
52
52
+
}
53
53
+
54
54
+
impl<S: BosStr> jacquard_common::xrpc::XrpcRequest for DeleteSave<S> {
55
55
+
const NSID: &'static str = "space.polymodel.graph.deleteSave";
56
56
+
const METHOD: jacquard_common::xrpc::XrpcMethod = jacquard_common::xrpc::XrpcMethod::Procedure(
57
57
+
"application/json",
58
58
+
);
59
59
+
type Response = DeleteSaveResponse;
60
60
+
}
61
61
+
62
62
+
/** Endpoint marker for the `space.polymodel.graph.deleteSave` procedure.
63
63
+
64
64
+
Path: `/xrpc/space.polymodel.graph.deleteSave`. The request payload type is `DeleteSave<S>`; send that request with `jacquard::Client` or use this marker through lower-level `XrpcEndpoint` APIs.*/
65
65
+
pub struct DeleteSaveRequest;
66
66
+
impl jacquard_common::xrpc::XrpcEndpoint for DeleteSaveRequest {
67
67
+
const PATH: &'static str = "/xrpc/space.polymodel.graph.deleteSave";
68
68
+
const METHOD: jacquard_common::xrpc::XrpcMethod = jacquard_common::xrpc::XrpcMethod::Procedure(
69
69
+
"application/json",
70
70
+
);
71
71
+
type Request<S: BosStr> = DeleteSave<S>;
72
72
+
type Response = DeleteSaveResponse;
73
73
+
}
···
1
1
+
// @generated by jacquard-lexicon. DO NOT EDIT.
2
2
+
//
3
3
+
// Lexicon: space.polymodel.graph.deleteTag
4
4
+
//
5
5
+
// This file was automatically generated from Lexicon schemas.
6
6
+
// Any manual changes will be overwritten on the next regeneration.
7
7
+
8
8
+
#[allow(unused_imports)]
9
9
+
use alloc::collections::BTreeMap;
10
10
+
11
11
+
#[allow(unused_imports)]
12
12
+
use core::marker::PhantomData;
13
13
+
use jacquard_common::{BosStr, DefaultStr, FromStaticStr};
14
14
+
use jacquard_common::deps::smol_str::SmolStr;
15
15
+
use jacquard_common::types::string::AtUri;
16
16
+
use jacquard_common::types::value::Data;
17
17
+
use jacquard_derive::IntoStatic;
18
18
+
use serde::{Serialize, Deserialize};
19
19
+
20
20
+
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic)]
21
21
+
#[serde(rename_all = "camelCase", bound(deserialize = "S: Deserialize<'de> + BosStr"))]
22
22
+
pub struct DeleteTag<S: BosStr = DefaultStr> {
23
23
+
pub uri: AtUri<S>,
24
24
+
#[serde(flatten, default, skip_serializing_if = "Option::is_none")]
25
25
+
pub extra_data: Option<BTreeMap<SmolStr, Data<S>>>,
26
26
+
}
27
27
+
28
28
+
29
29
+
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic)]
30
30
+
#[serde(rename_all = "camelCase", bound(deserialize = "S: Deserialize<'de> + BosStr"))]
31
31
+
pub struct DeleteTagOutput<S: BosStr = DefaultStr> {
32
32
+
pub deleted: bool,
33
33
+
#[serde(skip_serializing_if = "Option::is_none")]
34
34
+
pub uri: Option<AtUri<S>>,
35
35
+
#[serde(flatten, default, skip_serializing_if = "Option::is_none")]
36
36
+
pub extra_data: Option<BTreeMap<SmolStr, Data<S>>>,
37
37
+
}
38
38
+
39
39
+
/** Response marker for the `space.polymodel.graph.deleteTag` procedure.
40
40
+
41
41
+
Implements `jacquard_common::xrpc::XrpcResp`; successful bodies decode as `Self::Output<S>`, which is `DeleteTagOutput<S>` for this endpoint.*/
42
42
+
pub struct DeleteTagResponse;
43
43
+
impl jacquard_common::xrpc::XrpcResp for DeleteTagResponse {
44
44
+
const NSID: &'static str = "space.polymodel.graph.deleteTag";
45
45
+
const ENCODING: &'static str = "application/json";
46
46
+
type Output<S: BosStr> = DeleteTagOutput<S>;
47
47
+
type Err = jacquard_common::xrpc::GenericError;
48
48
+
}
49
49
+
50
50
+
impl<S: BosStr> jacquard_common::xrpc::XrpcRequest for DeleteTag<S> {
51
51
+
const NSID: &'static str = "space.polymodel.graph.deleteTag";
52
52
+
const METHOD: jacquard_common::xrpc::XrpcMethod = jacquard_common::xrpc::XrpcMethod::Procedure(
53
53
+
"application/json",
54
54
+
);
55
55
+
type Response = DeleteTagResponse;
56
56
+
}
57
57
+
58
58
+
/** Endpoint marker for the `space.polymodel.graph.deleteTag` procedure.
59
59
+
60
60
+
Path: `/xrpc/space.polymodel.graph.deleteTag`. The request payload type is `DeleteTag<S>`; send that request with `jacquard::Client` or use this marker through lower-level `XrpcEndpoint` APIs.*/
61
61
+
pub struct DeleteTagRequest;
62
62
+
impl jacquard_common::xrpc::XrpcEndpoint for DeleteTagRequest {
63
63
+
const PATH: &'static str = "/xrpc/space.polymodel.graph.deleteTag";
64
64
+
const METHOD: jacquard_common::xrpc::XrpcMethod = jacquard_common::xrpc::XrpcMethod::Procedure(
65
65
+
"application/json",
66
66
+
);
67
67
+
type Request<S: BosStr> = DeleteTag<S>;
68
68
+
type Response = DeleteTagResponse;
69
69
+
}
70
70
+
71
71
+
pub mod delete_tag_state {
72
72
+
73
73
+
pub use crate::builder_types::{Set, Unset, IsSet, IsUnset};
74
74
+
#[allow(unused)]
75
75
+
use ::core::marker::PhantomData;
76
76
+
mod sealed {
77
77
+
pub trait Sealed {}
78
78
+
}
79
79
+
/// State trait tracking which required fields have been set
80
80
+
pub trait State: sealed::Sealed {
81
81
+
type Uri;
82
82
+
}
83
83
+
/// Empty state - all required fields are unset
84
84
+
pub struct Empty(());
85
85
+
impl sealed::Sealed for Empty {}
86
86
+
impl State for Empty {
87
87
+
type Uri = Unset;
88
88
+
}
89
89
+
///State transition - sets the `uri` field to Set
90
90
+
pub struct SetUri<St: State = Empty>(PhantomData<fn() -> St>);
91
91
+
impl<St: State> sealed::Sealed for SetUri<St> {}
92
92
+
impl<St: State> State for SetUri<St> {
93
93
+
type Uri = Set<members::uri>;
94
94
+
}
95
95
+
/// Marker types for field names
96
96
+
#[allow(non_camel_case_types)]
97
97
+
pub mod members {
98
98
+
///Marker type for the `uri` field
99
99
+
pub struct uri(());
100
100
+
}
101
101
+
}
102
102
+
103
103
+
/// Builder for constructing an instance of this type.
104
104
+
pub struct DeleteTagBuilder<St: delete_tag_state::State, S: BosStr = DefaultStr> {
105
105
+
_state: PhantomData<fn() -> St>,
106
106
+
_fields: (Option<AtUri<S>>,),
107
107
+
_type: PhantomData<fn() -> S>,
108
108
+
}
109
109
+
110
110
+
impl DeleteTag<DefaultStr> {
111
111
+
/// Create a new builder for this type, using the default string type (DefaultStr = SmolStr) if needed
112
112
+
pub fn new() -> DeleteTagBuilder<delete_tag_state::Empty, DefaultStr> {
113
113
+
DeleteTagBuilder::new()
114
114
+
}
115
115
+
}
116
116
+
117
117
+
impl<S: BosStr> DeleteTag<S> {
118
118
+
/// Create a new builder for this type
119
119
+
pub fn builder() -> DeleteTagBuilder<delete_tag_state::Empty, S> {
120
120
+
DeleteTagBuilder::builder()
121
121
+
}
122
122
+
}
123
123
+
124
124
+
impl DeleteTagBuilder<delete_tag_state::Empty, DefaultStr> {
125
125
+
/// Create a new builder with all fields unset, using the default string type, if needed
126
126
+
pub fn new() -> Self {
127
127
+
DeleteTagBuilder {
128
128
+
_state: PhantomData,
129
129
+
_fields: (None,),
130
130
+
_type: PhantomData,
131
131
+
}
132
132
+
}
133
133
+
}
134
134
+
135
135
+
impl<S: BosStr> DeleteTagBuilder<delete_tag_state::Empty, S> {
136
136
+
/// Create a new builder with all fields unset
137
137
+
pub fn builder() -> Self {
138
138
+
DeleteTagBuilder {
139
139
+
_state: PhantomData,
140
140
+
_fields: (None,),
141
141
+
_type: PhantomData,
142
142
+
}
143
143
+
}
144
144
+
}
145
145
+
146
146
+
impl<St, S: BosStr> DeleteTagBuilder<St, S>
147
147
+
where
148
148
+
St: delete_tag_state::State,
149
149
+
St::Uri: delete_tag_state::IsUnset,
150
150
+
{
151
151
+
/// Set the `uri` field (required)
152
152
+
pub fn uri(
153
153
+
mut self,
154
154
+
value: impl Into<AtUri<S>>,
155
155
+
) -> DeleteTagBuilder<delete_tag_state::SetUri<St>, S> {
156
156
+
self._fields.0 = Option::Some(value.into());
157
157
+
DeleteTagBuilder {
158
158
+
_state: PhantomData,
159
159
+
_fields: self._fields,
160
160
+
_type: PhantomData,
161
161
+
}
162
162
+
}
163
163
+
}
164
164
+
165
165
+
impl<St, S: BosStr> DeleteTagBuilder<St, S>
166
166
+
where
167
167
+
St: delete_tag_state::State,
168
168
+
St::Uri: delete_tag_state::IsSet,
169
169
+
{
170
170
+
/// Build the final struct.
171
171
+
pub fn build(self) -> DeleteTag<S> {
172
172
+
DeleteTag {
173
173
+
uri: self._fields.0.unwrap(),
174
174
+
extra_data: Default::default(),
175
175
+
}
176
176
+
}
177
177
+
/// Build the final struct with custom extra_data.
178
178
+
pub fn build_with_data(
179
179
+
self,
180
180
+
extra_data: BTreeMap<SmolStr, Data<S>>,
181
181
+
) -> DeleteTag<S> {
182
182
+
DeleteTag {
183
183
+
uri: self._fields.0.unwrap(),
184
184
+
extra_data: Some(extra_data),
185
185
+
}
186
186
+
}
187
187
+
}
···
6
6
// Any manual changes will be overwritten on the next regeneration.
7
7
8
8
//! Generated bindings for the `space.polymodel.library` Lexicon namespace/module.
9
9
+
pub mod delete_thing;
9
10
pub mod get_author_things;
10
11
pub mod get_feed;
11
12
pub mod get_model;
12
13
pub mod get_thing;
13
14
pub mod model;
14
15
pub mod part;
16
16
+
pub mod publish_thing;
15
17
pub mod search_things;
18
18
+
pub mod stage_file;
16
19
pub mod thing;
20
20
+
pub mod update_thing;
17
21
18
22
19
23
#[allow(unused_imports)]
···
1
1
+
// @generated by jacquard-lexicon. DO NOT EDIT.
2
2
+
//
3
3
+
// Lexicon: space.polymodel.library.deleteThing
4
4
+
//
5
5
+
// This file was automatically generated from Lexicon schemas.
6
6
+
// Any manual changes will be overwritten on the next regeneration.
7
7
+
8
8
+
#[allow(unused_imports)]
9
9
+
use alloc::collections::BTreeMap;
10
10
+
11
11
+
#[allow(unused_imports)]
12
12
+
use core::marker::PhantomData;
13
13
+
use jacquard_common::{BosStr, DefaultStr, FromStaticStr};
14
14
+
use jacquard_common::deps::smol_str::SmolStr;
15
15
+
use jacquard_common::types::string::AtUri;
16
16
+
use jacquard_common::types::value::Data;
17
17
+
use jacquard_derive::IntoStatic;
18
18
+
use serde::{Serialize, Deserialize};
19
19
+
20
20
+
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic)]
21
21
+
#[serde(rename_all = "camelCase", bound(deserialize = "S: Deserialize<'de> + BosStr"))]
22
22
+
pub struct DeleteThing<S: BosStr = DefaultStr> {
23
23
+
pub uri: AtUri<S>,
24
24
+
#[serde(flatten, default, skip_serializing_if = "Option::is_none")]
25
25
+
pub extra_data: Option<BTreeMap<SmolStr, Data<S>>>,
26
26
+
}
27
27
+
28
28
+
29
29
+
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic)]
30
30
+
#[serde(rename_all = "camelCase", bound(deserialize = "S: Deserialize<'de> + BosStr"))]
31
31
+
pub struct DeleteThingOutput<S: BosStr = DefaultStr> {
32
32
+
pub deleted: bool,
33
33
+
#[serde(skip_serializing_if = "Option::is_none")]
34
34
+
pub deleted_uris: Option<Vec<AtUri<S>>>,
35
35
+
#[serde(flatten, default, skip_serializing_if = "Option::is_none")]
36
36
+
pub extra_data: Option<BTreeMap<SmolStr, Data<S>>>,
37
37
+
}
38
38
+
39
39
+
/** Response marker for the `space.polymodel.library.deleteThing` procedure.
40
40
+
41
41
+
Implements `jacquard_common::xrpc::XrpcResp`; successful bodies decode as `Self::Output<S>`, which is `DeleteThingOutput<S>` for this endpoint.*/
42
42
+
pub struct DeleteThingResponse;
43
43
+
impl jacquard_common::xrpc::XrpcResp for DeleteThingResponse {
44
44
+
const NSID: &'static str = "space.polymodel.library.deleteThing";
45
45
+
const ENCODING: &'static str = "application/json";
46
46
+
type Output<S: BosStr> = DeleteThingOutput<S>;
47
47
+
type Err = jacquard_common::xrpc::GenericError;
48
48
+
}
49
49
+
50
50
+
impl<S: BosStr> jacquard_common::xrpc::XrpcRequest for DeleteThing<S> {
51
51
+
const NSID: &'static str = "space.polymodel.library.deleteThing";
52
52
+
const METHOD: jacquard_common::xrpc::XrpcMethod = jacquard_common::xrpc::XrpcMethod::Procedure(
53
53
+
"application/json",
54
54
+
);
55
55
+
type Response = DeleteThingResponse;
56
56
+
}
57
57
+
58
58
+
/** Endpoint marker for the `space.polymodel.library.deleteThing` procedure.
59
59
+
60
60
+
Path: `/xrpc/space.polymodel.library.deleteThing`. The request payload type is `DeleteThing<S>`; send that request with `jacquard::Client` or use this marker through lower-level `XrpcEndpoint` APIs.*/
61
61
+
pub struct DeleteThingRequest;
62
62
+
impl jacquard_common::xrpc::XrpcEndpoint for DeleteThingRequest {
63
63
+
const PATH: &'static str = "/xrpc/space.polymodel.library.deleteThing";
64
64
+
const METHOD: jacquard_common::xrpc::XrpcMethod = jacquard_common::xrpc::XrpcMethod::Procedure(
65
65
+
"application/json",
66
66
+
);
67
67
+
type Request<S: BosStr> = DeleteThing<S>;
68
68
+
type Response = DeleteThingResponse;
69
69
+
}
70
70
+
71
71
+
pub mod delete_thing_state {
72
72
+
73
73
+
pub use crate::builder_types::{Set, Unset, IsSet, IsUnset};
74
74
+
#[allow(unused)]
75
75
+
use ::core::marker::PhantomData;
76
76
+
mod sealed {
77
77
+
pub trait Sealed {}
78
78
+
}
79
79
+
/// State trait tracking which required fields have been set
80
80
+
pub trait State: sealed::Sealed {
81
81
+
type Uri;
82
82
+
}
83
83
+
/// Empty state - all required fields are unset
84
84
+
pub struct Empty(());
85
85
+
impl sealed::Sealed for Empty {}
86
86
+
impl State for Empty {
87
87
+
type Uri = Unset;
88
88
+
}
89
89
+
///State transition - sets the `uri` field to Set
90
90
+
pub struct SetUri<St: State = Empty>(PhantomData<fn() -> St>);
91
91
+
impl<St: State> sealed::Sealed for SetUri<St> {}
92
92
+
impl<St: State> State for SetUri<St> {
93
93
+
type Uri = Set<members::uri>;
94
94
+
}
95
95
+
/// Marker types for field names
96
96
+
#[allow(non_camel_case_types)]
97
97
+
pub mod members {
98
98
+
///Marker type for the `uri` field
99
99
+
pub struct uri(());
100
100
+
}
101
101
+
}
102
102
+
103
103
+
/// Builder for constructing an instance of this type.
104
104
+
pub struct DeleteThingBuilder<St: delete_thing_state::State, S: BosStr = DefaultStr> {
105
105
+
_state: PhantomData<fn() -> St>,
106
106
+
_fields: (Option<AtUri<S>>,),
107
107
+
_type: PhantomData<fn() -> S>,
108
108
+
}
109
109
+
110
110
+
impl DeleteThing<DefaultStr> {
111
111
+
/// Create a new builder for this type, using the default string type (DefaultStr = SmolStr) if needed
112
112
+
pub fn new() -> DeleteThingBuilder<delete_thing_state::Empty, DefaultStr> {
113
113
+
DeleteThingBuilder::new()
114
114
+
}
115
115
+
}
116
116
+
117
117
+
impl<S: BosStr> DeleteThing<S> {
118
118
+
/// Create a new builder for this type
119
119
+
pub fn builder() -> DeleteThingBuilder<delete_thing_state::Empty, S> {
120
120
+
DeleteThingBuilder::builder()
121
121
+
}
122
122
+
}
123
123
+
124
124
+
impl DeleteThingBuilder<delete_thing_state::Empty, DefaultStr> {
125
125
+
/// Create a new builder with all fields unset, using the default string type, if needed
126
126
+
pub fn new() -> Self {
127
127
+
DeleteThingBuilder {
128
128
+
_state: PhantomData,
129
129
+
_fields: (None,),
130
130
+
_type: PhantomData,
131
131
+
}
132
132
+
}
133
133
+
}
134
134
+
135
135
+
impl<S: BosStr> DeleteThingBuilder<delete_thing_state::Empty, S> {
136
136
+
/// Create a new builder with all fields unset
137
137
+
pub fn builder() -> Self {
138
138
+
DeleteThingBuilder {
139
139
+
_state: PhantomData,
140
140
+
_fields: (None,),
141
141
+
_type: PhantomData,
142
142
+
}
143
143
+
}
144
144
+
}
145
145
+
146
146
+
impl<St, S: BosStr> DeleteThingBuilder<St, S>
147
147
+
where
148
148
+
St: delete_thing_state::State,
149
149
+
St::Uri: delete_thing_state::IsUnset,
150
150
+
{
151
151
+
/// Set the `uri` field (required)
152
152
+
pub fn uri(
153
153
+
mut self,
154
154
+
value: impl Into<AtUri<S>>,
155
155
+
) -> DeleteThingBuilder<delete_thing_state::SetUri<St>, S> {
156
156
+
self._fields.0 = Option::Some(value.into());
157
157
+
DeleteThingBuilder {
158
158
+
_state: PhantomData,
159
159
+
_fields: self._fields,
160
160
+
_type: PhantomData,
161
161
+
}
162
162
+
}
163
163
+
}
164
164
+
165
165
+
impl<St, S: BosStr> DeleteThingBuilder<St, S>
166
166
+
where
167
167
+
St: delete_thing_state::State,
168
168
+
St::Uri: delete_thing_state::IsSet,
169
169
+
{
170
170
+
/// Build the final struct.
171
171
+
pub fn build(self) -> DeleteThing<S> {
172
172
+
DeleteThing {
173
173
+
uri: self._fields.0.unwrap(),
174
174
+
extra_data: Default::default(),
175
175
+
}
176
176
+
}
177
177
+
/// Build the final struct with custom extra_data.
178
178
+
pub fn build_with_data(
179
179
+
self,
180
180
+
extra_data: BTreeMap<SmolStr, Data<S>>,
181
181
+
) -> DeleteThing<S> {
182
182
+
DeleteThing {
183
183
+
uri: self._fields.0.unwrap(),
184
184
+
extra_data: Some(extra_data),
185
185
+
}
186
186
+
}
187
187
+
}
···
1
1
+
// @generated by jacquard-lexicon. DO NOT EDIT.
2
2
+
//
3
3
+
// Lexicon: space.polymodel.library.publishThing
4
4
+
//
5
5
+
// This file was automatically generated from Lexicon schemas.
6
6
+
// Any manual changes will be overwritten on the next regeneration.
7
7
+
8
8
+
#[allow(unused_imports)]
9
9
+
use alloc::collections::BTreeMap;
10
10
+
11
11
+
#[allow(unused_imports)]
12
12
+
use core::marker::PhantomData;
13
13
+
use jacquard_common::{CowStr, BosStr, DefaultStr, FromStaticStr};
14
14
+
15
15
+
#[allow(unused_imports)]
16
16
+
use jacquard_common::deps::codegen::unicode_segmentation::UnicodeSegmentation;
17
17
+
use jacquard_common::deps::smol_str::SmolStr;
18
18
+
use jacquard_common::types::string::{AtUri, Cid};
19
19
+
use jacquard_common::types::value::Data;
20
20
+
use jacquard_derive::IntoStatic;
21
21
+
use jacquard_lexicon::lexicon::LexiconDoc;
22
22
+
use jacquard_lexicon::schema::LexiconSchema;
23
23
+
24
24
+
#[allow(unused_imports)]
25
25
+
use jacquard_lexicon::validation::{ConstraintError, ValidationPath};
26
26
+
use serde::{Serialize, Deserialize};
27
27
+
use crate::com_atproto::repo::strong_ref::StrongRef;
28
28
+
use crate::space_polymodel::library::Bbox;
29
29
+
use crate::space_polymodel::library::File;
30
30
+
use crate::space_polymodel::library::Image;
31
31
+
use crate::space_polymodel::library::publish_thing;
32
32
+
33
33
+
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic)]
34
34
+
#[serde(rename_all = "camelCase", bound(deserialize = "S: Deserialize<'de> + BosStr"))]
35
35
+
pub struct PublishThing<S: BosStr = DefaultStr> {
36
36
+
pub thing: publish_thing::ThingInput<S>,
37
37
+
#[serde(flatten, default, skip_serializing_if = "Option::is_none")]
38
38
+
pub extra_data: Option<BTreeMap<SmolStr, Data<S>>>,
39
39
+
}
40
40
+
41
41
+
42
42
+
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic)]
43
43
+
#[serde(rename_all = "camelCase", bound(deserialize = "S: Deserialize<'de> + BosStr"))]
44
44
+
pub struct PublishThingOutput<S: BosStr = DefaultStr> {
45
45
+
pub models: Vec<publish_thing::ModelPublishResult<S>>,
46
46
+
pub thing: publish_thing::RecordRef<S>,
47
47
+
#[serde(flatten, default, skip_serializing_if = "Option::is_none")]
48
48
+
pub extra_data: Option<BTreeMap<SmolStr, Data<S>>>,
49
49
+
}
50
50
+
51
51
+
52
52
+
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic)]
53
53
+
#[serde(rename_all = "camelCase", bound(deserialize = "S: Deserialize<'de> + BosStr"))]
54
54
+
pub struct ModelInput<S: BosStr = DefaultStr> {
55
55
+
#[serde(skip_serializing_if = "Option::is_none")]
56
56
+
pub cover: Option<Vec<Image<S>>>,
57
57
+
#[serde(skip_serializing_if = "Option::is_none")]
58
58
+
pub derived_from: Option<StrongRef<S>>,
59
59
+
#[serde(skip_serializing_if = "Option::is_none")]
60
60
+
pub instructions: Option<Vec<S>>,
61
61
+
#[serde(skip_serializing_if = "Option::is_none")]
62
62
+
pub intended_to_work_with: Option<Vec<StrongRef<S>>>,
63
63
+
#[serde(skip_serializing_if = "Option::is_none")]
64
64
+
pub license: Option<S>,
65
65
+
pub name: S,
66
66
+
pub parts: Vec<publish_thing::PartInput<S>>,
67
67
+
#[serde(skip_serializing_if = "Option::is_none")]
68
68
+
pub previews: Option<Vec<Image<S>>>,
69
69
+
#[serde(skip_serializing_if = "Option::is_none")]
70
70
+
pub summary: Option<S>,
71
71
+
#[serde(skip_serializing_if = "Option::is_none")]
72
72
+
pub tags: Option<Vec<S>>,
73
73
+
#[serde(flatten, default, skip_serializing_if = "Option::is_none")]
74
74
+
pub extra_data: Option<BTreeMap<SmolStr, Data<S>>>,
75
75
+
}
76
76
+
77
77
+
78
78
+
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic)]
79
79
+
#[serde(rename_all = "camelCase", bound(deserialize = "S: Deserialize<'de> + BosStr"))]
80
80
+
pub struct ModelPublishResult<S: BosStr = DefaultStr> {
81
81
+
pub model: publish_thing::RecordRef<S>,
82
82
+
pub parts: Vec<publish_thing::RecordRef<S>>,
83
83
+
#[serde(flatten, default, skip_serializing_if = "Option::is_none")]
84
84
+
pub extra_data: Option<BTreeMap<SmolStr, Data<S>>>,
85
85
+
}
86
86
+
87
87
+
88
88
+
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic, Default)]
89
89
+
#[serde(rename_all = "camelCase", bound(deserialize = "S: Deserialize<'de> + BosStr"))]
90
90
+
pub struct PartInput<S: BosStr = DefaultStr> {
91
91
+
#[serde(skip_serializing_if = "Option::is_none")]
92
92
+
pub derived_from: Option<StrongRef<S>>,
93
93
+
#[serde(skip_serializing_if = "Option::is_none")]
94
94
+
pub dimensions: Option<Bbox<S>>,
95
95
+
#[serde(skip_serializing_if = "Option::is_none")]
96
96
+
pub file: Option<File<S>>,
97
97
+
#[serde(skip_serializing_if = "Option::is_none")]
98
98
+
pub format: Option<S>,
99
99
+
#[serde(skip_serializing_if = "Option::is_none")]
100
100
+
pub intended_to_work_with: Option<Vec<StrongRef<S>>>,
101
101
+
pub name: S,
102
102
+
#[serde(skip_serializing_if = "Option::is_none")]
103
103
+
pub notes: Option<S>,
104
104
+
#[serde(skip_serializing_if = "Option::is_none")]
105
105
+
pub previews: Option<Vec<Image<S>>>,
106
106
+
#[serde(skip_serializing_if = "Option::is_none")]
107
107
+
pub print_settings: Option<Vec<S>>,
108
108
+
#[serde(skip_serializing_if = "Option::is_none")]
109
109
+
pub units: Option<S>,
110
110
+
#[serde(skip_serializing_if = "Option::is_none")]
111
111
+
pub upload_id: Option<S>,
112
112
+
#[serde(flatten, default, skip_serializing_if = "Option::is_none")]
113
113
+
pub extra_data: Option<BTreeMap<SmolStr, Data<S>>>,
114
114
+
}
115
115
+
116
116
+
117
117
+
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic)]
118
118
+
#[serde(rename_all = "camelCase", bound(deserialize = "S: Deserialize<'de> + BosStr"))]
119
119
+
pub struct RecordRef<S: BosStr = DefaultStr> {
120
120
+
pub cid: Cid<S>,
121
121
+
pub uri: AtUri<S>,
122
122
+
#[serde(flatten, default, skip_serializing_if = "Option::is_none")]
123
123
+
pub extra_data: Option<BTreeMap<SmolStr, Data<S>>>,
124
124
+
}
125
125
+
126
126
+
127
127
+
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic)]
128
128
+
#[serde(rename_all = "camelCase", bound(deserialize = "S: Deserialize<'de> + BosStr"))]
129
129
+
pub struct ThingInput<S: BosStr = DefaultStr> {
130
130
+
#[serde(skip_serializing_if = "Option::is_none")]
131
131
+
pub cover: Option<Vec<Image<S>>>,
132
132
+
#[serde(skip_serializing_if = "Option::is_none")]
133
133
+
pub derived_from: Option<StrongRef<S>>,
134
134
+
#[serde(skip_serializing_if = "Option::is_none")]
135
135
+
pub instructions: Option<Vec<S>>,
136
136
+
#[serde(skip_serializing_if = "Option::is_none")]
137
137
+
pub intended_to_work_with: Option<Vec<StrongRef<S>>>,
138
138
+
pub license: S,
139
139
+
pub models: Vec<publish_thing::ModelInput<S>>,
140
140
+
pub name: S,
141
141
+
#[serde(skip_serializing_if = "Option::is_none")]
142
142
+
pub previews: Option<Vec<Image<S>>>,
143
143
+
#[serde(skip_serializing_if = "Option::is_none")]
144
144
+
pub summary: Option<S>,
145
145
+
#[serde(skip_serializing_if = "Option::is_none")]
146
146
+
pub tags: Option<Vec<S>>,
147
147
+
#[serde(flatten, default, skip_serializing_if = "Option::is_none")]
148
148
+
pub extra_data: Option<BTreeMap<SmolStr, Data<S>>>,
149
149
+
}
150
150
+
151
151
+
/** Response marker for the `space.polymodel.library.publishThing` procedure.
152
152
+
153
153
+
Implements `jacquard_common::xrpc::XrpcResp`; successful bodies decode as `Self::Output<S>`, which is `PublishThingOutput<S>` for this endpoint.*/
154
154
+
pub struct PublishThingResponse;
155
155
+
impl jacquard_common::xrpc::XrpcResp for PublishThingResponse {
156
156
+
const NSID: &'static str = "space.polymodel.library.publishThing";
157
157
+
const ENCODING: &'static str = "application/json";
158
158
+
type Output<S: BosStr> = PublishThingOutput<S>;
159
159
+
type Err = jacquard_common::xrpc::GenericError;
160
160
+
}
161
161
+
162
162
+
impl<S: BosStr> jacquard_common::xrpc::XrpcRequest for PublishThing<S> {
163
163
+
const NSID: &'static str = "space.polymodel.library.publishThing";
164
164
+
const METHOD: jacquard_common::xrpc::XrpcMethod = jacquard_common::xrpc::XrpcMethod::Procedure(
165
165
+
"application/json",
166
166
+
);
167
167
+
type Response = PublishThingResponse;
168
168
+
}
169
169
+
170
170
+
/** Endpoint marker for the `space.polymodel.library.publishThing` procedure.
171
171
+
172
172
+
Path: `/xrpc/space.polymodel.library.publishThing`. The request payload type is `PublishThing<S>`; send that request with `jacquard::Client` or use this marker through lower-level `XrpcEndpoint` APIs.*/
173
173
+
pub struct PublishThingRequest;
174
174
+
impl jacquard_common::xrpc::XrpcEndpoint for PublishThingRequest {
175
175
+
const PATH: &'static str = "/xrpc/space.polymodel.library.publishThing";
176
176
+
const METHOD: jacquard_common::xrpc::XrpcMethod = jacquard_common::xrpc::XrpcMethod::Procedure(
177
177
+
"application/json",
178
178
+
);
179
179
+
type Request<S: BosStr> = PublishThing<S>;
180
180
+
type Response = PublishThingResponse;
181
181
+
}
182
182
+
183
183
+
impl<S: BosStr> LexiconSchema for ModelInput<S> {
184
184
+
fn nsid() -> &'static str {
185
185
+
"space.polymodel.library.publishThing"
186
186
+
}
187
187
+
fn def_name() -> &'static str {
188
188
+
"modelInput"
189
189
+
}
190
190
+
fn lexicon_doc() -> LexiconDoc<'static> {
191
191
+
lexicon_doc_space_polymodel_library_publishThing()
192
192
+
}
193
193
+
fn validate(&self) -> Result<(), ConstraintError> {
194
194
+
if let Some(ref value) = self.cover {
195
195
+
#[allow(unused_comparisons)]
196
196
+
if value.len() > 8usize {
197
197
+
return Err(ConstraintError::MaxLength {
198
198
+
path: ValidationPath::from_field("cover"),
199
199
+
max: 8usize,
200
200
+
actual: value.len(),
201
201
+
});
202
202
+
}
203
203
+
}
204
204
+
if let Some(ref value) = self.instructions {
205
205
+
#[allow(unused_comparisons)]
206
206
+
if value.len() > 100usize {
207
207
+
return Err(ConstraintError::MaxLength {
208
208
+
path: ValidationPath::from_field("instructions"),
209
209
+
max: 100usize,
210
210
+
actual: value.len(),
211
211
+
});
212
212
+
}
213
213
+
}
214
214
+
if let Some(ref value) = self.intended_to_work_with {
215
215
+
#[allow(unused_comparisons)]
216
216
+
if value.len() > 32usize {
217
217
+
return Err(ConstraintError::MaxLength {
218
218
+
path: ValidationPath::from_field("intended_to_work_with"),
219
219
+
max: 32usize,
220
220
+
actual: value.len(),
221
221
+
});
222
222
+
}
223
223
+
}
224
224
+
{
225
225
+
let value = &self.name;
226
226
+
#[allow(unused_comparisons)]
227
227
+
if <str>::len(value.as_ref()) > 256usize {
228
228
+
return Err(ConstraintError::MaxLength {
229
229
+
path: ValidationPath::from_field("name"),
230
230
+
max: 256usize,
231
231
+
actual: <str>::len(value.as_ref()),
232
232
+
});
233
233
+
}
234
234
+
}
235
235
+
{
236
236
+
let value = &self.name;
237
237
+
{
238
238
+
let count = UnicodeSegmentation::graphemes(value.as_ref(), true).count();
239
239
+
if count > 64usize {
240
240
+
return Err(ConstraintError::MaxGraphemes {
241
241
+
path: ValidationPath::from_field("name"),
242
242
+
max: 64usize,
243
243
+
actual: count,
244
244
+
});
245
245
+
}
246
246
+
}
247
247
+
}
248
248
+
{
249
249
+
let value = &self.parts;
250
250
+
#[allow(unused_comparisons)]
251
251
+
if value.len() > 1024usize {
252
252
+
return Err(ConstraintError::MaxLength {
253
253
+
path: ValidationPath::from_field("parts"),
254
254
+
max: 1024usize,
255
255
+
actual: value.len(),
256
256
+
});
257
257
+
}
258
258
+
}
259
259
+
if let Some(ref value) = self.previews {
260
260
+
#[allow(unused_comparisons)]
261
261
+
if value.len() > 12usize {
262
262
+
return Err(ConstraintError::MaxLength {
263
263
+
path: ValidationPath::from_field("previews"),
264
264
+
max: 12usize,
265
265
+
actual: value.len(),
266
266
+
});
267
267
+
}
268
268
+
}
269
269
+
if let Some(ref value) = self.summary {
270
270
+
#[allow(unused_comparisons)]
271
271
+
if <str>::len(value.as_ref()) > 2000usize {
272
272
+
return Err(ConstraintError::MaxLength {
273
273
+
path: ValidationPath::from_field("summary"),
274
274
+
max: 2000usize,
275
275
+
actual: <str>::len(value.as_ref()),
276
276
+
});
277
277
+
}
278
278
+
}
279
279
+
if let Some(ref value) = self.summary {
280
280
+
{
281
281
+
let count = UnicodeSegmentation::graphemes(value.as_ref(), true).count();
282
282
+
if count > 300usize {
283
283
+
return Err(ConstraintError::MaxGraphemes {
284
284
+
path: ValidationPath::from_field("summary"),
285
285
+
max: 300usize,
286
286
+
actual: count,
287
287
+
});
288
288
+
}
289
289
+
}
290
290
+
}
291
291
+
if let Some(ref value) = self.tags {
292
292
+
#[allow(unused_comparisons)]
293
293
+
if value.len() > 16usize {
294
294
+
return Err(ConstraintError::MaxLength {
295
295
+
path: ValidationPath::from_field("tags"),
296
296
+
max: 16usize,
297
297
+
actual: value.len(),
298
298
+
});
299
299
+
}
300
300
+
}
301
301
+
Ok(())
302
302
+
}
303
303
+
}
304
304
+
305
305
+
impl<S: BosStr> LexiconSchema for ModelPublishResult<S> {
306
306
+
fn nsid() -> &'static str {
307
307
+
"space.polymodel.library.publishThing"
308
308
+
}
309
309
+
fn def_name() -> &'static str {
310
310
+
"modelPublishResult"
311
311
+
}
312
312
+
fn lexicon_doc() -> LexiconDoc<'static> {
313
313
+
lexicon_doc_space_polymodel_library_publishThing()
314
314
+
}
315
315
+
fn validate(&self) -> Result<(), ConstraintError> {
316
316
+
Ok(())
317
317
+
}
318
318
+
}
319
319
+
320
320
+
impl<S: BosStr> LexiconSchema for PartInput<S> {
321
321
+
fn nsid() -> &'static str {
322
322
+
"space.polymodel.library.publishThing"
323
323
+
}
324
324
+
fn def_name() -> &'static str {
325
325
+
"partInput"
326
326
+
}
327
327
+
fn lexicon_doc() -> LexiconDoc<'static> {
328
328
+
lexicon_doc_space_polymodel_library_publishThing()
329
329
+
}
330
330
+
fn validate(&self) -> Result<(), ConstraintError> {
331
331
+
if let Some(ref value) = self.intended_to_work_with {
332
332
+
#[allow(unused_comparisons)]
333
333
+
if value.len() > 32usize {
334
334
+
return Err(ConstraintError::MaxLength {
335
335
+
path: ValidationPath::from_field("intended_to_work_with"),
336
336
+
max: 32usize,
337
337
+
actual: value.len(),
338
338
+
});
339
339
+
}
340
340
+
}
341
341
+
{
342
342
+
let value = &self.name;
343
343
+
#[allow(unused_comparisons)]
344
344
+
if <str>::len(value.as_ref()) > 256usize {
345
345
+
return Err(ConstraintError::MaxLength {
346
346
+
path: ValidationPath::from_field("name"),
347
347
+
max: 256usize,
348
348
+
actual: <str>::len(value.as_ref()),
349
349
+
});
350
350
+
}
351
351
+
}
352
352
+
{
353
353
+
let value = &self.name;
354
354
+
{
355
355
+
let count = UnicodeSegmentation::graphemes(value.as_ref(), true).count();
356
356
+
if count > 64usize {
357
357
+
return Err(ConstraintError::MaxGraphemes {
358
358
+
path: ValidationPath::from_field("name"),
359
359
+
max: 64usize,
360
360
+
actual: count,
361
361
+
});
362
362
+
}
363
363
+
}
364
364
+
}
365
365
+
if let Some(ref value) = self.notes {
366
366
+
#[allow(unused_comparisons)]
367
367
+
if <str>::len(value.as_ref()) > 2000usize {
368
368
+
return Err(ConstraintError::MaxLength {
369
369
+
path: ValidationPath::from_field("notes"),
370
370
+
max: 2000usize,
371
371
+
actual: <str>::len(value.as_ref()),
372
372
+
});
373
373
+
}
374
374
+
}
375
375
+
if let Some(ref value) = self.notes {
376
376
+
{
377
377
+
let count = UnicodeSegmentation::graphemes(value.as_ref(), true).count();
378
378
+
if count > 300usize {
379
379
+
return Err(ConstraintError::MaxGraphemes {
380
380
+
path: ValidationPath::from_field("notes"),
381
381
+
max: 300usize,
382
382
+
actual: count,
383
383
+
});
384
384
+
}
385
385
+
}
386
386
+
}
387
387
+
if let Some(ref value) = self.previews {
388
388
+
#[allow(unused_comparisons)]
389
389
+
if value.len() > 12usize {
390
390
+
return Err(ConstraintError::MaxLength {
391
391
+
path: ValidationPath::from_field("previews"),
392
392
+
max: 12usize,
393
393
+
actual: value.len(),
394
394
+
});
395
395
+
}
396
396
+
}
397
397
+
if let Some(ref value) = self.print_settings {
398
398
+
#[allow(unused_comparisons)]
399
399
+
if value.len() > 50usize {
400
400
+
return Err(ConstraintError::MaxLength {
401
401
+
path: ValidationPath::from_field("print_settings"),
402
402
+
max: 50usize,
403
403
+
actual: value.len(),
404
404
+
});
405
405
+
}
406
406
+
}
407
407
+
Ok(())
408
408
+
}
409
409
+
}
410
410
+
411
411
+
impl<S: BosStr> LexiconSchema for RecordRef<S> {
412
412
+
fn nsid() -> &'static str {
413
413
+
"space.polymodel.library.publishThing"
414
414
+
}
415
415
+
fn def_name() -> &'static str {
416
416
+
"recordRef"
417
417
+
}
418
418
+
fn lexicon_doc() -> LexiconDoc<'static> {
419
419
+
lexicon_doc_space_polymodel_library_publishThing()
420
420
+
}
421
421
+
fn validate(&self) -> Result<(), ConstraintError> {
422
422
+
Ok(())
423
423
+
}
424
424
+
}
425
425
+
426
426
+
impl<S: BosStr> LexiconSchema for ThingInput<S> {
427
427
+
fn nsid() -> &'static str {
428
428
+
"space.polymodel.library.publishThing"
429
429
+
}
430
430
+
fn def_name() -> &'static str {
431
431
+
"thingInput"
432
432
+
}
433
433
+
fn lexicon_doc() -> LexiconDoc<'static> {
434
434
+
lexicon_doc_space_polymodel_library_publishThing()
435
435
+
}
436
436
+
fn validate(&self) -> Result<(), ConstraintError> {
437
437
+
if let Some(ref value) = self.cover {
438
438
+
#[allow(unused_comparisons)]
439
439
+
if value.len() > 8usize {
440
440
+
return Err(ConstraintError::MaxLength {
441
441
+
path: ValidationPath::from_field("cover"),
442
442
+
max: 8usize,
443
443
+
actual: value.len(),
444
444
+
});
445
445
+
}
446
446
+
}
447
447
+
if let Some(ref value) = self.instructions {
448
448
+
#[allow(unused_comparisons)]
449
449
+
if value.len() > 100usize {
450
450
+
return Err(ConstraintError::MaxLength {
451
451
+
path: ValidationPath::from_field("instructions"),
452
452
+
max: 100usize,
453
453
+
actual: value.len(),
454
454
+
});
455
455
+
}
456
456
+
}
457
457
+
if let Some(ref value) = self.intended_to_work_with {
458
458
+
#[allow(unused_comparisons)]
459
459
+
if value.len() > 32usize {
460
460
+
return Err(ConstraintError::MaxLength {
461
461
+
path: ValidationPath::from_field("intended_to_work_with"),
462
462
+
max: 32usize,
463
463
+
actual: value.len(),
464
464
+
});
465
465
+
}
466
466
+
}
467
467
+
{
468
468
+
let value = &self.models;
469
469
+
#[allow(unused_comparisons)]
470
470
+
if value.len() > 256usize {
471
471
+
return Err(ConstraintError::MaxLength {
472
472
+
path: ValidationPath::from_field("models"),
473
473
+
max: 256usize,
474
474
+
actual: value.len(),
475
475
+
});
476
476
+
}
477
477
+
}
478
478
+
{
479
479
+
let value = &self.name;
480
480
+
#[allow(unused_comparisons)]
481
481
+
if <str>::len(value.as_ref()) > 256usize {
482
482
+
return Err(ConstraintError::MaxLength {
483
483
+
path: ValidationPath::from_field("name"),
484
484
+
max: 256usize,
485
485
+
actual: <str>::len(value.as_ref()),
486
486
+
});
487
487
+
}
488
488
+
}
489
489
+
{
490
490
+
let value = &self.name;
491
491
+
{
492
492
+
let count = UnicodeSegmentation::graphemes(value.as_ref(), true).count();
493
493
+
if count > 64usize {
494
494
+
return Err(ConstraintError::MaxGraphemes {
495
495
+
path: ValidationPath::from_field("name"),
496
496
+
max: 64usize,
497
497
+
actual: count,
498
498
+
});
499
499
+
}
500
500
+
}
501
501
+
}
502
502
+
if let Some(ref value) = self.previews {
503
503
+
#[allow(unused_comparisons)]
504
504
+
if value.len() > 12usize {
505
505
+
return Err(ConstraintError::MaxLength {
506
506
+
path: ValidationPath::from_field("previews"),
507
507
+
max: 12usize,
508
508
+
actual: value.len(),
509
509
+
});
510
510
+
}
511
511
+
}
512
512
+
if let Some(ref value) = self.summary {
513
513
+
#[allow(unused_comparisons)]
514
514
+
if <str>::len(value.as_ref()) > 2000usize {
515
515
+
return Err(ConstraintError::MaxLength {
516
516
+
path: ValidationPath::from_field("summary"),
517
517
+
max: 2000usize,
518
518
+
actual: <str>::len(value.as_ref()),
519
519
+
});
520
520
+
}
521
521
+
}
522
522
+
if let Some(ref value) = self.summary {
523
523
+
{
524
524
+
let count = UnicodeSegmentation::graphemes(value.as_ref(), true).count();
525
525
+
if count > 300usize {
526
526
+
return Err(ConstraintError::MaxGraphemes {
527
527
+
path: ValidationPath::from_field("summary"),
528
528
+
max: 300usize,
529
529
+
actual: count,
530
530
+
});
531
531
+
}
532
532
+
}
533
533
+
}
534
534
+
if let Some(ref value) = self.tags {
535
535
+
#[allow(unused_comparisons)]
536
536
+
if value.len() > 16usize {
537
537
+
return Err(ConstraintError::MaxLength {
538
538
+
path: ValidationPath::from_field("tags"),
539
539
+
max: 16usize,
540
540
+
actual: value.len(),
541
541
+
});
542
542
+
}
543
543
+
}
544
544
+
Ok(())
545
545
+
}
546
546
+
}
547
547
+
548
548
+
pub mod publish_thing_state {
549
549
+
550
550
+
pub use crate::builder_types::{Set, Unset, IsSet, IsUnset};
551
551
+
#[allow(unused)]
552
552
+
use ::core::marker::PhantomData;
553
553
+
mod sealed {
554
554
+
pub trait Sealed {}
555
555
+
}
556
556
+
/// State trait tracking which required fields have been set
557
557
+
pub trait State: sealed::Sealed {
558
558
+
type Thing;
559
559
+
}
560
560
+
/// Empty state - all required fields are unset
561
561
+
pub struct Empty(());
562
562
+
impl sealed::Sealed for Empty {}
563
563
+
impl State for Empty {
564
564
+
type Thing = Unset;
565
565
+
}
566
566
+
///State transition - sets the `thing` field to Set
567
567
+
pub struct SetThing<St: State = Empty>(PhantomData<fn() -> St>);
568
568
+
impl<St: State> sealed::Sealed for SetThing<St> {}
569
569
+
impl<St: State> State for SetThing<St> {
570
570
+
type Thing = Set<members::thing>;
571
571
+
}
572
572
+
/// Marker types for field names
573
573
+
#[allow(non_camel_case_types)]
574
574
+
pub mod members {
575
575
+
///Marker type for the `thing` field
576
576
+
pub struct thing(());
577
577
+
}
578
578
+
}
579
579
+
580
580
+
/// Builder for constructing an instance of this type.
581
581
+
pub struct PublishThingBuilder<St: publish_thing_state::State, S: BosStr = DefaultStr> {
582
582
+
_state: PhantomData<fn() -> St>,
583
583
+
_fields: (Option<publish_thing::ThingInput<S>>,),
584
584
+
_type: PhantomData<fn() -> S>,
585
585
+
}
586
586
+
587
587
+
impl PublishThing<DefaultStr> {
588
588
+
/// Create a new builder for this type, using the default string type (DefaultStr = SmolStr) if needed
589
589
+
pub fn new() -> PublishThingBuilder<publish_thing_state::Empty, DefaultStr> {
590
590
+
PublishThingBuilder::new()
591
591
+
}
592
592
+
}
593
593
+
594
594
+
impl<S: BosStr> PublishThing<S> {
595
595
+
/// Create a new builder for this type
596
596
+
pub fn builder() -> PublishThingBuilder<publish_thing_state::Empty, S> {
597
597
+
PublishThingBuilder::builder()
598
598
+
}
599
599
+
}
600
600
+
601
601
+
impl PublishThingBuilder<publish_thing_state::Empty, DefaultStr> {
602
602
+
/// Create a new builder with all fields unset, using the default string type, if needed
603
603
+
pub fn new() -> Self {
604
604
+
PublishThingBuilder {
605
605
+
_state: PhantomData,
606
606
+
_fields: (None,),
607
607
+
_type: PhantomData,
608
608
+
}
609
609
+
}
610
610
+
}
611
611
+
612
612
+
impl<S: BosStr> PublishThingBuilder<publish_thing_state::Empty, S> {
613
613
+
/// Create a new builder with all fields unset
614
614
+
pub fn builder() -> Self {
615
615
+
PublishThingBuilder {
616
616
+
_state: PhantomData,
617
617
+
_fields: (None,),
618
618
+
_type: PhantomData,
619
619
+
}
620
620
+
}
621
621
+
}
622
622
+
623
623
+
impl<St, S: BosStr> PublishThingBuilder<St, S>
624
624
+
where
625
625
+
St: publish_thing_state::State,
626
626
+
St::Thing: publish_thing_state::IsUnset,
627
627
+
{
628
628
+
/// Set the `thing` field (required)
629
629
+
pub fn thing(
630
630
+
mut self,
631
631
+
value: impl Into<publish_thing::ThingInput<S>>,
632
632
+
) -> PublishThingBuilder<publish_thing_state::SetThing<St>, S> {
633
633
+
self._fields.0 = Option::Some(value.into());
634
634
+
PublishThingBuilder {
635
635
+
_state: PhantomData,
636
636
+
_fields: self._fields,
637
637
+
_type: PhantomData,
638
638
+
}
639
639
+
}
640
640
+
}
641
641
+
642
642
+
impl<St, S: BosStr> PublishThingBuilder<St, S>
643
643
+
where
644
644
+
St: publish_thing_state::State,
645
645
+
St::Thing: publish_thing_state::IsSet,
646
646
+
{
647
647
+
/// Build the final struct.
648
648
+
pub fn build(self) -> PublishThing<S> {
649
649
+
PublishThing {
650
650
+
thing: self._fields.0.unwrap(),
651
651
+
extra_data: Default::default(),
652
652
+
}
653
653
+
}
654
654
+
/// Build the final struct with custom extra_data.
655
655
+
pub fn build_with_data(
656
656
+
self,
657
657
+
extra_data: BTreeMap<SmolStr, Data<S>>,
658
658
+
) -> PublishThing<S> {
659
659
+
PublishThing {
660
660
+
thing: self._fields.0.unwrap(),
661
661
+
extra_data: Some(extra_data),
662
662
+
}
663
663
+
}
664
664
+
}
665
665
+
666
666
+
pub mod model_input_state {
667
667
+
668
668
+
pub use crate::builder_types::{Set, Unset, IsSet, IsUnset};
669
669
+
#[allow(unused)]
670
670
+
use ::core::marker::PhantomData;
671
671
+
mod sealed {
672
672
+
pub trait Sealed {}
673
673
+
}
674
674
+
/// State trait tracking which required fields have been set
675
675
+
pub trait State: sealed::Sealed {
676
676
+
type Name;
677
677
+
type Parts;
678
678
+
}
679
679
+
/// Empty state - all required fields are unset
680
680
+
pub struct Empty(());
681
681
+
impl sealed::Sealed for Empty {}
682
682
+
impl State for Empty {
683
683
+
type Name = Unset;
684
684
+
type Parts = Unset;
685
685
+
}
686
686
+
///State transition - sets the `name` field to Set
687
687
+
pub struct SetName<St: State = Empty>(PhantomData<fn() -> St>);
688
688
+
impl<St: State> sealed::Sealed for SetName<St> {}
689
689
+
impl<St: State> State for SetName<St> {
690
690
+
type Name = Set<members::name>;
691
691
+
type Parts = St::Parts;
692
692
+
}
693
693
+
///State transition - sets the `parts` field to Set
694
694
+
pub struct SetParts<St: State = Empty>(PhantomData<fn() -> St>);
695
695
+
impl<St: State> sealed::Sealed for SetParts<St> {}
696
696
+
impl<St: State> State for SetParts<St> {
697
697
+
type Name = St::Name;
698
698
+
type Parts = Set<members::parts>;
699
699
+
}
700
700
+
/// Marker types for field names
701
701
+
#[allow(non_camel_case_types)]
702
702
+
pub mod members {
703
703
+
///Marker type for the `name` field
704
704
+
pub struct name(());
705
705
+
///Marker type for the `parts` field
706
706
+
pub struct parts(());
707
707
+
}
708
708
+
}
709
709
+
710
710
+
/// Builder for constructing an instance of this type.
711
711
+
pub struct ModelInputBuilder<St: model_input_state::State, S: BosStr = DefaultStr> {
712
712
+
_state: PhantomData<fn() -> St>,
713
713
+
_fields: (
714
714
+
Option<Vec<Image<S>>>,
715
715
+
Option<StrongRef<S>>,
716
716
+
Option<Vec<S>>,
717
717
+
Option<Vec<StrongRef<S>>>,
718
718
+
Option<S>,
719
719
+
Option<S>,
720
720
+
Option<Vec<publish_thing::PartInput<S>>>,
721
721
+
Option<Vec<Image<S>>>,
722
722
+
Option<S>,
723
723
+
Option<Vec<S>>,
724
724
+
),
725
725
+
_type: PhantomData<fn() -> S>,
726
726
+
}
727
727
+
728
728
+
impl ModelInput<DefaultStr> {
729
729
+
/// Create a new builder for this type, using the default string type (DefaultStr = SmolStr) if needed
730
730
+
pub fn new() -> ModelInputBuilder<model_input_state::Empty, DefaultStr> {
731
731
+
ModelInputBuilder::new()
732
732
+
}
733
733
+
}
734
734
+
735
735
+
impl<S: BosStr> ModelInput<S> {
736
736
+
/// Create a new builder for this type
737
737
+
pub fn builder() -> ModelInputBuilder<model_input_state::Empty, S> {
738
738
+
ModelInputBuilder::builder()
739
739
+
}
740
740
+
}
741
741
+
742
742
+
impl ModelInputBuilder<model_input_state::Empty, DefaultStr> {
743
743
+
/// Create a new builder with all fields unset, using the default string type, if needed
744
744
+
pub fn new() -> Self {
745
745
+
ModelInputBuilder {
746
746
+
_state: PhantomData,
747
747
+
_fields: (None, None, None, None, None, None, None, None, None, None),
748
748
+
_type: PhantomData,
749
749
+
}
750
750
+
}
751
751
+
}
752
752
+
753
753
+
impl<S: BosStr> ModelInputBuilder<model_input_state::Empty, S> {
754
754
+
/// Create a new builder with all fields unset
755
755
+
pub fn builder() -> Self {
756
756
+
ModelInputBuilder {
757
757
+
_state: PhantomData,
758
758
+
_fields: (None, None, None, None, None, None, None, None, None, None),
759
759
+
_type: PhantomData,
760
760
+
}
761
761
+
}
762
762
+
}
763
763
+
764
764
+
impl<St: model_input_state::State, S: BosStr> ModelInputBuilder<St, S> {
765
765
+
/// Set the `cover` field (optional)
766
766
+
pub fn cover(mut self, value: impl Into<Option<Vec<Image<S>>>>) -> Self {
767
767
+
self._fields.0 = value.into();
768
768
+
self
769
769
+
}
770
770
+
/// Set the `cover` field to an Option value (optional)
771
771
+
pub fn maybe_cover(mut self, value: Option<Vec<Image<S>>>) -> Self {
772
772
+
self._fields.0 = value;
773
773
+
self
774
774
+
}
775
775
+
}
776
776
+
777
777
+
impl<St: model_input_state::State, S: BosStr> ModelInputBuilder<St, S> {
778
778
+
/// Set the `derivedFrom` field (optional)
779
779
+
pub fn derived_from(mut self, value: impl Into<Option<StrongRef<S>>>) -> Self {
780
780
+
self._fields.1 = value.into();
781
781
+
self
782
782
+
}
783
783
+
/// Set the `derivedFrom` field to an Option value (optional)
784
784
+
pub fn maybe_derived_from(mut self, value: Option<StrongRef<S>>) -> Self {
785
785
+
self._fields.1 = value;
786
786
+
self
787
787
+
}
788
788
+
}
789
789
+
790
790
+
impl<St: model_input_state::State, S: BosStr> ModelInputBuilder<St, S> {
791
791
+
/// Set the `instructions` field (optional)
792
792
+
pub fn instructions(mut self, value: impl Into<Option<Vec<S>>>) -> Self {
793
793
+
self._fields.2 = value.into();
794
794
+
self
795
795
+
}
796
796
+
/// Set the `instructions` field to an Option value (optional)
797
797
+
pub fn maybe_instructions(mut self, value: Option<Vec<S>>) -> Self {
798
798
+
self._fields.2 = value;
799
799
+
self
800
800
+
}
801
801
+
}
802
802
+
803
803
+
impl<St: model_input_state::State, S: BosStr> ModelInputBuilder<St, S> {
804
804
+
/// Set the `intendedToWorkWith` field (optional)
805
805
+
pub fn intended_to_work_with(
806
806
+
mut self,
807
807
+
value: impl Into<Option<Vec<StrongRef<S>>>>,
808
808
+
) -> Self {
809
809
+
self._fields.3 = value.into();
810
810
+
self
811
811
+
}
812
812
+
/// Set the `intendedToWorkWith` field to an Option value (optional)
813
813
+
pub fn maybe_intended_to_work_with(
814
814
+
mut self,
815
815
+
value: Option<Vec<StrongRef<S>>>,
816
816
+
) -> Self {
817
817
+
self._fields.3 = value;
818
818
+
self
819
819
+
}
820
820
+
}
821
821
+
822
822
+
impl<St: model_input_state::State, S: BosStr> ModelInputBuilder<St, S> {
823
823
+
/// Set the `license` field (optional)
824
824
+
pub fn license(mut self, value: impl Into<Option<S>>) -> Self {
825
825
+
self._fields.4 = value.into();
826
826
+
self
827
827
+
}
828
828
+
/// Set the `license` field to an Option value (optional)
829
829
+
pub fn maybe_license(mut self, value: Option<S>) -> Self {
830
830
+
self._fields.4 = value;
831
831
+
self
832
832
+
}
833
833
+
}
834
834
+
835
835
+
impl<St, S: BosStr> ModelInputBuilder<St, S>
836
836
+
where
837
837
+
St: model_input_state::State,
838
838
+
St::Name: model_input_state::IsUnset,
839
839
+
{
840
840
+
/// Set the `name` field (required)
841
841
+
pub fn name(
842
842
+
mut self,
843
843
+
value: impl Into<S>,
844
844
+
) -> ModelInputBuilder<model_input_state::SetName<St>, S> {
845
845
+
self._fields.5 = Option::Some(value.into());
846
846
+
ModelInputBuilder {
847
847
+
_state: PhantomData,
848
848
+
_fields: self._fields,
849
849
+
_type: PhantomData,
850
850
+
}
851
851
+
}
852
852
+
}
853
853
+
854
854
+
impl<St, S: BosStr> ModelInputBuilder<St, S>
855
855
+
where
856
856
+
St: model_input_state::State,
857
857
+
St::Parts: model_input_state::IsUnset,
858
858
+
{
859
859
+
/// Set the `parts` field (required)
860
860
+
pub fn parts(
861
861
+
mut self,
862
862
+
value: impl Into<Vec<publish_thing::PartInput<S>>>,
863
863
+
) -> ModelInputBuilder<model_input_state::SetParts<St>, S> {
864
864
+
self._fields.6 = Option::Some(value.into());
865
865
+
ModelInputBuilder {
866
866
+
_state: PhantomData,
867
867
+
_fields: self._fields,
868
868
+
_type: PhantomData,
869
869
+
}
870
870
+
}
871
871
+
}
872
872
+
873
873
+
impl<St: model_input_state::State, S: BosStr> ModelInputBuilder<St, S> {
874
874
+
/// Set the `previews` field (optional)
875
875
+
pub fn previews(mut self, value: impl Into<Option<Vec<Image<S>>>>) -> Self {
876
876
+
self._fields.7 = value.into();
877
877
+
self
878
878
+
}
879
879
+
/// Set the `previews` field to an Option value (optional)
880
880
+
pub fn maybe_previews(mut self, value: Option<Vec<Image<S>>>) -> Self {
881
881
+
self._fields.7 = value;
882
882
+
self
883
883
+
}
884
884
+
}
885
885
+
886
886
+
impl<St: model_input_state::State, S: BosStr> ModelInputBuilder<St, S> {
887
887
+
/// Set the `summary` field (optional)
888
888
+
pub fn summary(mut self, value: impl Into<Option<S>>) -> Self {
889
889
+
self._fields.8 = value.into();
890
890
+
self
891
891
+
}
892
892
+
/// Set the `summary` field to an Option value (optional)
893
893
+
pub fn maybe_summary(mut self, value: Option<S>) -> Self {
894
894
+
self._fields.8 = value;
895
895
+
self
896
896
+
}
897
897
+
}
898
898
+
899
899
+
impl<St: model_input_state::State, S: BosStr> ModelInputBuilder<St, S> {
900
900
+
/// Set the `tags` field (optional)
901
901
+
pub fn tags(mut self, value: impl Into<Option<Vec<S>>>) -> Self {
902
902
+
self._fields.9 = value.into();
903
903
+
self
904
904
+
}
905
905
+
/// Set the `tags` field to an Option value (optional)
906
906
+
pub fn maybe_tags(mut self, value: Option<Vec<S>>) -> Self {
907
907
+
self._fields.9 = value;
908
908
+
self
909
909
+
}
910
910
+
}
911
911
+
912
912
+
impl<St, S: BosStr> ModelInputBuilder<St, S>
913
913
+
where
914
914
+
St: model_input_state::State,
915
915
+
St::Name: model_input_state::IsSet,
916
916
+
St::Parts: model_input_state::IsSet,
917
917
+
{
918
918
+
/// Build the final struct.
919
919
+
pub fn build(self) -> ModelInput<S> {
920
920
+
ModelInput {
921
921
+
cover: self._fields.0,
922
922
+
derived_from: self._fields.1,
923
923
+
instructions: self._fields.2,
924
924
+
intended_to_work_with: self._fields.3,
925
925
+
license: self._fields.4,
926
926
+
name: self._fields.5.unwrap(),
927
927
+
parts: self._fields.6.unwrap(),
928
928
+
previews: self._fields.7,
929
929
+
summary: self._fields.8,
930
930
+
tags: self._fields.9,
931
931
+
extra_data: Default::default(),
932
932
+
}
933
933
+
}
934
934
+
/// Build the final struct with custom extra_data.
935
935
+
pub fn build_with_data(
936
936
+
self,
937
937
+
extra_data: BTreeMap<SmolStr, Data<S>>,
938
938
+
) -> ModelInput<S> {
939
939
+
ModelInput {
940
940
+
cover: self._fields.0,
941
941
+
derived_from: self._fields.1,
942
942
+
instructions: self._fields.2,
943
943
+
intended_to_work_with: self._fields.3,
944
944
+
license: self._fields.4,
945
945
+
name: self._fields.5.unwrap(),
946
946
+
parts: self._fields.6.unwrap(),
947
947
+
previews: self._fields.7,
948
948
+
summary: self._fields.8,
949
949
+
tags: self._fields.9,
950
950
+
extra_data: Some(extra_data),
951
951
+
}
952
952
+
}
953
953
+
}
954
954
+
955
955
+
fn lexicon_doc_space_polymodel_library_publishThing() -> LexiconDoc<'static> {
956
956
+
#[allow(unused_imports)]
957
957
+
use jacquard_common::{CowStr, deps::smol_str::SmolStr, types::blob::MimeType};
958
958
+
use jacquard_lexicon::lexicon::*;
959
959
+
use alloc::collections::BTreeMap;
960
960
+
LexiconDoc {
961
961
+
lexicon: Lexicon::Lexicon1,
962
962
+
id: CowStr::new_static("space.polymodel.library.publishThing"),
963
963
+
defs: {
964
964
+
let mut map = BTreeMap::new();
965
965
+
map.insert(
966
966
+
SmolStr::new_static("main"),
967
967
+
LexUserType::XrpcProcedure(LexXrpcProcedure {
968
968
+
input: Some(LexXrpcBody {
969
969
+
encoding: CowStr::new_static("application/json"),
970
970
+
schema: Some(
971
971
+
LexXrpcBodySchema::Object(LexObject {
972
972
+
required: Some(vec![SmolStr::new_static("thing")]),
973
973
+
properties: {
974
974
+
#[allow(unused_mut)]
975
975
+
let mut map = BTreeMap::new();
976
976
+
map.insert(
977
977
+
SmolStr::new_static("thing"),
978
978
+
LexObjectProperty::Ref(LexRef {
979
979
+
r#ref: CowStr::new_static("#thingInput"),
980
980
+
..Default::default()
981
981
+
}),
982
982
+
);
983
983
+
map
984
984
+
},
985
985
+
..Default::default()
986
986
+
}),
987
987
+
),
988
988
+
..Default::default()
989
989
+
}),
990
990
+
..Default::default()
991
991
+
}),
992
992
+
);
993
993
+
map.insert(
994
994
+
SmolStr::new_static("modelInput"),
995
995
+
LexUserType::Object(LexObject {
996
996
+
required: Some(
997
997
+
vec![SmolStr::new_static("name"), SmolStr::new_static("parts")],
998
998
+
),
999
999
+
properties: {
1000
1000
+
#[allow(unused_mut)]
1001
1001
+
let mut map = BTreeMap::new();
1002
1002
+
map.insert(
1003
1003
+
SmolStr::new_static("cover"),
1004
1004
+
LexObjectProperty::Array(LexArray {
1005
1005
+
items: LexArrayItem::Ref(LexRef {
1006
1006
+
r#ref: CowStr::new_static(
1007
1007
+
"space.polymodel.library.defs#image",
1008
1008
+
),
1009
1009
+
..Default::default()
1010
1010
+
}),
1011
1011
+
max_length: Some(8usize),
1012
1012
+
..Default::default()
1013
1013
+
}),
1014
1014
+
);
1015
1015
+
map.insert(
1016
1016
+
SmolStr::new_static("derivedFrom"),
1017
1017
+
LexObjectProperty::Ref(LexRef {
1018
1018
+
r#ref: CowStr::new_static("com.atproto.repo.strongRef"),
1019
1019
+
..Default::default()
1020
1020
+
}),
1021
1021
+
);
1022
1022
+
map.insert(
1023
1023
+
SmolStr::new_static("instructions"),
1024
1024
+
LexObjectProperty::Array(LexArray {
1025
1025
+
items: LexArrayItem::String(LexString {
1026
1026
+
..Default::default()
1027
1027
+
}),
1028
1028
+
max_length: Some(100usize),
1029
1029
+
..Default::default()
1030
1030
+
}),
1031
1031
+
);
1032
1032
+
map.insert(
1033
1033
+
SmolStr::new_static("intendedToWorkWith"),
1034
1034
+
LexObjectProperty::Array(LexArray {
1035
1035
+
items: LexArrayItem::Ref(LexRef {
1036
1036
+
r#ref: CowStr::new_static("com.atproto.repo.strongRef"),
1037
1037
+
..Default::default()
1038
1038
+
}),
1039
1039
+
max_length: Some(32usize),
1040
1040
+
..Default::default()
1041
1041
+
}),
1042
1042
+
);
1043
1043
+
map.insert(
1044
1044
+
SmolStr::new_static("license"),
1045
1045
+
LexObjectProperty::String(LexString { ..Default::default() }),
1046
1046
+
);
1047
1047
+
map.insert(
1048
1048
+
SmolStr::new_static("name"),
1049
1049
+
LexObjectProperty::String(LexString {
1050
1050
+
max_length: Some(256usize),
1051
1051
+
max_graphemes: Some(64usize),
1052
1052
+
..Default::default()
1053
1053
+
}),
1054
1054
+
);
1055
1055
+
map.insert(
1056
1056
+
SmolStr::new_static("parts"),
1057
1057
+
LexObjectProperty::Array(LexArray {
1058
1058
+
items: LexArrayItem::Ref(LexRef {
1059
1059
+
r#ref: CowStr::new_static("#partInput"),
1060
1060
+
..Default::default()
1061
1061
+
}),
1062
1062
+
max_length: Some(1024usize),
1063
1063
+
..Default::default()
1064
1064
+
}),
1065
1065
+
);
1066
1066
+
map.insert(
1067
1067
+
SmolStr::new_static("previews"),
1068
1068
+
LexObjectProperty::Array(LexArray {
1069
1069
+
items: LexArrayItem::Ref(LexRef {
1070
1070
+
r#ref: CowStr::new_static(
1071
1071
+
"space.polymodel.library.defs#image",
1072
1072
+
),
1073
1073
+
..Default::default()
1074
1074
+
}),
1075
1075
+
max_length: Some(12usize),
1076
1076
+
..Default::default()
1077
1077
+
}),
1078
1078
+
);
1079
1079
+
map.insert(
1080
1080
+
SmolStr::new_static("summary"),
1081
1081
+
LexObjectProperty::String(LexString {
1082
1082
+
max_length: Some(2000usize),
1083
1083
+
max_graphemes: Some(300usize),
1084
1084
+
..Default::default()
1085
1085
+
}),
1086
1086
+
);
1087
1087
+
map.insert(
1088
1088
+
SmolStr::new_static("tags"),
1089
1089
+
LexObjectProperty::Array(LexArray {
1090
1090
+
items: LexArrayItem::String(LexString {
1091
1091
+
max_length: Some(64usize),
1092
1092
+
max_graphemes: Some(32usize),
1093
1093
+
..Default::default()
1094
1094
+
}),
1095
1095
+
max_length: Some(16usize),
1096
1096
+
..Default::default()
1097
1097
+
}),
1098
1098
+
);
1099
1099
+
map
1100
1100
+
},
1101
1101
+
..Default::default()
1102
1102
+
}),
1103
1103
+
);
1104
1104
+
map.insert(
1105
1105
+
SmolStr::new_static("modelPublishResult"),
1106
1106
+
LexUserType::Object(LexObject {
1107
1107
+
required: Some(
1108
1108
+
vec![SmolStr::new_static("model"), SmolStr::new_static("parts")],
1109
1109
+
),
1110
1110
+
properties: {
1111
1111
+
#[allow(unused_mut)]
1112
1112
+
let mut map = BTreeMap::new();
1113
1113
+
map.insert(
1114
1114
+
SmolStr::new_static("model"),
1115
1115
+
LexObjectProperty::Ref(LexRef {
1116
1116
+
r#ref: CowStr::new_static("#recordRef"),
1117
1117
+
..Default::default()
1118
1118
+
}),
1119
1119
+
);
1120
1120
+
map.insert(
1121
1121
+
SmolStr::new_static("parts"),
1122
1122
+
LexObjectProperty::Array(LexArray {
1123
1123
+
items: LexArrayItem::Ref(LexRef {
1124
1124
+
r#ref: CowStr::new_static("#recordRef"),
1125
1125
+
..Default::default()
1126
1126
+
}),
1127
1127
+
..Default::default()
1128
1128
+
}),
1129
1129
+
);
1130
1130
+
map
1131
1131
+
},
1132
1132
+
..Default::default()
1133
1133
+
}),
1134
1134
+
);
1135
1135
+
map.insert(
1136
1136
+
SmolStr::new_static("partInput"),
1137
1137
+
LexUserType::Object(LexObject {
1138
1138
+
required: Some(vec![SmolStr::new_static("name")]),
1139
1139
+
properties: {
1140
1140
+
#[allow(unused_mut)]
1141
1141
+
let mut map = BTreeMap::new();
1142
1142
+
map.insert(
1143
1143
+
SmolStr::new_static("derivedFrom"),
1144
1144
+
LexObjectProperty::Ref(LexRef {
1145
1145
+
r#ref: CowStr::new_static("com.atproto.repo.strongRef"),
1146
1146
+
..Default::default()
1147
1147
+
}),
1148
1148
+
);
1149
1149
+
map.insert(
1150
1150
+
SmolStr::new_static("dimensions"),
1151
1151
+
LexObjectProperty::Ref(LexRef {
1152
1152
+
r#ref: CowStr::new_static(
1153
1153
+
"space.polymodel.library.defs#bbox",
1154
1154
+
),
1155
1155
+
..Default::default()
1156
1156
+
}),
1157
1157
+
);
1158
1158
+
map.insert(
1159
1159
+
SmolStr::new_static("file"),
1160
1160
+
LexObjectProperty::Ref(LexRef {
1161
1161
+
r#ref: CowStr::new_static(
1162
1162
+
"space.polymodel.library.defs#file",
1163
1163
+
),
1164
1164
+
..Default::default()
1165
1165
+
}),
1166
1166
+
);
1167
1167
+
map.insert(
1168
1168
+
SmolStr::new_static("format"),
1169
1169
+
LexObjectProperty::String(LexString { ..Default::default() }),
1170
1170
+
);
1171
1171
+
map.insert(
1172
1172
+
SmolStr::new_static("intendedToWorkWith"),
1173
1173
+
LexObjectProperty::Array(LexArray {
1174
1174
+
items: LexArrayItem::Ref(LexRef {
1175
1175
+
r#ref: CowStr::new_static("com.atproto.repo.strongRef"),
1176
1176
+
..Default::default()
1177
1177
+
}),
1178
1178
+
max_length: Some(32usize),
1179
1179
+
..Default::default()
1180
1180
+
}),
1181
1181
+
);
1182
1182
+
map.insert(
1183
1183
+
SmolStr::new_static("name"),
1184
1184
+
LexObjectProperty::String(LexString {
1185
1185
+
max_length: Some(256usize),
1186
1186
+
max_graphemes: Some(64usize),
1187
1187
+
..Default::default()
1188
1188
+
}),
1189
1189
+
);
1190
1190
+
map.insert(
1191
1191
+
SmolStr::new_static("notes"),
1192
1192
+
LexObjectProperty::String(LexString {
1193
1193
+
max_length: Some(2000usize),
1194
1194
+
max_graphemes: Some(300usize),
1195
1195
+
..Default::default()
1196
1196
+
}),
1197
1197
+
);
1198
1198
+
map.insert(
1199
1199
+
SmolStr::new_static("previews"),
1200
1200
+
LexObjectProperty::Array(LexArray {
1201
1201
+
items: LexArrayItem::Ref(LexRef {
1202
1202
+
r#ref: CowStr::new_static(
1203
1203
+
"space.polymodel.library.defs#image",
1204
1204
+
),
1205
1205
+
..Default::default()
1206
1206
+
}),
1207
1207
+
max_length: Some(12usize),
1208
1208
+
..Default::default()
1209
1209
+
}),
1210
1210
+
);
1211
1211
+
map.insert(
1212
1212
+
SmolStr::new_static("printSettings"),
1213
1213
+
LexObjectProperty::Array(LexArray {
1214
1214
+
items: LexArrayItem::String(LexString {
1215
1215
+
..Default::default()
1216
1216
+
}),
1217
1217
+
max_length: Some(50usize),
1218
1218
+
..Default::default()
1219
1219
+
}),
1220
1220
+
);
1221
1221
+
map.insert(
1222
1222
+
SmolStr::new_static("units"),
1223
1223
+
LexObjectProperty::String(LexString { ..Default::default() }),
1224
1224
+
);
1225
1225
+
map.insert(
1226
1226
+
SmolStr::new_static("uploadId"),
1227
1227
+
LexObjectProperty::String(LexString { ..Default::default() }),
1228
1228
+
);
1229
1229
+
map
1230
1230
+
},
1231
1231
+
..Default::default()
1232
1232
+
}),
1233
1233
+
);
1234
1234
+
map.insert(
1235
1235
+
SmolStr::new_static("recordRef"),
1236
1236
+
LexUserType::Object(LexObject {
1237
1237
+
required: Some(
1238
1238
+
vec![SmolStr::new_static("uri"), SmolStr::new_static("cid")],
1239
1239
+
),
1240
1240
+
properties: {
1241
1241
+
#[allow(unused_mut)]
1242
1242
+
let mut map = BTreeMap::new();
1243
1243
+
map.insert(
1244
1244
+
SmolStr::new_static("cid"),
1245
1245
+
LexObjectProperty::String(LexString {
1246
1246
+
format: Some(LexStringFormat::Cid),
1247
1247
+
..Default::default()
1248
1248
+
}),
1249
1249
+
);
1250
1250
+
map.insert(
1251
1251
+
SmolStr::new_static("uri"),
1252
1252
+
LexObjectProperty::String(LexString {
1253
1253
+
format: Some(LexStringFormat::AtUri),
1254
1254
+
..Default::default()
1255
1255
+
}),
1256
1256
+
);
1257
1257
+
map
1258
1258
+
},
1259
1259
+
..Default::default()
1260
1260
+
}),
1261
1261
+
);
1262
1262
+
map.insert(
1263
1263
+
SmolStr::new_static("thingInput"),
1264
1264
+
LexUserType::Object(LexObject {
1265
1265
+
required: Some(
1266
1266
+
vec![
1267
1267
+
SmolStr::new_static("name"), SmolStr::new_static("license"),
1268
1268
+
SmolStr::new_static("models")
1269
1269
+
],
1270
1270
+
),
1271
1271
+
properties: {
1272
1272
+
#[allow(unused_mut)]
1273
1273
+
let mut map = BTreeMap::new();
1274
1274
+
map.insert(
1275
1275
+
SmolStr::new_static("cover"),
1276
1276
+
LexObjectProperty::Array(LexArray {
1277
1277
+
items: LexArrayItem::Ref(LexRef {
1278
1278
+
r#ref: CowStr::new_static(
1279
1279
+
"space.polymodel.library.defs#image",
1280
1280
+
),
1281
1281
+
..Default::default()
1282
1282
+
}),
1283
1283
+
max_length: Some(8usize),
1284
1284
+
..Default::default()
1285
1285
+
}),
1286
1286
+
);
1287
1287
+
map.insert(
1288
1288
+
SmolStr::new_static("derivedFrom"),
1289
1289
+
LexObjectProperty::Ref(LexRef {
1290
1290
+
r#ref: CowStr::new_static("com.atproto.repo.strongRef"),
1291
1291
+
..Default::default()
1292
1292
+
}),
1293
1293
+
);
1294
1294
+
map.insert(
1295
1295
+
SmolStr::new_static("instructions"),
1296
1296
+
LexObjectProperty::Array(LexArray {
1297
1297
+
items: LexArrayItem::String(LexString {
1298
1298
+
..Default::default()
1299
1299
+
}),
1300
1300
+
max_length: Some(100usize),
1301
1301
+
..Default::default()
1302
1302
+
}),
1303
1303
+
);
1304
1304
+
map.insert(
1305
1305
+
SmolStr::new_static("intendedToWorkWith"),
1306
1306
+
LexObjectProperty::Array(LexArray {
1307
1307
+
items: LexArrayItem::Ref(LexRef {
1308
1308
+
r#ref: CowStr::new_static("com.atproto.repo.strongRef"),
1309
1309
+
..Default::default()
1310
1310
+
}),
1311
1311
+
max_length: Some(32usize),
1312
1312
+
..Default::default()
1313
1313
+
}),
1314
1314
+
);
1315
1315
+
map.insert(
1316
1316
+
SmolStr::new_static("license"),
1317
1317
+
LexObjectProperty::String(LexString { ..Default::default() }),
1318
1318
+
);
1319
1319
+
map.insert(
1320
1320
+
SmolStr::new_static("models"),
1321
1321
+
LexObjectProperty::Array(LexArray {
1322
1322
+
items: LexArrayItem::Ref(LexRef {
1323
1323
+
r#ref: CowStr::new_static("#modelInput"),
1324
1324
+
..Default::default()
1325
1325
+
}),
1326
1326
+
max_length: Some(256usize),
1327
1327
+
..Default::default()
1328
1328
+
}),
1329
1329
+
);
1330
1330
+
map.insert(
1331
1331
+
SmolStr::new_static("name"),
1332
1332
+
LexObjectProperty::String(LexString {
1333
1333
+
max_length: Some(256usize),
1334
1334
+
max_graphemes: Some(64usize),
1335
1335
+
..Default::default()
1336
1336
+
}),
1337
1337
+
);
1338
1338
+
map.insert(
1339
1339
+
SmolStr::new_static("previews"),
1340
1340
+
LexObjectProperty::Array(LexArray {
1341
1341
+
items: LexArrayItem::Ref(LexRef {
1342
1342
+
r#ref: CowStr::new_static(
1343
1343
+
"space.polymodel.library.defs#image",
1344
1344
+
),
1345
1345
+
..Default::default()
1346
1346
+
}),
1347
1347
+
max_length: Some(12usize),
1348
1348
+
..Default::default()
1349
1349
+
}),
1350
1350
+
);
1351
1351
+
map.insert(
1352
1352
+
SmolStr::new_static("summary"),
1353
1353
+
LexObjectProperty::String(LexString {
1354
1354
+
max_length: Some(2000usize),
1355
1355
+
max_graphemes: Some(300usize),
1356
1356
+
..Default::default()
1357
1357
+
}),
1358
1358
+
);
1359
1359
+
map.insert(
1360
1360
+
SmolStr::new_static("tags"),
1361
1361
+
LexObjectProperty::Array(LexArray {
1362
1362
+
items: LexArrayItem::String(LexString {
1363
1363
+
max_length: Some(64usize),
1364
1364
+
max_graphemes: Some(32usize),
1365
1365
+
..Default::default()
1366
1366
+
}),
1367
1367
+
max_length: Some(16usize),
1368
1368
+
..Default::default()
1369
1369
+
}),
1370
1370
+
);
1371
1371
+
map
1372
1372
+
},
1373
1373
+
..Default::default()
1374
1374
+
}),
1375
1375
+
);
1376
1376
+
map
1377
1377
+
},
1378
1378
+
..Default::default()
1379
1379
+
}
1380
1380
+
}
1381
1381
+
1382
1382
+
pub mod model_publish_result_state {
1383
1383
+
1384
1384
+
pub use crate::builder_types::{Set, Unset, IsSet, IsUnset};
1385
1385
+
#[allow(unused)]
1386
1386
+
use ::core::marker::PhantomData;
1387
1387
+
mod sealed {
1388
1388
+
pub trait Sealed {}
1389
1389
+
}
1390
1390
+
/// State trait tracking which required fields have been set
1391
1391
+
pub trait State: sealed::Sealed {
1392
1392
+
type Model;
1393
1393
+
type Parts;
1394
1394
+
}
1395
1395
+
/// Empty state - all required fields are unset
1396
1396
+
pub struct Empty(());
1397
1397
+
impl sealed::Sealed for Empty {}
1398
1398
+
impl State for Empty {
1399
1399
+
type Model = Unset;
1400
1400
+
type Parts = Unset;
1401
1401
+
}
1402
1402
+
///State transition - sets the `model` field to Set
1403
1403
+
pub struct SetModel<St: State = Empty>(PhantomData<fn() -> St>);
1404
1404
+
impl<St: State> sealed::Sealed for SetModel<St> {}
1405
1405
+
impl<St: State> State for SetModel<St> {
1406
1406
+
type Model = Set<members::model>;
1407
1407
+
type Parts = St::Parts;
1408
1408
+
}
1409
1409
+
///State transition - sets the `parts` field to Set
1410
1410
+
pub struct SetParts<St: State = Empty>(PhantomData<fn() -> St>);
1411
1411
+
impl<St: State> sealed::Sealed for SetParts<St> {}
1412
1412
+
impl<St: State> State for SetParts<St> {
1413
1413
+
type Model = St::Model;
1414
1414
+
type Parts = Set<members::parts>;
1415
1415
+
}
1416
1416
+
/// Marker types for field names
1417
1417
+
#[allow(non_camel_case_types)]
1418
1418
+
pub mod members {
1419
1419
+
///Marker type for the `model` field
1420
1420
+
pub struct model(());
1421
1421
+
///Marker type for the `parts` field
1422
1422
+
pub struct parts(());
1423
1423
+
}
1424
1424
+
}
1425
1425
+
1426
1426
+
/// Builder for constructing an instance of this type.
1427
1427
+
pub struct ModelPublishResultBuilder<
1428
1428
+
St: model_publish_result_state::State,
1429
1429
+
S: BosStr = DefaultStr,
1430
1430
+
> {
1431
1431
+
_state: PhantomData<fn() -> St>,
1432
1432
+
_fields: (
1433
1433
+
Option<publish_thing::RecordRef<S>>,
1434
1434
+
Option<Vec<publish_thing::RecordRef<S>>>,
1435
1435
+
),
1436
1436
+
_type: PhantomData<fn() -> S>,
1437
1437
+
}
1438
1438
+
1439
1439
+
impl ModelPublishResult<DefaultStr> {
1440
1440
+
/// Create a new builder for this type, using the default string type (DefaultStr = SmolStr) if needed
1441
1441
+
pub fn new() -> ModelPublishResultBuilder<
1442
1442
+
model_publish_result_state::Empty,
1443
1443
+
DefaultStr,
1444
1444
+
> {
1445
1445
+
ModelPublishResultBuilder::new()
1446
1446
+
}
1447
1447
+
}
1448
1448
+
1449
1449
+
impl<S: BosStr> ModelPublishResult<S> {
1450
1450
+
/// Create a new builder for this type
1451
1451
+
pub fn builder() -> ModelPublishResultBuilder<model_publish_result_state::Empty, S> {
1452
1452
+
ModelPublishResultBuilder::builder()
1453
1453
+
}
1454
1454
+
}
1455
1455
+
1456
1456
+
impl ModelPublishResultBuilder<model_publish_result_state::Empty, DefaultStr> {
1457
1457
+
/// Create a new builder with all fields unset, using the default string type, if needed
1458
1458
+
pub fn new() -> Self {
1459
1459
+
ModelPublishResultBuilder {
1460
1460
+
_state: PhantomData,
1461
1461
+
_fields: (None, None),
1462
1462
+
_type: PhantomData,
1463
1463
+
}
1464
1464
+
}
1465
1465
+
}
1466
1466
+
1467
1467
+
impl<S: BosStr> ModelPublishResultBuilder<model_publish_result_state::Empty, S> {
1468
1468
+
/// Create a new builder with all fields unset
1469
1469
+
pub fn builder() -> Self {
1470
1470
+
ModelPublishResultBuilder {
1471
1471
+
_state: PhantomData,
1472
1472
+
_fields: (None, None),
1473
1473
+
_type: PhantomData,
1474
1474
+
}
1475
1475
+
}
1476
1476
+
}
1477
1477
+
1478
1478
+
impl<St, S: BosStr> ModelPublishResultBuilder<St, S>
1479
1479
+
where
1480
1480
+
St: model_publish_result_state::State,
1481
1481
+
St::Model: model_publish_result_state::IsUnset,
1482
1482
+
{
1483
1483
+
/// Set the `model` field (required)
1484
1484
+
pub fn model(
1485
1485
+
mut self,
1486
1486
+
value: impl Into<publish_thing::RecordRef<S>>,
1487
1487
+
) -> ModelPublishResultBuilder<model_publish_result_state::SetModel<St>, S> {
1488
1488
+
self._fields.0 = Option::Some(value.into());
1489
1489
+
ModelPublishResultBuilder {
1490
1490
+
_state: PhantomData,
1491
1491
+
_fields: self._fields,
1492
1492
+
_type: PhantomData,
1493
1493
+
}
1494
1494
+
}
1495
1495
+
}
1496
1496
+
1497
1497
+
impl<St, S: BosStr> ModelPublishResultBuilder<St, S>
1498
1498
+
where
1499
1499
+
St: model_publish_result_state::State,
1500
1500
+
St::Parts: model_publish_result_state::IsUnset,
1501
1501
+
{
1502
1502
+
/// Set the `parts` field (required)
1503
1503
+
pub fn parts(
1504
1504
+
mut self,
1505
1505
+
value: impl Into<Vec<publish_thing::RecordRef<S>>>,
1506
1506
+
) -> ModelPublishResultBuilder<model_publish_result_state::SetParts<St>, S> {
1507
1507
+
self._fields.1 = Option::Some(value.into());
1508
1508
+
ModelPublishResultBuilder {
1509
1509
+
_state: PhantomData,
1510
1510
+
_fields: self._fields,
1511
1511
+
_type: PhantomData,
1512
1512
+
}
1513
1513
+
}
1514
1514
+
}
1515
1515
+
1516
1516
+
impl<St, S: BosStr> ModelPublishResultBuilder<St, S>
1517
1517
+
where
1518
1518
+
St: model_publish_result_state::State,
1519
1519
+
St::Model: model_publish_result_state::IsSet,
1520
1520
+
St::Parts: model_publish_result_state::IsSet,
1521
1521
+
{
1522
1522
+
/// Build the final struct.
1523
1523
+
pub fn build(self) -> ModelPublishResult<S> {
1524
1524
+
ModelPublishResult {
1525
1525
+
model: self._fields.0.unwrap(),
1526
1526
+
parts: self._fields.1.unwrap(),
1527
1527
+
extra_data: Default::default(),
1528
1528
+
}
1529
1529
+
}
1530
1530
+
/// Build the final struct with custom extra_data.
1531
1531
+
pub fn build_with_data(
1532
1532
+
self,
1533
1533
+
extra_data: BTreeMap<SmolStr, Data<S>>,
1534
1534
+
) -> ModelPublishResult<S> {
1535
1535
+
ModelPublishResult {
1536
1536
+
model: self._fields.0.unwrap(),
1537
1537
+
parts: self._fields.1.unwrap(),
1538
1538
+
extra_data: Some(extra_data),
1539
1539
+
}
1540
1540
+
}
1541
1541
+
}
1542
1542
+
1543
1543
+
pub mod record_ref_state {
1544
1544
+
1545
1545
+
pub use crate::builder_types::{Set, Unset, IsSet, IsUnset};
1546
1546
+
#[allow(unused)]
1547
1547
+
use ::core::marker::PhantomData;
1548
1548
+
mod sealed {
1549
1549
+
pub trait Sealed {}
1550
1550
+
}
1551
1551
+
/// State trait tracking which required fields have been set
1552
1552
+
pub trait State: sealed::Sealed {
1553
1553
+
type Cid;
1554
1554
+
type Uri;
1555
1555
+
}
1556
1556
+
/// Empty state - all required fields are unset
1557
1557
+
pub struct Empty(());
1558
1558
+
impl sealed::Sealed for Empty {}
1559
1559
+
impl State for Empty {
1560
1560
+
type Cid = Unset;
1561
1561
+
type Uri = Unset;
1562
1562
+
}
1563
1563
+
///State transition - sets the `cid` field to Set
1564
1564
+
pub struct SetCid<St: State = Empty>(PhantomData<fn() -> St>);
1565
1565
+
impl<St: State> sealed::Sealed for SetCid<St> {}
1566
1566
+
impl<St: State> State for SetCid<St> {
1567
1567
+
type Cid = Set<members::cid>;
1568
1568
+
type Uri = St::Uri;
1569
1569
+
}
1570
1570
+
///State transition - sets the `uri` field to Set
1571
1571
+
pub struct SetUri<St: State = Empty>(PhantomData<fn() -> St>);
1572
1572
+
impl<St: State> sealed::Sealed for SetUri<St> {}
1573
1573
+
impl<St: State> State for SetUri<St> {
1574
1574
+
type Cid = St::Cid;
1575
1575
+
type Uri = Set<members::uri>;
1576
1576
+
}
1577
1577
+
/// Marker types for field names
1578
1578
+
#[allow(non_camel_case_types)]
1579
1579
+
pub mod members {
1580
1580
+
///Marker type for the `cid` field
1581
1581
+
pub struct cid(());
1582
1582
+
///Marker type for the `uri` field
1583
1583
+
pub struct uri(());
1584
1584
+
}
1585
1585
+
}
1586
1586
+
1587
1587
+
/// Builder for constructing an instance of this type.
1588
1588
+
pub struct RecordRefBuilder<St: record_ref_state::State, S: BosStr = DefaultStr> {
1589
1589
+
_state: PhantomData<fn() -> St>,
1590
1590
+
_fields: (Option<Cid<S>>, Option<AtUri<S>>),
1591
1591
+
_type: PhantomData<fn() -> S>,
1592
1592
+
}
1593
1593
+
1594
1594
+
impl RecordRef<DefaultStr> {
1595
1595
+
/// Create a new builder for this type, using the default string type (DefaultStr = SmolStr) if needed
1596
1596
+
pub fn new() -> RecordRefBuilder<record_ref_state::Empty, DefaultStr> {
1597
1597
+
RecordRefBuilder::new()
1598
1598
+
}
1599
1599
+
}
1600
1600
+
1601
1601
+
impl<S: BosStr> RecordRef<S> {
1602
1602
+
/// Create a new builder for this type
1603
1603
+
pub fn builder() -> RecordRefBuilder<record_ref_state::Empty, S> {
1604
1604
+
RecordRefBuilder::builder()
1605
1605
+
}
1606
1606
+
}
1607
1607
+
1608
1608
+
impl RecordRefBuilder<record_ref_state::Empty, DefaultStr> {
1609
1609
+
/// Create a new builder with all fields unset, using the default string type, if needed
1610
1610
+
pub fn new() -> Self {
1611
1611
+
RecordRefBuilder {
1612
1612
+
_state: PhantomData,
1613
1613
+
_fields: (None, None),
1614
1614
+
_type: PhantomData,
1615
1615
+
}
1616
1616
+
}
1617
1617
+
}
1618
1618
+
1619
1619
+
impl<S: BosStr> RecordRefBuilder<record_ref_state::Empty, S> {
1620
1620
+
/// Create a new builder with all fields unset
1621
1621
+
pub fn builder() -> Self {
1622
1622
+
RecordRefBuilder {
1623
1623
+
_state: PhantomData,
1624
1624
+
_fields: (None, None),
1625
1625
+
_type: PhantomData,
1626
1626
+
}
1627
1627
+
}
1628
1628
+
}
1629
1629
+
1630
1630
+
impl<St, S: BosStr> RecordRefBuilder<St, S>
1631
1631
+
where
1632
1632
+
St: record_ref_state::State,
1633
1633
+
St::Cid: record_ref_state::IsUnset,
1634
1634
+
{
1635
1635
+
/// Set the `cid` field (required)
1636
1636
+
pub fn cid(
1637
1637
+
mut self,
1638
1638
+
value: impl Into<Cid<S>>,
1639
1639
+
) -> RecordRefBuilder<record_ref_state::SetCid<St>, S> {
1640
1640
+
self._fields.0 = Option::Some(value.into());
1641
1641
+
RecordRefBuilder {
1642
1642
+
_state: PhantomData,
1643
1643
+
_fields: self._fields,
1644
1644
+
_type: PhantomData,
1645
1645
+
}
1646
1646
+
}
1647
1647
+
}
1648
1648
+
1649
1649
+
impl<St, S: BosStr> RecordRefBuilder<St, S>
1650
1650
+
where
1651
1651
+
St: record_ref_state::State,
1652
1652
+
St::Uri: record_ref_state::IsUnset,
1653
1653
+
{
1654
1654
+
/// Set the `uri` field (required)
1655
1655
+
pub fn uri(
1656
1656
+
mut self,
1657
1657
+
value: impl Into<AtUri<S>>,
1658
1658
+
) -> RecordRefBuilder<record_ref_state::SetUri<St>, S> {
1659
1659
+
self._fields.1 = Option::Some(value.into());
1660
1660
+
RecordRefBuilder {
1661
1661
+
_state: PhantomData,
1662
1662
+
_fields: self._fields,
1663
1663
+
_type: PhantomData,
1664
1664
+
}
1665
1665
+
}
1666
1666
+
}
1667
1667
+
1668
1668
+
impl<St, S: BosStr> RecordRefBuilder<St, S>
1669
1669
+
where
1670
1670
+
St: record_ref_state::State,
1671
1671
+
St::Cid: record_ref_state::IsSet,
1672
1672
+
St::Uri: record_ref_state::IsSet,
1673
1673
+
{
1674
1674
+
/// Build the final struct.
1675
1675
+
pub fn build(self) -> RecordRef<S> {
1676
1676
+
RecordRef {
1677
1677
+
cid: self._fields.0.unwrap(),
1678
1678
+
uri: self._fields.1.unwrap(),
1679
1679
+
extra_data: Default::default(),
1680
1680
+
}
1681
1681
+
}
1682
1682
+
/// Build the final struct with custom extra_data.
1683
1683
+
pub fn build_with_data(
1684
1684
+
self,
1685
1685
+
extra_data: BTreeMap<SmolStr, Data<S>>,
1686
1686
+
) -> RecordRef<S> {
1687
1687
+
RecordRef {
1688
1688
+
cid: self._fields.0.unwrap(),
1689
1689
+
uri: self._fields.1.unwrap(),
1690
1690
+
extra_data: Some(extra_data),
1691
1691
+
}
1692
1692
+
}
1693
1693
+
}
1694
1694
+
1695
1695
+
pub mod thing_input_state {
1696
1696
+
1697
1697
+
pub use crate::builder_types::{Set, Unset, IsSet, IsUnset};
1698
1698
+
#[allow(unused)]
1699
1699
+
use ::core::marker::PhantomData;
1700
1700
+
mod sealed {
1701
1701
+
pub trait Sealed {}
1702
1702
+
}
1703
1703
+
/// State trait tracking which required fields have been set
1704
1704
+
pub trait State: sealed::Sealed {
1705
1705
+
type License;
1706
1706
+
type Models;
1707
1707
+
type Name;
1708
1708
+
}
1709
1709
+
/// Empty state - all required fields are unset
1710
1710
+
pub struct Empty(());
1711
1711
+
impl sealed::Sealed for Empty {}
1712
1712
+
impl State for Empty {
1713
1713
+
type License = Unset;
1714
1714
+
type Models = Unset;
1715
1715
+
type Name = Unset;
1716
1716
+
}
1717
1717
+
///State transition - sets the `license` field to Set
1718
1718
+
pub struct SetLicense<St: State = Empty>(PhantomData<fn() -> St>);
1719
1719
+
impl<St: State> sealed::Sealed for SetLicense<St> {}
1720
1720
+
impl<St: State> State for SetLicense<St> {
1721
1721
+
type License = Set<members::license>;
1722
1722
+
type Models = St::Models;
1723
1723
+
type Name = St::Name;
1724
1724
+
}
1725
1725
+
///State transition - sets the `models` field to Set
1726
1726
+
pub struct SetModels<St: State = Empty>(PhantomData<fn() -> St>);
1727
1727
+
impl<St: State> sealed::Sealed for SetModels<St> {}
1728
1728
+
impl<St: State> State for SetModels<St> {
1729
1729
+
type License = St::License;
1730
1730
+
type Models = Set<members::models>;
1731
1731
+
type Name = St::Name;
1732
1732
+
}
1733
1733
+
///State transition - sets the `name` field to Set
1734
1734
+
pub struct SetName<St: State = Empty>(PhantomData<fn() -> St>);
1735
1735
+
impl<St: State> sealed::Sealed for SetName<St> {}
1736
1736
+
impl<St: State> State for SetName<St> {
1737
1737
+
type License = St::License;
1738
1738
+
type Models = St::Models;
1739
1739
+
type Name = Set<members::name>;
1740
1740
+
}
1741
1741
+
/// Marker types for field names
1742
1742
+
#[allow(non_camel_case_types)]
1743
1743
+
pub mod members {
1744
1744
+
///Marker type for the `license` field
1745
1745
+
pub struct license(());
1746
1746
+
///Marker type for the `models` field
1747
1747
+
pub struct models(());
1748
1748
+
///Marker type for the `name` field
1749
1749
+
pub struct name(());
1750
1750
+
}
1751
1751
+
}
1752
1752
+
1753
1753
+
/// Builder for constructing an instance of this type.
1754
1754
+
pub struct ThingInputBuilder<St: thing_input_state::State, S: BosStr = DefaultStr> {
1755
1755
+
_state: PhantomData<fn() -> St>,
1756
1756
+
_fields: (
1757
1757
+
Option<Vec<Image<S>>>,
1758
1758
+
Option<StrongRef<S>>,
1759
1759
+
Option<Vec<S>>,
1760
1760
+
Option<Vec<StrongRef<S>>>,
1761
1761
+
Option<S>,
1762
1762
+
Option<Vec<publish_thing::ModelInput<S>>>,
1763
1763
+
Option<S>,
1764
1764
+
Option<Vec<Image<S>>>,
1765
1765
+
Option<S>,
1766
1766
+
Option<Vec<S>>,
1767
1767
+
),
1768
1768
+
_type: PhantomData<fn() -> S>,
1769
1769
+
}
1770
1770
+
1771
1771
+
impl ThingInput<DefaultStr> {
1772
1772
+
/// Create a new builder for this type, using the default string type (DefaultStr = SmolStr) if needed
1773
1773
+
pub fn new() -> ThingInputBuilder<thing_input_state::Empty, DefaultStr> {
1774
1774
+
ThingInputBuilder::new()
1775
1775
+
}
1776
1776
+
}
1777
1777
+
1778
1778
+
impl<S: BosStr> ThingInput<S> {
1779
1779
+
/// Create a new builder for this type
1780
1780
+
pub fn builder() -> ThingInputBuilder<thing_input_state::Empty, S> {
1781
1781
+
ThingInputBuilder::builder()
1782
1782
+
}
1783
1783
+
}
1784
1784
+
1785
1785
+
impl ThingInputBuilder<thing_input_state::Empty, DefaultStr> {
1786
1786
+
/// Create a new builder with all fields unset, using the default string type, if needed
1787
1787
+
pub fn new() -> Self {
1788
1788
+
ThingInputBuilder {
1789
1789
+
_state: PhantomData,
1790
1790
+
_fields: (None, None, None, None, None, None, None, None, None, None),
1791
1791
+
_type: PhantomData,
1792
1792
+
}
1793
1793
+
}
1794
1794
+
}
1795
1795
+
1796
1796
+
impl<S: BosStr> ThingInputBuilder<thing_input_state::Empty, S> {
1797
1797
+
/// Create a new builder with all fields unset
1798
1798
+
pub fn builder() -> Self {
1799
1799
+
ThingInputBuilder {
1800
1800
+
_state: PhantomData,
1801
1801
+
_fields: (None, None, None, None, None, None, None, None, None, None),
1802
1802
+
_type: PhantomData,
1803
1803
+
}
1804
1804
+
}
1805
1805
+
}
1806
1806
+
1807
1807
+
impl<St: thing_input_state::State, S: BosStr> ThingInputBuilder<St, S> {
1808
1808
+
/// Set the `cover` field (optional)
1809
1809
+
pub fn cover(mut self, value: impl Into<Option<Vec<Image<S>>>>) -> Self {
1810
1810
+
self._fields.0 = value.into();
1811
1811
+
self
1812
1812
+
}
1813
1813
+
/// Set the `cover` field to an Option value (optional)
1814
1814
+
pub fn maybe_cover(mut self, value: Option<Vec<Image<S>>>) -> Self {
1815
1815
+
self._fields.0 = value;
1816
1816
+
self
1817
1817
+
}
1818
1818
+
}
1819
1819
+
1820
1820
+
impl<St: thing_input_state::State, S: BosStr> ThingInputBuilder<St, S> {
1821
1821
+
/// Set the `derivedFrom` field (optional)
1822
1822
+
pub fn derived_from(mut self, value: impl Into<Option<StrongRef<S>>>) -> Self {
1823
1823
+
self._fields.1 = value.into();
1824
1824
+
self
1825
1825
+
}
1826
1826
+
/// Set the `derivedFrom` field to an Option value (optional)
1827
1827
+
pub fn maybe_derived_from(mut self, value: Option<StrongRef<S>>) -> Self {
1828
1828
+
self._fields.1 = value;
1829
1829
+
self
1830
1830
+
}
1831
1831
+
}
1832
1832
+
1833
1833
+
impl<St: thing_input_state::State, S: BosStr> ThingInputBuilder<St, S> {
1834
1834
+
/// Set the `instructions` field (optional)
1835
1835
+
pub fn instructions(mut self, value: impl Into<Option<Vec<S>>>) -> Self {
1836
1836
+
self._fields.2 = value.into();
1837
1837
+
self
1838
1838
+
}
1839
1839
+
/// Set the `instructions` field to an Option value (optional)
1840
1840
+
pub fn maybe_instructions(mut self, value: Option<Vec<S>>) -> Self {
1841
1841
+
self._fields.2 = value;
1842
1842
+
self
1843
1843
+
}
1844
1844
+
}
1845
1845
+
1846
1846
+
impl<St: thing_input_state::State, S: BosStr> ThingInputBuilder<St, S> {
1847
1847
+
/// Set the `intendedToWorkWith` field (optional)
1848
1848
+
pub fn intended_to_work_with(
1849
1849
+
mut self,
1850
1850
+
value: impl Into<Option<Vec<StrongRef<S>>>>,
1851
1851
+
) -> Self {
1852
1852
+
self._fields.3 = value.into();
1853
1853
+
self
1854
1854
+
}
1855
1855
+
/// Set the `intendedToWorkWith` field to an Option value (optional)
1856
1856
+
pub fn maybe_intended_to_work_with(
1857
1857
+
mut self,
1858
1858
+
value: Option<Vec<StrongRef<S>>>,
1859
1859
+
) -> Self {
1860
1860
+
self._fields.3 = value;
1861
1861
+
self
1862
1862
+
}
1863
1863
+
}
1864
1864
+
1865
1865
+
impl<St, S: BosStr> ThingInputBuilder<St, S>
1866
1866
+
where
1867
1867
+
St: thing_input_state::State,
1868
1868
+
St::License: thing_input_state::IsUnset,
1869
1869
+
{
1870
1870
+
/// Set the `license` field (required)
1871
1871
+
pub fn license(
1872
1872
+
mut self,
1873
1873
+
value: impl Into<S>,
1874
1874
+
) -> ThingInputBuilder<thing_input_state::SetLicense<St>, S> {
1875
1875
+
self._fields.4 = Option::Some(value.into());
1876
1876
+
ThingInputBuilder {
1877
1877
+
_state: PhantomData,
1878
1878
+
_fields: self._fields,
1879
1879
+
_type: PhantomData,
1880
1880
+
}
1881
1881
+
}
1882
1882
+
}
1883
1883
+
1884
1884
+
impl<St, S: BosStr> ThingInputBuilder<St, S>
1885
1885
+
where
1886
1886
+
St: thing_input_state::State,
1887
1887
+
St::Models: thing_input_state::IsUnset,
1888
1888
+
{
1889
1889
+
/// Set the `models` field (required)
1890
1890
+
pub fn models(
1891
1891
+
mut self,
1892
1892
+
value: impl Into<Vec<publish_thing::ModelInput<S>>>,
1893
1893
+
) -> ThingInputBuilder<thing_input_state::SetModels<St>, S> {
1894
1894
+
self._fields.5 = Option::Some(value.into());
1895
1895
+
ThingInputBuilder {
1896
1896
+
_state: PhantomData,
1897
1897
+
_fields: self._fields,
1898
1898
+
_type: PhantomData,
1899
1899
+
}
1900
1900
+
}
1901
1901
+
}
1902
1902
+
1903
1903
+
impl<St, S: BosStr> ThingInputBuilder<St, S>
1904
1904
+
where
1905
1905
+
St: thing_input_state::State,
1906
1906
+
St::Name: thing_input_state::IsUnset,
1907
1907
+
{
1908
1908
+
/// Set the `name` field (required)
1909
1909
+
pub fn name(
1910
1910
+
mut self,
1911
1911
+
value: impl Into<S>,
1912
1912
+
) -> ThingInputBuilder<thing_input_state::SetName<St>, S> {
1913
1913
+
self._fields.6 = Option::Some(value.into());
1914
1914
+
ThingInputBuilder {
1915
1915
+
_state: PhantomData,
1916
1916
+
_fields: self._fields,
1917
1917
+
_type: PhantomData,
1918
1918
+
}
1919
1919
+
}
1920
1920
+
}
1921
1921
+
1922
1922
+
impl<St: thing_input_state::State, S: BosStr> ThingInputBuilder<St, S> {
1923
1923
+
/// Set the `previews` field (optional)
1924
1924
+
pub fn previews(mut self, value: impl Into<Option<Vec<Image<S>>>>) -> Self {
1925
1925
+
self._fields.7 = value.into();
1926
1926
+
self
1927
1927
+
}
1928
1928
+
/// Set the `previews` field to an Option value (optional)
1929
1929
+
pub fn maybe_previews(mut self, value: Option<Vec<Image<S>>>) -> Self {
1930
1930
+
self._fields.7 = value;
1931
1931
+
self
1932
1932
+
}
1933
1933
+
}
1934
1934
+
1935
1935
+
impl<St: thing_input_state::State, S: BosStr> ThingInputBuilder<St, S> {
1936
1936
+
/// Set the `summary` field (optional)
1937
1937
+
pub fn summary(mut self, value: impl Into<Option<S>>) -> Self {
1938
1938
+
self._fields.8 = value.into();
1939
1939
+
self
1940
1940
+
}
1941
1941
+
/// Set the `summary` field to an Option value (optional)
1942
1942
+
pub fn maybe_summary(mut self, value: Option<S>) -> Self {
1943
1943
+
self._fields.8 = value;
1944
1944
+
self
1945
1945
+
}
1946
1946
+
}
1947
1947
+
1948
1948
+
impl<St: thing_input_state::State, S: BosStr> ThingInputBuilder<St, S> {
1949
1949
+
/// Set the `tags` field (optional)
1950
1950
+
pub fn tags(mut self, value: impl Into<Option<Vec<S>>>) -> Self {
1951
1951
+
self._fields.9 = value.into();
1952
1952
+
self
1953
1953
+
}
1954
1954
+
/// Set the `tags` field to an Option value (optional)
1955
1955
+
pub fn maybe_tags(mut self, value: Option<Vec<S>>) -> Self {
1956
1956
+
self._fields.9 = value;
1957
1957
+
self
1958
1958
+
}
1959
1959
+
}
1960
1960
+
1961
1961
+
impl<St, S: BosStr> ThingInputBuilder<St, S>
1962
1962
+
where
1963
1963
+
St: thing_input_state::State,
1964
1964
+
St::License: thing_input_state::IsSet,
1965
1965
+
St::Models: thing_input_state::IsSet,
1966
1966
+
St::Name: thing_input_state::IsSet,
1967
1967
+
{
1968
1968
+
/// Build the final struct.
1969
1969
+
pub fn build(self) -> ThingInput<S> {
1970
1970
+
ThingInput {
1971
1971
+
cover: self._fields.0,
1972
1972
+
derived_from: self._fields.1,
1973
1973
+
instructions: self._fields.2,
1974
1974
+
intended_to_work_with: self._fields.3,
1975
1975
+
license: self._fields.4.unwrap(),
1976
1976
+
models: self._fields.5.unwrap(),
1977
1977
+
name: self._fields.6.unwrap(),
1978
1978
+
previews: self._fields.7,
1979
1979
+
summary: self._fields.8,
1980
1980
+
tags: self._fields.9,
1981
1981
+
extra_data: Default::default(),
1982
1982
+
}
1983
1983
+
}
1984
1984
+
/// Build the final struct with custom extra_data.
1985
1985
+
pub fn build_with_data(
1986
1986
+
self,
1987
1987
+
extra_data: BTreeMap<SmolStr, Data<S>>,
1988
1988
+
) -> ThingInput<S> {
1989
1989
+
ThingInput {
1990
1990
+
cover: self._fields.0,
1991
1991
+
derived_from: self._fields.1,
1992
1992
+
instructions: self._fields.2,
1993
1993
+
intended_to_work_with: self._fields.3,
1994
1994
+
license: self._fields.4.unwrap(),
1995
1995
+
models: self._fields.5.unwrap(),
1996
1996
+
name: self._fields.6.unwrap(),
1997
1997
+
previews: self._fields.7,
1998
1998
+
summary: self._fields.8,
1999
1999
+
tags: self._fields.9,
2000
2000
+
extra_data: Some(extra_data),
2001
2001
+
}
2002
2002
+
}
2003
2003
+
}
···
1
1
+
// @generated by jacquard-lexicon. DO NOT EDIT.
2
2
+
//
3
3
+
// Lexicon: space.polymodel.library.stageFile
4
4
+
//
5
5
+
// This file was automatically generated from Lexicon schemas.
6
6
+
// Any manual changes will be overwritten on the next regeneration.
7
7
+
8
8
+
#[allow(unused_imports)]
9
9
+
use alloc::collections::BTreeMap;
10
10
+
11
11
+
#[allow(unused_imports)]
12
12
+
use core::marker::PhantomData;
13
13
+
use jacquard_common::{CowStr, BosStr, DefaultStr, FromStaticStr};
14
14
+
use jacquard_common::deps::bytes::Bytes;
15
15
+
use jacquard_common::deps::smol_str::SmolStr;
16
16
+
use jacquard_common::types::value::Data;
17
17
+
use jacquard_derive::IntoStatic;
18
18
+
use serde::{Serialize, Deserialize};
19
19
+
use crate::space_polymodel::library::File;
20
20
+
21
21
+
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic)]
22
22
+
#[serde(rename_all = "camelCase")]
23
23
+
pub struct StageFile {
24
24
+
pub body: Bytes,
25
25
+
}
26
26
+
27
27
+
28
28
+
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic)]
29
29
+
#[serde(rename_all = "camelCase", bound(deserialize = "S: Deserialize<'de> + BosStr"))]
30
30
+
pub struct StageFileOutput<S: BosStr = DefaultStr> {
31
31
+
pub file: File<S>,
32
32
+
#[serde(skip_serializing_if = "Option::is_none")]
33
33
+
pub filename: Option<S>,
34
34
+
#[serde(skip_serializing_if = "Option::is_none")]
35
35
+
pub status: Option<StageFileOutputStatus<S>>,
36
36
+
pub upload_id: S,
37
37
+
#[serde(flatten, default, skip_serializing_if = "Option::is_none")]
38
38
+
pub extra_data: Option<BTreeMap<SmolStr, Data<S>>>,
39
39
+
}
40
40
+
41
41
+
42
42
+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
43
43
+
pub enum StageFileOutputStatus<S: BosStr = DefaultStr> {
44
44
+
Uploaded,
45
45
+
Other(S),
46
46
+
}
47
47
+
48
48
+
impl<S: BosStr> StageFileOutputStatus<S> {
49
49
+
pub fn as_str(&self) -> &str {
50
50
+
match self {
51
51
+
Self::Uploaded => "uploaded",
52
52
+
Self::Other(s) => s.as_ref(),
53
53
+
}
54
54
+
}
55
55
+
/// Construct from a string-like value, matching known values.
56
56
+
pub fn from_value(s: S) -> Self {
57
57
+
match s.as_ref() {
58
58
+
"uploaded" => Self::Uploaded,
59
59
+
_ => Self::Other(s),
60
60
+
}
61
61
+
}
62
62
+
}
63
63
+
64
64
+
impl<S: BosStr> core::fmt::Display for StageFileOutputStatus<S> {
65
65
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
66
66
+
write!(f, "{}", self.as_str())
67
67
+
}
68
68
+
}
69
69
+
70
70
+
impl<S: BosStr> AsRef<str> for StageFileOutputStatus<S> {
71
71
+
fn as_ref(&self) -> &str {
72
72
+
self.as_str()
73
73
+
}
74
74
+
}
75
75
+
76
76
+
impl<S: BosStr> Serialize for StageFileOutputStatus<S> {
77
77
+
fn serialize<Ser>(&self, serializer: Ser) -> Result<Ser::Ok, Ser::Error>
78
78
+
where
79
79
+
Ser: serde::Serializer,
80
80
+
{
81
81
+
serializer.serialize_str(self.as_str())
82
82
+
}
83
83
+
}
84
84
+
85
85
+
impl<'de, S: Deserialize<'de> + BosStr> Deserialize<'de> for StageFileOutputStatus<S> {
86
86
+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
87
87
+
where
88
88
+
D: serde::Deserializer<'de>,
89
89
+
{
90
90
+
let s = S::deserialize(deserializer)?;
91
91
+
Ok(Self::from_value(s))
92
92
+
}
93
93
+
}
94
94
+
95
95
+
impl<S: BosStr + Default> Default for StageFileOutputStatus<S> {
96
96
+
fn default() -> Self {
97
97
+
Self::Other(Default::default())
98
98
+
}
99
99
+
}
100
100
+
101
101
+
impl<S: BosStr> jacquard_common::IntoStatic for StageFileOutputStatus<S>
102
102
+
where
103
103
+
S: BosStr + jacquard_common::IntoStatic,
104
104
+
S::Output: BosStr,
105
105
+
{
106
106
+
type Output = StageFileOutputStatus<S::Output>;
107
107
+
fn into_static(self) -> Self::Output {
108
108
+
match self {
109
109
+
StageFileOutputStatus::Uploaded => StageFileOutputStatus::Uploaded,
110
110
+
StageFileOutputStatus::Other(v) => {
111
111
+
StageFileOutputStatus::Other(v.into_static())
112
112
+
}
113
113
+
}
114
114
+
}
115
115
+
}
116
116
+
117
117
+
/** Response marker for the `space.polymodel.library.stageFile` procedure.
118
118
+
119
119
+
Implements `jacquard_common::xrpc::XrpcResp`; successful bodies decode as `Self::Output<S>`, which is `StageFileOutput<S>` for this endpoint.*/
120
120
+
pub struct StageFileResponse;
121
121
+
impl jacquard_common::xrpc::XrpcResp for StageFileResponse {
122
122
+
const NSID: &'static str = "space.polymodel.library.stageFile";
123
123
+
const ENCODING: &'static str = "application/json";
124
124
+
type Output<S: BosStr> = StageFileOutput<S>;
125
125
+
type Err = jacquard_common::xrpc::GenericError;
126
126
+
}
127
127
+
128
128
+
impl jacquard_common::xrpc::XrpcRequest for StageFile {
129
129
+
const NSID: &'static str = "space.polymodel.library.stageFile";
130
130
+
const METHOD: jacquard_common::xrpc::XrpcMethod = jacquard_common::xrpc::XrpcMethod::Procedure(
131
131
+
"*/*",
132
132
+
);
133
133
+
type Response = StageFileResponse;
134
134
+
fn encode_body(
135
135
+
&self,
136
136
+
buffer: &mut Vec<u8>,
137
137
+
) -> Result<(), jacquard_common::xrpc::EncodeError>
138
138
+
where
139
139
+
Self: Serialize,
140
140
+
{
141
141
+
Ok(buffer.copy_from_slice(self.body.as_ref()))
142
142
+
}
143
143
+
fn decode_body<'de>(
144
144
+
body: &'de [u8],
145
145
+
) -> Result<Self, jacquard_common::error::DecodeError>
146
146
+
where
147
147
+
Self: Deserialize<'de>,
148
148
+
{
149
149
+
Ok(Self {
150
150
+
body: jacquard_common::deps::bytes::Bytes::copy_from_slice(body),
151
151
+
})
152
152
+
}
153
153
+
}
154
154
+
155
155
+
/** Endpoint marker for the `space.polymodel.library.stageFile` procedure.
156
156
+
157
157
+
Path: `/xrpc/space.polymodel.library.stageFile`. The request payload type is `StageFile`; send that request with `jacquard::Client` or use this marker through lower-level `XrpcEndpoint` APIs.*/
158
158
+
pub struct StageFileRequest;
159
159
+
impl jacquard_common::xrpc::XrpcEndpoint for StageFileRequest {
160
160
+
const PATH: &'static str = "/xrpc/space.polymodel.library.stageFile";
161
161
+
const METHOD: jacquard_common::xrpc::XrpcMethod = jacquard_common::xrpc::XrpcMethod::Procedure(
162
162
+
"*/*",
163
163
+
);
164
164
+
type Request<S: BosStr> = StageFile;
165
165
+
type Response = StageFileResponse;
166
166
+
}
···
1
1
+
// @generated by jacquard-lexicon. DO NOT EDIT.
2
2
+
//
3
3
+
// Lexicon: space.polymodel.library.updateThing
4
4
+
//
5
5
+
// This file was automatically generated from Lexicon schemas.
6
6
+
// Any manual changes will be overwritten on the next regeneration.
7
7
+
8
8
+
#[allow(unused_imports)]
9
9
+
use alloc::collections::BTreeMap;
10
10
+
11
11
+
#[allow(unused_imports)]
12
12
+
use core::marker::PhantomData;
13
13
+
use jacquard_common::{CowStr, BosStr, DefaultStr, FromStaticStr};
14
14
+
15
15
+
#[allow(unused_imports)]
16
16
+
use jacquard_common::deps::codegen::unicode_segmentation::UnicodeSegmentation;
17
17
+
use jacquard_common::deps::smol_str::SmolStr;
18
18
+
use jacquard_common::types::string::AtUri;
19
19
+
use jacquard_common::types::value::Data;
20
20
+
use jacquard_derive::IntoStatic;
21
21
+
use jacquard_lexicon::lexicon::LexiconDoc;
22
22
+
use jacquard_lexicon::schema::LexiconSchema;
23
23
+
24
24
+
#[allow(unused_imports)]
25
25
+
use jacquard_lexicon::validation::{ConstraintError, ValidationPath};
26
26
+
use serde::{Serialize, Deserialize};
27
27
+
use crate::com_atproto::repo::strong_ref::StrongRef;
28
28
+
use crate::space_polymodel::library::Bbox;
29
29
+
use crate::space_polymodel::library::File;
30
30
+
use crate::space_polymodel::library::Image;
31
31
+
use crate::space_polymodel::library::publish_thing::ModelPublishResult;
32
32
+
use crate::space_polymodel::library::publish_thing::RecordRef;
33
33
+
use crate::space_polymodel::library::update_thing;
34
34
+
35
35
+
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic)]
36
36
+
#[serde(rename_all = "camelCase", bound(deserialize = "S: Deserialize<'de> + BosStr"))]
37
37
+
pub struct UpdateThing<S: BosStr = DefaultStr> {
38
38
+
pub thing: update_thing::ThingInput<S>,
39
39
+
pub uri: AtUri<S>,
40
40
+
#[serde(flatten, default, skip_serializing_if = "Option::is_none")]
41
41
+
pub extra_data: Option<BTreeMap<SmolStr, Data<S>>>,
42
42
+
}
43
43
+
44
44
+
45
45
+
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic)]
46
46
+
#[serde(rename_all = "camelCase", bound(deserialize = "S: Deserialize<'de> + BosStr"))]
47
47
+
pub struct UpdateThingOutput<S: BosStr = DefaultStr> {
48
48
+
pub models: Vec<ModelPublishResult<S>>,
49
49
+
pub thing: RecordRef<S>,
50
50
+
#[serde(flatten, default, skip_serializing_if = "Option::is_none")]
51
51
+
pub extra_data: Option<BTreeMap<SmolStr, Data<S>>>,
52
52
+
}
53
53
+
54
54
+
55
55
+
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic)]
56
56
+
#[serde(rename_all = "camelCase", bound(deserialize = "S: Deserialize<'de> + BosStr"))]
57
57
+
pub struct ModelInput<S: BosStr = DefaultStr> {
58
58
+
#[serde(skip_serializing_if = "Option::is_none")]
59
59
+
pub cover: Option<Vec<Image<S>>>,
60
60
+
#[serde(skip_serializing_if = "Option::is_none")]
61
61
+
pub derived_from: Option<StrongRef<S>>,
62
62
+
#[serde(skip_serializing_if = "Option::is_none")]
63
63
+
pub instructions: Option<Vec<S>>,
64
64
+
#[serde(skip_serializing_if = "Option::is_none")]
65
65
+
pub intended_to_work_with: Option<Vec<StrongRef<S>>>,
66
66
+
#[serde(skip_serializing_if = "Option::is_none")]
67
67
+
pub license: Option<S>,
68
68
+
pub name: S,
69
69
+
pub parts: Vec<update_thing::PartInput<S>>,
70
70
+
#[serde(skip_serializing_if = "Option::is_none")]
71
71
+
pub previews: Option<Vec<Image<S>>>,
72
72
+
#[serde(skip_serializing_if = "Option::is_none")]
73
73
+
pub summary: Option<S>,
74
74
+
#[serde(skip_serializing_if = "Option::is_none")]
75
75
+
pub tags: Option<Vec<S>>,
76
76
+
#[serde(skip_serializing_if = "Option::is_none")]
77
77
+
pub uri: Option<AtUri<S>>,
78
78
+
#[serde(flatten, default, skip_serializing_if = "Option::is_none")]
79
79
+
pub extra_data: Option<BTreeMap<SmolStr, Data<S>>>,
80
80
+
}
81
81
+
82
82
+
83
83
+
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic, Default)]
84
84
+
#[serde(rename_all = "camelCase", bound(deserialize = "S: Deserialize<'de> + BosStr"))]
85
85
+
pub struct PartInput<S: BosStr = DefaultStr> {
86
86
+
#[serde(skip_serializing_if = "Option::is_none")]
87
87
+
pub derived_from: Option<StrongRef<S>>,
88
88
+
#[serde(skip_serializing_if = "Option::is_none")]
89
89
+
pub dimensions: Option<Bbox<S>>,
90
90
+
#[serde(skip_serializing_if = "Option::is_none")]
91
91
+
pub file: Option<File<S>>,
92
92
+
#[serde(skip_serializing_if = "Option::is_none")]
93
93
+
pub format: Option<S>,
94
94
+
#[serde(skip_serializing_if = "Option::is_none")]
95
95
+
pub intended_to_work_with: Option<Vec<StrongRef<S>>>,
96
96
+
pub name: S,
97
97
+
#[serde(skip_serializing_if = "Option::is_none")]
98
98
+
pub notes: Option<S>,
99
99
+
#[serde(skip_serializing_if = "Option::is_none")]
100
100
+
pub previews: Option<Vec<Image<S>>>,
101
101
+
#[serde(skip_serializing_if = "Option::is_none")]
102
102
+
pub print_settings: Option<Vec<S>>,
103
103
+
#[serde(skip_serializing_if = "Option::is_none")]
104
104
+
pub units: Option<S>,
105
105
+
#[serde(skip_serializing_if = "Option::is_none")]
106
106
+
pub upload_id: Option<S>,
107
107
+
#[serde(skip_serializing_if = "Option::is_none")]
108
108
+
pub uri: Option<AtUri<S>>,
109
109
+
#[serde(flatten, default, skip_serializing_if = "Option::is_none")]
110
110
+
pub extra_data: Option<BTreeMap<SmolStr, Data<S>>>,
111
111
+
}
112
112
+
113
113
+
114
114
+
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic)]
115
115
+
#[serde(rename_all = "camelCase", bound(deserialize = "S: Deserialize<'de> + BosStr"))]
116
116
+
pub struct ThingInput<S: BosStr = DefaultStr> {
117
117
+
#[serde(skip_serializing_if = "Option::is_none")]
118
118
+
pub cover: Option<Vec<Image<S>>>,
119
119
+
#[serde(skip_serializing_if = "Option::is_none")]
120
120
+
pub derived_from: Option<StrongRef<S>>,
121
121
+
#[serde(skip_serializing_if = "Option::is_none")]
122
122
+
pub instructions: Option<Vec<S>>,
123
123
+
#[serde(skip_serializing_if = "Option::is_none")]
124
124
+
pub intended_to_work_with: Option<Vec<StrongRef<S>>>,
125
125
+
pub license: S,
126
126
+
pub models: Vec<update_thing::ModelInput<S>>,
127
127
+
pub name: S,
128
128
+
#[serde(skip_serializing_if = "Option::is_none")]
129
129
+
pub previews: Option<Vec<Image<S>>>,
130
130
+
#[serde(skip_serializing_if = "Option::is_none")]
131
131
+
pub summary: Option<S>,
132
132
+
#[serde(skip_serializing_if = "Option::is_none")]
133
133
+
pub tags: Option<Vec<S>>,
134
134
+
#[serde(flatten, default, skip_serializing_if = "Option::is_none")]
135
135
+
pub extra_data: Option<BTreeMap<SmolStr, Data<S>>>,
136
136
+
}
137
137
+
138
138
+
/** Response marker for the `space.polymodel.library.updateThing` procedure.
139
139
+
140
140
+
Implements `jacquard_common::xrpc::XrpcResp`; successful bodies decode as `Self::Output<S>`, which is `UpdateThingOutput<S>` for this endpoint.*/
141
141
+
pub struct UpdateThingResponse;
142
142
+
impl jacquard_common::xrpc::XrpcResp for UpdateThingResponse {
143
143
+
const NSID: &'static str = "space.polymodel.library.updateThing";
144
144
+
const ENCODING: &'static str = "application/json";
145
145
+
type Output<S: BosStr> = UpdateThingOutput<S>;
146
146
+
type Err = jacquard_common::xrpc::GenericError;
147
147
+
}
148
148
+
149
149
+
impl<S: BosStr> jacquard_common::xrpc::XrpcRequest for UpdateThing<S> {
150
150
+
const NSID: &'static str = "space.polymodel.library.updateThing";
151
151
+
const METHOD: jacquard_common::xrpc::XrpcMethod = jacquard_common::xrpc::XrpcMethod::Procedure(
152
152
+
"application/json",
153
153
+
);
154
154
+
type Response = UpdateThingResponse;
155
155
+
}
156
156
+
157
157
+
/** Endpoint marker for the `space.polymodel.library.updateThing` procedure.
158
158
+
159
159
+
Path: `/xrpc/space.polymodel.library.updateThing`. The request payload type is `UpdateThing<S>`; send that request with `jacquard::Client` or use this marker through lower-level `XrpcEndpoint` APIs.*/
160
160
+
pub struct UpdateThingRequest;
161
161
+
impl jacquard_common::xrpc::XrpcEndpoint for UpdateThingRequest {
162
162
+
const PATH: &'static str = "/xrpc/space.polymodel.library.updateThing";
163
163
+
const METHOD: jacquard_common::xrpc::XrpcMethod = jacquard_common::xrpc::XrpcMethod::Procedure(
164
164
+
"application/json",
165
165
+
);
166
166
+
type Request<S: BosStr> = UpdateThing<S>;
167
167
+
type Response = UpdateThingResponse;
168
168
+
}
169
169
+
170
170
+
impl<S: BosStr> LexiconSchema for ModelInput<S> {
171
171
+
fn nsid() -> &'static str {
172
172
+
"space.polymodel.library.updateThing"
173
173
+
}
174
174
+
fn def_name() -> &'static str {
175
175
+
"modelInput"
176
176
+
}
177
177
+
fn lexicon_doc() -> LexiconDoc<'static> {
178
178
+
lexicon_doc_space_polymodel_library_updateThing()
179
179
+
}
180
180
+
fn validate(&self) -> Result<(), ConstraintError> {
181
181
+
if let Some(ref value) = self.cover {
182
182
+
#[allow(unused_comparisons)]
183
183
+
if value.len() > 8usize {
184
184
+
return Err(ConstraintError::MaxLength {
185
185
+
path: ValidationPath::from_field("cover"),
186
186
+
max: 8usize,
187
187
+
actual: value.len(),
188
188
+
});
189
189
+
}
190
190
+
}
191
191
+
if let Some(ref value) = self.instructions {
192
192
+
#[allow(unused_comparisons)]
193
193
+
if value.len() > 100usize {
194
194
+
return Err(ConstraintError::MaxLength {
195
195
+
path: ValidationPath::from_field("instructions"),
196
196
+
max: 100usize,
197
197
+
actual: value.len(),
198
198
+
});
199
199
+
}
200
200
+
}
201
201
+
if let Some(ref value) = self.intended_to_work_with {
202
202
+
#[allow(unused_comparisons)]
203
203
+
if value.len() > 32usize {
204
204
+
return Err(ConstraintError::MaxLength {
205
205
+
path: ValidationPath::from_field("intended_to_work_with"),
206
206
+
max: 32usize,
207
207
+
actual: value.len(),
208
208
+
});
209
209
+
}
210
210
+
}
211
211
+
{
212
212
+
let value = &self.name;
213
213
+
#[allow(unused_comparisons)]
214
214
+
if <str>::len(value.as_ref()) > 256usize {
215
215
+
return Err(ConstraintError::MaxLength {
216
216
+
path: ValidationPath::from_field("name"),
217
217
+
max: 256usize,
218
218
+
actual: <str>::len(value.as_ref()),
219
219
+
});
220
220
+
}
221
221
+
}
222
222
+
{
223
223
+
let value = &self.name;
224
224
+
{
225
225
+
let count = UnicodeSegmentation::graphemes(value.as_ref(), true).count();
226
226
+
if count > 64usize {
227
227
+
return Err(ConstraintError::MaxGraphemes {
228
228
+
path: ValidationPath::from_field("name"),
229
229
+
max: 64usize,
230
230
+
actual: count,
231
231
+
});
232
232
+
}
233
233
+
}
234
234
+
}
235
235
+
{
236
236
+
let value = &self.parts;
237
237
+
#[allow(unused_comparisons)]
238
238
+
if value.len() > 1024usize {
239
239
+
return Err(ConstraintError::MaxLength {
240
240
+
path: ValidationPath::from_field("parts"),
241
241
+
max: 1024usize,
242
242
+
actual: value.len(),
243
243
+
});
244
244
+
}
245
245
+
}
246
246
+
if let Some(ref value) = self.previews {
247
247
+
#[allow(unused_comparisons)]
248
248
+
if value.len() > 12usize {
249
249
+
return Err(ConstraintError::MaxLength {
250
250
+
path: ValidationPath::from_field("previews"),
251
251
+
max: 12usize,
252
252
+
actual: value.len(),
253
253
+
});
254
254
+
}
255
255
+
}
256
256
+
if let Some(ref value) = self.summary {
257
257
+
#[allow(unused_comparisons)]
258
258
+
if <str>::len(value.as_ref()) > 2000usize {
259
259
+
return Err(ConstraintError::MaxLength {
260
260
+
path: ValidationPath::from_field("summary"),
261
261
+
max: 2000usize,
262
262
+
actual: <str>::len(value.as_ref()),
263
263
+
});
264
264
+
}
265
265
+
}
266
266
+
if let Some(ref value) = self.summary {
267
267
+
{
268
268
+
let count = UnicodeSegmentation::graphemes(value.as_ref(), true).count();
269
269
+
if count > 300usize {
270
270
+
return Err(ConstraintError::MaxGraphemes {
271
271
+
path: ValidationPath::from_field("summary"),
272
272
+
max: 300usize,
273
273
+
actual: count,
274
274
+
});
275
275
+
}
276
276
+
}
277
277
+
}
278
278
+
if let Some(ref value) = self.tags {
279
279
+
#[allow(unused_comparisons)]
280
280
+
if value.len() > 16usize {
281
281
+
return Err(ConstraintError::MaxLength {
282
282
+
path: ValidationPath::from_field("tags"),
283
283
+
max: 16usize,
284
284
+
actual: value.len(),
285
285
+
});
286
286
+
}
287
287
+
}
288
288
+
Ok(())
289
289
+
}
290
290
+
}
291
291
+
292
292
+
impl<S: BosStr> LexiconSchema for PartInput<S> {
293
293
+
fn nsid() -> &'static str {
294
294
+
"space.polymodel.library.updateThing"
295
295
+
}
296
296
+
fn def_name() -> &'static str {
297
297
+
"partInput"
298
298
+
}
299
299
+
fn lexicon_doc() -> LexiconDoc<'static> {
300
300
+
lexicon_doc_space_polymodel_library_updateThing()
301
301
+
}
302
302
+
fn validate(&self) -> Result<(), ConstraintError> {
303
303
+
if let Some(ref value) = self.intended_to_work_with {
304
304
+
#[allow(unused_comparisons)]
305
305
+
if value.len() > 32usize {
306
306
+
return Err(ConstraintError::MaxLength {
307
307
+
path: ValidationPath::from_field("intended_to_work_with"),
308
308
+
max: 32usize,
309
309
+
actual: value.len(),
310
310
+
});
311
311
+
}
312
312
+
}
313
313
+
{
314
314
+
let value = &self.name;
315
315
+
#[allow(unused_comparisons)]
316
316
+
if <str>::len(value.as_ref()) > 256usize {
317
317
+
return Err(ConstraintError::MaxLength {
318
318
+
path: ValidationPath::from_field("name"),
319
319
+
max: 256usize,
320
320
+
actual: <str>::len(value.as_ref()),
321
321
+
});
322
322
+
}
323
323
+
}
324
324
+
{
325
325
+
let value = &self.name;
326
326
+
{
327
327
+
let count = UnicodeSegmentation::graphemes(value.as_ref(), true).count();
328
328
+
if count > 64usize {
329
329
+
return Err(ConstraintError::MaxGraphemes {
330
330
+
path: ValidationPath::from_field("name"),
331
331
+
max: 64usize,
332
332
+
actual: count,
333
333
+
});
334
334
+
}
335
335
+
}
336
336
+
}
337
337
+
if let Some(ref value) = self.notes {
338
338
+
#[allow(unused_comparisons)]
339
339
+
if <str>::len(value.as_ref()) > 2000usize {
340
340
+
return Err(ConstraintError::MaxLength {
341
341
+
path: ValidationPath::from_field("notes"),
342
342
+
max: 2000usize,
343
343
+
actual: <str>::len(value.as_ref()),
344
344
+
});
345
345
+
}
346
346
+
}
347
347
+
if let Some(ref value) = self.notes {
348
348
+
{
349
349
+
let count = UnicodeSegmentation::graphemes(value.as_ref(), true).count();
350
350
+
if count > 300usize {
351
351
+
return Err(ConstraintError::MaxGraphemes {
352
352
+
path: ValidationPath::from_field("notes"),
353
353
+
max: 300usize,
354
354
+
actual: count,
355
355
+
});
356
356
+
}
357
357
+
}
358
358
+
}
359
359
+
if let Some(ref value) = self.previews {
360
360
+
#[allow(unused_comparisons)]
361
361
+
if value.len() > 12usize {
362
362
+
return Err(ConstraintError::MaxLength {
363
363
+
path: ValidationPath::from_field("previews"),
364
364
+
max: 12usize,
365
365
+
actual: value.len(),
366
366
+
});
367
367
+
}
368
368
+
}
369
369
+
if let Some(ref value) = self.print_settings {
370
370
+
#[allow(unused_comparisons)]
371
371
+
if value.len() > 50usize {
372
372
+
return Err(ConstraintError::MaxLength {
373
373
+
path: ValidationPath::from_field("print_settings"),
374
374
+
max: 50usize,
375
375
+
actual: value.len(),
376
376
+
});
377
377
+
}
378
378
+
}
379
379
+
Ok(())
380
380
+
}
381
381
+
}
382
382
+
383
383
+
impl<S: BosStr> LexiconSchema for ThingInput<S> {
384
384
+
fn nsid() -> &'static str {
385
385
+
"space.polymodel.library.updateThing"
386
386
+
}
387
387
+
fn def_name() -> &'static str {
388
388
+
"thingInput"
389
389
+
}
390
390
+
fn lexicon_doc() -> LexiconDoc<'static> {
391
391
+
lexicon_doc_space_polymodel_library_updateThing()
392
392
+
}
393
393
+
fn validate(&self) -> Result<(), ConstraintError> {
394
394
+
if let Some(ref value) = self.cover {
395
395
+
#[allow(unused_comparisons)]
396
396
+
if value.len() > 8usize {
397
397
+
return Err(ConstraintError::MaxLength {
398
398
+
path: ValidationPath::from_field("cover"),
399
399
+
max: 8usize,
400
400
+
actual: value.len(),
401
401
+
});
402
402
+
}
403
403
+
}
404
404
+
if let Some(ref value) = self.instructions {
405
405
+
#[allow(unused_comparisons)]
406
406
+
if value.len() > 100usize {
407
407
+
return Err(ConstraintError::MaxLength {
408
408
+
path: ValidationPath::from_field("instructions"),
409
409
+
max: 100usize,
410
410
+
actual: value.len(),
411
411
+
});
412
412
+
}
413
413
+
}
414
414
+
if let Some(ref value) = self.intended_to_work_with {
415
415
+
#[allow(unused_comparisons)]
416
416
+
if value.len() > 32usize {
417
417
+
return Err(ConstraintError::MaxLength {
418
418
+
path: ValidationPath::from_field("intended_to_work_with"),
419
419
+
max: 32usize,
420
420
+
actual: value.len(),
421
421
+
});
422
422
+
}
423
423
+
}
424
424
+
{
425
425
+
let value = &self.models;
426
426
+
#[allow(unused_comparisons)]
427
427
+
if value.len() > 256usize {
428
428
+
return Err(ConstraintError::MaxLength {
429
429
+
path: ValidationPath::from_field("models"),
430
430
+
max: 256usize,
431
431
+
actual: value.len(),
432
432
+
});
433
433
+
}
434
434
+
}
435
435
+
{
436
436
+
let value = &self.name;
437
437
+
#[allow(unused_comparisons)]
438
438
+
if <str>::len(value.as_ref()) > 256usize {
439
439
+
return Err(ConstraintError::MaxLength {
440
440
+
path: ValidationPath::from_field("name"),
441
441
+
max: 256usize,
442
442
+
actual: <str>::len(value.as_ref()),
443
443
+
});
444
444
+
}
445
445
+
}
446
446
+
{
447
447
+
let value = &self.name;
448
448
+
{
449
449
+
let count = UnicodeSegmentation::graphemes(value.as_ref(), true).count();
450
450
+
if count > 64usize {
451
451
+
return Err(ConstraintError::MaxGraphemes {
452
452
+
path: ValidationPath::from_field("name"),
453
453
+
max: 64usize,
454
454
+
actual: count,
455
455
+
});
456
456
+
}
457
457
+
}
458
458
+
}
459
459
+
if let Some(ref value) = self.previews {
460
460
+
#[allow(unused_comparisons)]
461
461
+
if value.len() > 12usize {
462
462
+
return Err(ConstraintError::MaxLength {
463
463
+
path: ValidationPath::from_field("previews"),
464
464
+
max: 12usize,
465
465
+
actual: value.len(),
466
466
+
});
467
467
+
}
468
468
+
}
469
469
+
if let Some(ref value) = self.summary {
470
470
+
#[allow(unused_comparisons)]
471
471
+
if <str>::len(value.as_ref()) > 2000usize {
472
472
+
return Err(ConstraintError::MaxLength {
473
473
+
path: ValidationPath::from_field("summary"),
474
474
+
max: 2000usize,
475
475
+
actual: <str>::len(value.as_ref()),
476
476
+
});
477
477
+
}
478
478
+
}
479
479
+
if let Some(ref value) = self.summary {
480
480
+
{
481
481
+
let count = UnicodeSegmentation::graphemes(value.as_ref(), true).count();
482
482
+
if count > 300usize {
483
483
+
return Err(ConstraintError::MaxGraphemes {
484
484
+
path: ValidationPath::from_field("summary"),
485
485
+
max: 300usize,
486
486
+
actual: count,
487
487
+
});
488
488
+
}
489
489
+
}
490
490
+
}
491
491
+
if let Some(ref value) = self.tags {
492
492
+
#[allow(unused_comparisons)]
493
493
+
if value.len() > 16usize {
494
494
+
return Err(ConstraintError::MaxLength {
495
495
+
path: ValidationPath::from_field("tags"),
496
496
+
max: 16usize,
497
497
+
actual: value.len(),
498
498
+
});
499
499
+
}
500
500
+
}
501
501
+
Ok(())
502
502
+
}
503
503
+
}
504
504
+
505
505
+
pub mod update_thing_state {
506
506
+
507
507
+
pub use crate::builder_types::{Set, Unset, IsSet, IsUnset};
508
508
+
#[allow(unused)]
509
509
+
use ::core::marker::PhantomData;
510
510
+
mod sealed {
511
511
+
pub trait Sealed {}
512
512
+
}
513
513
+
/// State trait tracking which required fields have been set
514
514
+
pub trait State: sealed::Sealed {
515
515
+
type Thing;
516
516
+
type Uri;
517
517
+
}
518
518
+
/// Empty state - all required fields are unset
519
519
+
pub struct Empty(());
520
520
+
impl sealed::Sealed for Empty {}
521
521
+
impl State for Empty {
522
522
+
type Thing = Unset;
523
523
+
type Uri = Unset;
524
524
+
}
525
525
+
///State transition - sets the `thing` field to Set
526
526
+
pub struct SetThing<St: State = Empty>(PhantomData<fn() -> St>);
527
527
+
impl<St: State> sealed::Sealed for SetThing<St> {}
528
528
+
impl<St: State> State for SetThing<St> {
529
529
+
type Thing = Set<members::thing>;
530
530
+
type Uri = St::Uri;
531
531
+
}
532
532
+
///State transition - sets the `uri` field to Set
533
533
+
pub struct SetUri<St: State = Empty>(PhantomData<fn() -> St>);
534
534
+
impl<St: State> sealed::Sealed for SetUri<St> {}
535
535
+
impl<St: State> State for SetUri<St> {
536
536
+
type Thing = St::Thing;
537
537
+
type Uri = Set<members::uri>;
538
538
+
}
539
539
+
/// Marker types for field names
540
540
+
#[allow(non_camel_case_types)]
541
541
+
pub mod members {
542
542
+
///Marker type for the `thing` field
543
543
+
pub struct thing(());
544
544
+
///Marker type for the `uri` field
545
545
+
pub struct uri(());
546
546
+
}
547
547
+
}
548
548
+
549
549
+
/// Builder for constructing an instance of this type.
550
550
+
pub struct UpdateThingBuilder<St: update_thing_state::State, S: BosStr = DefaultStr> {
551
551
+
_state: PhantomData<fn() -> St>,
552
552
+
_fields: (Option<update_thing::ThingInput<S>>, Option<AtUri<S>>),
553
553
+
_type: PhantomData<fn() -> S>,
554
554
+
}
555
555
+
556
556
+
impl UpdateThing<DefaultStr> {
557
557
+
/// Create a new builder for this type, using the default string type (DefaultStr = SmolStr) if needed
558
558
+
pub fn new() -> UpdateThingBuilder<update_thing_state::Empty, DefaultStr> {
559
559
+
UpdateThingBuilder::new()
560
560
+
}
561
561
+
}
562
562
+
563
563
+
impl<S: BosStr> UpdateThing<S> {
564
564
+
/// Create a new builder for this type
565
565
+
pub fn builder() -> UpdateThingBuilder<update_thing_state::Empty, S> {
566
566
+
UpdateThingBuilder::builder()
567
567
+
}
568
568
+
}
569
569
+
570
570
+
impl UpdateThingBuilder<update_thing_state::Empty, DefaultStr> {
571
571
+
/// Create a new builder with all fields unset, using the default string type, if needed
572
572
+
pub fn new() -> Self {
573
573
+
UpdateThingBuilder {
574
574
+
_state: PhantomData,
575
575
+
_fields: (None, None),
576
576
+
_type: PhantomData,
577
577
+
}
578
578
+
}
579
579
+
}
580
580
+
581
581
+
impl<S: BosStr> UpdateThingBuilder<update_thing_state::Empty, S> {
582
582
+
/// Create a new builder with all fields unset
583
583
+
pub fn builder() -> Self {
584
584
+
UpdateThingBuilder {
585
585
+
_state: PhantomData,
586
586
+
_fields: (None, None),
587
587
+
_type: PhantomData,
588
588
+
}
589
589
+
}
590
590
+
}
591
591
+
592
592
+
impl<St, S: BosStr> UpdateThingBuilder<St, S>
593
593
+
where
594
594
+
St: update_thing_state::State,
595
595
+
St::Thing: update_thing_state::IsUnset,
596
596
+
{
597
597
+
/// Set the `thing` field (required)
598
598
+
pub fn thing(
599
599
+
mut self,
600
600
+
value: impl Into<update_thing::ThingInput<S>>,
601
601
+
) -> UpdateThingBuilder<update_thing_state::SetThing<St>, S> {
602
602
+
self._fields.0 = Option::Some(value.into());
603
603
+
UpdateThingBuilder {
604
604
+
_state: PhantomData,
605
605
+
_fields: self._fields,
606
606
+
_type: PhantomData,
607
607
+
}
608
608
+
}
609
609
+
}
610
610
+
611
611
+
impl<St, S: BosStr> UpdateThingBuilder<St, S>
612
612
+
where
613
613
+
St: update_thing_state::State,
614
614
+
St::Uri: update_thing_state::IsUnset,
615
615
+
{
616
616
+
/// Set the `uri` field (required)
617
617
+
pub fn uri(
618
618
+
mut self,
619
619
+
value: impl Into<AtUri<S>>,
620
620
+
) -> UpdateThingBuilder<update_thing_state::SetUri<St>, S> {
621
621
+
self._fields.1 = Option::Some(value.into());
622
622
+
UpdateThingBuilder {
623
623
+
_state: PhantomData,
624
624
+
_fields: self._fields,
625
625
+
_type: PhantomData,
626
626
+
}
627
627
+
}
628
628
+
}
629
629
+
630
630
+
impl<St, S: BosStr> UpdateThingBuilder<St, S>
631
631
+
where
632
632
+
St: update_thing_state::State,
633
633
+
St::Thing: update_thing_state::IsSet,
634
634
+
St::Uri: update_thing_state::IsSet,
635
635
+
{
636
636
+
/// Build the final struct.
637
637
+
pub fn build(self) -> UpdateThing<S> {
638
638
+
UpdateThing {
639
639
+
thing: self._fields.0.unwrap(),
640
640
+
uri: self._fields.1.unwrap(),
641
641
+
extra_data: Default::default(),
642
642
+
}
643
643
+
}
644
644
+
/// Build the final struct with custom extra_data.
645
645
+
pub fn build_with_data(
646
646
+
self,
647
647
+
extra_data: BTreeMap<SmolStr, Data<S>>,
648
648
+
) -> UpdateThing<S> {
649
649
+
UpdateThing {
650
650
+
thing: self._fields.0.unwrap(),
651
651
+
uri: self._fields.1.unwrap(),
652
652
+
extra_data: Some(extra_data),
653
653
+
}
654
654
+
}
655
655
+
}
656
656
+
657
657
+
pub mod model_input_state {
658
658
+
659
659
+
pub use crate::builder_types::{Set, Unset, IsSet, IsUnset};
660
660
+
#[allow(unused)]
661
661
+
use ::core::marker::PhantomData;
662
662
+
mod sealed {
663
663
+
pub trait Sealed {}
664
664
+
}
665
665
+
/// State trait tracking which required fields have been set
666
666
+
pub trait State: sealed::Sealed {
667
667
+
type Name;
668
668
+
type Parts;
669
669
+
}
670
670
+
/// Empty state - all required fields are unset
671
671
+
pub struct Empty(());
672
672
+
impl sealed::Sealed for Empty {}
673
673
+
impl State for Empty {
674
674
+
type Name = Unset;
675
675
+
type Parts = Unset;
676
676
+
}
677
677
+
///State transition - sets the `name` field to Set
678
678
+
pub struct SetName<St: State = Empty>(PhantomData<fn() -> St>);
679
679
+
impl<St: State> sealed::Sealed for SetName<St> {}
680
680
+
impl<St: State> State for SetName<St> {
681
681
+
type Name = Set<members::name>;
682
682
+
type Parts = St::Parts;
683
683
+
}
684
684
+
///State transition - sets the `parts` field to Set
685
685
+
pub struct SetParts<St: State = Empty>(PhantomData<fn() -> St>);
686
686
+
impl<St: State> sealed::Sealed for SetParts<St> {}
687
687
+
impl<St: State> State for SetParts<St> {
688
688
+
type Name = St::Name;
689
689
+
type Parts = Set<members::parts>;
690
690
+
}
691
691
+
/// Marker types for field names
692
692
+
#[allow(non_camel_case_types)]
693
693
+
pub mod members {
694
694
+
///Marker type for the `name` field
695
695
+
pub struct name(());
696
696
+
///Marker type for the `parts` field
697
697
+
pub struct parts(());
698
698
+
}
699
699
+
}
700
700
+
701
701
+
/// Builder for constructing an instance of this type.
702
702
+
pub struct ModelInputBuilder<St: model_input_state::State, S: BosStr = DefaultStr> {
703
703
+
_state: PhantomData<fn() -> St>,
704
704
+
_fields: (
705
705
+
Option<Vec<Image<S>>>,
706
706
+
Option<StrongRef<S>>,
707
707
+
Option<Vec<S>>,
708
708
+
Option<Vec<StrongRef<S>>>,
709
709
+
Option<S>,
710
710
+
Option<S>,
711
711
+
Option<Vec<update_thing::PartInput<S>>>,
712
712
+
Option<Vec<Image<S>>>,
713
713
+
Option<S>,
714
714
+
Option<Vec<S>>,
715
715
+
Option<AtUri<S>>,
716
716
+
),
717
717
+
_type: PhantomData<fn() -> S>,
718
718
+
}
719
719
+
720
720
+
impl ModelInput<DefaultStr> {
721
721
+
/// Create a new builder for this type, using the default string type (DefaultStr = SmolStr) if needed
722
722
+
pub fn new() -> ModelInputBuilder<model_input_state::Empty, DefaultStr> {
723
723
+
ModelInputBuilder::new()
724
724
+
}
725
725
+
}
726
726
+
727
727
+
impl<S: BosStr> ModelInput<S> {
728
728
+
/// Create a new builder for this type
729
729
+
pub fn builder() -> ModelInputBuilder<model_input_state::Empty, S> {
730
730
+
ModelInputBuilder::builder()
731
731
+
}
732
732
+
}
733
733
+
734
734
+
impl ModelInputBuilder<model_input_state::Empty, DefaultStr> {
735
735
+
/// Create a new builder with all fields unset, using the default string type, if needed
736
736
+
pub fn new() -> Self {
737
737
+
ModelInputBuilder {
738
738
+
_state: PhantomData,
739
739
+
_fields: (None, None, None, None, None, None, None, None, None, None, None),
740
740
+
_type: PhantomData,
741
741
+
}
742
742
+
}
743
743
+
}
744
744
+
745
745
+
impl<S: BosStr> ModelInputBuilder<model_input_state::Empty, S> {
746
746
+
/// Create a new builder with all fields unset
747
747
+
pub fn builder() -> Self {
748
748
+
ModelInputBuilder {
749
749
+
_state: PhantomData,
750
750
+
_fields: (None, None, None, None, None, None, None, None, None, None, None),
751
751
+
_type: PhantomData,
752
752
+
}
753
753
+
}
754
754
+
}
755
755
+
756
756
+
impl<St: model_input_state::State, S: BosStr> ModelInputBuilder<St, S> {
757
757
+
/// Set the `cover` field (optional)
758
758
+
pub fn cover(mut self, value: impl Into<Option<Vec<Image<S>>>>) -> Self {
759
759
+
self._fields.0 = value.into();
760
760
+
self
761
761
+
}
762
762
+
/// Set the `cover` field to an Option value (optional)
763
763
+
pub fn maybe_cover(mut self, value: Option<Vec<Image<S>>>) -> Self {
764
764
+
self._fields.0 = value;
765
765
+
self
766
766
+
}
767
767
+
}
768
768
+
769
769
+
impl<St: model_input_state::State, S: BosStr> ModelInputBuilder<St, S> {
770
770
+
/// Set the `derivedFrom` field (optional)
771
771
+
pub fn derived_from(mut self, value: impl Into<Option<StrongRef<S>>>) -> Self {
772
772
+
self._fields.1 = value.into();
773
773
+
self
774
774
+
}
775
775
+
/// Set the `derivedFrom` field to an Option value (optional)
776
776
+
pub fn maybe_derived_from(mut self, value: Option<StrongRef<S>>) -> Self {
777
777
+
self._fields.1 = value;
778
778
+
self
779
779
+
}
780
780
+
}
781
781
+
782
782
+
impl<St: model_input_state::State, S: BosStr> ModelInputBuilder<St, S> {
783
783
+
/// Set the `instructions` field (optional)
784
784
+
pub fn instructions(mut self, value: impl Into<Option<Vec<S>>>) -> Self {
785
785
+
self._fields.2 = value.into();
786
786
+
self
787
787
+
}
788
788
+
/// Set the `instructions` field to an Option value (optional)
789
789
+
pub fn maybe_instructions(mut self, value: Option<Vec<S>>) -> Self {
790
790
+
self._fields.2 = value;
791
791
+
self
792
792
+
}
793
793
+
}
794
794
+
795
795
+
impl<St: model_input_state::State, S: BosStr> ModelInputBuilder<St, S> {
796
796
+
/// Set the `intendedToWorkWith` field (optional)
797
797
+
pub fn intended_to_work_with(
798
798
+
mut self,
799
799
+
value: impl Into<Option<Vec<StrongRef<S>>>>,
800
800
+
) -> Self {
801
801
+
self._fields.3 = value.into();
802
802
+
self
803
803
+
}
804
804
+
/// Set the `intendedToWorkWith` field to an Option value (optional)
805
805
+
pub fn maybe_intended_to_work_with(
806
806
+
mut self,
807
807
+
value: Option<Vec<StrongRef<S>>>,
808
808
+
) -> Self {
809
809
+
self._fields.3 = value;
810
810
+
self
811
811
+
}
812
812
+
}
813
813
+
814
814
+
impl<St: model_input_state::State, S: BosStr> ModelInputBuilder<St, S> {
815
815
+
/// Set the `license` field (optional)
816
816
+
pub fn license(mut self, value: impl Into<Option<S>>) -> Self {
817
817
+
self._fields.4 = value.into();
818
818
+
self
819
819
+
}
820
820
+
/// Set the `license` field to an Option value (optional)
821
821
+
pub fn maybe_license(mut self, value: Option<S>) -> Self {
822
822
+
self._fields.4 = value;
823
823
+
self
824
824
+
}
825
825
+
}
826
826
+
827
827
+
impl<St, S: BosStr> ModelInputBuilder<St, S>
828
828
+
where
829
829
+
St: model_input_state::State,
830
830
+
St::Name: model_input_state::IsUnset,
831
831
+
{
832
832
+
/// Set the `name` field (required)
833
833
+
pub fn name(
834
834
+
mut self,
835
835
+
value: impl Into<S>,
836
836
+
) -> ModelInputBuilder<model_input_state::SetName<St>, S> {
837
837
+
self._fields.5 = Option::Some(value.into());
838
838
+
ModelInputBuilder {
839
839
+
_state: PhantomData,
840
840
+
_fields: self._fields,
841
841
+
_type: PhantomData,
842
842
+
}
843
843
+
}
844
844
+
}
845
845
+
846
846
+
impl<St, S: BosStr> ModelInputBuilder<St, S>
847
847
+
where
848
848
+
St: model_input_state::State,
849
849
+
St::Parts: model_input_state::IsUnset,
850
850
+
{
851
851
+
/// Set the `parts` field (required)
852
852
+
pub fn parts(
853
853
+
mut self,
854
854
+
value: impl Into<Vec<update_thing::PartInput<S>>>,
855
855
+
) -> ModelInputBuilder<model_input_state::SetParts<St>, S> {
856
856
+
self._fields.6 = Option::Some(value.into());
857
857
+
ModelInputBuilder {
858
858
+
_state: PhantomData,
859
859
+
_fields: self._fields,
860
860
+
_type: PhantomData,
861
861
+
}
862
862
+
}
863
863
+
}
864
864
+
865
865
+
impl<St: model_input_state::State, S: BosStr> ModelInputBuilder<St, S> {
866
866
+
/// Set the `previews` field (optional)
867
867
+
pub fn previews(mut self, value: impl Into<Option<Vec<Image<S>>>>) -> Self {
868
868
+
self._fields.7 = value.into();
869
869
+
self
870
870
+
}
871
871
+
/// Set the `previews` field to an Option value (optional)
872
872
+
pub fn maybe_previews(mut self, value: Option<Vec<Image<S>>>) -> Self {
873
873
+
self._fields.7 = value;
874
874
+
self
875
875
+
}
876
876
+
}
877
877
+
878
878
+
impl<St: model_input_state::State, S: BosStr> ModelInputBuilder<St, S> {
879
879
+
/// Set the `summary` field (optional)
880
880
+
pub fn summary(mut self, value: impl Into<Option<S>>) -> Self {
881
881
+
self._fields.8 = value.into();
882
882
+
self
883
883
+
}
884
884
+
/// Set the `summary` field to an Option value (optional)
885
885
+
pub fn maybe_summary(mut self, value: Option<S>) -> Self {
886
886
+
self._fields.8 = value;
887
887
+
self
888
888
+
}
889
889
+
}
890
890
+
891
891
+
impl<St: model_input_state::State, S: BosStr> ModelInputBuilder<St, S> {
892
892
+
/// Set the `tags` field (optional)
893
893
+
pub fn tags(mut self, value: impl Into<Option<Vec<S>>>) -> Self {
894
894
+
self._fields.9 = value.into();
895
895
+
self
896
896
+
}
897
897
+
/// Set the `tags` field to an Option value (optional)
898
898
+
pub fn maybe_tags(mut self, value: Option<Vec<S>>) -> Self {
899
899
+
self._fields.9 = value;
900
900
+
self
901
901
+
}
902
902
+
}
903
903
+
904
904
+
impl<St: model_input_state::State, S: BosStr> ModelInputBuilder<St, S> {
905
905
+
/// Set the `uri` field (optional)
906
906
+
pub fn uri(mut self, value: impl Into<Option<AtUri<S>>>) -> Self {
907
907
+
self._fields.10 = value.into();
908
908
+
self
909
909
+
}
910
910
+
/// Set the `uri` field to an Option value (optional)
911
911
+
pub fn maybe_uri(mut self, value: Option<AtUri<S>>) -> Self {
912
912
+
self._fields.10 = value;
913
913
+
self
914
914
+
}
915
915
+
}
916
916
+
917
917
+
impl<St, S: BosStr> ModelInputBuilder<St, S>
918
918
+
where
919
919
+
St: model_input_state::State,
920
920
+
St::Name: model_input_state::IsSet,
921
921
+
St::Parts: model_input_state::IsSet,
922
922
+
{
923
923
+
/// Build the final struct.
924
924
+
pub fn build(self) -> ModelInput<S> {
925
925
+
ModelInput {
926
926
+
cover: self._fields.0,
927
927
+
derived_from: self._fields.1,
928
928
+
instructions: self._fields.2,
929
929
+
intended_to_work_with: self._fields.3,
930
930
+
license: self._fields.4,
931
931
+
name: self._fields.5.unwrap(),
932
932
+
parts: self._fields.6.unwrap(),
933
933
+
previews: self._fields.7,
934
934
+
summary: self._fields.8,
935
935
+
tags: self._fields.9,
936
936
+
uri: self._fields.10,
937
937
+
extra_data: Default::default(),
938
938
+
}
939
939
+
}
940
940
+
/// Build the final struct with custom extra_data.
941
941
+
pub fn build_with_data(
942
942
+
self,
943
943
+
extra_data: BTreeMap<SmolStr, Data<S>>,
944
944
+
) -> ModelInput<S> {
945
945
+
ModelInput {
946
946
+
cover: self._fields.0,
947
947
+
derived_from: self._fields.1,
948
948
+
instructions: self._fields.2,
949
949
+
intended_to_work_with: self._fields.3,
950
950
+
license: self._fields.4,
951
951
+
name: self._fields.5.unwrap(),
952
952
+
parts: self._fields.6.unwrap(),
953
953
+
previews: self._fields.7,
954
954
+
summary: self._fields.8,
955
955
+
tags: self._fields.9,
956
956
+
uri: self._fields.10,
957
957
+
extra_data: Some(extra_data),
958
958
+
}
959
959
+
}
960
960
+
}
961
961
+
962
962
+
fn lexicon_doc_space_polymodel_library_updateThing() -> LexiconDoc<'static> {
963
963
+
#[allow(unused_imports)]
964
964
+
use jacquard_common::{CowStr, deps::smol_str::SmolStr, types::blob::MimeType};
965
965
+
use jacquard_lexicon::lexicon::*;
966
966
+
use alloc::collections::BTreeMap;
967
967
+
LexiconDoc {
968
968
+
lexicon: Lexicon::Lexicon1,
969
969
+
id: CowStr::new_static("space.polymodel.library.updateThing"),
970
970
+
defs: {
971
971
+
let mut map = BTreeMap::new();
972
972
+
map.insert(
973
973
+
SmolStr::new_static("main"),
974
974
+
LexUserType::XrpcProcedure(LexXrpcProcedure {
975
975
+
input: Some(LexXrpcBody {
976
976
+
encoding: CowStr::new_static("application/json"),
977
977
+
schema: Some(
978
978
+
LexXrpcBodySchema::Object(LexObject {
979
979
+
required: Some(
980
980
+
vec![
981
981
+
SmolStr::new_static("uri"), SmolStr::new_static("thing")
982
982
+
],
983
983
+
),
984
984
+
properties: {
985
985
+
#[allow(unused_mut)]
986
986
+
let mut map = BTreeMap::new();
987
987
+
map.insert(
988
988
+
SmolStr::new_static("thing"),
989
989
+
LexObjectProperty::Ref(LexRef {
990
990
+
r#ref: CowStr::new_static("#thingInput"),
991
991
+
..Default::default()
992
992
+
}),
993
993
+
);
994
994
+
map.insert(
995
995
+
SmolStr::new_static("uri"),
996
996
+
LexObjectProperty::String(LexString {
997
997
+
format: Some(LexStringFormat::AtUri),
998
998
+
..Default::default()
999
999
+
}),
1000
1000
+
);
1001
1001
+
map
1002
1002
+
},
1003
1003
+
..Default::default()
1004
1004
+
}),
1005
1005
+
),
1006
1006
+
..Default::default()
1007
1007
+
}),
1008
1008
+
..Default::default()
1009
1009
+
}),
1010
1010
+
);
1011
1011
+
map.insert(
1012
1012
+
SmolStr::new_static("modelInput"),
1013
1013
+
LexUserType::Object(LexObject {
1014
1014
+
required: Some(
1015
1015
+
vec![SmolStr::new_static("name"), SmolStr::new_static("parts")],
1016
1016
+
),
1017
1017
+
properties: {
1018
1018
+
#[allow(unused_mut)]
1019
1019
+
let mut map = BTreeMap::new();
1020
1020
+
map.insert(
1021
1021
+
SmolStr::new_static("cover"),
1022
1022
+
LexObjectProperty::Array(LexArray {
1023
1023
+
items: LexArrayItem::Ref(LexRef {
1024
1024
+
r#ref: CowStr::new_static(
1025
1025
+
"space.polymodel.library.defs#image",
1026
1026
+
),
1027
1027
+
..Default::default()
1028
1028
+
}),
1029
1029
+
max_length: Some(8usize),
1030
1030
+
..Default::default()
1031
1031
+
}),
1032
1032
+
);
1033
1033
+
map.insert(
1034
1034
+
SmolStr::new_static("derivedFrom"),
1035
1035
+
LexObjectProperty::Ref(LexRef {
1036
1036
+
r#ref: CowStr::new_static("com.atproto.repo.strongRef"),
1037
1037
+
..Default::default()
1038
1038
+
}),
1039
1039
+
);
1040
1040
+
map.insert(
1041
1041
+
SmolStr::new_static("instructions"),
1042
1042
+
LexObjectProperty::Array(LexArray {
1043
1043
+
items: LexArrayItem::String(LexString {
1044
1044
+
..Default::default()
1045
1045
+
}),
1046
1046
+
max_length: Some(100usize),
1047
1047
+
..Default::default()
1048
1048
+
}),
1049
1049
+
);
1050
1050
+
map.insert(
1051
1051
+
SmolStr::new_static("intendedToWorkWith"),
1052
1052
+
LexObjectProperty::Array(LexArray {
1053
1053
+
items: LexArrayItem::Ref(LexRef {
1054
1054
+
r#ref: CowStr::new_static("com.atproto.repo.strongRef"),
1055
1055
+
..Default::default()
1056
1056
+
}),
1057
1057
+
max_length: Some(32usize),
1058
1058
+
..Default::default()
1059
1059
+
}),
1060
1060
+
);
1061
1061
+
map.insert(
1062
1062
+
SmolStr::new_static("license"),
1063
1063
+
LexObjectProperty::String(LexString { ..Default::default() }),
1064
1064
+
);
1065
1065
+
map.insert(
1066
1066
+
SmolStr::new_static("name"),
1067
1067
+
LexObjectProperty::String(LexString {
1068
1068
+
max_length: Some(256usize),
1069
1069
+
max_graphemes: Some(64usize),
1070
1070
+
..Default::default()
1071
1071
+
}),
1072
1072
+
);
1073
1073
+
map.insert(
1074
1074
+
SmolStr::new_static("parts"),
1075
1075
+
LexObjectProperty::Array(LexArray {
1076
1076
+
items: LexArrayItem::Ref(LexRef {
1077
1077
+
r#ref: CowStr::new_static("#partInput"),
1078
1078
+
..Default::default()
1079
1079
+
}),
1080
1080
+
max_length: Some(1024usize),
1081
1081
+
..Default::default()
1082
1082
+
}),
1083
1083
+
);
1084
1084
+
map.insert(
1085
1085
+
SmolStr::new_static("previews"),
1086
1086
+
LexObjectProperty::Array(LexArray {
1087
1087
+
items: LexArrayItem::Ref(LexRef {
1088
1088
+
r#ref: CowStr::new_static(
1089
1089
+
"space.polymodel.library.defs#image",
1090
1090
+
),
1091
1091
+
..Default::default()
1092
1092
+
}),
1093
1093
+
max_length: Some(12usize),
1094
1094
+
..Default::default()
1095
1095
+
}),
1096
1096
+
);
1097
1097
+
map.insert(
1098
1098
+
SmolStr::new_static("summary"),
1099
1099
+
LexObjectProperty::String(LexString {
1100
1100
+
max_length: Some(2000usize),
1101
1101
+
max_graphemes: Some(300usize),
1102
1102
+
..Default::default()
1103
1103
+
}),
1104
1104
+
);
1105
1105
+
map.insert(
1106
1106
+
SmolStr::new_static("tags"),
1107
1107
+
LexObjectProperty::Array(LexArray {
1108
1108
+
items: LexArrayItem::String(LexString {
1109
1109
+
max_length: Some(64usize),
1110
1110
+
max_graphemes: Some(32usize),
1111
1111
+
..Default::default()
1112
1112
+
}),
1113
1113
+
max_length: Some(16usize),
1114
1114
+
..Default::default()
1115
1115
+
}),
1116
1116
+
);
1117
1117
+
map.insert(
1118
1118
+
SmolStr::new_static("uri"),
1119
1119
+
LexObjectProperty::String(LexString {
1120
1120
+
format: Some(LexStringFormat::AtUri),
1121
1121
+
..Default::default()
1122
1122
+
}),
1123
1123
+
);
1124
1124
+
map
1125
1125
+
},
1126
1126
+
..Default::default()
1127
1127
+
}),
1128
1128
+
);
1129
1129
+
map.insert(
1130
1130
+
SmolStr::new_static("partInput"),
1131
1131
+
LexUserType::Object(LexObject {
1132
1132
+
required: Some(vec![SmolStr::new_static("name")]),
1133
1133
+
properties: {
1134
1134
+
#[allow(unused_mut)]
1135
1135
+
let mut map = BTreeMap::new();
1136
1136
+
map.insert(
1137
1137
+
SmolStr::new_static("derivedFrom"),
1138
1138
+
LexObjectProperty::Ref(LexRef {
1139
1139
+
r#ref: CowStr::new_static("com.atproto.repo.strongRef"),
1140
1140
+
..Default::default()
1141
1141
+
}),
1142
1142
+
);
1143
1143
+
map.insert(
1144
1144
+
SmolStr::new_static("dimensions"),
1145
1145
+
LexObjectProperty::Ref(LexRef {
1146
1146
+
r#ref: CowStr::new_static(
1147
1147
+
"space.polymodel.library.defs#bbox",
1148
1148
+
),
1149
1149
+
..Default::default()
1150
1150
+
}),
1151
1151
+
);
1152
1152
+
map.insert(
1153
1153
+
SmolStr::new_static("file"),
1154
1154
+
LexObjectProperty::Ref(LexRef {
1155
1155
+
r#ref: CowStr::new_static(
1156
1156
+
"space.polymodel.library.defs#file",
1157
1157
+
),
1158
1158
+
..Default::default()
1159
1159
+
}),
1160
1160
+
);
1161
1161
+
map.insert(
1162
1162
+
SmolStr::new_static("format"),
1163
1163
+
LexObjectProperty::String(LexString { ..Default::default() }),
1164
1164
+
);
1165
1165
+
map.insert(
1166
1166
+
SmolStr::new_static("intendedToWorkWith"),
1167
1167
+
LexObjectProperty::Array(LexArray {
1168
1168
+
items: LexArrayItem::Ref(LexRef {
1169
1169
+
r#ref: CowStr::new_static("com.atproto.repo.strongRef"),
1170
1170
+
..Default::default()
1171
1171
+
}),
1172
1172
+
max_length: Some(32usize),
1173
1173
+
..Default::default()
1174
1174
+
}),
1175
1175
+
);
1176
1176
+
map.insert(
1177
1177
+
SmolStr::new_static("name"),
1178
1178
+
LexObjectProperty::String(LexString {
1179
1179
+
max_length: Some(256usize),
1180
1180
+
max_graphemes: Some(64usize),
1181
1181
+
..Default::default()
1182
1182
+
}),
1183
1183
+
);
1184
1184
+
map.insert(
1185
1185
+
SmolStr::new_static("notes"),
1186
1186
+
LexObjectProperty::String(LexString {
1187
1187
+
max_length: Some(2000usize),
1188
1188
+
max_graphemes: Some(300usize),
1189
1189
+
..Default::default()
1190
1190
+
}),
1191
1191
+
);
1192
1192
+
map.insert(
1193
1193
+
SmolStr::new_static("previews"),
1194
1194
+
LexObjectProperty::Array(LexArray {
1195
1195
+
items: LexArrayItem::Ref(LexRef {
1196
1196
+
r#ref: CowStr::new_static(
1197
1197
+
"space.polymodel.library.defs#image",
1198
1198
+
),
1199
1199
+
..Default::default()
1200
1200
+
}),
1201
1201
+
max_length: Some(12usize),
1202
1202
+
..Default::default()
1203
1203
+
}),
1204
1204
+
);
1205
1205
+
map.insert(
1206
1206
+
SmolStr::new_static("printSettings"),
1207
1207
+
LexObjectProperty::Array(LexArray {
1208
1208
+
items: LexArrayItem::String(LexString {
1209
1209
+
..Default::default()
1210
1210
+
}),
1211
1211
+
max_length: Some(50usize),
1212
1212
+
..Default::default()
1213
1213
+
}),
1214
1214
+
);
1215
1215
+
map.insert(
1216
1216
+
SmolStr::new_static("units"),
1217
1217
+
LexObjectProperty::String(LexString { ..Default::default() }),
1218
1218
+
);
1219
1219
+
map.insert(
1220
1220
+
SmolStr::new_static("uploadId"),
1221
1221
+
LexObjectProperty::String(LexString { ..Default::default() }),
1222
1222
+
);
1223
1223
+
map.insert(
1224
1224
+
SmolStr::new_static("uri"),
1225
1225
+
LexObjectProperty::String(LexString {
1226
1226
+
format: Some(LexStringFormat::AtUri),
1227
1227
+
..Default::default()
1228
1228
+
}),
1229
1229
+
);
1230
1230
+
map
1231
1231
+
},
1232
1232
+
..Default::default()
1233
1233
+
}),
1234
1234
+
);
1235
1235
+
map.insert(
1236
1236
+
SmolStr::new_static("thingInput"),
1237
1237
+
LexUserType::Object(LexObject {
1238
1238
+
required: Some(
1239
1239
+
vec![
1240
1240
+
SmolStr::new_static("name"), SmolStr::new_static("license"),
1241
1241
+
SmolStr::new_static("models")
1242
1242
+
],
1243
1243
+
),
1244
1244
+
properties: {
1245
1245
+
#[allow(unused_mut)]
1246
1246
+
let mut map = BTreeMap::new();
1247
1247
+
map.insert(
1248
1248
+
SmolStr::new_static("cover"),
1249
1249
+
LexObjectProperty::Array(LexArray {
1250
1250
+
items: LexArrayItem::Ref(LexRef {
1251
1251
+
r#ref: CowStr::new_static(
1252
1252
+
"space.polymodel.library.defs#image",
1253
1253
+
),
1254
1254
+
..Default::default()
1255
1255
+
}),
1256
1256
+
max_length: Some(8usize),
1257
1257
+
..Default::default()
1258
1258
+
}),
1259
1259
+
);
1260
1260
+
map.insert(
1261
1261
+
SmolStr::new_static("derivedFrom"),
1262
1262
+
LexObjectProperty::Ref(LexRef {
1263
1263
+
r#ref: CowStr::new_static("com.atproto.repo.strongRef"),
1264
1264
+
..Default::default()
1265
1265
+
}),
1266
1266
+
);
1267
1267
+
map.insert(
1268
1268
+
SmolStr::new_static("instructions"),
1269
1269
+
LexObjectProperty::Array(LexArray {
1270
1270
+
items: LexArrayItem::String(LexString {
1271
1271
+
..Default::default()
1272
1272
+
}),
1273
1273
+
max_length: Some(100usize),
1274
1274
+
..Default::default()
1275
1275
+
}),
1276
1276
+
);
1277
1277
+
map.insert(
1278
1278
+
SmolStr::new_static("intendedToWorkWith"),
1279
1279
+
LexObjectProperty::Array(LexArray {
1280
1280
+
items: LexArrayItem::Ref(LexRef {
1281
1281
+
r#ref: CowStr::new_static("com.atproto.repo.strongRef"),
1282
1282
+
..Default::default()
1283
1283
+
}),
1284
1284
+
max_length: Some(32usize),
1285
1285
+
..Default::default()
1286
1286
+
}),
1287
1287
+
);
1288
1288
+
map.insert(
1289
1289
+
SmolStr::new_static("license"),
1290
1290
+
LexObjectProperty::String(LexString { ..Default::default() }),
1291
1291
+
);
1292
1292
+
map.insert(
1293
1293
+
SmolStr::new_static("models"),
1294
1294
+
LexObjectProperty::Array(LexArray {
1295
1295
+
items: LexArrayItem::Ref(LexRef {
1296
1296
+
r#ref: CowStr::new_static("#modelInput"),
1297
1297
+
..Default::default()
1298
1298
+
}),
1299
1299
+
max_length: Some(256usize),
1300
1300
+
..Default::default()
1301
1301
+
}),
1302
1302
+
);
1303
1303
+
map.insert(
1304
1304
+
SmolStr::new_static("name"),
1305
1305
+
LexObjectProperty::String(LexString {
1306
1306
+
max_length: Some(256usize),
1307
1307
+
max_graphemes: Some(64usize),
1308
1308
+
..Default::default()
1309
1309
+
}),
1310
1310
+
);
1311
1311
+
map.insert(
1312
1312
+
SmolStr::new_static("previews"),
1313
1313
+
LexObjectProperty::Array(LexArray {
1314
1314
+
items: LexArrayItem::Ref(LexRef {
1315
1315
+
r#ref: CowStr::new_static(
1316
1316
+
"space.polymodel.library.defs#image",
1317
1317
+
),
1318
1318
+
..Default::default()
1319
1319
+
}),
1320
1320
+
max_length: Some(12usize),
1321
1321
+
..Default::default()
1322
1322
+
}),
1323
1323
+
);
1324
1324
+
map.insert(
1325
1325
+
SmolStr::new_static("summary"),
1326
1326
+
LexObjectProperty::String(LexString {
1327
1327
+
max_length: Some(2000usize),
1328
1328
+
max_graphemes: Some(300usize),
1329
1329
+
..Default::default()
1330
1330
+
}),
1331
1331
+
);
1332
1332
+
map.insert(
1333
1333
+
SmolStr::new_static("tags"),
1334
1334
+
LexObjectProperty::Array(LexArray {
1335
1335
+
items: LexArrayItem::String(LexString {
1336
1336
+
max_length: Some(64usize),
1337
1337
+
max_graphemes: Some(32usize),
1338
1338
+
..Default::default()
1339
1339
+
}),
1340
1340
+
max_length: Some(16usize),
1341
1341
+
..Default::default()
1342
1342
+
}),
1343
1343
+
);
1344
1344
+
map
1345
1345
+
},
1346
1346
+
..Default::default()
1347
1347
+
}),
1348
1348
+
);
1349
1349
+
map
1350
1350
+
},
1351
1351
+
..Default::default()
1352
1352
+
}
1353
1353
+
}
1354
1354
+
1355
1355
+
pub mod thing_input_state {
1356
1356
+
1357
1357
+
pub use crate::builder_types::{Set, Unset, IsSet, IsUnset};
1358
1358
+
#[allow(unused)]
1359
1359
+
use ::core::marker::PhantomData;
1360
1360
+
mod sealed {
1361
1361
+
pub trait Sealed {}
1362
1362
+
}
1363
1363
+
/// State trait tracking which required fields have been set
1364
1364
+
pub trait State: sealed::Sealed {
1365
1365
+
type License;
1366
1366
+
type Models;
1367
1367
+
type Name;
1368
1368
+
}
1369
1369
+
/// Empty state - all required fields are unset
1370
1370
+
pub struct Empty(());
1371
1371
+
impl sealed::Sealed for Empty {}
1372
1372
+
impl State for Empty {
1373
1373
+
type License = Unset;
1374
1374
+
type Models = Unset;
1375
1375
+
type Name = Unset;
1376
1376
+
}
1377
1377
+
///State transition - sets the `license` field to Set
1378
1378
+
pub struct SetLicense<St: State = Empty>(PhantomData<fn() -> St>);
1379
1379
+
impl<St: State> sealed::Sealed for SetLicense<St> {}
1380
1380
+
impl<St: State> State for SetLicense<St> {
1381
1381
+
type License = Set<members::license>;
1382
1382
+
type Models = St::Models;
1383
1383
+
type Name = St::Name;
1384
1384
+
}
1385
1385
+
///State transition - sets the `models` field to Set
1386
1386
+
pub struct SetModels<St: State = Empty>(PhantomData<fn() -> St>);
1387
1387
+
impl<St: State> sealed::Sealed for SetModels<St> {}
1388
1388
+
impl<St: State> State for SetModels<St> {
1389
1389
+
type License = St::License;
1390
1390
+
type Models = Set<members::models>;
1391
1391
+
type Name = St::Name;
1392
1392
+
}
1393
1393
+
///State transition - sets the `name` field to Set
1394
1394
+
pub struct SetName<St: State = Empty>(PhantomData<fn() -> St>);
1395
1395
+
impl<St: State> sealed::Sealed for SetName<St> {}
1396
1396
+
impl<St: State> State for SetName<St> {
1397
1397
+
type License = St::License;
1398
1398
+
type Models = St::Models;
1399
1399
+
type Name = Set<members::name>;
1400
1400
+
}
1401
1401
+
/// Marker types for field names
1402
1402
+
#[allow(non_camel_case_types)]
1403
1403
+
pub mod members {
1404
1404
+
///Marker type for the `license` field
1405
1405
+
pub struct license(());
1406
1406
+
///Marker type for the `models` field
1407
1407
+
pub struct models(());
1408
1408
+
///Marker type for the `name` field
1409
1409
+
pub struct name(());
1410
1410
+
}
1411
1411
+
}
1412
1412
+
1413
1413
+
/// Builder for constructing an instance of this type.
1414
1414
+
pub struct ThingInputBuilder<St: thing_input_state::State, S: BosStr = DefaultStr> {
1415
1415
+
_state: PhantomData<fn() -> St>,
1416
1416
+
_fields: (
1417
1417
+
Option<Vec<Image<S>>>,
1418
1418
+
Option<StrongRef<S>>,
1419
1419
+
Option<Vec<S>>,
1420
1420
+
Option<Vec<StrongRef<S>>>,
1421
1421
+
Option<S>,
1422
1422
+
Option<Vec<update_thing::ModelInput<S>>>,
1423
1423
+
Option<S>,
1424
1424
+
Option<Vec<Image<S>>>,
1425
1425
+
Option<S>,
1426
1426
+
Option<Vec<S>>,
1427
1427
+
),
1428
1428
+
_type: PhantomData<fn() -> S>,
1429
1429
+
}
1430
1430
+
1431
1431
+
impl ThingInput<DefaultStr> {
1432
1432
+
/// Create a new builder for this type, using the default string type (DefaultStr = SmolStr) if needed
1433
1433
+
pub fn new() -> ThingInputBuilder<thing_input_state::Empty, DefaultStr> {
1434
1434
+
ThingInputBuilder::new()
1435
1435
+
}
1436
1436
+
}
1437
1437
+
1438
1438
+
impl<S: BosStr> ThingInput<S> {
1439
1439
+
/// Create a new builder for this type
1440
1440
+
pub fn builder() -> ThingInputBuilder<thing_input_state::Empty, S> {
1441
1441
+
ThingInputBuilder::builder()
1442
1442
+
}
1443
1443
+
}
1444
1444
+
1445
1445
+
impl ThingInputBuilder<thing_input_state::Empty, DefaultStr> {
1446
1446
+
/// Create a new builder with all fields unset, using the default string type, if needed
1447
1447
+
pub fn new() -> Self {
1448
1448
+
ThingInputBuilder {
1449
1449
+
_state: PhantomData,
1450
1450
+
_fields: (None, None, None, None, None, None, None, None, None, None),
1451
1451
+
_type: PhantomData,
1452
1452
+
}
1453
1453
+
}
1454
1454
+
}
1455
1455
+
1456
1456
+
impl<S: BosStr> ThingInputBuilder<thing_input_state::Empty, S> {
1457
1457
+
/// Create a new builder with all fields unset
1458
1458
+
pub fn builder() -> Self {
1459
1459
+
ThingInputBuilder {
1460
1460
+
_state: PhantomData,
1461
1461
+
_fields: (None, None, None, None, None, None, None, None, None, None),
1462
1462
+
_type: PhantomData,
1463
1463
+
}
1464
1464
+
}
1465
1465
+
}
1466
1466
+
1467
1467
+
impl<St: thing_input_state::State, S: BosStr> ThingInputBuilder<St, S> {
1468
1468
+
/// Set the `cover` field (optional)
1469
1469
+
pub fn cover(mut self, value: impl Into<Option<Vec<Image<S>>>>) -> Self {
1470
1470
+
self._fields.0 = value.into();
1471
1471
+
self
1472
1472
+
}
1473
1473
+
/// Set the `cover` field to an Option value (optional)
1474
1474
+
pub fn maybe_cover(mut self, value: Option<Vec<Image<S>>>) -> Self {
1475
1475
+
self._fields.0 = value;
1476
1476
+
self
1477
1477
+
}
1478
1478
+
}
1479
1479
+
1480
1480
+
impl<St: thing_input_state::State, S: BosStr> ThingInputBuilder<St, S> {
1481
1481
+
/// Set the `derivedFrom` field (optional)
1482
1482
+
pub fn derived_from(mut self, value: impl Into<Option<StrongRef<S>>>) -> Self {
1483
1483
+
self._fields.1 = value.into();
1484
1484
+
self
1485
1485
+
}
1486
1486
+
/// Set the `derivedFrom` field to an Option value (optional)
1487
1487
+
pub fn maybe_derived_from(mut self, value: Option<StrongRef<S>>) -> Self {
1488
1488
+
self._fields.1 = value;
1489
1489
+
self
1490
1490
+
}
1491
1491
+
}
1492
1492
+
1493
1493
+
impl<St: thing_input_state::State, S: BosStr> ThingInputBuilder<St, S> {
1494
1494
+
/// Set the `instructions` field (optional)
1495
1495
+
pub fn instructions(mut self, value: impl Into<Option<Vec<S>>>) -> Self {
1496
1496
+
self._fields.2 = value.into();
1497
1497
+
self
1498
1498
+
}
1499
1499
+
/// Set the `instructions` field to an Option value (optional)
1500
1500
+
pub fn maybe_instructions(mut self, value: Option<Vec<S>>) -> Self {
1501
1501
+
self._fields.2 = value;
1502
1502
+
self
1503
1503
+
}
1504
1504
+
}
1505
1505
+
1506
1506
+
impl<St: thing_input_state::State, S: BosStr> ThingInputBuilder<St, S> {
1507
1507
+
/// Set the `intendedToWorkWith` field (optional)
1508
1508
+
pub fn intended_to_work_with(
1509
1509
+
mut self,
1510
1510
+
value: impl Into<Option<Vec<StrongRef<S>>>>,
1511
1511
+
) -> Self {
1512
1512
+
self._fields.3 = value.into();
1513
1513
+
self
1514
1514
+
}
1515
1515
+
/// Set the `intendedToWorkWith` field to an Option value (optional)
1516
1516
+
pub fn maybe_intended_to_work_with(
1517
1517
+
mut self,
1518
1518
+
value: Option<Vec<StrongRef<S>>>,
1519
1519
+
) -> Self {
1520
1520
+
self._fields.3 = value;
1521
1521
+
self
1522
1522
+
}
1523
1523
+
}
1524
1524
+
1525
1525
+
impl<St, S: BosStr> ThingInputBuilder<St, S>
1526
1526
+
where
1527
1527
+
St: thing_input_state::State,
1528
1528
+
St::License: thing_input_state::IsUnset,
1529
1529
+
{
1530
1530
+
/// Set the `license` field (required)
1531
1531
+
pub fn license(
1532
1532
+
mut self,
1533
1533
+
value: impl Into<S>,
1534
1534
+
) -> ThingInputBuilder<thing_input_state::SetLicense<St>, S> {
1535
1535
+
self._fields.4 = Option::Some(value.into());
1536
1536
+
ThingInputBuilder {
1537
1537
+
_state: PhantomData,
1538
1538
+
_fields: self._fields,
1539
1539
+
_type: PhantomData,
1540
1540
+
}
1541
1541
+
}
1542
1542
+
}
1543
1543
+
1544
1544
+
impl<St, S: BosStr> ThingInputBuilder<St, S>
1545
1545
+
where
1546
1546
+
St: thing_input_state::State,
1547
1547
+
St::Models: thing_input_state::IsUnset,
1548
1548
+
{
1549
1549
+
/// Set the `models` field (required)
1550
1550
+
pub fn models(
1551
1551
+
mut self,
1552
1552
+
value: impl Into<Vec<update_thing::ModelInput<S>>>,
1553
1553
+
) -> ThingInputBuilder<thing_input_state::SetModels<St>, S> {
1554
1554
+
self._fields.5 = Option::Some(value.into());
1555
1555
+
ThingInputBuilder {
1556
1556
+
_state: PhantomData,
1557
1557
+
_fields: self._fields,
1558
1558
+
_type: PhantomData,
1559
1559
+
}
1560
1560
+
}
1561
1561
+
}
1562
1562
+
1563
1563
+
impl<St, S: BosStr> ThingInputBuilder<St, S>
1564
1564
+
where
1565
1565
+
St: thing_input_state::State,
1566
1566
+
St::Name: thing_input_state::IsUnset,
1567
1567
+
{
1568
1568
+
/// Set the `name` field (required)
1569
1569
+
pub fn name(
1570
1570
+
mut self,
1571
1571
+
value: impl Into<S>,
1572
1572
+
) -> ThingInputBuilder<thing_input_state::SetName<St>, S> {
1573
1573
+
self._fields.6 = Option::Some(value.into());
1574
1574
+
ThingInputBuilder {
1575
1575
+
_state: PhantomData,
1576
1576
+
_fields: self._fields,
1577
1577
+
_type: PhantomData,
1578
1578
+
}
1579
1579
+
}
1580
1580
+
}
1581
1581
+
1582
1582
+
impl<St: thing_input_state::State, S: BosStr> ThingInputBuilder<St, S> {
1583
1583
+
/// Set the `previews` field (optional)
1584
1584
+
pub fn previews(mut self, value: impl Into<Option<Vec<Image<S>>>>) -> Self {
1585
1585
+
self._fields.7 = value.into();
1586
1586
+
self
1587
1587
+
}
1588
1588
+
/// Set the `previews` field to an Option value (optional)
1589
1589
+
pub fn maybe_previews(mut self, value: Option<Vec<Image<S>>>) -> Self {
1590
1590
+
self._fields.7 = value;
1591
1591
+
self
1592
1592
+
}
1593
1593
+
}
1594
1594
+
1595
1595
+
impl<St: thing_input_state::State, S: BosStr> ThingInputBuilder<St, S> {
1596
1596
+
/// Set the `summary` field (optional)
1597
1597
+
pub fn summary(mut self, value: impl Into<Option<S>>) -> Self {
1598
1598
+
self._fields.8 = value.into();
1599
1599
+
self
1600
1600
+
}
1601
1601
+
/// Set the `summary` field to an Option value (optional)
1602
1602
+
pub fn maybe_summary(mut self, value: Option<S>) -> Self {
1603
1603
+
self._fields.8 = value;
1604
1604
+
self
1605
1605
+
}
1606
1606
+
}
1607
1607
+
1608
1608
+
impl<St: thing_input_state::State, S: BosStr> ThingInputBuilder<St, S> {
1609
1609
+
/// Set the `tags` field (optional)
1610
1610
+
pub fn tags(mut self, value: impl Into<Option<Vec<S>>>) -> Self {
1611
1611
+
self._fields.9 = value.into();
1612
1612
+
self
1613
1613
+
}
1614
1614
+
/// Set the `tags` field to an Option value (optional)
1615
1615
+
pub fn maybe_tags(mut self, value: Option<Vec<S>>) -> Self {
1616
1616
+
self._fields.9 = value;
1617
1617
+
self
1618
1618
+
}
1619
1619
+
}
1620
1620
+
1621
1621
+
impl<St, S: BosStr> ThingInputBuilder<St, S>
1622
1622
+
where
1623
1623
+
St: thing_input_state::State,
1624
1624
+
St::License: thing_input_state::IsSet,
1625
1625
+
St::Models: thing_input_state::IsSet,
1626
1626
+
St::Name: thing_input_state::IsSet,
1627
1627
+
{
1628
1628
+
/// Build the final struct.
1629
1629
+
pub fn build(self) -> ThingInput<S> {
1630
1630
+
ThingInput {
1631
1631
+
cover: self._fields.0,
1632
1632
+
derived_from: self._fields.1,
1633
1633
+
instructions: self._fields.2,
1634
1634
+
intended_to_work_with: self._fields.3,
1635
1635
+
license: self._fields.4.unwrap(),
1636
1636
+
models: self._fields.5.unwrap(),
1637
1637
+
name: self._fields.6.unwrap(),
1638
1638
+
previews: self._fields.7,
1639
1639
+
summary: self._fields.8,
1640
1640
+
tags: self._fields.9,
1641
1641
+
extra_data: Default::default(),
1642
1642
+
}
1643
1643
+
}
1644
1644
+
/// Build the final struct with custom extra_data.
1645
1645
+
pub fn build_with_data(
1646
1646
+
self,
1647
1647
+
extra_data: BTreeMap<SmolStr, Data<S>>,
1648
1648
+
) -> ThingInput<S> {
1649
1649
+
ThingInput {
1650
1650
+
cover: self._fields.0,
1651
1651
+
derived_from: self._fields.1,
1652
1652
+
instructions: self._fields.2,
1653
1653
+
intended_to_work_with: self._fields.3,
1654
1654
+
license: self._fields.4.unwrap(),
1655
1655
+
models: self._fields.5.unwrap(),
1656
1656
+
name: self._fields.6.unwrap(),
1657
1657
+
previews: self._fields.7,
1658
1658
+
summary: self._fields.8,
1659
1659
+
tags: self._fields.9,
1660
1660
+
extra_data: Some(extra_data),
1661
1661
+
}
1662
1662
+
}
1663
1663
+
}
···
12
12
cargo fmt --all
13
13
# Run clippy without --fix; automatic clippy rewrites can corrupt Dioxus RSX.
14
14
lint:
15
15
-
cargo clippy --workspace --all-targets -- -D warnings
16
16
-
cargo clippy -p polymodel --all-targets --features server -- -D warnings
17
17
-
cargo clippy -p polymodel --target wasm32-unknown-unknown --features web -- -D warnings
15
15
+
cargo clippy --workspace --all-targets -- -D warnings -A clippy::useless_format
16
16
+
cargo clippy -p polymodel --all-targets --features server -- -D warnings -A clippy::useless_format
17
17
+
cargo clippy -p polymodel --target wasm32-unknown-unknown --features web -- -D warnings -A clippy::useless_format
18
18
# Compile checks across the whole workspace plus the app's server/wasm targets.
19
19
# The server check resolves compile-time `query!` macros against the committed
20
20
# `.sqlx` offline cache; regenerate it with `just sqlx-prepare` after changing
···
1
1
+
{
2
2
+
"lexicon": 1,
3
3
+
"id": "space.polymodel.actor.bootstrapProfile",
4
4
+
"defs": {
5
5
+
"main": {
6
6
+
"type": "procedure",
7
7
+
"description": "One-time authenticated bootstrap of the actor's Polymodel profile from their own PDS profile records. Creates space.polymodel.actor.profile/self only when absent locally and PDS-side.",
8
8
+
"output": {
9
9
+
"encoding": "application/json",
10
10
+
"schema": {
11
11
+
"type": "object",
12
12
+
"required": ["status", "profile"],
13
13
+
"properties": {
14
14
+
"status": {
15
15
+
"type": "string",
16
16
+
"knownValues": ["existingLocal", "existingRemote", "created"]
17
17
+
},
18
18
+
"uri": { "type": "string", "format": "at-uri" },
19
19
+
"cid": { "type": "string", "format": "cid" },
20
20
+
"profile": { "type": "ref", "ref": "space.polymodel.actor.defs#profileView" }
21
21
+
}
22
22
+
}
23
23
+
}
24
24
+
}
25
25
+
}
26
26
+
}
···
1
1
+
{
2
2
+
"lexicon": 1,
3
3
+
"id": "space.polymodel.graph.createLike",
4
4
+
"defs": {
5
5
+
"main": {
6
6
+
"type": "procedure",
7
7
+
"description": "Create a like record for an indexed Polymodel subject on the authenticated actor's PDS and eagerly project viewer/count state.",
8
8
+
"input": {
9
9
+
"encoding": "application/json",
10
10
+
"schema": {
11
11
+
"type": "object",
12
12
+
"required": ["subject"],
13
13
+
"properties": {
14
14
+
"subject": { "type": "ref", "ref": "com.atproto.repo.strongRef" }
15
15
+
}
16
16
+
}
17
17
+
},
18
18
+
"output": {
19
19
+
"encoding": "application/json",
20
20
+
"schema": {
21
21
+
"type": "object",
22
22
+
"required": ["uri", "cid"],
23
23
+
"properties": {
24
24
+
"uri": { "type": "string", "format": "at-uri" },
25
25
+
"cid": { "type": "string", "format": "cid" },
26
26
+
"created": { "type": "boolean" }
27
27
+
}
28
28
+
}
29
29
+
}
30
30
+
}
31
31
+
}
32
32
+
}
···
1
1
+
{
2
2
+
"lexicon": 1,
3
3
+
"id": "space.polymodel.graph.createSave",
4
4
+
"defs": {
5
5
+
"main": {
6
6
+
"type": "procedure",
7
7
+
"description": "Create a save record for an indexed Polymodel subject on the authenticated actor's PDS and eagerly project viewer/count state.",
8
8
+
"input": {
9
9
+
"encoding": "application/json",
10
10
+
"schema": {
11
11
+
"type": "object",
12
12
+
"required": ["subject"],
13
13
+
"properties": {
14
14
+
"subject": { "type": "ref", "ref": "com.atproto.repo.strongRef" },
15
15
+
"note": { "type": "string", "maxLength": 2000 }
16
16
+
}
17
17
+
}
18
18
+
},
19
19
+
"output": {
20
20
+
"encoding": "application/json",
21
21
+
"schema": {
22
22
+
"type": "object",
23
23
+
"required": ["uri", "cid"],
24
24
+
"properties": {
25
25
+
"uri": { "type": "string", "format": "at-uri" },
26
26
+
"cid": { "type": "string", "format": "cid" },
27
27
+
"created": { "type": "boolean" }
28
28
+
}
29
29
+
}
30
30
+
}
31
31
+
}
32
32
+
}
33
33
+
}
···
1
1
+
{
2
2
+
"lexicon": 1,
3
3
+
"id": "space.polymodel.graph.createTag",
4
4
+
"defs": {
5
5
+
"main": {
6
6
+
"type": "procedure",
7
7
+
"description": "Create a tag assertion for an indexed Polymodel subject on the authenticated actor's PDS and eagerly project viewer/count state.",
8
8
+
"input": {
9
9
+
"encoding": "application/json",
10
10
+
"schema": {
11
11
+
"type": "object",
12
12
+
"required": ["subject", "tag"],
13
13
+
"properties": {
14
14
+
"subject": { "type": "ref", "ref": "com.atproto.repo.strongRef" },
15
15
+
"tag": { "type": "string", "maxGraphemes": 32, "maxLength": 64 }
16
16
+
}
17
17
+
}
18
18
+
},
19
19
+
"output": {
20
20
+
"encoding": "application/json",
21
21
+
"schema": {
22
22
+
"type": "object",
23
23
+
"required": ["uri", "cid"],
24
24
+
"properties": {
25
25
+
"uri": { "type": "string", "format": "at-uri" },
26
26
+
"cid": { "type": "string", "format": "cid" },
27
27
+
"created": { "type": "boolean" }
28
28
+
}
29
29
+
}
30
30
+
}
31
31
+
}
32
32
+
}
33
33
+
}
···
1
1
+
{
2
2
+
"lexicon": 1,
3
3
+
"id": "space.polymodel.graph.deleteLike",
4
4
+
"defs": {
5
5
+
"main": {
6
6
+
"type": "procedure",
7
7
+
"description": "Delete the authenticated actor's like by subject or like record URI, then eagerly project viewer/count state.",
8
8
+
"input": {
9
9
+
"encoding": "application/json",
10
10
+
"schema": {
11
11
+
"type": "object",
12
12
+
"properties": {
13
13
+
"subject": { "type": "ref", "ref": "com.atproto.repo.strongRef" },
14
14
+
"uri": { "type": "string", "format": "at-uri" }
15
15
+
}
16
16
+
}
17
17
+
},
18
18
+
"output": {
19
19
+
"encoding": "application/json",
20
20
+
"schema": {
21
21
+
"type": "object",
22
22
+
"required": ["deleted"],
23
23
+
"properties": {
24
24
+
"deleted": { "type": "boolean" },
25
25
+
"uri": { "type": "string", "format": "at-uri" }
26
26
+
}
27
27
+
}
28
28
+
}
29
29
+
}
30
30
+
}
31
31
+
}
···
1
1
+
{
2
2
+
"lexicon": 1,
3
3
+
"id": "space.polymodel.graph.deleteSave",
4
4
+
"defs": {
5
5
+
"main": {
6
6
+
"type": "procedure",
7
7
+
"description": "Delete the authenticated actor's save by subject or save record URI, then eagerly project viewer/count state.",
8
8
+
"input": {
9
9
+
"encoding": "application/json",
10
10
+
"schema": {
11
11
+
"type": "object",
12
12
+
"properties": {
13
13
+
"subject": { "type": "ref", "ref": "com.atproto.repo.strongRef" },
14
14
+
"uri": { "type": "string", "format": "at-uri" }
15
15
+
}
16
16
+
}
17
17
+
},
18
18
+
"output": {
19
19
+
"encoding": "application/json",
20
20
+
"schema": {
21
21
+
"type": "object",
22
22
+
"required": ["deleted"],
23
23
+
"properties": {
24
24
+
"deleted": { "type": "boolean" },
25
25
+
"uri": { "type": "string", "format": "at-uri" }
26
26
+
}
27
27
+
}
28
28
+
}
29
29
+
}
30
30
+
}
31
31
+
}
···
1
1
+
{
2
2
+
"lexicon": 1,
3
3
+
"id": "space.polymodel.graph.deleteTag",
4
4
+
"defs": {
5
5
+
"main": {
6
6
+
"type": "procedure",
7
7
+
"description": "Delete a specific tag assertion record for the authenticated actor, then eagerly project viewer/count state. Tag delete requires a concrete tag record URI because tags are rkey-idempotent, not unique by subject+tag.",
8
8
+
"input": {
9
9
+
"encoding": "application/json",
10
10
+
"schema": {
11
11
+
"type": "object",
12
12
+
"required": ["uri"],
13
13
+
"properties": {
14
14
+
"uri": { "type": "string", "format": "at-uri" }
15
15
+
}
16
16
+
}
17
17
+
},
18
18
+
"output": {
19
19
+
"encoding": "application/json",
20
20
+
"schema": {
21
21
+
"type": "object",
22
22
+
"required": ["deleted"],
23
23
+
"properties": {
24
24
+
"deleted": { "type": "boolean" },
25
25
+
"uri": { "type": "string", "format": "at-uri" }
26
26
+
}
27
27
+
}
28
28
+
}
29
29
+
}
30
30
+
}
31
31
+
}
···
1
1
+
{
2
2
+
"lexicon": 1,
3
3
+
"id": "space.polymodel.library.deleteThing",
4
4
+
"defs": {
5
5
+
"main": {
6
6
+
"type": "procedure",
7
7
+
"description": "Delete an owned thing and its owned model/part composition records from the authenticated actor's PDS, then eagerly remove local projections.",
8
8
+
"input": {
9
9
+
"encoding": "application/json",
10
10
+
"schema": {
11
11
+
"type": "object",
12
12
+
"required": ["uri"],
13
13
+
"properties": {
14
14
+
"uri": { "type": "string", "format": "at-uri" }
15
15
+
}
16
16
+
}
17
17
+
},
18
18
+
"output": {
19
19
+
"encoding": "application/json",
20
20
+
"schema": {
21
21
+
"type": "object",
22
22
+
"required": ["deleted"],
23
23
+
"properties": {
24
24
+
"deleted": { "type": "boolean" },
25
25
+
"deletedUris": { "type": "array", "items": { "type": "string", "format": "at-uri" } }
26
26
+
}
27
27
+
}
28
28
+
}
29
29
+
}
30
30
+
}
31
31
+
}
···
1
1
+
{
2
2
+
"lexicon": 1,
3
3
+
"id": "space.polymodel.library.publishThing",
4
4
+
"defs": {
5
5
+
"main": {
6
6
+
"type": "procedure",
7
7
+
"description": "Publish a thing by creating part records first, model records second, and the thing record last on the authenticated actor's PDS, then eagerly projecting every successful record locally.",
8
8
+
"input": {
9
9
+
"encoding": "application/json",
10
10
+
"schema": {
11
11
+
"type": "object",
12
12
+
"required": ["thing"],
13
13
+
"properties": {
14
14
+
"thing": { "type": "ref", "ref": "#thingInput" }
15
15
+
}
16
16
+
}
17
17
+
},
18
18
+
"output": {
19
19
+
"encoding": "application/json",
20
20
+
"schema": {
21
21
+
"type": "object",
22
22
+
"required": ["thing", "models"],
23
23
+
"properties": {
24
24
+
"thing": { "type": "ref", "ref": "#recordRef" },
25
25
+
"models": { "type": "array", "items": { "type": "ref", "ref": "#modelPublishResult" } }
26
26
+
}
27
27
+
}
28
28
+
}
29
29
+
},
30
30
+
"partInput": {
31
31
+
"type": "object",
32
32
+
"required": ["name"],
33
33
+
"properties": {
34
34
+
"name": { "type": "string", "maxGraphemes": 64, "maxLength": 256 },
35
35
+
"file": { "type": "ref", "ref": "space.polymodel.library.defs#file" },
36
36
+
"uploadId": { "type": "string" },
37
37
+
"format": { "type": "string" },
38
38
+
"dimensions": { "type": "ref", "ref": "space.polymodel.library.defs#bbox" },
39
39
+
"units": { "type": "string" },
40
40
+
"notes": { "type": "string", "maxGraphemes": 300, "maxLength": 2000 },
41
41
+
"printSettings": { "type": "array", "items": { "type": "string" }, "maxLength": 50 },
42
42
+
"previews": { "type": "array", "items": { "type": "ref", "ref": "space.polymodel.library.defs#image" }, "maxLength": 12 },
43
43
+
"derivedFrom": { "type": "ref", "ref": "com.atproto.repo.strongRef" },
44
44
+
"intendedToWorkWith": { "type": "array", "items": { "type": "ref", "ref": "com.atproto.repo.strongRef" }, "maxLength": 32 }
45
45
+
}
46
46
+
},
47
47
+
"modelInput": {
48
48
+
"type": "object",
49
49
+
"required": ["name", "parts"],
50
50
+
"properties": {
51
51
+
"name": { "type": "string", "maxGraphemes": 64, "maxLength": 256 },
52
52
+
"summary": { "type": "string", "maxGraphemes": 300, "maxLength": 2000 },
53
53
+
"instructions": { "type": "array", "items": { "type": "string" }, "maxLength": 100 },
54
54
+
"license": { "type": "string" },
55
55
+
"tags": { "type": "array", "items": { "type": "string", "maxGraphemes": 32, "maxLength": 64 }, "maxLength": 16 },
56
56
+
"cover": { "type": "array", "items": { "type": "ref", "ref": "space.polymodel.library.defs#image" }, "maxLength": 8 },
57
57
+
"previews": { "type": "array", "items": { "type": "ref", "ref": "space.polymodel.library.defs#image" }, "maxLength": 12 },
58
58
+
"parts": { "type": "array", "items": { "type": "ref", "ref": "#partInput" }, "maxLength": 1024 },
59
59
+
"derivedFrom": { "type": "ref", "ref": "com.atproto.repo.strongRef" },
60
60
+
"intendedToWorkWith": { "type": "array", "items": { "type": "ref", "ref": "com.atproto.repo.strongRef" }, "maxLength": 32 }
61
61
+
}
62
62
+
},
63
63
+
"thingInput": {
64
64
+
"type": "object",
65
65
+
"required": ["name", "license", "models"],
66
66
+
"properties": {
67
67
+
"name": { "type": "string", "maxGraphemes": 64, "maxLength": 256 },
68
68
+
"summary": { "type": "string", "maxGraphemes": 300, "maxLength": 2000 },
69
69
+
"instructions": { "type": "array", "items": { "type": "string" }, "maxLength": 100 },
70
70
+
"license": { "type": "string" },
71
71
+
"tags": { "type": "array", "items": { "type": "string", "maxGraphemes": 32, "maxLength": 64 }, "maxLength": 16 },
72
72
+
"cover": { "type": "array", "items": { "type": "ref", "ref": "space.polymodel.library.defs#image" }, "maxLength": 8 },
73
73
+
"previews": { "type": "array", "items": { "type": "ref", "ref": "space.polymodel.library.defs#image" }, "maxLength": 12 },
74
74
+
"models": { "type": "array", "items": { "type": "ref", "ref": "#modelInput" }, "maxLength": 256 },
75
75
+
"derivedFrom": { "type": "ref", "ref": "com.atproto.repo.strongRef" },
76
76
+
"intendedToWorkWith": { "type": "array", "items": { "type": "ref", "ref": "com.atproto.repo.strongRef" }, "maxLength": 32 }
77
77
+
}
78
78
+
},
79
79
+
"recordRef": {
80
80
+
"type": "object",
81
81
+
"required": ["uri", "cid"],
82
82
+
"properties": {
83
83
+
"uri": { "type": "string", "format": "at-uri" },
84
84
+
"cid": { "type": "string", "format": "cid" }
85
85
+
}
86
86
+
},
87
87
+
"modelPublishResult": {
88
88
+
"type": "object",
89
89
+
"required": ["model", "parts"],
90
90
+
"properties": {
91
91
+
"model": { "type": "ref", "ref": "#recordRef" },
92
92
+
"parts": { "type": "array", "items": { "type": "ref", "ref": "#recordRef" } }
93
93
+
}
94
94
+
}
95
95
+
}
96
96
+
}
···
1
1
+
{
2
2
+
"lexicon": 1,
3
3
+
"id": "space.polymodel.library.stageFile",
4
4
+
"defs": {
5
5
+
"main": {
6
6
+
"type": "procedure",
7
7
+
"description": "Stage one raw geometry payload for publishing. The HTTP body is raw bytes, not base64 JSON; metadata is provided by headers. The server chunks and uploads blobs through the authenticated actor's PDS, stores owner-scoped staging metadata, and returns defs#file-compatible metadata.",
8
8
+
"input": {
9
9
+
"encoding": "*/*"
10
10
+
},
11
11
+
"output": {
12
12
+
"encoding": "application/json",
13
13
+
"schema": {
14
14
+
"type": "object",
15
15
+
"required": ["uploadId", "file"],
16
16
+
"properties": {
17
17
+
"uploadId": { "type": "string" },
18
18
+
"file": { "type": "ref", "ref": "space.polymodel.library.defs#file" },
19
19
+
"filename": { "type": "string" },
20
20
+
"status": { "type": "string", "knownValues": ["uploaded"] }
21
21
+
}
22
22
+
}
23
23
+
}
24
24
+
}
25
25
+
}
26
26
+
}
···
1
1
+
{
2
2
+
"lexicon": 1,
3
3
+
"id": "space.polymodel.library.updateThing",
4
4
+
"defs": {
5
5
+
"main": {
6
6
+
"type": "procedure",
7
7
+
"description": "Update an owned thing composition with updated thing/model/part data. Existing model/part inputs with uri are updated in place; inputs without uri create new records. Removed child records are detached from the updated parent records but not deleted, so they can be restored later.",
8
8
+
"input": {
9
9
+
"encoding": "application/json",
10
10
+
"schema": {
11
11
+
"type": "object",
12
12
+
"required": ["uri", "thing"],
13
13
+
"properties": {
14
14
+
"uri": { "type": "string", "format": "at-uri" },
15
15
+
"thing": { "type": "ref", "ref": "#thingInput" }
16
16
+
}
17
17
+
}
18
18
+
},
19
19
+
"output": {
20
20
+
"encoding": "application/json",
21
21
+
"schema": {
22
22
+
"type": "object",
23
23
+
"required": ["thing", "models"],
24
24
+
"properties": {
25
25
+
"thing": { "type": "ref", "ref": "space.polymodel.library.publishThing#recordRef" },
26
26
+
"models": { "type": "array", "items": { "type": "ref", "ref": "space.polymodel.library.publishThing#modelPublishResult" } }
27
27
+
}
28
28
+
}
29
29
+
}
30
30
+
},
31
31
+
"partInput": {
32
32
+
"type": "object",
33
33
+
"required": ["name"],
34
34
+
"properties": {
35
35
+
"uri": { "type": "string", "format": "at-uri" },
36
36
+
"name": { "type": "string", "maxGraphemes": 64, "maxLength": 256 },
37
37
+
"file": { "type": "ref", "ref": "space.polymodel.library.defs#file" },
38
38
+
"uploadId": { "type": "string" },
39
39
+
"format": { "type": "string" },
40
40
+
"dimensions": { "type": "ref", "ref": "space.polymodel.library.defs#bbox" },
41
41
+
"units": { "type": "string" },
42
42
+
"notes": { "type": "string", "maxGraphemes": 300, "maxLength": 2000 },
43
43
+
"printSettings": { "type": "array", "items": { "type": "string" }, "maxLength": 50 },
44
44
+
"previews": { "type": "array", "items": { "type": "ref", "ref": "space.polymodel.library.defs#image" }, "maxLength": 12 },
45
45
+
"derivedFrom": { "type": "ref", "ref": "com.atproto.repo.strongRef" },
46
46
+
"intendedToWorkWith": { "type": "array", "items": { "type": "ref", "ref": "com.atproto.repo.strongRef" }, "maxLength": 32 }
47
47
+
}
48
48
+
},
49
49
+
"modelInput": {
50
50
+
"type": "object",
51
51
+
"required": ["name", "parts"],
52
52
+
"properties": {
53
53
+
"uri": { "type": "string", "format": "at-uri" },
54
54
+
"name": { "type": "string", "maxGraphemes": 64, "maxLength": 256 },
55
55
+
"summary": { "type": "string", "maxGraphemes": 300, "maxLength": 2000 },
56
56
+
"instructions": { "type": "array", "items": { "type": "string" }, "maxLength": 100 },
57
57
+
"license": { "type": "string" },
58
58
+
"tags": { "type": "array", "items": { "type": "string", "maxGraphemes": 32, "maxLength": 64 }, "maxLength": 16 },
59
59
+
"cover": { "type": "array", "items": { "type": "ref", "ref": "space.polymodel.library.defs#image" }, "maxLength": 8 },
60
60
+
"previews": { "type": "array", "items": { "type": "ref", "ref": "space.polymodel.library.defs#image" }, "maxLength": 12 },
61
61
+
"parts": { "type": "array", "items": { "type": "ref", "ref": "#partInput" }, "maxLength": 1024 },
62
62
+
"derivedFrom": { "type": "ref", "ref": "com.atproto.repo.strongRef" },
63
63
+
"intendedToWorkWith": { "type": "array", "items": { "type": "ref", "ref": "com.atproto.repo.strongRef" }, "maxLength": 32 }
64
64
+
}
65
65
+
},
66
66
+
"thingInput": {
67
67
+
"type": "object",
68
68
+
"required": ["name", "license", "models"],
69
69
+
"properties": {
70
70
+
"name": { "type": "string", "maxGraphemes": 64, "maxLength": 256 },
71
71
+
"summary": { "type": "string", "maxGraphemes": 300, "maxLength": 2000 },
72
72
+
"instructions": { "type": "array", "items": { "type": "string" }, "maxLength": 100 },
73
73
+
"license": { "type": "string" },
74
74
+
"tags": { "type": "array", "items": { "type": "string", "maxGraphemes": 32, "maxLength": 64 }, "maxLength": 16 },
75
75
+
"cover": { "type": "array", "items": { "type": "ref", "ref": "space.polymodel.library.defs#image" }, "maxLength": 8 },
76
76
+
"previews": { "type": "array", "items": { "type": "ref", "ref": "space.polymodel.library.defs#image" }, "maxLength": 12 },
77
77
+
"models": { "type": "array", "items": { "type": "ref", "ref": "#modelInput" }, "maxLength": 256 },
78
78
+
"derivedFrom": { "type": "ref", "ref": "com.atproto.repo.strongRef" },
79
79
+
"intendedToWorkWith": { "type": "array", "items": { "type": "ref", "ref": "com.atproto.repo.strongRef" }, "maxLength": 32 }
80
80
+
}
81
81
+
}
82
82
+
}
83
83
+
}
···
1
1
+
-- PM-28: owner-scoped upload staging for raw geometry bytes.
2
2
+
--
3
3
+
-- stageFile writes blobs to the authenticated actor's PDS first, then stores the
4
4
+
-- reassembly metadata needed by publishThing to build space.polymodel.library.defs#file.
5
5
+
CREATE TABLE upload_staging (
6
6
+
owner_did TEXT NOT NULL,
7
7
+
upload_id TEXT NOT NULL,
8
8
+
sha256 TEXT NOT NULL,
9
9
+
mime_type TEXT NOT NULL,
10
10
+
size INTEGER NOT NULL,
11
11
+
filename TEXT,
12
12
+
status TEXT NOT NULL,
13
13
+
file_json TEXT NOT NULL,
14
14
+
chunks_json TEXT NOT NULL,
15
15
+
created_at INTEGER NOT NULL,
16
16
+
updated_at INTEGER NOT NULL,
17
17
+
PRIMARY KEY (owner_did, upload_id)
18
18
+
);
19
19
+
20
20
+
CREATE INDEX idx_upload_staging_owner_updated
21
21
+
ON upload_staging(owner_did, updated_at DESC);
···
1
1
+
-- Store CIDs on all social record projection tables. Existing rows cannot be
2
2
+
-- backfilled because previous schema versions did not store record CIDs; rebuild
3
3
+
-- these derived tables instead of introducing placeholder CIDs. Hydrant/firehose
4
4
+
-- can replay social records, and eager writes after this migration preserve CIDs.
5
5
+
6
6
+
DROP TABLE likes;
7
7
+
CREATE TABLE likes (
8
8
+
did TEXT NOT NULL,
9
9
+
rkey TEXT NOT NULL,
10
10
+
cid TEXT NOT NULL,
11
11
+
subject_uri TEXT NOT NULL,
12
12
+
created_at INTEGER NOT NULL,
13
13
+
PRIMARY KEY (did, rkey),
14
14
+
UNIQUE (did, subject_uri)
15
15
+
);
16
16
+
CREATE INDEX idx_likes_subject ON likes(subject_uri);
17
17
+
18
18
+
DROP TABLE saves;
19
19
+
CREATE TABLE saves (
20
20
+
did TEXT NOT NULL,
21
21
+
rkey TEXT NOT NULL,
22
22
+
cid TEXT NOT NULL,
23
23
+
subject_uri TEXT NOT NULL,
24
24
+
note TEXT,
25
25
+
created_at INTEGER NOT NULL,
26
26
+
PRIMARY KEY (did, rkey),
27
27
+
UNIQUE (did, subject_uri)
28
28
+
);
29
29
+
CREATE INDEX idx_saves_subject ON saves(subject_uri);
30
30
+
31
31
+
DROP TABLE tags;
32
32
+
CREATE TABLE tags (
33
33
+
did TEXT NOT NULL,
34
34
+
rkey TEXT NOT NULL,
35
35
+
cid TEXT NOT NULL,
36
36
+
subject_uri TEXT NOT NULL,
37
37
+
tag TEXT NOT NULL,
38
38
+
created_at INTEGER NOT NULL,
39
39
+
PRIMARY KEY (did, rkey)
40
40
+
);
41
41
+
CREATE INDEX idx_tags_subject ON tags(subject_uri);
42
42
+
CREATE INDEX idx_tags_tag ON tags(tag);
43
43
+
44
44
+
DROP TABLE listitems;
45
45
+
CREATE TABLE listitems (
46
46
+
did TEXT NOT NULL,
47
47
+
rkey TEXT NOT NULL,
48
48
+
cid TEXT NOT NULL,
49
49
+
list_uri TEXT NOT NULL,
50
50
+
subject_uri TEXT NOT NULL,
51
51
+
created_at INTEGER NOT NULL,
52
52
+
PRIMARY KEY (did, rkey)
53
53
+
);
54
54
+
CREATE INDEX idx_listitems_list ON listitems(list_uri, created_at);
55
55
+
56
56
+
UPDATE content_stats SET like_count = 0, save_count = 0, tag_count = 0;
···
13
13
/// Appview handler error. Maps onto the XRPC error response shape.
14
14
#[derive(Debug)]
15
15
pub(crate) enum AppError {
16
16
+
/// 401 `AuthenticationRequired`: strict OAuth extraction failed or an
17
17
+
/// authenticated PDS operation reported an auth failure.
18
18
+
Unauthorized(String),
16
19
/// 400 `InvalidRequest`: bad parameters (bad at-uri, unknown algorithm,
17
20
/// unparseable cursor, unresolvable handle).
18
21
InvalidRequest(String),
···
27
30
28
31
pub(super) fn invalid_request(msg: impl Into<String>) -> AppError {
29
32
AppError::InvalidRequest(msg.into())
33
33
+
}
34
34
+
35
35
+
pub(super) fn unauthorized(msg: impl Into<String>) -> AppError {
36
36
+
AppError::Unauthorized(msg.into())
30
37
}
31
38
32
39
pub(super) fn not_found() -> AppError {
···
53
60
impl IntoResponse for AppError {
54
61
fn into_response(self) -> Response {
55
62
let (status, code, message) = match self {
63
63
+
AppError::Unauthorized(m) => {
64
64
+
(StatusCode::UNAUTHORIZED, "AuthenticationRequired", Some(m))
65
65
+
}
56
66
AppError::InvalidRequest(m) => (StatusCode::BAD_REQUEST, "InvalidRequest", Some(m)),
57
67
AppError::NotFound => (StatusCode::NOT_FOUND, "RecordNotFound", None),
58
68
AppError::Internal(m) => (
···
16
16
mod actor;
17
17
mod graph;
18
18
mod library;
19
19
+
mod writes;
19
20
20
21
#[cfg(test)]
21
22
mod tests;
···
24
25
use jacquard_axum::IntoRouter;
25
26
use jacquard_common::xrpc::XrpcEndpoint;
26
27
use polymodel_api::space_polymodel::{
27
27
-
actor::{get_profile::GetProfileRequest, get_session::GetSessionRequest},
28
28
-
graph::get_list::GetListRequest,
28
28
+
actor::{
29
29
+
bootstrap_profile::BootstrapProfileRequest, get_profile::GetProfileRequest,
30
30
+
get_session::GetSessionRequest,
31
31
+
},
32
32
+
graph::{
33
33
+
create_like::CreateLikeRequest, create_save::CreateSaveRequest,
34
34
+
create_tag::CreateTagRequest, delete_like::DeleteLikeRequest,
35
35
+
delete_save::DeleteSaveRequest, delete_tag::DeleteTagRequest, get_list::GetListRequest,
36
36
+
},
29
37
library::{
30
30
-
get_author_things::GetAuthorThingsRequest, get_feed::GetFeedRequest,
31
31
-
get_model::GetModelRequest, get_thing::GetThingRequest, search_things::SearchThingsRequest,
38
38
+
delete_thing::DeleteThingRequest, get_author_things::GetAuthorThingsRequest,
39
39
+
get_feed::GetFeedRequest, get_model::GetModelRequest, get_thing::GetThingRequest,
40
40
+
publish_thing::PublishThingRequest, search_things::SearchThingsRequest,
41
41
+
stage_file::StageFileRequest, update_thing::UpdateThingRequest,
32
42
},
33
43
};
34
44
···
48
58
.merge(SearchThingsRequest::into_router(library::search_things))
49
59
.merge(GetListRequest::into_router(graph::get_list))
50
60
.merge(GetProfileRequest::into_router(actor::get_profile))
61
61
+
.merge(BootstrapProfileRequest::into_router(
62
62
+
writes::bootstrap_profile,
63
63
+
))
64
64
+
.route(
65
65
+
StageFileRequest::PATH,
66
66
+
axum::routing::post(writes::stage_file),
67
67
+
)
68
68
+
.merge(PublishThingRequest::into_router(writes::publish_thing))
69
69
+
.merge(UpdateThingRequest::into_router(writes::update_thing))
70
70
+
.merge(DeleteThingRequest::into_router(writes::delete_thing))
71
71
+
.merge(CreateLikeRequest::into_router(writes::create_like))
72
72
+
.merge(DeleteLikeRequest::into_router(writes::delete_like))
73
73
+
.merge(CreateSaveRequest::into_router(writes::create_save))
74
74
+
.merge(DeleteSaveRequest::into_router(writes::delete_save))
75
75
+
.merge(CreateTagRequest::into_router(writes::create_tag))
76
76
+
.merge(DeleteTagRequest::into_router(writes::delete_tag))
51
77
// getSession takes no parameters, so it bypasses ExtractXrpc (which
52
78
// decodes the query string; a unit-struct request from an empty query is
53
79
// rejected by serde_html_form). Identity comes from the session.
···
22
22
use jacquard::oauth::client::OAuthClient;
23
23
use jacquard_axum::oauth::{OAuthWebConfig, OAuthWebState};
24
24
use sqlx::SqlitePool;
25
25
+
use tokio::sync::Mutex;
25
26
26
27
use crate::oauth::{OAuthBootstrap, SqliteAuthStore};
27
28
···
42
43
pub oauth_config: OAuthWebConfig,
43
44
/// Signing key for the private `jacquard_oauth_session` cookie jar.
44
45
pub cookie_key: Key,
46
46
+
/// Serializes server-mediated writes that make decisions from local
47
47
+
/// projection state before issuing PDS mutations. This avoids app-process
48
48
+
/// TOCTOU races such as deleting a child record while another PM-28 write is
49
49
+
/// attaching it elsewhere.
50
50
+
pub write_lock: Arc<Mutex<()>>,
45
51
}
46
52
47
53
impl AppState {
···
55
61
oauth: Arc::new(bootstrap.client),
56
62
oauth_config: bootstrap.config,
57
63
cookie_key: bootstrap.cookie_key,
64
64
+
write_lock: Arc::new(Mutex::new(())),
58
65
}
59
66
}
60
67
}
···
396
396
let thing = seed_thing(&state.pool, DID_A, "t1", "Thing", &[], 0).await;
397
397
let list = "at://did:plc:l/space.polymodel.graph.list/self";
398
398
let non_thing = "at://did:plc:x/space.polymodel.graph.like/k1";
399
399
-
sqlx::query("INSERT INTO listitems (did, rkey, list_uri, subject_uri, created_at) VALUES (?, ?, ?, ?, ?)")
400
400
-
.bind(DID_A).bind("i1").bind(list).bind(&thing).bind(1)
399
399
+
sqlx::query("INSERT INTO listitems (did, rkey, cid, list_uri, subject_uri, created_at) VALUES (?, ?, ?, ?, ?, ?)")
400
400
+
.bind(DID_A).bind("i1").bind("bafyreilistitemone").bind(list).bind(&thing).bind(1)
401
401
.execute(&state.pool).await.unwrap();
402
402
-
sqlx::query("INSERT INTO listitems (did, rkey, list_uri, subject_uri, created_at) VALUES (?, ?, ?, ?, ?)")
403
403
-
.bind(DID_A).bind("i2").bind(list).bind(non_thing).bind(2)
402
402
+
sqlx::query("INSERT INTO listitems (did, rkey, cid, list_uri, subject_uri, created_at) VALUES (?, ?, ?, ?, ?, ?)")
403
403
+
.bind(DID_A).bind("i2").bind("bafyreilistitemtwo").bind(list).bind(non_thing).bind(2)
404
404
.execute(&state.pool).await.unwrap();
405
405
406
406
let (items, _cursor) = views::list_things(&state, list, 10, None, None)
···
504
504
.await
505
505
.unwrap();
506
506
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
507
507
+
}
508
508
+
509
509
+
#[tokio::test]
510
510
+
async fn unauthenticated_write_routes_return_xrpc_401_not_redirects() {
511
511
+
let state = state().await;
512
512
+
let app = super::router().with_state(state);
513
513
+
let routes = [
514
514
+
(
515
515
+
"/xrpc/space.polymodel.actor.bootstrapProfile",
516
516
+
"application/json",
517
517
+
"{}",
518
518
+
),
519
519
+
(
520
520
+
"/xrpc/space.polymodel.library.stageFile",
521
521
+
"model/stl",
522
522
+
"solid cube",
523
523
+
),
524
524
+
(
525
525
+
"/xrpc/space.polymodel.graph.createLike",
526
526
+
"application/json",
527
527
+
r#"{"subject":{"uri":"at://did:plc:aaaaaaaaaaaaaaaaaaaaaaaa/space.polymodel.library.thing/t1","cid":"bafycid"}}"#,
528
528
+
),
529
529
+
];
530
530
+
531
531
+
for (path, content_type, body) in routes {
532
532
+
let resp = app
533
533
+
.clone()
534
534
+
.oneshot(
535
535
+
Request::builder()
536
536
+
.method("POST")
537
537
+
.uri(path)
538
538
+
.header("content-type", content_type)
539
539
+
.body(Body::from(body))
540
540
+
.unwrap(),
541
541
+
)
542
542
+
.await
543
543
+
.unwrap();
544
544
+
assert_eq!(resp.status(), StatusCode::UNAUTHORIZED, "route {path}");
545
545
+
assert!(
546
546
+
resp.headers().get("location").is_none(),
547
547
+
"strict XRPC auth must not redirect for {path}"
548
548
+
);
549
549
+
let body = body_text(resp).await;
550
550
+
assert!(body.contains("AuthenticationRequired"), "body was: {body}");
551
551
+
}
507
552
}
508
553
509
554
#[tokio::test]
···
1
1
+
//! PM-28 authenticated write endpoints.
2
2
+
//!
3
3
+
//! These XRPC routes deliberately use strict OAuth extraction, not the browser
4
4
+
//! redirect extractor. The write order is PDS-first: only after the authenticated
5
5
+
//! actor's PDS accepts a record/blob operation do we eagerly project into SQLite
6
6
+
//! for read-your-own-writes. Hydrant/firehose will later deliver the same event;
7
7
+
//! projection reuse keeps convergence idempotent.
8
8
+
9
9
+
use axum::Json;
10
10
+
use axum::body::Body;
11
11
+
use axum::extract::State;
12
12
+
use axum::http::{HeaderMap, header};
13
13
+
use http_body_util::BodyExt;
14
14
+
use jacquard::client::{Agent, AgentSession, AgentSessionExt};
15
15
+
use jacquard::identity::PublicResolver;
16
16
+
use jacquard::oauth::client::OAuthSession;
17
17
+
use jacquard_axum::oauth::ExtractOAuthSession;
18
18
+
use jacquard_axum::{ExtractXrpc, XrpcResponse};
19
19
+
use jacquard_common::deps::smol_str::SmolStr;
20
20
+
use jacquard_common::types::blob::{BlobRef, MimeType};
21
21
+
use jacquard_common::types::collection::Collection;
22
22
+
use jacquard_common::types::ident::AtIdentifier;
23
23
+
use jacquard_common::types::recordkey::{RecordKey, Rkey};
24
24
+
use jacquard_common::types::string::{AtUri, Cid, Datetime, Did};
25
25
+
use polymodel_api::app_bsky::actor::profile::Profile as BskyProfile;
26
26
+
use polymodel_api::com_atproto::repo::strong_ref::StrongRef;
27
27
+
use polymodel_api::space_polymodel::actor::ProfileView;
28
28
+
use polymodel_api::space_polymodel::actor::bootstrap_profile::{
29
29
+
BootstrapProfileOutput, BootstrapProfileOutputStatus, BootstrapProfileRequest,
30
30
+
};
31
31
+
use polymodel_api::space_polymodel::actor::profile::Profile as PolymodelProfile;
32
32
+
use polymodel_api::space_polymodel::graph::create_like::{CreateLikeOutput, CreateLikeRequest};
33
33
+
use polymodel_api::space_polymodel::graph::create_save::{CreateSaveOutput, CreateSaveRequest};
34
34
+
use polymodel_api::space_polymodel::graph::create_tag::{CreateTagOutput, CreateTagRequest};
35
35
+
use polymodel_api::space_polymodel::graph::delete_like::{DeleteLikeOutput, DeleteLikeRequest};
36
36
+
use polymodel_api::space_polymodel::graph::delete_save::{DeleteSaveOutput, DeleteSaveRequest};
37
37
+
use polymodel_api::space_polymodel::graph::delete_tag::{DeleteTagOutput, DeleteTagRequest};
38
38
+
use polymodel_api::space_polymodel::graph::like::Like;
39
39
+
use polymodel_api::space_polymodel::graph::save::Save;
40
40
+
use polymodel_api::space_polymodel::graph::tag::Tag;
41
41
+
use polymodel_api::space_polymodel::library::delete_thing::{
42
42
+
DeleteThingOutput, DeleteThingRequest,
43
43
+
};
44
44
+
use polymodel_api::space_polymodel::library::publish_thing::{
45
45
+
ModelInput, ModelPublishResult, PublishThingOutput, PublishThingRequest, RecordRef, ThingInput,
46
46
+
};
47
47
+
use polymodel_api::space_polymodel::library::stage_file::{StageFileOutput, StageFileOutputStatus};
48
48
+
use polymodel_api::space_polymodel::library::update_thing::{
49
49
+
ModelInput as UpdateModelInput, PartInput as UpdatePartInput, ThingInput as UpdateThingInput,
50
50
+
UpdateThingOutput, UpdateThingRequest,
51
51
+
};
52
52
+
use polymodel_api::space_polymodel::library::{Chunk, File};
53
53
+
use polymodel_api::space_polymodel::library::{model::Model, part::Part, thing::Thing};
54
54
+
use serde::Serialize;
55
55
+
use sha2::{Digest, Sha256};
56
56
+
use std::collections::BTreeSet;
57
57
+
58
58
+
use super::error::{AppError, AppResult, db, internal, invalid_request, unauthorized};
59
59
+
use super::state::AppState;
60
60
+
use super::views;
61
61
+
use crate::indexing::projection::{ProjectionEvent, project_eager_record};
62
62
+
use crate::oauth::SqliteAuthStore;
63
63
+
64
64
+
const PROFILE_COLLECTION: &str = "space.polymodel.actor.profile";
65
65
+
const THING_COLLECTION: &str = "space.polymodel.library.thing";
66
66
+
const MODEL_COLLECTION: &str = "space.polymodel.library.model";
67
67
+
const PART_COLLECTION: &str = "space.polymodel.library.part";
68
68
+
const LIKE_COLLECTION: &str = "space.polymodel.graph.like";
69
69
+
const SAVE_COLLECTION: &str = "space.polymodel.graph.save";
70
70
+
const TAG_COLLECTION: &str = "space.polymodel.graph.tag";
71
71
+
const CHUNK_SIZE: usize = 10 * 1024 * 1024;
72
72
+
const MAX_STAGE_FILE_SIZE: usize = 100 * 1024 * 1024;
73
73
+
74
74
+
type UserAgent = Agent<OAuthSession<PublicResolver, SqliteAuthStore>>;
75
75
+
76
76
+
pub(super) async fn bootstrap_profile(
77
77
+
State(state): State<AppState>,
78
78
+
ExtractOAuthSession(session): ExtractOAuthSession<PublicResolver, SqliteAuthStore>,
79
79
+
) -> AppResult<XrpcResponse<BootstrapProfileRequest>> {
80
80
+
let agent = Agent::from(session);
81
81
+
let actor = authenticated_did(&agent).await?;
82
82
+
let outcome = ensure_polymodel_profile(&state, &agent, &actor, true).await?;
83
83
+
Ok(XrpcResponse(BootstrapProfileOutput {
84
84
+
cid: outcome.cid,
85
85
+
profile: outcome.profile,
86
86
+
status: outcome.status,
87
87
+
uri: outcome.uri,
88
88
+
extra_data: None,
89
89
+
}))
90
90
+
}
91
91
+
92
92
+
pub(super) async fn stage_file(
93
93
+
State(state): State<AppState>,
94
94
+
ExtractOAuthSession(session): ExtractOAuthSession<PublicResolver, SqliteAuthStore>,
95
95
+
headers: HeaderMap,
96
96
+
body: Body,
97
97
+
) -> AppResult<Json<StageFileOutput>> {
98
98
+
let agent = Agent::from(session);
99
99
+
let actor = authenticated_did(&agent).await?;
100
100
+
let _ = ensure_polymodel_profile(&state, &agent, &actor, false).await?;
101
101
+
102
102
+
let mime_type = content_type(&headers);
103
103
+
let filename = filename_header(&headers);
104
104
+
let staged = upload_streamed_file(&agent, body, mime_type.as_str()).await?;
105
105
+
106
106
+
let file = File {
107
107
+
chunks: staged.chunks,
108
108
+
digest: Some(SmolStr::from(staged.digest.clone())),
109
109
+
mime_type: SmolStr::from(mime_type.clone()),
110
110
+
size: staged.size as i64,
111
111
+
extra_data: None,
112
112
+
};
113
113
+
let upload_id = format!("{}-{}", &staged.digest[..16], staged.size);
114
114
+
persist_upload(
115
115
+
&state,
116
116
+
&actor,
117
117
+
UploadPersistence {
118
118
+
upload_id: &upload_id,
119
119
+
digest: &staged.digest,
120
120
+
mime_type: &mime_type,
121
121
+
size: staged.size,
122
122
+
filename: filename.as_deref(),
123
123
+
file: &file,
124
124
+
},
125
125
+
)
126
126
+
.await?;
127
127
+
128
128
+
Ok(Json(StageFileOutput {
129
129
+
file,
130
130
+
filename: filename.map(SmolStr::from),
131
131
+
status: Some(StageFileOutputStatus::Uploaded),
132
132
+
upload_id: SmolStr::from(upload_id),
133
133
+
extra_data: None,
134
134
+
}))
135
135
+
}
136
136
+
137
137
+
pub(super) async fn publish_thing(
138
138
+
State(state): State<AppState>,
139
139
+
ExtractOAuthSession(session): ExtractOAuthSession<PublicResolver, SqliteAuthStore>,
140
140
+
ExtractXrpc(req): ExtractXrpc<PublishThingRequest>,
141
141
+
) -> AppResult<XrpcResponse<PublishThingRequest>> {
142
142
+
let agent = Agent::from(session);
143
143
+
let actor = authenticated_did(&agent).await?;
144
144
+
let _ = ensure_polymodel_profile(&state, &agent, &actor, false).await?;
145
145
+
let _write_guard = state.write_lock.lock().await;
146
146
+
let out = publish_composition(&state, &agent, &actor, req.thing).await?;
147
147
+
Ok(XrpcResponse(out))
148
148
+
}
149
149
+
150
150
+
pub(super) async fn update_thing(
151
151
+
State(state): State<AppState>,
152
152
+
ExtractOAuthSession(session): ExtractOAuthSession<PublicResolver, SqliteAuthStore>,
153
153
+
ExtractXrpc(req): ExtractXrpc<UpdateThingRequest>,
154
154
+
) -> AppResult<XrpcResponse<UpdateThingRequest>> {
155
155
+
let agent = Agent::from(session);
156
156
+
let actor = authenticated_did(&agent).await?;
157
157
+
let _ = ensure_polymodel_profile(&state, &agent, &actor, false).await?;
158
158
+
let _write_guard = state.write_lock.lock().await;
159
159
+
validate_owned_uri(&actor, &req.uri, THING_COLLECTION)?;
160
160
+
let previous = load_owned_composition(&state, &actor, &req.uri).await?;
161
161
+
let out = update_composition(&state, &agent, &actor, req.uri, req.thing, &previous).await?;
162
162
+
Ok(XrpcResponse(UpdateThingOutput {
163
163
+
models: out.models,
164
164
+
thing: out.thing,
165
165
+
extra_data: None,
166
166
+
}))
167
167
+
}
168
168
+
169
169
+
pub(super) async fn delete_thing(
170
170
+
State(state): State<AppState>,
171
171
+
ExtractOAuthSession(session): ExtractOAuthSession<PublicResolver, SqliteAuthStore>,
172
172
+
ExtractXrpc(req): ExtractXrpc<DeleteThingRequest>,
173
173
+
) -> AppResult<XrpcResponse<DeleteThingRequest>> {
174
174
+
let agent = Agent::from(session);
175
175
+
let actor = authenticated_did(&agent).await?;
176
176
+
let _write_guard = state.write_lock.lock().await;
177
177
+
validate_owned_uri(&actor, &req.uri, THING_COLLECTION)?;
178
178
+
let existing = load_owned_composition(&state, &actor, &req.uri).await?;
179
179
+
let mut deleted_uris = delete_composition_children(&state, &agent, &actor, &existing).await?;
180
180
+
agent_delete_record::<Thing>(&agent, existing.thing_rkey.clone()).await?;
181
181
+
eager_delete(
182
182
+
&state,
183
183
+
&actor,
184
184
+
THING_COLLECTION,
185
185
+
existing.thing_rkey.as_ref(),
186
186
+
)
187
187
+
.await?;
188
188
+
deleted_uris.insert(0, existing.thing_uri);
189
189
+
Ok(XrpcResponse(DeleteThingOutput {
190
190
+
deleted: true,
191
191
+
deleted_uris: Some(deleted_uris),
192
192
+
extra_data: None,
193
193
+
}))
194
194
+
}
195
195
+
196
196
+
pub(super) async fn create_like(
197
197
+
State(state): State<AppState>,
198
198
+
ExtractOAuthSession(session): ExtractOAuthSession<PublicResolver, SqliteAuthStore>,
199
199
+
ExtractXrpc(req): ExtractXrpc<CreateLikeRequest>,
200
200
+
) -> AppResult<XrpcResponse<CreateLikeRequest>> {
201
201
+
let agent = Agent::from(session);
202
202
+
let actor = authenticated_did(&agent).await?;
203
203
+
let _ = ensure_polymodel_profile(&state, &agent, &actor, false).await?;
204
204
+
validate_subject(&state, &req.subject.uri).await?;
205
205
+
if let Some(existing) =
206
206
+
lookup_existing_social(&state, &actor, LIKE_COLLECTION, req.subject.uri.as_ref()).await?
207
207
+
{
208
208
+
return Ok(XrpcResponse(CreateLikeOutput {
209
209
+
cid: existing.cid,
210
210
+
created: Some(false),
211
211
+
uri: existing.uri,
212
212
+
extra_data: None,
213
213
+
}));
214
214
+
}
215
215
+
let record = Like {
216
216
+
created_at: Datetime::now(),
217
217
+
subject: req.subject,
218
218
+
extra_data: None,
219
219
+
};
220
220
+
let out = agent
221
221
+
.create_record(record.clone(), None)
222
222
+
.await
223
223
+
.map_err(|e| agent_error("create like", e))?;
224
224
+
let rkey = rkey_from_uri(&out.uri)?;
225
225
+
eager_project_record(
226
226
+
&state,
227
227
+
&actor,
228
228
+
LIKE_COLLECTION,
229
229
+
rkey.as_ref(),
230
230
+
Some(out.cid.clone()),
231
231
+
&record,
232
232
+
"create",
233
233
+
)
234
234
+
.await?;
235
235
+
Ok(XrpcResponse(CreateLikeOutput {
236
236
+
cid: out.cid,
237
237
+
created: Some(true),
238
238
+
uri: out.uri,
239
239
+
extra_data: None,
240
240
+
}))
241
241
+
}
242
242
+
243
243
+
pub(super) async fn delete_like(
244
244
+
State(state): State<AppState>,
245
245
+
ExtractOAuthSession(session): ExtractOAuthSession<PublicResolver, SqliteAuthStore>,
246
246
+
ExtractXrpc(req): ExtractXrpc<DeleteLikeRequest>,
247
247
+
) -> AppResult<XrpcResponse<DeleteLikeRequest>> {
248
248
+
let agent = Agent::from(session);
249
249
+
let actor = authenticated_did(&agent).await?;
250
250
+
let uri = match (req.uri, req.subject) {
251
251
+
(Some(uri), _) => uri,
252
252
+
(None, Some(subject)) => {
253
253
+
lookup_social_uri(&state, &actor, LIKE_COLLECTION, subject.uri.as_ref()).await?
254
254
+
}
255
255
+
(None, None) => return Err(invalid_request("deleteLike requires uri or subject")),
256
256
+
};
257
257
+
delete_social::<Like>(&state, &agent, &actor, LIKE_COLLECTION, &uri).await?;
258
258
+
Ok(XrpcResponse(DeleteLikeOutput {
259
259
+
deleted: true,
260
260
+
uri: Some(uri),
261
261
+
extra_data: None,
262
262
+
}))
263
263
+
}
264
264
+
265
265
+
pub(super) async fn create_save(
266
266
+
State(state): State<AppState>,
267
267
+
ExtractOAuthSession(session): ExtractOAuthSession<PublicResolver, SqliteAuthStore>,
268
268
+
ExtractXrpc(req): ExtractXrpc<CreateSaveRequest>,
269
269
+
) -> AppResult<XrpcResponse<CreateSaveRequest>> {
270
270
+
let agent = Agent::from(session);
271
271
+
let actor = authenticated_did(&agent).await?;
272
272
+
let _ = ensure_polymodel_profile(&state, &agent, &actor, false).await?;
273
273
+
validate_subject(&state, &req.subject.uri).await?;
274
274
+
if let Some(existing) =
275
275
+
lookup_existing_social(&state, &actor, SAVE_COLLECTION, req.subject.uri.as_ref()).await?
276
276
+
{
277
277
+
return Ok(XrpcResponse(CreateSaveOutput {
278
278
+
cid: existing.cid,
279
279
+
created: Some(false),
280
280
+
uri: existing.uri,
281
281
+
extra_data: None,
282
282
+
}));
283
283
+
}
284
284
+
let record = Save {
285
285
+
created_at: Datetime::now(),
286
286
+
note: req.note,
287
287
+
subject: req.subject,
288
288
+
extra_data: None,
289
289
+
};
290
290
+
let out = agent
291
291
+
.create_record(record.clone(), None)
292
292
+
.await
293
293
+
.map_err(|e| agent_error("create save", e))?;
294
294
+
let rkey = rkey_from_uri(&out.uri)?;
295
295
+
eager_project_record(
296
296
+
&state,
297
297
+
&actor,
298
298
+
SAVE_COLLECTION,
299
299
+
rkey.as_ref(),
300
300
+
Some(out.cid.clone()),
301
301
+
&record,
302
302
+
"create",
303
303
+
)
304
304
+
.await?;
305
305
+
Ok(XrpcResponse(CreateSaveOutput {
306
306
+
cid: out.cid,
307
307
+
created: Some(true),
308
308
+
uri: out.uri,
309
309
+
extra_data: None,
310
310
+
}))
311
311
+
}
312
312
+
313
313
+
pub(super) async fn delete_save(
314
314
+
State(state): State<AppState>,
315
315
+
ExtractOAuthSession(session): ExtractOAuthSession<PublicResolver, SqliteAuthStore>,
316
316
+
ExtractXrpc(req): ExtractXrpc<DeleteSaveRequest>,
317
317
+
) -> AppResult<XrpcResponse<DeleteSaveRequest>> {
318
318
+
let agent = Agent::from(session);
319
319
+
let actor = authenticated_did(&agent).await?;
320
320
+
let uri = match (req.uri, req.subject) {
321
321
+
(Some(uri), _) => uri,
322
322
+
(None, Some(subject)) => {
323
323
+
lookup_social_uri(&state, &actor, SAVE_COLLECTION, subject.uri.as_ref()).await?
324
324
+
}
325
325
+
(None, None) => return Err(invalid_request("deleteSave requires uri or subject")),
326
326
+
};
327
327
+
delete_social::<Save>(&state, &agent, &actor, SAVE_COLLECTION, &uri).await?;
328
328
+
Ok(XrpcResponse(DeleteSaveOutput {
329
329
+
deleted: true,
330
330
+
uri: Some(uri),
331
331
+
extra_data: None,
332
332
+
}))
333
333
+
}
334
334
+
335
335
+
pub(super) async fn create_tag(
336
336
+
State(state): State<AppState>,
337
337
+
ExtractOAuthSession(session): ExtractOAuthSession<PublicResolver, SqliteAuthStore>,
338
338
+
ExtractXrpc(req): ExtractXrpc<CreateTagRequest>,
339
339
+
) -> AppResult<XrpcResponse<CreateTagRequest>> {
340
340
+
let agent = Agent::from(session);
341
341
+
let actor = authenticated_did(&agent).await?;
342
342
+
let _ = ensure_polymodel_profile(&state, &agent, &actor, false).await?;
343
343
+
validate_subject(&state, &req.subject.uri).await?;
344
344
+
let record = Tag {
345
345
+
created_at: Datetime::now(),
346
346
+
subject: req.subject,
347
347
+
tag: req.tag,
348
348
+
extra_data: None,
349
349
+
};
350
350
+
let out = agent
351
351
+
.create_record(record.clone(), None)
352
352
+
.await
353
353
+
.map_err(|e| agent_error("create tag", e))?;
354
354
+
let rkey = rkey_from_uri(&out.uri)?;
355
355
+
eager_project_record(
356
356
+
&state,
357
357
+
&actor,
358
358
+
TAG_COLLECTION,
359
359
+
rkey.as_ref(),
360
360
+
Some(out.cid.clone()),
361
361
+
&record,
362
362
+
"create",
363
363
+
)
364
364
+
.await?;
365
365
+
Ok(XrpcResponse(CreateTagOutput {
366
366
+
cid: out.cid,
367
367
+
created: Some(true),
368
368
+
uri: out.uri,
369
369
+
extra_data: None,
370
370
+
}))
371
371
+
}
372
372
+
373
373
+
pub(super) async fn delete_tag(
374
374
+
State(state): State<AppState>,
375
375
+
ExtractOAuthSession(session): ExtractOAuthSession<PublicResolver, SqliteAuthStore>,
376
376
+
ExtractXrpc(req): ExtractXrpc<DeleteTagRequest>,
377
377
+
) -> AppResult<XrpcResponse<DeleteTagRequest>> {
378
378
+
let agent = Agent::from(session);
379
379
+
let actor = authenticated_did(&agent).await?;
380
380
+
delete_social::<Tag>(&state, &agent, &actor, TAG_COLLECTION, &req.uri).await?;
381
381
+
Ok(XrpcResponse(DeleteTagOutput {
382
382
+
deleted: true,
383
383
+
uri: Some(req.uri),
384
384
+
extra_data: None,
385
385
+
}))
386
386
+
}
387
387
+
388
388
+
struct BootstrapOutcome {
389
389
+
profile: ProfileView,
390
390
+
status: BootstrapProfileOutputStatus,
391
391
+
uri: Option<AtUri>,
392
392
+
cid: Option<Cid>,
393
393
+
}
394
394
+
395
395
+
async fn authenticated_did(agent: &UserAgent) -> AppResult<Did> {
396
396
+
agent
397
397
+
.session_info()
398
398
+
.await
399
399
+
.map(|(did, _)| did)
400
400
+
.ok_or_else(|| unauthorized("OAuth session is required"))
401
401
+
}
402
402
+
403
403
+
async fn ensure_polymodel_profile(
404
404
+
state: &AppState,
405
405
+
agent: &UserAgent,
406
406
+
actor: &Did,
407
407
+
allow_create: bool,
408
408
+
) -> AppResult<BootstrapOutcome> {
409
409
+
if let Some(profile) = views::polymodel_profile_view(state, actor).await? {
410
410
+
return Ok(BootstrapOutcome {
411
411
+
profile,
412
412
+
status: BootstrapProfileOutputStatus::ExistingLocal,
413
413
+
uri: None,
414
414
+
cid: None,
415
415
+
});
416
416
+
}
417
417
+
418
418
+
let profile_uri = at_uri_owned(actor, PROFILE_COLLECTION, "self")?;
419
419
+
match agent.get_record::<PolymodelProfile, _>(&profile_uri).await {
420
420
+
Ok(resp) => {
421
421
+
let output = resp
422
422
+
.into_output()
423
423
+
.map_err(|e| internal(format!("profile record decode failed: {e}")))?;
424
424
+
eager_project_record(
425
425
+
state,
426
426
+
actor,
427
427
+
PROFILE_COLLECTION,
428
428
+
"self",
429
429
+
output.cid.clone(),
430
430
+
&output.value,
431
431
+
"create",
432
432
+
)
433
433
+
.await?;
434
434
+
let profile = views::polymodel_profile_view(state, actor)
435
435
+
.await?
436
436
+
.ok_or_else(|| internal("projected profile was not readable"))?;
437
437
+
return Ok(BootstrapOutcome {
438
438
+
profile,
439
439
+
status: BootstrapProfileOutputStatus::ExistingRemote,
440
440
+
uri: Some(output.uri),
441
441
+
cid: output.cid,
442
442
+
});
443
443
+
}
444
444
+
Err(e) if client_error_is_not_found(&e) => {}
445
445
+
Err(e) => return Err(client_error("fetch polymodel profile", e)),
446
446
+
}
447
447
+
448
448
+
if !allow_create {
449
449
+
return Err(invalid_request(
450
450
+
"Polymodel profile has not been bootstrapped",
451
451
+
));
452
452
+
}
453
453
+
454
454
+
let bsky_uri = at_uri_owned(actor, "app.bsky.actor.profile", "self")?;
455
455
+
let bsky = match agent.get_record::<BskyProfile, _>(&bsky_uri).await {
456
456
+
Ok(resp) => {
457
457
+
resp.into_output()
458
458
+
.map_err(|e| internal(format!("bsky profile decode failed: {e}")))?
459
459
+
.value
460
460
+
}
461
461
+
Err(e) if client_error_is_not_found(&e) => BskyProfile {
462
462
+
avatar: None,
463
463
+
banner: None,
464
464
+
created_at: None,
465
465
+
description: None,
466
466
+
display_name: None,
467
467
+
joined_via_starter_pack: None,
468
468
+
labels: None,
469
469
+
pinned_post: None,
470
470
+
pronouns: None,
471
471
+
website: None,
472
472
+
extra_data: None,
473
473
+
},
474
474
+
Err(e) => return Err(client_error("fetch bsky profile", e)),
475
475
+
};
476
476
+
477
477
+
let profile = PolymodelProfile {
478
478
+
avatar: bsky.avatar,
479
479
+
default_license: None,
480
480
+
description: bsky.description,
481
481
+
display_name: bsky.display_name,
482
482
+
links: None,
483
483
+
printers: None,
484
484
+
pronouns: bsky.pronouns,
485
485
+
extra_data: None,
486
486
+
};
487
487
+
let create = agent
488
488
+
.create_record(profile.clone(), Some(record_key("self")?))
489
489
+
.await;
490
490
+
let output = match create {
491
491
+
Ok(out) => {
492
492
+
eager_project_record(
493
493
+
state,
494
494
+
actor,
495
495
+
PROFILE_COLLECTION,
496
496
+
"self",
497
497
+
Some(out.cid.clone()),
498
498
+
&profile,
499
499
+
"create",
500
500
+
)
501
501
+
.await?;
502
502
+
BootstrapOutcome {
503
503
+
profile: views::polymodel_profile_view(state, actor)
504
504
+
.await?
505
505
+
.ok_or_else(|| internal("created profile was not readable"))?,
506
506
+
status: BootstrapProfileOutputStatus::Created,
507
507
+
uri: Some(out.uri),
508
508
+
cid: Some(out.cid),
509
509
+
}
510
510
+
}
511
511
+
Err(e) if agent_error_is_conflict(&e) || agent_error_is_not_found(&e) => {
512
512
+
let resp = agent
513
513
+
.get_record::<PolymodelProfile, _>(&profile_uri)
514
514
+
.await
515
515
+
.map_err(|e| client_error("refetch raced profile", e))?;
516
516
+
let remote = resp
517
517
+
.into_output()
518
518
+
.map_err(|e| internal(format!("raced profile decode failed: {e}")))?;
519
519
+
eager_project_record(
520
520
+
state,
521
521
+
actor,
522
522
+
PROFILE_COLLECTION,
523
523
+
"self",
524
524
+
remote.cid.clone(),
525
525
+
&remote.value,
526
526
+
"create",
527
527
+
)
528
528
+
.await?;
529
529
+
BootstrapOutcome {
530
530
+
profile: views::polymodel_profile_view(state, actor)
531
531
+
.await?
532
532
+
.ok_or_else(|| internal("raced profile was not readable"))?,
533
533
+
status: BootstrapProfileOutputStatus::ExistingRemote,
534
534
+
uri: Some(remote.uri),
535
535
+
cid: remote.cid,
536
536
+
}
537
537
+
}
538
538
+
Err(e) => return Err(agent_error("create polymodel profile", e)),
539
539
+
};
540
540
+
Ok(output)
541
541
+
}
542
542
+
543
543
+
async fn publish_composition(
544
544
+
state: &AppState,
545
545
+
agent: &UserAgent,
546
546
+
actor: &Did,
547
547
+
thing: ThingInput,
548
548
+
) -> AppResult<PublishThingOutput> {
549
549
+
let mut model_results = Vec::new();
550
550
+
let mut model_refs = Vec::new();
551
551
+
552
552
+
for model_input in thing.models {
553
553
+
let (model_ref, result) = publish_model(state, agent, actor, model_input).await?;
554
554
+
model_refs.push(model_ref);
555
555
+
model_results.push(result);
556
556
+
}
557
557
+
558
558
+
let record = Thing {
559
559
+
cover: thing.cover,
560
560
+
created_at: Datetime::now(),
561
561
+
derived_from: thing.derived_from,
562
562
+
instructions: thing.instructions,
563
563
+
intended_to_work_with: thing.intended_to_work_with,
564
564
+
license: thing.license,
565
565
+
models: Some(model_refs),
566
566
+
name: thing.name,
567
567
+
previews: thing.previews,
568
568
+
summary: thing.summary,
569
569
+
tags: thing.tags,
570
570
+
extra_data: None,
571
571
+
};
572
572
+
573
573
+
let out = agent
574
574
+
.create_record(record.clone(), None)
575
575
+
.await
576
576
+
.map_err(|e| agent_error("create thing", e))?;
577
577
+
let rkey = rkey_from_uri(&out.uri)?;
578
578
+
eager_project_record(
579
579
+
state,
580
580
+
actor,
581
581
+
THING_COLLECTION,
582
582
+
rkey.as_ref(),
583
583
+
Some(out.cid.clone()),
584
584
+
&record,
585
585
+
"create",
586
586
+
)
587
587
+
.await?;
588
588
+
let created = RecordRef {
589
589
+
cid: out.cid,
590
590
+
uri: out.uri,
591
591
+
extra_data: None,
592
592
+
};
593
593
+
594
594
+
Ok(PublishThingOutput {
595
595
+
thing: created,
596
596
+
models: model_results,
597
597
+
extra_data: None,
598
598
+
})
599
599
+
}
600
600
+
601
601
+
async fn update_composition(
602
602
+
state: &AppState,
603
603
+
agent: &UserAgent,
604
604
+
actor: &Did,
605
605
+
thing_uri: AtUri,
606
606
+
thing: UpdateThingInput,
607
607
+
previous: &ExistingComposition,
608
608
+
) -> AppResult<PublishThingOutput> {
609
609
+
let mut model_results = Vec::new();
610
610
+
let mut model_refs = Vec::new();
611
611
+
612
612
+
for model_input in thing.models {
613
613
+
let (model_ref, result) = update_model(state, agent, actor, model_input, previous).await?;
614
614
+
model_refs.push(model_ref);
615
615
+
model_results.push(result);
616
616
+
}
617
617
+
618
618
+
let record = Thing {
619
619
+
cover: thing.cover,
620
620
+
created_at: Datetime::now(),
621
621
+
derived_from: thing.derived_from,
622
622
+
instructions: thing.instructions,
623
623
+
intended_to_work_with: thing.intended_to_work_with,
624
624
+
license: thing.license,
625
625
+
models: Some(model_refs),
626
626
+
name: thing.name,
627
627
+
previews: thing.previews,
628
628
+
summary: thing.summary,
629
629
+
tags: thing.tags,
630
630
+
extra_data: None,
631
631
+
};
632
632
+
633
633
+
let rkey = rkey_from_uri(&thing_uri)?;
634
634
+
let out = agent
635
635
+
.put_record::<Thing>(rkey.clone(), record.clone())
636
636
+
.await
637
637
+
.map_err(|e| agent_error("put thing", e))?;
638
638
+
eager_project_record(
639
639
+
state,
640
640
+
actor,
641
641
+
THING_COLLECTION,
642
642
+
rkey.as_ref(),
643
643
+
Some(out.cid.clone()),
644
644
+
&record,
645
645
+
"update",
646
646
+
)
647
647
+
.await?;
648
648
+
649
649
+
Ok(PublishThingOutput {
650
650
+
thing: RecordRef {
651
651
+
cid: out.cid,
652
652
+
uri: out.uri,
653
653
+
extra_data: None,
654
654
+
},
655
655
+
models: model_results,
656
656
+
extra_data: None,
657
657
+
})
658
658
+
}
659
659
+
660
660
+
async fn update_model(
661
661
+
state: &AppState,
662
662
+
agent: &UserAgent,
663
663
+
actor: &Did,
664
664
+
model_input: UpdateModelInput,
665
665
+
previous: &ExistingComposition,
666
666
+
) -> AppResult<(StrongRef, ModelPublishResult)> {
667
667
+
let mut part_refs = Vec::new();
668
668
+
let mut part_record_refs = Vec::new();
669
669
+
for part_input in model_input.parts {
670
670
+
let part_ref = update_part(state, agent, actor, part_input, previous).await?;
671
671
+
part_record_refs.push(RecordRef {
672
672
+
uri: part_ref.uri.clone(),
673
673
+
cid: part_ref.cid.clone(),
674
674
+
extra_data: None,
675
675
+
});
676
676
+
part_refs.push(part_ref);
677
677
+
}
678
678
+
679
679
+
let model = Model {
680
680
+
cover: model_input.cover,
681
681
+
created_at: Datetime::now(),
682
682
+
derived_from: model_input.derived_from,
683
683
+
instructions: model_input.instructions,
684
684
+
intended_to_work_with: model_input.intended_to_work_with,
685
685
+
license: model_input.license,
686
686
+
name: model_input.name,
687
687
+
parts: part_refs,
688
688
+
previews: model_input.previews,
689
689
+
summary: model_input.summary,
690
690
+
tags: model_input.tags,
691
691
+
extra_data: None,
692
692
+
};
693
693
+
694
694
+
let (uri, cid, action) = if let Some(uri) = model_input.uri {
695
695
+
validate_owned_uri(actor, &uri, MODEL_COLLECTION)?;
696
696
+
let rkey = rkey_from_uri(&uri)?;
697
697
+
validate_existing_update_child(
698
698
+
state,
699
699
+
actor,
700
700
+
&uri,
701
701
+
&rkey,
702
702
+
MODEL_COLLECTION,
703
703
+
&previous.models,
704
704
+
)
705
705
+
.await?;
706
706
+
let out = agent
707
707
+
.put_record::<Model>(rkey.clone(), model.clone())
708
708
+
.await
709
709
+
.map_err(|e| agent_error("put model", e))?;
710
710
+
eager_project_record(
711
711
+
state,
712
712
+
actor,
713
713
+
MODEL_COLLECTION,
714
714
+
rkey.as_ref(),
715
715
+
Some(out.cid.clone()),
716
716
+
&model,
717
717
+
"update",
718
718
+
)
719
719
+
.await?;
720
720
+
(out.uri, out.cid, "update")
721
721
+
} else {
722
722
+
let out = agent
723
723
+
.create_record(model.clone(), None)
724
724
+
.await
725
725
+
.map_err(|e| agent_error("create model", e))?;
726
726
+
let rkey = rkey_from_uri(&out.uri)?;
727
727
+
eager_project_record(
728
728
+
state,
729
729
+
actor,
730
730
+
MODEL_COLLECTION,
731
731
+
rkey.as_ref(),
732
732
+
Some(out.cid.clone()),
733
733
+
&model,
734
734
+
"create",
735
735
+
)
736
736
+
.await?;
737
737
+
(out.uri, out.cid, "create")
738
738
+
};
739
739
+
tracing::debug!(action, uri = %uri, "updated thing model composition member");
740
740
+
let model_ref = strong_ref(uri.clone(), cid.clone());
741
741
+
Ok((
742
742
+
model_ref,
743
743
+
ModelPublishResult {
744
744
+
model: RecordRef {
745
745
+
uri,
746
746
+
cid,
747
747
+
extra_data: None,
748
748
+
},
749
749
+
parts: part_record_refs,
750
750
+
extra_data: None,
751
751
+
},
752
752
+
))
753
753
+
}
754
754
+
755
755
+
async fn update_part(
756
756
+
state: &AppState,
757
757
+
agent: &UserAgent,
758
758
+
actor: &Did,
759
759
+
input: UpdatePartInput,
760
760
+
previous: &ExistingComposition,
761
761
+
) -> AppResult<StrongRef> {
762
762
+
let file = match (input.file, input.upload_id) {
763
763
+
(Some(file), _) => file,
764
764
+
(None, Some(upload_id)) => load_upload_file(state, actor, upload_id.as_ref()).await?,
765
765
+
(None, None) => return Err(invalid_request("part requires file or uploadId")),
766
766
+
};
767
767
+
let part = Part {
768
768
+
created_at: Datetime::now(),
769
769
+
derived_from: input.derived_from,
770
770
+
dimensions: input.dimensions,
771
771
+
file,
772
772
+
format: input.format,
773
773
+
intended_to_work_with: input.intended_to_work_with,
774
774
+
name: input.name,
775
775
+
notes: input.notes,
776
776
+
previews: input.previews,
777
777
+
print_settings: input.print_settings,
778
778
+
units: input.units,
779
779
+
extra_data: None,
780
780
+
};
781
781
+
782
782
+
if let Some(uri) = input.uri {
783
783
+
validate_owned_uri(actor, &uri, PART_COLLECTION)?;
784
784
+
let rkey = rkey_from_uri(&uri)?;
785
785
+
validate_existing_update_child(state, actor, &uri, &rkey, PART_COLLECTION, &previous.parts)
786
786
+
.await?;
787
787
+
let out = agent
788
788
+
.put_record::<Part>(rkey.clone(), part.clone())
789
789
+
.await
790
790
+
.map_err(|e| agent_error("put part", e))?;
791
791
+
eager_project_record(
792
792
+
state,
793
793
+
actor,
794
794
+
PART_COLLECTION,
795
795
+
rkey.as_ref(),
796
796
+
Some(out.cid.clone()),
797
797
+
&part,
798
798
+
"update",
799
799
+
)
800
800
+
.await?;
801
801
+
return Ok(strong_ref(out.uri, out.cid));
802
802
+
}
803
803
+
804
804
+
let out = agent
805
805
+
.create_record(part.clone(), None)
806
806
+
.await
807
807
+
.map_err(|e| agent_error("create part", e))?;
808
808
+
let rkey = rkey_from_uri(&out.uri)?;
809
809
+
eager_project_record(
810
810
+
state,
811
811
+
actor,
812
812
+
PART_COLLECTION,
813
813
+
rkey.as_ref(),
814
814
+
Some(out.cid.clone()),
815
815
+
&part,
816
816
+
"create",
817
817
+
)
818
818
+
.await?;
819
819
+
Ok(strong_ref(out.uri, out.cid))
820
820
+
}
821
821
+
822
822
+
async fn publish_model(
823
823
+
state: &AppState,
824
824
+
agent: &UserAgent,
825
825
+
actor: &Did,
826
826
+
model_input: ModelInput,
827
827
+
) -> AppResult<(StrongRef, ModelPublishResult)> {
828
828
+
let mut part_refs = Vec::new();
829
829
+
let mut part_record_refs = Vec::new();
830
830
+
for part_input in model_input.parts {
831
831
+
let part_ref = publish_part(state, agent, actor, part_input).await?;
832
832
+
part_record_refs.push(RecordRef {
833
833
+
uri: part_ref.uri.clone(),
834
834
+
cid: part_ref.cid.clone(),
835
835
+
extra_data: None,
836
836
+
});
837
837
+
part_refs.push(part_ref);
838
838
+
}
839
839
+
let model = Model {
840
840
+
cover: model_input.cover,
841
841
+
created_at: Datetime::now(),
842
842
+
derived_from: model_input.derived_from,
843
843
+
instructions: model_input.instructions,
844
844
+
intended_to_work_with: model_input.intended_to_work_with,
845
845
+
license: model_input.license,
846
846
+
name: model_input.name,
847
847
+
parts: part_refs,
848
848
+
previews: model_input.previews,
849
849
+
summary: model_input.summary,
850
850
+
tags: model_input.tags,
851
851
+
extra_data: None,
852
852
+
};
853
853
+
let out = agent
854
854
+
.create_record(model.clone(), None)
855
855
+
.await
856
856
+
.map_err(|e| agent_error("create model", e))?;
857
857
+
let rkey = rkey_from_uri(&out.uri)?;
858
858
+
eager_project_record(
859
859
+
state,
860
860
+
actor,
861
861
+
MODEL_COLLECTION,
862
862
+
rkey.as_ref(),
863
863
+
Some(out.cid.clone()),
864
864
+
&model,
865
865
+
"create",
866
866
+
)
867
867
+
.await?;
868
868
+
let model_ref = strong_ref(out.uri.clone(), out.cid.clone());
869
869
+
let record_ref = RecordRef {
870
870
+
uri: out.uri,
871
871
+
cid: out.cid,
872
872
+
extra_data: None,
873
873
+
};
874
874
+
Ok((
875
875
+
model_ref,
876
876
+
ModelPublishResult {
877
877
+
model: record_ref,
878
878
+
parts: part_record_refs,
879
879
+
extra_data: None,
880
880
+
},
881
881
+
))
882
882
+
}
883
883
+
884
884
+
async fn publish_part(
885
885
+
state: &AppState,
886
886
+
agent: &UserAgent,
887
887
+
actor: &Did,
888
888
+
input: polymodel_api::space_polymodel::library::publish_thing::PartInput,
889
889
+
) -> AppResult<StrongRef> {
890
890
+
let file = match (input.file, input.upload_id) {
891
891
+
(Some(file), _) => file,
892
892
+
(None, Some(upload_id)) => load_upload_file(state, actor, upload_id.as_ref()).await?,
893
893
+
(None, None) => return Err(invalid_request("part requires file or uploadId")),
894
894
+
};
895
895
+
let part = Part {
896
896
+
created_at: Datetime::now(),
897
897
+
derived_from: input.derived_from,
898
898
+
dimensions: input.dimensions,
899
899
+
file,
900
900
+
format: input.format,
901
901
+
intended_to_work_with: input.intended_to_work_with,
902
902
+
name: input.name,
903
903
+
notes: input.notes,
904
904
+
previews: input.previews,
905
905
+
print_settings: input.print_settings,
906
906
+
units: input.units,
907
907
+
extra_data: None,
908
908
+
};
909
909
+
let out = agent
910
910
+
.create_record(part.clone(), None)
911
911
+
.await
912
912
+
.map_err(|e| agent_error("create part", e))?;
913
913
+
let rkey = rkey_from_uri(&out.uri)?;
914
914
+
eager_project_record(
915
915
+
state,
916
916
+
actor,
917
917
+
PART_COLLECTION,
918
918
+
rkey.as_ref(),
919
919
+
Some(out.cid.clone()),
920
920
+
&part,
921
921
+
"create",
922
922
+
)
923
923
+
.await?;
924
924
+
Ok(strong_ref(out.uri, out.cid))
925
925
+
}
926
926
+
927
927
+
async fn delete_social<R>(
928
928
+
state: &AppState,
929
929
+
agent: &UserAgent,
930
930
+
actor: &Did,
931
931
+
collection: &str,
932
932
+
uri: &AtUri,
933
933
+
) -> AppResult<()>
934
934
+
where
935
935
+
R: Collection + Serialize,
936
936
+
{
937
937
+
validate_owned_uri(actor, uri, collection)?;
938
938
+
let rkey = rkey_from_uri(uri)?;
939
939
+
agent_delete_record::<R>(agent, rkey.clone()).await?;
940
940
+
eager_delete(state, actor, collection, rkey.as_ref()).await
941
941
+
}
942
942
+
943
943
+
struct ExistingComposition {
944
944
+
thing_uri: AtUri,
945
945
+
thing_rkey: RecordKey<Rkey>,
946
946
+
models: Vec<CompositionChild>,
947
947
+
parts: Vec<CompositionChild>,
948
948
+
}
949
949
+
950
950
+
struct CompositionChild {
951
951
+
rkey: RecordKey<Rkey>,
952
952
+
uri: AtUri,
953
953
+
}
954
954
+
955
955
+
#[derive(sqlx::FromRow)]
956
956
+
struct CompositionRecordRow {
957
957
+
did: String,
958
958
+
rkey: String,
959
959
+
uri: String,
960
960
+
}
961
961
+
962
962
+
async fn load_owned_composition(
963
963
+
state: &AppState,
964
964
+
actor: &Did,
965
965
+
thing_uri: &AtUri,
966
966
+
) -> AppResult<ExistingComposition> {
967
967
+
let thing = db(sqlx::query_as!(
968
968
+
CompositionRecordRow,
969
969
+
"SELECT did, rkey, uri FROM things WHERE uri = ?",
970
970
+
thing_uri.as_ref()
971
971
+
)
972
972
+
.fetch_optional(&state.pool)
973
973
+
.await)?
974
974
+
.ok_or_else(|| invalid_request("thing is not indexed locally"))?;
975
975
+
ensure_row_owned(actor, &thing, THING_COLLECTION)?;
976
976
+
977
977
+
let models = db(sqlx::query_as!(
978
978
+
CompositionRecordRow,
979
979
+
"SELECT m.did, m.rkey, m.uri FROM thing_models tm \
980
980
+
JOIN models m ON m.uri = tm.model_uri \
981
981
+
WHERE tm.thing_uri = ? ORDER BY tm.position",
982
982
+
thing_uri.as_ref()
983
983
+
)
984
984
+
.fetch_all(&state.pool)
985
985
+
.await)?;
986
986
+
let parts = db(sqlx::query_as!(
987
987
+
CompositionRecordRow,
988
988
+
"SELECT p.did, p.rkey, p.uri FROM thing_models tm \
989
989
+
JOIN model_parts mp ON mp.model_uri = tm.model_uri \
990
990
+
JOIN parts p ON p.uri = mp.part_uri \
991
991
+
WHERE tm.thing_uri = ? ORDER BY tm.position, mp.position",
992
992
+
thing_uri.as_ref()
993
993
+
)
994
994
+
.fetch_all(&state.pool)
995
995
+
.await)?;
996
996
+
997
997
+
let thing_uri = at_uri_from_row(&thing)?;
998
998
+
let mut model_children = Vec::new();
999
999
+
let mut seen_models = BTreeSet::new();
1000
1000
+
for row in models {
1001
1001
+
ensure_row_owned(actor, &row, MODEL_COLLECTION)?;
1002
1002
+
if seen_models.insert(row.uri.clone()) {
1003
1003
+
model_children.push(CompositionChild {
1004
1004
+
rkey: record_key(&row.rkey)?,
1005
1005
+
uri: at_uri_from_row(&row)?,
1006
1006
+
});
1007
1007
+
}
1008
1008
+
}
1009
1009
+
let mut part_children = Vec::new();
1010
1010
+
let mut seen_parts = BTreeSet::new();
1011
1011
+
for row in parts {
1012
1012
+
ensure_row_owned(actor, &row, PART_COLLECTION)?;
1013
1013
+
if seen_parts.insert(row.uri.clone()) {
1014
1014
+
part_children.push(CompositionChild {
1015
1015
+
rkey: record_key(&row.rkey)?,
1016
1016
+
uri: at_uri_from_row(&row)?,
1017
1017
+
});
1018
1018
+
}
1019
1019
+
}
1020
1020
+
1021
1021
+
Ok(ExistingComposition {
1022
1022
+
thing_uri,
1023
1023
+
thing_rkey: record_key(&thing.rkey)?,
1024
1024
+
models: model_children,
1025
1025
+
parts: part_children,
1026
1026
+
})
1027
1027
+
}
1028
1028
+
1029
1029
+
async fn delete_composition_children(
1030
1030
+
state: &AppState,
1031
1031
+
agent: &UserAgent,
1032
1032
+
actor: &Did,
1033
1033
+
composition: &ExistingComposition,
1034
1034
+
) -> AppResult<Vec<AtUri>> {
1035
1035
+
let mut deleted_uris = Vec::new();
1036
1036
+
for model in &composition.models {
1037
1037
+
if model_reference_count(state, &model.uri).await? > 1 {
1038
1038
+
continue;
1039
1039
+
}
1040
1040
+
agent_delete_record::<Model>(agent, model.rkey.clone()).await?;
1041
1041
+
eager_delete(state, actor, MODEL_COLLECTION, model.rkey.as_ref()).await?;
1042
1042
+
deleted_uris.push(model.uri.clone());
1043
1043
+
}
1044
1044
+
for part in &composition.parts {
1045
1045
+
if part_reference_count(state, &part.uri).await? > 0 {
1046
1046
+
continue;
1047
1047
+
}
1048
1048
+
agent_delete_record::<Part>(agent, part.rkey.clone()).await?;
1049
1049
+
eager_delete(state, actor, PART_COLLECTION, part.rkey.as_ref()).await?;
1050
1050
+
deleted_uris.push(part.uri.clone());
1051
1051
+
}
1052
1052
+
Ok(deleted_uris)
1053
1053
+
}
1054
1054
+
1055
1055
+
async fn model_reference_count(state: &AppState, uri: &AtUri) -> AppResult<i64> {
1056
1056
+
db(sqlx::query_scalar!(
1057
1057
+
"SELECT COUNT(*) AS count FROM thing_models WHERE model_uri = ?",
1058
1058
+
uri.as_ref()
1059
1059
+
)
1060
1060
+
.fetch_one(&state.pool)
1061
1061
+
.await)
1062
1062
+
}
1063
1063
+
1064
1064
+
async fn part_reference_count(state: &AppState, uri: &AtUri) -> AppResult<i64> {
1065
1065
+
db(sqlx::query_scalar!(
1066
1066
+
"SELECT COUNT(*) AS count FROM model_parts WHERE part_uri = ?",
1067
1067
+
uri.as_ref()
1068
1068
+
)
1069
1069
+
.fetch_one(&state.pool)
1070
1070
+
.await)
1071
1071
+
}
1072
1072
+
1073
1073
+
fn ensure_row_owned(actor: &Did, row: &impl OwnedRow, collection: &str) -> AppResult<()> {
1074
1074
+
if row.did() != actor.as_ref() {
1075
1075
+
return Err(invalid_request(format!(
1076
1076
+
"{collection} composition record is not owned by the authenticated actor"
1077
1077
+
)));
1078
1078
+
}
1079
1079
+
Ok(())
1080
1080
+
}
1081
1081
+
1082
1082
+
trait OwnedRow {
1083
1083
+
fn did(&self) -> &str;
1084
1084
+
fn uri(&self) -> &str;
1085
1085
+
}
1086
1086
+
1087
1087
+
impl OwnedRow for CompositionRecordRow {
1088
1088
+
fn did(&self) -> &str {
1089
1089
+
&self.did
1090
1090
+
}
1091
1091
+
fn uri(&self) -> &str {
1092
1092
+
&self.uri
1093
1093
+
}
1094
1094
+
}
1095
1095
+
1096
1096
+
fn at_uri_from_row(row: &impl OwnedRow) -> AppResult<AtUri> {
1097
1097
+
AtUri::new_owned(row.uri()).map_err(|e| internal(format!("stored at-uri is invalid: {e}")))
1098
1098
+
}
1099
1099
+
1100
1100
+
fn strong_ref(uri: AtUri, cid: Cid) -> StrongRef {
1101
1101
+
StrongRef {
1102
1102
+
uri,
1103
1103
+
cid,
1104
1104
+
extra_data: None,
1105
1105
+
}
1106
1106
+
}
1107
1107
+
1108
1108
+
fn record_key(value: &str) -> AppResult<RecordKey<Rkey>> {
1109
1109
+
RecordKey::any(SmolStr::from(value))
1110
1110
+
.map_err(|e| invalid_request(format!("invalid rkey {value:?}: {e}")))
1111
1111
+
}
1112
1112
+
1113
1113
+
fn rkey_from_uri(uri: &AtUri) -> AppResult<RecordKey<Rkey>> {
1114
1114
+
let rkey = uri
1115
1115
+
.rkey()
1116
1116
+
.ok_or_else(|| invalid_request("at-uri is missing record key"))?;
1117
1117
+
record_key(rkey.as_ref())
1118
1118
+
}
1119
1119
+
1120
1120
+
fn at_uri_owned(actor: &Did, collection: &str, rkey: &str) -> AppResult<AtUri> {
1121
1121
+
AtUri::new_owned(format!("at://{}/{}/{}", actor.as_ref(), collection, rkey))
1122
1122
+
.map_err(|e| invalid_request(format!("invalid at-uri: {e}")))
1123
1123
+
}
1124
1124
+
1125
1125
+
fn validate_owned_uri(actor: &Did, uri: &AtUri, expected_collection: &str) -> AppResult<()> {
1126
1126
+
match uri.authority() {
1127
1127
+
AtIdentifier::Did(did) if did.as_ref() == actor.as_ref() => {}
1128
1128
+
_ => {
1129
1129
+
return Err(invalid_request(
1130
1130
+
"record URI is not owned by the authenticated actor",
1131
1131
+
));
1132
1132
+
}
1133
1133
+
}
1134
1134
+
let collection = uri
1135
1135
+
.collection()
1136
1136
+
.ok_or_else(|| invalid_request("record URI missing collection"))?;
1137
1137
+
if collection.as_ref() != expected_collection {
1138
1138
+
return Err(invalid_request(format!(
1139
1139
+
"expected {expected_collection} URI"
1140
1140
+
)));
1141
1141
+
}
1142
1142
+
if uri.rkey().is_none() {
1143
1143
+
return Err(invalid_request("record URI missing rkey"));
1144
1144
+
}
1145
1145
+
Ok(())
1146
1146
+
}
1147
1147
+
1148
1148
+
async fn validate_existing_update_child(
1149
1149
+
state: &AppState,
1150
1150
+
actor: &Did,
1151
1151
+
uri: &AtUri,
1152
1152
+
rkey: &RecordKey<Rkey>,
1153
1153
+
collection: &str,
1154
1154
+
current_children: &[CompositionChild],
1155
1155
+
) -> AppResult<()> {
1156
1156
+
if current_children.iter().any(|child| &child.rkey == rkey) {
1157
1157
+
return Ok(());
1158
1158
+
}
1159
1159
+
let exists = match collection {
1160
1160
+
MODEL_COLLECTION => db(sqlx::query_scalar!(
1161
1161
+
"SELECT EXISTS(SELECT 1 FROM models WHERE did = ? AND uri = ?)",
1162
1162
+
actor.as_ref(),
1163
1163
+
uri.as_ref()
1164
1164
+
)
1165
1165
+
.fetch_one(&state.pool)
1166
1166
+
.await)?,
1167
1167
+
PART_COLLECTION => db(sqlx::query_scalar!(
1168
1168
+
"SELECT EXISTS(SELECT 1 FROM parts WHERE did = ? AND uri = ?)",
1169
1169
+
actor.as_ref(),
1170
1170
+
uri.as_ref()
1171
1171
+
)
1172
1172
+
.fetch_one(&state.pool)
1173
1173
+
.await)?,
1174
1174
+
_ => return Err(invalid_request("unsupported composition child collection")),
1175
1175
+
};
1176
1176
+
if exists == 0 {
1177
1177
+
return Err(invalid_request(
1178
1178
+
"existing child uri is not indexed as an owned composition record",
1179
1179
+
));
1180
1180
+
}
1181
1181
+
Ok(())
1182
1182
+
}
1183
1183
+
1184
1184
+
async fn validate_subject(state: &AppState, uri: &AtUri) -> AppResult<()> {
1185
1185
+
let collection = uri
1186
1186
+
.collection()
1187
1187
+
.ok_or_else(|| invalid_request("subject URI missing collection"))?;
1188
1188
+
let table = match collection.as_ref() {
1189
1189
+
THING_COLLECTION => "things",
1190
1190
+
MODEL_COLLECTION => "models",
1191
1191
+
PART_COLLECTION => "parts",
1192
1192
+
_ => {
1193
1193
+
return Err(invalid_request(
1194
1194
+
"subject must be a Polymodel thing, model, or part",
1195
1195
+
));
1196
1196
+
}
1197
1197
+
};
1198
1198
+
let exists = match table {
1199
1199
+
"things" => db(sqlx::query_scalar!(
1200
1200
+
"SELECT EXISTS(SELECT 1 FROM things WHERE uri = ?)",
1201
1201
+
uri.as_ref()
1202
1202
+
)
1203
1203
+
.fetch_one(&state.pool)
1204
1204
+
.await)?,
1205
1205
+
"models" => db(sqlx::query_scalar!(
1206
1206
+
"SELECT EXISTS(SELECT 1 FROM models WHERE uri = ?)",
1207
1207
+
uri.as_ref()
1208
1208
+
)
1209
1209
+
.fetch_one(&state.pool)
1210
1210
+
.await)?,
1211
1211
+
"parts" => db(sqlx::query_scalar!(
1212
1212
+
"SELECT EXISTS(SELECT 1 FROM parts WHERE uri = ?)",
1213
1213
+
uri.as_ref()
1214
1214
+
)
1215
1215
+
.fetch_one(&state.pool)
1216
1216
+
.await)?,
1217
1217
+
_ => unreachable!(),
1218
1218
+
};
1219
1219
+
if exists == 0 {
1220
1220
+
return Err(invalid_request("subject is not indexed locally"));
1221
1221
+
}
1222
1222
+
Ok(())
1223
1223
+
}
1224
1224
+
1225
1225
+
async fn eager_project_record<R: Serialize>(
1226
1226
+
state: &AppState,
1227
1227
+
actor: &Did,
1228
1228
+
collection: &str,
1229
1229
+
rkey: &str,
1230
1230
+
cid: Option<Cid>,
1231
1231
+
record: &R,
1232
1232
+
action: &str,
1233
1233
+
) -> AppResult<()> {
1234
1234
+
let record =
1235
1235
+
serde_json::to_value(record).map_err(|e| internal(format!("record serialize: {e}")))?;
1236
1236
+
let event = ProjectionEvent {
1237
1237
+
seq: 0,
1238
1238
+
did: actor.as_ref().to_string(),
1239
1239
+
collection: collection.to_string(),
1240
1240
+
rkey: rkey.to_string(),
1241
1241
+
action: action.to_string(),
1242
1242
+
record: Some(record),
1243
1243
+
cid: cid.map(|c| c.to_string()),
1244
1244
+
};
1245
1245
+
project_eager_record(&state.pool, &event)
1246
1246
+
.await
1247
1247
+
.map_err(|e| internal(format!("eager projection failed: {e}")))
1248
1248
+
}
1249
1249
+
1250
1250
+
async fn eager_delete(
1251
1251
+
state: &AppState,
1252
1252
+
actor: &Did,
1253
1253
+
collection: &str,
1254
1254
+
rkey: &str,
1255
1255
+
) -> AppResult<()> {
1256
1256
+
let event = ProjectionEvent {
1257
1257
+
seq: 0,
1258
1258
+
did: actor.as_ref().to_string(),
1259
1259
+
collection: collection.to_string(),
1260
1260
+
rkey: rkey.to_string(),
1261
1261
+
action: "delete".to_string(),
1262
1262
+
record: None,
1263
1263
+
cid: None,
1264
1264
+
};
1265
1265
+
project_eager_record(&state.pool, &event)
1266
1266
+
.await
1267
1267
+
.map_err(|e| internal(format!("eager delete projection failed: {e}")))
1268
1268
+
}
1269
1269
+
1270
1270
+
struct StreamedUpload {
1271
1271
+
chunks: Vec<Chunk>,
1272
1272
+
digest: String,
1273
1273
+
size: usize,
1274
1274
+
}
1275
1275
+
1276
1276
+
async fn upload_streamed_file(
1277
1277
+
agent: &UserAgent,
1278
1278
+
mut body: Body,
1279
1279
+
mime_type: &str,
1280
1280
+
) -> AppResult<StreamedUpload> {
1281
1281
+
let mut hasher = Sha256::new();
1282
1282
+
let mut chunks = Vec::new();
1283
1283
+
let mut pending = Vec::with_capacity(CHUNK_SIZE);
1284
1284
+
let mut size = 0usize;
1285
1285
+
1286
1286
+
while let Some(frame) = body
1287
1287
+
.frame()
1288
1288
+
.await
1289
1289
+
.transpose()
1290
1290
+
.map_err(|e| invalid_request(format!("stageFile body read failed: {e}")))?
1291
1291
+
{
1292
1292
+
let Ok(data) = frame.into_data() else {
1293
1293
+
continue;
1294
1294
+
};
1295
1295
+
size = size
1296
1296
+
.checked_add(data.len())
1297
1297
+
.ok_or_else(|| invalid_request("stageFile body is too large"))?;
1298
1298
+
if size > MAX_STAGE_FILE_SIZE {
1299
1299
+
return Err(invalid_request(format!(
1300
1300
+
"stageFile body exceeds the {} byte limit",
1301
1301
+
MAX_STAGE_FILE_SIZE
1302
1302
+
)));
1303
1303
+
}
1304
1304
+
hasher.update(&data);
1305
1305
+
let mut remaining = data.as_ref();
1306
1306
+
while !remaining.is_empty() {
1307
1307
+
let take = (CHUNK_SIZE - pending.len()).min(remaining.len());
1308
1308
+
pending.extend_from_slice(&remaining[..take]);
1309
1309
+
remaining = &remaining[take..];
1310
1310
+
if pending.len() == CHUNK_SIZE {
1311
1311
+
let offset = size - remaining.len() - pending.len();
1312
1312
+
upload_one_chunk(
1313
1313
+
agent,
1314
1314
+
mime_type,
1315
1315
+
&mut chunks,
1316
1316
+
offset,
1317
1317
+
std::mem::take(&mut pending),
1318
1318
+
)
1319
1319
+
.await?;
1320
1320
+
pending = Vec::with_capacity(CHUNK_SIZE);
1321
1321
+
}
1322
1322
+
}
1323
1323
+
}
1324
1324
+
1325
1325
+
if size == 0 {
1326
1326
+
return Err(invalid_request(
1327
1327
+
"stageFile requires a non-empty raw byte body",
1328
1328
+
));
1329
1329
+
}
1330
1330
+
if !pending.is_empty() {
1331
1331
+
let offset = size - pending.len();
1332
1332
+
upload_one_chunk(agent, mime_type, &mut chunks, offset, pending).await?;
1333
1333
+
}
1334
1334
+
let digest = hasher
1335
1335
+
.finalize()
1336
1336
+
.iter()
1337
1337
+
.map(|b| format!("{b:02x}"))
1338
1338
+
.collect();
1339
1339
+
Ok(StreamedUpload {
1340
1340
+
chunks,
1341
1341
+
digest,
1342
1342
+
size,
1343
1343
+
})
1344
1344
+
}
1345
1345
+
1346
1346
+
async fn upload_one_chunk(
1347
1347
+
agent: &UserAgent,
1348
1348
+
mime_type: &str,
1349
1349
+
chunks: &mut Vec<Chunk>,
1350
1350
+
offset: usize,
1351
1351
+
bytes: Vec<u8>,
1352
1352
+
) -> AppResult<()> {
1353
1353
+
let size = bytes.len();
1354
1354
+
let blob = agent_upload_blob(agent, bytes, mime_type).await?;
1355
1355
+
chunks.push(Chunk {
1356
1356
+
blob: BlobRef::from(blob),
1357
1357
+
offset: offset as i64,
1358
1358
+
size: size as i64,
1359
1359
+
extra_data: None,
1360
1360
+
});
1361
1361
+
Ok(())
1362
1362
+
}
1363
1363
+
1364
1364
+
async fn agent_upload_blob(
1365
1365
+
agent: &UserAgent,
1366
1366
+
bytes: Vec<u8>,
1367
1367
+
mime_type: &str,
1368
1368
+
) -> AppResult<jacquard_common::types::blob::Blob> {
1369
1369
+
agent
1370
1370
+
.upload_blob(bytes, MimeType::new(mime_type))
1371
1371
+
.await
1372
1372
+
.map_err(|e| agent_error("upload blob", e))
1373
1373
+
}
1374
1374
+
1375
1375
+
async fn agent_delete_record<R>(agent: &UserAgent, rkey: RecordKey<Rkey>) -> AppResult<()>
1376
1376
+
where
1377
1377
+
R: Collection + Serialize,
1378
1378
+
{
1379
1379
+
agent
1380
1380
+
.delete_record::<R>(rkey)
1381
1381
+
.await
1382
1382
+
.map(|_| ())
1383
1383
+
.map_err(|e| agent_error("delete record", e))
1384
1384
+
}
1385
1385
+
1386
1386
+
struct UploadPersistence<'a> {
1387
1387
+
upload_id: &'a str,
1388
1388
+
digest: &'a str,
1389
1389
+
mime_type: &'a str,
1390
1390
+
size: usize,
1391
1391
+
filename: Option<&'a str>,
1392
1392
+
file: &'a File,
1393
1393
+
}
1394
1394
+
1395
1395
+
async fn persist_upload(
1396
1396
+
state: &AppState,
1397
1397
+
actor: &Did,
1398
1398
+
upload: UploadPersistence<'_>,
1399
1399
+
) -> AppResult<()> {
1400
1400
+
let file_json = serde_json::to_string(upload.file)
1401
1401
+
.map_err(|e| internal(format!("file metadata serialize: {e}")))?;
1402
1402
+
let chunks_json = serde_json::to_string(&upload.file.chunks)
1403
1403
+
.map_err(|e| internal(format!("chunk metadata serialize: {e}")))?;
1404
1404
+
let now = chrono::Utc::now().timestamp_nanos_opt().unwrap_or_default();
1405
1405
+
db(sqlx::query!(
1406
1406
+
"INSERT INTO upload_staging (owner_did, upload_id, sha256, mime_type, size, filename, status, file_json, chunks_json, created_at, updated_at) \
1407
1407
+
VALUES (?, ?, ?, ?, ?, ?, 'uploaded', ?, ?, ?, ?) \
1408
1408
+
ON CONFLICT(owner_did, upload_id) DO UPDATE SET sha256 = excluded.sha256, mime_type = excluded.mime_type, size = excluded.size, filename = excluded.filename, status = excluded.status, file_json = excluded.file_json, chunks_json = excluded.chunks_json, updated_at = excluded.updated_at",
1409
1409
+
actor.as_ref(),
1410
1410
+
upload.upload_id,
1411
1411
+
upload.digest,
1412
1412
+
upload.mime_type,
1413
1413
+
upload.size as i64,
1414
1414
+
upload.filename,
1415
1415
+
file_json,
1416
1416
+
chunks_json,
1417
1417
+
now,
1418
1418
+
now,
1419
1419
+
).execute(&state.pool).await)?;
1420
1420
+
Ok(())
1421
1421
+
}
1422
1422
+
1423
1423
+
async fn load_upload_file(state: &AppState, actor: &Did, upload_id: &str) -> AppResult<File> {
1424
1424
+
#[derive(sqlx::FromRow)]
1425
1425
+
struct Row {
1426
1426
+
file_json: String,
1427
1427
+
}
1428
1428
+
let row = db(sqlx::query_as!(Row, "SELECT file_json FROM upload_staging WHERE owner_did = ? AND upload_id = ? AND status = 'uploaded'", actor.as_ref(), upload_id).fetch_optional(&state.pool).await)?
1429
1429
+
.ok_or_else(|| invalid_request("unknown or unavailable uploadId"))?;
1430
1430
+
serde_json::from_str(&row.file_json)
1431
1431
+
.map_err(|e| internal(format!("staged file metadata parse: {e}")))
1432
1432
+
}
1433
1433
+
1434
1434
+
async fn lookup_social_uri(
1435
1435
+
state: &AppState,
1436
1436
+
actor: &Did,
1437
1437
+
collection: &str,
1438
1438
+
subject_uri: &str,
1439
1439
+
) -> AppResult<AtUri> {
1440
1440
+
lookup_existing_social(state, actor, collection, subject_uri)
1441
1441
+
.await?
1442
1442
+
.map(|record| record.uri)
1443
1443
+
.ok_or_else(|| invalid_request("social record is not indexed locally"))
1444
1444
+
}
1445
1445
+
1446
1446
+
struct ExistingSocial {
1447
1447
+
cid: Cid,
1448
1448
+
uri: AtUri,
1449
1449
+
}
1450
1450
+
1451
1451
+
async fn lookup_existing_social(
1452
1452
+
state: &AppState,
1453
1453
+
actor: &Did,
1454
1454
+
collection: &str,
1455
1455
+
subject_uri: &str,
1456
1456
+
) -> AppResult<Option<ExistingSocial>> {
1457
1457
+
#[derive(sqlx::FromRow)]
1458
1458
+
struct Row {
1459
1459
+
cid: String,
1460
1460
+
rkey: String,
1461
1461
+
}
1462
1462
+
let row = match collection {
1463
1463
+
LIKE_COLLECTION => db(sqlx::query_as!(
1464
1464
+
Row,
1465
1465
+
"SELECT cid, rkey FROM likes WHERE did = ? AND subject_uri = ?",
1466
1466
+
actor.as_ref(),
1467
1467
+
subject_uri
1468
1468
+
)
1469
1469
+
.fetch_optional(&state.pool)
1470
1470
+
.await)?,
1471
1471
+
SAVE_COLLECTION => db(sqlx::query_as!(
1472
1472
+
Row,
1473
1473
+
"SELECT cid, rkey FROM saves WHERE did = ? AND subject_uri = ?",
1474
1474
+
actor.as_ref(),
1475
1475
+
subject_uri
1476
1476
+
)
1477
1477
+
.fetch_optional(&state.pool)
1478
1478
+
.await)?,
1479
1479
+
_ => return Err(invalid_request("unsupported social lookup")),
1480
1480
+
};
1481
1481
+
let existing = row
1482
1482
+
.map(|row| {
1483
1483
+
let uri = at_uri_owned(actor, collection, &row.rkey)?;
1484
1484
+
let cid = Cid::new_owned(row.cid.as_bytes())
1485
1485
+
.map_err(|e| internal(format!("stored social cid is invalid: {e}")))?;
1486
1486
+
Ok(ExistingSocial { cid, uri })
1487
1487
+
})
1488
1488
+
.transpose()?;
1489
1489
+
Ok(existing)
1490
1490
+
}
1491
1491
+
1492
1492
+
fn content_type(headers: &HeaderMap) -> String {
1493
1493
+
headers
1494
1494
+
.get(header::CONTENT_TYPE)
1495
1495
+
.and_then(|v| v.to_str().ok())
1496
1496
+
.and_then(|v| v.split(';').next())
1497
1497
+
.map(str::trim)
1498
1498
+
.filter(|v| !v.is_empty())
1499
1499
+
.unwrap_or("application/octet-stream")
1500
1500
+
.to_string()
1501
1501
+
}
1502
1502
+
1503
1503
+
fn filename_header(headers: &HeaderMap) -> Option<String> {
1504
1504
+
headers
1505
1505
+
.get("x-polymodel-filename")
1506
1506
+
.and_then(|v| v.to_str().ok())
1507
1507
+
.map(str::trim)
1508
1508
+
.filter(|v| !v.is_empty())
1509
1509
+
.map(ToOwned::to_owned)
1510
1510
+
}
1511
1511
+
1512
1512
+
#[cfg(test)]
1513
1513
+
fn hex_sha256(bytes: &[u8]) -> String {
1514
1514
+
let digest = Sha256::digest(bytes);
1515
1515
+
digest.iter().map(|b| format!("{b:02x}")).collect()
1516
1516
+
}
1517
1517
+
1518
1518
+
fn agent_error(operation: &str, err: jacquard::client::AgentError) -> AppError {
1519
1519
+
if agent_error_is_auth(&err) {
1520
1520
+
tracing::warn!(operation, error = %err, "authenticated PDS operation failed authorization");
1521
1521
+
return unauthorized("authenticated PDS operation was not authorized");
1522
1522
+
}
1523
1523
+
tracing::error!(operation, error = %err, "authenticated PDS operation failed");
1524
1524
+
internal("authenticated PDS operation failed")
1525
1525
+
}
1526
1526
+
1527
1527
+
fn client_error(operation: &str, err: jacquard_common::error::ClientError) -> AppError {
1528
1528
+
let text = format!("{err:?}");
1529
1529
+
if text.contains("Auth") || text.contains("Unauthorized") || text.contains("401") {
1530
1530
+
tracing::warn!(operation, error = %err, "authenticated PDS read failed authorization");
1531
1531
+
return unauthorized("authenticated PDS read was not authorized");
1532
1532
+
}
1533
1533
+
tracing::error!(operation, error = %err, "authenticated PDS read failed");
1534
1534
+
internal("authenticated PDS read failed")
1535
1535
+
}
1536
1536
+
1537
1537
+
fn client_error_is_not_found(err: &jacquard_common::error::ClientError) -> bool {
1538
1538
+
let text = format!("{err:?}");
1539
1539
+
text.contains("RecordNotFound") || text.contains("not found") || text.contains("404")
1540
1540
+
}
1541
1541
+
1542
1542
+
fn agent_error_is_auth(err: &jacquard::client::AgentError) -> bool {
1543
1543
+
format!("{err:?}").contains("Auth")
1544
1544
+
}
1545
1545
+
1546
1546
+
fn agent_error_is_not_found(err: &jacquard::client::AgentError) -> bool {
1547
1547
+
let text = format!("{err:?}");
1548
1548
+
text.contains("RecordNotFound") || text.contains("not found") || text.contains("404")
1549
1549
+
}
1550
1550
+
1551
1551
+
fn agent_error_is_conflict(err: &jacquard::client::AgentError) -> bool {
1552
1552
+
let text = format!("{err:?}");
1553
1553
+
text.contains("AlreadyExists") || text.contains("InvalidSwap") || text.contains("409")
1554
1554
+
}
1555
1555
+
1556
1556
+
#[cfg(test)]
1557
1557
+
mod tests {
1558
1558
+
use std::str::FromStr;
1559
1559
+
1560
1560
+
use jacquard_common::types::string::Did;
1561
1561
+
use serde_json::json;
1562
1562
+
use sqlx::SqlitePool;
1563
1563
+
use sqlx::sqlite::{SqliteConnectOptions, SqlitePoolOptions};
1564
1564
+
1565
1565
+
use super::*;
1566
1566
+
1567
1567
+
const DID_A: &str = "did:plc:aaaaaaaaaaaaaaaaaaaaaaaa";
1568
1568
+
const DID_B: &str = "did:plc:bbbbbbbbbbbbbbbbbbbbbbbb";
1569
1569
+
1570
1570
+
async fn state() -> AppState {
1571
1571
+
let options = SqliteConnectOptions::from_str("sqlite::memory:").unwrap();
1572
1572
+
let pool = SqlitePoolOptions::new()
1573
1573
+
.max_connections(1)
1574
1574
+
.connect_with(options)
1575
1575
+
.await
1576
1576
+
.unwrap();
1577
1577
+
sqlx::migrate!("./migrations").run(&pool).await.unwrap();
1578
1578
+
let bootstrap = crate::oauth::bootstrap_oauth(pool.clone(), Some("http://localhost"))
1579
1579
+
.expect("ephemeral OAuth bootstrap for tests");
1580
1580
+
AppState::new(pool, bootstrap)
1581
1581
+
}
1582
1582
+
1583
1583
+
fn did(s: &str) -> Did {
1584
1584
+
Did::new_owned(s).unwrap()
1585
1585
+
}
1586
1586
+
1587
1587
+
fn uri(did: &str, collection: &str, rkey: &str) -> String {
1588
1588
+
format!("at://{did}/{collection}/{rkey}")
1589
1589
+
}
1590
1590
+
1591
1591
+
async fn seed_content_row(
1592
1592
+
pool: &SqlitePool,
1593
1593
+
table: &str,
1594
1594
+
did: &str,
1595
1595
+
rkey: &str,
1596
1596
+
name: &str,
1597
1597
+
) -> String {
1598
1598
+
let collection = match table {
1599
1599
+
"things" => THING_COLLECTION,
1600
1600
+
"models" => MODEL_COLLECTION,
1601
1601
+
"parts" => PART_COLLECTION,
1602
1602
+
_ => unreachable!(),
1603
1603
+
};
1604
1604
+
let uri = uri(did, collection, rkey);
1605
1605
+
let now = chrono::Utc::now().timestamp_nanos_opt().unwrap();
1606
1606
+
match table {
1607
1607
+
"things" => {
1608
1608
+
sqlx::query(
1609
1609
+
"INSERT INTO things (did, rkey, uri, cid, name, summary, license, tags_json, tags_text, instructions_text, cover_json, derived_from_uri, created_at, indexed_at, record_json) VALUES (?, ?, ?, 'bafycid', ?, NULL, 'CC-BY-4.0', NULL, NULL, NULL, NULL, NULL, ?, ?, ?)",
1610
1610
+
)
1611
1611
+
.bind(did)
1612
1612
+
.bind(rkey)
1613
1613
+
.bind(&uri)
1614
1614
+
.bind(name)
1615
1615
+
.bind(now)
1616
1616
+
.bind(now)
1617
1617
+
.bind(json!({"name": name, "license": "CC-BY-4.0", "models": []}).to_string())
1618
1618
+
.execute(pool)
1619
1619
+
.await
1620
1620
+
.unwrap();
1621
1621
+
}
1622
1622
+
"models" => {
1623
1623
+
sqlx::query(
1624
1624
+
"INSERT INTO models (did, rkey, uri, cid, name, summary, created_at, indexed_at, record_json) VALUES (?, ?, ?, 'bafycid', ?, NULL, ?, ?, ?)",
1625
1625
+
)
1626
1626
+
.bind(did)
1627
1627
+
.bind(rkey)
1628
1628
+
.bind(&uri)
1629
1629
+
.bind(name)
1630
1630
+
.bind(now)
1631
1631
+
.bind(now)
1632
1632
+
.bind(json!({"name": name, "parts": []}).to_string())
1633
1633
+
.execute(pool)
1634
1634
+
.await
1635
1635
+
.unwrap();
1636
1636
+
}
1637
1637
+
"parts" => {
1638
1638
+
let file_json =
1639
1639
+
json!({"mimeType": "model/stl", "size": 1, "chunks": []}).to_string();
1640
1640
+
sqlx::query(
1641
1641
+
"INSERT INTO parts (did, rkey, uri, cid, name, format, file_json, created_at, indexed_at, record_json) VALUES (?, ?, ?, 'bafycid', ?, 'stl', ?, ?, ?, ?)",
1642
1642
+
)
1643
1643
+
.bind(did)
1644
1644
+
.bind(rkey)
1645
1645
+
.bind(&uri)
1646
1646
+
.bind(name)
1647
1647
+
.bind(&file_json)
1648
1648
+
.bind(now)
1649
1649
+
.bind(now)
1650
1650
+
.bind(json!({"name": name, "file": {"mimeType": "model/stl", "size": 1, "chunks": []}}).to_string())
1651
1651
+
.execute(pool)
1652
1652
+
.await
1653
1653
+
.unwrap();
1654
1654
+
}
1655
1655
+
_ => unreachable!(),
1656
1656
+
}
1657
1657
+
uri
1658
1658
+
}
1659
1659
+
1660
1660
+
#[tokio::test]
1661
1661
+
async fn upload_staging_round_trips_owner_scoped_file_metadata() {
1662
1662
+
let state = state().await;
1663
1663
+
let actor = did(DID_A);
1664
1664
+
let bytes = b"solid cube";
1665
1665
+
let digest = hex_sha256(bytes);
1666
1666
+
let file = File {
1667
1667
+
chunks: Vec::new(),
1668
1668
+
digest: Some(SmolStr::from(digest.clone())),
1669
1669
+
mime_type: SmolStr::from("model/stl"),
1670
1670
+
size: bytes.len() as i64,
1671
1671
+
extra_data: None,
1672
1672
+
};
1673
1673
+
1674
1674
+
persist_upload(
1675
1675
+
&state,
1676
1676
+
&actor,
1677
1677
+
UploadPersistence {
1678
1678
+
upload_id: "upload-1",
1679
1679
+
digest: &digest,
1680
1680
+
mime_type: "model/stl",
1681
1681
+
size: bytes.len(),
1682
1682
+
filename: Some("cube.stl"),
1683
1683
+
file: &file,
1684
1684
+
},
1685
1685
+
)
1686
1686
+
.await
1687
1687
+
.unwrap();
1688
1688
+
1689
1689
+
let loaded = load_upload_file(&state, &actor, "upload-1").await.unwrap();
1690
1690
+
assert_eq!(loaded.digest.as_deref(), Some(digest.as_str()));
1691
1691
+
assert_eq!(loaded.mime_type.as_str(), "model/stl");
1692
1692
+
assert_eq!(loaded.size, bytes.len() as i64);
1693
1693
+
assert!(
1694
1694
+
load_upload_file(&state, &did(DID_B), "upload-1")
1695
1695
+
.await
1696
1696
+
.is_err()
1697
1697
+
);
1698
1698
+
}
1699
1699
+
1700
1700
+
#[tokio::test]
1701
1701
+
async fn existing_like_lookup_returns_uri_and_cid_for_idempotent_duplicate_create() {
1702
1702
+
let state = state().await;
1703
1703
+
let actor = did(DID_A);
1704
1704
+
let subject = seed_content_row(&state.pool, "things", DID_A, "thing1", "Thing").await;
1705
1705
+
let cid = "bafyreiaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
1706
1706
+
sqlx::query(
1707
1707
+
"INSERT INTO likes (did, rkey, cid, subject_uri, created_at) VALUES (?, ?, ?, ?, 1)",
1708
1708
+
)
1709
1709
+
.bind(DID_A)
1710
1710
+
.bind("like1")
1711
1711
+
.bind(cid)
1712
1712
+
.bind(&subject)
1713
1713
+
.execute(&state.pool)
1714
1714
+
.await
1715
1715
+
.unwrap();
1716
1716
+
1717
1717
+
let existing = lookup_existing_social(&state, &actor, LIKE_COLLECTION, &subject)
1718
1718
+
.await
1719
1719
+
.unwrap()
1720
1720
+
.expect("like should be indexed");
1721
1721
+
assert_eq!(existing.uri.as_ref(), uri(DID_A, LIKE_COLLECTION, "like1"));
1722
1722
+
assert_eq!(existing.cid.to_string(), cid);
1723
1723
+
}
1724
1724
+
1725
1725
+
#[tokio::test]
1726
1726
+
async fn subject_validation_accepts_indexed_content_and_rejects_unknowns() {
1727
1727
+
let state = state().await;
1728
1728
+
let thing = seed_content_row(&state.pool, "things", DID_A, "thing1", "Thing").await;
1729
1729
+
let thing_uri = AtUri::new_owned(&thing).unwrap();
1730
1730
+
validate_subject(&state, &thing_uri).await.unwrap();
1731
1731
+
1732
1732
+
let missing = AtUri::new_owned(uri(DID_A, THING_COLLECTION, "missing")).unwrap();
1733
1733
+
assert!(validate_subject(&state, &missing).await.is_err());
1734
1734
+
let unsupported = AtUri::new_owned(uri(DID_A, LIKE_COLLECTION, "like1")).unwrap();
1735
1735
+
assert!(validate_subject(&state, &unsupported).await.is_err());
1736
1736
+
}
1737
1737
+
1738
1738
+
#[tokio::test]
1739
1739
+
async fn composition_load_requires_all_existing_children_to_be_owned() {
1740
1740
+
let state = state().await;
1741
1741
+
let thing = seed_content_row(&state.pool, "things", DID_A, "thing1", "Thing").await;
1742
1742
+
let model = seed_content_row(&state.pool, "models", DID_A, "model1", "Model").await;
1743
1743
+
let part = seed_content_row(&state.pool, "parts", DID_A, "part1", "Part").await;
1744
1744
+
sqlx::query("INSERT INTO thing_models (thing_uri, model_uri, position) VALUES (?, ?, 0)")
1745
1745
+
.bind(&thing)
1746
1746
+
.bind(&model)
1747
1747
+
.execute(&state.pool)
1748
1748
+
.await
1749
1749
+
.unwrap();
1750
1750
+
sqlx::query("INSERT INTO model_parts (model_uri, part_uri, position) VALUES (?, ?, 0)")
1751
1751
+
.bind(&model)
1752
1752
+
.bind(&part)
1753
1753
+
.execute(&state.pool)
1754
1754
+
.await
1755
1755
+
.unwrap();
1756
1756
+
1757
1757
+
let composition =
1758
1758
+
load_owned_composition(&state, &did(DID_A), &AtUri::new_owned(&thing).unwrap())
1759
1759
+
.await
1760
1760
+
.unwrap();
1761
1761
+
assert_eq!(composition.models.len(), 1);
1762
1762
+
assert_eq!(composition.parts.len(), 1);
1763
1763
+
1764
1764
+
sqlx::query("UPDATE parts SET did = ? WHERE uri = ?")
1765
1765
+
.bind(DID_B)
1766
1766
+
.bind(&part)
1767
1767
+
.execute(&state.pool)
1768
1768
+
.await
1769
1769
+
.unwrap();
1770
1770
+
assert!(
1771
1771
+
load_owned_composition(&state, &did(DID_A), &AtUri::new_owned(&thing).unwrap())
1772
1772
+
.await
1773
1773
+
.is_err()
1774
1774
+
);
1775
1775
+
}
1776
1776
+
1777
1777
+
#[tokio::test]
1778
1778
+
async fn update_child_validation_allows_owned_detached_records_to_be_reattached() {
1779
1779
+
let state = state().await;
1780
1780
+
let actor = did(DID_A);
1781
1781
+
let thing = seed_content_row(&state.pool, "things", DID_A, "thing1", "Thing").await;
1782
1782
+
let active_model = seed_content_row(&state.pool, "models", DID_A, "model1", "Model").await;
1783
1783
+
let detached_model =
1784
1784
+
seed_content_row(&state.pool, "models", DID_A, "model2", "Detached").await;
1785
1785
+
let foreign_model =
1786
1786
+
seed_content_row(&state.pool, "models", DID_B, "model3", "Foreign").await;
1787
1787
+
sqlx::query("INSERT INTO thing_models (thing_uri, model_uri, position) VALUES (?, ?, 0)")
1788
1788
+
.bind(&thing)
1789
1789
+
.bind(&active_model)
1790
1790
+
.execute(&state.pool)
1791
1791
+
.await
1792
1792
+
.unwrap();
1793
1793
+
1794
1794
+
let previous = load_owned_composition(&state, &actor, &AtUri::new_owned(&thing).unwrap())
1795
1795
+
.await
1796
1796
+
.unwrap();
1797
1797
+
let detached_uri = AtUri::new_owned(&detached_model).unwrap();
1798
1798
+
let detached_rkey = rkey_from_uri(&detached_uri).unwrap();
1799
1799
+
validate_existing_update_child(
1800
1800
+
&state,
1801
1801
+
&actor,
1802
1802
+
&detached_uri,
1803
1803
+
&detached_rkey,
1804
1804
+
MODEL_COLLECTION,
1805
1805
+
&previous.models,
1806
1806
+
)
1807
1807
+
.await
1808
1808
+
.unwrap();
1809
1809
+
1810
1810
+
let foreign_uri = AtUri::new_owned(&foreign_model).unwrap();
1811
1811
+
let foreign_rkey = rkey_from_uri(&foreign_uri).unwrap();
1812
1812
+
assert!(
1813
1813
+
validate_existing_update_child(
1814
1814
+
&state,
1815
1815
+
&actor,
1816
1816
+
&foreign_uri,
1817
1817
+
&foreign_rkey,
1818
1818
+
MODEL_COLLECTION,
1819
1819
+
&previous.models,
1820
1820
+
)
1821
1821
+
.await
1822
1822
+
.is_err()
1823
1823
+
);
1824
1824
+
}
1825
1825
+
1826
1826
+
#[tokio::test]
1827
1827
+
async fn child_reference_counts_identify_shared_delete_children() {
1828
1828
+
let state = state().await;
1829
1829
+
let thing_a = seed_content_row(&state.pool, "things", DID_A, "thing1", "Thing A").await;
1830
1830
+
let thing_b = seed_content_row(&state.pool, "things", DID_A, "thing2", "Thing B").await;
1831
1831
+
let model = seed_content_row(&state.pool, "models", DID_A, "model1", "Model").await;
1832
1832
+
let part = seed_content_row(&state.pool, "parts", DID_A, "part1", "Part").await;
1833
1833
+
sqlx::query("INSERT INTO thing_models (thing_uri, model_uri, position) VALUES (?, ?, 0)")
1834
1834
+
.bind(&thing_a)
1835
1835
+
.bind(&model)
1836
1836
+
.execute(&state.pool)
1837
1837
+
.await
1838
1838
+
.unwrap();
1839
1839
+
sqlx::query("INSERT INTO thing_models (thing_uri, model_uri, position) VALUES (?, ?, 0)")
1840
1840
+
.bind(&thing_b)
1841
1841
+
.bind(&model)
1842
1842
+
.execute(&state.pool)
1843
1843
+
.await
1844
1844
+
.unwrap();
1845
1845
+
sqlx::query("INSERT INTO model_parts (model_uri, part_uri, position) VALUES (?, ?, 0)")
1846
1846
+
.bind(&model)
1847
1847
+
.bind(&part)
1848
1848
+
.execute(&state.pool)
1849
1849
+
.await
1850
1850
+
.unwrap();
1851
1851
+
1852
1852
+
assert_eq!(
1853
1853
+
model_reference_count(&state, &AtUri::new_owned(&model).unwrap())
1854
1854
+
.await
1855
1855
+
.unwrap(),
1856
1856
+
2
1857
1857
+
);
1858
1858
+
assert_eq!(
1859
1859
+
part_reference_count(&state, &AtUri::new_owned(&part).unwrap())
1860
1860
+
.await
1861
1861
+
.unwrap(),
1862
1862
+
1
1863
1863
+
);
1864
1864
+
}
1865
1865
+
1866
1866
+
#[tokio::test]
1867
1867
+
async fn child_reference_counts_preserve_parts_reused_across_models() {
1868
1868
+
let state = state().await;
1869
1869
+
let thing = seed_content_row(&state.pool, "things", DID_A, "thing1", "Thing").await;
1870
1870
+
let model_a = seed_content_row(&state.pool, "models", DID_A, "model1", "Model A").await;
1871
1871
+
let model_b = seed_content_row(&state.pool, "models", DID_A, "model2", "Model B").await;
1872
1872
+
let part = seed_content_row(&state.pool, "parts", DID_A, "part1", "Shared Part").await;
1873
1873
+
for (position, model) in [(0, &model_a), (1, &model_b)] {
1874
1874
+
sqlx::query(
1875
1875
+
"INSERT INTO thing_models (thing_uri, model_uri, position) VALUES (?, ?, ?)",
1876
1876
+
)
1877
1877
+
.bind(&thing)
1878
1878
+
.bind(model)
1879
1879
+
.bind(position)
1880
1880
+
.execute(&state.pool)
1881
1881
+
.await
1882
1882
+
.unwrap();
1883
1883
+
sqlx::query("INSERT INTO model_parts (model_uri, part_uri, position) VALUES (?, ?, 0)")
1884
1884
+
.bind(model)
1885
1885
+
.bind(&part)
1886
1886
+
.execute(&state.pool)
1887
1887
+
.await
1888
1888
+
.unwrap();
1889
1889
+
}
1890
1890
+
1891
1891
+
let composition =
1892
1892
+
load_owned_composition(&state, &did(DID_A), &AtUri::new_owned(&thing).unwrap())
1893
1893
+
.await
1894
1894
+
.unwrap();
1895
1895
+
assert_eq!(composition.models.len(), 2);
1896
1896
+
assert_eq!(
1897
1897
+
composition.parts.len(),
1898
1898
+
1,
1899
1899
+
"reused part should be deduped once"
1900
1900
+
);
1901
1901
+
assert_eq!(
1902
1902
+
part_reference_count(&state, &AtUri::new_owned(&part).unwrap())
1903
1903
+
.await
1904
1904
+
.unwrap(),
1905
1905
+
2
1906
1906
+
);
1907
1907
+
}
1908
1908
+
}
···
107
107
Ok(())
108
108
}
109
109
110
110
+
/// Eagerly project a locally confirmed write without advancing the Hydrant
111
111
+
/// cursor.
112
112
+
///
113
113
+
/// PM-28 write handlers call this only after the user's PDS has accepted the
114
114
+
/// record/blob operation. Hydrant/firehose will later deliver the same record
115
115
+
/// event; sharing [`project_record`] keeps that convergence idempotent and avoids
116
116
+
/// a subtly different SQL path for read-your-own-writes.
117
117
+
pub async fn project_eager_record(db: &SqlitePool, event: &ProjectionEvent) -> anyhow::Result<()> {
118
118
+
let mut tx = db.begin().await?;
119
119
+
project_record(&mut tx, event).await?;
120
120
+
tx.commit().await?;
121
121
+
Ok(())
122
122
+
}
123
123
+
110
124
/// Dispatch a record event to its collection-specific projection rule.
111
125
async fn project_record(
112
126
tx: &mut Transaction<'_, Sqlite>,
···
600
614
let Some(subject) = ref_uri(rec, "subject") else {
601
615
return Ok(());
602
616
};
617
617
+
let Some(cid) = e.cid.clone() else {
618
618
+
return Ok(());
619
619
+
};
603
620
let created_at = parse_nanos(rec, "createdAt").unwrap_or_else(now_nanos);
604
621
605
622
let res = sqlx::query!(
606
606
-
"INSERT OR IGNORE INTO likes (did, rkey, subject_uri, created_at) VALUES (?, ?, ?, ?)",
623
623
+
"INSERT OR IGNORE INTO likes (did, rkey, cid, subject_uri, created_at) VALUES (?, ?, ?, ?, ?)",
607
624
e.did,
608
625
e.rkey,
626
626
+
cid,
609
627
subject,
610
628
created_at,
611
629
)
···
668
686
let Some(subject) = ref_uri(rec, "subject") else {
669
687
return Ok(());
670
688
};
689
689
+
let Some(cid) = e.cid.clone() else {
690
690
+
return Ok(());
691
691
+
};
671
692
let note = s_str(rec, "note");
672
693
let created_at = parse_nanos(rec, "createdAt").unwrap_or_else(now_nanos);
673
694
674
695
let res = sqlx::query!(
675
675
-
"INSERT OR IGNORE INTO saves (did, rkey, subject_uri, note, created_at) \
676
676
-
VALUES (?, ?, ?, ?, ?)",
696
696
+
"INSERT OR IGNORE INTO saves (did, rkey, cid, subject_uri, note, created_at) \
697
697
+
VALUES (?, ?, ?, ?, ?, ?)",
677
698
e.did,
678
699
e.rkey,
700
700
+
cid,
679
701
subject,
680
702
note,
681
703
created_at,
···
742
764
let Some(tag) = s_str(rec, "tag") else {
743
765
return Ok(());
744
766
};
767
767
+
let Some(cid) = e.cid.clone() else {
768
768
+
return Ok(());
769
769
+
};
745
770
let created_at = parse_nanos(rec, "createdAt").unwrap_or_else(now_nanos);
746
771
747
772
let res = sqlx::query!(
748
748
-
"INSERT OR IGNORE INTO tags (did, rkey, subject_uri, tag, created_at) \
749
749
-
VALUES (?, ?, ?, ?, ?)",
773
773
+
"INSERT OR IGNORE INTO tags (did, rkey, cid, subject_uri, tag, created_at) \
774
774
+
VALUES (?, ?, ?, ?, ?, ?)",
750
775
e.did,
751
776
e.rkey,
777
777
+
cid,
752
778
subject,
753
779
tag,
754
780
created_at,
···
811
837
let Some(subject_uri) = ref_uri(rec, "subject") else {
812
838
return Ok(());
813
839
};
840
840
+
let Some(cid) = e.cid.clone() else {
841
841
+
return Ok(());
842
842
+
};
814
843
let created_at = parse_nanos(rec, "createdAt").unwrap_or_else(now_nanos);
815
844
816
845
// No denormalized count for listitems; list membership is queried directly.
817
846
sqlx::query!(
818
818
-
"INSERT OR IGNORE INTO listitems (did, rkey, list_uri, subject_uri, created_at) \
819
819
-
VALUES (?, ?, ?, ?, ?)",
847
847
+
"INSERT OR IGNORE INTO listitems (did, rkey, cid, list_uri, subject_uri, created_at) \
848
848
+
VALUES (?, ?, ?, ?, ?, ?)",
820
849
e.did,
821
850
e.rkey,
851
851
+
cid,
822
852
list_uri,
823
853
subject_uri,
824
854
created_at,