mirror of
https://github.com/nushell/nushell
synced 2024-12-28 14:03:09 +00:00
Merge pull request #546 from jonathandturner/from_to_automatic
Automatically call matching from-* and to-* commands
This commit is contained in:
commit
339cf98dd1
17 changed files with 330 additions and 334 deletions
|
@ -17,7 +17,7 @@ use crate::prelude::*;
|
||||||
use log::{debug, trace};
|
use log::{debug, trace};
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use rustyline::error::ReadlineError;
|
use rustyline::error::ReadlineError;
|
||||||
use rustyline::{self, ColorMode, Config, Editor, config::Configurer, config::EditMode};
|
use rustyline::{self, config::Configurer, config::EditMode, ColorMode, Config, Editor};
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::io::{BufRead, BufReader, Write};
|
use std::io::{BufRead, BufReader, Write};
|
||||||
|
@ -180,6 +180,7 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
|
||||||
whole_stream_command(ToCSV),
|
whole_stream_command(ToCSV),
|
||||||
whole_stream_command(ToJSON),
|
whole_stream_command(ToJSON),
|
||||||
whole_stream_command(ToSQLite),
|
whole_stream_command(ToSQLite),
|
||||||
|
whole_stream_command(ToDB),
|
||||||
whole_stream_command(ToTOML),
|
whole_stream_command(ToTOML),
|
||||||
whole_stream_command(ToTSV),
|
whole_stream_command(ToTSV),
|
||||||
whole_stream_command(ToYAML),
|
whole_stream_command(ToYAML),
|
||||||
|
@ -194,10 +195,12 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
|
||||||
whole_stream_command(FromINI),
|
whole_stream_command(FromINI),
|
||||||
whole_stream_command(FromBSON),
|
whole_stream_command(FromBSON),
|
||||||
whole_stream_command(FromJSON),
|
whole_stream_command(FromJSON),
|
||||||
|
whole_stream_command(FromDB),
|
||||||
whole_stream_command(FromSQLite),
|
whole_stream_command(FromSQLite),
|
||||||
whole_stream_command(FromTOML),
|
whole_stream_command(FromTOML),
|
||||||
whole_stream_command(FromXML),
|
whole_stream_command(FromXML),
|
||||||
whole_stream_command(FromYAML),
|
whole_stream_command(FromYAML),
|
||||||
|
whole_stream_command(FromYML),
|
||||||
whole_stream_command(Pick),
|
whole_stream_command(Pick),
|
||||||
whole_stream_command(Get),
|
whole_stream_command(Get),
|
||||||
per_item_command(Remove),
|
per_item_command(Remove),
|
||||||
|
|
|
@ -82,11 +82,13 @@ pub(crate) use from_bson::FromBSON;
|
||||||
pub(crate) use from_csv::FromCSV;
|
pub(crate) use from_csv::FromCSV;
|
||||||
pub(crate) use from_ini::FromINI;
|
pub(crate) use from_ini::FromINI;
|
||||||
pub(crate) use from_json::FromJSON;
|
pub(crate) use from_json::FromJSON;
|
||||||
|
pub(crate) use from_sqlite::FromDB;
|
||||||
pub(crate) use from_sqlite::FromSQLite;
|
pub(crate) use from_sqlite::FromSQLite;
|
||||||
pub(crate) use from_toml::FromTOML;
|
pub(crate) use from_toml::FromTOML;
|
||||||
pub(crate) use from_tsv::FromTSV;
|
pub(crate) use from_tsv::FromTSV;
|
||||||
pub(crate) use from_xml::FromXML;
|
pub(crate) use from_xml::FromXML;
|
||||||
pub(crate) use from_yaml::FromYAML;
|
pub(crate) use from_yaml::FromYAML;
|
||||||
|
pub(crate) use from_yaml::FromYML;
|
||||||
pub(crate) use get::Get;
|
pub(crate) use get::Get;
|
||||||
pub(crate) use last::Last;
|
pub(crate) use last::Last;
|
||||||
pub(crate) use lines::Lines;
|
pub(crate) use lines::Lines;
|
||||||
|
@ -115,6 +117,7 @@ pub(crate) use to_array::ToArray;
|
||||||
pub(crate) use to_bson::ToBSON;
|
pub(crate) use to_bson::ToBSON;
|
||||||
pub(crate) use to_csv::ToCSV;
|
pub(crate) use to_csv::ToCSV;
|
||||||
pub(crate) use to_json::ToJSON;
|
pub(crate) use to_json::ToJSON;
|
||||||
|
pub(crate) use to_sqlite::ToDB;
|
||||||
pub(crate) use to_sqlite::ToSQLite;
|
pub(crate) use to_sqlite::ToSQLite;
|
||||||
pub(crate) use to_toml::ToTOML;
|
pub(crate) use to_toml::ToTOML;
|
||||||
pub(crate) use to_tsv::ToTSV;
|
pub(crate) use to_tsv::ToTSV;
|
||||||
|
|
|
@ -146,53 +146,9 @@ impl InternalCommand {
|
||||||
.insert_at_current(Box::new(ValueShell::new(value)));
|
.insert_at_current(Box::new(ValueShell::new(value)));
|
||||||
}
|
}
|
||||||
CommandAction::EnterShell(location) => {
|
CommandAction::EnterShell(location) => {
|
||||||
let path = std::path::Path::new(&location);
|
context.shell_manager.insert_at_current(Box::new(
|
||||||
|
FilesystemShell::with_location(location, context.registry().clone())?,
|
||||||
if path.is_dir() {
|
));
|
||||||
// If it's a directory, add a new filesystem shell
|
|
||||||
context.shell_manager.insert_at_current(Box::new(
|
|
||||||
FilesystemShell::with_location(
|
|
||||||
location,
|
|
||||||
context.registry().clone(),
|
|
||||||
)?,
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
// If it's a file, attempt to open the file as a value and enter it
|
|
||||||
let cwd = context.shell_manager.path();
|
|
||||||
|
|
||||||
let full_path = std::path::PathBuf::from(cwd);
|
|
||||||
|
|
||||||
let (file_extension, contents, contents_tag, span_source) =
|
|
||||||
crate::commands::open::fetch(
|
|
||||||
&full_path,
|
|
||||||
&location,
|
|
||||||
Span::unknown(),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
if let Some(uuid) = contents_tag.origin {
|
|
||||||
// If we have loaded something, track its source
|
|
||||||
context.add_span_source(uuid, span_source);
|
|
||||||
}
|
|
||||||
|
|
||||||
match contents {
|
|
||||||
Value::Primitive(Primitive::String(string)) => {
|
|
||||||
let value = crate::commands::open::parse_string_as_value(
|
|
||||||
file_extension,
|
|
||||||
string,
|
|
||||||
contents_tag,
|
|
||||||
Span::unknown(),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
context
|
|
||||||
.shell_manager
|
|
||||||
.insert_at_current(Box::new(ValueShell::new(value)));
|
|
||||||
}
|
|
||||||
value => context.shell_manager.insert_at_current(Box::new(
|
|
||||||
ValueShell::new(value.tagged(contents_tag)),
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
CommandAction::PreviousShell => {
|
CommandAction::PreviousShell => {
|
||||||
context.shell_manager.prev();
|
context.shell_manager.prev();
|
||||||
|
|
|
@ -512,7 +512,7 @@ pub trait PerItemCommand: Send + Sync {
|
||||||
&self,
|
&self,
|
||||||
call_info: &CallInfo,
|
call_info: &CallInfo,
|
||||||
registry: &CommandRegistry,
|
registry: &CommandRegistry,
|
||||||
shell_manager: &ShellManager,
|
raw_args: &RawCommandArgs,
|
||||||
input: Tagged<Value>,
|
input: Tagged<Value>,
|
||||||
) -> Result<OutputStream, ShellError>;
|
) -> Result<OutputStream, ShellError>;
|
||||||
|
|
||||||
|
@ -579,7 +579,7 @@ impl Command {
|
||||||
.call_info
|
.call_info
|
||||||
.evaluate(®istry, &Scope::it_value(x.clone()))
|
.evaluate(®istry, &Scope::it_value(x.clone()))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
match command.run(&call_info, ®istry, &raw_args.shell_manager, x) {
|
match command.run(&call_info, ®istry, &raw_args, x) {
|
||||||
Ok(o) => o,
|
Ok(o) => o,
|
||||||
Err(e) => VecDeque::from(vec![ReturnValue::Err(e)]).to_output_stream(),
|
Err(e) => VecDeque::from(vec![ReturnValue::Err(e)]).to_output_stream(),
|
||||||
}
|
}
|
||||||
|
@ -596,7 +596,10 @@ impl Command {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
// We don't have an $it or block, so just execute what we have
|
// We don't have an $it or block, so just execute what we have
|
||||||
|
|
||||||
match command.run(&call_info, ®istry, &raw_args.shell_manager, nothing) {
|
match command
|
||||||
|
.run(&call_info, ®istry, &raw_args, nothing)
|
||||||
|
.into()
|
||||||
|
{
|
||||||
Ok(o) => o,
|
Ok(o) => o,
|
||||||
Err(e) => OutputStream::one(Err(e)),
|
Err(e) => OutputStream::one(Err(e)),
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,10 +19,10 @@ impl PerItemCommand for Cpy {
|
||||||
&self,
|
&self,
|
||||||
call_info: &CallInfo,
|
call_info: &CallInfo,
|
||||||
_registry: &CommandRegistry,
|
_registry: &CommandRegistry,
|
||||||
shell_manager: &ShellManager,
|
raw_args: &RawCommandArgs,
|
||||||
_input: Tagged<Value>,
|
_input: Tagged<Value>,
|
||||||
) -> Result<OutputStream, ShellError> {
|
) -> Result<OutputStream, ShellError> {
|
||||||
call_info.process(shell_manager, cp)?.run()
|
call_info.process(&raw_args.shell_manager, cp)?.run()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
use crate::commands::command::CommandAction;
|
use crate::commands::command::CommandAction;
|
||||||
use crate::commands::PerItemCommand;
|
use crate::commands::PerItemCommand;
|
||||||
|
use crate::commands::UnevaluatedCallInfo;
|
||||||
use crate::errors::ShellError;
|
use crate::errors::ShellError;
|
||||||
use crate::parser::registry;
|
use crate::parser::registry;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
pub struct Enter;
|
pub struct Enter;
|
||||||
|
|
||||||
|
@ -18,18 +20,109 @@ impl PerItemCommand for Enter {
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
call_info: &CallInfo,
|
call_info: &CallInfo,
|
||||||
_registry: ®istry::CommandRegistry,
|
registry: ®istry::CommandRegistry,
|
||||||
_shell_manager: &ShellManager,
|
raw_args: &RawCommandArgs,
|
||||||
_input: Tagged<Value>,
|
_input: Tagged<Value>,
|
||||||
) -> Result<OutputStream, ShellError> {
|
) -> Result<OutputStream, ShellError> {
|
||||||
|
let registry = registry.clone();
|
||||||
|
let raw_args = raw_args.clone();
|
||||||
match call_info.args.expect_nth(0)? {
|
match call_info.args.expect_nth(0)? {
|
||||||
Tagged {
|
Tagged {
|
||||||
item: Value::Primitive(Primitive::String(location)),
|
item: Value::Primitive(Primitive::String(location)),
|
||||||
..
|
..
|
||||||
} => Ok(vec![Ok(ReturnSuccess::Action(CommandAction::EnterShell(
|
} => {
|
||||||
location.to_string(),
|
let location = location.to_string();
|
||||||
)))]
|
let location_clone = location.to_string();
|
||||||
.into()),
|
if PathBuf::from(location).is_dir() {
|
||||||
|
Ok(vec![Ok(ReturnSuccess::Action(CommandAction::EnterShell(
|
||||||
|
location_clone,
|
||||||
|
)))]
|
||||||
|
.into())
|
||||||
|
} else {
|
||||||
|
let stream = async_stream_block! {
|
||||||
|
// If it's a file, attempt to open the file as a value and enter it
|
||||||
|
let cwd = raw_args.shell_manager.path();
|
||||||
|
|
||||||
|
let full_path = std::path::PathBuf::from(cwd);
|
||||||
|
|
||||||
|
let (file_extension, contents, contents_tag, span_source) =
|
||||||
|
crate::commands::open::fetch(
|
||||||
|
&full_path,
|
||||||
|
&location_clone,
|
||||||
|
Span::unknown(),
|
||||||
|
)
|
||||||
|
.await.unwrap();
|
||||||
|
|
||||||
|
if let Some(uuid) = contents_tag.origin {
|
||||||
|
// If we have loaded something, track its source
|
||||||
|
yield ReturnSuccess::action(CommandAction::AddSpanSource(
|
||||||
|
uuid,
|
||||||
|
span_source,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
match contents {
|
||||||
|
Value::Primitive(Primitive::String(_)) => {
|
||||||
|
let tagged_contents = contents.tagged(contents_tag);
|
||||||
|
|
||||||
|
if let Some(extension) = file_extension {
|
||||||
|
let command_name = format!("from-{}", extension);
|
||||||
|
if let Some(converter) =
|
||||||
|
registry.get_command(&command_name)
|
||||||
|
{
|
||||||
|
let new_args = RawCommandArgs {
|
||||||
|
host: raw_args.host,
|
||||||
|
shell_manager: raw_args.shell_manager,
|
||||||
|
call_info: UnevaluatedCallInfo {
|
||||||
|
args: crate::parser::hir::Call {
|
||||||
|
head: raw_args.call_info.args.head,
|
||||||
|
positional: None,
|
||||||
|
named: None,
|
||||||
|
},
|
||||||
|
source: raw_args.call_info.source,
|
||||||
|
source_map: raw_args.call_info.source_map,
|
||||||
|
name_span: raw_args.call_info.name_span,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let mut result = converter.run(
|
||||||
|
new_args.with_input(vec![tagged_contents]),
|
||||||
|
®istry,
|
||||||
|
);
|
||||||
|
let result_vec: Vec<Result<ReturnSuccess, ShellError>> =
|
||||||
|
result.drain_vec().await;
|
||||||
|
for res in result_vec {
|
||||||
|
match res {
|
||||||
|
Ok(ReturnSuccess::Value(Tagged {
|
||||||
|
item,
|
||||||
|
..
|
||||||
|
})) => {
|
||||||
|
yield Ok(ReturnSuccess::Action(CommandAction::EnterValueShell(
|
||||||
|
Tagged {
|
||||||
|
item: item,
|
||||||
|
tag: contents_tag,
|
||||||
|
})));
|
||||||
|
}
|
||||||
|
x => yield x,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
yield Ok(ReturnSuccess::Action(CommandAction::EnterValueShell(tagged_contents)));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
yield Ok(ReturnSuccess::Action(CommandAction::EnterValueShell(tagged_contents)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let tagged_contents = contents.tagged(contents_tag);
|
||||||
|
|
||||||
|
yield Ok(ReturnSuccess::Action(CommandAction::EnterValueShell(tagged_contents)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(stream.to_output_stream())
|
||||||
|
}
|
||||||
|
}
|
||||||
x => Ok(
|
x => Ok(
|
||||||
vec![Ok(ReturnSuccess::Action(CommandAction::EnterValueShell(
|
vec![Ok(ReturnSuccess::Action(CommandAction::EnterValueShell(
|
||||||
x.clone(),
|
x.clone(),
|
||||||
|
|
|
@ -27,6 +27,26 @@ impl WholeStreamCommand for FromSQLite {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct FromDB;
|
||||||
|
|
||||||
|
impl WholeStreamCommand for FromDB {
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
args: CommandArgs,
|
||||||
|
registry: &CommandRegistry,
|
||||||
|
) -> Result<OutputStream, ShellError> {
|
||||||
|
from_sqlite(args, registry)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"from-db"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::build("from-db")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn convert_sqlite_file_to_nu_value(
|
pub fn convert_sqlite_file_to_nu_value(
|
||||||
path: &Path,
|
path: &Path,
|
||||||
tag: impl Into<Tag> + Clone,
|
tag: impl Into<Tag> + Clone,
|
||||||
|
|
|
@ -23,6 +23,26 @@ impl WholeStreamCommand for FromYAML {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct FromYML;
|
||||||
|
|
||||||
|
impl WholeStreamCommand for FromYML {
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
args: CommandArgs,
|
||||||
|
registry: &CommandRegistry,
|
||||||
|
) -> Result<OutputStream, ShellError> {
|
||||||
|
from_yaml(args, registry)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"from-yml"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::build("from-yml")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn convert_yaml_value_to_nu_value(v: &serde_yaml::Value, tag: impl Into<Tag>) -> Tagged<Value> {
|
fn convert_yaml_value_to_nu_value(v: &serde_yaml::Value, tag: impl Into<Tag>) -> Tagged<Value> {
|
||||||
let tag = tag.into();
|
let tag = tag.into();
|
||||||
|
|
||||||
|
|
|
@ -16,10 +16,10 @@ impl PerItemCommand for Mkdir {
|
||||||
&self,
|
&self,
|
||||||
call_info: &CallInfo,
|
call_info: &CallInfo,
|
||||||
_registry: &CommandRegistry,
|
_registry: &CommandRegistry,
|
||||||
shell_manager: &ShellManager,
|
raw_args: &RawCommandArgs,
|
||||||
_input: Tagged<Value>,
|
_input: Tagged<Value>,
|
||||||
) -> Result<OutputStream, ShellError> {
|
) -> Result<OutputStream, ShellError> {
|
||||||
call_info.process(shell_manager, mkdir)?.run()
|
call_info.process(&raw_args.shell_manager, mkdir)?.run()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
|
|
|
@ -29,10 +29,10 @@ impl PerItemCommand for Move {
|
||||||
&self,
|
&self,
|
||||||
call_info: &CallInfo,
|
call_info: &CallInfo,
|
||||||
_registry: &CommandRegistry,
|
_registry: &CommandRegistry,
|
||||||
shell_manager: &ShellManager,
|
raw_args: &RawCommandArgs,
|
||||||
_input: Tagged<Value>,
|
_input: Tagged<Value>,
|
||||||
) -> Result<OutputStream, ShellError> {
|
) -> Result<OutputStream, ShellError> {
|
||||||
call_info.process(shell_manager, mv)?.run()
|
call_info.process(&raw_args.shell_manager, mv)?.run()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
|
use crate::commands::UnevaluatedCallInfo;
|
||||||
use crate::context::SpanSource;
|
use crate::context::SpanSource;
|
||||||
use crate::errors::ShellError;
|
use crate::errors::ShellError;
|
||||||
use crate::object::{Primitive, Value};
|
use crate::object::Value;
|
||||||
use crate::parser::hir::SyntaxType;
|
use crate::parser::hir::SyntaxType;
|
||||||
use crate::parser::registry::Signature;
|
use crate::parser::registry::Signature;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
@ -25,15 +26,20 @@ impl PerItemCommand for Open {
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
call_info: &CallInfo,
|
call_info: &CallInfo,
|
||||||
_registry: &CommandRegistry,
|
registry: &CommandRegistry,
|
||||||
shell_manager: &ShellManager,
|
raw_args: &RawCommandArgs,
|
||||||
_input: Tagged<Value>,
|
_input: Tagged<Value>,
|
||||||
) -> Result<OutputStream, ShellError> {
|
) -> Result<OutputStream, ShellError> {
|
||||||
run(call_info, shell_manager)
|
run(call_info, registry, raw_args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(call_info: &CallInfo, shell_manager: &ShellManager) -> Result<OutputStream, ShellError> {
|
fn run(
|
||||||
|
call_info: &CallInfo,
|
||||||
|
registry: &CommandRegistry,
|
||||||
|
raw_args: &RawCommandArgs,
|
||||||
|
) -> Result<OutputStream, ShellError> {
|
||||||
|
let shell_manager = &raw_args.shell_manager;
|
||||||
let cwd = PathBuf::from(shell_manager.path());
|
let cwd = PathBuf::from(shell_manager.path());
|
||||||
let full_path = PathBuf::from(cwd);
|
let full_path = PathBuf::from(cwd);
|
||||||
|
|
||||||
|
@ -47,8 +53,9 @@ fn run(call_info: &CallInfo, shell_manager: &ShellManager) -> Result<OutputStrea
|
||||||
let path_buf = path.as_path()?;
|
let path_buf = path.as_path()?;
|
||||||
let path_str = path_buf.display().to_string();
|
let path_str = path_buf.display().to_string();
|
||||||
let path_span = path.span();
|
let path_span = path.span();
|
||||||
let name_span = call_info.name_span;
|
|
||||||
let has_raw = call_info.args.has("raw");
|
let has_raw = call_info.args.has("raw");
|
||||||
|
let registry = registry.clone();
|
||||||
|
let raw_args = raw_args.clone();
|
||||||
|
|
||||||
let stream = async_stream_block! {
|
let stream = async_stream_block! {
|
||||||
|
|
||||||
|
@ -65,7 +72,6 @@ fn run(call_info: &CallInfo, shell_manager: &ShellManager) -> Result<OutputStrea
|
||||||
file_extension.or(path_str.split('.').last().map(String::from))
|
file_extension.or(path_str.split('.').last().map(String::from))
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
if let Some(uuid) = contents_tag.origin {
|
if let Some(uuid) = contents_tag.origin {
|
||||||
// If we have loaded something, track its source
|
// If we have loaded something, track its source
|
||||||
yield ReturnSuccess::action(CommandAction::AddSpanSource(
|
yield ReturnSuccess::action(CommandAction::AddSpanSource(
|
||||||
|
@ -74,39 +80,46 @@ fn run(call_info: &CallInfo, shell_manager: &ShellManager) -> Result<OutputStrea
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
match contents {
|
let tagged_contents = contents.tagged(contents_tag);
|
||||||
Value::Primitive(Primitive::String(string)) => {
|
|
||||||
let value = parse_string_as_value(file_extension, string, contents_tag, name_span).unwrap();
|
|
||||||
|
|
||||||
match value {
|
if let Some(extension) = file_extension {
|
||||||
Tagged {
|
let command_name = format!("from-{}", extension);
|
||||||
item: Value::List(list),
|
if let Some(converter) = registry.get_command(&command_name) {
|
||||||
..
|
let new_args = RawCommandArgs {
|
||||||
} => {
|
host: raw_args.host,
|
||||||
for elem in list {
|
shell_manager: raw_args.shell_manager,
|
||||||
yield ReturnSuccess::value(elem);
|
call_info: UnevaluatedCallInfo {
|
||||||
}
|
args: crate::parser::hir::Call {
|
||||||
|
head: raw_args.call_info.args.head,
|
||||||
|
positional: None,
|
||||||
|
named: None
|
||||||
|
},
|
||||||
|
source: raw_args.call_info.source,
|
||||||
|
source_map: raw_args.call_info.source_map,
|
||||||
|
name_span: raw_args.call_info.name_span,
|
||||||
}
|
}
|
||||||
x => yield ReturnSuccess::value(x),
|
};
|
||||||
}
|
let mut result = converter.run(new_args.with_input(vec![tagged_contents]), ®istry);
|
||||||
}
|
let result_vec: Vec<Result<ReturnSuccess, ShellError>> = result.drain_vec().await;
|
||||||
Value::Binary(binary) => {
|
for res in result_vec {
|
||||||
let value = parse_binary_as_value(file_extension, binary, contents_tag, name_span).unwrap();
|
match res {
|
||||||
|
Ok(ReturnSuccess::Value(Tagged { item: Value::List(list), ..})) => {
|
||||||
match value {
|
for l in list {
|
||||||
Tagged {
|
yield Ok(ReturnSuccess::Value(l));
|
||||||
item: Value::List(list),
|
}
|
||||||
..
|
|
||||||
} => {
|
|
||||||
for elem in list {
|
|
||||||
yield ReturnSuccess::value(elem);
|
|
||||||
}
|
}
|
||||||
|
Ok(ReturnSuccess::Value(Tagged { item, .. })) => {
|
||||||
|
yield Ok(ReturnSuccess::Value(Tagged { item: item, tag: contents_tag }));
|
||||||
|
}
|
||||||
|
x => yield x,
|
||||||
}
|
}
|
||||||
x => yield ReturnSuccess::value(x),
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
yield ReturnSuccess::value(tagged_contents);
|
||||||
}
|
}
|
||||||
other => yield ReturnSuccess::value(other.tagged(contents_tag)),
|
} else {
|
||||||
};
|
yield ReturnSuccess::value(tagged_contents);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(stream.to_output_stream())
|
Ok(stream.to_output_stream())
|
||||||
|
@ -419,124 +432,3 @@ fn read_be_u16(input: &[u8]) -> Option<Vec<u16>> {
|
||||||
Some(result)
|
Some(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_string_as_value(
|
|
||||||
extension: Option<String>,
|
|
||||||
contents: String,
|
|
||||||
contents_tag: Tag,
|
|
||||||
name_span: Span,
|
|
||||||
) -> Result<Tagged<Value>, ShellError> {
|
|
||||||
match extension {
|
|
||||||
Some(ref x) if x == "csv" => {
|
|
||||||
crate::commands::from_csv::from_csv_string_to_value(contents, false, contents_tag)
|
|
||||||
.map_err(move |_| {
|
|
||||||
ShellError::labeled_error(
|
|
||||||
"Could not open as CSV",
|
|
||||||
"could not open as CSV",
|
|
||||||
name_span,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
Some(ref x) if x == "tsv" => {
|
|
||||||
crate::commands::from_tsv::from_tsv_string_to_value(contents, false, contents_tag)
|
|
||||||
.map_err(move |_| {
|
|
||||||
ShellError::labeled_error(
|
|
||||||
"Could not open as TSV",
|
|
||||||
"could not open as TSV",
|
|
||||||
name_span,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
Some(ref x) if x == "toml" => {
|
|
||||||
crate::commands::from_toml::from_toml_string_to_value(contents, contents_tag).map_err(
|
|
||||||
move |_| {
|
|
||||||
ShellError::labeled_error(
|
|
||||||
"Could not open as TOML",
|
|
||||||
"could not open as TOML",
|
|
||||||
name_span,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Some(ref x) if x == "json" => {
|
|
||||||
crate::commands::from_json::from_json_string_to_value(contents, contents_tag).map_err(
|
|
||||||
move |_| {
|
|
||||||
ShellError::labeled_error(
|
|
||||||
"Could not open as JSON",
|
|
||||||
"could not open as JSON",
|
|
||||||
name_span,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Some(ref x) if x == "ini" => crate::commands::from_ini::from_ini_string_to_value(
|
|
||||||
contents,
|
|
||||||
contents_tag,
|
|
||||||
)
|
|
||||||
.map_err(move |_| {
|
|
||||||
ShellError::labeled_error("Could not open as INI", "could not open as INI", name_span)
|
|
||||||
}),
|
|
||||||
Some(ref x) if x == "xml" => crate::commands::from_xml::from_xml_string_to_value(
|
|
||||||
contents,
|
|
||||||
contents_tag,
|
|
||||||
)
|
|
||||||
.map_err(move |_| {
|
|
||||||
ShellError::labeled_error("Could not open as XML", "could not open as XML", name_span)
|
|
||||||
}),
|
|
||||||
Some(ref x) if x == "yml" => {
|
|
||||||
crate::commands::from_yaml::from_yaml_string_to_value(contents, contents_tag).map_err(
|
|
||||||
move |_| {
|
|
||||||
ShellError::labeled_error(
|
|
||||||
"Could not open as YAML",
|
|
||||||
"could not open as YAML",
|
|
||||||
name_span,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Some(ref x) if x == "yaml" => {
|
|
||||||
crate::commands::from_yaml::from_yaml_string_to_value(contents, contents_tag).map_err(
|
|
||||||
move |_| {
|
|
||||||
ShellError::labeled_error(
|
|
||||||
"Could not open as YAML",
|
|
||||||
"could not open as YAML",
|
|
||||||
name_span,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
_ => Ok(Value::string(contents).tagged(contents_tag)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse_binary_as_value(
|
|
||||||
extension: Option<String>,
|
|
||||||
contents: Vec<u8>,
|
|
||||||
contents_tag: Tag,
|
|
||||||
name_span: Span,
|
|
||||||
) -> Result<Tagged<Value>, ShellError> {
|
|
||||||
match extension {
|
|
||||||
Some(ref x) if x == "bson" => {
|
|
||||||
crate::commands::from_bson::from_bson_bytes_to_value(contents, contents_tag).map_err(
|
|
||||||
move |_| {
|
|
||||||
ShellError::labeled_error(
|
|
||||||
"Could not open as BSON",
|
|
||||||
"could not open as BSON",
|
|
||||||
name_span,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Some(ref x) if x == "db" => {
|
|
||||||
crate::commands::from_sqlite::from_sqlite_bytes_to_value(contents, contents_tag)
|
|
||||||
.map_err(move |_| {
|
|
||||||
ShellError::labeled_error(
|
|
||||||
"Could not open as SQLite",
|
|
||||||
"could not open as SQLite",
|
|
||||||
name_span,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
_ => Ok(Value::Binary(contents).tagged(contents_tag)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -28,10 +28,10 @@ impl PerItemCommand for Remove {
|
||||||
&self,
|
&self,
|
||||||
call_info: &CallInfo,
|
call_info: &CallInfo,
|
||||||
_registry: &CommandRegistry,
|
_registry: &CommandRegistry,
|
||||||
shell_manager: &ShellManager,
|
raw_args: &RawCommandArgs,
|
||||||
_input: Tagged<Value>,
|
_input: Tagged<Value>,
|
||||||
) -> Result<OutputStream, ShellError> {
|
) -> Result<OutputStream, ShellError> {
|
||||||
call_info.process(shell_manager, rm)?.run()
|
call_info.process(&raw_args.shell_manager, rm)?.run()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,4 @@
|
||||||
use crate::commands::to_csv::{to_string as to_csv_to_string, value_to_csv_value};
|
use crate::commands::UnevaluatedCallInfo;
|
||||||
use crate::commands::to_tsv::{to_string as to_tsv_to_string, value_to_tsv_value};
|
|
||||||
use crate::commands::to_json::value_to_json_value;
|
|
||||||
use crate::commands::to_toml::value_to_toml_value;
|
|
||||||
use crate::commands::to_yaml::value_to_yaml_value;
|
|
||||||
use crate::commands::WholeStreamCommand;
|
use crate::commands::WholeStreamCommand;
|
||||||
use crate::errors::ShellError;
|
use crate::errors::ShellError;
|
||||||
use crate::object::Value;
|
use crate::object::Value;
|
||||||
|
@ -33,7 +29,7 @@ impl WholeStreamCommand for Save {
|
||||||
args: CommandArgs,
|
args: CommandArgs,
|
||||||
registry: &CommandRegistry,
|
registry: &CommandRegistry,
|
||||||
) -> Result<OutputStream, ShellError> {
|
) -> Result<OutputStream, ShellError> {
|
||||||
args.process(registry, save)?.run()
|
Ok(args.process_raw(registry, save)?.run())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,16 +43,19 @@ fn save(
|
||||||
name,
|
name,
|
||||||
shell_manager,
|
shell_manager,
|
||||||
source_map,
|
source_map,
|
||||||
|
host,
|
||||||
|
commands: registry,
|
||||||
..
|
..
|
||||||
}: RunnableContext,
|
}: RunnableContext,
|
||||||
|
raw_args: RawCommandArgs,
|
||||||
) -> Result<OutputStream, ShellError> {
|
) -> Result<OutputStream, ShellError> {
|
||||||
let mut full_path = PathBuf::from(shell_manager.path());
|
let mut full_path = PathBuf::from(shell_manager.path());
|
||||||
let name_span = name;
|
let name_span = name;
|
||||||
|
|
||||||
if path.is_none() {
|
let source_map = source_map.clone();
|
||||||
let source_map = source_map.clone();
|
let stream = async_stream_block! {
|
||||||
let stream = async_stream_block! {
|
let input: Vec<Tagged<Value>> = input.values.collect().await;
|
||||||
let input: Vec<Tagged<Value>> = input.values.collect().await;
|
if path.is_none() {
|
||||||
// If there is no filename, check the metadata for the origin filename
|
// If there is no filename, check the metadata for the origin filename
|
||||||
if input.len() > 0 {
|
if input.len() > 0 {
|
||||||
let origin = input[0].origin();
|
let origin = input[0].origin();
|
||||||
|
@ -88,50 +87,99 @@ fn save(
|
||||||
name_span,
|
name_span,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
let content = if !save_raw {
|
if let Some(file) = path {
|
||||||
to_string_for(full_path.extension(), &input)
|
full_path.push(file.item());
|
||||||
} else {
|
|
||||||
string_from(&input)
|
|
||||||
};
|
|
||||||
|
|
||||||
match content {
|
|
||||||
Ok(save_data) => match std::fs::write(full_path, save_data) {
|
|
||||||
Ok(o) => o,
|
|
||||||
Err(e) => yield Err(ShellError::string(e.to_string())),
|
|
||||||
},
|
|
||||||
Err(e) => yield Err(ShellError::string(e.to_string())),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(OutputStream::new(stream))
|
|
||||||
} else {
|
|
||||||
if let Some(file) = path {
|
|
||||||
full_path.push(file.item());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let stream = async_stream_block! {
|
let content = if !save_raw {
|
||||||
let input: Vec<Tagged<Value>> = input.values.collect().await;
|
if let Some(extension) = full_path.extension() {
|
||||||
|
let command_name = format!("to-{}", extension.to_str().unwrap());
|
||||||
let content = if !save_raw {
|
if let Some(converter) = registry.get_command(&command_name) {
|
||||||
to_string_for(full_path.extension(), &input)
|
let new_args = RawCommandArgs {
|
||||||
|
host: host,
|
||||||
|
shell_manager: shell_manager,
|
||||||
|
call_info: UnevaluatedCallInfo {
|
||||||
|
args: crate::parser::hir::Call {
|
||||||
|
head: raw_args.call_info.args.head,
|
||||||
|
positional: None,
|
||||||
|
named: None
|
||||||
|
},
|
||||||
|
source: raw_args.call_info.source,
|
||||||
|
source_map: raw_args.call_info.source_map,
|
||||||
|
name_span: raw_args.call_info.name_span,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let mut result = converter.run(new_args.with_input(input), ®istry);
|
||||||
|
let result_vec: Vec<Result<ReturnSuccess, ShellError>> = result.drain_vec().await;
|
||||||
|
let mut result_string = String::new();
|
||||||
|
for res in result_vec {
|
||||||
|
match res {
|
||||||
|
Ok(ReturnSuccess::Value(Tagged { item: Value::Primitive(Primitive::String(s)), .. })) => {
|
||||||
|
result_string.push_str(&s);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
yield Err(ShellError::labeled_error(
|
||||||
|
"Save could not successfully save",
|
||||||
|
"unexpected data during saveS",
|
||||||
|
name_span,
|
||||||
|
));
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(result_string)
|
||||||
|
} else {
|
||||||
|
let mut result_string = String::new();
|
||||||
|
for res in input {
|
||||||
|
match res {
|
||||||
|
Tagged { item: Value::Primitive(Primitive::String(s)), .. } => {
|
||||||
|
result_string.push_str(&s);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
yield Err(ShellError::labeled_error(
|
||||||
|
"Save could not successfully save",
|
||||||
|
"unexpected data during saveS",
|
||||||
|
name_span,
|
||||||
|
));
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(result_string)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
string_from(&input)
|
let mut result_string = String::new();
|
||||||
};
|
for res in input {
|
||||||
|
match res {
|
||||||
match content {
|
Tagged { item: Value::Primitive(Primitive::String(s)), .. } => {
|
||||||
Ok(save_data) => match std::fs::write(full_path, save_data) {
|
result_string.push_str(&s);
|
||||||
Ok(o) => o,
|
}
|
||||||
Err(e) => yield Err(ShellError::string(e.to_string())),
|
_ => {
|
||||||
},
|
yield Err(ShellError::labeled_error(
|
||||||
Err(e) => yield Err(ShellError::string(e.to_string())),
|
"Save could not successfully save",
|
||||||
|
"unexpected data during saveS",
|
||||||
|
name_span,
|
||||||
|
));
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(result_string)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
string_from(&input)
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(OutputStream::new(stream))
|
match content {
|
||||||
}
|
Ok(save_data) => match std::fs::write(full_path, save_data) {
|
||||||
|
Ok(o) => o,
|
||||||
|
Err(e) => yield Err(ShellError::string(e.to_string())),
|
||||||
|
},
|
||||||
|
Err(e) => yield Err(ShellError::string(e.to_string())),
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(OutputStream::new(stream))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn string_from(input: &Vec<Tagged<Value>>) -> Result<String, ShellError> {
|
fn string_from(input: &Vec<Tagged<Value>>) -> Result<String, ShellError> {
|
||||||
|
@ -153,66 +201,3 @@ fn string_from(input: &Vec<Tagged<Value>>) -> Result<String, ShellError> {
|
||||||
|
|
||||||
Ok(save_data)
|
Ok(save_data)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_string_for(
|
|
||||||
ext: Option<&std::ffi::OsStr>,
|
|
||||||
input: &Vec<Tagged<Value>>,
|
|
||||||
) -> Result<String, ShellError> {
|
|
||||||
let contents = match ext {
|
|
||||||
Some(x) if x == "csv" => {
|
|
||||||
if input.len() != 1 {
|
|
||||||
return Err(ShellError::string(
|
|
||||||
"saving to csv requires a single object (or use --raw)",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
to_csv_to_string(&value_to_csv_value(&input[0]))?
|
|
||||||
}
|
|
||||||
Some(x) if x == "tsv" => {
|
|
||||||
if input.len() != 1 {
|
|
||||||
return Err(ShellError::string(
|
|
||||||
"saving to tsv requires a single object (or use --raw)",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
to_tsv_to_string(&value_to_tsv_value(&input[0]))?
|
|
||||||
}
|
|
||||||
Some(x) if x == "toml" => {
|
|
||||||
if input.len() != 1 {
|
|
||||||
return Err(ShellError::string(
|
|
||||||
"saving to toml requires a single object (or use --raw)",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
toml::to_string(&value_to_toml_value(&input[0]))?
|
|
||||||
}
|
|
||||||
Some(x) if x == "json" => {
|
|
||||||
if input.len() != 1 {
|
|
||||||
return Err(ShellError::string(
|
|
||||||
"saving to json requires a single object (or use --raw)",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
serde_json::to_string(&value_to_json_value(&input[0]))?
|
|
||||||
}
|
|
||||||
Some(x) if x == "yml" => {
|
|
||||||
if input.len() != 1 {
|
|
||||||
return Err(ShellError::string(
|
|
||||||
"saving to yml requires a single object (or use --raw)",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
serde_yaml::to_string(&value_to_yaml_value(&input[0]))?
|
|
||||||
}
|
|
||||||
Some(x) if x == "yaml" => {
|
|
||||||
if input.len() != 1 {
|
|
||||||
return Err(ShellError::string(
|
|
||||||
"saving to yaml requires a single object (or use --raw)",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
serde_yaml::to_string(&value_to_yaml_value(&input[0]))?
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
return Err(ShellError::string(
|
|
||||||
"tried saving a single object with an unrecognized format.",
|
|
||||||
))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(contents)
|
|
||||||
}
|
|
||||||
|
|
|
@ -25,6 +25,26 @@ impl WholeStreamCommand for ToSQLite {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct ToDB;
|
||||||
|
|
||||||
|
impl WholeStreamCommand for ToDB {
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
args: CommandArgs,
|
||||||
|
registry: &CommandRegistry,
|
||||||
|
) -> Result<OutputStream, ShellError> {
|
||||||
|
to_sqlite(args, registry)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"to-db"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::build("to-db")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn comma_concat(acc: String, current: String) -> String {
|
fn comma_concat(acc: String, current: String) -> String {
|
||||||
if acc == "" {
|
if acc == "" {
|
||||||
current
|
current
|
||||||
|
|
|
@ -19,7 +19,7 @@ impl PerItemCommand for Where {
|
||||||
&self,
|
&self,
|
||||||
call_info: &CallInfo,
|
call_info: &CallInfo,
|
||||||
_registry: ®istry::CommandRegistry,
|
_registry: ®istry::CommandRegistry,
|
||||||
_shell_manager: &ShellManager,
|
_raw_args: &RawCommandArgs,
|
||||||
input: Tagged<Value>,
|
input: Tagged<Value>,
|
||||||
) -> Result<OutputStream, ShellError> {
|
) -> Result<OutputStream, ShellError> {
|
||||||
let input_clone = input.clone();
|
let input_clone = input.clone();
|
||||||
|
|
|
@ -39,11 +39,11 @@ pub fn path(head: impl Into<Expression>, tail: Vec<Tagged<impl Into<String>>>) -
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Getters, Serialize, Deserialize, new)]
|
#[derive(Debug, Clone, Eq, PartialEq, Getters, Serialize, Deserialize, new)]
|
||||||
pub struct Call {
|
pub struct Call {
|
||||||
#[get = "crate"]
|
#[get = "crate"]
|
||||||
head: Box<Expression>,
|
pub head: Box<Expression>,
|
||||||
#[get = "crate"]
|
#[get = "crate"]
|
||||||
positional: Option<Vec<Expression>>,
|
pub positional: Option<Vec<Expression>>,
|
||||||
#[get = "crate"]
|
#[get = "crate"]
|
||||||
named: Option<NamedArguments>,
|
pub named: Option<NamedArguments>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Call {
|
impl Call {
|
||||||
|
|
|
@ -52,6 +52,7 @@ pub(crate) use crate::commands::command::{
|
||||||
CallInfo, CommandAction, CommandArgs, ReturnSuccess, ReturnValue, RunnableContext,
|
CallInfo, CommandAction, CommandArgs, ReturnSuccess, ReturnValue, RunnableContext,
|
||||||
};
|
};
|
||||||
pub(crate) use crate::commands::PerItemCommand;
|
pub(crate) use crate::commands::PerItemCommand;
|
||||||
|
pub(crate) use crate::commands::RawCommandArgs;
|
||||||
pub(crate) use crate::context::CommandRegistry;
|
pub(crate) use crate::context::CommandRegistry;
|
||||||
pub(crate) use crate::context::{Context, SpanSource};
|
pub(crate) use crate::context::{Context, SpanSource};
|
||||||
pub(crate) use crate::env::host::handle_unexpected;
|
pub(crate) use crate::env::host::handle_unexpected;
|
||||||
|
|
Loading…
Reference in a new issue