From d00866a795d38a0a0106ad00063ea79d05f381ca Mon Sep 17 00:00:00 2001
From: Janne Mareike Koschinski <janne@kuschku.de>
Date: Tue, 10 Mar 2020 23:59:27 +0100
Subject: [PATCH] reorganize code to allow for easier testing

---
 Cargo.lock                                    | 48 ++++++++++++++-----
 Cargo.toml                                    |  8 ++--
 lib/ffmpeg_api/Cargo.toml                     | 16 +++++++
 {src/ffmpeg_api => lib/ffmpeg_api/src}/api.rs |  5 +-
 .../ffmpeg_api/src}/enums.rs                  |  0
 lib/ffmpeg_api/src/lib.rs                     |  5 ++
 lib/ffmpeg_api/src/tests.rs                   |  4 ++
 lib/media_time/Cargo.toml                     | 11 +++++
 lib/media_time/src/errors.rs                  | 17 +++++++
 lib/media_time/src/lib.rs                     |  8 ++++
 .../util => lib/media_time/src}/media_time.rs | 14 +++---
 lib/media_time/src/tests.rs                   |  4 ++
 lib/webvtt/Cargo.toml                         | 10 ++++
 lib/webvtt/src/lib.rs                         |  6 +++
 lib/webvtt/src/tests.rs                       |  4 ++
 {src/util => lib/webvtt/src}/webvtt.rs        |  8 ++--
 src/ffmpeg_api/mod.rs                         |  2 -
 src/ingest/mod.rs                             |  2 -
 src/{ingest/extract.rs => lib.rs}             | 20 ++++----
 src/main.rs                                   | 15 ++----
 src/{ingest => }/spritesheet.rs               |  4 +-
 src/{util => }/stream_metadata.rs             |  2 +-
 src/util/mod.rs                               |  3 --
 23 files changed, 157 insertions(+), 59 deletions(-)
 create mode 100644 lib/ffmpeg_api/Cargo.toml
 rename {src/ffmpeg_api => lib/ffmpeg_api/src}/api.rs (98%)
 rename {src/ffmpeg_api => lib/ffmpeg_api/src}/enums.rs (100%)
 create mode 100644 lib/ffmpeg_api/src/lib.rs
 create mode 100644 lib/ffmpeg_api/src/tests.rs
 create mode 100644 lib/media_time/Cargo.toml
 create mode 100644 lib/media_time/src/errors.rs
 create mode 100644 lib/media_time/src/lib.rs
 rename {src/util => lib/media_time/src}/media_time.rs (83%)
 create mode 100644 lib/media_time/src/tests.rs
 create mode 100644 lib/webvtt/Cargo.toml
 create mode 100644 lib/webvtt/src/lib.rs
 create mode 100644 lib/webvtt/src/tests.rs
 rename {src/util => lib/webvtt/src}/webvtt.rs (83%)
 delete mode 100644 src/ffmpeg_api/mod.rs
 delete mode 100644 src/ingest/mod.rs
 rename src/{ingest/extract.rs => lib.rs} (93%)
 rename src/{ingest => }/spritesheet.rs (98%)
 rename src/{util => }/stream_metadata.rs (96%)
 delete mode 100644 src/util/mod.rs

diff --git a/Cargo.lock b/Cargo.lock
index fc4140e..a31f669 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -8,9 +8,9 @@ checksum = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2"
 
 [[package]]
 name = "aho-corasick"
-version = "0.7.8"
+version = "0.7.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "743ad5a418686aad3b87fd14c43badd828cf26e214a00f92a384291cf22e1811"
+checksum = "8716408b8bc624ed7f65d223ddb9ac2d044c0547b6fa4b0d554f3a9540496ada"
 dependencies = [
  "memchr",
 ]
@@ -287,6 +287,18 @@ dependencies = [
  "tar",
 ]
 
+[[package]]
+name = "ffmpeg_api"
+version = "0.1.0"
+dependencies = [
+ "bitflags",
+ "enum_primitive",
+ "failure",
+ "ffmpeg-dev",
+ "fraction",
+ "media_time",
+]
+
 [[package]]
 name = "filetime"
 version = "0.2.8"
@@ -454,23 +466,30 @@ checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
 name = "media-ingestion"
 version = "0.1.0"
 dependencies = [
- "bitflags",
- "enum_primitive",
  "failure",
- "ffmpeg-dev",
+ "ffmpeg_api",
  "fraction",
  "image",
+ "media_time",
  "serde",
  "serde_json",
  "structopt",
+ "webvtt",
+]
+
+[[package]]
+name = "media_time"
+version = "0.1.0"
+dependencies = [
+ "fraction",
  "time",
 ]
 
 [[package]]
 name = "memchr"
-version = "2.3.2"
+version = "2.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "53445de381a1f436797497c61d851644d0e8e88e6140f22872ad33a704933978"
+checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
 
 [[package]]
 name = "memoffset"
@@ -719,9 +738,9 @@ dependencies = [
 
 [[package]]
 name = "regex-syntax"
-version = "0.6.14"
+version = "0.6.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b28dfe3fe9badec5dbf0a79a9cccad2cfc2ab5484bdb3e44cbd1ae8b3ba2be06"
+checksum = "1132f845907680735a84409c3bebc64d1364a5683ffbce899550cd09d5eaefc1"
 
 [[package]]
 name = "rustc-demangle"
@@ -1009,11 +1028,18 @@ version = "0.9.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce"
 
+[[package]]
+name = "webvtt"
+version = "0.1.0"
+dependencies = [
+ "media_time",
+]
+
 [[package]]
 name = "which"
-version = "3.1.0"
+version = "3.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5475d47078209a02e60614f7ba5e645ef3ed60f771920ac1906d7c1cc65024c8"
+checksum = "d011071ae14a2f6671d0b74080ae0cd8ebf3a6f8c9589a2cd45f23126fe29724"
 dependencies = [
  "libc",
 ]
diff --git a/Cargo.toml b/Cargo.toml
index b446c7f..38df703 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -17,13 +17,13 @@ edition = "2018"
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [dependencies]
-bitflags = "1.2.1"
-enum_primitive = "0.1.1"
 failure = "0.1.6"
-ffmpeg-dev = "0.3.8"
 fraction = "0.6.2"
 image = "0.23.0"
 serde_json = "1.0"
 serde = { version = "1.0", features = ["derive"] }
 structopt = "0.3"
-time = "0.2"
+
+ffmpeg_api = { path = "lib/ffmpeg_api" }
+media_time = { path = "lib/media_time" }
+webvtt = { path = "lib/webvtt" }
\ No newline at end of file
diff --git a/lib/ffmpeg_api/Cargo.toml b/lib/ffmpeg_api/Cargo.toml
new file mode 100644
index 0000000..ae7ec40
--- /dev/null
+++ b/lib/ffmpeg_api/Cargo.toml
@@ -0,0 +1,16 @@
+[package]
+name = "ffmpeg_api"
+version = "0.1.0"
+authors = ["Janne Mareike Koschinski <janne@kuschku.de>"]
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+bitflags = "1.2.1"
+enum_primitive = "0.1.1"
+failure = "0.1.6"
+ffmpeg-dev = "0.3.8"
+fraction = "0.6.2"
+
+media_time = { path = "../media_time" }
\ No newline at end of file
diff --git a/src/ffmpeg_api/api.rs b/lib/ffmpeg_api/src/api.rs
similarity index 98%
rename from src/ffmpeg_api/api.rs
rename to lib/ffmpeg_api/src/api.rs
index d2a9731..d84f9bc 100644
--- a/src/ffmpeg_api/api.rs
+++ b/lib/ffmpeg_api/src/api.rs
@@ -6,8 +6,7 @@ use failure::{bail, format_err, Error};
 use ffmpeg_dev::sys as ffi;
 use fraction::Fraction;
 
-use crate::ffmpeg_api::enums::*;
-use crate::util::media_time;
+use crate::enums::*;
 
 fn native_string(ptr: *const std::os::raw::c_char) -> Result<String, Error> {
     if ptr.is_null() {
@@ -96,6 +95,7 @@ impl AVFormatContext {
             unsafe { (*self.base).duration },
             Fraction::new(1 as u64, ffi::AV_TIME_BASE as u64),
         )
+        .map_err(|err| format_err!("error extracting duration: {}", err))
     }
 }
 
@@ -343,6 +343,7 @@ impl<'a> AVStream<'a> {
 
     pub fn timestamp(&self, timestamp: i64) -> Result<media_time::MediaTime, Error> {
         media_time::MediaTime::from_rational(timestamp, self.time_base())
+            .map_err(|err| format_err!("error extracting timestamp: {}", err))
     }
 
     pub fn duration(&self) -> Result<media_time::MediaTime, Error> {
diff --git a/src/ffmpeg_api/enums.rs b/lib/ffmpeg_api/src/enums.rs
similarity index 100%
rename from src/ffmpeg_api/enums.rs
rename to lib/ffmpeg_api/src/enums.rs
diff --git a/lib/ffmpeg_api/src/lib.rs b/lib/ffmpeg_api/src/lib.rs
new file mode 100644
index 0000000..6efd89a
--- /dev/null
+++ b/lib/ffmpeg_api/src/lib.rs
@@ -0,0 +1,5 @@
+#[cfg(test)]
+mod tests;
+
+pub mod api;
+pub mod enums;
diff --git a/lib/ffmpeg_api/src/tests.rs b/lib/ffmpeg_api/src/tests.rs
new file mode 100644
index 0000000..2837a57
--- /dev/null
+++ b/lib/ffmpeg_api/src/tests.rs
@@ -0,0 +1,4 @@
+#[test]
+fn it_works() {
+    assert_eq!(2 + 2, 4);
+}
diff --git a/lib/media_time/Cargo.toml b/lib/media_time/Cargo.toml
new file mode 100644
index 0000000..91c4c12
--- /dev/null
+++ b/lib/media_time/Cargo.toml
@@ -0,0 +1,11 @@
+[package]
+name = "media_time"
+version = "0.1.0"
+authors = ["Janne Mareike Koschinski <janne@kuschku.de>"]
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+fraction = "0.6.2"
+time = "0.2"
\ No newline at end of file
diff --git a/lib/media_time/src/errors.rs b/lib/media_time/src/errors.rs
new file mode 100644
index 0000000..287ad3d
--- /dev/null
+++ b/lib/media_time/src/errors.rs
@@ -0,0 +1,17 @@
+use std::error;
+use std::fmt;
+
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct TimeBaseError;
+
+impl fmt::Display for TimeBaseError {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "invalid time base")
+    }
+}
+
+impl error::Error for TimeBaseError {
+    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
+        None
+    }
+}
diff --git a/lib/media_time/src/lib.rs b/lib/media_time/src/lib.rs
new file mode 100644
index 0000000..9ddca6f
--- /dev/null
+++ b/lib/media_time/src/lib.rs
@@ -0,0 +1,8 @@
+#[cfg(test)]
+mod tests;
+
+mod errors;
+mod media_time;
+
+pub use errors::*;
+pub use media_time::MediaTime;
diff --git a/src/util/media_time.rs b/lib/media_time/src/media_time.rs
similarity index 83%
rename from src/util/media_time.rs
rename to lib/media_time/src/media_time.rs
index 00c5b11..9d20231 100644
--- a/src/util/media_time.rs
+++ b/lib/media_time/src/media_time.rs
@@ -1,17 +1,15 @@
-use failure::{format_err, Error};
+use crate::errors::TimeBaseError;
 use fraction::Fraction;
 
+type Result<T> = std::result::Result<T, TimeBaseError>;
+
 #[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
 pub struct MediaTime(time::Duration);
 
 impl MediaTime {
-    pub fn from_rational(timestamp: i64, base: Fraction) -> Result<MediaTime, Error> {
-        let num: u64 = *base
-            .numer()
-            .ok_or_else(|| format_err!("time base of unusable format"))?;
-        let den: u64 = *base
-            .denom()
-            .ok_or_else(|| format_err!("time base of unusable format"))?;
+    pub fn from_rational(timestamp: i64, base: Fraction) -> Result<MediaTime> {
+        let num: u64 = *base.numer().ok_or(TimeBaseError)?;
+        let den: u64 = *base.denom().ok_or(TimeBaseError)?;
 
         Ok(MediaTime(time::Duration::milliseconds(
             (1000 * timestamp as i128 * num as i128 / den as i128) as i64,
diff --git a/lib/media_time/src/tests.rs b/lib/media_time/src/tests.rs
new file mode 100644
index 0000000..2837a57
--- /dev/null
+++ b/lib/media_time/src/tests.rs
@@ -0,0 +1,4 @@
+#[test]
+fn it_works() {
+    assert_eq!(2 + 2, 4);
+}
diff --git a/lib/webvtt/Cargo.toml b/lib/webvtt/Cargo.toml
new file mode 100644
index 0000000..c665a7e
--- /dev/null
+++ b/lib/webvtt/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "webvtt"
+version = "0.1.0"
+authors = ["Janne Mareike Koschinski <janne@kuschku.de>"]
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+media_time = { path = "../media_time" }
\ No newline at end of file
diff --git a/lib/webvtt/src/lib.rs b/lib/webvtt/src/lib.rs
new file mode 100644
index 0000000..94a3747
--- /dev/null
+++ b/lib/webvtt/src/lib.rs
@@ -0,0 +1,6 @@
+#[cfg(test)]
+mod tests;
+
+mod webvtt;
+
+pub use webvtt::{WebVTTCue, WebVTTFile};
diff --git a/lib/webvtt/src/tests.rs b/lib/webvtt/src/tests.rs
new file mode 100644
index 0000000..2837a57
--- /dev/null
+++ b/lib/webvtt/src/tests.rs
@@ -0,0 +1,4 @@
+#[test]
+fn it_works() {
+    assert_eq!(2 + 2, 4);
+}
diff --git a/src/util/webvtt.rs b/lib/webvtt/src/webvtt.rs
similarity index 83%
rename from src/util/webvtt.rs
rename to lib/webvtt/src/webvtt.rs
index 7a6788e..a1e8d0c 100644
--- a/src/util/webvtt.rs
+++ b/lib/webvtt/src/webvtt.rs
@@ -4,7 +4,9 @@ use std::io::LineWriter;
 use std::path::Path;
 use std::string::String;
 
-use crate::util::media_time::MediaTime;
+use media_time::MediaTime;
+
+type Result<T> = std::result::Result<T, std::io::Error>;
 
 pub struct WebVTTFile {
     cues: Vec<WebVTTCue>,
@@ -25,7 +27,7 @@ impl WebVTTFile {
         self.cues.push(cue);
     }
 
-    pub fn save(&self, path: impl AsRef<Path>) -> Result<(), std::io::Error> {
+    pub fn save(&self, path: impl AsRef<Path>) -> Result<()> {
         let file = File::create(path)?;
         let mut file = LineWriter::new(file);
         file.write_all(b"WEBVTT\n\n")?;
@@ -46,7 +48,7 @@ impl WebVTTCue {
         }
     }
 
-    fn save(&self, writer: &mut LineWriter<File>) -> Result<(), std::io::Error> {
+    fn save(&self, writer: &mut LineWriter<File>) -> Result<()> {
         writer.write_all(format!("{} --> {}\n", self.start, self.end).as_bytes())?;
         writer.write_all(self.payload.as_bytes())?;
         writer.write_all(b"\n\n")?;
diff --git a/src/ffmpeg_api/mod.rs b/src/ffmpeg_api/mod.rs
deleted file mode 100644
index 30b930b..0000000
--- a/src/ffmpeg_api/mod.rs
+++ /dev/null
@@ -1,2 +0,0 @@
-pub(crate) mod api;
-pub(crate) mod enums;
diff --git a/src/ingest/mod.rs b/src/ingest/mod.rs
deleted file mode 100644
index 1d0c622..0000000
--- a/src/ingest/mod.rs
+++ /dev/null
@@ -1,2 +0,0 @@
-pub(crate) mod extract;
-pub(crate) mod spritesheet;
diff --git a/src/ingest/extract.rs b/src/lib.rs
similarity index 93%
rename from src/ingest/extract.rs
rename to src/lib.rs
index a633087..e86963f 100644
--- a/src/ingest/extract.rs
+++ b/src/lib.rs
@@ -1,19 +1,19 @@
+#![allow(dead_code)]
+pub mod spritesheet;
+pub mod stream_metadata;
+
 use std::path::Path;
 
 use failure::{format_err, Error};
+use ffmpeg_api::api::*;
+use ffmpeg_api::enums::*;
 use image::ImageOutputFormat;
 
-use crate::ffmpeg_api::api::*;
-use crate::ffmpeg_api::enums::*;
-use crate::ingest::spritesheet::*;
-use crate::util::media_time::*;
-use crate::util::stream_metadata::*;
-
 pub fn extract(
     max_size: u32,
     num_horizontal: u32,
     num_vertical: u32,
-    frame_interval: MediaTime,
+    frame_interval: media_time::MediaTime,
     input_file: &Path,
     output_folder: &Path,
     format: ImageOutputFormat,
@@ -29,7 +29,7 @@ pub fn extract(
 
     let spritesheet_path = output_folder.join("spritesheets");
     std::fs::create_dir_all(&spritesheet_path)?;
-    let mut spritesheet_manager = SpritesheetManager::new(
+    let mut spritesheet_manager = spritesheet::SpritesheetManager::new(
         max_size,
         num_horizontal,
         num_vertical,
@@ -63,7 +63,7 @@ pub fn extract(
         local_codec.name()?
     );
 
-    let mut metadata = StreamMetadata::new(
+    let mut metadata = stream_metadata::StreamMetadata::new(
         avformat_context
             .input_format()?
             .determine_mime(local_codec.name()?)?,
@@ -98,7 +98,7 @@ pub fn extract(
                     .in_packet(&mut packet)
                     .map_err(|error| format_err!("Could not load packet: {}", error))?;
                 while codec_context.out_frame(&mut frame).is_ok() {
-                    let timestamp = MediaTime::from_rational(frame.pts(), time_base)?;
+                    let timestamp = media_time::MediaTime::from_rational(frame.pts(), time_base)?;
 
                     println!(
                         "Frame {}: {} @ {}",
diff --git a/src/main.rs b/src/main.rs
index 3c99314..448acb1 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,17 +1,10 @@
-#![allow(dead_code)]
-
-pub(crate) mod ffmpeg_api;
-pub(crate) mod ingest;
-pub(crate) mod util;
-
 use std::path::Path;
 
 use failure::Error;
-use structopt::StructOpt;
-
-use crate::ffmpeg_api::enums::{SwsFlags, SwsScaler};
-use crate::util::media_time::MediaTime;
+use ffmpeg_api::enums::{SwsFlags, SwsScaler};
 use image::ImageOutputFormat;
+use media_time::MediaTime;
+use structopt::StructOpt;
 
 fn parse_scaler(src: &str) -> Result<SwsScaler, String> {
     match src {
@@ -71,7 +64,7 @@ fn main() -> Result<(), Error> {
         flags |= SwsFlags::BIT_EXACT_SCALING;
     }
 
-    ingest::extract::extract(
+    media_ingestion::extract(
         options.max_size,
         options.num_horizontal,
         options.num_vertical,
diff --git a/src/ingest/spritesheet.rs b/src/spritesheet.rs
similarity index 98%
rename from src/ingest/spritesheet.rs
rename to src/spritesheet.rs
index de26266..dc69f6d 100644
--- a/src/ingest/spritesheet.rs
+++ b/src/spritesheet.rs
@@ -5,8 +5,8 @@ use std::path::PathBuf;
 use failure::{bail, format_err, Error};
 use image::{DynamicImage, ImageOutputFormat, RgbImage};
 
-use crate::util::media_time::MediaTime;
-use crate::util::webvtt::{WebVTTCue, WebVTTFile};
+use media_time::MediaTime;
+use webvtt::{WebVTTCue, WebVTTFile};
 
 pub enum ImageFormat {
     Jpeg(i32),
diff --git a/src/util/stream_metadata.rs b/src/stream_metadata.rs
similarity index 96%
rename from src/util/stream_metadata.rs
rename to src/stream_metadata.rs
index 9120347..b4fdc88 100644
--- a/src/util/stream_metadata.rs
+++ b/src/stream_metadata.rs
@@ -4,7 +4,7 @@ use std::path::Path;
 
 use serde::{Deserialize, Serialize};
 
-use crate::util::media_time::MediaTime;
+use media_time::MediaTime;
 
 #[derive(Serialize, Deserialize)]
 pub struct StreamMetadata {
diff --git a/src/util/mod.rs b/src/util/mod.rs
deleted file mode 100644
index 28d1532..0000000
--- a/src/util/mod.rs
+++ /dev/null
@@ -1,3 +0,0 @@
-pub(crate) mod media_time;
-pub(crate) mod stream_metadata;
-pub(crate) mod webvtt;
-- 
GitLab