<commit_before>extern crate router;
extern crate persistent;

use std::io::Read;
use std::collections::HashMap;

use iron::prelude::*;
use iron::status;
use router::Router;
use rustc_serialize::json::{self, Json};

use super::{Globals, Index, Mapping, Document, query};

fn index_not_found_response() -> Response {
    let mut response = Response::with((status::NotFound, "{\"message\": \"Index not found\"}"));
    response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
    return response;
}

pub fn view_home(_: &mut Request) -> IronResult<Response> {
    Ok(Response::with((status::Ok, "Hello World!")))
}

pub fn view_count(req: &mut Request) -> IronResult<Response> {
    let ref glob = req.get::<persistent::Read<Globals>>().unwrap();

    // URL parameters
    let index_name = req.extensions.get::<Router>().unwrap().find("index").unwrap_or("");

    // Lock index array
    let indices = glob.indices.lock().unwrap();

    // Find index
    let index = match indices.get(index_name) {
        Some(index) => index,
        None => {
            return Ok(index_not_found_response());
        }
    };

    // Load query from body
    let mut payload = String::new();
    req.body.read_to_string(&mut payload).unwrap();

    let data = if !payload.is_empty() {
        Some(match Json::from_str(&payload) {
            Ok(data) => data,
            Err(error) => {
                // TODO: What specifically is bad about the JSON?
                let mut response = Response::with((status::BadRequest,
                                                   "{\"message\": \"Couldn't parse JSON\"}"));
                response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
                return Ok(response);
            }
        })
    } else {
        None
    };

    println!("{:#?}", query::parse_query(data.unwrap().as_object().unwrap().get("query").unwrap()));

    // TODO: Run query

    // Temporary count and return numbers
    let mut count = 0;
    for mapping in index.mappings.values() {
        count += mapping.docs.len();
    }

    let mut response = Response::with((status::Ok, format!("{{\"count\": {}}}", count)));
    response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
    Ok(response)
}

pub fn view_search(req: &mut Request) -> IronResult<Response> {
    let ref glob = req.get::<persistent::Read<Globals>>().unwrap();

    // URL parameters
    let index_name = req.extensions.get::<Router>().unwrap().find("index").unwrap_or("");

    // Lock index array
    let indices = glob.indices.lock().unwrap();

    // Find index
    let index = match indices.get(index_name) {
        Some(index) => index,
        None => {
            return Ok(index_not_found_response());
        }
    };

    // Load query from body
    let mut payload = String::new();
    req.body.read_to_string(&mut payload).unwrap();

    let data = if !payload.is_empty() {
        Some(match Json::from_str(&payload) {
            Ok(data) => data,
            Err(error) => {
                // TODO: What specifically is bad about the JSON?
                let mut response = Response::with((status::BadRequest,
                                                   "{\"message\": \"Couldn't parse JSON\"}"));
                response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
                return Ok(response);
            }
        })
    } else {
        None
    };

    println!("{:#?}", query::parse_query(data.unwrap().as_object().unwrap().get("query").unwrap()));

    // TODO: Run query

    let mut response = Response::with((status::Ok, "{\"hits\": {\"total\": 0, \"hits\": []}}"));
    response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
    Ok(response)
}

pub fn view_get_doc(req: &mut Request) -> IronResult<Response> {
    let ref glob = req.get::<persistent::Read<Globals>>().unwrap();

    // URL parameters
    let index_name = req.extensions.get::<Router>().unwrap().find("index").unwrap_or("");
    let mapping_name = req.extensions.get::<Router>().unwrap().find("mapping").unwrap_or("");
    let doc_id = req.extensions.get::<Router>().unwrap().find("doc").unwrap_or("");

    // Lock index array
    let indices = glob.indices.lock().unwrap();

    // Find index
    let index = match indices.get(index_name) {
        Some(index) => index,
        None => {
            return Ok(index_not_found_response());
        }
    };

    // Find mapping
    let mapping = match index.mappings.get(mapping_name) {
        Some(mapping) => mapping,
        None => {
            let mut response = Response::with((status::NotFound,
                                               "{\"message\": \"Mapping not found\"}"));
            response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
            return Ok(response);
        }
    };

    // Find document
    let doc = match mapping.docs.get(doc_id) {
        Some(doc) => doc,
        None => {
            let mut response = Response::with((status::NotFound,
                                               "{\"message\": \"Document not found\"}"));
            response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
            return Ok(response);
        }
    };

    let mut response = Response::with((status::Ok, json::encode(&doc.data).unwrap()));
    response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
    Ok(response)
}

pub fn view_put_doc(req: &mut Request) -> IronResult<Response> {
    let ref glob = req.get::<persistent::Read<Globals>>().unwrap();

    // URL parameters
    let index_name = req.extensions.get::<Router>().unwrap().find("index").unwrap_or("");
    let mapping_name = req.extensions.get::<Router>().unwrap().find("mapping").unwrap_or("");
    let ref doc_id = req.extensions.get::<Router>().unwrap().find("doc").unwrap_or("");

    // Lock index array
    let mut indices = glob.indices.lock().unwrap();

    // Find index
    let mut index = match indices.get_mut(index_name) {
        Some(index) => index,
        None => {
            return Ok(index_not_found_response());
        }
    };

    // Find mapping
    let mut mapping = match index.mappings.get_mut(mapping_name) {
        Some(mapping) => mapping,
        None => {
            let mut response = Response::with((status::NotFound,
                                               "{\"message\": \"Mapping not found\"}"));
            response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
            return Ok(response);
        }
    };

    // Load data from body
    let mut payload = String::new();
    req.body.read_to_string(&mut payload).unwrap();

    let data = if !payload.is_empty() {
        Some(match Json::from_str(&payload) {
            Ok(data) => data,
            Err(error) => {
                // TODO: What specifically is bad about the JSON?
                let mut response = Response::with((status::BadRequest,
                                                   "{\"message\": \"Couldn't parse JSON\"}"));
                response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
                return Ok(response);
            }
        })
    } else {
        None
    };

    // Create and insert document
    if let Some(data) = data {
        let doc = Document::from_json(data);
        mapping.docs.insert(doc_id.clone().to_owned(), doc);
    }

    let mut response = Response::with((status::Ok, "{}"));
    response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
    Ok(response)
}

pub fn view_put_index(req: &mut Request) -> IronResult<Response> {
    let ref glob = req.get::<persistent::Read<Globals>>().unwrap();

    // URL parameters
    let ref index_name = req.extensions.get::<Router>().unwrap().find("index").unwrap_or("");

    // Lock index array
    let mut indices = glob.indices.lock().unwrap();

    // Load data from body
    let mut payload = String::new();
    req.body.read_to_string(&mut payload).unwrap();

    let data = if !payload.is_empty() {
        Some(match Json::from_str(&payload) {
            Ok(data) => data,
            Err(error) => {
                // TODO: What specifically is bad about the JSON?
                let mut response = Response::with((status::BadRequest,
                                                   "{\"message\": \"Couldn't parse JSON\"}"));
                response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
                return Ok(response);
            }
        })
    } else {
        None
    };

    // Create index
    indices.insert(index_name.clone().to_owned(), Index::new());

    let mut response = Response::with((status::Ok, "{\"acknowledged\": true}"));
    response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
    Ok(response)
}

pub fn view_put_mapping(req: &mut Request) -> IronResult<Response> {
    let ref glob = req.get::<persistent::Read<Globals>>().unwrap();

    // URL parameters
    let index_name = req.extensions.get::<Router>().unwrap().find("index").unwrap_or("");
    let ref mapping_name = req.extensions.get::<Router>().unwrap().find("mapping").unwrap_or("");

    // Lock index array
    let mut indices = glob.indices.lock().unwrap();

    // Find index
    let mut index = match indices.get_mut(index_name) {
        Some(index) => index,
        None => {
            return Ok(index_not_found_response());
        }
    };

    // Load data from body
    let mut payload = String::new();
    req.body.read_to_string(&mut payload).unwrap();

    if payload.is_empty() {
        // TODO: Better error
        let mut response = Response::with((status::Ok, "{\"acknowledged\": false}"));
        response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
        return Ok(response)
    }

    let data = match Json::from_str(&payload) {
        Ok(data) => data,
        Err(error) => {
            // TODO: What specifically is bad about the JSON?
            let mut response = Response::with((status::BadRequest,
                                               "{\"message\": \"Couldn't parse JSON\"}"));
            response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
            return Ok(response);
        }
    };

    let data = data.as_object().unwrap().get(*mapping_name).unwrap();

    // Insert mapping
    let mapping = Mapping::from_json(&data);
    println!("{:#?}", mapping);
    index.mappings.insert(mapping_name.clone().to_owned(), mapping);

    let mut response = Response::with((status::Ok, "{\"acknowledged\": true}"));
    response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
    Ok(response)
}

pub fn view_post_bulk(req: &mut Request) -> IronResult<Response> {
    let ref glob = req.get::<persistent::Read<Globals>>().unwrap();

    // Lock index array
    let mut indices = glob.indices.lock().unwrap();

    // Load data from body
    let mut payload = String::new();
    req.body.read_to_string(&mut payload).unwrap();

    let mut items = Vec::new();

    // Iterate
    let mut payload_lines = payload.split('\n');
    loop {
        let action_line = payload_lines.next();

        // Check if end of input
        if action_line == None || action_line == Some("") { break; }

        // Parse action line
        let action_json = match Json::from_str(&action_line.unwrap()) {
            Ok(data) => data,
            Err(error) => {
                // TODO: What specifically is bad about the JSON?
                let mut response = Response::with((status::BadRequest,
                                                   "{\"message\": \"Couldn't parse JSON\"}"));
                response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
                return Ok(response);
            }
        };

        // Check action
        // Action should be an object with only one key, the key name indicates the action and
        // the value is the parameters for that action
        let action_name = action_json.as_object().unwrap().keys().nth(0).unwrap();
        let action_params = action_json.as_object().unwrap().get(action_name).unwrap()
                                                            .as_object().unwrap();

        let doc_id = action_params.get("_id").unwrap().as_string().unwrap();
        let doc_type = action_params.get("_type").unwrap().as_string().unwrap();
        let doc_index = action_params.get("_index").unwrap().as_string().unwrap();

        match action_name.as_ref() {
            "index" => {
                let doc_line = payload_lines.next();
                let doc_json =  match Json::from_str(&doc_line.unwrap()) {
                    Ok(data) => data,
                    Err(error) => {
                        // TODO: What specifically is bad about the JSON?
                        let mut response = Response::with((status::BadRequest,
                                                           "{\"message\": \"Couldn't parse JSON\"}"));
                        response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
                        return Ok(response);
                    }
                };

                // Find index
                let mut index = match indices.get_mut(doc_index) {
                    Some(index) => index,
                    None => {
                        return Ok(index_not_found_response());
                    }
                };

                // Find mapping
                let mut mapping = match index.mappings.get_mut(doc_type) {
                    Some(mapping) => mapping,
                    None => {
                        let mut response = Response::with((status::NotFound,
                                                           "{\"message\": \"Mapping not found\"}"));
                        response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
                        return Ok(response);
                    }
                };

                // Create and insert document
                let doc = Document::from_json(doc_json);
                mapping.docs.insert(doc_id.clone().to_owned(), doc);

                // Insert into "items" array
                let mut item = HashMap::new();
                item.insert("create", action_params.clone());
                items.push(item);
            }
            _ => {
                println!("Unrecognised action! {}", action_name);
            }
        }
    }

    let mut response = Response::with((status::Ok, format!("{{\"took\": {}, \"items\": {}}}", items.len(), json::encode(&items).unwrap())));
    response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
    Ok(response)
}

pub fn view_post_refresh(req: &mut Request) -> IronResult<Response> {
    let ref glob = req.get::<persistent::Read<Globals>>().unwrap();

    // Lock index array
    let mut indices = glob.indices.lock().unwrap();

    let mut response = Response::with((status::Ok, "{\"acknowledged\": true}"));
    response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
    Ok(response)
}

pub fn get_router() -> Router {
    router!(get "/" => view_home,
            get "/:index/_count" => view_count,
            get "/:index/_search" => view_search,
            post "/:index/_count" => view_count,
            post "/:index/_search" => view_search,
            get "/:index/:mapping/:doc" => view_get_doc,
            put "/:index/:mapping/:doc" => view_put_doc,
            put "/:index" => view_put_index,
            put "/:index/_mapping/:mapping" => view_put_mapping,
            post "/_bulk" => view_post_bulk,
            post ":index/_refresh" => view_post_refresh)
}
<commit_msg>Missing leading /<commit_after>extern crate router;
extern crate persistent;

use std::io::Read;
use std::collections::HashMap;

use iron::prelude::*;
use iron::status;
use router::Router;
use rustc_serialize::json::{self, Json};

use super::{Globals, Index, Mapping, Document, query};

fn index_not_found_response() -> Response {
    let mut response = Response::with((status::NotFound, "{\"message\": \"Index not found\"}"));
    response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
    return response;
}

pub fn view_home(_: &mut Request) -> IronResult<Response> {
    Ok(Response::with((status::Ok, "Hello World!")))
}

pub fn view_count(req: &mut Request) -> IronResult<Response> {
    let ref glob = req.get::<persistent::Read<Globals>>().unwrap();

    // URL parameters
    let index_name = req.extensions.get::<Router>().unwrap().find("index").unwrap_or("");

    // Lock index array
    let indices = glob.indices.lock().unwrap();

    // Find index
    let index = match indices.get(index_name) {
        Some(index) => index,
        None => {
            return Ok(index_not_found_response());
        }
    };

    // Load query from body
    let mut payload = String::new();
    req.body.read_to_string(&mut payload).unwrap();

    let data = if !payload.is_empty() {
        Some(match Json::from_str(&payload) {
            Ok(data) => data,
            Err(error) => {
                // TODO: What specifically is bad about the JSON?
                let mut response = Response::with((status::BadRequest,
                                                   "{\"message\": \"Couldn't parse JSON\"}"));
                response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
                return Ok(response);
            }
        })
    } else {
        None
    };

    println!("{:#?}", query::parse_query(data.unwrap().as_object().unwrap().get("query").unwrap()));

    // TODO: Run query

    // Temporary count and return numbers
    let mut count = 0;
    for mapping in index.mappings.values() {
        count += mapping.docs.len();
    }

    let mut response = Response::with((status::Ok, format!("{{\"count\": {}}}", count)));
    response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
    Ok(response)
}

pub fn view_search(req: &mut Request) -> IronResult<Response> {
    let ref glob = req.get::<persistent::Read<Globals>>().unwrap();

    // URL parameters
    let index_name = req.extensions.get::<Router>().unwrap().find("index").unwrap_or("");

    // Lock index array
    let indices = glob.indices.lock().unwrap();

    // Find index
    let index = match indices.get(index_name) {
        Some(index) => index,
        None => {
            return Ok(index_not_found_response());
        }
    };

    // Load query from body
    let mut payload = String::new();
    req.body.read_to_string(&mut payload).unwrap();

    let data = if !payload.is_empty() {
        Some(match Json::from_str(&payload) {
            Ok(data) => data,
            Err(error) => {
                // TODO: What specifically is bad about the JSON?
                let mut response = Response::with((status::BadRequest,
                                                   "{\"message\": \"Couldn't parse JSON\"}"));
                response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
                return Ok(response);
            }
        })
    } else {
        None
    };

    println!("{:#?}", query::parse_query(data.unwrap().as_object().unwrap().get("query").unwrap()));

    // TODO: Run query

    let mut response = Response::with((status::Ok, "{\"hits\": {\"total\": 0, \"hits\": []}}"));
    response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
    Ok(response)
}

pub fn view_get_doc(req: &mut Request) -> IronResult<Response> {
    let ref glob = req.get::<persistent::Read<Globals>>().unwrap();

    // URL parameters
    let index_name = req.extensions.get::<Router>().unwrap().find("index").unwrap_or("");
    let mapping_name = req.extensions.get::<Router>().unwrap().find("mapping").unwrap_or("");
    let doc_id = req.extensions.get::<Router>().unwrap().find("doc").unwrap_or("");

    // Lock index array
    let indices = glob.indices.lock().unwrap();

    // Find index
    let index = match indices.get(index_name) {
        Some(index) => index,
        None => {
            return Ok(index_not_found_response());
        }
    };

    // Find mapping
    let mapping = match index.mappings.get(mapping_name) {
        Some(mapping) => mapping,
        None => {
            let mut response = Response::with((status::NotFound,
                                               "{\"message\": \"Mapping not found\"}"));
            response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
            return Ok(response);
        }
    };

    // Find document
    let doc = match mapping.docs.get(doc_id) {
        Some(doc) => doc,
        None => {
            let mut response = Response::with((status::NotFound,
                                               "{\"message\": \"Document not found\"}"));
            response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
            return Ok(response);
        }
    };

    let mut response = Response::with((status::Ok, json::encode(&doc.data).unwrap()));
    response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
    Ok(response)
}

pub fn view_put_doc(req: &mut Request) -> IronResult<Response> {
    let ref glob = req.get::<persistent::Read<Globals>>().unwrap();

    // URL parameters
    let index_name = req.extensions.get::<Router>().unwrap().find("index").unwrap_or("");
    let mapping_name = req.extensions.get::<Router>().unwrap().find("mapping").unwrap_or("");
    let ref doc_id = req.extensions.get::<Router>().unwrap().find("doc").unwrap_or("");

    // Lock index array
    let mut indices = glob.indices.lock().unwrap();

    // Find index
    let mut index = match indices.get_mut(index_name) {
        Some(index) => index,
        None => {
            return Ok(index_not_found_response());
        }
    };

    // Find mapping
    let mut mapping = match index.mappings.get_mut(mapping_name) {
        Some(mapping) => mapping,
        None => {
            let mut response = Response::with((status::NotFound,
                                               "{\"message\": \"Mapping not found\"}"));
            response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
            return Ok(response);
        }
    };

    // Load data from body
    let mut payload = String::new();
    req.body.read_to_string(&mut payload).unwrap();

    let data = if !payload.is_empty() {
        Some(match Json::from_str(&payload) {
            Ok(data) => data,
            Err(error) => {
                // TODO: What specifically is bad about the JSON?
                let mut response = Response::with((status::BadRequest,
                                                   "{\"message\": \"Couldn't parse JSON\"}"));
                response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
                return Ok(response);
            }
        })
    } else {
        None
    };

    // Create and insert document
    if let Some(data) = data {
        let doc = Document::from_json(data);
        mapping.docs.insert(doc_id.clone().to_owned(), doc);
    }

    let mut response = Response::with((status::Ok, "{}"));
    response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
    Ok(response)
}

pub fn view_put_index(req: &mut Request) -> IronResult<Response> {
    let ref glob = req.get::<persistent::Read<Globals>>().unwrap();

    // URL parameters
    let ref index_name = req.extensions.get::<Router>().unwrap().find("index").unwrap_or("");

    // Lock index array
    let mut indices = glob.indices.lock().unwrap();

    // Load data from body
    let mut payload = String::new();
    req.body.read_to_string(&mut payload).unwrap();

    let data = if !payload.is_empty() {
        Some(match Json::from_str(&payload) {
            Ok(data) => data,
            Err(error) => {
                // TODO: What specifically is bad about the JSON?
                let mut response = Response::with((status::BadRequest,
                                                   "{\"message\": \"Couldn't parse JSON\"}"));
                response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
                return Ok(response);
            }
        })
    } else {
        None
    };

    // Create index
    indices.insert(index_name.clone().to_owned(), Index::new());

    let mut response = Response::with((status::Ok, "{\"acknowledged\": true}"));
    response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
    Ok(response)
}

pub fn view_put_mapping(req: &mut Request) -> IronResult<Response> {
    let ref glob = req.get::<persistent::Read<Globals>>().unwrap();

    // URL parameters
    let index_name = req.extensions.get::<Router>().unwrap().find("index").unwrap_or("");
    let ref mapping_name = req.extensions.get::<Router>().unwrap().find("mapping").unwrap_or("");

    // Lock index array
    let mut indices = glob.indices.lock().unwrap();

    // Find index
    let mut index = match indices.get_mut(index_name) {
        Some(index) => index,
        None => {
            return Ok(index_not_found_response());
        }
    };

    // Load data from body
    let mut payload = String::new();
    req.body.read_to_string(&mut payload).unwrap();

    if payload.is_empty() {
        // TODO: Better error
        let mut response = Response::with((status::Ok, "{\"acknowledged\": false}"));
        response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
        return Ok(response)
    }

    let data = match Json::from_str(&payload) {
        Ok(data) => data,
        Err(error) => {
            // TODO: What specifically is bad about the JSON?
            let mut response = Response::with((status::BadRequest,
                                               "{\"message\": \"Couldn't parse JSON\"}"));
            response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
            return Ok(response);
        }
    };

    let data = data.as_object().unwrap().get(*mapping_name).unwrap();

    // Insert mapping
    let mapping = Mapping::from_json(&data);
    println!("{:#?}", mapping);
    index.mappings.insert(mapping_name.clone().to_owned(), mapping);

    let mut response = Response::with((status::Ok, "{\"acknowledged\": true}"));
    response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
    Ok(response)
}

pub fn view_post_bulk(req: &mut Request) -> IronResult<Response> {
    let ref glob = req.get::<persistent::Read<Globals>>().unwrap();

    // Lock index array
    let mut indices = glob.indices.lock().unwrap();

    // Load data from body
    let mut payload = String::new();
    req.body.read_to_string(&mut payload).unwrap();

    let mut items = Vec::new();

    // Iterate
    let mut payload_lines = payload.split('\n');
    loop {
        let action_line = payload_lines.next();

        // Check if end of input
        if action_line == None || action_line == Some("") { break; }

        // Parse action line
        let action_json = match Json::from_str(&action_line.unwrap()) {
            Ok(data) => data,
            Err(error) => {
                // TODO: What specifically is bad about the JSON?
                let mut response = Response::with((status::BadRequest,
                                                   "{\"message\": \"Couldn't parse JSON\"}"));
                response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
                return Ok(response);
            }
        };

        // Check action
        // Action should be an object with only one key, the key name indicates the action and
        // the value is the parameters for that action
        let action_name = action_json.as_object().unwrap().keys().nth(0).unwrap();
        let action_params = action_json.as_object().unwrap().get(action_name).unwrap()
                                                            .as_object().unwrap();

        let doc_id = action_params.get("_id").unwrap().as_string().unwrap();
        let doc_type = action_params.get("_type").unwrap().as_string().unwrap();
        let doc_index = action_params.get("_index").unwrap().as_string().unwrap();

        match action_name.as_ref() {
            "index" => {
                let doc_line = payload_lines.next();
                let doc_json =  match Json::from_str(&doc_line.unwrap()) {
                    Ok(data) => data,
                    Err(error) => {
                        // TODO: What specifically is bad about the JSON?
                        let mut response = Response::with((status::BadRequest,
                                                           "{\"message\": \"Couldn't parse JSON\"}"));
                        response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
                        return Ok(response);
                    }
                };

                // Find index
                let mut index = match indices.get_mut(doc_index) {
                    Some(index) => index,
                    None => {
                        return Ok(index_not_found_response());
                    }
                };

                // Find mapping
                let mut mapping = match index.mappings.get_mut(doc_type) {
                    Some(mapping) => mapping,
                    None => {
                        let mut response = Response::with((status::NotFound,
                                                           "{\"message\": \"Mapping not found\"}"));
                        response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
                        return Ok(response);
                    }
                };

                // Create and insert document
                let doc = Document::from_json(doc_json);
                mapping.docs.insert(doc_id.clone().to_owned(), doc);

                // Insert into "items" array
                let mut item = HashMap::new();
                item.insert("create", action_params.clone());
                items.push(item);
            }
            _ => {
                println!("Unrecognised action! {}", action_name);
            }
        }
    }

    let mut response = Response::with((status::Ok, format!("{{\"took\": {}, \"items\": {}}}", items.len(), json::encode(&items).unwrap())));
    response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
    Ok(response)
}

pub fn view_post_refresh(req: &mut Request) -> IronResult<Response> {
    let ref glob = req.get::<persistent::Read<Globals>>().unwrap();

    // Lock index array
    let mut indices = glob.indices.lock().unwrap();

    let mut response = Response::with((status::Ok, "{\"acknowledged\": true}"));
    response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
    Ok(response)
}

pub fn get_router() -> Router {
    router!(get "/" => view_home,
            get "/:index/_count" => view_count,
            get "/:index/_search" => view_search,
            post "/:index/_count" => view_count,
            post "/:index/_search" => view_search,
            get "/:index/:mapping/:doc" => view_get_doc,
            put "/:index/:mapping/:doc" => view_put_doc,
            put "/:index" => view_put_index,
            put "/:index/_mapping/:mapping" => view_put_mapping,
            post "/_bulk" => view_post_bulk,
            post "/:index/_refresh" => view_post_refresh)
}
<|endoftext|>


<commit_before>use std::any::TypeId;
use std::collections::HashMap;
use std::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
use mopa::Any;
use {Index, Generation, Entity, StorageBase, Storage};

/// Abstract component type. Doesn't have to be Copy or even Clone.
pub trait Component: Any + Sized {
    /// Associated storage type for this component.
    type Storage: Storage<Self> + Any + Send + Sync;
}

/// A custom entity iterator. Needed because the world doesn't really store
/// entities directly, but rather has just a vector of Index -> Generation.
pub struct EntityIter<'a> {
    guard: RwLockReadGuard<'a, Vec<Generation>>,
    index: usize,
}

impl<'a> Iterator for EntityIter<'a> {
    type Item = Entity;
    fn next(&mut self) -> Option<Entity> {
        loop {
            match self.guard.get(self.index) {
                Some(&gen) if gen.is_alive() => {
                    let ent = Entity(self.index as Index, gen);
                    self.index += 1;
                    return Some(ent)
                },
                Some(_) => self.index += 1, // continue
                None => return None,
            }
        }
    }
}

/// Helper builder for entities.
pub struct EntityBuilder<'a>(Entity, &'a World);

impl<'a> EntityBuilder<'a> {
    /// Add a component value to the new entity.
    pub fn with<T: Component>(self, value: T) -> EntityBuilder<'a> {
        self.1.write::<T>().insert(self.0, value);
        self
    }
    /// Finish entity construction.
    pub fn build(self) -> Entity {
        self.0
    }
}

struct Appendix {
    next: Entity,
    add_queue: Vec<Entity>,
    sub_queue: Vec<Entity>,
}

fn find_next(gens: &[Generation], lowest_free_index: usize) -> Entity {
    if let Some((id, gen)) = gens.iter().enumerate().skip(lowest_free_index).find(|&(_, g)| !g.is_alive()) {
        return Entity(id as Index, gen.raised());
    }

    let new_index = if lowest_free_index > gens.len() {
        lowest_free_index as Index
    } else {
        gens.len() as Index
    };

    Entity(new_index, Generation(1))
}

/// Entity creation iterator. Will yield new empty entities infinitely.
/// Useful for bulk entity construction, since the locks are only happening once.
pub struct CreateEntityIter<'a> {
    gens: RwLockWriteGuard<'a, Vec<Generation>>,
    app: RwLockWriteGuard<'a, Appendix>,
}

impl<'a> Iterator for CreateEntityIter<'a> {
    type Item = Entity;
    fn next(&mut self) -> Option<Entity> {
        let ent = self.app.next;
        assert!(ent.get_gen().is_alive());
        if ent.get_gen().is_first() {
            assert!(self.gens.len() == ent.get_id());
            self.gens.push(ent.get_gen());
            self.app.next.0 += 1;
        } else {
            self.app.next = find_next(&self.gens, ent.get_id() + 1);
        }
        Some(ent)
    }
}

/// A custom entity iterator for dynamically added entities.
pub struct DynamicEntityIter<'a> {
    guard: RwLockReadGuard<'a, Appendix>,
    index: usize,
}

impl<'a> Iterator for DynamicEntityIter<'a> {
    type Item = Entity;
    fn next(&mut self) -> Option<Entity> {
        let ent = self.guard.add_queue.get(self.index);
        self.index += 1;
        ent.cloned()
    }
}

trait StorageLock: Any + Send + Sync {
    fn del_slice(&self, &[Entity]);
}

mopafy!(StorageLock);

impl<S: StorageBase + Any + Send + Sync> StorageLock for RwLock<S> {
    fn del_slice(&self, entities: &[Entity]) {
        let mut guard = self.write().unwrap();
        for &e in entities.iter() {
            guard.del(e);
        }
    }
}

/// The world struct contains all the data, which is entities and their components.
/// The methods are supposed to be valid for any context they are available in.
pub struct World {
    generations: RwLock<Vec<Generation>>,
    components: HashMap<TypeId, Box<StorageLock>>,
    appendix: RwLock<Appendix>,
}

impl World {
    /// Create a new empty world.
    pub fn new() -> World {
        World {
            generations: RwLock::new(Vec::new()),
            components: HashMap::new(),
            appendix: RwLock::new(Appendix {
                next: Entity(0, Generation(1)),
                add_queue: Vec::new(),
                sub_queue: Vec::new(),
            }),
        }
    }
    /// Register a new component type.
    pub fn register<T: Component>(&mut self) {
        let any = RwLock::new(T::Storage::new());
        self.components.insert(TypeId::of::<T>(), Box::new(any));
    }
    /// Unregister a component type.
    pub fn unregister<T: Component>(&mut self) -> Option<T::Storage> {
        self.components.remove(&TypeId::of::<T>()).map(|boxed|
            match boxed.downcast::<RwLock<T::Storage>>() {
                Ok(b) => (*b).into_inner().unwrap(),
                Err(_) => panic!("Unable to downcast the storage type"),
            }
        )
    }
    fn lock<T: Component>(&self) -> &RwLock<T::Storage> {
        let boxed = self.components.get(&TypeId::of::<T>())
            .expect("Tried to perform an operation on type that was not registered");
        boxed.downcast_ref().unwrap()
    }
    /// Lock a component's storage for reading.
    pub fn read<T: Component>(&self) -> RwLockReadGuard<T::Storage> {
        self.lock::<T>().read().unwrap()
    }
    /// Lock a component's storage for writing.
    pub fn write<T: Component>(&self) -> RwLockWriteGuard<T::Storage> {
        self.lock::<T>().write().unwrap()
    }
    /// Return the entity iterator.
    pub fn entities(&self) -> EntityIter {
        EntityIter {
            guard: self.generations.read().unwrap(),
            index: 0,
        }
    }
    /// Return the dynamic entity iterator. It goes through entities that were
    /// dynamically created by systems but not yet merged.
    pub fn dynamic_entities(&self) -> DynamicEntityIter {
        DynamicEntityIter {
            guard: self.appendix.read().unwrap(),
            index: 0,
        }
    }
    /// Return the entity creation iterator. Can be used to create many
    /// empty entities at once without paying the locking overhead.
    pub fn create_iter(&self) -> CreateEntityIter {
        CreateEntityIter {
            gens: self.generations.write().unwrap(),
            app: self.appendix.write().unwrap(),
        }
    }
    /// Create a new entity instantly, with locking the generations data.
    pub fn create_now(&self) -> EntityBuilder {
        let mut app = self.appendix.write().unwrap();
        let ent = app.next;
        assert!(ent.get_gen().is_alive());
        if ent.get_gen().is_first() {
            let mut gens = self.generations.write().unwrap();
            assert!(gens.len() == ent.get_id());
            gens.push(ent.get_gen());
            app.next.0 += 1;
        } else {
            let gens = self.generations.read().unwrap();
            app.next = find_next(&gens, ent.get_id() + 1);
        }
        EntityBuilder(ent, self)
    }
    /// Delete a new entity instantly, with locking the generations data.
    pub fn delete_now(&self, entity: Entity) {
        for comp in self.components.values() {
            comp.del_slice(&[entity]);
        }
        let mut gens = self.generations.write().unwrap();
        let mut gen = &mut gens[entity.get_id() as usize];
        assert!(gen.is_alive());
        gen.die();
        let mut app = self.appendix.write().unwrap();
        if entity.get_id() < app.next.get_id() {
            app.next = Entity(entity.0, gen.raised());
        }
    }
    /// Create a new entity dynamically.
    pub fn create_later(&self) -> Entity {
        let mut app = self.appendix.write().unwrap();
        let ent = app.next;
        app.add_queue.push(ent);
        app.next = find_next(&*self.generations.read().unwrap(), ent.get_id() + 1);
        ent
    }
    /// Delete an entity dynamically.
    pub fn delete_later(&self, entity: Entity) {
        let mut app = self.appendix.write().unwrap();
        app.sub_queue.push(entity);
    }
    /// Merge in the appendix, recording all the dynamically created
    /// and deleted entities into the persistent generations vector.
    /// Also removes all the abandoned components.
    pub fn merge(&self) {
        // We can't lock Appendix and components at the same time,
        // or otherwise we deadlock with a system that tries to process
        // newly added entities.
        // So we copy dead components out first, and then process them separately.
        let mut temp_list = Vec::new(); //TODO: avoid allocation
        {
            let mut gens = self.generations.write().unwrap();
            let mut app = self.appendix.write().unwrap();
            for ent in app.add_queue.drain(..) {
                while gens.len() <= ent.get_id() {
                    gens.push(Generation(0));
                }
                assert_eq!(ent.get_gen(), gens[ent.get_id()].raised());
                gens[ent.get_id()] = ent.get_gen();
            }
            let mut next = app.next;
            for ent in app.sub_queue.drain(..) {
                assert_eq!(ent.get_gen(), gens[ent.get_id()]);
                let gen = &mut gens[ent.get_id()];
                gen.die();
                temp_list.push(ent);
                if ent.get_id() < next.get_id() {
                    next = Entity(ent.0, gen.raised());
                }
            }
            app.next = next;
        }
        for comp in self.components.values() {
            comp.del_slice(&temp_list);
        }
    }
}

/// System fetch-time argument. The fetch is executed at the start of the run.
/// It contains a subset of World methods that make sense during initialization.
pub struct FetchArg<'a>(&'a World);

impl<'a> FetchArg<'a> {
    /// Construct the new arg, not supposed to be used.
    #[doc(hidden)]
    pub fn new(w: &'a World) -> FetchArg<'a> {
        FetchArg(w)
    }
    /// Lock a component for reading.
    pub fn read<T: Component>(&self) -> RwLockReadGuard<'a, T::Storage> {
        self.0.read::<T>()
    }
    /// Lock a component for writing.
    pub fn write<T: Component>(&self) -> RwLockWriteGuard<'a, T::Storage> {
        self.0.write::<T>()
    }
    /// Return the entity iterator.
    pub fn entities(self) -> EntityIter<'a> {
        self.0.entities()
    }
}
<commit_msg>remove redundant `assert!`<commit_after>use std::any::TypeId;
use std::collections::HashMap;
use std::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
use mopa::Any;
use {Index, Generation, Entity, StorageBase, Storage};

/// Abstract component type. Doesn't have to be Copy or even Clone.
pub trait Component: Any + Sized {
    /// Associated storage type for this component.
    type Storage: Storage<Self> + Any + Send + Sync;
}

/// A custom entity iterator. Needed because the world doesn't really store
/// entities directly, but rather has just a vector of Index -> Generation.
pub struct EntityIter<'a> {
    guard: RwLockReadGuard<'a, Vec<Generation>>,
    index: usize,
}

impl<'a> Iterator for EntityIter<'a> {
    type Item = Entity;
    fn next(&mut self) -> Option<Entity> {
        loop {
            match self.guard.get(self.index) {
                Some(&gen) if gen.is_alive() => {
                    let ent = Entity(self.index as Index, gen);
                    self.index += 1;
                    return Some(ent)
                },
                Some(_) => self.index += 1, // continue
                None => return None,
            }
        }
    }
}

/// Helper builder for entities.
pub struct EntityBuilder<'a>(Entity, &'a World);

impl<'a> EntityBuilder<'a> {
    /// Add a component value to the new entity.
    pub fn with<T: Component>(self, value: T) -> EntityBuilder<'a> {
        self.1.write::<T>().insert(self.0, value);
        self
    }
    /// Finish entity construction.
    pub fn build(self) -> Entity {
        self.0
    }
}

struct Appendix {
    next: Entity,
    add_queue: Vec<Entity>,
    sub_queue: Vec<Entity>,
}

fn find_next(gens: &[Generation], lowest_free_index: usize) -> Entity {
    if let Some((id, gen)) = gens.iter().enumerate().skip(lowest_free_index).find(|&(_, g)| !g.is_alive()) {
        return Entity(id as Index, gen.raised());
    }

    let new_index = if lowest_free_index > gens.len() {
        lowest_free_index as Index
    } else {
        gens.len() as Index
    };

    Entity(new_index, Generation(1))
}

/// Entity creation iterator. Will yield new empty entities infinitely.
/// Useful for bulk entity construction, since the locks are only happening once.
pub struct CreateEntityIter<'a> {
    gens: RwLockWriteGuard<'a, Vec<Generation>>,
    app: RwLockWriteGuard<'a, Appendix>,
}

impl<'a> Iterator for CreateEntityIter<'a> {
    type Item = Entity;
    fn next(&mut self) -> Option<Entity> {
        let ent = self.app.next;
        assert!(ent.get_gen().is_alive());
        if ent.get_gen().is_first() {
            assert!(self.gens.len() == ent.get_id());
            self.gens.push(ent.get_gen());
            self.app.next.0 += 1;
        } else {
            self.app.next = find_next(&self.gens, ent.get_id() + 1);
        }
        Some(ent)
    }
}

/// A custom entity iterator for dynamically added entities.
pub struct DynamicEntityIter<'a> {
    guard: RwLockReadGuard<'a, Appendix>,
    index: usize,
}

impl<'a> Iterator for DynamicEntityIter<'a> {
    type Item = Entity;
    fn next(&mut self) -> Option<Entity> {
        let ent = self.guard.add_queue.get(self.index);
        self.index += 1;
        ent.cloned()
    }
}

trait StorageLock: Any + Send + Sync {
    fn del_slice(&self, &[Entity]);
}

mopafy!(StorageLock);

impl<S: StorageBase + Any + Send + Sync> StorageLock for RwLock<S> {
    fn del_slice(&self, entities: &[Entity]) {
        let mut guard = self.write().unwrap();
        for &e in entities.iter() {
            guard.del(e);
        }
    }
}

/// The world struct contains all the data, which is entities and their components.
/// The methods are supposed to be valid for any context they are available in.
pub struct World {
    generations: RwLock<Vec<Generation>>,
    components: HashMap<TypeId, Box<StorageLock>>,
    appendix: RwLock<Appendix>,
}

impl World {
    /// Create a new empty world.
    pub fn new() -> World {
        World {
            generations: RwLock::new(Vec::new()),
            components: HashMap::new(),
            appendix: RwLock::new(Appendix {
                next: Entity(0, Generation(1)),
                add_queue: Vec::new(),
                sub_queue: Vec::new(),
            }),
        }
    }
    /// Register a new component type.
    pub fn register<T: Component>(&mut self) {
        let any = RwLock::new(T::Storage::new());
        self.components.insert(TypeId::of::<T>(), Box::new(any));
    }
    /// Unregister a component type.
    pub fn unregister<T: Component>(&mut self) -> Option<T::Storage> {
        self.components.remove(&TypeId::of::<T>()).map(|boxed|
            match boxed.downcast::<RwLock<T::Storage>>() {
                Ok(b) => (*b).into_inner().unwrap(),
                Err(_) => panic!("Unable to downcast the storage type"),
            }
        )
    }
    fn lock<T: Component>(&self) -> &RwLock<T::Storage> {
        let boxed = self.components.get(&TypeId::of::<T>())
            .expect("Tried to perform an operation on type that was not registered");
        boxed.downcast_ref().unwrap()
    }
    /// Lock a component's storage for reading.
    pub fn read<T: Component>(&self) -> RwLockReadGuard<T::Storage> {
        self.lock::<T>().read().unwrap()
    }
    /// Lock a component's storage for writing.
    pub fn write<T: Component>(&self) -> RwLockWriteGuard<T::Storage> {
        self.lock::<T>().write().unwrap()
    }
    /// Return the entity iterator.
    pub fn entities(&self) -> EntityIter {
        EntityIter {
            guard: self.generations.read().unwrap(),
            index: 0,
        }
    }
    /// Return the dynamic entity iterator. It goes through entities that were
    /// dynamically created by systems but not yet merged.
    pub fn dynamic_entities(&self) -> DynamicEntityIter {
        DynamicEntityIter {
            guard: self.appendix.read().unwrap(),
            index: 0,
        }
    }
    /// Return the entity creation iterator. Can be used to create many
    /// empty entities at once without paying the locking overhead.
    pub fn create_iter(&self) -> CreateEntityIter {
        CreateEntityIter {
            gens: self.generations.write().unwrap(),
            app: self.appendix.write().unwrap(),
        }
    }
    /// Create a new entity instantly, with locking the generations data.
    pub fn create_now(&self) -> EntityBuilder {
        let mut app = self.appendix.write().unwrap();
        let ent = app.next;
        assert!(ent.get_gen().is_alive());
        if ent.get_gen().is_first() {
            let mut gens = self.generations.write().unwrap();
            assert!(gens.len() == ent.get_id());
            gens.push(ent.get_gen());
            app.next.0 += 1;
        } else {
            let gens = self.generations.read().unwrap();
            app.next = find_next(&gens, ent.get_id() + 1);
        }
        EntityBuilder(ent, self)
    }
    /// Delete a new entity instantly, with locking the generations data.
    pub fn delete_now(&self, entity: Entity) {
        for comp in self.components.values() {
            comp.del_slice(&[entity]);
        }
        let mut gens = self.generations.write().unwrap();
        let mut gen = &mut gens[entity.get_id() as usize];
        gen.die();
        let mut app = self.appendix.write().unwrap();
        if entity.get_id() < app.next.get_id() {
            app.next = Entity(entity.0, gen.raised());
        }
    }
    /// Create a new entity dynamically.
    pub fn create_later(&self) -> Entity {
        let mut app = self.appendix.write().unwrap();
        let ent = app.next;
        app.add_queue.push(ent);
        app.next = find_next(&*self.generations.read().unwrap(), ent.get_id() + 1);
        ent
    }
    /// Delete an entity dynamically.
    pub fn delete_later(&self, entity: Entity) {
        let mut app = self.appendix.write().unwrap();
        app.sub_queue.push(entity);
    }
    /// Merge in the appendix, recording all the dynamically created
    /// and deleted entities into the persistent generations vector.
    /// Also removes all the abandoned components.
    pub fn merge(&self) {
        // We can't lock Appendix and components at the same time,
        // or otherwise we deadlock with a system that tries to process
        // newly added entities.
        // So we copy dead components out first, and then process them separately.
        let mut temp_list = Vec::new(); //TODO: avoid allocation
        {
            let mut gens = self.generations.write().unwrap();
            let mut app = self.appendix.write().unwrap();
            for ent in app.add_queue.drain(..) {
                while gens.len() <= ent.get_id() {
                    gens.push(Generation(0));
                }
                assert_eq!(ent.get_gen(), gens[ent.get_id()].raised());
                gens[ent.get_id()] = ent.get_gen();
            }
            let mut next = app.next;
            for ent in app.sub_queue.drain(..) {
                assert_eq!(ent.get_gen(), gens[ent.get_id()]);
                let gen = &mut gens[ent.get_id()];
                gen.die();
                temp_list.push(ent);
                if ent.get_id() < next.get_id() {
                    next = Entity(ent.0, gen.raised());
                }
            }
            app.next = next;
        }
        for comp in self.components.values() {
            comp.del_slice(&temp_list);
        }
    }
}

/// System fetch-time argument. The fetch is executed at the start of the run.
/// It contains a subset of World methods that make sense during initialization.
pub struct FetchArg<'a>(&'a World);

impl<'a> FetchArg<'a> {
    /// Construct the new arg, not supposed to be used.
    #[doc(hidden)]
    pub fn new(w: &'a World) -> FetchArg<'a> {
        FetchArg(w)
    }
    /// Lock a component for reading.
    pub fn read<T: Component>(&self) -> RwLockReadGuard<'a, T::Storage> {
        self.0.read::<T>()
    }
    /// Lock a component for writing.
    pub fn write<T: Component>(&self) -> RwLockWriteGuard<'a, T::Storage> {
        self.0.write::<T>()
    }
    /// Return the entity iterator.
    pub fn entities(self) -> EntityIter<'a> {
        self.0.entities()
    }
}
<|endoftext|>


<commit_before>use object::read::macho::DyldCache;
use object::Endianness;
use std::{env, fs, process};

fn main() {
    let arg_len = env::args().len();
    if arg_len <= 1 {
        eprintln!("Usage: {} <file> ...", env::args().next().unwrap());
        process::exit(1);
    }

    for file_path in env::args().skip(1) {
        if arg_len > 2 {
            println!();
            println!("{}:", file_path);
        }

        let file = match fs::File::open(&file_path) {
            Ok(file) => file,
            Err(err) => {
                println!("Failed to open file '{}': {}", file_path, err,);
                continue;
            }
        };
        let subcache_files = open_subcaches_if_exist(&file_path);
        let file = match unsafe { memmap2::Mmap::map(&file) } {
            Ok(mmap) => mmap,
            Err(err) => {
                println!("Failed to map file '{}': {}", file_path, err,);
                continue;
            }
        };
        let subcache_files: Option<Vec<_>> = subcache_files
            .into_iter()
            .map(
                |subcache_file| match unsafe { memmap2::Mmap::map(&subcache_file) } {
                    Ok(mmap) => Some(mmap),
                    Err(err) => {
                        eprintln!("Failed to map file '{}': {}", file_path, err);
                        None
                    }
                },
            )
            .collect();
        let subcache_files: Vec<&[u8]> = match &subcache_files {
            Some(subcache_files) => subcache_files
                .iter()
                .map(|subcache_file| &**subcache_file)
                .collect(),
            None => continue,
        };
        let cache = match DyldCache::<Endianness>::parse(&*file, &subcache_files) {
            Ok(cache) => cache,
            Err(err) => {
                println!(
                    "Failed to parse Dyld shared cache file '{}': {}",
                    file_path, err,
                );
                continue;
            }
        };

        // Print the list of image paths in this file.
        for image in cache.images() {
            if let Ok(path) = image.path() {
                println!("{}", path);
            }
        }
    }
}

// If the file is a dyld shared cache, and we're on macOS 12 or later,
// then there will be one or more "subcache" files next to this file,
// with the names filename.1, filename.2, ..., filename.symbols.
fn open_subcaches_if_exist(path: &str) -> Vec<fs::File> {
    let mut files = Vec::new();
    for i in 1.. {
        let subcache_path = format!("{}.{}", path, i);
        match fs::File::open(&subcache_path) {
            Ok(subcache_file) => files.push(subcache_file),
            Err(_) => break,
        };
    }
    let symbols_subcache_path = format!("{}.symbols", path);
    if let Ok(subcache_file) = fs::File::open(&symbols_subcache_path) {
        files.push(subcache_file);
    };
    println!("Found {} subcache files", files.len());
    files
}
<commit_msg>examples/dyldcachedump: fix finding dyld subcaches on macOS 13 (#472)<commit_after>use object::read::macho::DyldCache;
use object::Endianness;
use std::{env, fs, process};

fn main() {
    let arg_len = env::args().len();
    if arg_len <= 1 {
        eprintln!("Usage: {} <file> ...", env::args().next().unwrap());
        process::exit(1);
    }

    for file_path in env::args().skip(1) {
        if arg_len > 2 {
            println!();
            println!("{}:", file_path);
        }

        let file = match fs::File::open(&file_path) {
            Ok(file) => file,
            Err(err) => {
                println!("Failed to open file '{}': {}", file_path, err,);
                continue;
            }
        };
        let subcache_files = open_subcaches_if_exist(&file_path);
        let file = match unsafe { memmap2::Mmap::map(&file) } {
            Ok(mmap) => mmap,
            Err(err) => {
                println!("Failed to map file '{}': {}", file_path, err,);
                continue;
            }
        };
        let subcache_files: Option<Vec<_>> = subcache_files
            .into_iter()
            .map(
                |subcache_file| match unsafe { memmap2::Mmap::map(&subcache_file) } {
                    Ok(mmap) => Some(mmap),
                    Err(err) => {
                        eprintln!("Failed to map file '{}': {}", file_path, err);
                        None
                    }
                },
            )
            .collect();
        let subcache_files: Vec<&[u8]> = match &subcache_files {
            Some(subcache_files) => subcache_files
                .iter()
                .map(|subcache_file| &**subcache_file)
                .collect(),
            None => continue,
        };
        let cache = match DyldCache::<Endianness>::parse(&*file, &subcache_files) {
            Ok(cache) => cache,
            Err(err) => {
                println!(
                    "Failed to parse Dyld shared cache file '{}': {}",
                    file_path, err,
                );
                continue;
            }
        };

        // Print the list of image paths in this file.
        for image in cache.images() {
            if let Ok(path) = image.path() {
                println!("{}", path);
            }
        }
    }
}

// If the file is a dyld shared cache, and we're on macOS 12 or later,
// then there will be one or more "subcache" files next to this file,
// with the names filename.1, filename.2, ..., filename.symbols
// or filename.01, filename.02 on macOS 13
fn open_subcaches_if_exist(path: &str) -> Vec<fs::File> {
    let mut files = Vec::new();
    for i in 1.. {
        let subcache_path = format!("{}.{}", path, i);
        match fs::File::open(&subcache_path) {
            Ok(subcache_file) => files.push(subcache_file),
            Err(_) => break,
        };
    }
    if files.is_empty() {
        for i in 1.. {
            let subcache_path = format!("{}.{:02}", path, i);
            match fs::File::open(&subcache_path) {
                Ok(subcache_file) => files.push(subcache_file),
                Err(_) => break,
            };
        }
    }
    let symbols_subcache_path = format!("{}.symbols", path);
    if let Ok(subcache_file) = fs::File::open(&symbols_subcache_path) {
        files.push(subcache_file);
    };
    println!("Found {} subcache files", files.len());
    files
}
<|endoftext|>


<commit_before>// Copyright (C) 2019 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: Apache-2.0

use super::*;
use crate::{
    cloud_mock,
    firmware::{
        tests::{create_fake_metadata, FakeDevice},
        Metadata,
    },
    runtime_settings::RuntimeSettings,
    settings::Settings,
};
use actix::{Addr, MessageResult, System};
use pretty_assertions::assert_eq;
use std::fs;

enum Setup {
    HasUpdate,
    NoUpdate,
}

enum Probe {
    Enabled,
    Disabled,
}

#[derive(Default)]
struct FakeMachine {
    step_count: usize,
    step_expect: usize,
}

impl Actor for FakeMachine {
    type Context = Context<Self>;

    // In tests, only one reference to the Actor's Addr is held, and it is held by
    // the stepper, when it stops the system can be shutdown and we can assert the
    // number of steppers received
    fn stopped(&mut self, _: &mut Context<Self>) {
        assert_eq!(self.step_count, self.step_expect);
        System::current().stop();
    }
}

impl Handler<Step> for FakeMachine {
    type Result = MessageResult<Step>;

    fn handle(&mut self, _: Step, _: &mut Context<Self>) -> Self::Result {
        self.step_count += 1;
        if self.step_count >= self.step_expect {
            MessageResult(super::StepTransition::Never)
        } else {
            MessageResult(super::StepTransition::Immediate)
        }
    }
}

fn setup_actor(kind: Setup, probe: Probe) -> (Addr<Machine>, Settings, Metadata) {
    let tmpfile = tempfile::NamedTempFile::new().unwrap();
    let tmpfile = tmpfile.path();
    fs::remove_file(&tmpfile).unwrap();
    let mut settings = Settings::default();
    settings.polling.enabled = match probe {
        Probe::Enabled => true,
        Probe::Disabled => false,
    };
    let runtime_settings = RuntimeSettings::load(tmpfile).unwrap();
    let firmware = Metadata::from_path(&create_fake_metadata(match kind {
        Setup::HasUpdate => FakeDevice::HasUpdate,
        Setup::NoUpdate => FakeDevice::NoUpdate,
    }))
    .unwrap();

    let settings_clone = settings.clone();
    let firmware_clone = firmware.clone();
    match kind {
        Setup::HasUpdate => cloud_mock::setup_fake_response(cloud_mock::FakeResponse::HasUpdate),
        Setup::NoUpdate => cloud_mock::setup_fake_response(cloud_mock::FakeResponse::NoUpdate),
    }

    (
        // We use the actix::Actor::start here instead of the Machine::start in order to not
        // start the stepper and thus have control of how many steps are been sent to the Machine
        actix::Actor::start(Machine::new(
            StateMachine::EntryPoint(State(EntryPoint {})),
            settings,
            runtime_settings,
            firmware,
        )),
        settings_clone,
        firmware_clone,
    )
}

#[actix_rt::test]
async fn info_request() {
    let (addr, settings, firmware) = setup_actor(Setup::NoUpdate, Probe::Enabled);
    let response = addr.send(info::Request).await.unwrap();
    assert_eq!(response.state, "entry_point");
    assert_eq!(response.version, crate::version().to_string());
    assert_eq!(response.config, settings.0);
    assert_eq!(response.firmware, firmware.0);
}

#[actix_rt::test]
async fn step_sequence() {
    let (addr, ..) = setup_actor(Setup::NoUpdate, Probe::Enabled);
    let response = addr.send(info::Request).await.unwrap();
    assert_eq!(response.state, "entry_point");

    addr.send(Step).await.unwrap();
    let res = addr.send(info::Request).await.unwrap();
    assert_eq!(res.state, "poll");

    addr.send(Step).await.unwrap();
    let res = addr.send(info::Request).await.unwrap();
    assert_eq!(res.state, "probe");

    addr.send(Step).await.unwrap();
    let res = addr.send(info::Request).await.unwrap();
    assert_eq!(res.state, "entry_point");
}

#[actix_rt::test]
async fn download_abort() {
    let (addr, ..) = setup_actor(Setup::HasUpdate, Probe::Enabled);
    addr.send(Step).await.unwrap(); // Idle -> Poll
    addr.send(Step).await.unwrap(); // Poll -> Probe
    addr.send(Step).await.unwrap(); // Probe -> Validation
    addr.send(Step).await.unwrap(); // Validation -> PrepareDownload
    let res = addr.send(info::Request).await.unwrap();
    assert_eq!(res.state, "prepare_download");

    addr.send(download_abort::Request).await.unwrap();
    let res = addr.send(info::Request).await.unwrap();
    assert_eq!(res.state, "entry_point");
}

#[actix_rt::test]
async fn trigger_probe() {
    let (addr, ..) = setup_actor(Setup::NoUpdate, Probe::Disabled);
    addr.send(Step).await.unwrap();
    let res = addr.send(info::Request).await.unwrap();
    assert_eq!(res.state, "park");

    addr.send(probe::Request(None)).await.unwrap().unwrap();
    let res = addr.send(info::Request).await.unwrap();
    assert_eq!(res.state, "entry_point");
}

#[actix_rt::test]
async fn local_install_probe() {
    let (addr, ..) = setup_actor(Setup::NoUpdate, Probe::Disabled);
    addr.send(Step).await.unwrap();
    let res = addr.send(info::Request).await.unwrap();
    assert_eq!(res.state, "park");

    addr.send(local_install::Request(std::path::PathBuf::from("/foo/bar"))).await.unwrap();
    let res = addr.send(info::Request).await.unwrap();
    assert_eq!(res.state, "prepare_local_install");
}

#[actix_rt::test]
async fn stepper_with_never() {
    let mock = actix::Actor::start(FakeMachine { step_expect: 15, ..FakeMachine::default() });
    let mut stepper = super::stepper::Controller::default();
    stepper.start(mock);
}
<commit_msg>actor: Make stepper_with_never single thread compatible<commit_after>// Copyright (C) 2019 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: Apache-2.0

use super::*;
use crate::{
    cloud_mock,
    firmware::{
        tests::{create_fake_metadata, FakeDevice},
        Metadata,
    },
    runtime_settings::RuntimeSettings,
    settings::Settings,
};
use actix::{Addr, MessageResult, System};
use pretty_assertions::assert_eq;
use std::fs;

enum Setup {
    HasUpdate,
    NoUpdate,
}

enum Probe {
    Enabled,
    Disabled,
}

#[derive(Default)]
struct FakeMachine {
    step_count: usize,
    step_expect: usize,
}

impl Actor for FakeMachine {
    type Context = Context<Self>;

    // In tests, only one reference to the Actor's Addr is held, and it is held by
    // the stepper, when it stops the system can be shutdown and we can assert the
    // number of steppers received
    fn stopped(&mut self, _: &mut Context<Self>) {
        assert_eq!(self.step_count, self.step_expect);
        System::current().stop();
    }
}

impl Handler<Step> for FakeMachine {
    type Result = MessageResult<Step>;

    fn handle(&mut self, _: Step, _: &mut Context<Self>) -> Self::Result {
        self.step_count += 1;
        if self.step_count >= self.step_expect {
            MessageResult(super::StepTransition::Never)
        } else {
            MessageResult(super::StepTransition::Immediate)
        }
    }
}

fn setup_actor(kind: Setup, probe: Probe) -> (Addr<Machine>, Settings, Metadata) {
    let tmpfile = tempfile::NamedTempFile::new().unwrap();
    let tmpfile = tmpfile.path();
    fs::remove_file(&tmpfile).unwrap();
    let mut settings = Settings::default();
    settings.polling.enabled = match probe {
        Probe::Enabled => true,
        Probe::Disabled => false,
    };
    let runtime_settings = RuntimeSettings::load(tmpfile).unwrap();
    let firmware = Metadata::from_path(&create_fake_metadata(match kind {
        Setup::HasUpdate => FakeDevice::HasUpdate,
        Setup::NoUpdate => FakeDevice::NoUpdate,
    }))
    .unwrap();

    let settings_clone = settings.clone();
    let firmware_clone = firmware.clone();
    match kind {
        Setup::HasUpdate => cloud_mock::setup_fake_response(cloud_mock::FakeResponse::HasUpdate),
        Setup::NoUpdate => cloud_mock::setup_fake_response(cloud_mock::FakeResponse::NoUpdate),
    }

    (
        // We use the actix::Actor::start here instead of the Machine::start in order to not
        // start the stepper and thus have control of how many steps are been sent to the Machine
        actix::Actor::start(Machine::new(
            StateMachine::EntryPoint(State(EntryPoint {})),
            settings,
            runtime_settings,
            firmware,
        )),
        settings_clone,
        firmware_clone,
    )
}

#[actix_rt::test]
async fn info_request() {
    let (addr, settings, firmware) = setup_actor(Setup::NoUpdate, Probe::Enabled);
    let response = addr.send(info::Request).await.unwrap();
    assert_eq!(response.state, "entry_point");
    assert_eq!(response.version, crate::version().to_string());
    assert_eq!(response.config, settings.0);
    assert_eq!(response.firmware, firmware.0);
}

#[actix_rt::test]
async fn step_sequence() {
    let (addr, ..) = setup_actor(Setup::NoUpdate, Probe::Enabled);
    let response = addr.send(info::Request).await.unwrap();
    assert_eq!(response.state, "entry_point");

    addr.send(Step).await.unwrap();
    let res = addr.send(info::Request).await.unwrap();
    assert_eq!(res.state, "poll");

    addr.send(Step).await.unwrap();
    let res = addr.send(info::Request).await.unwrap();
    assert_eq!(res.state, "probe");

    addr.send(Step).await.unwrap();
    let res = addr.send(info::Request).await.unwrap();
    assert_eq!(res.state, "entry_point");
}

#[actix_rt::test]
async fn download_abort() {
    let (addr, ..) = setup_actor(Setup::HasUpdate, Probe::Enabled);
    addr.send(Step).await.unwrap(); // Idle -> Poll
    addr.send(Step).await.unwrap(); // Poll -> Probe
    addr.send(Step).await.unwrap(); // Probe -> Validation
    addr.send(Step).await.unwrap(); // Validation -> PrepareDownload
    let res = addr.send(info::Request).await.unwrap();
    assert_eq!(res.state, "prepare_download");

    addr.send(download_abort::Request).await.unwrap();
    let res = addr.send(info::Request).await.unwrap();
    assert_eq!(res.state, "entry_point");
}

#[actix_rt::test]
async fn trigger_probe() {
    let (addr, ..) = setup_actor(Setup::NoUpdate, Probe::Disabled);
    addr.send(Step).await.unwrap();
    let res = addr.send(info::Request).await.unwrap();
    assert_eq!(res.state, "park");

    addr.send(probe::Request(None)).await.unwrap().unwrap();
    let res = addr.send(info::Request).await.unwrap();
    assert_eq!(res.state, "entry_point");
}

#[actix_rt::test]
async fn local_install_probe() {
    let (addr, ..) = setup_actor(Setup::NoUpdate, Probe::Disabled);
    addr.send(Step).await.unwrap();
    let res = addr.send(info::Request).await.unwrap();
    assert_eq!(res.state, "park");

    addr.send(local_install::Request(std::path::PathBuf::from("/foo/bar"))).await.unwrap();
    let res = addr.send(info::Request).await.unwrap();
    assert_eq!(res.state, "prepare_local_install");
}

#[test]
fn stepper_with_never() {
    let sys = actix_rt::System::new("stepper_with_never");
    let mock = actix::Actor::start(FakeMachine { step_expect: 15, ..FakeMachine::default() });
    let mut stepper = super::stepper::Controller::default();
    stepper.start(mock);
    sys.run().unwrap();
}
<|endoftext|>


<commit_before>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Martin Kysel <code@martinkysel.com>
//  *
//  * For the full copyright and license information, please view the LICENSE file
//  * that was distributed with this source code.

// spell-checker:ignore (ToDO) lflag ICANON tcgetattr tcsetattr TCSADRAIN

#[macro_use]
extern crate uucore;

use std::{
    convert::TryInto,
    fs::File,
    io::{stdin, stdout, BufReader, Read, Stdout, Write},
    path::Path,
    time::Duration,
};

#[cfg(all(unix, not(target_os = "fuchsia")))]
extern crate nix;

use clap::{App, Arg};
use crossterm::{
    event::{self, Event, KeyCode, KeyEvent, KeyModifiers},
    execute, queue,
    style::Attribute,
    terminal,
};

pub mod options {
    pub const SILENT: &str = "silent";
    pub const LOGICAL: &str = "logical";
    pub const NO_PAUSE: &str = "no-pause";
    pub const PRINT_OVER: &str = "print-over";
    pub const CLEAN_PRINT: &str = "clean-print";
    pub const SQUEEZE: &str = "squeeze";
    pub const PLAIN: &str = "plain";
    pub const LINES: &str = "lines";
    pub const NUMBER: &str = "number";
    pub const PATTERN: &str = "pattern";
    pub const FROM_LINE: &str = "from-line";
    pub const FILES: &str = "files";
}

const MULTI_FILE_TOP_PROMPT: &str = "::::::::::::::\n{}\n::::::::::::::\n";

pub fn uumain(args: impl uucore::Args) -> i32 {
    let matches = App::new(executable!())
        .about("A file perusal filter for CRT viewing.")
        .version(env!("CARGO_PKG_VERSION"))
        .arg(
            Arg::with_name(options::SILENT)
                .short("d")
                .long(options::SILENT)
                .help("Display help instead of ringing bell"),
        )
        .arg(
            Arg::with_name(options::LOGICAL)
                .short("f")
                .long(options::LOGICAL)
                .help("Count logical rather than screen lines"),
        )
        .arg(
            Arg::with_name(options::NO_PAUSE)
                .short("l")
                .long(options::NO_PAUSE)
                .help("Suppress pause after form feed"),
        )
        .arg(
            Arg::with_name(options::PRINT_OVER)
                .short("c")
                .long(options::PRINT_OVER)
                .help("Do not scroll, display text and clean line ends"),
        )
        .arg(
            Arg::with_name(options::CLEAN_PRINT)
                .short("p")
                .long(options::CLEAN_PRINT)
                .help("Do not scroll, clean screen and display text"),
        )
        .arg(
            Arg::with_name(options::SQUEEZE)
                .short("s")
                .long(options::SQUEEZE)
                .help("Squeeze multiple blank lines into one"),
        )
        .arg(
            Arg::with_name(options::PLAIN)
                .short("u")
                .long(options::PLAIN)
                .help("Suppress underlining and bold"),
        )
        .arg(
            Arg::with_name(options::LINES)
                .short("n")
                .long(options::LINES)
                .value_name("number")
                .takes_value(true)
                .help("The number of lines per screenful"),
        )
        .arg(
            Arg::with_name(options::NUMBER)
                .allow_hyphen_values(true)
                .long(options::NUMBER)
                .required(false)
                .takes_value(true)
                .help("Same as --lines"),
        )
        .arg(
            Arg::with_name(options::FROM_LINE)
                .short("F")
                .allow_hyphen_values(true)
                .required(false)
                .takes_value(true)
                .value_name("number")
                .help("Display file beginning from line number"),
        )
        .arg(
            Arg::with_name(options::PATTERN)
                .short("P")
                .allow_hyphen_values(true)
                .required(false)
                .takes_value(true)
                .help("Display file beginning from pattern match"),
        )
        .arg(
            Arg::with_name(options::FILES)
                .required(false)
                .multiple(true)
                .help("Path to the files to be read"),
        )
        .get_matches_from(args);
    let mut buff = String::new();
    if let Some(filenames) = matches.values_of(options::FILES) {
        let mut stdout = setup_term();
        let length = filenames.len();
        for (idx, fname) in filenames.enumerate() {
            let fname = Path::new(fname);
            if fname.is_dir() {
                terminal::disable_raw_mode().unwrap();
                show_usage_error!("'{}' is a directory.", fname.display());
                return 1;
            }
            if !fname.exists() {
                terminal::disable_raw_mode().unwrap();
                show_error!(
                    "cannot open {}: No such file or directory",
                    fname.display()
                );
                return 1;
            }
            if length > 1 {
                buff.push_str(&MULTI_FILE_TOP_PROMPT.replace("{}", fname.to_str().unwrap()));
            }
            let mut reader = BufReader::new(File::open(fname).unwrap());
            reader.read_to_string(&mut buff).unwrap();
            let is_last = idx + 1 == length;
            more(&buff, &mut stdout, is_last);
            buff.clear();
        }
        reset_term(&mut stdout);
    } else if atty::isnt(atty::Stream::Stdin) {
        stdin().read_to_string(&mut buff).unwrap();
        let mut stdout = setup_term();
        more(&buff, &mut stdout, true);
        reset_term(&mut stdout);
    } else {
        show_usage_error!("bad usage");
    }
    0
}

#[cfg(not(target_os = "fuchsia"))]
fn setup_term() -> std::io::Stdout {
    let stdout = stdout();
    terminal::enable_raw_mode().unwrap();
    stdout
}

#[cfg(target_os = "fuchsia")]
#[inline(always)]
fn setup_term() -> usize {
    0
}

#[cfg(not(target_os = "fuchsia"))]
fn reset_term(stdout: &mut std::io::Stdout) {
    terminal::disable_raw_mode().unwrap();
    // Clear the prompt
    queue!(stdout, terminal::Clear(terminal::ClearType::CurrentLine)).unwrap();
    // Move cursor to the beginning without printing new line
    print!("\r");
    stdout.flush().unwrap();
}

#[cfg(target_os = "fuchsia")]
#[inline(always)]
fn reset_term(_: &mut usize) {}

fn more(buff: &str, mut stdout: &mut Stdout, is_last: bool) {
    let (cols, rows) = terminal::size().unwrap();
    let lines = break_buff(buff, usize::from(cols));
    let line_count: u16 = lines.len().try_into().unwrap();

    let mut upper_mark = 0;
    let mut lines_left = line_count.saturating_sub(upper_mark + rows);

    draw(
        &mut upper_mark,
        rows,
        &mut stdout,
        lines.clone(),
        line_count,
    );

    // Specifies whether we have reached the end of the file and should
    // return on the next keypress. However, we immediately return when
    // this is the last file.
    let mut to_be_done = false;
    if lines_left == 0 && is_last {
        if is_last {
            return;
        } else {
            to_be_done = true;
        }
    }

    loop {
        if event::poll(Duration::from_millis(10)).unwrap() {
            match event::read().unwrap() {
                Event::Key(KeyEvent {
                    code: KeyCode::Char('q'),
                    modifiers: KeyModifiers::NONE,
                })
                | Event::Key(KeyEvent {
                    code: KeyCode::Char('c'),
                    modifiers: KeyModifiers::CONTROL,
                }) => {
                    reset_term(&mut stdout);
                    std::process::exit(0);
                }
                Event::Key(KeyEvent {
                    code: KeyCode::Down,
                    modifiers: KeyModifiers::NONE,
                })
                | Event::Key(KeyEvent {
                    code: KeyCode::Char(' '),
                    modifiers: KeyModifiers::NONE,
                }) => {
                    upper_mark = upper_mark.saturating_add(rows.saturating_sub(1));

                }
                Event::Key(KeyEvent {
                    code: KeyCode::Up,
                    modifiers: KeyModifiers::NONE,
                }) => {
                    upper_mark = upper_mark.saturating_sub(rows.saturating_sub(1));
                }
                _ => continue,
            }
            lines_left = line_count.saturating_sub(upper_mark + rows);
            draw(
                &mut upper_mark,
                rows,
                &mut stdout,
                lines.clone(),
                line_count,
            );

            if lines_left == 0 {
                if to_be_done || is_last {
                    return
                }
                to_be_done = true;
            }
        }
    }
}

fn draw(
    upper_mark: &mut u16,
    rows: u16,
    mut stdout: &mut std::io::Stdout,
    lines: Vec<String>,
    lc: u16,
) {
    execute!(stdout, terminal::Clear(terminal::ClearType::CurrentLine)).unwrap();
    let (up_mark, lower_mark) = calc_range(*upper_mark, rows, lc);
    // Reduce the row by 1 for the prompt
    let displayed_lines = lines
        .iter()
        .skip(up_mark.into())
        .take(usize::from(rows.saturating_sub(1)));

    for line in displayed_lines {
        stdout
            .write_all(format!("\r{}\n", line).as_bytes())
            .unwrap();
    }
    make_prompt_and_flush(&mut stdout, lower_mark, lc);
    *upper_mark = up_mark;
}

// Break the lines on the cols of the terminal
fn break_buff(buff: &str, cols: usize) -> Vec<String> {
    let mut lines = Vec::new();

    for l in buff.lines() {
        lines.append(&mut break_line(l, cols));
    }
    lines
}

fn break_line(mut line: &str, cols: usize) -> Vec<String> {
    let breaks = (line.len() / cols).saturating_add(1);
    let mut lines = Vec::with_capacity(breaks);
    // TODO: Use unicode width instead of the length in bytes.
    if line.len() < cols {
        lines.push(line.to_string());
        return lines;
    }

    for _ in 1..=breaks {
        let (line1, line2) = line.split_at(cols);
        lines.push(line1.to_string());
        if line2.len() < cols {
            lines.push(line2.to_string());
            break;
        }
        line = line2;
    }
    lines
}

// Calculate upper_mark based on certain parameters
fn calc_range(mut upper_mark: u16, rows: u16, line_count: u16) -> (u16, u16) {
    let mut lower_mark = upper_mark.saturating_add(rows);

    if lower_mark >= line_count {
        upper_mark = line_count.saturating_sub(rows);
        lower_mark = line_count;
    } else {
        lower_mark = lower_mark.saturating_sub(1)
    }
    (upper_mark, lower_mark)
}

// Make a prompt similar to original more
fn make_prompt_and_flush(stdout: &mut Stdout, lower_mark: u16, lc: u16) {
    write!(
        stdout,
        "\r{}--More--({}%){}",
        Attribute::Reverse,
        ((lower_mark as f64 / lc as f64) * 100.0).round() as u16,
        Attribute::Reset
    )
    .unwrap();
    stdout.flush().unwrap();
}

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

    // It is good to test the above functions
    #[test]
    fn test_calc_range() {
        assert_eq!((0, 24), calc_range(0, 25, 100));
        assert_eq!((50, 74), calc_range(50, 25, 100));
        assert_eq!((75, 100), calc_range(85, 25, 100));
    }
    #[test]
    fn test_break_lines_long() {
        let mut test_string = String::with_capacity(100);
        for _ in 0..200 {
            test_string.push('#');
        }

        let lines = break_line(&test_string, 80);

        assert_eq!(
            (80, 80, 40),
            (lines[0].len(), lines[1].len(), lines[2].len())
        );
    }

    #[test]
    fn test_break_lines_short() {
        let mut test_string = String::with_capacity(100);
        for _ in 0..20 {
            test_string.push('#');
        }

        let lines = break_line(&test_string, 80);

        assert_eq!(20, lines[0].len());
    }
}
<commit_msg>more: comment out unimplemented arguments<commit_after>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Martin Kysel <code@martinkysel.com>
//  *
//  * For the full copyright and license information, please view the LICENSE file
//  * that was distributed with this source code.

// spell-checker:ignore (ToDO) lflag ICANON tcgetattr tcsetattr TCSADRAIN

#[macro_use]
extern crate uucore;

use std::{
    convert::TryInto,
    fs::File,
    io::{stdin, stdout, BufReader, Read, Stdout, Write},
    path::Path,
    time::Duration,
};

#[cfg(all(unix, not(target_os = "fuchsia")))]
extern crate nix;

use clap::{App, Arg};
use crossterm::{
    event::{self, Event, KeyCode, KeyEvent, KeyModifiers},
    execute, queue,
    style::Attribute,
    terminal,
};

pub mod options {
    pub const SILENT: &str = "silent";
    pub const LOGICAL: &str = "logical";
    pub const NO_PAUSE: &str = "no-pause";
    pub const PRINT_OVER: &str = "print-over";
    pub const CLEAN_PRINT: &str = "clean-print";
    pub const SQUEEZE: &str = "squeeze";
    pub const PLAIN: &str = "plain";
    pub const LINES: &str = "lines";
    pub const NUMBER: &str = "number";
    pub const PATTERN: &str = "pattern";
    pub const FROM_LINE: &str = "from-line";
    pub const FILES: &str = "files";
}

const MULTI_FILE_TOP_PROMPT: &str = "::::::::::::::\n{}\n::::::::::::::\n";

pub fn uumain(args: impl uucore::Args) -> i32 {
    let matches = App::new(executable!())
        .about("A file perusal filter for CRT viewing.")
        .version(env!("CARGO_PKG_VERSION"))
        // The commented arguments below are unimplemented:
        /*
        .arg(
            Arg::with_name(options::SILENT)
                .short("d")
                .long(options::SILENT)
                .help("Display help instead of ringing bell"),
        )
        .arg(
            Arg::with_name(options::LOGICAL)
                .short("f")
                .long(options::LOGICAL)
                .help("Count logical rather than screen lines"),
        )
        .arg(
            Arg::with_name(options::NO_PAUSE)
                .short("l")
                .long(options::NO_PAUSE)
                .help("Suppress pause after form feed"),
        )
        .arg(
            Arg::with_name(options::PRINT_OVER)
                .short("c")
                .long(options::PRINT_OVER)
                .help("Do not scroll, display text and clean line ends"),
        )
        .arg(
            Arg::with_name(options::CLEAN_PRINT)
                .short("p")
                .long(options::CLEAN_PRINT)
                .help("Do not scroll, clean screen and display text"),
        )
        .arg(
            Arg::with_name(options::SQUEEZE)
                .short("s")
                .long(options::SQUEEZE)
                .help("Squeeze multiple blank lines into one"),
        )
        .arg(
            Arg::with_name(options::PLAIN)
                .short("u")
                .long(options::PLAIN)
                .help("Suppress underlining and bold"),
        )
        .arg(
            Arg::with_name(options::LINES)
                .short("n")
                .long(options::LINES)
                .value_name("number")
                .takes_value(true)
                .help("The number of lines per screenful"),
        )
        .arg(
            Arg::with_name(options::NUMBER)
                .allow_hyphen_values(true)
                .long(options::NUMBER)
                .required(false)
                .takes_value(true)
                .help("Same as --lines"),
        )
        .arg(
            Arg::with_name(options::FROM_LINE)
                .short("F")
                .allow_hyphen_values(true)
                .required(false)
                .takes_value(true)
                .value_name("number")
                .help("Display file beginning from line number"),
        )
        .arg(
            Arg::with_name(options::PATTERN)
                .short("P")
                .allow_hyphen_values(true)
                .required(false)
                .takes_value(true)
                .help("Display file beginning from pattern match"),
        )
        */
        .arg(
            Arg::with_name(options::FILES)
                .required(false)
                .multiple(true)
                .help("Path to the files to be read"),
        )
        .get_matches_from(args);

    let mut buff = String::new();
    if let Some(filenames) = matches.values_of(options::FILES) {
        let mut stdout = setup_term();
        let length = filenames.len();
        for (idx, fname) in filenames.enumerate() {
            let fname = Path::new(fname);
            if fname.is_dir() {
                terminal::disable_raw_mode().unwrap();
                show_usage_error!("'{}' is a directory.", fname.display());
                return 1;
            }
            if !fname.exists() {
                terminal::disable_raw_mode().unwrap();
                show_error!(
                    "cannot open {}: No such file or directory",
                    fname.display()
                );
                return 1;
            }
            if length > 1 {
                buff.push_str(&MULTI_FILE_TOP_PROMPT.replace("{}", fname.to_str().unwrap()));
            }
            let mut reader = BufReader::new(File::open(fname).unwrap());
            reader.read_to_string(&mut buff).unwrap();
            let is_last = idx + 1 == length;
            more(&buff, &mut stdout, is_last);
            buff.clear();
        }
        reset_term(&mut stdout);
    } else if atty::isnt(atty::Stream::Stdin) {
        stdin().read_to_string(&mut buff).unwrap();
        let mut stdout = setup_term();
        more(&buff, &mut stdout, true);
        reset_term(&mut stdout);
    } else {
        show_usage_error!("bad usage");
    }
    0
}

#[cfg(not(target_os = "fuchsia"))]
fn setup_term() -> std::io::Stdout {
    let stdout = stdout();
    terminal::enable_raw_mode().unwrap();
    stdout
}

#[cfg(target_os = "fuchsia")]
#[inline(always)]
fn setup_term() -> usize {
    0
}

#[cfg(not(target_os = "fuchsia"))]
fn reset_term(stdout: &mut std::io::Stdout) {
    terminal::disable_raw_mode().unwrap();
    // Clear the prompt
    queue!(stdout, terminal::Clear(terminal::ClearType::CurrentLine)).unwrap();
    // Move cursor to the beginning without printing new line
    print!("\r");
    stdout.flush().unwrap();
}

#[cfg(target_os = "fuchsia")]
#[inline(always)]
fn reset_term(_: &mut usize) {}

fn more(buff: &str, mut stdout: &mut Stdout, is_last: bool) {
    let (cols, rows) = terminal::size().unwrap();
    let lines = break_buff(buff, usize::from(cols));
    let line_count: u16 = lines.len().try_into().unwrap();

    let mut upper_mark = 0;
    let mut lines_left = line_count.saturating_sub(upper_mark + rows);

    draw(
        &mut upper_mark,
        rows,
        &mut stdout,
        lines.clone(),
        line_count,
    );

    // Specifies whether we have reached the end of the file and should
    // return on the next keypress. However, we immediately return when
    // this is the last file.
    let mut to_be_done = false;
    if lines_left == 0 && is_last {
        if is_last {
            return;
        } else {
            to_be_done = true;
        }
    }

    loop {
        if event::poll(Duration::from_millis(10)).unwrap() {
            match event::read().unwrap() {
                Event::Key(KeyEvent {
                    code: KeyCode::Char('q'),
                    modifiers: KeyModifiers::NONE,
                })
                | Event::Key(KeyEvent {
                    code: KeyCode::Char('c'),
                    modifiers: KeyModifiers::CONTROL,
                }) => {
                    reset_term(&mut stdout);
                    std::process::exit(0);
                }
                Event::Key(KeyEvent {
                    code: KeyCode::Down,
                    modifiers: KeyModifiers::NONE,
                })
                | Event::Key(KeyEvent {
                    code: KeyCode::Char(' '),
                    modifiers: KeyModifiers::NONE,
                }) => {
                    upper_mark = upper_mark.saturating_add(rows.saturating_sub(1));

                }
                Event::Key(KeyEvent {
                    code: KeyCode::Up,
                    modifiers: KeyModifiers::NONE,
                }) => {
                    upper_mark = upper_mark.saturating_sub(rows.saturating_sub(1));
                }
                _ => continue,
            }
            lines_left = line_count.saturating_sub(upper_mark + rows);
            draw(
                &mut upper_mark,
                rows,
                &mut stdout,
                lines.clone(),
                line_count,
            );

            if lines_left == 0 {
                if to_be_done || is_last {
                    return
                }
                to_be_done = true;
            }
        }
    }
}

fn draw(
    upper_mark: &mut u16,
    rows: u16,
    mut stdout: &mut std::io::Stdout,
    lines: Vec<String>,
    lc: u16,
) {
    execute!(stdout, terminal::Clear(terminal::ClearType::CurrentLine)).unwrap();
    let (up_mark, lower_mark) = calc_range(*upper_mark, rows, lc);
    // Reduce the row by 1 for the prompt
    let displayed_lines = lines
        .iter()
        .skip(up_mark.into())
        .take(usize::from(rows.saturating_sub(1)));

    for line in displayed_lines {
        stdout
            .write_all(format!("\r{}\n", line).as_bytes())
            .unwrap();
    }
    make_prompt_and_flush(&mut stdout, lower_mark, lc);
    *upper_mark = up_mark;
}

// Break the lines on the cols of the terminal
fn break_buff(buff: &str, cols: usize) -> Vec<String> {
    let mut lines = Vec::new();

    for l in buff.lines() {
        lines.append(&mut break_line(l, cols));
    }
    lines
}

fn break_line(mut line: &str, cols: usize) -> Vec<String> {
    let breaks = (line.len() / cols).saturating_add(1);
    let mut lines = Vec::with_capacity(breaks);
    // TODO: Use unicode width instead of the length in bytes.
    if line.len() < cols {
        lines.push(line.to_string());
        return lines;
    }

    for _ in 1..=breaks {
        let (line1, line2) = line.split_at(cols);
        lines.push(line1.to_string());
        if line2.len() < cols {
            lines.push(line2.to_string());
            break;
        }
        line = line2;
    }
    lines
}

// Calculate upper_mark based on certain parameters
fn calc_range(mut upper_mark: u16, rows: u16, line_count: u16) -> (u16, u16) {
    let mut lower_mark = upper_mark.saturating_add(rows);

    if lower_mark >= line_count {
        upper_mark = line_count.saturating_sub(rows);
        lower_mark = line_count;
    } else {
        lower_mark = lower_mark.saturating_sub(1)
    }
    (upper_mark, lower_mark)
}

// Make a prompt similar to original more
fn make_prompt_and_flush(stdout: &mut Stdout, lower_mark: u16, lc: u16) {
    write!(
        stdout,
        "\r{}--More--({}%){}",
        Attribute::Reverse,
        ((lower_mark as f64 / lc as f64) * 100.0).round() as u16,
        Attribute::Reset
    )
    .unwrap();
    stdout.flush().unwrap();
}

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

    // It is good to test the above functions
    #[test]
    fn test_calc_range() {
        assert_eq!((0, 24), calc_range(0, 25, 100));
        assert_eq!((50, 74), calc_range(50, 25, 100));
        assert_eq!((75, 100), calc_range(85, 25, 100));
    }
    #[test]
    fn test_break_lines_long() {
        let mut test_string = String::with_capacity(100);
        for _ in 0..200 {
            test_string.push('#');
        }

        let lines = break_line(&test_string, 80);

        assert_eq!(
            (80, 80, 40),
            (lines[0].len(), lines[1].len(), lines[2].len())
        );
    }

    #[test]
    fn test_break_lines_short() {
        let mut test_string = String::with_capacity(100);
        for _ in 0..20 {
            test_string.push('#');
        }

        let lines = break_line(&test_string, 80);

        assert_eq!(20, lines[0].len());
    }
}
<|endoftext|>