1 use crate::{types::TypeInfo, Ownership}; 2 use heck::*; 3 use wit_parser::*; 4 5 #[derive(Debug, Copy, Clone, PartialEq)] 6 pub enum TypeMode { 7 Owned, 8 AllBorrowed(&'static str), 9 } 10 11 pub trait RustGenerator<'a> { 12 fn resolve(&self) -> &'a Resolve; 13 14 fn push_str(&mut self, s: &str); 15 fn info(&self, ty: TypeId) -> TypeInfo; 16 fn path_to_interface(&self, interface: InterfaceId) -> Option<String>; 17 fn is_imported_interface(&self, interface: InterfaceId) -> bool; 18 19 /// This determines whether we generate owning types or (where appropriate) 20 /// borrowing types. 21 /// 22 /// For example, when generating a type which is only used as a parameter to 23 /// a guest-exported function, there is no need for it to own its fields. 24 /// However, constructing deeply-nested borrows (e.g. `&[&[&[&str]]]]` for 25 /// `list<list<list<string>>>`) can be very awkward, so by default we 26 /// generate owning types and use only shallow borrowing at the top level 27 /// inside function signatures. 28 fn ownership(&self) -> Ownership; 29 30 fn print_ty(&mut self, ty: &Type, mode: TypeMode) { 31 match ty { 32 Type::Id(t) => self.print_tyid(*t, mode), 33 Type::Bool => self.push_str("bool"), 34 Type::U8 => self.push_str("u8"), 35 Type::U16 => self.push_str("u16"), 36 Type::U32 => self.push_str("u32"), 37 Type::U64 => self.push_str("u64"), 38 Type::S8 => self.push_str("i8"), 39 Type::S16 => self.push_str("i16"), 40 Type::S32 => self.push_str("i32"), 41 Type::S64 => self.push_str("i64"), 42 Type::F32 => self.push_str("f32"), 43 Type::F64 => self.push_str("f64"), 44 Type::Char => self.push_str("char"), 45 Type::String => match mode { 46 TypeMode::AllBorrowed(lt) => { 47 self.push_str("&"); 48 if lt != "'_" { 49 self.push_str(lt); 50 self.push_str(" "); 51 } 52 self.push_str("str"); 53 } 54 TypeMode::Owned => self.push_str("wasmtime::component::__internal::String"), 55 }, 56 } 57 } 58 59 fn print_optional_ty(&mut self, ty: Option<&Type>, mode: TypeMode) { 60 match ty { 61 Some(ty) => self.print_ty(ty, mode), 62 None => self.push_str("()"), 63 } 64 } 65 66 fn print_tyid(&mut self, id: TypeId, mode: TypeMode) { 67 let info = self.info(id); 68 let lt = self.lifetime_for(&info, mode); 69 let ty = &self.resolve().types[id]; 70 if ty.name.is_some() { 71 // If this type has a list internally, no lifetime is being printed, 72 // but we're in a borrowed mode, then that means we're in a borrowed 73 // context and don't want ownership of the type but we're using an 74 // owned type definition. Inject a `&` in front to indicate that, at 75 // the API level, ownership isn't required. 76 if info.has_list && lt.is_none() { 77 if let TypeMode::AllBorrowed(lt) = mode { 78 self.push_str("&"); 79 if lt != "'_" { 80 self.push_str(lt); 81 self.push_str(" "); 82 } 83 } 84 } 85 let name = if lt.is_some() { 86 self.param_name(id) 87 } else { 88 self.result_name(id) 89 }; 90 self.print_type_name_in_interface(ty.owner, &name); 91 92 // If the type recursively owns data and it's a 93 // variant/record/list, then we need to place the 94 // lifetime parameter on the type as well. 95 if info.has_list && needs_generics(self.resolve(), &ty.kind) { 96 self.print_generics(lt); 97 } 98 99 return; 100 101 fn needs_generics(resolve: &Resolve, ty: &TypeDefKind) -> bool { 102 match ty { 103 TypeDefKind::Variant(_) 104 | TypeDefKind::Record(_) 105 | TypeDefKind::Option(_) 106 | TypeDefKind::Result(_) 107 | TypeDefKind::Future(_) 108 | TypeDefKind::Stream(_) 109 | TypeDefKind::List(_) 110 | TypeDefKind::Flags(_) 111 | TypeDefKind::Enum(_) 112 | TypeDefKind::Tuple(_) 113 | TypeDefKind::Handle(_) 114 | TypeDefKind::Resource => true, 115 TypeDefKind::Type(Type::Id(t)) => { 116 needs_generics(resolve, &resolve.types[*t].kind) 117 } 118 TypeDefKind::Type(Type::String) => true, 119 TypeDefKind::Type(_) => false, 120 TypeDefKind::Unknown => unreachable!(), 121 } 122 } 123 } 124 125 match &ty.kind { 126 TypeDefKind::List(t) => self.print_list(t, mode), 127 128 TypeDefKind::Option(t) => { 129 self.push_str("Option<"); 130 self.print_ty(t, mode); 131 self.push_str(">"); 132 } 133 134 TypeDefKind::Result(r) => { 135 self.push_str("Result<"); 136 self.print_optional_ty(r.ok.as_ref(), mode); 137 self.push_str(","); 138 self.print_optional_ty(r.err.as_ref(), mode); 139 self.push_str(">"); 140 } 141 142 TypeDefKind::Variant(_) => panic!("unsupported anonymous variant"), 143 144 // Tuple-like records are mapped directly to Rust tuples of 145 // types. Note the trailing comma after each member to 146 // appropriately handle 1-tuples. 147 TypeDefKind::Tuple(t) => { 148 self.push_str("("); 149 for ty in t.types.iter() { 150 self.print_ty(ty, mode); 151 self.push_str(","); 152 } 153 self.push_str(")"); 154 } 155 TypeDefKind::Record(_) => { 156 panic!("unsupported anonymous type reference: record") 157 } 158 TypeDefKind::Flags(_) => { 159 panic!("unsupported anonymous type reference: flags") 160 } 161 TypeDefKind::Enum(_) => { 162 panic!("unsupported anonymous type reference: enum") 163 } 164 TypeDefKind::Future(ty) => { 165 self.push_str("Future<"); 166 self.print_optional_ty(ty.as_ref(), mode); 167 self.push_str(">"); 168 } 169 TypeDefKind::Stream(stream) => { 170 self.push_str("Stream<"); 171 self.print_optional_ty(stream.element.as_ref(), mode); 172 self.push_str(","); 173 self.print_optional_ty(stream.end.as_ref(), mode); 174 self.push_str(">"); 175 } 176 177 TypeDefKind::Handle(handle) => { 178 self.print_handle(handle); 179 } 180 TypeDefKind::Resource => unreachable!(), 181 182 TypeDefKind::Type(t) => self.print_ty(t, mode), 183 TypeDefKind::Unknown => unreachable!(), 184 } 185 } 186 187 fn print_type_name_in_interface(&mut self, owner: TypeOwner, name: &str) { 188 if let TypeOwner::Interface(id) = owner { 189 if let Some(path) = self.path_to_interface(id) { 190 self.push_str(&path); 191 self.push_str("::"); 192 } 193 } 194 self.push_str(name); 195 } 196 197 fn print_list(&mut self, ty: &Type, mode: TypeMode) { 198 let next_mode = if matches!(self.ownership(), Ownership::Owning) { 199 TypeMode::Owned 200 } else { 201 mode 202 }; 203 match mode { 204 TypeMode::AllBorrowed(lt) => { 205 self.push_str("&"); 206 if lt != "'_" { 207 self.push_str(lt); 208 self.push_str(" "); 209 } 210 self.push_str("["); 211 self.print_ty(ty, next_mode); 212 self.push_str("]"); 213 } 214 TypeMode::Owned => { 215 self.push_str("wasmtime::component::__internal::Vec<"); 216 self.print_ty(ty, next_mode); 217 self.push_str(">"); 218 } 219 } 220 } 221 222 fn print_handle(&mut self, handle: &Handle) { 223 // Handles are either printed as `ResourceAny` for any guest-defined 224 // resource or `Resource<T>` for all host-defined resources. This means 225 // that this function needs to determine if `handle` points to a host 226 // or a guest resource which is determined by: 227 // 228 // * For world-owned resources, they're always imported. 229 // * For interface-owned resources, it depends on the how bindings were 230 // last generated for this interface. 231 // 232 // Additionally type aliases via `use` are "peeled" here to find the 233 // original definition of the resource since that's the one that we 234 // care about for determining whether it's imported or not. 235 let resource = match handle { 236 Handle::Own(t) | Handle::Borrow(t) => *t, 237 }; 238 let ty = &self.resolve().types[resource]; 239 let def_id = super::resolve_type_definition_id(self.resolve(), resource); 240 let ty_def = &self.resolve().types[def_id]; 241 let is_host_defined = match ty_def.owner { 242 TypeOwner::Interface(i) => self.is_imported_interface(i), 243 _ => true, 244 }; 245 if is_host_defined { 246 self.push_str("wasmtime::component::Resource<"); 247 self.print_type_name_in_interface( 248 ty.owner, 249 &ty.name.as_ref().unwrap().to_upper_camel_case(), 250 ); 251 self.push_str(">"); 252 } else { 253 self.push_str("wasmtime::component::ResourceAny"); 254 } 255 } 256 257 fn print_generics(&mut self, lifetime: Option<&str>) { 258 if lifetime.is_none() { 259 return; 260 } 261 self.push_str("<"); 262 if let Some(lt) = lifetime { 263 self.push_str(lt); 264 self.push_str(","); 265 } 266 self.push_str(">"); 267 } 268 269 fn modes_of(&self, ty: TypeId) -> Vec<(String, TypeMode)> { 270 let info = self.info(ty); 271 if !info.owned && !info.borrowed { 272 return Vec::new(); 273 } 274 let mut result = Vec::new(); 275 let first_mode = 276 if info.owned || !info.borrowed || matches!(self.ownership(), Ownership::Owning) { 277 TypeMode::Owned 278 } else { 279 assert!(!self.uses_two_names(&info)); 280 TypeMode::AllBorrowed("'a") 281 }; 282 result.push((self.result_name(ty), first_mode)); 283 if self.uses_two_names(&info) { 284 result.push((self.param_name(ty), TypeMode::AllBorrowed("'a"))); 285 } 286 result 287 } 288 289 fn param_name(&self, ty: TypeId) -> String { 290 let info = self.info(ty); 291 let name = self.resolve().types[ty] 292 .name 293 .as_ref() 294 .unwrap() 295 .to_upper_camel_case(); 296 if self.uses_two_names(&info) { 297 format!("{}Param", name) 298 } else { 299 name 300 } 301 } 302 303 fn result_name(&self, ty: TypeId) -> String { 304 let info = self.info(ty); 305 let name = self.resolve().types[ty] 306 .name 307 .as_ref() 308 .unwrap() 309 .to_upper_camel_case(); 310 if self.uses_two_names(&info) { 311 format!("{}Result", name) 312 } else { 313 name 314 } 315 } 316 317 fn uses_two_names(&self, info: &TypeInfo) -> bool { 318 info.has_list 319 && info.borrowed 320 && info.owned 321 && matches!( 322 self.ownership(), 323 Ownership::Borrowing { 324 duplicate_if_necessary: true 325 } 326 ) 327 } 328 329 fn lifetime_for(&self, info: &TypeInfo, mode: TypeMode) -> Option<&'static str> { 330 if matches!(self.ownership(), Ownership::Owning) { 331 return None; 332 } 333 let lt = match mode { 334 TypeMode::AllBorrowed(s) => s, 335 _ => return None, 336 }; 337 // No lifetimes needed unless this has a list. 338 if !info.has_list { 339 return None; 340 } 341 // If two names are used then this type will have an owned and a 342 // borrowed copy and the borrowed copy is being used, so it needs a 343 // lifetime. Otherwise if it's only borrowed and not owned then this can 344 // also use a lifetime since it's not needed in two contexts and only 345 // the borrowed version of the structure was generated. 346 if self.uses_two_names(info) || (info.borrowed && !info.owned) { 347 Some(lt) 348 } else { 349 None 350 } 351 } 352 } 353 354 /// Translate `name` to a Rust `snake_case` identifier. 355 pub fn to_rust_ident(name: &str) -> String { 356 match name { 357 // Escape Rust keywords. 358 // Source: https://doc.rust-lang.org/reference/keywords.html 359 "as" => "as_".into(), 360 "break" => "break_".into(), 361 "const" => "const_".into(), 362 "continue" => "continue_".into(), 363 "crate" => "crate_".into(), 364 "else" => "else_".into(), 365 "enum" => "enum_".into(), 366 "extern" => "extern_".into(), 367 "false" => "false_".into(), 368 "fn" => "fn_".into(), 369 "for" => "for_".into(), 370 "if" => "if_".into(), 371 "impl" => "impl_".into(), 372 "in" => "in_".into(), 373 "let" => "let_".into(), 374 "loop" => "loop_".into(), 375 "match" => "match_".into(), 376 "mod" => "mod_".into(), 377 "move" => "move_".into(), 378 "mut" => "mut_".into(), 379 "pub" => "pub_".into(), 380 "ref" => "ref_".into(), 381 "return" => "return_".into(), 382 "self" => "self_".into(), 383 "static" => "static_".into(), 384 "struct" => "struct_".into(), 385 "super" => "super_".into(), 386 "trait" => "trait_".into(), 387 "true" => "true_".into(), 388 "type" => "type_".into(), 389 "unsafe" => "unsafe_".into(), 390 "use" => "use_".into(), 391 "where" => "where_".into(), 392 "while" => "while_".into(), 393 "async" => "async_".into(), 394 "await" => "await_".into(), 395 "dyn" => "dyn_".into(), 396 "abstract" => "abstract_".into(), 397 "become" => "become_".into(), 398 "box" => "box_".into(), 399 "do" => "do_".into(), 400 "final" => "final_".into(), 401 "macro" => "macro_".into(), 402 "override" => "override_".into(), 403 "priv" => "priv_".into(), 404 "typeof" => "typeof_".into(), 405 "unsized" => "unsized_".into(), 406 "virtual" => "virtual_".into(), 407 "yield" => "yield_".into(), 408 "try" => "try_".into(), 409 s => s.to_snake_case(), 410 } 411 } 412 413 /// Translate `name` to a Rust `UpperCamelCase` identifier. 414 pub fn to_rust_upper_camel_case(name: &str) -> String { 415 match name { 416 // We use `Host` as the name of the trait for host implementations 417 // to fill in, so rename it if "Host" is used as a regular identifier. 418 "host" => "Host_".into(), 419 s => s.to_upper_camel_case(), 420 } 421 } 422