Merge branch 'stack_redesign' into 'master'

Stack redesign

See merge request alatiera/Hammond!4
This commit is contained in:
Jordan Petridis 2017-12-13 16:28:06 +00:00
commit da57a966d5
21 changed files with 494 additions and 595 deletions

View File

@ -20,6 +20,12 @@ It is recommended to add a pre-commit hook to run cargo test and cargo fmt
cargo test -- --test-threads=1 && cargo fmt --all -- --write-mode=diff
```
## Running the test suite
The test suite sets a temporary sqlite database in the /tmp folder. Due to that it's not possible to run them in parrallel.
In order to run the test suite use the following: `cargo test -- --test-threads=1`
# Issues, issues and more issues!
There are many ways you can contribute to Hammond, and all of them involve creating issues
@ -67,8 +73,10 @@ Steps to reproduce:
## Pull Request Process
1. Ensure your code compiles. Run `make` before creating the pull request.
2. If you're adding new API, it must be properly documented.
3. The commit message is formatted as follows:
2. Ensure the test suit passes. Run `cargo test -- --test-threads=1`.
3. Ensure your code is properly formated. Run `cargo fmt --all`.
4. If you're adding new API, it must be properly documented.
5. The commit message is formatted as follows:
```
component: <summary>
@ -78,7 +86,7 @@ Steps to reproduce:
<link to the bug ticket>
```
4. You may merge the pull request in once you have the sign-off of the maintainers, or if you
6. You may merge the pull request in once you have the sign-off of the maintainers, or if you
do not have permission to do that, you may request the second reviewer to merge it for you.
## Code of Conduct

78
Cargo.lock generated
View File

@ -91,7 +91,7 @@ dependencies = [
[[package]]
name = "base64"
version = "0.6.0"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
@ -590,7 +590,7 @@ dependencies = [
"diesel 0.99.0 (registry+https://github.com/rust-lang/crates.io-index)",
"error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"hammond-data 0.1.0",
"hyper 0.11.7 (registry+https://github.com/rust-lang/crates.io-index)",
"hyper 0.11.9 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"mime_guess 1.8.3 (registry+https://github.com/rust-lang/crates.io-index)",
"reqwest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
@ -636,10 +636,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "hyper"
version = "0.11.7"
version = "0.11.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"base64 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-cpupool 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
@ -650,7 +650,7 @@ dependencies = [
"percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"relay 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-core 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-core 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-io 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -663,9 +663,9 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
"hyper 0.11.7 (registry+https://github.com/rust-lang/crates.io-index)",
"hyper 0.11.9 (registry+https://github.com/rust-lang/crates.io-index)",
"native-tls 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-core 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-core 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-io 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-tls 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
@ -897,8 +897,8 @@ name = "native-tls"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"openssl 0.9.22 (registry+https://github.com/rust-lang/crates.io-index)",
"schannel 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
"openssl 0.9.23 (registry+https://github.com/rust-lang/crates.io-index)",
"schannel 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
"security-framework 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
"security-framework-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
"tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
@ -963,19 +963,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "openssl"
version = "0.9.22"
version = "0.9.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)",
"openssl-sys 0.9.22 (registry+https://github.com/rust-lang/crates.io-index)",
"openssl-sys 0.9.23 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "openssl-sys"
version = "0.9.22"
version = "0.9.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cc 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1144,7 +1144,7 @@ dependencies = [
"aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"regex-syntax 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"thread_local 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -1168,16 +1168,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
"hyper 0.11.7 (registry+https://github.com/rust-lang/crates.io-index)",
"hyper 0.11.9 (registry+https://github.com/rust-lang/crates.io-index)",
"hyper-tls 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libflate 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"mime_guess 2.0.0-alpha.3 (registry+https://github.com/rust-lang/crates.io-index)",
"native-tls 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_urlencoded 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-core 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-core 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-io 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-tls 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1220,13 +1220,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "schannel"
version = "0.1.8"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"advapi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"crypt32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"secur32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1281,18 +1281,18 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.23"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "serde_json"
version = "1.0.7"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -1302,7 +1302,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)",
"url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -1335,7 +1335,7 @@ dependencies = [
"lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"phf_shared 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)",
"precomputed-hash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)",
"string_cache_codegen 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"string_cache_shared 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -1409,10 +1409,10 @@ dependencies = [
[[package]]
name = "thread_local"
version = "0.3.4"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -1429,7 +1429,7 @@ dependencies = [
[[package]]
name = "tokio-core"
version = "0.1.10"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1464,7 +1464,7 @@ dependencies = [
"slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"take 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-core 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-core 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-io 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -1484,7 +1484,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
"native-tls 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-core 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-core 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-io 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -1619,7 +1619,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum atty 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "21e50800ec991574876040fff8ee46b136a53e985286fbe6a3bdfe6421b78860"
"checksum backtrace 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8709cc7ec06f6f0ae6c2c7e12f6ed41540781f72b488d83734978295ceae182e"
"checksum backtrace-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "44585761d6161b0f57afc49482ab6bd067e4edef48c12a152c237eb0203f7661"
"checksum base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "96434f987501f0ed4eb336a411e0631ecd1afa11574fe148587adc4ff96143c9"
"checksum base64 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7c4a342b450b268e1be8036311e2c613d7f8a7ed31214dff1cc3b60852a3168d"
"checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d"
"checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5"
"checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf"
@ -1672,7 +1672,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum gtk-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "905fcfbaaad1b44ec0b4bba9e4d527d728284c62bc2ba41fccedace2b096766f"
"checksum html5ever 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba3a1fd1857a714d410c191364c5d7bf8a6487c0ab5575146d37dd7eb17ef523"
"checksum httparse 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "af2f2dd97457e8fb1ae7c5a420db346af389926e36f43768b96f101546b04a07"
"checksum hyper 0.11.7 (registry+https://github.com/rust-lang/crates.io-index)" = "4959ca95f55df4265bff2ad63066147255e6fa733682cf6d1cb5eaff6e53324b"
"checksum hyper 0.11.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e0594792d2109069d0caffd176f674d770a84adf024c5bb48e686b1ee5ac7659"
"checksum hyper-tls 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9c81fa95203e2a6087242c38691a0210f23e9f3f8f944350bd676522132e2985"
"checksum idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "014b298351066f1512874135335d62a789ffe78a9974f94b43ed5621951eaf7d"
"checksum iovec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b6e8b9c2247fcf6c6a1151f1156932be5606c9fd6f55a2d7f9fc1cb29386b2f7"
@ -1709,8 +1709,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "cacfcab5eb48250ee7d0c7896b51a2c5eec99c1feea5f32025635f5ae4b00070"
"checksum num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "514f0d73e64be53ff320680ca671b64fe3fb91da01e1ae2ddc99eb51d453b20d"
"checksum open 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c281318d992e4432cfa799969467003d05921582a7489a8325e37f8a450d5113"
"checksum openssl 0.9.22 (registry+https://github.com/rust-lang/crates.io-index)" = "419ef26bb651d72b6c5a603bcc4e4856a362460e62352dfffa53de91d2e81181"
"checksum openssl-sys 0.9.22 (registry+https://github.com/rust-lang/crates.io-index)" = "5483bdc56756041ba6aa37c9cb59cc2219f012a2a1377d97ad35556ac6676ee7"
"checksum openssl 0.9.23 (registry+https://github.com/rust-lang/crates.io-index)" = "169a4b9160baf9b9b1ab975418c673686638995ba921683a7f1e01470dcb8854"
"checksum openssl-sys 0.9.23 (registry+https://github.com/rust-lang/crates.io-index)" = "2200ffec628e3f14c39fc0131a301db214f1a7d584e36507ee8700b0c7fb7a46"
"checksum pango 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3e81c404ab81ea7ea2fc2431a0a7672507b80e4b8bf4b41eac3fc83cc665104e"
"checksum pango-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "34f34a1be107fe16abb2744e0e206bee4b3b07460b5fddd3009a6aaf60bd69ab"
"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831"
@ -1738,15 +1738,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum rustc-demangle 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "aee45432acc62f7b9a108cc054142dac51f979e69e71ddce7d6fc7adf29e817e"
"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
"checksum safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e27a8b19b835f7aea908818e871f5cc3a5a186550c30773be987e155e8163d8f"
"checksum schannel 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7554288337c1110e34d7a2433518d889374c1de1a45f856b7bcddb03702131fc"
"checksum schannel 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "4330c2e874379fbd28fa67ba43239dbe8c7fb00662ceb1078bd37474f08bf5ce"
"checksum scheduled-thread-pool 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1a2ff3fc5223829be817806c6441279c676e454cc7da608faf03b0ccc09d3889"
"checksum scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f417c22df063e9450888a7561788e9bd46d3bb3c1466435b4eccb903807f147d"
"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27"
"checksum secur32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3f412dfa83308d893101dd59c10d6fda8283465976c28c287c5c855bf8d216bc"
"checksum security-framework 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "dfa44ee9c54ce5eecc9de7d5acbad112ee58755239381f687e564004ba4a2332"
"checksum security-framework-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "5421621e836278a0b139268f36eee0dc7e389b784dc3f79d8f11aabadf41bead"
"checksum serde 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)" = "6a7c37d7f192f00041e8a613e936717923a71bc0c9051fc4425a49b104140f05"
"checksum serde_json 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "ea28ea0cca944668919bec6af209864a8dfe769fd2b0b723f36b22e20c1bf69f"
"checksum serde 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)" = "1c57ab4ec5fa85d08aaf8ed9245899d9bbdd66768945b21113b84d5f595cb6a1"
"checksum serde_json 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7cf5b0b5b4bd22eeecb7e01ac2e1225c7ef5e4272b79ee28a8392a8c8489c839"
"checksum serde_urlencoded 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ce0fd303af908732989354c6f02e05e2e6d597152870f2c6990efb0577137480"
"checksum siphasher 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0df90a788073e8d0235a67e50441d47db7c8ad9debd91cbf43736a2a92d36537"
"checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23"
@ -1761,9 +1761,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "87974a6f5c1dfb344d733055601650059a3363de2a6104819293baff662132d6"
"checksum tendril 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9de21546595a0873061940d994bbbc5c35f024ae4fd61ec5c5b159115684f508"
"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096"
"checksum thread_local 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1697c4b57aeeb7a536b647165a2825faddffb1d3bad386d507709bd51a90bb14"
"checksum thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279ef31c19ededf577bfd12dfae728040a21f635b06a24cd670ff510edd38963"
"checksum time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)" = "d5d788d3aa77bc0ef3e9621256885555368b47bd495c13dd2e7413c89f845520"
"checksum tokio-core 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "c843a027f7c1df5f81e7734a0df3f67bf329411781ebf36393ce67beef6071e3"
"checksum tokio-core 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c87c27560184212c9dc45cd8f38623f37918248aad5b58fb65303b5d07a98c6e"
"checksum tokio-io 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "514aae203178929dbf03318ad7c683126672d4d96eccb77b29603d33c9e25743"
"checksum tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8fbb47ae81353c63c487030659494b295f6cb6576242f907f203473b191b0389"
"checksum tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "24da22d077e0f15f55162bdbdc661228c1581892f52074fb242678d015b45162"

View File

@ -8,8 +8,7 @@ This is a prototype of a podcast client written in Rust.
![podcast_widget](./assets/podcast_widget.png)
## Getting in Touch
If you have any questions regarding the
use or development of Hammond, want to discuss design or simply hang out, please join us in [#hammond on irc.gnome.org.](irc://irc.gnome.org/#hammond)
If you have any questions regarding the use or development of Hammond, want to discuss design or simply hang out, please join us in [#hammond on irc.gnome.org.](irc://irc.gnome.org/#hammond)
Sidenote:
@ -70,14 +69,6 @@ cd Hammond/
cargo build --all
```
## Call for designers
Currently there no design plans or mockups. They are highly needed in order to advance the Gtk Client.
There is the will for a complete client re-write if a someone contributes the mockups.
If you happen to be a designer and want to contribute please hope on [#hammond](irc://irc.gnome.org/#hammond) and get in touch with us.
## Contributing
There alot of thins yet to be done.

View File

@ -81,7 +81,6 @@ pub(crate) fn new_episode(item: &Item, parent_id: i32) -> Result<NewEpisode> {
.unwrap())
}
#[cfg(test)]
mod tests {
use std::fs::File;

View File

@ -6,7 +6,7 @@ workspace = "../"
[dependencies]
error-chain = "0.11.0"
hyper = "0.11.7"
hyper = "0.11.9"
log = "0.3.8"
mime_guess = "1.8.3"
reqwest = "0.8.1"
@ -14,7 +14,7 @@ tempdir = "0.3.5"
[dependencies.diesel]
features = ["sqlite"]
version = "0.99"
version = "0.99.0"
[dependencies.hammond-data]
path = "../hammond-data"

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.1 -->
<!-- Generated with glade 3.20.2 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<object class="GtkBox" id="episode_box">
@ -11,26 +11,23 @@
<property name="margin_bottom">5</property>
<property name="spacing">5</property>
<child>
<object class="GtkButton" id="play_button">
<object class="GtkButton" id="delete_button">
<property name="name">delete_button</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="halign">end</property>
<property name="valign">center</property>
<property name="margin_top">5</property>
<property name="margin_bottom">5</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="stock">gtk-media-play</property>
<property name="use_fallback">True</property>
<property name="icon_name">edit-delete-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="padding">5</property>
<property name="pack_type">end</property>
<property name="position">0</property>
</packing>
@ -49,8 +46,7 @@
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="stock">gtk-save</property>
<property name="use_fallback">True</property>
<property name="icon_name">document-save-symbolic</property>
</object>
</child>
</object>
@ -63,23 +59,25 @@
</packing>
</child>
<child>
<object class="GtkButton" id="delete_button">
<property name="name">delete_button</property>
<object class="GtkButton" id="play_button">
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="halign">end</property>
<property name="valign">center</property>
<property name="margin_top">5</property>
<property name="margin_bottom">5</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="stock">gtk-delete</property>
<property name="icon_name">media-playback-start-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="padding">5</property>
<property name="pack_type">end</property>
<property name="position">0</property>
</packing>
@ -95,7 +93,7 @@
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="stock">gtk-undo</property>
<property name="icon_name">edit-undo-symbolic</property>
</object>
</child>
</object>
@ -118,7 +116,7 @@
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="stock">gtk-apply</property>
<property name="icon_name">object-select-symbolic</property>
</object>
</child>
</object>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.1 -->
<!-- Generated with glade 3.20.2 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<object class="GtkPopover" id="add-popover">
@ -109,24 +109,141 @@
</object>
</child>
</object>
<object class="GtkHeaderBar" id="headerbar1">
<object class="GtkBox" id="back_view">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="has_subtitle">False</property>
<property name="show_close_button">True</property>
<child>
<object class="GtkMenuButton" id="add-toggle-button">
<object class="GtkButton" id="back_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">go-previous-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child type="center">
<object class="GtkLabel" id="show_title">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Show Title</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack_type">end</property>
<property name="position">2</property>
</packing>
</child>
</object>
<object class="GtkHeaderBar" id="headerbar">
<property name="can_focus">False</property>
<property name="show_close_button">True</property>
<child type="title">
<object class="GtkStack" id="headerbar_stack">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<child>
<placeholder/>
</child>
</object>
</child>
</object>
<object class="GtkBox" id="normal_view">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">5</property>
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack_type">end</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkMenuButton" id="menu">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">open-menu-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="pack_type">end</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkButton" id="ref_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="always_show_image">True</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">view-refresh-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="pack_type">end</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkMenuButton" id="add_toggle_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="tooltip_text" translatable="yes">Add a new feed</property>
<property name="valign">center</property>
<child>
<object class="GtkImage" id="add-button-image">
<object class="GtkImage" id="add-button-image1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="stock">gtk-add</property>
<property name="use_fallback">True</property>
<property name="icon_name">list-add-symbolic</property>
<property name="icon_size">1</property>
</object>
</child>
@ -135,40 +252,23 @@
</style>
</object>
<packing>
<property name="position">1</property>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">3</property>
</packing>
</child>
<child>
<child type="center">
<object class="GtkStackSwitcher" id="switch">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="hexpand">True</property>
<property name="icon_size">0</property>
</object>
<packing>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkButton" id="refbutton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="valign">center</property>
<property name="use_underline">True</property>
<property name="always_show_image">True</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="stock">gtk-refresh</property>
<property name="use_fallback">True</property>
</object>
</child>
</object>
<packing>
<property name="pack_type">end</property>
<property name="position">-1</property>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">4</property>
</packing>
</child>
</object>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.1 -->
<!-- Generated with glade 3.20.2 -->
<interface domain="gnome-music">
<requires lib="gtk+" version="3.12"/>
<object class="GtkBox" id="podcast_widget">
@ -35,8 +35,6 @@
<property name="margin_right">1</property>
<property name="margin_start">1</property>
<property name="margin_end">1</property>
<property name="stock">gtk-missing-image</property>
<property name="use_fallback">True</property>
</object>
<packing>
<property name="expand">False</property>
@ -95,7 +93,7 @@
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Unsubrscribe from this Podcast.
Warn: This will delete downloaded content associated with this Podcast.</property>
<property name="stock">gtk-delete</property>
<property name="icon_name">edit-delete-symbolic</property>
</object>
</child>
</object>
@ -118,7 +116,7 @@ Warn: This will delete downloaded content associated with this Podcast.</propert
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="stock">gtk-apply</property>
<property name="icon_name">object-select-symbolic</property>
</object>
</child>
</object>

View File

@ -3,10 +3,10 @@
<gresource prefix="/org/gnome/hammond/">
<file>banner.png</file>
<file preprocess="xml-stripblanks">gtk/episode_widget.ui</file>
<file preprocess="xml-stripblanks">gtk/podcast_widget.ui</file>
<file preprocess="xml-stripblanks">gtk/show_widget.ui</file>
<file preprocess="xml-stripblanks">gtk/empty_view.ui</file>
<file preprocess="xml-stripblanks">gtk/podcasts_view.ui</file>
<file preprocess="xml-stripblanks">gtk/podcasts_child.ui</file>
<file preprocess="xml-stripblanks">gtk/shows_view.ui</file>
<file preprocess="xml-stripblanks">gtk/shows_child.ui</file>
<file preprocess="xml-stripblanks">gtk/headerbar.ui</file>
</gresource>
</gresources>

View File

@ -4,412 +4,174 @@ use gtk::prelude::*;
use hammond_data::Podcast;
use hammond_data::dbqueries;
use widgets::podcast::PodcastWidget;
use views::podcasts::PopulatedView;
use views::shows::ShowsPopulated;
use views::empty::EmptyView;
#[derive(Debug)]
use widgets::show::ShowWidget;
use headerbar::Header;
use std::rc::Rc;
#[derive(Debug, Clone)]
pub struct Content {
pub stack: gtk::Stack,
pub widget: PodcastWidget,
pub podcasts: PopulatedView,
pub empty: EmptyView,
pub shows: Rc<ShowStack>,
episodes: Rc<EpisodeStack>,
}
impl Content {
pub fn new() -> Content {
pub fn new(header: Rc<Header>) -> Rc<Content> {
let stack = gtk::Stack::new();
let shows = ShowStack::new(header);
let episodes = EpisodeStack::new();
let widget = PodcastWidget::new();
let podcasts = PopulatedView::new();
let empty = EmptyView::new();
stack.add_titled(&episodes.stack, "episodes", "Episodes");
stack.add_titled(&shows.stack, "shows", "Shows");
stack.add_titled(&widget.container, "widget", "Episodes");
stack.add_titled(&podcasts.container, "podcasts", "Shows");
stack.add_named(&empty.container, "empty");
Content {
Rc::new(Content {
stack,
widget,
empty,
podcasts,
}
}
pub fn new_initialized() -> Content {
let ct = Content::new();
ct.init();
ct
}
pub fn init(&self) {
self.podcasts.init(&self.stack);
if self.podcasts.flowbox.get_children().is_empty() {
self.stack.set_visible_child_name("empty");
return;
}
self.stack.set_visible_child_name("podcasts");
}
fn replace_widget(&mut self, pdw: PodcastWidget) {
let vis = self.stack.get_visible_child_name().unwrap();
let old = self.stack.get_child_by_name("widget").unwrap();
self.stack.remove(&old);
self.widget = pdw;
self.stack
.add_titled(&self.widget.container, "widget", "Episodes");
self.stack.set_visible_child_name(&vis);
old.destroy();
}
fn replace_podcasts(&mut self, pop: PopulatedView) {
let vis = self.stack.get_visible_child_name().unwrap();
let old = self.stack.get_child_by_name("podcasts").unwrap();
self.stack.remove(&old);
self.podcasts = pop;
self.stack
.add_titled(&self.podcasts.container, "podcasts", "Shows");
self.stack.set_visible_child_name(&vis);
old.destroy();
}
}
#[derive(Debug)]
// Experiementing with Wrapping gtk::Stack into a State machine.
// Gonna revist it when TryInto trais is stabilized.
pub struct ContentState<S> {
content: Content,
state: S,
}
pub trait UpdateView {
fn update(&mut self);
}
pub struct Empty;
#[derive(Debug)]
pub struct PodcastsView {}
#[derive(Debug)]
pub struct WidgetsView {}
impl Into<ContentState<PodcastsView>> for ContentState<Empty> {
fn into(self) -> ContentState<PodcastsView> {
self.content.stack.set_visible_child_name("podcasts");
ContentState {
content: self.content,
state: PodcastsView {},
}
}
}
impl UpdateView for ContentState<Empty> {
fn update(&mut self) {}
}
impl Into<ContentState<Empty>> for ContentState<PodcastsView> {
fn into(self) -> ContentState<Empty> {
self.content.stack.set_visible_child_name("empty");
ContentState {
content: self.content,
state: Empty {},
}
}
}
impl Into<ContentState<WidgetsView>> for ContentState<PodcastsView> {
fn into(self) -> ContentState<WidgetsView> {
self.content.stack.set_visible_child_name("widget");
ContentState {
content: self.content,
state: WidgetsView {},
}
}
}
impl UpdateView for ContentState<PodcastsView> {
fn update(&mut self) {
let pop = PopulatedView::new_initialized(&self.content.stack);
self.content.replace_podcasts(pop)
}
}
impl Into<ContentState<PodcastsView>> for ContentState<WidgetsView> {
fn into(self) -> ContentState<PodcastsView> {
self.content.stack.set_visible_child_name("podcasts");
ContentState {
content: self.content,
state: PodcastsView {},
}
}
}
impl Into<ContentState<Empty>> for ContentState<WidgetsView> {
fn into(self) -> ContentState<Empty> {
self.content.stack.set_visible_child_name("empty");
ContentState {
content: self.content,
state: Empty {},
}
}
}
impl UpdateView for ContentState<WidgetsView> {
fn update(&mut self) {
let old = self.content.stack.get_child_by_name("widget").unwrap();
let id = WidgetExt::get_name(&old).unwrap();
let pd = dbqueries::get_podcast_from_id(id.parse::<i32>().unwrap()).unwrap();
let pdw = PodcastWidget::new_initialized(&self.content.stack, &pd);
self.content.replace_widget(pdw);
}
}
impl ContentState<PodcastsView> {
#[allow(dead_code)]
pub fn new() -> Result<ContentState<PodcastsView>, ContentState<Empty>> {
let content = Content::new();
content.podcasts.init(&content.stack);
if content.podcasts.flowbox.get_children().is_empty() {
content.stack.set_visible_child_name("empty");
return Err(ContentState {
content,
state: Empty {},
});
}
content.stack.set_visible_child_name("podcasts");
Ok(ContentState {
content,
state: PodcastsView {},
shows,
episodes,
})
}
#[allow(dead_code)]
pub fn update(&self) {
self.shows.update();
self.episodes.update();
}
pub fn get_stack(&self) -> gtk::Stack {
self.content.stack.clone()
self.stack.clone()
}
}
fn replace_widget(stack: &gtk::Stack, pdw: &PodcastWidget) {
let old = stack.get_child_by_name("widget").unwrap();
stack.remove(&old);
stack.add_titled(&pdw.container, "widget", "Episode");
old.destroy();
}
fn replace_podcasts(stack: &gtk::Stack, pop: &PopulatedView) {
let old = stack.get_child_by_name("podcasts").unwrap();
stack.remove(&old);
stack.add_titled(&pop.container, "podcasts", "Shows");
old.destroy();
}
#[allow(dead_code)]
pub fn show_widget(stack: &gtk::Stack) {
stack.set_visible_child_name("widget")
}
pub fn show_podcasts(stack: &gtk::Stack) {
stack.set_visible_child_name("podcasts")
}
pub fn show_empty(stack: &gtk::Stack) {
stack.set_visible_child_name("empty")
}
pub fn update_podcasts(stack: &gtk::Stack) {
let pods = PopulatedView::new_initialized(stack);
if pods.flowbox.get_children().is_empty() {
show_empty(stack)
}
replace_podcasts(stack, &pods);
}
pub fn update_widget(stack: &gtk::Stack, pd: &Podcast) {
let pdw = PodcastWidget::new_initialized(stack, pd);
replace_widget(stack, &pdw);
}
pub fn update_podcasts_preserve_vis(stack: &gtk::Stack) {
let vis = stack.get_visible_child_name().unwrap();
update_podcasts(stack);
if vis != "empty" {
stack.set_visible_child_name(&vis)
}
}
pub fn update_widget_preserve_vis(stack: &gtk::Stack, pd: &Podcast) {
let vis = stack.get_visible_child_name().unwrap();
update_widget(stack, pd);
stack.set_visible_child_name(&vis)
}
pub fn on_podcasts_child_activate(stack: &gtk::Stack, pd: &Podcast) {
update_widget(stack, pd);
stack.set_visible_child_full("widget", gtk::StackTransitionType::SlideLeft);
}
// FIXME: Rename and remove aliases
type ShowsPopulated = PopulatedView;
type ShowsEmpty = EmptyView;
type EpisodesPopulated = PodcastWidget;
type EpisodesEmpty = EmptyView;
struct Populated;
// struct Empty;
// struct Shows;
// struct Episodes;
// Thats probably too overengineered
// struct StackStateMachine<S, T> {
// shows: ShowsMachine<S>,
// episodes: EpisodesMachine<S>,
// stack: gtk::Stack,
// state: T,
// }
#[derive(Debug, Clone)]
struct ShowsMachine<S> {
populated: ShowsPopulated,
empty: ShowsEmpty,
stack: gtk::Stack,
state: S,
pub struct ShowStack {
pub stack: gtk::Stack,
header: Rc<Header>,
}
#[derive(Debug)]
struct EpisodesMachine<S> {
populated: EpisodesPopulated,
empty: EpisodesEmpty,
stack: gtk::Stack,
state: S,
impl ShowStack {
fn new(header: Rc<Header>) -> Rc<ShowStack> {
let stack = gtk::Stack::new();
let show = Rc::new(ShowStack {
stack,
header: header.clone(),
});
let pop = ShowsPopulated::new_initialized(show.clone(), header);
let widget = ShowWidget::new();
let empty = EmptyView::new();
show.stack.add_named(&pop.container, "podcasts");
show.stack.add_named(&widget.container, "widget");
show.stack.add_named(&empty.container, "empty");
if pop.is_empty() {
show.stack.set_visible_child_name("empty")
} else {
show.stack.set_visible_child_name("podcasts")
}
// impl Into<StackStateMachine<Populated, Shows>> for StackStateMachine<Populated, Episodes> {
// fn into(self) -> StackStateMachine<Populated, Shows> {
// self.stack.set_visible_child_name("shows");
// StackStateMachine {
// shows: self.shows,
// episodes: self.episodes,
// stack: self.stack,
// state: Shows {},
// }
// }
// }
// impl Into<StackStateMachine<Populated, Episodes>> for StackStateMachine<Populated, Shows> {
// fn into(self) -> StackStateMachine<Populated, Episodes> {
// self.stack.set_visible_child_name("episodes");
// StackStateMachine {
// shows: self.shows,
// episodes: self.episodes,
// stack: self.stack,
// state: Episodes {},
// }
// }
// }
impl Into<ShowsMachine<Populated>> for ShowsMachine<Empty> {
fn into(self) -> ShowsMachine<Populated> {
self.stack.set_visible_child_name("populated");
ShowsMachine {
populated: self.populated,
empty: self.empty,
stack: self.stack,
state: Populated {},
}
}
show
}
impl Into<ShowsMachine<Empty>> for ShowsMachine<Populated> {
fn into(self) -> ShowsMachine<Empty> {
// fn is_empty(&self) -> bool {
// self.podcasts.is_empty()
// }
pub fn update(&self) {
self.update_podcasts();
self.update_widget();
}
pub fn update_podcasts(&self) {
let vis = self.stack.get_visible_child_name().unwrap();
let old = self.stack.get_child_by_name("podcasts").unwrap();
let pop = ShowsPopulated::new();
pop.init(Rc::new(self.clone()), self.header.clone());
self.stack.remove(&old);
self.stack.add_named(&pop.container, "podcasts");
if pop.is_empty() {
self.stack.set_visible_child_name("empty");
ShowsMachine {
populated: self.populated,
empty: self.empty,
stack: self.stack,
state: Empty {},
} else if vis != "empty" {
self.stack.set_visible_child_name(&vis);
} else {
self.stack.set_visible_child_name("podcasts");
}
old.destroy();
}
pub fn replace_widget(&self, pd: &Podcast) {
let old = self.stack.get_child_by_name("widget").unwrap();
let new = ShowWidget::new_initialized(Rc::new(self.clone()), self.header.clone(), pd);
self.stack.remove(&old);
self.stack.add_named(&new.container, "widget");
}
pub fn update_widget(&self) {
let vis = self.stack.get_visible_child_name().unwrap();
let old = self.stack.get_child_by_name("widget").unwrap();
let id = WidgetExt::get_name(&old).unwrap();
if id == "GtkBox" {
return;
}
let pd = dbqueries::get_podcast_from_id(id.parse::<i32>().unwrap());
if let Ok(pd) = pd {
self.replace_widget(&pd);
self.stack.set_visible_child_name(&vis);
old.destroy();
}
}
impl Into<EpisodesMachine<Populated>> for EpisodesMachine<Empty> {
fn into(self) -> EpisodesMachine<Populated> {
self.stack.set_visible_child_name("populated");
EpisodesMachine {
populated: self.populated,
empty: self.empty,
stack: self.stack,
state: Populated {},
pub fn switch_podcasts_animated(&self) {
self.stack
.set_visible_child_full("podcasts", gtk::StackTransitionType::SlideRight);
}
pub fn switch_widget_animated(&self) {
self.stack
.set_visible_child_full("widget", gtk::StackTransitionType::SlideLeft)
}
}
impl Into<EpisodesMachine<Empty>> for EpisodesMachine<Populated> {
fn into(self) -> EpisodesMachine<Empty> {
self.stack.set_visible_child_name("empty");
#[derive(Debug, Clone)]
struct RecentEpisodes;
EpisodesMachine {
populated: self.populated,
empty: self.empty,
stack: self.stack,
state: Empty {},
}
}
#[derive(Debug, Clone)]
struct EpisodeStack {
// populated: RecentEpisodes,
// empty: EmptyView,
stack: gtk::Stack,
}
// enum StackStateWrapper<S> {
// Shows(StackStateMachine<S, Shows>),
// Episodes(StackStateMachine<S, Episodes>),
// }
impl EpisodeStack {
fn new() -> Rc<EpisodeStack> {
let _pop = RecentEpisodes {};
let empty = EmptyView::new();
let stack = gtk::Stack::new();
enum ShowStateWrapper {
Populated(EpisodesMachine<Populated>),
Empty(EpisodesMachine<Empty>),
// stack.add_named(&pop.container, "populated");
stack.add_named(&empty.container, "empty");
// FIXME:
stack.set_visible_child_name("empty");
Rc::new(EpisodeStack {
// empty,
// populated: pop,
stack,
})
}
enum EpisodeStateWrapper {
Populated(EpisodesMachine<Populated>),
Empty(EpisodesMachine<Empty>),
}
// impl <S>StackStateWrapper<S> {
// fn switch(mut self) -> Self {
// match self {
// StackStateWrapper::Shows(val) => StackStateWrapper::Episodes(val.into()),
// StackStateWrapper::Episodes(val) => StackStateWrapper::Shows(val.into())
// }
// }
// }
impl ShowStateWrapper {
fn switch(self) -> Self {
match self {
ShowStateWrapper::Populated(val) => ShowStateWrapper::Empty(val.into()),
ShowStateWrapper::Empty(val) => ShowStateWrapper::Populated(val.into()),
}
}
}
impl EpisodeStateWrapper {
fn switch(self) -> Self {
match self {
EpisodeStateWrapper::Populated(val) => EpisodeStateWrapper::Empty(val.into()),
EpisodeStateWrapper::Empty(val) => EpisodeStateWrapper::Populated(val.into()),
}
fn update(&self) {
// unimplemented!()
}
}

View File

@ -4,8 +4,10 @@ use gtk::prelude::*;
use hammond_data::Source;
use hammond_data::utils::url_cleaner;
use std::rc::Rc;
use utils;
// use content;
use content::Content;
#[derive(Debug)]
pub struct Header {
@ -13,47 +15,69 @@ pub struct Header {
refresh: gtk::Button,
add_toggle: gtk::MenuButton,
switch: gtk::StackSwitcher,
stack: gtk::Stack,
back_button: gtk::Button,
show_title: gtk::Label,
}
impl Header {
pub fn new() -> Header {
pub fn new() -> Rc<Header> {
let builder = gtk::Builder::new_from_resource("/org/gnome/hammond/gtk/headerbar.ui");
let header: gtk::HeaderBar = builder.get_object("headerbar1").unwrap();
let refresh: gtk::Button = builder.get_object("refbutton").unwrap();
let add_toggle: gtk::MenuButton = builder.get_object("add-toggle-button").unwrap();
let header: gtk::HeaderBar = builder.get_object("headerbar").unwrap();
let refresh: gtk::Button = builder.get_object("ref_button").unwrap();
let add_toggle: gtk::MenuButton = builder.get_object("add_toggle_button").unwrap();
let switch: gtk::StackSwitcher = builder.get_object("switch").unwrap();
let stack: gtk::Stack = builder.get_object("headerbar_stack").unwrap();
let normal_view: gtk::Box = builder.get_object("normal_view").unwrap();
let back_view: gtk::Box = builder.get_object("back_view").unwrap();
let back_button: gtk::Button = builder.get_object("back_button").unwrap();
let show_title: gtk::Label = builder.get_object("show_title").unwrap();
let stack = stack.clone();
back_button.connect_clicked(clone!(stack => move |_| {
stack.set_visible_child_name("normal_view");
}));
switch.set_halign(gtk::Align::Center);
switch.show();
Header {
stack.add_named(&normal_view, "normal_view");
stack.add_named(&back_view, "back_view");
stack.set_transition_type(gtk::StackTransitionType::Crossfade);
stack.set_visible_child_name("normal_view");
Rc::new(Header {
container: header,
refresh,
add_toggle,
switch,
}
stack,
back_button,
show_title,
})
}
pub fn new_initialized(stack: &gtk::Stack) -> Header {
let header = Header::new();
header.init(stack);
header
}
// pub fn new_initialized(content: Rc<Content>) -> Rc<Header> {
// let header = Header::new();
// header.init(content);
// header
// }
fn init(&self, stack: &gtk::Stack) {
pub fn init(&self, content: Rc<Content>) {
let builder = gtk::Builder::new_from_resource("/org/gnome/hammond/gtk/headerbar.ui");
let add_popover: gtk::Popover = builder.get_object("add-popover").unwrap();
let new_url: gtk::Entry = builder.get_object("new-url").unwrap();
let add_button: gtk::Button = builder.get_object("add-button").unwrap();
self.switch.set_stack(stack);
self.switch.set_stack(&content.stack);
new_url.connect_changed(move |url| {
println!("{:?}", url.get_text());
});
add_button.connect_clicked(clone!(stack, add_popover, new_url => move |_| {
on_add_bttn_clicked(&stack, &new_url);
add_button.connect_clicked(clone!(content, add_popover, new_url => move |_| {
on_add_bttn_clicked(content.clone(), &new_url);
// TODO: lock the button instead of hiding and add notification of feed added.
// TODO: map the spinner
@ -62,13 +86,32 @@ impl Header {
self.add_toggle.set_popover(&add_popover);
// FIXME: There appears to be a memmory leak here.
self.refresh.connect_clicked(clone!(stack => move |_| {
utils::refresh_feed(&stack, None, None);
self.refresh.connect_clicked(clone!(content => move |_| {
utils::refresh_feed(content.clone(), None, None);
}));
let stack = self.stack.clone();
self.back_button
.connect_clicked(clone!(content => move |_| {
content.shows.stack.set_visible_child_full("podcasts", gtk::StackTransitionType::SlideLeft);
stack.set_visible_child_name("normal_view")
}));
}
pub fn switch_to_normal(&self) {
self.stack.set_visible_child_name("normal_view")
}
pub fn switch_to_back(&self) {
self.stack.set_visible_child_name("back_view")
}
pub fn set_show_title(&self, title: &str) {
self.show_title.set_text(title)
}
}
fn on_add_bttn_clicked(stack: &gtk::Stack, entry: &gtk::Entry) {
fn on_add_bttn_clicked(content: Rc<Content>, entry: &gtk::Entry) {
let url = entry.get_text().unwrap_or_default();
let url = url_cleaner(&url);
let source = Source::from_url(&url);
@ -76,7 +119,7 @@ fn on_add_bttn_clicked(stack: &gtk::Stack, entry: &gtk::Entry) {
if let Ok(s) = source {
info!("{:?} feed added", url);
// update the db
utils::refresh_feed(stack, Some(vec![s]), None);
utils::refresh_feed(content, Some(vec![s]), None);
} else {
error!("Feed probably already exists.");
error!("Error: {:?}", source.unwrap_err());

View File

@ -1,3 +1,5 @@
#![cfg_attr(feature = "cargo-clippy", allow(clone_on_ref_ptr))]
extern crate gdk;
extern crate gdk_pixbuf;
extern crate gio;
@ -67,9 +69,14 @@ fn build_ui(app: &gtk::Application) {
// let ct = content::ContentState::new().unwrap();
// let stack = ct.get_stack();
let ct = content::Content::new_initialized();
let stack = ct.stack;
window.add(&stack);
// let ct = content::Content::new_initialized();
// Get the headerbar
let header = headerbar::Header::new();
let ct = content::Content::new(header.clone());
header.init(ct.clone());
window.set_titlebar(&header.container);
window.add(&ct.get_stack());
window.connect_delete_event(|w, _| {
w.destroy();
@ -92,8 +99,8 @@ fn build_ui(app: &gtk::Application) {
app.add_action(&check);
// queue a db update 1 minute after the startup.
gtk::idle_add(clone!(stack => move || {
utils::refresh_feed(&stack, None, Some(60));
gtk::idle_add(clone!(ct => move || {
utils::refresh_feed(ct.clone(), None, Some(60));
glib::Continue(false)
}));
@ -102,10 +109,6 @@ fn build_ui(app: &gtk::Application) {
glib::Continue(false)
});
// Get the headerbar
let header = headerbar::Header::new_initialized(&stack);
window.set_titlebar(&header.container);
window.show_all();
window.activate();
app.connect_activate(move |_| ());

View File

@ -1,5 +1,4 @@
use glib;
use gtk;
use gdk_pixbuf::Pixbuf;
use hammond_data::feed;
@ -9,12 +8,12 @@ use hammond_downloader::downloader;
use std::{thread, time};
use std::cell::RefCell;
use std::sync::mpsc::{channel, Receiver};
use std::borrow::Cow;
use content;
use regex::Regex;
use content::Content;
type Foo = RefCell<Option<(gtk::Stack, Receiver<bool>)>>;
use std::rc::Rc;
type Foo = RefCell<Option<(Rc<Content>, Receiver<bool>)>>;
// Create a thread local storage that will store the arguments to be transfered.
thread_local!(static GLOBAL: Foo = RefCell::new(None));
@ -23,13 +22,13 @@ thread_local!(static GLOBAL: Foo = RefCell::new(None));
/// If `source` is None, Fetches all the `Source` entries in the database and updates them.
/// `delay` represents the desired time in seconds for the thread to sleep before executing.
/// When It's done,it queues up a `podcast_view` refresh.
pub fn refresh_feed(stack: &gtk::Stack, source: Option<Vec<Source>>, delay: Option<u64>) {
pub fn refresh_feed(content: Rc<Content>, source: Option<Vec<Source>>, delay: Option<u64>) {
// Create a async channel.
let (sender, receiver) = channel();
// Pass the desired arguments into the Local Thread Storage.
GLOBAL.with(clone!(stack => move |global| {
*global.borrow_mut() = Some((stack, receiver));
GLOBAL.with(clone!(content => move |global| {
*global.borrow_mut() = Some((content.clone(), receiver));
}));
thread::spawn(move || {
@ -57,9 +56,9 @@ pub fn refresh_feed(stack: &gtk::Stack, source: Option<Vec<Source>>, delay: Opti
fn refresh_podcasts_view() -> glib::Continue {
GLOBAL.with(|global| {
if let Some((ref stack, ref reciever)) = *global.borrow() {
if let Some((ref content, ref reciever)) = *global.borrow() {
if reciever.try_recv().is_ok() {
content::update_podcasts_preserve_vis(stack);
content.update();
}
}
});
@ -71,18 +70,6 @@ pub fn get_pixbuf_from_path(pd: &Podcast) -> Option<Pixbuf> {
Pixbuf::new_from_file_at_scale(&img_path, 256, 256, true).ok()
}
#[allow(dead_code)]
// WIP: parse html to markup
pub fn html_to_markup(s: &mut str) -> Cow<str> {
s.trim();
s.replace('&', "&amp;");
s.replace('<', "&lt;");
s.replace('>', "&gt;");
let re = Regex::new("(?P<url>https?://[^\\s&,)(\"]+(&\\w=[\\w._-]?)*(#[\\w._-]+)?)").unwrap();
re.replace_all(s, "<a href=\"$url\">$url</a>")
}
#[cfg(test)]
mod tests {
use hammond_data::Source;

View File

@ -0,0 +1 @@

View File

@ -1,2 +1,3 @@
pub mod podcasts;
pub mod shows;
pub mod episodes;
pub mod empty;

View File

@ -7,18 +7,20 @@ use hammond_data::dbqueries;
use hammond_data::Podcast;
use utils::get_pixbuf_from_path;
use content::ShowStack;
use headerbar::Header;
use content;
use std::rc::Rc;
#[derive(Debug, Clone)]
pub struct PopulatedView {
pub struct ShowsPopulated {
pub container: gtk::Box,
pub flowbox: gtk::FlowBox,
flowbox: gtk::FlowBox,
viewport: gtk::Viewport,
}
#[derive(Debug)]
struct PodcastChild {
struct ShowsChild {
container: gtk::Box,
title: gtk::Label,
cover: gtk::Image,
@ -27,14 +29,14 @@ struct PodcastChild {
child: gtk::FlowBoxChild,
}
impl PopulatedView {
pub fn new() -> PopulatedView {
let builder = gtk::Builder::new_from_resource("/org/gnome/hammond/gtk/podcasts_view.ui");
impl ShowsPopulated {
pub fn new() -> ShowsPopulated {
let builder = gtk::Builder::new_from_resource("/org/gnome/hammond/gtk/shows_view.ui");
let container: gtk::Box = builder.get_object("fb_parent").unwrap();
let flowbox: gtk::FlowBox = builder.get_object("flowbox").unwrap();
let viewport: gtk::Viewport = builder.get_object("viewport").unwrap();
PopulatedView {
ShowsPopulated {
container,
flowbox,
viewport,
@ -42,23 +44,27 @@ impl PopulatedView {
}
#[allow(dead_code)]
pub fn new_initialized(stack: &gtk::Stack) -> PopulatedView {
let pop = PopulatedView::new();
pop.init(stack);
pub fn new_initialized(show: Rc<ShowStack>, header: Rc<Header>) -> ShowsPopulated {
let pop = ShowsPopulated::new();
pop.init(show, header);
pop
}
pub fn init(&self, stack: &gtk::Stack) {
pub fn init(&self, show: Rc<ShowStack>, header: Rc<Header>) {
use gtk::WidgetExt;
// TODO: handle unwraps.
// TODO: implement back button.
self.flowbox
.connect_child_activated(clone!(stack => move |_, child| {
.connect_child_activated(clone!(show => move |_, child| {
// This is such an ugly hack...
// let id = child.get_name().unwrap().parse::<i32>().unwrap();
let id = WidgetExt::get_name(child).unwrap().parse::<i32>().unwrap();
let parent = dbqueries::get_podcast_from_id(id).unwrap();
on_flowbox_child_activate(&stack, &parent);
let pd = dbqueries::get_podcast_from_id(id).unwrap();
show.replace_widget(&pd);
header.set_show_title(pd.title());
header.switch_to_back();
show.switch_widget_animated();
}));
// Populate the flowbox with the Podcasts.
self.populate_flowbox();
@ -69,17 +75,21 @@ impl PopulatedView {
if let Ok(pds) = podcasts {
pds.iter().for_each(|parent| {
let flowbox_child = PodcastChild::new_initialized(parent);
let flowbox_child = ShowsChild::new_initialized(parent);
self.flowbox.add(&flowbox_child.child);
});
self.flowbox.show_all();
}
}
pub fn is_empty(&self) -> bool {
self.flowbox.get_children().is_empty()
}
}
impl PodcastChild {
fn new() -> PodcastChild {
let builder = gtk::Builder::new_from_resource("/org/gnome/hammond/gtk/podcasts_child.ui");
impl ShowsChild {
fn new() -> ShowsChild {
let builder = gtk::Builder::new_from_resource("/org/gnome/hammond/gtk/shows_child.ui");
// Copy of gnome-music AlbumWidget
let container: gtk::Box = builder.get_object("fb_child").unwrap();
@ -91,7 +101,7 @@ impl PodcastChild {
let child = gtk::FlowBoxChild::new();
child.add(&container);
PodcastChild {
ShowsChild {
container,
title,
cover,
@ -113,8 +123,8 @@ impl PodcastChild {
self.configure_banner(pd);
}
pub fn new_initialized(pd: &Podcast) -> PodcastChild {
let child = PodcastChild::new();
pub fn new_initialized(pd: &Podcast) -> ShowsChild {
let child = ShowsChild::new();
child.init(pd);
child
@ -138,7 +148,3 @@ impl PodcastChild {
}
}
}
fn on_flowbox_child_activate(stack: &gtk::Stack, parent: &Podcast) {
content::on_podcasts_child_activate(stack, parent)
}

View File

@ -13,8 +13,6 @@ use hammond_data::utils::*;
use hammond_data::errors::*;
use hammond_data::utils::replace_extra_spaces;
// use utils::html_to_markup;
use std::thread;
use std::cell::RefCell;
use std::sync::mpsc::{channel, Receiver};

View File

@ -1,2 +1,2 @@
pub mod podcast;
pub mod show;
pub mod episode;

View File

@ -10,10 +10,13 @@ use hammond_downloader::downloader;
use widgets::episode::episodes_listbox;
use utils::get_pixbuf_from_path;
use content;
use content::ShowStack;
use headerbar::Header;
#[derive(Debug)]
pub struct PodcastWidget {
use std::rc::Rc;
#[derive(Debug, Clone)]
pub struct ShowWidget {
pub container: gtk::Box,
cover: gtk::Image,
title: gtk::Label,
@ -23,10 +26,10 @@ pub struct PodcastWidget {
played: gtk::Button,
}
impl PodcastWidget {
pub fn new() -> PodcastWidget {
impl ShowWidget {
pub fn new() -> ShowWidget {
// Adapted from gnome-music AlbumWidget
let builder = gtk::Builder::new_from_resource("/org/gnome/hammond/gtk/podcast_widget.ui");
let builder = gtk::Builder::new_from_resource("/org/gnome/hammond/gtk/show_widget.ui");
let container: gtk::Box = builder.get_object("podcast_widget").unwrap();
let cover: gtk::Image = builder.get_object("cover").unwrap();
@ -36,7 +39,7 @@ impl PodcastWidget {
let unsub: gtk::Button = builder.get_object("unsub_button").unwrap();
let played: gtk::Button = builder.get_object("mark_all_played_button").unwrap();
PodcastWidget {
ShowWidget {
container,
cover,
title,
@ -47,18 +50,19 @@ impl PodcastWidget {
}
}
pub fn new_initialized(stack: &gtk::Stack, pd: &Podcast) -> PodcastWidget {
let pdw = PodcastWidget::new();
pdw.init(stack, pd);
pub fn new_initialized(shows: Rc<ShowStack>, header: Rc<Header>, pd: &Podcast) -> ShowWidget {
let pdw = ShowWidget::new();
pdw.init(shows, header, pd);
pdw
}
pub fn init(&self, stack: &gtk::Stack, pd: &Podcast) {
pub fn init(&self, shows: Rc<ShowStack>, header: Rc<Header>, pd: &Podcast) {
WidgetExt::set_name(&self.container, &pd.id().to_string());
// TODO: should spawn a thread to avoid locking the UI probably.
self.unsub.connect_clicked(clone!(stack, pd => move |bttn| {
on_unsub_button_clicked(&stack, &pd, bttn);
self.unsub.connect_clicked(clone!(shows, pd => move |bttn| {
on_unsub_button_clicked(shows.clone(), &pd, bttn);
header.switch_to_normal();
}));
self.title.set_text(pd.title());
@ -77,8 +81,8 @@ impl PodcastWidget {
self.cover.set_from_pixbuf(&i);
}
self.played.connect_clicked(clone!(stack, pd => move |_| {
on_played_button_clicked(&stack, &pd);
self.played.connect_clicked(clone!(shows, pd => move |_| {
on_played_button_clicked(shows.clone(), &pd);
}));
self.show_played_button(pd);
@ -95,7 +99,7 @@ impl PodcastWidget {
}
}
fn on_unsub_button_clicked(stack: &gtk::Stack, pd: &Podcast, unsub_button: &gtk::Button) {
fn on_unsub_button_clicked(shows: Rc<ShowStack>, pd: &Podcast, unsub_button: &gtk::Button) {
let res = dbqueries::remove_feed(pd);
if res.is_ok() {
info!("{} was removed succesfully.", pd.title());
@ -111,12 +115,12 @@ fn on_unsub_button_clicked(stack: &gtk::Stack, pd: &Podcast, unsub_button: &gtk:
}
};
}
content::update_podcasts(stack);
content::show_podcasts(stack);
shows.switch_podcasts_animated();
shows.update_podcasts();
}
fn on_played_button_clicked(stack: &gtk::Stack, pd: &Podcast) {
fn on_played_button_clicked(shows: Rc<ShowStack>, pd: &Podcast) {
let _ = dbqueries::update_none_to_played_now(pd);
content::update_widget_preserve_vis(stack, pd);
shows.update_widget();
}