<commit_before>//! Endpoints for managing a per user list of followed crates

use diesel::associations::Identifiable;

use crate::controllers::frontend_prelude::*;
use crate::db::DieselPooledConn;
use crate::models::{Crate, Follow};
use crate::schema::*;

fn follow_target(
    req: &dyn RequestExt,
    conn: &DieselPooledConn<'_>,
    user_id: i32,
) -> AppResult<Follow> {
    let crate_name = &req.params()["crate_id"];
    let crate_id = Crate::by_name(crate_name)
        .select(crates::id)
        .first(&**conn)?;
    Ok(Follow { user_id, crate_id })
}

/// Handles the `PUT /crates/:crate_id/follow` route.
pub fn follow(req: &mut dyn RequestExt) -> EndpointResult {
    let user_id = req.authenticate()?.user_id();
    let conn = req.db_conn()?;
    let follow = follow_target(req, &conn, user_id)?;
    diesel::insert_into(follows::table)
        .values(&follow)
        .on_conflict_do_nothing()
        .execute(&*conn)?;

    ok_true()
}

/// Handles the `DELETE /crates/:crate_id/follow` route.
pub fn unfollow(req: &mut dyn RequestExt) -> EndpointResult {
    let user_id = req.authenticate()?.user_id();
    let conn = req.db_conn()?;
    let follow = follow_target(req, &conn, user_id)?;
    diesel::delete(&follow).execute(&*conn)?;

    ok_true()
}

/// Handles the `GET /crates/:crate_id/following` route.
pub fn following(req: &mut dyn RequestExt) -> EndpointResult {
    use diesel::dsl::exists;

    let user_id = req.authenticate()?.forbid_api_token_auth()?.user_id();
    let conn = req.db_read_only()?;
    let follow = follow_target(req, &conn, user_id)?;
    let following = diesel::select(exists(follows::table.find(follow.id()))).get_result(&*conn)?;

    #[derive(Serialize)]
    struct R {
        following: bool,
    }
    Ok(req.json(&R { following }))
}<commit_msg>controllers::krate::follow: Use `json!` macro to simplify JSON serialization code<commit_after>//! Endpoints for managing a per user list of followed crates

use diesel::associations::Identifiable;

use crate::controllers::frontend_prelude::*;
use crate::db::DieselPooledConn;
use crate::models::{Crate, Follow};
use crate::schema::*;

fn follow_target(
    req: &dyn RequestExt,
    conn: &DieselPooledConn<'_>,
    user_id: i32,
) -> AppResult<Follow> {
    let crate_name = &req.params()["crate_id"];
    let crate_id = Crate::by_name(crate_name)
        .select(crates::id)
        .first(&**conn)?;
    Ok(Follow { user_id, crate_id })
}

/// Handles the `PUT /crates/:crate_id/follow` route.
pub fn follow(req: &mut dyn RequestExt) -> EndpointResult {
    let user_id = req.authenticate()?.user_id();
    let conn = req.db_conn()?;
    let follow = follow_target(req, &conn, user_id)?;
    diesel::insert_into(follows::table)
        .values(&follow)
        .on_conflict_do_nothing()
        .execute(&*conn)?;

    ok_true()
}

/// Handles the `DELETE /crates/:crate_id/follow` route.
pub fn unfollow(req: &mut dyn RequestExt) -> EndpointResult {
    let user_id = req.authenticate()?.user_id();
    let conn = req.db_conn()?;
    let follow = follow_target(req, &conn, user_id)?;
    diesel::delete(&follow).execute(&*conn)?;

    ok_true()
}

/// Handles the `GET /crates/:crate_id/following` route.
pub fn following(req: &mut dyn RequestExt) -> EndpointResult {
    use diesel::dsl::exists;

    let user_id = req.authenticate()?.forbid_api_token_auth()?.user_id();
    let conn = req.db_read_only()?;
    let follow = follow_target(req, &conn, user_id)?;
    let following =
        diesel::select(exists(follows::table.find(follow.id()))).get_result::<bool>(&*conn)?;

    Ok(req.json(&json!({ "following": following })))
}
{'fraction_non_alphanumeric': 0.16561384235100246, 'fraction_numerical': 0.0010985992859104642, 'mean_word_length': 3.989041095890411, 'pattern_counts': {'":': 1, '<': 8, '<?xml version=': 0, '>': 16, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2340161', 'n_tokens_mistral': 1260, 'n_tokens_neox': 1273, 'n_words': 266}