4afb7f60创建于 2024年9月30日历史提交
/*
 * The MIT License (MIT)
 * Copyright (C) 2024 Huawei Device Co., Ltd.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 */

use napi::{
  bindgen_prelude::{Buffer, ClassInstance, ObjectFinalize, This, Uint8Array, Unknown},
  oh_log_info, Env, Property, Result,
};
use napi_derive::napi;
use std::collections::HashMap;

use crate::r#enum::Kind;

/// `constructor` option for `struct` requires all fields to be public,
/// otherwise tag impl fn as constructor
/// #[napi(constructor)]
#[napi]
pub struct Animal {
  #[napi(readonly)]
  /// Kind of animal
  pub kind: Kind,

  name: String,
}

#[napi]
impl Animal {
  /// This is the constructor
  #[napi(constructor)]
  pub fn new(kind: Kind, name: String) -> Self {
    Animal { kind, name }
  }

  /// This is a factory method
  #[napi(factory)]
  pub fn with_kind(kind: Kind) -> Self {
    Animal {
      kind,
      name: "Default".to_owned(),
    }
  }

  #[napi(getter)]
  pub fn get_name(&self) -> &str {
    self.name.as_str()
  }

  #[napi(setter)]
  pub fn set_name(&mut self, name: String) {
    self.name = name;
  }

  #[napi(getter, js_name = "type")]
  pub fn kind(&self) -> Kind {
    self.kind
  }

  #[napi(setter, js_name = "type")]
  pub fn set_kind(&mut self, kind: Kind) {
    self.kind = kind;
  }

  /// This is a
  /// multi-line comment
  /// with an emoji 🚀
  #[napi]
  pub fn whoami(&self) -> String {
    match self.kind {
      Kind::Dog => {
        format!("Dog: {}", self.name)
      }
      Kind::Cat => format!("Cat: {}", self.name),
      Kind::Duck => format!("Duck: {}", self.name),
    }
  }

  #[napi]
  /// This is static...
  pub fn get_dog_kind() -> Kind {
    Kind::Dog
  }

  #[napi]
  /// Here are some characters and character sequences
  /// that should be escaped correctly:
  /// \[]{}/\:""{
  /// }
  pub fn return_other_class(&self) -> Dog {
    Dog {
      name: "Doge".to_owned(),
    }
  }

  #[napi]
  pub fn return_other_class_with_custom_constructor(&self) -> Bird {
    Bird::new("parrot".to_owned())
  }

  #[napi]
  pub fn override_individual_arg_on_method(
    &self,
    normal_ty: String,
    #[napi(ts_arg_type = "{n: string}")] overridden_ty: napi::JsObject,
  ) -> Bird {
    let obj = overridden_ty.coerce_to_object().unwrap();
    let the_n: Option<String> = obj.get("n").unwrap();

    Bird::new(format!("{}-{}", normal_ty, the_n.unwrap()))
  }
}

#[napi(constructor)]
pub struct Dog {
  pub name: String,
}

#[napi]
pub struct Bird {
  pub name: String,
}

#[napi]
impl Bird {
  #[napi(constructor)]
  pub fn new(name: String) -> Self {
    Bird { name }
  }

  #[napi]
  pub fn get_count(&self) -> u32 {
    1234
  }

  #[napi]
  pub async fn get_name_async(&self) -> &str {
    tokio::time::sleep(std::time::Duration::new(1, 0)).await;
    self.name.as_str()
  }

  #[napi]
  pub fn accept_slice_method(&self, slice: &[u8]) -> u32 {
    slice.len() as u32
  }
}

/// Smoking test for type generation
#[napi]
#[repr(transparent)]
pub struct Blake2bHasher(u32);

#[napi]
impl Blake2bHasher {
  #[napi(factory)]
  pub fn with_key(key: &Blake2bKey) -> Self {
    Blake2bHasher(key.get_inner())
  }
}

#[napi]
impl Blake2bHasher {
  #[napi]
  pub fn update(&mut self, data: Buffer) {
    self.0 += data.len() as u32;
  }

  #[napi(getter)]
  pub fn get_num(&self) -> u32 {
    self.0
  }
}

#[napi]
pub struct Blake2bKey(u32);

#[napi]
impl Blake2bKey {
  #[napi(constructor)]
  pub fn new() -> Self {
    Self(10)
  }
  fn get_inner(&self) -> u32 {
    self.0
  }
}

#[napi]
pub struct Context {
  data: String,
  pub maybe_need: Option<bool>,
  pub buffer: Uint8Array,
}

// Test for return `napi::Result` and `Result`
#[napi]
impl Context {
  #[napi(constructor)]
  pub fn new() -> napi::Result<Self> {
    Ok(Self {
      data: "not empty".into(),
      maybe_need: None,
      buffer: Uint8Array::new(vec![0, 1, 2, 3]),
    })
  }

  #[napi(factory)]
  pub fn with_data(data: String) -> Result<Self> {
    Ok(Self {
      data,
      maybe_need: Some(true),
      buffer: Uint8Array::new(vec![0, 1, 2, 3]),
    })
  }

  #[napi(factory)]
  pub fn with_buffer(buf: Uint8Array) -> Self {
    Self {
      data: "not empty".into(),
      maybe_need: None,
      buffer: buf,
    }
  }

  #[napi]
  pub fn method(&self) -> String {
    self.data.clone()
  }
}

#[napi(constructor)]
pub struct AnimalWithDefaultConstructor {
  pub name: String,
  pub kind: u32,
}

// Test for skip_typescript
#[napi]
pub struct NinjaTurtle {
  pub name: String,
  #[napi(skip_typescript)]
  pub mask_color: String,
}

#[napi]
impl NinjaTurtle {
  #[napi]
  pub fn is_instance_of(env: Env, value: Unknown) -> Result<bool> {
    Self::instance_of(env, value)
  }

  /// Create your ninja turtle! 🐢
  #[napi(factory)]
  pub fn new_raph() -> Self {
    Self {
      name: "Raphael".to_owned(),
      mask_color: "Red".to_owned(),
    }
  }

  /// We are not going to expose this character, so we just skip it...
  #[napi(factory, skip_typescript)]
  pub fn new_leo() -> Self {
    Self {
      name: "Leonardo".to_owned(),
      mask_color: "Blue".to_owned(),
    }
  }

  #[napi]
  pub fn get_mask_color(&self) -> &str {
    self.mask_color.as_str()
  }

  #[napi]
  pub fn get_name(&self) -> &str {
    self.name.as_str()
  }

  #[napi]
  pub fn return_this(&self, this: This) -> This {
    this
  }
}

#[napi(js_name = "Assets")]
pub struct JsAssets {}

#[napi]
impl JsAssets {
  #[napi(constructor)]
  #[allow(clippy::new_without_default)]
  pub fn new() -> Self {
    JsAssets {}
  }

  #[napi]
  pub fn get(&mut self, _id: u32) -> Option<JsAsset> {
    Some(JsAsset {})
  }
}

#[napi(js_name = "Asset")]
pub struct JsAsset {}

#[napi]
impl JsAsset {
  #[napi(constructor)]
  #[allow(clippy::new_without_default)]
  pub fn new() -> Self {
    Self {}
  }

  #[napi(getter)]
  pub fn get_file_path(&self) -> u32 {
    1
  }
}

#[napi(object)]
pub struct ObjectFieldClassInstance {
  pub bird: ClassInstance<Bird>,
}

#[napi]
pub fn create_object_with_class_field(env: Env) -> Result<ObjectFieldClassInstance> {
  Ok(ObjectFieldClassInstance {
    bird: Bird {
      name: "Carolyn".to_owned(),
    }
    .into_instance(env)?,
  })
}

#[napi]
pub fn receive_object_with_class_field(
  object: ObjectFieldClassInstance,
) -> Result<ClassInstance<Bird>> {
  Ok(object.bird)
}

#[napi(constructor)]
pub struct NotWritableClass {
  #[napi(writable = false)]
  pub name: String,
}

#[napi]
impl NotWritableClass {
  #[napi(writable = false)]
  pub fn set_name(&mut self, name: String) {
    self.name = name;
  }
}

#[napi(custom_finalize)]
pub struct CustomFinalize {
  width: u32,
  height: u32,
  inner: Vec<u8>,
}

#[napi]
impl CustomFinalize {
  #[napi(constructor)]
  pub fn new(width: u32, height: u32) -> Result<Self> {
    let inner = vec![0; (width * height * 4) as usize];
    Ok(Self {
      width,
      height,
      inner,
    })
  }

  #[napi]
  pub fn get_size(&self) -> u32 {
    self.width * self.height * 4
  }
}

impl ObjectFinalize for CustomFinalize {
  fn finalize(self, _env: Env) -> Result<()> {
    oh_log_info!("{:?}", self.inner);
    Ok(())
  }
}

#[napi]
pub struct GetterSetterWithClosures {}

#[napi]
impl GetterSetterWithClosures {
  #[napi(constructor)]
  pub fn new(mut this: This) -> Result<Self> {
    this.define_properties(&[
      Property::new("name")?
        .with_setter_closure(move |_env, mut this, value: String| {
          this.set_named_property("_name", format!("I'm {}", value))?;
          Ok(())
        })
        .with_getter_closure(|_env, this| this.get_named_property_unchecked::<Unknown>("_name")),
      Property::new("age")?.with_getter_closure(|_env, _this| Ok(0.3)),
    ])?;
    Ok(Self {})
  }
}

#[napi]
pub struct CatchOnConstructor {}

#[napi]
impl CatchOnConstructor {
  #[napi(constructor, catch_unwind)]
  pub fn new() -> Self {
    Self {}
  }
}

#[napi]
pub struct CatchOnConstructor2 {}

#[napi]
impl CatchOnConstructor2 {
  #[napi(constructor, catch_unwind)]
  pub fn new() -> Self {
    panic!("CatchOnConstructor2 panic");
  }
}

#[napi]
pub struct ZeroStruct;

#[napi]
impl ZeroStruct {
  #[napi(constructor)]
  pub fn new() -> Self {
    ZeroStruct
  }

  #[napi]
  pub fn get_name(&self) -> String {
    "hello".to_string()
  }
}

#[napi(catch_unwind)]
pub fn return_zero_struct() -> ZeroStruct {
  ZeroStruct
}

#[derive(Debug)]
struct Address {
  mailbox: String,
}
impl Address {
  pub fn new() -> Self {
    Self {
      mailbox: "123456@qq.com".to_string(),
    }
  }
  pub fn get_mailbox(&self) -> String {
    self.mailbox.clone()
  }
}
#[napi]
pub struct ComplexPeople {
  name: String,
  age: Box<u32>,
  address: Address,
  interest: Vec<String>,
  grades: HashMap<String, u8>,
  height: (u32, u32),
}

#[napi]
impl ComplexPeople {
  #[napi(constructor)]
  pub fn new() -> Self {
    Self {
      name: "qwe".to_string(),
      age: Box::new(1),
      address: Address::new(),
      interest: vec!["1".to_string(), "2".to_string()],
      grades: HashMap::new(),
      height: (10, 20),
    }
  }

  #[napi]
  pub fn get_inner_mail(&self) -> String {
    self.address.get_mailbox()
  }

  #[napi]
  pub fn show_info(&self) {
    oh_log_info!("name = {:?}", self.name);
    oh_log_info!("name = {:?}", self.age);
    oh_log_info!("name = {:?}", self.address);
    oh_log_info!("name = {:?}", self.interest);
    oh_log_info!("name = {:?}", self.grades);
    oh_log_info!("name = {:?}", self.height);
  }
}

#[napi]
pub struct RefBird {
  age: u32,
}

#[napi]
impl RefBird {
  #[napi(constructor)]
  pub fn new(age: u32) -> Self {
    RefBird { age }
  }

  #[napi]
  pub fn get_age(&self) -> u32 {
    self.age
  }

  #[napi]
  pub fn set_age(&mut self, age: u32) {
    self.age = age;
  }
}

#[napi]
pub fn class_ref0(mut bird: ClassInstance<RefBird>) {
  bird.set_age(100)
}

#[napi]
pub fn class_ref1(bird: &mut RefBird) {
  bird.set_age(200)
}

#[napi]
pub fn class_ref2(bird: &RefBird) -> u32 {
  bird.get_age()
}