<commit_before>use dicom::core::smallvec;
use dicom::{core::dicom_value, dictionary_std::tags};
use dicom::{
    core::{DataElement, PrimitiveValue, VR},
    encoding::transfer_syntax,
    object::{mem::InMemDicomObject, open_file, StandardDataDictionary},
    transfer_syntax::TransferSyntaxRegistry,
};
use dicom_ul::pdu::Pdu;
use dicom_ul::{
    association::ClientAssociationOptions,
    pdu::{reader::PDU_HEADER_SIZE, PDataValue, PDataValueType},
};
use smallvec::smallvec;
use std::path::PathBuf;
use structopt::StructOpt;
use transfer_syntax::TransferSyntaxIndex;

/// DICOM C-STORE SCU
#[derive(Debug, StructOpt)]
struct App {
    /// socket address to STORE SCP (example: "127.0.0.1:104")
    addr: String,
    /// the DICOM file to store
    file: PathBuf,
    /// verbose mode
    #[structopt(short = "v", long = "verbose")]
    verbose: bool,
    /// the C-STORE message ID
    #[structopt(short = "m", long = "message-id", default_value = "1")]
    message_id: u16,
    /// the calling AE title
    #[structopt(long = "calling-ae-title", default_value = "STORE-SCU")]
    calling_ae_title: String,
    /// the called AE title
    #[structopt(long = "called-ae-title", default_value = "ANY-SCP")]
    called_ae_title: String,
    /// the maximum PDU length accepted by the SCU
    #[structopt(long = "max-pdu-length", default_value = "16384")]
    max_pdu_length: u32,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let App {
        addr,
        file,
        verbose,
        message_id,
        calling_ae_title,
        called_ae_title,
        max_pdu_length,
    } = App::from_args();

    if verbose {
        println!("Opening file '{}'...", file.display());
    }

    let dicom_file = open_file(file)?;

    let meta = dicom_file.meta();

    let storage_sop_class_uid = &meta.media_storage_sop_class_uid;
    let storage_sop_instance_uid = &meta.media_storage_sop_instance_uid;
    let transfer_syntax = &meta.transfer_syntax.trim_end_matches("\0");

    let file_ts_desc = TransferSyntaxRegistry
        .get(transfer_syntax)
        .ok_or("Unsupported file transfer syntax")?;

    if verbose {
        println!("Establishing association with '{}'...", &addr);
    }

    let mut scu = ClientAssociationOptions::new()
        .with_abstract_syntax(storage_sop_class_uid.to_string())
        .with_transfer_syntax(transfer_syntax.to_string())
        .calling_ae_title(calling_ae_title)
        .called_ae_title(called_ae_title)
        .max_pdu_length(max_pdu_length)
        .establish(addr)?;

    if verbose {
        println!("Association established");
    }

    // TODO(#106) transfer syntax conversion is currently not supported
    let pc_selected = if let Some(pc) = scu.presentation_contexts().iter().find(|pc| {
        // Check support for this transfer syntax.
        // If it is the same as the file, we're good.
        // Otherwise, uncompressed data set encoding
        // and native pixel data is required on both ends.
        let ts = &pc.transfer_syntax;
        ts == transfer_syntax
            || TransferSyntaxRegistry
                .get(&pc.transfer_syntax)
                .filter(|ts| file_ts_desc.is_codec_free() && ts.is_codec_free())
                .map(|_| true)
                .unwrap_or(false)
    }) {
        pc.clone()
    } else {
        eprintln!("Could not choose a transfer syntax");
        let _ = scu.abort();
        std::process::exit(-2);
    };

    let ts = TransferSyntaxRegistry
        .get(&pc_selected.transfer_syntax)
        .ok_or_else(|| "Poorly negotiated transfer syntax")?;

    if verbose {
        println!("Transfer Syntax: {}", ts.name());
    }

    let cmd = store_req_command(
        &storage_sop_class_uid,
        &storage_sop_instance_uid,
        message_id,
    );

    let mut cmd_data = Vec::with_capacity(128);
    cmd.write_dataset_with_ts(
        &mut cmd_data,
        &dicom::transfer_syntax::entries::IMPLICIT_VR_LITTLE_ENDIAN.erased(),
    )?;

    let mut object_data = Vec::with_capacity(1024);
    dicom_file.write_dataset_with_ts(&mut object_data, &ts)?;

    let nbytes = cmd_data.len() + object_data.len();

    if verbose {
        println!("Sending payload (~ {} kB)...", nbytes / 1_000);
    }

    if nbytes < scu.acceptor_max_pdu_length() as usize - 100 {
        let pdu = Pdu::PData {
            data: vec![
                PDataValue {
                    presentation_context_id: pc_selected.id,
                    value_type: PDataValueType::Command,
                    is_last: true,
                    data: cmd_data,
                },
                PDataValue {
                    presentation_context_id: pc_selected.id,
                    value_type: PDataValueType::Data,
                    is_last: true,
                    data: object_data,
                },
            ],
        };

        scu.send(&pdu)?;
    } else {
        let pdu = Pdu::PData {
            data: vec![PDataValue {
                presentation_context_id: pc_selected.id,
                value_type: PDataValueType::Command,
                is_last: true,
                data: cmd_data,
            }],
        };

        scu.send(&pdu)?;

        let chunk_size = (scu.acceptor_max_pdu_length() - PDU_HEADER_SIZE) as usize;
        let last_chunk = object_data.len() / chunk_size;
        let last_chunk = if object_data.len() % chunk_size != 0 {
            last_chunk
        } else {
            last_chunk - 1
        };

        for (i, chunk) in object_data.chunks(chunk_size).enumerate() {
            let pdu = Pdu::PData {
                data: vec![PDataValue {
                    presentation_context_id: pc_selected.id,
                    value_type: PDataValueType::Data,
                    is_last: i == last_chunk,
                    data: chunk.to_vec(),
                }],
            };
            scu.send(&pdu)?;
        }
    }

    if verbose {
        println!("Awaiting response...");
    }

    let rsp_pdu = scu.receive()?;

    match rsp_pdu {
        Pdu::PData { data } => {
            let data_value = &data[0];

            let cmd_obj = InMemDicomObject::read_dataset_with_ts(
                &data_value.data[..],
                &dicom::transfer_syntax::entries::IMPLICIT_VR_LITTLE_ENDIAN.erased(),
            )?;
            if verbose {
                println!("Response: {:?}", cmd_obj);
            }
            let status = cmd_obj.element(tags::STATUS)?.to_int::<u16>()?;
            let storage_sop_instance_uid = storage_sop_instance_uid.trim_end_matches(|c: char| c.is_whitespace() || c == '\0');
            if status == 0 {
                println!("Sucessfully stored instance `{}`", storage_sop_instance_uid);
            } else {
                println!(
                    "Failed to store instance `{}` (status code {})",
                    storage_sop_instance_uid, status
                );
            }

            scu.release()?;
        }

        pdu @ Pdu::Unknown { .. }
        | pdu @ Pdu::AssociationRQ { .. }
        | pdu @ Pdu::AssociationAC { .. }
        | pdu @ Pdu::AssociationRJ { .. }
        | pdu @ Pdu::ReleaseRQ
        | pdu @ Pdu::ReleaseRP
        | pdu @ Pdu::AbortRQ { .. } => {
            eprintln!("Unexpected SCP response: {:?}", pdu);
            let _ = scu.abort();
            std::process::exit(-2);
        }
    }

    Ok(())
}

fn store_req_command(
    storage_sop_class_uid: &str,
    storage_sop_instance_uid: &str,
    message_id: u16,
) -> InMemDicomObject<StandardDataDictionary> {
    let mut obj = InMemDicomObject::create_empty();

    // group length
    obj.put(DataElement::new(
        tags::COMMAND_GROUP_LENGTH,
        VR::UL,
        PrimitiveValue::from(
            12 + 8
                + even_len(storage_sop_class_uid.len())
                + 10
                + 10
                + 10
                + 10
                + 8
                + even_len(storage_sop_instance_uid.len()),
        ),
    ));

    // SOP Class UID
    obj.put(DataElement::new(
        tags::AFFECTED_SOP_CLASS_UID,
        VR::UI,
        dicom_value!(Str, storage_sop_class_uid),
    ));

    // command field
    obj.put(DataElement::new(
        tags::COMMAND_FIELD,
        VR::US,
        dicom_value!(U16, [0x0001]),
    ));

    // message ID
    obj.put(DataElement::new(
        tags::MESSAGE_ID,
        VR::US,
        dicom_value!(U16, [message_id]),
    ));

    //priority
    obj.put(DataElement::new(
        tags::PRIORITY,
        VR::US,
        dicom_value!(U16, [0x0000]),
    ));

    // data set type
    obj.put(DataElement::new(
        tags::COMMAND_DATA_SET_TYPE,
        VR::US,
        dicom_value!(U16, [0x0000]),
    ));

    // affected SOP Instance UID
    obj.put(DataElement::new(
        tags::AFFECTED_SOP_INSTANCE_UID,
        VR::UI,
        dicom_value!(Str, storage_sop_instance_uid),
    ));

    obj
}

fn even_len(l: usize) -> u32 {
    ((l + 1) & !1) as u32
}

#[cfg(test)]
mod tests {
    use super::even_len;
    #[test]
    fn test_even_len() {
        assert_eq!(even_len(0), 0);
        assert_eq!(even_len(1), 2);
        assert_eq!(even_len(2), 2);
        assert_eq!(even_len(3), 4);
        assert_eq!(even_len(4), 4);
        assert_eq!(even_len(5), 6);
    }
}
<commit_msg>[storescu] revert to using pdata abstraction again<commit_after>use dicom::core::smallvec;
use dicom::{core::dicom_value, dictionary_std::tags};
use dicom::{
    core::{DataElement, PrimitiveValue, VR},
    encoding::transfer_syntax,
    object::{mem::InMemDicomObject, open_file, StandardDataDictionary},
    transfer_syntax::TransferSyntaxRegistry,
};
use dicom_ul::pdu::Pdu;
use dicom_ul::{
    association::ClientAssociationOptions,
    pdu::{PDataValue, PDataValueType},
};
use smallvec::smallvec;
use std::io::Write;
use std::path::PathBuf;
use structopt::StructOpt;
use transfer_syntax::TransferSyntaxIndex;

/// DICOM C-STORE SCU
#[derive(Debug, StructOpt)]
struct App {
    /// socket address to STORE SCP (example: "127.0.0.1:104")
    addr: String,
    /// the DICOM file to store
    file: PathBuf,
    /// verbose mode
    #[structopt(short = "v", long = "verbose")]
    verbose: bool,
    /// the C-STORE message ID
    #[structopt(short = "m", long = "message-id", default_value = "1")]
    message_id: u16,
    /// the calling AE title
    #[structopt(long = "calling-ae-title", default_value = "STORE-SCU")]
    calling_ae_title: String,
    /// the called AE title
    #[structopt(long = "called-ae-title", default_value = "ANY-SCP")]
    called_ae_title: String,
    /// the maximum PDU length accepted by the SCU
    #[structopt(long = "max-pdu-length", default_value = "16384")]
    max_pdu_length: u32,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let App {
        addr,
        file,
        verbose,
        message_id,
        calling_ae_title,
        called_ae_title,
        max_pdu_length,
    } = App::from_args();

    if verbose {
        println!("Opening file '{}'...", file.display());
    }

    let dicom_file = open_file(file)?;

    let meta = dicom_file.meta();

    let storage_sop_class_uid = &meta.media_storage_sop_class_uid;
    let storage_sop_instance_uid = &meta.media_storage_sop_instance_uid;
    let transfer_syntax = &meta.transfer_syntax.trim_end_matches("\0");

    let file_ts_desc = TransferSyntaxRegistry
        .get(transfer_syntax)
        .ok_or("Unsupported file transfer syntax")?;

    if verbose {
        println!("Establishing association with '{}'...", &addr);
    }

    let mut scu = ClientAssociationOptions::new()
        .with_abstract_syntax(storage_sop_class_uid.to_string())
        .with_transfer_syntax(transfer_syntax.to_string())
        .calling_ae_title(calling_ae_title)
        .called_ae_title(called_ae_title)
        .max_pdu_length(max_pdu_length)
        .establish(addr)?;

    if verbose {
        println!("Association established");
    }

    // TODO(#106) transfer syntax conversion is currently not supported
    let pc_selected = if let Some(pc) = scu.presentation_contexts().iter().find(|pc| {
        // Check support for this transfer syntax.
        // If it is the same as the file, we're good.
        // Otherwise, uncompressed data set encoding
        // and native pixel data is required on both ends.
        let ts = &pc.transfer_syntax;
        ts == transfer_syntax
            || TransferSyntaxRegistry
                .get(&pc.transfer_syntax)
                .filter(|ts| file_ts_desc.is_codec_free() && ts.is_codec_free())
                .map(|_| true)
                .unwrap_or(false)
    }) {
        pc.clone()
    } else {
        eprintln!("Could not choose a transfer syntax");
        let _ = scu.abort();
        std::process::exit(-2);
    };

    let ts = TransferSyntaxRegistry
        .get(&pc_selected.transfer_syntax)
        .ok_or_else(|| "Poorly negotiated transfer syntax")?;

    if verbose {
        println!("Transfer Syntax: {}", ts.name());
    }

    let cmd = store_req_command(
        &storage_sop_class_uid,
        &storage_sop_instance_uid,
        message_id,
    );

    let mut cmd_data = Vec::with_capacity(128);
    cmd.write_dataset_with_ts(
        &mut cmd_data,
        &dicom::transfer_syntax::entries::IMPLICIT_VR_LITTLE_ENDIAN.erased(),
    )?;

    let mut object_data = Vec::with_capacity(2048);
    dicom_file.write_dataset_with_ts(&mut object_data, &ts)?;

    let nbytes = cmd_data.len() + object_data.len();

    if verbose {
        println!("Sending payload (~ {} kB)...", nbytes / 1_000);
    }

    if nbytes < scu.acceptor_max_pdu_length() as usize - 100 {
        let pdu = Pdu::PData {
            data: vec![
                PDataValue {
                    presentation_context_id: pc_selected.id,
                    value_type: PDataValueType::Command,
                    is_last: true,
                    data: cmd_data,
                },
                PDataValue {
                    presentation_context_id: pc_selected.id,
                    value_type: PDataValueType::Data,
                    is_last: true,
                    data: object_data,
                },
            ],
        };

        scu.send(&pdu)?;
    } else {
        let pdu = Pdu::PData {
            data: vec![PDataValue {
                presentation_context_id: pc_selected.id,
                value_type: PDataValueType::Command,
                is_last: true,
                data: cmd_data,
            }],
        };

        scu.send(&pdu)?;

        {
            let mut pdata = scu.send_pdata(pc_selected.id);
            pdata.write_all(&object_data)?;
        }
    }

    if verbose {
        println!("Awaiting response...");
    }

    let rsp_pdu = scu.receive()?;

    match rsp_pdu {
        Pdu::PData { data } => {
            let data_value = &data[0];

            let cmd_obj = InMemDicomObject::read_dataset_with_ts(
                &data_value.data[..],
                &dicom::transfer_syntax::entries::IMPLICIT_VR_LITTLE_ENDIAN.erased(),
            )?;
            if verbose {
                println!("Response: {:?}", cmd_obj);
            }
            let status = cmd_obj.element(tags::STATUS)?.to_int::<u16>()?;
            let storage_sop_instance_uid =
                storage_sop_instance_uid.trim_end_matches(|c: char| c.is_whitespace() || c == '\0');
            if status == 0 {
                println!("Sucessfully stored instance `{}`", storage_sop_instance_uid);
            } else {
                println!(
                    "Failed to store instance `{}` (status code {})",
                    storage_sop_instance_uid, status
                );
            }

            scu.release()?;
        }

        pdu @ Pdu::Unknown { .. }
        | pdu @ Pdu::AssociationRQ { .. }
        | pdu @ Pdu::AssociationAC { .. }
        | pdu @ Pdu::AssociationRJ { .. }
        | pdu @ Pdu::ReleaseRQ
        | pdu @ Pdu::ReleaseRP
        | pdu @ Pdu::AbortRQ { .. } => {
            eprintln!("Unexpected SCP response: {:?}", pdu);
            let _ = scu.abort();
            std::process::exit(-2);
        }
    }

    Ok(())
}

fn store_req_command(
    storage_sop_class_uid: &str,
    storage_sop_instance_uid: &str,
    message_id: u16,
) -> InMemDicomObject<StandardDataDictionary> {
    let mut obj = InMemDicomObject::create_empty();

    // group length
    obj.put(DataElement::new(
        tags::COMMAND_GROUP_LENGTH,
        VR::UL,
        PrimitiveValue::from(
            12 + 8
                + even_len(storage_sop_class_uid.len())
                + 10
                + 10
                + 10
                + 10
                + 8
                + even_len(storage_sop_instance_uid.len()),
        ),
    ));

    // SOP Class UID
    obj.put(DataElement::new(
        tags::AFFECTED_SOP_CLASS_UID,
        VR::UI,
        dicom_value!(Str, storage_sop_class_uid),
    ));

    // command field
    obj.put(DataElement::new(
        tags::COMMAND_FIELD,
        VR::US,
        dicom_value!(U16, [0x0001]),
    ));

    // message ID
    obj.put(DataElement::new(
        tags::MESSAGE_ID,
        VR::US,
        dicom_value!(U16, [message_id]),
    ));

    //priority
    obj.put(DataElement::new(
        tags::PRIORITY,
        VR::US,
        dicom_value!(U16, [0x0000]),
    ));

    // data set type
    obj.put(DataElement::new(
        tags::COMMAND_DATA_SET_TYPE,
        VR::US,
        dicom_value!(U16, [0x0000]),
    ));

    // affected SOP Instance UID
    obj.put(DataElement::new(
        tags::AFFECTED_SOP_INSTANCE_UID,
        VR::UI,
        dicom_value!(Str, storage_sop_instance_uid),
    ));

    obj
}

fn even_len(l: usize) -> u32 {
    ((l + 1) & !1) as u32
}

#[cfg(test)]
mod tests {
    use super::even_len;
    #[test]
    fn test_even_len() {
        assert_eq!(even_len(0), 0);
        assert_eq!(even_len(1), 2);
        assert_eq!(even_len(2), 2);
        assert_eq!(even_len(3), 4);
        assert_eq!(even_len(4), 4);
        assert_eq!(even_len(5), 6);
    }
}
<|endoftext|>


<commit_before>extern crate nom;

use std::str;
use nom::*;

fn is_echar(c: char) -> bool {
    c == '\\' || c == 't' || c == 'b' || c == 'n' || c == 'r' || c == 'f' || c == '"' || c == '\''
}
/* [26] UCHAR */
named!(uchar<&str, &str>, alt!(
    verify!(
        take_s!(6),
        |val:&str| {
            let len = val.char_indices().count();
            val.char_indices().all(|(idx, c)| {
                if idx == 0 {
                    c == '\\'
                } else if idx == 1 {
                    c == 'u'
                } else {
                    is_hex_digit(c as u8)
                }
            })
        }
    ) |
    verify!(
        take_s!(10),
        |val:&str| {
            let len = val.char_indices().count();
            val.char_indices().all(|(idx, c)| {
                if idx == 0 {
                    c == '\\'
                } else if idx == 1 {
                    c == 'U'
                } else {
                    is_hex_digit(c as u8)
                }
            })
        }
    )
));

/* [159s] ECHAR */
named!(echar<&str, &str>, verify!(
    take_s!(2),
    |val:&str| {
        let len = val.char_indices().count();
        val.char_indices().all(|(idx, c)| {
            if idx == 0 {
                c == '\\'
            } else {
                is_echar(c)
            }
        })
    }
));

named!(pub string_literal_long_single_quote_body<&str, Vec<&str>>, many0!(
    alt!(tag!("'") | echar | uchar)
));
/* [24] STRING_LITERAL_LONG_SINGLE_QUOTE */
named!(pub string_literal_long_single_quote<&str, &str>, delimited!(
    tag!("'''"),
    verify!(
        take_until_s!("'''"),
        |val: &str| {
            match string_literal_long_single_quote_body(val) {
                IResult::Error(_) => false,
                _                 => true,
            }
        }
    ),
    tag!("'''")
));

named!(pub string_literal_long_quote_body<&str, Vec<&str>>, many0!(
    alt!(tag!("\"") | echar | uchar)
));
/* [25] STRING_LITERAL_LONG_QUOTE */
named!(pub string_literal_long_quote<&str, &str>, delimited!(
    tag!("\"\"\""),
    verify!(
        take_until_s!("\"\"\""),
        |val: &str| {
            match string_literal_long_quote_body(val) {
                IResult::Error(_) => false,
                _                 => true,
            }
        }
    ),
    tag!("\"\"\"")
));

#[cfg(test)]
mod test {
    use super::*;
    use nom::IResult;

    #[test]
    fn uchar_test() {
        assert_eq!(uchar("\\u02FFrest")    , IResult::Done("rest", "\\u02FF")    );
        assert_eq!(uchar("\\U02FFAABBrest"), IResult::Done("rest", "\\U02FFAABB"));
    }

    #[test]
    fn echar_test() {
        assert_eq!(echar("\\trest") , IResult::Done("rest", "\\t") );
        assert_eq!(echar("\\brest") , IResult::Done("rest", "\\b") );
        assert_eq!(echar("\\nrest") , IResult::Done("rest", "\\n") );
        assert_eq!(echar("\\rrest") , IResult::Done("rest", "\\r") );
        assert_eq!(echar("\\frest") , IResult::Done("rest", "\\f") );
        assert_eq!(echar("\\\"rest"), IResult::Done("rest", "\\\""));
        assert_eq!(echar("\\'rest") , IResult::Done("rest", "\\'") );
        assert_eq!(echar("\\\\rest"), IResult::Done("rest", "\\\\"));
    }

    #[test]
    fn string_literal_long_single_quote_test() {
        let input    = "'''\\t\\u02FF'''rest";
        let expected = "\\t\\u02FF";
        assert_eq!(string_literal_long_single_quote(input), IResult::Done("rest", expected));
    }

    #[test]
    fn string_literal_long_quote_test() {
        let input    = "\"\"\"\\t\\u02FF\"\"\"rest";
        let expected = "\\t\\u02FF";
        assert_eq!(string_literal_long_quote(input), IResult::Done("rest", expected));
    }
}
<commit_msg>Implement string_literal_single_quote<commit_after>extern crate nom;

use std::str;
use nom::*;

fn is_echar(c: char) -> bool {
    c == '\\' || c == 't' || c == 'b' || c == 'n' || c == 'r' || c == 'f' || c == '"' || c == '\''
}
/* [26] UCHAR */
named!(uchar<&str, &str>, alt!(
    verify!(
        take_s!(6),
        |val:&str| {
            let len = val.char_indices().count();
            val.char_indices().all(|(idx, c)| {
                if idx == 0 {
                    c == '\\'
                } else if idx == 1 {
                    c == 'u'
                } else {
                    is_hex_digit(c as u8)
                }
            })
        }
    ) |
    verify!(
        take_s!(10),
        |val:&str| {
            let len = val.char_indices().count();
            val.char_indices().all(|(idx, c)| {
                if idx == 0 {
                    c == '\\'
                } else if idx == 1 {
                    c == 'U'
                } else {
                    is_hex_digit(c as u8)
                }
            })
        }
    )
));

/* [159s] ECHAR */
named!(echar<&str, &str>, verify!(
    take_s!(2),
    |val:&str| {
        let len = val.char_indices().count();
        val.char_indices().all(|(idx, c)| {
            if idx == 0 {
                c == '\\'
            } else {
                is_echar(c)
            }
        })
    }
));

named!(string_literal_single_quote_body_char<&str, &str>, verify!(
    take_s!(1),
    |val:&str| {
        if let Some(c) = val.chars().next() {
            let u = c as u32;
            match u {
                0x22 | 0x5C | 0xA | 0xD
                  => false,
                _ => true,
            }
        } else {
            false
        }
    }
));

named!(pub string_literal_single_quote_body<&str, Vec<&str>>, many0!(
    alt!(string_literal_single_quote_body_char | echar | uchar)
));
/* [25] STRING_LITERAL_LONG_QUOTE */
named!(pub string_literal_single_quote<&str, &str>, delimited!(
    tag!("'"),
    verify!(
        take_until_s!("'"),
        |val: &str| {
            match string_literal_single_quote_body(val) {
                IResult::Error(_) => false,
                _                 => true,
            }
        }
    ),
    tag!("'")
));

named!(pub string_literal_long_single_quote_body<&str, Vec<&str>>, many0!(
    alt!(tag!("'") | echar | uchar)
));
/* [24] STRING_LITERAL_LONG_SINGLE_QUOTE */
named!(pub string_literal_long_single_quote<&str, &str>, delimited!(
    tag!("'''"),
    verify!(
        take_until_s!("'''"),
        |val: &str| {
            match string_literal_long_single_quote_body(val) {
                IResult::Error(_) => false,
                _                 => true,
            }
        }
    ),
    tag!("'''")
));

named!(pub string_literal_long_quote_body<&str, Vec<&str>>, many0!(
    alt!(tag!("\"") | echar | uchar)
));
/* [25] STRING_LITERAL_LONG_QUOTE */
named!(pub string_literal_long_quote<&str, &str>, delimited!(
    tag!("\"\"\""),
    verify!(
        take_until_s!("\"\"\""),
        |val: &str| {
            match string_literal_long_quote_body(val) {
                IResult::Error(_) => false,
                _                 => true,
            }
        }
    ),
    tag!("\"\"\"")
));

#[cfg(test)]
mod test {
    use super::*;
    use nom::IResult;

    #[test]
    fn uchar_test() {
        assert_eq!(uchar("\\u02FFrest")    , IResult::Done("rest", "\\u02FF")    );
        assert_eq!(uchar("\\U02FFAABBrest"), IResult::Done("rest", "\\U02FFAABB"));
    }

    #[test]
    fn echar_test() {
        assert_eq!(echar("\\trest") , IResult::Done("rest", "\\t") );
        assert_eq!(echar("\\brest") , IResult::Done("rest", "\\b") );
        assert_eq!(echar("\\nrest") , IResult::Done("rest", "\\n") );
        assert_eq!(echar("\\rrest") , IResult::Done("rest", "\\r") );
        assert_eq!(echar("\\frest") , IResult::Done("rest", "\\f") );
        assert_eq!(echar("\\\"rest"), IResult::Done("rest", "\\\""));
        assert_eq!(echar("\\'rest") , IResult::Done("rest", "\\'") );
        assert_eq!(echar("\\\\rest"), IResult::Done("rest", "\\\\"));
    }

    #[test]
    fn string_literal_single_quote_test() {
        let input    = "'\\t\\u02FFhoge'rest";
        let expected = "\\t\\u02FFhoge";
        assert_eq!(string_literal_single_quote(input), IResult::Done("rest", expected));
    }

    #[test]
    fn string_literal_long_single_quote_test() {
        let input    = "'''\\t\\u02FF'''rest";
        let expected = "\\t\\u02FF";
        assert_eq!(string_literal_long_single_quote(input), IResult::Done("rest", expected));
    }

    #[test]
    fn string_literal_long_quote_test() {
        let input    = "\"\"\"\\t\\u02FF\"\"\"rest";
        let expected = "\\t\\u02FF";
        assert_eq!(string_literal_long_quote(input), IResult::Done("rest", expected));
    }
}
<|endoftext|>


<commit_before>use super::bls::{Keypair, PublicKey};
use super::{Address, Hash256};

#[derive(Debug, PartialEq, Clone, Copy)]
pub enum ValidatorStatus {
    PendingActivation = 0,
    Active = 1,
    PendingExit = 2,
    PendingWithdraw = 3,
    Withdrawn = 5,
    Penalized = 127,
}

#[derive(Debug, Clone, PartialEq)]
pub struct ValidatorRecord {
    pub pubkey: PublicKey,
    pub withdrawal_credentials: Hash32,
    pub randao_commitment: Hash32,
    pub randao_skips: u64,
    pub balance: u64,
    pub status: u64,
    pub latest_status_change_slot: u64,
    pub exit_count: u64
}

impl ValidatorRecord {
    /// Generates a new instance where the keypair is generated using
    /// `rand::thread_rng` entropy and all other fields are set to zero.
    ///
    /// Returns the new instance and new keypair.
    pub fn zero_with_thread_rand_keypair() -> (Self, Keypair) {
        let keypair = Keypair::random();
        let s = Self {
            pubkey: keypair.pk.clone(),
            withdrawal_shard: 0,
            withdrawal_address: Address::zero(),
            randao_commitment: Hash256::zero(),
            randao_last_change: 0,
            balance: 0,
            status: 0,
            exit_slot: 0,
        };
        (s, keypair)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_validator_record_zero_rand_keypair() {
        let (v, _kp) = ValidatorRecord::zero_with_thread_rand_keypair();
        assert_eq!(v.withdrawal_shard, 0);
        assert!(v.withdrawal_address.is_zero());
        assert!(v.randao_commitment.is_zero());
        assert_eq!(v.randao_last_change, 0);
        assert_eq!(v.balance, 0);
        assert_eq!(v.status, 0);
        assert_eq!(v.exit_slot, 0);
    }
}
<commit_msg>ValidatorRecord initialization updated to reflect struct<commit_after>use super::bls::{Keypair, PublicKey};
use super::{Hash256};

#[derive(Debug, PartialEq, Clone, Copy)]
pub enum ValidatorStatus {
    PendingActivation = 0,
    Active = 1,
    PendingExit = 2,
    PendingWithdraw = 3,
    Withdrawn = 5,
    Penalized = 127,
}

#[derive(Debug, Clone, PartialEq)]
pub struct ValidatorRecord {
    pub pubkey: PublicKey,
    pub withdrawal_credentials: Hash256,
    pub randao_commitment: Hash256,
    pub randao_skips: u64,
    pub balance: u64,
    pub status: u64,
    pub latest_status_change_slot: u64,
    pub exit_count: u64
}

impl ValidatorRecord {
    /// Generates a new instance where the keypair is generated using
    /// `rand::thread_rng` entropy and all other fields are set to zero.
    ///
    /// Returns the new instance and new keypair.
    pub fn zero_with_thread_rand_keypair() -> (Self, Keypair) {
        let keypair = Keypair::random();
        let s = Self {
            pubkey: keypair.pk.clone(),
            withdrawal_credentials: Hash256::zero(),
            randao_commitment: Hash256::zero(),
            randao_skips: 0,
            balance: 0,
            status: 0,
            latest_status_change_slot: 0,
            exit_count: 0
        };
        (s, keypair)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_validator_record_zero_rand_keypair() {
        let (v, _kp) = ValidatorRecord::zero_with_thread_rand_keypair();
        assert_eq!(v.withdrawal_shard, 0);
        assert!(v.withdrawal_address.is_zero());
        assert!(v.randao_commitment.is_zero());
        assert_eq!(v.randao_last_change, 0);
        assert_eq!(v.balance, 0);
        assert_eq!(v.status, 0);
        assert_eq!(v.exit_slot, 0);
    }
}
<|endoftext|>


<commit_before>use std::borrow::Cow;
use std::ffi::{OsStr, OsString};
use std::str;

use os_str_bytes::{OsStrBytes, OsStringBytes};

#[derive(Debug)]
pub(crate) struct ArgStr<'a>(Cow<'a, [u8]>);

impl<'a> ArgStr<'a> {
    pub(crate) fn new(s: &'a OsStr) -> Self {
        Self(s.to_bytes())
    }

    pub(crate) fn starts_with(&self, s: &str) -> bool {
        self.0.starts_with(s.as_bytes())
    }

    fn to_borrowed(&'a self) -> Self {
        Self(Cow::Borrowed(&self.0))
    }

    pub(crate) fn contains_byte(&self, byte: u8) -> bool {
        assert!(byte.is_ascii());

        self.0.contains(&byte)
    }

    pub(crate) fn contains_char(&self, ch: char) -> bool {
        let mut bytes = [0; 4];
        let bytes = ch.encode_utf8(&mut bytes).as_bytes();
        for i in 0..self.0.len().saturating_sub(bytes.len() - 1) {
            if self.0[i..].starts_with(bytes) {
                return true;
            }
        }
        false
    }

    pub(crate) fn split_at_byte(&self, byte: u8) -> (ArgStr<'_>, ArgStr<'_>) {
        assert!(byte.is_ascii());

        for (i, b) in self.0.iter().enumerate() {
            if b == &byte {
                return self.split_at_unchecked(i);
            }
        }
        (self.to_borrowed(), Self(Cow::Borrowed(&[])))
    }

    pub(crate) fn trim_start_matches(&self, byte: u8) -> ArgStr<'_> {
        assert!(byte.is_ascii());

        let mut found = false;
        for (i, b) in self.0.iter().enumerate() {
            if b != &byte {
                return Self(Cow::Borrowed(&self.0[i..]));
            } else {
                found = true;
            }
        }
        if found {
            return Self(Cow::Borrowed(&[]));
        }
        self.to_borrowed()
    }

    // Like `trim_start_matches`, but trims no more than `n` matches
    #[inline]
    pub(crate) fn trim_start_n_matches(&self, n: usize, ch: u8) -> ArgStr<'_> {
        assert!(ch.is_ascii());

        let i = self
            .0
            .iter()
            .take(n)
            .take_while(|c| **c == ch)
            .enumerate()
            .last()
            .map(|(i, _)| i + 1)
            .unwrap_or(0);

        self.split_at_unchecked(i).1
    }

    pub(crate) fn split_at_unchecked(&self, i: usize) -> (ArgStr<'_>, ArgStr<'_>) {
        (
            Self(Cow::Borrowed(&self.0[..i])),
            Self(Cow::Borrowed(&self.0[i..])),
        )
    }

    pub(crate) fn split(&self, ch: char) -> ArgSplit<'_> {
        let mut sep = [0; 4];
        ArgSplit {
            sep_len: ch.encode_utf8(&mut sep).as_bytes().len(),
            sep,
            val: &self.0,
            pos: 0,
        }
    }

    pub(crate) fn as_raw_bytes(&self) -> &[u8] {
        &self.0
    }

    pub(crate) fn len(&self) -> usize {
        self.0.len()
    }

    pub(crate) fn is_empty(&self) -> bool {
        self.0.is_empty()
    }

    pub(crate) fn to_str(&self) -> Option<&str> {
        str::from_utf8(&self.0).ok()
    }

    pub(crate) fn to_string_lossy(&self) -> Cow<'_, str> {
        String::from_utf8_lossy(&self.0)
    }

    pub(crate) fn to_os_string(&self) -> OsString {
        OsString::from_bytes(&self.0).unwrap()
    }
}

impl<'a> PartialEq<str> for ArgStr<'a> {
    fn eq(&self, other: &str) -> bool {
        self.0 == other.as_bytes()
    }
}

impl<'a> PartialEq<&str> for ArgStr<'a> {
    fn eq(&self, other: &&str) -> bool {
        self.eq(*other)
    }
}

impl<'a> PartialEq<ArgStr<'a>> for str {
    fn eq(&self, other: &ArgStr<'a>) -> bool {
        other.eq(self)
    }
}

impl<'a> PartialEq<ArgStr<'a>> for &str {
    fn eq(&self, other: &ArgStr<'a>) -> bool {
        other.eq(self)
    }
}

#[derive(Clone, Debug)]
pub(crate) struct ArgSplit<'a> {
    sep: [u8; 4],
    sep_len: usize,
    val: &'a [u8],
    pos: usize,
}

impl<'a> Iterator for ArgSplit<'a> {
    type Item = ArgStr<'a>;

    fn next(&mut self) -> Option<ArgStr<'a>> {
        debug!("ArgSplit::next: self={:?}", self);

        if self.pos == self.val.len() {
            return None;
        }
        let start = self.pos;
        while self.pos < self.val.len() {
            if self.val[self.pos..].starts_with(&self.sep[..self.sep_len]) {
                let arg = ArgStr(Cow::Borrowed(&self.val[start..self.pos]));
                self.pos += self.sep_len;
                return Some(arg);
            }
            self.pos += 1;
        }
        Some(ArgStr(Cow::Borrowed(&self.val[start..])))
    }
}
<commit_msg>Fix beta errors<commit_after>use std::borrow::Cow;
use std::ffi::{OsStr, OsString};
use std::str;

use os_str_bytes::{OsStrBytes, OsStringBytes};

#[derive(Debug)]
pub(crate) struct ArgStr<'a>(Cow<'a, [u8]>);

impl<'a> ArgStr<'a> {
    pub(crate) fn new(s: &'a OsStr) -> Self {
        Self(s.to_bytes())
    }

    pub(crate) fn starts_with(&self, s: &str) -> bool {
        self.0.starts_with(s.as_bytes())
    }

    fn to_borrowed(&'a self) -> Self {
        Self(Cow::Borrowed(&self.0))
    }

    pub(crate) fn contains_byte(&self, byte: u8) -> bool {
        assert!(byte.is_ascii());

        self.0.contains(&byte)
    }

    pub(crate) fn contains_char(&self, ch: char) -> bool {
        let mut bytes = [0; 4];
        let bytes = ch.encode_utf8(&mut bytes).as_bytes();
        for i in 0..self.0.len().saturating_sub(bytes.len() - 1) {
            if self.0[i..].starts_with(bytes) {
                return true;
            }
        }
        false
    }

    pub(crate) fn split_at_byte(&self, byte: u8) -> (ArgStr<'_>, ArgStr<'_>) {
        assert!(byte.is_ascii());

        for (i, b) in self.0.iter().enumerate() {
            if b == &byte {
                return self.split_at_unchecked(i);
            }
        }
        (self.to_borrowed(), Self(Cow::Borrowed(&[])))
    }

    pub(crate) fn trim_start_matches(&'a self, byte: u8) -> ArgStr<'_> {
        assert!(byte.is_ascii());

        let mut found = false;
        for (i, b) in self.0.iter().enumerate() {
            if b != &byte {
                return Self(Cow::Borrowed(&self.0[i..]));
            } else {
                found = true;
            }
        }
        if found {
            return Self(Cow::Borrowed(&[]));
        }
        self.to_borrowed()
    }

    // Like `trim_start_matches`, but trims no more than `n` matches
    #[inline]
    pub(crate) fn trim_start_n_matches(&self, n: usize, ch: u8) -> ArgStr<'_> {
        assert!(ch.is_ascii());

        let i = self
            .0
            .iter()
            .take(n)
            .take_while(|c| **c == ch)
            .enumerate()
            .last()
            .map(|(i, _)| i + 1)
            .unwrap_or(0);

        self.split_at_unchecked(i).1
    }

    pub(crate) fn split_at_unchecked(&'a self, i: usize) -> (ArgStr<'_>, ArgStr<'_>) {
        (
            Self(Cow::Borrowed(&self.0[..i])),
            Self(Cow::Borrowed(&self.0[i..])),
        )
    }

    pub(crate) fn split(&self, ch: char) -> ArgSplit<'_> {
        let mut sep = [0; 4];
        ArgSplit {
            sep_len: ch.encode_utf8(&mut sep).as_bytes().len(),
            sep,
            val: &self.0,
            pos: 0,
        }
    }

    pub(crate) fn as_raw_bytes(&self) -> &[u8] {
        &self.0
    }

    pub(crate) fn len(&self) -> usize {
        self.0.len()
    }

    pub(crate) fn is_empty(&self) -> bool {
        self.0.is_empty()
    }

    pub(crate) fn to_str(&self) -> Option<&str> {
        str::from_utf8(&self.0).ok()
    }

    pub(crate) fn to_string_lossy(&self) -> Cow<'_, str> {
        String::from_utf8_lossy(&self.0)
    }

    pub(crate) fn to_os_string(&self) -> OsString {
        OsString::from_bytes(&self.0).unwrap()
    }
}

impl<'a> PartialEq<str> for ArgStr<'a> {
    fn eq(&self, other: &str) -> bool {
        self.0 == other.as_bytes()
    }
}

impl<'a> PartialEq<&str> for ArgStr<'a> {
    fn eq(&self, other: &&str) -> bool {
        self.eq(*other)
    }
}

impl<'a> PartialEq<ArgStr<'a>> for str {
    fn eq(&self, other: &ArgStr<'a>) -> bool {
        other.eq(self)
    }
}

impl<'a> PartialEq<ArgStr<'a>> for &str {
    fn eq(&self, other: &ArgStr<'a>) -> bool {
        other.eq(self)
    }
}

#[derive(Clone, Debug)]
pub(crate) struct ArgSplit<'a> {
    sep: [u8; 4],
    sep_len: usize,
    val: &'a [u8],
    pos: usize,
}

impl<'a> Iterator for ArgSplit<'a> {
    type Item = ArgStr<'a>;

    fn next(&mut self) -> Option<ArgStr<'a>> {
        debug!("ArgSplit::next: self={:?}", self);

        if self.pos == self.val.len() {
            return None;
        }
        let start = self.pos;
        while self.pos < self.val.len() {
            if self.val[self.pos..].starts_with(&self.sep[..self.sep_len]) {
                let arg = ArgStr(Cow::Borrowed(&self.val[start..self.pos]));
                self.pos += self.sep_len;
                return Some(arg);
            }
            self.pos += 1;
        }
        Some(ArgStr(Cow::Borrowed(&self.val[start..])))
    }
}
<|endoftext|>


<commit_before>extern crate rustbox;
extern crate regex;

use std::cmp::min;
use std::error::Error;
use self::rustbox::{Color, RustBox, Key};
use self::rustbox::Event::KeyEvent;
use self::regex::Regex;

// Error

// Screen Status
pub enum Status {
    Selected(String),
    Escaped,
    Continue,
    Quit,
    Error,
}

trait Print {
    fn print_line(&self, y: usize, item: &str, fg: Color, bg: Color);
    fn print_lines(&self, item: &str, fg: Color, bg: Color);
}

impl Print for RustBox {

    fn print_line(&self, y: usize, item: &str, fg: Color, bg: Color) {
        for x in 0..(self.width()) {
            let ch = item.chars().nth(x).unwrap_or(' ');
            self.print_char(x, y, rustbox::RB_NORMAL, fg, bg, ch);
        }
    }

    fn print_lines(&self, item: &str, fg: Color, bg: Color) {

        let mut cursor = 0;

        for line in item.split("\n") {

            // fold string if line lingth longer than window widht
            if line.chars().count() > self.width() {

                let (first, last) = line.split_at(self.width());
                self.print_line(cursor,    first, fg, bg);
                self.print_line(cursor + 1, last, fg, bg);
                cursor += 2;
                continue;

            }

            self.print_line(cursor, line, fg, bg);
            cursor += 1;
        }
    }
}

pub struct Screen {
    body:     String,
    lines:    Vec<String>,
    prompt:   String,
    // offset: usize,
    y_offset: usize,
    filtered: Vec<String>,
    query:    String,
    cursor:   usize,
    rustbox:  RustBox
}

impl Screen {

    pub fn new() -> Screen {

        let rustbox = match RustBox::init(Default::default()) {
            Result::Ok(v) => v,
            Result::Err(e) => panic!("{}", e),
        };

        Screen {
            body: String::new(),
            lines: vec!(),
            filtered: vec!(),
            prompt: "🍓  ".to_owned(),
            y_offset: 1,
            query: String::new(),
            cursor: 0,
            // offset: 0,
            rustbox: rustbox
        }
    }

    pub fn update_screen(&mut self, body: &str, lines: &Vec<String>) {
        self.y_offset = body.lines().count() + 1;
        self.body = body.to_string();
        self.cursor = 0;
        self.query = String::new();
        self.lines = lines.to_owned();
        self.filtered = self.lines.clone();
    }

    pub fn render(&self, item: &str) -> Status {

        self.rustbox.clear();

        self.rustbox.print_lines(item, Color::Blue, Color::Black);

        self.rustbox.present();

        match self.rustbox.poll_event(false) {

            Ok(KeyEvent(Key::Char('q'))) => Status::Quit,
            _ => Status::Escaped
        }
    }

    pub fn select_line(&mut self) -> Status {

        loop {

            self.render_items();

            match self.rustbox.poll_event(false) {
                Err(err) => panic!("{:?}", err),
                Ok(event) => match self.handle_event(event) {
                    Ok(Status::Selected(s)) => return Status::Selected(s),
                    Ok(Status::Quit) => return Status::Quit,
                    _ => (),
                }
            }
        }
    }

    fn handle_event(&mut self, event: rustbox::Event) -> Result<Status, Box<Error>> {

        match event {
            KeyEvent(Key::Enter)     => Ok(Status::Selected(self.filtered[self.cursor].to_owned())),
            KeyEvent(Key::Esc)       => Ok(Status::Escaped),
            KeyEvent(Key::Char('q')) => Ok(Status::Quit),
            KeyEvent(Key::Char(c))   => self.append_query(c).and(Ok(Status::Continue)),
            KeyEvent(Key::Backspace) => self.remove_query().and(Ok(Status::Continue)),
            KeyEvent(Key::Up) |
            KeyEvent(Key::Ctrl('p')) => {
                self.cursor_up();
                Ok(Status::Continue)
            },
            KeyEvent(Key::Down) |
            KeyEvent(Key::Ctrl('n')) => {
                self.cursor_down();
                Ok(Status::Continue)
            },
            _ => Ok(Status::Continue),
        }
    }

    fn append_query(&mut self, c: char) -> Result<(), Box<Error>> {

        self.query.push(c);
        self.apply_filter()
    }

    fn remove_query(&mut self) -> Result<(), Box<Error>> {

        if self.query.is_empty() {
            return Ok(());
        }

        let idx = self.query.len() - 1;
        self.query.remove(idx);
        self.apply_filter()
    }

    fn apply_filter(&mut self) -> Result<(), Box<Error>> {
        self.filtered = if self.query.len() == 0 {
            self.lines.clone()
        } else {
            let re = try!(Regex::new(self.query.as_str()));
            self.lines.iter().filter(|&input| re.is_match(input)).cloned().collect()
        };

        self.cursor = 0;

        Ok(())
    }

    fn cursor_up(&mut self) {

        if self.cursor != 0 { self.cursor -= 1; }
    }

    fn cursor_down(&mut self) {

        let height = self.rustbox.height();

        self.cursor = min(
            (self.cursor + 1),
            min(
                (self.filtered.len() - 1),
                (height - 1)
            )
        );
    }

    fn render_items(&self) {

        self.rustbox.clear();

        for(y, item) in self.filtered.iter().enumerate() {
            if y == self.cursor {
                self.rustbox.print_line(y + self.y_offset, &format!("[{0}]: {1}", y, &item), Color::Black, Color::Green);
            } else {
                self.rustbox.print_line(y + self.y_offset, &format!("[{0}]: {1}", y, &item), Color::Blue, Color::Black);
            }
        }

        // print query line and move the cursor to end.
        let query_str = format!("{}{}", self.prompt, self.query);
        self.rustbox.print_lines(&self.body, Color::Green, Color::Black);
        self.rustbox.print_line(self.y_offset - 1, &query_str, Color::Blue, Color::Black);
        self.rustbox.present();
    }

}

<commit_msg>Fix bottom line<commit_after>extern crate rustbox;
extern crate regex;

use std::cmp::min;
use std::error::Error;
use self::rustbox::{Color, RustBox, Key};
use self::rustbox::Event::KeyEvent;
use self::regex::Regex;

// Error

// Screen Status
pub enum Status {
    Selected(String),
    Escaped,
    Continue,
    Quit,
    Error,
}

trait Print {
    fn print_line(&self, y: usize, item: &str, fg: Color, bg: Color);
    fn print_lines(&self, item: &str, fg: Color, bg: Color);
}

impl Print for RustBox {

    fn print_line(&self, y: usize, item: &str, fg: Color, bg: Color) {
        for x in 0..(self.width()) {
            let ch = item.chars().nth(x).unwrap_or(' ');
            self.print_char(x, y, rustbox::RB_NORMAL, fg, bg, ch);
        }
    }

    fn print_lines(&self, item: &str, fg: Color, bg: Color) {

        let mut cursor = 0;

        for line in item.split("\n") {

            // fold string if line lingth longer than window widht
            if line.chars().count() > self.width() {

                let (first, last) = line.split_at(self.width());
                self.print_line(cursor,    first, fg, bg);
                self.print_line(cursor + 1, last, fg, bg);
                cursor += 2;
                continue;

            }

            self.print_line(cursor, line, fg, bg);
            cursor += 1;
        }
    }
}

pub struct Screen {
    body:     String,
    lines:    Vec<String>,
    prompt:   String,
    // offset: usize,
    y_offset: usize,
    filtered: Vec<String>,
    query:    String,
    cursor:   usize,
    rustbox:  RustBox
}

impl Screen {

    pub fn new() -> Screen {

        let rustbox = match RustBox::init(Default::default()) {
            Result::Ok(v) => v,
            Result::Err(e) => panic!("{}", e),
        };

        Screen {
            body: String::new(),
            lines: vec!(),
            filtered: vec!(),
            prompt: "🍓  ".to_owned(),
            y_offset: 1,
            query: String::new(),
            cursor: 0,
            // offset: 0,
            rustbox: rustbox
        }
    }

    pub fn update_screen(&mut self, body: &str, lines: &Vec<String>) {
        self.y_offset = body.lines().count() + 1;
        self.body = body.to_string();
        self.cursor = 0;
        self.query = String::new();
        self.lines = lines.to_owned();
        self.filtered = self.lines.clone();
    }

    pub fn render(&self, item: &str) -> Status {

        self.rustbox.clear();

        self.rustbox.print_lines(item, Color::Blue, Color::Black);

        self.rustbox.present();

        match self.rustbox.poll_event(false) {

            Ok(KeyEvent(Key::Char('q'))) => Status::Quit,
            _ => Status::Escaped
        }
    }

    pub fn select_line(&mut self) -> Status {

        loop {

            self.render_items();

            match self.rustbox.poll_event(false) {
                Err(err) => panic!("{:?}", err),
                Ok(event) => match self.handle_event(event) {
                    Ok(Status::Selected(s)) => return Status::Selected(s),
                    Ok(Status::Quit) => return Status::Quit,
                    _ => (),
                }
            }
        }
    }

    fn handle_event(&mut self, event: rustbox::Event) -> Result<Status, Box<Error>> {

        match event {
            KeyEvent(Key::Enter)     => Ok(Status::Selected(self.filtered[self.cursor].to_owned())),
            KeyEvent(Key::Esc)       => Ok(Status::Escaped),
            KeyEvent(Key::Char('q')) => Ok(Status::Quit),
            KeyEvent(Key::Char(c))   => self.append_query(c).and(Ok(Status::Continue)),
            KeyEvent(Key::Backspace) => self.remove_query().and(Ok(Status::Continue)),
            KeyEvent(Key::Up) |
            KeyEvent(Key::Ctrl('p')) => {
                self.cursor_up();
                Ok(Status::Continue)
            },
            KeyEvent(Key::Down) |
            KeyEvent(Key::Ctrl('n')) => {
                self.cursor_down();
                Ok(Status::Continue)
            },
            _ => Ok(Status::Continue),
        }
    }

    fn append_query(&mut self, c: char) -> Result<(), Box<Error>> {

        self.query.push(c);
        self.apply_filter()
    }

    fn remove_query(&mut self) -> Result<(), Box<Error>> {

        if self.query.is_empty() {
            return Ok(());
        }

        let idx = self.query.len() - 1;
        self.query.remove(idx);
        self.apply_filter()
    }

    fn apply_filter(&mut self) -> Result<(), Box<Error>> {
        self.filtered = if self.query.len() == 0 {
            self.lines.clone()
        } else {
            let re = try!(Regex::new(self.query.as_str()));
            self.lines.iter().filter(|&input| re.is_match(input)).cloned().collect()
        };

        self.cursor = 0;

        Ok(())
    }

    fn cursor_up(&mut self) {

        if self.cursor != 0 { self.cursor -= 1; }
    }

    fn cursor_down(&mut self) {

        let height = self.rustbox.height();

        self.cursor = min(
            (self.cursor + 1),
            min(
                (self.filtered.len() - 1),
                (height - 1)
            )
        );
    }

    fn render_items(&self) {

        self.rustbox.clear();

        for(y, item) in self.filtered.iter().take(&self.rustbox.height() - 1 - self.y_offset).enumerate() {
            if y == self.cursor {
                self.rustbox.print_line(y + self.y_offset, &format!("[{0}]: {1}", y, &item), Color::Black, Color::Green);
            } else {
                self.rustbox.print_line(y + self.y_offset, &format!("[{0}]: {1}", y, &item), Color::Blue, Color::Black);
            }
        }

        // print query line and move the cursor to end.
        let query_str = format!("{}{}", self.prompt, self.query);
        self.rustbox.print_lines(&self.body, Color::Green, Color::Black);
        self.rustbox.print_line(self.y_offset - 1, &query_str, Color::Blue, Color::Black);
        self.rustbox.present();
    }

}

<|endoftext|>


<commit_before>use std::path::Path;
use super::{Driver, Dataset, Feature, FeatureIterator, FieldValue, Geometry, OGRFieldType, WkbType};
use spatial_ref::SpatialRef;

mod convert_geo;

macro_rules! fixture {
    ($name:expr) => (
        Path::new(file!())
            .parent().unwrap()
            .parent().unwrap()
            .parent().unwrap()
            .parent().unwrap()
            .join("fixtures").as_path()
            .join($name).as_path()
    )
}

fn assert_almost_eq(a: f64, b: f64) {
    let f: f64 = a / b;
    assert!(f < 1.00001);
    assert!(f > 0.99999);
}

#[test]
fn test_layer_count() {
    let ds = Dataset::open(fixture!("roads.geojson")).unwrap();
    assert_eq!(ds.count(), 1);
}

fn with_features<F>(name: &str, f: F) where F: Fn(FeatureIterator) {
    let mut ds = Dataset::open(fixture!(name)).unwrap();
    let layer = ds.layer(0).unwrap();
    f(layer.features());
}

fn with_first_feature<F>(name: &str, f: F) where F: Fn(Feature) {
    with_features(name, |mut features| f(features.next().unwrap()));
}

#[test]
fn test_iterate_features() {
    with_features("roads.geojson", |features| {
        let feature_vec: Vec<Feature> = features.collect();
        assert_eq!(feature_vec.len(), 21);
    });
}

#[test]
fn test_string_field() {
    with_features("roads.geojson", |mut features| {
        let feature = features.next().unwrap();
        assert_eq!(feature.field("highway")
                          .unwrap()
                          .to_string(),
                   Some("footway".to_string()));
        assert_eq!(
            features.filter(|field| {
                let highway = field.field("highway")
                                   .unwrap()
                                   .to_string();
                highway == Some("residential".to_string()) })
                .count(),
            2);
    });
}

#[test]
fn test_float_field() {
    with_first_feature("roads.geojson", |feature| {
        assert_almost_eq(
            feature.field("sort_key")
                   .unwrap()
                   .to_real()
                   .unwrap(),
            -9.0
        );
    });
}

#[test]
fn test_missing_field() {
    with_first_feature("roads.geojson", |feature| {
        assert!(feature.field("no such field").is_err());
    });
}

#[test]
fn test_geom_accessors() {
    with_first_feature("roads.geojson", |feature| {
        let geom = feature.geometry();
        assert_eq!(geom.geometry_type(), WkbType::WkbLinestring);
        let coords = geom.get_point_vec();
        assert_eq!(coords, [(26.1019276, 44.4302748, 0.0), (26.1019382, 44.4303191, 0.0), (26.1020002, 44.4304202, 0.0)]);
        assert_eq!(geom.geometry_count(), 0);

        let geom = feature.geometry_by_index(0).unwrap();
        assert_eq!(geom.geometry_type(), WkbType::WkbLinestring);
        assert!(feature.geometry_by_index(1).is_err());
        let geom = feature.geometry_by_name("");
        assert!(!geom.is_err());
        let geom = feature.geometry_by_name("").unwrap();
        assert_eq!(geom.geometry_type(), WkbType::WkbLinestring);
        assert!(feature.geometry_by_name("FOO").is_err());
    });
}

#[test]
fn test_wkt() {
    with_first_feature("roads.geojson", |feature| {
        let wkt = feature.geometry().wkt().unwrap();
        let wkt_ok = format!("{}{}",
            "LINESTRING (26.1019276 44.4302748,",
            "26.1019382 44.4303191,26.1020002 44.4304202)"
            ).to_string();
        assert_eq!(wkt, wkt_ok);
    });
}

#[test]
fn test_json() {
    with_first_feature("roads.geojson", |feature| {
        let json = feature.geometry().json();
        let json_ok = format!("{}{}{}{}",
            "{ \"type\": \"LineString\", \"coordinates\": [ ",
            "[ 26.1019276, 44.4302748 ], ",
            "[ 26.1019382, 44.4303191 ], ",
            "[ 26.1020002, 44.4304202 ] ] }"
            ).to_string();
        assert_eq!(json.unwrap(), json_ok);
    });
}

#[test]
fn test_schema() {
    let mut ds = Dataset::open(fixture!("roads.geojson")).unwrap();
    let layer = ds.layer(0).unwrap();
    assert_eq!(layer.name(), "roads".to_string());
    let name_list: Vec<(String, OGRFieldType)> = layer
        .defn().fields()
        .map(|f| (f.name(), f.field_type()))
        .collect();
    let ok_names_types: Vec<(String, OGRFieldType)> = vec!(
        ("id", OGRFieldType::OFTString),
        ("kind", OGRFieldType::OFTString),
        ("sort_key",  OGRFieldType::OFTReal),
        ("is_link", OGRFieldType::OFTString),
        ("is_tunnel", OGRFieldType::OFTString),
        ("is_bridge", OGRFieldType::OFTString),
        ("railway", OGRFieldType::OFTString),
        ("highway", OGRFieldType::OFTString))
        .iter().map(|s| (s.0.to_string(), s.1)).collect();
    assert_eq!(name_list, ok_names_types);
}

#[test]
fn test_geom_fields() {
    let mut ds = Dataset::open(fixture!("roads.geojson")).unwrap();
    let layer = ds.layer(0).unwrap();
    let name_list: Vec<(String, WkbType)> = layer
        .defn().geom_fields()
        .map(|f| (f.name(), f.field_type()))
        .collect();
    let ok_names_types: Vec<(String, WkbType)> = vec!(
        ("", WkbType::WkbLinestring))
        .iter().map(|s| (s.0.to_string(), s.1.clone())).collect();
    assert_eq!(name_list, ok_names_types);

    let geom_field = layer.defn().geom_fields().next().unwrap();
    let spatial_ref2 = SpatialRef::from_epsg(4326).unwrap();
    assert!(geom_field.spatial_ref().unwrap() == spatial_ref2);
}

#[test]
fn test_get_layer_by_name() {
    let mut ds = Dataset::open(fixture!("roads.geojson")).unwrap();
    let layer = ds.layer_by_name("roads").unwrap();
    assert_eq!(layer.name(), "roads");
}

#[test]
fn test_create_bbox() {
    let bbox = Geometry::bbox(-27., 33., 52., 85.).unwrap();
    assert_eq!(bbox.json().unwrap(), "{ \"type\": \"Polygon\", \"coordinates\": [ [ [ -27.0, 85.0 ], [ 52.0, 85.0 ], [ 52.0, 33.0 ], [ -27.0, 33.0 ], [ -27.0, 85.0 ] ] ] }");
}

#[test]
fn test_spatial_filter() {
    let mut ds = Dataset::open(fixture!("roads.geojson")).unwrap();
    let layer = ds.layer(0).unwrap();

    let all_features: Vec<Feature> = layer.features().collect();
    assert_eq!(all_features.len(), 21);

    let bbox = Geometry::bbox(26.1017, 44.4297, 26.1025, 44.4303).unwrap();
    layer.set_spatial_filter(&bbox);

    let some_features: Vec<Feature> = layer.features().collect();
    assert_eq!(some_features.len(), 7);

    layer.clear_spatial_filter();

    let again_all_features: Vec<Feature> = layer.features().collect();
    assert_eq!(again_all_features.len(), 21);
}

#[test]
fn test_convex_hull() {
    let star = "POLYGON ((0 1,3 1,1 3,1.5 0.0,2 3,0 1))";
    let hull = "POLYGON ((1.5 0.0,0 1,1 3,2 3,3 1,1.5 0.0))";
    assert_eq!(Geometry::from_wkt(star).unwrap().convex_hull().unwrap().wkt().unwrap(), hull);
}

#[test]
fn test_write_features() {
    use std::fs;

    {
        let driver = Driver::get("GeoJSON").unwrap();
        let mut ds = driver.create(fixture!("output.geojson")).unwrap();
        let mut layer = ds.create_layer().unwrap();
        layer.create_defn_fields(&[("Name",  OGRFieldType::OFTString), ("Value",  OGRFieldType::OFTReal), ("Int_value", OGRFieldType::OFTInteger)]);
        layer.create_feature_fields(
            Geometry::from_wkt("POINT (1 2)").unwrap(), &["Name", "Value", "Int_value"],
            &[FieldValue::StringValue("Feature 1".to_string()), FieldValue::RealValue(45.78), FieldValue::IntegerValue(1)]
            ).unwrap();
        // dataset is closed here
    }

    let mut ds = Dataset::open(fixture!("output.geojson")).unwrap();
    fs::remove_file(fixture!("output.geojson")).unwrap();
    let layer = ds.layer(0).unwrap();
    let ft = layer.features().next().unwrap();
    assert_eq!(ft.geometry().wkt().unwrap(), "POINT (1 2)");
    assert_eq!(ft.field("Name").unwrap().to_string(), Some("Feature 1".to_string()));
    assert_eq!(ft.field("Value").unwrap().to_real(), Some(45.78));
    assert_eq!(ft.field("Int_value").unwrap().to_int(), Some(1));
}
<commit_msg>change tests to make travis happy<commit_after>use std::path::Path;
use super::{Driver, Dataset, Feature, FeatureIterator, FieldValue, Geometry, OGRFieldType, WkbType};
use spatial_ref::SpatialRef;

mod convert_geo;

macro_rules! fixture {
    ($name:expr) => (
        Path::new(file!())
            .parent().unwrap()
            .parent().unwrap()
            .parent().unwrap()
            .parent().unwrap()
            .join("fixtures").as_path()
            .join($name).as_path()
    )
}

fn assert_almost_eq(a: f64, b: f64) {
    let f: f64 = a / b;
    assert!(f < 1.00001);
    assert!(f > 0.99999);
}

#[test]
fn test_layer_count() {
    let ds = Dataset::open(fixture!("roads.geojson")).unwrap();
    assert_eq!(ds.count(), 1);
}

fn with_features<F>(name: &str, f: F) where F: Fn(FeatureIterator) {
    let mut ds = Dataset::open(fixture!(name)).unwrap();
    let layer = ds.layer(0).unwrap();
    f(layer.features());
}

fn with_first_feature<F>(name: &str, f: F) where F: Fn(Feature) {
    with_features(name, |mut features| f(features.next().unwrap()));
}

#[test]
fn test_iterate_features() {
    with_features("roads.geojson", |features| {
        let feature_vec: Vec<Feature> = features.collect();
        assert_eq!(feature_vec.len(), 21);
    });
}

#[test]
fn test_string_field() {
    with_features("roads.geojson", |mut features| {
        let feature = features.next().unwrap();
        assert_eq!(feature.field("highway")
                          .unwrap()
                          .to_string(),
                   Some("footway".to_string()));
        assert_eq!(
            features.filter(|field| {
                let highway = field.field("highway")
                                   .unwrap()
                                   .to_string();
                highway == Some("residential".to_string()) })
                .count(),
            2);
    });
}

#[test]
fn test_float_field() {
    with_first_feature("roads.geojson", |feature| {
        assert_almost_eq(
            feature.field("sort_key")
                   .unwrap()
                   .to_real()
                   .unwrap(),
            -9.0
        );
    });
}

#[test]
fn test_missing_field() {
    with_first_feature("roads.geojson", |feature| {
        assert!(feature.field("no such field").is_err());
    });
}

#[test]
fn test_geom_accessors() {
    with_first_feature("roads.geojson", |feature| {
        let geom = feature.geometry();
        assert_eq!(geom.geometry_type(), WkbType::WkbLinestring);
        let coords = geom.get_point_vec();
        assert_eq!(coords, [(26.1019276, 44.4302748, 0.0), (26.1019382, 44.4303191, 0.0), (26.1020002, 44.4304202, 0.0)]);
        assert_eq!(geom.geometry_count(), 0);

        let geom = feature.geometry_by_index(0).unwrap();
        assert_eq!(geom.geometry_type(), WkbType::WkbLinestring);
        assert!(feature.geometry_by_index(1).is_err());
        let geom = feature.geometry_by_name("");
        assert!(!geom.is_err());
        let geom = feature.geometry_by_name("").unwrap();
        assert_eq!(geom.geometry_type(), WkbType::WkbLinestring);
        assert!(feature.geometry_by_name("FOO").is_err());
    });
}

#[test]
fn test_wkt() {
    with_first_feature("roads.geojson", |feature| {
        let wkt = feature.geometry().wkt().unwrap();
        let wkt_ok = format!("{}{}",
            "LINESTRING (26.1019276 44.4302748,",
            "26.1019382 44.4303191,26.1020002 44.4304202)"
            ).to_string();
        assert_eq!(wkt, wkt_ok);
    });
}

#[test]
fn test_json() {
    with_first_feature("roads.geojson", |feature| {
        let json = feature.geometry().json();
        let json_ok = format!("{}{}{}{}",
            "{ \"type\": \"LineString\", \"coordinates\": [ ",
            "[ 26.1019276, 44.4302748 ], ",
            "[ 26.1019382, 44.4303191 ], ",
            "[ 26.1020002, 44.4304202 ] ] }"
            ).to_string();
        assert_eq!(json.unwrap(), json_ok);
    });
}

#[test]
fn test_schema() {
    let mut ds = Dataset::open(fixture!("roads.geojson")).unwrap();
    let layer = ds.layer(0).unwrap();
    assert_eq!(layer.name(), "OGRGeoJSON".to_string()); // change to "roads" for GDAL >= 2.2
    let name_list: Vec<(String, OGRFieldType)> = layer
        .defn().fields()
        .map(|f| (f.name(), f.field_type()))
        .collect();
    let ok_names_types: Vec<(String, OGRFieldType)> = vec!(
        ("id", OGRFieldType::OFTString),
        ("kind", OGRFieldType::OFTString),
        ("sort_key",  OGRFieldType::OFTReal),
        ("is_link", OGRFieldType::OFTString),
        ("is_tunnel", OGRFieldType::OFTString),
        ("is_bridge", OGRFieldType::OFTString),
        ("railway", OGRFieldType::OFTString),
        ("highway", OGRFieldType::OFTString))
        .iter().map(|s| (s.0.to_string(), s.1)).collect();
    assert_eq!(name_list, ok_names_types);
}

#[test]
fn test_geom_fields() {
    let mut ds = Dataset::open(fixture!("roads.geojson")).unwrap();
    let layer = ds.layer(0).unwrap();
    let name_list: Vec<(String, WkbType)> = layer
        .defn().geom_fields()
        .map(|f| (f.name(), f.field_type()))
        .collect();
    let ok_names_types: Vec<(String, WkbType)> = vec!(
        ("", WkbType::WkbLinestring))
        .iter().map(|s| (s.0.to_string(), s.1.clone())).collect();
    assert_eq!(name_list, ok_names_types);

    let geom_field = layer.defn().geom_fields().next().unwrap();
    let spatial_ref2 = SpatialRef::from_epsg(4326).unwrap();
    assert!(geom_field.spatial_ref().unwrap() == spatial_ref2);
}

#[test]
fn test_get_layer_by_name() {
    let mut ds = Dataset::open(fixture!("roads.geojson")).unwrap();
    let layer = ds.layer_by_name("OGRGeoJSON").unwrap(); // change to "roads" for GDAL >= 2.2
    assert_eq!(layer.name(), "OGRGeoJSON"); // change to "roads" for GDAL >= 2.2
}

#[test]
fn test_create_bbox() {
    let bbox = Geometry::bbox(-27., 33., 52., 85.).unwrap();
    assert_eq!(bbox.json().unwrap(), "{ \"type\": \"Polygon\", \"coordinates\": [ [ [ -27.0, 85.0 ], [ 52.0, 85.0 ], [ 52.0, 33.0 ], [ -27.0, 33.0 ], [ -27.0, 85.0 ] ] ] }");
}

#[test]
fn test_spatial_filter() {
    let mut ds = Dataset::open(fixture!("roads.geojson")).unwrap();
    let layer = ds.layer(0).unwrap();

    let all_features: Vec<Feature> = layer.features().collect();
    assert_eq!(all_features.len(), 21);

    let bbox = Geometry::bbox(26.1017, 44.4297, 26.1025, 44.4303).unwrap();
    layer.set_spatial_filter(&bbox);

    let some_features: Vec<Feature> = layer.features().collect();
    assert_eq!(some_features.len(), 7);

    layer.clear_spatial_filter();

    let again_all_features: Vec<Feature> = layer.features().collect();
    assert_eq!(again_all_features.len(), 21);
}

#[test]
fn test_convex_hull() {
    let star = "POLYGON ((0 1,3 1,1 3,1.5 0.0,2 3,0 1))";
    let hull = "POLYGON ((1.5 0.0,0 1,1 3,2 3,3 1,1.5 0.0))";
    assert_eq!(Geometry::from_wkt(star).unwrap().convex_hull().unwrap().wkt().unwrap(), hull);
}

#[test]
fn test_write_features() {
    use std::fs;

    {
        let driver = Driver::get("GeoJSON").unwrap();
        let mut ds = driver.create(fixture!("output.geojson")).unwrap();
        let mut layer = ds.create_layer().unwrap();
        layer.create_defn_fields(&[("Name",  OGRFieldType::OFTString), ("Value",  OGRFieldType::OFTReal), ("Int_value", OGRFieldType::OFTInteger)]);
        layer.create_feature_fields(
            Geometry::from_wkt("POINT (1 2)").unwrap(), &["Name", "Value", "Int_value"],
            &[FieldValue::StringValue("Feature 1".to_string()), FieldValue::RealValue(45.78), FieldValue::IntegerValue(1)]
            ).unwrap();
        // dataset is closed here
    }

    let mut ds = Dataset::open(fixture!("output.geojson")).unwrap();
    fs::remove_file(fixture!("output.geojson")).unwrap();
    let layer = ds.layer(0).unwrap();
    let ft = layer.features().next().unwrap();
    assert_eq!(ft.geometry().wkt().unwrap(), "POINT (1 2)");
    assert_eq!(ft.field("Name").unwrap().to_string(), Some("Feature 1".to_string()));
    assert_eq!(ft.field("Value").unwrap().to_real(), Some(45.78));
    assert_eq!(ft.field("Int_value").unwrap().to_int(), Some(1));
}
<|endoftext|>


<commit_before>use unicode_segmentation::UnicodeSegmentation;

use widgets::{Widget, Block};
use buffer::Buffer;
use layout::Rect;
use style::Color;

pub struct Text<'a> {
    block: Option<Block<'a>>,
    color: Color,
    background_color: Color,
    wrapping: bool,
    text: &'a str,
    colors: &'a [(u16, u16, u16, Color, Color)],
}

impl<'a> Default for Text<'a> {
    fn default() -> Text<'a> {
        Text {
            block: None,
            color: Color::Reset,
            background_color: Color::Reset,
            wrapping: false,
            text: "",
            colors: &[],
        }
    }
}

impl<'a> Text<'a> {
    pub fn block(&'a mut self, block: Block<'a>) -> &mut Text<'a> {
        self.block = Some(block);
        self
    }

    pub fn text(&mut self, text: &'a str) -> &mut Text<'a> {
        self.text = text;
        self
    }

    pub fn background_color(&mut self, background_color: Color) -> &mut Text<'a> {
        self.background_color = background_color;
        self
    }

    pub fn color(&mut self, color: Color) -> &mut Text<'a> {
        self.color = color;
        self
    }

    pub fn colors(&mut self, colors: &'a [(u16, u16, u16, Color, Color)]) -> &mut Text<'a> {
        self.colors = colors;
        self
    }

    pub fn wrap(&mut self, flag: bool) -> &mut Text<'a> {
        self.wrapping = flag;
        self
    }
}

struct Parser<'a> {
    text: Vec<&'a str>,
    mark: bool,
    color_string: String,
    color: Color,
    base_color: Color,
    escaping: bool,
    coloring: bool,
}

impl<'a> Parser<'a> {
    fn new(text: &'a str, base_color: Color) -> Parser<'a> {
        Parser {
            text: UnicodeSegmentation::graphemes(text, true).rev().collect::<Vec<&str>>(),
            mark: false,
            color_string: String::from(""),
            color: base_color,
            base_color: base_color,
            escaping: false,
            coloring: false,
        }
    }

    fn update_color(&mut self) {
        self.color = match &*self.color_string {
            "black" => Color::Black,
            "red" => Color::Red,
            "green" => Color::Green,
            "yellow" => Color::Yellow,
            "magenta" => Color::Magenta,
            "cyan" => Color::Cyan,
            "gray" => Color::Gray,
            "dark_gray" => Color::DarkGray,
            "light_red" => Color::LightRed,
            "light_green" => Color::LightGreen,
            "light_yellow" => Color::LightYellow,
            "light_magenta" => Color::LightMagenta,
            "light_cyan" => Color::LightCyan,
            "white" => Color::White,
            _ => Color::Reset,
        }
    }

    fn reset(&mut self) {
        self.coloring = false;
        self.mark = false;
        self.color = self.base_color;
        self.color_string.clear();
    }
}

impl<'a> Iterator for Parser<'a> {
    type Item = (&'a str, Color);
    fn next(&mut self) -> Option<(&'a str, Color)> {
        match self.text.pop() {
            Some(s) => {
                if s == "\\" {
                    if self.escaping {
                        Some((s, self.color))
                    } else {
                        self.escaping = true;
                        self.next()
                    }
                } else if s == "{" {
                    if self.escaping {
                        self.escaping = false;
                        Some((s, self.color))
                    } else {
                        self.color = self.base_color;
                        self.mark = true;
                        self.next()
                    }
                } else if s == "}" && self.mark {
                    self.reset();
                    self.next()
                } else if s == " " && self.mark {
                    self.coloring = true;
                    self.update_color();
                    self.next()
                } else if self.mark && !self.coloring {
                    self.color_string.push_str(s);
                    self.next()
                } else {
                    Some((s, self.color))
                }
            }
            None => None,
        }
    }
}

impl<'a> Widget for Text<'a> {
    fn draw(&self, area: &Rect, buf: &mut Buffer) {
        let text_area = match self.block {
            Some(ref b) => {
                b.draw(area, buf);
                b.inner(area)
            }
            None => *area,
        };

        if text_area.height < 1 {
            return;
        }

        if self.background_color != Color::Reset {
            self.background(&text_area, buf, self.background_color);
        }

        let mut x = 0;
        let mut y = 0;
        for (s, c) in Parser::new(self.text, self.color) {
            if s == "\n" {
                x = 0;
                y += 1;
                continue;
            }
            if x >= text_area.width {
                if self.wrapping {
                    x = 0;
                    y += 1;
                }
                continue;
            }

            if y > text_area.height - 1 {
                break;
            }

            buf.set_cell(text_area.left() + x,
                         text_area.top() + y,
                         s,
                         c,
                         self.background_color);
            x += 1;
        }

        for &(x, y, width, fg, bg) in self.colors {
            for i in 0..width {
                buf.set_fg(x + i, y + text_area.top(), fg);
                buf.set_bg(x + i, y + text_area.top(), bg);
            }
        }
    }
}
<commit_msg>Fix text width measure in Text widget<commit_after>use unicode_segmentation::UnicodeSegmentation;

use widgets::{Widget, Block};
use buffer::Buffer;
use layout::Rect;
use style::Color;

pub struct Text<'a> {
    block: Option<Block<'a>>,
    color: Color,
    background_color: Color,
    wrapping: bool,
    text: &'a str,
    colors: &'a [(u16, u16, u16, Color, Color)],
}

impl<'a> Default for Text<'a> {
    fn default() -> Text<'a> {
        Text {
            block: None,
            color: Color::Reset,
            background_color: Color::Reset,
            wrapping: false,
            text: "",
            colors: &[],
        }
    }
}

impl<'a> Text<'a> {
    pub fn block(&'a mut self, block: Block<'a>) -> &mut Text<'a> {
        self.block = Some(block);
        self
    }

    pub fn text(&mut self, text: &'a str) -> &mut Text<'a> {
        self.text = text;
        self
    }

    pub fn background_color(&mut self, background_color: Color) -> &mut Text<'a> {
        self.background_color = background_color;
        self
    }

    pub fn color(&mut self, color: Color) -> &mut Text<'a> {
        self.color = color;
        self
    }

    pub fn colors(&mut self, colors: &'a [(u16, u16, u16, Color, Color)]) -> &mut Text<'a> {
        self.colors = colors;
        self
    }

    pub fn wrap(&mut self, flag: bool) -> &mut Text<'a> {
        self.wrapping = flag;
        self
    }
}

struct Parser<'a> {
    text: Vec<&'a str>,
    mark: bool,
    color_string: String,
    color: Color,
    base_color: Color,
    escaping: bool,
    coloring: bool,
}

impl<'a> Parser<'a> {
    fn new(text: &'a str, base_color: Color) -> Parser<'a> {
        Parser {
            text: UnicodeSegmentation::graphemes(text, true).rev().collect::<Vec<&str>>(),
            mark: false,
            color_string: String::from(""),
            color: base_color,
            base_color: base_color,
            escaping: false,
            coloring: false,
        }
    }

    fn update_color(&mut self) {
        self.color = match &*self.color_string {
            "black" => Color::Black,
            "red" => Color::Red,
            "green" => Color::Green,
            "yellow" => Color::Yellow,
            "magenta" => Color::Magenta,
            "cyan" => Color::Cyan,
            "gray" => Color::Gray,
            "dark_gray" => Color::DarkGray,
            "light_red" => Color::LightRed,
            "light_green" => Color::LightGreen,
            "light_yellow" => Color::LightYellow,
            "light_magenta" => Color::LightMagenta,
            "light_cyan" => Color::LightCyan,
            "white" => Color::White,
            _ => Color::Reset,
        }
    }

    fn reset(&mut self) {
        self.coloring = false;
        self.mark = false;
        self.color = self.base_color;
        self.color_string.clear();
    }
}

impl<'a> Iterator for Parser<'a> {
    type Item = (&'a str, Color);
    fn next(&mut self) -> Option<(&'a str, Color)> {
        match self.text.pop() {
            Some(s) => {
                if s == "\\" {
                    if self.escaping {
                        Some((s, self.color))
                    } else {
                        self.escaping = true;
                        self.next()
                    }
                } else if s == "{" {
                    if self.escaping {
                        self.escaping = false;
                        Some((s, self.color))
                    } else {
                        self.color = self.base_color;
                        self.mark = true;
                        self.next()
                    }
                } else if s == "}" && self.mark {
                    self.reset();
                    self.next()
                } else if s == " " && self.mark {
                    self.coloring = true;
                    self.update_color();
                    self.next()
                } else if self.mark && !self.coloring {
                    self.color_string.push_str(s);
                    self.next()
                } else {
                    Some((s, self.color))
                }
            }
            None => None,
        }
    }
}

impl<'a> Widget for Text<'a> {
    fn draw(&self, area: &Rect, buf: &mut Buffer) {
        let text_area = match self.block {
            Some(ref b) => {
                b.draw(area, buf);
                b.inner(area)
            }
            None => *area,
        };

        if text_area.height < 1 {
            return;
        }

        if self.background_color != Color::Reset {
            self.background(&text_area, buf, self.background_color);
        }

        let mut x = 0;
        let mut y = 0;
        for (s, c) in Parser::new(self.text, self.color) {
            if s == "\n" {
                x = 0;
                y += 1;
                continue;
            }
            if x >= text_area.width {
                if self.wrapping {
                    x = 0;
                    y += 1;
                }
                continue;
            }

            if y > text_area.height - 1 {
                break;
            }

            buf.set_cell(text_area.left() + x,
                         text_area.top() + y,
                         s,
                         c,
                         self.background_color);
            x += s.width() as u16;
        }
    }
}
<|endoftext|>


<commit_before>#![doc(html_favicon_url = "https://www.ruma.io/favicon.ico")]
#![doc(html_logo_url = "https://www.ruma.io/images/logo.png")]
//! A minimal [Matrix](https://matrix.org/) client library.
//!
//! # Usage
//!
//! Begin by creating a `Client` type, usually using the `https` method for a client that supports
//! secure connections, and then logging in:
//!
//! ```ignore
//! use ruma_client::Client;
//!
//! let work = async {
//!     let homeserver_url = "https://example.com".parse().unwrap();
//!     let client = Client::new(homeserver_url, None);
//!
//!     let session = client
//!         .log_in("@alice:example.com", "secret", None, None)
//!         .await?;
//!
//!     // You're now logged in! Write the session to a file if you want to restore it later.
//!     // Then start using the API!
//! # Result::<(), ruma_client::Error<_>>::Ok(())
//! };
//! ```
//!
//! You can also pass an existing access token to the `Client` constructor to restore a previous
//! session rather than calling `log_in`. This can also be used to create a session for an
//! application service that does not need to log in, but uses the access_token directly:
//!
//! ```ignore
//! use ruma_client::Client;
//!
//! let work = async {
//!     let homeserver_url = "https://example.com".parse().unwrap();
//!     let client = Client::new(homeserver_url, Some("as_access_token".into()));
//!
//!     // make calls to the API
//! };
//! ```
//!
//! The `Client` type also provides methods for registering a new account if you don't already have
//! one with the given homeserver.
//!
//! Beyond these basic convenience methods, `ruma-client` gives you access to the entire Matrix
//! client-server API via the `request` method. You can pass it any of the `Request` types found in
//! `ruma::api::*` and get back a corresponding response from the homeserver.
//!
//! For example:
//!
//! ```ignore
//! # use ruma_client::Client;
//! # let homeserver_url = "https://example.com".parse().unwrap();
//! # let client = Client::new(homeserver_url, None);
//! use std::convert::TryFrom;
//!
//! use ruma::{
//!     api::client::r0::alias::get_alias,
//!     room_alias_id, room_id,
//! };
//!
//! async {
//!     let response = client
//!         .send_request(get_alias::Request::new(&room_alias_id!("#example_room:example.com")))
//!         .await?;
//!
//!     assert_eq!(response.room_id, room_id!("!n8f893n9:example.com"));
//! #   Result::<(), ruma_client::Error<_, _>>::Ok(())
//! }
//! # ;
//! ```

#![warn(rust_2018_idioms)]
#![deny(missing_debug_implementations, missing_docs)]

use std::{
    future::Future,
    sync::{Arc, Mutex},
};

use ruma_api::{OutgoingRequest, SendAccessToken};

// "Undo" rename from `Cargo.toml` that only serves to make `hyper-rustls` available as a Cargo
// feature name.
#[cfg(feature = "hyper-rustls")]
extern crate hyper_rustls_crate as hyper_rustls;

#[cfg(feature = "client-api")]
mod client_api;
mod error;
pub mod http_client;

pub use self::{
    error::Error,
    http_client::{DefaultConstructibleHttpClient, HttpClient, HttpClientExt},
};

/// The result of sending the request `R` with the http client `C`.
pub type ResponseResult<C, R> = Result<
    <R as OutgoingRequest>::IncomingResponse,
    Error<<C as HttpClient>::Error, <R as OutgoingRequest>::EndpointError>,
>;

/// A client for the Matrix client-server API.
#[derive(Clone, Debug)]
pub struct Client<C>(Arc<ClientData<C>>);

/// Data contained in Client's Rc
#[derive(Debug)]
struct ClientData<C> {
    /// The URL of the homeserver to connect to.
    homeserver_url: String,

    /// The underlying HTTP client.
    http_client: C,

    /// User session data.
    access_token: Mutex<Option<String>>,
}

impl<C> Client<C> {
    /// Creates a new client using the given underlying HTTP client.
    ///
    /// This allows the user to configure the details of HTTP as desired.
    pub fn with_http_client(
        http_client: C,
        homeserver_url: String,
        access_token: Option<String>,
    ) -> Self {
        Self(Arc::new(ClientData {
            homeserver_url,
            http_client,
            access_token: Mutex::new(access_token),
        }))
    }

    /// Get a copy of the current `access_token`, if any.
    ///
    /// Useful for serializing and persisting the session to be restored later.
    pub fn access_token(&self) -> Option<String> {
        self.0.access_token.lock().expect("session mutex was poisoned").clone()
    }
}

impl<C: DefaultConstructibleHttpClient> Client<C> {
    /// Creates a new client based on a default-constructed hyper HTTP client.
    pub fn new(homeserver_url: String, access_token: Option<String>) -> Self {
        Self(Arc::new(ClientData {
            homeserver_url,
            http_client: DefaultConstructibleHttpClient::default(),
            access_token: Mutex::new(access_token),
        }))
    }
}

impl<C: HttpClient> Client<C> {
    /// Makes a request to a Matrix API endpoint.
    pub async fn send_request<R: OutgoingRequest>(
        &self,
        request: R,
    ) -> Result<R::IncomingResponse, Error<C::Error, R::EndpointError>> {
        self.send_customized_request(request, |_| {}).await
    }

    /// Makes a request to a Matrix API endpoint including additional URL parameters.
    pub async fn send_customized_request<R, F>(
        &self,
        request: R,
        customize: F,
    ) -> Result<R::IncomingResponse, Error<C::Error, R::EndpointError>>
    where
        R: OutgoingRequest,
        F: FnOnce(&mut http::Request<C::RequestBody>),
    {
        let access_token = self.access_token();
        let send_access_token = match access_token.as_deref() {
            Some(at) => SendAccessToken::IfRequired(at),
            None => SendAccessToken::None,
        };

        send_customized_request(
            &self.0.http_client,
            &self.0.homeserver_url,
            send_access_token,
            request,
            customize,
        )
        .await
    }
}

fn send_customized_request<'a, C, R, F>(
    http_client: &'a C,
    homeserver_url: &str,
    send_access_token: SendAccessToken<'_>,
    request: R,
    customize: F,
) -> impl Future<Output = Result<R::IncomingResponse, Error<C::Error, R::EndpointError>>> + Send + 'a
where
    C: HttpClient + ?Sized,
    R: OutgoingRequest,
    F: FnOnce(&mut http::Request<C::RequestBody>),
{
    let mut http_req = request.try_into_http_request(homeserver_url, send_access_token);
    if let Ok(req) = &mut http_req {
        customize(req);
    }

    async move {
        let http_res = http_client.send_http_request(http_req?).await.map_err(Error::Response)?;
        Ok(ruma_api::IncomingResponse::try_from_http_response(http_res)?)
    }
}
<commit_msg>client: Use ResponseResult type alias in more places<commit_after>#![doc(html_favicon_url = "https://www.ruma.io/favicon.ico")]
#![doc(html_logo_url = "https://www.ruma.io/images/logo.png")]
//! A minimal [Matrix](https://matrix.org/) client library.
//!
//! # Usage
//!
//! Begin by creating a `Client` type, usually using the `https` method for a client that supports
//! secure connections, and then logging in:
//!
//! ```ignore
//! use ruma_client::Client;
//!
//! let work = async {
//!     let homeserver_url = "https://example.com".parse().unwrap();
//!     let client = Client::new(homeserver_url, None);
//!
//!     let session = client
//!         .log_in("@alice:example.com", "secret", None, None)
//!         .await?;
//!
//!     // You're now logged in! Write the session to a file if you want to restore it later.
//!     // Then start using the API!
//! # Result::<(), ruma_client::Error<_>>::Ok(())
//! };
//! ```
//!
//! You can also pass an existing access token to the `Client` constructor to restore a previous
//! session rather than calling `log_in`. This can also be used to create a session for an
//! application service that does not need to log in, but uses the access_token directly:
//!
//! ```ignore
//! use ruma_client::Client;
//!
//! let work = async {
//!     let homeserver_url = "https://example.com".parse().unwrap();
//!     let client = Client::new(homeserver_url, Some("as_access_token".into()));
//!
//!     // make calls to the API
//! };
//! ```
//!
//! The `Client` type also provides methods for registering a new account if you don't already have
//! one with the given homeserver.
//!
//! Beyond these basic convenience methods, `ruma-client` gives you access to the entire Matrix
//! client-server API via the `request` method. You can pass it any of the `Request` types found in
//! `ruma::api::*` and get back a corresponding response from the homeserver.
//!
//! For example:
//!
//! ```ignore
//! # use ruma_client::Client;
//! # let homeserver_url = "https://example.com".parse().unwrap();
//! # let client = Client::new(homeserver_url, None);
//! use std::convert::TryFrom;
//!
//! use ruma::{
//!     api::client::r0::alias::get_alias,
//!     room_alias_id, room_id,
//! };
//!
//! async {
//!     let response = client
//!         .send_request(get_alias::Request::new(&room_alias_id!("#example_room:example.com")))
//!         .await?;
//!
//!     assert_eq!(response.room_id, room_id!("!n8f893n9:example.com"));
//! #   Result::<(), ruma_client::Error<_, _>>::Ok(())
//! }
//! # ;
//! ```

#![warn(rust_2018_idioms)]
#![deny(missing_debug_implementations, missing_docs)]

use std::{
    future::Future,
    sync::{Arc, Mutex},
};

use ruma_api::{OutgoingRequest, SendAccessToken};

// "Undo" rename from `Cargo.toml` that only serves to make `hyper-rustls` available as a Cargo
// feature name.
#[cfg(feature = "hyper-rustls")]
extern crate hyper_rustls_crate as hyper_rustls;

#[cfg(feature = "client-api")]
mod client_api;
mod error;
pub mod http_client;

pub use self::{
    error::Error,
    http_client::{DefaultConstructibleHttpClient, HttpClient, HttpClientExt},
};

/// The result of sending the request `R` with the http client `C`.
pub type ResponseResult<C, R> = Result<
    <R as OutgoingRequest>::IncomingResponse,
    Error<<C as HttpClient>::Error, <R as OutgoingRequest>::EndpointError>,
>;

/// A client for the Matrix client-server API.
#[derive(Clone, Debug)]
pub struct Client<C>(Arc<ClientData<C>>);

/// Data contained in Client's Rc
#[derive(Debug)]
struct ClientData<C> {
    /// The URL of the homeserver to connect to.
    homeserver_url: String,

    /// The underlying HTTP client.
    http_client: C,

    /// User session data.
    access_token: Mutex<Option<String>>,
}

impl<C> Client<C> {
    /// Creates a new client using the given underlying HTTP client.
    ///
    /// This allows the user to configure the details of HTTP as desired.
    pub fn with_http_client(
        http_client: C,
        homeserver_url: String,
        access_token: Option<String>,
    ) -> Self {
        Self(Arc::new(ClientData {
            homeserver_url,
            http_client,
            access_token: Mutex::new(access_token),
        }))
    }

    /// Get a copy of the current `access_token`, if any.
    ///
    /// Useful for serializing and persisting the session to be restored later.
    pub fn access_token(&self) -> Option<String> {
        self.0.access_token.lock().expect("session mutex was poisoned").clone()
    }
}

impl<C: DefaultConstructibleHttpClient> Client<C> {
    /// Creates a new client based on a default-constructed hyper HTTP client.
    pub fn new(homeserver_url: String, access_token: Option<String>) -> Self {
        Self(Arc::new(ClientData {
            homeserver_url,
            http_client: DefaultConstructibleHttpClient::default(),
            access_token: Mutex::new(access_token),
        }))
    }
}

impl<C: HttpClient> Client<C> {
    /// Makes a request to a Matrix API endpoint.
    pub async fn send_request<R: OutgoingRequest>(&self, request: R) -> ResponseResult<C, R> {
        self.send_customized_request(request, |_| {}).await
    }

    /// Makes a request to a Matrix API endpoint including additional URL parameters.
    pub async fn send_customized_request<R, F>(
        &self,
        request: R,
        customize: F,
    ) -> ResponseResult<C, R>
    where
        R: OutgoingRequest,
        F: FnOnce(&mut http::Request<C::RequestBody>),
    {
        let access_token = self.access_token();
        let send_access_token = match access_token.as_deref() {
            Some(at) => SendAccessToken::IfRequired(at),
            None => SendAccessToken::None,
        };

        send_customized_request(
            &self.0.http_client,
            &self.0.homeserver_url,
            send_access_token,
            request,
            customize,
        )
        .await
    }
}

fn send_customized_request<'a, C, R, F>(
    http_client: &'a C,
    homeserver_url: &str,
    send_access_token: SendAccessToken<'_>,
    request: R,
    customize: F,
) -> impl Future<Output = ResponseResult<C, R>> + Send + 'a
where
    C: HttpClient + ?Sized,
    R: OutgoingRequest,
    F: FnOnce(&mut http::Request<C::RequestBody>),
{
    let mut http_req = request.try_into_http_request(homeserver_url, send_access_token);
    if let Ok(req) = &mut http_req {
        customize(req);
    }

    async move {
        let http_res = http_client.send_http_request(http_req?).await.map_err(Error::Response)?;
        Ok(ruma_api::IncomingResponse::try_from_http_response(http_res)?)
    }
}
<|endoftext|>