Compare commits

...

218 Commits
v0.1.0 ... main

Author SHA1 Message Date
M3DZIK b6ef4241ca
Release v0.11.3 2024-04-11 22:43:15 +02:00
M3DZIK 6d6cbbd4b0
Update docs in imgurs lib 2024-04-11 22:40:46 +02:00
M3DZIK 723bf26dbd
Delete total lines, code size and build status badges from readme 2024-04-11 22:39:39 +02:00
M3DZIK 56cbd96053
Fix clippy error 2024-04-11 22:37:59 +02:00
Andre Julius af0864d272 Update dependencies and replace validator crate with url crate 2024-04-11 22:32:30 +02:00
M3DZIK e782617f08
chore(release): v0.11.2 2023-06-22 13:50:08 +02:00
dependabot[bot] 7062046679 chore(deps): bump enumflags2 from 0.7.5 to 0.7.7
Bumps [enumflags2](https://github.com/meithecatte/enumflags2) from 0.7.5 to 0.7.7.
- [Release notes](https://github.com/meithecatte/enumflags2/releases)
- [Commits](https://github.com/meithecatte/enumflags2/compare/v0.7.5...v0.7.7)

---
updated-dependencies:
- dependency-name: enumflags2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-22 13:36:39 +02:00
dependabot[bot] 9ba2233734 chore(deps): bump openssl from 0.10.48 to 0.10.55
Bumps [openssl](https://github.com/sfackler/rust-openssl) from 0.10.48 to 0.10.55.
- [Release notes](https://github.com/sfackler/rust-openssl/releases)
- [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-v0.10.48...openssl-v0.10.55)

---
updated-dependencies:
- dependency-name: openssl
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-22 13:36:30 +02:00
dependabot[bot] 044fa6525d chore(deps): bump h2 from 0.3.14 to 0.3.17
Bumps [h2](https://github.com/hyperium/h2) from 0.3.14 to 0.3.17.
- [Release notes](https://github.com/hyperium/h2/releases)
- [Changelog](https://github.com/hyperium/h2/blob/master/CHANGELOG.md)
- [Commits](https://github.com/hyperium/h2/compare/v0.3.14...v0.3.17)

---
updated-dependencies:
- dependency-name: h2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-15 13:26:15 +02:00
dependabot[bot] f6d8e906fb chore(deps): bump bumpalo from 3.11.0 to 3.12.0
Bumps [bumpalo](https://github.com/fitzgen/bumpalo) from 3.11.0 to 3.12.0.
- [Release notes](https://github.com/fitzgen/bumpalo/releases)
- [Changelog](https://github.com/fitzgen/bumpalo/blob/main/CHANGELOG.md)
- [Commits](https://github.com/fitzgen/bumpalo/compare/3.11.0...3.12.0)

---
updated-dependencies:
- dependency-name: bumpalo
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-03 20:31:43 +02:00
dependabot[bot] 47b6225280 chore(deps): bump openssl from 0.10.42 to 0.10.48
Bumps [openssl](https://github.com/sfackler/rust-openssl) from 0.10.42 to 0.10.48.
- [Release notes](https://github.com/sfackler/rust-openssl/releases)
- [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-v0.10.42...openssl-v0.10.48)

---
updated-dependencies:
- dependency-name: openssl
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-03 20:31:05 +02:00
Andre Julius e9a7108f90 Fix deprecation warning of base64 crate.
See: https://github.com/M3DZIK/imgurs/actions/runs/3871046426/jobs/6598478600
2023-04-03 20:29:17 +02:00
renovate[bot] 0398f7f675 fix(deps): update all non-major dependencies 2023-01-30 04:52:40 +00:00
renovate[bot] 300fa61a65 fix(deps): update all non-major dependencies to 4.1 2023-01-16 04:10:52 +00:00
renovate[bot] 0ad55015ac fix(deps): update rust crate tokio to 1.24 [security] 2023-01-09 12:54:49 +00:00
renovate[bot] 965c4407f1 fix(deps): update all non-major dependencies 2023-01-09 05:30:05 +00:00
renovate[bot] 80292730f3 fix(deps): update all non-major dependencies 2022-12-12 04:55:31 +00:00
MedzikUser 304034f037
chore(release): v0.11.1 2022-12-11 11:05:51 +01:00
Andre Julius b6e715accf album_title can be null. account_id can be a string or a number 2022-12-11 09:40:18 +01:00
renovate[bot] d29b299a4b fix(deps): update rust crate notify-rust to 4.6 2022-12-05 04:22:49 +00:00
renovate[bot] c08e640421 fix(deps): update rust crate tokio to 1.22 2022-11-21 04:10:16 +00:00
MedzikUser 60119be30b
chore: update 2022-11-07 17:17:09 +01:00
MedzikUser 986a3b365c
chore(release): v0.11.0 2022-11-07 17:13:50 +01:00
MedzikUser 25cc96774c
fix: rename function to `with_http_client` 2022-11-07 17:05:01 +01:00
renovate[bot] 111fef82d1 fix(deps): update rust crate arboard to v3 2022-11-07 08:33:47 +01:00
renovate[bot] 5d5beaecd8 fix(deps): update rust crate simple_logger to v4 2022-11-07 08:33:32 +01:00
Andre Julius 89a7969353 Add `with_http_client` method to ImgurClient
This will allow customization of the http client or just providing an already existing http client.
In my use case I already have a client I'd like to reuse.
2022-11-07 08:30:13 +01:00
MedzikUser 25190ba5c1
chore(deps): update cargo lock 2022-10-01 18:47:02 +02:00
MedzikUser 012784a352
chore(cli): release v0.10.0 2022-10-01 18:44:54 +02:00
MedzikUser 1bd632e906
chore(release): v0.10.0
Add configuration for tls using features (rustls-tls or native-tls).
2022-10-01 18:43:08 +02:00
MedzikUser a66fea840a
chore(release): v0.9.1
Delete debug info from cli in release build.
2022-09-22 18:59:49 +02:00
dependabot[bot] dd44c5f041 chore(deps): bump iana-time-zone from 0.1.44 to 0.1.46
Bumps [iana-time-zone](https://github.com/strawlab/iana-time-zone) from 0.1.44 to 0.1.46.
- [Release notes](https://github.com/strawlab/iana-time-zone/releases)
- [Changelog](https://github.com/strawlab/iana-time-zone/blob/main/CHANGELOG.md)
- [Commits](https://github.com/strawlab/iana-time-zone/compare/0.1.44...v0.1.46)

---
updated-dependencies:
- dependency-name: iana-time-zone
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-18 13:55:38 +02:00
MedzikUser 30a79bc8bb
ci: delete publish to crates.io 2022-09-05 21:21:25 +02:00
MedzikUser 2ac3424338
fix(test): disable run get album info test 2022-09-05 21:11:59 +02:00
MedzikUser ecb5855e75
feat: add get_album function (#76)
Co-authored-by: Andre Julius <noromoron@gmail.com>
2022-09-05 21:07:21 +02:00
MedzikUser 31c737689c
fix: clippy warnings 2022-09-05 20:54:59 +02:00
MedzikUser 8ddfeb3c19
fix: delete broken 0x0 2022-09-05 20:51:50 +02:00
renovate[bot] f5c70e4f4f fix(deps): update all non-major dependencies 2022-09-05 03:52:51 +00:00
MedzikUser c6b6bd1af7
fix(0x0): fix builder error 2022-08-24 17:02:13 +02:00
MedzikUser 312d9cce19
ci: fix cli build 2022-08-24 15:44:10 +02:00
MedzikUser ad117bf3ec
feat: move cli to other crate and add The Null Pointer (0x0.st) 2022-08-24 15:35:51 +02:00
renovate[bot] be73023511 fix(deps): update all non-major dependencies 2022-08-22 04:31:52 +00:00
renovate[bot] 0a8d4afb42 fix(deps): update all non-major dependencies 2022-08-15 04:36:08 +00:00
renovate[bot] bd91fb330c fix(deps): update all non-major dependencies 2022-08-08 04:01:40 +00:00
renovate[bot] 066cda0539 fix(deps): update all non-major dependencies 2022-08-01 03:20:31 +00:00
renovate[bot] b0c4dab74d fix(deps): update all non-major dependencies 2022-07-25 03:31:40 +00:00
renovate[bot] ac1cfc9535 fix(deps): update all non-major dependencies 2022-07-18 03:05:01 +00:00
MedzikUser f26170bc12
update 2022-07-08 11:54:48 +02:00
MedzikUser 71d124f435
chore(webhook): delete discord webhook 2022-07-08 11:38:16 +02:00
renovate[bot] c75cad024f fix(deps): update rust crate simple_logger to 2.2.0 2022-07-04 14:28:02 +00:00
renovate[bot] 0eea52da89 fix(deps): update rust crate serde to 1.0.138 2022-07-02 06:10:51 +00:00
renovate[bot] 54eea0f968 fix(deps): update rust crate clap to 3.2.8 2022-06-30 16:40:41 +00:00
renovate[bot] 69211e9720 fix(deps): update rust crate serde_json to 1.0.82 2022-06-30 00:37:35 +00:00
renovate[bot] 1d6aa51df6 fix(deps): update rust crate clap_mangen to 0.1.10 2022-06-29 01:03:14 +00:00
renovate[bot] cfa28a2660 fix(deps): update rust crate clap_complete to 3.2.3 2022-06-28 21:31:49 +00:00
renovate[bot] ba494c594a fix(deps): update rust crate clap to 3.2.7 2022-06-28 18:17:12 +00:00
MedzikUser 7081367bae
update 2022-06-22 20:34:16 +02:00
renovate[bot] cc2dde74cd fix(deps): update rust crate clap_mangen to 0.1.9 2022-06-22 01:23:06 +00:00
renovate[bot] 78dcb79b97 fix(deps): update rust crate clap_complete to 3.2.2 2022-06-21 22:53:01 +00:00
renovate[bot] 78c1c27332 fix(deps): update rust crate clap to 3.2.6 2022-06-21 19:59:52 +00:00
renovate[bot] 13d9c82fcd fix(deps): update rust crate anyhow to 1.0.58 2022-06-19 02:38:16 +00:00
MedzikUser 3c5507adaa
chore(release): v0.8.1 and use `serde` instead of `serde_derive` 2022-06-17 23:07:12 +02:00
MedzikUser 0bc00599e9
chore(deps): upgrade 2022-06-17 23:01:19 +02:00
MedzikUser 2d1f1a4001
add missing doc 2022-06-17 23:00:45 +02:00
Renovate Bot 66551e77ad fix(deps): update rust crate clap to 3.2.5 2022-06-15 21:56:16 +00:00
Renovate Bot d0e5367414 fix(deps): update rust crate clap to 3.2.4 2022-06-14 23:52:40 +00:00
Renovate Bot 7446b0b420 fix(deps): update rust crate clap_complete to 3.2.1 2022-06-14 06:28:52 +00:00
Renovate Bot a1ca4dad9c fix(deps): update rust crate reqwest to 0.11.11 2022-06-14 04:06:40 +00:00
Renovate Bot 7567d26b5b fix(deps): update rust crate clap to 3.2.1 2022-06-14 00:36:29 +00:00
Renovate Bot feea8f8f2e fix(deps): update rust crate clap_mangen to 0.1.8 2022-06-13 20:25:10 +00:00
MedzikUser 3f2af4f6f9
update 2022-06-12 18:22:45 +02:00
MedzikUser b396ccce19
chore: format code 2022-06-12 17:41:50 +02:00
MedzikUser 80dcd27bc1
chore(release): v0.8.0 2022-06-12 17:38:52 +02:00
MedzikUser 8bbee811d2
doc: remove github badge from readme 2022-06-12 17:29:16 +02:00
MedzikUser 831f816447
refactor code 2022-06-12 17:28:25 +02:00
Renovate Bot 6da2dc252f fix(deps): update rust crate tokio to 1.19.2 2022-06-06 22:25:54 +00:00
Renovate Bot f935a3dddc fix(deps): update rust crate tokio to 1.19.1 2022-06-05 13:49:29 +00:00
Renovate Bot 15aa471d4f fix(deps): update rust crate tokio to 1.19.0 2022-06-03 23:48:55 +00:00
MedzikUser 9538ca5be8
fix: typo 2022-05-29 11:13:56 +02:00
Renovate Bot 5d3f5ed27b fix(deps): update rust crate arboard to 2.1.1 2022-05-19 03:13:44 +00:00
MedzikUser aac9819bc5
fix: fix clipboard on windows and macos 2022-05-18 21:25:35 +02:00
MedzikUser fb72dc1112
chore(release): v0.7.3 2022-05-18 20:58:00 +02:00
MedzikUser f4e0044678
chore: change `String` to `&str` 2022-05-18 20:56:15 +02:00
MedzikUser 6f9559c6d8
chore: remove unnecessary map_err 2022-05-18 20:26:29 +02:00
MedzikUser 9b7705ed26
chore: change Result to anyhow::Result 2022-05-18 20:16:42 +02:00
MedzikUser 58acb3df50
docs(readme): add badges 2022-05-18 20:16:13 +02:00
MedzikUser 70ffc05fee
ci: add `--no-deps` to cargo clippy 2022-05-18 20:00:49 +02:00
MedzikUser cca523f2b5
ci: fix 2022-05-18 19:55:10 +02:00
MedzikUser 18dfdb88a4
chore(deps): upgrade 2022-05-18 19:49:43 +02:00
MedzikUser aabbea4182
chore: small changes 2022-05-18 19:48:39 +02:00
Renovate Bot 20f0039d08 fix(deps): update rust crate clap to 3.1.18 2022-05-11 00:40:18 +00:00
Renovate Bot 4e985ac39b fix(deps): update rust crate tokio to 1.18.2 2022-05-08 23:02:03 +00:00
Renovate Bot 8eeaa4dde6 fix(deps): update rust crate clap to 3.1.17 2022-05-06 19:43:46 +00:00
Renovate Bot b7be76debf fix(deps): update rust crate clap_complete to 3.1.4 2022-05-06 17:55:40 +00:00
Renovate Bot 771d4e8dcc fix(deps): update rust crate clap to 3.1.16 2022-05-06 04:48:12 +00:00
Renovate Bot 415e6c5735 fix(deps): update rust crate serde_json to 1.0.81 2022-05-04 00:30:54 +00:00
Renovate Bot d73dc16909 fix(deps): update rust crate validator to 0.15.0 2022-05-03 13:51:28 +00:00
Renovate Bot e5f91d7261 fix(deps): update rust crate log to 0.4.17 2022-05-03 05:11:13 +00:00
Renovate Bot 424175ed21 fix(deps): update rust crate tokio to 1.18.1 2022-05-03 02:21:19 +00:00
Renovate Bot 848767763e fix(deps): update rust crate clap to 3.1.15 2022-05-02 22:22:16 +00:00
Renovate Bot be4c05a4da fix(deps): update rust crate serde_derive to 1.0.137 2022-05-01 13:35:19 +00:00
Renovate Bot 4fde33bad8 fix(deps): update rust crate serde to 1.0.137 2022-05-01 11:54:59 +00:00
Renovate Bot c2adcc1f32 fix(deps): update rust crate clap to 3.1.14 2022-05-01 04:44:30 +00:00
Renovate Bot 24d91f6151 fix(deps): update rust crate serde_json to 1.0.80 2022-05-01 01:09:26 +00:00
Renovate Bot 1916df1bc0 fix(deps): update rust crate clap_complete to 3.1.3 2022-04-30 19:16:59 +00:00
Renovate Bot 931929dacb fix(deps): update rust crate clap to 3.1.13 2022-04-30 17:01:29 +00:00
Renovate Bot d46a52d906 fix(deps): update rust crate tokio to 1.18.0 2022-04-27 20:18:07 +00:00
Renovate Bot 6c13b844ca fix(deps): update rust crate clap to 3.1.12 2022-04-22 16:58:46 +00:00
Renovate Bot ccc120433a fix(deps): update rust crate clap to 3.1.11 2022-04-22 06:42:39 +00:00
Renovate Bot 56cc3e30a1 fix(deps): update rust crate anyhow to 1.0.57 2022-04-21 21:51:43 +00:00
Renovate Bot b024a5056e fix(deps): update rust crate clap_mangen to 0.1.6 2022-04-20 23:01:43 +00:00
Renovate Bot fc154fe83d fix(deps): update rust crate clap_mangen to 0.1.5 2022-04-20 08:42:36 +02:00
Renovate Bot 250a216b94 fix(deps): update rust crate clap_complete to 3.1.2 2022-04-20 02:59:39 +00:00
Renovate Bot 7966fb5553 fix(deps): update rust crate clap to 3.1.10 2022-04-19 22:24:23 +00:00
Renovate Bot 939d267c28 fix(deps): update rust crate clap_mangen to 0.1.3 2022-04-16 01:51:43 +00:00
Renovate Bot b9186a31c5 fix(deps): update rust crate clap to 3.1.9 2022-04-15 23:38:59 +00:00
Renovate Bot 4b224d1331 fix(deps): update rust crate toml to 0.5.9 2022-04-15 02:40:27 +00:00
MedzikUser 8b1f394575
v0.7.2 2022-04-05 17:06:17 +02:00
MedzikUser 1048ea298f
hotfix: upload image from file (revert encode bytes to base64) 2022-04-05 17:04:13 +02:00
MedzikUser 8cd1a31b72
docs: add `;` at the end of the line in the rust example code 2022-04-04 19:08:33 +02:00
MedzikUser c3718579db
v0.7.1 2022-04-04 19:04:49 +02:00
MedzikUser b0e871cbf6
chore (release): v0.7.0 2022-04-03 21:43:50 +02:00
MedzikUser d98208f677
documentation: add in lib.rs 2022-04-03 21:32:42 +02:00
MedzikUser 08c25212ae
fix clippy 2022-04-03 21:11:11 +02:00
MedzikUser b1bfe52a4c
check CHANGELOG.md for a list of variables 2022-04-03 21:01:58 +02:00
Renovate Bot b83847ff20 fix(deps): update rust crate clap to 3.1.8 2022-04-01 19:25:06 +00:00
Renovate Bot 91746ad99c fix(deps): update rust crate clap to 3.1.7 2022-03-31 20:21:14 +00:00
Renovate Bot b4cc9d5940 fix(deps): update rust crate notify-rust to 4.5.8 2022-03-26 03:55:21 +00:00
Renovate Bot a5f25caac2 fix(deps): update rust crate log to 0.4.16 2022-03-23 12:51:39 +00:00
Renovate Bot 89da666def fix(deps): update rust crate log to 0.4.15 2022-03-22 13:27:59 +00:00
Renovate Bot dbeefd9430 fix(deps): update rust crate notify-rust to 4.5.7 2022-03-20 14:41:42 +00:00
Renovate Bot 5477bd2ef6 fix(deps): update rust crate reqwest to 0.11.10 2022-03-15 09:21:09 +01:00
MedzikUser 506aad97dc
v0.6.0 2022-03-14 19:31:02 +01:00
MedzikUser aa59201da7
ci: remove audit 2022-03-11 20:40:38 +01:00
Renovate Bot 03f57025e8 fix(deps): update rust crate arboard to 2.1.0 2022-03-11 18:04:44 +00:00
MedzikUser 09e7418658
ci: add workflow_dispatch for all workflows 2022-03-10 22:14:33 +01:00
MedzikUser 2451f47fa8
ci: add scheduled audit 2022-03-10 22:13:16 +01:00
MedzikUser 9f22754fc3
rustfmt 2022-03-10 22:08:01 +01:00
MedzikUser 60b0dc0d84
read commit description
- webhook: added url in title
- cli: change image domain to your own (set in config)
- if the configuration file cannot be open, ask the user whether to overwrite the file instead of overwriting it without asking
- logger: max_level_debug in debug binary
2022-03-10 22:02:03 +01:00
MedzikUser 9b813457e7
downgrade changelog-reader-action 2022-03-10 21:01:06 +01:00
MedzikUser 0cf1ad449e
update changelog 2022-03-09 18:40:31 +01:00
MedzikUser dba1dbae01
v0.5.1 2022-03-08 22:18:55 +01:00
Renovate Bot f4f92b2713 fix(deps): update rust crate anyhow to 1.0.56 2022-03-08 01:35:09 +00:00
MedzikUser 52cceb1ec1
rustfmt 2022-03-07 20:21:46 +01:00
MedzikUser 8a25a33110
update 2022-03-07 20:19:13 +01:00
MedzikUser a1c29a08c1
change webhook to discord-webhook (to use rustls) 2022-03-07 20:10:58 +01:00
MedzikUser 6f29b8682e
v0.5.0 2022-03-07 19:12:44 +01:00
MedzikUser bf796f3358
fix(deps): update rust crate clap to 3.1.6 2022-03-07 18:56:38 +01:00
MedzikUser b9c0daab88
update 2022-03-04 19:51:19 +01:00
MedzikUser ef1f9342ee
rustfmt 2022-03-04 19:46:58 +01:00
MedzikUser 06a908ff36
update cross config 2022-03-04 19:37:30 +01:00
MedzikUser 160c3b0b2f
add cross config 2022-03-04 19:27:41 +01:00
renovate[bot] 94d80c25af
chore(deps): update actions/upload-artifact action to v3
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2022-03-04 18:57:56 +01:00
MedzikUser 6abb99d50a
feat(webhook): add discord webhook 2022-03-04 18:57:02 +01:00
MedzikUser 572899509f
feat(webhook): add discord webhook
example https://i.imgur.com/fgSyFZP.png
2022-03-03 21:14:43 +01:00
MedzikUser 6ec952b60f
chore(ci): rename -all to --all (fix) 2022-03-02 22:23:00 +01:00
MedzikUser e1164e1147
update 2022-03-02 22:17:52 +01:00
MedzikUser 6c7c334ede
update 2022-03-02 22:16:44 +01:00
MedzikUser adc52ba207
feat(lib): if body length is > 30 return `body is too length` 2022-03-02 22:10:56 +01:00
MedzikUser 22bde2819e
chore(changelog): update 2022-02-28 23:32:26 +01:00
MedzikUser ee66bc93c4
fix(clippy): try using a `char` instead: `:` 2022-02-28 23:29:16 +01:00
MedzikUser a591d427f3
chore: rename anyhow_err to anyhow::Error 2022-02-28 23:26:35 +01:00
MedzikUser 4ad662e35e
feat(clipboard): add support for xclip and termux 2022-02-28 23:20:50 +01:00
Renovate Bot a29ca66e0d fix(deps): update rust crate clap to 3.1.3 2022-02-28 19:08:56 +00:00
renovate[bot] a177602edd
chore(deps): update mindsers/changelog-reader-action action to v2
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2022-02-27 12:43:52 +01:00
Medzik d59b7240b3
v0.4.0 2022-02-27 12:41:49 +01:00
Medzik f0a52fa512
update github workflow fmt 2022-02-27 12:33:36 +01:00
Medzik e129ab0d65 if upload image error send notify 2022-02-27 12:32:20 +01:00
MedzikUserBot 24d7be84fa rustfmt 2022-02-27 11:06:34 +00:00
Medzik b7c98934e6
add gen manpage and completion for elvish 2022-02-27 12:06:02 +01:00
MedzikUserBot 7598ba5293 rustfmt 2022-02-27 10:39:28 +00:00
Medzik 461b53d681
update logger 2022-02-27 11:39:02 +01:00
MedzikUserBot 2c22737070 rustfmt 2022-02-26 21:51:41 +00:00
Medzik a4a17a4fc5
fix clipboard 2022-02-26 22:51:12 +01:00
Medzik 329263edb0
add clipboard, print colors in image info 2022-02-25 18:24:24 +01:00
Renovate Bot f516bde8cd fix(deps): update rust crate tokio to 1.17.0 2022-02-24 14:20:20 +00:00
Renovate Bot fd12defe14 fix(deps): update rust crate clap_complete to 3.1.0 2022-02-24 12:44:43 +00:00
Renovate Bot 5f182560c5 fix(deps): update rust crate clap to 3.1.2 2022-02-24 10:08:11 +00:00
Renovate Bot f065eb6402 fix(deps): update rust crate serde_json to 1.0.79 2022-02-24 08:53:13 +00:00
Renovate Bot 36c74a4a3f fix(deps): update rust crate notify-rust to 4.5.6 2022-02-24 04:24:31 +00:00
Renovate Bot 56323eee26 fix(deps): update rust crate anyhow to 1.0.55 2022-02-24 02:04:52 +00:00
Renovate Bot 572ff08d03 chore(deps): update ad-m/github-push-action commit hash to a3fd843 2022-02-23 23:48:54 +00:00
Medzik e8e8d498f7
update renovate config 2022-02-23 22:50:45 +01:00
Medzik a0192d997d
add renovate config 2022-02-23 22:46:28 +01:00
MedzikUserBot d272e3435d rustfmt 2022-02-23 21:39:57 +00:00
Medzik 39f1debdd2
add clipboard 2022-02-23 22:39:37 +01:00
Oskar 86397d5e23
Update README.md 2022-02-12 23:24:20 +01:00
MedzikUser 1cbcc8a92b
v0.3.0 2022-01-28 22:45:58 +01:00
MedzikUser a42972c65f
update dependencies 2022-01-28 18:25:15 +01:00
MedzikUser 6d01fe6112
cli: add url validate 2022-01-27 16:49:47 +01:00
MedzikUser 7455932107
better panic, rename ImgurHandle -> ImgurClient 2022-01-27 14:14:46 +01:00
MedzikUser ecace5aa46
upgrade dependencies 2022-01-27 12:33:20 +01:00
MedzikUser 979490e9f0
cli config: update error return 2022-01-27 11:03:37 +01:00
MedzikUser bfb8db96e1
update cargo manifest 2022-01-27 11:02:56 +01:00
MedzikUser 5a620ea1c2
library: return anyhow::Error instead String 2022-01-26 20:57:51 +01:00
MedzikUser 62c0352345
SimpleLogger error handling, update CI and code format 2022-01-26 19:55:08 +01:00
MedzikUser 60e7ba5bec
update README 2022-01-26 19:24:37 +01:00
MedzikUser b1b3b18b7a
add README 2022-01-26 19:22:15 +01:00
MedzikUser d421835c54
update manifest 2022-01-25 20:25:34 +01:00
MedzikUser 03970a517a v0.2.0 2022-01-25 19:53:51 +01:00
MedzikUser e7fab7de2d
api: move reqwest to send_api_request function 2022-01-25 19:47:08 +01:00
MedzikUserBot b804e8faa2 rustfmt 2022-01-25 17:26:52 +00:00
MedzikUser 62531904f9 cli completions use match instead of if 2022-01-25 18:25:52 +01:00
MedzikUserBot bd3ef20a7f rustfmt 2022-01-25 16:14:23 +00:00
MedzikUser a83788d0c6
add shell completions 2022-01-25 17:13:43 +01:00
MedzikUser 37f51f97dd
github actions add build for "aarch64-unknown-linux-musl" 2022-01-24 23:28:18 +01:00
MedzikUser 605e051cdb
update config 2022-01-24 23:15:55 +01:00
MedzikUserBot 4fc60d8bbe rustfmt 2022-01-24 19:49:27 +00:00
MedzikUser 5edeac60fd add option in config to disable notifications 2022-01-24 20:48:40 +01:00
MedzikUserBot af6830da0c rustfmt 2022-01-24 19:40:08 +00:00
MedzikUser 2d38cf7e28
when the image uploaded, send a notification 2022-01-24 20:39:33 +01:00
MedzikUserBot 076d7e04c0 rustfmt 2022-01-24 18:37:50 +00:00
MedzikUser ee7293791c
add create default config if not exist, create CHANGELOG.md 2022-01-24 19:33:44 +01:00
MedzikUser 93ffaaed5d
switch from OpenSSL to RustTLS, add aarch64-apple-darwin in workflows 2022-01-23 21:40:09 +01:00
MedzikUser c2a2c20de2
update 2022-01-23 20:52:18 +01:00
MedzikUser 754874388a
update 2022-01-23 20:29:33 +01:00
MedzikUser 3bd2a67198
update 2022-01-23 14:59:29 +01:00
MedzikUser be0452644b
update worklows 2022-01-23 14:51:51 +01:00
51 changed files with 3619 additions and 996 deletions

View File

@ -1,20 +0,0 @@
# EditorConfig is awesome: https://EditorConfig.org
# top-most EditorConfig file
root = true
[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = false
[*.yml]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = false

View File

@ -1,6 +1,14 @@
name: Build release binaries (and publish them if this is a tag)
on: [push, pull_request]
on:
push:
paths-ignore:
- '*.md'
workflow_dispatch:
env:
CARGO_TERM_COLOR: always
jobs:
binaries:
@ -9,18 +17,31 @@ jobs:
matrix:
target:
- x86_64-unknown-linux-gnu
- x86_64-unknown-linux-musl
- aarch64-unknown-linux-musl
- x86_64-pc-windows-msvc
- x86_64-apple-darwin
- aarch64-apple-darwin
include:
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
artifact_name: target/x86_64-unknown-linux-gnu/release/imgurs
release_name: x86_64-unknown-linux-gnu
cross: false
target: x86_64-unknown-linux-musl
artifact_name: target/x86_64-unknown-linux-musl/release/imgurs
release_name: x86_64-unknown-linux-musl
cross: true
strip: true
compress: true
cargo_flags: ""
- os: ubuntu-latest
target: aarch64-unknown-linux-musl
artifact_name: target/aarch64-unknown-linux-musl/release/imgurs
release_name: aarch64-unknown-linux-musl
cross: true
strip: false
compress: true
cargo_flags: ""
- os: windows-latest
target: x86_64-pc-windows-msvc
artifact_name: target/x86_64-pc-windows-msvc/release/imgurs.exe
@ -29,6 +50,7 @@ jobs:
strip: true
compress: true
cargo_flags: ""
- os: macos-latest
target: x86_64-apple-darwin
artifact_name: target/x86_64-apple-darwin/release/imgurs
@ -38,6 +60,15 @@ jobs:
compress: true
cargo_flags: ""
- os: macos-latest
target: aarch64-apple-darwin
artifact_name: target/aarch64-apple-darwin/release/imgurs
release_name: aarch64-apple-darwin
cross: false
strip: true
compress: true
cargo_flags: ""
name: ${{ matrix.os }} for ${{ matrix.target }}
runs-on: ${{ matrix.os }}
@ -55,7 +86,7 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: build
args: --release --target=${{ matrix.target }} ${{ matrix.cargo_flags }}
args: --all --release --target=${{ matrix.target }} ${{ matrix.cargo_flags }}
use-cross: ${{ matrix.cross }}
- name: Compress binaries
@ -67,7 +98,7 @@ jobs:
if: ${{ matrix.compress }}
- name: Upload artifact
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
with:
name: ${{ matrix.target }}
path: ${{ matrix.artifact_name }}
@ -97,6 +128,6 @@ jobs:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: ${{ matrix.artifact_name }}
tag: ${{ github.ref }}
asset_name: miniserve-$tag-${{ matrix.release_name }}
asset_name: imgurs-$tag-${{ matrix.release_name }}
body: ${{ steps.changelog_reader.outputs.log_entry }}
if: startsWith(github.ref, 'refs/tags/v')

View File

@ -2,7 +2,13 @@ name: Rust
on:
push:
branches: [ main ]
branches:
- main
paths-ignore:
- '*.md'
workflow_dispatch:
env:
CARGO_TERM_COLOR: always
@ -19,23 +25,31 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- name: Checkout code
uses: actions/checkout@v3
- name: Setup Rust toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ matrix.rust }}
override: true
components: rustfmt, clippy
- name: Setup Rust toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ matrix.rust }}
override: true
components: rustfmt, clippy
- name: cargo build
uses: actions-rs/cargo@v1
with:
command: build
- name: cargo build
uses: actions-rs/cargo@v1
with:
command: build
args: --all
- name: cargo clippy
uses: actions-rs/cargo@v1
with:
command: clippy
args: -- -D warnings
- name: cargo test
uses: actions-rs/cargo@v1
with:
command: test
args: --all-features
- name: cargo clippy
uses: actions-rs/cargo@v1
with:
command: clippy
args: -- -D warnings --no-deps

View File

@ -1,45 +0,0 @@
name: Rust fmt
on:
push:
branches: [ main ]
env:
CARGO_TERM_COLOR: always
jobs:
fmt:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup GIT
run: |
git config --global user.name "MedzikUserBot"
git config --global user.email "rm99iv9s@duck.com"
- name: Setup Rust toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
components: rustfmt, clippy
- name: cargo fmt
uses: actions-rs/cargo@v1
with:
command: fmt
args: --all
- name: Commit changes
run: |
git add .
git diff-index --quiet HEAD || git commit -m "rustfmt"
- name: Push changes
uses: ad-m/github-push-action@b007e7b818e33b04afd056e4c4b57ba917145d7a
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
branch: 'main'

6
.gitignore vendored
View File

@ -1 +1,7 @@
# Generated by Cargo
# will have compiled files and executables
/target
# IDE configs
.vscode
.idea

186
CHANGELOG.md Normal file
View File

@ -0,0 +1,186 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).
<!-- next-header -->
## [Unreleased]
## [0.11.3] - 2024-04-11
- Updated dependencies
- Replaced url validation with url crate in imgurs-cli
## [0.11.2] - 2023-06-22
- Updated dependencies
- Fix deprecation warning of base64 crate.
## [0.11.1] - 2022-12-11
### Fixed
- `album_title` can be null, `account_id` can be null, string or number #92, thanks to @NotNorom
### Changed
- Updated dependencies
## [0.11.0] - 2022-11-07
### Added
- Added `with_http_client` method to ImgurClient #87, thanks to @NotNorom
### Changed
- Updated crate arboard to v3
- Updated crate simple_logger to v4
## [0.10.0] - 2022-10-01
- add configuration for tls (rustls-tls or native-tls)
## [0.9.1] - 2022-09-22
- delete debug info from cli in release build
## [0.9.0] - 2022-09-05
- moved cli to other crate
- added get_album function
## [0.8.1] - 2022-06-18
- fix tests
- add missing doc
- use `serde` instead of `serde_derive`
## [0.8.0] - 2022-06-13
- add custom `Error` type
- move api requests to `requests/` mod
- comment code
## [0.7.4] - 2022-05-18
### HOTFIX
- fixed built on macos and windows
## [0.7.3] - 2022-05-18
### Library
- add code comments and tests
- change `String` to `&str` in ImgurClient functions
### Other
- bump deps
- use `anyhow::Result<...>` instead `Result<..., Error>`
## [0.7.2] - 2022-04-05
### HotFix
- fix upload image from file
## [0.7.1] - 2022-04-04
- fix build on what is not linux
## [0.7.0] - 2022-04-03
### CLI
- completions: changed type from String to Shell
- removed `&` from `cli.commands` (line 54 in [parse.rs](./src/cli/parse.rs))
### Library
- removed `.map_err(anyhow::Error::new)` when function returns error
### Added
- commands in the code
- api functions to `impl` in `ImgurClient`
- documentation (example usage)
### Breaking Changes
- lib: moved everything to the main package with api submodules (before `imgurs::api::ImgurClient`, after `imgurs::api::ImgurClient`)
## [0.6.0] - 2022-03-14
### CLI
- webhook: added url in title
- cli: change image domain to your own (set in config)
- if the configuration file cannot be open, ask the user whether to overwrite the file instead of overwriting it without asking
- logger: set `max_level_debug` in debug binary
## [0.5.1] - 2022-03-08
### Cli
- change webhook to discord-webhook (to use rustls)
## [0.5.0] - 2022-03-07
### CLI
- clipboard: add support for xclip and termux
- webhook: send webhook to discord if image uploaded ([example](https://i.imgur.com/CPpHEec.png))
### Library
- if body length is greater than 30, return message `body is too length`
## [0.4.0] - 2022-02-27
### CLI
- update logger
- added clipboard
- added manpage
- added completion for elvish
- if failed to upload image send notify with error message
### Library
- added Clone derive
- if body length is > 30 return body is too length
## [0.3.0] - 2022-01-28
### CLI
- SimpleLogger init error handling
- better panic
- panic instead of send log error
- add url validate
### Library
- The returned error in the Result is from now on anyhow::Error and not String.
- Do not exit program if send_api_request error
- rename ImgurHandle -> ImgurClient
## [0.2.0] - 2022-01-23
### Added
#### CLI
- create default config, if not exits
- when the image uploaded, send a notification (can be turn off in config)
- shell completions
#### Library
- change OpenSSL to RusTLS
- move api request to fn send_api_request
### Fixed
- api rate limit (error decoding response body: invalid value: integer \`200\`, expected i8 at line 1 column 140)
## [0.1.0] - 2022-01-23
### CLI
- commands
- credits
- delete
- info
- upload
- toml config parser
### Library
- image info
- rate limit
- image info
- delete image
- upload image
<!-- next-url -->
[Unreleased]: https://github.com/MedzikUser/imgurs/compare/v0.11.3...HEAD
[0.11.3]: https://github.com/MedzikUser/imgurs/commits/v0.11.3
[0.11.2]: https://github.com/MedzikUser/imgurs/commits/v0.11.2
[0.11.1]: https://github.com/MedzikUser/imgurs/commits/v0.11.1
[0.11.0]: https://github.com/MedzikUser/imgurs/commits/v0.11.0
[0.10.0]: https://github.com/MedzikUser/imgurs/commits/v0.10.0
[0.9.1]: https://github.com/MedzikUser/imgurs/commits/v0.9.1
[0.9.0]: https://github.com/MedzikUser/imgurs/commits/v0.9.0
[0.8.1]: https://github.com/MedzikUser/imgurs/commits/v0.8.1
[0.8.0]: https://github.com/MedzikUser/imgurs/commits/v0.8.0
[0.7.4]: https://github.com/MedzikUser/imgurs/commits/v0.7.4
[0.7.3]: https://github.com/MedzikUser/imgurs/commits/v0.7.3
[0.7.2]: https://github.com/MedzikUser/imgurs/commits/v0.7.2
[0.7.1]: https://github.com/MedzikUser/imgurs/commits/v0.7.1
[0.7.0]: https://github.com/MedzikUser/imgurs/commits/v0.7.0
[0.6.0]: https://github.com/MedzikUser/imgurs/commits/v0.6.0
[0.5.1]: https://github.com/MedzikUser/imgurs/commits/v0.5.1
[0.5.0]: https://github.com/MedzikUser/imgurs/commits/v0.5.0
[0.4.0]: https://github.com/MedzikUser/imgurs/commits/v0.4.0
[0.3.0]: https://github.com/MedzikUser/imgurs/commits/v0.3.0
[0.2.0]: https://github.com/MedzikUser/imgurs/commits/v0.2.0
[0.1.0]: https://github.com/MedzikUser/imgurs/commits/v0.1.0

2495
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,36 +1,43 @@
[workspace]
members = ["imgurs-cli"]
resolver = "2"
[package]
name = "imgurs"
version = "0.1.0"
version = "0.11.3"
description = "API for Imgur"
license = "BSD-3-Clause"
authors = ["M3DZIK <me@medzik.dev>"]
homepage = "https://github.com/M3DZIK/imgurs"
repository = "https://github.com/M3DZIK/imgurs.git"
keywords = ["imgur", "imgur-api", "image", "image-upload"]
edition = "2021"
[features]
default = ["imgur", "rustls-tls"]
full = ["imgur"]
rustls-tls = ["reqwest/rustls-tls"]
native-tls = ["reqwest/native-tls"]
imgur = []
[profile.release]
lto = true
opt-level = 'z'
codegen-units = 1
[dependencies]
dirs = "4.0.0"
serde = "1.0.134"
serde_derive = "1.0.134"
toml = "0.5.8"
serde_json = "1.0.75"
chrono = "0.4.19"
base64 = "0.13.0"
# HTTP
reqwest = { version = "0.12", default-features = false, features = ["json", "multipart"] }
# Request
base64 = "0.22"
url = "2.5.0" # validate url address
# Response
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
[dependencies.clap]
version = "3.0.7"
features = ["derive", "cargo", "unicode"]
# Errors
thiserror = "1.0"
[dependencies.log]
version = "0.4.14"
features = ["release_max_level_info"]
[dependencies.simple_logger]
version = "2.1.0"
default-features = false
features = ["colors"]
[dependencies.reqwest]
version = "0.11.9"
features = ["json"]
[dependencies.tokio]
version = "1.15.0"
features = ["full"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dev-dependencies]
# Async tests
tokio = { version = "1.37", features = ["macros", "rt-multi-thread"] }

71
README.md Normal file
View File

@ -0,0 +1,71 @@
[crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust
[docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs
# Imgurs - CLI and Library for Imgur API
[![crates-io]](https://crates.io/crates/imgurs)
[![docs-rs]](https://docs.rs/imgurs)
## Screenshots
![](https://i.imgur.com/MG35kvf.png)
![](https://i.imgur.com/TSxBrhO.png)
## Shell completions
Here are some examples of usage
```bash
# For bash
imgurs completions bash > ~/.local/share/bash-completion/completions/imgurs
# For zsh
imgurs completions zsh > /usr/local/share/zsh/site-functions/_imgurs
# For fish
imgurs completions fish > ~/.config/fish/completions/imgurs.fish
```
## Man page
Generate manpage
imgurs manpage | gzip > /usr/share/man/man1/imgurs.1.gz
## Dependencies
- support clipboard on Linux
- **xsel**
- **xclip** - alternative to **xsel**
- **termux-api** - on **Termux**
- **libnotify** - support notification on Linux
## How to install Imgurs CLI?
### **Linux**
Download imgurs-linux from [the releases page](https://github.com/MedzikUser/imgurs/releases/latest) and run
chmod +x imgurs-linux
./imgurs-linux
#### **Arch Linux**
Using yay ([AUR](https://aur.archlinux.org/packages/imgurs))
yay -S imgurs
or can add [this repo](https://github.com/archlinux-pkg/packages) and run
sudo pacman -Sy imgurs
### **OSX**
Download imgurs-darwin from [the releases page](https://github.com/MedzikUser/imgurs/releases/latest) and run
chmod +x imgurs-darwin
./imgurs-darwin
### **Windows**
Download imgurs-windows.exe from [the releases page](https://github.com/MedzikUser/imgurs/releases/latest) and run
imgurs-windows.exe
### **Compile with Cargo**
Make sure you have a recent version of Rust. Then you can run
cargo install imgurs-cli

9
config.toml Normal file
View File

@ -0,0 +1,9 @@
[imgur]
id = '3e3ce0d7ac14d56'
image_cdn = 'i.imgur.com'
[notification]
enabled = true
[clipboard]
enabled = true

46
imgurs-cli/Cargo.toml Normal file
View File

@ -0,0 +1,46 @@
[package]
name = "imgurs-cli"
version = "0.11.3"
description = "CLI for Imgur"
license = "BSD-3-Clause"
authors = ["M3DZIK <me@medzik.dev>"]
homepage = "https://github.com/M3DZIK/imgurs"
repository = "https://github.com/M3DZIK/imgurs.git"
keywords = ["imgur", "imgur-api", "image", "image-upload"]
categories = ["command-line-utilities"]
edition = "2021"
[[bin]]
name = "imgurs"
path = "src/main.rs"
[dependencies]
# Async runtime
tokio = { version = "1.37", features = ["macros", "rt-multi-thread"] }
# CLI
clap = { version = "4.5", features = ["derive"] }
clap_complete = "4.5"
clap_mangen = "0.2"
# Errors
anyhow = "1.0"
# Logger
log = { version = "0.4", features = ["release_max_level_info"] }
simple_logger = "4.3"
colored = "2.1"
# Config
toml = "0.8"
serde = { version = "1.0", features = ["derive"] }
# Other
chrono = "0.4" # parse upload date
notify-rust = "4.11" # send notification after upload
dirs = "5.0" # get system configuration directory
imgurs = { path = "..", version = "0.11.0", features = ["full"] }
[target.'cfg(not(all(unix, not(any(target_os="macos", target_os="android", target_os="emscripten")))))'.dependencies]
arboard = "3.3" # copy url to clipboard

1
imgurs-cli/config.toml Symbolic link
View File

@ -0,0 +1 @@
../config.toml

View File

@ -0,0 +1,37 @@
use serde::{Deserialize, Serialize};
pub mod toml;
/// Configuration schema
#[derive(Debug, Serialize, Deserialize)]
pub struct Config {
/// Imgur API configuration options
pub imgur: ConfigImgur,
/// Notification options
pub notification: ConfigNotification,
/// Clipboard options
pub clipboard: ConfigClipboard,
}
/// Imgur API configuration options
#[derive(Debug, Serialize, Deserialize)]
pub struct ConfigImgur {
/// Imgur Client ID
pub id: String,
/// Imgur Domain (e.g. if you have a imgur proxy)
pub image_cdn: String,
}
/// Notification options
#[derive(Debug, Serialize, Deserialize)]
pub struct ConfigNotification {
/// Send notification
pub enabled: bool,
}
/// Clipboard options
#[derive(Debug, Serialize, Deserialize)]
pub struct ConfigClipboard {
/// Copy image url to clipboard
pub enabled: bool,
}

View File

@ -0,0 +1,63 @@
use std::{
fs::{create_dir_all, read_to_string, File},
io::{self, Write as _},
path::Path,
};
use colored::Colorize;
use dirs::config_dir;
use log::warn;
use toml::from_str as toml_from_str;
use super::Config;
/// Configuration file path (in system config directory).
const CONFIG_DIR: &str = "/imgurs/config.toml";
/// Parse configuration file
pub fn parse() -> Config {
// parse config or use default
toml().unwrap_or_else(|err| {
let mut stdout = std::io::stdout();
write!(stdout, "{}", "The configuration file could not be opened. Do you want to create/overwrite with DEFAULT values? (Y/n): ".yellow()).unwrap();
stdout.flush().unwrap();
let mut value = String::new();
io::stdin()
.read_line(&mut value)
.expect("failed to read line");
if value.to_lowercase() != "n\n" {
warn!("Parse toml config error: {err}! Creating config file...");
let default_config = include_str!(concat!("../../config.toml"));
let sys_config_dir = config_dir().expect("find config dir");
let config_dir = format!("{}{CONFIG_DIR}", sys_config_dir.to_string_lossy());
let config_path = Path::new(&config_dir);
let prefix = config_path.parent().unwrap();
create_dir_all(prefix).expect("create config dir");
let mut file_ref = File::create(config_path).expect("create failed");
file_ref
.write_all(default_config.as_bytes())
.expect("failed write default config to file");
toml().expect("parse toml config")
} else {
panic!("Configuration file creation cancelled!")
}
})
}
fn toml() -> anyhow::Result<Config> {
let config_dir = config_dir().unwrap();
let file_dir = format!("{}{CONFIG_DIR}", config_dir.to_string_lossy());
let toml_str = read_to_string(file_dir)?;
let decode = toml_from_str(&toml_str)?;
Ok(decode)
}

View File

@ -0,0 +1,85 @@
#[cfg(all(
unix,
not(any(target_os = "macos", target_os = "android", target_os = "emscripten"))
))]
// use xclip (or a similar program that is installed) because the kernel deletes the clipboard after the process ends
pub fn set_clipboard(content: &str) {
fn is_program_in_path(program: &str) -> bool {
if let Ok(path) = std::env::var("PATH") {
for p in path.split(':') {
let p_str = format!("{}/{}", p, program);
if std::fs::metadata(p_str).is_ok() {
return true;
}
}
}
false
}
use std::{
io::Write,
process::{Command, Stdio},
};
use colored::Colorize;
let mut child;
// xsel
if is_program_in_path("xsel") {
child = Command::new("xsel")
.arg("--input")
.arg("--clipboard")
.stdin(Stdio::piped())
.spawn()
.expect("execute command xsel")
// xclip
} else if is_program_in_path("xclip") {
child = Command::new("xclip")
.arg("-in")
.arg("-selection")
.arg("clipboard")
.stdin(Stdio::piped())
.spawn()
.expect("execute command xclip")
// termux
} else if is_program_in_path("termux-clipboard-set") {
child = Command::new("termux-clipboard-set")
.stdin(Stdio::piped())
.spawn()
.expect("execute command termux-clipboard-set")
// the above programs responsible for the clipboard were not found
} else {
println!(
"{} {}",
"WARN".yellow(),
"command for clipboard not found".magenta()
);
return;
}
// copy the content (send it to stdin command)
child
.stdin
.as_mut()
.unwrap()
.write_all(content.as_bytes())
.expect("execute command");
child
.wait_with_output()
.expect("wait for clipboard command output");
}
#[cfg(not(all(
unix,
not(any(target_os = "macos", target_os = "android", target_os = "emscripten"))
)))]
pub fn set_clipboard(content: &str) {
let mut clipboard = arboard::Clipboard::new().unwrap();
clipboard.set_text(content.to_string()).unwrap();
}

View File

@ -0,0 +1,45 @@
use std::time::{Duration, UNIX_EPOCH};
use chrono::{prelude::DateTime, Utc};
use colored::Colorize;
use imgurs::ImgurClient;
pub async fn credits(client: ImgurClient) {
// get client ratelimit from imgur api
let i = client
.rate_limit()
.await
.expect("send request to imgur api");
// format image upload date
let date = UNIX_EPOCH + Duration::from_secs(i.data.user_reset.try_into().unwrap());
let datetime = DateTime::<Utc>::from(date);
let timestamp_str = datetime.format("%Y-%m-%d %H:%M:%S").to_string();
println!(
"{} {}",
"user limit".green(),
i.data.user_limit.to_string().magenta()
);
println!(
"{} {}",
"user remaining".green(),
i.data.user_remaining.to_string().magenta()
);
println!(
"{} {} {}",
"user reset".green(),
timestamp_str.magenta(),
"(UTC)".blue()
);
println!(
"{} {}",
"client limit".green(),
i.data.client_limit.to_string().magenta()
);
println!(
"{} {}",
"client remaining ".green(),
i.data.client_remaining.to_string().magenta()
);
}

View File

@ -0,0 +1,15 @@
use colored::Colorize;
use imgurs::ImgurClient;
pub async fn delete_image(client: ImgurClient, delete_hash: String) {
// delete image from imgur
client
.delete_image(&delete_hash)
.await
.expect("send api request");
println!(
"{}",
"If Delete Hash was correct the image was deleted!".magenta()
);
}

View File

@ -0,0 +1,14 @@
use imgurs::ImgurClient;
use super::print_image_info;
pub async fn image_info(client: ImgurClient, id: String) {
// get a image info from imgur
let info = client
.image_info(&id)
.await
.expect("send request to imfur api");
// print image information from imgur
print_image_info(&info);
}

View File

@ -0,0 +1,82 @@
mod clipboard;
mod credits;
mod delete_image;
mod info_image;
mod upload_image;
use std::time::{Duration, UNIX_EPOCH};
use chrono::{prelude::DateTime, Utc};
use colored::Colorize;
use imgurs::ImageInfo;
pub use self::{clipboard::*, credits::*, delete_image::*, info_image::*, upload_image::*};
// print image information from imgur
pub fn print_image_info(i: &ImageInfo) {
// format image upload date
let d = UNIX_EPOCH + Duration::from_secs(i.data.datetime.try_into().unwrap());
let datetime = DateTime::<Utc>::from(d);
let timestamp_str = datetime.format("%Y-%m-%d %H:%M:%S").to_string();
// image title
if i.data.title != None {
let title = i.data.title.clone();
println!(
"{} {}",
"title".green(),
title.unwrap_or_else(|| "unknown".to_string()).magenta()
);
}
// image description
if i.data.description != None {
let desc = i.data.description.clone();
println!(
"{} {}",
"description".green(),
desc.unwrap_or_else(|| "unknown".to_string()).magenta()
);
}
// image deletehas
if i.data.deletehash != None {
let delhash = i.data.deletehash.clone();
println!(
"{} {}",
"deletehash".green(),
delhash.unwrap_or_else(|| "unknown".to_string()).magenta()
);
}
println!("{} {}", "id".green(), i.data.id.magenta());
println!(
"{} {} {}",
"upload date".green(),
timestamp_str.magenta(),
"(UTC)".blue()
);
println!("{} {}", "type".green(), i.data.img_type.magenta());
println!("{} {}", "width".green(), i.data.width.to_string().magenta());
println!(
"{} {}",
"height".green(),
i.data.height.to_string().magenta()
);
println!(
"{} {} {}",
"size".green(),
(i.data.size / 1000).to_string().magenta(),
"KB".blue()
);
println!("{} {}", "views".green(), i.data.views.to_string().magenta());
println!(
"{} {}",
"bandwidth".green(),
i.data.bandwidth.to_string().magenta()
);
println!("{} {}", "link".green(), i.data.link.magenta());
}

View File

@ -0,0 +1,49 @@
use imgurs::ImgurClient;
use notify_rust::Notification;
use crate::{
config::toml,
imgur::{clipboard::set_clipboard, print_image_info},
};
// show notification
macro_rules! notify (
($notification: expr) => (
if toml::parse().notification.enabled {
$notification.show().expect("send notification");
}
);
);
pub async fn upload_image(client: ImgurClient, path: String) {
// parse configuration file
let config = toml::parse();
// upload a image to imgur
let mut i = client.upload_image(&path).await.unwrap_or_else(|err| {
notify!(Notification::new()
.summary("Error!")
.body(&format!("Error: {}", err))
.appname("Imgurs")); // I don't think you can set it to error
panic!("send request to imgur api: {}", err)
});
// change domain to proxy (to be set in config)
if config.imgur.image_cdn != "i.imgur.com" {
i.data.link = i.data.link.replace("i.imgur.com", &config.imgur.image_cdn)
}
// print image information from imgur
print_image_info(&i);
// send notification that the image has been uploaded
notify!(Notification::new()
.summary("Imgurs")
.body(&format!("Uploaded {}", i.data.link)));
// if enabled copy link to clipboard
if config.clipboard.enabled {
set_clipboard(&i.data.link)
}
}

82
imgurs-cli/src/main.rs Normal file
View File

@ -0,0 +1,82 @@
use std::io::stdout;
use clap::{Command, CommandFactory, Parser};
use clap_complete::{generate, Generator, Shell};
use imgurs::ImgurClient;
use simple_logger::SimpleLogger;
use crate::imgur::*;
mod config;
mod imgur;
#[derive(Parser, Debug)]
#[clap(
name = "imgurs",
about = "Imgur API CLI",
long_about = env!("CARGO_PKG_DESCRIPTION"),
version = env!("CARGO_PKG_VERSION"),
)]
enum Cli {
#[clap(about = "Print Client Rate Limit", display_order = 1)]
Credits,
#[clap(about = "Upload image to Imgur", display_order = 2)]
Upload { path: String },
#[clap(about = "Delete image from Imgur", display_order = 3)]
Delete { delete_hash: String },
#[clap(about = "Print image info", display_order = 4)]
Info { id: String },
#[clap(
about = "Generate completion file for a shell [bash, elvish, fish, powershell, zsh]",
display_order = 5
)]
Completions { shell: Shell },
#[clap(about = "Generate man page", display_order = 6)]
Manpage,
}
#[tokio::main]
async fn main() {
SimpleLogger::new().init().unwrap();
// parse config file
let config = config::toml::parse();
// create imgur client
let client = ImgurClient::new(&config.imgur.id);
let args = Cli::parse();
match args {
Cli::Credits => credits(client).await,
Cli::Upload { path } => upload_image(client, path).await,
Cli::Delete { delete_hash } => delete_image(client, delete_hash.to_string()).await,
Cli::Info { id } => image_info(client, id.to_string()).await,
Cli::Completions { shell } => {
let mut app = Cli::command();
fn print_completions<G: Generator>(gen: G, app: &mut Command) {
generate(gen, app, app.get_name().to_string(), &mut stdout())
}
print_completions(shell, &mut app)
},
Cli::Manpage => {
let clap_app = Cli::command();
let man = clap_mangen::Man::new(clap_app);
man.render(&mut stdout())
.expect("Failed to generate man page");
},
}
}

25
renovate.json Normal file
View File

@ -0,0 +1,25 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:base",
"schedule:weekly",
"group:allNonMajor",
":semanticCommits"
],
"labels": [
"dependencies"
],
"automergeType": "pr",
"prCreation": "immediate",
"packageRules": [
{
"matchUpdateTypes": [
"minor",
"patch",
"pin",
"digest"
],
"automerge": true
}
]
}

11
rustfmt.toml Normal file
View File

@ -0,0 +1,11 @@
# https://rust-lang.github.io/rustfmt
# stable
edition = "2021"
newline_style = "Unix"
match_block_trailing_comma = true
# nightly
group_imports = "StdExternalCrate"
imports_granularity = "Crate"
format_code_in_doc_comments = true

View File

@ -1,28 +0,0 @@
use crate::api::configuration::{api_url, ImgurHandle};
pub async fn delete_image(c: ImgurHandle, delete_hash: String) -> Result<String, String> {
const VERSION: Option<&str> = option_env!("CARGO_PKG_VERSION");
let res = c
.client
.delete(api_url!(format!("image/{delete_hash}")))
.header("Authorization", format!("Client-ID {}", c.client_id))
.header(
"User-Agent",
format!("Imgur/{:?}", VERSION.unwrap_or("unknown")),
)
.send()
.await
.map_err(|err| err.to_string())?;
let status = res.status();
if status.is_client_error() || status.is_server_error() {
let body = res.text().await.map_err(|err| err.to_string())?;
Err(format!(
"server returned non-successful status code = {status}. body = {body}"
))
} else {
Ok("If the delete hash was correct the image was deleted!".to_string())
}
}

View File

@ -1,29 +0,0 @@
use crate::api::configuration::{api_url, ImgurHandle};
use crate::api::ImageInfo;
pub async fn get_image(c: ImgurHandle, image: &str) -> Result<ImageInfo, String> {
const VERSION: Option<&str> = option_env!("CARGO_PKG_VERSION");
let res = c
.client
.get(api_url!(format!("image/{image}")))
.header("Authorization", format!("Client-ID {}", c.client_id))
.header(
"User-Agent",
format!("Imgurs/{:?}", VERSION.unwrap_or("unknown")),
)
.send()
.await
.map_err(|err| err.to_string())?;
let status = res.status();
if status.is_client_error() || status.is_server_error() {
Err(format!(
"server returned non-successful status code = {status}."
))
} else {
let content: ImageInfo = res.json().await.map_err(|err| err.to_string())?;
Ok(content)
}
}

View File

@ -1,25 +0,0 @@
use serde_derive::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
pub struct ImageInfo {
pub data: ImageInfoData,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct ImageInfoData {
pub id: String,
pub title: Option<String>,
pub description: Option<String>,
pub datetime: i32,
#[serde(rename = "type")]
pub img_type: String,
pub animated: bool,
pub width: i32,
pub height: i32,
pub size: i32,
pub views: i32,
pub bandwidth: i64,
pub favorite: bool,
pub deletehash: Option<String>,
pub link: String,
}

View File

@ -1,10 +0,0 @@
mod image_type;
pub mod configuration;
pub mod delete_image;
pub mod get_image;
pub mod rate_limit;
pub mod upload_image;
pub use configuration::ImgurHandle;
pub use image_type::*;

View File

@ -1,52 +0,0 @@
use crate::api::configuration::{api_url, ImgurHandle};
use serde_derive::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
pub struct RateLimitInfo {
pub data: RateLimitData,
pub success: bool,
pub status: i8,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct RateLimitData {
#[serde(rename = "UserLimit")]
pub user_limit: i32,
#[serde(rename = "UserRemaining")]
pub user_remaining: i32,
#[serde(rename = "UserReset")]
pub user_reset: i32,
#[serde(rename = "ClientLimit")]
pub client_limit: i32,
#[serde(rename = "ClientRemaining")]
pub client_remaining: i32,
}
pub async fn rate_limit(c: ImgurHandle) -> Result<RateLimitInfo, String> {
const VERSION: Option<&str> = option_env!("CARGO_PKG_VERSION");
let res = c
.client
.get(api_url!("credits"))
.header("Authorization", format!("Client-ID {}", c.client_id))
.header(
"User-Agent",
format!("Imgur/{:?}", VERSION.unwrap_or("unknown")),
)
.send()
.await
.map_err(|err| err.to_string())?;
let status = res.status();
if status.is_client_error() || status.is_server_error() {
let body = res.text().await.map_err(|err| err.to_string())?;
Err(format!(
"server returned non-successful status code = {status}. body = {body}"
))
} else {
let content: RateLimitInfo = res.json().await.map_err(|err| err.to_string())?;
Ok(content)
}
}

View File

@ -1,37 +0,0 @@
use crate::api::configuration::{api_url, ImgurHandle};
use crate::api::ImageInfo;
use std::collections::HashMap;
pub async fn upload_image(c: ImgurHandle, image: &str) -> Result<ImageInfo, String> {
const VERSION: Option<&str> = option_env!("CARGO_PKG_VERSION");
let mut form = HashMap::new();
form.insert("image", image.to_string());
let res = c
.client
.post(api_url!("image"))
.header("Authorization", format!("Client-ID {}", c.client_id))
.header(
"User-Agent",
format!("Imgurs/{:?}", VERSION.unwrap_or("unknown")),
)
.form(&form)
.send()
.await
.map_err(|err| err.to_string())?;
let status = res.status();
if status.is_client_error() || status.is_server_error() {
let body = res.text().await.map_err(|err| err.to_string())?;
Err(format!(
"server returned non-successful status code = {status}. body = {body}"
))
} else {
let content: ImageInfo = res.json().await.map_err(|err| err.to_string())?;
Ok(content)
}
}

View File

@ -1,28 +0,0 @@
use imgurs::api::rate_limit::*;
use imgurs::api::ImgurHandle;
use log::{error, info};
use chrono::prelude::DateTime;
use chrono::Utc;
use std::time::{Duration, UNIX_EPOCH};
pub async fn credits(client: ImgurHandle) {
match rate_limit(client).await {
Ok(i) => {
let d = UNIX_EPOCH + Duration::from_secs(i.data.user_reset.try_into().unwrap());
let datetime = DateTime::<Utc>::from(d);
let timestamp_str = datetime.format("%Y-%m-%d %H:%M:%S").to_string();
info!("User Limit {}", i.data.user_limit);
info!("User Remaining {}", i.data.user_remaining);
info!("User Reset {} (UTC)", timestamp_str);
info!("Client Limit {}", i.data.client_limit);
info!("Client Remaining {}", i.data.client_remaining);
}
Err(e) => {
error!("{}", e);
}
}
}

View File

@ -1,16 +0,0 @@
use imgurs::api;
use imgurs::api::configuration::ImgurHandle;
use log::{error, info};
pub async fn delete_image(client: ImgurHandle, delete_hash: String) {
match api::delete_image::delete_image(client, delete_hash).await {
Ok(i) => {
info!("{i}")
}
Err(e) => {
error!("{}", e);
}
}
}

View File

@ -1,26 +0,0 @@
use imgurs::api::{configuration::ImgurHandle, get_image::get_image};
use super::print_image_info;
use base64;
use log::error;
use std::fs;
use std::path::Path;
pub async fn image_info(client: ImgurHandle, path: &str) {
let mut image: String = path.to_string();
if Path::new(path).exists() {
let bytes = fs::read(path).map_err(|err| err.to_string()).unwrap();
image = base64::encode(bytes);
}
match get_image(client, &image).await {
Ok(i) => print_image_info(i),
Err(e) => {
error!("{e}");
}
}
}

View File

@ -1,47 +0,0 @@
pub mod credits;
pub mod delete_image;
pub mod info_image;
pub mod parse;
pub mod upload_image;
use chrono::prelude::DateTime;
use chrono::Utc;
use log::info;
use std::time::{Duration, UNIX_EPOCH};
use imgurs::api::ImageInfo;
pub fn print_image_info(i: ImageInfo) {
let d = UNIX_EPOCH + Duration::from_secs(i.data.datetime.try_into().unwrap());
let datetime = DateTime::<Utc>::from(d);
let timestamp_str = datetime.format("%Y-%m-%d %H:%M:%S").to_string();
if i.data.title != None {
info!(
"Title {}",
i.data.title.unwrap_or_else(|| "unknown".to_string())
);
}
if i.data.description != None {
info!(
"Description {}",
i.data.description.unwrap_or_else(|| "unknown".to_string())
);
}
if i.data.deletehash != None {
info!(
"Deletehash {}",
i.data.deletehash.unwrap_or_else(|| "unknown".to_string())
);
}
info!("ID {}", i.data.id);
info!("Upload Date {} (UTC)", timestamp_str);
info!("Type {}", i.data.img_type);
info!("Width {}", i.data.width);
info!("Height {}", i.data.height);
info!("File Size {} KB", i.data.size / 1000);
info!("Views {}", i.data.views);
info!("Bandwidth {}", i.data.bandwidth);
info!("Link {}", i.data.link);
}

View File

@ -1,62 +0,0 @@
use clap::{AppSettings, Parser, Subcommand};
use imgurs::api::configuration::ImgurHandle;
use crate::cli::{credits::*, delete_image::*, info_image::*, upload_image::*};
const VERSION: Option<&str> = option_env!("CARGO_PKG_VERSION");
const NAME: Option<&str> = option_env!("CARGO_PKG_NAME");
#[derive(Parser, Debug)]
#[clap(
name = NAME.unwrap_or("unknown"),
about = "Imgur API CLI", long_about = None,
version = VERSION.unwrap_or("unknown")
)]
struct Cli {
#[clap(subcommand)]
command: Commands,
}
#[derive(Subcommand, Debug)]
enum Commands {
#[clap(about = "Get API ratelimit")]
Credits,
#[clap(
setting(AppSettings::ArgRequiredElseHelp),
about = "Upload image to imgur"
)]
Upload { path: String },
#[clap(
setting(AppSettings::ArgRequiredElseHelp),
about = "Delete image from imgur"
)]
Delete { delete_hash: String },
#[clap(setting(AppSettings::ArgRequiredElseHelp), about = "Print image info")]
Info { id: String },
}
pub async fn parse(client: ImgurHandle) {
let args = Cli::parse();
match &args.command {
Commands::Credits => {
credits(client).await;
}
Commands::Upload { path } => {
upload_image(client, path).await;
}
Commands::Delete { delete_hash } => {
delete_image(client, delete_hash.to_string()).await;
}
Commands::Info { id } => {
image_info(client, id).await;
}
}
}

View File

@ -1,25 +0,0 @@
use super::print_image_info;
use imgurs::api::configuration::ImgurHandle;
use base64;
use log::error;
use std::fs;
use std::path::Path;
pub async fn upload_image(client: ImgurHandle, path: &str) {
let mut image: String = path.to_string();
if Path::new(path).exists() {
let bytes = fs::read(path).map_err(|err| err.to_string()).unwrap();
image = base64::encode(bytes);
}
match imgurs::api::upload_image::upload_image(client, &image).await {
Ok(i) => print_image_info(i),
Err(e) => {
error!("{e}");
}
}
}

View File

@ -1 +0,0 @@
pub mod toml;

View File

@ -1,25 +0,0 @@
use dirs::config_dir;
use serde_derive::Deserialize;
use std::fs::read_to_string;
use toml::from_str;
#[derive(Debug, Deserialize)]
pub struct Config {
pub imgur: ConfigImgur,
}
#[derive(Debug, Deserialize)]
pub struct ConfigImgur {
pub id: String,
}
pub fn parse() -> Result<Config, String> {
let config_dir = config_dir().unwrap();
let file_dir: String = String::from(config_dir.to_string_lossy()) + "/imgur/config.toml";
let toml_str = read_to_string(file_dir).map_err(|err| err.to_string())?;
let decode = from_str(&toml_str).map_err(|err| err.to_string())?;
Ok(decode)
}

36
src/error.rs Normal file
View File

@ -0,0 +1,36 @@
use thiserror::Error;
/// Client Errors
#[derive(Debug, Error)]
pub enum Error {
/// Imgur API returned non-successful status code
#[error("server reponse non-successful status code - {0}, body = `{1}`")]
ApiError(u16, String),
/// Imgur API returned non-successful status code (body is too long)
#[error("server reponse non-successful status code - {0}, (response body is too long)")]
ApiErrorBodyTooLong(u16),
/// Invalid file path or URL adress
#[error("{0} is not url or file path")]
InvalidUrlOrFile(String),
/// Imgur API error or reqwest::Error
#[error("send request to imgur api: {0}")]
SendApiRequest(reqwest::Error),
/// std::io::Error
#[error("io error - {0}")]
IoError(std::io::Error),
}
impl From<reqwest::Error> for Error {
fn from(err: reqwest::Error) -> Self {
Error::SendApiRequest(err)
}
}
impl From<std::io::Error> for Error {
fn from(err: std::io::Error) -> Self {
Error::IoError(err)
}
}
/// A `Result` alias where the `Err` case is `imgurs::Error`
pub type Result<T> = std::result::Result<T, Error>;

69
src/imgur/album_type.rs Normal file
View File

@ -0,0 +1,69 @@
use serde::{Deserialize, Serialize};
use crate::ImageInfoData;
/// Album Info Response
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
pub struct AlbumInfo {
/// Image Data
pub data: AlbumInfoData,
/// Request processed success or not.
pub success: bool,
/// HTTP status code from API request.
pub status: i32,
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
pub struct AlbumInfoData {
/// Album ID
pub id: String,
/// Title of the album
pub title: Option<String>,
/// Description of the album
pub description: Option<String>,
pub datetime: i64,
pub cover: String,
pub cover_edited: Option<String>,
pub cover_width: i64,
pub cover_height: i64,
pub account_url: Option<String>,
pub account_id: Option<AccountId>,
pub privacy: String,
pub layout: String,
pub views: i64,
/// Album link
pub link: String,
pub favorite: bool,
pub nsfw: bool,
pub section: Option<String>,
pub images_count: i64,
pub in_gallery: bool,
pub is_ad: bool,
pub include_album_ads: bool,
pub is_album: bool,
pub images: Vec<ImageInfoData>,
pub ad_config: AdConfig,
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
#[serde(untagged)]
pub enum AccountId {
String(String),
Int(i64),
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
pub struct AdConfig {
#[serde(rename = "safeFlags")]
pub safe_flags: Vec<String>,
#[serde(rename = "highRiskFlags")]
pub high_risk_flags: Vec<String>,
#[serde(rename = "unsafeFlags")]
pub unsafe_flags: Vec<String>,
#[serde(rename = "wallUnsafeFlags")]
pub wall_unsafe_flags: Vec<String>,
#[serde(rename = "showsAds")]
pub shows_ads: bool,
#[serde(rename = "showAdLevel")]
pub show_ad_level: i64,
}

View File

@ -1,28 +1,25 @@
use reqwest::Client;
use std::fmt;
macro_rules! api_url (
($path: expr) => (
format!("{}{}", "https://api.imgur.com/3/", $path)
);
);
pub(crate) use api_url;
use std::fmt;
pub struct ImgurHandle {
pub(crate) use api_url;
use reqwest::Client;
/// Imgur Client
#[derive(Clone)]
pub struct ImgurClient {
/// Imgur API Client ID
pub client_id: String,
/// HTTP Client
pub client: Client,
}
impl fmt::Debug for ImgurHandle {
impl fmt::Debug for ImgurClient {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "ImgurClient - client_id: {}", self.client_id)
}
}
impl ImgurHandle {
pub fn new(client_id: String) -> Self {
let client = Client::new();
ImgurHandle { client_id, client }
}
}

48
src/imgur/image_type.rs Normal file
View File

@ -0,0 +1,48 @@
use serde::{Deserialize, Serialize};
/// Image Info Response
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
pub struct ImageInfo {
/// Image Data
pub data: ImageInfoData,
/// Request processed success or not.
pub success: bool,
/// HTTP status code from API request.
pub status: i32,
}
/// Image Info Reponse (`data` json)
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
pub struct ImageInfoData {
/// Image ID
/// e.g. `iDYNKJq`
pub id: String,
/// Image title
pub title: Option<String>,
/// Description of this image
pub description: Option<String>,
/// Image uploaded time
pub datetime: i32,
/// Image type
/// e.g. `image/png`
#[serde(rename = "type")]
pub img_type: String,
/// If image if animated (gif, etc)
pub animated: bool,
/// Width of this image
pub width: i32,
/// Height of this image
pub height: i32,
/// Image size in bytes
pub size: i32,
/// Unique image views
pub views: i32,
/// Bandwidth used by this image
pub bandwidth: i64,
/// If image is added to favorite
pub favorite: bool,
/// Delete hash (only show after image upload)
pub deletehash: Option<String>,
/// Link of this image
pub link: String,
}

148
src/imgur/mod.rs Normal file
View File

@ -0,0 +1,148 @@
mod album_type;
mod client;
mod image_type;
mod requests;
mod send_api_request;
pub use album_type::*;
pub(crate) use client::api_url;
pub use client::ImgurClient;
pub use image_type::*;
pub use send_api_request::*;
use url::Url;
use crate::{Error, Result};
impl ImgurClient {
/// Create a new Imgur Client
/// ```
/// use imgurs::ImgurClient;
///
/// let client = ImgurClient::new("3e3ce0d7ac14d56");
/// ```
pub fn new(client_id: &str) -> Self {
let client_id = client_id.to_string();
let client = reqwest::Client::new();
ImgurClient { client_id, client }
}
/// Create a new Imgur Client with the provided `reqwest::Client`
///
/// This allows for customization of the http client settings like timeout or the user agent.
/// ```
/// use imgurs::ImgurClient;
/// use reqwest::Client;
///
/// let http_client = Client::builder().build().unwrap();
///
/// let client = ImgurClient::with_http_client("3e3ce0d7ac14d56", http_client);
/// ```
pub fn with_http_client(client_id: &str, http_client: reqwest::Client) -> Self {
let client_id = client_id.to_string();
ImgurClient {
client_id,
client: http_client,
}
}
/// Upload image to Imgur
/// ```
/// use imgurs::ImgurClient;
///
/// #[tokio::main]
/// async fn main() {
/// let client = ImgurClient::new("3e3ce0d7ac14d56");
///
/// client
/// .upload_image("https://i.imgur.com/lFaGr1x.png")
/// .await
/// .expect("upload image");
/// }
/// ```
pub async fn upload_image(&self, path: &str) -> Result<ImageInfo> {
use base64::prelude::{Engine, BASE64_STANDARD};
let mut image = path.to_string();
// check if the specified file exists if not then check if it is a url
if std::path::Path::new(path).exists() {
let bytes = std::fs::read(path)?;
image = BASE64_STANDARD.encode(bytes)
}
// validate url adress
else if Url::parse(path).is_err() {
Err(Error::InvalidUrlOrFile(path.to_string()))?;
}
requests::upload_image(self, image).await
}
/// Delete image from Imgur
/// ```
/// use imgurs::ImgurClient;
///
/// #[tokio::main]
/// async fn main() {
/// let client = ImgurClient::new("3e3ce0d7ac14d56");
///
/// let image = client
/// .upload_image("https://i.imgur.com/lFaGr1x.png")
/// .await
/// .expect("upload image");
/// let deletehash = image.data.deletehash.unwrap();
///
/// client
/// .delete_image(&deletehash)
/// .await
/// .expect("delete image");
/// }
/// ```
pub async fn delete_image(&self, delete_hash: &str) -> Result<()> {
requests::delete_image(self, delete_hash).await
}
/// Get Rame Limit of this Imgur Client
/// ```
/// use imgurs::ImgurClient;
///
/// #[tokio::main]
/// async fn main() {
/// let client = ImgurClient::new("3e3ce0d7ac14d56");
///
/// client.rate_limit().await.expect("get rate limit");
/// }
/// ```
pub async fn rate_limit(&self) -> Result<requests::RateLimitInfo> {
requests::rate_limit(self).await
}
/// Get image info from a Imgur
/// ```
/// use imgurs::ImgurClient;
///
/// #[tokio::main]
/// async fn main() {
/// let client = ImgurClient::new("3e3ce0d7ac14d56");
///
/// client.image_info("lFaGr1x").await.expect("delete image");
/// }
/// ```
pub async fn image_info(&self, id: &str) -> Result<ImageInfo> {
requests::get_image(self, id).await
}
/// Get album info from a Imgur
/// ```no_run
/// use imgurs::ImgurClient;
///
/// #[tokio::main]
/// async fn main() {
/// let client = ImgurClient::new("3e3ce0d7ac14d56");
///
/// client.album_info("id").await.expect("get album info");
/// }
/// ```
pub async fn album_info(&self, id: &str) -> Result<AlbumInfo> {
requests::get_album(self, id).await
}
}

View File

@ -0,0 +1,23 @@
use reqwest::Method;
use crate::{api_url, send_api_request, Error, ImgurClient, Result};
pub async fn delete_image(client: &ImgurClient, delete_hash: &str) -> Result<()> {
// get imgur api url
let uri = api_url!(format!("image/{delete_hash}"));
// send request to imgur api
let res = send_api_request(client, Method::DELETE, uri, None).await?;
// get response http code
let status = res.status();
// check if an error has occurred
if status.is_client_error() || status.is_server_error() {
let body = res.text().await?;
Err(Error::ApiError(status.as_u16(), body))?;
}
Ok(())
}

View File

@ -0,0 +1,24 @@
use reqwest::Method;
use crate::{api_url, send_api_request, AlbumInfo, Error, ImgurClient, Result};
pub async fn get_album(client: &ImgurClient, album: &str) -> Result<AlbumInfo> {
// get imgur api url
let uri = api_url!(format!("album/{album}"));
// send request to imgur api
let res = send_api_request(client, Method::GET, uri, None).await?;
// get response http code
let status = res.status();
// check if an error has occurred
if status.is_client_error() || status.is_server_error() {
let body = res.text().await?;
return Err(Error::ApiError(status.as_u16(), body));
}
// return `ImageInfo`
Ok(res.json().await?)
}

View File

@ -0,0 +1,24 @@
use reqwest::Method;
use crate::{api_url, send_api_request, Error, ImageInfo, ImgurClient, Result};
pub async fn get_image(client: &ImgurClient, image: &str) -> Result<ImageInfo> {
// get imgur api url
let uri = api_url!(format!("image/{image}"));
// send request to imgur api
let res = send_api_request(client, Method::GET, uri, None).await?;
// get response http code
let status = res.status();
// check if an error has occurred
if status.is_client_error() || status.is_server_error() {
let body = res.text().await?;
return Err(Error::ApiError(status.as_u16(), body));
}
// return `ImageInfo`
Ok(res.json().await?)
}

11
src/imgur/requests/mod.rs Normal file
View File

@ -0,0 +1,11 @@
mod delete_image;
mod get_album;
mod get_image;
mod rate_limit;
mod upload_image;
pub use delete_image::*;
pub use get_album::*;
pub use get_image::*;
pub use rate_limit::*;
pub use upload_image::*;

View File

@ -0,0 +1,46 @@
use reqwest::Method;
use serde::{Deserialize, Serialize};
use crate::{api_url, send_api_request, Error, ImgurClient, Result};
#[derive(Debug, Serialize, Deserialize)]
pub struct RateLimitInfo {
pub data: RateLimitData,
pub success: bool,
pub status: i32,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct RateLimitData {
#[serde(rename = "UserLimit")]
pub user_limit: i32,
#[serde(rename = "UserRemaining")]
pub user_remaining: i32,
#[serde(rename = "UserReset")]
pub user_reset: i32,
#[serde(rename = "ClientLimit")]
pub client_limit: i32,
#[serde(rename = "ClientRemaining")]
pub client_remaining: i32,
}
pub async fn rate_limit(client: &ImgurClient) -> Result<RateLimitInfo> {
// get imgur api url
let uri = api_url!("credits");
// send request to imgur api
let res = send_api_request(client, Method::GET, uri, None).await?;
// get response http code
let status = res.status();
// check if an error has occurred
if status.is_client_error() || status.is_server_error() {
let body = res.text().await?;
return Err(Error::ApiError(status.as_u16(), body));
}
// return `RateLimitInfo`
Ok(res.json().await?)
}

View File

@ -0,0 +1,36 @@
use std::collections::HashMap;
use reqwest::Method;
use crate::{api_url, send_api_request, Error, ImageInfo, ImgurClient, Result};
pub async fn upload_image(client: &ImgurClient, image: String) -> Result<ImageInfo> {
// create http form (hashmap)
let mut form = HashMap::new();
// insert image to form
form.insert("image", image);
// get imgur api url
let uri = api_url!("image");
// send request to imgur api
let res = send_api_request(client, Method::POST, uri, Some(form)).await?;
// get response http code
let status = res.status();
// check if an error has occurred
if status.is_client_error() || status.is_server_error() {
let body = res.text().await?;
// if body is too long do not return it (imgur sometimes returns whole Request)
if body.chars().count() > 50 {
Err(Error::ApiErrorBodyTooLong(status.as_u16()))?;
}
return Err(Error::ApiError(status.as_u16(), body));
}
// return `ImageInfo`
Ok(res.json().await?)
}

View File

@ -0,0 +1,38 @@
use std::collections::HashMap;
use reqwest::{Method, Response};
use crate::{ImgurClient, Result};
/// Send request to a Imgur API
pub async fn send_api_request(
config: &ImgurClient,
method: Method,
uri: String,
form: Option<HashMap<&str, String>>,
) -> Result<Response> {
// get http client
let client = &config.client;
// create Request buidler
let mut req = client.request(method, uri.as_str());
// add `Authorization` and `User-Agent` to Request
req = req
.header("Authorization", format!("Client-ID {}", config.client_id))
.header(
"User-Agent",
format!("Imgur/{:?}", env!("CARGO_PKG_VERSION")),
);
// if exists add HashMap to Request
if form.is_some() {
req = req.form(&form.unwrap())
}
// build Request
let req = req.build()?;
// send Request
Ok(client.execute(req).await?)
}

View File

@ -1 +1,102 @@
pub mod api;
//! [![github]](https://github.com/M3DZIK/imgurs)
//! [![crates-io]](https://crates.io/crates/imgurs)
//! [![docs-rs]](https://docs.rs/imgurs)
//!
//! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
//! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust
//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs
//!
//! This crate is an unofficial implementation of the [Imgur API](https://imgur.com) in Rust.
//!
//! # Installation
//!
//! ## Requirements
//! - Rust 1.58 (earlier versions are not tested (only the latest stable version is tested!))
//! - Network connection
//!
//! ## Importing
//! The driver is available on [crates.io](https://crates.io/crates/imgurs). To use the driver in
//! your application, simply add it to your project's `Cargo.toml`.
//! ```toml
//! [dependencies]
//! imgurs = "0.11.3"
//! ```
//!
//! # Example Usage
//!
//! ## Create new ImgurClient
//! ```
//! use imgurs::ImgurClient;
//!
//! let client = ImgurClient::new("client_id");
//! ```
//!
//! ## Image Upload
//! ```no_run
//! use imgurs::ImgurClient;
//!
//! #[tokio::main]
//! async fn main() {
//! let client = ImgurClient::new("client_id");
//!
//! // From URL
//! let info = client
//! .upload_image("https://i.imgur.com/lFaGr1x.png")
//! .await
//! .unwrap();
//! println!("{:?}", info);
//!
//! // From File
//! let info = client.upload_image("path/to/file.png").await.unwrap();
//! println!("{:?}", info);
//! }
//! ```
//!
//! ## Delete Image
//! ```no_run
//! use imgurs::ImgurClient;
//!
//! #[tokio::main]
//! async fn main() {
//! let client = ImgurClient::new("client_id");
//!
//! client.delete_image("delete_hash").await.unwrap(); // delete hash
//! }
//! ```
//!
//! ## Get Image Info
//! ```no_run
//! use imgurs::ImgurClient;
//!
//! #[tokio::main]
//! async fn main() {
//! let client = ImgurClient::new("client_id");
//!
//! let info = client.image_info("lFaGr1x").await.unwrap(); // image id
//!
//! println!("{:?}", info);
//! }
//! ```
//!
//! ## Get Client RateLimit
//! ```no_run
//! use imgurs::ImgurClient;
//!
//! #[tokio::main]
//! async fn main() {
//! let client = ImgurClient::new("client_id");
//!
//! let info = client.rate_limit().await.unwrap();
//!
//! println!("{:?}", info);
//! }
//! ```
mod error;
pub use error::*;
#[cfg(feature = "imgur")]
mod imgur;
#[cfg(feature = "imgur")]
pub use imgur::*;

View File

@ -1,24 +0,0 @@
mod cli;
mod config;
use cli::parse::parse;
use log::error;
use simple_logger::SimpleLogger;
use std::process::exit;
use imgurs::api::ImgurHandle;
#[tokio::main]
async fn main() {
SimpleLogger::new().init().unwrap();
let config = config::toml::parse().unwrap_or_else(|error| {
error!("Parse toml config: {}", error);
exit(1);
});
let client = ImgurHandle::new((&config.imgur.id).to_string());
parse(client).await;
}