1use std::fmt::Display;
2
3use gc_arena::Mutation;
4use num_enum::FromPrimitive;
5use openpine_compiler::{
6 instructions::{Color, TypeDescriptor, tag},
7 program::Program,
8};
9use openpine_macros::Enum;
10use serde::{Deserialize, Serialize, Serializer};
11use serde_value::Value;
12
13use crate::{
14 TimeFrame,
15 objects::{PineRef, string::PineString},
16 quote_types::Candlestick,
17 raw_value::{RawValue, ToRawValue},
18 visuals::PlotDisplay,
19};
20
21#[derive(Debug, thiserror::Error, Clone, PartialEq)]
23#[error("{0}")]
24pub(crate) struct ValueSerdeError(String);
25
26impl ValueSerdeError {
27 pub(crate) fn mismatched_type(expected: impl Display, actual: impl Display) -> Self {
28 Self(format!(
29 "mismatched type expected `{expected}`, actual `{actual}`"
30 ))
31 }
32
33 pub(crate) fn not_supported(ty: impl Display) -> Self {
34 Self(format!("`{ty}` is not supported"))
35 }
36}
37
38fn convert_serde_value<'a>(
39 mc: &'a Mutation<'a>,
40 program: &Program,
41 vt: &TypeDescriptor,
42 value: &Value,
43) -> Result<RawValue, ValueSerdeError> {
44 match value {
45 Value::Bool(b) => {
46 if vt.tag() != tag::BOOLEAN {
47 return Err(ValueSerdeError::mismatched_type(
48 vt.display(program),
49 "bool",
50 ));
51 }
52 Ok(b.to_raw_value())
53 }
54 Value::I8(v) => convert_serde_value(mc, program, vt, &Value::I64(*v as i64)),
55 Value::I16(v) => convert_serde_value(mc, program, vt, &Value::I64(*v as i64)),
56 Value::I32(v) => convert_serde_value(mc, program, vt, &Value::I64(*v as i64)),
57 Value::I64(v) => {
58 if !matches!(vt.tag(), tag::INTEGER | tag::FLOAT | tag::ENUM) {
59 return Err(ValueSerdeError::mismatched_type(vt.display(program), "int"));
60 }
61 Ok(v.to_raw_value())
62 }
63 Value::U8(v) => convert_serde_value(mc, program, vt, &Value::I64(*v as i64)),
64 Value::U16(v) => convert_serde_value(mc, program, vt, &Value::I64(*v as i64)),
65 Value::U32(v) => convert_serde_value(mc, program, vt, &Value::I64(*v as i64)),
66 Value::U64(v) => {
67 if !matches!(vt.tag(), tag::INTEGER | tag::FLOAT) {
68 return Err(ValueSerdeError::mismatched_type(vt.display(program), "int"));
69 }
70 Ok((*v as i64).to_raw_value())
71 }
72 Value::F32(v) => convert_serde_value(mc, program, vt, &Value::F64(*v as f64)),
73 Value::F64(v) => {
74 if vt.tag() != tag::FLOAT {
75 return Err(ValueSerdeError::mismatched_type(
76 vt.display(program),
77 "float",
78 ));
79 }
80 Ok(v.to_raw_value())
81 }
82 Value::String(s) => {
83 if vt.tag() != tag::STRING {
84 return Err(ValueSerdeError::mismatched_type(
85 vt.display(program),
86 "string",
87 ));
88 }
89 Ok(PineRef::new(mc, PineString::new(s)).to_raw_value())
90 }
91 Value::Option(None) | Value::Unit => Ok(RawValue::NA),
92 Value::Option(Some(inner)) | Value::Newtype(inner) => {
93 convert_serde_value(mc, program, vt, inner)
94 }
95 other => Err(ValueSerdeError::not_supported(format!("{other:?}"))),
96 }
97}
98
99#[derive(Debug, Clone, Serialize, Deserialize)]
101pub struct InputInt {
102 pub id: i64,
104 pub default_value: i64,
106 pub title: Option<String>,
108 pub tooltip: Option<String>,
110 pub inline: Option<String>,
112 pub group: Option<String>,
114 pub display: PlotDisplay,
116 pub active: bool,
118 pub options: Vec<i64>,
120 pub min_value: Option<i64>,
122 pub max_value: Option<i64>,
124 pub step: Option<i64>,
126 pub confirm: bool,
128}
129
130#[derive(Debug, Clone, Serialize, Deserialize)]
132pub struct InputFloat {
133 pub id: i64,
135 pub default_value: f64,
137 pub title: Option<String>,
139 pub tooltip: Option<String>,
141 pub inline: Option<String>,
143 pub group: Option<String>,
145 pub display: PlotDisplay,
147 pub active: bool,
149 pub options: Option<Vec<f64>>,
151 pub min_value: Option<f64>,
153 pub max_value: Option<f64>,
155 pub step: Option<f64>,
157 pub confirm: bool,
159}
160
161#[derive(Debug, Clone, Serialize, Deserialize)]
163pub struct InputBool {
164 pub id: i64,
166 pub default_value: bool,
168 pub title: Option<String>,
170 pub tooltip: Option<String>,
172 pub inline: Option<String>,
174 pub group: Option<String>,
176 pub display: PlotDisplay,
178 pub active: bool,
180 pub confirm: bool,
182}
183
184#[derive(Debug, Clone, Serialize, Deserialize)]
186pub struct InputColor {
187 pub id: i64,
189 pub default_value: Color,
191 pub title: Option<String>,
193 pub tooltip: Option<String>,
195 pub inline: Option<String>,
197 pub group: Option<String>,
199 pub display: PlotDisplay,
201 pub active: bool,
203 pub confirm: bool,
205}
206
207#[derive(Debug, Clone, Serialize, Deserialize)]
209pub struct InputString {
210 pub id: i64,
212 pub default_value: String,
214 pub title: Option<String>,
216 pub tooltip: Option<String>,
218 pub inline: Option<String>,
220 pub group: Option<String>,
222 pub display: PlotDisplay,
224 pub active: bool,
226 pub options: Vec<String>,
228 pub confirm: bool,
230}
231
232#[derive(Debug, Clone, Serialize, Deserialize)]
234pub struct InputPrice {
235 pub id: i64,
237 pub default_value: f64,
239 pub title: Option<String>,
241 pub tooltip: Option<String>,
243 pub inline: Option<String>,
245 pub group: Option<String>,
247 pub display: PlotDisplay,
249 pub active: bool,
251 pub confirm: bool,
253}
254
255#[derive(Debug, Clone, Serialize, Deserialize)]
257pub struct InputSymbol {
258 pub id: i64,
260 pub default_value: String,
262 pub title: Option<String>,
264 pub tooltip: Option<String>,
266 pub inline: Option<String>,
268 pub group: Option<String>,
270 pub display: PlotDisplay,
272 pub active: bool,
274 pub confirm: bool,
276}
277
278#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
288pub enum TimeFrameDefaultValue {
289 Chart,
291 Custom(TimeFrame),
293}
294
295impl Serialize for TimeFrameDefaultValue {
296 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
297 where
298 S: Serializer,
299 {
300 match self {
301 Self::Chart => serializer.serialize_str(""),
302 Self::Custom(tf) => serializer.collect_str(tf),
303 }
304 }
305}
306
307impl<'de> Deserialize<'de> for TimeFrameDefaultValue {
308 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
309 where
310 D: serde::Deserializer<'de>,
311 {
312 let s = String::deserialize(deserializer)?;
313 if s.is_empty() {
314 Ok(Self::Chart)
315 } else {
316 s.parse::<TimeFrame>()
317 .map(Self::Custom)
318 .map_err(serde::de::Error::custom)
319 }
320 }
321}
322
323#[derive(Debug, Clone, Serialize, Deserialize)]
325pub struct InputTimeFrame {
326 pub id: i64,
328 pub default_value: TimeFrameDefaultValue,
330 pub title: Option<String>,
332 pub tooltip: Option<String>,
334 pub inline: Option<String>,
336 pub group: Option<String>,
338 pub display: PlotDisplay,
340 pub active: bool,
342 pub options: Vec<TimeFrame>,
344 pub confirm: bool,
346}
347
348#[derive(Debug, Copy, Clone, PartialEq, Eq, FromPrimitive, Enum)]
350#[openpine(rename_all = "lowercase")]
351#[repr(i64)]
352pub enum SourceType {
353 #[default]
355 Open = 0,
356 High = 1,
358 Low = 2,
360 Close = 3,
362 Hl2 = 4,
364 Hlc3 = 5,
366 Ohlc4 = 6,
368 Hlcc4 = 7,
370}
371
372impl Serialize for SourceType {
373 #[inline]
374 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
375 where
376 S: Serializer,
377 {
378 (*self as i64).serialize(serializer)
379 }
380}
381
382impl<'de> Deserialize<'de> for SourceType {
383 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
384 where
385 D: serde::Deserializer<'de>,
386 {
387 let value = i64::deserialize(deserializer)?;
388 Ok(Self::from(value))
389 }
390}
391
392impl SourceType {
393 #[inline]
395 pub fn display_name(&self) -> &'static str {
396 match self {
397 SourceType::Open => "Open",
398 SourceType::High => "High",
399 SourceType::Low => "Low",
400 SourceType::Close => "Close",
401 SourceType::Hl2 => "(H + L) / 2",
402 SourceType::Hlc3 => "(H + L + C) / 3",
403 SourceType::Ohlc4 => "(O + H + L + C) / 4",
404 SourceType::Hlcc4 => "(H + L + C + C) / 4",
405 }
406 }
407
408 pub(crate) fn value(&self, candlestick: &Candlestick) -> f64 {
409 match self {
410 SourceType::Open => candlestick.open,
411 SourceType::High => candlestick.high,
412 SourceType::Low => candlestick.low,
413 SourceType::Close => candlestick.close,
414 SourceType::Hl2 => candlestick.hl2(),
415 SourceType::Hlc3 => candlestick.hlc3(),
416 SourceType::Ohlc4 => candlestick.ohlc4(),
417 SourceType::Hlcc4 => candlestick.hlcc4(),
418 }
419 }
420}
421
422#[derive(Debug, Clone, Serialize, Deserialize)]
424pub struct InputSource {
425 pub id: i64,
427 pub default_value: SourceType,
429 pub title: Option<String>,
431 pub tooltip: Option<String>,
433 pub inline: Option<String>,
435 pub group: Option<String>,
437 pub display: PlotDisplay,
439 pub active: bool,
441 pub confirm: bool,
443}
444
445#[derive(Debug, Clone, Serialize, Deserialize)]
447pub struct InputEnumOption {
448 pub value: i64,
450 pub title: String,
452}
453
454#[derive(Debug, Clone, Serialize, Deserialize)]
456pub struct InputEnum {
457 pub id: i64,
459 pub enum_typeid: usize,
461 pub default_value: i64,
463 pub title: Option<String>,
465 pub tooltip: Option<String>,
467 pub inline: Option<String>,
469 pub group: Option<String>,
471 pub display: PlotDisplay,
473 pub active: bool,
475 pub options: Vec<InputEnumOption>,
477 pub confirm: bool,
479}
480
481#[derive(Debug, Clone, Serialize, Deserialize)]
483pub struct InputSession {
484 pub id: i64,
486 pub default_value: String,
488 pub title: Option<String>,
490 pub tooltip: Option<String>,
492 pub inline: Option<String>,
494 pub group: Option<String>,
496 pub display: PlotDisplay,
498 pub active: bool,
500 pub options: Vec<String>,
502 pub confirm: bool,
504}
505
506#[derive(Debug, Clone, Serialize, Deserialize)]
508pub struct InputTime {
509 pub id: i64,
511 pub default_value: i64,
513 pub title: Option<String>,
515 pub tooltip: Option<String>,
517 pub inline: Option<String>,
519 pub group: Option<String>,
521 pub display: PlotDisplay,
523 pub active: bool,
525 pub confirm: bool,
527}
528
529#[derive(Debug, Clone, Serialize, Deserialize)]
531pub struct InputTextArea {
532 pub id: i64,
534 pub default_value: String,
536 pub title: Option<String>,
538 pub tooltip: Option<String>,
540 pub group: Option<String>,
542 pub display: PlotDisplay,
544 pub active: bool,
546 pub confirm: bool,
548}
549
550#[derive(Debug, Clone, Serialize, Deserialize)]
552pub enum Input {
553 Int(InputInt),
555 Float(InputFloat),
557 Bool(InputBool),
559 Color(InputColor),
561 String(InputString),
563 Price(InputPrice),
565 Symbol(InputSymbol),
567 TimeFrame(InputTimeFrame),
569 Source(InputSource),
571 Enum(InputEnum),
573 Session(InputSession),
575 Time(InputTime),
577 TextArea(InputTextArea),
579}
580
581impl Input {
582 pub(crate) fn value_type(&self) -> TypeDescriptor {
583 match self {
584 Input::Int(_) | Input::Time(_) => TypeDescriptor::INTEGER,
585 Input::Float(_) | Input::Price(_) => TypeDescriptor::FLOAT,
586 Input::Bool(_) => TypeDescriptor::BOOLEAN,
587 Input::Color(_) => TypeDescriptor::COLOR,
588 Input::String(_)
589 | Input::Symbol(_)
590 | Input::TimeFrame(_)
591 | Input::Session(_)
592 | Input::TextArea(_) => TypeDescriptor::STRING,
593 Input::Source(_) => TypeDescriptor::FLOAT,
594 Input::Enum(input) => TypeDescriptor::enum_type(input.enum_typeid),
595 }
596 }
597
598 pub(crate) fn serialize_value<'a>(
599 &self,
600 mc: &'a Mutation<'a>,
601 program: &Program,
602 value: &serde_value::Value,
603 ) -> Result<RawValue, ValueSerdeError> {
604 let value_type = self.value_type();
605 convert_serde_value(mc, program, &value_type, value)
606 }
607
608 pub(crate) fn serialize_default_value<'a>(
609 &self,
610 mc: &'a Mutation<'a>,
611 program: &Program,
612 ) -> Result<RawValue, ValueSerdeError> {
613 let value_type = self.value_type();
614 let sv = match self {
615 Input::Int(input) => serde_value::to_value(input.default_value),
616 Input::Float(input) => serde_value::to_value(input.default_value),
617 Input::Bool(input) => serde_value::to_value(input.default_value),
618 Input::Color(input) => serde_value::to_value(input.default_value),
619 Input::String(input) => serde_value::to_value(&input.default_value),
620 Input::Price(input) => serde_value::to_value(input.default_value),
621 Input::Symbol(input) => serde_value::to_value(&input.default_value),
622 Input::TimeFrame(input) => serde_value::to_value(input.default_value),
623 Input::Source(input) => serde_value::to_value(input.default_value as i64),
624 Input::Enum(input) => serde_value::to_value(input.default_value),
625 Input::Session(input) => serde_value::to_value(&input.default_value),
626 Input::Time(input) => serde_value::to_value(input.default_value),
627 Input::TextArea(input) => serde_value::to_value(&input.default_value),
628 }
629 .unwrap_or(serde_value::Value::Unit);
630 convert_serde_value(mc, program, &value_type, &sv)
631 }
632}