this repo has no description

after talk

Orual 5ec76dc8 89973a3e

+77 -49
+1
.gitignore
··· 23 23 *.db-shm 24 24 *.ttf 25 25 *.jpg 26 + *.png 26 27 *.db-wal 27 28 package-lock.json 28 29 *.sqlite
+26 -35
assets/slides.md
··· 4 4 5 5 --- 6 6 7 + # @nonbinary.computer 8 + 9 + - Embedded (and everything else) developer, electronics designer 10 + - Utopian in the Terra Ignota sense 11 + - atproto hit me like a truck 12 + 13 + --- 14 + 7 15 ## Spite-driven development 8 16 9 17 - Existing atproto libraries kinda sucked to work with ··· 30 38 --- 31 39 32 40 ## Why Rust? 33 - 34 - Only language where you can actually run it anywhere 35 - 36 - and it's not a stupid idea to. 37 - 38 - --- 39 - 40 - ## Why Rust? 41 - 42 - - Rust has a reputation as a difficult and demanding language 43 - - That reputation is sort of deserved 44 - 45 - 46 - --- 47 - 48 - ## Why Rust? 49 - 50 41 - The things Rust asks of you are things you often need to think about anyway 51 42 - It just makes them explicit 52 43 - We can make the sharp edges easier, if we care to ··· 59 50 60 51 # "Parse, don't validate" considered harmful 61 52 62 - If serde_json can't parse something as the type you're asking of it, 63 - 64 - it will give you nothing except an error. 65 - 66 - --- 67 - 68 - # "Parse, don't validate" considered harmful 69 - 70 53 Jacquard has its `Data<S>` type to handle some classes of this, 71 54 72 55 to work with freeform data in useful ways, ··· 89 72 90 73 # Smart constraints 91 74 92 - - Also a bad developer experience. 93 - - All of a sudden you can't take advantage of all the nice types! 94 - - `serde_json::Value` or `ipld_core::Ipld` offers literally nothing useful beyond "this thing is valid JSON/CBOR". 75 + `Data<S>` is **less** useless but still sucks donkey balls when you wanted your type. 95 76 96 77 --- 97 78 98 - # Smart constraints 99 - 100 - `Data<S>` is **less useless** but still sucks donkey balls when you wanted your type. 101 - 102 79 ![](avasarala.jpg) 103 80 104 81 --- 105 82 106 83 # Smart constraints 107 84 108 - And that is why jacquard's handle constructor has a specific carveout for "handle.invalid" 85 + And that is why jacquard's handle constructor has a specific carveout for 'handle.invalid' 109 86 110 87 Because there's a time to follow the spec 111 88 ··· 122 99 123 100 --- 124 101 102 + ## Why borrowing and lifetimes everywhere 103 + 104 + 105 + - Lifetimes (Jacquard, `serde_json_borrow`) 106 + - Unsafe (wrapped up, like `zerocopy` crate) 107 + - Separate borrowed/owned type variants (what Rust does) 108 + - `bytemuck` approach (`&'a Did` and `Box<Did>`) 109 + - **Borrow or Share** (available on `bos-beta` branch on tangled) 110 + 111 + 112 + --- 113 + 125 114 ## What you're seeing 126 115 127 116 - Each dot is a Jetstream event ··· 136 125 137 126 - We can change that! 138 127 128 + ![](example-derive.png) 129 + 139 130 --- 140 131 141 132 ## HOW? ··· 147 138 148 139 --- 149 140 150 - ## Thank you! 141 + ## Thank you 151 142 152 - Questions? 143 + ![Questions?](qr-code.png)
+50 -14
src/event_card.rs
··· 969 969 let wrapper = world 970 970 .spawn(Node { 971 971 align_self: AlignSelf::Center, 972 + max_width: Val::Percent(60.0), 973 + max_height: Val::Percent(40.0), 972 974 margin: UiRect::axes(Val::Px(0.0), Val::Px(8.0)), 973 975 ..default() 974 976 }) 975 977 .id(); 976 978 977 - let img = world 978 - .spawn(ImageNode::new(handle)) 979 - .id(); 979 + let img = world.spawn(ImageNode::new(handle)).id(); 980 980 981 981 world.entity_mut(wrapper).add_child(img); 982 982 world.entity_mut(content_entity).add_child(wrapper); ··· 1003 1003 let cached_text = { 1004 1004 let cache = world.resource::<RecordCache>(); 1005 1005 cache.records.get(uri).map(|cached| { 1006 - let text = cached.data.get_at_path("text") 1006 + let text = cached 1007 + .data 1008 + .get_at_path("text") 1007 1009 .and_then(|d| d.as_str()) 1008 1010 .unwrap_or("[no text]") 1009 1011 .to_string(); ··· 1013 1015 }; 1014 1016 1015 1017 let border = UiRect::all(Val::Px(1.0)); 1016 - let (display_text, author_label) = cached_text 1017 - .unwrap_or_else(|| (format!("loading {uri}…"), String::new())); 1018 + let (display_text, author_label) = 1019 + cached_text.unwrap_or_else(|| (format!("loading {uri}…"), String::new())); 1018 1020 1019 1021 let container = world 1020 1022 .spawn(( ··· 1047 1049 let author_node = world 1048 1050 .spawn(( 1049 1051 Text::new(author_display), 1050 - TextFont { font: font.clone(), font_size: 28.0, ..default() }, 1052 + TextFont { 1053 + font: font.clone(), 1054 + font_size: 28.0, 1055 + ..default() 1056 + }, 1051 1057 TextColor(dim), 1052 1058 )) 1053 1059 .id(); ··· 1057 1063 let text_node = world 1058 1064 .spawn(( 1059 1065 Text::new(display_text), 1060 - TextFont { font: font.clone(), font_size: 32.0, ..default() }, 1066 + TextFont { 1067 + font: font.clone(), 1068 + font_size: 32.0, 1069 + ..default() 1070 + }, 1061 1071 TextColor(bright), 1062 1072 TextLayout::new_with_linebreak(LineBreak::WordBoundary), 1063 1073 )) ··· 1086 1096 let mut cache = world.resource_mut::<RecordCache>(); 1087 1097 cache.records_pending.remove(&uri_owned); 1088 1098 if let Some((data, author_did)) = result { 1089 - cache.records.insert(uri_owned, CachedRecord { data, author_did }); 1099 + cache 1100 + .records 1101 + .insert(uri_owned, CachedRecord { data, author_did }); 1090 1102 cache.bump(); 1103 + // Trigger slide re-render 1104 + let mut render = world.resource_mut::<CardRenderState>(); 1105 + render.slide_generation += 1; 1091 1106 } 1092 - }); 1107 + }) 1108 + .await; 1093 1109 }); 1094 1110 } 1095 1111 } ··· 1117 1133 return world 1118 1134 .spawn(( 1119 1135 Text::new(full_text), 1120 - TextFont { font: font.clone(), font_size, ..default() }, 1136 + TextFont { 1137 + font: font.clone(), 1138 + font_size, 1139 + ..default() 1140 + }, 1121 1141 TextColor(bright), 1122 1142 TextLayout::new_with_linebreak(LineBreak::WordBoundary), 1123 1143 )) ··· 1145 1165 let child = world 1146 1166 .spawn(( 1147 1167 Text::new(span.text.clone()), 1148 - TextFont { font: span_font, font_size, ..default() }, 1168 + TextFont { 1169 + font: span_font, 1170 + font_size, 1171 + ..default() 1172 + }, 1149 1173 TextColor(bright), 1150 1174 )) 1151 1175 .id(); ··· 1162 1186 use jacquard::types::string::Did; 1163 1187 use smol_str::ToSmolStr; 1164 1188 1165 - let parsed = AtUri::new(uri.to_smolstr()).ok()?; 1166 - let output = client.fetch_record_slingshot(&parsed).await.ok()?; 1189 + let parsed = match AtUri::new(uri.to_smolstr()) { 1190 + Ok(u) => u, 1191 + Err(e) => { 1192 + warn!("fetch_record_by_uri: invalid AT-URI {uri}: {e:?}"); 1193 + return None; 1194 + } 1195 + }; 1196 + let output = match client.fetch_record_slingshot(&parsed).await { 1197 + Ok(o) => o, 1198 + Err(e) => { 1199 + warn!("fetch_record_by_uri: slingshot failed for {uri}: {e:?}"); 1200 + return None; 1201 + } 1202 + }; 1167 1203 let author = Did::new(parsed.authority().as_str().to_smolstr()).ok()?; 1168 1204 Some((output.value, author)) 1169 1205 }