kzones-config 0.1.2

Make definitions of kzones layouts easier
use serde::Serialize;

#[derive(Debug, Serialize, Clone, PartialEq, Eq, Hash)]
pub struct Zone {
    pub x: u8,
    pub y: u8,
    pub width: u8,
    pub height: u8,
}

impl Zone {
    pub fn full() -> Zone {
        Zone {
            x: 0,
            y: 0,
            width: 100,
            height: 100,
        }
    }

    pub fn slice<'a>(
        &'a self,
        divisions: &'a [f64],
        direction: Direction,
    ) -> impl Iterator<Item = Zone> + 'a {
        let size = match direction {
            Direction::Horizontal => self.width,
            Direction::Vertical => self.height,
        };

        let total: f64 = divisions.iter().sum();
        let mut sizes: Vec<u8> = divisions
            .iter()
            .map(move |d| size as f64 * (d / total))
            .map(|f: f64| f.floor() as u8)
            .collect();

        let rem = size - sizes.iter().sum::<u8>();
        for size in sizes.iter_mut().take(rem as usize) {
            *size += 1
        }

        let mut pos = match direction {
            Direction::Horizontal => self.x,
            Direction::Vertical => self.y,
        };

        sizes.into_iter().map(move |size| {
            pos += size;
            match direction {
                Direction::Horizontal => Zone {
                    x: pos - size,
                    y: self.y,
                    width: size,
                    height: self.height,
                },
                Direction::Vertical => Zone {
                    x: self.x,
                    y: pos - size,
                    width: self.width,
                    height: size,
                },
            }
        })
    }
}

impl Default for Zone {
    fn default() -> Self {
        Self::full()
    }
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Direction {
    Horizontal,
    Vertical,
}

#[cfg(test)]
mod tests {
    use super::{Direction, Zone};

    #[test]
    fn test_horizontal_half() {
        let expected = vec![
            Zone {
                x: 0,
                y: 0,
                width: 50,
                height: 100,
            },
            Zone {
                x: 50,
                y: 0,
                width: 50,
                height: 100,
            },
        ];
        let actual: Vec<_> = Zone::full()
            .slice(&[1.0, 1.0], Direction::Horizontal)
            .collect();

        assert_eq!(expected, actual);
    }

    #[test]
    fn test_vertical_three() {
        let expected = vec![
            Zone {
                x: 0,
                y: 0,
                width: 100,
                height: 34,
            },
            Zone {
                x: 0,
                y: 34,
                width: 100,
                height: 33,
            },
            Zone {
                x: 0,
                y: 67,
                width: 100,
                height: 33,
            },
        ];
        let actual: Vec<_> = Zone::full()
            .slice(&[1.0, 1.0, 1.0], Direction::Vertical)
            .collect();

        assert_eq!(expected, actual);
    }
}