diff --git a/Cargo.lock b/Cargo.lock index c6f773f..e0169a3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1577,6 +1577,7 @@ dependencies = [ "rocket_codegen", "rocket_http", "serde", + "serde_json", "state", "tempfile", "time 0.2.27", diff --git a/Cargo.toml b/Cargo.toml index 6cb8c7d..329d8d8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ log = "0.4.14" once_cell = "1.8.0" pulldown-cmark = "0.8.0" rand = "0.8.3" -rocket = "0.5.0-rc.1" +rocket = { version = "0.5.0-rc.1", features = ["json"] } rocket_dyn_templates = { version = "0.1.0-rc.1", features = ["tera"] } rocket_sync_db_pools = { version = "0.1.0-rc.1", features = ["diesel_postgres_pool"] } serde = { version = "1.0.126", features = ["derive"] } diff --git a/src/main.rs b/src/main.rs index 66af0dc..1b015d8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -24,7 +24,7 @@ mod models; mod routes; mod schema; -use crate::routes::{display_paste, index, insert_paste, raw_paste}; +use crate::routes::{api_language, display_paste, index, insert_paste, raw_paste}; use diesel::prelude::*; use rocket::fairing::AdHoc; use rocket::fs::{relative, FileServer}; @@ -63,6 +63,9 @@ async fn rocket() -> _ { .expect("database to be migrated"); rocket })) - .mount("/", routes![index, insert_paste, display_paste, raw_paste]) + .mount( + "/", + routes![api_language, index, insert_paste, display_paste, raw_paste], + ) .mount("/static", FileServer::from(relative!("static"))) } diff --git a/src/routes/api_language_route.rs b/src/routes/api_language_route.rs new file mode 100644 index 0000000..99e24e9 --- /dev/null +++ b/src/routes/api_language_route.rs @@ -0,0 +1,133 @@ +// pastebin.run +// Copyright (C) 2020-2021 Konrad Borowski +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +use crate::schema::{implementation_wrappers, implementations, languages}; +use crate::Db; +use diesel::prelude::*; +use rocket::response::Debug; +use rocket::serde::json::Json; +use serde::Serialize; + +#[derive(Queryable)] +struct Language { + id: i32, + hello_world: Option, +} + +#[derive(Serialize, Queryable)] +#[serde(rename_all = "camelCase")] +struct Wrapper { + identifier: String, + label: String, + is_asm: bool, + is_formatter: bool, +} + +#[derive(Identifiable, Queryable)] +struct Implementation { + id: i32, + label: String, +} + +#[derive(Associations, Identifiable, Queryable)] +#[belongs_to(Implementation)] +struct ImplementationWrapper { + id: i32, + implementation_id: i32, + identifier: String, + label: String, + is_asm: bool, + is_formatter: bool, +} + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +pub struct JsonLanguage { + hello_world: Option, + implementations: Vec, +} + +#[derive(Serialize)] +struct JsonImplementation { + label: String, + wrappers: Vec, +} + +#[get("/api/v0/language/")] +pub async fn api_language( + db: Db, + identifier: String, +) -> Result>, Debug> { + db.run(|conn| { + let language: Option = languages::table + .filter(languages::identifier.eq(identifier)) + .select((languages::language_id, languages::hello_world)) + .get_result(conn) + .optional()?; + Ok(if let Some(language) = language { + let implementations = implementations::table + .select((implementations::implementation_id, implementations::label)) + .filter(implementations::language_id.eq(language.id)) + .order(implementations::ordering) + .load(conn)?; + let implementation_wrappers = ImplementationWrapper::belonging_to(&implementations) + .select(( + implementation_wrappers::implementation_wrapper_id, + implementation_wrappers::implementation_id, + implementation_wrappers::identifier, + implementation_wrappers::label, + implementation_wrappers::is_asm, + implementation_wrappers::is_formatter, + )) + .order(implementation_wrappers::ordering) + .load(conn)?; + let implementations = implementation_wrappers + .grouped_by(&implementations) + .into_iter() + .zip(implementations) + .map(|(wrappers, implementation)| JsonImplementation { + label: implementation.label, + wrappers: wrappers + .into_iter() + .map( + |ImplementationWrapper { + identifier, + label, + is_asm, + is_formatter, + .. + }| { + Wrapper { + identifier, + label, + is_asm, + is_formatter, + } + }, + ) + .collect(), + }) + .collect(); + Some(Json(JsonLanguage { + implementations, + hello_world: language.hello_world, + })) + } else { + None + }) + }) + .await +} diff --git a/src/routes/mod.rs b/src/routes/mod.rs index 3100f81..6622481 100644 --- a/src/routes/mod.rs +++ b/src/routes/mod.rs @@ -14,11 +14,13 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . +mod api_language_route; mod display_paste_route; mod index_route; mod insert_paste_route; mod raw_paste_route; +pub use api_language_route::*; pub use display_paste_route::*; pub use index_route::*; pub use insert_paste_route::*;