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