stdweb
stdweb copied to clipboard
How to convert js type back to Rust type?
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?
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 anErrvalue: ConversionError { kind: Custom("missing fieldcoords") }
But in the console it clearly has the field coords (and timestamp), so what's the correct way to convert it? :)
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
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::InstanceOfis not satisfied
When I write #[reference(instance_of = "Object")] it compiles but doesn't seem to do anything at runtime (weird).
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 anErrvalue: ConversionError { kind: Custom("missing fieldcoords") }
@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.
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.