Skip to content
This repository was archived by the owner on Jan 15, 2024. It is now read-only.

Commit e4931e3

Browse files
committed
Dependency injection for backend in wallet server.
This will be important when we get to integration, as it allows us to use a mock backend for the API tests and a real one for the actual executable. Unfortunately, right now it crashes the gd Rust compiler. See rust-lang/rust#90691 for an apparently related issue.
1 parent 9f442a0 commit e4931e3

File tree

5 files changed

+133
-64
lines changed

5 files changed

+133
-64
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

wallet/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ default-run = "cape_wallet"
1212

1313
[dependencies]
1414
async-std = { version = "1.9.0", features = ["unstable", "attributes"] }
15+
async-trait = "0.1.51"
1516
bincode = "1.3.3"
1617
futures = "0.3.0"
1718
futures-util = "0.3.8"

wallet/src/disco.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// Copyright © 2021 Translucence Research, Inc. All rights reserved.
22

3-
use crate::routes::check_api;
3+
use crate::routes::{check_api, BackendAdapter};
44
use crate::WebState;
55
use std::fs::read_to_string;
66
use std::path::Path;
@@ -20,7 +20,9 @@ pub fn load_messages(path: &Path) -> toml::Value {
2020
/// a documentation page for the web API.
2121
///
2222
/// The results of this could be precomputed and cached.
23-
pub async fn compose_help(req: tide::Request<WebState>) -> Result<tide::Response, tide::Error> {
23+
pub async fn compose_help(
24+
req: tide::Request<WebState<impl BackendAdapter>>,
25+
) -> Result<tide::Response, tide::Error> {
2426
let api = &req.state().api;
2527
let meta = &api["meta"];
2628
let mut help = meta["HTML_TOP"]

wallet/src/main.rs

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
// Copyright © 2021 Translucence Research, Inc. All rights reserved.
22

33
use crate::routes::{
4-
dispatch_url, dispatch_web_socket, RouteBinding, UrlSegmentType, UrlSegmentValue, Wallet,
4+
dispatch_url, dispatch_web_socket, BackendAdapter, MockBackendAdapter, RouteBinding,
5+
UrlSegmentType, UrlSegmentValue,
56
};
67
use async_std::{
78
sync::{Arc, Mutex},
@@ -13,7 +14,7 @@ use std::str::FromStr;
1314
use structopt::StructOpt;
1415
use tide::StatusCode;
1516
use tide_websockets::{WebSocket, WebSocketConnection};
16-
use zerok_lib::api::server;
17+
use zerok_lib::{api::server, wallet::cape::CapeWallet};
1718

1819
mod disco;
1920
mod ip;
@@ -71,14 +72,27 @@ fn default_api_path() -> PathBuf {
7172
[&dir, Path::new(API_FILE)].iter().collect()
7273
}
7374

74-
#[derive(Clone)]
75-
pub struct WebState {
75+
pub struct WebState<Adapter: BackendAdapter> {
7676
web_path: PathBuf,
7777
api: toml::Value,
78-
wallet: Arc<Mutex<Option<Wallet>>>,
78+
wallet: Arc<Mutex<Option<CapeWallet<'static, Adapter::Backend>>>>,
79+
adapter: Adapter,
7980
}
8081

81-
async fn form_demonstration(req: tide::Request<WebState>) -> Result<tide::Body, tide::Error> {
82+
impl<Adapter: BackendAdapter> Clone for WebState<Adapter> {
83+
fn clone(&self) -> Self {
84+
Self {
85+
web_path: self.web_path.clone(),
86+
api: self.api.clone(),
87+
wallet: self.wallet.clone(),
88+
adapter: self.adapter.clone(),
89+
}
90+
}
91+
}
92+
93+
async fn form_demonstration(
94+
req: tide::Request<WebState<impl BackendAdapter>>,
95+
) -> Result<tide::Body, tide::Error> {
8296
let mut index_html: PathBuf = req.state().web_path.clone();
8397
index_html.push("index.html");
8498
Ok(tide::Body::from_file(index_html).await?)
@@ -87,7 +101,7 @@ async fn form_demonstration(req: tide::Request<WebState>) -> Result<tide::Body,
87101
// Get the route pattern that matches the URL of a request, and the bindings for parameters in the
88102
// pattern. If no route matches, the error is a documentation string explaining what went wrong.
89103
fn parse_route(
90-
req: &tide::Request<WebState>,
104+
req: &tide::Request<WebState<impl BackendAdapter>>,
91105
) -> Result<(String, HashMap<String, RouteBinding>), String> {
92106
let first_segment = &req
93107
.url()
@@ -212,15 +226,17 @@ fn parse_route(
212226
/// This function duplicates the logic for deciding which route was requested. This
213227
/// is an unfortunate side-effect of defining the routes in an external file.
214228
// todo !corbett Convert the error feedback into HTML
215-
async fn entry_page(req: tide::Request<WebState>) -> Result<tide::Response, tide::Error> {
229+
async fn entry_page(
230+
req: tide::Request<WebState<impl BackendAdapter>>,
231+
) -> Result<tide::Response, tide::Error> {
216232
match parse_route(&req) {
217233
Ok((pattern, bindings)) => dispatch_url(req, pattern.as_str(), &bindings).await,
218234
Err(arg_doc) => Ok(tide::Response::builder(200).body(arg_doc).build()),
219235
}
220236
}
221237

222238
async fn handle_web_socket(
223-
req: tide::Request<WebState>,
239+
req: tide::Request<WebState<impl BackendAdapter>>,
224240
wsc: WebSocketConnection,
225241
) -> tide::Result<()> {
226242
match parse_route(&req) {
@@ -231,23 +247,25 @@ async fn handle_web_socket(
231247

232248
// This route is a demonstration of a form with a WebSocket connection
233249
// for asynchronous updates. Once we have useful forms, this can go...
234-
fn add_form_demonstration(web_server: &mut tide::Server<WebState>) {
250+
fn add_form_demonstration(web_server: &mut tide::Server<WebState<impl 'static + BackendAdapter>>) {
235251
web_server
236252
.at("/transfer/:id/:recipient/:amount")
237253
.with(WebSocket::new(handle_web_socket))
238254
.get(form_demonstration);
239255
}
240256

241-
fn init_server(
257+
fn init_server<Adapter: 'static + BackendAdapter>(
242258
api_path: PathBuf,
243259
web_path: PathBuf,
244260
port: u64,
261+
adapter: Adapter,
245262
) -> std::io::Result<JoinHandle<std::io::Result<()>>> {
246263
let api = disco::load_messages(&api_path);
247264
let mut web_server = tide::with_state(WebState {
248265
web_path: web_path.clone(),
249266
api: api.clone(),
250267
wallet: Arc::new(Mutex::new(None)),
268+
adapter,
251269
});
252270
web_server.with(server::trace).with(server::add_error_body);
253271

@@ -329,7 +347,14 @@ async fn main() -> Result<(), std::io::Error> {
329347

330348
// Use something different than the default Spectrum port (60000 vs 50000).
331349
let port = std::env::var("PORT").unwrap_or_else(|_| String::from("60000"));
332-
init_server(api_path, web_path, port.parse().unwrap())?.await?;
350+
// TODO replace the mock backend with a real backend.
351+
init_server(
352+
api_path,
353+
web_path,
354+
port.parse().unwrap(),
355+
MockBackendAdapter::default(),
356+
)?
357+
.await?;
333358

334359
Ok(())
335360
}
@@ -380,7 +405,12 @@ mod tests {
380405
// the server will continue running until the process is killed, even after the test
381406
// ends. This is probably not so bad, since each test's server task should be idle once
382407
// the test is over, and anyways I don't see a good way around it.
383-
init_server(default_api_path(), default_web_path(), port).unwrap();
408+
init_server::<MockCapeBackend<'static, LoaderMetadata>>(
409+
default_api_path(),
410+
default_web_path(),
411+
port,
412+
)
413+
.unwrap();
384414

385415
let client: surf::Client = surf::Config::new()
386416
.set_base_url(Url::parse(&format!("http://localhost:{}", port)).unwrap())

0 commit comments

Comments
 (0)