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