Merge pull request #184 from jonathandturner/pixel_ascii

Fix plugin loading and fix rendering for binary
This commit is contained in:
Jonathan Turner 2019-07-16 19:38:46 +12:00 committed by GitHub
commit 3d28b50a53
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 189 additions and 75 deletions

View file

@ -6,6 +6,7 @@ use crate::commands::classified::{
}; };
use crate::commands::command::sink; use crate::commands::command::sink;
use crate::commands::plugin::JsonRpc; use crate::commands::plugin::JsonRpc;
use crate::commands::plugin::{PluginCommand, PluginSink};
use crate::context::Context; use crate::context::Context;
crate use crate::errors::ShellError; crate use crate::errors::ShellError;
use crate::evaluate::Scope; use crate::evaluate::Scope;
@ -43,8 +44,6 @@ impl<T> MaybeOwned<'a, T> {
} }
fn load_plugin(path: &std::path::Path, context: &mut Context) -> Result<(), ShellError> { fn load_plugin(path: &std::path::Path, context: &mut Context) -> Result<(), ShellError> {
use crate::commands::{command, plugin};
let mut child = std::process::Command::new(path) let mut child = std::process::Command::new(path)
.stdin(std::process::Stdio::piped()) .stdin(std::process::Stdio::piped())
.stdout(std::process::Stdio::piped()) .stdout(std::process::Stdio::piped())
@ -70,20 +69,17 @@ fn load_plugin(path: &std::path::Path, context: &mut Context) -> Result<(), Shel
Ok(jrpc) => match jrpc.params { Ok(jrpc) => match jrpc.params {
Ok(params) => { Ok(params) => {
let fname = path.to_string_lossy(); let fname = path.to_string_lossy();
//println!("Loaded: {} from {}", params.name, fname);
if params.is_filter { if params.is_filter {
let fname = fname.to_string(); let fname = fname.to_string();
context.add_commands(vec![command( let name = params.name.clone();
&params.name, context.add_commands(vec![Arc::new(PluginCommand::new(
Box::new(move |x| plugin::filter_plugin(fname.clone(), x)), name, fname, params,
)]); ))]);
Ok(()) Ok(())
} else if params.is_sink { } else if params.is_sink {
let fname = fname.to_string(); let fname = fname.to_string();
context.add_sinks(vec![sink( let name = params.name.clone();
&params.name, context.add_sinks(vec![Arc::new(PluginSink::new(name, fname, params))]);
Box::new(move |x| plugin::sink_plugin(fname.clone(), x)),
)]);
Ok(()) Ok(())
} else { } else {
Ok(()) Ok(())

View file

@ -1,4 +1,5 @@
#[doc(hidden)] #[doc(hidden)]
#[allow(unused)]
macro_rules! named_type { macro_rules! named_type {
($name:ident) => { ($name:ident) => {
$crate::parser::registry::NamedType::$($name)* $crate::parser::registry::NamedType::$($name)*

View file

@ -1,6 +1,7 @@
use crate::commands::command::SinkCommandArgs;
use crate::errors::ShellError; use crate::errors::ShellError;
use crate::parser::registry;
use crate::prelude::*; use crate::prelude::*;
use derive_new::new;
use serde::{self, Deserialize, Serialize}; use serde::{self, Deserialize, Serialize};
use std::io::prelude::*; use std::io::prelude::*;
use std::io::BufReader; use std::io::BufReader;
@ -32,6 +33,44 @@ pub enum NuResult {
}, },
} }
#[derive(new)]
pub struct PluginCommand {
name: String,
path: String,
config: registry::CommandConfig,
}
impl Command for PluginCommand {
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
filter_plugin(self.path.clone(), args)
}
fn name(&self) -> &str {
&self.name
}
fn config(&self) -> registry::CommandConfig {
self.config.clone()
}
}
#[derive(new)]
pub struct PluginSink {
name: String,
path: String,
config: registry::CommandConfig,
}
impl Sink for PluginSink {
fn run(&self, args: SinkCommandArgs) -> Result<(), ShellError> {
sink_plugin(self.path.clone(), args)
}
fn name(&self) -> &str {
&self.name
}
fn config(&self) -> registry::CommandConfig {
self.config.clone()
}
}
pub fn filter_plugin(path: String, args: CommandArgs) -> Result<OutputStream, ShellError> { pub fn filter_plugin(path: String, args: CommandArgs) -> Result<OutputStream, ShellError> {
let mut child = std::process::Command::new(path) let mut child = std::process::Command::new(path)
.stdin(std::process::Stdio::piped()) .stdin(std::process::Stdio::piped())

View file

@ -54,9 +54,6 @@ impl ExtractType for Spanned<Value> {
} }
} }
#[derive(Debug)]
pub struct FilePath;
impl ExtractType for std::path::PathBuf { impl ExtractType for std::path::PathBuf {
fn syntax_type() -> hir::SyntaxType { fn syntax_type() -> hir::SyntaxType {
hir::SyntaxType::Path hir::SyntaxType::Path
@ -66,7 +63,7 @@ impl ExtractType for std::path::PathBuf {
match &value { match &value {
Spanned { Spanned {
item: Value::Primitive(Primitive::String(p)), item: Value::Primitive(Primitive::String(p)),
span, ..
} => Ok(PathBuf::from(p)), } => Ok(PathBuf::from(p)),
other => Err(ShellError::type_error("Path", other.spanned_type_name())), other => Err(ShellError::type_error("Path", other.spanned_type_name())),
} }

View file

@ -21,7 +21,7 @@ pub fn baseline_parse_token_as_string(token: &Token, source: &Text) -> hir::Expr
} }
RawToken::Variable(span) => hir::Expression::variable(span, token.span), RawToken::Variable(span) => hir::Expression::variable(span, token.span),
RawToken::Integer(_) => hir::Expression::bare(token.span), RawToken::Integer(_) => hir::Expression::bare(token.span),
RawToken::Size(int, unit) => hir::Expression::bare(token.span), RawToken::Size(_, _) => hir::Expression::bare(token.span),
RawToken::Bare => hir::Expression::bare(token.span), RawToken::Bare => hir::Expression::bare(token.span),
RawToken::String(span) => hir::Expression::string(span, token.span), RawToken::String(span) => hir::Expression::string(span, token.span),
} }

View file

@ -102,15 +102,15 @@ impl TokenNode {
pub fn type_name(&self) -> String { pub fn type_name(&self) -> String {
match self { match self {
TokenNode::Token(t) => t.type_name(), TokenNode::Token(t) => t.type_name(),
TokenNode::Call(s) => "command", TokenNode::Call(_) => "command",
TokenNode::Delimited(d) => d.type_name(), TokenNode::Delimited(d) => d.type_name(),
TokenNode::Pipeline(s) => "pipeline", TokenNode::Pipeline(_) => "pipeline",
TokenNode::Operator(s) => "operator", TokenNode::Operator(_) => "operator",
TokenNode::Flag(s) => "flag", TokenNode::Flag(_) => "flag",
TokenNode::Member(s) => "member", TokenNode::Member(_) => "member",
TokenNode::Whitespace(s) => "whitespace", TokenNode::Whitespace(_) => "whitespace",
TokenNode::Error(s) => "error", TokenNode::Error(_) => "error",
TokenNode::Path(s) => "path", TokenNode::Path(_) => "path",
} }
.to_string() .to_string()
} }

View file

@ -9,7 +9,7 @@ use serde::{Deserialize, Serialize};
use std::fmt; use std::fmt;
#[allow(unused)] #[allow(unused)]
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize, Clone)]
pub enum NamedType { pub enum NamedType {
Switch, Switch,
Mandatory(SyntaxType), Mandatory(SyntaxType),
@ -32,10 +32,19 @@ impl PositionalType {
PositionalType::Mandatory(name.to_string(), SyntaxType::Any) PositionalType::Mandatory(name.to_string(), SyntaxType::Any)
} }
pub fn optional(name: &str, ty: SyntaxType) -> PositionalType {
PositionalType::Optional(name.to_string(), ty)
}
pub fn optional_any(name: &str) -> PositionalType {
PositionalType::Optional(name.to_string(), SyntaxType::Any)
}
pub fn mandatory_block(name: &str) -> PositionalType { pub fn mandatory_block(name: &str) -> PositionalType {
PositionalType::Mandatory(name.to_string(), SyntaxType::Block) PositionalType::Mandatory(name.to_string(), SyntaxType::Block)
} }
#[allow(unused)]
crate fn to_coerce_hint(&self) -> Option<SyntaxType> { crate fn to_coerce_hint(&self) -> Option<SyntaxType> {
match self { match self {
PositionalType::Mandatory(_, SyntaxType::Block) PositionalType::Mandatory(_, SyntaxType::Block)
@ -59,7 +68,7 @@ impl PositionalType {
} }
} }
#[derive(Debug, Getters, Serialize, Deserialize)] #[derive(Debug, Getters, Serialize, Deserialize, Clone)]
#[get = "crate"] #[get = "crate"]
pub struct CommandConfig { pub struct CommandConfig {
pub name: String, pub name: String,

View file

@ -1,6 +1,6 @@
use crossterm::{cursor, terminal, Attribute, RawScreen}; use crossterm::{cursor, terminal, Attribute, RawScreen};
use indexmap::IndexMap; use indexmap::IndexMap;
use nu::{serve_plugin, Args, CommandConfig, Plugin, ShellError, Spanned, Value}; use nu::{serve_plugin, Args, CommandConfig, NamedType, Plugin, ShellError, Spanned, Value};
use pretty_hex::*; use pretty_hex::*;
struct BinaryView; struct BinaryView;
@ -13,24 +13,26 @@ impl BinaryView {
impl Plugin for BinaryView { impl Plugin for BinaryView {
fn config(&mut self) -> Result<CommandConfig, ShellError> { fn config(&mut self) -> Result<CommandConfig, ShellError> {
let mut named = IndexMap::new();
named.insert("lores".to_string(), NamedType::Switch);
Ok(CommandConfig { Ok(CommandConfig {
name: "binaryview".to_string(), name: "binaryview".to_string(),
positional: vec![], positional: vec![],
is_filter: false, is_filter: false,
is_sink: true, is_sink: true,
named: IndexMap::new(), named,
rest_positional: true, rest_positional: false,
}) })
} }
fn sink(&mut self, _args: Args, input: Vec<Spanned<Value>>) { fn sink(&mut self, args: Args, input: Vec<Spanned<Value>>) {
for v in input { for v in input {
match v { match v {
Spanned { Spanned {
item: Value::Binary(b), item: Value::Binary(b),
.. ..
} => { } => {
let _ = view_binary(&b); let _ = view_binary(&b, args.has("lores"));
} }
_ => {} _ => {}
} }
@ -38,62 +40,65 @@ impl Plugin for BinaryView {
} }
} }
fn view_binary(b: &[u8]) -> Result<(), Box<dyn std::error::Error>> { fn view_binary(b: &[u8], lores_mode: bool) -> Result<(), Box<dyn std::error::Error>> {
if b.len() > 3 { if b.len() > 3 {
match (b[0], b[1], b[2]) { match (b[0], b[1], b[2]) {
(0x4e, 0x45, 0x53) => { (0x4e, 0x45, 0x53) => {
view_contents_interactive(b)?; view_contents_interactive(b, lores_mode)?;
return Ok(()); return Ok(());
} }
_ => {} _ => {}
} }
} }
view_contents(b)?; view_contents(b, lores_mode)?;
Ok(()) Ok(())
} }
pub struct Context { pub struct RenderContext {
pub width: usize, pub width: usize,
pub height: usize, pub height: usize,
pub frame_buffer: Vec<(char, (u8, u8, u8))>, pub frame_buffer: Vec<(u8, u8, u8)>,
pub since_last_button: Vec<usize>, pub since_last_button: Vec<usize>,
pub lores_mode: bool,
} }
impl Context { impl RenderContext {
pub fn blank() -> Context { pub fn blank(lores_mode: bool) -> RenderContext {
Context { RenderContext {
width: 0, width: 0,
height: 0, height: 0,
frame_buffer: vec![], frame_buffer: vec![],
since_last_button: vec![0; 8], since_last_button: vec![0; 8],
lores_mode,
} }
} }
pub fn clear(&mut self) { pub fn clear(&mut self) {
self.frame_buffer = vec![(' ', (0, 0, 0)); self.width * self.height as usize]; self.frame_buffer = vec![(0, 0, 0); self.width * self.height as usize];
} }
pub fn flush(&self) -> Result<(), Box<dyn std::error::Error>> {
let cursor = cursor();
cursor.goto(0, 0)?;
fn render_to_screen_lores(&mut self) -> Result<(), Box<dyn std::error::Error>> {
let mut prev_color: Option<(u8, u8, u8)> = None; let mut prev_color: Option<(u8, u8, u8)> = None;
let mut prev_count = 1; let mut prev_count = 1;
let cursor = cursor();
cursor.goto(0, 0)?;
for pixel in &self.frame_buffer { for pixel in &self.frame_buffer {
match prev_color { match prev_color {
Some(c) if c == pixel.1 => { Some(c) if c == *pixel => {
prev_count += 1; prev_count += 1;
} }
Some(c) => { Some(c) => {
print!( print!(
"{}", "{}",
ansi_term::Colour::RGB(c.0, c.1, c.2) ansi_term::Colour::RGB(c.0, c.1, c.2)
.paint((0..prev_count).map(|_| pixel.0).collect::<String>()) .paint((0..prev_count).map(|_| "").collect::<String>())
); );
prev_color = Some(pixel.1); prev_color = Some(*pixel);
prev_count = 1; prev_count = 1;
} }
_ => { _ => {
prev_color = Some(pixel.1); prev_color = Some(*pixel);
prev_count = 1; prev_count = 1;
} }
} }
@ -104,15 +109,77 @@ impl Context {
print!( print!(
"{}", "{}",
ansi_term::Colour::RGB(color.0, color.1, color.2) ansi_term::Colour::RGB(color.0, color.1, color.2)
.paint((0..prev_count).map(|_| "@").collect::<String>()) .paint((0..prev_count).map(|_| "").collect::<String>())
); );
} }
} }
println!("{}", Attribute::Reset); println!("{}", Attribute::Reset);
Ok(()) Ok(())
} }
fn render_to_screen_hires(&mut self) -> Result<(), Box<dyn std::error::Error>> {
let mut prev_fg: Option<(u8, u8, u8)> = None;
let mut prev_bg: Option<(u8, u8, u8)> = None;
let mut prev_count = 1;
let mut pos = 0;
let fb_len = self.frame_buffer.len();
let cursor = cursor();
cursor.goto(0, 0)?;
while pos < (fb_len - self.width) {
let top_pixel = self.frame_buffer[pos];
let bottom_pixel = self.frame_buffer[pos + self.width];
match (prev_fg, prev_bg) {
(Some(c), Some(d)) if c == top_pixel && d == bottom_pixel => {
prev_count += 1;
}
(Some(c), Some(d)) => {
print!(
"{}",
ansi_term::Colour::RGB(c.0, c.1, c.2)
.on(ansi_term::Colour::RGB(d.0, d.1, d.2,))
.paint((0..prev_count).map(|_| "").collect::<String>())
);
prev_fg = Some(top_pixel);
prev_bg = Some(bottom_pixel);
prev_count = 1;
}
_ => {
prev_fg = Some(top_pixel);
prev_bg = Some(bottom_pixel);
prev_count = 1;
}
}
pos += 1;
if pos % self.width == 0 {
pos += self.width;
}
}
if prev_count > 0 {
match (prev_fg, prev_bg) {
(Some(c), Some(d)) => {
print!(
"{}",
ansi_term::Colour::RGB(c.0, c.1, c.2)
.on(ansi_term::Colour::RGB(d.0, d.1, d.2,))
.paint((0..prev_count).map(|_| "").collect::<String>())
);
}
_ => {}
}
}
println!("{}", Attribute::Reset);
Ok(())
}
pub fn flush(&mut self) -> Result<(), Box<dyn std::error::Error>> {
if self.lores_mode {
self.render_to_screen_lores()
} else {
self.render_to_screen_hires()
}
}
pub fn update(&mut self) -> Result<(), Box<dyn std::error::Error>> { pub fn update(&mut self) -> Result<(), Box<dyn std::error::Error>> {
let terminal = terminal(); let terminal = terminal();
let terminal_size = terminal.terminal_size(); let terminal_size = terminal.terminal_size();
@ -122,7 +189,11 @@ impl Context {
cursor.hide()?; cursor.hide()?;
self.width = terminal_size.0 as usize + 1; self.width = terminal_size.0 as usize + 1;
self.height = terminal_size.1 as usize; self.height = if self.lores_mode {
terminal_size.1 as usize
} else {
terminal_size.1 as usize * 2
};
} }
Ok(()) Ok(())
@ -176,7 +247,7 @@ fn load_from_jpg_buffer(buffer: &[u8]) -> Option<(RawImageBuffer)> {
}) })
} }
pub fn view_contents(buffer: &[u8]) -> Result<(), Box<dyn std::error::Error>> { pub fn view_contents(buffer: &[u8], lores_mode: bool) -> Result<(), Box<dyn std::error::Error>> {
let mut raw_image_buffer = load_from_png_buffer(buffer); let mut raw_image_buffer = load_from_png_buffer(buffer);
if raw_image_buffer.is_none() { if raw_image_buffer.is_none() {
@ -190,9 +261,9 @@ pub fn view_contents(buffer: &[u8]) -> Result<(), Box<dyn std::error::Error>> {
} }
let raw_image_buffer = raw_image_buffer.unwrap(); let raw_image_buffer = raw_image_buffer.unwrap();
let mut context: Context = Context::blank(); let mut render_context: RenderContext = RenderContext::blank(lores_mode);
let _ = context.update(); let _ = render_context.update();
context.clear(); render_context.clear();
match raw_image_buffer.colortype { match raw_image_buffer.colortype {
image::ColorType::RGBA(8) => { image::ColorType::RGBA(8) => {
@ -205,8 +276,8 @@ pub fn view_contents(buffer: &[u8]) -> Result<(), Box<dyn std::error::Error>> {
let resized_img = image::imageops::resize( let resized_img = image::imageops::resize(
&img, &img,
context.width as u32, render_context.width as u32,
context.height as u32, render_context.height as u32,
image::FilterType::Lanczos3, image::FilterType::Lanczos3,
); );
@ -214,8 +285,7 @@ pub fn view_contents(buffer: &[u8]) -> Result<(), Box<dyn std::error::Error>> {
for pixel in resized_img.pixels() { for pixel in resized_img.pixels() {
use image::Pixel; use image::Pixel;
let rgb = pixel.to_rgb(); let rgb = pixel.to_rgb();
//print!("{}", rgb[0]); render_context.frame_buffer[count] = (rgb[0], rgb[1], rgb[2]);
context.frame_buffer[count] = ('@', (rgb[0], rgb[1], rgb[2]));
count += 1; count += 1;
} }
} }
@ -229,8 +299,8 @@ pub fn view_contents(buffer: &[u8]) -> Result<(), Box<dyn std::error::Error>> {
let resized_img = image::imageops::resize( let resized_img = image::imageops::resize(
&img, &img,
context.width as u32, render_context.width as u32,
context.height as u32, render_context.height as u32,
image::FilterType::Lanczos3, image::FilterType::Lanczos3,
); );
@ -238,8 +308,7 @@ pub fn view_contents(buffer: &[u8]) -> Result<(), Box<dyn std::error::Error>> {
for pixel in resized_img.pixels() { for pixel in resized_img.pixels() {
use image::Pixel; use image::Pixel;
let rgb = pixel.to_rgb(); let rgb = pixel.to_rgb();
//print!("{}", rgb[0]); render_context.frame_buffer[count] = (rgb[0], rgb[1], rgb[2]);
context.frame_buffer[count] = ('@', (rgb[0], rgb[1], rgb[2]));
count += 1; count += 1;
} }
} }
@ -250,7 +319,7 @@ pub fn view_contents(buffer: &[u8]) -> Result<(), Box<dyn std::error::Error>> {
} }
} }
context.flush()?; render_context.flush()?;
let cursor = cursor(); let cursor = cursor();
let _ = cursor.show(); let _ = cursor.show();
@ -261,7 +330,10 @@ pub fn view_contents(buffer: &[u8]) -> Result<(), Box<dyn std::error::Error>> {
Ok(()) Ok(())
} }
pub fn view_contents_interactive(buffer: &[u8]) -> Result<(), Box<dyn std::error::Error>> { pub fn view_contents_interactive(
buffer: &[u8],
lores_mode: bool,
) -> Result<(), Box<dyn std::error::Error>> {
use rawkey::{KeyCode, RawKey}; use rawkey::{KeyCode, RawKey};
let mut nes = neso::Nes::new(48000.0); let mut nes = neso::Nes::new(48000.0);
@ -271,7 +343,7 @@ pub fn view_contents_interactive(buffer: &[u8]) -> Result<(), Box<dyn std::error
nes.reset(); nes.reset();
if let Ok(_raw) = RawScreen::into_raw_mode() { if let Ok(_raw) = RawScreen::into_raw_mode() {
let mut context: Context = Context::blank(); let mut render_context: RenderContext = RenderContext::blank(lores_mode);
let input = crossterm::input(); let input = crossterm::input();
let _ = input.read_async(); let _ = input.read_async();
let cursor = cursor(); let cursor = cursor();
@ -290,7 +362,7 @@ pub fn view_contents_interactive(buffer: &[u8]) -> Result<(), Box<dyn std::error
cursor.hide()?; cursor.hide()?;
'gameloop: loop { 'gameloop: loop {
let _ = context.update(); let _ = render_context.update();
nes.step_frame(); nes.step_frame();
let image_buffer = nes.image_buffer(); let image_buffer = nes.image_buffer();
@ -300,22 +372,22 @@ pub fn view_contents_interactive(buffer: &[u8]) -> Result<(), Box<dyn std::error
image::ImageBuffer::<image::Rgba<u8>, &[u8]>::from_raw(256, 240, slice).unwrap(); image::ImageBuffer::<image::Rgba<u8>, &[u8]>::from_raw(256, 240, slice).unwrap();
let resized_img = image::imageops::resize( let resized_img = image::imageops::resize(
&img, &img,
context.width as u32, render_context.width as u32,
context.height as u32, render_context.height as u32,
image::FilterType::Lanczos3, image::FilterType::Lanczos3,
); );
context.clear(); render_context.clear();
let mut count = 0; let mut count = 0;
for pixel in resized_img.pixels() { for pixel in resized_img.pixels() {
use image::Pixel; use image::Pixel;
let rgb = pixel.to_rgb(); let rgb = pixel.to_rgb();
context.frame_buffer[count] = ('@', (rgb[0], rgb[1], rgb[2])); render_context.frame_buffer[count] = (rgb[0], rgb[1], rgb[2]);
count += 1; count += 1;
} }
context.flush()?; render_context.flush()?;
if rawkey.is_pressed(rawkey::KeyCode::Escape) { if rawkey.is_pressed(rawkey::KeyCode::Escape) {
break 'gameloop; break 'gameloop;

View file

@ -17,7 +17,7 @@ impl Plugin for Inc {
fn config(&mut self) -> Result<CommandConfig, ShellError> { fn config(&mut self) -> Result<CommandConfig, ShellError> {
Ok(CommandConfig { Ok(CommandConfig {
name: "inc".to_string(), name: "inc".to_string(),
positional: vec![PositionalType::mandatory_any("Increment")], positional: vec![PositionalType::optional_any("Increment")],
is_filter: true, is_filter: true,
is_sink: false, is_sink: false,
named: IndexMap::new(), named: IndexMap::new(),

View file

@ -34,7 +34,7 @@ macro_rules! trace_stream {
crate use crate::cli::MaybeOwned; crate use crate::cli::MaybeOwned;
crate use crate::commands::command::{ crate use crate::commands::command::{
Command, CommandAction, CommandArgs, ReturnSuccess, ReturnValue, Command, CommandAction, CommandArgs, ReturnSuccess, ReturnValue, Sink, SinkCommandArgs,
}; };
crate use crate::context::Context; crate use crate::context::Context;
crate use crate::env::host::handle_unexpected; crate use crate::env::host::handle_unexpected;