#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate tablestream;
#[macro_use]
extern crate tracing;
use std::fs::File;
use std::io::{Read, Write};
use std::io;
use std::path::Path;
use std::str::FromStr;
use std::sync::Arc;
use clap::{App, Arg, ArgMatches, SubCommand};
use starlane_core::error::Error;
use tracing_subscriber::FmtSubscriber;
use tracing::dispatcher::set_global_default;
use tokio::runtime::Runtime;
use starlane_core::starlane::StarlaneMachine;
use starlane_core::template::ConstellationLayout;
use starlane_core::util::shutdown;
use starlane_core::util;
use starlane_core::starlane::api::StarlaneApi;
use std::convert::TryInto;
use mesh_portal::version::latest::entity::request::create::Require;
use mesh_portal::version::latest::id::Address;
use tokio::io::AsyncReadExt;
use tracing::error;
use starlane_core::command::cli::{CliClient, outlet};
use starlane_core::command::cli::outlet::Frame;
use starlane_core::command::compose::CommandOp;
use starlane_core::command::parse::{command_line, rec_script_line};
use starlane_core::mechtron::portal_client::launch_mechtron_client;
use starlane_core::mechtron::process::launch_mechtron_process;
use starlane_core::star::shell::sys::SysCall::Create;
pub mod cli;
pub mod resource;
fn main() -> Result<(), Error> {
let rt = Runtime::new().unwrap();
rt.block_on( async move { go().await });
Ok(())
}
async fn go() -> Result<(),Error> {
let subscriber = FmtSubscriber::default();
set_global_default(subscriber.into()).expect("setting global default tracer failed");
ctrlc::set_handler(move || {
std::process::exit(1);
})
.expect("expected to be able to set ctrl-c handler");
let mut clap_app = App::new("Starlane")
.version("0.2.0")
.author("Scott Williams <scott@mightydevco.com>")
.about("A Resource Mesh").subcommands(vec![SubCommand::with_name("serve").usage("serve a starlane machine instance").arg(Arg::with_name("with-external").long("with-external").takes_value(false).required(false).default_value("false")).display_order(0),
SubCommand::with_name("config").subcommands(vec![SubCommand::with_name("set-shell").usage("set the shell that the starlane CLI connects to").arg(Arg::with_name("hostname").required(true).help("the hostname of the starlane instance you wish to connect to")).display_order(0),
SubCommand::with_name("get-shell").usage("get the shell that the starlane CLI connects to")]).usage("read or manipulate the cli config").display_order(1).display_order(1),
SubCommand::with_name("exec").usage("execute a command").args(vec![Arg::with_name("command_line").required(true).help("command line to execute")].as_slice()),
SubCommand::with_name("script").usage("execute commands in a script").args(vec![Arg::with_name("script_file").required(true).help("the script file to execute")].as_slice()),
SubCommand::with_name("mechtron").usage("launch a mechtron portal client").args(vec![Arg::with_name("server").required(true).help("the portal server to connect to"),
Arg::with_name("wasm_src").required(true).help("the address of the wasm source"),
].as_slice()),
]);
let matches = clap_app.clone().get_matches();
if let Option::Some(serve) = matches.subcommand_matches("serve") {
let starlane = StarlaneMachine::new("server".to_string()).expect("StarlaneMachine server");
let layout = match serve.value_of("with-external") {
Some(value) => {
match bool::from_str(value ) {
Ok(true) => {
ConstellationLayout::standalone_with_external().expect("standalone_with_external")
}
_ =>{
ConstellationLayout::standalone().expect("standalone")
}
}
},
None=> ConstellationLayout::standalone().expect("standalone")
};
starlane
.create_constellation("standalone", layout)
.await
.expect("constellation");
starlane.listen().await.expect("expected listen to work");
starlane.join().await;
} else if let Option::Some(matches) = matches.subcommand_matches("config") {
if let Option::Some(_) = matches.subcommand_matches("get-shell") {
let config = crate::cli::CLI_CONFIG.lock()?;
println!("{}", config.hostname);
} else if let Option::Some(args) = matches.subcommand_matches("set-shell") {
let mut config = crate::cli::CLI_CONFIG.lock()?;
config.hostname = args
.value_of("hostname")
.ok_or("expected hostname")?
.to_string();
config.save()?;
} else {
clap_app.print_long_help().unwrap_or_default();
}
} else if let Option::Some(args) = matches.subcommand_matches("exec") {
exec(args.clone()).await.unwrap();
} else if let Option::Some(args) = matches.subcommand_matches("script") {
match script(args.clone()).await {
Ok(_) => {
println!("Script OK");
}
Err(err) => {
eprintln!("Script Error {}", err.to_string() );
}
}
} else if let Option::Some(args) = matches.subcommand_matches("mechtron") {
mechtron(args.clone()).await?;
} else {
clap_app.print_long_help().unwrap_or_default();
}
Ok(())
}
async fn exec_command_line(client: CliClient, line: String) -> Result<(CliClient,i32), Error> {
let op = CommandOp::from_str(line.as_str() )?;
let requires = op.requires();
let mut exchange = client.send(line).await?;
for require in requires {
match require {
Require::File(name) => {
println!("transferring: '{}'",name.as_str());
match File::open(name.clone() ) {
Ok(mut file) => {
let mut buf = vec![];
file.read_to_end(&mut buf)?;
let bin = Arc::new(buf);
exchange.file( name, bin).await?;
}
Err(err) => {
error!("{}",err.to_string())
}
}
}
}
}
exchange.end_requires().await?;
while let Option::Some(Ok(frame)) = exchange.read().await {
match frame {
outlet::Frame::StdOut(line) => {
println!("{}", line);
}
outlet::Frame::StdErr(line) => {
eprintln!("{}", line);
}
outlet::Frame::EndOfCommand(code) => {
return Ok((exchange.into(), code) );
}
}
}
Err("client disconnected unexpect".into())
}
async fn exec(args: ArgMatches<'_>) -> Result<(), Error> {
let mut client = client().await?;
let line = args.value_of("command_line").ok_or("expected command line")?.to_string();
let (_,code) = exec_command_line(client,line).await?;
std::process::exit(code);
Ok(())
}
async fn script(args: ArgMatches<'_>) -> Result<(), Error> {
let mut client = client().await?;
let script_file = args.value_of("script_file").ok_or("expected script filename")?.to_string();
let mut file = File::open(script_file ).unwrap();
let mut buf = vec![];
file.read_to_end(&mut buf)?;
let mut script = String::from_utf8(buf)?;
loop {
let (next,line) = rec_script_line(script.as_str() )?;
println!("{}",line);
let (c,code) = exec_command_line(client, line.to_string() ).await?;
client = c;
if code != 0 {
std::process::exit(code);
}
script = next.to_string();
if script.is_empty() {
break;
}
}
std::process::exit(0);
}
async fn mechtron(args: ArgMatches<'_>) -> Result<(), Error> {
println!("Staring starlane mechtron process");
async fn launch(args: ArgMatches<'_>) -> Result<(), Error> {
let server = args.value_of("server").ok_or("expected server hostname")?.to_string();
let wasm_src = args.value_of("wasm_src").ok_or("expected Wasm source")?.to_string();
let wasm_src = Address::from_str(wasm_src.as_str())?;
launch_mechtron_client( server, wasm_src ).await?;
Ok(())
}
match launch(args).await {
Ok(_) => {
std::process::exit(0);
}
Err(err) => {
eprintln!("client launch error: {}",err.to_string());
std::process::exit(1);
}
}
}
pub async fn client() -> Result<CliClient, Error> {
let host = {
let config = crate::cli::CLI_CONFIG.lock()?;
config.hostname.clone()
};
CliClient::new(host).await
}