<commit_before>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{errors::JsonRpcError, views::AccountView, JsonRpcResponse};
use anyhow::{ensure, format_err, Error, Result};
use libra_types::{account_address::AccountAddress, transaction::SignedTransaction};
use reqwest::{Client, ClientBuilder, Url};
use serde_json::{json, Value};
use std::{collections::HashSet, convert::TryFrom, fmt, time::Duration};

#[derive(Clone, Default)]
pub struct JsonRpcBatch {
    pub requests: Vec<(String, Vec<Value>)>,
}

impl JsonRpcBatch {
    pub fn new() -> Self {
        Self { requests: vec![] }
    }

    pub fn json_request(&self) -> Value {
        let requests = self
            .requests
            .iter()
            .enumerate()
            .map(|(id, (method, params))| {
                serde_json::json!({"jsonrpc": "2.0", "method": method, "params": params, "id": id})
            })
            .collect();
        serde_json::Value::Array(requests)
    }

    pub fn add_request(&mut self, method_name: String, parameters: Vec<Value>) {
        self.requests.push((method_name, parameters));
    }

    pub fn add_submit_request(&mut self, transaction: SignedTransaction) -> Result<()> {
        let txn_payload = hex::encode(lcs::to_bytes(&transaction)?);
        self.add_request("submit".to_string(), vec![Value::String(txn_payload)]);
        Ok(())
    }

    pub fn add_get_account_request(&mut self, address: AccountAddress) {
        self.add_request(
            "get_account".to_string(),
            vec![Value::String(address.to_string())],
        );
    }

    pub fn add_get_metadata_request(&mut self, version: Option<u64>) {
        self.add_request("get_metadata".to_string(), vec![json!(version)]);
    }

    pub fn add_get_currencies_info(&mut self) {
        self.add_request("get_currencies".to_string(), vec![]);
    }

    pub fn add_get_transactions_request(
        &mut self,
        start_version: u64,
        limit: u64,
        include_events: bool,
    ) {
        self.add_request(
            "get_transactions".to_string(),
            vec![json!(start_version), json!(limit), json!(include_events)],
        );
    }

    pub fn add_get_account_transaction_request(
        &mut self,
        account: AccountAddress,
        sequence: u64,
        include_events: bool,
    ) {
        self.add_request(
            "get_account_transaction".to_string(),
            vec![
                json!(account.to_string()),
                json!(sequence),
                json!(include_events),
            ],
        );
    }

    pub fn add_get_events_request(&mut self, event_key: String, start: u64, limit: u64) {
        self.add_request(
            "get_events".to_string(),
            vec![json!(event_key), json!(start), json!(limit)],
        );
    }

    pub fn add_get_state_proof_request(&mut self, known_version: u64) {
        self.add_request("get_state_proof".to_string(), vec![json!(known_version)]);
    }

    pub fn add_get_account_state_with_proof_request(
        &mut self,
        account: AccountAddress,
        version: Option<u64>,
        ledger_version: Option<u64>,
    ) {
        self.add_request(
            "get_account_state_with_proof".to_string(),
            vec![
                json!(account.to_string()),
                json!(version),
                json!(ledger_version),
            ],
        );
    }

    pub fn add_get_network_status_request(&mut self) {
        self.add_request("get_network_status".to_string(), vec![]);
    }
}

#[derive(Clone)]
pub struct JsonRpcAsyncClient {
    address: String,
    client: Client,
}

impl JsonRpcAsyncClient {
    /// Pass in full url for endpoint, supports HTTPS
    pub fn new(url: Url) -> Self {
        Self {
            address: url.to_string(),
            client: ClientBuilder::new()
                .timeout(Duration::from_secs(30))
                .build()
                .expect("Unable to build Client."),
        }
    }

    pub fn new_with_client(client: Client, url: Url) -> Self {
        Self {
            address: url.to_string(),
            client,
        }
    }

    pub async fn get_accounts(
        &self,
        accounts: &[AccountAddress],
    ) -> Result<Vec<Option<AccountView>>> {
        let mut batch = JsonRpcBatch::new();
        for account in accounts {
            batch.add_get_account_request(*account);
        }
        let exec_results = self.execute(batch).await?;
        let mut results = vec![];
        for exec_result in exec_results {
            let exec_result = exec_result?;
            if let JsonRpcResponse::AccountResponse(r) = exec_result {
                results.push(r);
            } else {
                panic!("Unexpected response for get_accounts {:?}", exec_result)
            }
        }
        ensure!(
            results.len() == accounts.len(),
            "Received unexpected number of JSON RPC responses ({}) for {} requests",
            results.len(),
            accounts.len()
        );
        Ok(results)
    }

    pub async fn submit_transaction(&self, txn: SignedTransaction) -> Result<()> {
        let mut batch = JsonRpcBatch::new();
        batch.add_submit_request(txn)?;
        let mut exec_result = self.execute(batch).await?;
        assert!(exec_result.len() == 1);
        exec_result.remove(0).map(|_| ())
    }

    pub async fn execute(&self, batch: JsonRpcBatch) -> Result<Vec<Result<JsonRpcResponse>>> {
        let requests = batch.json_request();
        let resp = self
            .client
            .post(&self.address)
            .json(&requests)
            .send()
            .await?;

        ensure!(
            resp.status() == 200,
            format!("Http error code {}", resp.status())
        );

        let responses: Vec<Value> = resp.json().await?;
        process_batch_response(batch, responses)
    }
}

impl fmt::Debug for JsonRpcAsyncClient {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.address)
    }
}

////////////////////////////////////////////////////
/// Helper methods for basic payload processing ///
///////////////////////////////////////////////////

pub fn process_batch_response(
    batch: JsonRpcBatch,
    responses: Vec<Value>,
) -> Result<Vec<Result<JsonRpcResponse>>> {
    let mut result = batch
        .requests
        .iter()
        .map(|_| Err(format_err!("response is missing")))
        .collect::<Vec<_>>();

    let mut seen_ids = HashSet::new();
    for response in responses {
        // check JSON RPC protocol
        let json_rpc_protocol = response.get("jsonrpc");
        ensure!(
            json_rpc_protocol == Some(&json!("2.0")),
            "JSON RPC response with incorrect protocol: {:?}",
            json_rpc_protocol
        );

        if let Ok(req_id) = fetch_id(&response) {
            ensure!(
                !seen_ids.contains(&req_id),
                "received JSON RPC response with duplicate response ID"
            );
            seen_ids.insert(req_id);
            if req_id < result.len() {
                let resp = if let Some(err_data) = response.get("error") {
                    let err_json: JsonRpcError = serde_json::from_value(err_data.clone())?;
                    Err(Error::new(err_json))
                } else if let Some(data) = response.get("result") {
                    let method = batch.requests[req_id].0.clone();
                    Ok(JsonRpcResponse::try_from((method, data.clone()))?)
                } else {
                    continue;
                };
                result[req_id] = resp;
            }
        }
    }

    Ok(result)
}

pub fn get_response_from_batch(
    index: usize,
    batch: &[Result<JsonRpcResponse>],
) -> Result<&Result<JsonRpcResponse>> {
    batch.get(index).ok_or_else(|| {
        format_err!(
            "[JSON RPC client] response missing in batch at index {}",
            index
        )
    })
}

fn fetch_id(response: &Value) -> Result<usize> {
    match response.get("id") {
        Some(id) => Ok(serde_json::from_value::<usize>(id.clone())?),
        None => Err(format_err!("request id is missing")),
    }
}
<commit_msg>Add Error enum to JSON rpc client<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{errors::JsonRpcError, views::AccountView, JsonRpcResponse};
use anyhow::{ensure, format_err, Error, Result};
use libra_types::{account_address::AccountAddress, transaction::SignedTransaction};
use reqwest::{Client, ClientBuilder, Url};
use serde_json::{json, Value};
use std::{collections::HashSet, convert::TryFrom, fmt, time::Duration};

#[derive(Clone, Default)]
pub struct JsonRpcBatch {
    pub requests: Vec<(String, Vec<Value>)>,
}

impl JsonRpcBatch {
    pub fn new() -> Self {
        Self { requests: vec![] }
    }

    pub fn json_request(&self) -> Value {
        let requests = self
            .requests
            .iter()
            .enumerate()
            .map(|(id, (method, params))| {
                serde_json::json!({"jsonrpc": "2.0", "method": method, "params": params, "id": id})
            })
            .collect();
        serde_json::Value::Array(requests)
    }

    pub fn add_request(&mut self, method_name: String, parameters: Vec<Value>) {
        self.requests.push((method_name, parameters));
    }

    pub fn add_submit_request(&mut self, transaction: SignedTransaction) -> Result<()> {
        let txn_payload = hex::encode(lcs::to_bytes(&transaction)?);
        self.add_request("submit".to_string(), vec![Value::String(txn_payload)]);
        Ok(())
    }

    pub fn add_get_account_request(&mut self, address: AccountAddress) {
        self.add_request(
            "get_account".to_string(),
            vec![Value::String(address.to_string())],
        );
    }

    pub fn add_get_metadata_request(&mut self, version: Option<u64>) {
        self.add_request("get_metadata".to_string(), vec![json!(version)]);
    }

    pub fn add_get_currencies_info(&mut self) {
        self.add_request("get_currencies".to_string(), vec![]);
    }

    pub fn add_get_transactions_request(
        &mut self,
        start_version: u64,
        limit: u64,
        include_events: bool,
    ) {
        self.add_request(
            "get_transactions".to_string(),
            vec![json!(start_version), json!(limit), json!(include_events)],
        );
    }

    pub fn add_get_account_transaction_request(
        &mut self,
        account: AccountAddress,
        sequence: u64,
        include_events: bool,
    ) {
        self.add_request(
            "get_account_transaction".to_string(),
            vec![
                json!(account.to_string()),
                json!(sequence),
                json!(include_events),
            ],
        );
    }

    pub fn add_get_events_request(&mut self, event_key: String, start: u64, limit: u64) {
        self.add_request(
            "get_events".to_string(),
            vec![json!(event_key), json!(start), json!(limit)],
        );
    }

    pub fn add_get_state_proof_request(&mut self, known_version: u64) {
        self.add_request("get_state_proof".to_string(), vec![json!(known_version)]);
    }

    pub fn add_get_account_state_with_proof_request(
        &mut self,
        account: AccountAddress,
        version: Option<u64>,
        ledger_version: Option<u64>,
    ) {
        self.add_request(
            "get_account_state_with_proof".to_string(),
            vec![
                json!(account.to_string()),
                json!(version),
                json!(ledger_version),
            ],
        );
    }

    pub fn add_get_network_status_request(&mut self) {
        self.add_request("get_network_status".to_string(), vec![]);
    }
}

#[derive(Debug)]
pub enum JsonRpcAsyncClientError {
    ClientError(reqwest::Error),
    InvalidServerResponse(String),
}

impl JsonRpcAsyncClientError {
    pub fn is_retriable(&self) -> bool {
        match self {
            JsonRpcAsyncClientError::ClientError(e) => {
                // TODO: Add following condition when is_request is added to reqwest stable release
                // // Error sending request
                //  if e.is_request() {
                //      return true;
                //  }
                if let Some(status) = e.status() {
                    // Returned status code indicates a server error
                    return status.is_server_error();
                }
                false
            }
            JsonRpcAsyncClientError::InvalidServerResponse(_) => false,
        }
    }
}

impl std::convert::From<JsonRpcAsyncClientError> for anyhow::Error {
    fn from(e: JsonRpcAsyncClientError) -> Self {
        anyhow::Error::msg(e.to_string())
    }
}

impl std::fmt::Display for JsonRpcAsyncClientError {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "JsonRpcAsyncClientError {}", self)
    }
}

#[derive(Clone)]
pub struct JsonRpcAsyncClient {
    address: String,
    client: Client,
}

impl JsonRpcAsyncClient {
    /// Pass in full url for endpoint, supports HTTPS
    pub fn new(url: Url) -> Self {
        Self {
            address: url.to_string(),
            client: ClientBuilder::new()
                .timeout(Duration::from_secs(30))
                .build()
                .expect("Unable to build Client."),
        }
    }

    pub fn new_with_client(client: Client, url: Url) -> Self {
        Self {
            address: url.to_string(),
            client,
        }
    }

    pub async fn get_accounts(
        &self,
        accounts: &[AccountAddress],
    ) -> Result<Vec<Option<AccountView>>, JsonRpcAsyncClientError> {
        let mut batch = JsonRpcBatch::new();
        for account in accounts {
            batch.add_get_account_request(*account);
        }
        let exec_results = self.execute(batch).await?;
        let mut results = vec![];
        for exec_result in exec_results {
            let exec_result = exec_result
                .map_err(|e| JsonRpcAsyncClientError::InvalidServerResponse(e.to_string()))?;
            if let JsonRpcResponse::AccountResponse(r) = exec_result {
                results.push(r);
            } else {
                panic!("Unexpected response for get_accounts {:?}", exec_result)
            }
        }

        if results.len() != accounts.len() {
            return Err(JsonRpcAsyncClientError::InvalidServerResponse(format!(
                "Received unexpected number of JSON RPC responses ({}) for {} requests",
                results.len(),
                accounts.len()
            )));
        }

        Ok(results)
    }

    pub async fn submit_transaction(
        &self,
        txn: SignedTransaction,
    ) -> Result<(), JsonRpcAsyncClientError> {
        let mut batch = JsonRpcBatch::new();
        batch
            .add_submit_request(txn)
            .map_err(|e| JsonRpcAsyncClientError::InvalidServerResponse(e.to_string()))?;
        let mut exec_result = self.execute(batch).await?;
        assert!(exec_result.len() == 1);
        exec_result
            .remove(0)
            .map(|_| ())
            .map_err(|e| JsonRpcAsyncClientError::InvalidServerResponse(e.to_string()))
    }

    pub async fn execute(
        &self,
        batch: JsonRpcBatch,
    ) -> Result<Vec<Result<JsonRpcResponse>>, JsonRpcAsyncClientError> {
        let requests = batch.json_request();
        let resp = self
            .client
            .post(&self.address)
            .json(&requests)
            .send()
            .await
            .map_err(JsonRpcAsyncClientError::ClientError)?;

        if resp.status() != 200 {
            return Err(JsonRpcAsyncClientError::InvalidServerResponse(format!(
                "Http error code {}",
                resp.status()
            )));
        }

        let responses: Vec<Value> = resp
            .json()
            .await
            .map_err(|e| JsonRpcAsyncClientError::InvalidServerResponse(e.to_string()))?;
        process_batch_response(batch, responses)
            .map_err(|e| JsonRpcAsyncClientError::InvalidServerResponse(e.to_string()))
    }
}

impl fmt::Debug for JsonRpcAsyncClient {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.address)
    }
}

////////////////////////////////////////////////////
/// Helper methods for basic payload processing ///
///////////////////////////////////////////////////

pub fn process_batch_response(
    batch: JsonRpcBatch,
    responses: Vec<Value>,
) -> Result<Vec<Result<JsonRpcResponse>>> {
    let mut result = batch
        .requests
        .iter()
        .map(|_| Err(format_err!("response is missing")))
        .collect::<Vec<_>>();

    let mut seen_ids = HashSet::new();
    for response in responses {
        // check JSON RPC protocol
        let json_rpc_protocol = response.get("jsonrpc");
        ensure!(
            json_rpc_protocol == Some(&json!("2.0")),
            "JSON RPC response with incorrect protocol: {:?}",
            json_rpc_protocol
        );

        if let Ok(req_id) = fetch_id(&response) {
            ensure!(
                !seen_ids.contains(&req_id),
                "received JSON RPC response with duplicate response ID"
            );
            seen_ids.insert(req_id);
            if req_id < result.len() {
                let resp = if let Some(err_data) = response.get("error") {
                    let err_json: JsonRpcError = serde_json::from_value(err_data.clone())?;
                    Err(Error::new(err_json))
                } else if let Some(data) = response.get("result") {
                    let method = batch.requests[req_id].0.clone();
                    Ok(JsonRpcResponse::try_from((method, data.clone()))?)
                } else {
                    continue;
                };
                result[req_id] = resp;
            }
        }
    }

    Ok(result)
}

pub fn get_response_from_batch(
    index: usize,
    batch: &[Result<JsonRpcResponse>],
) -> Result<&Result<JsonRpcResponse>> {
    batch.get(index).ok_or_else(|| {
        format_err!(
            "[JSON RPC client] response missing in batch at index {}",
            index
        )
    })
}

fn fetch_id(response: &Value) -> Result<usize> {
    match response.get("id") {
        Some(id) => Ok(serde_json::from_value::<usize>(id.clone())?),
        None => Err(format_err!("request id is missing")),
    }
}
<|endoftext|>


<commit_before>extern crate bincode;
extern crate byteorder;
extern crate euclid;
extern crate glutin;
extern crate gleam;
extern crate webrender;
extern crate webrender_traits;

use bincode::serde::deserialize;
use byteorder::{LittleEndian, ReadBytesExt};
use euclid::Size2D;
use gleam::gl;
use std::fs::File;
use std::io::{BufReader};
use std::io::prelude::*;
use std::path::PathBuf;
use std::env;
use webrender_traits::{ApiMsg, RenderApi, PipelineId};
use glutin::{Event, ElementState, VirtualKeyCode as Key};

struct Notifier {
window_proxy: glutin::WindowProxy,
}

impl Notifier {
	fn new(window_proxy: glutin::WindowProxy) -> Notifier {
		Notifier {window_proxy: window_proxy,
		}
	}
}

impl webrender_traits::RenderNotifier for Notifier {
	fn new_frame_ready(&mut self) {
		self.window_proxy.wakeup_event_loop();
	}

	fn pipeline_size_changed(&mut self,
			_: PipelineId,
			_: Option<Size2D<f32>>) {
	}

	fn new_scroll_frame_ready(&mut self, composite_needed:bool){}
}

fn get_file(dir:&String, frame: i32) -> Option<File>{
	let filename = format!("{}/frame_{}.bin", dir,frame);
	File::open(filename).ok()
}

fn read_file(file: &mut File, api: &RenderApi){
	while let Some(len) = file.read_u32::<LittleEndian>().ok(){
		let mut buffer = vec![0; len as usize];
		file.read_exact(&mut buffer).unwrap();
		let msg:Option<ApiMsg> = deserialize(&buffer).ok();
		match msg{
			Some(msg) => {
				api.api_sender.send(msg).unwrap();
			}
			None => {
				api.payload_sender.send(&buffer[..]).unwrap();
			}
		}
	}
}

fn main() {
	let args:Vec<String> = env::args().collect();
	if args.len() != 3{
		println!("{}  <resources_path> <directory>", args[0]);
		return;
	}
	let resource_path = &args[1];
	let ref dir= args[2];
	let window = glutin::WindowBuilder::new()
		.with_gl(glutin::GlRequest::Specific(glutin::Api::OpenGl, (3,2)))
		.build()
		.unwrap();

	let (width, height) = window.get_outer_size().unwrap();
	unsafe{
		window.make_current().ok();
		gl::load_with(|symbol| window.get_proc_address(symbol) as *const _);
	}

	let opts = webrender::RendererOptions{device_pixel_ratio: 2.0,
		resource_path: PathBuf::from(resource_path),
		enable_aa: false,
		enable_msaa: false,
		enable_profiler: false,
		enable_recording: false,
	};

	let (mut renderer, sender) = webrender::renderer::Renderer::new(opts);
	let mut api = sender.create_api();
	let notifier = Box::new(Notifier::new(window.create_window_proxy()));
	renderer.set_render_notifier(notifier);
	let (mut width, mut height) = window.get_outer_size().unwrap();
	width *= window.hidpi_factor() as u32;
	height *= window.hidpi_factor() as u32;

	//read and send the resources file
	let mut frame_num = 0;
	if let Some(mut file) = get_file(dir, frame_num){
		read_file(&mut file, &api);
	}
	for event in window.wait_events(){
		match event {
			Event::Closed => break,
			Event::Awakened => {
					gl::clear(gl::COLOR_BUFFER_BIT);
					renderer.update();
					renderer.render(Size2D::new(width, height));
					window.swap_buffers();
			}
			Event::KeyboardInput(ElementState::Pressed, _, Some(Key::Right)) =>{
				frame_num += 1;
				if let Some(mut file) = get_file(dir, frame_num){
					read_file(&mut file, &api);
				}
				else{
					frame_num -= 1;
					println!("At last frame.");
				}
			}
			Event::KeyboardInput(ElementState::Pressed, _, Some(Key::Left)) => {
				frame_num -= 1;
				if let Some(mut file) = get_file(dir, frame_num){
					read_file(&mut file, &api);
				}

				else{
					frame_num +=1;
					println!("At first frame.");
				}
			}
			_ => ()
		}
	}
}

<commit_msg>[replay] display frame number<commit_after>extern crate bincode;
extern crate byteorder;
extern crate euclid;
extern crate glutin;
extern crate gleam;
extern crate webrender;
extern crate webrender_traits;

use bincode::serde::deserialize;
use byteorder::{LittleEndian, ReadBytesExt};
use euclid::Size2D;
use gleam::gl;
use std::fs::File;
use std::io::{BufReader};
use std::io::prelude::*;
use std::path::PathBuf;
use std::env;
use webrender_traits::{ApiMsg, RenderApi, PipelineId};
use glutin::{Event, ElementState, VirtualKeyCode as Key};

struct Notifier {
window_proxy: glutin::WindowProxy,
}

impl Notifier {
	fn new(window_proxy: glutin::WindowProxy) -> Notifier {
		Notifier {window_proxy: window_proxy,
		}
	}
}

impl webrender_traits::RenderNotifier for Notifier {
	fn new_frame_ready(&mut self) {
		self.window_proxy.wakeup_event_loop();
	}

	fn pipeline_size_changed(&mut self,
			_: PipelineId,
			_: Option<Size2D<f32>>) {
	}

	fn new_scroll_frame_ready(&mut self, composite_needed:bool){}
}

fn get_file(dir:&String, frame: i32) -> Option<File>{
	let filename = format!("{}/frame_{}.bin", dir,frame);
	File::open(filename).ok()
}

fn read_file(file: &mut File, api: &RenderApi){
	while let Some(len) = file.read_u32::<LittleEndian>().ok(){
		let mut buffer = vec![0; len as usize];
		file.read_exact(&mut buffer).unwrap();
		let msg:Option<ApiMsg> = deserialize(&buffer).ok();
		match msg{
			Some(msg) => {
				api.api_sender.send(msg).unwrap();
			}
			None => {
				api.payload_sender.send(&buffer[..]).unwrap();
			}
		}
	}
}

fn main() {
	let args:Vec<String> = env::args().collect();
	if args.len() != 3{
		println!("{}  <resources_path> <directory>", args[0]);
		return;
	}
	let resource_path = &args[1];
	let ref dir= args[2];
	let window = glutin::WindowBuilder::new()
		.with_gl(glutin::GlRequest::Specific(glutin::Api::OpenGl, (3,2)))
		.build()
		.unwrap();

	let (width, height) = window.get_outer_size().unwrap();
	unsafe{
		window.make_current().ok();
		gl::load_with(|symbol| window.get_proc_address(symbol) as *const _);
	}

	let opts = webrender::RendererOptions{device_pixel_ratio: 2.0,
		resource_path: PathBuf::from(resource_path),
		enable_aa: false,
		enable_msaa: false,
		enable_profiler: false,
		enable_recording: false,
	};

	let (mut renderer, sender) = webrender::renderer::Renderer::new(opts);
	let mut api = sender.create_api();
	let notifier = Box::new(Notifier::new(window.create_window_proxy()));
	renderer.set_render_notifier(notifier);
	let (mut width, mut height) = window.get_outer_size().unwrap();
	width *= window.hidpi_factor() as u32;
	height *= window.hidpi_factor() as u32;

	//read and send the resources file
	let mut frame_num = 0;
	if let Some(mut file) = get_file(dir, frame_num){
		read_file(&mut file, &api);
	}
	for event in window.wait_events(){
		match event {
			Event::Closed => break,
			Event::Awakened => {
					gl::clear(gl::COLOR_BUFFER_BIT);
					renderer.update();
					renderer.render(Size2D::new(width, height));
					window.swap_buffers();
					println!("Showing frame #{}", frame_num);
			}
			Event::KeyboardInput(ElementState::Pressed, _, Some(Key::Right)) =>{
				frame_num += 1;
				if let Some(mut file) = get_file(dir, frame_num){
					read_file(&mut file, &api);
				}
				else{
					frame_num -= 1;
					println!("At last frame.");
				}
			}
			Event::KeyboardInput(ElementState::Pressed, _, Some(Key::Left)) => {
				frame_num -= 1;
				if let Some(mut file) = get_file(dir, frame_num){
					read_file(&mut file, &api);
				}

				else{
					frame_num +=1;
					println!("At first frame.");
				}
			}
			_ => ()
		}
	}
}

<|endoftext|>


<commit_before>use super::behaviors::MouseBehavior;
use crate::prelude::*;
use crate::shell::{Key, KeyEvent};
use core::f64::MAX;
use rust_decimal::prelude::*;

pub static ID_INPUT: &'static str = "numeric_box_input";
pub static ELEMENT_INPUT: &'static str = "numeric_box_input";
pub static ELEMENT_BTN: &'static str = "numeric_box_button";

pub enum InputAction {
    Inc,
    Dec,
    ChangeByKey(KeyEvent),
    ChangeByMouseScroll(Point),
    Focus,
}

#[derive(Default, AsAny)]
pub struct NumericBoxState {
    pub action: Option<InputAction>,
    pub input: Entity,
    pub min: Decimal,
    pub max: Decimal,
    pub step: Decimal,
    pub current_value: Decimal,
}

impl NumericBoxState {
    fn action(&mut self, action: InputAction) {
        self.action = Some(action);
    }

    fn change_val(&mut self, new_value: Decimal, ctx: &mut Context) {
        self.current_value = self.max(self.min(new_value));
        ctx.get_widget(self.input)
            .set::<String16>("text", String16::from(self.current_value.to_string()));

        ctx.push_event_strategy_by_entity(
            ChangedEvent(ctx.entity),
            ctx.entity,
            EventStrategy::Direct,
        );
    }

    fn min(&self, d: Decimal) -> Decimal {
        if d <= self.min {
            return self.min;
        } else {
            return d;
        }
    }

    fn max(&self, d: Decimal) -> Decimal {
       if d >= self.max {
           return self.max;
       } else {
           return d;
       }
    }

    fn request_focus(&self, ctx: &mut Context) {
        if !ctx.widget().get::<bool>("focused") {
            ctx.widget().set::<bool>("focused", true);
            ctx.push_event_by_window(FocusEvent::RequestFocus(ctx.entity));
        }
    }
}

fn default_or(key: &str, default_value: f64, ctx: &mut Context) -> Decimal {
    let property = ctx.widget().clone_or_default(key);

    match Decimal::from_f64(property) {
        Some(val) => val,
        None => Decimal::from_f64(default_value).unwrap(),
    }
}

impl State for NumericBoxState {
    fn init(&mut self, _: &mut Registry, ctx: &mut Context) {
        self.input = ctx.entity_of_child(ID_INPUT).expect(
            "NumericBoxState
        .init(): the child input could not be found!",
        );
        self.min = default_or("min", 0.0, ctx);
        self.max = default_or("max", MAX, ctx);
        self.step = default_or("step", 1.0, ctx);
        self.current_value = default_or("val", 0.0, ctx);

        let init_value = String16::from(self.current_value.to_string());
        ctx.get_widget(self.input)
            .set::<String16>("text", init_value);
    }

    // TODO: let the user type the value, or select it for cut, copy, paste operations
    fn update(&mut self, _: &mut Registry, ctx: &mut Context) {
        if let Some(action) = &self.action {
            match action {
                InputAction::Inc => {
                    self.change_val(self.current_value + self.step, ctx);
                }
                InputAction::Dec => {
                    self.change_val(self.current_value - self.step, ctx);
                }
                InputAction::ChangeByKey(key_event) => match key_event.key {
                    Key::Up | Key::NumpadAdd => {
                        self.change_val(self.current_value + self.step, ctx);
                    }
                    Key::Down | Key::NumpadSubtract => {
                        self.change_val(self.current_value - self.step, ctx);
                    }
                    Key::Enter => {
                        if *ctx.widget().get::<bool>("lost_focus_on_activation") {
                            ctx.push_event_by_window(FocusEvent::RemoveFocus(ctx.entity));
                        }

                        ctx.push_event_strategy_by_entity(
                            ActivateEvent(ctx.entity),
                            ctx.entity,
                            EventStrategy::Direct,
                        )
                    }
                    _ => {}
                },
                InputAction::ChangeByMouseScroll(delta) => {
                    if delta.y < 0.0 {
                        self.change_val(self.current_value - self.step, ctx);
                    } else {
                        self.change_val(self.current_value + self.step, ctx);
                    }
                }
                InputAction::Focus => {
                    self.request_focus(ctx);
                }
            }
            self.action = None;
        }
    }
}

widget!(
    /// `NumericBox` is used to let the user increase or decrease
    /// the value of the input by a given, fixed value called `step` until it reaches the upper or
    /// lower bounds.
    /// The widget can be controlled by clicking on the two control buttons, or the keybaord's
    /// Up and Down, Numpad+ and Numpad- keys, or the mouse scroll.
    /// Note: after the widget is initialized, changing the min, max or step properties has no effect.
    ///
    /// # Examples:
    /// Create a NumericBox with default values:
    /// ```rust
    /// NumericBox::new().build(ctx)
    /// ```
    ///
    /// Create a NumericBox with custom values:
    /// ```rust
    /// NumericBox::new().min(10.0).max(100.0).val(50.0).step(5.0).build(ctx)
    /// ```
    NumericBox<NumericBoxState>: ActivateHandler, ChangedHandler, KeyDownHandler {
        /// Sets or shares the background color property
        background: Brush,

        /// Sets or shares the border color property
        border_brush: Brush,

        /// Sets or shares the border width property
        border_width: Thickness,

        /// Sets or shares the border radius property
        border_radius: f64,

        /// Sets or shares the focused property
        focused: bool,

        /// Sets or shares the foreground color property
        foreground: Brush,

        /// Sets or shares the value that describes if the NumericBox should lost focus on activation (when enter pressed).
        lost_focus_on_activation: bool,

        /// Sets or shares the minimum allowed value property
        min: f64,

        /// Sets or shares the maximum allowed value property
        max: f64,

        /// Sets or shares the stepping value property
        step: f64,

        /// Sets or shares the current value property
        val: f64
    }
);

impl Template for NumericBox {
    fn template(self, id: Entity, ctx: &mut BuildContext) -> Self {
        self.name("NumericBox")
            .background("transparent")
            .foreground(colors::LINK_WATER_COLOR)
            .border_brush("#647b91")
            .border_width(1.0)
            .border_radius(3.0)
            .element("numeric_box")
            .focused(false)
            .height(32.0)
            .lost_focus_on_activation(true)
            .margin(4.0)
            .min(0.0)
            .max(200.0)
            .step(1.0)
            .val(0.0)
            .width(128.0)
            .child(
                MouseBehavior::new()
                    .on_mouse_down(move |states, _| {
                        states
                            .get_mut::<NumericBoxState>(id)
                            .action(InputAction::Focus);
                        true
                    })
                    .on_scroll(move |states, delta| {
                        states
                            .get_mut::<NumericBoxState>(id)
                            .action(InputAction::ChangeByMouseScroll(delta));
                        true
                    })
                    .build(ctx),
            )
            .child(
                Grid::new()
                    .columns(Columns::new().add("*").add(32.))
                    .rows(Rows::new().add(16.0).add(16.0))
                    .child(
                        TextBox::new()
                            .h_align("stretch")
                            .attach(Grid::column(0))
                            .attach(Grid::row_span(2))
                            .attach(Grid::row(0))
                            .border_brush(id)
                            .border_radius(id)
                            .border_width(id)
                            .element(ELEMENT_INPUT)
                            .enabled(false)
                            .id(ID_INPUT)
                            .max_width(96.)
                            .text("0")
                            .build(ctx),
                    )
                    .child(
                        Button::new()
                            .attach(Grid::column(1))
                            .attach(Grid::row(0))
                            .border_brush("transparent")
                            .border_radius(0.0)
                            .border_width(0.0)
                            .min_width(30.0)
                            .height(30.0)
                            .class("single_content")
                            .element(ELEMENT_BTN)
                            .text("+")
                            .margin(1)
                            .on_click(move |states, _| {
                                states
                                    .get_mut::<NumericBoxState>(id)
                                    .action(InputAction::Inc);
                                true
                            })
                            .build(ctx),
                    )
                    .child(
                        Button::new()
                            .attach(Grid::column(1))
                            .attach(Grid::row(1))
                            .border_brush("transparent")
                            .border_radius(0.0)
                            .border_width(0.0)
                            .class("single_content")
                            .element(ELEMENT_BTN)
                            .min_width(30.0)
                            .height(30.0)
                            .padding(0.0)
                            .margin(1)
                            .text("-")
                            .on_click(move |states, _| {
                                states
                                    .get_mut::<NumericBoxState>(id)
                                    .action(InputAction::Dec);
                                true
                            })
                            .build(ctx),
                    )
                    .build(ctx),
            )
            .on_key_down(move |states, event| -> bool {
                states
                    .get_mut::<NumericBoxState>(id)
                    .action(InputAction::ChangeByKey(event));
                false
            })
    }

    fn render_object(&self) -> Box<dyn RenderObject> {
        Box::new(RectangleRenderObject)
    }
}
<commit_msg>Notify change when state is really changed<commit_after>use super::behaviors::MouseBehavior;
use crate::prelude::*;
use crate::shell::{Key, KeyEvent};
use core::f64::MAX;
use rust_decimal::prelude::*;

pub static ID_INPUT: &'static str = "numeric_box_input";
pub static ELEMENT_INPUT: &'static str = "numeric_box_input";
pub static ELEMENT_BTN: &'static str = "numeric_box_button";

pub enum InputAction {
    Inc,
    Dec,
    ChangeByKey(KeyEvent),
    ChangeByMouseScroll(Point),
    Focus,
}

#[derive(Default, AsAny)]
pub struct NumericBoxState {
    pub action: Option<InputAction>,
    pub input: Entity,
    pub min: Decimal,
    pub max: Decimal,
    pub step: Decimal,
    pub current_value: Decimal,
}

impl NumericBoxState {
    fn action(&mut self, action: InputAction) {
        self.action = Some(action);
    }

    fn change_val(&mut self, new_value: Decimal, ctx: &mut Context) {
        if self.current_value == self.min && new_value < self.min
        || self.current_value == self.max && new_value > self.max {
            return;
        }

        self.current_value = self.max(self.min(new_value));
        ctx.get_widget(self.input)
            .set::<String16>("text", String16::from(self.current_value.to_string()));

        ctx.push_event_strategy_by_entity(
            ChangedEvent(ctx.entity),
            ctx.entity,
            EventStrategy::Direct,
        );
    }

    fn min(&self, d: Decimal) -> Decimal {
        if d <= self.min {
            return self.min;
        } else {
            return d;
        }
    }

    fn max(&self, d: Decimal) -> Decimal {
       if d >= self.max {
           return self.max;
       } else {
           return d;
       }
    }

    fn request_focus(&self, ctx: &mut Context) {
        if !ctx.widget().get::<bool>("focused") {
            ctx.widget().set::<bool>("focused", true);
            ctx.push_event_by_window(FocusEvent::RequestFocus(ctx.entity));
        }
    }
}

fn default_or(key: &str, default_value: f64, ctx: &mut Context) -> Decimal {
    let property = ctx.widget().clone_or_default(key);

    match Decimal::from_f64(property) {
        Some(val) => val,
        None => Decimal::from_f64(default_value).unwrap(),
    }
}

impl State for NumericBoxState {
    fn init(&mut self, _: &mut Registry, ctx: &mut Context) {
        self.input = ctx.entity_of_child(ID_INPUT).expect(
            "NumericBoxState
        .init(): the child input could not be found!",
        );
        self.min = default_or("min", 0.0, ctx);
        self.max = default_or("max", MAX, ctx);
        self.step = default_or("step", 1.0, ctx);
        self.current_value = default_or("val", 0.0, ctx);

        let init_value = String16::from(self.current_value.to_string());
        ctx.get_widget(self.input)
            .set::<String16>("text", init_value);
    }

    // TODO: let the user type the value, or select it for cut, copy, paste operations
    fn update(&mut self, _: &mut Registry, ctx: &mut Context) {
        if let Some(action) = &self.action {
            match action {
                InputAction::Inc => {
                    self.change_val(self.current_value + self.step, ctx);
                }
                InputAction::Dec => {
                    self.change_val(self.current_value - self.step, ctx);
                }
                InputAction::ChangeByKey(key_event) => match key_event.key {
                    Key::Up | Key::NumpadAdd => {
                        self.change_val(self.current_value + self.step, ctx);
                    }
                    Key::Down | Key::NumpadSubtract => {
                        self.change_val(self.current_value - self.step, ctx);
                    }
                    Key::Enter => {
                        if *ctx.widget().get::<bool>("lost_focus_on_activation") {
                            ctx.push_event_by_window(FocusEvent::RemoveFocus(ctx.entity));
                        }

                        ctx.push_event_strategy_by_entity(
                            ActivateEvent(ctx.entity),
                            ctx.entity,
                            EventStrategy::Direct,
                        )
                    }
                    _ => {}
                },
                InputAction::ChangeByMouseScroll(delta) => {
                    if delta.y < 0.0 {
                        self.change_val(self.current_value - self.step, ctx);
                    } else {
                        self.change_val(self.current_value + self.step, ctx);
                    }
                }
                InputAction::Focus => {
                    self.request_focus(ctx);
                }
            }
            self.action = None;
        }
    }
}

widget!(
    /// `NumericBox` is used to let the user increase or decrease
    /// the value of the input by a given, fixed value called `step` until it reaches the upper or
    /// lower bounds.
    /// The widget can be controlled by clicking on the two control buttons, or the keybaord's
    /// Up and Down, Numpad+ and Numpad- keys, or the mouse scroll.
    /// Note: after the widget is initialized, changing the min, max or step properties has no effect.
    ///
    /// # Examples:
    /// Create a NumericBox with default values:
    /// ```rust
    /// NumericBox::new().build(ctx)
    /// ```
    ///
    /// Create a NumericBox with custom values:
    /// ```rust
    /// NumericBox::new().min(10.0).max(100.0).val(50.0).step(5.0).build(ctx)
    /// ```
    NumericBox<NumericBoxState>: ActivateHandler, ChangedHandler, KeyDownHandler {
        /// Sets or shares the background color property
        background: Brush,

        /// Sets or shares the border color property
        border_brush: Brush,

        /// Sets or shares the border width property
        border_width: Thickness,

        /// Sets or shares the border radius property
        border_radius: f64,

        /// Sets or shares the focused property
        focused: bool,

        /// Sets or shares the foreground color property
        foreground: Brush,

        /// Sets or shares the value that describes if the NumericBox should lost focus on activation (when enter pressed).
        lost_focus_on_activation: bool,

        /// Sets or shares the minimum allowed value property
        min: f64,

        /// Sets or shares the maximum allowed value property
        max: f64,

        /// Sets or shares the stepping value property
        step: f64,

        /// Sets or shares the current value property
        val: f64
    }
);

impl Template for NumericBox {
    fn template(self, id: Entity, ctx: &mut BuildContext) -> Self {
        self.name("NumericBox")
            .background("transparent")
            .foreground(colors::LINK_WATER_COLOR)
            .border_brush("#647b91")
            .border_width(1.0)
            .border_radius(3.0)
            .element("numeric_box")
            .focused(false)
            .height(32.0)
            .lost_focus_on_activation(true)
            .margin(4.0)
            .min(0.0)
            .max(200.0)
            .step(1.0)
            .val(0.0)
            .width(128.0)
            .child(
                MouseBehavior::new()
                    .on_mouse_down(move |states, _| {
                        states
                            .get_mut::<NumericBoxState>(id)
                            .action(InputAction::Focus);
                        true
                    })
                    .on_scroll(move |states, delta| {
                        states
                            .get_mut::<NumericBoxState>(id)
                            .action(InputAction::ChangeByMouseScroll(delta));
                        true
                    })
                    .build(ctx),
            )
            .child(
                Grid::new()
                    .columns(Columns::new().add("*").add(32.))
                    .rows(Rows::new().add(16.0).add(16.0))
                    .child(
                        TextBox::new()
                            .h_align("stretch")
                            .attach(Grid::column(0))
                            .attach(Grid::row_span(2))
                            .attach(Grid::row(0))
                            .border_brush(id)
                            .border_radius(id)
                            .border_width(id)
                            .element(ELEMENT_INPUT)
                            .enabled(false)
                            .id(ID_INPUT)
                            .max_width(96.)
                            .text("0")
                            .build(ctx),
                    )
                    .child(
                        Button::new()
                            .attach(Grid::column(1))
                            .attach(Grid::row(0))
                            .border_brush("transparent")
                            .border_radius(0.0)
                            .border_width(0.0)
                            .min_width(30.0)
                            .height(30.0)
                            .class("single_content")
                            .element(ELEMENT_BTN)
                            .text("+")
                            .margin(1)
                            .on_click(move |states, _| {
                                states
                                    .get_mut::<NumericBoxState>(id)
                                    .action(InputAction::Inc);
                                true
                            })
                            .build(ctx),
                    )
                    .child(
                        Button::new()
                            .attach(Grid::column(1))
                            .attach(Grid::row(1))
                            .border_brush("transparent")
                            .border_radius(0.0)
                            .border_width(0.0)
                            .class("single_content")
                            .element(ELEMENT_BTN)
                            .min_width(30.0)
                            .height(30.0)
                            .padding(0.0)
                            .margin(1)
                            .text("-")
                            .on_click(move |states, _| {
                                states
                                    .get_mut::<NumericBoxState>(id)
                                    .action(InputAction::Dec);
                                true
                            })
                            .build(ctx),
                    )
                    .build(ctx),
            )
            .on_key_down(move |states, event| -> bool {
                states
                    .get_mut::<NumericBoxState>(id)
                    .action(InputAction::ChangeByKey(event));
                false
            })
    }

    fn render_object(&self) -> Box<dyn RenderObject> {
        Box::new(RectangleRenderObject)
    }
}
<|endoftext|>


<commit_before>//! `fitsio` - a thin wrapper around the [`cfitsio`][1] C library.
//!
//! * [HDU access](#hdu-access)
//! * [Header keys](#header-keys)
//! * [Reading file data](#reading-file-data)
//!     * [Images](#images)
//!     * [Tables](#tables)
//!
//! This library wraps the low level `cfitsio` bindings: [`fitsio-sys`][2] and provides a more
//! native experience for rust users.
//!
//! The main interface to a fits file is [`FitsFile`](struct.FitsFile.html). All file manipulation
//! and reading starts with this class.
//!
//! Opening a file:
//!
//! ```rust
//! # fn main() {
//! # let filename = "../testdata/full_example.fits";
//! use fitsio::FitsFile;
//!
//! // let filename = ...;
//! let fptr = FitsFile::open(filename).unwrap();
//! # }
//! ```
//!
//! Alternatively a new file can be created on disk with the companion method
//! [`create`](struct.FitsFile.html#method.create):
//!
//! ```rust
//! # extern crate tempdir;
//! # extern crate fitsio;
//! # use fitsio::FitsFile;
//! # fn main() {
//! # let tdir = tempdir::TempDir::new("fitsio-").unwrap();
//! # let tdir_path = tdir.path();
//! # let _filename = tdir_path.join("test.fits");
//! # let filename = _filename.to_str().unwrap();
//! use fitsio::FitsFile;
//!
//! // let filename = ...;
//! let fptr = FitsFile::create(filename).unwrap();
//! # }
//! ```
//!
//! From this point, the current HDU can be queried and changed, or fits header cards can be read
//! or file contents can be read.
//!
//! ## HDU access
//!
//! HDU information belongs to the [`FitsHdu`](struct.FitsHdu.html) object. HDUs can be fetched by
//! `String`/`str` or integer (0-indexed). The `HduInfo` object contains information about the current
//! HDU:
//!
//! ```rust
//! # extern crate fitsio;
//! # extern crate fitsio_sys;
//! # use fitsio::{FitsFile, HduInfo};
//! #
//! # fn main() {
//! # let filename = "../testdata/full_example.fits";
//! # let fptr = FitsFile::open(filename).unwrap();
//! let hdu = fptr.hdu(0).unwrap();
//! // image HDU
//! if let HduInfo::ImageInfo { shape } = hdu.info {
//!    println!("Image is {}-dimensional", shape.len());
//!    println!("Found image with shape {:?}", shape);
//! }
//! # let hdu = fptr.hdu("TESTEXT").unwrap();
//!
//! // tables
//! if let HduInfo::TableInfo { column_descriptions, num_rows, .. } = hdu.info {
//!     println!("Table contains {} rows", num_rows);
//!     println!("Table has {} columns", column_descriptions.len());
//! }
//! # }
//! ```
//!
//! ## Header keys
//!
//! Header keys are read through the [`read_key`](struct.FitsFile.html#method.read_key) function,
//! and is generic over types that implement the [`ReadsKey`](trait.ReadsKey.html) trait:
//!
//! ```rust
//! # extern crate fitsio;
//! #
//! # fn main() {
//! # let filename = "../testdata/full_example.fits";
//! # let fptr = fitsio::FitsFile::open(filename).unwrap();
//! # {
//! let int_value: i64 = fptr.hdu(0).unwrap().read_key("INTTEST").unwrap();
//! # }
//!
//! // Alternatively
//! # {
//! let int_value = fptr.hdu(0).unwrap().read_key::<i64>("INTTEST").unwrap();
//! # }
//!
//! // Or let the compiler infer the types (if possible)
//! # }
//! ```
//!
//! Header cards can be written through the method
//! [`write_key`](struct.FitsFile.html#method.write_key). It takes a key name and value. See [the
//! `WritesKey`](trait.WritesKey.html) trait for supported data types.
//!
//! ```rust
//! # extern crate tempdir;
//! # extern crate fitsio;
//! # let tdir = tempdir::TempDir::new("fitsio-").unwrap();
//! # let tdir_path = tdir.path();
//! # let filename = tdir_path.join("test.fits");
//! # {
//! # let fptr = fitsio::FitsFile::create(filename.to_str().unwrap()).unwrap();
//! fptr.hdu(0).unwrap().write_key("foo", 1i64).unwrap();
//! assert_eq!(fptr.hdu(0).unwrap().read_key::<i64>("foo").unwrap(), 1i64);
//! # }
//! ```
//!
//! ## Reading file data
//!
//! ### Images
//!
//! Image data can be read through either
//! [`read_section`](struct.FitsHdu.html#method.read_section) which reads contiguous pixels
//! between a start index and end index, or
//! [`read_region`](struct.FitsHdu.html#method.read_region) which reads rectangular chunks from
//! the image.
//!
//! ```rust
//! # extern crate fitsio;
//! #
//! # fn main() {
//! # let filename = "../testdata/full_example.fits";
//! # let fptr = fitsio::FitsFile::open(filename).unwrap();
//! # let hdu = fptr.hdu(0).unwrap();
//! // Read the first 100 pixels
//! let first_row: Vec<i32> = hdu.read_section(0, 100).unwrap();
//!
//! // Read a square section of the image
//!
//! let xcoord = 0..10;
//! let ycoord = 0..10;
//! let chunk: Vec<i32> = hdu.read_region(&vec![&xcoord, &ycoord]).unwrap();
//! # }
//! ```
//!
//! ### Tables
//!
//! Columns can be read using the [`read_col`](struct.FitsFile.html#method.read_col) function,
//! which can convert data types on the fly. See the [`ReadsCol`](trait.ReadsCol.html) trait for
//! supported data types.
//!
//! ```rust
//! # extern crate fitsio;
//! #
//! # fn main() {
//! # let filename = "../testdata/full_example.fits";
//! # let fptr = fitsio::FitsFile::open(filename).unwrap();
//! # fptr.change_hdu(1).unwrap();
//! let integer_data: Vec<i32> = fptr.hdu(1).and_then(|hdu| hdu.read_col("intcol")).unwrap();
//! # }
//! ```
//!
//! The [`columns`](struct.FitsFile.html#method.columns) method returns an iterator over all of the
//! columns in a table.
//!
//! [1]: http://heasarc.gsfc.nasa.gov/fitsio/fitsio.html
//! [2]: https://crates.io/crates/fitsio-sys

#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]

extern crate fitsio_sys as sys;
extern crate libc;

#[macro_use]
pub mod fitserror;
mod stringutils;
pub mod types;
mod columndescription;
mod fitsfile;
pub mod fitshdu;
mod conversions;

pub use self::fitsfile::FitsFile;
pub use self::types::HduInfo;
<commit_msg>Make rustfmt happy<commit_after>//! `fitsio` - a thin wrapper around the [`cfitsio`][1] C library.
//!
//! * [HDU access](#hdu-access)
//! * [Header keys](#header-keys)
//! * [Reading file data](#reading-file-data)
//!     * [Images](#images)
//!     * [Tables](#tables)
//!
//! This library wraps the low level `cfitsio` bindings: [`fitsio-sys`][2] and provides a more
//! native experience for rust users.
//!
//! The main interface to a fits file is [`FitsFile`](struct.FitsFile.html). All file manipulation
//! and reading starts with this class.
//!
//! Opening a file:
//!
//! ```rust
//! # fn main() {
//! # let filename = "../testdata/full_example.fits";
//! use fitsio::FitsFile;
//!
//! // let filename = ...;
//! let fptr = FitsFile::open(filename).unwrap();
//! # }
//! ```
//!
//! Alternatively a new file can be created on disk with the companion method
//! [`create`](struct.FitsFile.html#method.create):
//!
//! ```rust
//! # extern crate tempdir;
//! # extern crate fitsio;
//! # use fitsio::FitsFile;
//! # fn main() {
//! # let tdir = tempdir::TempDir::new("fitsio-").unwrap();
//! # let tdir_path = tdir.path();
//! # let _filename = tdir_path.join("test.fits");
//! # let filename = _filename.to_str().unwrap();
//! use fitsio::FitsFile;
//!
//! // let filename = ...;
//! let fptr = FitsFile::create(filename).unwrap();
//! # }
//! ```
//!
//! From this point, the current HDU can be queried and changed, or fits header cards can be read
//! or file contents can be read.
//!
//! ## HDU access
//!
//! HDU information belongs to the [`FitsHdu`](struct.FitsHdu.html) object. HDUs can be fetched by
//! `String`/`str` or integer (0-indexed).
//! The `HduInfo` object contains information about the current HDU:
//!
//! ```rust
//! # extern crate fitsio;
//! # extern crate fitsio_sys;
//! # use fitsio::{FitsFile, HduInfo};
//! #
//! # fn main() {
//! # let filename = "../testdata/full_example.fits";
//! # let fptr = FitsFile::open(filename).unwrap();
//! let hdu = fptr.hdu(0).unwrap();
//! // image HDU
//! if let HduInfo::ImageInfo { shape } = hdu.info {
//!    println!("Image is {}-dimensional", shape.len());
//!    println!("Found image with shape {:?}", shape);
//! }
//! # let hdu = fptr.hdu("TESTEXT").unwrap();
//!
//! // tables
//! if let HduInfo::TableInfo { column_descriptions, num_rows, .. } = hdu.info {
//!     println!("Table contains {} rows", num_rows);
//!     println!("Table has {} columns", column_descriptions.len());
//! }
//! # }
//! ```
//!
//! ## Header keys
//!
//! Header keys are read through the [`read_key`](struct.FitsFile.html#method.read_key) function,
//! and is generic over types that implement the [`ReadsKey`](trait.ReadsKey.html) trait:
//!
//! ```rust
//! # extern crate fitsio;
//! #
//! # fn main() {
//! # let filename = "../testdata/full_example.fits";
//! # let fptr = fitsio::FitsFile::open(filename).unwrap();
//! # {
//! let int_value: i64 = fptr.hdu(0).unwrap().read_key("INTTEST").unwrap();
//! # }
//!
//! // Alternatively
//! # {
//! let int_value = fptr.hdu(0).unwrap().read_key::<i64>("INTTEST").unwrap();
//! # }
//!
//! // Or let the compiler infer the types (if possible)
//! # }
//! ```
//!
//! Header cards can be written through the method
//! [`write_key`](struct.FitsFile.html#method.write_key). It takes a key name and value. See [the
//! `WritesKey`](trait.WritesKey.html) trait for supported data types.
//!
//! ```rust
//! # extern crate tempdir;
//! # extern crate fitsio;
//! # let tdir = tempdir::TempDir::new("fitsio-").unwrap();
//! # let tdir_path = tdir.path();
//! # let filename = tdir_path.join("test.fits");
//! # {
//! # let fptr = fitsio::FitsFile::create(filename.to_str().unwrap()).unwrap();
//! fptr.hdu(0).unwrap().write_key("foo", 1i64).unwrap();
//! assert_eq!(fptr.hdu(0).unwrap().read_key::<i64>("foo").unwrap(), 1i64);
//! # }
//! ```
//!
//! ## Reading file data
//!
//! ### Images
//!
//! Image data can be read through either
//! [`read_section`](struct.FitsHdu.html#method.read_section) which reads contiguous pixels
//! between a start index and end index, or
//! [`read_region`](struct.FitsHdu.html#method.read_region) which reads rectangular chunks from
//! the image.
//!
//! ```rust
//! # extern crate fitsio;
//! #
//! # fn main() {
//! # let filename = "../testdata/full_example.fits";
//! # let fptr = fitsio::FitsFile::open(filename).unwrap();
//! # let hdu = fptr.hdu(0).unwrap();
//! // Read the first 100 pixels
//! let first_row: Vec<i32> = hdu.read_section(0, 100).unwrap();
//!
//! // Read a square section of the image
//!
//! let xcoord = 0..10;
//! let ycoord = 0..10;
//! let chunk: Vec<i32> = hdu.read_region(&vec![&xcoord, &ycoord]).unwrap();
//! # }
//! ```
//!
//! ### Tables
//!
//! Columns can be read using the [`read_col`](struct.FitsFile.html#method.read_col) function,
//! which can convert data types on the fly. See the [`ReadsCol`](trait.ReadsCol.html) trait for
//! supported data types.
//!
//! ```rust
//! # extern crate fitsio;
//! #
//! # fn main() {
//! # let filename = "../testdata/full_example.fits";
//! # let fptr = fitsio::FitsFile::open(filename).unwrap();
//! # fptr.change_hdu(1).unwrap();
//! let integer_data: Vec<i32> = fptr.hdu(1).and_then(|hdu| hdu.read_col("intcol")).unwrap();
//! # }
//! ```
//!
//! The [`columns`](struct.FitsFile.html#method.columns) method returns an iterator over all of the
//! columns in a table.
//!
//! [1]: http://heasarc.gsfc.nasa.gov/fitsio/fitsio.html
//! [2]: https://crates.io/crates/fitsio-sys

#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]

extern crate fitsio_sys as sys;
extern crate libc;

#[macro_use]
pub mod fitserror;
mod stringutils;
pub mod types;
mod columndescription;
mod fitsfile;
pub mod fitshdu;
mod conversions;

pub use self::fitsfile::FitsFile;
pub use self::types::HduInfo;
<|endoftext|>


<commit_before>macro_rules! declare_deprecated_lint {
    (pub $name: ident, $_reason: expr) => {
        declare_lint!(pub $name, Allow, "deprecated lint")
    }
}

declare_deprecated_lint! {
    /// **What it does:** Nothing. This lint has been deprecated.
    ///
    /// **Deprecation reason:** This used to check for `assert!(a == b)` and recommend
    /// replacement with `assert_eq!(a, b)`, but this is no longer needed after RFC 2011.
    pub SHOULD_ASSERT_EQ,
    "`assert!()` will be more flexible with RFC 2011"
}

declare_deprecated_lint! {
    /// **What it does:** Nothing. This lint has been deprecated.
    ///
    /// **Deprecation reason:** This used to check for `Vec::extend`, which was slower than
    /// `Vec::extend_from_slice`. Thanks to specialization, this is no longer true.
    pub EXTEND_FROM_SLICE,
    "`.extend_from_slice(_)` is a faster way to extend a Vec by a slice"
}

declare_deprecated_lint! {
    /// **What it does:** Nothing. This lint has been deprecated.
    ///
    /// **Deprecation reason:** `Range::step_by(0)` used to be linted since it's
    /// an infinite iterator, which is better expressed by `iter::repeat`,
    /// but the method has been removed for `Iterator::step_by` which panics
    /// if given a zero
    pub RANGE_STEP_BY_ZERO,
    "`iterator.step_by(0)` panics nowadays"
}

declare_deprecated_lint! {
    /// **What it does:** Nothing. This lint has been deprecated.
    ///
    /// **Deprecation reason:** This used to check for `Vec::as_slice`, which was unstable with good
    /// stable alternatives. `Vec::as_slice` has now been stabilized.
    pub UNSTABLE_AS_SLICE,
    "`Vec::as_slice` has been stabilized in 1.7"
}

declare_deprecated_lint! {
    /// **What it does:** Nothing. This lint has been deprecated.
    ///
    /// **Deprecation reason:** This used to check for `Vec::as_mut_slice`, which was unstable with good
    /// stable alternatives. `Vec::as_mut_slice` has now been stabilized.
    pub UNSTABLE_AS_MUT_SLICE,
    "`Vec::as_mut_slice` has been stabilized in 1.7"
}

declare_deprecated_lint! {
    /// **What it does:** Nothing. This lint has been deprecated.
    ///
    /// **Deprecation reason:** This used to check for `.to_string()` method calls on values
    /// of type `&str`. This is not unidiomatic and with specialization coming, `to_string` could be
    /// specialized to be as efficient as `to_owned`.
    pub STR_TO_STRING,
    "using `str::to_string` is common even today and specialization will likely happen soon"
}

declare_deprecated_lint! {
    /// **What it does:** Nothing. This lint has been deprecated.
    ///
    /// **Deprecation reason:** This used to check for `.to_string()` method calls on values
    /// of type `String`. This is not unidiomatic and with specialization coming, `to_string` could be
    /// specialized to be as efficient as `clone`.
    pub STRING_TO_STRING,
    "using `string::to_string` is common even today and specialization will likely happen soon"
}

declare_deprecated_lint! {
    /// **What it does:** Nothing. This lint has been deprecated.
    ///
    /// **Deprecation reason:** This lint should never have applied to non-pointer types, as transmuting
    /// between non-pointer types of differing alignment is well-defined behavior (it's semantically
    /// equivalent to a memcpy). This lint has thus been refactored into two separate lints:
    /// cast_ptr_alignment and transmute_ptr_to_ptr.
    pub MISALIGNED_TRANSMUTE,
    "this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr"
}

/// **What it does:** Nothing. This lint has been deprecated.
///
/// **Deprecation reason:** This lint is too subjective, not having a good reason for being in clippy.
/// Additionally, compound assignment operators may be overloaded separately from their non-assigning
/// counterparts, so this lint may suggest a change in behavior or the code may not compile.
declare_deprecated_lint! {
    pub ASSIGN_OPS,
    "using compound assignment operators (e.g., `+=`) is harmless"
}

declare_deprecated_lint! {
    /// **What it does:** Nothing. This lint has been deprecated.
    ///
    /// **Deprecation reason:** The original rule will only lint for `if let`. After
    /// making it support to lint `match`, naming as `if let` is not suitable for it.
    /// So, this lint is deprecated.
    pub IF_LET_REDUNDANT_PATTERN_MATCHING,
    "this lint has been changed to redundant_pattern_matching"
}

declare_deprecated_lint! {
    /// **What it does:** Nothing. This lint has been deprecated.
    ///
    /// **Deprecation reason:** This lint used to suggest replacing `let mut vec =
    /// Vec::with_capacity(n); vec.set_len(n);` with `let vec = vec![0; n];`. The
    /// replacement has very different performance characteristics so the lint is
    /// deprecated.
    pub UNSAFE_VECTOR_INITIALIZATION,
    "the replacement suggested by this lint had substantially different behavior"
}

declare_deprecated_lint! {
    /// **What it does:** Nothing. This lint has been deprecated.
    ///
    /// **Deprecation reason:** This lint has been superseded by the warn-by-default
    /// `invalid_value` rustc lint.
    pub INVALID_REF,
    "superseded by rustc lint `invalid_value`"
}

declare_deprecated_lint! {
    /// **What it does:** Nothing. This lint has been deprecated.
    ///
    /// **Deprecation reason:** This lint has been superseded by #[must_use] in rustc.
    pub UNUSED_COLLECT,
    "`collect` has been marked as #[must_use] in rustc and that covers all cases of this lint"
}
<commit_msg>deprecated_lints: re-fix ASSIGN_OPS lint doc-comment<commit_after>macro_rules! declare_deprecated_lint {
    (pub $name: ident, $_reason: expr) => {
        declare_lint!(pub $name, Allow, "deprecated lint")
    }
}

declare_deprecated_lint! {
    /// **What it does:** Nothing. This lint has been deprecated.
    ///
    /// **Deprecation reason:** This used to check for `assert!(a == b)` and recommend
    /// replacement with `assert_eq!(a, b)`, but this is no longer needed after RFC 2011.
    pub SHOULD_ASSERT_EQ,
    "`assert!()` will be more flexible with RFC 2011"
}

declare_deprecated_lint! {
    /// **What it does:** Nothing. This lint has been deprecated.
    ///
    /// **Deprecation reason:** This used to check for `Vec::extend`, which was slower than
    /// `Vec::extend_from_slice`. Thanks to specialization, this is no longer true.
    pub EXTEND_FROM_SLICE,
    "`.extend_from_slice(_)` is a faster way to extend a Vec by a slice"
}

declare_deprecated_lint! {
    /// **What it does:** Nothing. This lint has been deprecated.
    ///
    /// **Deprecation reason:** `Range::step_by(0)` used to be linted since it's
    /// an infinite iterator, which is better expressed by `iter::repeat`,
    /// but the method has been removed for `Iterator::step_by` which panics
    /// if given a zero
    pub RANGE_STEP_BY_ZERO,
    "`iterator.step_by(0)` panics nowadays"
}

declare_deprecated_lint! {
    /// **What it does:** Nothing. This lint has been deprecated.
    ///
    /// **Deprecation reason:** This used to check for `Vec::as_slice`, which was unstable with good
    /// stable alternatives. `Vec::as_slice` has now been stabilized.
    pub UNSTABLE_AS_SLICE,
    "`Vec::as_slice` has been stabilized in 1.7"
}

declare_deprecated_lint! {
    /// **What it does:** Nothing. This lint has been deprecated.
    ///
    /// **Deprecation reason:** This used to check for `Vec::as_mut_slice`, which was unstable with good
    /// stable alternatives. `Vec::as_mut_slice` has now been stabilized.
    pub UNSTABLE_AS_MUT_SLICE,
    "`Vec::as_mut_slice` has been stabilized in 1.7"
}

declare_deprecated_lint! {
    /// **What it does:** Nothing. This lint has been deprecated.
    ///
    /// **Deprecation reason:** This used to check for `.to_string()` method calls on values
    /// of type `&str`. This is not unidiomatic and with specialization coming, `to_string` could be
    /// specialized to be as efficient as `to_owned`.
    pub STR_TO_STRING,
    "using `str::to_string` is common even today and specialization will likely happen soon"
}

declare_deprecated_lint! {
    /// **What it does:** Nothing. This lint has been deprecated.
    ///
    /// **Deprecation reason:** This used to check for `.to_string()` method calls on values
    /// of type `String`. This is not unidiomatic and with specialization coming, `to_string` could be
    /// specialized to be as efficient as `clone`.
    pub STRING_TO_STRING,
    "using `string::to_string` is common even today and specialization will likely happen soon"
}

declare_deprecated_lint! {
    /// **What it does:** Nothing. This lint has been deprecated.
    ///
    /// **Deprecation reason:** This lint should never have applied to non-pointer types, as transmuting
    /// between non-pointer types of differing alignment is well-defined behavior (it's semantically
    /// equivalent to a memcpy). This lint has thus been refactored into two separate lints:
    /// cast_ptr_alignment and transmute_ptr_to_ptr.
    pub MISALIGNED_TRANSMUTE,
    "this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr"
}

declare_deprecated_lint! {
    /// **What it does:** Nothing. This lint has been deprecated.
    ///
    /// **Deprecation reason:** This lint is too subjective, not having a good reason for being in clippy.
    /// Additionally, compound assignment operators may be overloaded separately from their non-assigning
    /// counterparts, so this lint may suggest a change in behavior or the code may not compile.
    pub ASSIGN_OPS,
    "using compound assignment operators (e.g., `+=`) is harmless"
}

declare_deprecated_lint! {
    /// **What it does:** Nothing. This lint has been deprecated.
    ///
    /// **Deprecation reason:** The original rule will only lint for `if let`. After
    /// making it support to lint `match`, naming as `if let` is not suitable for it.
    /// So, this lint is deprecated.
    pub IF_LET_REDUNDANT_PATTERN_MATCHING,
    "this lint has been changed to redundant_pattern_matching"
}

declare_deprecated_lint! {
    /// **What it does:** Nothing. This lint has been deprecated.
    ///
    /// **Deprecation reason:** This lint used to suggest replacing `let mut vec =
    /// Vec::with_capacity(n); vec.set_len(n);` with `let vec = vec![0; n];`. The
    /// replacement has very different performance characteristics so the lint is
    /// deprecated.
    pub UNSAFE_VECTOR_INITIALIZATION,
    "the replacement suggested by this lint had substantially different behavior"
}

declare_deprecated_lint! {
    /// **What it does:** Nothing. This lint has been deprecated.
    ///
    /// **Deprecation reason:** This lint has been superseded by the warn-by-default
    /// `invalid_value` rustc lint.
    pub INVALID_REF,
    "superseded by rustc lint `invalid_value`"
}

declare_deprecated_lint! {
    /// **What it does:** Nothing. This lint has been deprecated.
    ///
    /// **Deprecation reason:** This lint has been superseded by #[must_use] in rustc.
    pub UNUSED_COLLECT,
    "`collect` has been marked as #[must_use] in rustc and that covers all cases of this lint"
}
<|endoftext|>


<commit_before>#![doc(html_root_url = "http://alexcrichton.com/flate2-rs")]
#![allow(bad_style)]

extern crate libc;

pub const MZ_NO_FLUSH: libc::c_int = 0;
pub const MZ_SYNC_FLUSH: libc::c_int = 2;
pub const MZ_FINISH: libc::c_int = 4;

pub const MZ_OK: libc::c_int = 0;
pub const MZ_STREAM_END: libc::c_int = 1;
pub const MZ_NEED_DICT: libc::c_int = 2;
pub const MZ_ERRNO: libc::c_int = -1;
pub const MZ_STREAM_ERROR: libc::c_int = -2;
pub const MZ_DATA_ERROR: libc::c_int = -3;
pub const MZ_MEM_ERROR: libc::c_int = -4;
pub const MZ_BUF_ERROR: libc::c_int = -5;
pub const MZ_VERSION_ERROR: libc::c_int = -6;
pub const MZ_PARAM_ERROR: libc::c_int = -10000;

pub const MZ_DEFLATED: libc::c_int = 8;
pub const MZ_DEFAULT_WINDOW_BITS: libc::c_int = 15;
pub const MZ_DEFAULT_STRATEGY: libc::c_int = 0;

#[repr(C)]
pub struct mz_stream {
    pub next_in: *const u8,
    pub avail_in: libc::c_uint,
    pub total_in: libc::c_ulong,

    pub next_out: *mut u8,
    pub avail_out: libc::c_uint,
    pub total_out: libc::c_ulong,

    pub msg: *const libc::c_char,
    pub state: *mut mz_internal_state,

    pub zalloc: Option<mz_alloc_func>,
    pub zfree: Option<mz_free_func>,
    pub opaque: *mut libc::c_void,

    pub data_type: libc::c_int,
    pub adler: libc::c_ulong,
    pub reserved: libc::c_ulong,
}

pub enum mz_internal_state {}

pub type mz_alloc_func = extern fn(*mut libc::c_void,
                                   libc::size_t,
                                   libc::size_t) -> *mut libc::c_void;
pub type mz_free_func = extern fn(*mut libc::c_void, *mut libc::c_void);

extern {
    pub fn mz_deflateInit2(stream: *mut mz_stream,
                           level: libc::c_int,
                           method: libc::c_int,
                           window_bits: libc::c_int,
                           mem_level: libc::c_int,
                           strategy: libc::c_int) -> libc::c_int;
    pub fn mz_deflate(stream: *mut mz_stream, flush: libc::c_int) -> libc::c_int;
    pub fn mz_deflateEnd(stream: *mut mz_stream) -> libc::c_int;

    pub fn mz_inflateInit2(stream: *mut mz_stream,
                           window_bits: libc::c_int) -> libc::c_int;
    pub fn mz_inflate(stream: *mut mz_stream, flush: libc::c_int) -> libc::c_int;
    pub fn mz_inflateEnd(stream: *mut mz_stream) -> libc::c_int;

    pub fn mz_crc32(crc: libc::c_ulong, ptr: *const u8,
                    len: libc::size_t) -> libc::c_ulong;
}

<commit_msg>miniz-sys: Add missing flush modes and tweak style<commit_after>#![doc(html_root_url = "http://alexcrichton.com/flate2-rs")]
#![allow(bad_style)]

extern crate libc;
use libc::*;

pub const MZ_NO_FLUSH: c_int = 0;
pub const MZ_PARTIAL_FLUSH: c_int = 1;
pub const MZ_SYNC_FLUSH: c_int = 2;
pub const MZ_FULL_FLUSH: c_int = 3;
pub const MZ_FINISH: c_int = 4;
pub const MZ_BLOCK: c_int = 5;

pub const MZ_OK: c_int = 0;
pub const MZ_STREAM_END: c_int = 1;
pub const MZ_NEED_DICT: c_int = 2;
pub const MZ_ERRNO: c_int = -1;
pub const MZ_STREAM_ERROR: c_int = -2;
pub const MZ_DATA_ERROR: c_int = -3;
pub const MZ_MEM_ERROR: c_int = -4;
pub const MZ_BUF_ERROR: c_int = -5;
pub const MZ_VERSION_ERROR: c_int = -6;
pub const MZ_PARAM_ERROR: c_int = -10000;

pub const MZ_DEFLATED: c_int = 8;
pub const MZ_DEFAULT_WINDOW_BITS: c_int = 15;
pub const MZ_DEFAULT_STRATEGY: c_int = 0;

#[repr(C)]
pub struct mz_stream {
    pub next_in: *const u8,
    pub avail_in: c_uint,
    pub total_in: c_ulong,

    pub next_out: *mut u8,
    pub avail_out: c_uint,
    pub total_out: c_ulong,

    pub msg: *const c_char,
    pub state: *mut mz_internal_state,

    pub zalloc: Option<mz_alloc_func>,
    pub zfree: Option<mz_free_func>,
    pub opaque: *mut c_void,

    pub data_type: c_int,
    pub adler: c_ulong,
    pub reserved: c_ulong,
}

pub enum mz_internal_state {}

pub type mz_alloc_func = extern fn(*mut c_void,
                                   size_t,
                                   size_t) -> *mut c_void;
pub type mz_free_func = extern fn(*mut c_void, *mut c_void);

extern {
    pub fn mz_deflateInit2(stream: *mut mz_stream,
                           level: c_int,
                           method: c_int,
                           window_bits: c_int,
                           mem_level: c_int,
                           strategy: c_int) -> c_int;
    pub fn mz_deflate(stream: *mut mz_stream, flush: c_int) -> c_int;
    pub fn mz_deflateEnd(stream: *mut mz_stream) -> c_int;
    pub fn mz_deflateReset(stream: *mut mz_stream) -> c_int;

    pub fn mz_inflateInit2(stream: *mut mz_stream,
                           window_bits: c_int) -> c_int;
    pub fn mz_inflate(stream: *mut mz_stream, flush: c_int) -> c_int;
    pub fn mz_inflateEnd(stream: *mut mz_stream) -> c_int;

    pub fn mz_crc32(crc: c_ulong, ptr: *const u8,
                    len: size_t) -> c_ulong;
}

<|endoftext|>