stdweb icon indicating copy to clipboard operation
stdweb copied to clipboard

How to convert js type back to Rust type?

Open Boscop opened this issue 7 years ago • 6 comments

I'm getting:

basic.js:536 Panic error message: Argument 1 is not convertible to '<Rust type>'

When success_action gets called:

pub struct GeolocationService(Value);

impl GeolocationService {
	pub fn new() -> Self {
		let geolocation = js! {
			return navigator.geolocation;
		};
		GeolocationService(geolocation)
	}
	pub fn get_current_position(&self, success_cb: Callback<Position>, error_cb: Callback<PositionError>, options: PositionOptions) {
		let success_cb = move |arg: Position| success_cb.emit(arg);
		let error_cb = move |arg: PositionError| error_cb.emit(arg);
		let geolocation = &self.0;
		js! { @(no_return)
			var geolocation = @{geolocation};
			var success_cb = @{success_cb};
			var error_cb = @{error_cb};
			var options = @{options};
			var success_action = function(arg) {
				success_cb(arg);
				success_cb.drop();
			};
			var error_action = function(arg) {
				error_cb(arg);
				error_cb.drop();
			};
			geolocation.getCurrentPosition(success_action, error_action, options);
		}
	}
}

How can I convert the arg to its Rust type (Position) when calling success_cb(arg);? (Doing the opposite of @{..}.) Btw, I have js_serializable!(Position); and js_deserializable!(Position);.

Btw, the error_cb doesn't get dropped when error_action is not called (when success_action is called instead), right? Should I change both _action functions so that they both drop both _cbs?


Btw, what's the best way to make the error callback and options arguments optional in this case?

Boscop avatar Aug 22 '18 05:08 Boscop

When I do this, it logs the position object in the console correctly (both times) but fails to convert it:

	pub fn get_current_position(&self, success_cb: Callback<Position>, error_cb: Callback<PositionError>, options: PositionOptions) {
		let success_cb = move |arg: Value| {
			js! { @(no_return)
				console.log(@{arg.clone()});
			}
			success_cb.emit(arg.try_into().unwrap());
		};
		let error_cb = move |arg: Value| error_cb.emit(arg.try_into().unwrap());
		let geolocation = &self.0;
		js! { @(no_return)
			var geolocation = @{geolocation};
			var success_cb = @{success_cb};
			var error_cb = @{error_cb};
			var options = @{options};
			var success_action = function(arg) {
				console.log(arg);
				success_cb(arg);
				success_cb.drop();
				error_cb.drop();
			};
			var error_action = function(arg) {
				console.error(arg);
				error_cb(arg);
				success_cb.drop();
				error_cb.drop();
			};
			geolocation.getCurrentPosition(success_action, error_action, options);
		}
	}

Panic error message: called Result::unwrap() on an Err value: ConversionError { kind: Custom("missing field coords") }

But in the console it clearly has the field coords (and timestamp), so what's the correct way to convert it? :)

Boscop avatar Aug 22 '18 06:08 Boscop

Btw, these are my types:

pub type DOMTimeStamp = u64;

pub type DOMString = String;

#[derive(Serialize, Deserialize, Debug, SmartDefault)]
pub struct PositionOptions {
	pub enableHighAccuracy: bool,
	#[default = "0xFFFFFFFF"]
	pub timeout: u32,
	pub maximumAge: u32,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct Position {
	pub coords: Coordinates,
	pub timestamp: DOMTimeStamp,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct Coordinates {
	pub latitude: f64,
	pub longitude: f64,
	pub altitude: Option<f64>,
	pub accuracy: f64,
	pub altitudeAccuracy: Option<f64>,
	pub heading: Option<f64>,
	pub speed: Option<f64>,
}

#[repr(u16)]
#[derive(Serialize, Deserialize, Debug)]
pub enum PositionErrorCode {
	PERMISSION_DENIED = 1,
	POSITION_UNAVAILABLE = 2,
	TIMEOUT = 3,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct PositionError {
	pub code: PositionErrorCode,
	pub message: DOMString,
}

js_serializable!(Position); js_deserializable!(Position);
js_serializable!(PositionOptions); js_deserializable!(PositionOptions);
js_serializable!(Coordinates); js_deserializable!(Coordinates);
js_serializable!(PositionError); js_deserializable!(PositionError);
js_serializable!(PositionErrorCode); js_deserializable!(PositionErrorCode);

Based on the Web IDL: https://w3c.github.io/geolocation-api/#idl-index

Boscop avatar Aug 22 '18 06:08 Boscop

This also doesn't work:

#[derive(Clone, Debug, ReferenceType)]
#[reference(instance_of = "Position")]
pub struct Position(Reference);

impl Position {
	pub fn get_timestamp( &self ) -> DOMTimeStamp {
		js! (
			return @{&self.0}.timestamp;
		).try_into().unwrap()
	}
}

Uncaught ReferenceError: Position is not defined

And when I omit #[reference(instance_of = "Position")] I get:

error[E0277]: the trait bound Position: stdweb::webcore::instance_of::InstanceOf is not satisfied

When I write #[reference(instance_of = "Object")] it compiles but doesn't seem to do anything at runtime (weird).

Boscop avatar Aug 22 '18 07:08 Boscop

According to this, the first way (try_into) should work: https://docs.rs/stdweb/*/stdweb/macro.js_deserializable.html But I'm getting

Panic error message: called Result::unwrap() on an Err value: ConversionError { kind: Custom("missing field coords") }

Boscop avatar Aug 22 '18 07:08 Boscop

@Boscop Would it be possible for you to post a minimal crate which reproduces your problem?

And yes, if you don't explicitly drop the callbacks then they won't be dropped.

koute avatar Aug 22 '18 22:08 koute

I pushed my crate & example to Github: https://github.com/Boscop/yew-geolocation When you run it with cargo web start --example basic --target=wasm32-unknown-unknown, as you can see it logs the Position to the console on the js side but fails to convert to the Rust Position struct with try_into() even though it is js_deserializable.

Also you notice that it first crashes due to https://github.com/koute/stdweb/issues/261 but the app still runs afterwards. If you clear the console after the error caused by https://github.com/koute/stdweb/issues/261, and ONLY THEN allow geolocation access, you will cleanly see the error caused by this issue.

Boscop avatar Aug 23 '18 01:08 Boscop