+63
-1
Cargo.lock
+63
-1
Cargo.lock
···
31
31
]
32
32
33
33
[[package]]
34
+
name = "aliasable"
35
+
version = "0.1.3"
36
+
source = "registry+https://github.com/rust-lang/crates.io-index"
37
+
checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd"
38
+
39
+
[[package]]
34
40
name = "android_system_properties"
35
41
version = "0.1.5"
36
42
source = "registry+https://github.com/rust-lang/crates.io-index"
···
275
281
source = "registry+https://github.com/rust-lang/crates.io-index"
276
282
checksum = "bbfd7eae0b0f1a6e63d4b13c9c478de77c2eb546fba158ad50b4203dc24b9f9c"
277
283
dependencies = [
278
-
"heck",
284
+
"heck 0.5.0",
279
285
"proc-macro2",
280
286
"quote",
281
287
"syn 2.0.106",
···
504
510
505
511
[[package]]
506
512
name = "heck"
513
+
version = "0.4.1"
514
+
source = "registry+https://github.com/rust-lang/crates.io-index"
515
+
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
516
+
517
+
[[package]]
518
+
name = "heck"
507
519
version = "0.5.0"
508
520
source = "registry+https://github.com/rust-lang/crates.io-index"
509
521
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
···
718
730
"miette",
719
731
"multibase",
720
732
"multihash",
733
+
"ouroboros",
721
734
"regex",
722
735
"serde",
723
736
"serde_html_form",
···
865
878
checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad"
866
879
867
880
[[package]]
881
+
name = "ouroboros"
882
+
version = "0.18.5"
883
+
source = "registry+https://github.com/rust-lang/crates.io-index"
884
+
checksum = "1e0f050db9c44b97a94723127e6be766ac5c340c48f2c4bb3ffa11713744be59"
885
+
dependencies = [
886
+
"aliasable",
887
+
"ouroboros_macro",
888
+
"static_assertions",
889
+
]
890
+
891
+
[[package]]
892
+
name = "ouroboros_macro"
893
+
version = "0.18.5"
894
+
source = "registry+https://github.com/rust-lang/crates.io-index"
895
+
checksum = "3c7028bdd3d43083f6d8d4d5187680d0d3560d54df4cc9d752005268b41e64d0"
896
+
dependencies = [
897
+
"heck 0.4.1",
898
+
"proc-macro2",
899
+
"proc-macro2-diagnostics",
900
+
"quote",
901
+
"syn 2.0.106",
902
+
]
903
+
904
+
[[package]]
868
905
name = "percent-encoding"
869
906
version = "2.3.2"
870
907
source = "registry+https://github.com/rust-lang/crates.io-index"
···
916
953
checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
917
954
dependencies = [
918
955
"unicode-ident",
956
+
]
957
+
958
+
[[package]]
959
+
name = "proc-macro2-diagnostics"
960
+
version = "0.10.1"
961
+
source = "registry+https://github.com/rust-lang/crates.io-index"
962
+
checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8"
963
+
dependencies = [
964
+
"proc-macro2",
965
+
"quote",
966
+
"syn 2.0.106",
967
+
"version_check",
968
+
"yansi",
919
969
]
920
970
921
971
[[package]]
···
1179
1229
"syn 2.0.106",
1180
1230
"thiserror 1.0.69",
1181
1231
]
1232
+
1233
+
[[package]]
1234
+
name = "static_assertions"
1235
+
version = "1.1.0"
1236
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1237
+
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
1182
1238
1183
1239
[[package]]
1184
1240
name = "strsim"
···
1550
1606
version = "0.6.1"
1551
1607
source = "registry+https://github.com/rust-lang/crates.io-index"
1552
1608
checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb"
1609
+
1610
+
[[package]]
1611
+
name = "yansi"
1612
+
version = "1.0.1"
1613
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1614
+
checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
1553
1615
1554
1616
[[package]]
1555
1617
name = "yoke"
+1
crates/jacquard-common/Cargo.toml
+1
crates/jacquard-common/Cargo.toml
+465
-88
crates/jacquard-common/src/types/aturi.rs
+465
-88
crates/jacquard-common/src/types/aturi.rs
···
1
-
use crate::CowStr;
2
1
use crate::types::ident::AtIdentifier;
3
2
use crate::types::nsid::Nsid;
4
3
use crate::types::recordkey::{RecordKey, Rkey};
5
4
use crate::types::string::AtStrError;
5
+
use crate::{CowStr, IntoStatic};
6
6
use regex::Regex;
7
7
use serde::Serializer;
8
8
use serde::{Deserialize, Deserializer, Serialize, de::Error};
9
9
use smol_str::{SmolStr, ToSmolStr};
10
10
use std::fmt;
11
+
use std::hash::{Hash, Hasher};
11
12
use std::sync::LazyLock;
12
13
use std::{ops::Deref, str::FromStr};
13
14
···
15
16
///
16
17
/// based on the regex here: https://github.com/bluesky-social/atproto/blob/main/packages/syntax/src/aturi_validation.ts
17
18
///
18
-
/// Doesn't support the query segment, but then neither does the Typescript SDK
19
-
///
20
-
/// TODO: support IntoStatic on string types. For composites like this where all borrow from (present) input,
21
-
/// perhaps use some careful unsafe to launder the lifetimes.
22
-
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
19
+
/// Doesn't support the query segment, but then neither does the Typescript SDK.
20
+
#[derive(PartialEq, Eq, Debug)]
23
21
pub struct AtUri<'u> {
22
+
inner: Inner<'u>,
23
+
}
24
+
25
+
#[ouroboros::self_referencing]
26
+
#[derive(PartialEq, Eq, Debug)]
27
+
struct Inner<'u> {
24
28
uri: CowStr<'u>,
25
-
pub authority: AtIdentifier<'u>,
26
-
pub path: Option<UriPath<'u>>,
27
-
pub fragment: Option<CowStr<'u>>,
29
+
#[borrows(uri)]
30
+
#[covariant]
31
+
pub authority: AtIdentifier<'this>,
32
+
#[borrows(uri)]
33
+
#[covariant]
34
+
pub path: Option<UriPath<'this>>,
35
+
#[borrows(uri)]
36
+
#[covariant]
37
+
pub fragment: Option<CowStr<'this>>,
38
+
}
39
+
40
+
impl Clone for AtUri<'_> {
41
+
fn clone(&self) -> Self {
42
+
let uri = self.inner.borrow_uri();
43
+
44
+
Self {
45
+
inner: Inner::new(
46
+
CowStr::Owned(uri.as_ref().to_smolstr()),
47
+
|uri| {
48
+
let parts = ATURI_REGEX.captures(uri).unwrap();
49
+
unsafe { AtIdentifier::unchecked(parts.name("authority").unwrap().as_str()) }
50
+
},
51
+
|uri| {
52
+
let parts = ATURI_REGEX.captures(uri).unwrap();
53
+
if let Some(collection) = parts.name("collection") {
54
+
let collection = unsafe { Nsid::unchecked(collection.as_str()) };
55
+
let rkey = if let Some(rkey) = parts.name("rkey") {
56
+
let rkey = unsafe { RecordKey::from(Rkey::unchecked(rkey.as_str())) };
57
+
Some(rkey)
58
+
} else {
59
+
None
60
+
};
61
+
Some(UriPath { collection, rkey })
62
+
} else {
63
+
None
64
+
}
65
+
},
66
+
|uri| {
67
+
let parts = ATURI_REGEX.captures(uri).unwrap();
68
+
parts.name("fragment").map(|fragment| {
69
+
let fragment = CowStr::Borrowed(fragment.as_str());
70
+
fragment
71
+
})
72
+
},
73
+
),
74
+
}
75
+
}
76
+
}
77
+
78
+
impl Hash for AtUri<'_> {
79
+
fn hash<H: Hasher>(&self, state: &mut H) {
80
+
self.inner.borrow_uri().hash(state);
81
+
}
28
82
}
29
83
30
84
/// at:// URI path component (current subset)
···
32
86
pub struct UriPath<'u> {
33
87
pub collection: Nsid<'u>,
34
88
pub rkey: Option<RecordKey<Rkey<'u>>>,
89
+
}
90
+
91
+
impl IntoStatic for UriPath<'_> {
92
+
type Output = UriPath<'static>;
93
+
94
+
fn into_static(self) -> Self::Output {
95
+
UriPath {
96
+
collection: self.collection.into_static(),
97
+
rkey: self.rkey.map(|rkey| rkey.into_static()),
98
+
}
99
+
}
35
100
}
36
101
37
102
pub type UriPathBuf = UriPath<'static>;
···
68
133
fragment
69
134
});
70
135
Ok(AtUri {
71
-
uri: CowStr::Borrowed(uri),
72
-
authority,
73
-
path,
74
-
fragment,
136
+
inner: InnerBuilder {
137
+
uri: CowStr::Borrowed(uri),
138
+
authority_builder: |_| authority,
139
+
path_builder: |_| path,
140
+
fragment_builder: |_| fragment,
141
+
}
142
+
.build(),
75
143
})
76
144
} else {
77
145
Err(AtStrError::missing("at-uri-scheme", uri, "authority"))
···
106
174
fragment
107
175
});
108
176
AtUri {
109
-
uri: CowStr::Borrowed(uri),
110
-
authority,
111
-
path,
112
-
fragment,
177
+
inner: InnerBuilder {
178
+
uri: CowStr::Borrowed(uri),
179
+
authority_builder: |_| authority,
180
+
path_builder: |_| path,
181
+
fragment_builder: |_| fragment,
182
+
}
183
+
.build(),
113
184
}
114
185
} else {
115
186
panic!("at:// URI missing authority")
···
119
190
}
120
191
}
121
192
122
-
pub fn new_owned(uri: impl AsRef<str>) -> Result<Self, AtStrError> {
123
-
let uri = uri.as_ref();
193
+
/// Unchecked borrowing constructor. This one does do some validation but if that fails will just
194
+
/// dump everything in the authority field.
195
+
///
196
+
/// TODO: do some fallback splitting, but really, if you use this on something invalid, you deserve it.
197
+
pub unsafe fn unchecked(uri: &'u str) -> Self {
124
198
if let Some(parts) = ATURI_REGEX.captures(uri) {
125
199
if let Some(authority) = parts.name("authority") {
126
-
let authority = AtIdentifier::new_owned(authority.as_str())
127
-
.map_err(|e| AtStrError::wrap("at-uri-scheme", uri.to_string(), e))?;
200
+
let authority = unsafe { AtIdentifier::unchecked(authority.as_str()) };
128
201
let path = if let Some(collection) = parts.name("collection") {
129
-
let collection = Nsid::new_owned(collection.as_str())
130
-
.map_err(|e| AtStrError::wrap("at-uri-scheme", uri.to_string(), e))?;
202
+
let collection = unsafe { Nsid::unchecked(collection.as_str()) };
131
203
let rkey = if let Some(rkey) = parts.name("rkey") {
132
-
let rkey =
133
-
RecordKey::from(Rkey::new_owned(rkey.as_str()).map_err(|e| {
134
-
AtStrError::wrap("at-uri-scheme", uri.to_string(), e)
135
-
})?);
204
+
let rkey = RecordKey::from(unsafe { Rkey::unchecked(rkey.as_str()) });
136
205
Some(rkey)
137
206
} else {
138
207
None
···
142
211
None
143
212
};
144
213
let fragment = parts.name("fragment").map(|fragment| {
145
-
let fragment = CowStr::Owned(fragment.as_str().to_smolstr());
214
+
let fragment = CowStr::Borrowed(fragment.as_str());
146
215
fragment
147
216
});
217
+
AtUri {
218
+
inner: InnerBuilder {
219
+
uri: CowStr::Borrowed(uri),
220
+
authority_builder: |_| authority,
221
+
path_builder: |_| path,
222
+
fragment_builder: |_| fragment,
223
+
}
224
+
.build(),
225
+
}
226
+
} else {
227
+
// let mut uriParts = uri.split('#');
228
+
// let mut parts = uriParts.next().unwrap_or(uri).split('/');
229
+
// let auth = parts.next().unwrap_or(uri);
230
+
Self {
231
+
inner: InnerBuilder {
232
+
uri: CowStr::Borrowed(uri),
233
+
authority_builder: |_| unsafe { AtIdentifier::unchecked(uri) },
234
+
path_builder: |_| None,
235
+
fragment_builder: |_| None,
236
+
}
237
+
.build(),
238
+
}
239
+
}
240
+
} else {
241
+
Self {
242
+
inner: InnerBuilder {
243
+
uri: CowStr::Borrowed(uri),
244
+
authority_builder: |_| unsafe { AtIdentifier::unchecked(uri) },
245
+
path_builder: |_| None,
246
+
fragment_builder: |_| None,
247
+
}
248
+
.build(),
249
+
}
250
+
}
251
+
}
252
+
253
+
/// Clone method that should be O(1) in terms of time
254
+
///
255
+
/// Calling on a borrowed variant will turn it into an owned variant, taking a little
256
+
/// more time and allocating memory for each part. Calling it on an owned variant will
257
+
/// increment all the internal reference counters (or, if constructed from a `&'static str`,
258
+
/// essentially do nothing).
259
+
pub fn fast_clone(&self) -> AtUri<'static> {
260
+
self.inner.with(move |u| {
261
+
let uri = u.uri.clone().into_static();
262
+
let authority = u.authority.clone().into_static();
263
+
let path = u.path.clone().into_static();
264
+
let fragment = u.fragment.clone().into_static();
265
+
AtUri {
266
+
inner: InnerBuilder {
267
+
uri,
268
+
authority_builder: |_| authority,
269
+
path_builder: |_| path,
270
+
fragment_builder: |_| fragment,
271
+
}
272
+
.build(),
273
+
}
274
+
})
275
+
}
276
+
277
+
pub fn as_str(&self) -> &str {
278
+
{
279
+
let this = &self.inner.borrow_uri();
280
+
this
281
+
}
282
+
}
283
+
284
+
pub fn authority(&self) -> &AtIdentifier<'_> {
285
+
self.inner.borrow_authority()
286
+
}
287
+
288
+
pub fn path(&self) -> &Option<UriPath<'_>> {
289
+
self.inner.borrow_path()
290
+
}
291
+
292
+
pub fn fragment(&self) -> &Option<CowStr<'_>> {
293
+
self.inner.borrow_fragment()
294
+
}
295
+
296
+
pub fn collection(&self) -> Option<&Nsid<'_>> {
297
+
self.inner.borrow_path().as_ref().map(|p| &p.collection)
298
+
}
299
+
300
+
pub fn rkey(&self) -> Option<&RecordKey<Rkey<'_>>> {
301
+
self.inner
302
+
.borrow_path()
303
+
.as_ref()
304
+
.and_then(|p| p.rkey.as_ref())
305
+
}
306
+
}
307
+
308
+
impl AtUri<'static> {
309
+
/// Owned constructor
310
+
///
311
+
/// Uses ouroboros self-referential tricks internally to make sure everything
312
+
/// borrows efficiently from the uri `CowStr<'static>`.
313
+
///
314
+
/// Performs validation up-front, but is slower than the borrowing constructor
315
+
/// due to currently having to re-run the main regex, in addition to allocating.
316
+
///
317
+
/// `.into_static()` and Clone implementations have similar limitations.
318
+
///
319
+
/// O(1) clone mathod is AtUri::fast_clone().
320
+
///
321
+
/// Future optimization involves working out the indices borrowed and either using those
322
+
/// to avoid re-computing in some places, or, for a likely fully optimal version, only storing
323
+
/// the indices and constructing the borrowed components unsafely when asked.
324
+
pub fn new_owned(uri: impl AsRef<str>) -> Result<Self, AtStrError> {
325
+
if let Some(parts) = ATURI_REGEX.captures(uri.as_ref()) {
326
+
if let Some(authority) = parts.name("authority") {
327
+
let _authority = AtIdentifier::new(authority.as_str())
328
+
.map_err(|e| AtStrError::wrap("at-uri-scheme", uri.as_ref().to_string(), e))?;
329
+
let path = if let Some(collection) = parts.name("collection") {
330
+
let collection = Nsid::new(collection.as_str()).map_err(|e| {
331
+
AtStrError::wrap("at-uri-scheme", uri.as_ref().to_string(), e)
332
+
})?;
333
+
let rkey = if let Some(rkey) = parts.name("rkey") {
334
+
let rkey = RecordKey::from(Rkey::new(rkey.as_str()).map_err(|e| {
335
+
AtStrError::wrap("at-uri-scheme", uri.as_ref().to_string(), e)
336
+
})?);
337
+
Some(rkey)
338
+
} else {
339
+
None
340
+
};
341
+
Some(UriPath { collection, rkey })
342
+
} else {
343
+
None
344
+
};
345
+
148
346
Ok(AtUri {
149
-
uri: CowStr::Owned(uri.to_smolstr()),
150
-
authority,
151
-
path,
152
-
fragment,
347
+
inner: Inner::new(
348
+
CowStr::Owned(uri.as_ref().to_smolstr()),
349
+
|uri| {
350
+
let parts = ATURI_REGEX.captures(uri).unwrap();
351
+
unsafe {
352
+
AtIdentifier::unchecked(parts.name("authority").unwrap().as_str())
353
+
}
354
+
},
355
+
|uri| {
356
+
if path.is_some() {
357
+
let parts = ATURI_REGEX.captures(uri).unwrap();
358
+
if let Some(collection) = parts.name("collection") {
359
+
let collection =
360
+
unsafe { Nsid::unchecked(collection.as_str()) };
361
+
let rkey = if let Some(rkey) = parts.name("rkey") {
362
+
let rkey = unsafe {
363
+
RecordKey::from(Rkey::unchecked(rkey.as_str()))
364
+
};
365
+
Some(rkey)
366
+
} else {
367
+
None
368
+
};
369
+
Some(UriPath { collection, rkey })
370
+
} else {
371
+
None
372
+
}
373
+
} else {
374
+
None
375
+
}
376
+
},
377
+
|uri| {
378
+
let parts = ATURI_REGEX.captures(uri).unwrap();
379
+
parts.name("fragment").map(|fragment| {
380
+
let fragment = CowStr::Borrowed(fragment.as_str());
381
+
fragment
382
+
})
383
+
},
384
+
),
153
385
})
154
386
} else {
155
-
Err(AtStrError::missing("at-uri-scheme", uri, "authority"))
387
+
Err(AtStrError::missing(
388
+
"at-uri-scheme",
389
+
&uri.as_ref(),
390
+
"authority",
391
+
))
156
392
}
157
393
} else {
158
394
Err(AtStrError::regex(
159
395
"at-uri-scheme",
160
-
uri,
396
+
&uri.as_ref(),
161
397
SmolStr::new_static("doesn't match schema"),
162
398
))
163
399
}
164
400
}
165
401
166
-
pub fn new_static(uri: &'static str) -> Result<AtUri<'static>, AtStrError> {
402
+
pub fn new_static(uri: &'static str) -> Result<Self, AtStrError> {
167
403
let uri = uri.as_ref();
168
404
if let Some(parts) = ATURI_REGEX.captures(uri) {
169
405
if let Some(authority) = parts.name("authority") {
···
190
426
fragment
191
427
});
192
428
Ok(AtUri {
193
-
uri: CowStr::new_static(uri),
194
-
authority,
195
-
path,
196
-
fragment,
429
+
inner: InnerBuilder {
430
+
uri: CowStr::new_static(uri),
431
+
authority_builder: |_| authority,
432
+
path_builder: |_| path,
433
+
fragment_builder: |_| fragment,
434
+
}
435
+
.build(),
197
436
})
198
437
} else {
199
438
Err(AtStrError::missing("at-uri-scheme", uri, "authority"))
···
206
445
))
207
446
}
208
447
}
448
+
}
209
449
210
-
pub unsafe fn unchecked(uri: &'u str) -> Self {
211
-
if let Some(parts) = ATURI_REGEX.captures(uri) {
450
+
impl FromStr for AtUri<'_> {
451
+
type Err = AtStrError;
452
+
453
+
/// Has to take ownership due to the lifetime constraints of the FromStr trait.
454
+
/// Prefer `AtUri::new()` or `AtUri::raw()` if you want to borrow.
455
+
fn from_str(uri: &str) -> Result<Self, Self::Err> {
456
+
if let Some(parts) = ATURI_REGEX.captures(uri.as_ref()) {
212
457
if let Some(authority) = parts.name("authority") {
213
-
let authority = unsafe { AtIdentifier::unchecked(authority.as_str()) };
458
+
let _authority = AtIdentifier::new(authority.as_str())
459
+
.map_err(|e| AtStrError::wrap("at-uri-scheme", uri.to_string(), e))?;
214
460
let path = if let Some(collection) = parts.name("collection") {
215
-
let collection = unsafe { Nsid::unchecked(collection.as_str()) };
461
+
let collection = Nsid::new(collection.as_str())
462
+
.map_err(|e| AtStrError::wrap("at-uri-scheme", uri.to_string(), e))?;
216
463
let rkey = if let Some(rkey) = parts.name("rkey") {
217
-
let rkey = RecordKey::from(unsafe { Rkey::unchecked(rkey.as_str()) });
464
+
let rkey =
465
+
RecordKey::from(Rkey::new(rkey.as_str()).map_err(|e| {
466
+
AtStrError::wrap("at-uri-scheme", uri.to_string(), e)
467
+
})?);
218
468
Some(rkey)
219
469
} else {
220
470
None
···
223
473
} else {
224
474
None
225
475
};
226
-
let fragment = parts.name("fragment").map(|fragment| {
227
-
let fragment = CowStr::Borrowed(fragment.as_str());
228
-
fragment
229
-
});
230
-
AtUri {
231
-
uri: CowStr::Borrowed(uri),
232
-
authority,
233
-
path,
234
-
fragment,
235
-
}
476
+
477
+
Ok(AtUri {
478
+
inner: Inner::new(
479
+
CowStr::Owned(uri.to_smolstr()),
480
+
|uri| {
481
+
let parts = ATURI_REGEX.captures(uri).unwrap();
482
+
unsafe {
483
+
AtIdentifier::unchecked(parts.name("authority").unwrap().as_str())
484
+
}
485
+
},
486
+
|uri| {
487
+
if path.is_some() {
488
+
let parts = ATURI_REGEX.captures(uri).unwrap();
489
+
if let Some(collection) = parts.name("collection") {
490
+
let collection =
491
+
unsafe { Nsid::unchecked(collection.as_str()) };
492
+
let rkey = if let Some(rkey) = parts.name("rkey") {
493
+
let rkey = unsafe {
494
+
RecordKey::from(Rkey::unchecked(rkey.as_str()))
495
+
};
496
+
Some(rkey)
497
+
} else {
498
+
None
499
+
};
500
+
Some(UriPath { collection, rkey })
501
+
} else {
502
+
None
503
+
}
504
+
} else {
505
+
None
506
+
}
507
+
},
508
+
|uri| {
509
+
let parts = ATURI_REGEX.captures(uri).unwrap();
510
+
parts.name("fragment").map(|fragment| {
511
+
let fragment = CowStr::Borrowed(fragment.as_str());
512
+
fragment
513
+
})
514
+
},
515
+
),
516
+
})
236
517
} else {
237
-
Self {
238
-
uri: CowStr::Borrowed(uri),
239
-
authority: unsafe { AtIdentifier::unchecked(uri) },
240
-
path: None,
241
-
fragment: None,
242
-
}
518
+
Err(AtStrError::missing(
519
+
"at-uri-scheme",
520
+
&uri.as_ref(),
521
+
"authority",
522
+
))
243
523
}
244
524
} else {
245
-
Self {
246
-
uri: CowStr::Borrowed(uri),
247
-
authority: unsafe { AtIdentifier::unchecked(uri) },
248
-
path: None,
249
-
fragment: None,
250
-
}
251
-
}
252
-
}
253
-
254
-
pub fn as_str(&self) -> &str {
255
-
{
256
-
let this = &self.uri;
257
-
this
525
+
Err(AtStrError::regex(
526
+
"at-uri-scheme",
527
+
&uri.as_ref(),
528
+
SmolStr::new_static("doesn't match schema"),
529
+
))
258
530
}
259
531
}
260
532
}
261
533
262
-
impl FromStr for AtUri<'_> {
263
-
type Err = AtStrError;
534
+
impl IntoStatic for AtUri<'_> {
535
+
type Output = AtUri<'static>;
264
536
265
-
/// Has to take ownership due to the lifetime constraints of the FromStr trait.
266
-
/// Prefer `AtUri::new()` or `AtUri::raw()` if you want to borrow.
267
-
fn from_str(s: &str) -> Result<Self, Self::Err> {
268
-
Self::new_owned(s)
537
+
fn into_static(self) -> AtUri<'static> {
538
+
AtUri {
539
+
inner: Inner::new(
540
+
self.inner.borrow_uri().clone().into_static(),
541
+
|uri| {
542
+
let parts = ATURI_REGEX.captures(uri).unwrap();
543
+
unsafe { AtIdentifier::unchecked(parts.name("authority").unwrap().as_str()) }
544
+
},
545
+
|uri| {
546
+
if self.inner.borrow_path().is_some() {
547
+
let parts = ATURI_REGEX.captures(uri).unwrap();
548
+
if let Some(collection) = parts.name("collection") {
549
+
let collection = unsafe { Nsid::unchecked(collection.as_str()) };
550
+
let rkey = if let Some(rkey) = parts.name("rkey") {
551
+
let rkey =
552
+
unsafe { RecordKey::from(Rkey::unchecked(rkey.as_str())) };
553
+
Some(rkey)
554
+
} else {
555
+
None
556
+
};
557
+
Some(UriPath { collection, rkey })
558
+
} else {
559
+
None
560
+
}
561
+
} else {
562
+
None
563
+
}
564
+
},
565
+
|uri| {
566
+
if self.inner.borrow_fragment().is_some() {
567
+
let parts = ATURI_REGEX.captures(uri).unwrap();
568
+
parts.name("fragment").map(|fragment| {
569
+
let fragment = CowStr::Borrowed(fragment.as_str());
570
+
fragment
571
+
})
572
+
} else {
573
+
None
574
+
}
575
+
},
576
+
),
577
+
}
269
578
}
270
579
}
271
580
···
284
593
where
285
594
S: Serializer,
286
595
{
287
-
serializer.serialize_str(&self.uri)
596
+
serializer.serialize_str(&self.inner.borrow_uri())
288
597
}
289
598
}
290
599
291
600
impl fmt::Display for AtUri<'_> {
292
601
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
293
-
f.write_str(&self.uri)
602
+
f.write_str(&self.inner.borrow_uri())
294
603
}
295
604
}
296
605
297
606
impl<'d> From<AtUri<'d>> for String {
298
607
fn from(value: AtUri<'d>) -> Self {
299
-
value.uri.to_string()
608
+
value.inner.borrow_uri().to_string()
300
609
}
301
610
}
302
611
303
612
impl<'d> From<AtUri<'d>> for CowStr<'d> {
304
613
fn from(value: AtUri<'d>) -> Self {
305
-
value.uri
614
+
value.inner.borrow_uri().clone()
306
615
}
307
616
}
308
617
···
316
625
317
626
impl<'d> TryFrom<CowStr<'d>> for AtUri<'d> {
318
627
type Error = AtStrError;
319
-
/// TODO: rewrite to avoid taking ownership/cloning
320
-
fn try_from(value: CowStr<'d>) -> Result<Self, Self::Error> {
321
-
Self::new_owned(value)
628
+
fn try_from(uri: CowStr<'d>) -> Result<Self, Self::Error> {
629
+
if let Some(parts) = ATURI_REGEX.captures(uri.as_ref()) {
630
+
if let Some(authority) = parts.name("authority") {
631
+
let _authority = AtIdentifier::new(authority.as_str())
632
+
.map_err(|e| AtStrError::wrap("at-uri-scheme", uri.to_string(), e))?;
633
+
let _path = if let Some(collection) = parts.name("collection") {
634
+
let collection = Nsid::new(collection.as_str())
635
+
.map_err(|e| AtStrError::wrap("at-uri-scheme", uri.to_string(), e))?;
636
+
let rkey = if let Some(rkey) = parts.name("rkey") {
637
+
let rkey =
638
+
RecordKey::from(Rkey::new(rkey.as_str()).map_err(|e| {
639
+
AtStrError::wrap("at-uri-scheme", uri.to_string(), e)
640
+
})?);
641
+
Some(rkey)
642
+
} else {
643
+
None
644
+
};
645
+
Some(UriPath { collection, rkey })
646
+
} else {
647
+
None
648
+
};
649
+
drop(parts);
650
+
651
+
Ok(AtUri {
652
+
inner: Inner::new(
653
+
uri,
654
+
|uri| {
655
+
let parts = ATURI_REGEX.captures(uri).unwrap();
656
+
unsafe {
657
+
AtIdentifier::unchecked(parts.name("authority").unwrap().as_str())
658
+
}
659
+
},
660
+
|uri| {
661
+
let parts = ATURI_REGEX.captures(uri).unwrap();
662
+
if let Some(collection) = parts.name("collection") {
663
+
let collection = unsafe { Nsid::unchecked(collection.as_str()) };
664
+
let rkey = if let Some(rkey) = parts.name("rkey") {
665
+
let rkey =
666
+
unsafe { RecordKey::from(Rkey::unchecked(rkey.as_str())) };
667
+
Some(rkey)
668
+
} else {
669
+
None
670
+
};
671
+
Some(UriPath { collection, rkey })
672
+
} else {
673
+
None
674
+
}
675
+
},
676
+
|uri| {
677
+
let parts = ATURI_REGEX.captures(uri).unwrap();
678
+
parts.name("fragment").map(|fragment| {
679
+
let fragment = CowStr::Borrowed(fragment.as_str());
680
+
fragment
681
+
})
682
+
},
683
+
),
684
+
})
685
+
} else {
686
+
Err(AtStrError::missing(
687
+
"at-uri-scheme",
688
+
&uri.as_ref(),
689
+
"authority",
690
+
))
691
+
}
692
+
} else {
693
+
Err(AtStrError::regex(
694
+
"at-uri-scheme",
695
+
&uri.as_ref(),
696
+
SmolStr::new_static("doesn't match schema"),
697
+
))
698
+
}
322
699
}
323
700
}
324
701
325
702
impl AsRef<str> for AtUri<'_> {
326
703
fn as_ref(&self) -> &str {
327
-
&self.uri.as_ref()
704
+
&self.inner.borrow_uri().as_ref()
328
705
}
329
706
}
330
707
···
332
709
type Target = str;
333
710
334
711
fn deref(&self) -> &Self::Target {
335
-
self.uri.as_ref()
712
+
self.inner.borrow_uri().as_ref()
336
713
}
337
714
}
+43
-4
crates/jacquard-common/src/types/blob.rs
+43
-4
crates/jacquard-common/src/types/blob.rs
···
21
21
pub size: usize,
22
22
}
23
23
24
-
impl<'r> BlobRef<'r> {
25
-
pub fn blob(&self) -> &Blob<'r> {
26
-
match self {
27
-
BlobRef::Blob(blob) => blob,
24
+
impl IntoStatic for Blob<'_> {
25
+
type Output = Blob<'static>;
26
+
27
+
fn into_static(self) -> Self::Output {
28
+
Blob {
29
+
r#ref: self.r#ref.into_static(),
30
+
mime_type: self.mime_type.into_static(),
31
+
size: self.size,
28
32
}
29
33
}
30
34
}
31
35
32
36
/// Current, typed blob reference.
37
+
/// Quite dislike this nesting, but it serves the same purpose as it did in Atrium
38
+
/// Couple of helper methods and conversions to make it less annoying.
39
+
/// TODO: revisit nesting and maybe hand-roll a serde impl that supports this sans nesting
33
40
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
34
41
#[serde(tag = "$type", rename_all = "lowercase")]
35
42
pub enum BlobRef<'r> {
36
43
#[serde(borrow)]
37
44
Blob(Blob<'r>),
45
+
}
46
+
47
+
impl<'r> BlobRef<'r> {
48
+
pub fn blob(&self) -> &Blob<'r> {
49
+
match self {
50
+
BlobRef::Blob(blob) => blob,
51
+
}
52
+
}
53
+
}
54
+
55
+
impl<'b> From<BlobRef<'b>> for Blob<'b> {
56
+
fn from(blob_ref: BlobRef<'b>) -> Self {
57
+
match blob_ref {
58
+
BlobRef::Blob(blob) => blob,
59
+
}
60
+
}
61
+
}
62
+
63
+
impl<'b> From<Blob<'b>> for BlobRef<'b> {
64
+
fn from(blob: Blob<'b>) -> Self {
65
+
BlobRef::Blob(blob)
66
+
}
67
+
}
68
+
69
+
impl IntoStatic for BlobRef<'_> {
70
+
type Output = BlobRef<'static>;
71
+
72
+
fn into_static(self) -> Self::Output {
73
+
match self {
74
+
BlobRef::Blob(blob) => BlobRef::Blob(blob.into_static()),
75
+
}
76
+
}
38
77
}
39
78
40
79
/// Wrapper for file type
+3
-6
crates/jacquard-common/src/types/string.rs
+3
-6
crates/jacquard-common/src/types/string.rs
···
1
-
use bytes::Bytes;
2
1
use miette::SourceSpan;
3
2
use serde::{Deserialize, Deserializer, Serialize, Serializer};
4
3
use smol_str::{SmolStr, ToSmolStr};
5
-
use std::{collections::BTreeMap, str::FromStr, sync::Arc};
4
+
use std::{str::FromStr, sync::Arc};
6
5
7
6
use crate::IntoStatic;
8
7
pub use crate::{
···
187
186
AtprotoStr::Did(did) => AtprotoStr::Did(did.into_static()),
188
187
AtprotoStr::Handle(handle) => AtprotoStr::Handle(handle.into_static()),
189
188
AtprotoStr::AtIdentifier(ident) => AtprotoStr::AtIdentifier(ident.into_static()),
190
-
AtprotoStr::AtUri(at_uri) => {
191
-
AtprotoStr::AtUri(AtUri::new_owned(at_uri.as_str()).unwrap())
192
-
}
189
+
AtprotoStr::AtUri(at_uri) => AtprotoStr::AtUri(at_uri.into_static()),
193
190
AtprotoStr::Uri(uri) => AtprotoStr::Uri(uri.into_static()),
194
191
AtprotoStr::Cid(cid) => AtprotoStr::Cid(cid.into_static()),
195
192
AtprotoStr::RecordKey(record_key) => AtprotoStr::RecordKey(record_key.into_static()),
···
214
211
pub struct AtStrError {
215
212
pub spec: SmolStr,
216
213
#[source_code]
217
-
source: String,
214
+
pub source: String,
218
215
#[source]
219
216
#[diagnostic_source]
220
217
pub kind: StrParseKind,