#![allow(clippy::module_inception)]
#![allow(non_local_definitions)] use crate::attrs::{AttrMap, Date, DateTime, FromAttribute, Time};
use crate::expressions::ExprResult;
use crate::plugins::{load_library_safe, NadiPlugin};
use crate::prelude::*;
use crate::table::{contents_2_md, ColumnAlign};
use crate::timeseries::{FromSeries, Series, TimeSeries};
use abi_stable::std_types::Tuple2;
use abi_stable::{
sabi_trait,
std_types::{
map::REntry,
RBox, RErr, RHashMap, ROk,
ROption::{self, RNone, RSome},
RResult, RString, RVec,
},
StableAbi,
};
use colored::Colorize;
use std::collections::HashMap;
use std::fs::File;
use std::io::{BufWriter, Write};
use std::path::Path;
pub type FunctionArgs<'a> = &'a [FunctionInput<'a>];
pub type FunctionKwArgs<'a> = &'a RHashMap<RString, FunctionInput<'a>>;
#[cfg(feature = "functions")]
use crate::tasks::FunctionType;
#[repr(C)]
#[derive(StableAbi, Default, Debug)]
pub enum FunctionInput<'a> {
#[default]
None,
AttrOwn(Attribute),
SeriesOwn(Series),
TsOwn(TimeSeries),
Attr(&'a Attribute),
Series(&'a Series),
Ts(&'a TimeSeries),
}
impl From<Option<Attribute>> for FunctionInput<'_> {
fn from(value: Option<Attribute>) -> Self {
value.map(Self::AttrOwn).unwrap_or(Self::None)
}
}
impl<'a> From<Option<&'a Attribute>> for FunctionInput<'a> {
fn from(value: Option<&'a Attribute>) -> Self {
value.map(Self::Attr).unwrap_or(Self::None)
}
}
impl From<ROption<Attribute>> for FunctionInput<'_> {
fn from(value: ROption<Attribute>) -> Self {
value.map(Self::AttrOwn).unwrap_or(Self::None)
}
}
impl<'a> From<ROption<&'a Attribute>> for FunctionInput<'a> {
fn from(value: ROption<&'a Attribute>) -> Self {
value.map(Self::Attr).unwrap_or(Self::None)
}
}
impl<'a> From<&'a ROption<Attribute>> for FunctionInput<'a> {
fn from(value: &'a ROption<Attribute>) -> Self {
value.as_ref().map(Self::Attr).unwrap_or(Self::None)
}
}
impl From<Attribute> for FunctionInput<'_> {
fn from(value: Attribute) -> Self {
Self::AttrOwn(value)
}
}
impl<'a> From<&'a Attribute> for FunctionInput<'a> {
fn from(value: &'a Attribute) -> Self {
Self::Attr(value)
}
}
impl From<Series> for FunctionInput<'_> {
fn from(value: Series) -> Self {
Self::SeriesOwn(value)
}
}
impl From<TimeSeries> for FunctionInput<'_> {
fn from(value: TimeSeries) -> Self {
Self::TsOwn(value)
}
}
impl<'a> FunctionInput<'a> {
fn clone_as_ref(&'a self) -> Self {
match self {
Self::None => Self::None,
Self::Attr(a) => Self::Attr(a),
Self::AttrOwn(a) => Self::Attr(a),
Self::Series(s) => Self::Series(s),
Self::SeriesOwn(s) => Self::Series(s),
Self::Ts(ts) => Self::Ts(ts),
Self::TsOwn(ts) => Self::Ts(ts),
}
}
}
impl FunctionInput<'_> {
pub fn type_name(&self) -> String {
match self {
Self::None => "<None>".into(),
Self::Attr(a) => a.type_name().into(),
Self::AttrOwn(a) => a.type_name().into(),
Self::Series(s) => format!("Series<{}>", s.type_name()),
Self::SeriesOwn(s) => format!("Series<{}>", s.type_name()),
Self::Ts(ts) => format!("TimeSeries<{}>", ts.series().type_name()),
Self::TsOwn(ts) => format!("TimeSeries<{}>", ts.series().type_name()),
}
}
pub fn is_none(&self) -> bool {
matches!(self, FunctionInput::None)
}
pub fn attribute(&self) -> Result<Attribute, EvalError> {
match self {
Self::None => Err(EvalErrorType::EmptyValue(None).no_pos()),
Self::Attr(&ref a) => Ok(a.clone()),
Self::AttrOwn(a) => Ok(a.clone()),
_ => Err(EvalErrorType::EmptyValue(None).no_pos()),
}
}
pub fn attribute_ref<'a>(&'a self) -> Result<&'a Attribute, EvalError> {
match self {
Self::None => Err(EvalErrorType::EmptyValue(None).no_pos()),
Self::Attr(&ref a) => Ok(a),
Self::AttrOwn(a) => Ok(a),
_ => Err(EvalErrorType::EmptyValue(None).no_pos()),
}
}
pub fn attr_relaxed<P: FromAttributeRelaxed>(&self) -> Option<Result<P, String>> {
match self {
Self::None => None,
Self::Attr(arg) => Some(FromAttributeRelaxed::try_from_attr_relaxed(arg)),
Self::AttrOwn(arg) => Some(FromAttributeRelaxed::try_from_attr_relaxed(arg)),
_ => Some(Err(format!(
"can not be constructed from {}",
self.type_name()
))),
}
}
}
pub trait ResolveFuncArg<'a>: Sized {
fn resolve_arg(val: &'a FunctionInput<'a>) -> Option<Self>;
}
impl<'a, T: ResolveFuncArg<'a>> ResolveFuncArg<'a> for Option<T> {
fn resolve_arg(val: &'a FunctionInput<'a>) -> Option<Self> {
match val {
FunctionInput::None => Some(None),
_ => ResolveFuncArg::resolve_arg(val).map(Some),
}
}
}
impl<'a, T: ResolveFuncArg<'a>> ResolveFuncArg<'a> for ROption<T> {
fn resolve_arg(val: &'a FunctionInput<'a>) -> Option<Self> {
match val {
FunctionInput::None => Some(RNone),
_ => ResolveFuncArg::resolve_arg(val).map(RSome),
}
}
}
impl<'a, T: FromAttribute> ResolveFuncArg<'a> for T {
fn resolve_arg(val: &FunctionInput<'a>) -> Option<Self> {
match val {
FunctionInput::Attr(a) => FromAttribute::from_attr(a),
FunctionInput::AttrOwn(a) => FromAttribute::from_attr(a),
_ => None,
}
}
}
macro_rules! attr_ref_types_impl {
($t: tt, $x: path) => {
impl<'a> ResolveFuncArg<'a> for &'a $t {
fn resolve_arg(val: &'a FunctionInput<'a>) -> Option<Self> {
match val {
FunctionInput::Attr($x(a)) => Some(a),
FunctionInput::AttrOwn($x(a)) => Some(a),
_ => None,
}
}
}
impl<'a> ResolveFuncArg<'a> for &'a [$t] {
fn resolve_arg(val: &'a FunctionInput<'a>) -> Option<Self> {
match val {
FunctionInput::Series(a) => FromSeries::from_series(a),
FunctionInput::SeriesOwn(a) => FromSeries::from_series(a),
FunctionInput::Ts(a) => FromSeries::from_series(a.series()),
FunctionInput::TsOwn(a) => FromSeries::from_series(a.series()),
_ => None,
}
}
}
impl<'a> ResolveFuncArg<'a> for &'a [ROption<$t>] {
fn resolve_arg(val: &'a FunctionInput<'a>) -> Option<Self> {
match val {
FunctionInput::Series(a) => FromSeries::from_series(a),
FunctionInput::SeriesOwn(a) => FromSeries::from_series(a),
FunctionInput::Ts(a) => FromSeries::from_series(a.series()),
FunctionInput::TsOwn(a) => FromSeries::from_series(a.series()),
_ => None,
}
}
}
};
}
attr_ref_types_impl!(bool, Attribute::Bool);
attr_ref_types_impl!(RString, Attribute::String);
attr_ref_types_impl!(i64, Attribute::Integer);
attr_ref_types_impl!(f64, Attribute::Float);
attr_ref_types_impl!(Date, Attribute::Date);
attr_ref_types_impl!(Time, Attribute::Time);
attr_ref_types_impl!(DateTime, Attribute::DateTime);
impl<'a> ResolveFuncArg<'a> for &'a FunctionInput<'a> {
fn resolve_arg(val: &'a FunctionInput<'a>) -> Option<Self> {
Some(val)
}
}
impl<'a> ResolveFuncArg<'a> for FunctionInput<'a> {
fn resolve_arg(val: &'a FunctionInput<'a>) -> Option<Self> {
Some(val.clone_as_ref())
}
}
impl<'a> ResolveFuncArg<'a> for &'a Attribute {
fn resolve_arg(val: &'a FunctionInput<'a>) -> Option<Self> {
match val {
FunctionInput::Attr(a) => Some(a),
FunctionInput::AttrOwn(a) => Some(a),
_ => None,
}
}
}
impl<'a> ResolveFuncArg<'a> for &'a str {
fn resolve_arg(val: &'a FunctionInput<'a>) -> Option<Self> {
match val {
FunctionInput::Attr(Attribute::String(a)) => Some(a.as_str()),
FunctionInput::AttrOwn(Attribute::String(a)) => Some(a.as_str()),
_ => None,
}
}
}
impl<'a> ResolveFuncArg<'a> for &'a Path {
fn resolve_arg(val: &'a FunctionInput<'a>) -> Option<Self> {
match val {
FunctionInput::Attr(Attribute::String(a)) => Some(a.as_str().as_ref()),
FunctionInput::AttrOwn(Attribute::String(a)) => Some(a.as_str().as_ref()),
_ => None,
}
}
}
impl<'a> ResolveFuncArg<'a> for &'a AttrMap {
fn resolve_arg(val: &'a FunctionInput<'a>) -> Option<Self> {
match val {
FunctionInput::Attr(Attribute::Table(a)) => Some(a),
FunctionInput::AttrOwn(Attribute::Table(a)) => Some(a),
_ => None,
}
}
}
impl<'a> ResolveFuncArg<'a> for &'a [Attribute] {
fn resolve_arg(val: &'a FunctionInput<'a>) -> Option<Self> {
match val {
FunctionInput::Attr(Attribute::Array(a)) => Some(a.as_slice()),
FunctionInput::AttrOwn(Attribute::Array(a)) => Some(a.as_slice()),
FunctionInput::Series(a) => FromSeries::from_series(a),
FunctionInput::SeriesOwn(a) => FromSeries::from_series(a),
FunctionInput::Ts(a) => FromSeries::from_series(a.series()),
FunctionInput::TsOwn(a) => FromSeries::from_series(a.series()),
_ => None,
}
}
}
impl<'a> ResolveFuncArg<'a> for Series {
fn resolve_arg(val: &'a FunctionInput<'a>) -> Option<Self> {
match val {
FunctionInput::Series(&ref a) => Some(a.clone()),
FunctionInput::SeriesOwn(a) => Some(a.clone()),
FunctionInput::Ts(a) => Some(a.series().clone()),
FunctionInput::TsOwn(a) => Some(a.series().clone()),
_ => None,
}
}
}
impl<'a> ResolveFuncArg<'a> for &'a Series {
fn resolve_arg(val: &'a FunctionInput<'a>) -> Option<Self> {
match val {
FunctionInput::Series(a) => Some(a),
FunctionInput::SeriesOwn(a) => Some(&a),
FunctionInput::Ts(a) => Some(a.series()),
FunctionInput::TsOwn(a) => Some(a.series()),
_ => None,
}
}
}
impl<'a> ResolveFuncArg<'a> for TimeSeries {
fn resolve_arg(val: &'a FunctionInput<'a>) -> Option<Self> {
match val {
FunctionInput::Ts(&ref a) => Some(a.clone()),
FunctionInput::TsOwn(a) => Some(a.clone()),
_ => None,
}
}
}
impl<'a> ResolveFuncArg<'a> for &'a TimeSeries {
fn resolve_arg(val: &'a FunctionInput<'a>) -> Option<Self> {
match val {
FunctionInput::Ts(a) => Some(a),
FunctionInput::TsOwn(a) => Some(&a),
_ => None,
}
}
}
#[repr(C)]
#[derive(StableAbi, Default)]
pub enum FunctionRet {
#[default]
None,
Some(Attribute),
Series(Series),
Ts(TimeSeries),
Error(RString),
Image(RString),
Images(RVec<RString>),
File(RString),
Doc(RString),
}
impl FunctionRet {
pub fn res(self) -> Result<ExprResult, String> {
match self {
Self::None => Ok(ExprResult::None),
Self::Some(a) => Ok(ExprResult::Val(a)),
Self::Series(a) => Ok(ExprResult::Series(a)),
Self::Ts(a) => Ok(ExprResult::TimeSeries(a)),
Self::Image(a) => Ok(ExprResult::Image(a.to_string())),
Self::Images(a) => Ok(ExprResult::Images(
a.into_iter().map(|v| v.to_string()).collect(),
)),
Self::File(a) => Ok(ExprResult::File(a.to_string())),
Self::Doc(a) => Ok(ExprResult::Doc(a.to_string())),
Self::Error(e) => Err(e.to_string()),
}
}
}
impl From<()> for FunctionRet {
fn from(_value: ()) -> Self {
Self::None
}
}
impl<T> From<T> for FunctionRet
where
Attribute: From<T>,
{
fn from(value: T) -> Self {
Self::Some(Attribute::from(value))
}
}
impl From<Series> for FunctionRet {
fn from(value: Series) -> Self {
Self::Series(value)
}
}
impl From<TimeSeries> for FunctionRet {
fn from(value: TimeSeries) -> Self {
Self::Ts(value)
}
}
impl From<std::io::Error> for FunctionRet {
fn from(value: std::io::Error) -> Self {
Self::Error(RString::from(value.to_string()))
}
}
impl<T> From<Option<T>> for FunctionRet
where
FunctionRet: From<T>,
{
fn from(value: Option<T>) -> Self {
match value {
Some(v) => Self::from(v),
None => Self::None,
}
}
}
impl<T> From<ROption<T>> for FunctionRet
where
FunctionRet: From<T>,
{
fn from(value: ROption<T>) -> Self {
match value {
RSome(v) => Self::from(v),
RNone => Self::None,
}
}
}
impl<T, S> From<RResult<T, S>> for FunctionRet
where
FunctionRet: From<T>,
RString: From<S>,
{
fn from(value: RResult<T, S>) -> Self {
match value {
ROk(v) => Self::from(v),
RErr(e) => Self::Error(RString::from(e)),
}
}
}
impl<T, S> From<Result<T, S>> for FunctionRet
where
FunctionRet: From<T>,
S: ToString,
{
fn from(value: Result<T, S>) -> Self {
match value {
Ok(v) => Self::from(v),
Err(e) => Self::Error(RString::from(e.to_string())),
}
}
}
#[repr(C)]
#[derive(StableAbi, Debug, Clone)]
pub struct FuncArg {
pub name: RString,
pub ty: RString,
pub help: RString,
pub category: FuncArgType,
}
impl std::fmt::Display for FuncArg {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match &self.category {
FuncArgType::Arg => write!(f, "{}: '{}'", self.name, self.ty),
FuncArgType::OptArg => write!(f, "{}: '{}'", self.name, self.ty),
FuncArgType::DefArg(val) => write!(f, "{}: '{}' = {}", self.name, self.ty, val),
FuncArgType::Args => write!(f, "*{}", self.name),
FuncArgType::KwArgs => write!(f, "**{}", self.name),
}
}
}
impl FuncArg {
fn to_colored_string(&self) -> String {
match &self.category {
FuncArgType::Arg => format!("{}: '{}'", self.name, self.ty.green()),
FuncArgType::OptArg => format!("{}: '{}'", self.name, self.ty.green()),
FuncArgType::DefArg(val) => {
format!("{}: '{}' = {}", self.name, self.ty.green(), val.yellow())
}
FuncArgType::Args => format!("*{}", self.name.red()),
FuncArgType::KwArgs => format!("**{}", self.name.red()),
}
}
}
#[repr(C)]
#[derive(StableAbi, Clone, Debug)]
pub enum FuncArgType {
Arg,
OptArg,
DefArg(RString),
Args,
KwArgs,
}
#[sabi_trait]
pub trait EnvFunction: Debug + Clone + Send + Sync {
fn name(&self) -> RString;
fn help(&self) -> RString;
fn short_help(&self) -> RString {
self.help()
.trim()
.split('\n')
.next()
.unwrap_or("No Help")
.into()
}
fn has_kw_args(&self) -> bool {
self.args()
.iter()
.any(|a| matches!(a.category, FuncArgType::Args | FuncArgType::KwArgs))
}
fn args(&self) -> RVec<FuncArg>;
fn signature(&self) -> RString {
self.args()
.iter()
.map(|f| f.to_string())
.collect::<Vec<String>>()
.join(", ")
.into()
}
fn code(&self) -> RString;
fn call(&self, ctx: &FunctionCtx) -> FunctionRet;
}
#[sabi_trait]
pub trait NodeFunction: Debug + Clone + Send + Sync {
fn name(&self) -> RString;
fn help(&self) -> RString;
fn short_help(&self) -> RString {
self.help()
.trim()
.split('\n')
.next()
.unwrap_or("No Help")
.into()
}
fn has_kw_args(&self) -> bool {
self.args()
.iter()
.any(|a| matches!(a.category, FuncArgType::Args | FuncArgType::KwArgs))
}
fn args(&self) -> RVec<FuncArg>;
fn signature(&self) -> RString {
self.args()
.iter()
.map(|f| f.to_string())
.collect::<Vec<String>>()
.join(", ")
.into()
}
fn code(&self) -> RString;
fn call(&self, _obj: &NodeInner, _ctx: &FunctionCtx) -> FunctionRet {
FunctionRet::Error(RString::from("Function mutates the node"))
}
fn call_mut(&self, obj: &mut NodeInner, ctx: &FunctionCtx) -> FunctionRet {
self.call(obj, ctx)
}
}
#[sabi_trait]
pub trait NetworkFunction: Debug + Clone + Send + Sync {
fn name(&self) -> RString;
fn help(&self) -> RString;
fn short_help(&self) -> RString {
self.help()
.trim()
.split('\n')
.next()
.unwrap_or("No Help")
.into()
}
fn has_kw_args(&self) -> bool {
self.args()
.iter()
.any(|a| matches!(a.category, FuncArgType::Args | FuncArgType::KwArgs))
}
fn args(&self) -> RVec<FuncArg>;
fn signature(&self) -> RString {
self.args()
.iter()
.map(|f| f.to_string())
.collect::<Vec<String>>()
.join(", ")
.into()
}
fn code(&self) -> RString;
fn call(&self, _obj: &Network, _ctx: &FunctionCtx) -> FunctionRet {
FunctionRet::Error(RString::from("Function mutates the network"))
}
fn call_mut(&self, obj: &mut Network, ctx: &FunctionCtx) -> FunctionRet {
self.call(obj, ctx)
}
}
pub type EnvFunctionBox = EnvFunction_TO<'static, RBox<()>>;
pub type NodeFunctionBox = NodeFunction_TO<'static, RBox<()>>;
pub type NetworkFunctionBox = NetworkFunction_TO<'static, RBox<()>>;
#[repr(C)]
#[derive(StableAbi, Default, Clone)]
pub struct PluginFunctions {
env: RVec<RString>,
node: RVec<RString>,
network: RVec<RString>,
}
impl PluginFunctions {
pub fn with_network(mut self, func: RString) -> Self {
self.network.push(func);
self
}
pub fn with_node(mut self, func: RString) -> Self {
self.node.push(func);
self
}
pub fn with_env(mut self, func: RString) -> Self {
self.env.push(func);
self
}
pub fn push_network(&mut self, func: RString) {
self.network.push(func);
}
pub fn push_node(&mut self, func: RString) {
self.node.push(func);
}
pub fn push_env(&mut self, func: RString) {
self.env.push(func);
}
pub fn network(&self) -> &RVec<RString> {
&self.network
}
pub fn node(&self) -> &RVec<RString> {
&self.node
}
pub fn env(&self) -> &RVec<RString> {
&self.env
}
}
#[repr(C)]
#[derive(StableAbi, Default, Clone)]
pub struct NadiFunctions {
env: RHashMap<RString, EnvFunctionBox>,
env_alias: RHashMap<RString, RString>,
node: RHashMap<RString, NodeFunctionBox>,
node_alias: RHashMap<RString, RString>,
network: RHashMap<RString, NetworkFunctionBox>,
network_alias: RHashMap<RString, RString>,
plugins: RHashMap<RString, PluginFunctions>,
}
impl NadiFunctions {
#[cfg(feature = "functions")]
pub fn internals() -> Self {
let mut funcs = Self::default();
crate::internal::register_internal(&mut funcs);
funcs
}
#[cfg(feature = "functions")]
pub fn internals_w_plugins() -> Self {
let mut funcs = Self::internals();
funcs.load_plugins().unwrap();
funcs
}
pub fn remove_plugin(&mut self, plugin: &str) {
if let Some(p) = self.plugins.get(plugin) {
for func in &p.env {
let n = format!("{plugin}.{func}");
self.env.remove(n.as_str());
self.env_alias.remove(func.as_str());
}
for func in &p.node {
let n = format!("{plugin}.{func}");
self.node.remove(n.as_str());
self.node_alias.remove(func.as_str());
}
for func in &p.network {
let n = format!("{plugin}.{func}");
self.network.remove(n.as_str());
self.network_alias.remove(func.as_str());
}
}
}
#[cfg(feature = "functions")]
pub(crate) fn register_alias(&mut self, name: String, alias: String, ftype: FunctionType) {
let old = match ftype {
FunctionType::Env => self
.env_alias
.insert(alias.to_string().into(), name.to_string().into()),
FunctionType::Node => self
.node_alias
.insert(alias.to_string().into(), name.to_string().into()),
FunctionType::Network => self
.network_alias
.insert(alias.to_string().into(), name.to_string().into()),
};
if let RSome(oldname) = old {
if name != oldname {
eprintln!(
"WARN {} Function {} now refers to {} instead of {}, use full name for disambiguity",
ftype, alias, name, oldname
);
}
}
}
pub fn register_network_function(&mut self, plugin: &str, func: NetworkFunctionBox) {
let name = func.name();
let fullname = RString::from(format!("{}.{}", plugin, name));
self.network.insert(fullname.clone(), func);
match self.plugins.entry(plugin.into()) {
REntry::Occupied(mut o) => o.get_mut().push_network(name),
REntry::Vacant(v) => {
v.insert(PluginFunctions::default().with_network(name));
}
};
}
pub fn register_node_function(&mut self, plugin: &str, func: NodeFunctionBox) {
let name = func.name();
let fullname = RString::from(format!("{}.{}", plugin, name));
self.node.insert(fullname.clone(), func);
match self.plugins.entry(plugin.into()) {
REntry::Occupied(mut o) => o.get_mut().push_node(name),
REntry::Vacant(v) => {
v.insert(PluginFunctions::default().with_node(name));
}
};
}
pub fn register_env_function(&mut self, plugin: &str, func: EnvFunctionBox) {
let name = func.name();
let fullname = RString::from(format!("{}.{}", plugin, name));
self.env.insert(fullname.clone(), func);
match self.plugins.entry(plugin.into()) {
REntry::Occupied(mut o) => o.get_mut().push_env(name),
REntry::Vacant(v) => {
v.insert(PluginFunctions::default().with_env(name));
}
};
}
pub fn load_plugins(&mut self) -> anyhow::Result<()> {
if let Ok(plugin_dirs) = std::env::var("NADI_PLUGIN_DIRS") {
for pdir in plugin_dirs.split(':') {
let pdir = Path::new(pdir).join(crate::NADI_CORE_VERSION);
if let Ok(dir) = std::fs::read_dir(&pdir) {
for path in dir {
let p = path?.path();
if p.is_dir() {
continue;
}
if let Some(lib) = load_library_safe(&p) {
lib.register(self);
}
}
} else {
eprintln!("WARN: Can't read plugin directory: {pdir:?}");
}
}
} else {
eprintln!("WARN: Environmental variable NADI_PLUGIN_DIRS is not set.");
}
Ok(())
}
pub fn env_functions(&self) -> &RHashMap<RString, EnvFunctionBox> {
&self.env
}
pub fn env_alias(&self) -> &RHashMap<RString, RString> {
&self.env_alias
}
pub fn node_functions(&self) -> &RHashMap<RString, NodeFunctionBox> {
&self.node
}
pub fn node_alias(&self) -> &RHashMap<RString, RString> {
&self.node_alias
}
pub fn network_functions(&self) -> &RHashMap<RString, NetworkFunctionBox> {
&self.network
}
pub fn network_alias(&self) -> &RHashMap<RString, RString> {
&self.network_alias
}
pub fn plugins(&self) -> &RHashMap<RString, PluginFunctions> {
&self.plugins
}
pub fn plugins_doc<P: AsRef<Path>>(&self, outdir: P) -> anyhow::Result<()> {
let mut doc = BufWriter::new(File::create(outdir.as_ref().join("index.md"))?);
writeln!(doc, "# All Plugin Functions")?;
writeln!(
doc,
"All the functions available on this instance of nadi, are listed here.\n"
)?;
let (node_funcs, net_funcs, env_funcs) = self.list_functions_md(true);
writeln!(
doc,
"## Env Functions\n{}\n\n## Node Functions\n{}\n\n## Network Functions\n{}",
env_funcs, node_funcs, net_funcs
)?;
fn func_sig(fargs: RVec<FuncArg>) -> (String, String) {
let args: Vec<String> = fargs.iter().map(|s| s.to_string()).collect();
let args_help: Vec<String> = fargs
.iter()
.map(|s| format!("- `{}` => {}", s, s.help))
.collect();
(
if args.len() < 3 {
format!("({})", args.join(", "))
} else {
format!("(\n {}\n)", args.join(",\n "))
},
args_help.join("\n"),
)
}
for Tuple2(plug, funcs) in self.plugins() {
let mut doc = BufWriter::new(File::create(
outdir.as_ref().join(plug.as_str()).with_extension("md"),
)?);
if !funcs.env().is_empty() {
writeln!(doc, "# Env Functions")?;
for func in funcs.env() {
let fname = format!("{plug}.{func}");
let func_obj = self.env(&fname).expect("Func Should Exist");
writeln!(doc, "## {func} {{#env.{func}}}")?;
writeln!(doc, "```sig")?;
let (s, h) = func_sig(func_obj.args());
writeln!(doc, "env {}.{}{}", plug, func, s)?;
writeln!(doc, "```\n")?;
writeln!(doc, "**Arguments:**\n{}\n", h)?;
writeln!(doc, "{}", func_obj.help().replace("\n#", "\n###"))?;
}
}
if !funcs.node().is_empty() {
writeln!(doc, "# Node Functions")?;
for func in funcs.node() {
let fname = format!("{plug}.{func}");
let func_obj = self.node(&fname).expect("Func Should Exist");
writeln!(doc, "## {func} {{#node.{func}}}")?;
writeln!(doc, "```sig")?;
let (s, h) = func_sig(func_obj.args());
writeln!(doc, "node {}.{}{}", plug, func, s)?;
writeln!(doc, "```\n")?;
writeln!(doc, "**Arguments:**\n{}\n", h)?;
writeln!(doc, "{}", func_obj.help().replace("\n#", "\n###"))?;
}
}
if !funcs.network().is_empty() {
writeln!(doc, "# Network Functions")?;
for func in funcs.network() {
let fname = format!("{plug}.{func}");
let func_obj = self.network(&fname).expect("Func Should Exist");
writeln!(doc, "## {func} {{#network.{func}}}")?;
writeln!(doc, "```sig")?;
let (s, h) = func_sig(func_obj.args());
writeln!(doc, "network {}.{}{}", plug, func, s)?;
writeln!(doc, "```\n")?;
writeln!(doc, "**Arguments:**\n{}\n", h)?;
writeln!(doc, "{}", func_obj.help().replace("\n#", "\n###"))?;
}
}
}
Ok(())
}
pub fn list_functions(&self) {
fn print_func(p: &RString, t: &str, f: &RString, args: RVec<FuncArg>) {
print!("{} {}.{}", t, p.as_str().red(), f.as_str().blue(),);
let args: Vec<String> = args.iter().map(|s| s.to_colored_string()).collect();
if args.len() < 3 {
println!("({})", args.join(", "));
} else {
println!("(\n {}\n)", args.join(",\n "));
}
}
for Tuple2(plug, funcs) in self.plugins() {
if !funcs.env().is_empty() {
for func in funcs.env() {
let fname = format!("{plug}.{func}");
let func_obj = self.env(&fname).expect("Func Should Exist");
print_func(plug, "node", func, func_obj.args());
}
}
if !funcs.node().is_empty() {
for func in funcs.node() {
let fname = format!("{plug}.{func}");
let func_obj = self.node(&fname).expect("Func Should Exist");
print_func(plug, "node", func, func_obj.args());
}
}
if !funcs.network().is_empty() {
for func in funcs.network() {
let fname = format!("{plug}.{func}");
let func_obj = self.network(&fname).expect("Func Should Exist");
print_func(plug, "network", func, func_obj.args());
}
}
}
}
pub fn list_functions_md(&self, link: bool) -> (String, String, String) {
let mut node_functions = vec![];
let mut net_functions = vec![];
let mut env_functions = vec![];
let fname = if link {
|p: &str, t: &str, n: &str, h: RString| {
vec"),
format!("[`{n}`]({p}.md#{t}.{n})"),
h.to_string(),
]
}
} else {
|p: &str, _t: &str, n: &str, h: RString| {
vec![p.to_string(), n.to_string(), h.to_string()]
}
};
for Tuple2(func, fobj) in &self.node {
let (plug, name) = func.split_once('.').unwrap_or(("null", func.as_str()));
node_functions.push(fname(plug, "node", name, fobj.short_help()));
}
for Tuple2(func, fobj) in &self.network {
let (plug, name) = func.split_once('.').unwrap_or(("null", func.as_str()));
net_functions.push(fname(plug, "network", name, fobj.short_help()));
}
for Tuple2(func, fobj) in &self.env {
let (plug, name) = func.split_once('.').unwrap_or(("null", func.as_str()));
env_functions.push(fname(plug, "env", name, fobj.short_help()));
}
(
contents_2_md(
&["Plugin", "Function", "Help"],
&[&ColumnAlign::Left, &ColumnAlign::Left, &ColumnAlign::Left],
node_functions,
),
contents_2_md(
&["Plugin", "Function", "Help"],
&[&ColumnAlign::Left, &ColumnAlign::Left, &ColumnAlign::Left],
net_functions,
),
contents_2_md(
&["Plugin", "Function", "Help"],
&[&ColumnAlign::Left, &ColumnAlign::Left, &ColumnAlign::Left],
env_functions,
),
)
}
pub fn env(&self, func: &str) -> Option<&EnvFunctionBox> {
if func.contains('.') {
self.env.get(func)
} else {
self.env_alias.get(func).and_then(|f| self.env.get(f))
}
}
pub fn node(&self, func: &str) -> Option<&NodeFunctionBox> {
if func.contains('.') {
self.node.get(func)
} else {
self.node_alias.get(func).and_then(|f| self.node.get(f))
}
}
pub fn network(&self, func: &str) -> Option<&NetworkFunctionBox> {
if func.contains('.') {
self.network.get(func)
} else {
self.network_alias
.get(func)
.and_then(|f| self.network.get(f))
}
}
pub fn help(&self, func: &str) -> Option<String> {
self.help_network(func).or_else(|| self.help_node(func))
}
pub fn help_node(&self, func: &str) -> Option<String> {
self.node(func).map(|f| f.help().into_string())
}
pub fn help_network(&self, func: &str) -> Option<String> {
self.network(func).map(|f| f.help().into_string())
}
pub fn help_env(&self, func: &str) -> Option<String> {
self.env(func).map(|f| f.help().into_string())
}
pub fn code(&self, func: &str) -> Option<String> {
self.code_network(func).or_else(|| self.code_node(func))
}
pub fn code_node(&self, func: &str) -> Option<String> {
self.node(func).map(|f| f.code().into_string())
}
pub fn code_network(&self, func: &str) -> Option<String> {
self.network(func).map(|f| f.code().into_string())
}
pub fn code_env(&self, func: &str) -> Option<String> {
self.env(func).map(|f| f.code().into_string())
}
}
#[repr(C)]
#[derive(StableAbi, Default)]
pub struct FunctionCtx<'a> {
pub args: RVec<FunctionInput<'a>>,
pub kwargs: RHashMap<RString, FunctionInput<'a>>,
}
impl<'a> FunctionCtx<'a> {
pub fn from_arg_kwarg(
args: Vec<FunctionInput<'a>>,
kwargs: HashMap<String, FunctionInput<'a>>,
) -> Self {
let args = RVec::from(args);
let kwargs = kwargs
.into_iter()
.map(|(k, v)| (RString::from(k), v))
.collect();
Self { args, kwargs }
}
pub fn args(&'a self) -> FunctionArgs<'a> {
self.args.as_slice()
}
pub fn try_args<U: ResolveFuncArg<'a>, T: FromIterator<U>>(&'a self) -> Result<T, RString> {
self.args()
.iter()
.map(|v| {
ResolveFuncArg::resolve_arg(v)
.ok_or(format!("Invalid Type: {}", v.type_name()).into())
})
.collect()
}
pub fn try_kwargs<
T: From<&'a RString> + std::hash::Hash + std::cmp::Eq,
U: ResolveFuncArg<'a>,
V: FromIterator<(T, U)>,
>(
&'a self,
) -> Result<V, RString> {
self.kwargs()
.iter()
.map(|Tuple2(k, v)| {
ResolveFuncArg::resolve_arg(v)
.ok_or(format!("Invalid Type: {}", v.type_name()).into())
.map(|x| (k.into(), x))
})
.collect()
}
pub fn arg(&'a self, ind: usize) -> Option<&'a FunctionInput<'a>> {
self.args.get(ind)
}
pub fn kwargs(&'a self) -> FunctionKwArgs<'a> {
&self.kwargs
}
pub fn kwarg(&self, name: &str) -> Option<&FunctionInput<'a>> {
self.kwargs.get(name)
}
pub fn just_kwarg<P: ResolveFuncArg<'a>>(&'a self, name: &str) -> Option<Result<P, String>> {
let arg = self.kwarg(name)?;
Some(match ResolveFuncArg::resolve_arg(arg) {
Some(v) => Ok(v),
None => Err(format!(
"Argument {}: type {} can not be obtained from type {}",
name,
nadi_core::attrs::type_name::<P>(),
arg.type_name()
)),
})
}
pub fn just_kwarg_relaxed<P: FromAttributeRelaxed>(
&self,
name: &str,
) -> Option<Result<P, String>> {
Some(self.kwarg(name)?.attr_relaxed()?.map_err(|e| {
format!(
"Argument {} [{}]: {e}",
name,
nadi_core::attrs::type_name::<P>(),
)
}))
}
pub fn arg_kwarg<P: ResolveFuncArg<'a>>(
&'a self,
ind: usize,
name: &str,
) -> Option<Result<P, String>> {
let arg = self.kwarg(name).or_else(|| self.arg(ind))?;
Some(match ResolveFuncArg::resolve_arg(arg) {
Some(v) => Ok(v),
None => Err(format!(
"Argument {}: type {} can not be obtained from type {}",
name,
nadi_core::attrs::type_name::<P>(),
arg.type_name()
)),
})
}
pub fn arg_kwarg_relaxed<P: FromAttributeRelaxed>(
&self,
ind: usize,
name: &str,
) -> Option<Result<P, String>> {
let res = self.kwarg(name).or_else(|| self.arg(ind))?;
Some(res.attr_relaxed()?.map_err(|e| {
format!(
"Argument {} [{}]: {e}",
name,
nadi_core::attrs::type_name::<P>(),
)
}))
}
}
#[repr(C)]
#[derive(StableAbi, Debug, Clone, PartialEq)]
pub enum FunctionArg {
Arg(Attribute),
KwArg(KeyVal),
}
#[repr(C)]
#[derive(StableAbi, Debug, Clone, PartialEq)]
pub struct KeyVal {
pub key: RString,
pub val: Attribute,
}