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