1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
extern crate icns;

use crate::{Icon, Entry, SourceImage, Error, STD_CAPACITY};
use std::{result, io::{self, Write}, fmt::{self, Debug, Formatter}};
use image::{DynamicImage, GenericImageView};

/// A collection of entries stored in a single `.icns` file.
pub struct Icns {
    icon_family: icns::IconFamily,
    entries: Vec<u32>
}

impl Icon<Entry> for Icns {
    fn new() -> Self {
        Icns {
            icon_family: icns::IconFamily::new(),
            entries: Vec::with_capacity(STD_CAPACITY)
        }
    }

    fn add_entry<F: FnMut(&SourceImage, u32) -> DynamicImage>(
        &mut self,
        mut filter: F,
        source: &SourceImage,
        entry: Entry
    ) -> Result<(), Error<Entry>> {
        let icon = filter(source, entry.0);
        let data = icon.to_rgba().into_vec();

        if self.entries.contains(&entry.0) {
            return Err(Error::AlreadyIncluded(entry));
        }

        // The Image::from_data method only fails when the specified
        // image dimensions do not fit the buffer length
        let image = icns::Image::from_data(icns::PixelFormat::RGBA, entry.0, entry.0, data)
            .map_err(|_| Error::InvalidDimensions(entry.0, icon.dimensions()))?;

        // The IconFamily::add_icon method only fails when the
        // specified image dimensions are not supported by ICNS
        self.icon_family.add_icon(&image)
        .map_err(|_| Error::InvalidSize(entry.0))
    }

    fn write<W: Write>(&mut self, w: &mut W) -> io::Result<()> {
        self.icon_family.write(w)
    }
}

impl Clone for Icns {
    fn clone(&self) -> Self {
        let mut icon_family = icns::IconFamily {
            elements: Vec::with_capacity(self.icon_family.elements.len())
        };

        for element in &self.icon_family.elements {
            let clone = icns::IconElement::new(element.ostype, element.data.clone());

            icon_family.elements.push(clone);
        }

        Icns {
            icon_family,
            entries: self.entries.clone()
        }
    }
}

macro_rules! element {
    ($elm:expr) => {
        format!("IconElement {{ ostype: {:?}, data: {:?} }}", $elm.ostype, $elm.data )
    };
}

impl Debug for Icns {
    fn fmt(&self, f: &mut Formatter) -> result::Result<(), fmt::Error> {
        let entries_strs: Vec<String> = self.icon_family.elements.iter()
            .map(|element| element!(element)).collect();

        let icon_dir = format!(
            "icns::IconFamily {{ elements: [{}] }}",
            entries_strs.join(", ")
        );

        write!(f, "iconwriter::Icns {{ icon_family: {} }} ", icon_dir)
    }
}