Skip to main content

pine_core/
output.rs

1// Output-related types, traits, and implementations
2
3use std::collections::HashMap;
4
5/// Represents a color with RGBA components
6#[derive(Clone, Debug, PartialEq)]
7pub struct Color {
8    pub r: u8, // Red component (0-255)
9    pub g: u8, // Green component (0-255)
10    pub b: u8, // Blue component (0-255)
11    pub t: u8, // Transparency (0-100)
12}
13
14impl Color {
15    pub fn new(r: u8, g: u8, b: u8, t: u8) -> Self {
16        Color { r, g, b, t }
17    }
18}
19
20/// Represents a label drawable object
21#[derive(Clone, Debug)]
22pub struct Label {
23    pub x: f64,
24    pub y: f64,
25    pub text: String,
26    pub xloc: String,
27    pub yloc: String,
28    pub color: Option<Color>,
29    pub style: String,
30    pub textcolor: Option<Color>,
31    pub size: String,
32    pub textalign: String,
33    pub tooltip: Option<String>,
34    pub text_font_family: String,
35}
36
37/// Represents a box drawable object
38/// A `fill(...)` between two plots or hlines.
39#[derive(Clone, Debug)]
40pub struct FillObject {
41    /// Id of the first plot/hline (`None` when it was `na`).
42    pub id1: Option<usize>,
43    /// Id of the second plot/hline.
44    pub id2: Option<usize>,
45    pub color: Option<Color>,
46    pub title: String,
47}
48
49/// Chart-wide settings written by global functions like `bgcolor`/`barcolor`.
50#[derive(Clone, Debug, Default)]
51pub struct GlobalContext {
52    /// Background color (`bgcolor`).
53    pub bgcolor: Option<Color>,
54    /// Price-bar color (`barcolor`).
55    pub barcolor: Option<Color>,
56}
57
58/// How often an `alert(...)` call re-fires within a bar / across bars.
59#[derive(Clone, Copy, Debug, PartialEq, Eq)]
60pub enum Frequency {
61    /// Every time the call is reached (`alert.freq_all`).
62    All,
63    /// Once per realtime bar (`alert.freq_once_per_bar`).
64    OncePerBar,
65    /// Once per bar, on the bar's close (`alert.freq_once_per_bar_close`).
66    OncePerBarClose,
67}
68
69impl Frequency {
70    /// Parse a `freq_*` constant string, defaulting to `OncePerBar`.
71    pub fn from_const(value: &str) -> Frequency {
72        match value {
73            "freq_all" => Frequency::All,
74            "freq_once_per_bar_close" => Frequency::OncePerBarClose,
75            _ => Frequency::OncePerBar,
76        }
77    }
78}
79
80/// An `alertcondition(...)` declaration or an `alert(...)` fire — a named alert
81/// with a message. `frequency` is `None` for `alertcondition` and `Some` for
82/// `alert`, which specifies one.
83#[derive(Clone, Debug, Default)]
84pub struct AlertCondition {
85    pub title: String,
86    pub message: String,
87    pub frequency: Option<Frequency>,
88}
89
90/// The `indicator(...)` declaration — a script's identity and display settings.
91#[derive(Clone, Debug, Default)]
92pub struct Indicator {
93    pub title: String,
94    pub shorttitle: String,
95    pub overlay: bool,
96    pub format: String,
97    pub precision: Option<i64>,
98    pub timeframe: String,
99}
100
101/// The `library(...)` declaration — marks a script as a reusable library.
102#[derive(Clone, Debug, Default)]
103pub struct Library {
104    pub title: String,
105    pub overlay: bool,
106    pub dynamic_requests: Option<bool>,
107}
108
109/// A trend line drawn between two points, `(x1, y1)`–`(x2, y2)`.
110#[derive(Clone, Debug)]
111pub struct LineObject {
112    pub x1: f64,
113    pub y1: f64,
114    pub x2: f64,
115    pub y2: f64,
116    pub xloc: String,
117    pub extend: String,
118    pub color: Option<Color>,
119    pub style: String,
120    pub width: f64,
121}
122
123/// One cell of a [`Table`].
124#[derive(Clone, Debug, Default)]
125pub struct TableCell {
126    pub text: String,
127    pub text_color: Option<Color>,
128    pub bgcolor: Option<Color>,
129    pub text_size: String,
130    pub text_halign: String,
131    pub text_valign: String,
132}
133
134/// A table overlay: a fixed grid of cells anchored to a chart position.
135#[derive(Clone, Debug)]
136pub struct Table {
137    pub position: String,
138    pub columns: usize,
139    pub rows: usize,
140    pub bgcolor: Option<Color>,
141    pub cells: HashMap<(usize, usize), TableCell>,
142}
143
144#[derive(Clone, Debug)]
145pub struct PineBox {
146    pub left: f64,
147    pub top: f64,
148    pub right: f64,
149    pub bottom: f64,
150    pub border_color: Option<Color>,
151    pub border_width: f64,
152    pub border_style: String,
153    pub extend: String,
154    pub xloc: String,
155    pub bgcolor: Option<Color>,
156    pub text: String,
157    pub text_size: f64,
158    pub text_color: Option<Color>,
159    pub text_halign: String,
160    pub text_valign: String,
161    pub text_wrap: String,
162    pub text_font_family: String,
163}
164
165/// Represents a plot output
166#[derive(Clone, Debug, Default)]
167pub struct Plot {
168    pub series: f64,
169    pub title: String,
170    pub color: Option<Color>,
171    pub linewidth: f64,
172    pub style: String,
173    pub trackprice: bool,
174    pub histbase: f64,
175    pub offset: f64,
176    pub join: bool,
177    pub editable: bool,
178    pub show_last: Option<f64>,
179    pub display: String,
180    pub format: Option<String>,
181    pub precision: Option<f64>,
182    pub force_overlay: bool,
183    pub linestyle: String,
184}
185
186/// Represents a plotarrow output
187#[derive(Clone, Debug)]
188pub struct Plotarrow {
189    pub series: f64,
190    pub title: String,
191    pub colorup: Option<Color>,
192    pub colordown: Option<Color>,
193    pub offset: f64,
194    pub minheight: f64,
195    pub maxheight: f64,
196    pub editable: bool,
197    pub show_last: Option<f64>,
198    pub display: String,
199    pub format: Option<String>,
200    pub precision: Option<f64>,
201    pub force_overlay: bool,
202}
203
204/// Represents a plotbar output
205#[derive(Clone, Debug)]
206pub struct Plotbar {
207    pub open: f64,
208    pub high: f64,
209    pub low: f64,
210    pub close: f64,
211    pub title: String,
212    pub color: Option<Color>,
213    pub editable: bool,
214    pub show_last: Option<f64>,
215    pub display: String,
216    pub format: Option<String>,
217    pub precision: Option<f64>,
218    pub force_overlay: bool,
219}
220
221/// Represents a plotcandle output
222#[derive(Clone, Debug)]
223pub struct Plotcandle {
224    pub open: f64,
225    pub high: f64,
226    pub low: f64,
227    pub close: f64,
228    pub title: String,
229    pub color: Option<Color>,
230    pub wickcolor: Option<Color>,
231    pub editable: bool,
232    pub show_last: Option<f64>,
233    pub bordercolor: Option<Color>,
234    pub display: String,
235    pub format: Option<String>,
236    pub precision: Option<f64>,
237    pub force_overlay: bool,
238}
239
240/// Represents a plotchar output
241#[derive(Clone, Debug)]
242pub struct Plotchar {
243    pub series: f64,
244    pub title: String,
245    pub char: String,
246    pub location: String,
247    pub color: Option<Color>,
248    pub offset: f64,
249    pub text: String,
250    pub textcolor: Option<Color>,
251    pub editable: bool,
252    pub size: String,
253    pub show_last: Option<f64>,
254    pub display: String,
255    pub format: Option<String>,
256    pub precision: Option<f64>,
257    pub force_overlay: bool,
258}
259
260/// Represents a plotshape output
261#[derive(Clone, Debug)]
262pub struct Plotshape {
263    pub series: f64,
264    pub title: String,
265    pub style: String,
266    pub location: String,
267    pub color: Option<Color>,
268    pub offset: f64,
269    pub text: String,
270    pub textcolor: Option<Color>,
271    pub editable: bool,
272    pub size: String,
273    pub show_last: Option<f64>,
274    pub display: String,
275    pub format: Option<String>,
276    pub precision: Option<f64>,
277    pub force_overlay: bool,
278}
279
280/// Log level
281#[derive(Debug, Clone, Copy, PartialEq, Eq)]
282pub enum LogLevel {
283    Info,
284    Warning,
285    Error,
286}
287
288/// A log entry with level and message
289#[derive(Debug, Clone)]
290pub struct LogEntry {
291    pub level: LogLevel,
292    pub message: String,
293}
294
295/// The default value of a declared input, preserved with its type so a host can
296/// render the right settings widget.
297///
298/// Deserializes from a JSON scalar (untagged) so host input overrides can be read
299/// from config.
300#[derive(Debug, Clone, PartialEq, serde::Deserialize)]
301#[serde(untagged)]
302pub enum InputValue {
303    Bool(bool),
304    Int(i64),
305    Float(f64),
306    Str(String),
307    #[serde(skip)]
308    Color(Color),
309}
310
311/// A declared script input (`input.int(...)`, `input.source(...)`, ...).
312///
313/// Recorded into the output so a host can enumerate a script's configurable
314/// settings without executing anything itself.
315#[derive(Debug, Clone)]
316pub struct Input {
317    /// Which `input.*` function declared it: `"int"`, `"float"`, `"bool"`,
318    /// `"string"`, `"source"`, `"color"`, `"session"`, `"time"`.
319    pub kind: String,
320    /// Display title (`title` argument), empty when none was given.
321    pub title: String,
322    /// Group the input belongs to (`group` argument), empty when none.
323    pub group: String,
324    /// The `defval` the script declared.
325    pub default: InputValue,
326    pub value: InputValue,
327}
328
329/// Base trait for all output implementations
330///
331/// This trait defines the minimal contract that all output types must implement.
332/// Extension traits (LogOutput, PlotOutput, etc.) add additional capabilities.
333pub trait PineOutput: Default + Clone + std::fmt::Debug + 'static {
334    /// Clear all output data for a new iteration
335    fn clear(&mut self);
336}
337
338/// Extension trait for logging output
339pub trait LogOutput: PineOutput {
340    /// Add a log entry with the given level and message
341    fn add_log(&mut self, level: LogLevel, message: String);
342    /// Get all log entries
343    fn get_logs(&self) -> &[LogEntry];
344}
345
346/// Macro to easily implement all output traits by delegating to a base field
347///
348/// # Example
349/// ```ignore
350/// impl_output_traits_delegate!(CustomOutput, base);
351/// ```
352#[macro_export]
353macro_rules! impl_output_traits_delegate {
354    ($type:ty, $field:ident) => {
355        impl $crate::LogOutput for $type {
356            fn add_log(&mut self, level: $crate::LogLevel, message: String) {
357                self.$field.add_log(level, message)
358            }
359            fn get_logs(&self) -> &[$crate::LogEntry] {
360                self.$field.get_logs()
361            }
362        }
363
364        impl $crate::PlotOutput for $type {
365            fn add_plot(&mut self, plot: $crate::Plot) {
366                self.$field.add_plot(plot)
367            }
368            fn plots(&self) -> &[$crate::Plot] {
369                self.$field.plots()
370            }
371            fn add_plotarrow(&mut self, arrow: $crate::Plotarrow) {
372                self.$field.add_plotarrow(arrow)
373            }
374            fn plotarrows(&self) -> &[$crate::Plotarrow] {
375                self.$field.plotarrows()
376            }
377            fn add_plotbar(&mut self, bar: $crate::Plotbar) {
378                self.$field.add_plotbar(bar)
379            }
380            fn plotbars(&self) -> &[$crate::Plotbar] {
381                self.$field.plotbars()
382            }
383            fn add_plotcandle(&mut self, candle: $crate::Plotcandle) {
384                self.$field.add_plotcandle(candle)
385            }
386            fn plotcandles(&self) -> &[$crate::Plotcandle] {
387                self.$field.plotcandles()
388            }
389            fn add_plotchar(&mut self, char: $crate::Plotchar) {
390                self.$field.add_plotchar(char)
391            }
392            fn plotchars(&self) -> &[$crate::Plotchar] {
393                self.$field.plotchars()
394            }
395            fn add_plotshape(&mut self, shape: $crate::Plotshape) {
396                self.$field.add_plotshape(shape)
397            }
398            fn plotshapes(&self) -> &[$crate::Plotshape] {
399                self.$field.plotshapes()
400            }
401        }
402
403        impl $crate::LabelOutput for $type {
404            fn add_label(&mut self, label: $crate::Label) -> usize {
405                self.$field.add_label(label)
406            }
407            fn get_label(&self, id: usize) -> Option<&$crate::Label> {
408                self.$field.get_label(id)
409            }
410            fn get_label_mut(&mut self, id: usize) -> Option<&mut $crate::Label> {
411                self.$field.get_label_mut(id)
412            }
413            fn delete_label(&mut self, id: usize) -> bool {
414                self.$field.delete_label(id)
415            }
416        }
417
418        impl $crate::BoxOutput for $type {
419            fn add_box(&mut self, box_obj: $crate::PineBox) -> usize {
420                self.$field.add_box(box_obj)
421            }
422            fn get_box(&self, id: usize) -> Option<&$crate::PineBox> {
423                self.$field.get_box(id)
424            }
425            fn get_box_mut(&mut self, id: usize) -> Option<&mut $crate::PineBox> {
426                self.$field.get_box_mut(id)
427            }
428            fn delete_box(&mut self, id: usize) -> bool {
429                self.$field.delete_box(id)
430            }
431        }
432
433        impl $crate::InputOutput for $type {
434            fn add_input(&mut self, input: $crate::Input) {
435                self.$field.add_input(input)
436            }
437            fn inputs(&self) -> &[$crate::Input] {
438                self.$field.inputs()
439            }
440        }
441
442        impl $crate::MetadataOutput for $type {
443            fn set_indicator(&mut self, indicator: $crate::Indicator) {
444                self.$field.set_indicator(indicator)
445            }
446            fn indicator(&self) -> Option<&$crate::Indicator> {
447                self.$field.indicator()
448            }
449            fn set_library(&mut self, library: $crate::Library) {
450                self.$field.set_library(library)
451            }
452            fn library(&self) -> Option<&$crate::Library> {
453                self.$field.library()
454            }
455        }
456
457        impl $crate::AlertConditionOutput for $type {
458            fn add_alertcondition(&mut self, alert: $crate::AlertCondition) {
459                self.$field.add_alertcondition(alert)
460            }
461            fn alertconditions(&self) -> &[$crate::AlertCondition] {
462                self.$field.alertconditions()
463            }
464        }
465
466        impl $crate::FillOutput for $type {
467            fn add_fill(&mut self, fill: $crate::FillObject) {
468                self.$field.add_fill(fill)
469            }
470            fn fills(&self) -> &[$crate::FillObject] {
471                self.$field.fills()
472            }
473        }
474
475        impl $crate::GlobalOutput for $type {
476            fn set_bgcolor(&mut self, color: Option<$crate::Color>) {
477                self.$field.set_bgcolor(color)
478            }
479            fn set_barcolor(&mut self, color: Option<$crate::Color>) {
480                self.$field.set_barcolor(color)
481            }
482            fn global_context(&self) -> &$crate::GlobalContext {
483                self.$field.global_context()
484            }
485        }
486
487        impl $crate::LineOutput for $type {
488            fn add_line(&mut self, line: $crate::LineObject) -> usize {
489                self.$field.add_line(line)
490            }
491            fn get_line(&self, id: usize) -> Option<&$crate::LineObject> {
492                self.$field.get_line(id)
493            }
494            fn get_line_mut(&mut self, id: usize) -> Option<&mut $crate::LineObject> {
495                self.$field.get_line_mut(id)
496            }
497            fn delete_line(&mut self, id: usize) -> bool {
498                self.$field.delete_line(id)
499            }
500        }
501
502        impl $crate::DrawingOutput for $type {
503            fn add_linefill(&mut self, linefill: $crate::LinefillObject) -> usize {
504                self.$field.add_linefill(linefill)
505            }
506            fn get_linefill(&self, id: usize) -> Option<&$crate::LinefillObject> {
507                self.$field.get_linefill(id)
508            }
509            fn get_linefill_mut(&mut self, id: usize) -> Option<&mut $crate::LinefillObject> {
510                self.$field.get_linefill_mut(id)
511            }
512            fn delete_linefill(&mut self, id: usize) -> bool {
513                self.$field.delete_linefill(id)
514            }
515            fn add_polyline(&mut self, polyline: $crate::PolylineObject) -> usize {
516                self.$field.add_polyline(polyline)
517            }
518            fn delete_polyline(&mut self, id: usize) -> bool {
519                self.$field.delete_polyline(id)
520            }
521        }
522
523        impl $crate::TableOutput for $type {
524            fn add_table(&mut self, table: $crate::Table) -> usize {
525                self.$field.add_table(table)
526            }
527            fn get_table(&self, id: usize) -> Option<&$crate::Table> {
528                self.$field.get_table(id)
529            }
530            fn get_table_mut(&mut self, id: usize) -> Option<&mut $crate::Table> {
531                self.$field.get_table_mut(id)
532            }
533            fn delete_table(&mut self, id: usize) -> bool {
534                self.$field.delete_table(id)
535            }
536        }
537    };
538}
539
540/// Extension trait for plot-related output
541pub trait PlotOutput: PineOutput {
542    /// Add a plot output
543    fn add_plot(&mut self, plot: Plot);
544    /// Get all plot outputs
545    fn plots(&self) -> &[Plot];
546
547    /// Add a plotarrow output
548    fn add_plotarrow(&mut self, arrow: Plotarrow);
549    /// Get all plotarrow outputs
550    fn plotarrows(&self) -> &[Plotarrow];
551
552    /// Add a plotbar output
553    fn add_plotbar(&mut self, bar: Plotbar);
554    /// Get all plotbar outputs
555    fn plotbars(&self) -> &[Plotbar];
556
557    /// Add a plotcandle output
558    fn add_plotcandle(&mut self, candle: Plotcandle);
559    /// Get all plotcandle outputs
560    fn plotcandles(&self) -> &[Plotcandle];
561
562    /// Add a plotchar output
563    fn add_plotchar(&mut self, char: Plotchar);
564    /// Get all plotchar outputs
565    fn plotchars(&self) -> &[Plotchar];
566
567    /// Add a plotshape output
568    fn add_plotshape(&mut self, shape: Plotshape);
569    /// Get all plotshape outputs
570    fn plotshapes(&self) -> &[Plotshape];
571}
572
573/// Extension trait for label output
574pub trait LabelOutput: PineOutput {
575    /// Add a label and return its ID
576    fn add_label(&mut self, label: Label) -> usize;
577    /// Get a reference to a label by ID
578    fn get_label(&self, id: usize) -> Option<&Label>;
579    /// Get a mutable reference to a label by ID
580    fn get_label_mut(&mut self, id: usize) -> Option<&mut Label>;
581    /// Delete a label by ID and return true if it existed
582    fn delete_label(&mut self, id: usize) -> bool;
583}
584
585/// Extension trait for box output
586pub trait BoxOutput: PineOutput {
587    /// Add a box and return its ID
588    fn add_box(&mut self, box_obj: PineBox) -> usize;
589    /// Get a reference to a box by ID
590    fn get_box(&self, id: usize) -> Option<&PineBox>;
591    /// Get a mutable reference to a box by ID
592    fn get_box_mut(&mut self, id: usize) -> Option<&mut PineBox>;
593    /// Delete a box by ID and return true if it existed
594    fn delete_box(&mut self, id: usize) -> bool;
595}
596
597/// Extension trait for table output
598pub trait TableOutput: PineOutput {
599    /// Add a table and return its ID
600    fn add_table(&mut self, table: Table) -> usize;
601    /// Get a reference to a table by ID
602    fn get_table(&self, id: usize) -> Option<&Table>;
603    /// Get a mutable reference to a table by ID
604    fn get_table_mut(&mut self, id: usize) -> Option<&mut Table>;
605    /// Delete a table by ID and return true if it existed
606    fn delete_table(&mut self, id: usize) -> bool;
607}
608
609/// Extension trait for line output
610pub trait LineOutput: PineOutput {
611    /// Add a line and return its ID
612    fn add_line(&mut self, line: LineObject) -> usize;
613    /// Get a reference to a line by ID
614    fn get_line(&self, id: usize) -> Option<&LineObject>;
615    /// Get a mutable reference to a line by ID
616    fn get_line_mut(&mut self, id: usize) -> Option<&mut LineObject>;
617    /// Delete a line by ID and return true if it existed
618    fn delete_line(&mut self, id: usize) -> bool;
619}
620
621/// A fill between two lines (`linefill.new`).
622#[derive(Clone, Debug)]
623pub struct LinefillObject {
624    pub line1: usize,
625    pub line2: usize,
626    pub color: Option<Color>,
627}
628
629/// A multi-point polyline (`polyline.new`).
630#[derive(Clone, Debug, Default)]
631pub struct PolylineObject {
632    /// The `(x, y)` vertices, in order.
633    pub points: Vec<(f64, f64)>,
634    pub curved: bool,
635    pub closed: bool,
636    pub xloc: String,
637    pub line_color: Option<Color>,
638    pub fill_color: Option<Color>,
639    pub line_style: String,
640    pub line_width: f64,
641}
642
643/// One extension trait recording both `linefill` and `polyline` drawings, which
644/// share a sink since neither carries much state.
645pub trait DrawingOutput: PineOutput {
646    fn add_linefill(&mut self, linefill: LinefillObject) -> usize;
647    fn get_linefill(&self, id: usize) -> Option<&LinefillObject>;
648    fn get_linefill_mut(&mut self, id: usize) -> Option<&mut LinefillObject>;
649    fn delete_linefill(&mut self, id: usize) -> bool;
650    fn add_polyline(&mut self, polyline: PolylineObject) -> usize;
651    fn delete_polyline(&mut self, id: usize) -> bool;
652}
653
654/// Extension trait for recording `fill(...)` areas.
655pub trait FillOutput: PineOutput {
656    fn add_fill(&mut self, fill: FillObject);
657    fn fills(&self) -> &[FillObject];
658}
659
660/// Extension trait for chart-wide globals (`bgcolor`, `barcolor`).
661pub trait GlobalOutput: PineOutput {
662    fn set_bgcolor(&mut self, color: Option<Color>);
663    fn set_barcolor(&mut self, color: Option<Color>);
664    /// The accumulated chart-wide settings.
665    fn global_context(&self) -> &GlobalContext;
666}
667
668/// Extension trait for recording declared alert conditions.
669pub trait AlertConditionOutput: PineOutput {
670    /// Record a declared alert condition.
671    fn add_alertcondition(&mut self, alert: AlertCondition);
672    /// Every alert condition declared so far, in declaration order.
673    fn alertconditions(&self) -> &[AlertCondition];
674}
675
676/// Extension trait for a script's declaration statement — `indicator(...)` or
677/// `library(...)` (a script has at most one).
678pub trait MetadataOutput: PineOutput {
679    /// Record the indicator declaration.
680    fn set_indicator(&mut self, indicator: Indicator);
681    /// The indicator declaration, if the script declared one.
682    fn indicator(&self) -> Option<&Indicator>;
683    /// Record the library declaration.
684    fn set_library(&mut self, library: Library);
685    /// The library declaration, if the script declared one.
686    fn library(&self) -> Option<&Library>;
687}
688
689/// Extension trait for recording declared inputs
690pub trait InputOutput: PineOutput {
691    /// Record a declared input.
692    fn add_input(&mut self, input: Input);
693    /// Every input declared so far, in declaration order.
694    fn inputs(&self) -> &[Input];
695}
696
697/// Default implementation of PineOutput that supports all features
698#[derive(Default, Clone, Debug)]
699pub struct DefaultPineOutput {
700    /// Label storage for drawable objects
701    labels: HashMap<usize, Label>,
702    /// Next label ID
703    next_label_id: usize,
704    /// Box storage for drawable objects
705    boxes: HashMap<usize, PineBox>,
706    /// Next box ID
707    next_box_id: usize,
708    /// Line storage for drawable objects
709    lines: HashMap<usize, LineObject>,
710    /// Next line ID
711    next_line_id: usize,
712    /// Table storage for drawable objects
713    tables: HashMap<usize, Table>,
714    /// Next table ID
715    next_table_id: usize,
716    /// Plot outputs
717    plots: Vec<Plot>,
718    /// Plotarrow outputs
719    plotarrows: Vec<Plotarrow>,
720    /// Plotbar outputs
721    plotbars: Vec<Plotbar>,
722    /// Plotcandle outputs
723    plotcandles: Vec<Plotcandle>,
724    /// Plotchar outputs
725    plotchars: Vec<Plotchar>,
726    /// Plotshape outputs
727    plotshapes: Vec<Plotshape>,
728    /// Log entries
729    logs: Vec<LogEntry>,
730    /// Declared inputs
731    inputs: Vec<Input>,
732    /// The `indicator(...)` declaration, if any.
733    indicator: Option<Indicator>,
734    /// The `library(...)` declaration, if any.
735    library: Option<Library>,
736    /// Chart-wide settings (`bgcolor`, `barcolor`).
737    globals: GlobalContext,
738    /// Declared alert conditions.
739    alertconditions: Vec<AlertCondition>,
740    /// `fill(...)` areas.
741    fills: Vec<FillObject>,
742    /// Linefill storage for drawable objects.
743    linefills: HashMap<usize, LinefillObject>,
744    /// Next linefill ID.
745    next_linefill_id: usize,
746    /// Polyline storage for drawable objects.
747    polylines: HashMap<usize, PolylineObject>,
748    /// Next polyline ID.
749    next_polyline_id: usize,
750}
751
752impl PineOutput for DefaultPineOutput {
753    fn clear(&mut self) {
754        self.labels.clear();
755        self.boxes.clear();
756        self.plots.clear();
757        self.plotarrows.clear();
758        self.plotbars.clear();
759        self.plotcandles.clear();
760        self.plotchars.clear();
761        self.plotshapes.clear();
762        self.logs.clear();
763        self.inputs.clear();
764        self.indicator = None;
765        self.globals = GlobalContext::default();
766        self.alertconditions.clear();
767        self.fills.clear();
768        self.lines.clear();
769        self.tables.clear();
770        // Reset ID counters
771        self.next_label_id = 0;
772        self.next_box_id = 0;
773        self.next_line_id = 0;
774        self.next_table_id = 0;
775    }
776}
777
778impl LogOutput for DefaultPineOutput {
779    fn add_log(&mut self, level: LogLevel, message: String) {
780        self.logs.push(LogEntry { level, message });
781    }
782
783    fn get_logs(&self) -> &[LogEntry] {
784        &self.logs
785    }
786}
787
788impl PlotOutput for DefaultPineOutput {
789    fn add_plot(&mut self, plot: Plot) {
790        self.plots.push(plot);
791    }
792
793    fn plots(&self) -> &[Plot] {
794        &self.plots
795    }
796
797    fn add_plotarrow(&mut self, arrow: Plotarrow) {
798        self.plotarrows.push(arrow);
799    }
800
801    fn plotarrows(&self) -> &[Plotarrow] {
802        &self.plotarrows
803    }
804
805    fn add_plotbar(&mut self, bar: Plotbar) {
806        self.plotbars.push(bar);
807    }
808
809    fn plotbars(&self) -> &[Plotbar] {
810        &self.plotbars
811    }
812
813    fn add_plotcandle(&mut self, candle: Plotcandle) {
814        self.plotcandles.push(candle);
815    }
816
817    fn plotcandles(&self) -> &[Plotcandle] {
818        &self.plotcandles
819    }
820
821    fn add_plotchar(&mut self, char: Plotchar) {
822        self.plotchars.push(char);
823    }
824
825    fn plotchars(&self) -> &[Plotchar] {
826        &self.plotchars
827    }
828
829    fn add_plotshape(&mut self, shape: Plotshape) {
830        self.plotshapes.push(shape);
831    }
832
833    fn plotshapes(&self) -> &[Plotshape] {
834        &self.plotshapes
835    }
836}
837
838impl LabelOutput for DefaultPineOutput {
839    fn add_label(&mut self, label: Label) -> usize {
840        let id = self.next_label_id;
841        self.next_label_id += 1;
842        self.labels.insert(id, label);
843        id
844    }
845
846    fn get_label(&self, id: usize) -> Option<&Label> {
847        self.labels.get(&id)
848    }
849
850    fn get_label_mut(&mut self, id: usize) -> Option<&mut Label> {
851        self.labels.get_mut(&id)
852    }
853
854    fn delete_label(&mut self, id: usize) -> bool {
855        self.labels.remove(&id).is_some()
856    }
857}
858
859impl BoxOutput for DefaultPineOutput {
860    fn add_box(&mut self, box_obj: PineBox) -> usize {
861        let id = self.next_box_id;
862        self.next_box_id += 1;
863        self.boxes.insert(id, box_obj);
864        id
865    }
866
867    fn get_box(&self, id: usize) -> Option<&PineBox> {
868        self.boxes.get(&id)
869    }
870
871    fn get_box_mut(&mut self, id: usize) -> Option<&mut PineBox> {
872        self.boxes.get_mut(&id)
873    }
874
875    fn delete_box(&mut self, id: usize) -> bool {
876        self.boxes.remove(&id).is_some()
877    }
878}
879
880impl LineOutput for DefaultPineOutput {
881    fn add_line(&mut self, line: LineObject) -> usize {
882        let id = self.next_line_id;
883        self.next_line_id += 1;
884        self.lines.insert(id, line);
885        id
886    }
887
888    fn get_line(&self, id: usize) -> Option<&LineObject> {
889        self.lines.get(&id)
890    }
891
892    fn get_line_mut(&mut self, id: usize) -> Option<&mut LineObject> {
893        self.lines.get_mut(&id)
894    }
895
896    fn delete_line(&mut self, id: usize) -> bool {
897        self.lines.remove(&id).is_some()
898    }
899}
900
901impl DrawingOutput for DefaultPineOutput {
902    fn add_linefill(&mut self, linefill: LinefillObject) -> usize {
903        let id = self.next_linefill_id;
904        self.next_linefill_id += 1;
905        self.linefills.insert(id, linefill);
906        id
907    }
908
909    fn get_linefill(&self, id: usize) -> Option<&LinefillObject> {
910        self.linefills.get(&id)
911    }
912
913    fn get_linefill_mut(&mut self, id: usize) -> Option<&mut LinefillObject> {
914        self.linefills.get_mut(&id)
915    }
916
917    fn delete_linefill(&mut self, id: usize) -> bool {
918        self.linefills.remove(&id).is_some()
919    }
920
921    fn add_polyline(&mut self, polyline: PolylineObject) -> usize {
922        let id = self.next_polyline_id;
923        self.next_polyline_id += 1;
924        self.polylines.insert(id, polyline);
925        id
926    }
927
928    fn delete_polyline(&mut self, id: usize) -> bool {
929        self.polylines.remove(&id).is_some()
930    }
931}
932
933impl TableOutput for DefaultPineOutput {
934    fn add_table(&mut self, table: Table) -> usize {
935        let id = self.next_table_id;
936        self.next_table_id += 1;
937        self.tables.insert(id, table);
938        id
939    }
940
941    fn get_table(&self, id: usize) -> Option<&Table> {
942        self.tables.get(&id)
943    }
944
945    fn get_table_mut(&mut self, id: usize) -> Option<&mut Table> {
946        self.tables.get_mut(&id)
947    }
948
949    fn delete_table(&mut self, id: usize) -> bool {
950        self.tables.remove(&id).is_some()
951    }
952}
953
954impl InputOutput for DefaultPineOutput {
955    fn add_input(&mut self, input: Input) {
956        self.inputs.push(input);
957    }
958
959    fn inputs(&self) -> &[Input] {
960        &self.inputs
961    }
962}
963
964impl MetadataOutput for DefaultPineOutput {
965    fn set_indicator(&mut self, indicator: Indicator) {
966        self.indicator = Some(indicator);
967    }
968
969    fn indicator(&self) -> Option<&Indicator> {
970        self.indicator.as_ref()
971    }
972
973    fn set_library(&mut self, library: Library) {
974        self.library = Some(library);
975    }
976
977    fn library(&self) -> Option<&Library> {
978        self.library.as_ref()
979    }
980}
981
982impl AlertConditionOutput for DefaultPineOutput {
983    fn add_alertcondition(&mut self, alert: AlertCondition) {
984        self.alertconditions.push(alert);
985    }
986
987    fn alertconditions(&self) -> &[AlertCondition] {
988        &self.alertconditions
989    }
990}
991
992impl FillOutput for DefaultPineOutput {
993    fn add_fill(&mut self, fill: FillObject) {
994        self.fills.push(fill);
995    }
996
997    fn fills(&self) -> &[FillObject] {
998        &self.fills
999    }
1000}
1001
1002impl GlobalOutput for DefaultPineOutput {
1003    fn set_bgcolor(&mut self, color: Option<Color>) {
1004        self.globals.bgcolor = color;
1005    }
1006
1007    fn set_barcolor(&mut self, color: Option<Color>) {
1008        self.globals.barcolor = color;
1009    }
1010
1011    fn global_context(&self) -> &GlobalContext {
1012        &self.globals
1013    }
1014}