chore: move CLI into its own "studio" app

This commit is contained in:
Jonathan Kelley 2021-07-07 16:58:23 -04:00
parent 207784800d
commit fd7933561f
37 changed files with 0 additions and 1481 deletions

View file

@ -11,7 +11,6 @@ description = "Core functionality for Dioxus - a concurrent renderer-agnostic Vi
dioxus-core = { path="./packages/core", version="0.1.0" }
dioxus-core-macro = { path="./packages/core-macro", version="0.1.0" }
dioxus-html-namespace = { path="./packages/html-namespace" }
dioxus-web = { path="./packages/web", optional=true }
# dioxus-hooks = { path="./packages/hooks", version="0.0.0" }
@ -48,10 +47,7 @@ members = [
"packages/html-namespace",
"packages/web",
"packages/webview",
"packages/cli",
# "packages/atoms",
# "packages/ssr",
# "packages/docsite",
# "packages/router",
# "packages/inputs",
]

View file

@ -1,2 +0,0 @@
/target
Cargo.lock

View file

@ -1,3 +0,0 @@
{
"rust-analyzer.inlayHints.enable": false
}

View file

@ -1,33 +0,0 @@
[package]
name = "dioxus-cli"
version = "0.1.0"
authors = ["Jonathan Kelley <jkelleyrtp@gmail.com>"]
edition = "2018"
description = "CLI tool for developing, testing, and publishing Dioxus apps"
"license" = "MIT/Apache-2.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
thiserror = "1.0.23"
log = "0.4.13"
fern = { version="0.6.0", features=["colored"] }
wasm-bindgen-cli-support = "0.2.73"
anyhow = "1.0.38"
argh = "0.1.4"
serde = "1.0.120"
serde_json = "1.0.61"
async-std = { version="1.9.0", features=["attributes"] }
tide = "0.15.0"
fs_extra = "1.2.0"
cargo_toml = "0.8.1"
futures = "0.3.12"
notify = "5.0.0-pre.4"
rjdebounce = "0.2.1"
tempfile = "3.2.0"
[[bin]]
path = "src/main.rs"
name = "dioxus"

View file

@ -1,48 +0,0 @@
<div align="center">
<h1>📦✨ dioxus-cli</h1>
<p>
<strong>Tooling to supercharge dioxus projects</strong>
</p>
</div>
# About
---
dioxus-cli (inspired by wasm-pack and webpack) is a tool to help get dioxus projects off the ground. It handles all the build, development, bundling, and publishing to make web development just a simple two commands: `cargo init` and `dioxus-cli publish`.
Best thing:
- No NPM.
- No Webpack.
- No `node_modules`.
- No Babel
- No parcel
- No rollup
- No ESLint
Just install Rust, dioxus-cli, and you're good to go.
`cargo install --git github.com/jkelleyrtp/dioxus-cli`
Need a development server?
`dioxus develop`
Need to run an example?
`dioxus develop --example textbox`
Need to benchmark a component?
`dioxus bench`
Need to test your code?
`dioxus test`
Need to build your code into a bundle?
`dioxus build --outdir public`
Need to publish your code to GitHub pages, Netlify, etc?
`dioxus publish --ghpages myrepo.git`
# Use in your project
---
Sometimes you'll want to include static assets without bundling them into your .wasm content. dioxus-cli provides a few ways of doing this:
- Load in dynamic content using `dioxus::asset("./static/images/blah.svg")`
- Live-reload HTML templates without rebuilding your .wasm with `dioxus::template("./templates/blah.html")`
- Use a CSS library like tailwind in your dioxus configuration with

View file

@ -1,146 +0,0 @@
use crate::{
cli::BuildOptions,
config::{Config, ExecutableType},
error::Result,
};
use log::{info, warn};
use std::{io::Write, process::Command};
use wasm_bindgen_cli_support::Bindgen;
pub struct BuildConfig {}
impl Into<BuildConfig> for BuildOptions {
fn into(self) -> BuildConfig {
BuildConfig {}
}
}
impl Default for BuildConfig {
fn default() -> Self {
Self {}
}
}
pub fn build(config: &Config, _build_config: &BuildConfig) -> Result<()> {
/*
[1] Build the project with cargo, generating a wasm32-unknown-unknown target (is there a more specific, better target to leverage?)
[2] Generate the appropriate build folders
[3] Wasm-bindgen the .wasm fiile, and move it into the {builddir}/modules/xxxx/xxxx_bg.wasm
[4] Wasm-opt the .wasm file with whatever optimizations need to be done
[5] Link up the html page to the wasm module
*/
let Config {
out_dir,
crate_dir,
target_dir,
static_dir,
executable,
..
} = config;
let t_start = std::time::Instant::now();
// [1] Build the .wasm module
info!("Running build commands...");
let mut cmd = Command::new("cargo");
cmd.current_dir(&crate_dir)
.arg("build")
.arg("--target")
.arg("wasm32-unknown-unknown")
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped());
if config.release {
cmd.arg("--release");
}
match executable {
ExecutableType::Binary(name) => cmd.arg("--bin").arg(name),
ExecutableType::Lib(name) => cmd.arg("--lib").arg(name),
ExecutableType::Example(name) => cmd.arg("--example").arg(name),
};
let mut child = cmd.spawn()?;
let _err_code = child.wait()?;
info!("Build complete!");
// [2] Establish the output directory structure
let bindgen_outdir = out_dir.join("wasm");
// [3] Bindgen the final binary for use easy linking
let mut bindgen_builder = Bindgen::new();
let release_type = match config.release {
true => "release",
false => "debug",
};
let input_path = match executable {
ExecutableType::Binary(name) | ExecutableType::Lib(name) => target_dir
// .join("wasm32-unknown-unknown/release")
.join(format!("wasm32-unknown-unknown/{}", release_type))
.join(format!("{}.wasm", name)),
ExecutableType::Example(name) => target_dir
// .join("wasm32-unknown-unknown/release/examples")
.join(format!("wasm32-unknown-unknown/{}/examples", release_type))
.join(format!("{}.wasm", name)),
};
bindgen_builder
.input_path(input_path)
.web(true)?
.debug(true)
.demangle(true)
.keep_debug(true)
.remove_name_section(false)
.remove_producers_section(false)
.out_name("module")
.generate(&bindgen_outdir)?;
// [4]
// TODO: wasm-opt
// [5] Generate the html file with the module name
// TODO: support names via options
info!("Writing to '{:#?}' directory...", out_dir);
let mut file = std::fs::File::create(out_dir.join("index.html"))?;
file.write_all(gen_page("./wasm/module.js").as_str().as_bytes())?;
let copy_options = fs_extra::dir::CopyOptions::new();
match fs_extra::dir::copy(static_dir, out_dir, &copy_options) {
Ok(_) => {}
Err(_e) => {
warn!("Error copying dir");
}
}
let t_end = std::time::Instant::now();
log::info!("Done in {}ms! 🎉", (t_end - t_start).as_millis());
Ok(())
}
fn gen_page(module: &str) -> String {
format!(
r#"
<html>
<head>
<meta content="text/html;charset=utf-8" http-equiv="Content-Type" />
<meta charset="UTF-8" />
<link
href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css"
rel="stylesheet"
/>
</head>
<body>
<!-- Note the usage of `type=module` here as this is an ES6 module -->
<script type="module">
import init from "{}";
init("./wasm/module_bg.wasm");
</script>
</body>
</html>
"#,
module
)
}

View file

@ -1,69 +0,0 @@
//! Utilities for working with cargo and rust files
use crate::error::{Error, Result};
use std::{env, fs, path::PathBuf, process::Command, str};
/// How many parent folders are searched for a `Cargo.toml`
const MAX_ANCESTORS: u32 = 10;
/// Returns the root of the crate that the command is run from
///
/// If the command is run from the workspace root, this will return the top-level Cargo.toml
pub fn crate_root() -> Result<PathBuf> {
// From the current directory we work our way up, looking for `Cargo.toml`
env::current_dir()
.ok()
.and_then(|mut wd| {
for _ in 0..MAX_ANCESTORS {
if contains_manifest(&wd) {
return Some(wd);
}
if !wd.pop() {
break;
}
}
None
})
.ok_or_else(|| Error::CargoError("Failed to find the cargo directory".to_string()))
}
/// Returns the root of a workspace
/// TODO @Jon, find a different way that doesn't rely on the cargo metadata command (it's slow)
pub fn workspace_root() -> Result<PathBuf> {
let output = Command::new("cargo")
.args(&["metadata"])
.output()
.map_err(|_| Error::CargoError("Manifset".to_string()))?;
if !output.status.success() {
let mut msg = str::from_utf8(&output.stderr).unwrap().trim();
if msg.starts_with("error: ") {
msg = &msg[7..];
}
return Err(Error::CargoError(msg.to_string()));
}
let stdout = str::from_utf8(&output.stdout).unwrap();
for line in stdout.lines() {
let meta: serde_json::Value = serde_json::from_str(line)
.map_err(|_| Error::CargoError("InvalidOutput".to_string()))?;
let root = meta["workspace_root"]
.as_str()
.ok_or_else(|| Error::CargoError("InvalidOutput".to_string()))?;
return Ok(root.into());
}
Err(Error::CargoError("InvalidOutput".to_string()))
}
/// Checks if the directory contains `Cargo.toml`
fn contains_manifest(path: &PathBuf) -> bool {
fs::read_dir(path)
.map(|entries| {
entries
.filter_map(Result::ok)
.any(|ent| &ent.file_name() == "Cargo.toml")
})
.unwrap_or(false)
}

View file

@ -1,62 +0,0 @@
use argh::FromArgs;
#[derive(FromArgs, PartialEq, Debug)]
/// Top-level command.
pub struct LaunchOptions {
#[argh(subcommand)]
pub command: LaunchCommand,
}
/// The various kinds of commands that `wasm-pack` can execute.
#[derive(FromArgs, PartialEq, Debug)]
#[argh(subcommand)]
pub enum LaunchCommand {
Develop(DevelopOptions),
Build(BuildOptions),
Test(TestOptions),
Publish(PublishOptions),
}
/// Publish your yew application to Github Pages, Netlify, or S3
#[derive(FromArgs, PartialEq, Debug)]
#[argh(subcommand, name = "publish")]
pub struct PublishOptions {}
/// 🔬 test your yew application!
#[derive(FromArgs, PartialEq, Debug)]
#[argh(subcommand, name = "test")]
pub struct TestOptions {
/// an example in the crate
#[argh(option)]
pub example: Option<String>,
}
/// 🏗️ Build your yew application
#[derive(FromArgs, PartialEq, Debug, Clone)]
#[argh(subcommand, name = "build")]
pub struct BuildOptions {
/// an optional direction which is "up" by default
#[argh(option, short = 'o', default = "String::from(\"public\")")]
pub outdir: String,
/// an example in the crate
#[argh(option)]
pub example: Option<String>,
/// develop in release mode
#[argh(switch, short = 'r')]
pub release: bool,
}
/// 🛠 Start a development server
#[derive(FromArgs, PartialEq, Debug)]
#[argh(subcommand, name = "develop")]
pub struct DevelopOptions {
/// an example in the crate
#[argh(option)]
pub example: Option<String>,
/// develop in release mode
#[argh(switch, short = 'r')]
pub release: bool,
}

View file

@ -1,92 +0,0 @@
use crate::{
cli::{BuildOptions, DevelopOptions},
error::Result,
};
use std::{io::Write, path::PathBuf, process::Command};
#[derive(Debug, Clone)]
pub struct Config {
pub out_dir: PathBuf,
pub crate_dir: PathBuf,
pub workspace_dir: PathBuf,
pub target_dir: PathBuf,
pub static_dir: PathBuf,
pub manifest: cargo_toml::Manifest<cargo_toml::Value>,
pub executable: ExecutableType,
pub release: bool,
}
#[derive(Debug, Clone)]
pub enum ExecutableType {
Binary(String),
Lib(String),
Example(String),
}
impl Config {
pub fn new() -> Result<Self> {
let crate_dir = crate::cargo::crate_root()?;
let workspace_dir = crate::cargo::workspace_root()?;
let target_dir = workspace_dir.join("target");
let out_dir = crate_dir.join("public");
let cargo_def = &crate_dir.join("Cargo.toml");
let static_dir = crate_dir.join("static");
let manifest = cargo_toml::Manifest::from_path(&cargo_def).unwrap();
// We just assume they're using a 'main.rs'
// Anyway, we've already parsed the manifest, so it should be easy to change the type
let output_filename = (&manifest)
.lib
.as_ref()
.and_then(|lib| lib.name.clone())
.or_else(|| {
(&manifest)
.package
.as_ref()
.and_then(|pkg| Some(pkg.name.replace("-", "_")))
.clone()
})
.expect("No lib found from cargo metadata");
let executable = ExecutableType::Binary(output_filename);
let release = false;
Ok(Self {
out_dir,
crate_dir,
workspace_dir,
target_dir,
static_dir,
manifest,
executable,
release,
})
}
pub fn as_example(&mut self, example_name: String) -> &mut Self {
self.executable = ExecutableType::Example(example_name);
self
}
pub fn with_release(&mut self, release: bool) -> &mut Self {
self.release = release;
self
}
pub fn with_build_options(&mut self, options: &BuildOptions) {
if let Some(name) = &options.example {
self.as_example(name.clone());
}
self.release = options.release;
self.out_dir = options.outdir.clone().into();
}
pub fn with_develop_options(&mut self, options: &DevelopOptions) {
if let Some(name) = &options.example {
self.as_example(name.clone());
}
self.release = options.release;
self.out_dir = tempfile::Builder::new().tempdir().expect("").into_path();
}
}

View file

@ -1,111 +0,0 @@
use crate::{builder::BuildConfig, cli::DevelopOptions, config::Config, error::Result};
use async_std::prelude::FutureExt;
use async_std::future;
use async_std::prelude::*;
use log::info;
use notify::{RecommendedWatcher, RecursiveMode, Watcher};
use std::path::PathBuf;
use std::time::Duration;
pub struct DevelopConfig {}
impl Into<DevelopConfig> for DevelopOptions {
fn into(self) -> DevelopConfig {
DevelopConfig {}
}
}
pub async fn start(config: &Config, _options: &DevelopConfig) -> Result<()> {
log::info!("Starting development server 🚀");
let Config { out_dir, .. } = config;
// Spawn the server onto a seperate task
// This lets the task progress while we handle file updates
let server = async_std::task::spawn(launch_server(out_dir.clone()));
let watcher = async_std::task::spawn(watch_directory(config.clone()));
match server.race(watcher).await {
Err(e) => log::warn!("Error running development server, {:?}", e),
_ => {}
}
Ok(())
}
async fn watch_directory(config: Config) -> Result<()> {
// Create a channel to receive the events.
let (watcher_tx, watcher_rx) = async_std::channel::bounded(100);
// Automatically select the best implementation for your platform.
// You can also access each implementation directly e.g. INotifyWatcher.
let mut watcher: RecommendedWatcher = Watcher::new_immediate(move |res| {
async_std::task::block_on(watcher_tx.send(res));
})
.expect("failed to make watcher");
let src_dir = crate::cargo::crate_root()?;
// Add a path to be watched. All files and directories at that path and
// below will be monitored for changes.
watcher
.watch(src_dir.join("src"), RecursiveMode::Recursive)
.expect("Failed to watch dir");
watcher
.watch(src_dir.join("examples"), RecursiveMode::Recursive)
.expect("Failed to watch dir");
let build_config = BuildConfig::default();
'run: loop {
crate::builder::build(&config, &build_config)?;
// Wait for the message with a debounce
let _msg = watcher_rx
.recv()
.join(future::ready(1_usize).delay(Duration::from_millis(2000)))
.await;
info!("File updated, rebuilding app");
}
Ok(())
}
async fn launch_server(outdir: PathBuf) -> Result<()> {
let _crate_dir = crate::cargo::crate_root()?;
let _workspace_dir = crate::cargo::workspace_root()?;
let mut app = tide::with_state(ServerState::new(outdir.to_owned()));
let p = outdir.display().to_string();
app.at("/")
.get(|req: tide::Request<ServerState>| async move {
log::info!("Connected to development server");
let state = req.state();
Ok(tide::Body::from_file(state.serv_path.clone().join("index.html")).await?)
})
.serve_dir(p)?;
let port = "8080";
let serve_addr = format!("0.0.0.0:{}", port);
// let serve_addr = format!("127.0.0.1:{}", port);
info!("App available at http://{}", serve_addr);
app.listen(serve_addr).await?;
Ok(())
}
/// https://github.com/http-rs/tide/blob/main/examples/state.rs
/// Tide seems to prefer using state instead of injecting into the app closure
/// The app closure needs to be static and
#[derive(Clone)]
struct ServerState {
serv_path: PathBuf,
}
impl ServerState {
fn new(serv_path: PathBuf) -> Self {
Self { serv_path }
}
}

View file

@ -1,34 +0,0 @@
use thiserror::Error as ThisError;
pub type Result<T, E = Error> = std::result::Result<T, E>;
#[derive(ThisError, Debug)]
pub enum Error {
/// Used when errors need to propogate but are too unique to be typed
#[error("{0}")]
Unique(String),
#[error("I/O Error: {0}")]
IO(#[from] std::io::Error),
#[error("Failed to write error")]
FailedToWrite,
#[error("Failed to write error")]
CargoError(String),
#[error(transparent)]
Other(#[from] anyhow::Error),
}
impl From<&str> for Error {
fn from(s: &str) -> Self {
Error::Unique(s.to_string())
}
}
impl From<String> for Error {
fn from(s: String) -> Self {
Error::Unique(s)
}
}

View file

@ -1,8 +0,0 @@
pub mod builder;
pub mod cargo;
pub mod cli;
pub mod config;
pub mod develop;
pub mod error;
pub mod logging;
pub mod watch;

View file

@ -1,49 +0,0 @@
use fern::colors::{Color, ColoredLevelConfig};
use log::debug;
pub fn set_up_logging() {
// configure colors for the whole line
let colors_line = ColoredLevelConfig::new()
.error(Color::Red)
.warn(Color::Yellow)
// we actually don't need to specify the color for debug and info, they are white by default
.info(Color::White)
.debug(Color::White)
// depending on the terminals color scheme, this is the same as the background color
.trace(Color::BrightBlack);
// configure colors for the name of the level.
// since almost all of them are the same as the color for the whole line, we
// just clone `colors_line` and overwrite our changes
let colors_level = colors_line.clone().info(Color::Green);
// here we set up our fern Dispatch
fern::Dispatch::new()
.format(move |out, message, record| {
out.finish(format_args!(
"{color_line}[{level}{color_line}] {message}\x1B[0m",
color_line = format_args!(
"\x1B[{}m",
colors_line.get_color(&record.level()).to_fg_str()
),
level = colors_level.color(record.level()),
message = message,
));
})
// set the default log level. to filter out verbose log messages from dependencies, set
// this to Warn and overwrite the log level for your crate.
.level(log::LevelFilter::Info)
// .level(log::LevelFilter::Warn)
// change log levels for individual modules. Note: This looks for the record's target
// field which defaults to the module path but can be overwritten with the `target`
// parameter:
// `info!(target="special_target", "This log message is about special_target");`
// .level_for("dioxus", log::LevelFilter::Debug)
// .level_for("dioxus", log::LevelFilter::Info)
// .level_for("pretty_colored", log::LevelFilter::Trace)
// output to stdout
.chain(std::io::stdout())
.apply()
.unwrap();
debug!("finished setting up logging! yay!");
}

View file

@ -1,28 +0,0 @@
use diopack::cli::{LaunchCommand, LaunchOptions};
use dioxus_cli as diopack;
#[async_std::main]
async fn main() -> diopack::error::Result<()> {
diopack::logging::set_up_logging();
let opts: LaunchOptions = argh::from_env();
let mut config = diopack::config::Config::new()?;
match opts.command {
LaunchCommand::Build(options) => {
config.with_build_options(&options);
diopack::builder::build(&config, &(options.into()))?;
}
LaunchCommand::Develop(options) => {
config.with_develop_options(&options);
diopack::develop::start(&config, &(options.into())).await?;
}
_ => {
todo!("Command not currently implemented");
}
}
Ok(())
}

View file

@ -1,11 +0,0 @@
pub struct Watcher {}
impl Watcher {
// Spawns a new notify instance that alerts us of changes
pub fn new() -> Self {
Self {}
}
/// Subsribe to the watcher
pub fn subscribe() {}
}

View file

@ -7,7 +7,6 @@
use crate::innerlude::Context;
use crate::innerlude::*;
use std::{
cell::RefCell,
ops::{Deref, DerefMut},

View file

@ -1,8 +0,0 @@
[package]
name = "dioxus-inputs"
version = "0.0.0"
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View file

@ -1,28 +0,0 @@
# Cross-platform controlled inputs
Dioxus does not include controlled inputs in Dioxus-Core. This would require core integration with HTML that is inherently not cross platform. Instead, this crate exists to provide a bunch of cross-platform input types that abstract over renderer quirks.
Included is:
- Button
- Checkbox
- Color
- Date
- Datetime-local
- Email
- File
- Hidden
- Image
- Month
- Number
- Password
- Radio
- Range
- Reset
- Search
- Submit
- Tel
- Text
- Time
- Url
- Week

View file

@ -1,7 +0,0 @@
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
}

View file

@ -1 +0,0 @@
.vscode/

View file

@ -1,39 +0,0 @@
[package]
name = "dioxus-livehost"
version = "0.0.0"
authors = ["Jonathan Kelley <jkelleyrtp@gmail.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
dioxus-core = { path = "../core", version = "0.1.2" }
dioxus-web = { path = "../web" }
serde = { version = "1", features = ['derive'] }
tide = "0.16.0"
tide-websockets = "0.3.0"
async-std = { version = "1.8.0", features = ["attributes"] }
log = "0.4"
fern = { version = "0.6.0", features = ["colored"] }
async-channel = "1.5.1"
futures-lite = "1.11.2"
anyhow = "1.0.35"
js-sys = { version = "0.3", optional = true }
wasm-bindgen = { version = "0.2.71", optional = true }
lazy_static = { version = "1.4.0", optional = true }
wasm-bindgen-futures = { version = "0.4.20", optional = true }
serde_millis = "0.1"
serde_json = "1"
strum = { version = "0.20", features = ["derive"] }
[features]
default = ["client", "server"]
server = []
client = []

View file

@ -1,13 +0,0 @@
# Livehost
Stream events from a server to a client.
This crate provides drivers for Actix, Warp, and Tide to run coupled frontend and backend.
This comes in the form of two approaches:
- tight coupling: frontend and backend are locked together
- loose coupling: hooks subscribe a component to a server using the suspense mechanism
Tight coupling is basically an implmentation of loose coupling where **all** events move through the backend connection. This coupling option has higher latency but is very simple to deploy. We use this approach for dioxus-webview where latency is minimal (hosted locally) and we want builds to be simple - no need to manually bundle a custom frontend because everything is server rendered.

View file

@ -1,131 +0,0 @@
pub fn main() {
#[cfg(feature = "client")]
wasm_bindgen_futures::spawn_local(async { client::app().await.unwrap() });
#[cfg(feature = "server")]
async_std::task::block_on(async { server::app().await.expect("") });
}
/// ===============================
/// Common code (shared types)
/// ===============================
#[derive(PartialEq, strum::EnumIter, strum::Display, strum::AsRefStr, strum::EnumString)]
pub enum SelectedStream {
Football,
Hockey,
Socker,
}
/// Client-specific code
#[cfg(feature = "client")]
mod client {
use super::*;
use dioxus_core::prelude::*;
use strum::IntoEnumIterator;
pub async fn app() -> anyhow::Result<()> {
Ok(dioxus_web::WebsysRenderer::start(APP).await)
}
static APP: FC<()> = |cx| {
todo!()
// let (selected_stream, set_stream) = use_state(cx, || SelectedStream::Football);
// let opts = SelectedStream::iter().map(|name| rsx! { option { "{name}", value: "{name}" } });
// cx.render(rsx! {
// div {
// h1 { "Tide basic CRUD app" }
// h2 { "Chosen stream: {selected_stream}" }
// select {
// value: {selected_stream.as_ref()}
// "{selected_stream}"
// {opts}
// }
// }
// })
};
}
/// Server-specific code
#[cfg(feature = "server")]
mod server {
use async_std::sync::RwLock;
pub use log::info;
use std::sync::Arc;
use tide::Request;
use tide_websockets::{Message, WebSocket, WebSocketConnection};
use crate::SelectedStream;
// type ServerRequest = Request<Arc<RwLock<()>>>;
type ServerRequest = Request<()>;
// type ServerRequest = Request<Arc<RwLock<ServerState>>>;
static CLIENT_PATH: &'static str = "";
pub async fn app() -> anyhow::Result<()> {
let mut app = tide::new();
app.at("")
.serve_dir(format!("{}/pkg", CLIENT_PATH))
.expect("Cannot serve directory");
app.at("/updates").get(WebSocket::new(socket_handler));
let addr = "0.0.0.0:9001";
log::info!("Congrats! Server is up and running at http://{}", addr);
app.listen(addr).await?;
Ok(())
}
async fn socket_handler(
request: ServerRequest,
stream: WebSocketConnection,
) -> tide::Result<()> {
// clone the receiver channel
// subscribe to any updates
// let receiver = request.state().read().await.receiver.clone();
// while let Ok(evt) = receiver.recv().await {
// let response_msg = serde_json::to_string(&evt)?;
// stream.send_string(response_msg).await?;
// }
Ok(())
}
use dioxus_core::prelude::*;
#[derive(PartialEq, Props)]
struct SreamListProps {
selected_stream: SelectedStream,
}
static STREAM_LIST: FC<SreamListProps> = |cx| {
//
let g = match cx.selected_stream {
SelectedStream::Football => cx.render(rsx! {
li {
"watch football!"
}
}),
SelectedStream::Hockey => cx.render(rsx! {
li {
"watch football!"
}
}),
SelectedStream::Socker => cx.render(rsx! {
li {
"watch football!"
}
}),
};
cx.render(rsx! {
div {
}
})
};
}

View file

@ -1,182 +0,0 @@
pub fn main() {
#[cfg(feature = "client")]
wasm_bindgen_futures::spawn_local(async { client::app().await.unwrap() });
#[cfg(feature = "server")]
async_std::task::block_on(async { server::app().await.expect("") });
}
/// ===============================
/// Common code (shared types)
/// ===============================
#[derive(PartialEq, strum::EnumIter, strum::Display, strum::AsRefStr, strum::EnumString)]
pub enum SelectedStream {
Football,
Hockey,
Socker,
}
/// Client-specific code
#[cfg(feature = "client")]
mod client {
use super::*;
use dioxus_core::prelude::*;
use strum::IntoEnumIterator;
pub async fn app() -> anyhow::Result<()> {
Ok(dioxus_web::WebsysRenderer::start(APP).await)
}
static APP: FC<()> = |cx| {
todo!()
// let (selected_stream, set_stream) = use_state(cx, || SelectedStream::Football);
// let opts = SelectedStream::iter().map(|name| rsx! { option { "{name}", value: "{name}" } });
// cx.render(rsx! {
// div {
// h1 { "Tide basic CRUD app" }
// h2 { "Chosen stream: {selected_stream}" }
// select {
// value: {selected_stream.as_ref()}
// "{selected_stream}"
// {opts}
// }
// }
// })
};
}
/// Server-specific code
#[cfg(feature = "server")]
mod server {
use async_std::sync::RwLock;
pub use log::info;
use std::sync::Arc;
use tide::Request;
use tide_websockets::{Message, WebSocket, WebSocketConnection};
use crate::SelectedStream;
// type ServerRequest = Request<Arc<RwLock<()>>>;
type ServerRequest = Request<()>;
// type ServerRequest = Request<Arc<RwLock<ServerState>>>;
static CLIENT_PATH: &'static str = "";
pub async fn app() -> anyhow::Result<()> {
let mut app = tide::new();
app.at("")
.serve_dir(format!("{}/pkg", CLIENT_PATH))
.expect("Cannot serve directory");
app.at("/updates").get(WebSocket::new(socket_handler));
let addr = "0.0.0.0:9001";
log::info!("Congrats! Server is up and running at http://{}", addr);
app.listen(addr).await?;
Ok(())
}
async fn socket_handler(
request: ServerRequest,
stream: WebSocketConnection,
) -> tide::Result<()> {
// clone the receiver channel
// subscribe to any updates
// let receiver = request.state().read().await.receiver.clone();
// while let Ok(evt) = receiver.recv().await {
// let response_msg = serde_json::to_string(&evt)?;
// stream.send_string(response_msg).await?;
// }
Ok(())
}
use dioxus_core::prelude::*;
#[derive(PartialEq, Props)]
struct SreamListProps {
selected_stream: SelectedStream,
}
static STREAM_LIST: FC<SreamListProps> = |cx| {
//
match cx.selected_stream {
SelectedStream::Football => cx.render(rsx! {
li {
"watch football!"
}
}),
_ => unimplemented!()
// .render(cx),
// SelectedStream::Hockey => rsx! {
// li {
// "watch football!"
// }
// }
// .render(cx),
// SelectedStream::Socker => rsx! {
// li {
// "watch football!"
// }
// }
// .render(cx),
}
};
}
mod ergorsx {
// struct Ncx {}
// struct VVNode {}
// struct DTree {
// // struct DTree<F: Fn(&Ncx) -> VVNode> {
// caller: F,
// }
// impl<F: Fn(&Ncx) -> VVNode> DTree<F> {
// fn new(f: F) -> Self {
// Self { caller: f }
// }
// }
// trait Renderable {
// fn render(self, nodecx: &Ncx) -> VVNode;
// }
// impl<F: Fn(&Ncx) -> VVNode> Renderable for DTree<F> {
// fn render(self, nodecx: &Ncx) -> VVNode {
// (self.caller)(nodecx)
// }
// }
// fn test() {
// let t = 123;
// let r = match t {
// 123 => DTree::new(|f| VVNode {}).render(cx),
// 456 => DTree::new(|f| VVNode {}).render(cx),
// 789 => DTree::new(|f| VVNode {}).render(cx),
// _ => unreachable!(),
// };
// }
// fn example() {
// rsx! {
// div {
// }
// }.render(cx)
// }
// fn example() {
// cx.render(rsx!{
// div {
// }
// })
// }
}

View file

@ -1,294 +0,0 @@
<!-- a js-only interpreter for the dioxus patch stream :) -->
<!DOCTYPE html>
<html>
<head>
<meta content="text/html;charset=utf-8" http-equiv="Content-Type" />
<meta charset="UTF-8" />
<link
href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css"
rel="stylesheet"
/>
</head>
<body>
<div></div>
</body>
<script>
class Interpreter {
constructor(root) {
this.stack = [root];
}
top() {
return this.stack[this.stack.length - 1];
}
pop() {
return this.stack.pop();
}
}
class OPTABLE {
// 0
SetText(edit, interp) {
interp.top(interp.stack).textContent = edit.text;
}
// 1
RemoveSelfAndNextSiblings(edit) {
const node = interp.pop();
let sibling = node.nextSibling;
while (sibling) {
const temp = sibling.nextSibling;
sibling.remove();
sibling = temp;
}
node.remove();
}
// 2
ReplaceWith(edit, interp) {
const newNode = interp.pop();
const oldNode = interp.pop();
oldNode.replaceWith(newNode);
interp.stack.push(newNode);
}
// 3
SetAttribute(edit, interp) {
const name = edit.name;
const value = edit.value;
const node = interp.top(interp.stack);
node.setAttribute(name, value);
// Some attributes are "volatile" and don't work through `setAttribute`.
if ((name === "value", interp)) {
node.value = value;
}
if ((name === "checked", interp)) {
node.checked = true;
}
if ((name === "selected", interp)) {
node.selected = true;
}
}
// 4
RemoveAttribute(edit, interp) {
const name = edit.name;
const node = interp.top(interp.stack);
node.removeAttribute(name);
// Some attributes are "volatile" and don't work through `removeAttribute`.
if ((name === "value", interp)) {
node.value = null;
}
if ((name === "checked", interp)) {
node.checked = false;
}
if ((name === "selected", interp)) {
node.selected = false;
}
}
// 5
PushReverseChild(edit, interp) {
const n = edit.n;
const parent = interp.top(interp.stack);
const children = parent.childNodes;
const child = children[children.length - n - 1];
interp.stack.push(child);
}
// 6
PopPushChild(edit, interp) {
const n = edit.n;
interp.pop();
const parent = interp.top(interp.stack);
const children = parent.childNodes;
const child = children[n];
interp.stack.push(child);
}
// 7
Pop(edit, interp) {
interp.pop();
}
// 8
AppendChild(edit, interp) {
console.log(interp.stack);
const child = interp.pop();
console.log(interp.stack);
interp.top().appendChild(child);
}
// 9
CreateTextNode(edit, interp) {
// interp.stack.push(document.createTextNode("asd"));
console.log(interp.stack);
interp.stack.push(document.createTextNode(edit.text));
console.log(interp.stack);
}
// 10
CreateElement(edit, interp) {
const tagName = edit.tag_name;
interp.stack.push(document.createElement(tagName));
}
// 11
NewEventListener(edit, interp) {
// todo!
const eventId = mem32[i++];
const eventType = interp.getCachedString(eventId);
const a = mem32[i++];
const b = mem32[i++];
const el = interp.top(interp.stack);
el.addEventListener(eventType, interp.eventHandler);
el[`dodrio-a-${eventType}`] = a;
el[`dodrio-b-${eventType}`] = b;
}
// 12
UpdateEventListener(edit, interp) {
// todo!
const eventId = mem32[i++];
const eventType = interp.getCachedString(eventId);
const el = interp.top(interp.stack);
el[`dodrio-a-${eventType}`] = mem32[i++];
el[`dodrio-b-${eventType}`] = mem32[i++];
}
// 13
RemoveEventListener(edit, interp) {
// todo!
const eventId = mem32[i++];
const eventType = interp.getCachedString(eventId);
const el = interp.top(interp.stack);
el.removeEventListener(eventType, interp.eventHandler);
}
// 14
AddCachedString(edit, interp) {
// todo!
const pointer = mem32[i++];
const length = mem32[i++];
const id = mem32[i++];
const str = string(mem8, pointer, length);
interp.addCachedString(str, id);
}
// 15
DropCachedString(edit, interp) {
// todo!
const id = mem32[i++];
interp.dropCachedString(id);
}
// 16
CreateElementNs(edit, interp) {
// const tagNameId = mem32[i++];
// const tagName = interp.getCachedString(tagNameId);
// const nsId = mem32[i++];
// const ns = interp.getCachedString(nsId);
interp.stack.push(document.createElementNS(edit.ns, edit.tag_name));
}
// 17
SaveChildrenToTemporaries(edit, interp) {
// let temp = mem32[i++];
// const start = mem32[i++];
// const end = mem32[i++];
let temp = edit.temp;
const start = edit.start;
const end = edit.end;
const parent = interp.top(interp.stack);
const children = parent.childNodes;
for (let i = start; i < end; i++, interp) {
interp.temporaries[temp++] = children[i];
}
}
// 18
PushChild(edit, interp) {
const parent = interp.top(interp.stack);
// const n = mem32[i++];
const n = edit.n;
const child = parent.childNodes[n];
interp.stack.push(child);
}
// 19
PushTemporary(edit, interp) {
// const temp = mem32[i++];
const temp = edit.temp;
interp.stack.push(interp.temporaries[temp]);
}
// 20
InsertBefore(edit, interp) {
const before = interp.pop();
const after = interp.pop();
after.parentNode.insertBefore(before, after);
interp.stack.push(before);
}
// 21
PopPushReverseChild(edit, interp) {
// const n = mem32[i++];
const n = edit.n;
interp.pop();
const parent = interp.top(interp.stack);
const children = parent.childNodes;
const child = children[children.length - n - 1];
interp.stack.push(child);
}
// 22
RemoveChild(edit, interp) {
// const n = mem32[i++];
const n = edit.n;
const parent = interp.top(interp.stack);
const child = parent.childNodes[n];
child.remove();
}
// 23
SetClass(edit, interp) {
// const classId = mem32[i++];
const className = edit.class_name;
interp.top(interp.stack).className = className;
}
// 24
SaveTemplate(edit, interp) {
const id = mem32[i++];
const template = interp.top(interp.stack);
interp.saveTemplate(id, template.cloneNode(true));
}
// 25
PushTemplate(edit, interp) {
const id = mem32[i++];
const template = interp.getTemplate(id);
interp.stack.push(template.cloneNode(true));
}
}
const op_table = new OPTABLE();
const interpreter = new Interpreter(window.document.body);
function EditListReceived(rawEditList) {
let editList = JSON.parse(rawEditList);
console.warn("hnelllo");
editList.forEach(function (edit, index) {
console.log(edit);
op_table[edit.type](edit, interpreter);
});
}
external.invoke("initiate");
</script>
</html>

View file

@ -1 +0,0 @@
//! Request handler for actix

View file

@ -1 +0,0 @@
//! Request handler for rocket

View file

@ -1 +0,0 @@
//! Request handler for tide

View file

@ -1 +0,0 @@
//! Request handler for warp

View file

@ -1,10 +0,0 @@
mod handlers {
pub mod h_actix;
pub mod h_rocket;
pub mod h_tide;
pub mod h_warp;
}

View file

@ -1,9 +0,0 @@
[package]
name = "dioxus-router"
version = "0.0.0"
authors = ["Jonathan Kelley <jkelleyrtp@gmail.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View file

@ -1,40 +0,0 @@
# Router hook for Dioxus apps
Dioxus-router provides a use_router hook that returns a different value depending on the route.
The router is generic over any value, however it makes sense to return a different set of VNodes
and feed them into the App's return VNodes.
Using the router should feel similar to tide's routing framework where an "address" book is assembled at the head of the app.
Here's an example of how to use the router hook:
```rust
static App: FC<()> = |cx| {
// Route returns the associated VNodes
// This hook re-fires when the route changes
let route = use_router(cx, |router| {
router.at("/").get(|path| {
rsx!{ <LandingPage /> }
});
router.at("/shoes/:id").get(|path| {
let id: Uuid = path.parse().unwrap();
rsx!{ <ShoesPage id=id /> }
});
router.at("/pants/:id").get(|path| {
let id: Uuid = path.parse().unwrap();
rsx!{ <PantsPage id=id /> }
});
});
cx.render(rsx!{
div {
Navbar {}
{route}
Footer {}
}
})
};
```
Currently, the router is only supported in a web environment, but we plan to add 1st-party support via the context API when new renderers are available.

View file

@ -1,7 +0,0 @@
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
}

View file

@ -218,10 +218,3 @@ pub mod testing {
}
#[cfg(feature = "atoms")]
pub mod atoms {}
// #[cfg(feature = "desktop")]
pub mod webview {
//! A webview based renderer for building desktop applications with Dioxus
use dioxus_core::prelude::{Properties, FC};
pub fn launch<P: Properties>(f: FC<P>) {}
}