├── .gitignore ├── .gitmodules ├── Cargo.toml ├── README.md ├── build.rs ├── shim ├── shim.cpp └── shim.h ├── src ├── OpenMaya │ ├── core.rs │ ├── mfn.rs │ ├── mod.rs │ └── mpx.rs ├── lib.rs ├── native.rs └── native2.rs └── wrapper.h /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /target/ 3 | **/*.rs.bk 4 | Cargo.lock 5 | .vscode/* 6 | !.vscode/settings.json 7 | !.vscode/tasks.json 8 | !.vscode/launch.json 9 | !.vscode/extensions.json 10 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "third_party/rust-bindgen"] 2 | path = third_party/rust-bindgen 3 | url = https://github.com/sdao/rust-bindgen 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "maya2018" 3 | version = "0.1.0" 4 | authors = ["Steven Dao "] 5 | 6 | [dependencies] 7 | 8 | [build-dependencies] 9 | bindgen = { path = "third_party/rust-bindgen", version = "0.32.3" } 10 | cc = "1.0.4" 11 | syn = { version = "0.12.7", features = ["full", "visit"] } 12 | quote = "0.4.2" 13 | regex = "0.2.5" 14 | rustfmt = "0.10.0" 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Rust Bindings for Maya 2018 2 | =========================== 3 | 4 | I'm currently developing these on Windows with Maya 2018. In theory you could compile on Mac or 5 | Linux and with a different Maya version with some Maya tweaking, but I only have Maya on Windows 6 | at my disposal so that's what we're going with :) 7 | 8 | There are some hacks to get stuff on Windows working but they're disabled if your environment 9 | isn't set up with the MSVC toolchain. 10 | 11 | This uses a fork of rust-bindgen that adds support for operators to get bindings for Maya 12 | operators, equals, etc. 13 | 14 | Differences between Rust and C++ API 15 | ------------------------------------ 16 | 17 | - MPx classes will be exposed as Rust traits. You won't be able to have a C++-style inheritance 18 | hierarchy, but you will be able to implement the virtuals just be implementing the trait 19 | in Rust. 20 | - Functions that return by argument will return normally in the Rust binding, using tuples if 21 | necessary. Functions that return an MStatus or have an MStatus pointer argument will return 22 | Result in Rust; this enforces handling of the MStatus in error cases. 23 | - MString's aren't exposed in the Rust API. You need to pass &str's or things convertible to 24 | &str. The API will take care of handling all conversions in the background. 25 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | #![recursion_limit="128"] 2 | 3 | extern crate bindgen; 4 | extern crate cc; 5 | extern crate syn; 6 | #[macro_use] extern crate quote; 7 | extern crate regex; 8 | extern crate rustfmt; 9 | 10 | use std::env; 11 | use std::path::PathBuf; 12 | use std::io::prelude::*; 13 | use std::fs::File; 14 | use std::collections::{BTreeMap, HashSet}; 15 | 16 | use syn::{ItemImpl, ItemMod, ImplItemMethod, ItemType, ItemEnum, ItemStruct, Type}; 17 | use quote::ToTokens; 18 | use regex::Regex; 19 | 20 | #[derive(PartialEq)] 21 | enum MayaVisitMethodType { 22 | Constructor, 23 | Destructor, 24 | Operator, 25 | Normal 26 | } 27 | #[derive(PartialEq)] 28 | enum MayaVisitPtrType { 29 | NotPtr, 30 | ConstPtr, 31 | MutPtr 32 | } 33 | struct MayaVisitFirstPass { 34 | pub structs: HashSet, 35 | pub impls: HashSet, 36 | pub types: HashSet, 37 | pub excluded: HashSet, 38 | } 39 | impl MayaVisitFirstPass { 40 | pub fn new() -> MayaVisitFirstPass { 41 | MayaVisitFirstPass { 42 | structs: HashSet::new(), 43 | impls: HashSet::new(), 44 | types: HashSet::new(), 45 | excluded: HashSet::new(), 46 | } 47 | } 48 | } 49 | impl<'ast> syn::visit::Visit<'ast> for MayaVisitFirstPass { 50 | fn visit_item_struct(&mut self, i: &'ast ItemStruct) { 51 | // Skip MPx classes because we need to implement manual shims for those. 52 | // Skip Maya internal classes/bindgen internal classes. 53 | let ident_name = i.ident.to_string(); 54 | if ident_name.starts_with("M") && 55 | !ident_name.starts_with("MPx") && 56 | !ident_name.contains("bindgen") && 57 | !ident_name.contains("Tapi") && 58 | ident_name != "MString" 59 | { 60 | self.structs.insert(i.ident); 61 | } 62 | else { 63 | self.excluded.insert(i.ident); 64 | } 65 | } 66 | fn visit_item_impl(&mut self, i: &'ast ItemImpl) { 67 | match *i.self_ty { 68 | Type::Path(ref p) => { 69 | let impl_type = p.path.segments.last().unwrap().value().ident; 70 | self.impls.insert(impl_type); 71 | }, 72 | _ => {} 73 | } 74 | } 75 | fn visit_item_type(&mut self, i: &'ast ItemType) { 76 | let ident_name = i.ident.to_string(); 77 | if ident_name.starts_with("M") { 78 | self.types.insert(i.ident); 79 | } 80 | else { 81 | self.excluded.insert(i.ident); 82 | } 83 | } 84 | fn visit_item_enum(&mut self, i: &'ast ItemEnum) { 85 | let ident_name = i.ident.to_string(); 86 | if ident_name.starts_with("M") { 87 | self.types.insert(i.ident); 88 | } 89 | else { 90 | self.excluded.insert(i.ident); 91 | } 92 | } 93 | } 94 | struct MayaVisitSecondPass { 95 | first_pass: MayaVisitFirstPass, 96 | tokens: Vec, 97 | cur_namespace: Vec, 98 | impl_type: syn::Ident, 99 | impl_fns: Vec, 100 | impl_traits: Vec, 101 | impl_partialeq: BTreeMap>, 102 | impl_getters: BTreeMap, 103 | impl_setters: BTreeMap, 104 | } 105 | impl MayaVisitSecondPass { 106 | pub fn new(first_pass: MayaVisitFirstPass) -> Self { 107 | Self { 108 | first_pass: first_pass, 109 | tokens: vec![], 110 | cur_namespace: vec![], 111 | impl_type: syn::Ident::from("FAKE_IDENT"), 112 | impl_fns: vec![], 113 | impl_traits: vec![], 114 | impl_partialeq: BTreeMap::new(), 115 | impl_getters: BTreeMap::new(), 116 | impl_setters: BTreeMap::new(), 117 | } 118 | } 119 | fn process_method(ident: &syn::Ident) -> MayaVisitMethodType { 120 | let constructor_regex = Regex::new("^new[0-9]*$").unwrap(); 121 | 122 | let ident_name = ident.to_string(); 123 | if constructor_regex.is_match(&ident_name) { 124 | MayaVisitMethodType::Constructor 125 | } 126 | else if ident_name == "destruct" { 127 | MayaVisitMethodType::Destructor 128 | } 129 | else if ident_name.starts_with("operator_") { 130 | MayaVisitMethodType::Operator 131 | } 132 | else { 133 | MayaVisitMethodType::Normal 134 | } 135 | } 136 | /// Does the fn_arg represent a *const/mut ns1::ns2::ident for the given ident? 137 | fn is_ptr(fn_arg: &syn::FnArg, ident: &syn::Ident) -> bool { 138 | match fn_arg { 139 | &syn::FnArg::Captured(ref fn_arg) => { 140 | match MayaVisitSecondPass::quote_ty(&fn_arg.ty) { 141 | Some((ty_ident, _, is_ptr)) => ident == &ty_ident && match is_ptr { 142 | MayaVisitPtrType::NotPtr => false, 143 | _ => true 144 | }, 145 | None => false 146 | } 147 | }, 148 | _ => false 149 | } 150 | } 151 | /// Is this an OpenMaya struct type? 152 | fn is_struct(&self, ident: &syn::Ident) -> bool { 153 | self.first_pass.structs.contains(ident) 154 | } 155 | /// Was this one of the OpenMaya excluded struct/enum/types? 156 | fn is_excluded(&self, ident: &syn::Ident) -> bool { 157 | self.first_pass.excluded.contains(ident) 158 | } 159 | /// Is this type implemented with the bindings we have? 160 | /// i.e. the type must be a Rust native type or an OpenMaya type with an impl. 161 | fn is_implemented(&self, ident: &syn::Ident) -> bool { 162 | if self.is_excluded(&ident) { 163 | return false; 164 | } 165 | if self.is_struct(&ident) && !self.first_pass.impls.contains(&ident) { 166 | return false; 167 | } 168 | return true; 169 | } 170 | /// Convert type from bindgen to high-level API. 171 | /// Returns: Option((ident, arr_size, is_ptr)) where ident is the new type token; arr_size 172 | /// is the array size or 0 if not a statically-sized array; is_ptr is whether the type is a 173 | /// *const/mut ident. 174 | fn quote_ty(ty: &syn::Type) -> Option<(syn::Ident, Option, MayaVisitPtrType)> { 175 | match ty { 176 | &syn::Type::Path(ref ty) => { 177 | let name = ty.path.segments.last().unwrap().value().ident; 178 | match name.to_string().as_str() { 179 | "c_char" => Some((syn::Ident::from("String"), None, MayaVisitPtrType::NotPtr)), 180 | "c_double" => Some((syn::Ident::from("f64"), None, MayaVisitPtrType::NotPtr)), 181 | "c_float" => Some((syn::Ident::from("f32"), None, MayaVisitPtrType::NotPtr)), 182 | "c_int" => Some((syn::Ident::from("i32"), None, MayaVisitPtrType::NotPtr)), 183 | "c_long" => Some((syn::Ident::from("i64"), None, MayaVisitPtrType::NotPtr)), 184 | "c_longlong" => Some((syn::Ident::from("i64"), None, MayaVisitPtrType::NotPtr)), 185 | "c_schar" => None, // XXX need to handle chars 186 | "c_short" => Some((syn::Ident::from("i16"), None, MayaVisitPtrType::NotPtr)), 187 | "c_uchar" => Some((syn::Ident::from("u8"), None, MayaVisitPtrType::NotPtr)), 188 | "c_uint" => Some((syn::Ident::from("u32"), None, MayaVisitPtrType::NotPtr)), 189 | "c_ulong" => Some((syn::Ident::from("u64"), None, MayaVisitPtrType::NotPtr)), 190 | "c_ulonglong" => 191 | Some((syn::Ident::from("u64"), None, MayaVisitPtrType::NotPtr)), 192 | "c_ushort" => Some((syn::Ident::from("u16"), None, MayaVisitPtrType::NotPtr)), 193 | "c_void" => None, // XXX maybe handle in unsafe fn 194 | "MInt64" => Some((syn::Ident::from("i64"), None, MayaVisitPtrType::NotPtr)), 195 | "MString" => Some((syn::Ident::from("String"), None, MayaVisitPtrType::NotPtr)), 196 | _ => Some((name, None, MayaVisitPtrType::NotPtr)), 197 | } 198 | }, 199 | &syn::Type::Array(ref ty) => { 200 | let inner_ty = MayaVisitSecondPass::quote_ty(&ty.elem); 201 | match inner_ty { 202 | Some((ident, _, _)) => { 203 | let arr_size = if let syn::Expr::Lit(ref lit) = ty.len { 204 | if let syn::Lit::Int(ref i) = lit.lit { 205 | Some(i.value() as usize) 206 | } else { 207 | None 208 | } 209 | } 210 | else { 211 | None 212 | }; 213 | Some((ident, arr_size, MayaVisitPtrType::NotPtr)) 214 | }, 215 | _ => None 216 | } 217 | }, 218 | &syn::Type::Ptr(ref ty) => { 219 | let inner_ty = MayaVisitSecondPass::quote_ty(&ty.elem); 220 | match inner_ty { 221 | Some((ident, arr_size, _)) => { 222 | let is_const = ty.const_token.is_some(); 223 | let ptr_kind = if is_const { 224 | MayaVisitPtrType::ConstPtr 225 | } 226 | else { 227 | MayaVisitPtrType::MutPtr 228 | }; 229 | Some((ident, arr_size, ptr_kind)) 230 | }, 231 | _ => None 232 | } 233 | }, 234 | _ => None 235 | } 236 | } 237 | fn quote_arr(ty: &syn::Ident, arr_size: Option) -> quote::Tokens { 238 | match arr_size { 239 | Some(arr_size) => quote! { [#ty ; #arr_size] }, 240 | _ => quote! { #ty } 241 | } 242 | } 243 | /// Substitutes the Rust identifier for one better-suited in an input argument context. 244 | /// Currently, just allows you to use &str instead of &String. 245 | /// Note that you can't use &str instead of String since the object must be moved. 246 | /// XXX can we auto-upgrade String sites to be &str since we're wrapping anwyays? 247 | fn inputize(ty_ident: &syn::Ident, is_ptr: &MayaVisitPtrType) -> syn::Ident { 248 | if *ty_ident == syn::Ident::from("String") && *is_ptr != MayaVisitPtrType::NotPtr { 249 | syn::Ident::from("str") 250 | } 251 | else { 252 | *ty_ident 253 | } 254 | } 255 | fn process_binary_operator(&mut self, i: &ImplItemMethod, trait_name: &syn::Ident, 256 | fn_name: &syn::Ident) 257 | { 258 | let impl_type = self.impl_type; 259 | let fn_ident = &i.sig.ident; 260 | let fn_inputs = &i.sig.decl.inputs; 261 | if let &syn::FnArg::Captured(ref fn_arg) = *fn_inputs.last().unwrap().value() { 262 | if let Some((rhs_ident, None, rhs_is_ptr)) = 263 | MayaVisitSecondPass::quote_ty(&fn_arg.ty) 264 | { 265 | let rhs_ident = MayaVisitSecondPass::inputize(&rhs_ident, &rhs_is_ptr); 266 | if let &syn::ReturnType::Type(_, ref ret_ty) = &i.sig.decl.output { 267 | if let Some((ret_ident, None, _)) = 268 | MayaVisitSecondPass::quote_ty(&ret_ty) 269 | { 270 | let invoke_byval = match rhs_is_ptr { 271 | MayaVisitPtrType::NotPtr => quote! { 272 | unsafe { self_._native.#fn_ident(other) } 273 | }, 274 | MayaVisitPtrType::ConstPtr => quote! { 275 | unsafe { self_._native.#fn_ident(&other._native) } 276 | }, 277 | MayaVisitPtrType::MutPtr => quote! { 278 | unsafe { self_._native.#fn_ident(&mut other._native) } 279 | }, 280 | }; 281 | let ret_byval = self.wrap(&invoke_byval, &ret_ident); 282 | let invoke_byref = match rhs_is_ptr { 283 | MayaVisitPtrType::NotPtr => quote! { 284 | unsafe { self_._native.#fn_ident(*other) } 285 | }, 286 | MayaVisitPtrType::ConstPtr => quote! { 287 | unsafe { self_._native.#fn_ident(&other._native) } 288 | }, 289 | MayaVisitPtrType::MutPtr => quote! { 290 | unsafe { self_._native.#fn_ident(&mut other._native) } 291 | }, 292 | }; 293 | let ret_byref = self.wrap(&invoke_byref, &ret_ident); 294 | // Because the Maya API is weird enough as-is... 295 | // If the lhs or rhs of the argument is non-const, clone it before using. 296 | let self_mut_fix = match *fn_inputs.first().unwrap().value() { 297 | &syn::FnArg::SelfRef(ref self_arg) 298 | if self_arg.mutability.is_some() => quote! { 299 | let mut self_ = self.clone(); 300 | }, 301 | _ => quote! { 302 | let self_ = self; 303 | } 304 | }; 305 | let other_mut_fix = match rhs_is_ptr { 306 | MayaVisitPtrType::MutPtr => quote! { 307 | let mut other = other.clone(); 308 | }, 309 | _ => quote! {} 310 | }; 311 | // Since we're just auto-generating everything, generate all op traits 312 | // for ref/non-ref combos. 313 | self.impl_traits.push(quote! { 314 | impl ::std::ops::#trait_name<#rhs_ident> for #impl_type { 315 | type Output = #ret_ident; 316 | fn #fn_name(self, other: #rhs_ident) -> #ret_ident { 317 | #self_mut_fix 318 | #other_mut_fix 319 | #ret_byval 320 | } 321 | } 322 | impl<'a> ::std::ops::#trait_name<#rhs_ident> for &'a #impl_type { 323 | type Output = #ret_ident; 324 | fn #fn_name(self, other: #rhs_ident) -> #ret_ident { 325 | #self_mut_fix 326 | #other_mut_fix 327 | #ret_byval 328 | } 329 | } 330 | impl<'b> ::std::ops::#trait_name<&'b #rhs_ident> for #impl_type { 331 | type Output = #ret_ident; 332 | fn #fn_name(self, other: &'b #rhs_ident) -> #ret_ident { 333 | #self_mut_fix 334 | #other_mut_fix 335 | #ret_byref 336 | } 337 | } 338 | impl<'a, 'b> ::std::ops::#trait_name<&'b #rhs_ident> 339 | for &'a #impl_type 340 | { 341 | type Output = #ret_ident; 342 | fn #fn_name(self, other: &'b #rhs_ident) -> #ret_ident { 343 | #self_mut_fix 344 | #other_mut_fix 345 | #ret_byref 346 | } 347 | } 348 | }); 349 | } 350 | } 351 | } 352 | } 353 | } 354 | fn process_index_operator(&mut self, i: &ImplItemMethod) { 355 | let impl_type = self.impl_type; 356 | let fn_ident = &i.sig.ident; 357 | let fn_inputs = &i.sig.decl.inputs; 358 | if let &syn::FnArg::Captured(ref fn_arg) = *fn_inputs.last().unwrap().value() { 359 | if let Some((rhs_ident, None, rhs_is_ptr)) = 360 | MayaVisitSecondPass::quote_ty(&fn_arg.ty) 361 | { 362 | let rhs_ident = MayaVisitSecondPass::inputize(&rhs_ident, &rhs_is_ptr); 363 | if let &syn::ReturnType::Type(_, ref ret_ty) = &i.sig.decl.output { 364 | if let Some((ret_ident, None, ret_is_ptr)) = 365 | MayaVisitSecondPass::quote_ty(&ret_ty) 366 | { 367 | let invoke_byval = match rhs_is_ptr { 368 | MayaVisitPtrType::NotPtr => quote! { 369 | arr._native.#fn_ident(self_) 370 | }, 371 | MayaVisitPtrType::ConstPtr => quote! { 372 | arr._native.#fn_ident(&self_._native) 373 | }, 374 | MayaVisitPtrType::MutPtr => quote! { 375 | arr._native.#fn_ident(&mut self_._native) 376 | }, 377 | }; 378 | let invoke_byref = match rhs_is_ptr { 379 | MayaVisitPtrType::NotPtr => quote! { 380 | arr._native.#fn_ident(*self_) 381 | }, 382 | MayaVisitPtrType::ConstPtr => quote! { 383 | arr._native.#fn_ident(&self_._native) 384 | }, 385 | MayaVisitPtrType::MutPtr => quote! { 386 | arr._native.#fn_ident(&mut self_._native) 387 | }, 388 | }; 389 | // Because the Maya API is weird enough as-is... 390 | // arr must be non-mut in getters; if func requires mut, then clone. 391 | // self (indexer) must be non-mut everywhere. 392 | let arr_mut_fix = match *fn_inputs.first().unwrap().value() { 393 | &syn::FnArg::SelfRef(ref self_arg) 394 | if self_arg.mutability.is_some() => quote! { 395 | let mut arr = arr.clone(); 396 | }, 397 | _ => quote! {} 398 | }; 399 | let indexer_mut_fix = match rhs_is_ptr { 400 | MayaVisitPtrType::MutPtr => quote! { 401 | let mut self_ = self.clone(); 402 | }, 403 | _ => quote! { 404 | let self_ = self; 405 | } 406 | }; 407 | // Always return value, not pointer, from Getter. Prefer the native function 408 | // that returns by value, but fall back to dereferencing the pointer and 409 | // cloning in the case of one that doesn't return by value. 410 | // Thus, NotPtr always gets to insert its getter but ConstPtr and MutPtr 411 | // must check the priority order first. 412 | match ret_is_ptr { 413 | MayaVisitPtrType::NotPtr => { 414 | // Getter. Return as-is. 415 | let ret_byval = self.wrap(&invoke_byval, &ret_ident); 416 | let ret_byref = self.wrap(&invoke_byref, &ret_ident); 417 | self.impl_getters.insert(rhs_ident, (MayaVisitPtrType::NotPtr, 418 | quote! { 419 | impl<'a> Getter<#impl_type> for #rhs_ident { 420 | type Output = #ret_ident; 421 | fn get(self, arr: &#impl_type) -> Self::Output { 422 | #arr_mut_fix 423 | #indexer_mut_fix 424 | unsafe { #ret_byval } 425 | } 426 | } 427 | impl<'b> Getter<#impl_type> for &'b #rhs_ident { 428 | type Output = #ret_ident; 429 | fn get(self, arr: &#impl_type) -> Self::Output { 430 | #arr_mut_fix 431 | #indexer_mut_fix 432 | unsafe { #ret_byref } 433 | } 434 | } 435 | })); 436 | }, 437 | MayaVisitPtrType::ConstPtr => { 438 | // Getter. Must wrap pointer in value. 439 | let insert_ok = match self.impl_getters.get(&rhs_ident) { 440 | Some(&(ref priority, _)) => 441 | *priority != MayaVisitPtrType::NotPtr, 442 | _ => true 443 | }; 444 | let retrieval = if self.is_struct(&ret_ident) { 445 | quote! { 446 | let mut ret = #ret_ident::default(); 447 | ret._native.operator_assign(ptr); 448 | ret 449 | } 450 | } 451 | else { 452 | quote! { *ptr } 453 | }; 454 | if insert_ok { 455 | self.impl_getters.insert(rhs_ident, (MayaVisitPtrType::ConstPtr, 456 | quote! { 457 | impl Getter<#impl_type> for #rhs_ident { 458 | type Output = #ret_ident; 459 | fn get(self, arr: &#impl_type) -> Self::Output { 460 | #arr_mut_fix 461 | #indexer_mut_fix 462 | unsafe { 463 | let ptr = #invoke_byval; 464 | #retrieval 465 | } 466 | } 467 | } 468 | impl<'b> Getter<#impl_type> for &'b #rhs_ident { 469 | type Output = #ret_ident; 470 | fn get(self, arr: &#impl_type) -> Self::Output { 471 | #arr_mut_fix 472 | #indexer_mut_fix 473 | unsafe { 474 | let ptr = #invoke_byref; 475 | #retrieval 476 | } 477 | } 478 | } 479 | })); 480 | } 481 | }, 482 | MayaVisitPtrType::MutPtr => { 483 | // Getter. Must wrap pointer in value. 484 | let insert_ok = match self.impl_getters.get(&rhs_ident) { 485 | Some(&(ref priority, _)) => 486 | *priority != MayaVisitPtrType::NotPtr && 487 | *priority != MayaVisitPtrType::ConstPtr, 488 | _ => true 489 | }; 490 | let retrieval = if self.is_struct(&ret_ident) { 491 | quote! { 492 | let mut ret = #ret_ident::default(); 493 | ret._native.operator_assign(ptr); 494 | ret 495 | } 496 | } 497 | else { 498 | quote! { *ptr } 499 | }; 500 | if insert_ok { 501 | self.impl_getters.insert(rhs_ident, (MayaVisitPtrType::MutPtr, 502 | quote! { 503 | impl Getter<#impl_type> for #rhs_ident { 504 | type Output = #ret_ident; 505 | fn get(self, arr: &#impl_type) -> Self::Output { 506 | #arr_mut_fix 507 | #indexer_mut_fix 508 | unsafe { 509 | let ptr = #invoke_byval; 510 | #retrieval 511 | } 512 | } 513 | } 514 | impl<'b> Getter<#impl_type> for &'b #rhs_ident { 515 | type Output = #ret_ident; 516 | fn get(self, arr: &#impl_type) -> Self::Output { 517 | #arr_mut_fix 518 | #indexer_mut_fix 519 | unsafe { 520 | let ptr = #invoke_byref; 521 | #retrieval 522 | } 523 | } 524 | } 525 | })); 526 | } 527 | 528 | // Setter. Accepts both reference and value input via Borrow. 529 | let ret_ident = MayaVisitSecondPass::inputize( 530 | &ret_ident, &ret_is_ptr); 531 | let unwrap_value = self.unwrap("e! { value }, &ret_ident, 532 | &ret_ty, MayaVisitPtrType::ConstPtr); 533 | let assignment = if ret_ident == syn::Ident::from("str") { 534 | quote! { (*ptr).operator_assign(#unwrap_value); } 535 | } 536 | else if self.is_struct(&ret_ident) { 537 | quote! { (*ptr).operator_assign(#unwrap_value); } 538 | } 539 | else { 540 | quote! { *ptr = *#unwrap_value; } 541 | }; 542 | self.impl_setters.insert(rhs_ident, quote! { 543 | impl<'i> Setter<'i, #impl_type> for #rhs_ident { 544 | type Input = #ret_ident; 545 | fn set(self, arr: &mut #impl_type, value: &Self::Input) { 546 | #indexer_mut_fix 547 | unsafe { 548 | let ptr = #invoke_byval; 549 | #assignment 550 | } 551 | } 552 | } 553 | impl<'b, 'i> Setter<'i, #impl_type> for &'b #rhs_ident { 554 | type Input = #ret_ident; 555 | fn set(self, arr: &mut #impl_type, value: &Self::Input) { 556 | #indexer_mut_fix 557 | unsafe { 558 | let ptr = #invoke_byref; 559 | #assignment 560 | } 561 | } 562 | } 563 | }); 564 | } 565 | }; 566 | } 567 | } 568 | } 569 | } 570 | } 571 | // Wraps the raw result of the given native expression with the given native type. 572 | fn wrap(&self, native_expr: "e::Tokens, rust_ty: &syn::Ident) -> quote::Tokens { 573 | if *rust_ty == syn::Ident::from("String") { 574 | // Special, we auto-convert the MString into a Rust String. 575 | quote! { String::unsafe_from(&#native_expr) } 576 | } 577 | else if *rust_ty == syn::Ident::from("str") { 578 | // Special, we auto-convert the MString into a Rust &str. 579 | quote! { String::unsafe_from(&#native_expr).as_str() } 580 | } 581 | else if self.first_pass.structs.contains(&rust_ty) { 582 | quote! { #rust_ty::from_native(#native_expr) } 583 | } 584 | else { 585 | quote! { #native_expr } 586 | } 587 | } 588 | fn unwrap(&self, rust_expr: "e::Tokens, rust_ty: &syn::Ident, native_ty: &syn::Type, 589 | is_ptr: MayaVisitPtrType) -> quote::Tokens 590 | { 591 | let ref_token = match is_ptr { 592 | MayaVisitPtrType::NotPtr => quote! {}, 593 | MayaVisitPtrType::ConstPtr => quote! { & }, 594 | MayaVisitPtrType::MutPtr => quote! { &mut }, 595 | }; 596 | let is_char = match native_ty { 597 | &syn::Type::Ptr(ref ty) => { 598 | match &*ty.elem { 599 | &syn::Type::Path(ref ty) => { 600 | let name = ty.path.segments.last().unwrap().value().ident; 601 | match name.to_string().as_str() { 602 | "c_char" => true, 603 | _ => false 604 | } 605 | }, 606 | _ => false 607 | } 608 | }, 609 | _ => false 610 | }; 611 | if *rust_ty == syn::Ident::from("String") { 612 | // Special, we auto-convert from Rust String back into MString/*char. 613 | if is_char { 614 | quote! { 615 | unimplemented!() 616 | } 617 | } 618 | else { 619 | quote! { 620 | #ref_token 621 | root::AUTODESK_NAMESPACE::MAYA_NAMESPACE::API_VERSION::MString::unsafe_from( 622 | (#rust_expr).as_str()) 623 | } 624 | } 625 | } 626 | else if *rust_ty == syn::Ident::from("str") { 627 | // Special, we auto-convert from Rust &str back into MString/*char. 628 | if is_char { 629 | quote! { 630 | unimplemented!() 631 | } 632 | } 633 | else { 634 | quote! { 635 | #ref_token 636 | root::AUTODESK_NAMESPACE::MAYA_NAMESPACE::API_VERSION::MString::unsafe_from( 637 | #rust_expr) 638 | } 639 | } 640 | } 641 | else if self.first_pass.structs.contains(&rust_ty) { 642 | // Grab native either by value or by reference. By value consumes the original. 643 | match is_ptr { 644 | MayaVisitPtrType::NotPtr => { 645 | quote! { #ref_token #rust_ty::into_native(#rust_expr) } 646 | }, 647 | _ => { 648 | quote! { #ref_token (#rust_expr)._native } 649 | } 650 | } 651 | } 652 | else { 653 | quote! { #rust_expr } 654 | } 655 | } 656 | } 657 | impl<'ast> syn::visit::Visit<'ast> for MayaVisitSecondPass { 658 | fn visit_item_mod(&mut self, i: &'ast ItemMod) { 659 | self.cur_namespace.push(i.ident); 660 | syn::visit::visit_item_mod(self, i); 661 | self.cur_namespace.pop(); 662 | } 663 | fn visit_item_struct(&mut self, i: &'ast ItemStruct) { 664 | let struct_type = &i.ident; 665 | if self.first_pass.structs.contains(struct_type) { 666 | let ns = &self.cur_namespace; 667 | self.tokens.push(quote! { 668 | pub struct #struct_type { 669 | _native: #( #ns :: )* #struct_type, 670 | } 671 | }); 672 | } 673 | } 674 | fn visit_item_impl(&mut self, i: &'ast ItemImpl) { 675 | match *i.self_ty { 676 | Type::Path(ref p) if self.first_pass.structs.contains( 677 | &p.path.segments.last().unwrap().value().ident) => 678 | { 679 | let impl_type = p.path.segments.last().unwrap().value().ident; 680 | self.impl_type = impl_type; 681 | self.impl_fns.clear(); 682 | self.impl_traits.clear(); 683 | self.impl_partialeq.clear(); 684 | self.impl_getters.clear(); 685 | self.impl_setters.clear(); 686 | syn::visit::visit_item_impl(self, i); 687 | 688 | if !self.impl_getters.is_empty() { 689 | self.impl_traits.push(quote! { 690 | impl Get for #impl_type {} 691 | }) 692 | } 693 | if !self.impl_setters.is_empty() { 694 | self.impl_traits.push(quote! { 695 | impl Set for #impl_type {} 696 | }) 697 | } 698 | 699 | let ns = &self.cur_namespace; 700 | let impl_fns = &self.impl_fns; 701 | let impl_traits = &self.impl_traits; 702 | let impl_partialeq = if self.impl_partialeq.is_empty() { 703 | vec![] 704 | } 705 | else { 706 | let mut partialeqs = vec![]; 707 | for (ident, tokens) in &self.impl_partialeq { 708 | partialeqs.push(quote! { 709 | impl PartialEq<#ident> for #impl_type { 710 | #( #tokens )* 711 | } 712 | }) 713 | } 714 | partialeqs 715 | }; 716 | let impl_getters = self.impl_getters.values().map(|x| &x.1); 717 | let impl_setters = self.impl_setters.values(); 718 | self.tokens.push(quote! { 719 | impl #impl_type { 720 | pub fn from_native(native: #( #ns :: )* #impl_type) -> Self { 721 | Self { _native: native } 722 | } 723 | pub unsafe fn into_native(s: Self) -> #( #ns :: )* #impl_type { 724 | ::std::mem::transmute(s) 725 | } 726 | #( #impl_fns )* 727 | } 728 | #( #impl_traits )* 729 | #( #impl_partialeq )* 730 | #( #impl_getters )* 731 | #( #impl_setters )* 732 | }) 733 | }, 734 | _ => {} 735 | } 736 | } 737 | fn visit_impl_item_method(&mut self, i: &'ast ImplItemMethod) { 738 | match MayaVisitSecondPass::process_method(&i.sig.ident) { 739 | MayaVisitMethodType::Constructor => { 740 | let impl_type = &self.impl_type; 741 | let ns = &self.cur_namespace; 742 | 743 | let fn_ident = &i.sig.ident; 744 | let fn_inputs = &i.sig.decl.inputs; 745 | if fn_inputs.len() == 0 { 746 | // Implement Default trait as well as new() constructor. 747 | self.impl_traits.push(quote! { 748 | impl Default for #impl_type { 749 | fn default() -> Self { 750 | Self::new() 751 | } 752 | } 753 | }); 754 | self.impl_fns.push(quote! { 755 | pub fn new() -> Self { 756 | Self::from_native(unsafe { #( #ns :: )* #impl_type :: #fn_ident() }) 757 | } 758 | }); 759 | } 760 | else if fn_inputs.len() == 1 && 761 | MayaVisitSecondPass::is_ptr(fn_inputs.first().unwrap().value(), impl_type) 762 | { 763 | // Implement Clone trait. 764 | self.impl_traits.push(quote! { 765 | impl Clone for #impl_type { 766 | fn clone(&self) -> Self { 767 | Self { _native: unsafe { 768 | #( #ns :: )* #impl_type :: #fn_ident(&self._native) 769 | } } 770 | } 771 | } 772 | }); 773 | } 774 | else { 775 | // Treat like a regular function. XXX 776 | } 777 | }, 778 | MayaVisitMethodType::Destructor => { 779 | let impl_type = self.impl_type; 780 | self.impl_traits.push(quote! { 781 | impl Drop for #impl_type { 782 | fn drop(&mut self) { 783 | unsafe { self._native.destruct() } 784 | } 785 | } 786 | }); 787 | }, 788 | MayaVisitMethodType::Operator => { 789 | let impl_type = self.impl_type; 790 | let fn_ident = &i.sig.ident; 791 | let fn_inputs = &i.sig.decl.inputs; 792 | 793 | // == and != are special because they belong to PartialEq. 794 | if fn_ident.to_string().starts_with("operator_eq") { 795 | if let &syn::FnArg::Captured(ref fn_arg) = *fn_inputs.last().unwrap().value() { 796 | if let Some((rhs_ident, None, rhs_is_ptr)) = 797 | MayaVisitSecondPass::quote_ty(&fn_arg.ty) 798 | { 799 | let rhs_ident = MayaVisitSecondPass::inputize(&rhs_ident, &rhs_is_ptr); 800 | let rhs_access = match rhs_is_ptr { 801 | MayaVisitPtrType::NotPtr => quote! { *other }, 802 | _ => quote! { &other._native } 803 | }; 804 | let v = self.impl_partialeq.entry(rhs_ident).or_insert(vec![]); 805 | v.push(quote! { 806 | fn eq(&self, other: &#rhs_ident) -> bool { 807 | unsafe { 808 | self._native.#fn_ident(#rhs_access) 809 | } 810 | } 811 | }); 812 | } 813 | } 814 | } 815 | if fn_ident.to_string().starts_with("operator_neq") { 816 | if let &syn::FnArg::Captured(ref fn_arg) = *fn_inputs.last().unwrap().value() { 817 | if let Some((rhs_ident, None, rhs_is_ptr)) = 818 | MayaVisitSecondPass::quote_ty(&fn_arg.ty) 819 | { 820 | let rhs_ident = MayaVisitSecondPass::inputize(&rhs_ident, &rhs_is_ptr); 821 | let rhs_access = match rhs_is_ptr { 822 | MayaVisitPtrType::NotPtr => quote! { *other }, 823 | _ => quote! { &other._native } 824 | }; 825 | let v = self.impl_partialeq.entry(rhs_ident).or_insert(vec![]); 826 | v.push(quote! { 827 | fn ne(&self, other: &#rhs_ident) -> bool { 828 | unsafe { 829 | self._native.#fn_ident(#rhs_access) 830 | } 831 | } 832 | }); 833 | } 834 | } 835 | } 836 | // ! is a special unary operator. 837 | else if fn_ident.to_string().starts_with("operator_not") { 838 | self.impl_traits.push(quote! { 839 | impl ::std::ops::Not for #impl_type { 840 | type Output = bool; 841 | fn not(self) -> bool { 842 | unsafe { self._native.#fn_ident() } 843 | } 844 | } 845 | impl<'a> ::std::ops::Not for &'a #impl_type { 846 | type Output = bool; 847 | fn not(self) -> bool { 848 | unsafe { self._native.#fn_ident() } 849 | } 850 | } 851 | }) 852 | } 853 | // [] is a special sorta-binary operator. 854 | else if fn_ident.to_string().starts_with("operator_index") { 855 | self.process_index_operator(i); 856 | } 857 | // Remaining operators (binary). 858 | else if fn_ident.to_string().starts_with("operator_add") { 859 | self.process_binary_operator( 860 | i, &syn::Ident::from("Add"), &syn::Ident::from("add")); 861 | } 862 | else if fn_ident.to_string().starts_with("operator_sub") { 863 | self.process_binary_operator( 864 | i, &syn::Ident::from("Sub"), &syn::Ident::from("sub")); 865 | } 866 | else if fn_ident.to_string().starts_with("operator_mul") { 867 | self.process_binary_operator( 868 | i, &syn::Ident::from("Mul"), &syn::Ident::from("mul")); 869 | } 870 | else if fn_ident.to_string().starts_with("operator_div") { 871 | self.process_binary_operator( 872 | i, &syn::Ident::from("Div"), &syn::Ident::from("div")); 873 | } 874 | else if fn_ident.to_string().starts_with("operator_or") { 875 | self.process_binary_operator( 876 | i, &syn::Ident::from("BitOr"), &syn::Ident::from("bitor")); 877 | } 878 | else if fn_ident.to_string().starts_with("operator_xor") { 879 | self.process_binary_operator( 880 | i, &syn::Ident::from("BitXor"), &syn::Ident::from("bitxor")); 881 | } 882 | }, 883 | MayaVisitMethodType::Normal => { 884 | let impl_type = &self.impl_type; 885 | let ns = &self.cur_namespace; 886 | 887 | let fn_name = i.sig.ident; 888 | let fn_inputs = &i.sig.decl.inputs; 889 | let (ret_ident, ret_is_ptr) = match &i.sig.decl.output { 890 | &syn::ReturnType::Type(_, ref ret_ty) => { 891 | if let Some((ret_ident, None, ret_is_ptr)) = 892 | MayaVisitSecondPass::quote_ty(&ret_ty) 893 | { 894 | if !self.is_implemented(&ret_ident) { 895 | eprintln!("{}::{} -- skipping because return type `{}` not \ 896 | implemented in bindings", impl_type, fn_name, ret_ident); 897 | return; 898 | } 899 | (Some(ret_ident), ret_is_ptr) 900 | } 901 | else { 902 | // Unknown return type; skip function processing. 903 | return; 904 | } 905 | }, 906 | &syn::ReturnType::Default => { 907 | (None, MayaVisitPtrType::NotPtr) 908 | } 909 | }; 910 | 911 | // Conform the function to a Rust-style API. 912 | let mut preface_transforms = vec![]; 913 | let mut postscript_transforms = vec![]; 914 | let mut inputs = vec![]; 915 | let mut outputs = vec![]; 916 | let mut invoke_args = vec![]; 917 | let mut self_call = false; 918 | for input_pair in fn_inputs.pairs() { 919 | let arg = match input_pair { 920 | syn::punctuated::Pair::Punctuated(arg, _) => arg, 921 | syn::punctuated::Pair::End(arg) => arg 922 | }; 923 | match arg { 924 | &syn::FnArg::SelfRef(..) | &syn::FnArg::SelfValue(..) => { 925 | inputs.push(quote! { #arg }); 926 | self_call = true; 927 | }, 928 | &syn::FnArg::Captured(ref arg) => { 929 | match MayaVisitSecondPass::quote_ty(&arg.ty) { 930 | Some((ty_ident, arr_size, ty_is_ptr)) => { 931 | let name = &arg.pat; 932 | 933 | if let Some(arr_size) = arr_size { 934 | if arr_size == 0 { 935 | // Can't handle this right now. 936 | eprintln!("{}::{} -- skipping because arg type \ 937 | `{}: {}` is 0-sized array", 938 | impl_type, fn_name, 939 | name.into_tokens(), ty_ident); 940 | return; 941 | } 942 | } 943 | 944 | if !self.is_implemented(&ty_ident) { 945 | // Can't handle this right now. 946 | eprintln!("{}::{} -- skipping because arg type \ 947 | `{}: {}` not implemented in bindings", 948 | impl_type, fn_name, name.into_tokens(), ty_ident); 949 | return; 950 | } 951 | 952 | match ty_is_ptr { 953 | MayaVisitPtrType::MutPtr => { 954 | if ty_ident == syn::Ident::from("MDataBlock") || 955 | ty_ident == syn::Ident::from("MDGContext") { 956 | // Special case -- mutable inputs. 957 | // XXX -- may need to generalize check types here. 958 | let ty_ident = MayaVisitSecondPass::inputize( 959 | &ty_ident, &MayaVisitPtrType::MutPtr); 960 | let unwrapped = self.unwrap( 961 | "e! { #name }, &ty_ident, &arg.ty, 962 | MayaVisitPtrType::MutPtr); 963 | let ty_arr = MayaVisitSecondPass::quote_arr( 964 | &ty_ident, arr_size); 965 | inputs.push(quote! { #name : &mut #ty_arr }); 966 | invoke_args.push(unwrapped); 967 | } 968 | else { 969 | // Normal case -- mutables are output parameters. 970 | // Note: the declaration make #name into a value, 971 | // not pointer, so we need to refer to it by 972 | // &mut #name. 973 | // Warning: DO NOT inputize in this case! 974 | let unwrapped = self.unwrap( 975 | "e! { &mut #name }, &ty_ident, &arg.ty, 976 | MayaVisitPtrType::MutPtr); 977 | let ty_arr = MayaVisitSecondPass::quote_arr( 978 | &ty_ident, arr_size); 979 | preface_transforms.push(quote! { 980 | let mut #name: #ty_arr = Default::default(); 981 | }); 982 | outputs.push( 983 | (quote! { #name }, ty_ident, arr_size)); 984 | invoke_args.push(unwrapped); 985 | } 986 | }, 987 | MayaVisitPtrType::ConstPtr => { 988 | let ty_ident = MayaVisitSecondPass::inputize( 989 | &ty_ident, &MayaVisitPtrType::ConstPtr); 990 | let unwrapped = self.unwrap( 991 | "e! { #name }, &ty_ident, &arg.ty, 992 | MayaVisitPtrType::ConstPtr); 993 | let ty_arr = MayaVisitSecondPass::quote_arr( 994 | &ty_ident, arr_size); 995 | inputs.push(quote! { #name : &#ty_arr }); 996 | invoke_args.push(unwrapped); 997 | }, 998 | MayaVisitPtrType::NotPtr => { 999 | let ty_ident = MayaVisitSecondPass::inputize( 1000 | &ty_ident, &MayaVisitPtrType::NotPtr); 1001 | let unwrapped = self.unwrap( 1002 | "e! { #name }, &ty_ident, &arg.ty, 1003 | MayaVisitPtrType::NotPtr); 1004 | inputs.push(quote! { #name: #ty_ident }); 1005 | invoke_args.push(unwrapped); 1006 | }, 1007 | } 1008 | } 1009 | None => return // don't know how to handle 1010 | }; 1011 | } 1012 | _ => return // don't know how to handle 1013 | }; 1014 | } 1015 | 1016 | let invoke = if self_call { 1017 | quote! { self._native.#fn_name(#( #invoke_args ),*) } 1018 | } 1019 | else { 1020 | quote! { #( #ns ::)* #impl_type :: #fn_name(#( #invoke_args ),*) } 1021 | }; 1022 | 1023 | if let Some(ret_ident) = ret_ident { 1024 | match ret_is_ptr { 1025 | MayaVisitPtrType::NotPtr => { 1026 | // Need to wrap because the function returned a native arg by value. 1027 | let ret_wrapped = self.wrap("e! { __maya_ret }, &ret_ident); 1028 | outputs.push((ret_wrapped, ret_ident, None)); 1029 | }, 1030 | _ => { 1031 | // Manually wrap by constructing the wrapper ahead-of-time, assigning 1032 | // the pointer into the wrapper, and then returning the wrapper. 1033 | // (Strings and primitives can be copied after-the-fact.) 1034 | if ret_ident == syn::Ident::from("String") { 1035 | postscript_transforms.push(quote! { 1036 | let __maya_ret_val = String::unsafe_from(unsafe { __maya_ret }); 1037 | }); 1038 | } 1039 | else if self.is_struct(&ret_ident) { 1040 | postscript_transforms.push(quote! { 1041 | let mut __maya_ret_val = #ret_ident::default(); 1042 | unsafe { __maya_ret_val._native.operator_assign(__maya_ret); } 1043 | }); 1044 | } 1045 | else { 1046 | postscript_transforms.push(quote! { 1047 | let __maya_ret_val = unsafe { *__maya_ret }; 1048 | }) 1049 | } 1050 | outputs.push((quote! { __maya_ret_val }, ret_ident, None)); 1051 | } 1052 | } 1053 | } 1054 | 1055 | // Look through the outputs to see if one of them is MStatus-typed. 1056 | // If so, loft it into the Result type. 1057 | let mut status = None; 1058 | for i in 0..outputs.len() { 1059 | let (_, ret_ident, _) = outputs[i]; 1060 | if ret_ident == syn::Ident::from("MStatus") { 1061 | let (status_quote, _, _) = outputs.remove(i); 1062 | status = Some(status_quote); 1063 | break; 1064 | } 1065 | } 1066 | let ret_decl = match (outputs.len(), &status) { 1067 | (0, &None) => quote! {}, 1068 | (0, &Some(..)) => quote! { -> Result<(), MStatus> }, 1069 | (1, &None) => { 1070 | let &(_, only_ty, arr_size) = outputs.first().unwrap(); 1071 | let only_out = MayaVisitSecondPass::quote_arr(&only_ty, arr_size); 1072 | quote! { -> #only_out } 1073 | }, 1074 | (1, &Some(..)) => { 1075 | let &(_, only_ty, arr_size) = outputs.first().unwrap(); 1076 | let only_out = MayaVisitSecondPass::quote_arr(&only_ty, arr_size); 1077 | quote! { -> Result<#only_out, MStatus> } 1078 | }, 1079 | (_, &None) => { 1080 | let outs = outputs.iter().map( 1081 | |&(_, ty, asz)| MayaVisitSecondPass::quote_arr(&ty, asz)); 1082 | quote! { -> (#( #outs ),*) } 1083 | }, 1084 | (_, &Some(..)) => { 1085 | let outs = outputs.iter().map( 1086 | |&(_, ty, asz)| MayaVisitSecondPass::quote_arr(&ty, asz)); 1087 | quote! { -> Result<(#( #outs ),*), MStatus> } 1088 | }, 1089 | }; 1090 | let ret_statement = match (outputs.len(), status) { 1091 | (0, None) => quote! {}, 1092 | (0, Some(status_quote)) => quote! { 1093 | let __maya_ret_status = #status_quote; 1094 | match __maya_ret_status.error() { 1095 | false => Ok(()), 1096 | true => Err(__maya_ret_status), 1097 | } 1098 | }, 1099 | (1, None) => { 1100 | let &(ref only_ret, _, _) = outputs.first().unwrap(); 1101 | quote! { #only_ret } 1102 | }, 1103 | (1, Some(status_quote)) => { 1104 | let &(ref only_ret, _, _) = outputs.first().unwrap(); 1105 | quote! { 1106 | let __maya_ret_status = #status_quote; 1107 | match __maya_ret_status.error() { 1108 | false => Ok(#only_ret), 1109 | true => Err(__maya_ret_status), 1110 | } 1111 | } 1112 | }, 1113 | (_, None) => { 1114 | let rets = outputs.iter().map(|&(ref ret, _, _)| ret); 1115 | quote! { (#( #rets ),*) } 1116 | }, 1117 | (_, Some(status_quote)) => { 1118 | let rets = outputs.iter().map(|&(ref ret, _, _)| ret); 1119 | quote! { 1120 | let __maya_ret_status = #status_quote; 1121 | match __maya_ret_status.error() { 1122 | false => Ok((#( #rets ),*)), 1123 | true => Err(__maya_ret_status), 1124 | } 1125 | } 1126 | }, 1127 | }; 1128 | 1129 | self.impl_fns.push(quote! { 1130 | pub fn #fn_name(#( #inputs ),*) #ret_decl { 1131 | #( #preface_transforms )* 1132 | let __maya_ret = unsafe { #invoke }; 1133 | #( #postscript_transforms )* 1134 | #ret_statement 1135 | } 1136 | }); 1137 | } 1138 | } 1139 | } 1140 | fn visit_item_type(&mut self, i: &'ast ItemType) { 1141 | let ident = &i.ident; 1142 | let ns = &self.cur_namespace; 1143 | 1144 | // Verbatim typedef. 1145 | if self.first_pass.types.contains(ident) { 1146 | self.tokens.push(quote! { 1147 | pub type #ident = #( #ns :: )* #ident; 1148 | }); 1149 | } 1150 | } 1151 | fn visit_item_enum(&mut self, i: &'ast ItemEnum) { 1152 | let ident = &i.ident; 1153 | let ns = &self.cur_namespace; 1154 | 1155 | // Typedef the actual enum. 1156 | if self.first_pass.types.contains(ident) { 1157 | self.tokens.push(quote! { 1158 | pub type #ident = #( #ns :: )* #ident; 1159 | }); 1160 | } 1161 | } 1162 | } 1163 | 1164 | fn main() { 1165 | // XXX: Default is Windows-specific. 1166 | let maya = PathBuf::from(match env::var("MAYA_LOCATION") { 1167 | Ok(loc) => String::from(loc.as_str()), 1168 | Err(_) => String::from("C:/Program Files/Autodesk/Maya2018") 1169 | }); 1170 | 1171 | // Link Maya's libraries. 1172 | println!("cargo:rustc-link-lib=dylib=OpenMaya"); 1173 | println!("cargo:rustc-link-lib=dylib=Foundation"); 1174 | println!("cargo:rustc-link-search=native={}", maya.join("lib").to_str().unwrap()); 1175 | 1176 | // Add shims for implementing virtual classes. 1177 | cc::Build::new() 1178 | .cpp(true) 1179 | .file("shim/shim.cpp") 1180 | .include("shim") 1181 | .include(maya.join("include").to_str().unwrap()) 1182 | .compile("shim"); 1183 | 1184 | // The bindgen::Builder is the main entry point 1185 | // to bindgen, and lets you build up options for 1186 | // the resulting bindings. 1187 | let bindings = bindgen::Builder::default() 1188 | // The input header we would like to generate 1189 | // bindings for. 1190 | .header("wrapper.h") 1191 | .clang_arg(format!("-isystem{}", maya.join("include").to_str().unwrap())) 1192 | .clang_arg("-Ishim") 1193 | .clang_arg("-x") 1194 | .clang_arg("c++") 1195 | .clang_arg("-std=c++14") 1196 | .enable_cxx_namespaces() 1197 | .derive_copy(false) 1198 | .rustfmt_bindings(true) 1199 | .generate_comments(true) 1200 | .link("OpenMaya") 1201 | .link("Foundation") 1202 | .opaque_type("std.*") // We don't need C++ stdlib access. 1203 | .whitelist_type("Shim.*") 1204 | .whitelist_type(".*MArgList") 1205 | .whitelist_type(".*MDagPath") 1206 | .whitelist_type(".*MDagPathArray") 1207 | .whitelist_type(".*MFnBase") 1208 | .whitelist_type(".*MFnDagNode") 1209 | .whitelist_type(".*MFnPlugin") 1210 | .whitelist_type(".*MGlobal") 1211 | .whitelist_type(".*MObject") 1212 | .whitelist_type(".*MSelectionList") 1213 | .whitelist_type(".*MSyntax") 1214 | .whitelist_type(".*MVector") 1215 | .rustified_enum(".*MFn_Type") 1216 | .rustified_enum(".*MStatus_MStatusCode") 1217 | // Finish the builder and generate the bindings. 1218 | .generate() 1219 | // Unwrap the Result and panic on failure. 1220 | .expect("Unable to generate bindings"); 1221 | 1222 | // Write the bindings to the $OUT_DIR/bindings.rs file. 1223 | let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); 1224 | bindings 1225 | .write_to_file(out_path.join("bindings.rs")) 1226 | .expect("Couldn't write bindings!"); 1227 | 1228 | // Write the high-level Maya API bindings to the $OUT_DIR/maya.rs file. 1229 | let file = syn::parse_file(&bindings.to_string()).expect("Unable to parse file"); 1230 | let mut first_pass = MayaVisitFirstPass::new(); 1231 | syn::visit::visit_file(&mut first_pass, &file); 1232 | let mut second_pass = MayaVisitSecondPass::new(first_pass); 1233 | syn::visit::visit_file(&mut second_pass, &file); 1234 | let gen_lines: Vec = second_pass.tokens.iter().map(|x| x.to_string()).collect(); 1235 | let gen = gen_lines.join("\n"); 1236 | let mut formatted = Vec::new(); 1237 | let (_, filemap, _) = rustfmt::format_input( 1238 | rustfmt::Input::Text(gen), 1239 | &rustfmt::config::Config::default(), 1240 | Some(&mut formatted)) 1241 | .expect("Formatter failed!"); 1242 | File::create(&out_path.join("maya.rs")) 1243 | .expect("Couldn't create file!") 1244 | .write_all(filemap[0].1.to_string().as_bytes()) 1245 | .expect("Couldn't write Maya API!"); 1246 | } 1247 | -------------------------------------------------------------------------------- /shim/shim.cpp: -------------------------------------------------------------------------------- 1 | #include "shim.h" 2 | 3 | ShimCommand::ShimCommand(void* rustData, const FunctionTable& functions) 4 | : _rustData(rustData), _functions(functions) {} 5 | ShimCommand::~ShimCommand() 6 | { 7 | _functions.destruct(_rustData); 8 | } 9 | MStatus ShimCommand::doIt(const MArgList& args) 10 | { 11 | return _functions.doIt(_rustData, args); 12 | } 13 | void* ShimCommand::Create(void* rustData, const FunctionTable& functions) 14 | { 15 | return new ShimCommand(rustData, functions); 16 | } 17 | -------------------------------------------------------------------------------- /shim/shim.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | struct ShimCommand : public MPxCommand 5 | { 6 | struct FunctionTable { 7 | MStatus (*doIt)(void*, const MArgList&); 8 | void (*destruct)(void*); 9 | }; 10 | 11 | ShimCommand(void* rustData, const FunctionTable& functions); 12 | ~ShimCommand() override; 13 | MStatus doIt(const MArgList& args) override; 14 | 15 | void* _rustData; 16 | FunctionTable _functions; 17 | 18 | static void* Create(void*, const FunctionTable&); 19 | }; 20 | -------------------------------------------------------------------------------- /src/OpenMaya/core.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_upper_case_globals, non_camel_case_types, non_snake_case)] 2 | 3 | use native; 4 | use std::ffi::CStr; 5 | use std::fmt::{Debug, Error, Formatter}; 6 | 7 | pub mod MFn { 8 | use native; 9 | pub type Type = native::MFn_Type; 10 | } 11 | pub mod MStatusCode { 12 | use native; 13 | pub type Type = native::MStatus_MStatusCode; 14 | } 15 | 16 | pub struct MObject { 17 | pub _native: native::MObject 18 | } 19 | impl MObject { 20 | pub fn wrap(n: native::MObject) -> Self { 21 | Self { _native: n } 22 | } 23 | pub fn new() -> Self { 24 | Self { _native: unsafe { native::MObject::new() } } 25 | } 26 | pub fn hasFn(&self, fs: MFn::Type) -> bool { 27 | unsafe { self._native.hasFn(fs) } 28 | } 29 | pub fn isNull(&self) -> bool { 30 | unsafe { self._native.isNull() } 31 | } 32 | pub fn apiType(&self) -> MFn::Type { 33 | unsafe { self._native.apiType() } 34 | } 35 | pub fn apiTypeStr(&self) -> &str { 36 | unsafe { 37 | let s_ptr = self._native.apiTypeStr(); 38 | CStr::from_ptr(s_ptr).to_str().expect("invalid apiTypeStr from OpenMaya") 39 | } 40 | } 41 | } 42 | impl PartialEq for MObject { 43 | fn eq(&self, other: &Self) -> bool { 44 | unsafe { self._native.operator_eq(&other._native) } 45 | } 46 | } 47 | impl Clone for MObject { 48 | fn clone(&self) -> Self { 49 | Self { _native: unsafe { native::MObject::new1(&self._native) } } 50 | } 51 | } 52 | impl Drop for MObject { 53 | fn drop(&mut self) { 54 | unsafe { self._native.destruct(); } 55 | } 56 | } 57 | 58 | pub struct MStatus { 59 | pub _native: native::MStatus 60 | } 61 | impl MStatus { 62 | pub fn wrap(n: native::MStatus) -> Self { 63 | Self { _native: n } 64 | } 65 | pub fn new() -> Self { 66 | Self { _native: unsafe { native::MStatus::new() } } 67 | } 68 | pub fn new_code(code: MStatusCode::Type) -> Self { 69 | Self { _native: unsafe { native::MStatus::new1(code) } } 70 | } 71 | pub fn error(&self) -> bool { 72 | unsafe { self._native.error() } 73 | } 74 | pub fn clear(&mut self) { 75 | unsafe { self._native.clear() } 76 | } 77 | pub fn statusCode(&self) -> MStatusCode::Type { 78 | unsafe { self._native.statusCode() } 79 | } 80 | pub fn errorString(&self) -> String { 81 | unsafe { String::from(&self._native.errorString()) } 82 | } 83 | pub fn perror1(&self, s: &str) { 84 | let mstring = native::MString::from(s); 85 | unsafe { self._native.perror1(&mstring) } 86 | } 87 | pub fn clone_native(&self) -> native::MStatus { 88 | self.clone()._native 89 | } 90 | } 91 | impl PartialEq for MStatus { 92 | fn eq(&self, other: &Self) -> bool { 93 | unsafe { self._native.operator_eq(&other._native) } 94 | } 95 | } 96 | impl Clone for MStatus { 97 | fn clone(&self) -> Self { 98 | Self { _native: unsafe { native::MStatus::new2(&self._native) } } 99 | } 100 | } 101 | impl Debug for MStatus { 102 | fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { 103 | write!(f, "MStatus({})", self.errorString()) 104 | } 105 | } 106 | #[macro_export] macro_rules! MS { 107 | ($code:ident) => { 108 | ::maya2018::OpenMaya::MStatus::new_code(::maya2018::OpenMaya::MStatusCode::$code) 109 | }; 110 | } 111 | #[macro_export] macro_rules! check_mstatus { 112 | ($ret:expr, $status:expr) => {{ 113 | match $status.error() { 114 | true => Err($status), 115 | false => Ok($ret) 116 | } 117 | }}; 118 | } 119 | 120 | pub struct MDagPath { 121 | pub _native: native::MDagPath 122 | } 123 | impl MDagPath { 124 | pub fn wrap(n: native::MDagPath) -> Self { 125 | Self { _native: n } 126 | } 127 | pub fn new() -> Self { 128 | Self { _native: unsafe { native::MDagPath::new() }} 129 | } 130 | // XXX getAllPathsBelow 131 | pub fn hasFn(&self, apiType: MFn::Type) -> Result { 132 | let mut status = MStatus::new(); 133 | unsafe { 134 | let hasFn = self._native.hasFn(apiType, &mut status._native); 135 | check_mstatus!(hasFn, status) 136 | } 137 | } 138 | pub fn apiType(&self) -> Result { 139 | let mut status = MStatus::new(); 140 | unsafe { 141 | let apiType = self._native.apiType(&mut status._native); 142 | check_mstatus!(apiType, status) 143 | } 144 | } 145 | pub fn isValid(&self) -> Result { 146 | let mut status = MStatus::new(); 147 | unsafe { 148 | let isValid = self._native.isValid(&mut status._native); 149 | check_mstatus!(isValid, status) 150 | } 151 | } 152 | pub fn node(&self) -> Result { 153 | let mut status = MStatus::new(); 154 | unsafe { 155 | let obj = self._native.node(&mut status._native); 156 | check_mstatus!(MObject::wrap(obj), status) 157 | } 158 | } 159 | pub fn transform(&self) -> Result { 160 | let mut status = MStatus::new(); 161 | unsafe { 162 | let obj = self._native.transform(&mut status._native); 163 | check_mstatus!(MObject::wrap(obj), status) 164 | } 165 | } 166 | pub fn length(&self) -> Result { 167 | let mut status = MStatus::new(); 168 | unsafe { 169 | let length = self._native.length(&mut status._native); 170 | check_mstatus!(length, status) 171 | } 172 | } 173 | pub fn extendToShape(&mut self) -> Result<(), MStatus> { 174 | unsafe { 175 | let native_status = self._native.extendToShape(); 176 | let status = MStatus::wrap(native_status); 177 | check_mstatus!((), status) 178 | } 179 | } 180 | pub fn extendToShapeDirectlyBelow(&mut self, i: u32) -> Result<(), MStatus> { 181 | unsafe { 182 | let native_status = self._native.extendToShapeDirectlyBelow(i); 183 | let status = MStatus::wrap(native_status); 184 | check_mstatus!((), status) 185 | } 186 | } 187 | pub fn numberOfShapesDirectlyBelow(&self) -> Result { 188 | let mut num = 0u32; 189 | unsafe { 190 | let native_status = self._native.numberOfShapesDirectlyBelow(&mut num); 191 | let status = MStatus::wrap(native_status); 192 | check_mstatus!(num, status) 193 | } 194 | } 195 | pub fn push(&mut self, obj: &MObject) -> Result<(), MStatus> { 196 | unsafe { 197 | let native_status = self._native.push(&obj._native); 198 | let status = MStatus::wrap(native_status); 199 | check_mstatus!((), status) 200 | } 201 | } 202 | pub fn pop(&mut self, num: u32) -> Result<(), MStatus> { 203 | unsafe { 204 | let native_status = self._native.pop(num); 205 | let status = MStatus::wrap(native_status); 206 | check_mstatus!((), status) 207 | } 208 | } 209 | pub fn childCount(&mut self) -> Result { 210 | let mut status = MStatus::new(); 211 | unsafe { 212 | let childCount = self._native.childCount(&mut status._native); 213 | check_mstatus!(childCount, status) 214 | } 215 | } 216 | pub fn child(&self, i: u32) -> Result { 217 | let mut status = MStatus::new(); 218 | unsafe { 219 | let native_obj = self._native.child(i, &mut status._native); 220 | check_mstatus!(MObject::wrap(native_obj), status) 221 | } 222 | } 223 | // XXX inclusiveMatrix 224 | // XXX exclusiveMatrix 225 | // XXX inclusiveMatrixInverse 226 | // XXX exclusiveMatrixInverse 227 | pub fn set(&mut self, src: &MDagPath) -> Result<(), MStatus> { 228 | unsafe { 229 | let native_status = self._native.set(&src._native); 230 | let status = MStatus::wrap(native_status); 231 | check_mstatus!((), status) 232 | } 233 | } 234 | pub fn pathCount(&self) -> Result { 235 | let mut status = MStatus::new(); 236 | unsafe { 237 | let pathCount = self._native.pathCount(&mut status._native); 238 | check_mstatus!(pathCount, status) 239 | } 240 | } 241 | pub fn getPath(&self, i: u32) -> Result { 242 | let mut path = MDagPath::new(); 243 | unsafe { 244 | let native_status = self._native.getPath(&mut path._native, i); 245 | let status = MStatus::wrap(native_status); 246 | check_mstatus!(path, status) 247 | } 248 | } 249 | pub fn fullPathName(&self) -> Result { 250 | let mut status = MStatus::new(); 251 | unsafe { 252 | let mstring = self._native.fullPathName(&mut status._native); 253 | let name = String::from(&mstring); 254 | check_mstatus!(name, status) 255 | } 256 | } 257 | pub fn partialPathName(&self) -> Result { 258 | let mut status = MStatus::new(); 259 | unsafe { 260 | let mstring = self._native.partialPathName(&mut status._native); 261 | let name = String::from(&mstring); 262 | check_mstatus!(name, status) 263 | } 264 | } 265 | pub fn isInstanced(&self) -> Result { 266 | let mut status = MStatus::new(); 267 | unsafe { 268 | let isInstanced = self._native.isInstanced(&mut status._native); 269 | check_mstatus!(isInstanced, status) 270 | } 271 | } 272 | pub fn instanceNumber(&self) -> Result { 273 | let mut status = MStatus::new(); 274 | unsafe { 275 | let instanceNumber = self._native.instanceNumber(&mut status._native); 276 | check_mstatus!(instanceNumber, status) 277 | } 278 | } 279 | pub fn isVisible(&self) -> Result { 280 | let mut status = MStatus::new(); 281 | unsafe { 282 | let isVisible = self._native.isVisible(&mut status._native); 283 | check_mstatus!(isVisible, status) 284 | } 285 | } 286 | pub fn isTemplated(&self) -> Result { 287 | let mut status = MStatus::new(); 288 | unsafe { 289 | let isTemplated = self._native.isTemplated(&mut status._native); 290 | check_mstatus!(isTemplated, status) 291 | } 292 | } 293 | // XXX getDrawOverrideInfo 294 | } 295 | impl PartialEq for MDagPath { 296 | fn eq(&self, other: &Self) -> bool { 297 | unsafe { self._native.operator_eq(&other._native) } 298 | } 299 | } 300 | impl Clone for MDagPath { 301 | fn clone(&self) -> Self { 302 | Self { _native: unsafe { native::MDagPath::new1(&self._native) }} 303 | } 304 | } 305 | impl Drop for MDagPath { 306 | fn drop(&mut self) { 307 | unsafe { native::MDagPath_MDagPath_destructor(&mut self._native); } 308 | } 309 | } 310 | 311 | // XXX unimplemented 312 | pub struct MArgList { 313 | pub _native: native::MArgList 314 | } 315 | impl MArgList { 316 | pub fn wrap(n: native::MArgList) -> Self { 317 | Self { _native : n} 318 | } 319 | } 320 | 321 | // XXX unimplemented 322 | pub struct MSyntax { 323 | pub _native: native::MSyntax 324 | } 325 | impl MSyntax { 326 | pub fn wrap(n: native::MSyntax) -> Self { 327 | Self { _native : n} 328 | } 329 | pub fn new() -> Self { 330 | Self { _native: unsafe { native::MSyntax::new() } } 331 | } 332 | } 333 | impl Clone for MSyntax { 334 | fn clone(&self) -> Self { 335 | Self { _native: unsafe { native::MSyntax::new1(&self._native) } } 336 | } 337 | } 338 | 339 | pub mod MGlobal { 340 | use native; 341 | pub fn displayInfo(s: &str) { 342 | unsafe { native::MGlobal::displayInfo(&native::MString::from(s)); } 343 | } 344 | } 345 | -------------------------------------------------------------------------------- /src/OpenMaya/mfn.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_upper_case_globals, non_camel_case_types, non_snake_case)] 2 | 3 | use std; 4 | use std::ffi::CString; 5 | use native; 6 | use OpenMaya::core::*; 7 | use OpenMaya::mpx::*; 8 | 9 | pub struct MFnPlugin { 10 | _native: native::MFnPlugin 11 | } 12 | impl MFnPlugin { 13 | pub fn new(obj: &mut MObject, vendor: &str, version: &str, api: &str) -> 14 | Result 15 | { 16 | let mut status = MStatus::new(); 17 | let cvendor = CString::new(vendor).unwrap(); 18 | let cversion = CString::new(version).unwrap(); 19 | let capi = CString::new(api).unwrap(); 20 | let plugin = unsafe { native::MFnPlugin::new1( 21 | &mut obj._native, cvendor.as_ptr(), cversion.as_ptr(), capi.as_ptr(), 22 | &mut status._native) }; 23 | check_mstatus!(MFnPlugin { _native: plugin }, status) 24 | } 25 | pub fn registerCommand(&mut self, name: &str) -> Result<(), MStatus> 26 | where T: MPxCommand + Sized 27 | { 28 | let creator: Option *mut std::os::raw::c_void> = 29 | Some(T::shim_creator); 30 | let syntax_creator: Option native::MSyntax> = 31 | Some(T::shim_syntax_creator); 32 | let native_status = unsafe { self._native.registerCommand( 33 | &native::MString::from(name), creator, syntax_creator) }; 34 | let status = MStatus::wrap(native_status); 35 | check_mstatus!((), status) 36 | } 37 | pub fn deregisterCommand(&mut self, name: &str) -> Result<(), MStatus> { 38 | let native_status = unsafe { self._native.deregisterCommand(&native::MString::from(name)) }; 39 | let status = MStatus::wrap(native_status); 40 | check_mstatus!((), status) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/OpenMaya/mod.rs: -------------------------------------------------------------------------------- 1 | /// Core OpenMaya data structures. 2 | #[macro_use] mod core; 3 | pub use OpenMaya::core::*; 4 | 5 | /// Maya proxy objects, implemented via Rust traits. 6 | mod mpx; 7 | pub use OpenMaya::mpx::*; 8 | 9 | // Maya function sets, implemented as Rust wrapper structs. 10 | mod mfn; 11 | pub use OpenMaya::mfn::*; -------------------------------------------------------------------------------- /src/OpenMaya/mpx.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_upper_case_globals, non_camel_case_types, non_snake_case)] 2 | 3 | use std; 4 | use native; 5 | use OpenMaya::core::*; 6 | 7 | pub trait MPxCommand { 8 | fn new() -> Box where Self: Sized; 9 | fn syntax() -> MSyntax where Self: Sized { 10 | MSyntax::new() 11 | } 12 | fn doIt(&self, args: &MArgList) -> MStatus; 13 | 14 | unsafe extern fn shim_creator() -> *mut std::os::raw::c_void where Self : Sized { 15 | let a: Box = Self::new(); 16 | let b: Box> = Box::new(a); // This Box is temporary so we can get raw ptr. 17 | let ptr: *mut Box = Box::into_raw(b); 18 | native::ShimCommand::Create(ptr as *mut std::os::raw::c_void, 19 | &native::ShimCommand_FunctionTable { 20 | doIt: Some(Self::shim_doIt), 21 | destruct: Some(Self::shim_destruct) 22 | }) 23 | } 24 | unsafe extern fn shim_syntax_creator() -> native::MSyntax where Self : Sized { 25 | Self::syntax().clone()._native 26 | } 27 | unsafe extern fn shim_destruct(ptr: *mut std::os::raw::c_void) where Self: Sized { 28 | let a: *mut Box = ptr as *mut Box; 29 | Box::from_raw(a); 30 | } 31 | unsafe extern fn shim_doIt(ptr: *mut std::os::raw::c_void, args: *const native::MArgList) 32 | -> native::MStatus where Self: Sized 33 | { 34 | let cmd = &*(ptr as *mut Box); 35 | let args_copy = native::MArgList::new1(args); 36 | cmd.doIt(&MArgList::wrap(args_copy)).clone()._native 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod native; 2 | pub mod native2; 3 | #[allow(non_snake_case)] pub mod OpenMaya; 4 | -------------------------------------------------------------------------------- /src/native.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | include!(concat!(env!("OUT_DIR"), "/bindings.rs")); 3 | 4 | pub use self::root::*; 5 | pub use self::root::AUTODESK_NAMESPACE::MAYA_NAMESPACE::API_VERSION::*; 6 | 7 | use std::ffi::{CStr, CString}; 8 | 9 | impl<'a> From<&'a MString> for String { 10 | fn from(mstring: &MString) -> String { 11 | unsafe { 12 | let s_ptr = mstring.asUTF8(); 13 | let s = CStr::from_ptr(s_ptr).to_str().expect("invalid MString from OpenMaya"); 14 | String::from(s) 15 | } 16 | } 17 | } 18 | impl<'a> From<&'a str> for MString { 19 | fn from(string: &str) -> MString { 20 | let cstring = CString::new(string).unwrap(); 21 | unsafe { MString::new1(cstring.as_ptr()) } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/native2.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_snake_case, non_camel_case_types, non_upper_case_globals)] 2 | include!(concat!(env!("OUT_DIR"), "/bindings.rs")); 3 | 4 | use std::ffi::{CStr, CString}; 5 | use std::os::raw::c_char; 6 | unsafe trait UnsafeFrom { 7 | fn unsafe_from(original: FromType) -> Self where Self: Sized; 8 | } 9 | impl<'a> From<&'a root::AUTODESK_NAMESPACE::MAYA_NAMESPACE::API_VERSION::MString> for String { 10 | fn from(mstring: &root::AUTODESK_NAMESPACE::MAYA_NAMESPACE::API_VERSION::MString) -> String { 11 | unsafe { 12 | let s_ptr = mstring.asUTF8(); 13 | let s = CStr::from_ptr(s_ptr).to_str().expect("invalid MString from OpenMaya"); 14 | String::from(s) 15 | } 16 | } 17 | } 18 | impl<'a> From<&'a str> for root::AUTODESK_NAMESPACE::MAYA_NAMESPACE::API_VERSION::MString { 19 | fn from(string: &str) -> root::AUTODESK_NAMESPACE::MAYA_NAMESPACE::API_VERSION::MString { 20 | let cstring = CString::new(string).unwrap(); 21 | unsafe { 22 | root::AUTODESK_NAMESPACE::MAYA_NAMESPACE::API_VERSION::MString::new1(cstring.as_ptr()) 23 | } 24 | } 25 | } 26 | unsafe impl<'a> UnsafeFrom<&'a root::AUTODESK_NAMESPACE::MAYA_NAMESPACE::API_VERSION::MString> 27 | for String 28 | { 29 | fn unsafe_from(string: &'a root::AUTODESK_NAMESPACE::MAYA_NAMESPACE::API_VERSION::MString) 30 | -> String 31 | { 32 | String::from(string) 33 | } 34 | } 35 | unsafe impl UnsafeFrom<*const root::AUTODESK_NAMESPACE::MAYA_NAMESPACE::API_VERSION::MString> 36 | for String 37 | { 38 | fn unsafe_from(string: *const root::AUTODESK_NAMESPACE::MAYA_NAMESPACE::API_VERSION::MString) 39 | -> String 40 | { 41 | String::from(&*string) 42 | } 43 | } 44 | unsafe impl UnsafeFrom<*mut root::AUTODESK_NAMESPACE::MAYA_NAMESPACE::API_VERSION::MString> 45 | for String 46 | { 47 | fn unsafe_from(string: *mut root::AUTODESK_NAMESPACE::MAYA_NAMESPACE::API_VERSION::MString) 48 | -> String 49 | { 50 | String::from(&*string) 51 | } 52 | } 53 | unsafe impl<'a> UnsafeFrom<&'a str> 54 | for root::AUTODESK_NAMESPACE::MAYA_NAMESPACE::API_VERSION::MString 55 | { 56 | fn unsafe_from(string: &str) -> root::AUTODESK_NAMESPACE::MAYA_NAMESPACE::API_VERSION::MString { 57 | root::AUTODESK_NAMESPACE::MAYA_NAMESPACE::API_VERSION::MString::from(string) 58 | } 59 | } 60 | unsafe impl UnsafeFrom<*const c_char> for String { 61 | fn unsafe_from(string: *const c_char) -> String { 62 | unsafe { 63 | let s = CStr::from_ptr(string).to_str().expect("invalid const char* from OpenMaya"); 64 | String::from(s) 65 | } 66 | } 67 | } 68 | unsafe impl UnsafeFrom<*mut c_char> for String { 69 | fn unsafe_from(string: *mut c_char) -> String { 70 | unsafe { 71 | let s = CStr::from_ptr(string).to_str().expect("invalid const char* from OpenMaya"); 72 | String::from(s) 73 | } 74 | } 75 | } 76 | 77 | use std::borrow::Borrow; 78 | /// Used for implementing overloaded Get::get via generics. 79 | pub trait Getter { 80 | type Output; 81 | fn get(self, arr: &T) -> Self::Output; 82 | } 83 | /// Used for implementing overloaded Set::set via generics. 84 | pub trait Setter<'i, T> { 85 | type Input: ?Sized; 86 | fn set(self, arr: &mut T, input: &Self::Input); 87 | } 88 | /// Trait for exposing get-indexing on Maya native arrays. 89 | /// Maya arrays and array-like objects such as plugs implement this trait; use get() to index 90 | /// into the array and retrieve the value at the given index in the same way that you would use 91 | /// the [] index operator in C++. This is overloaded if the corresponding C++ implementation is 92 | /// overloaded. 93 | /// Due to limitations with index operator overloading in Rust, the [] operator isn't directly 94 | /// supported, and you need to use get() instead. 95 | pub trait Get { 96 | fn get(&self, index: T) -> T::Output where T: Getter, Self: Sized { 97 | index.get(self) 98 | } 99 | } 100 | /// Trait for exposing set-indexing on Maya native arrays. 101 | /// Maya arrays and array-like objects such as plugs implement this trait; use set() to place a 102 | /// value at an index in the array in the same way that you would assign using the [] index operator 103 | /// in C++. Note that you can provide a reference or a value to be placed; they will be converted 104 | /// automatically depending on the C++ function signature. This is overloaded if the corresponding 105 | /// C++ implementation is overloaded. 106 | /// Due to limitations with index operator overloading in Rust, the [] operator isn't directly 107 | /// supported, and you need to use set() instead. 108 | pub trait Set { 109 | fn set<'i, T, Input: Borrow>(&mut self, index: T, value: Input) 110 | where T: Setter<'i, Self>, Self: Sized 111 | { 112 | index.set(self, value.borrow()) 113 | } 114 | } 115 | 116 | include!(concat!(env!("OUT_DIR"), "/maya.rs")); -------------------------------------------------------------------------------- /wrapper.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "shim.h" 15 | --------------------------------------------------------------------------------