diff options
| author | Fuwn <[email protected]> | 2021-04-26 15:42:39 -0700 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2021-04-26 15:42:39 -0700 |
| commit | 1b82b0a7776aee31f553fb588be4fa9691592248 (patch) | |
| tree | d0a678e4a6735dfd92e92b1aab33a3447c3666ee | |
| parent | fmt: Change case (diff) | |
| download | api-1b82b0a7776aee31f553fb588be4fa9691592248.tar.xz api-1b82b0a7776aee31f553fb588be4fa9691592248.zip | |
major: :star:
| -rw-r--r-- | .dockerignore | 1 | ||||
| -rw-r--r-- | .editorconfig | 9 | ||||
| -rw-r--r-- | .envrc | 1 | ||||
| -rw-r--r-- | .gitignore | 11 | ||||
| -rw-r--r-- | .license_template | 2 | ||||
| -rw-r--r-- | .mergify.yml | 8 | ||||
| -rw-r--r-- | Cargo.toml | 22 | ||||
| -rw-r--r-- | Dockerfile | 18 | ||||
| -rw-r--r-- | Procfile | 1 | ||||
| -rw-r--r-- | Rocket.toml | 2 | ||||
| -rw-r--r-- | contributing.md | 6 | ||||
| -rw-r--r-- | default.nix | 19 | ||||
| -rw-r--r-- | maintainers | 1 | ||||
| -rw-r--r-- | nix/rust.nix | 9 | ||||
| -rw-r--r-- | nix/sources.json | 50 | ||||
| -rw-r--r-- | nix/sources.nix | 174 | ||||
| -rw-r--r-- | readme.md | 30 | ||||
| -rw-r--r-- | rust-toolchain | 2 | ||||
| -rw-r--r-- | rust-toolchain.toml | 2 | ||||
| -rw-r--r-- | rustfmt.toml | 29 | ||||
| -rw-r--r-- | senpy-api.nix | 17 | ||||
| -rw-r--r-- | shell.nix | 9 | ||||
| -rw-r--r-- | src/constants.rs | 8 | ||||
| -rw-r--r-- | src/main.rs | 27 | ||||
| -rw-r--r-- | src/routes.rs | 78 | ||||
| -rw-r--r-- | src/structures.rs | 28 | ||||
| -rw-r--r-- | src/utils.rs | 52 |
27 files changed, 616 insertions, 0 deletions
diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..b83d222 --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +/target/ diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..9d08a1a --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true @@ -0,0 +1 @@ +eval "$(lorri direnv)" @@ -8,3 +8,14 @@ Cargo.lock # These are backup files generated by rustfmt **/*.rs.bk + +# nix +result* + +# ide +/.idea/ +/senpy-api.iml + +# development +.env +/shell-dev.nix diff --git a/.license_template b/.license_template new file mode 100644 index 0000000..f8ef223 --- /dev/null +++ b/.license_template @@ -0,0 +1,2 @@ +// Copyleft {20\d{2}(-20\d{2})?} The Senpy Club +// SPDX-License-Identifier: GPL-3.0-only diff --git a/.mergify.yml b/.mergify.yml new file mode 100644 index 0000000..e8e3205 --- /dev/null +++ b/.mergify.yml @@ -0,0 +1,8 @@ +pull_request_rules: + - name: Automatic merge when required reviews are approved + conditions: + - base=develop + - "#approved-reviews-by>=2" + actions: + merge: + method: merge diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..25c3869 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "senpy-api" +version = "0.1.0" +authors = ["Fuwn <[email protected]>"] +edition = "2018" +#description = "" +readme = "README.md" +homepage = "http://github.com/senpy-club/api" +repository = "http://github.com/senpy-club/api" +license = "GPL-3.0-only" +keywords = ["rust", "anime", "senpy"] +publish = false + +[dependencies] +rocket = { version = "0.5.0-dev", git = "https://github.com/SergioBenitez/Rocket.git" } +rocket_contrib = { version = "0.5.0-dev", git = "https://github.com/SergioBenitez/Rocket.git" } +reqwest = { version = "0.11.3", features = ["json"] } +tokio = { version = "1.5.0", features = ["full"] } +serde = "1.0.125" +serde_derive = "1.0.125" +rand = "0.8.3" +dotenv = "0.15.0" diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..d7e3b91 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,18 @@ +# # NOTICE +# This Dockerfile is *probably* not stable as of 2021. 04. 26. +# +# Can you use it? Yes, but you probably shouldn't. + +FROM rustlang/rust:nightly-slim AS build + +WORKDIR /src/senpy-api + +COPY . . + +RUN cargo build --release + +FROM ubuntu:18.04 + +COPY --from=build /src/senpy-api/target/release/senpy-api /usr/local/bin/senpy-api + +CMD ["/usr/local/bin/senpy-api"] diff --git a/Procfile b/Procfile new file mode 100644 index 0000000..cb5ceb6 --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +web: ROCKET_PORT=$PORT ROCKET_KEEP_ALIVE=0 ./target/release/senpy-api diff --git a/Rocket.toml b/Rocket.toml new file mode 100644 index 0000000..d53c0a4 --- /dev/null +++ b/Rocket.toml @@ -0,0 +1,2 @@ +[release] +port = 8084 diff --git a/contributing.md b/contributing.md new file mode 100644 index 0000000..b4aee98 --- /dev/null +++ b/contributing.md @@ -0,0 +1,6 @@ +# contribution guidelines +this document isn't very well detailed at the moment... + +## guidelines +1. at the moment, there is no github action in place to check if your pull request will compile, + please ensure that your changes **do** in-fact compile before submitting your pull request (s). diff --git a/default.nix b/default.nix new file mode 100644 index 0000000..4f4ca20 --- /dev/null +++ b/default.nix @@ -0,0 +1,19 @@ +{ system ? builtins.currentSystem }: + +let + sources = import ./nix/sources.nix; + pkgs = import sources.nixpkgs { }; + senpy-api = import ./senpy-api.nix { inherit sources pkgs; }; + + name = "senpy-club/api"; + tag = "latest"; +in pkgs.dockerTools.buildLayeredImage { + inherit name tag; + contents = [ senpy-api ]; + + config = { + Cmd = [ "/bin/senpy-api" ]; + Env = [ ]; + WorkingDir = "/"; + }; +} diff --git a/maintainers b/maintainers new file mode 100644 index 0000000..6fad053 --- /dev/null +++ b/maintainers @@ -0,0 +1 @@ +Fuwn <[email protected]> (@fuwn) diff --git a/nix/rust.nix b/nix/rust.nix new file mode 100644 index 0000000..77c9190 --- /dev/null +++ b/nix/rust.nix @@ -0,0 +1,9 @@ +{ sources ? import ./sources.nix }: + +let + pkgs = import sources.nixpkgs { overlays = [ (import sources.nixpkgs-mozilla) ]; }; + channel = "nightly"; + date = "2021-04-24"; + targets = [ ]; + chan = pkgs.rustChannelOfTargets channel date targets; +in chan diff --git a/nix/sources.json b/nix/sources.json new file mode 100644 index 0000000..33453bf --- /dev/null +++ b/nix/sources.json @@ -0,0 +1,50 @@ +{ + "naersk": { + "branch": "master", + "description": "Build rust crates in Nix. No configuration, no code generation, no IFD. Sandbox friendly.", + "homepage": "", + "owner": "nmattia", + "repo": "naersk", + "rev": "32e3ba39d9d83098b13720a4384bdda191dd0445", + "sha256": "0yxqggmj4c65nzmgjmwxii9ibxmxw9w87pbqpgmnyf5rpyp79nv7", + "type": "tarball", + "url": "https://github.com/nmattia/naersk/archive/32e3ba39d9d83098b13720a4384bdda191dd0445.tar.gz", + "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz" + }, + "niv": { + "branch": "master", + "description": "Easy dependency management for Nix projects", + "homepage": "https://github.com/nmattia/niv", + "owner": "nmattia", + "repo": "niv", + "rev": "af958e8057f345ee1aca714c1247ef3ba1c15f5e", + "sha256": "1qjavxabbrsh73yck5dcq8jggvh3r2jkbr6b5nlz5d9yrqm9255n", + "type": "tarball", + "url": "https://github.com/nmattia/niv/archive/af958e8057f345ee1aca714c1247ef3ba1c15f5e.tar.gz", + "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz" + }, + "nixpkgs": { + "branch": "release-20.03", + "description": "Nix Packages collection", + "homepage": "", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "6d1a044fc9ff3cc96fca5fa3ba9c158522bbf2a5", + "sha256": "07a3nyrj3pwl017ig0rbn5rbmbf14gl3vqggvkyrdby01726p5fg", + "type": "tarball", + "url": "https://github.com/NixOS/nixpkgs/archive/6d1a044fc9ff3cc96fca5fa3ba9c158522bbf2a5.tar.gz", + "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz" + }, + "nixpkgs-mozilla": { + "branch": "master", + "description": "mozilla related nixpkgs (extends nixos/nixpkgs repo)", + "homepage": "", + "owner": "mozilla", + "repo": "nixpkgs-mozilla", + "rev": "8c007b60731c07dd7a052cce508de3bb1ae849b4", + "sha256": "1zybp62zz0h077zm2zmqs2wcg3whg6jqaah9hcl1gv4x8af4zhs6", + "type": "tarball", + "url": "https://github.com/mozilla/nixpkgs-mozilla/archive/8c007b60731c07dd7a052cce508de3bb1ae849b4.tar.gz", + "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz" + } +} diff --git a/nix/sources.nix b/nix/sources.nix new file mode 100644 index 0000000..1938409 --- /dev/null +++ b/nix/sources.nix @@ -0,0 +1,174 @@ +# This file has been generated by Niv. + +let + + # + # The fetchers. fetch_<type> fetches specs of type <type>. + # + + fetch_file = pkgs: name: spec: + let + name' = sanitizeName name + "-src"; + in + if spec.builtin or true then + builtins_fetchurl { inherit (spec) url sha256; name = name'; } + else + pkgs.fetchurl { inherit (spec) url sha256; name = name'; }; + + fetch_tarball = pkgs: name: spec: + let + name' = sanitizeName name + "-src"; + in + if spec.builtin or true then + builtins_fetchTarball { name = name'; inherit (spec) url sha256; } + else + pkgs.fetchzip { name = name'; inherit (spec) url sha256; }; + + fetch_git = name: spec: + let + ref = + if spec ? ref then spec.ref else + if spec ? branch then "refs/heads/${spec.branch}" else + if spec ? tag then "refs/tags/${spec.tag}" else + abort "In git source '${name}': Please specify `ref`, `tag` or `branch`!"; + in + builtins.fetchGit { url = spec.repo; inherit (spec) rev; inherit ref; }; + + fetch_local = spec: spec.path; + + fetch_builtin-tarball = name: throw + ''[${name}] The niv type "builtin-tarball" is deprecated. You should instead use `builtin = true`. + $ niv modify ${name} -a type=tarball -a builtin=true''; + + fetch_builtin-url = name: throw + ''[${name}] The niv type "builtin-url" will soon be deprecated. You should instead use `builtin = true`. + $ niv modify ${name} -a type=file -a builtin=true''; + + # + # Various helpers + # + + # https://github.com/NixOS/nixpkgs/pull/83241/files#diff-c6f540a4f3bfa4b0e8b6bafd4cd54e8bR695 + sanitizeName = name: + ( + concatMapStrings (s: if builtins.isList s then "-" else s) + ( + builtins.split "[^[:alnum:]+._?=-]+" + ((x: builtins.elemAt (builtins.match "\\.*(.*)" x) 0) name) + ) + ); + + # The set of packages used when specs are fetched using non-builtins. + mkPkgs = sources: system: + let + sourcesNixpkgs = + import (builtins_fetchTarball { inherit (sources.nixpkgs) url sha256; }) { inherit system; }; + hasNixpkgsPath = builtins.any (x: x.prefix == "nixpkgs") builtins.nixPath; + hasThisAsNixpkgsPath = <nixpkgs> == ./.; + in + if builtins.hasAttr "nixpkgs" sources + then sourcesNixpkgs + else if hasNixpkgsPath && ! hasThisAsNixpkgsPath then + import <nixpkgs> {} + else + abort + '' + Please specify either <nixpkgs> (through -I or NIX_PATH=nixpkgs=...) or + add a package called "nixpkgs" to your sources.json. + ''; + + # The actual fetching function. + fetch = pkgs: name: spec: + + if ! builtins.hasAttr "type" spec then + abort "ERROR: niv spec ${name} does not have a 'type' attribute" + else if spec.type == "file" then fetch_file pkgs name spec + else if spec.type == "tarball" then fetch_tarball pkgs name spec + else if spec.type == "git" then fetch_git name spec + else if spec.type == "local" then fetch_local spec + else if spec.type == "builtin-tarball" then fetch_builtin-tarball name + else if spec.type == "builtin-url" then fetch_builtin-url name + else + abort "ERROR: niv spec ${name} has unknown type ${builtins.toJSON spec.type}"; + + # If the environment variable NIV_OVERRIDE_${name} is set, then use + # the path directly as opposed to the fetched source. + replace = name: drv: + let + saneName = stringAsChars (c: if isNull (builtins.match "[a-zA-Z0-9]" c) then "_" else c) name; + ersatz = builtins.getEnv "NIV_OVERRIDE_${saneName}"; + in + if ersatz == "" then drv else + # this turns the string into an actual Nix path (for both absolute and + # relative paths) + if builtins.substring 0 1 ersatz == "/" then /. + ersatz else /. + builtins.getEnv "PWD" + "/${ersatz}"; + + # Ports of functions for older nix versions + + # a Nix version of mapAttrs if the built-in doesn't exist + mapAttrs = builtins.mapAttrs or ( + f: set: with builtins; + listToAttrs (map (attr: { name = attr; value = f attr set.${attr}; }) (attrNames set)) + ); + + # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/lists.nix#L295 + range = first: last: if first > last then [] else builtins.genList (n: first + n) (last - first + 1); + + # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L257 + stringToCharacters = s: map (p: builtins.substring p 1 s) (range 0 (builtins.stringLength s - 1)); + + # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L269 + stringAsChars = f: s: concatStrings (map f (stringToCharacters s)); + concatMapStrings = f: list: concatStrings (map f list); + concatStrings = builtins.concatStringsSep ""; + + # https://github.com/NixOS/nixpkgs/blob/8a9f58a375c401b96da862d969f66429def1d118/lib/attrsets.nix#L331 + optionalAttrs = cond: as: if cond then as else {}; + + # fetchTarball version that is compatible between all the versions of Nix + builtins_fetchTarball = { url, name ? null, sha256 }@attrs: + let + inherit (builtins) lessThan nixVersion fetchTarball; + in + if lessThan nixVersion "1.12" then + fetchTarball ({ inherit url; } // (optionalAttrs (!isNull name) { inherit name; })) + else + fetchTarball attrs; + + # fetchurl version that is compatible between all the versions of Nix + builtins_fetchurl = { url, name ? null, sha256 }@attrs: + let + inherit (builtins) lessThan nixVersion fetchurl; + in + if lessThan nixVersion "1.12" then + fetchurl ({ inherit url; } // (optionalAttrs (!isNull name) { inherit name; })) + else + fetchurl attrs; + + # Create the final "sources" from the config + mkSources = config: + mapAttrs ( + name: spec: + if builtins.hasAttr "outPath" spec + then abort + "The values in sources.json should not have an 'outPath' attribute" + else + spec // { outPath = replace name (fetch config.pkgs name spec); } + ) config.sources; + + # The "config" used by the fetchers + mkConfig = + { sourcesFile ? if builtins.pathExists ./sources.json then ./sources.json else null + , sources ? if isNull sourcesFile then {} else builtins.fromJSON (builtins.readFile sourcesFile) + , system ? builtins.currentSystem + , pkgs ? mkPkgs sources system + }: rec { + # The sources, i.e. the attribute set of spec name to spec + inherit sources; + + # The "pkgs" (evaluated nixpkgs) to use for e.g. non-builtin fetchers + inherit pkgs; + }; + +in +mkSources (mkConfig {}) // { __functor = _: settings: mkSources (mkConfig settings); } diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..81fe664 --- /dev/null +++ b/readme.md @@ -0,0 +1,30 @@ +<p align="center"> +<h1>senpy-api</h1> +</p> + +<p align="center"> +<a href="https://discord.com/invite/yWKgRT6" title="discord"> +<img src="https://img.shields.io/discord/246524734718738442" alt="discord"> +</a> +<a href="./license" title="license"> +<img src="https://img.shields.io/github/license/Whirlsplash/whirl" alt="license"> +</a> +</p> + +## notice +nix integration is currently broken. until [rocket](https://crates.io/crates/rocket) officially +releases version `0.5.0`, it will stay broken. + +## nix +- build: `nix-build senpy-api.nix` +- docker: `nix-build default.nix` + +## usage +- run (dev): `ROCKET_ENV=dev cargo run` +- build (prod): `ROCKET_ENV=prod cargo build --release` + +## contributing +please reference the [contribution guidelines](./contributing.md) of this repository. + +### license +[gnu general public license v3.0](https://github.com/senpy-club/api/blob/main/license) diff --git a/rust-toolchain b/rust-toolchain new file mode 100644 index 0000000..011b8ea --- /dev/null +++ b/rust-toolchain @@ -0,0 +1,2 @@ +[toolchain] +channel = "nightly-2021-04-24" diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..011b8ea --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "nightly-2021-04-24" diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..de1e0ce --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,29 @@ +condense_wildcard_suffixes = true +edition = "2018" +enum_discrim_align_threshold = 20 +error_on_line_overflow = true +error_on_unformatted = true +fn_single_line = true +force_multiline_blocks = true +format_code_in_doc_comments = true +format_macro_matchers = true +format_strings = true +imports_layout = "HorizontalVertical" +license_template_path = ".license_template" +match_arm_blocks = false +imports_granularity = "Crate" +newline_style = "Unix" +normalize_comments = true +normalize_doc_attributes = true +reorder_impl_items = true +group_imports = "StdExternalCrate" +reorder_modules = true +report_fixme = "Always" +report_todo = "Always" +struct_field_align_threshold = 20 +struct_lit_single_line = false +tab_spaces = 2 +use_field_init_shorthand = true +use_try_shorthand = true +where_single_line = true +wrap_comments = true diff --git a/senpy-api.nix b/senpy-api.nix new file mode 100644 index 0000000..a08a9b2 --- /dev/null +++ b/senpy-api.nix @@ -0,0 +1,17 @@ +{ sources ? import ./nix/sources.nix, pkgs ? import sources.nixpkgs { } }: + +let + rust = import ./nix/rust.nix { inherit sources; }; + + naersk = pkgs.callPackage sources.naersk { + rustc = rust; + cargo = rust; + }; + + src = builtins.filterSource + (path: type: type != "directory" || builtins.baseNameOf path != "target") + ./.; +in naersk.buildPackage { + inherit src; + remapPathPrefix = true; +} diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..2fbdb27 --- /dev/null +++ b/shell.nix @@ -0,0 +1,9 @@ +let + sources = import ./nix/sources.nix; + rust = import ./nix/rust.nix { inherit sources; }; + pkgs = import sources.nixpkgs { }; +in pkgs.mkShell { + buildInputs = with pkgs; [ rust ]; + + # GITHUB_TOKEN = ""; +} diff --git a/src/constants.rs b/src/constants.rs new file mode 100644 index 0000000..adbde90 --- /dev/null +++ b/src/constants.rs @@ -0,0 +1,8 @@ +// Copyleft 2021-2021 The Senpy Club +// SPDX-License-Identifier: GPL-3.0-only + +pub const GITHUB_USER_CONTENT: &str = + "https://raw.githubusercontent.com/laynH/Anime-Girls-Holding-Programming-Books/master/"; +pub const GITHUB_API_ENDPOINT: &str = "https://api.github.com/repos/laynH/Anime-Girls-Holding-Progr\ +amming-Books/git/trees/master?recursive=1"; +pub const USER_AGENT: &str = env!("CARGO_PKG_NAME"); diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..35af208 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,27 @@ +// Copyleft 2021-2021 The Senpy Club +// SPDX-License-Identifier: GPL-3.0-only + +#![feature(proc_macro_hygiene, decl_macro, type_ascription)] + +#[macro_use] +extern crate rocket; + +pub mod constants; +pub mod routes; +pub mod structures; +pub mod utils; + +#[launch] +fn rocket() -> _ { + dotenv::dotenv().ok(); + + rocket::build().mount("/", routes![routes::index]).mount( + "/api/v1", + routes![ + routes::github, + routes::languages, + routes::language, + routes::random + ], + ) +} diff --git a/src/routes.rs b/src/routes.rs new file mode 100644 index 0000000..e492404 --- /dev/null +++ b/src/routes.rs @@ -0,0 +1,78 @@ +// Copyleft 2021-2021 The Senpy Club +// SPDX-License-Identifier: GPL-3.0-only + +use rand::{thread_rng, Rng}; +use rocket_contrib::json::Json; + +use crate::{ + structures::{GitHubAPIResponse, SenpyRandom}, + utils::{filter_images_by_language, filter_languages, github_api}, +}; + +#[get("/")] +pub fn index() -> &'static str { + r#"# senpy-api +## routes +if a language requires a parameter, it will be notated like <this>. +for example; if a route is notated as "/api/v1/route?<parameter>", you can +access that route via the url +"http://this.domain/api/v1/route?parameter=something" + +- / + - /: index page (you are here) + +- /api/v1 + - /github: github api mirror + - /languages: a list of all languages that appear in _the_ repository + - /language?<lang>: get a list of all images that pertain to the language "<lang>" + +## notes +### rate-limit (s) +there aren't any rate-limits or whatnot on api usage but don't abuse it, it only takes one bad +apple to spoil the lot. + +### contributing +if you'd like to support the project in anyway, check out the repository! +https://github.com/senpy-club/api + +### supporting +if you would like to support my development ventures, visit my github profile here :3 +https://github.com/fuwn + +### license +gnu general public license v3.0 (gpl-3.0-only) +https://github.com/senpy-club/api/blob/main/LICENSE"# +} + +#[get("/github")] +pub async fn github() -> Json<GitHubAPIResponse> { Json(github_api().await.unwrap()) } + +#[get("/languages")] +pub async fn languages() -> Json<Vec<String>> { Json(filter_languages().await) } + +#[get("/language?<lang>")] +pub async fn language(lang: Option<String>) -> Json<Vec<String>> { + // lang.map(async |lang| Json(filter_images_by_language(lang).await)) + // .unwrap_or_else(|| Json(vec!["invalid language or no language + // specified".to_string()])); + + return if lang.is_none() { + Json(vec!["invalid language or no language specified".to_string()]) + } else { + Json(filter_images_by_language(lang.unwrap()).await) + }; +} + +#[get("/random")] +pub async fn random() -> Json<SenpyRandom> { + let filtered_languages = filter_languages().await; + let random_language = + &filtered_languages[thread_rng().gen_range(0..filtered_languages.len() - 1)]; + let filtered_images = filter_images_by_language(random_language.clone().to_owned()).await; + let random_image = &filtered_images[thread_rng().gen_range(0..filtered_images.len() - 1)]; + + Json(SenpyRandom { + language: random_language.clone().to_owned(), + image: random_image.clone().to_owned(), + }) +} diff --git a/src/structures.rs b/src/structures.rs new file mode 100644 index 0000000..2a85111 --- /dev/null +++ b/src/structures.rs @@ -0,0 +1,28 @@ +// Copyleft 2021-2021 The Senpy Club +// SPDX-License-Identifier: GPL-3.0-only + +use serde_derive::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug)] +pub struct GitHubAPIResponse { + pub sha: String, + pub url: String, + pub tree: Vec<GitHubAPIResponseTree>, + pub truncated: bool, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct GitHubAPIResponseTree { + pub path: String, + pub mode: String, + #[serde(rename = "type")] + pub _type: String, + pub sha: String, + pub url: String, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct SenpyRandom { + pub(crate) language: String, + pub image: String, +} diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..d51a813 --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,52 @@ +// Copyleft 2021-2021 The Senpy Club +// SPDX-License-Identifier: GPL-3.0-only + +use crate::{ + constants::{GITHUB_API_ENDPOINT, GITHUB_USER_CONTENT, USER_AGENT}, + structures::GitHubAPIResponse, +}; + +pub async fn github_api() -> Result<GitHubAPIResponse, reqwest::Error> { + Ok( + reqwest::Client::new() + .get(GITHUB_API_ENDPOINT) + .header("User-Agent", USER_AGENT) + .header( + "Authorization", + format!("token {}", std::env::var("GITHUB_TOKEN").unwrap()), + ) + .send() + .await? + .json::<GitHubAPIResponse>() + .await?, + ) +} + +pub async fn filter_languages() -> Vec<String> { + let mut languages = vec![]; + + for i in github_api().await.unwrap().tree { + if i._type == "tree" { + languages.push(i.path); + } + } + + languages +} + +pub async fn filter_images_by_language(language: String) -> Vec<String> { + let mut images = vec![]; + + for i in github_api().await.unwrap().tree { + // Example: + // "Language/Image.png" would become ["Language", "Image.png"] + + // TODO: Fix this with type_ascription + let x: Vec<&str> = i.path.split("/").collect(); + if x[0] == language && i.path.contains('/') { + images.push(format!("{}{}", GITHUB_USER_CONTENT, i.path)) + } + } + + images +} |