Pipe external to internal

Each line is a string object
This commit is contained in:
Yehuda Katz
2019-05-24 11:48:33 -07:00
parent f9fb353c5c
commit 9f8d2a4de5
15 changed files with 391 additions and 97 deletions

View File

@ -1,7 +1,7 @@
use crate::prelude::*;
use crate::commands::classified::{
ClassifiedCommand, ClassifiedInputStream, ExternalCommand, InternalCommand,
ClassifiedCommand, ClassifiedInputStream, ExternalCommand, InternalCommand, StreamNext,
};
use crate::context::Context;
crate use crate::errors::ShellError;
@ -185,7 +185,7 @@ async fn process_line(readline: Result<String, ReadlineError>, ctx: &mut Context
(
Some(ClassifiedCommand::External(left)),
Some(ClassifiedCommand::External(_)),
) => match left.run(ctx, input, true).await {
) => match left.run(ctx, input, StreamNext::External).await {
Ok(val) => val,
Err(err) => return LineResult::Error(format!("{}", err.description())),
},
@ -196,12 +196,15 @@ async fn process_line(readline: Result<String, ReadlineError>, ctx: &mut Context
) => unimplemented!(),
(
Some(ClassifiedCommand::External(_)),
Some(ClassifiedCommand::External(left)),
Some(ClassifiedCommand::Internal(_)),
) => unimplemented!(),
) => match left.run(ctx, input, StreamNext::Internal).await {
Ok(val) => val,
Err(err) => return LineResult::Error(format!("{}", err.description())),
},
(Some(ClassifiedCommand::External(left)), None) => {
match left.run(ctx, input, false).await {
match left.run(ctx, input, StreamNext::Last).await {
Ok(val) => val,
Err(err) => return LineResult::Error(format!("{}", err.description())),
}

View File

@ -1,4 +1,6 @@
use crate::prelude::*;
use futures::compat::AsyncRead01CompatExt;
use futures_codec::{Framed, LinesCodec};
use std::sync::Arc;
use subprocess::Exec;
@ -107,20 +109,29 @@ crate struct ExternalCommand {
crate args: Vec<String>,
}
crate enum StreamNext {
Last,
External,
Internal,
}
impl ExternalCommand {
crate async fn run(
self,
context: &mut Context,
mut input: ClassifiedInputStream,
stream_next: bool,
stream_next: StreamNext,
) -> Result<ClassifiedInputStream, ShellError> {
let mut process = Exec::shell(&self.name)
.args(&self.args)
.cwd(context.env.lock().unwrap().cwd());
if stream_next {
process = process.stdout(subprocess::Redirection::Pipe)
}
let mut process = match stream_next {
StreamNext::Last => process,
StreamNext::External | StreamNext::Internal => {
process.stdout(subprocess::Redirection::Pipe)
}
};
if let Some(stdin) = input.stdin {
process = process.stdin(stdin);
@ -128,15 +139,31 @@ impl ExternalCommand {
let mut popen = process.popen().unwrap();
if stream_next {
match &popen.stdout {
None => unreachable!(),
Some(stdout) => Ok(ClassifiedInputStream::from_stdout(stdout.try_clone()?)),
match stream_next {
StreamNext::Last => {
popen.wait()?;
Ok(ClassifiedInputStream::new())
}
StreamNext::External => {
let stdout = popen.stdout.take().unwrap();
Ok(ClassifiedInputStream::from_stdout(stdout))
}
StreamNext::Internal => {
let stdout = popen.stdout.take().unwrap();
let file = futures::io::AllowStdIo::new(stdout);
let stream = Framed::new(file, LinesCodec {});
let stream = stream.map(|line| Value::string(line.unwrap()));
Ok(ClassifiedInputStream::from_input_stream(stream.boxed()))
}
} else {
popen.stdin.take();
popen.wait()?;
Ok(ClassifiedInputStream::new())
}
// if stream_next {
// let stdout = popen.stdout.take().unwrap();
// Ok(ClassifiedInputStream::from_stdout(stdout))
// } else {
// // popen.stdin.take();
// popen.wait()?;
// Ok(ClassifiedInputStream::new())
// }
}
}

19
src/env/host.rs vendored
View File

@ -1,9 +1,20 @@
pub trait Host {
fn out_terminal(&self) -> Box<term::StdoutTerminal>;
fn err_terminal(&self) -> Box<term::StderrTerminal>;
fn stdout(&mut self, out: &str);
fn stderr(&mut self, out: &str);
}
impl Host for Box<dyn Host> {
fn out_terminal(&self) -> Box<term::StdoutTerminal> {
(**self).out_terminal()
}
fn err_terminal(&self) -> Box<term::StderrTerminal> {
(**self).err_terminal()
}
fn stdout(&mut self, out: &str) {
(**self).stdout(out)
}
@ -16,6 +27,14 @@ impl Host for Box<dyn Host> {
crate struct BasicHost;
impl Host for BasicHost {
fn out_terminal(&self) -> Box<term::StdoutTerminal> {
term::stdout().unwrap()
}
fn err_terminal(&self) -> Box<term::StderrTerminal> {
term::stderr().unwrap()
}
fn stdout(&mut self, out: &str) {
match out {
"\n" => println!(""),

View File

@ -10,16 +10,9 @@ crate use generic::GenericView;
crate use table::TableView;
crate trait RenderView {
fn render_view(&self, host: &dyn Host) -> Vec<String>;
fn render_view(&self, host: &mut dyn Host) -> Result<(), ShellError>;
}
fn print_rendered(lines: &[String], host: &mut dyn Host) {
for line in lines {
host.stdout(line);
}
}
crate fn print_view(view: &impl RenderView, host: &mut Host) {
// let mut ctx = context.lock().unwrap();
crate::format::print_rendered(&view.render_view(host), host);
crate fn print_view(view: &impl RenderView, host: &mut dyn Host) -> Result<(), ShellError> {
view.render_view(host)
}

View File

@ -10,7 +10,7 @@ use derive_new::new;
// another_name : ...
#[derive(new)]
pub struct EntriesView {
entries: Vec<(String, String)>,
entries: Vec<(crate::object::DescriptorName, String)>,
}
impl EntriesView {
@ -31,17 +31,28 @@ impl EntriesView {
}
impl RenderView for EntriesView {
fn render_view(&self, _host: &dyn Host) -> Vec<String> {
fn render_view(&self, _host: &mut dyn Host) -> Result<(), ShellError> {
if self.entries.len() == 0 {
return vec![];
return Ok(());
}
let max_name_size: usize = self.entries.iter().map(|(n, _)| n.len()).max().unwrap();
self.entries
let max_name_size: usize = self
.entries
.iter()
.map(|(k, v)| format!("{:width$} : {}", k, v, width = max_name_size))
.collect()
.map(|(n, _)| n.display().len())
.max()
.unwrap();
for (name, value) in &self.entries {
println!(
"{:width$} : {}",
name.display(),
value,
width = max_name_size
)
}
Ok(())
}
}
@ -58,26 +69,22 @@ impl EntriesListView {
}
impl RenderView for EntriesListView {
fn render_view(&self, host: &dyn Host) -> Vec<String> {
fn render_view(&self, host: &mut dyn Host) -> Result<(), ShellError> {
if self.values.len() == 0 {
return vec![];
return Ok(());
}
let mut strings = vec![];
let last = self.values.len() - 1;
for (i, item) in self.values.iter().enumerate() {
let view = EntriesView::from_value(item);
let out = view.render_view(host);
strings.extend(out);
view.render_view(host);
if i != last {
strings.push("\n".to_string());
host.stdout("\n");
}
}
strings
Ok(())
}
}

View File

@ -10,17 +10,17 @@ pub struct GenericView<'value> {
}
impl RenderView for GenericView<'value> {
fn render_view(&self, host: &dyn Host) -> Vec<String> {
fn render_view(&self, host: &mut dyn Host) -> Result<(), ShellError> {
match self.value {
Value::Primitive(p) => vec![p.format(None)],
Value::Primitive(p) => Ok(host.stdout(&p.format(None))),
Value::List(l) => {
let view = TableView::from_list(l);
if let Some(view) = view {
view.render_view(host)
} else {
vec![]
view.render_view(host);
}
Ok(())
// let mut list: Vec<String> = vec![];
// for item in l {
// match item {
@ -39,11 +39,14 @@ impl RenderView for GenericView<'value> {
o @ Value::Object(_) => {
let view = EntriesView::from_value(o);
let out = view.render_view(host);
out
view.render_view(host);
Ok(())
}
Value::Error(e) => vec![format!("{}", e)],
Value::Error(e) => {
host.stdout(&format!("{:?}", e));
Ok(())
}
}
}
}

View File

@ -11,14 +11,12 @@ pub struct ListView {
}
impl RenderView for ListView {
fn render_view(&self, _host: &dyn Host) -> Vec<String> {
let mut out = vec![];
fn render_view(&self, host: &mut dyn Host) -> Result<(), ShellError> {
for output in &self.list {
let string: String = output.iter().map(|l| format!("{}\n", l)).collect();
out.push(format!("{}{}", string, self.sep));
host.stdout(&format!("{}{}", string, self.sep));
}
out
Ok(())
}
}

View File

@ -1,8 +1,9 @@
use crate::format::RenderView;
use crate::object::Value;
use crate::prelude::*;
use ansi_term::Color;
use derive_new::new;
use prettytable::{Cell, Row, Table};
use prettytable::{color, Attr, Cell, Row, Table};
// An entries list is printed like this:
//
@ -24,7 +25,7 @@ impl TableView {
let item = &values[0];
let descs = item.data_descriptors();
let headers: Vec<String> = descs.iter().map(|d| d.name.clone()).collect();
let headers: Vec<String> = descs.iter().map(|d| d.name.display().to_string()).collect();
let mut entries = vec![];
@ -43,13 +44,27 @@ impl TableView {
}
impl RenderView for TableView {
fn render_view(&self, _host: &dyn Host) -> Vec<String> {
fn render_view(&self, host: &mut dyn Host) -> Result<(), ShellError> {
if self.entries.len() == 0 {
return vec![];
return Ok(());
}
let mut table = Table::new();
let header: Vec<Cell> = self.headers.iter().map(|h| Cell::new(h)).collect();
// let format = prettytable::format::FormatBuilder::new();
// .column_separator(Color::Black.bold().paint("|"));
table.set_format(*prettytable::format::consts::FORMAT_NO_COLSEP);
let header: Vec<Cell> = self
.headers
.iter()
.map(|h| {
Cell::new(h)
.with_style(Attr::ForegroundColor(color::GREEN))
.with_style(Attr::Bold)
})
.collect();
table.add_row(Row::new(header));
@ -57,9 +72,8 @@ impl RenderView for TableView {
table.add_row(Row::new(row.iter().map(|h| Cell::new(h)).collect()));
}
let mut out = vec![];
table.print(&mut out).unwrap();
table.print_term(&mut *host.out_terminal()).unwrap();
vec![String::from_utf8_lossy(&out).to_string()]
Ok(())
}
}

View File

@ -6,5 +6,6 @@ crate mod process;
crate mod types;
crate use base::{Primitive, Value};
crate use desc::{DataDescriptor, DescriptorName};
crate use dict::Dictionary;
crate use files::dir_entry_dict;

View File

@ -1,5 +1,5 @@
use crate::errors::ShellError;
use crate::object::desc::DataDescriptor;
use crate::object::{DataDescriptor, DescriptorName};
use crate::parser::parse::Operator;
use crate::prelude::*;
use ansi_term::Color;
@ -69,7 +69,7 @@ pub enum Value {
impl Value {
crate fn data_descriptors(&self) -> Vec<DataDescriptor> {
match self {
Value::Primitive(_) => vec![],
Value::Primitive(_) => vec![DataDescriptor::value_of()],
Value::Object(o) => o.data_descriptors(),
Value::List(_) => vec![],
Value::Error(_) => vec![],
@ -87,7 +87,7 @@ impl Value {
crate fn get_data(&'a self, desc: &DataDescriptor) -> MaybeOwned<'a, Value> {
match self {
Value::Primitive(_) => MaybeOwned::Owned(Value::nothing()),
p @ Value::Primitive(_) => MaybeOwned::Borrowed(p),
Value::Object(o) => o.get_data(desc),
Value::List(_) => MaybeOwned::Owned(Value::nothing()),
Value::Error(_) => MaybeOwned::Owned(Value::nothing()),
@ -203,9 +203,9 @@ crate fn select_fields(obj: &Value, fields: &[String]) -> crate::object::Diction
let descs = obj.data_descriptors();
for field in fields {
match descs.iter().find(|d| d.name == *field) {
None => out.add(field.to_string(), Value::nothing()),
Some(desc) => out.add(field.to_string(), obj.get_data(desc).borrow().copy()),
match descs.iter().find(|d| d.name.is_string(field)) {
None => out.add(DataDescriptor::for_string_name(field), Value::nothing()),
Some(desc) => out.add(desc.copy(), obj.get_data(desc).borrow().copy()),
}
}
@ -218,10 +218,10 @@ crate fn reject_fields(obj: &Value, fields: &[String]) -> crate::object::Diction
let descs = obj.data_descriptors();
for desc in descs {
if fields.contains(&desc.name) {
continue;
} else {
out.add(desc.name.clone(), obj.get_data(&desc).borrow().copy())
match desc.name.as_string() {
None => continue,
Some(s) if fields.iter().any(|field| field == s) => continue,
Some(s) => out.add(desc.copy(), obj.get_data(&desc).borrow().copy()),
}
}
@ -230,7 +230,7 @@ crate fn reject_fields(obj: &Value, fields: &[String]) -> crate::object::Diction
crate fn find(obj: &Value, field: &str, op: &Operator, rhs: &Value) -> bool {
let descs = obj.data_descriptors();
match descs.iter().find(|d| d.name == *field) {
match descs.iter().find(|d| d.name.is_string(field)) {
None => false,
Some(desc) => {
let v = obj.get_data(desc).borrow().copy();

View File

@ -1,17 +1,110 @@
use crate::object::types::Type;
use crate::object::types::{AnyShell, Type};
use derive_new::new;
#[derive(new)]
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum DescriptorName {
String(String),
ValueOf,
}
impl DescriptorName {
crate fn display(&self) -> &str {
match self {
DescriptorName::String(s) => s,
DescriptorName::ValueOf => "value",
}
}
crate fn as_string(&self) -> Option<&str> {
match self {
DescriptorName::String(s) => Some(s),
DescriptorName::ValueOf => None,
}
}
crate fn is_string(&self, string: &str) -> bool {
match self {
DescriptorName::String(s) => s == string,
DescriptorName::ValueOf => false,
}
}
}
#[derive(Debug, new)]
pub struct DataDescriptor {
crate name: String,
crate name: DescriptorName,
crate readonly: bool,
crate ty: Box<dyn Type>,
}
impl From<&str> for DataDescriptor {
fn from(input: &str) -> DataDescriptor {
DataDescriptor {
name: DescriptorName::String(input.to_string()),
readonly: true,
ty: Box::new(AnyShell),
}
}
}
impl From<String> for DataDescriptor {
fn from(input: String) -> DataDescriptor {
DataDescriptor {
name: DescriptorName::String(input),
readonly: true,
ty: Box::new(AnyShell),
}
}
}
impl PartialEq for DataDescriptor {
fn eq(&self, other: &DataDescriptor) -> bool {
self.name == other.name && self.readonly == other.readonly && self.ty.equal(&*other.ty)
}
}
impl DataDescriptor {}
impl std::hash::Hash for DataDescriptor {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.name.hash(state);
self.readonly.hash(state);
self.ty.id().hash(state);
}
}
impl Eq for DataDescriptor {}
impl DescriptorName {
crate fn for_string_name(name: impl Into<String>) -> DescriptorName {
DescriptorName::String(name.into())
}
}
impl DataDescriptor {
crate fn value_of() -> DataDescriptor {
DataDescriptor {
name: DescriptorName::ValueOf,
readonly: true,
ty: Box::new(AnyShell),
}
}
crate fn for_name(name: impl Into<DescriptorName>) -> DataDescriptor {
DataDescriptor {
name: name.into(),
readonly: true,
ty: Box::new(AnyShell),
}
}
crate fn for_string_name(name: impl Into<String>) -> DataDescriptor {
DataDescriptor::for_name(DescriptorName::for_string_name(name))
}
crate fn copy(&self) -> DataDescriptor {
DataDescriptor {
name: self.name.clone(),
readonly: self.readonly,
ty: self.ty.copy(),
}
}
}

View File

@ -1,13 +1,13 @@
use crate::prelude::*;
use crate::object::desc::DataDescriptor;
use crate::object::{DataDescriptor, DescriptorName};
use crate::object::{Primitive, Value};
use indexmap::IndexMap;
use std::cmp::{Ordering, PartialOrd};
#[derive(Debug, Default, Eq, PartialEq)]
pub struct Dictionary {
entries: IndexMap<String, Value>,
entries: IndexMap<DataDescriptor, Value>,
}
impl PartialOrd for Dictionary {
@ -41,7 +41,7 @@ impl PartialEq<Value> for Dictionary {
}
impl Dictionary {
crate fn add(&mut self, name: impl Into<String>, value: Value) {
crate fn add(&mut self, name: impl Into<DataDescriptor>, value: Value) {
self.entries.insert(name.into(), value);
}
@ -49,31 +49,30 @@ impl Dictionary {
let mut out = Dictionary::default();
for (key, value) in self.entries.iter() {
out.add(key.clone(), value.copy());
out.add(key.copy(), value.copy());
}
out
}
crate fn data_descriptors(&self) -> Vec<DataDescriptor> {
self.entries
.iter()
.map(|(name, _)| {
DataDescriptor::new(name.clone(), true, Box::new(crate::object::types::AnyShell))
})
.collect()
self.entries.iter().map(|(name, _)| name.copy()).collect()
}
crate fn get_data(&'a self, desc: &DataDescriptor) -> MaybeOwned<'a, Value> {
match self.entries.get(&desc.name) {
match self.entries.get(desc) {
Some(v) => MaybeOwned::Borrowed(v),
None => MaybeOwned::Owned(Value::Primitive(Primitive::Nothing)),
}
}
crate fn get_data_by_key(&self, name: &str) -> MaybeOwned<'_, Value> {
match self.entries.get(name) {
Some(v) => MaybeOwned::Borrowed(v),
match self
.entries
.iter()
.find(|(desc_name, _)| desc_name.name.is_string(name))
{
Some((_, v)) => MaybeOwned::Borrowed(v),
None => MaybeOwned::Owned(Value::Primitive(Primitive::Nothing)),
}
}

View File

@ -1,11 +1,14 @@
use std::any::Any;
use std::fmt::Debug;
pub trait Type {
pub trait Type: Debug + Send {
fn as_any(&self) -> &dyn Any;
fn equal(&self, other: &dyn Type) -> bool;
fn id(&self) -> u64;
fn copy(&self) -> Box<Type>;
}
#[derive(Eq, PartialEq)]
#[derive(Debug, Eq, PartialEq)]
pub struct AnyShell;
impl Type for AnyShell {
@ -16,4 +19,12 @@ impl Type for AnyShell {
fn equal(&self, other: &dyn Type) -> bool {
other.as_any().is::<AnyShell>()
}
fn id(&self) -> u64 {
0
}
fn copy(&self) -> Box<Type> {
Box::new(AnyShell)
}
}