mirror of
https://github.com/project-gauntlet/gauntlet.git
synced 2025-12-23 10:35:53 +00:00
Remove workaround for imagesource being an object
This commit is contained in:
parent
844166d56b
commit
1024f2bc20
16 changed files with 367 additions and 159 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -850,8 +850,6 @@ dependencies = [
|
|||
"iced",
|
||||
"iced_aw",
|
||||
"itertools 0.12.1",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"strum 0.26.2",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
|
|
|
|||
|
|
@ -42,9 +42,7 @@ declare global {
|
|||
children?: ElementComponent<typeof MetadataTagList | typeof MetadataLink | typeof MetadataValue | typeof MetadataIcon | typeof MetadataSeparator>;
|
||||
};
|
||||
["gauntlet:image"]: {
|
||||
source: {
|
||||
data: ArrayBuffer;
|
||||
};
|
||||
source: ImageSource;
|
||||
};
|
||||
["gauntlet:h1"]: {
|
||||
children?: StringComponent;
|
||||
|
|
@ -119,17 +117,13 @@ declare global {
|
|||
["gauntlet:empty_view"]: {
|
||||
title: string;
|
||||
description?: string;
|
||||
image?: {
|
||||
data: ArrayBuffer;
|
||||
};
|
||||
image?: ImageSource;
|
||||
};
|
||||
["gauntlet:list_item"]: {
|
||||
id: string;
|
||||
title: string;
|
||||
subtitle?: string;
|
||||
icon?: {
|
||||
data: ArrayBuffer;
|
||||
} | Icons;
|
||||
icon?: ImageSource | Icons;
|
||||
};
|
||||
["gauntlet:list_section"]: {
|
||||
children?: ElementComponent<typeof ListItem>;
|
||||
|
|
@ -340,6 +334,9 @@ export enum Icons {
|
|||
Indent = "Indent",
|
||||
Unindent = "Unindent"
|
||||
}
|
||||
export type ImageSource = {
|
||||
data: ArrayBuffer;
|
||||
};
|
||||
export interface ActionProps {
|
||||
id?: string;
|
||||
title: string;
|
||||
|
|
@ -430,9 +427,7 @@ Metadata.Value = MetadataValue;
|
|||
Metadata.Icon = MetadataIcon;
|
||||
Metadata.Separator = MetadataSeparator;
|
||||
export interface ImageProps {
|
||||
source: {
|
||||
data: ArrayBuffer;
|
||||
};
|
||||
source: ImageSource;
|
||||
}
|
||||
export const Image: FC<ImageProps> = (props: ImageProps): ReactNode => {
|
||||
return <gauntlet:image source={props.source}/>;
|
||||
|
|
@ -623,9 +618,7 @@ Inline.Center = Content;
|
|||
export interface EmptyViewProps {
|
||||
title: string;
|
||||
description?: string;
|
||||
image?: {
|
||||
data: ArrayBuffer;
|
||||
};
|
||||
image?: ImageSource;
|
||||
}
|
||||
export const EmptyView: FC<EmptyViewProps> = (props: EmptyViewProps): ReactNode => {
|
||||
return <gauntlet:empty_view title={props.title} description={props.description} image={props.image}/>;
|
||||
|
|
@ -634,9 +627,7 @@ export interface ListItemProps {
|
|||
id: string;
|
||||
title: string;
|
||||
subtitle?: string;
|
||||
icon?: {
|
||||
data: ArrayBuffer;
|
||||
} | Icons;
|
||||
icon?: ImageSource | Icons;
|
||||
}
|
||||
export const ListItem: FC<ListItemProps> = (props: ListItemProps): ReactNode => {
|
||||
return <gauntlet:list_item id={props.id} title={props.title} subtitle={props.subtitle} icon={props.icon}/>;
|
||||
|
|
|
|||
|
|
@ -639,18 +639,11 @@ function makeType(type: PropertyType): ts.TypeNode {
|
|||
]
|
||||
)
|
||||
}
|
||||
case "image_source": {
|
||||
return ts.factory.createTypeLiteralNode([
|
||||
ts.factory.createPropertySignature(
|
||||
undefined,
|
||||
ts.factory.createIdentifier("data"),
|
||||
undefined,
|
||||
ts.factory.createTypeReferenceNode(
|
||||
ts.factory.createIdentifier("ArrayBuffer"),
|
||||
undefined
|
||||
)
|
||||
)
|
||||
])
|
||||
case "image_data": {
|
||||
return ts.factory.createTypeReferenceNode(
|
||||
ts.factory.createIdentifier("ArrayBuffer"),
|
||||
undefined
|
||||
)
|
||||
}
|
||||
case "enum": {
|
||||
return ts.factory.createTypeReferenceNode(
|
||||
|
|
|
|||
|
|
@ -111,6 +111,8 @@ export function getEntrypointPreferences(): Record<string, any> {
|
|||
|
||||
function createWidget(hostContext: HostContext, type: ComponentType, properties: Props, children: UiWidget[] = []): Instance {
|
||||
const component = hostContext.componentModel[type];
|
||||
const rootComponent = hostContext.componentModel["gauntlet:root"] as RootComponent;
|
||||
const sharedTypes = rootComponent.sharedTypes;
|
||||
|
||||
const props = Object.fromEntries(
|
||||
Object.entries(properties)
|
||||
|
|
@ -118,11 +120,33 @@ function createWidget(hostContext: HostContext, type: ComponentType, properties:
|
|||
.map(([key, value]) => {
|
||||
if (component.type === "standard" && !!value) {
|
||||
const prop = component.props.find(prop => prop.name === key)
|
||||
if (prop && prop.type.type === "image_source") {
|
||||
if (value.data !== undefined) {
|
||||
return [key, Array.from(new Uint8Array(value.data))]
|
||||
} else {
|
||||
throw new Error("'data' property should be provided on image source property")
|
||||
|
||||
if (prop) {
|
||||
switch (prop.type.type) {
|
||||
case "image_data": {
|
||||
return [key, Array.from(new Uint8Array(value))]
|
||||
}
|
||||
case "object": {
|
||||
// TODO nested objects?
|
||||
const sharedType = sharedTypes[prop.type.name]!!;
|
||||
if (sharedType.type !== "object" || typeof value !== "object") {
|
||||
throw new Error(key + " property is expected to be an object")
|
||||
}
|
||||
|
||||
const object = Object.fromEntries(
|
||||
Object.entries(value)
|
||||
.map(([key, value]) => {
|
||||
const prop = sharedType.items[key]
|
||||
if (prop.type === "image_data") {
|
||||
return [key, Array.from(new Uint8Array(value as any))] // TODO ugly cast
|
||||
}
|
||||
|
||||
return [key, value]
|
||||
})
|
||||
);
|
||||
|
||||
return [key, object]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
6
js/typings/index.d.ts
vendored
6
js/typings/index.d.ts
vendored
|
|
@ -173,7 +173,7 @@ type ComponentRef = {
|
|||
componentName: string,
|
||||
}
|
||||
|
||||
type PropertyType = TypeString | TypeNumber | TypeBoolean | TypeComponent | TypeFunction | TypeImageSource | TypeImageEnum | TypeImageUnion | TypeImageObject
|
||||
type PropertyType = TypeString | TypeNumber | TypeBoolean | TypeComponent | TypeFunction | TypeImageData | TypeImageEnum | TypeImageUnion | TypeImageObject
|
||||
|
||||
type TypeString = {
|
||||
type: "string"
|
||||
|
|
@ -192,8 +192,8 @@ type TypeFunction = {
|
|||
type: "function"
|
||||
arguments: Property[]
|
||||
}
|
||||
type TypeImageSource = {
|
||||
type: "image_source"
|
||||
type TypeImageData = {
|
||||
type: "image_data"
|
||||
}
|
||||
type TypeImageEnum = {
|
||||
type: "enum"
|
||||
|
|
|
|||
|
|
@ -18,8 +18,6 @@ tonic = "0.11.0"
|
|||
global-hotkey = "0.4.2"
|
||||
itertools = "0.12.1"
|
||||
component_model = { path = "../component_model" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
strum = { version = "0.26", features = ["derive"] }
|
||||
|
||||
[build-dependencies]
|
||||
|
|
|
|||
|
|
@ -79,8 +79,7 @@ fn main() -> anyhow::Result<()> {
|
|||
for prop in props {
|
||||
match &prop.property_type {
|
||||
PropertyType::Union { items } => {
|
||||
output.push_str("#[derive(Debug, serde::Deserialize)]\n");
|
||||
output.push_str("#[serde(untagged)]\n");
|
||||
output.push_str("#[derive(Debug)]\n");
|
||||
output.push_str(&format!("enum {}{} {{\n", component_name, prop.name.to_case(Case::Pascal)));
|
||||
|
||||
for (index, property_type) in items.iter().enumerate() {
|
||||
|
|
@ -103,7 +102,7 @@ fn main() -> anyhow::Result<()> {
|
|||
for (type_name, shared_type) in shared_types {
|
||||
match shared_type {
|
||||
SharedType::Enum { items } => {
|
||||
output.push_str("#[derive(Debug, strum::EnumString, serde::Deserialize)]\n");
|
||||
output.push_str("#[derive(Debug, strum::EnumString)]\n");
|
||||
output.push_str(&format!("enum {} {{\n", type_name));
|
||||
|
||||
for item in items {
|
||||
|
|
@ -114,11 +113,11 @@ fn main() -> anyhow::Result<()> {
|
|||
output.push_str("\n");
|
||||
}
|
||||
SharedType::Object { items } => {
|
||||
output.push_str("#[derive(Debug, serde::Deserialize)]\n");
|
||||
output.push_str("#[derive(Debug)]\n");
|
||||
output.push_str(&format!("struct {} {{\n", type_name));
|
||||
|
||||
for (property_name, property_type) in items {
|
||||
output.push_str(&format!(" {}: {},\n", &type_name, generate_required_type(&property_type, format!("{}{}", type_name, property_name))));
|
||||
output.push_str(&format!(" {}: {},\n", &property_name, generate_required_type(&property_type, format!("{}{}", type_name, property_name))));
|
||||
}
|
||||
|
||||
output.push_str("}\n");
|
||||
|
|
@ -178,7 +177,7 @@ fn main() -> anyhow::Result<()> {
|
|||
PropertyType::Component { .. } => {
|
||||
// component properties are found in a children array
|
||||
}
|
||||
PropertyType::ImageSource => {
|
||||
PropertyType::ImageData => {
|
||||
if prop.optional {
|
||||
output.push_str(&format!(" {}: parse_bytes_optional(&properties, \"{}\")?,\n", prop.name, prop.name));
|
||||
} else {
|
||||
|
|
@ -192,11 +191,18 @@ fn main() -> anyhow::Result<()> {
|
|||
output.push_str(&format!(" {}: parse_enum(&properties, \"{}\")?,\n", prop.name, prop.name));
|
||||
}
|
||||
}
|
||||
PropertyType::Union { .. } | PropertyType::Object { .. } => {
|
||||
PropertyType::Union { .. } => {
|
||||
if prop.optional {
|
||||
output.push_str(&format!(" {}: parse_json_optional(&properties, \"{}\")?,\n", prop.name, prop.name));
|
||||
output.push_str(&format!(" {}: parse_union_optional(&properties, \"{}\")?,\n", prop.name, prop.name));
|
||||
} else {
|
||||
output.push_str(&format!(" {}: parse_optional(&properties, \"{}\")?,\n", prop.name, prop.name));
|
||||
output.push_str(&format!(" {}: parse_union(&properties, \"{}\")?,\n", prop.name, prop.name));
|
||||
}
|
||||
}
|
||||
PropertyType::Object { .. } => {
|
||||
if prop.optional {
|
||||
output.push_str(&format!(" {}: parse_object_optional(&properties, \"{}\")?,\n", prop.name, prop.name));
|
||||
} else {
|
||||
output.push_str(&format!(" {}: parse_object(&properties, \"{}\")?,\n", prop.name, prop.name));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -514,7 +520,7 @@ fn generate_required_type(property_type: &PropertyType, union_name: String) -> S
|
|||
PropertyType::Boolean => "bool".to_owned(),
|
||||
PropertyType::Function { .. } => panic!("client doesn't know about functions in properties"),
|
||||
PropertyType::Component { .. } => panic!("component properties are found in children array"),
|
||||
PropertyType::ImageSource => "Vec<u8>".to_owned(),
|
||||
PropertyType::ImageData => "Vec<u8>".to_owned(),
|
||||
PropertyType::Union { .. } => union_name,
|
||||
PropertyType::Object { name } => name.to_owned(),
|
||||
PropertyType::Enum { name } => name.to_owned()
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use serde::de::DeserializeOwned;
|
||||
|
||||
use common::model::{EntrypointId, PluginId, PropertyValue, RenderLocation};
|
||||
use common::rpc::{RpcUiPropertyValue, RpcUiWidget};
|
||||
|
|
@ -73,7 +72,7 @@ pub fn from_rpc(value: HashMap<String, RpcUiPropertyValue>) -> anyhow::Result<Ha
|
|||
Value::Number(value) => NativeUiPropertyValue::Number(value),
|
||||
Value::Bool(value) => NativeUiPropertyValue::Bool(value),
|
||||
Value::Bytes(value) => NativeUiPropertyValue::Bytes(value),
|
||||
Value::Json(value) => NativeUiPropertyValue::Json(value),
|
||||
Value::Object(value) => NativeUiPropertyValue::Object(property_value_from_rpc(value.value)),
|
||||
_ => {
|
||||
return Err(anyhow!("invalid type"))
|
||||
}
|
||||
|
|
@ -88,13 +87,31 @@ pub fn from_rpc(value: HashMap<String, RpcUiPropertyValue>) -> anyhow::Result<Ha
|
|||
Ok(result)
|
||||
}
|
||||
|
||||
|
||||
fn property_value_from_rpc(map: HashMap<String, RpcUiPropertyValue>) -> HashMap<String, NativeUiPropertyValue> {
|
||||
map.into_iter()
|
||||
.map(|(name, value)| {
|
||||
let value = match value.value.unwrap() {
|
||||
Value::Undefined(_) => unreachable!(),
|
||||
Value::String(value) => NativeUiPropertyValue::String(value),
|
||||
Value::Number(value) => NativeUiPropertyValue::Number(value),
|
||||
Value::Bool(value) => NativeUiPropertyValue::Bool(value),
|
||||
Value::Bytes(value) => NativeUiPropertyValue::Bytes(value),
|
||||
Value::Object(value) => NativeUiPropertyValue::Object(property_value_from_rpc(value.value)),
|
||||
};
|
||||
|
||||
(name, value)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum NativeUiPropertyValue {
|
||||
String(String),
|
||||
Number(f64),
|
||||
Bool(bool),
|
||||
Bytes(Vec<u8>),
|
||||
Json(String),
|
||||
Object(HashMap<String, NativeUiPropertyValue>),
|
||||
}
|
||||
|
||||
impl NativeUiPropertyValue {
|
||||
|
|
@ -126,13 +143,24 @@ impl NativeUiPropertyValue {
|
|||
None
|
||||
}
|
||||
}
|
||||
pub fn as_json<T: DeserializeOwned>(&self) -> Option<T> {
|
||||
if let NativeUiPropertyValue::Json(val) = self {
|
||||
Some(serde_json::from_str(val).expect("invalid json sent from backend?"))
|
||||
pub fn as_object<T: ValueToStruct>(&self) -> Option<T> {
|
||||
if let NativeUiPropertyValue::Object(val) = self {
|
||||
Some(ValueToStruct::convert(val).expect("invalid object"))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
pub fn as_union<T: ValueToEnum>(&self) -> anyhow::Result<T> {
|
||||
ValueToEnum::convert(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ValueToStruct {
|
||||
fn convert(value: &HashMap<String, NativeUiPropertyValue>) -> anyhow::Result<Self> where Self: Sized;
|
||||
}
|
||||
|
||||
pub trait ValueToEnum {
|
||||
fn convert(value: &NativeUiPropertyValue) -> anyhow::Result<Self> where Self: Sized;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ use tonic::transport::Server;
|
|||
|
||||
use client_context::ClientContext;
|
||||
use common::model::{EntrypointId, PluginId, PropertyValue, RenderLocation};
|
||||
use common::rpc::{BackendClient, RpcEntrypointTypeSearchResult, RpcEventKeyboardEvent, RpcEventRenderView, RpcEventRunCommand, RpcEventRunGeneratedCommand, RpcEventViewEvent, RpcOpenSettingsWindowPreferencesRequest, RpcRequestRunCommandRequest, RpcRequestRunGeneratedCommandRequest, RpcRequestViewRenderRequest, RpcRequestViewRenderResponseActionKind, RpcSearchRequest, RpcSendKeyboardEventRequest, RpcSendOpenEventRequest, RpcSendViewEventRequest, RpcUiPropertyValue, RpcUiWidgetId};
|
||||
use common::rpc::{BackendClient, RpcEntrypointTypeSearchResult, RpcEventKeyboardEvent, RpcEventRenderView, RpcEventRunCommand, RpcEventRunGeneratedCommand, RpcEventViewEvent, RpcOpenSettingsWindowPreferencesRequest, RpcRequestRunCommandRequest, RpcRequestRunGeneratedCommandRequest, RpcRequestViewRenderRequest, RpcRequestViewRenderResponseActionKind, RpcSearchRequest, RpcSendKeyboardEventRequest, RpcSendOpenEventRequest, RpcSendViewEventRequest, RpcUiPropertyValue, RpcUiPropertyValueObject, RpcUiWidgetId};
|
||||
use common::rpc::rpc_backend_client::RpcBackendClient;
|
||||
use common::rpc::rpc_frontend_server::RpcFrontendServer;
|
||||
use common::rpc::rpc_ui_property_value::Value;
|
||||
|
|
@ -376,14 +376,7 @@ impl Application for AppModel {
|
|||
let widget_id = RpcUiWidgetId { value: widget_id };
|
||||
let event_arguments = event_arguments
|
||||
.into_iter()
|
||||
.map(|value| match value {
|
||||
PropertyValue::Bytes(value) => RpcUiPropertyValue { value: Some(Value::Bytes(value)) },
|
||||
PropertyValue::String(value) => RpcUiPropertyValue { value: Some(Value::String(value)) },
|
||||
PropertyValue::Number(value) => RpcUiPropertyValue { value: Some(Value::Number(value)) },
|
||||
PropertyValue::Bool(value) => RpcUiPropertyValue { value: Some(Value::Bool(value)) },
|
||||
PropertyValue::Json(value) => RpcUiPropertyValue { value: Some(Value::Json(value)) },
|
||||
PropertyValue::Undefined => RpcUiPropertyValue { value: Some(Value::Undefined(0)) },
|
||||
})
|
||||
.map(|value| convert_property_value(value))
|
||||
.collect();
|
||||
|
||||
let event = RpcEventViewEvent {
|
||||
|
|
@ -797,6 +790,23 @@ impl AppModel {
|
|||
}
|
||||
}
|
||||
|
||||
fn convert_property_value(value: PropertyValue) -> RpcUiPropertyValue {
|
||||
match value {
|
||||
PropertyValue::Bytes(value) => RpcUiPropertyValue { value: Some(Value::Bytes(value)) },
|
||||
PropertyValue::String(value) => RpcUiPropertyValue { value: Some(Value::String(value)) },
|
||||
PropertyValue::Number(value) => RpcUiPropertyValue { value: Some(Value::Number(value)) },
|
||||
PropertyValue::Bool(value) => RpcUiPropertyValue { value: Some(Value::Bool(value)) },
|
||||
PropertyValue::Object(value) => {
|
||||
let value: HashMap<String, _> = value.into_iter()
|
||||
.map(|(name, value)| (name, convert_property_value(value)))
|
||||
.collect();
|
||||
|
||||
RpcUiPropertyValue { value: Some(Value::Object(RpcUiPropertyValueObject { value })) }
|
||||
}
|
||||
PropertyValue::Undefined => RpcUiPropertyValue { value: Some(Value::Undefined(0)) },
|
||||
}
|
||||
}
|
||||
|
||||
fn register_shortcut() -> GlobalHotKeyManager {
|
||||
use global_hotkey::hotkey::{Code, HotKey, Modifiers};
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ use std::collections::HashMap;
|
|||
use std::fmt::{Debug, Display};
|
||||
use std::str::FromStr;
|
||||
use std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||
use anyhow::anyhow;
|
||||
|
||||
use iced::{Font, Length, Padding};
|
||||
use iced::alignment::Horizontal;
|
||||
|
|
@ -15,13 +16,10 @@ use iced_aw::date_picker::Date;
|
|||
use iced_aw::floating_element::Offset;
|
||||
use iced_aw::helpers::{date_picker, grid, grid_row, wrap_horizontal};
|
||||
use itertools::Itertools;
|
||||
use serde::de::{DeserializeOwned, StdError};
|
||||
use serde::Deserialize;
|
||||
|
||||
use common::model::PluginId;
|
||||
use component_model::PropertyType;
|
||||
|
||||
use crate::model::{NativeUiPropertyValue, NativeUiViewEvent, NativeUiWidgetId};
|
||||
use crate::model::{NativeUiPropertyValue, NativeUiViewEvent, NativeUiWidgetId, ValueToEnum, ValueToStruct};
|
||||
use crate::ui::{ActionShortcut, ActionShortcutKind};
|
||||
use crate::ui::theme::{ButtonStyle, ContainerStyle, Element, TextInputStyle, TextStyle};
|
||||
|
||||
|
|
@ -460,7 +458,7 @@ impl ComponentWidgetWrapper {
|
|||
// }
|
||||
// }
|
||||
ComponentWidget::Image { source } => {
|
||||
image(Handle::from_memory(source.clone())) // FIXME really expensive clone
|
||||
image(Handle::from_memory(source.data.clone())) // FIXME really expensive clone
|
||||
.into()
|
||||
}
|
||||
ComponentWidget::H1 { children } => {
|
||||
|
|
@ -791,8 +789,8 @@ impl ComponentWidgetWrapper {
|
|||
.into()
|
||||
}
|
||||
ComponentWidget::EmptyView { title, description, image } => {
|
||||
let image: Option<Element<_>> = image.clone() // FIXME really expensive clone
|
||||
.map(|image| iced::widget::image(Handle::from_memory(image)).into());
|
||||
let image: Option<Element<_>> = image.as_ref()
|
||||
.map(|image| iced::widget::image(Handle::from_memory(image.data.clone())).into()); // FIXME really expensive clone
|
||||
|
||||
let title: Element<_> = text(title)
|
||||
.into();
|
||||
|
|
@ -826,7 +824,7 @@ impl ComponentWidgetWrapper {
|
|||
.map(|icon| {
|
||||
match icon {
|
||||
ListItemIcon::_0(bytes) => {
|
||||
image(Handle::from_memory(bytes.clone())) // FIXME really expensive clone
|
||||
image(Handle::from_memory(bytes.data.clone())) // FIXME really expensive clone
|
||||
.into()
|
||||
},
|
||||
ListItemIcon::_1(icon) => {
|
||||
|
|
@ -1540,7 +1538,7 @@ pub fn parse_date(value: &str) -> Option<(i32, u32, u32)> {
|
|||
fn parse_bytes_optional(properties: &HashMap<String, NativeUiPropertyValue>, name: &str) -> anyhow::Result<Option<Vec<u8>>> {
|
||||
match properties.get(name) {
|
||||
None => Ok(None),
|
||||
Some(value) => Ok(Some(value.as_bytes().ok_or(anyhow::anyhow!("{} has to be a string", name))?.to_owned())),
|
||||
Some(value) => Ok(Some(value.as_bytes().ok_or(anyhow::anyhow!("{} has to be a byte array", name))?.to_owned())),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1563,19 +1561,31 @@ fn parse_enum<T: FromStr<Err = strum::ParseError>>(properties: &HashMap<String,
|
|||
parse_enum_optional(properties, name)?.ok_or(anyhow::anyhow!("{} is required", name))
|
||||
}
|
||||
|
||||
fn parse_json_optional<T: DeserializeOwned>(properties: &HashMap<String, NativeUiPropertyValue>, name: &str) -> anyhow::Result<Option<T>> {
|
||||
|
||||
fn parse_object_optional<T: ValueToStruct>(properties: &HashMap<String, NativeUiPropertyValue>, name: &str) -> anyhow::Result<Option<T>> {
|
||||
match properties.get(name) {
|
||||
None => Ok(None),
|
||||
Some(value) => {
|
||||
let value = value.as_json().ok_or(anyhow::anyhow!("{} has to be a json", name))?;
|
||||
let value = value.as_object().ok_or(anyhow::anyhow!("{} has to be a object", name))?;
|
||||
|
||||
Ok(Some(value))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_json<T: DeserializeOwned>(properties: &HashMap<String, NativeUiPropertyValue>, name: &str, ) -> anyhow::Result<T> {
|
||||
parse_json_optional(properties, name)?.ok_or(anyhow::anyhow!("{} is required", name))
|
||||
fn parse_object<T: ValueToStruct>(properties: &HashMap<String, NativeUiPropertyValue>, name: &str) -> anyhow::Result<T> {
|
||||
parse_object_optional(properties, name)?.ok_or(anyhow::anyhow!("{} is required", name))
|
||||
}
|
||||
|
||||
fn parse_union_optional<T: ValueToEnum>(properties: &HashMap<String, NativeUiPropertyValue>, name: &str) -> anyhow::Result<Option<T>> {
|
||||
match properties.get(name) {
|
||||
None => Ok(None),
|
||||
Some(value) => Ok(Some(value.as_union()?)),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_union<T: ValueToEnum>(properties: &HashMap<String, NativeUiPropertyValue>, name: &str) -> anyhow::Result<T> {
|
||||
parse_union_optional(properties, name)?.ok_or(anyhow::anyhow!("{} is required", name))
|
||||
}
|
||||
|
||||
fn icon_to_bootstrap(icon: &Icons) -> icons::Bootstrap {
|
||||
|
|
@ -1801,4 +1811,28 @@ fn icon_to_bootstrap(icon: &Icons) -> icons::Bootstrap {
|
|||
Icons::Indent => icons::Bootstrap::Indent,
|
||||
Icons::Unindent => icons::Bootstrap::Unindent,
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueToEnum for ListItemIcon {
|
||||
fn convert(value: &NativeUiPropertyValue) -> anyhow::Result<ListItemIcon> {
|
||||
match value {
|
||||
NativeUiPropertyValue::String(value) => Ok(ListItemIcon::_1(Icons::from_str(value)?)),
|
||||
NativeUiPropertyValue::Number(_) => Err(anyhow!("unexpected type number for ListItemIcon")),
|
||||
NativeUiPropertyValue::Bool(_) => Err(anyhow!("unexpected type bool for ListItemIcon")),
|
||||
NativeUiPropertyValue::Bytes(_) => Err(anyhow!("unexpected type bytes for ListItemIcon")),
|
||||
NativeUiPropertyValue::Object(value) => {
|
||||
Ok(ListItemIcon::_0(ImageSource {
|
||||
data: parse_bytes(value, "data")?,
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueToStruct for ImageSource {
|
||||
fn convert(value: &HashMap<String, NativeUiPropertyValue>) -> anyhow::Result<ImageSource> {
|
||||
Ok(ImageSource {
|
||||
data: parse_bytes(value, "data")?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use anyhow::anyhow;
|
||||
|
|
@ -66,7 +67,7 @@ pub enum PropertyValue {
|
|||
Number(f64),
|
||||
Bool(bool),
|
||||
Bytes(Vec<u8>),
|
||||
Json(String),
|
||||
Object(HashMap<String, PropertyValue>),
|
||||
Undefined,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -81,8 +81,8 @@ pub enum PropertyType {
|
|||
Function {
|
||||
arguments: Vec<Property>
|
||||
},
|
||||
#[serde(rename = "image_source")]
|
||||
ImageSource,
|
||||
#[serde(rename = "image_data")]
|
||||
ImageData,
|
||||
#[serde(rename = "enum")]
|
||||
Enum {
|
||||
name: String
|
||||
|
|
@ -462,6 +462,10 @@ fn root(children: &[&Component]) -> Component {
|
|||
|
||||
].into_iter().map(|s| s.to_string()).collect()
|
||||
}),
|
||||
(
|
||||
"ImageSource".to_owned(),
|
||||
SharedType::Object { items: HashMap::from([("data".to_owned(), PropertyType::ImageData)]) }
|
||||
)
|
||||
]),
|
||||
}
|
||||
}
|
||||
|
|
@ -607,7 +611,7 @@ pub fn create_component_model() -> Vec<Component> {
|
|||
"image",
|
||||
"Image",
|
||||
[
|
||||
property("source", false, PropertyType::ImageSource)
|
||||
property("source", false, PropertyType::Object { name: "ImageSource".to_owned() })
|
||||
],
|
||||
children_none(),
|
||||
);
|
||||
|
|
@ -851,7 +855,7 @@ pub fn create_component_model() -> Vec<Component> {
|
|||
[
|
||||
property("title", false, PropertyType::String),
|
||||
property("description", true, PropertyType::String),
|
||||
property("image", true, PropertyType::ImageSource),
|
||||
property("image", true, PropertyType::Object { name: "ImageSource".to_owned() }),
|
||||
],
|
||||
children_none(),
|
||||
);
|
||||
|
|
@ -863,7 +867,7 @@ pub fn create_component_model() -> Vec<Component> {
|
|||
property("id", false, PropertyType::String),
|
||||
property("title", false, PropertyType::String),
|
||||
property("subtitle", true, PropertyType::String),
|
||||
property("icon", true, PropertyType::Union { items: vec![PropertyType::ImageSource, PropertyType::Enum { name: "Icons".to_owned() }] }),
|
||||
property("icon", true, PropertyType::Union { items: vec![PropertyType::Object { name: "ImageSource".to_owned() }, PropertyType::Enum { name: "Icons".to_owned() }] }),
|
||||
// accessories
|
||||
],
|
||||
children_none(),
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use deno_core::serde_v8;
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use common::model::{EntrypointId, PropertyValue};
|
||||
use common::rpc::{RpcUiPropertyValue, RpcUiWidget, RpcUiWidgetId};
|
||||
use common::rpc::{RpcUiPropertyValue, RpcUiPropertyValueObject, RpcUiWidget, RpcUiWidgetId};
|
||||
use common::rpc::rpc_ui_property_value::Value;
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
@ -175,20 +175,23 @@ impl From<IntermediateUiWidget> for RpcUiWidget {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn from_rpc_to_intermediate_value(value: RpcUiPropertyValue) -> Option<PropertyValue> {
|
||||
let value = match value.value? {
|
||||
pub fn from_rpc_to_intermediate_value(value: RpcUiPropertyValue) -> PropertyValue {
|
||||
match value.value.unwrap() {
|
||||
Value::Undefined(_) => PropertyValue::Undefined,
|
||||
Value::String(value) => PropertyValue::String(value),
|
||||
Value::Number(value) => PropertyValue::Number(value),
|
||||
Value::Bool(value) => PropertyValue::Bool(value),
|
||||
Value::Bytes(value) => PropertyValue::Bytes(value),
|
||||
Value::Json(value) => PropertyValue::Json(value)
|
||||
};
|
||||
Value::Object(value) => {
|
||||
let value = value.value.into_iter()
|
||||
.map(|(name, value)| (name, from_rpc_to_intermediate_value(value)))
|
||||
.collect();
|
||||
|
||||
Some(value)
|
||||
PropertyValue::Object(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn from_intermediate_to_rpc_properties(value: HashMap<String, PropertyValue>) -> HashMap<String, RpcUiPropertyValue> {
|
||||
value.into_iter()
|
||||
.filter_map(|(key, value)| {
|
||||
|
|
@ -197,7 +200,11 @@ fn from_intermediate_to_rpc_properties(value: HashMap<String, PropertyValue>) ->
|
|||
PropertyValue::Number(value) => Some((key, RpcUiPropertyValue { value: Some(Value::Number(value)) })),
|
||||
PropertyValue::Bool(value) => Some((key, RpcUiPropertyValue { value: Some(Value::Bool(value)) })),
|
||||
PropertyValue::Bytes(value) => Some((key, RpcUiPropertyValue { value: Some(Value::Bytes(value)) })),
|
||||
PropertyValue::Json(value) => Some((key, RpcUiPropertyValue { value: Some(Value::Json(value)) })),
|
||||
PropertyValue::Object(value) => Some((key, RpcUiPropertyValue {
|
||||
value: Some(Value::Object(RpcUiPropertyValueObject {
|
||||
value: from_intermediate_to_rpc_properties(value)
|
||||
}))
|
||||
})),
|
||||
PropertyValue::Undefined => None
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ use anyhow::{anyhow, Context};
|
|||
use deno_core::{FastString, futures, ModuleLoader, ModuleSource, ModuleSourceFuture, ModuleType, op, OpState, ResolutionKind, serde_v8, StaticModuleLoader, v8};
|
||||
use deno_core::futures::{FutureExt, Stream, StreamExt};
|
||||
use deno_core::futures::executor::block_on;
|
||||
use deno_core::v8::{Array, GetPropertyNamesArgs, IndexFilter, KeyCollectionMode, KeyConversionMode, Object, PropertyFilter};
|
||||
use deno_runtime::deno_core::ModuleSpecifier;
|
||||
use deno_runtime::permissions::{Permissions, PermissionsContainer, PermissionsOptions};
|
||||
use deno_runtime::worker::MainWorker;
|
||||
|
|
@ -26,7 +27,7 @@ use common::model::{EntrypointId, PluginId, PropertyValue, RenderLocation};
|
|||
use common::rpc::{FrontendClient, RpcClearInlineViewRequest, RpcRenderLocation, RpcReplaceViewRequest, RpcShowPreferenceRequiredViewRequest, RpcUiPropertyValue, RpcUiWidgetId};
|
||||
use common::rpc::rpc_frontend_client::RpcFrontendClient;
|
||||
use common::rpc::rpc_frontend_server::RpcFrontend;
|
||||
use component_model::{Children, Component, create_component_model, Property, PropertyType};
|
||||
use component_model::{Children, Component, create_component_model, Property, PropertyType, SharedType};
|
||||
|
||||
use crate::model::{from_rpc_to_intermediate_value, IntermediateUiEvent, IntermediateUiWidget, JsPropertyValue, JsRenderLocation, JsUiEvent, JsUiRequestData, JsUiResponseData, JsUiWidget, PreferenceUserData, UiWidgetId};
|
||||
use crate::plugins::data_db_repository::{DataDbRepository, db_entrypoint_from_str, DbPluginEntrypointType, DbPluginPreference, DbPluginPreferenceUserData, DbReadPlugin, DbReadPluginEntrypoint};
|
||||
|
|
@ -889,11 +890,15 @@ fn op_react_replace_view(
|
|||
// validate_child(&state, &container.widget_type, &new_child.widget_type)?
|
||||
// }
|
||||
|
||||
let Component::Root { shared_types, .. } = component_model.components.get("gauntlet:root").unwrap() else {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
let data = JsUiRequestData::ReplaceView {
|
||||
entrypoint_id: EntrypointId::from_string(entrypoint_id),
|
||||
render_location,
|
||||
top_level_view,
|
||||
container: from_js_to_intermediate_widget(scope, container, component_model)?,
|
||||
container: from_js_to_intermediate_widget(scope, container, component_model, shared_types)?,
|
||||
};
|
||||
|
||||
match make_request(&state, data).context("ReplaceView frontend response")? {
|
||||
|
|
@ -966,7 +971,7 @@ fn validate_properties(state: &Rc<RefCell<OpState>>, internal_name: &str, proper
|
|||
PropertyValue::Bytes(_) => {
|
||||
todo!()
|
||||
}
|
||||
PropertyValue::Json(_) => {
|
||||
PropertyValue::Object(_) => {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
|
@ -1129,7 +1134,7 @@ fn from_intermediate_to_js_event(event: IntermediateUiEvent) -> JsUiEvent {
|
|||
PropertyValue::Number(value) => JsPropertyValue::Number { value },
|
||||
PropertyValue::Bool(value) => JsPropertyValue::Bool { value },
|
||||
PropertyValue::Undefined => JsPropertyValue::Undefined,
|
||||
PropertyValue::Bytes(_) | PropertyValue::Json(_) => {
|
||||
PropertyValue::Bytes(_) | PropertyValue::Object(_) => {
|
||||
todo!()
|
||||
}
|
||||
})
|
||||
|
|
@ -1159,9 +1164,9 @@ fn from_intermediate_to_js_event(event: IntermediateUiEvent) -> JsUiEvent {
|
|||
}
|
||||
}
|
||||
|
||||
fn from_js_to_intermediate_widget(scope: &mut v8::HandleScope, ui_widget: JsUiWidget, component_model: &ComponentModel) -> anyhow::Result<IntermediateUiWidget> {
|
||||
fn from_js_to_intermediate_widget(scope: &mut v8::HandleScope, ui_widget: JsUiWidget, component_model: &ComponentModel, shared_types: &HashMap<String, SharedType>) -> anyhow::Result<IntermediateUiWidget> {
|
||||
let children = ui_widget.widget_children.into_iter()
|
||||
.map(|child| from_js_to_intermediate_widget(scope, child, component_model))
|
||||
.map(|child| from_js_to_intermediate_widget(scope, child, component_model, shared_types))
|
||||
.collect::<anyhow::Result<Vec<IntermediateUiWidget>>>()?;
|
||||
|
||||
let component = component_model.components
|
||||
|
|
@ -1180,7 +1185,7 @@ fn from_js_to_intermediate_widget(scope: &mut v8::HandleScope, ui_widget: JsUiWi
|
|||
.map(|prop| (&prop.name, &prop.property_type))
|
||||
.collect::<HashMap<_, _>>();
|
||||
|
||||
let properties = from_js_to_intermediate_properties(scope, ui_widget.widget_properties, &props);
|
||||
let properties = from_js_to_intermediate_properties(scope, ui_widget.widget_properties, &props, shared_types);
|
||||
|
||||
Ok(IntermediateUiWidget {
|
||||
widget_id: ui_widget.widget_id,
|
||||
|
|
@ -1193,9 +1198,9 @@ fn from_js_to_intermediate_widget(scope: &mut v8::HandleScope, ui_widget: JsUiWi
|
|||
fn from_js_to_intermediate_properties(
|
||||
scope: &mut v8::HandleScope,
|
||||
v8_properties: HashMap<String, serde_v8::Value>,
|
||||
component_props: &HashMap<&String, &PropertyType>
|
||||
component_props: &HashMap<&String, &PropertyType>,
|
||||
shared_types: &HashMap<String, SharedType>
|
||||
) -> anyhow::Result<HashMap<String, PropertyValue>> {
|
||||
|
||||
let vec = v8_properties.into_iter()
|
||||
.filter(|(name, _)| name.as_str() != "children")
|
||||
.filter(|(_, value)| !value.v8_value.is_function())
|
||||
|
|
@ -1206,65 +1211,172 @@ fn from_js_to_intermediate_properties(
|
|||
return Err(anyhow!("unknown property encountered {:?}", name))
|
||||
};
|
||||
|
||||
match property_type {
|
||||
PropertyType::String => {
|
||||
if val.is_string() {
|
||||
Ok((name, PropertyValue::String(val.to_rust_string_lossy(scope))))
|
||||
} else {
|
||||
Err(anyhow!("invalid type for property {:?}, found: {:?}, expected: string", name, val.type_repr()))
|
||||
}
|
||||
}
|
||||
PropertyType::Number => {
|
||||
if val.is_number() {
|
||||
Ok((name, PropertyValue::Number(val.number_value(scope).expect("expected number"))))
|
||||
} else {
|
||||
Err(anyhow!("invalid type for property {:?}, found: {:?}, expected: number", name, val.type_repr()))
|
||||
}
|
||||
}
|
||||
PropertyType::Boolean => {
|
||||
if val.is_boolean() {
|
||||
Ok((name, PropertyValue::Bool(val.boolean_value(scope))))
|
||||
} else {
|
||||
Err(anyhow!("invalid type for property {:?}, found: {:?}, expected: boolean", name, val.type_repr()))
|
||||
}
|
||||
}
|
||||
PropertyType::Component { .. } => {
|
||||
panic!("components should not be present here")
|
||||
}
|
||||
PropertyType::Function { .. } => {
|
||||
panic!("functions are filtered out")
|
||||
}
|
||||
PropertyType::ImageSource => {
|
||||
if val.is_array() { // TODO arraybuffer? fix when migrating to deno's op2
|
||||
Ok((name, PropertyValue::Bytes(serde_v8::from_v8(scope, val)?)))
|
||||
} else {
|
||||
Err(anyhow!("invalid type for property {:?}, found: {:?}, expected: string", name, val.type_repr()))
|
||||
}
|
||||
}
|
||||
PropertyType::Enum { .. } => {
|
||||
if val.is_string() {
|
||||
Ok((name, PropertyValue::String(val.to_rust_string_lossy(scope))))
|
||||
} else {
|
||||
Err(anyhow!("invalid type for property {:?}, found: {:?}, expected: string", name, val.type_repr()))
|
||||
}
|
||||
}
|
||||
PropertyType::Union { .. } => {
|
||||
Ok((name.clone(), PropertyValue::Json(object_to_json(scope, val).context(format!("error while reading property {}", name))?)))
|
||||
}
|
||||
PropertyType::Object { .. } => {
|
||||
if val.is_object() {
|
||||
Ok((name.clone(), PropertyValue::Json(object_to_json(scope, val).context(format!("error while reading property {}", name))?)))
|
||||
} else {
|
||||
Err(anyhow!("invalid type for property {:?}, found: {:?}, expected: boolean", name, val.type_repr()))
|
||||
}
|
||||
}
|
||||
}
|
||||
convert(scope, property_type, name, val, shared_types)
|
||||
})
|
||||
.collect::<anyhow::Result<Vec<(_, _)>>>()?;
|
||||
|
||||
Ok(vec.into_iter().collect())
|
||||
}
|
||||
|
||||
fn convert(
|
||||
scope: &mut v8::HandleScope,
|
||||
property_type: &PropertyType,
|
||||
name: String,
|
||||
value: v8::Local<v8::Value>,
|
||||
shared_types: &HashMap<String, SharedType>
|
||||
) -> anyhow::Result<(String, PropertyValue)> {
|
||||
match property_type {
|
||||
PropertyType::String | PropertyType::Enum { .. } => {
|
||||
if value.is_string() {
|
||||
convert_string(scope, name, value)
|
||||
} else {
|
||||
invalid_type_err(name, value, property_type)
|
||||
}
|
||||
}
|
||||
PropertyType::Number => {
|
||||
if value.is_number() {
|
||||
convert_num(scope, name, value)
|
||||
} else {
|
||||
invalid_type_err(name, value, property_type)
|
||||
}
|
||||
}
|
||||
PropertyType::Boolean => {
|
||||
if value.is_boolean() {
|
||||
convert_boolean(scope, name, value)
|
||||
} else {
|
||||
invalid_type_err(name, value, property_type)
|
||||
}
|
||||
}
|
||||
PropertyType::Component { .. } => {
|
||||
panic!("components should not be present here")
|
||||
}
|
||||
PropertyType::Function { .. } => {
|
||||
panic!("functions are filtered out")
|
||||
}
|
||||
PropertyType::ImageData => {
|
||||
if value.is_array() { // TODO arraybuffer? fix when migrating to deno's op2
|
||||
convert_bytes(scope, name, value)
|
||||
} else {
|
||||
invalid_type_err(name, value, property_type)
|
||||
}
|
||||
}
|
||||
PropertyType::Object { name: object_name } => {
|
||||
if value.is_object() {
|
||||
convert_object(scope, name, value, object_name, shared_types)
|
||||
} else {
|
||||
invalid_type_err(name, value, property_type)
|
||||
}
|
||||
}
|
||||
PropertyType::Union { items } => {
|
||||
if value.is_string() {
|
||||
match items.iter().find(|prop_type| matches!(prop_type, PropertyType::String | PropertyType::Enum { .. })) {
|
||||
None => invalid_type_err(name, value, property_type),
|
||||
Some(_) => convert_string(scope, name, value)
|
||||
}
|
||||
} else if value.is_number() {
|
||||
match items.iter().find(|prop_type| matches!(prop_type, PropertyType::Number)) {
|
||||
None => invalid_type_err(name, value, property_type),
|
||||
Some(_) => convert_num(scope, name, value)
|
||||
}
|
||||
} else if value.is_boolean() {
|
||||
match items.iter().find(|prop_type| matches!(prop_type, PropertyType::Boolean)) {
|
||||
None => invalid_type_err(name, value, property_type),
|
||||
Some(_) => convert_boolean(scope, name, value)
|
||||
}
|
||||
} else if value.is_array() { // TODO arraybuffer? fix when migrating to deno's op2
|
||||
match items.iter().find(|prop_type| matches!(prop_type, PropertyType::ImageData)) {
|
||||
None => invalid_type_err(name, value, property_type),
|
||||
Some(_) => convert_bytes(scope, name, value)
|
||||
}
|
||||
} else if value.is_object() {
|
||||
match items.iter().find(|prop_type| matches!(prop_type, PropertyType::Object { .. })) {
|
||||
None => invalid_type_err(name, value, property_type),
|
||||
Some(PropertyType::Object { name: object_name }) => {
|
||||
convert_object(scope, name, value, object_name, shared_types)
|
||||
},
|
||||
_ => unreachable!()
|
||||
}
|
||||
} else {
|
||||
invalid_type_err(name, value, property_type)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_num(scope: &mut v8::HandleScope, name: String, value: v8::Local<v8::Value>) -> anyhow::Result<(String, PropertyValue)> {
|
||||
Ok((name, PropertyValue::Number(value.number_value(scope).expect("expected number"))))
|
||||
}
|
||||
|
||||
fn convert_string(scope: &mut v8::HandleScope, name: String, value: v8::Local<v8::Value>) -> anyhow::Result<(String, PropertyValue)> {
|
||||
Ok((name, PropertyValue::String(value.to_rust_string_lossy(scope))))
|
||||
}
|
||||
|
||||
fn convert_boolean(scope: &mut v8::HandleScope, name: String, value: v8::Local<v8::Value>) -> anyhow::Result<(String, PropertyValue)> {
|
||||
Ok((name, PropertyValue::Bool(value.boolean_value(scope))))
|
||||
}
|
||||
|
||||
fn convert_bytes(scope: &mut v8::HandleScope, name: String, value: v8::Local<v8::Value>) -> anyhow::Result<(String, PropertyValue)> {
|
||||
Ok((name, PropertyValue::Bytes(serde_v8::from_v8(scope, value)?)))
|
||||
}
|
||||
|
||||
fn convert_object(scope: &mut v8::HandleScope, name: String, value: v8::Local<v8::Value>, object_name: &str, shared_types: &HashMap<String, SharedType>) -> anyhow::Result<(String, PropertyValue)> {
|
||||
let object: v8::Local<Object> = value.try_into().context(format!("error while reading property {}", name))?;
|
||||
|
||||
let props = object
|
||||
.get_own_property_names(scope, GetPropertyNamesArgs {
|
||||
property_filter: PropertyFilter::ONLY_ENUMERABLE | PropertyFilter::SKIP_SYMBOLS,
|
||||
key_conversion: KeyConversionMode::NoNumbers,
|
||||
..Default::default()
|
||||
})
|
||||
.context("error getting get_own_property_names".to_string())?;
|
||||
|
||||
let mut result_obj: HashMap<String, PropertyValue> = HashMap::new();
|
||||
|
||||
for index in 0..props.length() {
|
||||
let key = props.get_index(scope, index).unwrap();
|
||||
let value = object.get(scope, key).unwrap();
|
||||
let key = key.to_string(scope).unwrap().to_rust_string_lossy(scope);
|
||||
|
||||
let property_type = match shared_types.get(object_name).unwrap() {
|
||||
SharedType::Enum { .. } => unreachable!(),
|
||||
SharedType::Object { items } => items.get(&key).unwrap()
|
||||
};
|
||||
|
||||
let (key, value) = convert(scope, property_type, key, value, shared_types)?;
|
||||
|
||||
result_obj.insert(key, value);
|
||||
}
|
||||
|
||||
Ok((name, PropertyValue::Object(result_obj)))
|
||||
}
|
||||
|
||||
fn invalid_type_err<T>(name: String, value: v8::Local<v8::Value>, property_type: &PropertyType) -> anyhow::Result<T> {
|
||||
Err(anyhow!("invalid type for property {:?}, found: {:?}, expected: {}", name, value.type_repr(), expected_type(property_type)))
|
||||
}
|
||||
|
||||
fn expected_type(prop_type: &PropertyType) -> String {
|
||||
match prop_type {
|
||||
PropertyType::String => "string".to_owned(),
|
||||
PropertyType::Number => "number".to_owned(),
|
||||
PropertyType::Boolean => "boolean".to_owned(),
|
||||
PropertyType::Component { .. } => {
|
||||
panic!("components should not be present here")
|
||||
}
|
||||
PropertyType::Function { .. } => {
|
||||
panic!("functions are filtered out")
|
||||
}
|
||||
PropertyType::ImageData => "bytearray".to_owned(),
|
||||
PropertyType::Enum { .. } => "enum".to_owned(),
|
||||
PropertyType::Union { items } => {
|
||||
items.into_iter()
|
||||
.map(|prop_type| expected_type(prop_type))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
},
|
||||
PropertyType::Object { .. } => "object".to_owned(),
|
||||
}
|
||||
}
|
||||
|
||||
fn object_to_json(
|
||||
scope: &mut v8::HandleScope,
|
||||
val: v8::Local<v8::Value>
|
||||
|
|
|
|||
|
|
@ -113,8 +113,7 @@ impl RpcBackend for RpcBackendServerImpl {
|
|||
|
||||
let event_arguments = event_arguments.into_iter()
|
||||
.map(|arg| from_rpc_to_intermediate_value(arg))
|
||||
.collect::<Option<Vec<_>>>()
|
||||
.ok_or(Status::invalid_argument("event_arguments"))?;
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
self.application_manager.handle_view_event(PluginId::from_string(plugin_id), widget_id, event_name, event_arguments);
|
||||
Ok(Response::new(RpcSendViewEventResponse::default()))
|
||||
|
|
|
|||
|
|
@ -18,7 +18,10 @@ message RpcUiPropertyValue {
|
|||
double number = 3;
|
||||
bool bool = 4;
|
||||
bytes bytes = 5;
|
||||
string json = 6;
|
||||
RpcUiPropertyValueObject object = 6;
|
||||
}
|
||||
}
|
||||
|
||||
message RpcUiPropertyValueObject {
|
||||
map<string, RpcUiPropertyValue> value = 1;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue