takoyaki_core 1.2.0

Core package to build plugins for takoyaki
Documentation
// Import dependencies
use crate::{Pick, TakoyakiError};
use colored::*;
use serde_json::Value;

/// Defines a printable
///
/// ## Properties
/// * `color` - The color that should be used (fallback color)
/// * `count` - The number of contribution done by the user
#[derive(Default, Clone, PartialEq, Debug, Eq)]
pub struct Printable {
    pub color: String,
    pub count: i64,
}

/// The PrintableGrid. This handles generating the grid and printing it to the terminal
#[derive(Default, Clone, PartialEq, Debug, Eq)]
pub struct PrintableGrid<'a> {
    pick: Pick<'a>,
    data: serde_json::Value,
    pub grid: Vec<Vec<Printable>>,
}

// Add functions
impl<'a> PrintableGrid<'a> {
    /// Creates a new instance of printable grid
    ///
    /// # Arguments
    /// * `pick` - The Pick instance which defines which keys to pick for certain values
    /// * `data` - This data that needs to be printed
    ///
    /// # Examples
    ///
    /// ```
    /// use takoyaki_core::{PrintableGrid, Pick};
    ///
    /// let printable = PrintableGrid::new(
    ///     Pick {
    ///         array_root: "array_root",
    ///         weeks_root: "weeks_root",
    ///         color_key: "color_key",
    ///         contribution_count_key: "contribution_count_key",
    ///     },
    ///     serde_json::Value::String("".to_string())
    /// );
    /// ```
    pub fn new(pick: Pick<'a>, data: serde_json::Value) -> Self {
        Self {
            pick,
            data,
            grid: vec![],
        }
    }

    /// Inserts a printable at a specific position in the grid
    ///
    /// # Arguments
    /// * `x` - The position on the x axis
    /// * `y` - The position on the y axis
    /// * `item` - The Printable that needs to be inserted at the specific position
    ///
    /// # Examples
    ///
    /// ```
    /// use takoyaki_core::{PrintableGrid, Pick, Printable};
    ///
    /// let mut printable_grid = PrintableGrid::new(
    ///     Pick {
    ///         array_root: "array_root",
    ///         weeks_root: "weeks_root",
    ///         color_key: "color_key",
    ///         contribution_count_key: "contribution_count_key",
    ///     },
    ///     serde_json::Value::String("".to_string())
    /// );
    ///
    /// let printable = Printable {
    ///     color: "#88C0D0".to_string(),
    ///     count: 10
    /// };
    ///
    /// printable_grid.insert_at(2, 1, printable.clone());
    ///
    /// assert_eq!(printable_grid.grid[2][1], printable);
    /// ```
    pub fn insert_at(&mut self, x: usize, y: usize, item: Printable) {
        // Resize the grid on the x axis with vec![] as a default fill value
        if self.grid.len() <= x {
            self.grid.resize(x + 1, vec![])
        }

        // Resize the grid on the y axis with PrintableGrid::default() as a default fill value
        if self.grid[x].len() <= y {
            self.grid[x].resize(y, Printable::default())
        }

        // Insert at the specific location
        self.grid[x].insert(y, item);
    }

    /// Prints the grid to the terminal
    fn print(&self) -> Result<(), TakoyakiError> {
        // Get the config
        let config = crate::Config::new()?;

        // Iterate through the rows of the grid
        for row in &self.grid {
            // Iterate through the items of the row
            for item in row {
                // Get the fallback color
                let fallback = serde_yaml::Value::String(item.color.clone());

                // Get the color defined in the config
                let color = config
                    .colors
                    .get(format!("{}_contribution", item.count))
                    .or(config.colors.get("x_contribution"))
                    .unwrap_or(&fallback)
                    .as_str()
                    .unwrap();

                // Convert it to a hex format
                let rgb = colorsys::Rgb::from_hex_str(color)?;

                // Check if the user wants to paint bg or fg
                if config.unicode.paint_bg {
                    print!(
                        "{}",
                        config.unicode.character.on_truecolor(
                            rgb.red() as u8,
                            rgb.green() as u8,
                            rgb.blue() as u8
                        )
                    )
                } else {
                    print!(
                        "{}",
                        config.unicode.character.truecolor(
                            rgb.red() as u8,
                            rgb.green() as u8,
                            rgb.blue() as u8
                        )
                    )
                }
            }

            // Go to next line
            println!()
        }

        // Ok!
        Ok(())
    }

    fn iterate_through_value(
        &self,
        json: &'a Value,
        keys: &'a str,
    ) -> Result<&'a Value, TakoyakiError> {
        // Get the specific target
        keys.split('.')
            .fold(Some(json), |prev, next| {
                prev.and_then(|value| value.get(next))
            })
            .ok_or(TakoyakiError::RootNotFound(keys.to_string()))
    }

    /// Pretty prints the grid to the terminal
    pub fn pretty_print(&mut self) -> Result<(), TakoyakiError> {
        // Create a clone of the root data
        let root_clone = &self.data.clone();

        // Get the root of the contribution count
        let weeks_list = self
            .iterate_through_value(root_clone, self.pick.array_root)?
            .as_array()
            .ok_or(TakoyakiError::UnexpectedType("Vec<Value>".to_string()))?;

        // Positions of the printable
        let mut x = 0;

        // Iterate through all the weeks
        for (y, week) in weeks_list.iter().enumerate() {
            // Create days list
            let days_list = self
                .iterate_through_value(week, self.pick.weeks_root)?
                .as_array()
                .ok_or(TakoyakiError::UnexpectedType("Vec<Value>".to_string()))?;

            // Iterate through the days
            for day in days_list {
                // Insert the printable
                self.insert_at(
                    x,
                    y,
                    Printable {
                        color: self
                            .iterate_through_value(day, self.pick.color_key)
                            .unwrap()
                            .as_str()
                            .unwrap()
                            .to_string(),
                        count: self
                            .iterate_through_value(day, self.pick.contribution_count_key)
                            .unwrap()
                            .as_i64()
                            .unwrap(),
                    },
                );
                x += 1;
            }

            x = 0;
        }

        // Print the data
        self.print()?;

        // Ok!
        Ok(())
    }
}