mirror of
https://github.com/project-gauntlet/gauntlet.git
synced 2025-12-23 10:35:53 +00:00
ImageSource on js plugin side is now just a reference to asset instead of binary array with image
This commit is contained in:
parent
af0b0ca51b
commit
5c19f79574
9 changed files with 210 additions and 120 deletions
|
|
@ -60,8 +60,6 @@ export default function DetailView(): ReactElement {
|
|||
const env = Deno.env.get("RUST_LOG");
|
||||
console.log("RUST_LOG:", env);
|
||||
|
||||
const logoData = assetData("logo.png");
|
||||
|
||||
console.error("DetailView error")
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -113,7 +111,7 @@ export default function DetailView(): ReactElement {
|
|||
<Detail.Content.H4>H4 Title</Detail.Content.H4>
|
||||
<Detail.Content.H5>H5 Title</Detail.Content.H5>
|
||||
<Detail.Content.H6>H6 Title</Detail.Content.H6>
|
||||
<Detail.Content.Image source={{ data: logoData }}/>
|
||||
<Detail.Content.Image source={{ asset: "logo.png" }}/>
|
||||
<Detail.Content.CodeBlock>Code block Test</Detail.Content.CodeBlock>
|
||||
<Detail.Content.HorizontalBreak/>
|
||||
<Detail.Content.Paragraph>
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ declare global {
|
|||
children?: ElementComponent<typeof MetadataTagList | typeof MetadataLink | typeof MetadataValue | typeof MetadataIcon | typeof MetadataSeparator>;
|
||||
};
|
||||
["gauntlet:image"]: {
|
||||
source: ImageSource;
|
||||
source: ImageSource | Icons;
|
||||
};
|
||||
["gauntlet:h1"]: {
|
||||
children?: StringComponent;
|
||||
|
|
@ -119,7 +119,7 @@ declare global {
|
|||
["gauntlet:empty_view"]: {
|
||||
title: string;
|
||||
description?: string;
|
||||
image?: ImageSource;
|
||||
image?: ImageSource | Icons;
|
||||
};
|
||||
["gauntlet:list_item"]: {
|
||||
id: string;
|
||||
|
|
@ -163,6 +163,9 @@ export type EmptyNode = boolean | null | undefined;
|
|||
export type ElementComponent<Comp extends FC<any>> = Element<Comp> | EmptyNode | Iterable<ElementComponent<Comp>>;
|
||||
export type StringComponent = StringNode | EmptyNode | Iterable<StringComponent>;
|
||||
export type StringOrElementComponent<Comp extends FC<any>> = StringNode | EmptyNode | Element<Comp> | Iterable<StringOrElementComponent<Comp>>;
|
||||
export type ImageSource = {
|
||||
asset: string;
|
||||
};
|
||||
export enum Icons {
|
||||
PersonAdd = "PersonAdd",
|
||||
Airplane = "Airplane",
|
||||
|
|
@ -336,9 +339,6 @@ export enum Icons {
|
|||
Indent = "Indent",
|
||||
Unindent = "Unindent"
|
||||
}
|
||||
export type ImageSource = {
|
||||
data: ArrayBuffer;
|
||||
};
|
||||
export interface ActionProps {
|
||||
id?: string;
|
||||
title: string;
|
||||
|
|
@ -429,7 +429,7 @@ Metadata.Value = MetadataValue;
|
|||
Metadata.Icon = MetadataIcon;
|
||||
Metadata.Separator = MetadataSeparator;
|
||||
export interface ImageProps {
|
||||
source: ImageSource;
|
||||
source: ImageSource | Icons;
|
||||
}
|
||||
export const Image: FC<ImageProps> = (props: ImageProps): ReactNode => {
|
||||
return <gauntlet:image source={props.source}></gauntlet:image>;
|
||||
|
|
@ -623,7 +623,7 @@ Inline.Center = Content;
|
|||
export interface EmptyViewProps {
|
||||
title: string;
|
||||
description?: string;
|
||||
image?: ImageSource;
|
||||
image?: ImageSource | Icons;
|
||||
}
|
||||
export const EmptyView: FC<EmptyViewProps> = (props: EmptyViewProps): ReactNode => {
|
||||
return <gauntlet:empty_view title={props.title} description={props.description} image={props.image}></gauntlet:empty_view>;
|
||||
|
|
|
|||
|
|
@ -241,6 +241,31 @@ function makeComponents(modelInput: Component[]): ts.SourceFile {
|
|||
|
||||
const root = modelInput.find((component): component is RootComponent => component.type === "root");
|
||||
if (root != null) {
|
||||
// image special case
|
||||
// export type ImageSource = { asset: string } | { url: string };
|
||||
|
||||
const imageSourceDeclaration = ts.factory.createTypeAliasDeclaration(
|
||||
[ts.factory.createToken(ts.SyntaxKind.ExportKeyword)],
|
||||
ts.factory.createIdentifier("ImageSource"),
|
||||
undefined,
|
||||
ts.factory.createUnionTypeNode([
|
||||
ts.factory.createTypeLiteralNode([ts.factory.createPropertySignature(
|
||||
undefined,
|
||||
ts.factory.createIdentifier("asset"),
|
||||
undefined,
|
||||
ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword)
|
||||
)]),
|
||||
// ts.factory.createTypeLiteralNode([ts.factory.createPropertySignature( // TODO implement url imagesource
|
||||
// undefined,
|
||||
// ts.factory.createIdentifier("url"),
|
||||
// undefined,
|
||||
// ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword)
|
||||
// )])
|
||||
])
|
||||
);
|
||||
|
||||
publicDeclarations.push(imageSourceDeclaration)
|
||||
|
||||
for (const [name, sharedType] of Object.entries(root.sharedTypes)) {
|
||||
|
||||
switch (sharedType.type) {
|
||||
|
|
@ -633,9 +658,9 @@ function makeType(type: PropertyType): ts.TypeNode {
|
|||
]
|
||||
)
|
||||
}
|
||||
case "image_data": {
|
||||
case "image_source": {
|
||||
return ts.factory.createTypeReferenceNode(
|
||||
ts.factory.createIdentifier("ArrayBuffer"),
|
||||
ts.factory.createIdentifier("ImageSource"),
|
||||
undefined
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -116,49 +116,9 @@ 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)
|
||||
.filter(([key, _]) => key !== "children")
|
||||
.map(([key, value]) => {
|
||||
if (component.type === "standard" && !!value) {
|
||||
const prop = component.props.find(prop => prop.name === key)
|
||||
|
||||
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 arraybuffer? fix when migrating to deno's op2
|
||||
}
|
||||
|
||||
return [key, value]
|
||||
})
|
||||
);
|
||||
|
||||
return [key, object]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [key, value]
|
||||
})
|
||||
);
|
||||
|
||||
const instance: Instance = {
|
||||
|
|
|
|||
6
js/typings/index.d.ts
vendored
6
js/typings/index.d.ts
vendored
|
|
@ -182,7 +182,7 @@ type ComponentRef = {
|
|||
componentName: string,
|
||||
}
|
||||
|
||||
type PropertyType = TypeString | TypeNumber | TypeBoolean | TypeComponent | TypeFunction | TypeImageData | TypeImageEnum | TypeImageUnion | TypeImageObject
|
||||
type PropertyType = TypeString | TypeNumber | TypeBoolean | TypeComponent | TypeFunction | TypeImageSource | TypeImageEnum | TypeImageUnion | TypeImageObject
|
||||
|
||||
type TypeString = {
|
||||
type: "string"
|
||||
|
|
@ -201,8 +201,8 @@ type TypeFunction = {
|
|||
type: "function"
|
||||
arguments: Property[]
|
||||
}
|
||||
type TypeImageData = {
|
||||
type: "image_data"
|
||||
type TypeImageSource = {
|
||||
type: "image_source"
|
||||
}
|
||||
type TypeImageEnum = {
|
||||
type: "enum"
|
||||
|
|
|
|||
|
|
@ -177,7 +177,7 @@ fn main() -> anyhow::Result<()> {
|
|||
PropertyType::Component { .. } => {
|
||||
// component properties are found in a children array
|
||||
}
|
||||
PropertyType::ImageData => {
|
||||
PropertyType::ImageSource => {
|
||||
if prop.optional {
|
||||
output.push_str(&format!(" {}: parse_bytes_optional(&properties, \"{}\")?,\n", prop.name, prop.name));
|
||||
} else {
|
||||
|
|
@ -520,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::ImageData => "Vec<u8>".to_owned(),
|
||||
PropertyType::ImageSource => "Vec<u8>".to_owned(),
|
||||
PropertyType::Union { .. } => union_name,
|
||||
PropertyType::Object { name } => name.to_owned(),
|
||||
PropertyType::Enum { name } => name.to_owned()
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use std::str::FromStr;
|
|||
use std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||
|
||||
use anyhow::anyhow;
|
||||
use iced::{Alignment, Application, Font, Length};
|
||||
use iced::{Alignment, Font, Length};
|
||||
use iced::alignment::Horizontal;
|
||||
use iced::font::Weight;
|
||||
use iced::widget::{button, checkbox, column, container, horizontal_rule, horizontal_space, image, pick_list, row, scrollable, Space, text, text_input, tooltip, vertical_rule};
|
||||
|
|
@ -435,8 +435,17 @@ impl ComponentWidgetWrapper {
|
|||
ComponentWidget::Image { source } => {
|
||||
let centered = context.is_content_centered();
|
||||
|
||||
let content: Element<_> = image(Handle::from_memory(source.data.clone())) // FIXME really expensive clone
|
||||
.into();
|
||||
let content: Element<_> = match source {
|
||||
ImageSource::_0(bytes) => {
|
||||
image(Handle::from_memory(bytes.clone())) // FIXME really expensive clone
|
||||
.into()
|
||||
}
|
||||
ImageSource::_1(icon) => {
|
||||
text(icon_to_bootstrap(icon))
|
||||
.font(icons::BOOTSTRAP_FONT) // TODO size, height and width
|
||||
.into()
|
||||
}
|
||||
};
|
||||
|
||||
let mut content = container(content)
|
||||
.width(Length::Fill);
|
||||
|
|
@ -828,11 +837,20 @@ impl ComponentWidgetWrapper {
|
|||
|
||||
content
|
||||
}
|
||||
ComponentWidget::EmptyView { title, description, image } => {
|
||||
let image: Option<Element<_>> = image.as_ref()
|
||||
.map(|image| {
|
||||
iced::widget::image(Handle::from_memory(image.data.clone())) // FIXME really expensive clone
|
||||
.themed(ImageStyle::EmptyViewImage)
|
||||
ComponentWidget::EmptyView { title, description, image: empty_view_image } => {
|
||||
let image: Option<Element<_>> = empty_view_image.as_ref()
|
||||
.map(|empty_view_image| {
|
||||
match empty_view_image {
|
||||
EmptyViewImage::_0(bytes) => {
|
||||
image(Handle::from_memory(bytes.clone())) // FIXME really expensive clone
|
||||
.themed(ImageStyle::EmptyViewImage)
|
||||
}
|
||||
EmptyViewImage::_1(icon) => {
|
||||
text(icon_to_bootstrap(icon))
|
||||
.font(icons::BOOTSTRAP_FONT) // TODO size, height and width
|
||||
.into()
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let title: Element<_> = text(title)
|
||||
|
|
@ -875,7 +893,7 @@ impl ComponentWidgetWrapper {
|
|||
.map(|icon| {
|
||||
match icon {
|
||||
ListItemIcon::_0(bytes) => {
|
||||
image(Handle::from_memory(bytes.data.clone())) // FIXME really expensive clone
|
||||
image(Handle::from_memory(bytes.clone())) // FIXME really expensive clone
|
||||
.into()
|
||||
},
|
||||
ListItemIcon::_1(icon) => {
|
||||
|
|
@ -1916,23 +1934,37 @@ impl UiPropertyValueToEnum for ListItemIcon {
|
|||
fn convert(value: &UiPropertyValue) -> anyhow::Result<ListItemIcon> {
|
||||
match value {
|
||||
UiPropertyValue::String(value) => Ok(ListItemIcon::_1(Icons::from_str(value)?)),
|
||||
UiPropertyValue::Bytes(value) => Ok(ListItemIcon::_0(value.clone())), // FIXME really expensive clone
|
||||
UiPropertyValue::Number(_) => Err(anyhow!("unexpected type number for ListItemIcon")),
|
||||
UiPropertyValue::Bool(_) => Err(anyhow!("unexpected type bool for ListItemIcon")),
|
||||
UiPropertyValue::Bytes(_) => Err(anyhow!("unexpected type bytes for ListItemIcon")),
|
||||
UiPropertyValue::Object(value) => {
|
||||
Ok(ListItemIcon::_0(ImageSource {
|
||||
data: parse_bytes(value, "data")?,
|
||||
}))
|
||||
}
|
||||
UiPropertyValue::Object(_) => Err(anyhow!("unexpected type object for ListItemIcon")),
|
||||
UiPropertyValue::Undefined => Err(anyhow!("unexpected type undefined for ListItemIcon"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UiPropertyValueToStruct for ImageSource {
|
||||
fn convert(value: &HashMap<String, UiPropertyValue>) -> anyhow::Result<ImageSource> {
|
||||
Ok(ImageSource {
|
||||
data: parse_bytes(value, "data")?,
|
||||
})
|
||||
impl UiPropertyValueToEnum for EmptyViewImage {
|
||||
fn convert(value: &UiPropertyValue) -> anyhow::Result<EmptyViewImage> {
|
||||
match value {
|
||||
UiPropertyValue::String(value) => Ok(EmptyViewImage::_1(Icons::from_str(value)?)),
|
||||
UiPropertyValue::Bytes(value) => Ok(EmptyViewImage::_0(value.clone())), // FIXME really expensive clone
|
||||
UiPropertyValue::Number(_) => Err(anyhow!("unexpected type number for ListItemIcon")),
|
||||
UiPropertyValue::Bool(_) => Err(anyhow!("unexpected type bool for ListItemIcon")),
|
||||
UiPropertyValue::Object(_) => Err(anyhow!("unexpected type object for ListItemIcon")),
|
||||
UiPropertyValue::Undefined => Err(anyhow!("unexpected type undefined for ListItemIcon"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UiPropertyValueToEnum for ImageSource {
|
||||
fn convert(value: &UiPropertyValue) -> anyhow::Result<ImageSource> {
|
||||
match value {
|
||||
UiPropertyValue::String(value) => Ok(ImageSource::_1(Icons::from_str(value)?)),
|
||||
UiPropertyValue::Bytes(value) => Ok(ImageSource::_0(value.clone())), // FIXME really expensive clone
|
||||
UiPropertyValue::Number(_) => Err(anyhow!("unexpected type number for ListItemIcon")),
|
||||
UiPropertyValue::Bool(_) => Err(anyhow!("unexpected type bool for ListItemIcon")),
|
||||
UiPropertyValue::Object(_) => Err(anyhow!("unexpected type object for ListItemIcon")),
|
||||
UiPropertyValue::Undefined => Err(anyhow!("unexpected type undefined for ListItemIcon"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,8 +82,8 @@ pub enum PropertyType {
|
|||
Function {
|
||||
arguments: Vec<Property>
|
||||
},
|
||||
#[serde(rename = "image_data")]
|
||||
ImageData,
|
||||
#[serde(rename = "image_source")]
|
||||
ImageSource,
|
||||
#[serde(rename = "enum")]
|
||||
Enum {
|
||||
name: String
|
||||
|
|
@ -477,10 +477,6 @@ fn root(children: &[&Component]) -> Component {
|
|||
|
||||
].into_iter().map(|s| s.to_string()).collect()
|
||||
}),
|
||||
(
|
||||
"ImageSource".to_owned(),
|
||||
SharedType::Object { items: IndexMap::from([("data".to_owned(), PropertyType::ImageData)]) }
|
||||
)
|
||||
]),
|
||||
}
|
||||
}
|
||||
|
|
@ -639,7 +635,7 @@ pub fn create_component_model() -> Vec<Component> {
|
|||
mark_doc!("/image/description.md"),
|
||||
"Image",
|
||||
[
|
||||
property("source", mark_doc!("/image/props/source.md"), false, PropertyType::Object { name: "ImageSource".to_owned() })
|
||||
property("source", mark_doc!("/image/props/source.md"), false, PropertyType::Union { items: vec![PropertyType::ImageSource, PropertyType::Enum { name: "Icons".to_owned() }] })
|
||||
],
|
||||
children_none(),
|
||||
);
|
||||
|
|
@ -917,7 +913,7 @@ pub fn create_component_model() -> Vec<Component> {
|
|||
[
|
||||
property("title", mark_doc!("/empty_view/props/title.md"),false, PropertyType::String),
|
||||
property("description", mark_doc!("/empty_view/props/description.md"),true, PropertyType::String),
|
||||
property("image", mark_doc!("/empty_view/props/image.md"),true, PropertyType::Object { name: "ImageSource".to_owned() }),
|
||||
property("image", mark_doc!("/empty_view/props/image.md"),true, PropertyType::Union { items: vec![PropertyType::ImageSource, PropertyType::Enum { name: "Icons".to_owned() }] }),
|
||||
],
|
||||
children_none(),
|
||||
);
|
||||
|
|
@ -930,7 +926,7 @@ pub fn create_component_model() -> Vec<Component> {
|
|||
property("id", mark_doc!("/list_item/props/id.md"),false, PropertyType::String),
|
||||
property("title", mark_doc!("/list_item/props/title.md"),false, PropertyType::String),
|
||||
property("subtitle", mark_doc!("/list_item/props/subtitle.md"),true, PropertyType::String),
|
||||
property("icon", mark_doc!("/list_item/props/icon.md"),true, PropertyType::Union { items: vec![PropertyType::Object { name: "ImageSource".to_owned() }, PropertyType::Enum { name: "Icons".to_owned() }] }),
|
||||
property("icon", mark_doc!("/list_item/props/icon.md"),true, PropertyType::Union { items: vec![PropertyType::ImageSource, PropertyType::Enum { name: "Icons".to_owned() }] }),
|
||||
// accessories
|
||||
],
|
||||
children_none(),
|
||||
|
|
|
|||
|
|
@ -3,8 +3,10 @@ use std::collections::HashMap;
|
|||
use std::rc::Rc;
|
||||
use anyhow::{anyhow, Context};
|
||||
use deno_core::{op, OpState, serde_v8, v8};
|
||||
use deno_core::futures::executor::block_on;
|
||||
use deno_core::v8::{GetPropertyNamesArgs, KeyConversionMode, PropertyFilter};
|
||||
use indexmap::IndexMap;
|
||||
use serde::Deserialize;
|
||||
use common::model::{EntrypointId, PhysicalKey, UiPropertyValue, UiWidget};
|
||||
use component_model::{Component, Property, PropertyType, SharedType};
|
||||
use crate::model::{JsUiRenderLocation, JsUiRequestData, JsUiResponseData, JsUiWidget};
|
||||
|
|
@ -88,7 +90,7 @@ fn op_react_replace_view(
|
|||
entrypoint_id: EntrypointId::from_string(entrypoint_id),
|
||||
render_location,
|
||||
top_level_view,
|
||||
container: from_js_to_intermediate_widget(scope, container, component_model, shared_types)?,
|
||||
container: from_js_to_intermediate_widget(state.clone(), scope, container, component_model, shared_types)?,
|
||||
};
|
||||
|
||||
match make_request(&state, data).context("ReplaceView frontend response")? {
|
||||
|
|
@ -146,9 +148,9 @@ async fn fetch_action_id_for_shortcut(
|
|||
Ok(result)
|
||||
}
|
||||
|
||||
fn from_js_to_intermediate_widget(scope: &mut v8::HandleScope, ui_widget: JsUiWidget, component_model: &ComponentModel, shared_types: &IndexMap<String, SharedType>) -> anyhow::Result<UiWidget> {
|
||||
fn from_js_to_intermediate_widget(state: Rc<RefCell<OpState>>, scope: &mut v8::HandleScope, ui_widget: JsUiWidget, component_model: &ComponentModel, shared_types: &IndexMap<String, SharedType>) -> anyhow::Result<UiWidget> {
|
||||
let children = ui_widget.widget_children.into_iter()
|
||||
.map(|child| from_js_to_intermediate_widget(scope, child, component_model, shared_types))
|
||||
.map(|child| from_js_to_intermediate_widget(state.clone(), scope, child, component_model, shared_types))
|
||||
.collect::<anyhow::Result<Vec<UiWidget>>>()?;
|
||||
|
||||
let component = component_model.components
|
||||
|
|
@ -167,7 +169,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, shared_types);
|
||||
let properties = from_js_to_intermediate_properties(state.clone(), scope, ui_widget.widget_properties, &props, shared_types);
|
||||
|
||||
Ok(UiWidget {
|
||||
widget_id: ui_widget.widget_id,
|
||||
|
|
@ -178,6 +180,7 @@ fn from_js_to_intermediate_widget(scope: &mut v8::HandleScope, ui_widget: JsUiWi
|
|||
}
|
||||
|
||||
fn from_js_to_intermediate_properties(
|
||||
state: Rc<RefCell<OpState>>,
|
||||
scope: &mut v8::HandleScope,
|
||||
v8_properties: HashMap<String, serde_v8::Value>,
|
||||
component_props: &HashMap<&String, &PropertyType>,
|
||||
|
|
@ -193,7 +196,7 @@ fn from_js_to_intermediate_properties(
|
|||
return Err(anyhow!("unknown property encountered {:?}", name))
|
||||
};
|
||||
|
||||
convert(scope, property_type, name, val, shared_types)
|
||||
convert(state.clone(), scope, property_type, name, val, shared_types)
|
||||
})
|
||||
.collect::<anyhow::Result<Vec<(_, _)>>>()?;
|
||||
|
||||
|
|
@ -201,6 +204,7 @@ fn from_js_to_intermediate_properties(
|
|||
}
|
||||
|
||||
fn convert(
|
||||
state: Rc<RefCell<OpState>>,
|
||||
scope: &mut v8::HandleScope,
|
||||
property_type: &PropertyType,
|
||||
name: String,
|
||||
|
|
@ -235,16 +239,13 @@ fn convert(
|
|||
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::ImageSource => {
|
||||
let source: ImageSource = serde_v8::from_v8(scope, value)?;
|
||||
convert_image_source(state.clone(), name, source)
|
||||
}
|
||||
PropertyType::Object { name: object_name } => {
|
||||
if value.is_object() {
|
||||
convert_object(scope, name, value, object_name, shared_types)
|
||||
convert_object(state.clone(), scope, name, value, object_name, shared_types)
|
||||
} else {
|
||||
invalid_type_err(name, value, property_type)
|
||||
}
|
||||
|
|
@ -265,21 +266,34 @@ fn convert(
|
|||
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)
|
||||
if !value.is_object() {
|
||||
invalid_type_err(name, value, property_type)
|
||||
} else {
|
||||
let image_source = items.iter().find(|prop_type| matches!(prop_type, PropertyType::ImageSource));
|
||||
let object = items.iter().find(|prop_type| matches!(prop_type, PropertyType::Object { .. }));
|
||||
|
||||
match (image_source, object) {
|
||||
(Some(PropertyType::ImageSource), Some(PropertyType::Object { .. })) => {
|
||||
panic!("image_source and object is conflicting prop_types")
|
||||
}
|
||||
(None, Some(PropertyType::Object { name: object_name })) => {
|
||||
convert_object(state.clone(), scope, name, value, &object_name, shared_types)
|
||||
}
|
||||
(Some(PropertyType::ImageSource), None) => {
|
||||
println!("test: {}", debug_object_to_json(scope, value.clone()));
|
||||
|
||||
let source: ImageSource = serde_v8::from_v8(scope, value)?;
|
||||
convert_image_source(state.clone(), name, source)
|
||||
}
|
||||
(Some(_), Some(_)) | (Some(_), None) | (None, Some(_)) => {
|
||||
unreachable!()
|
||||
}
|
||||
(None, None) => {
|
||||
invalid_type_err(name, value, property_type)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -297,11 +311,14 @@ fn convert_boolean(scope: &mut v8::HandleScope, name: String, value: v8::Local<v
|
|||
Ok((name, UiPropertyValue::Bool(value.boolean_value(scope))))
|
||||
}
|
||||
|
||||
fn convert_bytes(scope: &mut v8::HandleScope, name: String, value: v8::Local<v8::Value>) -> anyhow::Result<(String, UiPropertyValue)> {
|
||||
Ok((name, UiPropertyValue::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: &IndexMap<String, SharedType>) -> anyhow::Result<(String, UiPropertyValue)> {
|
||||
fn convert_object(
|
||||
state: Rc<RefCell<OpState>>,
|
||||
scope: &mut v8::HandleScope,
|
||||
name: String,
|
||||
value: v8::Local<v8::Value>,
|
||||
object_name: &str,
|
||||
shared_types: &IndexMap<String, SharedType>
|
||||
) -> anyhow::Result<(String, UiPropertyValue)> {
|
||||
let object: v8::Local<v8::Object> = value.try_into().context(format!("error while reading property {}", name))?;
|
||||
|
||||
let props = object
|
||||
|
|
@ -324,7 +341,7 @@ fn convert_object(scope: &mut v8::HandleScope, name: String, value: v8::Local<v8
|
|||
SharedType::Object { items } => items.get(&key).unwrap()
|
||||
};
|
||||
|
||||
let (key, value) = convert(scope, property_type, key, value, shared_types)?;
|
||||
let (key, value) = convert(state.clone(), scope, property_type, key, value, shared_types)?;
|
||||
|
||||
result_obj.insert(key, value);
|
||||
}
|
||||
|
|
@ -347,7 +364,7 @@ fn expected_type(prop_type: &PropertyType) -> String {
|
|||
PropertyType::Function { .. } => {
|
||||
panic!("functions are filtered out")
|
||||
}
|
||||
PropertyType::ImageData => "bytearray".to_owned(),
|
||||
PropertyType::ImageSource => "ImageSource".to_owned(),
|
||||
PropertyType::Enum { .. } => "enum".to_owned(),
|
||||
PropertyType::Union { items } => {
|
||||
items.into_iter()
|
||||
|
|
@ -358,3 +375,65 @@ fn expected_type(prop_type: &PropertyType) -> String {
|
|||
PropertyType::Object { .. } => "object".to_owned(),
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_image_source(state: Rc<RefCell<OpState>>, name: String, source: ImageSource) -> anyhow::Result<(String, UiPropertyValue)> {
|
||||
match source {
|
||||
ImageSource::Asset { asset } => {
|
||||
let bytes = {
|
||||
let state = state.borrow();
|
||||
|
||||
let plugin_id = state
|
||||
.borrow::<PluginData>()
|
||||
.plugin_id()
|
||||
.clone();
|
||||
|
||||
let repository = state
|
||||
.borrow::<DataDbRepository>()
|
||||
.clone();
|
||||
|
||||
block_on(async {
|
||||
repository.get_asset_data(&plugin_id.to_string(), &asset).await
|
||||
})?
|
||||
};
|
||||
|
||||
Ok((name, UiPropertyValue::Bytes(bytes)))
|
||||
}
|
||||
// ImageSource::Url { url } => { TODO implement url imagesource
|
||||
// Ok((name, UiPropertyValue::Bytes()))
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
fn debug_object_to_json(
|
||||
scope: &mut v8::HandleScope,
|
||||
val: v8::Local<v8::Value>
|
||||
) -> String {
|
||||
let local = scope.get_current_context();
|
||||
let global = local.global(scope);
|
||||
let json_string = v8::String::new(scope, "Deno").expect("Unable to create Deno string");
|
||||
let json_object = global.get(scope, json_string.into()).expect("Global Deno object not found");
|
||||
let json_object: v8::Local<v8::Object> = json_object.try_into().expect("Deno value is not an object");
|
||||
let inspect_string = v8::String::new(scope, "inspect").expect("Unable to create inspect string");
|
||||
let inspect_object = json_object.get(scope, inspect_string.into()).expect("Unable to get inspect on global Deno object");
|
||||
let stringify_fn: v8::Local<v8::Function> = inspect_object.try_into().expect("inspect value is not a function");;
|
||||
let undefined = v8::undefined(scope).into();
|
||||
|
||||
let json_object = stringify_fn.call(scope, undefined, &[val]).expect("Unable to get serialize prop");
|
||||
let json_string: v8::Local<v8::String> = json_object.try_into().expect("result is not a string");
|
||||
|
||||
let result = json_string.to_rust_string_lossy(scope);
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
enum ImageSource {
|
||||
Asset {
|
||||
asset: String
|
||||
},
|
||||
// Url { TODO implement url imagesource
|
||||
// url: String
|
||||
// }
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue