<commit_before>extern crate bytes;
extern crate chrono;
extern crate clap;
extern crate env_logger;
extern crate futures;
extern crate futures_cpupool;
extern crate grpc;
extern crate log;
extern crate protobuf;

use futures::future::Future;

extern crate grpc_interop;
use grpc_interop::*;

use bytes::Bytes;

use grpc::*;

use chrono::*;
use clap::{App, Arg};

fn empty_unary(client: TestServiceClient) {
    client
        .empty_call(grpc::RequestOptions::new(), Empty::new())
        .wait_drop_metadata()
        .expect("failed to get EmptyUnary result");
    println!("{} EmptyUnary done", Local::now().to_rfc3339());
}

// TODO: file a bug on this failure.
fn large_unary(client: TestServiceClient) {
    let mut payload = Payload::new();
    payload.set_body(vec![0; 271828]);
    let mut request = SimpleRequest::new();
    request.set_payload(payload);
    request.set_response_size(314159);
    let response = client
        .unary_call(grpc::RequestOptions::new(), request)
        .wait_drop_metadata()
        .expect("expected full frame");
    assert!(response.get_payload().body.len() == 314159);
    println!("{} LargeUnary done", Local::now().to_rfc3339());
}

fn client_streaming(client: TestServiceClient) {
    let mut requests = Vec::new();
    for size in [27182, 8, 1828, 45904].iter() {
        let mut request = StreamingInputCallRequest::new();
        let mut payload = Payload::new();
        payload.set_body(vec![0; size.to_owned()]);
        request.set_payload(payload);
        requests.push(request);
    }

    let response = client
        .streaming_input_call(
            grpc::RequestOptions::new(),
            grpc::StreamingRequest::iter(requests),
        ).wait_drop_metadata()
        .expect("expected response");
    assert!(response.aggregated_payload_size == 74922);
    println!("{} ClientStreaming done", Local::now().to_rfc3339());
}

// TODO: test fails with an assertion error, we never see a response from the client.
// fails with 'expected a response: Other("partial frame")'
fn server_streaming(client: TestServiceClient) {
    let mut req = StreamingOutputCallRequest::new();
    let mut params = Vec::new();
    for &size in [31415, 9, 2653, 58979].iter() {
        let mut rp = ResponseParameters::new();
        rp.set_size(size as i32);
        params.push(rp);
    }
    req.set_response_parameters(::protobuf::RepeatedField::from_vec(params));

    let response_stream = client
        .streaming_output_call(grpc::RequestOptions::new(), req)
        .wait_drop_metadata();

    let mut response_sizes = Vec::new();

    {
        // this scope is to satisfy the borrow checker.
        let bar = response_stream.map(|response| {
            response_sizes.push(
                response
                    .expect("expected a response")
                    .get_payload()
                    .body
                    .len(),
            );
        });
        assert!(bar.count() == 4);
    }

    assert!(response_sizes.len() == 4);
    assert!(response_sizes[0] == 31415);
    assert!(response_sizes[1] == 9);
    assert!(response_sizes[2] == 2653);
    assert!(response_sizes[3] == 58979);
    println!("{} ServerStreaming done", Local::now().to_rfc3339());
}

// TODO: this test needs to interleave requests with responses and check along the way.
// Need to find a way to model that with the blocking API.
fn ping_pong(client: TestServiceClient) {
    let mut requests = Vec::new();
    for &size_body_len in [(31415, 27182), (9, 8), (2653, 1828), (58979, 45904)].iter() {
        let mut req = StreamingOutputCallRequest::new();
        let mut params = ResponseParameters::new();
        params.set_size(size_body_len.0);
        req.set_response_parameters(::protobuf::RepeatedField::from_vec(vec![params]));
        let mut payload = Payload::new();
        payload.set_body(vec![0; size_body_len.1]);
        req.set_payload(payload);
        requests.push(req);
    }
    let response = client
        .full_duplex_call(
            grpc::RequestOptions::new(),
            grpc::StreamingRequest::iter(requests),
        ).wait_drop_metadata();
    let mut response_sizes = Vec::new();
    {
        // this scope is to satisfy the borrow checker.
        let bar = response.map(|result| {
            response_sizes.push(result.unwrap().get_payload().body.len());
        });
        assert!(bar.count() == 4);
    }

    println!("response_sizes.len is {}", response_sizes.len());
    assert!(response_sizes.len() == 4);
    assert!(response_sizes[0] == 31415);
    assert!(response_sizes[1] == 9);
    assert!(response_sizes[2] == 2653);
    assert!(response_sizes[3] == 58979);
    println!("{} PingPong done", Local::now().to_rfc3339());
}

fn empty_stream(client: TestServiceClient) {
    let response = client
        .full_duplex_call(grpc::RequestOptions::new(), grpc::StreamingRequest::empty())
        .wait_drop_metadata();
    assert!(response.count() == 0);
    println!("{} EmptyStream done", Local::now().to_rfc3339());
}

fn custom_metadata(client: TestServiceClient) {
    fn make_options() -> grpc::RequestOptions {
        // The client attaches custom metadata with the following keys and values:
        // key: "x-grpc-test-echo-initial", value: "test_initial_metadata_value"
        // key: "x-grpc-test-echo-initial", value: "test_initial_metadata_value"
        let mut options = grpc::RequestOptions::new();
        options.metadata.add(
            MetadataKey::from("x-grpc-test-echo-initial"),
            Bytes::from("test_initial_metadata_value"),
        );
        options.metadata.add(
            MetadataKey::from("x-grpc-test-echo-trailing-bin"),
            Bytes::from(&b"\xab\xab\xab"[..]),
        );
        options
    }

    fn assert_result_metadata(initial: grpc::Metadata, trailing: grpc::Metadata) {
        assert_eq!(
            Some(&b"test_initial_metadata_value"[..]),
            initial.get("x-grpc-test-echo-initial")
        );
        assert_eq!(
            Some(&b"\xab\xab\xab"[..]),
            trailing.get("x-grpc-test-echo-trailing-bin")
        );
    }

    {
        // to a UnaryCall with request:
        // {
        //   response_size: 314159
        //   payload:{
        //     body: 271828 bytes of zeros
        //   }
        // }
        let mut req = SimpleRequest::new();
        req.set_response_size(314159);
        let mut payload = Payload::new();
        payload.set_body(vec![0; 271828]);
        req.set_payload(payload);
        let (initial, _result, trailing) = client
            .unary_call(make_options(), req)
            .wait()
            .expect("UnaryCall");

        assert_result_metadata(initial, trailing);
    }

    {
        // to a FullDuplexCall with request:
        // {
        //   response_parameters:{
        //     size: 314159
        //   }
        //   payload:{
        //     body: 271828 bytes of zeros
        //   }
        // }
        let mut req = StreamingOutputCallRequest::new();
        {
            let mut rp = ResponseParameters::new();
            rp.set_size(314159);
            req.mut_response_parameters().push(rp);
        }
        {
            let mut p = Payload::new();
            p.set_body(vec![0; 271828]);
            req.set_payload(p);
        }

        let (initial, _res, trailing) = client
            .full_duplex_call(make_options(), grpc::StreamingRequest::single(req))
            .collect()
            .wait()
            .expect("FullDuplexCall");

        assert_result_metadata(initial, trailing);
    }
}

// The flags we use are defined in the gRPC Interopability doc
// https://github.com/grpc/grpc/blob/master/doc/interop-test-descriptions.md
fn main() {
    env_logger::init().expect("env_logger::init");

    let options = App::new("gRPC interopability client")
        .version("0.1")
        .author("Steve Jenson <stevej@buoyant.io>")
        .about("Interoperability Test Client for grpc-rust")
        .arg(
            Arg::with_name("server_host")
                .long("server_host")
                .help("The server host to connect to. For example, \"localhost\" or \"127.0.0.1\"")
                .takes_value(true),
        ).arg(
            Arg::with_name("server_host_override")
                .long("server_host_override")
                .help(
                    "The server host to claim to be connecting to, for use in TLS and HTTP/2 \
                     :authority header. If unspecified, the value of --server_host will be used",
                ).takes_value(true),
        ).arg(
            Arg::with_name("server_port")
                .long("server_port")
                .help("The server port to connect to. For example, \"8080\"")
                .takes_value(true),
        ).arg(
            Arg::with_name("test_case")
                .long("test_case")
                .help("The name of the test case to execute. For example, \"empty_unary\"")
                .takes_value(true),
        ).arg(
            Arg::with_name("use_tls") // boolean
            .long("use_tls")
            .help("Whether to use a plaintext or encrypted connection")
            .takes_value(true),
        ).arg(
            Arg::with_name("use_test_ca")
                .long("use_test_ca")
                .help("Whether to replace platform root CAs with ca.pem as the CA root")
                .takes_value(true),
        ).arg(
            Arg::with_name("default_service_account")
                .long("default_service_account")
                .help("Email of the GCE default service account.")
                .takes_value(true),
        ).arg(
            Arg::with_name("oauth_scope")
                .long("oauth_scope")
                .help("OAuth scope. For example, \"https://www.googleapis.com/auth/xapi.zoo\"")
                .takes_value(true),
        ).arg(
            Arg::with_name("service_account_key_file")
                .long("service_account_key_file")
                .help(
                    "The path to the service account JSON key file generated from GCE developer \
                     console.",
                ).takes_value(true),
        ).get_matches();

    let hostname = options.value_of("server_host").unwrap_or("localhost");
    let serverport = options
        .value_of("server_port")
        .map(|s| s.parse().unwrap())
        .unwrap_or(DEFAULT_PORT);

    let client =
        TestServiceClient::new_plain(hostname, serverport, Default::default()).expect("init");

    let testcase = options.value_of("test_case").unwrap_or("");
    match testcase.as_ref() {
        "empty_unary" => empty_unary(client),
        "cacheable_unary" => panic!("cacheable_unary not done yet"),
        "large_unary" => large_unary(client),
        "client_compressed_unary" => panic!("client_compressed_unary not done yet"),
        "server_compressed_unary" => panic!("server_compressed_unary not done yet"),
        "client_streaming" => client_streaming(client),
        "client_compressed_streaming" => panic!("client_compressed_streaming not done yet"),
        "server_streaming" => server_streaming(client),
        "server_compressed_streaming" => panic!("server_compressed_streaming not done yet"),
        "ping_pong" => ping_pong(client),
        "empty_stream" => empty_stream(client),
        "compute_engine_creds" => panic!("compute_engine_creds not done yet"),
        "jwt_token_creds" => panic!("jwt_token_creds not done yet"),
        "oauth2_auth_token" => panic!("oauth2_auth_token not done yet"),
        "per_rpc_creds" => panic!("per_rpc_creds not done yet"),
        "custom_metadata" => custom_metadata(client),
        "status_code_and_message" => panic!("status_code_and_message not done yet"),
        "unimplemented_method" => panic!("unimplemented_method not done yet"),
        "unimplemented_service" => panic!("unimplemented_service not done yet"),
        "cancel_after_begin" => panic!("cancel_after_begin not done yet"),
        "cancel_after_first_response" => panic!("cancel_after_first_response not done yet"),
        "timeout_on_sleeping_server" => panic!("timeout_on_sleeping_server not done yet"),
        _ => panic!("no test_case specified"),
    }
}
<commit_msg>rustfmt<commit_after>extern crate bytes;
extern crate chrono;
extern crate clap;
extern crate env_logger;
extern crate futures;
extern crate futures_cpupool;
extern crate grpc;
extern crate log;
extern crate protobuf;

use futures::future::Future;

extern crate grpc_interop;
use grpc_interop::*;

use bytes::Bytes;

use grpc::*;

use chrono::*;
use clap::{App, Arg};

fn empty_unary(client: TestServiceClient) {
    client
        .empty_call(grpc::RequestOptions::new(), Empty::new())
        .wait_drop_metadata()
        .expect("failed to get EmptyUnary result");
    println!("{} EmptyUnary done", Local::now().to_rfc3339());
}

// TODO: file a bug on this failure.
fn large_unary(client: TestServiceClient) {
    let mut payload = Payload::new();
    payload.set_body(vec![0; 271828]);
    let mut request = SimpleRequest::new();
    request.set_payload(payload);
    request.set_response_size(314159);
    let response = client
        .unary_call(grpc::RequestOptions::new(), request)
        .wait_drop_metadata()
        .expect("expected full frame");
    assert!(response.get_payload().body.len() == 314159);
    println!("{} LargeUnary done", Local::now().to_rfc3339());
}

fn client_streaming(client: TestServiceClient) {
    let mut requests = Vec::new();
    for size in [27182, 8, 1828, 45904].iter() {
        let mut request = StreamingInputCallRequest::new();
        let mut payload = Payload::new();
        payload.set_body(vec![0; size.to_owned()]);
        request.set_payload(payload);
        requests.push(request);
    }

    let response = client
        .streaming_input_call(
            grpc::RequestOptions::new(),
            grpc::StreamingRequest::iter(requests),
        ).wait_drop_metadata()
        .expect("expected response");
    assert!(response.aggregated_payload_size == 74922);
    println!("{} ClientStreaming done", Local::now().to_rfc3339());
}

// TODO: test fails with an assertion error, we never see a response from the client.
// fails with 'expected a response: Other("partial frame")'
fn server_streaming(client: TestServiceClient) {
    let mut req = StreamingOutputCallRequest::new();
    let mut params = Vec::new();
    for &size in [31415, 9, 2653, 58979].iter() {
        let mut rp = ResponseParameters::new();
        rp.set_size(size as i32);
        params.push(rp);
    }
    req.set_response_parameters(::protobuf::RepeatedField::from_vec(params));

    let response_stream = client
        .streaming_output_call(grpc::RequestOptions::new(), req)
        .wait_drop_metadata();

    let mut response_sizes = Vec::new();

    {
        // this scope is to satisfy the borrow checker.
        let bar = response_stream.map(|response| {
            response_sizes.push(
                response
                    .expect("expected a response")
                    .get_payload()
                    .body
                    .len(),
            );
        });
        assert!(bar.count() == 4);
    }

    assert!(response_sizes.len() == 4);
    assert!(response_sizes[0] == 31415);
    assert!(response_sizes[1] == 9);
    assert!(response_sizes[2] == 2653);
    assert!(response_sizes[3] == 58979);
    println!("{} ServerStreaming done", Local::now().to_rfc3339());
}

// TODO: this test needs to interleave requests with responses and check along the way.
// Need to find a way to model that with the blocking API.
fn ping_pong(client: TestServiceClient) {
    let mut requests = Vec::new();
    for &size_body_len in [(31415, 27182), (9, 8), (2653, 1828), (58979, 45904)].iter() {
        let mut req = StreamingOutputCallRequest::new();
        let mut params = ResponseParameters::new();
        params.set_size(size_body_len.0);
        req.set_response_parameters(::protobuf::RepeatedField::from_vec(vec![params]));
        let mut payload = Payload::new();
        payload.set_body(vec![0; size_body_len.1]);
        req.set_payload(payload);
        requests.push(req);
    }
    let response = client
        .full_duplex_call(
            grpc::RequestOptions::new(),
            grpc::StreamingRequest::iter(requests),
        ).wait_drop_metadata();
    let mut response_sizes = Vec::new();
    {
        // this scope is to satisfy the borrow checker.
        let bar = response.map(|result| {
            response_sizes.push(result.unwrap().get_payload().body.len());
        });
        assert!(bar.count() == 4);
    }

    println!("response_sizes.len is {}", response_sizes.len());
    assert!(response_sizes.len() == 4);
    assert!(response_sizes[0] == 31415);
    assert!(response_sizes[1] == 9);
    assert!(response_sizes[2] == 2653);
    assert!(response_sizes[3] == 58979);
    println!("{} PingPong done", Local::now().to_rfc3339());
}

fn empty_stream(client: TestServiceClient) {
    let response = client
        .full_duplex_call(grpc::RequestOptions::new(), grpc::StreamingRequest::empty())
        .wait_drop_metadata();
    assert!(response.count() == 0);
    println!("{} EmptyStream done", Local::now().to_rfc3339());
}

fn custom_metadata(client: TestServiceClient) {
    fn make_options() -> grpc::RequestOptions {
        // The client attaches custom metadata with the following keys and values:
        // key: "x-grpc-test-echo-initial", value: "test_initial_metadata_value"
        // key: "x-grpc-test-echo-initial", value: "test_initial_metadata_value"
        let mut options = grpc::RequestOptions::new();
        options.metadata.add(
            MetadataKey::from("x-grpc-test-echo-initial"),
            Bytes::from("test_initial_metadata_value"),
        );
        options.metadata.add(
            MetadataKey::from("x-grpc-test-echo-trailing-bin"),
            Bytes::from(&b"\xab\xab\xab"[..]),
        );
        options
    }

    fn assert_result_metadata(initial: grpc::Metadata, trailing: grpc::Metadata) {
        assert_eq!(
            Some(&b"test_initial_metadata_value"[..]),
            initial.get("x-grpc-test-echo-initial")
        );
        assert_eq!(
            Some(&b"\xab\xab\xab"[..]),
            trailing.get("x-grpc-test-echo-trailing-bin")
        );
    }

    {
        // to a UnaryCall with request:
        // {
        //   response_size: 314159
        //   payload:{
        //     body: 271828 bytes of zeros
        //   }
        // }
        let mut req = SimpleRequest::new();
        req.set_response_size(314159);
        let mut payload = Payload::new();
        payload.set_body(vec![0; 271828]);
        req.set_payload(payload);
        let (initial, _result, trailing) = client
            .unary_call(make_options(), req)
            .wait()
            .expect("UnaryCall");

        assert_result_metadata(initial, trailing);
    }

    {
        // to a FullDuplexCall with request:
        // {
        //   response_parameters:{
        //     size: 314159
        //   }
        //   payload:{
        //     body: 271828 bytes of zeros
        //   }
        // }
        let mut req = StreamingOutputCallRequest::new();
        {
            let mut rp = ResponseParameters::new();
            rp.set_size(314159);
            req.mut_response_parameters().push(rp);
        }
        {
            let mut p = Payload::new();
            p.set_body(vec![0; 271828]);
            req.set_payload(p);
        }

        let (initial, _res, trailing) = client
            .full_duplex_call(make_options(), grpc::StreamingRequest::single(req))
            .collect()
            .wait()
            .expect("FullDuplexCall");

        assert_result_metadata(initial, trailing);
    }
}

// The flags we use are defined in the gRPC Interopability doc
// https://github.com/grpc/grpc/blob/master/doc/interop-test-descriptions.md
fn main() {
    env_logger::init().expect("env_logger::init");

    let options = App::new("gRPC interopability client")
        .version("0.1")
        .author("Steve Jenson <stevej@buoyant.io>")
        .about("Interoperability Test Client for grpc-rust")
        .arg(
            Arg::with_name("server_host")
                .long("server_host")
                .help("The server host to connect to. For example, \"localhost\" or \"127.0.0.1\"")
                .takes_value(true),
        ).arg(
            Arg::with_name("server_host_override")
                .long("server_host_override")
                .help(
                    "The server host to claim to be connecting to, for use in TLS and HTTP/2 \
                     :authority header. If unspecified, the value of --server_host will be used",
                ).takes_value(true),
        ).arg(
            Arg::with_name("server_port")
                .long("server_port")
                .help("The server port to connect to. For example, \"8080\"")
                .takes_value(true),
        ).arg(
            Arg::with_name("test_case")
                .long("test_case")
                .help("The name of the test case to execute. For example, \"empty_unary\"")
                .takes_value(true),
        ).arg(
            Arg::with_name("use_tls") // boolean
                .long("use_tls")
                .help("Whether to use a plaintext or encrypted connection")
                .takes_value(true),
        ).arg(
            Arg::with_name("use_test_ca")
                .long("use_test_ca")
                .help("Whether to replace platform root CAs with ca.pem as the CA root")
                .takes_value(true),
        ).arg(
            Arg::with_name("default_service_account")
                .long("default_service_account")
                .help("Email of the GCE default service account.")
                .takes_value(true),
        ).arg(
            Arg::with_name("oauth_scope")
                .long("oauth_scope")
                .help("OAuth scope. For example, \"https://www.googleapis.com/auth/xapi.zoo\"")
                .takes_value(true),
        ).arg(
            Arg::with_name("service_account_key_file")
                .long("service_account_key_file")
                .help(
                    "The path to the service account JSON key file generated from GCE developer \
                     console.",
                ).takes_value(true),
        ).get_matches();

    let hostname = options.value_of("server_host").unwrap_or("localhost");
    let serverport = options
        .value_of("server_port")
        .map(|s| s.parse().unwrap())
        .unwrap_or(DEFAULT_PORT);

    let client =
        TestServiceClient::new_plain(hostname, serverport, Default::default()).expect("init");

    let testcase = options.value_of("test_case").unwrap_or("");
    match testcase.as_ref() {
        "empty_unary" => empty_unary(client),
        "cacheable_unary" => panic!("cacheable_unary not done yet"),
        "large_unary" => large_unary(client),
        "client_compressed_unary" => panic!("client_compressed_unary not done yet"),
        "server_compressed_unary" => panic!("server_compressed_unary not done yet"),
        "client_streaming" => client_streaming(client),
        "client_compressed_streaming" => panic!("client_compressed_streaming not done yet"),
        "server_streaming" => server_streaming(client),
        "server_compressed_streaming" => panic!("server_compressed_streaming not done yet"),
        "ping_pong" => ping_pong(client),
        "empty_stream" => empty_stream(client),
        "compute_engine_creds" => panic!("compute_engine_creds not done yet"),
        "jwt_token_creds" => panic!("jwt_token_creds not done yet"),
        "oauth2_auth_token" => panic!("oauth2_auth_token not done yet"),
        "per_rpc_creds" => panic!("per_rpc_creds not done yet"),
        "custom_metadata" => custom_metadata(client),
        "status_code_and_message" => panic!("status_code_and_message not done yet"),
        "unimplemented_method" => panic!("unimplemented_method not done yet"),
        "unimplemented_service" => panic!("unimplemented_service not done yet"),
        "cancel_after_begin" => panic!("cancel_after_begin not done yet"),
        "cancel_after_first_response" => panic!("cancel_after_first_response not done yet"),
        "timeout_on_sleeping_server" => panic!("timeout_on_sleeping_server not done yet"),
        _ => panic!("no test_case specified"),
    }
}
<|endoftext|>


<commit_before>use crate::instructions::*;

use std::collections::VecDeque;

fn scan_for_trust_me(code: &Code, jmp_offsets: &mut VecDeque<usize>, after_idx: &mut usize) {
    for (idx, instr) in code[*after_idx..].iter().enumerate() {
        match instr {
            &Line::Choice(ChoiceInstruction::TrustMe)
          | &Line::IndexedChoice(IndexedChoiceInstruction::Trust(..)) => {
                *after_idx += idx;
                return;
            }
            &Line::Control(ControlInstruction::JmpBy(_, offset, ..)) => {
                jmp_offsets.push_back(*after_idx + idx + offset)
            }
            _ => {}
        }
    }
}

fn capture_next_range(code: &Code, queue: &mut VecDeque<usize>, last_idx: &mut usize) {
    loop {
        match &code[*last_idx] {
            &Line::Choice(ChoiceInstruction::TryMeElse(..))
          | &Line::IndexedChoice(IndexedChoiceInstruction::Try(..)) => {
                    *last_idx += 1;
                    scan_for_trust_me(code, queue, last_idx);
                }
            &Line::Control(ControlInstruction::JmpBy(_, offset, _, false)) => {
                queue.push_back(*last_idx + offset);
                *last_idx += 1;
            }
            &Line::Control(ControlInstruction::JmpBy(_, offset, _, true)) => {
                queue.push_back(*last_idx + offset);
                break;
            }
            &Line::Control(ControlInstruction::Proceed)
          | &Line::Control(ControlInstruction::CallClause(_, _, _, true, _)) =>
                break,
            _ =>
                *last_idx += 1,
        };
    }
}

/* This function walks the code of a single predicate, supposed to
 * begin in code at the offset p. Each instruction is passed to the
 * walker function.
 */
pub fn walk_code(code: &Code, p: usize, mut walker: impl FnMut(&Line))
{
    let mut queue = VecDeque::from(vec![p]);

    while let Some(first_idx) = queue.pop_front() {
        let mut last_idx = first_idx;

        capture_next_range(code, &mut queue, &mut last_idx);

        for instr in &code[first_idx .. last_idx + 1] {
            walker(instr);
        }
    }
}

/* A function for code walking that might result in modification to
 * the code. Otherwise identical to walk_code.
 */
pub fn walk_code_mut(code: &mut Code, p: usize, mut walker: impl FnMut(&mut Line))
{
    let mut queue = VecDeque::from(vec![p]);

    while let Some(first_idx) = queue.pop_front() {
        let mut last_idx = first_idx;

        capture_next_range(code, &mut queue, &mut last_idx);

        for instr in &mut code[first_idx .. last_idx + 1] {
            walker(instr);
        }
    }
}
<commit_msg>fix bug in code walker causing entire clauses to be skipped (#736)<commit_after>use crate::instructions::*;

use std::collections::VecDeque;

fn scan_for_trust_me(
    code: &Code,
    jmp_offsets: &mut VecDeque<usize>,
    before_idx: usize,
    after_idx: &mut usize,
) {
    // record the location of the line after the TrustMe capping the
    // choice instruction sequence to after_idx.
    loop {
        match &code[*after_idx] {
            &Line::Choice(ChoiceInstruction::DefaultRetryMeElse(offset)) |
            &Line::Choice(ChoiceInstruction::RetryMeElse(offset)) |
            &Line::IndexedChoice(IndexedChoiceInstruction::Retry(offset)) => {
                *after_idx += offset;
            }
            &Line::Choice(ChoiceInstruction::DefaultTrustMe) |
            &Line::Choice(ChoiceInstruction::TrustMe) |
            &Line::IndexedChoice(IndexedChoiceInstruction::Trust(..)) => {
                break;
            }
            _ => {
                *after_idx += 1;
            }
        }
    }

    // search the code in the range for JmpBy instructions and record their
    // offsets for future scanning.
    for (idx, instr) in code[before_idx .. *after_idx].iter().enumerate() {
        match instr {
            &Line::Control(ControlInstruction::JmpBy(_, offset, ..)) => {
                jmp_offsets.push_back(before_idx + idx + offset)
            }
            _ => {
            }
        }
    }

    *after_idx += 1;
}

fn capture_next_range(code: &Code, queue: &mut VecDeque<usize>, last_idx: &mut usize) {
    loop {
        match &code[*last_idx] {
            &Line::Choice(ChoiceInstruction::TryMeElse(offset)) |
            &Line::IndexedChoice(IndexedChoiceInstruction::Try(offset)) => {
                let before_idx = *last_idx;
                *last_idx += offset;

                scan_for_trust_me(code, queue, before_idx, last_idx);
            }
            &Line::Control(ControlInstruction::JmpBy(_, offset, _, false)) => {
                queue.push_back(*last_idx + offset);
                *last_idx += 1;
            }
            &Line::Control(ControlInstruction::JmpBy(_, offset, _, true)) => {
                queue.push_back(*last_idx + offset);
                break;
            }
            &Line::Control(ControlInstruction::Proceed) |
            &Line::Control(ControlInstruction::CallClause(_, _, _, true, _)) =>
                break,
            _ =>
                *last_idx += 1,
        };
    }
}

/* This function walks the code of a single predicate, supposed to
 * begin in code at the offset p. Each instruction is passed to the
 * walker function.
 */
pub fn walk_code(code: &Code, p: usize, mut walker: impl FnMut(&Line))
{
    let mut queue = VecDeque::from(vec![p]);

    while let Some(first_idx) = queue.pop_front() {
        let mut last_idx = first_idx;

        capture_next_range(code, &mut queue, &mut last_idx);

        for instr in &code[first_idx .. last_idx + 1] {
            walker(instr);
        }
    }
}

/* A function for code walking that might result in modification to
 * the code. Otherwise identical to walk_code.
 */
pub fn walk_code_mut(code: &mut Code, p: usize, mut walker: impl FnMut(&mut Line))
{
    let mut queue = VecDeque::from(vec![p]);

    while let Some(first_idx) = queue.pop_front() {
        let mut last_idx = first_idx;

        capture_next_range(code, &mut queue, &mut last_idx);

        for instr in &mut code[first_idx .. last_idx + 1] {
            walker(instr);
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

use crate::{
    auth::with_auth,
    constants::MAX_CONTENT_LENGTH,
    context::Context,
    errors::{MetricsIngestError, ServiceError},
    metrics::METRICS_INGEST_BACKEND_REQUEST_DURATION,
    types::{auth::Claims, common::NodeType},
};
use crate::{debug, error};
use aptos_types::chain_id::ChainId;
use reqwest::{header::CONTENT_ENCODING, StatusCode};
use tokio::time::Instant;
use warp::{filters::BoxedFilter, hyper::body::Bytes, reject, reply, Filter, Rejection, Reply};

/// TODO: Cleanup after v1 API is ramped up
pub fn metrics_ingest_legacy(context: Context) -> BoxedFilter<(impl Reply,)> {
    warp::path!("push-metrics")
        .and(warp::post())
        .and(context.clone().filter())
        .and(with_auth(
            context,
            vec![
                NodeType::Validator,
                NodeType::ValidatorFullNode,
                NodeType::PublicFullNode,
            ],
        ))
        .and(warp::header::optional(CONTENT_ENCODING.as_str()))
        .and(warp::body::content_length_limit(MAX_CONTENT_LENGTH))
        .and(warp::body::bytes())
        .and_then(handle_metrics_ingest)
        .boxed()
}

pub fn metrics_ingest(context: Context) -> BoxedFilter<(impl Reply,)> {
    warp::path!("ingest" / "metrics")
        .and(warp::post())
        .and(context.clone().filter())
        .and(with_auth(
            context,
            vec![
                NodeType::Validator,
                NodeType::ValidatorFullNode,
                NodeType::PublicFullNode,
            ],
        ))
        .and(warp::header::optional(CONTENT_ENCODING.as_str()))
        .and(warp::body::content_length_limit(MAX_CONTENT_LENGTH))
        .and(warp::body::bytes())
        .and_then(handle_metrics_ingest)
        .boxed()
}

pub async fn handle_metrics_ingest(
    context: Context,
    claims: Claims,
    encoding: Option<String>,
    metrics_body: Bytes,
) -> anyhow::Result<impl Reply, Rejection> {
    debug!("handling prometheus metrics ingest");

    let extra_labels = claims_to_extra_labels(
        &claims,
        context
            .peer_identities()
            .get(&claims.chain_id)
            .and_then(|peers| peers.get(&claims.peer_id)),
    );

    let start_timer = Instant::now();

    let filtered_clients = context.metrics_client().iter().filter(|(name, _)| {
        if claims.chain_id.id() == 3 {
            return true;
        } else if claims.chain_id == ChainId::mainnet() {
            return !name.starts_with("default");
        }
        name.starts_with("default")
    });

    let post_futures = filtered_clients.clone().map(|(_, client)| {
        client.post_prometheus_metrics(
            metrics_body.clone(),
            extra_labels.clone(),
            encoding.clone().unwrap_or_default(),
        )
    });

    let results = futures::future::join_all(post_futures)
        .await
        .into_iter()
        .zip(filtered_clients.map(|(name, _)| name))
        .map(|(res, name)| {
            match res {
                Ok(res) => {
                    METRICS_INGEST_BACKEND_REQUEST_DURATION
                        .with_label_values(&[name, res.status().as_str()])
                        .observe(start_timer.elapsed().as_secs_f64());
                    if res.status().is_success() {
                        debug!("remote write to victoria metrics succeeded");
                    } else {
                        error!(
                            "remote write failed to victoria_metrics for client {}: {}",
                            name,
                            res.error_for_status().err().unwrap()
                        );
                        return Err(());
                    }
                }
                Err(err) => {
                    METRICS_INGEST_BACKEND_REQUEST_DURATION
                        .with_label_values(&[name, "Unknown"])
                        .observe(start_timer.elapsed().as_secs_f64());
                    error!(
                        "error sending remote write request for client {}: {}",
                        name, err
                    );
                    return Err(());
                }
            }
            Ok(())
        });

    #[allow(clippy::unnecessary_fold)]
    if results.fold(true, |acc, r| acc && r.is_err()) {
        return Err(reject::custom(ServiceError::internal(
            MetricsIngestError::IngestionError.into(),
        )));
    }

    Ok(reply::with_status(reply::reply(), StatusCode::CREATED))
}

fn claims_to_extra_labels(claims: &Claims, common_name: Option<&String>) -> Vec<String> {
    let chain_name = if claims.chain_id.id() == 3 {
        format!("chain_name={}", claims.chain_id.id())
    } else {
        format!("chain_name={}", claims.chain_id)
    };
    let pod_name = if let Some(common_name) = common_name {
        format!(
            "kubernetes_pod_name=peer_id:{}//{}",
            common_name,
            claims.peer_id.to_hex_literal()
        )
    } else {
        // for community nodes we cannot determine which pod name they run in (or whether they run in k8s at all),
        // so we use the peer id as an approximation/replacement for pod_name
        // This works well with our existing grafana dashboards
        format!(
            "kubernetes_pod_name=peer_id:{}",
            claims.peer_id.to_hex_literal()
        )
    };
    vec![
        format!("role={}", claims.node_type),
        chain_name,
        format!("namespace={}", "telemetry-service"),
        pod_name,
    ]
}

#[cfg(test)]
mod test {
    use std::str::FromStr;

    use crate::tests::test_context;
    use crate::MetricsClient;

    use super::*;
    use aptos_types::{chain_id::ChainId, PeerId};
    use httpmock::MockServer;
    use reqwest::Url;

    #[test]
    fn verify_labels() {
        let claims = claims_to_extra_labels(
            &super::Claims {
                chain_id: ChainId::new(25),
                peer_id: PeerId::from_str("0x1").unwrap(),
                node_type: NodeType::Validator,
                epoch: 3,
                exp: 123,
                iat: 123,
            },
            Some(&String::from("test_name")),
        );
        assert_eq!(
            claims,
            vec![
                "role=validator",
                "chain_name=25",
                "namespace=telemetry-service",
                "kubernetes_pod_name=peer_id:test_name//0x1",
            ]
        );

        let claims = claims_to_extra_labels(
            &super::Claims {
                chain_id: ChainId::new(25),
                peer_id: PeerId::from_str("0x1").unwrap(),
                node_type: NodeType::Validator,
                epoch: 3,
                exp: 123,
                iat: 123,
            },
            None,
        );
        assert_eq!(
            claims,
            vec![
                "role=validator",
                "chain_name=25",
                "namespace=telemetry-service",
                "kubernetes_pod_name=peer_id:0x1",
            ]
        );
    }

    #[tokio::test]
    async fn test_metrics_ingest_all_success() {
        let mut test_context = test_context::new_test_context().await;
        let claims = Claims::test();
        let body = Bytes::from_static(b"hello");

        let server1 = MockServer::start();
        let mock1 = server1.mock(|when, then| {
            when.method("POST").path("/api/v1/import/prometheus");
            then.status(200);
        });

        let server2 = MockServer::start();
        let mock2 = server2.mock(|when, then| {
            when.method("POST").path("/api/v1/import/prometheus");
            then.status(200);
        });

        let clients = test_context.inner.metrics_client_mut();
        clients.insert(
            "default1".into(),
            MetricsClient::new(Url::parse(&server1.base_url()).unwrap(), "token1".into()),
        );
        clients.insert(
            "default2".into(),
            MetricsClient::new(Url::parse(&server2.base_url()).unwrap(), "token2".into()),
        );

        let result =
            handle_metrics_ingest(test_context.inner, claims, Some("gzip".into()), body).await;

        mock1.assert();
        mock2.assert();
        assert!(result.is_ok());
    }

    #[tokio::test]
    async fn test_metrics_ingest_partial_success() {
        let mut test_context = test_context::new_test_context().await;
        let claims = Claims::test();
        let body = Bytes::from_static(b"hello");

        let server1 = MockServer::start();
        let mock1 = server1.mock(|when, then| {
            when.method("POST").path("/api/v1/import/prometheus");
            then.status(200);
        });

        let server2 = MockServer::start();
        let mock2 = server2.mock(|when, then| {
            when.method("POST").path("/api/v1/import/prometheus");
            then.status(500);
        });

        let clients = test_context.inner.metrics_client_mut();
        clients.insert(
            "default1".into(),
            MetricsClient::new(Url::parse(&server1.base_url()).unwrap(), "token1".into()),
        );
        clients.insert(
            "default2".into(),
            MetricsClient::new(Url::parse(&server2.base_url()).unwrap(), "token2".into()),
        );

        let result =
            handle_metrics_ingest(test_context.inner, claims, Some("gzip".into()), body).await;

        mock1.assert();
        mock2.assert_hits(4);
        assert!(result.is_ok());
    }

    #[tokio::test]
    async fn test_metrics_ingest_all_failure() {
        let mut test_context = test_context::new_test_context().await;
        let claims = Claims::test();
        let body = Bytes::from_static(b"hello");

        let server1 = MockServer::start();
        let mock1 = server1.mock(|when, then| {
            when.method("POST").path("/api/v1/import/prometheus");
            then.status(500);
        });

        let server2 = MockServer::start();
        let mock2 = server2.mock(|when, then| {
            when.method("POST").path("/api/v1/import/prometheus");
            then.status(401);
        });

        let clients = test_context.inner.metrics_client_mut();
        clients.insert(
            "default1".into(),
            MetricsClient::new(Url::parse(&server1.base_url()).unwrap(), "token1".into()),
        );
        clients.insert(
            "default2".into(),
            MetricsClient::new(Url::parse(&server2.base_url()).unwrap(), "token2".into()),
        );

        let result =
            handle_metrics_ingest(test_context.inner, claims, Some("gzip".into()), body).await;

        mock1.assert_hits(4);
        mock2.assert();
        assert!(result.is_err());
    }
}
<commit_msg>introduce metrics_source label in telemetry service<commit_after>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

use crate::{
    auth::with_auth,
    constants::MAX_CONTENT_LENGTH,
    context::Context,
    errors::{MetricsIngestError, ServiceError},
    metrics::METRICS_INGEST_BACKEND_REQUEST_DURATION,
    types::{auth::Claims, common::NodeType},
};
use crate::{debug, error};
use aptos_types::chain_id::ChainId;
use reqwest::{header::CONTENT_ENCODING, StatusCode};
use tokio::time::Instant;
use warp::{filters::BoxedFilter, hyper::body::Bytes, reject, reply, Filter, Rejection, Reply};

/// TODO: Cleanup after v1 API is ramped up
pub fn metrics_ingest_legacy(context: Context) -> BoxedFilter<(impl Reply,)> {
    warp::path!("push-metrics")
        .and(warp::post())
        .and(context.clone().filter())
        .and(with_auth(
            context,
            vec![
                NodeType::Validator,
                NodeType::ValidatorFullNode,
                NodeType::PublicFullNode,
            ],
        ))
        .and(warp::header::optional(CONTENT_ENCODING.as_str()))
        .and(warp::body::content_length_limit(MAX_CONTENT_LENGTH))
        .and(warp::body::bytes())
        .and_then(handle_metrics_ingest)
        .boxed()
}

pub fn metrics_ingest(context: Context) -> BoxedFilter<(impl Reply,)> {
    warp::path!("ingest" / "metrics")
        .and(warp::post())
        .and(context.clone().filter())
        .and(with_auth(
            context,
            vec![
                NodeType::Validator,
                NodeType::ValidatorFullNode,
                NodeType::PublicFullNode,
            ],
        ))
        .and(warp::header::optional(CONTENT_ENCODING.as_str()))
        .and(warp::body::content_length_limit(MAX_CONTENT_LENGTH))
        .and(warp::body::bytes())
        .and_then(handle_metrics_ingest)
        .boxed()
}

pub async fn handle_metrics_ingest(
    context: Context,
    claims: Claims,
    encoding: Option<String>,
    metrics_body: Bytes,
) -> anyhow::Result<impl Reply, Rejection> {
    debug!("handling prometheus metrics ingest");

    let extra_labels = claims_to_extra_labels(
        &claims,
        context
            .peer_identities()
            .get(&claims.chain_id)
            .and_then(|peers| peers.get(&claims.peer_id)),
    );

    let start_timer = Instant::now();

    let filtered_clients = context.metrics_client().iter().filter(|(name, _)| {
        if claims.chain_id.id() == 3 {
            return true;
        } else if claims.chain_id == ChainId::mainnet() {
            return !name.starts_with("default");
        }
        name.starts_with("default")
    });

    let post_futures = filtered_clients.clone().map(|(_, client)| {
        client.post_prometheus_metrics(
            metrics_body.clone(),
            extra_labels.clone(),
            encoding.clone().unwrap_or_default(),
        )
    });

    let results = futures::future::join_all(post_futures)
        .await
        .into_iter()
        .zip(filtered_clients.map(|(name, _)| name))
        .map(|(res, name)| {
            match res {
                Ok(res) => {
                    METRICS_INGEST_BACKEND_REQUEST_DURATION
                        .with_label_values(&[name, res.status().as_str()])
                        .observe(start_timer.elapsed().as_secs_f64());
                    if res.status().is_success() {
                        debug!("remote write to victoria metrics succeeded");
                    } else {
                        error!(
                            "remote write failed to victoria_metrics for client {}: {}",
                            name,
                            res.error_for_status().err().unwrap()
                        );
                        return Err(());
                    }
                }
                Err(err) => {
                    METRICS_INGEST_BACKEND_REQUEST_DURATION
                        .with_label_values(&[name, "Unknown"])
                        .observe(start_timer.elapsed().as_secs_f64());
                    error!(
                        "error sending remote write request for client {}: {}",
                        name, err
                    );
                    return Err(());
                }
            }
            Ok(())
        });

    #[allow(clippy::unnecessary_fold)]
    if results.fold(true, |acc, r| acc && r.is_err()) {
        return Err(reject::custom(ServiceError::internal(
            MetricsIngestError::IngestionError.into(),
        )));
    }

    Ok(reply::with_status(reply::reply(), StatusCode::CREATED))
}

fn claims_to_extra_labels(claims: &Claims, common_name: Option<&String>) -> Vec<String> {
    let chain_name = if claims.chain_id.id() == 3 {
        format!("chain_name={}", claims.chain_id.id())
    } else {
        format!("chain_name={}", claims.chain_id)
    };
    let pod_name = if let Some(common_name) = common_name {
        format!(
            "kubernetes_pod_name=peer_id:{}//{}",
            common_name,
            claims.peer_id.to_hex_literal()
        )
    } else {
        // for community nodes we cannot determine which pod name they run in (or whether they run in k8s at all),
        // so we use the peer id as an approximation/replacement for pod_name
        // This works well with our existing grafana dashboards
        format!(
            "kubernetes_pod_name=peer_id:{}",
            claims.peer_id.to_hex_literal()
        )
    };
    vec![
        format!("role={}", claims.node_type),
        format!("metrics_source={}", "telemetry-service"),
        chain_name,
        format!("namespace={}", "telemetry-service"),
        pod_name,
    ]
}

#[cfg(test)]
mod test {
    use std::str::FromStr;

    use crate::tests::test_context;
    use crate::MetricsClient;

    use super::*;
    use aptos_types::{chain_id::ChainId, PeerId};
    use httpmock::MockServer;
    use reqwest::Url;

    #[test]
    fn verify_labels() {
        let claims = claims_to_extra_labels(
            &super::Claims {
                chain_id: ChainId::new(25),
                peer_id: PeerId::from_str("0x1").unwrap(),
                node_type: NodeType::Validator,
                epoch: 3,
                exp: 123,
                iat: 123,
            },
            Some(&String::from("test_name")),
        );
        assert_eq!(
            claims,
            vec![
                "role=validator",
                "metrics_source=telemetry-service",
                "chain_name=25",
                "namespace=telemetry-service",
                "kubernetes_pod_name=peer_id:test_name//0x1",
            ]
        );

        let claims = claims_to_extra_labels(
            &super::Claims {
                chain_id: ChainId::new(25),
                peer_id: PeerId::from_str("0x1").unwrap(),
                node_type: NodeType::Validator,
                epoch: 3,
                exp: 123,
                iat: 123,
            },
            None,
        );
        assert_eq!(
            claims,
            vec![
                "role=validator",
                "metrics_source=telemetry-service",
                "chain_name=25",
                "namespace=telemetry-service",
                "kubernetes_pod_name=peer_id:0x1",
            ]
        );
    }

    #[tokio::test]
    async fn test_metrics_ingest_all_success() {
        let mut test_context = test_context::new_test_context().await;
        let claims = Claims::test();
        let body = Bytes::from_static(b"hello");

        let server1 = MockServer::start();
        let mock1 = server1.mock(|when, then| {
            when.method("POST").path("/api/v1/import/prometheus");
            then.status(200);
        });

        let server2 = MockServer::start();
        let mock2 = server2.mock(|when, then| {
            when.method("POST").path("/api/v1/import/prometheus");
            then.status(200);
        });

        let clients = test_context.inner.metrics_client_mut();
        clients.insert(
            "default1".into(),
            MetricsClient::new(Url::parse(&server1.base_url()).unwrap(), "token1".into()),
        );
        clients.insert(
            "default2".into(),
            MetricsClient::new(Url::parse(&server2.base_url()).unwrap(), "token2".into()),
        );

        let result =
            handle_metrics_ingest(test_context.inner, claims, Some("gzip".into()), body).await;

        mock1.assert();
        mock2.assert();
        assert!(result.is_ok());
    }

    #[tokio::test]
    async fn test_metrics_ingest_partial_success() {
        let mut test_context = test_context::new_test_context().await;
        let claims = Claims::test();
        let body = Bytes::from_static(b"hello");

        let server1 = MockServer::start();
        let mock1 = server1.mock(|when, then| {
            when.method("POST").path("/api/v1/import/prometheus");
            then.status(200);
        });

        let server2 = MockServer::start();
        let mock2 = server2.mock(|when, then| {
            when.method("POST").path("/api/v1/import/prometheus");
            then.status(500);
        });

        let clients = test_context.inner.metrics_client_mut();
        clients.insert(
            "default1".into(),
            MetricsClient::new(Url::parse(&server1.base_url()).unwrap(), "token1".into()),
        );
        clients.insert(
            "default2".into(),
            MetricsClient::new(Url::parse(&server2.base_url()).unwrap(), "token2".into()),
        );

        let result =
            handle_metrics_ingest(test_context.inner, claims, Some("gzip".into()), body).await;

        mock1.assert();
        mock2.assert_hits(4);
        assert!(result.is_ok());
    }

    #[tokio::test]
    async fn test_metrics_ingest_all_failure() {
        let mut test_context = test_context::new_test_context().await;
        let claims = Claims::test();
        let body = Bytes::from_static(b"hello");

        let server1 = MockServer::start();
        let mock1 = server1.mock(|when, then| {
            when.method("POST").path("/api/v1/import/prometheus");
            then.status(500);
        });

        let server2 = MockServer::start();
        let mock2 = server2.mock(|when, then| {
            when.method("POST").path("/api/v1/import/prometheus");
            then.status(401);
        });

        let clients = test_context.inner.metrics_client_mut();
        clients.insert(
            "default1".into(),
            MetricsClient::new(Url::parse(&server1.base_url()).unwrap(), "token1".into()),
        );
        clients.insert(
            "default2".into(),
            MetricsClient::new(Url::parse(&server2.base_url()).unwrap(), "token2".into()),
        );

        let result =
            handle_metrics_ingest(test_context.inner, claims, Some("gzip".into()), body).await;

        mock1.assert_hits(4);
        mock2.assert();
        assert!(result.is_err());
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use servo_msg::compositor_msg::LayerBuffer;
use font_context::FontContext;
use geometry::Au;
use opts::Opts;

use azure::azure_hl::{B8G8R8A8, Color, ColorPattern, DrawOptions};
use azure::azure_hl::{DrawSurfaceOptions, DrawTarget, Linear, StrokeOptions};
use azure::AzFloat;
use std::libc::types::common::c99::uint16_t;
use geom::point::Point2D;
use geom::rect::Rect;
use geom::size::Size2D;
use geom::side_offsets::SideOffsets2D;
use servo_net::image::base::Image;
use extra::arc::Arc;

pub struct RenderContext<'self> {
    canvas: &'self ~LayerBuffer,
    font_ctx: @mut FontContext,
    opts: &'self Opts
}

impl<'self> RenderContext<'self>  {
    pub fn get_draw_target(&self) -> &'self DrawTarget {
        &self.canvas.draw_target
    }

    pub fn draw_solid_color(&self, bounds: &Rect<Au>, color: Color) {
        self.canvas.draw_target.make_current();
        self.canvas.draw_target.fill_rect(&bounds.to_azure_rect(), &ColorPattern(color));
    }

    pub fn draw_border(&self,
                       bounds: &Rect<Au>,
                       border: SideOffsets2D<Au>,
                       color: SideOffsets2D<Color>) {
        let draw_opts = DrawOptions(1 as AzFloat, 0 as uint16_t);
        let stroke_fields = 2; // CAP_SQUARE
        let mut stroke_opts = StrokeOptions(0 as AzFloat, 10 as AzFloat, stroke_fields);

        let rect = bounds.to_azure_rect();
        let border = border.to_float_px();

        self.canvas.draw_target.make_current();

        // draw top border
        stroke_opts.line_width = border.top;
        let y = rect.origin.y + border.top * 0.5;
        let start = Point2D(rect.origin.x, y);
        let end = Point2D(rect.origin.x + rect.size.width, y);
        self.canvas.draw_target.stroke_line(start, end, &ColorPattern(color.top), &stroke_opts, &draw_opts);

        // draw bottom border
        stroke_opts.line_width = border.bottom;
        let y = rect.origin.y + rect.size.height - border.bottom * 0.5;
        let start = Point2D(rect.origin.x, y);
        let end = Point2D(rect.origin.x + rect.size.width, y);
        self.canvas.draw_target.stroke_line(start, end, &ColorPattern(color.bottom), &stroke_opts, &draw_opts);

        // draw left border
        stroke_opts.line_width = border.left;
        let x = rect.origin.x + border.left * 0.5;
        let start = Point2D(x, rect.origin.y);
        let end = Point2D(x, rect.origin.y + rect.size.height);
        self.canvas.draw_target.stroke_line(start, end, &ColorPattern(color.left), &stroke_opts, &draw_opts);

        // draw right border
        stroke_opts.line_width = border.right;
        let x = rect.origin.x + rect.size.width - border.right * 0.5;
        let start = Point2D(x, rect.origin.y);
        let end = Point2D(x, rect.origin.y + rect.size.height);
        self.canvas.draw_target.stroke_line(start, end, &ColorPattern(color.right), &stroke_opts, &draw_opts);
    }

    pub fn draw_image(&self, bounds: Rect<Au>, image: Arc<~Image>) {
        let image = image.get();
        let size = Size2D(image.width as i32, image.height as i32);
        let stride = image.width * 4;

        self.canvas.draw_target.make_current();
        let draw_target_ref = &self.canvas.draw_target;
        let azure_surface = draw_target_ref.create_source_surface_from_data(image.data, size,
                                                                            stride as i32, B8G8R8A8);
        let source_rect = Rect(Point2D(0 as AzFloat, 0 as AzFloat),
                               Size2D(image.width as AzFloat, image.height as AzFloat));
        let dest_rect = bounds.to_azure_rect();
        let draw_surface_options = DrawSurfaceOptions(Linear, true);
        let draw_options = DrawOptions(1.0f as AzFloat, 0);
        draw_target_ref.draw_surface(azure_surface,
                                     dest_rect,
                                     source_rect,
                                     draw_surface_options,
                                     draw_options);
    }

    pub fn clear(&self) {
        let pattern = ColorPattern(Color(1.0, 1.0, 1.0, 1.0));
        let rect = Rect(Point2D(self.canvas.rect.origin.x as AzFloat,
                                self.canvas.rect.origin.y as AzFloat),
                        Size2D(self.canvas.screen_pos.size.width as AzFloat,
                               self.canvas.screen_pos.size.height as AzFloat));
        self.canvas.draw_target.make_current();
        self.canvas.draw_target.fill_rect(&rect, &pattern);
    }
}

trait to_float {
    fn to_float(&self) -> float;
}

impl to_float for u8 {
    fn to_float(&self) -> float {
        (*self as float) / 255f
    }
}

trait ToAzureRect {
    fn to_azure_rect(&self) -> Rect<AzFloat>;
}

impl ToAzureRect for Rect<Au> {
    fn to_azure_rect(&self) -> Rect<AzFloat> {
        Rect(Point2D(self.origin.x.to_px() as AzFloat, self.origin.y.to_px() as AzFloat),
             Size2D(self.size.width.to_px() as AzFloat, self.size.height.to_px() as AzFloat))
    }
}

trait ToSideOffsetsPx {
    fn to_float_px(&self) -> SideOffsets2D<AzFloat>;
}

impl ToSideOffsetsPx for SideOffsets2D<Au> {
    fn to_float_px(&self) -> SideOffsets2D<AzFloat> {
        SideOffsets2D::new(self.top.to_px() as AzFloat,
                           self.right.to_px() as AzFloat,
                           self.bottom.to_px() as AzFloat,
                           self.left.to_px() as AzFloat)
    }
}
<commit_msg>Fix draw-order for border.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use servo_msg::compositor_msg::LayerBuffer;
use font_context::FontContext;
use geometry::Au;
use opts::Opts;

use azure::azure_hl::{B8G8R8A8, Color, ColorPattern, DrawOptions};
use azure::azure_hl::{DrawSurfaceOptions, DrawTarget, Linear, StrokeOptions};
use azure::AzFloat;
use std::libc::types::common::c99::uint16_t;
use geom::point::Point2D;
use geom::rect::Rect;
use geom::size::Size2D;
use geom::side_offsets::SideOffsets2D;
use servo_net::image::base::Image;
use extra::arc::Arc;

pub struct RenderContext<'self> {
    canvas: &'self ~LayerBuffer,
    font_ctx: @mut FontContext,
    opts: &'self Opts
}

impl<'self> RenderContext<'self>  {
    pub fn get_draw_target(&self) -> &'self DrawTarget {
        &self.canvas.draw_target
    }

    pub fn draw_solid_color(&self, bounds: &Rect<Au>, color: Color) {
        self.canvas.draw_target.make_current();
        self.canvas.draw_target.fill_rect(&bounds.to_azure_rect(), &ColorPattern(color));
    }

    pub fn draw_border(&self,
                       bounds: &Rect<Au>,
                       border: SideOffsets2D<Au>,
                       color: SideOffsets2D<Color>) {
        let draw_opts = DrawOptions(1 as AzFloat, 0 as uint16_t);
        let stroke_fields = 2; // CAP_SQUARE
        let mut stroke_opts = StrokeOptions(0 as AzFloat, 10 as AzFloat, stroke_fields);

        let rect = bounds.to_azure_rect();
        let border = border.to_float_px();

        self.canvas.draw_target.make_current();

        // draw top border
        stroke_opts.line_width = border.top;
        let y = rect.origin.y + border.top * 0.5;
        let start = Point2D(rect.origin.x, y);
        let end = Point2D(rect.origin.x + rect.size.width, y);
        self.canvas.draw_target.stroke_line(start, end, &ColorPattern(color.top), &stroke_opts, &draw_opts);

        // draw right border
        stroke_opts.line_width = border.right;
        let x = rect.origin.x + rect.size.width - border.right * 0.5;
        let start = Point2D(x, rect.origin.y);
        let end = Point2D(x, rect.origin.y + rect.size.height);
        self.canvas.draw_target.stroke_line(start, end, &ColorPattern(color.right), &stroke_opts, &draw_opts);

        // draw bottom border
        stroke_opts.line_width = border.bottom;
        let y = rect.origin.y + rect.size.height - border.bottom * 0.5;
        let start = Point2D(rect.origin.x, y);
        let end = Point2D(rect.origin.x + rect.size.width, y);
        self.canvas.draw_target.stroke_line(start, end, &ColorPattern(color.bottom), &stroke_opts, &draw_opts);

        // draw left border
        stroke_opts.line_width = border.left;
        let x = rect.origin.x + border.left * 0.5;
        let start = Point2D(x, rect.origin.y);
        let end = Point2D(x, rect.origin.y + rect.size.height);
        self.canvas.draw_target.stroke_line(start, end, &ColorPattern(color.left), &stroke_opts, &draw_opts);
    }

    pub fn draw_image(&self, bounds: Rect<Au>, image: Arc<~Image>) {
        let image = image.get();
        let size = Size2D(image.width as i32, image.height as i32);
        let stride = image.width * 4;

        self.canvas.draw_target.make_current();
        let draw_target_ref = &self.canvas.draw_target;
        let azure_surface = draw_target_ref.create_source_surface_from_data(image.data, size,
                                                                            stride as i32, B8G8R8A8);
        let source_rect = Rect(Point2D(0 as AzFloat, 0 as AzFloat),
                               Size2D(image.width as AzFloat, image.height as AzFloat));
        let dest_rect = bounds.to_azure_rect();
        let draw_surface_options = DrawSurfaceOptions(Linear, true);
        let draw_options = DrawOptions(1.0f as AzFloat, 0);
        draw_target_ref.draw_surface(azure_surface,
                                     dest_rect,
                                     source_rect,
                                     draw_surface_options,
                                     draw_options);
    }

    pub fn clear(&self) {
        let pattern = ColorPattern(Color(1.0, 1.0, 1.0, 1.0));
        let rect = Rect(Point2D(self.canvas.rect.origin.x as AzFloat,
                                self.canvas.rect.origin.y as AzFloat),
                        Size2D(self.canvas.screen_pos.size.width as AzFloat,
                               self.canvas.screen_pos.size.height as AzFloat));
        self.canvas.draw_target.make_current();
        self.canvas.draw_target.fill_rect(&rect, &pattern);
    }
}

trait to_float {
    fn to_float(&self) -> float;
}

impl to_float for u8 {
    fn to_float(&self) -> float {
        (*self as float) / 255f
    }
}

trait ToAzureRect {
    fn to_azure_rect(&self) -> Rect<AzFloat>;
}

impl ToAzureRect for Rect<Au> {
    fn to_azure_rect(&self) -> Rect<AzFloat> {
        Rect(Point2D(self.origin.x.to_px() as AzFloat, self.origin.y.to_px() as AzFloat),
             Size2D(self.size.width.to_px() as AzFloat, self.size.height.to_px() as AzFloat))
    }
}

trait ToSideOffsetsPx {
    fn to_float_px(&self) -> SideOffsets2D<AzFloat>;
}

impl ToSideOffsetsPx for SideOffsets2D<Au> {
    fn to_float_px(&self) -> SideOffsets2D<AzFloat> {
        SideOffsets2D::new(self.top.to_px() as AzFloat,
                           self.right.to_px() as AzFloat,
                           self.bottom.to_px() as AzFloat,
                           self.left.to_px() as AzFloat)
    }
}
<|endoftext|>


<commit_before>use crate::core::Editor;
use parking_lot::RwLock;
use std::rc::Rc;

use crate::core::view::ContentFilter;
use crate::core::view::FilterData;
use crate::core::view::FilterIo;
use crate::core::view::LayoutEnv;
use std::collections::HashMap;

use super::TextModeContext;
use crate::core::view::View;

use crate::core::codec::text::u32_to_char;

use crate::core::codepointinfo::TextStyle;

pub struct CharMapFilter {
    char_map: Option<HashMap<char, String>>, // TODO(ceg): add CharMap type
    color_map: Option<HashMap<char, (u8, u8, u8)>>,
}

impl CharMapFilter {
    pub fn new() -> Self {
        CharMapFilter {
            char_map: None,
            color_map: None,
        }
    }
}

impl ContentFilter<'_> for CharMapFilter {
    fn name(&self) -> &'static str {
        &"CharMapFilter"
    }

    fn setup(
        &mut self,
        _editor: &Editor,
        _env: &mut LayoutEnv,
        view: &Rc<RwLock<View>>,
        _parent_view: Option<&View<'static>>,
    ) {
        let v = view.read();
        let tm = v.mode_ctx::<TextModeContext>("text-mode");
        let char_map = tm.char_map.clone();
        let color_map = tm.color_map.clone();

        // TODO(ceg): reload only on view change ? ref ?
        self.char_map = char_map;
        self.color_map = color_map;
    }

    fn run(
        &mut self,
        _view: &View,
        _env: &mut LayoutEnv,
        filter_in: &Vec<FilterIo>,
        filter_out: &mut Vec<FilterIo>,
    ) {
        for io in filter_in.iter() {
            match io {
                FilterIo {
                    metadata,
                    style,
                    offset,
                    size,
                    data:
                        FilterData::TextInfo {
                            real_cp,
                            displayed_cp,
                            ..
                        },
                    ..
                } => {
                    let v = transform_io_data(
                        self.char_map.as_ref(),
                        self.color_map.as_ref(),
                        u32_to_char(*real_cp),
                        u32_to_char(*displayed_cp),
                        *offset,
                        *size,
                        style.is_selected,
                        style.color,
                        style.bg_color,
                        *metadata,
                    );

                    for new_io in v {
                        filter_out.push(new_io.clone())
                    }
                }

                _ => filter_out.push(io.clone()),
            }
        }
    }
}

// TODO return array of CodePointInfo  0x7f -> <DEL>
pub fn transform_io_data(
    char_map: Option<&HashMap<char, String>>,
    color_map: Option<&HashMap<char, (u8, u8, u8)>>,
    real_cp: char,
    displayed_cp: char,
    offset: Option<u64>,
    size: usize,
    is_selected: bool,
    color: (u8, u8, u8),
    bg_color: (u8, u8, u8),
    metadata: bool,
) -> Vec<FilterIo> {
    let mut cp_vec = Vec::new();

    let orig_metadata = metadata;
    let orig_size = size;

    // debug
    if metadata && size > 0 {
        dbg_println!(
            "real_cp = {}, displayed_cp = {}, size = {}, metadata = {}",
            real_cp,
            displayed_cp,
            size,
            metadata
        );
        panic!("");
    }

    // debug
    if !metadata && size == 0 {
        dbg_println!(
            "real_cp = {}, displayed_cp = {}, size = {}, metadata = {}",
            real_cp,
            displayed_cp,
            size,
            metadata
        );
        panic!("");
    }

    let mut fg_color = color;
    if let Some(color_map) = color_map {
        fg_color = *color_map.get(&real_cp).unwrap_or(&color);
    }

    let mut style = TextStyle::new();
    style.is_selected = is_selected;
    style.is_inverse = false;
    style.color = fg_color;
    style.bg_color = bg_color;

    if char_map.is_none() || real_cp != displayed_cp {
        cp_vec.push(FilterIo {
            metadata,
            style,
            offset,
            size,
            data: FilterData::TextInfo {
                real_cp: real_cp as u32,
                displayed_cp: displayed_cp as u32,
            },
        });
        return cp_vec;
    }

    let char_map = char_map.unwrap();

    let s = char_map.get(&real_cp);
    if s.is_none() {
        cp_vec.push(FilterIo {
            metadata,
            style,
            offset,
            size,
            data: FilterData::TextInfo {
                real_cp: real_cp as u32,
                displayed_cp: displayed_cp as u32,
            },
        });
        return cp_vec;
    }

    let s = s.unwrap();

    for (idx, displayed_cp) in s.chars().enumerate() {
        let (size, metadata) = if idx == 0 {
            (orig_size, orig_metadata)
        } else {
            (0, true)
        };

        cp_vec.push(FilterIo {
            metadata,
            style,
            offset,
            size,
            data: FilterData::TextInfo {
                real_cp: real_cp as u32,
                displayed_cp: displayed_cp as u32,
            },
        });
    }

    return cp_vec;
}
<commit_msg>char map: enable only for invisible ascii chars<commit_after>use crate::core::Editor;
use parking_lot::RwLock;
use std::rc::Rc;

use crate::core::view::ContentFilter;
use crate::core::view::FilterData;
use crate::core::view::FilterIo;
use crate::core::view::LayoutEnv;
use std::collections::HashMap;

use super::TextModeContext;
use crate::core::view::View;

use crate::core::codec::text::u32_to_char;

use crate::core::codepointinfo::TextStyle;

pub struct CharMapFilter {
    char_map: Option<HashMap<char, String>>, // TODO(ceg): add CharMap type
    color_map: Option<HashMap<char, (u8, u8, u8)>>,
}

impl CharMapFilter {
    pub fn new() -> Self {
        CharMapFilter {
            char_map: None,
            color_map: None,
        }
    }
}

impl ContentFilter<'_> for CharMapFilter {
    fn name(&self) -> &'static str {
        &"CharMapFilter"
    }

    fn setup(
        &mut self,
        _editor: &Editor,
        _env: &mut LayoutEnv,
        view: &Rc<RwLock<View>>,
        _parent_view: Option<&View<'static>>,
    ) {
        let v = view.read();
        let tm = v.mode_ctx::<TextModeContext>("text-mode");
        let char_map = tm.char_map.clone();
        let color_map = tm.color_map.clone();

        // TODO(ceg): reload only on view change ? ref ?
        self.char_map = char_map;
        self.color_map = color_map;
    }

    fn run(
        &mut self,
        _view: &View,
        _env: &mut LayoutEnv,
        filter_in: &Vec<FilterIo>,
        filter_out: &mut Vec<FilterIo>,
    ) {
        for io in filter_in.iter() {
            match io {
                FilterIo {
                    metadata,
                    style,
                    offset,
                    size,
                    data:
                        FilterData::TextInfo {
                            real_cp,
                            displayed_cp,
                            ..
                        },
                    ..
                } => {
                    // enable only for invisible ascii chars
                    let do_transform = if *real_cp < 0x9 {
                        true
                    } else if *real_cp >= 0xb && *real_cp <= 0x1f {
                        true
                    } else if *real_cp == 0x07f || *real_cp == 0x80 {
                        true
                    } else {
                        false
                    };

                    if !do_transform {
                        filter_out.push(io.clone());
                        continue;
                    }

                    let v = transform_io_data(
                        self.char_map.as_ref(),
                        self.color_map.as_ref(),
                        u32_to_char(*real_cp),
                        u32_to_char(*displayed_cp),
                        *offset,
                        *size,
                        style.is_selected,
                        style.color,
                        style.bg_color,
                        *metadata,
                    );

                    for new_io in v {
                        filter_out.push(new_io.clone())
                    }
                }

                _ => filter_out.push(io.clone()),
            }
        }
    }
}

// TODO return array of CodePointInfo  0x7f -> <DEL>
pub fn transform_io_data(
    char_map: Option<&HashMap<char, String>>,
    color_map: Option<&HashMap<char, (u8, u8, u8)>>,
    real_cp: char,
    displayed_cp: char,
    offset: Option<u64>,
    size: usize,
    is_selected: bool,
    color: (u8, u8, u8),
    bg_color: (u8, u8, u8),
    metadata: bool,
) -> Vec<FilterIo> {
    let mut cp_vec = Vec::new();

    let orig_metadata = metadata;
    let orig_size = size;

    // debug
    if metadata && size > 0 {
        dbg_println!(
            "real_cp = {}, displayed_cp = {}, size = {}, metadata = {}",
            real_cp,
            displayed_cp,
            size,
            metadata
        );
        panic!("");
    }

    // debug
    if !metadata && size == 0 {
        dbg_println!(
            "real_cp = {}, displayed_cp = {}, size = {}, metadata = {}",
            real_cp,
            displayed_cp,
            size,
            metadata
        );
        panic!("");
    }

    let mut fg_color = color;
    if let Some(color_map) = color_map {
        fg_color = *color_map.get(&real_cp).unwrap_or(&color);
    }

    let mut style = TextStyle::new();
    style.is_selected = is_selected;
    style.is_inverse = false;
    style.color = fg_color;
    style.bg_color = bg_color;

    if char_map.is_none() || real_cp != displayed_cp {
        cp_vec.push(FilterIo {
            metadata,
            style,
            offset,
            size,
            data: FilterData::TextInfo {
                real_cp: real_cp as u32,
                displayed_cp: displayed_cp as u32,
            },
        });
        return cp_vec;
    }

    let char_map = char_map.unwrap();

    let s = char_map.get(&real_cp);
    if s.is_none() {
        cp_vec.push(FilterIo {
            metadata,
            style,
            offset,
            size,
            data: FilterData::TextInfo {
                real_cp: real_cp as u32,
                displayed_cp: displayed_cp as u32,
            },
        });
        return cp_vec;
    }

    let s = s.unwrap();

    for (idx, displayed_cp) in s.chars().enumerate() {
        let (size, metadata) = if idx == 0 {
            (orig_size, orig_metadata)
        } else {
            (0, true)
        };

        cp_vec.push(FilterIo {
            metadata,
            style,
            offset,
            size,
            data: FilterData::TextInfo {
                real_cp: real_cp as u32,
                displayed_cp: displayed_cp as u32,
            },
        });
    }

    return cp_vec;
}
<|endoftext|>


<commit_before>// See LICENSE file for copyright and license details.

use std::f32::consts::{PI};
use rand::{thread_rng, Rng};
use cgmath::{Vector3, Vector, rad};
use common::types::{MapPos, ZFloat, UnitId};
use core::game_state::GameState;
use core::core;
use core::unit::{UnitTypeId};
use core::pathfinder::{MapPath};
use zgl::mesh::{MeshId};
use zgl::font_stash::{FontStash};
use zgl::zgl::{Zgl};
use zgl::types::{Time, WorldPos};
use geom;
use scene::{
    Scene,
    SceneNode,
    NodeId,
    MIN_MARKER_NODE_ID,
    SHELL_NODE_ID,
};
use unit_type_visual_info::{UnitTypeVisualInfo};
use move_helper::{MoveHelper};
use map_text::{MapTextManager};

fn unit_id_to_node_id(unit_id: &UnitId) -> NodeId {
    NodeId{id: unit_id.id}
}

fn marker_id(unit_id: &UnitId) -> NodeId {
    NodeId{id: MIN_MARKER_NODE_ID.id + unit_id.id}
}

pub trait EventVisualizer {
    fn is_finished(&self) -> bool;
    // fn draw(&mut self, scene: &mut Scene, dtime: &Time, map_text: &MapTextManager);
    fn draw(&mut self, scene: &mut Scene, dtime: &Time);
    fn end(&mut self, scene: &mut Scene, state: &GameState);
}

// TODO: store CoreEvent
pub struct EventMoveVisualizer {
    unit_id: UnitId,
    path: Vec<WorldPos>,
    move_helper: MoveHelper,
    speed: ZFloat,
}

impl EventVisualizer for EventMoveVisualizer {
    fn is_finished(&self) -> bool {
        self.path.len() == 1
    }

    fn draw(&mut self, scene: &mut Scene, dtime: &Time) {
        let pos = self.move_helper.step(dtime);
        {
            let marker_node = scene.node(&marker_id(&self.unit_id));
            marker_node.pos.v = pos.v.add_v(&vec3_z(geom::HEX_EX_RADIUS / 2.0));
        }
        let node_id = unit_id_to_node_id(&self.unit_id);
        let node = scene.node(&node_id);
        node.pos = pos;
        if self.move_helper.is_finished() {
            let _ = self.path.remove(0);
            if self.path.len() > 1 {
                self.update_waypoint(node);
            }
            node.pos = self.current_waypoint().clone();
        }
    }

    fn end(&mut self, scene: &mut Scene, _: &GameState) {
        assert!(self.path.len() == 1);
        let node_id = unit_id_to_node_id(&self.unit_id);
        let node = scene.node(&node_id);
        node.pos = self.current_waypoint().clone();
    }
}

impl EventMoveVisualizer {
    pub fn new(
        scene: &mut Scene,
        _: &GameState,
        unit_id: UnitId,
        unit_type_visual_info: &UnitTypeVisualInfo,
        path: MapPath,
    ) -> Box<EventVisualizer> {
        let mut world_path = Vec::new();
        for path_node in path.nodes().iter() {
            let world_pos = geom::map_pos_to_world_pos(&path_node.pos);
            world_path.push(world_pos);
        }
        let speed = unit_type_visual_info.move_speed;
        let node_id = unit_id_to_node_id(&unit_id);
        let node = scene.node(&node_id);
        node.rot = geom::get_rot_angle(
            &world_path[0], &world_path[1]);
        let move_helper = MoveHelper::new(
            &world_path[0], &world_path[1], speed);
        let mut vis = box EventMoveVisualizer {
            unit_id: unit_id.clone(),
            path: world_path,
            move_helper: move_helper,
            speed: speed,
        };
        vis.update_waypoint(node);
        vis as Box<EventVisualizer>
    }

    fn update_waypoint(&mut self, node: &mut SceneNode) {
        self.move_helper = MoveHelper::new(
            self.current_waypoint(),
            self.next_waypoint(),
            self.speed,
        );
        node.rot = geom::get_rot_angle(
            self.current_waypoint(),
            self.next_waypoint()
        );
    }

    fn current_waypoint(&self) -> &WorldPos {
        assert!(self.path.len() >= 1);
        &self.path[0]
    }

    fn next_waypoint(&self) -> &WorldPos {
        assert!(self.path.len() >= 2);
        &self.path[1]
    }
}

pub struct EventEndTurnVisualizer;

impl EventEndTurnVisualizer {
    pub fn new() -> Box<EventVisualizer> {
        box EventEndTurnVisualizer as Box<EventVisualizer>
    }
}

impl EventVisualizer for EventEndTurnVisualizer {
    fn is_finished(&self) -> bool {
        true
    }

    fn draw(&mut self, _: &mut Scene, _: &Time) {}

    fn end(&mut self, _: &mut Scene, _: &GameState) {}
}

fn show_unit_at(
    core: &core::Core,
    scene: &mut Scene,
    id: &UnitId,
    type_id: &UnitTypeId,
    pos: &MapPos,
    mesh_id: &MeshId,
    marker_mesh_id: &MeshId,
) {
    let node_id = unit_id_to_node_id(id);
    let world_pos = geom::map_pos_to_world_pos(pos);
    let to = world_pos;
    let rot = rad(thread_rng().gen_range(0.0, PI * 2.0));
    scene.nodes.insert(node_id, SceneNode {
        pos: to.clone(),
        rot: rot,
        mesh_id: None,
        children: get_unit_scene_nodes(core, type_id, mesh_id),
    });
    scene.nodes.insert(marker_id(id), SceneNode {
        pos: WorldPos{v: to.v.add_v(&vec3_z(geom::HEX_EX_RADIUS / 2.0))},
        rot: rad(0.0),
        mesh_id: Some(marker_mesh_id.clone()),
        children: Vec::new(),
    });
}

pub struct EventCreateUnitVisualizer {
    id: UnitId,
    move_helper: MoveHelper,
}

fn get_unit_scene_nodes(
    core: &core::Core,
    type_id: &UnitTypeId,
    mesh_id: &MeshId,
) -> Vec<SceneNode> {
    let count = core.object_types().get_unit_type(type_id).count;
    let mut vec = Vec::new();
    if count == 1 {
        vec![SceneNode {
            pos: WorldPos{v: Vector3{x: 0.0, y: 0.0, z: 0.0}},
            rot: rad(0.0),
            mesh_id: Some(mesh_id.clone()),
            children: vec![],
        }]
    } else {
        for i in 0 .. count {
            let pos = geom::index_to_circle_vertex(count, i).v.mul_s(0.3f32);
            vec.push(SceneNode {
                pos: WorldPos{v: pos},
                rot: rad(0.0),
                mesh_id: Some(mesh_id.clone()),
                children: vec![],
            });
        }
        vec
    }
}

impl EventCreateUnitVisualizer {
    pub fn new(
        core: &core::Core,
        scene: &mut Scene,
        _: &GameState,
        id: UnitId,
        type_id: &UnitTypeId,
        pos: &MapPos,
        mesh_id: &MeshId,
        marker_mesh_id: &MeshId,
    ) -> Box<EventVisualizer> {
        let node_id = unit_id_to_node_id(&id);
        let to = geom::map_pos_to_world_pos(pos);
        let from = WorldPos{v: to.v.sub_v(&vec3_z(geom::HEX_EX_RADIUS / 2.0))};
        show_unit_at(core, scene, &id, type_id, pos, mesh_id, marker_mesh_id);
        let move_helper = MoveHelper::new(&from, &to, 1.0);
        scene.nodes.get_mut(&node_id).unwrap().pos = from.clone();
        box EventCreateUnitVisualizer {
            id: id,
            move_helper: move_helper,
        } as Box<EventVisualizer>
    }
}

impl EventVisualizer for EventCreateUnitVisualizer {
    fn is_finished(&self) -> bool {
        self.move_helper.is_finished()
    }

    fn draw(&mut self, scene: &mut Scene, dtime: &Time) {
        let node_id = unit_id_to_node_id(&self.id);
        let node = scene.node(&node_id);
        node.pos = self.move_helper.step(dtime);
    }

    fn end(&mut self, _: &mut Scene, _: &GameState) {}
}

fn vec3_z(z: ZFloat) -> Vector3<ZFloat> {
    Vector3{x: 0.0, y: 0.0, z: z}
}

pub struct EventAttackUnitVisualizer {
    defender_id: UnitId,
    killed: bool,
    move_helper: MoveHelper,
    shell_move: MoveHelper,
}

impl EventAttackUnitVisualizer {
    pub fn new(
        zgl: &Zgl,
        scene: &mut Scene,
        _: &GameState,
        attacker_id: UnitId,
        defender_id: UnitId,
        killed: bool,
        mode: core::FireMode,
        shell_mesh_id: MeshId,
        map_text: &mut MapTextManager,
        font_stash: &mut FontStash,
    ) -> Box<EventVisualizer> {
        let node_id = unit_id_to_node_id(&defender_id);
        let from = scene.nodes.get(&node_id)
            .expect("Can not find defender scene node")
            .pos.clone();
        let to = WorldPos{v: from.v.sub_v(&vec3_z(geom::HEX_EX_RADIUS / 2.0))};
        let move_helper = MoveHelper::new(&from, &to, 1.0);
        let attacker_pos = scene.nodes.get(&unit_id_to_node_id(&attacker_id))
            .expect("Can not find attacker").pos.clone();
        let defender_pos = scene.nodes.get(&unit_id_to_node_id(&defender_id))
            .expect("Can not find defender").pos.clone();
        let shell_move = {
            scene.nodes.insert(SHELL_NODE_ID, SceneNode {
                pos: from.clone(),
                rot: rad(0.0),
                mesh_id: Some(shell_mesh_id),
                children: Vec::new(),
            });
            MoveHelper::new(&attacker_pos, &defender_pos, 10.0)
        };
        if killed {
            map_text.add_text_to_world_pos(zgl, font_stash, "-1", &defender_pos);
        } else {
            map_text.add_text_to_world_pos(zgl, font_stash, "miss", &defender_pos);
        }
        match mode {
            core::FireMode::Reactive => {
                map_text.add_text_to_world_pos(zgl, font_stash, "reaction fire", &attacker_pos);
            },
            core::FireMode::Active => {},
        }
        box EventAttackUnitVisualizer {
            defender_id: defender_id,
            killed: killed,
            move_helper: move_helper,
            shell_move: shell_move,
        } as Box<EventVisualizer>
    }
}

impl EventVisualizer for EventAttackUnitVisualizer {
    fn is_finished(&self) -> bool {
        if self.killed {
            self.move_helper.is_finished()
        } else {
            self.shell_move.is_finished()
        }
    }

    fn draw(&mut self, scene: &mut Scene, dtime: &Time) {
        scene.node(&SHELL_NODE_ID).pos = self.shell_move.step(dtime);
        if self.killed {
            if self.shell_move.is_finished() {
                let node_id = unit_id_to_node_id(&self.defender_id);
                scene.node(&node_id).pos = self.move_helper.step(dtime);
            }
        }
    }

    fn end(&mut self, scene: &mut Scene, _: &GameState) {
        if self.killed {
            let node_id = unit_id_to_node_id(&self.defender_id);
            scene.nodes.remove(&node_id);
            scene.nodes.remove(&marker_id(&self.defender_id));
        }
        scene.nodes.remove(&SHELL_NODE_ID);
    }
}

pub struct EventShowUnitVisualizer;

impl EventShowUnitVisualizer {
    pub fn new(
        core: &core::Core,
        scene: &mut Scene,
        _: &GameState,
        id: UnitId,
        type_id: &UnitTypeId,
        pos: &MapPos,
        mesh_id: &MeshId,
        marker_mesh_id: &MeshId,
    ) -> Box<EventVisualizer> {
        show_unit_at(core, scene, &id, type_id, pos, mesh_id, marker_mesh_id);
        box EventShowUnitVisualizer as Box<EventVisualizer>
    }
}

impl EventVisualizer for EventShowUnitVisualizer {
    fn is_finished(&self) -> bool {
        true
    }

    fn draw(&mut self, _: &mut Scene, _: &Time) {}

    fn end(&mut self, _: &mut Scene, _: &GameState) {}
}

pub struct EventHideUnitVisualizer;

impl EventHideUnitVisualizer {
    pub fn new(scene: &mut Scene, unit_id: &UnitId) -> Box<EventVisualizer> {
        scene.nodes.remove(&unit_id_to_node_id(&unit_id));
        scene.nodes.remove(&marker_id(&unit_id));
        box EventHideUnitVisualizer as Box<EventVisualizer>
    }
}

impl EventVisualizer for EventHideUnitVisualizer {
    fn is_finished(&self) -> bool {
        true
    }

    fn draw(&mut self, _: &mut Scene, _: &Time) {}

    fn end(&mut self, _: &mut Scene, _: &GameState) {}
}

// vim: set tabstop=4 shiftwidth=4 softtabstop=4 expandtab:
<commit_msg>event_visualizer.rs: Removed old commented-out code<commit_after>// See LICENSE file for copyright and license details.

use std::f32::consts::{PI};
use rand::{thread_rng, Rng};
use cgmath::{Vector3, Vector, rad};
use common::types::{MapPos, ZFloat, UnitId};
use core::game_state::GameState;
use core::core;
use core::unit::{UnitTypeId};
use core::pathfinder::{MapPath};
use zgl::mesh::{MeshId};
use zgl::font_stash::{FontStash};
use zgl::zgl::{Zgl};
use zgl::types::{Time, WorldPos};
use geom;
use scene::{
    Scene,
    SceneNode,
    NodeId,
    MIN_MARKER_NODE_ID,
    SHELL_NODE_ID,
};
use unit_type_visual_info::{UnitTypeVisualInfo};
use move_helper::{MoveHelper};
use map_text::{MapTextManager};

fn unit_id_to_node_id(unit_id: &UnitId) -> NodeId {
    NodeId{id: unit_id.id}
}

fn marker_id(unit_id: &UnitId) -> NodeId {
    NodeId{id: MIN_MARKER_NODE_ID.id + unit_id.id}
}

pub trait EventVisualizer {
    fn is_finished(&self) -> bool;
    fn draw(&mut self, scene: &mut Scene, dtime: &Time);
    fn end(&mut self, scene: &mut Scene, state: &GameState);
}

// TODO: store CoreEvent
pub struct EventMoveVisualizer {
    unit_id: UnitId,
    path: Vec<WorldPos>,
    move_helper: MoveHelper,
    speed: ZFloat,
}

impl EventVisualizer for EventMoveVisualizer {
    fn is_finished(&self) -> bool {
        self.path.len() == 1
    }

    fn draw(&mut self, scene: &mut Scene, dtime: &Time) {
        let pos = self.move_helper.step(dtime);
        {
            let marker_node = scene.node(&marker_id(&self.unit_id));
            marker_node.pos.v = pos.v.add_v(&vec3_z(geom::HEX_EX_RADIUS / 2.0));
        }
        let node_id = unit_id_to_node_id(&self.unit_id);
        let node = scene.node(&node_id);
        node.pos = pos;
        if self.move_helper.is_finished() {
            let _ = self.path.remove(0);
            if self.path.len() > 1 {
                self.update_waypoint(node);
            }
            node.pos = self.current_waypoint().clone();
        }
    }

    fn end(&mut self, scene: &mut Scene, _: &GameState) {
        assert!(self.path.len() == 1);
        let node_id = unit_id_to_node_id(&self.unit_id);
        let node = scene.node(&node_id);
        node.pos = self.current_waypoint().clone();
    }
}

impl EventMoveVisualizer {
    pub fn new(
        scene: &mut Scene,
        _: &GameState,
        unit_id: UnitId,
        unit_type_visual_info: &UnitTypeVisualInfo,
        path: MapPath,
    ) -> Box<EventVisualizer> {
        let mut world_path = Vec::new();
        for path_node in path.nodes().iter() {
            let world_pos = geom::map_pos_to_world_pos(&path_node.pos);
            world_path.push(world_pos);
        }
        let speed = unit_type_visual_info.move_speed;
        let node_id = unit_id_to_node_id(&unit_id);
        let node = scene.node(&node_id);
        node.rot = geom::get_rot_angle(
            &world_path[0], &world_path[1]);
        let move_helper = MoveHelper::new(
            &world_path[0], &world_path[1], speed);
        let mut vis = box EventMoveVisualizer {
            unit_id: unit_id.clone(),
            path: world_path,
            move_helper: move_helper,
            speed: speed,
        };
        vis.update_waypoint(node);
        vis as Box<EventVisualizer>
    }

    fn update_waypoint(&mut self, node: &mut SceneNode) {
        self.move_helper = MoveHelper::new(
            self.current_waypoint(),
            self.next_waypoint(),
            self.speed,
        );
        node.rot = geom::get_rot_angle(
            self.current_waypoint(),
            self.next_waypoint()
        );
    }

    fn current_waypoint(&self) -> &WorldPos {
        assert!(self.path.len() >= 1);
        &self.path[0]
    }

    fn next_waypoint(&self) -> &WorldPos {
        assert!(self.path.len() >= 2);
        &self.path[1]
    }
}

pub struct EventEndTurnVisualizer;

impl EventEndTurnVisualizer {
    pub fn new() -> Box<EventVisualizer> {
        box EventEndTurnVisualizer as Box<EventVisualizer>
    }
}

impl EventVisualizer for EventEndTurnVisualizer {
    fn is_finished(&self) -> bool {
        true
    }

    fn draw(&mut self, _: &mut Scene, _: &Time) {}

    fn end(&mut self, _: &mut Scene, _: &GameState) {}
}

fn show_unit_at(
    core: &core::Core,
    scene: &mut Scene,
    id: &UnitId,
    type_id: &UnitTypeId,
    pos: &MapPos,
    mesh_id: &MeshId,
    marker_mesh_id: &MeshId,
) {
    let node_id = unit_id_to_node_id(id);
    let world_pos = geom::map_pos_to_world_pos(pos);
    let to = world_pos;
    let rot = rad(thread_rng().gen_range(0.0, PI * 2.0));
    scene.nodes.insert(node_id, SceneNode {
        pos: to.clone(),
        rot: rot,
        mesh_id: None,
        children: get_unit_scene_nodes(core, type_id, mesh_id),
    });
    scene.nodes.insert(marker_id(id), SceneNode {
        pos: WorldPos{v: to.v.add_v(&vec3_z(geom::HEX_EX_RADIUS / 2.0))},
        rot: rad(0.0),
        mesh_id: Some(marker_mesh_id.clone()),
        children: Vec::new(),
    });
}

pub struct EventCreateUnitVisualizer {
    id: UnitId,
    move_helper: MoveHelper,
}

fn get_unit_scene_nodes(
    core: &core::Core,
    type_id: &UnitTypeId,
    mesh_id: &MeshId,
) -> Vec<SceneNode> {
    let count = core.object_types().get_unit_type(type_id).count;
    let mut vec = Vec::new();
    if count == 1 {
        vec![SceneNode {
            pos: WorldPos{v: Vector3{x: 0.0, y: 0.0, z: 0.0}},
            rot: rad(0.0),
            mesh_id: Some(mesh_id.clone()),
            children: vec![],
        }]
    } else {
        for i in 0 .. count {
            let pos = geom::index_to_circle_vertex(count, i).v.mul_s(0.3f32);
            vec.push(SceneNode {
                pos: WorldPos{v: pos},
                rot: rad(0.0),
                mesh_id: Some(mesh_id.clone()),
                children: vec![],
            });
        }
        vec
    }
}

impl EventCreateUnitVisualizer {
    pub fn new(
        core: &core::Core,
        scene: &mut Scene,
        _: &GameState,
        id: UnitId,
        type_id: &UnitTypeId,
        pos: &MapPos,
        mesh_id: &MeshId,
        marker_mesh_id: &MeshId,
    ) -> Box<EventVisualizer> {
        let node_id = unit_id_to_node_id(&id);
        let to = geom::map_pos_to_world_pos(pos);
        let from = WorldPos{v: to.v.sub_v(&vec3_z(geom::HEX_EX_RADIUS / 2.0))};
        show_unit_at(core, scene, &id, type_id, pos, mesh_id, marker_mesh_id);
        let move_helper = MoveHelper::new(&from, &to, 1.0);
        scene.nodes.get_mut(&node_id).unwrap().pos = from.clone();
        box EventCreateUnitVisualizer {
            id: id,
            move_helper: move_helper,
        } as Box<EventVisualizer>
    }
}

impl EventVisualizer for EventCreateUnitVisualizer {
    fn is_finished(&self) -> bool {
        self.move_helper.is_finished()
    }

    fn draw(&mut self, scene: &mut Scene, dtime: &Time) {
        let node_id = unit_id_to_node_id(&self.id);
        let node = scene.node(&node_id);
        node.pos = self.move_helper.step(dtime);
    }

    fn end(&mut self, _: &mut Scene, _: &GameState) {}
}

fn vec3_z(z: ZFloat) -> Vector3<ZFloat> {
    Vector3{x: 0.0, y: 0.0, z: z}
}

pub struct EventAttackUnitVisualizer {
    defender_id: UnitId,
    killed: bool,
    move_helper: MoveHelper,
    shell_move: MoveHelper,
}

impl EventAttackUnitVisualizer {
    pub fn new(
        zgl: &Zgl,
        scene: &mut Scene,
        _: &GameState,
        attacker_id: UnitId,
        defender_id: UnitId,
        killed: bool,
        mode: core::FireMode,
        shell_mesh_id: MeshId,
        map_text: &mut MapTextManager,
        font_stash: &mut FontStash,
    ) -> Box<EventVisualizer> {
        let node_id = unit_id_to_node_id(&defender_id);
        let from = scene.nodes.get(&node_id)
            .expect("Can not find defender scene node")
            .pos.clone();
        let to = WorldPos{v: from.v.sub_v(&vec3_z(geom::HEX_EX_RADIUS / 2.0))};
        let move_helper = MoveHelper::new(&from, &to, 1.0);
        let attacker_pos = scene.nodes.get(&unit_id_to_node_id(&attacker_id))
            .expect("Can not find attacker").pos.clone();
        let defender_pos = scene.nodes.get(&unit_id_to_node_id(&defender_id))
            .expect("Can not find defender").pos.clone();
        let shell_move = {
            scene.nodes.insert(SHELL_NODE_ID, SceneNode {
                pos: from.clone(),
                rot: rad(0.0),
                mesh_id: Some(shell_mesh_id),
                children: Vec::new(),
            });
            MoveHelper::new(&attacker_pos, &defender_pos, 10.0)
        };
        if killed {
            map_text.add_text_to_world_pos(zgl, font_stash, "-1", &defender_pos);
        } else {
            map_text.add_text_to_world_pos(zgl, font_stash, "miss", &defender_pos);
        }
        match mode {
            core::FireMode::Reactive => {
                map_text.add_text_to_world_pos(zgl, font_stash, "reaction fire", &attacker_pos);
            },
            core::FireMode::Active => {},
        }
        box EventAttackUnitVisualizer {
            defender_id: defender_id,
            killed: killed,
            move_helper: move_helper,
            shell_move: shell_move,
        } as Box<EventVisualizer>
    }
}

impl EventVisualizer for EventAttackUnitVisualizer {
    fn is_finished(&self) -> bool {
        if self.killed {
            self.move_helper.is_finished()
        } else {
            self.shell_move.is_finished()
        }
    }

    fn draw(&mut self, scene: &mut Scene, dtime: &Time) {
        scene.node(&SHELL_NODE_ID).pos = self.shell_move.step(dtime);
        if self.killed {
            if self.shell_move.is_finished() {
                let node_id = unit_id_to_node_id(&self.defender_id);
                scene.node(&node_id).pos = self.move_helper.step(dtime);
            }
        }
    }

    fn end(&mut self, scene: &mut Scene, _: &GameState) {
        if self.killed {
            let node_id = unit_id_to_node_id(&self.defender_id);
            scene.nodes.remove(&node_id);
            scene.nodes.remove(&marker_id(&self.defender_id));
        }
        scene.nodes.remove(&SHELL_NODE_ID);
    }
}

pub struct EventShowUnitVisualizer;

impl EventShowUnitVisualizer {
    pub fn new(
        core: &core::Core,
        scene: &mut Scene,
        _: &GameState,
        id: UnitId,
        type_id: &UnitTypeId,
        pos: &MapPos,
        mesh_id: &MeshId,
        marker_mesh_id: &MeshId,
    ) -> Box<EventVisualizer> {
        show_unit_at(core, scene, &id, type_id, pos, mesh_id, marker_mesh_id);
        box EventShowUnitVisualizer as Box<EventVisualizer>
    }
}

impl EventVisualizer for EventShowUnitVisualizer {
    fn is_finished(&self) -> bool {
        true
    }

    fn draw(&mut self, _: &mut Scene, _: &Time) {}

    fn end(&mut self, _: &mut Scene, _: &GameState) {}
}

pub struct EventHideUnitVisualizer;

impl EventHideUnitVisualizer {
    pub fn new(scene: &mut Scene, unit_id: &UnitId) -> Box<EventVisualizer> {
        scene.nodes.remove(&unit_id_to_node_id(&unit_id));
        scene.nodes.remove(&marker_id(&unit_id));
        box EventHideUnitVisualizer as Box<EventVisualizer>
    }
}

impl EventVisualizer for EventHideUnitVisualizer {
    fn is_finished(&self) -> bool {
        true
    }

    fn draw(&mut self, _: &mut Scene, _: &Time) {}

    fn end(&mut self, _: &mut Scene, _: &GameState) {}
}

// vim: set tabstop=4 shiftwidth=4 softtabstop=4 expandtab:
<|endoftext|>