From 1b82b0a7776aee31f553fb588be4fa9691592248 Mon Sep 17 00:00:00 2001
From: Fuwn
Date: Mon, 26 Apr 2021 15:42:39 -0700
Subject: major: :star:
---
.dockerignore | 1 +
.editorconfig | 9 +++
.envrc | 1 +
.gitignore | 11 ++++
.license_template | 2 +
.mergify.yml | 8 +++
Cargo.toml | 22 +++++++
Dockerfile | 18 ++++++
Procfile | 1 +
Rocket.toml | 2 +
contributing.md | 6 ++
default.nix | 19 ++++++
maintainers | 1 +
nix/rust.nix | 9 +++
nix/sources.json | 50 +++++++++++++++
nix/sources.nix | 174 ++++++++++++++++++++++++++++++++++++++++++++++++++++
readme.md | 30 +++++++++
rust-toolchain | 2 +
rust-toolchain.toml | 2 +
rustfmt.toml | 29 +++++++++
senpy-api.nix | 17 +++++
shell.nix | 9 +++
src/constants.rs | 8 +++
src/main.rs | 27 ++++++++
src/routes.rs | 78 +++++++++++++++++++++++
src/structures.rs | 28 +++++++++
src/utils.rs | 52 ++++++++++++++++
27 files changed, 616 insertions(+)
create mode 100644 .dockerignore
create mode 100644 .editorconfig
create mode 100644 .envrc
create mode 100644 .license_template
create mode 100644 .mergify.yml
create mode 100644 Cargo.toml
create mode 100644 Dockerfile
create mode 100644 Procfile
create mode 100644 Rocket.toml
create mode 100644 contributing.md
create mode 100644 default.nix
create mode 100644 maintainers
create mode 100644 nix/rust.nix
create mode 100644 nix/sources.json
create mode 100644 nix/sources.nix
create mode 100644 readme.md
create mode 100644 rust-toolchain
create mode 100644 rust-toolchain.toml
create mode 100644 rustfmt.toml
create mode 100644 senpy-api.nix
create mode 100644 shell.nix
create mode 100644 src/constants.rs
create mode 100644 src/main.rs
create mode 100644 src/routes.rs
create mode 100644 src/structures.rs
create mode 100644 src/utils.rs
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
diff --git a/.envrc b/.envrc
new file mode 100644
index 0000000..051d09d
--- /dev/null
+++ b/.envrc
@@ -0,0 +1 @@
+eval "$(lorri direnv)"
diff --git a/.gitignore b/.gitignore
index 088ba6b..683610a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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 "]
+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 (@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///archive/.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///archive/.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///archive/.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///archive/.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_ fetches specs of 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 = == ./.;
+ in
+ if builtins.hasAttr "nixpkgs" sources
+ then sourcesNixpkgs
+ else if hasNixpkgsPath && ! hasThisAsNixpkgsPath then
+ import {}
+ else
+ abort
+ ''
+ Please specify either (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 @@
+
+
senpy-api
+
+
+
+
+
+
+
+
+
+
+
+## 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 .
+for example; if a route is notated as "/api/v1/route?", 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?: get a list of all images that pertain to the language ""
+
+## 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 { Json(github_api().await.unwrap()) }
+
+#[get("/languages")]
+pub async fn languages() -> Json> { Json(filter_languages().await) }
+
+#[get("/language?")]
+pub async fn language(lang: Option) -> Json> {
+ // 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 {
+ 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,
+ 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 {
+ 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::()
+ .await?,
+ )
+}
+
+pub async fn filter_languages() -> Vec {
+ 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 {
+ 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
+}
--
cgit v1.2.3