Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1066,7 +1066,7 @@ async fn make_app(current_dir: &std::ffi::OsString) -> Result<Command> {
.short('t')
.long("template")
.help("Template to create")
.value_parser(["blank", "chat", "echo", "fibonacci", "file-transfer"])
.value_parser(["blank", "chat", "echo", "fibonacci", "file-transfer", "hyperapp-echo"])
.default_value("chat")
)
.arg(Arg::new("UI")
Expand Down
5 changes: 4 additions & 1 deletion src/new/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ pub enum Template {
Echo,
Fibonacci,
FileTransfer,
HyperappEcho,
}

impl Language {
Expand All @@ -44,6 +45,7 @@ impl Template {
Template::Echo => "echo",
Template::Fibonacci => "fibonacci",
Template::FileTransfer => "file-transfer",
Template::HyperappEcho => "hyperapp-echo",
}
.to_string()
}
Expand All @@ -68,7 +70,8 @@ impl From<&String> for Template {
"echo" => Template::Echo,
"fibonacci" => Template::Fibonacci,
"file-transfer" => Template::FileTransfer,
_ => panic!("kit: template must be 'blank', 'chat', 'echo', or 'fibonacci'; not '{s}'"),
"hyperapp-echo" => Template::HyperappEcho,
_ => panic!("kit: template must be 'blank', 'chat', 'echo', 'fibonacci', or 'hyperapp-echo'; not '{s}'"),
}
}
}
Expand Down
10 changes: 10 additions & 0 deletions src/new/templates/rust/no-ui/hyperapp-echo/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
api
!test/hyperapp-echo-test/api
*swp
**/target
pkg/*.wasm
pkg/*.zip
pkg/ui
crates/
caller-utils/
**/.DS_Store
11 changes: 11 additions & 0 deletions src/new/templates/rust/no-ui/hyperapp-echo/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[profile.release]
lto = true
opt-level = "s"
panic = "abort"

[workspace]
members = [
"hyperapp-echo",
"test/hyperapp-echo-test/hyperapp-echo-test",
]
resolver = "2"
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
[dependencies]
process_macros = "0.1"
serde_json = "1.0"
wit-bindgen = "0.36.0"


[dependencies.hyperprocess_macro]
git = "https://github.com/hyperware-ai/hyperprocess-macro"
rev = "47400ab"

[dependencies.hyperware_app_common]
git = "https://github.com/hyperware-ai/hyperprocess-macro"
rev = "47400ab"

[dependencies.serde]
features = ["derive"]
version = "1.0"

[features]
simulation-mode = []

[lib]
crate-type = ["cdylib"]

[package]
edition = "2021"
name = "hyperapp-echo"
version = "0.1.0"

[package.metadata.component]
package = "hyperware:process"
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
use serde::{Serialize, Deserialize};
use hyperprocess_macro::hyperprocess;
use hyperware_process_lib::println;

#[derive(Default, Debug, Serialize, Deserialize)]
pub struct HyperappEchoState {}

#[derive(Serialize, Deserialize, Debug)]
pub struct Argument {
header: String,
body: String,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to put serde_json::Value as the type for the body field (to represent a generic JSON object, but I checked the WIT conversion and we don't handle this type of WIT type (I am not entirely sure whether its technically possible, but putting an arbitrary JSON object as an argument does seem useful.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah that definitely won't work. We always serialize to either a bytes string (with serde_json::to_vec()) or a string (serde_json::to_string()), so those are your two options. When we serialize the whole struct using JSON, we should choose to serialize body to a String because that will play nicer with JSON than an array of bytes

}

#[derive(Serialize, Deserialize, Debug)]
pub struct ReturnValue {
response: String,
}

#[hyperprocess(
name = "HyperappEcho",
ui = None,
endpoints = vec![
Binding::Http {
path: "/api",
config: HttpBindingConfig::new(false, false, false, None),
},
Binding::Ws {
path: "/ws",
config: WsBindingConfig::new(false, false, false),
}
],
save_config = SaveOptions::Never,
wit_world = "hyperapp-echo-template-dot-os-v0"
)]

impl HyperappEchoState {
// Initialize the process, every application needs an init function
#[init]
async fn initialize(&mut self) {
println!("init HyperappEcho");
}

// Endpoint accepting both local, remote Hyperware requests, and HTTP requests
#[local]
#[remote]
#[http]
async fn echo(&self, arg: Argument) -> ReturnValue {
println!("header: {:?}, body: {:?}", arg.header, arg.body);

ReturnValue { response: "Ack".to_string() }
}

}
20 changes: 20 additions & 0 deletions src/new/templates/rust/no-ui/hyperapp-echo/metadata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "Hyperapp Echo",
"description": "Echo process for Hyperapp framework.",
"image": "",
"properties": {
"package_name": "hyperapp-echo",
"current_version": "0.1.0",
"publisher": "template.os",
"mirrors": [],
"code_hashes": {
"0.1.0": ""
},
"wit_version": 1,
"dependencies": [
"hyperapp-echo:template.os"
]
},
"external_url": "https://hyperware.ai",
"animation_url": ""
}
23 changes: 23 additions & 0 deletions src/new/templates/rust/no-ui/hyperapp-echo/pkg/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[
{
"process_name": "hyperapp-echo",
"process_wasm_path": "/hyperapp-echo.wasm",
"on_exit": "Restart",
"request_networking": true,
"request_capabilities": [
"homepage:homepage:sys",
"http-client:distro:sys",
"http-server:distro:sys",
"terminal:terminal:sys",
"vfs:distro:sys"
],
"grant_capabilities": [
"homepage:homepage:sys",
"http-client:distro:sys",
"http-server:distro:sys",
"terminal:terminal:sys",
"vfs:distro:sys"
],
"public": false
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[workspace]
resolver = "2"
members = [
"hyperapp-echo-test",
]

[profile.release]
panic = "abort"
opt-level = "s"
lto = true
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
world hyperapp-echo-test-template-dot-os-v0 {
import hyperapp-echo;
import tester;
include process-v1;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
[package]
name = "hyperapp-echo-test"
version = "0.1.0"
edition = "2021"
publish = false

[dependencies.caller-utils]
path = "../../../target/caller-utils"

[dependencies.hyperware_app_common]
git = "https://github.com/hyperware-ai/hyperprocess-macro"
rev = "b6ad495"

[dependencies]
anyhow = "1.0"
hyperware_process_lib = { git = "https://github.com/hyperware-ai/process_lib", features = ["logging"], rev = "b7c9d27" }
process_macros = "0.1.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
wit-bindgen = "0.36.0"


[lib]
crate-type = ["cdylib"]

[package.metadata.component]
package = "hyperware:process"
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// Import necessary types and functions explicitly from caller_utils
use caller_utils::{Argument, ReturnValue}; // Import types directly (via path dependency)
use caller_utils::hyperapp_echo::{echo_local_rpc, echo_remote_rpc}; // Import RPC functions

// Add this import here, as fail! is expanded in this file
use crate::hyperware::process::tester::{FailResponse, Response as TesterResponse};

mod tester_lib;

async_test_suite!(
"hyperapp-echo-test-template-dot-os-v0",

test_basic_math: async {
if 2 + 2 != 4 {
fail!("wrong result");
}
Ok(())
},

// Test local echo RPC call
test_echo_local_rpc: async {
// Define the target process address
let address: Address = ("hyperapp-echo.os", "hyperapp-echo", "hyperapp-echo", "template.os").into();
// Define the argument for the echo function
let arg = Argument {
header: "LocalTestHeader".to_string(),
body: "LocalTestBody".to_string(),
};
// Define the expected return value
let expected_return = ReturnValue {
response: "Ack".to_string(),
};

match echo_local_rpc(&address, arg).await {
Ok(actual_value) => {
// Compare the 'response' field directly
if actual_value.response != expected_return.response {
// fail! macro uses FailResponse/TesterResponse imported above
fail!(format!(
"echo_local_rpc unexpected result: expected {:?}, got {:?}",
expected_return, actual_value // Keep original structs for error message
));
}
// If the result matches, the test passes for this step
Ok(())
}
Err(e) => {
// Use fail! macro if the RPC call itself returned an error
fail!(format!("echo_local_rpc failed: {:?}", e));
}
}
},

// Test remote echo RPC call
test_echo_remote_rpc: async {
// Define the target process address
let address: Address = ("hyperapp-echo.os", "hyperapp-echo", "hyperapp-echo", "template.os").into();
// Define the argument for the echo function
let arg = Argument {
header: "RemoteTestHeader".to_string(),
body: "RemoteTestBody".to_string(),
};
// Define the expected return value
let expected_return = ReturnValue {
response: "Ack".to_string(),
};

// Call the remote echo RPC stub
match echo_remote_rpc(&address, arg).await {
Ok(actual_value) => {
// Compare the 'response' field directly
if actual_value.response != expected_return.response {
// fail! macro uses FailResponse/TesterResponse imported above
fail!(format!(
"echo_remote_rpc unexpected result: expected {:?}, got {:?}",
expected_return, actual_value // Keep original structs for error message
));
}
// If the result matches, the test passes for this step
Ok(())
}
Err(e) => {
// Use fail! macro if the RPC call itself returned an error
fail!(format!("echo_remote_rpc failed: {:?}", e));
}
}
},
);
Loading