<commit_before>use iron::prelude::*;
use router::Router;
use hyper::status::StatusCode;
use params::{Params, FromValue};
use postgres::types::ToSql;
use std::str::FromStr;
use std::i32;

use super::request_body;
use ::api::authorization::Authorizer;
use ::api::ruleset::DEFAULT_RULESET_ID;
use ::proto::response::*;
use ::proto::error::*;
use ::proto::schema::*;
use ::db::schema::*;
use ::db::*;

pub fn get_hotels(req: &mut Request) -> IronResult<Response> {
    let ofst = req.get_ref::<Params>()
        .unwrap()
        .find(&["offset"])
        .map(|val| i32::from_value(val).unwrap_or(0))
        .unwrap_or(0);

    let cnt = req.extensions
        .get::<Router>()
        .unwrap()
        .find("cnt")
        .map(|s| i32::from_str(s).unwrap())
        .unwrap_or(i32::MAX);

    info!("request GET /hotels/{}?offset={}", cnt, ofst);

    let conn = get_db_connection();
    let hotels = conn.query(&Hotel::select_builder()
                   .limit(cnt)
                   .offset(ofst)
                   .build(),
               &[])
        .unwrap()
        .into_iter()
        .map(Hotel::from)
        .collect::<Vec<Hotel>>();

    Ok(hotels.as_response())
}

pub fn get_hotel(req: &mut Request) -> IronResult<Response> {
    let hotel_id = req.extensions
        .get::<Router>()
        .unwrap()
        .find("id")
        .map(|s| i32::from_str(s).unwrap())
        .expect("No Hotel ID in request");

    info!("request GET /hotel/{}", hotel_id);

    let conn = get_db_connection();
    let hotel = conn.query(&Hotel::select_builder()
                   .filter("ID = $1")
                   .build(),
               &[&hotel_id])
        .unwrap()
        .into_iter()
        .last()
        .map(Hotel::from);

    match hotel {
        Some(hotel) => Ok(hotel.as_response()),
        None => Err(NotFoundError::from_str(format!("No Hotel with id {} found", hotel_id)).into()),
    }
}

pub fn put_hotel(req: &mut Request) -> IronResult<Response> {
    let conn = get_db_connection();
    let user = Authorizer::authorize_request(&conn, req)?;

    if !user.roles.Owner {
        return Err(NotAuthorizedError::from_str("Only owner can add a hotel").into());
    }

    let new_hotel: NewHotel = request_body(req)?;

    info!("request PUT /hotel/ {{ {:?} }}", new_hotel);

    let hotel = Hotel::new(user.id,
                           DEFAULT_RULESET_ID,
                           new_hotel.CityID,
                           None,
                           new_hotel.Name,
                           new_hotel.Description,
                           None,
                           new_hotel.Stars);

    conn.execute(&Hotel::insert_query(), &hotel.insert_args())
        .unwrap();

    Ok(Response::with(StatusCode::Ok))
}

pub fn update_hotel(req: &mut Request) -> IronResult<Response> {
    let hotel_id = req.extensions
        .get::<Router>()
        .unwrap()
        .find("id")
        .map(|s| i32::from_str(s).unwrap())
        .expect("No Hotel ID in request");

    let conn = get_db_connection();
    let user = Authorizer::authorize_request(&conn, req)?;

    if !user.roles.Owner && !user.roles.Manager {
        return Err(NotAuthorizedError::from_str("Only owner and manager update hotel's info")
            .into());
    }

    let update_hotel: UpdateHotel = request_body(req)?;

    info!("request POST /hotel/ {{ {:?} }}", update_hotel);

    // Check authority in the hotel-to-update
    if !user.roles.Owns.map_or(false, |owns| owns.contains(&hotel_id)) &&
       !user.roles.EmployedIn.map_or(false, |emp| emp.contains(&hotel_id)) {
        return Err(NotAuthorizedError::from_str(format!("Not owner or employee of hotel {}",
                                                        hotel_id))
            .into());
    }

    let mut update = Hotel::update_builder().filter(format!("ID = {}", hotel_id));
    let mut values: Vec<&ToSql> = Vec::with_capacity(4);

    if let Some(id) = update_hotel.RuleSetID.as_ref() {
        update = update.set("RuleSetID");
        values.push(id);
    }

    if let Some(name) = update_hotel.Name.as_ref() {
        update = update.set("Name");
        values.push(name);
    }

    if let Some(desc) = update_hotel.Description.as_ref() {
        update = update.set("Description");
        values.push(desc);
    }

    if let Some(photoset) = update_hotel.PhotoSetID.as_ref() {
        update = update.set("PhotoSetID");
        values.push(photoset);
    }

    if let Some(stars) = update_hotel.Stars.as_ref() {
        update = update.set("Stars");
        values.push(stars);
    }

    // Early exit if we got empty json
    if values.is_empty() {
        return Ok(Response::with(StatusCode::Ok));
    }

    conn.execute(&update.build(), &values)
        .unwrap();

    Ok(Response::with(StatusCode::Ok))
}

pub fn get_rooms(req: &mut Request) -> IronResult<Response> {
    let ofst = req.get_ref::<Params>()
        .unwrap()
        .find(&["offset"])
        .map(|val| i32::from_value(val).unwrap_or(0))
        .unwrap_or(0);

    let hotel_id = req.extensions
        .get::<Router>()
        .unwrap()
        .find("id")
        .map(|s| i32::from_str(s).unwrap())
        .expect("No Hotel ID in request");

    let cnt = req.extensions
        .get::<Router>()
        .unwrap()
        .find("cnt")
        .map(|s| i32::from_str(s).unwrap())
        .unwrap_or(i32::MAX);

    info!("request GET /hotel/{}/rooms/{}?offset={}",
          hotel_id,
          cnt,
          ofst);

    let conn = get_db_connection();
    let rooms = conn.query(&Room::select_builder()
                   .filter("HotelID = $1")
                   .limit(cnt)
                   .offset(ofst)
                   .build(),
               &[&hotel_id])
        .unwrap()
        .into_iter()
        .map(Room::from)
        .collect::<Vec<Room>>();

    Ok(rooms.as_response())
}

pub fn get_room(req: &mut Request) -> IronResult<Response> {
    let hotel_id = req.extensions
        .get::<Router>()
        .unwrap()
        .find("id")
        .map(|s| i32::from_str(s).unwrap())
        .expect("No Hotel ID in request");

    let room_number = req.extensions
        .get::<Router>()
        .unwrap()
        .find("number")
        .map(|s| i32::from_str(s).unwrap())
        .expect("No Room ID in request");

    info!("request GET /hotel/{}/room/{}", hotel_id, room_number);

    let conn = get_db_connection();
    let room = conn.query(&Room::select_builder()
                   .filter("HotelID = $1 and RoomNumber = $2")
                   .build(),
               &[&hotel_id, &room_number])
        .unwrap()
        .into_iter()
        .last()
        .map(Room::from);

    match room {
        Some(room) => Ok(room.as_response()),
        None => {
            Err(NotFoundError::from_str(format!("Room number {} not found in hotel {}",
                                               room_number,
                                               hotel_id))
                .into())
        }
    }
}

pub fn put_room(req: &mut Request) -> IronResult<Response> {
    let hotel_id = req.extensions
        .get::<Router>()
        .unwrap()
        .find("id")
        .map(|s| i32::from_str(s).unwrap())
        .expect("No Hotel ID in request");

    let conn = get_db_connection();
    let user = Authorizer::authorize_request(&conn, req)?;

    // Check authority
    if !user.roles.Owns.map_or(false, |owns| owns.contains(&hotel_id)) &&
       !user.roles.EmployedIn.map_or(false, |emp| emp.contains(&hotel_id)) {
        return Err(NotAuthorizedError::from_str(format!("Not owner or employee of hotel {}",
                                                        hotel_id))
            .into());
    }

    let new_room: NewRoom = request_body(req)?;

    info!("request PUT /hotel/{}/room/ {{ {:?} }}", hotel_id, new_room);

    let room = Room {
        HotelID: hotel_id,
        RoomNumber: new_room.RoomNumber,
        RoomLevelID: new_room.RoomLevelID,
        PhotoSetID: new_room.PhotoSetID,
    };

    conn.execute(&Room::insert_query(), &room.insert_args())
        .unwrap();
    Ok(Response::with(StatusCode::Ok))
}

pub fn update_room(req: &mut Request) -> IronResult<Response> {
    let hotel_id = req.extensions
        .get::<Router>()
        .unwrap()
        .find("id")
        .map(|s| i32::from_str(s).unwrap())
        .expect("No Hotel ID in request");

    let room_number = req.extensions
        .get::<Router>()
        .unwrap()
        .find("number")
        .map(|s| i32::from_str(s).unwrap())
        .expect("No Room ID in request");

    let conn = get_db_connection();
    let user = Authorizer::authorize_request(&conn, req)?;

    // Check authority
    if !user.roles.Owns.map_or(false, |owns| owns.contains(&hotel_id)) &&
       !user.roles.EmployedIn.map_or(false, |emp| emp.contains(&hotel_id)) {
        return Err(NotAuthorizedError::from_str(format!("Not owner or employee of hotel {}",
                                                       hotel_id))
            .into());
    }

    let upd_room: UpdateRoom = request_body(req)?;

    info!("request POST /hotel/{}/room/{} {{ {:?} }}",
          hotel_id,
          room_number,
          upd_room);

    let mut values: Vec<&ToSql> = Vec::new();
    let mut update = Room::update_builder()
        .filter(format!("HotelID = {} and RoomNumber = {}", hotel_id, room_number));

    if let Some(room_lvl_id) = upd_room.RoomLevelID.as_ref() {
        values.push(room_lvl_id);
        update = update.set("RoomLevelID");
    }

    if let Some(photo_set_id) = upd_room.PhotoSetID.as_ref() {
        values.push(photo_set_id);
        update = update.set("PhotoSetID");
    }

    conn.execute(&update.build(), &values)
        .unwrap();

    Ok(Response::with(StatusCode::Ok))
}

pub fn get_reviews(req: &mut Request) -> IronResult<Response> {
    let ofst = req.get_ref::<Params>()
        .unwrap()
        .find(&["offset"])
        .map(|val| i32::from_value(val).unwrap_or(0))
        .unwrap_or(0);

    let hotel_id = req.extensions
        .get::<Router>()
        .unwrap()
        .find("id")
        .map(|s| i32::from_str(s).unwrap())
        .expect("No Hotel ID in request");

    let cnt = req.extensions
        .get::<Router>()
        .unwrap()
        .find("cnt")
        .map(|s| i32::from_str(s).unwrap())
        .unwrap_or(i32::MAX);

    info!("request GET /hotel/{}/reviews/{}?offset={}",
          hotel_id,
          cnt,
          ofst);

    let conn = get_db_connection();
    let reviews = conn.query(&SelectQueryBuilder::default()
                   .columns("Review.*")
                   .from_tables("Review, Booking")
                   .filter("Booking.HotelID = $1 and Review.BookingID = Booking.ID")
                   .limit(cnt)
                   .offset(ofst)
                   .build(),
               &[&hotel_id])
        .unwrap()
        .into_iter()
        .map(Review::from)
        .collect::<Vec<Review>>();

    Ok(reviews.as_response())
}

pub fn get_employees(req: &mut Request) -> IronResult<Response> {
    let ofst = req.get_ref::<Params>()
        .unwrap()
        .find(&["offset"])
        .map(|val| i32::from_value(val).unwrap_or(0))
        .unwrap_or(0);

    let hotel_id = req.extensions
        .get::<Router>()
        .unwrap()
        .find("id")
        .map(|s| i32::from_str(s).unwrap())
        .expect("No Hotel ID in request");

    let cnt = req.extensions
        .get::<Router>()
        .unwrap()
        .find("cnt")
        .map(|s| i32::from_str(s).unwrap())
        .unwrap_or(i32::MAX);

    info!("request GET /hotel/{}/employees/{}?offset={}",
          hotel_id,
          cnt,
          ofst);

    let conn = get_db_connection();
    let persons = conn.query(&SelectQueryBuilder::default()
                   .columns("Person.*")
                   .from_tables("EmployedIn, Person")
                   .filter("EmployedIn.HotelID = $1 and EmployedIn.PersonID = Person.ID")
                   .limit(cnt)
                   .offset(ofst)
                   .build(),
               &[&hotel_id])
        .unwrap()
        .into_iter()
        .map(Person::from)
        .collect::<Vec<Person>>();

    let mut employees = Vec::with_capacity(persons.len());
    for person in persons.into_iter() {
        let roles = Authorizer::get_roles(&conn, person.ID)?;

        employees.push(Employee {
            Person: person,
            Roles: roles,
        });
    }

    Ok(employees.as_response())
}

pub fn fire_employee(req: &mut Request) -> IronResult<Response> {
    let hotel_id = req.extensions
        .get::<Router>()
        .unwrap()
        .find("id")
        .map(|s| i32::from_str(s).unwrap())
        .expect("No Hotel ID found in request");

    let emp_id = req.extensions
        .get::<Router>()
        .unwrap()
        .find("eid")
        .map(|s| i32::from_str(s).unwrap())
        .expect("No Employee ID found in request");

    let conn = get_db_connection();
    let user = Authorizer::authorize_request(&conn, req)?;

    info!("request DELETE /hotel/{}/employee/{}", hotel_id, emp_id);

    // Check authority in the hotel-to-update
    if !user.roles.Owns.map_or(false, |owns| owns.contains(&hotel_id)) {
        return Err(NotAuthorizedError::from_str(format!("Not owner or employee of hotel {}",
                                                       hotel_id))
            .into());
    }

    let emp_roles = Authorizer::get_roles(&conn, emp_id)?;

    if !emp_roles.EmployedIn.map_or(false, |employed_in| employed_in.contains(&emp_id)) {
        return Err(NotFoundError::from_str(format!("Person {} is not employed in {}",
                                                  emp_id,
                                                  hotel_id))
            .into());
    }

    conn.execute(&EmployedIn::delete_builder()
                     .filter("HotelID = $1, PersonID = $2")
                     .build(),
                 &[&hotel_id, &emp_id])
        .unwrap();

    Ok(Response::with(StatusCode::Ok))
}

pub fn get_ruleset(req: &mut Request) -> IronResult<Response> {
    unimplemented!()
}

pub fn update_ruleset(req: &mut Request) -> IronResult<Response> {
    unimplemented!()
}<commit_msg>Early exit condition on no update<commit_after>use iron::prelude::*;
use router::Router;
use hyper::status::StatusCode;
use params::{Params, FromValue};
use postgres::types::ToSql;
use std::str::FromStr;
use std::i32;

use super::request_body;
use ::api::authorization::Authorizer;
use ::api::ruleset::DEFAULT_RULESET_ID;
use ::proto::response::*;
use ::proto::error::*;
use ::proto::schema::*;
use ::db::schema::*;
use ::db::*;

pub fn get_hotels(req: &mut Request) -> IronResult<Response> {
    let ofst = req.get_ref::<Params>()
        .unwrap()
        .find(&["offset"])
        .map(|val| i32::from_value(val).unwrap_or(0))
        .unwrap_or(0);

    let cnt = req.extensions
        .get::<Router>()
        .unwrap()
        .find("cnt")
        .map(|s| i32::from_str(s).unwrap())
        .unwrap_or(i32::MAX);

    info!("request GET /hotels/{}?offset={}", cnt, ofst);

    let conn = get_db_connection();
    let hotels = conn.query(&Hotel::select_builder()
                   .limit(cnt)
                   .offset(ofst)
                   .build(),
               &[])
        .unwrap()
        .into_iter()
        .map(Hotel::from)
        .collect::<Vec<Hotel>>();

    Ok(hotels.as_response())
}

pub fn get_hotel(req: &mut Request) -> IronResult<Response> {
    let hotel_id = req.extensions
        .get::<Router>()
        .unwrap()
        .find("id")
        .map(|s| i32::from_str(s).unwrap())
        .expect("No Hotel ID in request");

    info!("request GET /hotel/{}", hotel_id);

    let conn = get_db_connection();
    let hotel = conn.query(&Hotel::select_builder()
                   .filter("ID = $1")
                   .build(),
               &[&hotel_id])
        .unwrap()
        .into_iter()
        .last()
        .map(Hotel::from);

    match hotel {
        Some(hotel) => Ok(hotel.as_response()),
        None => Err(NotFoundError::from_str(format!("No Hotel with id {} found", hotel_id)).into()),
    }
}

pub fn put_hotel(req: &mut Request) -> IronResult<Response> {
    let conn = get_db_connection();
    let user = Authorizer::authorize_request(&conn, req)?;

    if !user.roles.Owner {
        return Err(NotAuthorizedError::from_str("Only owner can add a hotel").into());
    }

    let new_hotel: NewHotel = request_body(req)?;

    info!("request PUT /hotel/ {{ {:?} }}", new_hotel);

    let hotel = Hotel::new(user.id,
                           DEFAULT_RULESET_ID,
                           new_hotel.CityID,
                           None,
                           new_hotel.Name,
                           new_hotel.Description,
                           None,
                           new_hotel.Stars);

    conn.execute(&Hotel::insert_query(), &hotel.insert_args())
        .unwrap();

    Ok(Response::with(StatusCode::Ok))
}

pub fn update_hotel(req: &mut Request) -> IronResult<Response> {
    let hotel_id = req.extensions
        .get::<Router>()
        .unwrap()
        .find("id")
        .map(|s| i32::from_str(s).unwrap())
        .expect("No Hotel ID in request");

    let conn = get_db_connection();
    let user = Authorizer::authorize_request(&conn, req)?;

    if !user.roles.Owner && !user.roles.Manager {
        return Err(NotAuthorizedError::from_str("Only owner and manager update hotel's info")
            .into());
    }

    let update_hotel: UpdateHotel = request_body(req)?;

    info!("request POST /hotel/ {{ {:?} }}", update_hotel);

    // Check authority in the hotel-to-update
    if !user.roles.Owns.map_or(false, |owns| owns.contains(&hotel_id)) &&
       !user.roles.EmployedIn.map_or(false, |emp| emp.contains(&hotel_id)) {
        return Err(NotAuthorizedError::from_str(format!("Not owner or employee of hotel {}",
                                                        hotel_id))
            .into());
    }

    let mut update = Hotel::update_builder().filter(format!("ID = {}", hotel_id));
    let mut values: Vec<&ToSql> = Vec::with_capacity(4);

    if let Some(id) = update_hotel.RuleSetID.as_ref() {
        update = update.set("RuleSetID");
        values.push(id);
    }

    if let Some(name) = update_hotel.Name.as_ref() {
        update = update.set("Name");
        values.push(name);
    }

    if let Some(desc) = update_hotel.Description.as_ref() {
        update = update.set("Description");
        values.push(desc);
    }

    if let Some(photoset) = update_hotel.PhotoSetID.as_ref() {
        update = update.set("PhotoSetID");
        values.push(photoset);
    }

    if let Some(stars) = update_hotel.Stars.as_ref() {
        update = update.set("Stars");
        values.push(stars);
    }

    // Early exit if we got empty json
    if values.is_empty() {
        return Ok(Response::with(StatusCode::Ok));
    }

    conn.execute(&update.build(), &values)
        .unwrap();

    Ok(Response::with(StatusCode::Ok))
}

pub fn get_rooms(req: &mut Request) -> IronResult<Response> {
    let ofst = req.get_ref::<Params>()
        .unwrap()
        .find(&["offset"])
        .map(|val| i32::from_value(val).unwrap_or(0))
        .unwrap_or(0);

    let hotel_id = req.extensions
        .get::<Router>()
        .unwrap()
        .find("id")
        .map(|s| i32::from_str(s).unwrap())
        .expect("No Hotel ID in request");

    let cnt = req.extensions
        .get::<Router>()
        .unwrap()
        .find("cnt")
        .map(|s| i32::from_str(s).unwrap())
        .unwrap_or(i32::MAX);

    info!("request GET /hotel/{}/rooms/{}?offset={}",
          hotel_id,
          cnt,
          ofst);

    let conn = get_db_connection();
    let rooms = conn.query(&Room::select_builder()
                   .filter("HotelID = $1")
                   .limit(cnt)
                   .offset(ofst)
                   .build(),
               &[&hotel_id])
        .unwrap()
        .into_iter()
        .map(Room::from)
        .collect::<Vec<Room>>();

    Ok(rooms.as_response())
}

pub fn get_room(req: &mut Request) -> IronResult<Response> {
    let hotel_id = req.extensions
        .get::<Router>()
        .unwrap()
        .find("id")
        .map(|s| i32::from_str(s).unwrap())
        .expect("No Hotel ID in request");

    let room_number = req.extensions
        .get::<Router>()
        .unwrap()
        .find("number")
        .map(|s| i32::from_str(s).unwrap())
        .expect("No Room ID in request");

    info!("request GET /hotel/{}/room/{}", hotel_id, room_number);

    let conn = get_db_connection();
    let room = conn.query(&Room::select_builder()
                   .filter("HotelID = $1 and RoomNumber = $2")
                   .build(),
               &[&hotel_id, &room_number])
        .unwrap()
        .into_iter()
        .last()
        .map(Room::from);

    match room {
        Some(room) => Ok(room.as_response()),
        None => {
            Err(NotFoundError::from_str(format!("Room number {} not found in hotel {}",
                                               room_number,
                                               hotel_id))
                .into())
        }
    }
}

pub fn put_room(req: &mut Request) -> IronResult<Response> {
    let hotel_id = req.extensions
        .get::<Router>()
        .unwrap()
        .find("id")
        .map(|s| i32::from_str(s).unwrap())
        .expect("No Hotel ID in request");

    let conn = get_db_connection();
    let user = Authorizer::authorize_request(&conn, req)?;

    // Check authority
    if !user.roles.Owns.map_or(false, |owns| owns.contains(&hotel_id)) &&
       !user.roles.EmployedIn.map_or(false, |emp| emp.contains(&hotel_id)) {
        return Err(NotAuthorizedError::from_str(format!("Not owner or employee of hotel {}",
                                                        hotel_id))
            .into());
    }

    let new_room: NewRoom = request_body(req)?;

    info!("request PUT /hotel/{}/room/ {{ {:?} }}", hotel_id, new_room);

    let room = Room {
        HotelID: hotel_id,
        RoomNumber: new_room.RoomNumber,
        RoomLevelID: new_room.RoomLevelID,
        PhotoSetID: new_room.PhotoSetID,
    };

    conn.execute(&Room::insert_query(), &room.insert_args())
        .unwrap();
    Ok(Response::with(StatusCode::Ok))
}

pub fn update_room(req: &mut Request) -> IronResult<Response> {
    let hotel_id = req.extensions
        .get::<Router>()
        .unwrap()
        .find("id")
        .map(|s| i32::from_str(s).unwrap())
        .expect("No Hotel ID in request");

    let room_number = req.extensions
        .get::<Router>()
        .unwrap()
        .find("number")
        .map(|s| i32::from_str(s).unwrap())
        .expect("No Room ID in request");

    let conn = get_db_connection();
    let user = Authorizer::authorize_request(&conn, req)?;

    // Check authority
    if !user.roles.Owns.map_or(false, |owns| owns.contains(&hotel_id)) &&
       !user.roles.EmployedIn.map_or(false, |emp| emp.contains(&hotel_id)) {
        return Err(NotAuthorizedError::from_str(format!("Not owner or employee of hotel {}",
                                                       hotel_id))
            .into());
    }

    let upd_room: UpdateRoom = request_body(req)?;

    info!("request POST /hotel/{}/room/{} {{ {:?} }}",
          hotel_id,
          room_number,
          upd_room);

    let mut values: Vec<&ToSql> = Vec::new();
    let mut update = Room::update_builder()
        .filter(format!("HotelID = {} and RoomNumber = {}", hotel_id, room_number));

    if let Some(room_lvl_id) = upd_room.RoomLevelID.as_ref() {
        values.push(room_lvl_id);
        update = update.set("RoomLevelID");
    }

    if let Some(photo_set_id) = upd_room.PhotoSetID.as_ref() {
        values.push(photo_set_id);
        update = update.set("PhotoSetID");
    }

    // Early exit if we got empty json
    if values.is_empty() {
        return Ok(Response::with(StatusCode::Ok));
    }

    conn.execute(&update.build(), &values)
        .unwrap();

    Ok(Response::with(StatusCode::Ok))
}

pub fn get_reviews(req: &mut Request) -> IronResult<Response> {
    let ofst = req.get_ref::<Params>()
        .unwrap()
        .find(&["offset"])
        .map(|val| i32::from_value(val).unwrap_or(0))
        .unwrap_or(0);

    let hotel_id = req.extensions
        .get::<Router>()
        .unwrap()
        .find("id")
        .map(|s| i32::from_str(s).unwrap())
        .expect("No Hotel ID in request");

    let cnt = req.extensions
        .get::<Router>()
        .unwrap()
        .find("cnt")
        .map(|s| i32::from_str(s).unwrap())
        .unwrap_or(i32::MAX);

    info!("request GET /hotel/{}/reviews/{}?offset={}",
          hotel_id,
          cnt,
          ofst);

    let conn = get_db_connection();
    let reviews = conn.query(&SelectQueryBuilder::default()
                   .columns("Review.*")
                   .from_tables("Review, Booking")
                   .filter("Booking.HotelID = $1 and Review.BookingID = Booking.ID")
                   .limit(cnt)
                   .offset(ofst)
                   .build(),
               &[&hotel_id])
        .unwrap()
        .into_iter()
        .map(Review::from)
        .collect::<Vec<Review>>();

    Ok(reviews.as_response())
}

pub fn get_employees(req: &mut Request) -> IronResult<Response> {
    let ofst = req.get_ref::<Params>()
        .unwrap()
        .find(&["offset"])
        .map(|val| i32::from_value(val).unwrap_or(0))
        .unwrap_or(0);

    let hotel_id = req.extensions
        .get::<Router>()
        .unwrap()
        .find("id")
        .map(|s| i32::from_str(s).unwrap())
        .expect("No Hotel ID in request");

    let cnt = req.extensions
        .get::<Router>()
        .unwrap()
        .find("cnt")
        .map(|s| i32::from_str(s).unwrap())
        .unwrap_or(i32::MAX);

    info!("request GET /hotel/{}/employees/{}?offset={}",
          hotel_id,
          cnt,
          ofst);

    let conn = get_db_connection();
    let persons = conn.query(&SelectQueryBuilder::default()
                   .columns("Person.*")
                   .from_tables("EmployedIn, Person")
                   .filter("EmployedIn.HotelID = $1 and EmployedIn.PersonID = Person.ID")
                   .limit(cnt)
                   .offset(ofst)
                   .build(),
               &[&hotel_id])
        .unwrap()
        .into_iter()
        .map(Person::from)
        .collect::<Vec<Person>>();

    let mut employees = Vec::with_capacity(persons.len());
    for person in persons.into_iter() {
        let roles = Authorizer::get_roles(&conn, person.ID)?;

        employees.push(Employee {
            Person: person,
            Roles: roles,
        });
    }

    Ok(employees.as_response())
}

pub fn fire_employee(req: &mut Request) -> IronResult<Response> {
    let hotel_id = req.extensions
        .get::<Router>()
        .unwrap()
        .find("id")
        .map(|s| i32::from_str(s).unwrap())
        .expect("No Hotel ID found in request");

    let emp_id = req.extensions
        .get::<Router>()
        .unwrap()
        .find("eid")
        .map(|s| i32::from_str(s).unwrap())
        .expect("No Employee ID found in request");

    let conn = get_db_connection();
    let user = Authorizer::authorize_request(&conn, req)?;

    info!("request DELETE /hotel/{}/employee/{}", hotel_id, emp_id);

    // Check authority in the hotel-to-update
    if !user.roles.Owns.map_or(false, |owns| owns.contains(&hotel_id)) {
        return Err(NotAuthorizedError::from_str(format!("Not owner or employee of hotel {}",
                                                       hotel_id))
            .into());
    }

    let emp_roles = Authorizer::get_roles(&conn, emp_id)?;

    if !emp_roles.EmployedIn.map_or(false, |employed_in| employed_in.contains(&emp_id)) {
        return Err(NotFoundError::from_str(format!("Person {} is not employed in {}",
                                                  emp_id,
                                                  hotel_id))
            .into());
    }

    conn.execute(&EmployedIn::delete_builder()
                     .filter("HotelID = $1, PersonID = $2")
                     .build(),
                 &[&hotel_id, &emp_id])
        .unwrap();

    Ok(Response::with(StatusCode::Ok))
}

pub fn get_ruleset(req: &mut Request) -> IronResult<Response> {
    unimplemented!()
}

pub fn update_ruleset(req: &mut Request) -> IronResult<Response> {
    unimplemented!()
}<|endoftext|>


<commit_before>use std::path::PathBuf;
use std::str::FromStr;
use std::process::exit;

enum Depth {
    Unlimited,
    Limited(usize)
}

impl Depth {
    fn accepts(&self, level: usize) -> bool {
        match *self {
            Depth::Unlimited => true,
            Depth::Limited(size) => size > level
        }
    }
}

impl FromStr for Depth {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, String> {
        if s == "all" { return Ok(Depth::Unlimited); }

        let number = match s.parse::<usize>() {
            Ok(number) => number,
            Err(_) => return Err("Not a positive integer".to_string())
        };

        if number > 0 {
            Ok(Depth::Limited(number))
        } else {
            Ok(Depth::Unlimited)
        }
    }
}

enum Limit {
    Unlimited,
    Limited(usize)
}

impl FromStr for Limit {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, String> {
        if s == "none" { return Ok(Limit::Unlimited); }

        let number = match s.parse::<usize>() {
            Ok(number) => number,
            Err(_) => return Err("Not a positive integer".to_string())
        };

        if number > 0 {
            Ok(Limit::Limited(number))
        } else {
            Ok(Limit::Unlimited)
        }
    }
}

pub struct Options {
    roots: Vec<String>,
    limit: Limit,
    depth: Depth,
    show_all: bool,
}

impl Options {
    pub fn roots(&self) -> Vec<PathBuf> {
        self.roots.iter().map(|root| PathBuf::from(&root)).collect()
    }

    pub fn depth_accepts(&self, level: usize) -> bool {
        self.depth.accepts(level)
    }

    pub fn limit_reached(&self, shown_entries: usize) -> bool {
        match self.limit {
            Limit::Unlimited => false,
            Limit::Limited(max) => max <= shown_entries
        }
    }

    pub fn should_show_hidden(&self) -> bool {
        self.show_all
    }
}

pub fn parse() -> Options {
    let matches = clap_app!(dutop =>
        (version: "0.1")
        (author: "Magnus Bergmark <magnus.bergmark@gmail.com>")
        (about: "Prints the largest entries in a directory")
        (usage: "dutop [OPTIONS] [--] [DIR [DIR...]]")

        (@arg DIR: ... "The directories to look in (defaults to current working directory).")

        (@arg limit:
            -n [LIMIT]
            {|value| {
                if value == "none" { return Ok(()); }
                let parsed = value.parse::<usize>();
                if parsed.is_ok() {
                    Ok(())
                } else {
                    Err("Limit needs to be a non-negative integer or \"none\".".to_string())
                }
            }}
            "The max number of children shown per directory. Defaults to 1. 0 or \"none\" means \
                no limit."
        )

        (@arg depth:
            -d --depth [DEPTH]
            {|value| {
                if value == "all" { return Ok(()); }
                let parsed = value.parse::<usize>();
                if parsed.is_ok() {
                    Ok(())
                } else {
                    Err("Depth must be a non-negative integer or \"all\".".to_string())
                }
            }}
            "The depth to recurse when printing out entries. Defaults to 1. 0 or \"all\" means \
                unlimited depth."
        )

        (@arg recursive:
            -r --recursive
            "Show the entire tree instead of just the direct children. This implies \
                unlimited --depth."
        )

        (@arg all:
            -a --all
            "Show hidden files and directories. They are always counted for the total sum."
        )
    ).get_matches();

    let roots = matches.values_of("DIR").unwrap_or(vec!["."]);

    let default_depth = if matches.is_present("recursive") {
        Depth::Unlimited
    } else {
        Depth::Limited(1)
    };

    let depth = match matches.is_present("depth") {
        true => {
            // We can unwrap since the argument is required. We'd never get here unless the value
            // exists.
            let value = matches.value_of("depth").unwrap();
            value.parse::<Depth>().unwrap_or_else(|error| {
                println!("Could not determine depth: {}", error);
                exit(2);
            })
        },
        false => default_depth
    };

    let limit = matches.value_of("limit").unwrap_or("1")
        .parse::<Limit>().unwrap_or_else(|error| {
            println!("Could not determine depth: {}", error);
            exit(2);
        });

    Options{
        roots: roots.iter().map(|value| value.to_string()).collect(),
        limit: limit,
        depth: depth,
        show_all: matches.is_present("all"),
    }
}
<commit_msg>Change limit alias to "all"; add simple tests<commit_after>use std::path::PathBuf;
use std::str::FromStr;
use std::process::exit;

enum Depth {
    Unlimited,
    Limited(usize)
}

impl Depth {
    fn accepts(&self, level: usize) -> bool {
        match *self {
            Depth::Unlimited => true,
            Depth::Limited(size) => size > level
        }
    }
}

impl FromStr for Depth {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, String> {
        if s == "all" { return Ok(Depth::Unlimited); }

        let number = match s.parse::<usize>() {
            Ok(number) => number,
            Err(_) => return Err("Not a positive integer or \"all\"".to_string())
        };

        if number > 0 {
            Ok(Depth::Limited(number))
        } else {
            Ok(Depth::Unlimited)
        }
    }
}

enum Limit {
    Unlimited,
    Limited(usize)
}

impl FromStr for Limit {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, String> {
        if s == "all" { return Ok(Limit::Unlimited); }

        let number = match s.parse::<usize>() {
            Ok(number) => number,
            Err(_) => return Err("Not a positive integer or \"all\"".to_string())
        };

        if number > 0 {
            Ok(Limit::Limited(number))
        } else {
            Ok(Limit::Unlimited)
        }
    }
}

pub struct Options {
    roots: Vec<String>,
    limit: Limit,
    depth: Depth,
    show_all: bool,
}

impl Options {
    pub fn roots(&self) -> Vec<PathBuf> {
        self.roots.iter().map(|root| PathBuf::from(&root)).collect()
    }

    pub fn depth_accepts(&self, level: usize) -> bool {
        self.depth.accepts(level)
    }

    pub fn limit_reached(&self, shown_entries: usize) -> bool {
        match self.limit {
            Limit::Unlimited => false,
            Limit::Limited(max) => max <= shown_entries
        }
    }

    pub fn should_show_hidden(&self) -> bool {
        self.show_all
    }
}

pub fn parse() -> Options {
    let matches = clap_app!(dutop =>
        (version: "0.1")
        (author: "Magnus Bergmark <magnus.bergmark@gmail.com>")
        (about: "Prints the largest entries in a directory")
        (usage: "dutop [OPTIONS] [--] [DIR [DIR...]]")

        (@arg DIR: ... "The directories to look in (defaults to current working directory).")

        (@arg limit:
            -n [LIMIT]
            {|value| {
                if value == "all" { return Ok(()); }
                let parsed = value.parse::<usize>();
                if parsed.is_ok() {
                    Ok(())
                } else {
                    Err("Limit needs to be a non-negative integer or \"all\".".to_string())
                }
            }}
            "The max number of children shown per directory. Defaults to 1. 0 or \"all\" means \
                no limit."
        )

        (@arg depth:
            -d --depth [DEPTH]
            {|value| {
                if value == "all" { return Ok(()); }
                let parsed = value.parse::<usize>();
                if parsed.is_ok() {
                    Ok(())
                } else {
                    Err("Depth must be a non-negative integer or \"all\".".to_string())
                }
            }}
            "The depth to recurse when printing out entries. Defaults to 1. 0 or \"all\" means \
                unlimited depth."
        )

        (@arg recursive:
            -r --recursive
            "Show the entire tree instead of just the direct children. This implies \
                unlimited --depth."
        )

        (@arg all:
            -a --all
            "Show hidden files and directories. They are always counted for the total sum."
        )
    ).get_matches();

    let roots = matches.values_of("DIR").unwrap_or(vec!["."]);

    let default_depth = if matches.is_present("recursive") {
        Depth::Unlimited
    } else {
        Depth::Limited(1)
    };

    let depth = match matches.is_present("depth") {
        true => {
            // We can unwrap since the argument is required. We'd never get here unless the value
            // exists.
            let value = matches.value_of("depth").unwrap();
            value.parse::<Depth>().unwrap_or_else(|error| {
                println!("Could not determine depth: {}", error);
                exit(2);
            })
        },
        false => default_depth
    };

    let limit = matches.value_of("limit").unwrap_or("1")
        .parse::<Limit>().unwrap_or_else(|error| {
            println!("Could not determine depth: {}", error);
            exit(2);
        });

    Options{
        roots: roots.iter().map(|value| value.to_string()).collect(),
        limit: limit,
        depth: depth,
        show_all: matches.is_present("all"),
    }
}

#[cfg(test)]
mod tests {
    use super::{Depth,Limit};

    #[test]
    fn it_parses_positive_depth_from_strings() {
        match "12".parse::<Depth>() {
            Ok(Depth::Limited(number)) => assert_eq!(number, 12),
            Ok(Depth::Unlimited) => panic!("Got an unlimited depth when parsing \"12\"!"),
            Err(error) => panic!("Could not parse \"12\" into a Depth, got {}", error)
        }
    }

    #[test]
    fn it_parses_zero_depth_from_strings() {
        match "0".parse::<Depth>() {
            Ok(Depth::Unlimited) => (),
            Ok(Depth::Limited(number)) => panic!("Got a Limited Depth on {}", number),
            Err(error) => panic!("Could not parse \"0\" into a Depth, got {}", error)
        }
    }

    #[test]
    fn it_parses_named_alias_for_unlimited_depth_from_strings() {
        match "all".parse::<Depth>() {
            Ok(Depth::Unlimited) => (),
            Ok(Depth::Limited(number)) => panic!("Got a Limited Depth on {}", number),
            Err(error) => panic!("Could not parse \"all\" into a Depth, got {}", error)
        }
    }

    #[test]
    #[should_panic]
    fn it_panics_on_broken_depth_strings() {
        "totally broken".parse::<Depth>().unwrap();
    }

    #[test]
    fn it_parses_positive_limit_from_strings() {
        match "12".parse::<Limit>() {
            Ok(Limit::Limited(number)) => assert_eq!(number, 12),
            Ok(Limit::Unlimited) => panic!("Got an unlimited limit when parsing \"12\"!"),
            Err(error) => panic!("Could not parse \"12\" into a Limit, got {}", error)
        }
    }

    #[test]
    fn it_parses_zero_limit_from_strings() {
        match "0".parse::<Limit>() {
            Ok(Limit::Unlimited) => (),
            Ok(Limit::Limited(number)) => panic!("Got a Limited Limit on {}", number),
            Err(error) => panic!("Could not parse \"0\" into a Limit, got {}", error)
        }
    }

    #[test]
    fn it_parses_named_alias_for_unlimited_limit_from_strings() {
        match "all".parse::<Limit>() {
            Ok(Limit::Unlimited) => (),
            Ok(Limit::Limited(number)) => panic!("Got a Limited Limit on {}", number),
            Err(error) => panic!("Could not parse \"all\" into a Limit, got {}", error)
        }
    }

    #[test]
    #[should_panic]
    fn it_panics_on_broken_limit_strings() {
        "totally broken".parse::<Limit>().unwrap();
    }
}
<|endoftext|>


<commit_before>use std;
use std::error::Error;
use std::io::Write;
use std::rc::Rc;
use std::result::Result;

use serde_yaml;

use super::traits::{ConvertResult, Converter, ImportResult, Importer};
use crate::build::Val;

pub struct YamlConverter {}

impl YamlConverter {
    pub fn new() -> Self {
        YamlConverter {}
    }

    fn convert_list(&self, items: &Vec<Rc<Val>>) -> std::io::Result<serde_yaml::Value> {
        let mut v = Vec::new();
        for val in items.iter() {
            v.push(self.convert_value(val)?);
        }
        Ok(serde_yaml::Value::Sequence(v))
    }

    fn convert_env(&self, items: &Vec<(String, String)>) -> std::io::Result<serde_yaml::Value> {
        let mut mp = serde_yaml::Mapping::new();
        for &(ref k, ref v) in items.iter() {
            mp.insert(
                serde_yaml::Value::String(k.clone()),
                serde_yaml::Value::String(v.clone()),
            );
        }
        Ok(serde_yaml::Value::Mapping(mp))
    }

    fn convert_tuple(&self, items: &Vec<(String, Rc<Val>)>) -> std::io::Result<serde_yaml::Value> {
        let mut mapping = serde_yaml::Mapping::new();
        for &(ref k, ref v) in items.iter() {
            mapping.insert(serde_yaml::Value::String(k.clone()), self.convert_value(v)?);
        }
        Ok(serde_yaml::Value::Mapping(mapping))
    }

    fn convert_value(&self, v: &Val) -> std::io::Result<serde_yaml::Value> {
        let yaml_val = match v {
            &Val::Boolean(b) => serde_yaml::Value::Bool(b),
            &Val::Empty => serde_yaml::Value::Null,
            &Val::Float(f) => match serde_yaml::to_value(f) {
                Ok(v) => v,
                _ => panic!("Float is too large or not a Number {}", f),
            },
            &Val::Int(i) => match serde_yaml::to_value(i) {
                Ok(v) => v,
                _ => panic!("Int is too large or not a Number {}", i),
            },
            &Val::Str(ref s) => serde_yaml::Value::String(s.clone()),
            &Val::Func(_) => {
                eprintln!("Skipping func encoding as null...");
                serde_yaml::Value::Null
            }
            &Val::Module(_) => {
                eprintln!("Skipping module encoding as null...");
                serde_yaml::Value::Null
            }
            &Val::Env(ref fs) => self.convert_env(fs)?,
            &Val::List(ref l) => self.convert_list(l)?,
            &Val::Tuple(ref t) => self.convert_tuple(t)?,
        };
        Ok(yaml_val)
    }

    fn convert_yaml_val(&self, v: &serde_yaml::Value) -> Result<Val, Box<dyn Error>> {
        Ok(match v {
            serde_yaml::Value::String(s) => Val::Str(s.clone()),
            serde_yaml::Value::Number(n) => {
                if let Some(i) = n.as_i64() {
                    Val::Int(i)
                } else {
                    Val::Float(n.as_f64().expect("Number was not an int or a float!!"))
                }
            }
            serde_yaml::Value::Bool(b) => Val::Boolean(*b),
            serde_yaml::Value::Null => Val::Empty,
            serde_yaml::Value::Sequence(l) => {
                let mut vs = Vec::with_capacity(l.len());
                for aval in l {
                    vs.push(Rc::new(self.convert_yaml_val(aval)?));
                }
                Val::List(vs)
            }
            serde_yaml::Value::Mapping(m) => {
                let mut fs = Vec::with_capacity(m.len());
                for (key, value) in m {
                    // This is a little gross but since yaml allows maps to be keyed
                    // by more than just a string it's necessary.
                    let key = match key {
                        serde_yaml::Value::Bool(b) => b.to_string(),
                        serde_yaml::Value::Null => "null".to_string(),
                        serde_yaml::Value::Number(n) => n.to_string(),
                        serde_yaml::Value::String(s) => s.clone(),
                        serde_yaml::Value::Sequence(_) | serde_yaml::Value::Mapping(_) => {
                            eprintln!("Unsupported key type in yaml import skipping");
                            continue;
                        }
                    };
                    eprintln!("yaml key is: {}", key);
                    fs.push((key, Rc::new(self.convert_yaml_val(value)?)));
                }
                Val::Tuple(fs)
            }
        })
    }

    fn write(&self, v: &Val, w: &mut Write) -> ConvertResult {
        let jsn_val = self.convert_value(v)?;
        serde_yaml::to_writer(w, &jsn_val)?;
        Ok(())
    }
}

impl Converter for YamlConverter {
    fn convert(&self, v: Rc<Val>, mut w: &mut Write) -> ConvertResult {
        self.write(&v, &mut w)
    }

    fn file_ext(&self) -> String {
        String::from("yaml")
    }

    fn description(&self) -> String {
        "Convert ucg Vals into valid yaml.".to_string()
    }

    #[allow(unused_must_use)]
    fn help(&self) -> String {
        include_str!("yaml_help.txt").to_string()
    }
}

impl Importer for YamlConverter {
    fn import(&self, bytes: &[u8]) -> ImportResult {
        let json_val = serde_yaml::from_slice(bytes)?;
        Ok(Rc::new(self.convert_yaml_val(&json_val)?))
    }
}
<commit_msg>DEV: Support the merge key syntax for yaml deserialization.<commit_after>use std;
use std::error::Error;
use std::io::Write;
use std::rc::Rc;
use std::result::Result;

use serde_yaml;

use super::traits::{ConvertResult, Converter, ImportResult, Importer};
use crate::build::Val;

pub struct YamlConverter {}

impl YamlConverter {
    pub fn new() -> Self {
        YamlConverter {}
    }

    fn convert_list(&self, items: &Vec<Rc<Val>>) -> std::io::Result<serde_yaml::Value> {
        let mut v = Vec::new();
        for val in items.iter() {
            v.push(self.convert_value(val)?);
        }
        Ok(serde_yaml::Value::Sequence(v))
    }

    fn convert_env(&self, items: &Vec<(String, String)>) -> std::io::Result<serde_yaml::Value> {
        let mut mp = serde_yaml::Mapping::new();
        for &(ref k, ref v) in items.iter() {
            mp.insert(
                serde_yaml::Value::String(k.clone()),
                serde_yaml::Value::String(v.clone()),
            );
        }
        Ok(serde_yaml::Value::Mapping(mp))
    }

    fn convert_tuple(&self, items: &Vec<(String, Rc<Val>)>) -> std::io::Result<serde_yaml::Value> {
        let mut mapping = serde_yaml::Mapping::new();
        for &(ref k, ref v) in items.iter() {
            mapping.insert(serde_yaml::Value::String(k.clone()), self.convert_value(v)?);
        }
        Ok(serde_yaml::Value::Mapping(mapping))
    }

    fn convert_value(&self, v: &Val) -> std::io::Result<serde_yaml::Value> {
        let yaml_val = match v {
            &Val::Boolean(b) => serde_yaml::Value::Bool(b),
            &Val::Empty => serde_yaml::Value::Null,
            &Val::Float(f) => match serde_yaml::to_value(f) {
                Ok(v) => v,
                _ => panic!("Float is too large or not a Number {}", f),
            },
            &Val::Int(i) => match serde_yaml::to_value(i) {
                Ok(v) => v,
                _ => panic!("Int is too large or not a Number {}", i),
            },
            &Val::Str(ref s) => serde_yaml::Value::String(s.clone()),
            &Val::Func(_) => {
                eprintln!("Skipping func encoding as null...");
                serde_yaml::Value::Null
            }
            &Val::Module(_) => {
                eprintln!("Skipping module encoding as null...");
                serde_yaml::Value::Null
            }
            &Val::Env(ref fs) => self.convert_env(fs)?,
            &Val::List(ref l) => self.convert_list(l)?,
            &Val::Tuple(ref t) => self.convert_tuple(t)?,
        };
        Ok(yaml_val)
    }

    fn merge_mapping_keys(&self, mut fs: &mut Vec<(String, Rc<Val>)>, m: &serde_yaml::Mapping) -> Result<(), Box<dyn Error>> {
        for (key, value) in m {
            // This is a little gross but since yaml allows maps to be keyed
            // by more than just a string it's necessary.
            let key = match key {
                serde_yaml::Value::Bool(b) => b.to_string(),
                serde_yaml::Value::Null => "null".to_string(),
                serde_yaml::Value::Number(n) => n.to_string(),
                serde_yaml::Value::String(s) => s.clone(),
                serde_yaml::Value::Sequence(_) | serde_yaml::Value::Mapping(_) => {
                    eprintln!("Unsupported key type in yaml import skipping");
                    continue;
                }
            };
            if key == "<<" {
                // TODO(jwall): Handle merge keys
                if let serde_yaml::Value::Mapping(merge_map) = value {
                    self.merge_mapping_keys(&mut fs, merge_map)?;
                }

            } else {
                fs.push((key, Rc::new(self.convert_yaml_val(&value)?)));
            }
        }
        Ok(())
    }

    fn convert_yaml_val(&self, v: &serde_yaml::Value) -> Result<Val, Box<dyn Error>> {
        Ok(match v {
            serde_yaml::Value::String(s) => Val::Str(s.clone()),
            serde_yaml::Value::Number(n) => {
                if let Some(i) = n.as_i64() {
                    Val::Int(i)
                } else {
                    Val::Float(n.as_f64().expect("Number was not an int or a float!!"))
                }
            }
            serde_yaml::Value::Bool(b) => Val::Boolean(*b),
            serde_yaml::Value::Null => Val::Empty,
            serde_yaml::Value::Sequence(l) => {
                let mut vs = Vec::with_capacity(l.len());
                for aval in l {
                    vs.push(Rc::new(self.convert_yaml_val(aval)?));
                }
                Val::List(vs)
            }
            serde_yaml::Value::Mapping(m) => {
                let mut fs = Vec::with_capacity(m.len());
                self.merge_mapping_keys(&mut fs, m)?;
                Val::Tuple(fs)
            }
        })
    }

    fn write(&self, v: &Val, w: &mut Write) -> ConvertResult {
        let jsn_val = self.convert_value(v)?;
        serde_yaml::to_writer(w, &jsn_val)?;
        Ok(())
    }
}

impl Converter for YamlConverter {
    fn convert(&self, v: Rc<Val>, mut w: &mut Write) -> ConvertResult {
        self.write(&v, &mut w)
    }

    fn file_ext(&self) -> String {
        String::from("yaml")
    }

    fn description(&self) -> String {
        "Convert ucg Vals into valid yaml.".to_string()
    }

    #[allow(unused_must_use)]
    fn help(&self) -> String {
        include_str!("yaml_help.txt").to_string()
    }
}

impl Importer for YamlConverter {
    fn import(&self, bytes: &[u8]) -> ImportResult {
        let json_val = serde_yaml::from_slice(bytes)?;
        Ok(Rc::new(self.convert_yaml_val(&json_val)?))
    }
}
<|endoftext|>


<commit_before>#![doc(html_root_url = "https://docs.rs/tokio-macros/0.2.0-alpha.1")]
#![warn(missing_debug_implementations, unreachable_pub, rust_2018_idioms)]
#![doc(test(no_crate_inject, attr(deny(rust_2018_idioms))))]

//! Macros for use with Tokio

extern crate proc_macro;

use proc_macro::TokenStream;
use quote::{quote, quote_spanned};
use syn::spanned::Spanned;

/// Marks async function to be executed by selected runtime.
///
/// ## Options:
///
/// - `single_thread` - Uses `current_thread`.
/// - `multi_thread` - Uses multi-threaded runtime. Used by default.
///
/// ## Usage
///
/// ### Select runtime
///
/// ```rust
///#![feature(async_await)]
///
/// #[tokio::main(single_thread)]
/// async fn main() {
///     println!("Hello world");
/// }
/// ```
/// ### Using default
///
/// ```rust
///#![feature(async_await)]
///
/// #[tokio::main]
/// async fn main() {
///     println!("Hello world");
/// }
/// ```
#[proc_macro_attribute]
#[cfg(not(test))] // Work around for rust-lang/rust#62127
pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {
    enum RuntimeType {
        Single,
        Multi,
    }

    let input = syn::parse_macro_input!(item as syn::ItemFn);
    let args = syn::parse_macro_input!(args as syn::AttributeArgs);

    let ret = &input.decl.output;
    let name = &input.ident;
    let body = &input.block;
    let attrs = &input.attrs;

    if input.asyncness.is_none() {
        let tokens = quote_spanned! { input.span() =>
          compile_error!("the async keyword is missing from the function declaration");
        };

        return TokenStream::from(tokens);
    } else if !input.decl.inputs.is_empty() {
        let tokens = quote_spanned! { input.span() =>
          compile_error!("the main function cannot accept arguments");
        };

        return TokenStream::from(tokens);
    }

    let mut runtime = RuntimeType::Multi;

    for arg in args {
        if let syn::NestedMeta::Meta(syn::Meta::Word(ident)) = arg {
            match ident.to_string().to_lowercase().as_str() {
                "multi_thread" => runtime = RuntimeType::Multi,
                "single_thread" => runtime = RuntimeType::Single,
                name => panic!("Unknown attribute {} is specified", name),
            }
        }
    }

    let result = match runtime {
        RuntimeType::Multi => quote! {
            #(#attrs)*
            fn #name() #ret {
                let mut rt = tokio::runtime::Runtime::new().unwrap();
                rt.block_on(async { #body })
            }
        },
        RuntimeType::Single => quote! {
            #(#attrs)*
            fn #name() #ret {
                let mut rt = tokio::runtime::current_thread::Runtime::new().unwrap();
                rt.block_on(async { #body })
            }
        },
    };

    result.into()
}

/// Marks async function to be executed by runtime, suitable to test enviornment
///
/// Uses `current_thread` runtime.
///
/// # Examples
///
/// ```no_run
/// #![feature(async_await)]
///
/// #[tokio::test]
/// async fn my_test() {
///     assert!(true);
/// }
/// ```
#[proc_macro_attribute]
pub fn test(_attr: TokenStream, item: TokenStream) -> TokenStream {
    let input = syn::parse_macro_input!(item as syn::ItemFn);

    let ret = &input.decl.output;
    let name = &input.ident;
    let body = &input.block;
    let attrs = &input.attrs;

    for attr in attrs {
        if attr.path.is_ident("test") {
            let tokens = quote_spanned! { input.span() =>
                compile_error!("second test attribute is supplied");
            };

            return TokenStream::from(tokens);
        }
    }

    if input.asyncness.is_none() {
        let tokens = quote_spanned! { input.span() =>
          compile_error!("the async keyword is missing from the function declaration");
        };

        return TokenStream::from(tokens);
    } else if !input.decl.inputs.is_empty() {
        let tokens = quote_spanned! { input.span() =>
          compile_error!("the test function cannot accept arguments");
        };

        return TokenStream::from(tokens);
    }

    let result = quote! {
        #[test]
        #(#attrs)*
        fn #name() #ret {
            let mut rt = tokio::runtime::current_thread::Runtime::new().unwrap();
            rt.block_on(async { #body })
        }
    };

    result.into()
}
<commit_msg>macros: improve error messages (#1420)<commit_after>#![doc(html_root_url = "https://docs.rs/tokio-macros/0.2.0-alpha.1")]
#![warn(missing_debug_implementations, unreachable_pub, rust_2018_idioms)]
#![doc(test(no_crate_inject, attr(deny(rust_2018_idioms))))]

//! Macros for use with Tokio

extern crate proc_macro;

use proc_macro::TokenStream;
use quote::quote;

/// Marks async function to be executed by selected runtime.
///
/// ## Options:
///
/// - `single_thread` - Uses `current_thread`.
/// - `multi_thread` - Uses multi-threaded runtime. Used by default.
///
/// ## Usage
///
/// ### Select runtime
///
/// ```rust
///#![feature(async_await)]
///
/// #[tokio::main(single_thread)]
/// async fn main() {
///     println!("Hello world");
/// }
/// ```
/// ### Using default
///
/// ```rust
///#![feature(async_await)]
///
/// #[tokio::main]
/// async fn main() {
///     println!("Hello world");
/// }
/// ```
#[proc_macro_attribute]
#[cfg(not(test))] // Work around for rust-lang/rust#62127
pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {
    enum RuntimeType {
        Single,
        Multi,
    }

    let input = syn::parse_macro_input!(item as syn::ItemFn);
    let args = syn::parse_macro_input!(args as syn::AttributeArgs);

    let ret = &input.decl.output;
    let name = &input.ident;
    let body = &input.block;
    let attrs = &input.attrs;

    if input.asyncness.is_none() {
        let msg = "the async keyword is missing from the function declaration";
        return syn::Error::new_spanned(input.decl.fn_token, msg)
            .to_compile_error()
            .into();
    } else if !input.decl.inputs.is_empty() {
        let msg = "the main function cannot accept arguments";
        return syn::Error::new_spanned(&input.decl.inputs, msg)
            .to_compile_error()
            .into();
    }

    let mut runtime = RuntimeType::Multi;

    for arg in args {
        if let syn::NestedMeta::Meta(syn::Meta::Word(ident)) = arg {
            match ident.to_string().to_lowercase().as_str() {
                "multi_thread" => runtime = RuntimeType::Multi,
                "single_thread" => runtime = RuntimeType::Single,
                name => {
                    let msg = format!("Unknown attribute {} is specified", name);
                    return syn::Error::new_spanned(ident, msg)
                        .to_compile_error()
                        .into();
                }
            }
        }
    }

    let result = match runtime {
        RuntimeType::Multi => quote! {
            #(#attrs)*
            fn #name() #ret {
                let mut rt = tokio::runtime::Runtime::new().unwrap();
                rt.block_on(async { #body })
            }
        },
        RuntimeType::Single => quote! {
            #(#attrs)*
            fn #name() #ret {
                let mut rt = tokio::runtime::current_thread::Runtime::new().unwrap();
                rt.block_on(async { #body })
            }
        },
    };

    result.into()
}

/// Marks async function to be executed by runtime, suitable to test enviornment
///
/// Uses `current_thread` runtime.
///
/// # Examples
///
/// ```no_run
/// #![feature(async_await)]
///
/// #[tokio::test]
/// async fn my_test() {
///     assert!(true);
/// }
/// ```
#[proc_macro_attribute]
pub fn test(_attr: TokenStream, item: TokenStream) -> TokenStream {
    let input = syn::parse_macro_input!(item as syn::ItemFn);

    let ret = &input.decl.output;
    let name = &input.ident;
    let body = &input.block;
    let attrs = &input.attrs;

    for attr in attrs {
        if attr.path.is_ident("test") {
            let msg = "second test attribute is supplied";
            return syn::Error::new_spanned(&attr, msg)
                .to_compile_error()
                .into();
        }
    }

    if input.asyncness.is_none() {
        let msg = "the async keyword is missing from the function declaration";
        return syn::Error::new_spanned(&input, msg)
            .to_compile_error()
            .into();
    } else if !input.decl.inputs.is_empty() {
        let msg = "the test function cannot accept arguments";
        return syn::Error::new_spanned(&input.decl.inputs, msg)
            .to_compile_error()
            .into();
    }

    let result = quote! {
        #[test]
        #(#attrs)*
        fn #name() #ret {
            let mut rt = tokio::runtime::current_thread::Runtime::new().unwrap();
            rt.block_on(async { #body })
        }
    };

    result.into()
}
<|endoftext|>


<commit_before>mod render;

#[cfg(test)]
mod tests;

use std::iter;

use either::Either;
use hir::{HasSource, Semantics};
use ide_db::{
    base_db::FileRange,
    defs::{Definition, IdentClass, OperatorClass},
    famous_defs::FamousDefs,
    helpers::pick_best_token,
    FxIndexSet, RootDatabase,
};
use itertools::Itertools;
use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxNode, SyntaxToken, T};

use crate::{
    doc_links::token_as_doc_comment,
    markup::Markup,
    runnables::{runnable_fn, runnable_mod},
    FileId, FilePosition, NavigationTarget, RangeInfo, Runnable, TryToNav,
};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct HoverConfig {
    pub links_in_hover: bool,
    pub documentation: Option<HoverDocFormat>,
    pub keywords: bool,
}

impl HoverConfig {
    fn markdown(&self) -> bool {
        matches!(self.documentation, Some(HoverDocFormat::Markdown))
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum HoverDocFormat {
    Markdown,
    PlainText,
}

#[derive(Debug, Clone)]
pub enum HoverAction {
    Runnable(Runnable),
    Implementation(FilePosition),
    Reference(FilePosition),
    GoToType(Vec<HoverGotoTypeData>),
}

impl HoverAction {
    fn goto_type_from_targets(db: &RootDatabase, targets: Vec<hir::ModuleDef>) -> Self {
        let targets = targets
            .into_iter()
            .filter_map(|it| {
                Some(HoverGotoTypeData {
                    mod_path: render::path(
                        db,
                        it.module(db)?,
                        it.name(db).map(|name| name.to_string()),
                    ),
                    nav: it.try_to_nav(db)?,
                })
            })
            .collect();
        HoverAction::GoToType(targets)
    }
}

#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct HoverGotoTypeData {
    pub mod_path: String,
    pub nav: NavigationTarget,
}

/// Contains the results when hovering over an item
#[derive(Debug, Default)]
pub struct HoverResult {
    pub markup: Markup,
    pub actions: Vec<HoverAction>,
}

// Feature: Hover
//
// Shows additional information, like the type of an expression or the documentation for a definition when "focusing" code.
// Focusing is usually hovering with a mouse, but can also be triggered with a shortcut.
//
// image::https://user-images.githubusercontent.com/48062697/113020658-b5f98b80-917a-11eb-9f88-3dbc27320c95.gif[]
pub(crate) fn hover(
    db: &RootDatabase,
    FileRange { file_id, range }: FileRange,
    config: &HoverConfig,
) -> Option<RangeInfo<HoverResult>> {
    let sema = &hir::Semantics::new(db);
    let file = sema.parse(file_id).syntax().clone();

    if !range.is_empty() {
        return hover_ranged(&file, range, sema, config);
    }
    let offset = range.start();

    let original_token = pick_best_token(file.token_at_offset(offset), |kind| match kind {
        IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] | T![super] | T![crate] | T![Self] => 4,
        // index and prefix ops
        T!['['] | T![']'] | T![?] | T![*] | T![-] | T![!] => 3,
        kind if kind.is_keyword() => 2,
        T!['('] | T![')'] => 2,
        kind if kind.is_trivia() => 0,
        _ => 1,
    })?;

    if let Some(doc_comment) = token_as_doc_comment(&original_token) {
        cov_mark::hit!(no_highlight_on_comment_hover);
        return doc_comment.get_definition_with_descend_at(sema, offset, |def, node, range| {
            let res = hover_for_definition(sema, file_id, def, &node, config)?;
            Some(RangeInfo::new(range, res))
        });
    }

    let in_attr = matches!(original_token.parent().and_then(ast::TokenTree::cast), Some(tt) if tt.syntax().ancestors().any(|it| ast::Meta::can_cast(it.kind())));
    // prefer descending the same token kind in attribute expansions, in normal macros text
    // equivalency is more important
    let descended = if in_attr {
        [sema.descend_into_macros_with_kind_preference(original_token.clone())].into()
    } else {
        sema.descend_into_macros_with_same_text(original_token.clone())
    };

    // FIXME: Definition should include known lints and the like instead of having this special case here
    let hovered_lint = descended.iter().find_map(|token| {
        let attr = token.parent_ancestors().find_map(ast::Attr::cast)?;
        render::try_for_lint(&attr, token)
    });
    if let Some(res) = hovered_lint {
        return Some(RangeInfo::new(original_token.text_range(), res));
    }

    let result = descended
        .iter()
        .filter_map(|token| {
            let node = token.parent()?;
            let class = IdentClass::classify_token(sema, token)?;
            if let IdentClass::Operator(OperatorClass::Await(_)) = class {
                // It's better for us to fall back to the keyword hover here,
                // rendering poll is very confusing
                return None;
            }
            Some(class.definitions().into_iter().zip(iter::once(node).cycle()))
        })
        .flatten()
        .unique_by(|&(def, _)| def)
        .filter_map(|(def, node)| hover_for_definition(sema, file_id, def, &node, config))
        .reduce(|mut acc: HoverResult, HoverResult { markup, actions }| {
            acc.actions.extend(actions);
            acc.markup = Markup::from(format!("{}\n---\n{}", acc.markup, markup));
            acc
        });

    if result.is_none() {
        // fallbacks, show keywords or types

        let res = descended.iter().find_map(|token| render::keyword(sema, config, token));
        if let Some(res) = res {
            return Some(RangeInfo::new(original_token.text_range(), res));
        }
        let res = descended
            .iter()
            .find_map(|token| hover_type_fallback(sema, config, token, &original_token));
        if let Some(_) = res {
            return res;
        }
    }
    result.map(|mut res: HoverResult| {
        res.actions = dedupe_or_merge_hover_actions(res.actions);
        RangeInfo::new(original_token.text_range(), res)
    })
}

pub(crate) fn hover_for_definition(
    sema: &Semantics<'_, RootDatabase>,
    file_id: FileId,
    definition: Definition,
    node: &SyntaxNode,
    config: &HoverConfig,
) -> Option<HoverResult> {
    let famous_defs = match &definition {
        Definition::BuiltinType(_) => Some(FamousDefs(sema, sema.scope(node)?.krate())),
        _ => None,
    };
    render::definition(sema.db, definition, famous_defs.as_ref(), config).map(|markup| {
        HoverResult {
            markup: render::process_markup(sema.db, definition, &markup, config),
            actions: show_implementations_action(sema.db, definition)
                .into_iter()
                .chain(show_fn_references_action(sema.db, definition))
                .chain(runnable_action(sema, definition, file_id))
                .chain(goto_type_action_for_def(sema.db, definition))
                .collect(),
        }
    })
}

fn hover_ranged(
    file: &SyntaxNode,
    range: syntax::TextRange,
    sema: &Semantics<'_, RootDatabase>,
    config: &HoverConfig,
) -> Option<RangeInfo<HoverResult>> {
    // FIXME: make this work in attributes
    let expr_or_pat = file.covering_element(range).ancestors().find_map(|it| {
        match_ast! {
            match it {
                ast::Expr(expr) => Some(Either::Left(expr)),
                ast::Pat(pat) => Some(Either::Right(pat)),
                _ => None,
            }
        }
    })?;
    let res = match &expr_or_pat {
        Either::Left(ast::Expr::TryExpr(try_expr)) => render::try_expr(sema, config, try_expr),
        Either::Left(ast::Expr::PrefixExpr(prefix_expr))
            if prefix_expr.op_kind() == Some(ast::UnaryOp::Deref) =>
        {
            render::deref_expr(sema, config, prefix_expr)
        }
        _ => None,
    };
    let res = res.or_else(|| render::type_info(sema, config, &expr_or_pat));
    res.map(|it| {
        let range = match expr_or_pat {
            Either::Left(it) => it.syntax().text_range(),
            Either::Right(it) => it.syntax().text_range(),
        };
        RangeInfo::new(range, it)
    })
}

fn hover_type_fallback(
    sema: &Semantics<'_, RootDatabase>,
    config: &HoverConfig,
    token: &SyntaxToken,
    original_token: &SyntaxToken,
) -> Option<RangeInfo<HoverResult>> {
    let node =
        token.parent_ancestors().take_while(|it| !ast::Item::can_cast(it.kind())).find(|n| {
            ast::Expr::can_cast(n.kind())
                || ast::Pat::can_cast(n.kind())
                || ast::Type::can_cast(n.kind())
        })?;

    let expr_or_pat = match_ast! {
        match node {
            ast::Expr(it) => Either::Left(it),
            ast::Pat(it) => Either::Right(it),
            // If this node is a MACRO_CALL, it means that `descend_into_macros_many` failed to resolve.
            // (e.g expanding a builtin macro). So we give up here.
            ast::MacroCall(_it) => return None,
            _ => return None,
        }
    };

    let res = render::type_info(sema, config, &expr_or_pat)?;
    let range = sema
        .original_range_opt(&node)
        .map(|frange| frange.range)
        .unwrap_or_else(|| original_token.text_range());
    Some(RangeInfo::new(range, res))
}

fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> {
    fn to_action(nav_target: NavigationTarget) -> HoverAction {
        HoverAction::Implementation(FilePosition {
            file_id: nav_target.file_id,
            offset: nav_target.focus_or_full_range().start(),
        })
    }

    let adt = match def {
        Definition::Trait(it) => return it.try_to_nav(db).map(to_action),
        Definition::Adt(it) => Some(it),
        Definition::SelfType(it) => it.self_ty(db).as_adt(),
        _ => None,
    }?;
    adt.try_to_nav(db).map(to_action)
}

fn show_fn_references_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> {
    match def {
        Definition::Function(it) => it.try_to_nav(db).map(|nav_target| {
            HoverAction::Reference(FilePosition {
                file_id: nav_target.file_id,
                offset: nav_target.focus_or_full_range().start(),
            })
        }),
        _ => None,
    }
}

fn runnable_action(
    sema: &hir::Semantics<'_, RootDatabase>,
    def: Definition,
    file_id: FileId,
) -> Option<HoverAction> {
    match def {
        Definition::Module(it) => runnable_mod(sema, it).map(HoverAction::Runnable),
        Definition::Function(func) => {
            let src = func.source(sema.db)?;
            if src.file_id != file_id.into() {
                cov_mark::hit!(hover_macro_generated_struct_fn_doc_comment);
                cov_mark::hit!(hover_macro_generated_struct_fn_doc_attr);
                return None;
            }

            runnable_fn(sema, func).map(HoverAction::Runnable)
        }
        _ => None,
    }
}

fn goto_type_action_for_def(db: &RootDatabase, def: Definition) -> Option<HoverAction> {
    let mut targets: Vec<hir::ModuleDef> = Vec::new();
    let mut push_new_def = |item: hir::ModuleDef| {
        if !targets.contains(&item) {
            targets.push(item);
        }
    };

    if let Definition::GenericParam(hir::GenericParam::TypeParam(it)) = def {
        it.trait_bounds(db).into_iter().for_each(|it| push_new_def(it.into()));
    } else {
        let ty = match def {
            Definition::Local(it) => it.ty(db),
            Definition::GenericParam(hir::GenericParam::ConstParam(it)) => it.ty(db),
            Definition::Field(field) => field.ty(db),
            Definition::Function(function) => function.ret_type(db),
            _ => return None,
        };

        walk_and_push_ty(db, &ty, &mut push_new_def);
    }

    Some(HoverAction::goto_type_from_targets(db, targets))
}

fn walk_and_push_ty(
    db: &RootDatabase,
    ty: &hir::Type,
    push_new_def: &mut dyn FnMut(hir::ModuleDef),
) {
    ty.walk(db, |t| {
        if let Some(adt) = t.as_adt() {
            push_new_def(adt.into());
        } else if let Some(trait_) = t.as_dyn_trait() {
            push_new_def(trait_.into());
        } else if let Some(traits) = t.as_impl_traits(db) {
            traits.for_each(|it| push_new_def(it.into()));
        } else if let Some(trait_) = t.as_associated_type_parent_trait(db) {
            push_new_def(trait_.into());
        }
    });
}

fn dedupe_or_merge_hover_actions(actions: Vec<HoverAction>) -> Vec<HoverAction> {
    let mut deduped_actions = Vec::with_capacity(actions.len());
    let mut go_to_type_targets = FxIndexSet::default();

    let mut seen_implementation = false;
    let mut seen_reference = false;
    let mut seen_runnable = false;
    for action in actions {
        match action {
            HoverAction::GoToType(targets) => {
                go_to_type_targets.extend(targets);
            }
            HoverAction::Implementation(..) => {
                if !seen_implementation {
                    seen_implementation = true;
                    deduped_actions.push(action);
                }
            }
            HoverAction::Reference(..) => {
                if !seen_reference {
                    seen_reference = true;
                    deduped_actions.push(action);
                }
            }
            HoverAction::Runnable(..) => {
                if !seen_runnable {
                    seen_runnable = true;
                    deduped_actions.push(action);
                }
            }
        };
    }

    if !go_to_type_targets.is_empty() {
        deduped_actions.push(HoverAction::GoToType(go_to_type_targets.into_iter().collect()));
    }

    deduped_actions
}
<commit_msg>fix: Fix hover in attributed items not preferring similar kinded tokens<commit_after>mod render;

#[cfg(test)]
mod tests;

use std::iter;

use either::Either;
use hir::{HasSource, Semantics};
use ide_db::{
    base_db::FileRange,
    defs::{Definition, IdentClass, OperatorClass},
    famous_defs::FamousDefs,
    helpers::pick_best_token,
    FxIndexSet, RootDatabase,
};
use itertools::Itertools;
use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxNode, SyntaxToken, T};

use crate::{
    doc_links::token_as_doc_comment,
    markup::Markup,
    runnables::{runnable_fn, runnable_mod},
    FileId, FilePosition, NavigationTarget, RangeInfo, Runnable, TryToNav,
};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct HoverConfig {
    pub links_in_hover: bool,
    pub documentation: Option<HoverDocFormat>,
    pub keywords: bool,
}

impl HoverConfig {
    fn markdown(&self) -> bool {
        matches!(self.documentation, Some(HoverDocFormat::Markdown))
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum HoverDocFormat {
    Markdown,
    PlainText,
}

#[derive(Debug, Clone)]
pub enum HoverAction {
    Runnable(Runnable),
    Implementation(FilePosition),
    Reference(FilePosition),
    GoToType(Vec<HoverGotoTypeData>),
}

impl HoverAction {
    fn goto_type_from_targets(db: &RootDatabase, targets: Vec<hir::ModuleDef>) -> Self {
        let targets = targets
            .into_iter()
            .filter_map(|it| {
                Some(HoverGotoTypeData {
                    mod_path: render::path(
                        db,
                        it.module(db)?,
                        it.name(db).map(|name| name.to_string()),
                    ),
                    nav: it.try_to_nav(db)?,
                })
            })
            .collect();
        HoverAction::GoToType(targets)
    }
}

#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct HoverGotoTypeData {
    pub mod_path: String,
    pub nav: NavigationTarget,
}

/// Contains the results when hovering over an item
#[derive(Debug, Default)]
pub struct HoverResult {
    pub markup: Markup,
    pub actions: Vec<HoverAction>,
}

// Feature: Hover
//
// Shows additional information, like the type of an expression or the documentation for a definition when "focusing" code.
// Focusing is usually hovering with a mouse, but can also be triggered with a shortcut.
//
// image::https://user-images.githubusercontent.com/48062697/113020658-b5f98b80-917a-11eb-9f88-3dbc27320c95.gif[]
pub(crate) fn hover(
    db: &RootDatabase,
    FileRange { file_id, range }: FileRange,
    config: &HoverConfig,
) -> Option<RangeInfo<HoverResult>> {
    let sema = &hir::Semantics::new(db);
    let file = sema.parse(file_id).syntax().clone();

    if !range.is_empty() {
        return hover_ranged(&file, range, sema, config);
    }
    let offset = range.start();

    let original_token = pick_best_token(file.token_at_offset(offset), |kind| match kind {
        IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] | T![super] | T![crate] | T![Self] => 4,
        // index and prefix ops
        T!['['] | T![']'] | T![?] | T![*] | T![-] | T![!] => 3,
        kind if kind.is_keyword() => 2,
        T!['('] | T![')'] => 2,
        kind if kind.is_trivia() => 0,
        _ => 1,
    })?;

    if let Some(doc_comment) = token_as_doc_comment(&original_token) {
        cov_mark::hit!(no_highlight_on_comment_hover);
        return doc_comment.get_definition_with_descend_at(sema, offset, |def, node, range| {
            let res = hover_for_definition(sema, file_id, def, &node, config)?;
            Some(RangeInfo::new(range, res))
        });
    }

    let in_attr = original_token
        .parent_ancestors()
        .filter_map(ast::Item::cast)
        .any(|item| sema.is_attr_macro_call(&item))
        && !matches!(
            original_token.parent().and_then(ast::TokenTree::cast),
            Some(tt) if tt.syntax().ancestors().any(|it| ast::Meta::can_cast(it.kind()))
        );
    // prefer descending the same token kind in attribute expansions, in normal macros text
    // equivalency is more important
    let descended = if in_attr {
        [sema.descend_into_macros_with_kind_preference(original_token.clone())].into()
    } else {
        sema.descend_into_macros_with_same_text(original_token.clone())
    };

    // FIXME: Definition should include known lints and the like instead of having this special case here
    let hovered_lint = descended.iter().find_map(|token| {
        let attr = token.parent_ancestors().find_map(ast::Attr::cast)?;
        render::try_for_lint(&attr, token)
    });
    if let Some(res) = hovered_lint {
        return Some(RangeInfo::new(original_token.text_range(), res));
    }

    let result = descended
        .iter()
        .filter_map(|token| {
            let node = token.parent()?;
            let class = IdentClass::classify_token(sema, token)?;
            if let IdentClass::Operator(OperatorClass::Await(_)) = class {
                // It's better for us to fall back to the keyword hover here,
                // rendering poll is very confusing
                return None;
            }
            Some(class.definitions().into_iter().zip(iter::once(node).cycle()))
        })
        .flatten()
        .unique_by(|&(def, _)| def)
        .filter_map(|(def, node)| hover_for_definition(sema, file_id, def, &node, config))
        .reduce(|mut acc: HoverResult, HoverResult { markup, actions }| {
            acc.actions.extend(actions);
            acc.markup = Markup::from(format!("{}\n---\n{}", acc.markup, markup));
            acc
        });

    if result.is_none() {
        // fallbacks, show keywords or types

        let res = descended.iter().find_map(|token| render::keyword(sema, config, token));
        if let Some(res) = res {
            return Some(RangeInfo::new(original_token.text_range(), res));
        }
        let res = descended
            .iter()
            .find_map(|token| hover_type_fallback(sema, config, token, &original_token));
        if let Some(_) = res {
            return res;
        }
    }
    result.map(|mut res: HoverResult| {
        res.actions = dedupe_or_merge_hover_actions(res.actions);
        RangeInfo::new(original_token.text_range(), res)
    })
}

pub(crate) fn hover_for_definition(
    sema: &Semantics<'_, RootDatabase>,
    file_id: FileId,
    definition: Definition,
    node: &SyntaxNode,
    config: &HoverConfig,
) -> Option<HoverResult> {
    let famous_defs = match &definition {
        Definition::BuiltinType(_) => Some(FamousDefs(sema, sema.scope(node)?.krate())),
        _ => None,
    };
    render::definition(sema.db, definition, famous_defs.as_ref(), config).map(|markup| {
        HoverResult {
            markup: render::process_markup(sema.db, definition, &markup, config),
            actions: show_implementations_action(sema.db, definition)
                .into_iter()
                .chain(show_fn_references_action(sema.db, definition))
                .chain(runnable_action(sema, definition, file_id))
                .chain(goto_type_action_for_def(sema.db, definition))
                .collect(),
        }
    })
}

fn hover_ranged(
    file: &SyntaxNode,
    range: syntax::TextRange,
    sema: &Semantics<'_, RootDatabase>,
    config: &HoverConfig,
) -> Option<RangeInfo<HoverResult>> {
    // FIXME: make this work in attributes
    let expr_or_pat = file.covering_element(range).ancestors().find_map(|it| {
        match_ast! {
            match it {
                ast::Expr(expr) => Some(Either::Left(expr)),
                ast::Pat(pat) => Some(Either::Right(pat)),
                _ => None,
            }
        }
    })?;
    let res = match &expr_or_pat {
        Either::Left(ast::Expr::TryExpr(try_expr)) => render::try_expr(sema, config, try_expr),
        Either::Left(ast::Expr::PrefixExpr(prefix_expr))
            if prefix_expr.op_kind() == Some(ast::UnaryOp::Deref) =>
        {
            render::deref_expr(sema, config, prefix_expr)
        }
        _ => None,
    };
    let res = res.or_else(|| render::type_info(sema, config, &expr_or_pat));
    res.map(|it| {
        let range = match expr_or_pat {
            Either::Left(it) => it.syntax().text_range(),
            Either::Right(it) => it.syntax().text_range(),
        };
        RangeInfo::new(range, it)
    })
}

fn hover_type_fallback(
    sema: &Semantics<'_, RootDatabase>,
    config: &HoverConfig,
    token: &SyntaxToken,
    original_token: &SyntaxToken,
) -> Option<RangeInfo<HoverResult>> {
    let node =
        token.parent_ancestors().take_while(|it| !ast::Item::can_cast(it.kind())).find(|n| {
            ast::Expr::can_cast(n.kind())
                || ast::Pat::can_cast(n.kind())
                || ast::Type::can_cast(n.kind())
        })?;

    let expr_or_pat = match_ast! {
        match node {
            ast::Expr(it) => Either::Left(it),
            ast::Pat(it) => Either::Right(it),
            // If this node is a MACRO_CALL, it means that `descend_into_macros_many` failed to resolve.
            // (e.g expanding a builtin macro). So we give up here.
            ast::MacroCall(_it) => return None,
            _ => return None,
        }
    };

    let res = render::type_info(sema, config, &expr_or_pat)?;
    let range = sema
        .original_range_opt(&node)
        .map(|frange| frange.range)
        .unwrap_or_else(|| original_token.text_range());
    Some(RangeInfo::new(range, res))
}

fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> {
    fn to_action(nav_target: NavigationTarget) -> HoverAction {
        HoverAction::Implementation(FilePosition {
            file_id: nav_target.file_id,
            offset: nav_target.focus_or_full_range().start(),
        })
    }

    let adt = match def {
        Definition::Trait(it) => return it.try_to_nav(db).map(to_action),
        Definition::Adt(it) => Some(it),
        Definition::SelfType(it) => it.self_ty(db).as_adt(),
        _ => None,
    }?;
    adt.try_to_nav(db).map(to_action)
}

fn show_fn_references_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> {
    match def {
        Definition::Function(it) => it.try_to_nav(db).map(|nav_target| {
            HoverAction::Reference(FilePosition {
                file_id: nav_target.file_id,
                offset: nav_target.focus_or_full_range().start(),
            })
        }),
        _ => None,
    }
}

fn runnable_action(
    sema: &hir::Semantics<'_, RootDatabase>,
    def: Definition,
    file_id: FileId,
) -> Option<HoverAction> {
    match def {
        Definition::Module(it) => runnable_mod(sema, it).map(HoverAction::Runnable),
        Definition::Function(func) => {
            let src = func.source(sema.db)?;
            if src.file_id != file_id.into() {
                cov_mark::hit!(hover_macro_generated_struct_fn_doc_comment);
                cov_mark::hit!(hover_macro_generated_struct_fn_doc_attr);
                return None;
            }

            runnable_fn(sema, func).map(HoverAction::Runnable)
        }
        _ => None,
    }
}

fn goto_type_action_for_def(db: &RootDatabase, def: Definition) -> Option<HoverAction> {
    let mut targets: Vec<hir::ModuleDef> = Vec::new();
    let mut push_new_def = |item: hir::ModuleDef| {
        if !targets.contains(&item) {
            targets.push(item);
        }
    };

    if let Definition::GenericParam(hir::GenericParam::TypeParam(it)) = def {
        it.trait_bounds(db).into_iter().for_each(|it| push_new_def(it.into()));
    } else {
        let ty = match def {
            Definition::Local(it) => it.ty(db),
            Definition::GenericParam(hir::GenericParam::ConstParam(it)) => it.ty(db),
            Definition::Field(field) => field.ty(db),
            Definition::Function(function) => function.ret_type(db),
            _ => return None,
        };

        walk_and_push_ty(db, &ty, &mut push_new_def);
    }

    Some(HoverAction::goto_type_from_targets(db, targets))
}

fn walk_and_push_ty(
    db: &RootDatabase,
    ty: &hir::Type,
    push_new_def: &mut dyn FnMut(hir::ModuleDef),
) {
    ty.walk(db, |t| {
        if let Some(adt) = t.as_adt() {
            push_new_def(adt.into());
        } else if let Some(trait_) = t.as_dyn_trait() {
            push_new_def(trait_.into());
        } else if let Some(traits) = t.as_impl_traits(db) {
            traits.for_each(|it| push_new_def(it.into()));
        } else if let Some(trait_) = t.as_associated_type_parent_trait(db) {
            push_new_def(trait_.into());
        }
    });
}

fn dedupe_or_merge_hover_actions(actions: Vec<HoverAction>) -> Vec<HoverAction> {
    let mut deduped_actions = Vec::with_capacity(actions.len());
    let mut go_to_type_targets = FxIndexSet::default();

    let mut seen_implementation = false;
    let mut seen_reference = false;
    let mut seen_runnable = false;
    for action in actions {
        match action {
            HoverAction::GoToType(targets) => {
                go_to_type_targets.extend(targets);
            }
            HoverAction::Implementation(..) => {
                if !seen_implementation {
                    seen_implementation = true;
                    deduped_actions.push(action);
                }
            }
            HoverAction::Reference(..) => {
                if !seen_reference {
                    seen_reference = true;
                    deduped_actions.push(action);
                }
            }
            HoverAction::Runnable(..) => {
                if !seen_runnable {
                    seen_runnable = true;
                    deduped_actions.push(action);
                }
            }
        };
    }

    if !go_to_type_targets.is_empty() {
        deduped_actions.push(HoverAction::GoToType(go_to_type_targets.into_iter().collect()));
    }

    deduped_actions
}
<|endoftext|>


<commit_before>use std::process::Command;

fn lk_to_kubectl(commands: &Vec<String>) -> String {
    let (mut cluster, mut pod) = ("", "");
    let cmds: Vec<String> = commands.iter().map(|c| {
        if c.contains("/") {
            let split: Vec<&str> = c.split("/").collect();
            if split.iter().count() == 2 {
                cluster = split[0];
                pod = split[1];

                return pod.to_string();
            }
        }
        return c.to_string();
    }).collect();

    let env = if cluster.contains("staging") { "staging" } else { "production" };
    let proj = pod.split("-").next().unwrap();

    return format!(
        "--cluster {} -e {} kubectl -- -n {}-{} {}",
        cluster, env, proj, env, cmds.join(" ")
    );
}

pub fn lk(commands: &Vec<String>) {
    let final_cmd = lk_to_kubectl(commands);
    println!("lyftkube {}", final_cmd);
    let mut child = Command::new("lyftkube").args(final_cmd.split(" "))
                                            .spawn()
                                            .unwrap();
    child.wait().unwrap();
}

#[cfg(test)]
mod tests {
    use crate::lyftkube::lk::lk_to_kubectl;
    #[test]
    fn test_lk() {
        let expected = "--cluster some-staging-1 -e staging kubectl -- -n abc-staging get pod abc-def-xxx-yyy";
        let cmds: Vec<String> = ["get", "pod", "some-staging-1/abc-def-xxx-yyy"].iter().map(|d| d.to_string()).collect();
        let real = lk_to_kubectl(&cmds);
        assert_eq!(expected, real);
    }
}
<commit_msg>refine code<commit_after>use std::process::Command;

fn lk_to_kubectl(commands: &Vec<String>) -> String {
    let (mut cluster, mut pod) = ("", "");
    let cmds: Vec<String> = commands.iter().map(|c| {
        if c.contains("/") {
            let split: Vec<&str> = c.split("/").collect();
            if split.iter().count() == 2 {
                cluster = split[0];
                pod = split[1];

                return pod.to_string();
            }
        }
        return c.to_string();
    }).collect();

    let env = if cluster.contains("staging") { "staging" } else { "production" };
    let proj = pod.split("-").next().unwrap();

    return format!(
        "--cluster {} -e {} kubectl -- -n {}-{} {}",
        cluster, env, proj, env, cmds.join(" ")
    );
}

pub fn lk(commands: &Vec<String>) {
    let final_cmd = lk_to_kubectl(commands);
    println!("lyftkube {}", final_cmd);
    let mut child = Command::new("lyftkube").args(final_cmd.split(" "))
                                            .spawn()
                                            .unwrap();
    child.wait().unwrap();
}

#[cfg(test)]
mod tests {
    use crate::lyftkube::lk::lk_to_kubectl;

    #[test]
    fn test_lk_works() {
        let expected = "--cluster some-staging-1 -e staging kubectl -- -n abc-staging get pod abc-def-xxx-yyy";
        let cmds: Vec<String> = ["get", "pod", "some-staging-1/abc-def-xxx-yyy"].iter().map(|d| d.to_string()).collect();
        let real = lk_to_kubectl(&cmds);
        assert_eq!(expected, real);
    }

    #[test]
    fn test_lk_not_work_when_no_cluster_pod() {
        let expected = "--cluster some-staging-1 -e staging kubectl -- -n abc-staging get pod abc-def-xxx-yyy";
        let cmds: Vec<String> = ["get", "pod", "abc-def-xxx-yyy"].iter().map(|d| d.to_string()).collect();
        let real = lk_to_kubectl(&cmds);
        assert_ne!(expected, real);
    }

}
<|endoftext|>