rtimelog 0.51.0

System for tracking time in a text-log-based format.
Documentation
//! Represent the configuration for a legend on a [`PieChart`]
//!
//! # Examples
//!
//! ```rust, no_run
//! use xml::writer::EmitterConfig;
//! use timelog::chart::{ColorIter, Legend, TagPercent};
//! # use timelog::Result;
//!
//! # fn main() -> Result<()> {
//! # let mut target: Vec<u8> = Vec::new();
//!   let mut writer = EmitterConfig::new()
//!       .perform_indent(true)
//!       .create_writer(&mut target);
//!   let legend = Legend::new(16.0, ColorIter::default());
//!   let percents = vec![
//!       TagPercent::new("First",  40.0).unwrap(),
//!       TagPercent::new("Second", 30.0).unwrap(),
//!       TagPercent::new("Third",  20.0).unwrap(),
//!       TagPercent::new("Fourth", 10.0).unwrap(),
//!   ];
//!
//!   legend.write(&mut writer, percents.iter()).expect("Failed to write");
//! #   Ok(())
//! #  }
//! ```
//!
//! # Description
//!
//! The [`Legend`] struct holds the configuration information needed to build a
//! legend for a [`PieChart`].

use std::io::Write;

use xml::writer::{EventWriter, XmlEvent};

use crate::chart::tag_percent::TagPercent;
use crate::chart::ColorIter;
#[cfg(doc)]
use crate::chart::PieChart;
use crate::emit_xml;
use crate::Result;

/// Configuration for displaying a legend for a pie chart
pub struct Legend<'a> {
    /// Size of the font used for the legend text.
    font_size: f32,
    /// Iterator over colors for the legends
    colors:    ColorIter<'a>
}

impl<'a> Legend<'a> {
    /// Create a new [ `Legend` ] object.
    pub fn new(font_size: f32, colors: ColorIter<'a>) -> Self { Self { font_size, colors } }

    /// Return the font size for the legend.
    pub fn font_size(&self) -> f32 { self.font_size }

    // Write the color block for a line of the legend
    //
    // # Errors
    //
    // Could return any formatting error
    fn color<W: Write>(&self, w: &mut EventWriter<W>, clr: &str) -> Result<()> {
        let size = format!("{}", self.font_size());
        emit_xml!(w, svg, height: &size, width: &size => {
            emit_xml!(w, rect, height: &size, width: &size, fill: clr)
        })
    }

    // Write a single line of the legend
    //
    // # Errors
    //
    // Could return any formatting error
    fn write_line<W: Write>(&self, w: &mut EventWriter<W>, clr: &str, label: &str) -> Result<()> {
        emit_xml!(w, tr => {
            emit_xml!(w, td => {
                self.color(w, clr)?;
                emit_xml!(w, span; label)
            })
        })
    }

    /// Write a legend representing the supplied percentages.
    ///
    /// # Errors
    ///
    /// Could return any formatting error
    pub fn write<'b, W, Iter>(&self, w: &mut EventWriter<W>, percents: Iter) -> Result<()>
    where
        Iter: Iterator<Item = &'b TagPercent>,
        W: Write
    {
        emit_xml!(w, table, class: "legend", style: &format!("font-size: {}px", self.font_size()) => {
            for (p, clr) in percents.zip(self.colors.clone()) {
                self.write_line(w, clr, &p.display_label())?;
            }
            Ok(())
        })
    }
}

#[cfg(test)]
mod tests {
    use spectral::prelude::*;
    use xml::writer::EmitterConfig;

    use super::*;

    #[test]
    fn test_new() {
        let legend = Legend::new(12.0, ColorIter::default());
        assert_that!(legend.font_size()).is_equal_to(&12.0);
    }

    #[test]
    fn test_fmt_line() {
        let colors = ["blue", "green"];
        let legend = Legend::new(14.0, ColorIter::new(&colors));
        let tags = [
            TagPercent::new("Foo", 30.0).unwrap(),
            TagPercent::new("Bar", 70.0).unwrap()
        ];

        let mut actual: Vec<u8> = Vec::new();
        let mut w = EmitterConfig::new()
            .perform_indent(true)
            .write_document_declaration(false)
            .create_writer(&mut actual);

        let expected = r#"<table class="legend" style="font-size: 14px">
  <tr>
    <td>
      <svg height="14" width="14">
        <rect height="14" width="14" fill="blue" />
      </svg>
      <span>30% - Foo</span>
    </td>
  </tr>
  <tr>
    <td>
      <svg height="14" width="14">
        <rect height="14" width="14" fill="green" />
      </svg>
      <span>70% - Bar</span>
    </td>
  </tr>
</table>"#;
        assert_that!(legend.write(&mut w, tags.iter())).is_ok();
        assert_that!(String::from_utf8(actual).unwrap()).is_equal_to(expected.to_string())
    }
}