document.rs 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. use failure::Error;
  2. use image;
  3. use serde_json;
  4. use std::io::{Read, Write, Seek};
  5. use zip::{ZipArchive, ZipWriter};
  6. /// This value represents both the current document in-memory as well
  7. /// as the entirety of the values that we will want to both save and
  8. /// restore.
  9. pub struct Document {
  10. pub tilesheet: image::DynamicImage,
  11. pub metadata: Metadata,
  12. pub rules: (),
  13. }
  14. /// This should be renamed probably, but it's the configuration-level
  15. /// info about a document (e.g. the tile size)
  16. #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
  17. pub struct Metadata {
  18. pub tile_width: u16,
  19. pub tile_height: u16,
  20. pub config_loop_forever: bool,
  21. }
  22. impl Document {
  23. /// Attempt to read a `Document` from anything which implements
  24. /// `Read` and `Seek`. The file format for Palladio files is
  25. /// documented externally.
  26. pub fn open_from_file<R: Read + Seek>(r: &mut R) -> Result<Document, Error> {
  27. let mut archive = ZipArchive::new(r)?;
  28. let tilesheet = {
  29. let mut buf = Vec::new();
  30. archive.by_name("tilesheet.png")?.read_to_end(&mut buf)?;
  31. image::load_from_memory(&buf)?
  32. };
  33. let metadata = {
  34. let file = archive.by_name("metadata.json")?;
  35. serde_json::from_reader(file)?
  36. };
  37. let rules = {
  38. let file = archive.by_name("rules.json")?;
  39. serde_json::from_reader(file)?
  40. };
  41. Ok(Document{ tilesheet, metadata, rules })
  42. }
  43. /// Attempt to write a `Document` from anything which implements
  44. /// `Write` and `Seek`. The file format for Palladio files is
  45. /// documented externally.
  46. pub fn save_to_file<W: Write + Seek>(&self, w: &mut W) -> Result<(), Error> {
  47. use zip::write::FileOptions;
  48. let mut zip = ZipWriter::new(w);
  49. zip.start_file("tilesheet.png", FileOptions::default())?;
  50. self.tilesheet.write_to(&mut zip, image::ImageOutputFormat::PNG)?;
  51. zip.start_file("metadata.json", FileOptions::default())?;
  52. serde_json::to_writer(&mut zip, &self.metadata)?;
  53. zip.start_file("rules.json", FileOptions::default())?;
  54. serde_json::to_writer(&mut zip, &self.rules)?;
  55. zip.start_file("info.txt", FileOptions::default())?;
  56. writeln!(
  57. &mut zip,
  58. "Created by {} v{}",
  59. ::constants::PROGRAM_NAME,
  60. ::constants::PROGRAM_VERSION,
  61. )?;
  62. zip.finish()?;
  63. Ok(())
  64. }
  65. /// Create a new fresh document with an empty 32x32 image, a
  66. /// configured tile size of 16x16, and no rules
  67. pub fn default() -> Document {
  68. Document {
  69. tilesheet: image::DynamicImage::new_rgb8(32, 32),
  70. metadata: Metadata {
  71. tile_width: 16,
  72. tile_height: 16,
  73. config_loop_forever: false,
  74. },
  75. rules: (),
  76. }
  77. }
  78. }
  79. #[cfg(test)]
  80. mod tests {
  81. use super::Document;
  82. use std::io::{BufReader, Cursor};
  83. #[test]
  84. fn round_trip() {
  85. // First, save our dummy `Document` to an in-memory buffer
  86. let mut buf = Cursor::new(Vec::new());
  87. let doc1 = Document::default();
  88. doc1.save_to_file(&mut buf).unwrap();
  89. // then take that buffer back, and reparse it
  90. let buf = buf.into_inner();
  91. let doc2 = Document::open_from_file(&mut BufReader::new(Cursor::new(buf))).unwrap();
  92. // we can't assert equality over the image itself, so let's
  93. // just assert that the other parts are equal
  94. assert!(doc1.metadata == doc2.metadata);
  95. assert!(doc1.rules == doc2.rules);
  96. }
  97. }