use pine_builtin_macro::BuiltinFunction;
use pine_core::{Input, InputOutput, InputValue, PineOutput};
use pine_interpreter::{Builtin, BuiltinFn, Interpreter, RuntimeError, Value};
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
const TYPE_TAGS: &[&str] = &[
"integer",
"float",
"bool",
"string",
"source",
"color",
"symbol",
"session",
"resolution",
"time",
"price",
];
const GLOBAL_TYPE_TAGS: &[&str] = &["integer", "source", "symbol", "resolution", "price"];
#[derive(BuiltinFunction)]
#[builtin(name = "input")]
struct InputLegacy<O: PineOutput + InputOutput> {
defval: Value<O>,
#[arg(default = "")]
title: String,
#[arg(default = Value::Na)]
r#type: Value<O>,
#[arg(default = None)]
minval: Option<f64>,
#[arg(default = None)]
maxval: Option<f64>,
#[arg(default = None)]
step: Option<f64>,
#[arg(default = "")]
group: String,
#[arg(default = "")]
tooltip: String,
#[arg(default = Value::Na)]
options: Value<O>,
#[arg(default = false)]
confirm: bool,
}
impl<O: PineOutput + InputOutput> InputLegacy<O> {
fn execute(&self, ctx: &mut Interpreter<O>) -> Result<Value<O>, RuntimeError> {
let _ = (self.step, &self.tooltip, self.confirm);
let kind = match &self.r#type {
Value::String(tag) if !tag.is_empty() => tag.clone(),
_ => infer_kind(&self.defval),
};
let effective = match &self.defval {
Value::Bool(b) => Value::Bool(super::bool_input(ctx, &self.title, *b)),
Value::Int(n) => Value::Number(
super::num_input(ctx, &self.title, *n as f64, self.minval, self.maxval).trunc(),
),
Value::Number(n) => Value::Number(super::num_input(
ctx,
&self.title,
*n,
self.minval,
self.maxval,
)),
Value::String(s) => {
Value::String(super::string_input(ctx, &self.title, s, &self.options))
}
other => other.clone(),
};
ctx.output.add_input(Input {
title: self.title.clone(),
group: self.group.clone(),
default: to_input_value(&kind, &self.defval),
value: to_input_value(&kind, &effective),
kind,
});
Ok(effective)
}
}
fn infer_kind<O: PineOutput>(defval: &Value<O>) -> String {
match defval {
Value::Bool(_) => "bool",
Value::String(_) => "string",
Value::Color(_) => "color",
Value::Series(_) => "source",
_ => "float",
}
.to_string()
}
fn to_input_value<O: PineOutput>(kind: &str, defval: &Value<O>) -> InputValue {
match defval {
Value::Number(n) if kind == "integer" || kind == "int" => InputValue::Int(*n as i64),
Value::Number(n) => InputValue::Float(*n),
Value::Bool(b) => InputValue::Bool(*b),
Value::String(s) => InputValue::Str(s.clone()),
Value::Color(c) => InputValue::Color(c.clone()),
Value::Series(series) => InputValue::Str(series.id.clone()),
other => InputValue::Str(format!("{:?}", other)),
}
}
pub fn register<O: PineOutput + InputOutput>() -> Vec<(String, Value<O>)> {
let mut members: HashMap<String, Value<O>> = HashMap::new();
for tag in TYPE_TAGS {
members.insert(tag.to_string(), Value::String(tag.to_string()));
}
let input_object = Value::Object {
type_name: "input".to_string(),
fields: Rc::new(RefCell::new(members)),
call: Some(Builtin::untyped(
Rc::new(InputLegacy::<O>::builtin_fn) as BuiltinFn<O>
)),
value: None,
};
let mut entries = vec![("input".to_string(), input_object)];
for tag in GLOBAL_TYPE_TAGS {
entries.push((tag.to_string(), Value::String(tag.to_string())));
}
entries
}