Skip to content

Commit

Permalink
add flags docs and stuff
Browse files Browse the repository at this point in the history
  • Loading branch information
Jacherr committed Jun 23, 2024
1 parent 4f68142 commit 9513c69
Show file tree
Hide file tree
Showing 10 changed files with 245 additions and 71 deletions.
2 changes: 2 additions & 0 deletions assyst-common/src/markdown.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,8 @@ pub fn parse_codeblock(input: String) -> String {
let new = r.split(" ").skip(1).collect::<Vec<_>>();
let joined = new.join(" ");
joined[..joined.len() - 3].to_owned()
} else if input.trim().starts_with("`") && input.trim().ends_with("`") {
input[1..input.len() - 1].to_owned()
} else {
input
}
Expand Down
2 changes: 2 additions & 0 deletions assyst-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,5 @@ rand = "0.8.5"
assyst-tag = { path = "../assyst-tag" }
urlencoding = "2.1.3"
twilight-util = { version = "=0.16.0-rc.1", features = ["builder"] }
dash_vm = { git = "https://github.com/y21/dash" }
dash_rt = { git = "https://github.com/y21/dash" }
14 changes: 14 additions & 0 deletions assyst-core/src/command/flags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ flag_parse_argument! { RustFlags }
#[derive(Default)]
pub struct LangFlags {
pub verbose: bool,
pub llir: bool,
pub opt: u64,
}
impl FlagDecode for LangFlags {
fn from_str(input: &str) -> anyhow::Result<Self>
Expand All @@ -89,10 +91,22 @@ impl FlagDecode for LangFlags {
{
let mut valid_flags = HashMap::new();
valid_flags.insert("verbose", FlagType::NoValue);
valid_flags.insert("llir", FlagType::NoValue);
valid_flags.insert("opt", FlagType::WithValue);

let raw_decode = flags_from_str(input, valid_flags)?;
let opt = raw_decode
.get("opt")
.map(|x| x.as_deref())
.flatten()
.map(|x| x.parse::<u64>())
.unwrap_or(Ok(0))
.context("Failed to parse optimisation level")?;

let result = Self {
verbose: raw_decode.get("verbose").is_some(),
llir: raw_decode.get("llir").is_some(),
opt,
};

Ok(result)
Expand Down
7 changes: 4 additions & 3 deletions assyst-core/src/command/group.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ macro_rules! define_commandgroup {
#[::async_trait::async_trait]
impl crate::command::Command for [<$groupname _command>] {
fn metadata(&self) -> &'static crate::command::CommandMetadata {
static META: crate::command::CommandMetadata = crate::command::CommandMetadata {
static META: std::sync::OnceLock<crate::command::CommandMetadata> = std::sync::OnceLock::new();
META.get_or_init(|| crate::command::CommandMetadata {
access: $crate::defaults!(access $($access)?),
category: $category,
aliases: $crate::defaults!(aliases $(&$aliases)?),
Expand All @@ -72,8 +73,8 @@ macro_rules! define_commandgroup {
age_restricted: $crate::defaults!(age_restricted $($age_restricted)?),
usage: $crate::defaults!(usage $($usage)?),
send_processing: $crate::defaults!(send_processing $($send_processing)?),
};
&META
flag_descriptions: std::collections::HashMap::new()
})
}

fn subcommands(&self) -> Option<&'static [(&'static str, crate::command::TCommand)]> {
Expand Down
59 changes: 40 additions & 19 deletions assyst-core/src/command/misc/help.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,10 @@ pub async fn help(ctxt: CommandCtxt<'_>, labels: Vec<Word>) -> anyhow::Result<()
if let Some(Word(base_command)) = labels.next() {
// if the base is a command
if let Some(mut command) = find_command_by_name(&base_command) {
let mut meta = command.metadata();

let mut usage = format!("{}{}", "Usage: ".fg_yellow(), ctxt.data.calling_prefix);
let mut name_fmt = meta.name.to_owned();

// For better error reporting, store the "chain of commands" (e.g. `-t create`)
let mut command_chain = command.metadata().name.to_owned();
Expand Down Expand Up @@ -74,13 +77,45 @@ pub async fn help(ctxt: CommandCtxt<'_>, labels: Vec<Word>) -> anyhow::Result<()
command_chain += " ";
command_chain += command.metadata().name;
}
usage += command.metadata().name;

meta = command.metadata();

usage += meta.name;
usage += " ";
usage += command.metadata().usage;
usage += meta.usage;

let meta = command.metadata();
name_fmt += " ";
name_fmt += meta.name;

let flags_format = if !meta.flag_descriptions.is_empty() {
format!(
"\n{}",
meta.flag_descriptions
.iter()
.map(|(x, y)| { format!("--{}: {}", x, y) })
.collect::<Vec<_>>()
.join("\n")
)
} else {
"None".to_owned()
};
let flags = "Flags:".fg_cyan() + &flags_format;

let name_fmt = (meta.name.to_owned() + ":").fg_green();
let examples_format = if !meta.examples.is_empty() {
format!(
"\n{}",
meta.examples
.iter()
.map(|x| { format!("{}{} {}", ctxt.data.calling_prefix, name_fmt, x) })
.collect::<Vec<_>>()
.join("\n")
)
} else {
"None".to_owned()
};
let examples = "Examples: ".fg_cyan() + &examples_format;

name_fmt = (name_fmt.to_owned() + ":").fg_green();
let description = meta.description;
let aliases = "Aliases: ".fg_yellow()
+ &(if !meta.aliases.is_empty() {
Expand All @@ -100,23 +135,9 @@ pub async fn help(ctxt: CommandCtxt<'_>, labels: Vec<Word>) -> anyhow::Result<()
String::new()
};

let examples_format = if !meta.examples.is_empty() {
format!(
"\n{}",
meta.examples
.iter()
.map(|x| { format!("{}{} {}", ctxt.data.calling_prefix, meta.name, x) })
.collect::<Vec<_>>()
.join("\n")
)
} else {
"None".to_owned()
};
let examples = "Examples: ".fg_cyan() + &examples_format;

ctxt.reply(
format!(
"{name_fmt} {description}\n\n{aliases}\n{cooldown}\n{access}\n{usage}{subcommands}\n\n{examples}"
"{name_fmt} {description}\n\n{aliases}\n{cooldown}\n{access}\n{usage}{subcommands}\n\n{examples}\n\n{flags}"
)
.trim()
.codeblock("ansi"),
Expand Down
190 changes: 151 additions & 39 deletions assyst-core/src/command/misc/run.rs
Original file line number Diff line number Diff line change
@@ -1,69 +1,132 @@
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use std::process::{ExitCode, ExitStatus};
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};

use anyhow::bail;
use assyst_common::markdown::Markdown;
use assyst_common::util::process::{exec_sync, CommandOutput};
use assyst_proc_macro::command;
use dash_rt::format_value;
use dash_vm::eval::EvalError;
use dash_vm::value::Root;
use dash_vm::Vm;

use crate::command::arguments::Codeblock;
use crate::command::flags::{LangFlags, RustFlags};
use crate::command::messagebuilder::{Attachment, MessageBuilder};
use crate::command::{Availability, Category, CommandCtxt};
use crate::define_commandgroup;
use crate::rest::rust::{run_benchmark, run_binary, run_clippy, run_godbolt, run_miri, OptimizationLevel};

/*
struct TempDrop(String);
impl Drop for TempDrop {
fn drop(&mut self) {
let _ = std::fs::remove_dir_all(self.0.clone());
}
}*/

#[command(
description = "execute some lang",
cooldown = Duration::from_millis(100),
access = Availability::Dev,
category = Category::Misc,
usage = "[script] <flags: --verbose>",
usage = "[script] <flags>",
examples = ["1"],
send_processing = true
send_processing = true,
flag_descriptions = [
("verbose", "Get verbose output"),
("llir", "Output LLVM IR"),
("opt [level:0|1|2|3]", "Set optimisation level of LLVM")
]
)]
pub async fn lang(ctxt: CommandCtxt<'_>, script: Codeblock, flags: LangFlags) -> anyhow::Result<()> {
let dir = format!(
"/tmp/lang/{}",
SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis()
);
let dir = "/tmp/lang".to_owned();

exec_sync(&format!("git clone https://github.com/y21/lang.git {dir}"))?;
std::fs::write(format!("{dir}/input"), script.0)?;
//#[allow(unused)]
//let dir_temp_drop = TempDrop(dir.clone());

if std::fs::metadata(format!("{dir}/.git")).is_err() {
std::fs::remove_dir_all(&dir)?;
exec_sync(&format!("git clone https://github.com/y21/lang.git {dir} --depth=0"))?;
};

exec_sync(&format!("cd {dir} && git pull"))?;
std::fs::write(format!("{dir}/input"), script.0)?;
exec_sync(&format!("cd {dir} && npm i --save-dev @types/node && tsc"))?;
let result = exec_sync(&format!(
"cd {dir} && node . input {}",
if flags.verbose { "--verbose" } else { "" }
))?;

let bin_result = if std::fs::metadata(format!("{dir}/a.out")).is_ok() {
Some(exec_sync(&format!("cd {dir} && ./a.out"))?)
} else {
None
};
let commit_hash = exec_sync(&format!("cd {dir} && git rev-parse HEAD"))
.map(|x| x.stdout[..8].to_owned())
.unwrap_or("Unknown".to_owned());

let stdout = result.stdout + "\n" + &bin_result.clone().unwrap_or(CommandOutput::default()).stdout;
let stderr = result.stderr + "\n" + &bin_result.clone().unwrap_or(CommandOutput::default()).stderr;
let mut flags_string = String::new();
if flags.verbose {
flags_string += "--verbose"
};

let mut output = "".to_owned();
if !stdout.trim().is_empty() {
output = format!("`stdout`: ```ansi\n{}```\n", stdout);
if flags.llir {
flags_string += " --print-llir-only --no-timings"
}

if !stderr.trim().is_empty() {
output = format!("{}`stderr`: ```ansi\n{}```", output, stderr);
}
output.push_str(&format!(
"\nCompiler: {}\nExecutable: {}",
result.exit_code,
if let Some(b) = bin_result {
b.exit_code.to_string()
flags_string += &format!(" -O{}", flags.opt);

let result = exec_sync(&format!("cd {dir} && node . input {}", flags_string.trim()))?;

if !flags.llir {
let bin_start = Instant::now();
let bin_result = if std::fs::metadata(format!("{dir}/a.out")).is_ok() {
Some(exec_sync(&format!("cd {dir} && ./a.out"))?)
} else {
"N/A".to_owned()
None
};
let bin_time = bin_start.elapsed();

let stdout = result.stdout + "\n" + &bin_result.clone().unwrap_or(CommandOutput::default()).stdout;
let stderr = result.stderr + "\n" + &bin_result.clone().unwrap_or(CommandOutput::default()).stderr;

let mut output = "".to_owned();
if !stdout.trim().is_empty() {
output = format!("`stdout`: {}\n", stdout.codeblock("ansi"));
}
));

// todo: delete `dir`
if !stderr.trim().is_empty() {
output = format!("{}`stderr`: {}\n", output, stderr.codeblock("ansi"));
}

ctxt.reply(output).await?;
output.push_str(&format!(
"\nCompiler: {}\nExecutable: {}\nCommit Hash: {commit_hash}",
result.exit_code,
if let Some(b) = bin_result {
format!("{} (execution time {:?})", b.exit_code.to_string(), bin_time)
} else {
"N/A".to_owned()
}
));

ctxt.reply(output).await?;
} else {
let stdout = result.stdout;
let stderr = result.stderr;

if result.exit_code.code() != Some(0) {
ctxt.reply(format!("Compilation failed: {}", stderr.codeblock("")))
.await?;
} else {
if stdout.split("\n").count() < 100 {
ctxt.reply(format!("{}", stdout.codeblock("llvm"))).await?;
} else {
ctxt.reply(MessageBuilder {
content: None,
attachment: Some(Attachment {
name: "out.txt".into(),
data: stdout.as_bytes().to_vec(),
}),
})
.await?;
}
}
}

// todo: delete `dir`

Ok(())
}
Expand All @@ -73,9 +136,16 @@ pub async fn lang(ctxt: CommandCtxt<'_>, script: Codeblock, flags: LangFlags) ->
cooldown = Duration::from_millis(100),
access = Availability::Public,
category = Category::Misc,
usage = "[script] <flags: --miri|--asm|--clippy|--bench|--release>",
examples = ["println!(\"Hello World!\")"],
send_processing = true
usage = "[script] <flags>",
examples = ["println!(\"Hello, world!\")"],
send_processing = true,
flag_descriptions = [
("miri", "Run code in miri debugger"),
("asm", "Output ASM of Rust code"),
("clippy", "Lint code using Clippy"),
("bench", "Run code as a benchmark"),
("release", "Run code in release mode")
]
)]
pub async fn rust(ctxt: CommandCtxt<'_>, script: Codeblock, flags: RustFlags) -> anyhow::Result<()> {
let opt = if flags.release {
Expand All @@ -99,6 +169,47 @@ pub async fn rust(ctxt: CommandCtxt<'_>, script: Codeblock, flags: RustFlags) ->
ctxt.reply(result.format().codeblock("rs")).await
}

#[command(
description = "execute some dash",
cooldown = Duration::from_millis(100),
access = Availability::Dev,
category = Category::Misc,
usage = "[script]",
examples = ["\"Hello, world!\""],
send_processing = true
)]
pub async fn dash(ctxt: CommandCtxt<'_>, script: Codeblock) -> anyhow::Result<()> {
let str_result = {
let mut vm = Vm::new(Default::default());
let result = vm.eval(&script.0, Default::default());
let mut scope = vm.scope();
match result {
Ok(result) => {
let fmt = format_value(result.root(&mut scope), &mut scope);
if let Ok(f) = fmt {
f.to_string()
} else {
format!("{:?}", fmt.unwrap_err())
}
},
Err(err) => match err {
EvalError::Exception(unrooted) => {
let fmt = format_value(unrooted.root(&mut scope), &mut scope);
if let Ok(f) = fmt {
format!("Exception: {}", f.to_string())
} else {
format!("Exception: {:?}", fmt.unwrap_err())
}
},
EvalError::Middle(middle) => format!("Middle error: {:?}", middle),
},
}
};

ctxt.reply(str_result).await?;
Ok(())
}

define_commandgroup! {
name: run,
access: Availability::Public,
Expand All @@ -107,6 +218,7 @@ define_commandgroup! {
usage: "[language/runtime] [code] <...flags>",
commands: [
"lang" => lang,
"rust" => rust
"rust" => rust,
"dash" => dash
]
}
Loading

0 comments on commit 9513c69

Please sign in to comment.