<commit_before>use std::collections::HashMap;
use std::sync::{Arc, Mutex};

use bazel_protos;
use futures;
use grpcio;

use bytes::Bytes;
use futures::{Future, IntoFuture, Stream};
use hashing::{Digest, Fingerprint};

///
/// Implements the ContentAddressableStorage gRPC API, answering read requests with either known
/// content, NotFound for valid but unknown content, or InvalidArguments for bad arguments.
///
pub struct StubCAS {
  server_transport: grpcio::Server,
  read_request_count: Arc<Mutex<usize>>,
  pub write_message_sizes: Arc<Mutex<Vec<usize>>>,
  pub blobs: Arc<Mutex<HashMap<Fingerprint, Bytes>>>,
}

impl StubCAS {
  ///
  /// # Arguments
  /// * `chunk_size_bytes` - The maximum number of bytes of content to include per streamed message.
  ///                        Messages will saturate until the last one, which may be smaller than
  ///                        this value.
  ///                        If a negative value is given, all requests will receive an error.
  /// * `blobs`            - Known Fingerprints and their content responses. These are not checked
  ///                        for correctness.
  pub fn new(chunk_size_bytes: i64, blobs: HashMap<Fingerprint, Bytes>) -> StubCAS {
    let env = Arc::new(grpcio::Environment::new(1));
    let read_request_count = Arc::new(Mutex::new(0));
    let write_message_sizes = Arc::new(Mutex::new(Vec::new()));
    let blobs = Arc::new(Mutex::new(blobs));
    let responder = StubCASResponder {
      chunk_size_bytes: chunk_size_bytes,
      blobs: blobs.clone(),
      read_request_count: read_request_count.clone(),
      write_message_sizes: write_message_sizes.clone(),
    };
    let mut server_transport = grpcio::ServerBuilder::new(env)
      .register_service(bazel_protos::bytestream_grpc::create_byte_stream(
        responder.clone(),
      ))
      .register_service(
        bazel_protos::remote_execution_grpc::create_content_addressable_storage(responder.clone()),
      )
      .bind("localhost", 0)
      .build()
      .unwrap();
    server_transport.start();

    let cas = StubCAS {
      server_transport,
      read_request_count,
      write_message_sizes,
      blobs,
    };
    cas
  }

  pub fn empty() -> StubCAS {
    StubCAS::new(1024, HashMap::new())
  }

  pub fn always_errors() -> StubCAS {
    StubCAS::new(-1, HashMap::new())
  }

  ///
  /// The address on which this server is listening over insecure HTTP transport.
  ///
  pub fn address(&self) -> String {
    let bind_addr = self.server_transport.bind_addrs().first().unwrap();
    format!("{}:{}", bind_addr.0, bind_addr.1)
  }

  pub fn read_request_count(&self) -> usize {
    self.read_request_count.lock().unwrap().clone()
  }
}

#[derive(Clone, Debug)]
pub struct StubCASResponder {
  chunk_size_bytes: i64,
  blobs: Arc<Mutex<HashMap<Fingerprint, Bytes>>>,
  pub read_request_count: Arc<Mutex<usize>>,
  pub write_message_sizes: Arc<Mutex<Vec<usize>>>,
}

impl StubCASResponder {
  fn should_always_fail(&self) -> bool {
    self.chunk_size_bytes < 0
  }

  fn read_internal(
    &self,
    req: bazel_protos::bytestream::ReadRequest,
  ) -> Result<Vec<bazel_protos::bytestream::ReadResponse>, grpcio::RpcStatus> {
    let parts: Vec<_> = req.get_resource_name().splitn(4, "/").collect();
    if parts.len() != 4 || parts.get(0) != Some(&"") || parts.get(1) != Some(&"blobs") {
      return Err(grpcio::RpcStatus::new(
        grpcio::RpcStatusCode::InvalidArgument,
        Some(format!(
          "Bad resource name format {} - want /blobs/some-sha256/size",
          req.get_resource_name()
        )),
      ));
    }
    let digest = parts.get(2).unwrap();
    let fingerprint = Fingerprint::from_hex_string(digest).map_err(|e| {
      grpcio::RpcStatus::new(
        grpcio::RpcStatusCode::InvalidArgument,
        Some(format!("Bad digest {}: {}", digest, e)),
      )
    })?;
    if self.should_always_fail() {
      return Err(grpcio::RpcStatus::new(
        grpcio::RpcStatusCode::Internal,
        Some("StubCAS is configured to always fail".to_owned()),
      ));
    }
    let blobs = self.blobs.lock().unwrap();
    let maybe_bytes = blobs.get(&fingerprint);
    match maybe_bytes {
      Some(bytes) => Ok(
        bytes
          .chunks(self.chunk_size_bytes as usize)
          .map(|b| {
            let mut resp = bazel_protos::bytestream::ReadResponse::new();
            resp.set_data(Bytes::from(b));
            resp
          })
          .collect(),
      ),
      None => Err(grpcio::RpcStatus::new(
        grpcio::RpcStatusCode::NotFound,
        Some(format!("Did not find digest {}", fingerprint)),
      )),
    }
  }

  ///
  /// Sends a stream of responses down a sink, in ctx's threadpool.
  ///
  fn send<Item, S>(
    &self,
    ctx: grpcio::RpcContext,
    sink: grpcio::ServerStreamingSink<Item>,
    stream: S,
  ) where
    Item: Send + 'static,
    S: futures::Stream<Item = (Item, grpcio::WriteFlags), Error = grpcio::Error> + Send + 'static,
  {
    ctx.spawn(stream.forward(sink).map(|_| ()).map_err(|_| ()));
  }
}

impl bazel_protos::bytestream_grpc::ByteStream for StubCASResponder {
  fn read(
    &self,
    ctx: grpcio::RpcContext,
    req: bazel_protos::bytestream::ReadRequest,
    sink: grpcio::ServerStreamingSink<bazel_protos::bytestream::ReadResponse>,
  ) {
    {
      let mut request_count = self.read_request_count.lock().unwrap();
      *request_count = *request_count + 1;
    }
    match self.read_internal(req) {
      Ok(response) => self.send(
        ctx,
        sink,
        futures::stream::iter_ok(
          response
            .into_iter()
            .map(|chunk| (chunk, grpcio::WriteFlags::default())),
        ),
      ),
      Err(err) => {
        sink.fail(err);
      }
    }
  }

  fn write(
    &self,
    ctx: grpcio::RpcContext,
    stream: grpcio::RequestStream<bazel_protos::bytestream::WriteRequest>,
    sink: grpcio::ClientStreamingSink<bazel_protos::bytestream::WriteResponse>,
  ) {
    let should_always_fail = self.should_always_fail();
    let write_message_sizes = self.write_message_sizes.clone();
    let blobs = self.blobs.clone();
    ctx.spawn(
      stream
        .collect()
        .into_future()
        .and_then(move |reqs| {
          let mut maybe_resource_name = None;
          let mut want_next_offset = 0;
          let mut bytes = Bytes::new();
          for req in reqs {
            match maybe_resource_name {
              None => maybe_resource_name = Some(req.get_resource_name().to_owned()),
              Some(ref resource_name) => {
                if resource_name != req.get_resource_name() {
                  return Err(grpcio::Error::RpcFailure(grpcio::RpcStatus::new(
                    grpcio::RpcStatusCode::InvalidArgument,
                    Some(format!(
                      "All resource names in stream must be the same. Got {} but earlier saw {}",
                      req.get_resource_name(),
                      resource_name
                    )),
                  )));
                }
              }
            }
            if req.get_write_offset() != want_next_offset {
              return Err(grpcio::Error::RpcFailure(grpcio::RpcStatus::new(
                grpcio::RpcStatusCode::InvalidArgument,
                Some(format!(
                  "Missing chunk. Expected next offset {}, got next offset: {}",
                  want_next_offset,
                  req.get_write_offset()
                )),
              )));
            }
            want_next_offset += req.get_data().len() as i64;
            write_message_sizes
              .lock()
              .unwrap()
              .push(req.get_data().len());
            bytes.extend(req.get_data());
          }
          Ok((maybe_resource_name, bytes))
        })
        .map_err(move |err: grpcio::Error| match err {
          grpcio::Error::RpcFailure(status) => status,
          e => grpcio::RpcStatus::new(grpcio::RpcStatusCode::Unknown, Some(format!("{:?}", e))),
        })
        .and_then(
          move |(maybe_resource_name, bytes)| match maybe_resource_name {
            None => Err(grpcio::RpcStatus::new(
              grpcio::RpcStatusCode::InvalidArgument,
              Some(format!("Stream saw no messages")),
            )),
            Some(resource_name) => {
              let parts: Vec<_> = resource_name.splitn(6, "/").collect();
              if parts.len() != 6 || parts.get(1) != Some(&"uploads")
                || parts.get(3) != Some(&"blobs")
              {
                return Err(
                  (grpcio::RpcStatus::new(
                    grpcio::RpcStatusCode::InvalidArgument,
                    Some(format!("Bad resource name: {}", resource_name)),
                  )),
                );
              }
              let fingerprint = match Fingerprint::from_hex_string(parts.get(4).unwrap()) {
                Ok(f) => f,
                Err(err) => {
                  return Err(grpcio::RpcStatus::new(
                    grpcio::RpcStatusCode::InvalidArgument,
                    Some(format!(
                      "Bad fingerprint in resource name: {}: {}",
                      parts.get(4).unwrap(),
                      err
                    )),
                  ))
                }
              };
              let size = match parts.get(5).unwrap().parse::<usize>() {
                Ok(s) => s,
                Err(err) => {
                  return Err(grpcio::RpcStatus::new(
                    grpcio::RpcStatusCode::InvalidArgument,
                    Some(format!(
                      "Bad size in resource name: {}: {}",
                      parts.get(5).unwrap(),
                      err
                    )),
                  ))
                }
              };
              if size != bytes.len() {
                return Err(grpcio::RpcStatus::new(
                  grpcio::RpcStatusCode::InvalidArgument,
                  Some(format!(
                    "Size was incorrect: resource name said size={} but got {}",
                    size,
                    bytes.len()
                  )),
                ));
              }

              if should_always_fail {
                return Err(grpcio::RpcStatus::new(
                  grpcio::RpcStatusCode::Internal,
                  Some("StubCAS is configured to always fail".to_owned()),
                ));
              }

              {
                let mut blobs = blobs.lock().unwrap();
                blobs.insert(fingerprint, bytes);
              }

              let mut response = bazel_protos::bytestream::WriteResponse::new();
              response.set_committed_size(size as i64);
              Ok(response)
            }
          },
        )
        .then(move |result| match result {
          Ok(resp) => sink.success(resp),
          Err(err) => sink.fail(err),
        })
        .then(move |_| Ok(())),
    );
  }

  fn query_write_status(
    &self,
    _ctx: grpcio::RpcContext,
    _req: bazel_protos::bytestream::QueryWriteStatusRequest,
    sink: grpcio::UnarySink<bazel_protos::bytestream::QueryWriteStatusResponse>,
  ) {
    sink.fail(grpcio::RpcStatus::new(
      grpcio::RpcStatusCode::Unimplemented,
      None,
    ));
  }
}

impl bazel_protos::remote_execution_grpc::ContentAddressableStorage for StubCASResponder {
  fn find_missing_blobs(
    &self,
    _ctx: grpcio::RpcContext,
    req: bazel_protos::remote_execution::FindMissingBlobsRequest,
    sink: grpcio::UnarySink<bazel_protos::remote_execution::FindMissingBlobsResponse>,
  ) {
    if self.should_always_fail() {
      sink.fail(grpcio::RpcStatus::new(
        grpcio::RpcStatusCode::Internal,
        Some("StubCAS is configured to always fail".to_owned()),
      ));
      return;
    }
    let blobs = self.blobs.lock().unwrap();
    let mut response = bazel_protos::remote_execution::FindMissingBlobsResponse::new();
    for digest in req.get_blob_digests() {
      let hashing_digest: Digest = digest.into();
      if !blobs.contains_key(&hashing_digest.0) {
        response.mut_missing_blob_digests().push(digest.clone())
      }
    }
    sink.success(response);
  }

  fn batch_update_blobs(
    &self,
    _ctx: grpcio::RpcContext,
    _req: bazel_protos::remote_execution::BatchUpdateBlobsRequest,
    sink: grpcio::UnarySink<bazel_protos::remote_execution::BatchUpdateBlobsResponse>,
  ) {
    sink.fail(grpcio::RpcStatus::new(
      grpcio::RpcStatusCode::Unimplemented,
      None,
    ));
  }

  fn get_tree(
    &self,
    _ctx: grpcio::RpcContext,
    _req: bazel_protos::remote_execution::GetTreeRequest,
    sink: grpcio::UnarySink<bazel_protos::remote_execution::GetTreeResponse>,
  ) {
    sink.fail(grpcio::RpcStatus::new(
      grpcio::RpcStatusCode::Unimplemented,
      None,
    ));
  }
}
<commit_msg>Remove unnecessary parens (#5693)<commit_after>use std::collections::HashMap;
use std::sync::{Arc, Mutex};

use bazel_protos;
use futures;
use grpcio;

use bytes::Bytes;
use futures::{Future, IntoFuture, Stream};
use hashing::{Digest, Fingerprint};

///
/// Implements the ContentAddressableStorage gRPC API, answering read requests with either known
/// content, NotFound for valid but unknown content, or InvalidArguments for bad arguments.
///
pub struct StubCAS {
  server_transport: grpcio::Server,
  read_request_count: Arc<Mutex<usize>>,
  pub write_message_sizes: Arc<Mutex<Vec<usize>>>,
  pub blobs: Arc<Mutex<HashMap<Fingerprint, Bytes>>>,
}

impl StubCAS {
  ///
  /// # Arguments
  /// * `chunk_size_bytes` - The maximum number of bytes of content to include per streamed message.
  ///                        Messages will saturate until the last one, which may be smaller than
  ///                        this value.
  ///                        If a negative value is given, all requests will receive an error.
  /// * `blobs`            - Known Fingerprints and their content responses. These are not checked
  ///                        for correctness.
  pub fn new(chunk_size_bytes: i64, blobs: HashMap<Fingerprint, Bytes>) -> StubCAS {
    let env = Arc::new(grpcio::Environment::new(1));
    let read_request_count = Arc::new(Mutex::new(0));
    let write_message_sizes = Arc::new(Mutex::new(Vec::new()));
    let blobs = Arc::new(Mutex::new(blobs));
    let responder = StubCASResponder {
      chunk_size_bytes: chunk_size_bytes,
      blobs: blobs.clone(),
      read_request_count: read_request_count.clone(),
      write_message_sizes: write_message_sizes.clone(),
    };
    let mut server_transport = grpcio::ServerBuilder::new(env)
      .register_service(bazel_protos::bytestream_grpc::create_byte_stream(
        responder.clone(),
      ))
      .register_service(
        bazel_protos::remote_execution_grpc::create_content_addressable_storage(responder.clone()),
      )
      .bind("localhost", 0)
      .build()
      .unwrap();
    server_transport.start();

    let cas = StubCAS {
      server_transport,
      read_request_count,
      write_message_sizes,
      blobs,
    };
    cas
  }

  pub fn empty() -> StubCAS {
    StubCAS::new(1024, HashMap::new())
  }

  pub fn always_errors() -> StubCAS {
    StubCAS::new(-1, HashMap::new())
  }

  ///
  /// The address on which this server is listening over insecure HTTP transport.
  ///
  pub fn address(&self) -> String {
    let bind_addr = self.server_transport.bind_addrs().first().unwrap();
    format!("{}:{}", bind_addr.0, bind_addr.1)
  }

  pub fn read_request_count(&self) -> usize {
    self.read_request_count.lock().unwrap().clone()
  }
}

#[derive(Clone, Debug)]
pub struct StubCASResponder {
  chunk_size_bytes: i64,
  blobs: Arc<Mutex<HashMap<Fingerprint, Bytes>>>,
  pub read_request_count: Arc<Mutex<usize>>,
  pub write_message_sizes: Arc<Mutex<Vec<usize>>>,
}

impl StubCASResponder {
  fn should_always_fail(&self) -> bool {
    self.chunk_size_bytes < 0
  }

  fn read_internal(
    &self,
    req: bazel_protos::bytestream::ReadRequest,
  ) -> Result<Vec<bazel_protos::bytestream::ReadResponse>, grpcio::RpcStatus> {
    let parts: Vec<_> = req.get_resource_name().splitn(4, "/").collect();
    if parts.len() != 4 || parts.get(0) != Some(&"") || parts.get(1) != Some(&"blobs") {
      return Err(grpcio::RpcStatus::new(
        grpcio::RpcStatusCode::InvalidArgument,
        Some(format!(
          "Bad resource name format {} - want /blobs/some-sha256/size",
          req.get_resource_name()
        )),
      ));
    }
    let digest = parts.get(2).unwrap();
    let fingerprint = Fingerprint::from_hex_string(digest).map_err(|e| {
      grpcio::RpcStatus::new(
        grpcio::RpcStatusCode::InvalidArgument,
        Some(format!("Bad digest {}: {}", digest, e)),
      )
    })?;
    if self.should_always_fail() {
      return Err(grpcio::RpcStatus::new(
        grpcio::RpcStatusCode::Internal,
        Some("StubCAS is configured to always fail".to_owned()),
      ));
    }
    let blobs = self.blobs.lock().unwrap();
    let maybe_bytes = blobs.get(&fingerprint);
    match maybe_bytes {
      Some(bytes) => Ok(
        bytes
          .chunks(self.chunk_size_bytes as usize)
          .map(|b| {
            let mut resp = bazel_protos::bytestream::ReadResponse::new();
            resp.set_data(Bytes::from(b));
            resp
          })
          .collect(),
      ),
      None => Err(grpcio::RpcStatus::new(
        grpcio::RpcStatusCode::NotFound,
        Some(format!("Did not find digest {}", fingerprint)),
      )),
    }
  }

  ///
  /// Sends a stream of responses down a sink, in ctx's threadpool.
  ///
  fn send<Item, S>(
    &self,
    ctx: grpcio::RpcContext,
    sink: grpcio::ServerStreamingSink<Item>,
    stream: S,
  ) where
    Item: Send + 'static,
    S: futures::Stream<Item = (Item, grpcio::WriteFlags), Error = grpcio::Error> + Send + 'static,
  {
    ctx.spawn(stream.forward(sink).map(|_| ()).map_err(|_| ()));
  }
}

impl bazel_protos::bytestream_grpc::ByteStream for StubCASResponder {
  fn read(
    &self,
    ctx: grpcio::RpcContext,
    req: bazel_protos::bytestream::ReadRequest,
    sink: grpcio::ServerStreamingSink<bazel_protos::bytestream::ReadResponse>,
  ) {
    {
      let mut request_count = self.read_request_count.lock().unwrap();
      *request_count = *request_count + 1;
    }
    match self.read_internal(req) {
      Ok(response) => self.send(
        ctx,
        sink,
        futures::stream::iter_ok(
          response
            .into_iter()
            .map(|chunk| (chunk, grpcio::WriteFlags::default())),
        ),
      ),
      Err(err) => {
        sink.fail(err);
      }
    }
  }

  fn write(
    &self,
    ctx: grpcio::RpcContext,
    stream: grpcio::RequestStream<bazel_protos::bytestream::WriteRequest>,
    sink: grpcio::ClientStreamingSink<bazel_protos::bytestream::WriteResponse>,
  ) {
    let should_always_fail = self.should_always_fail();
    let write_message_sizes = self.write_message_sizes.clone();
    let blobs = self.blobs.clone();
    ctx.spawn(
      stream
        .collect()
        .into_future()
        .and_then(move |reqs| {
          let mut maybe_resource_name = None;
          let mut want_next_offset = 0;
          let mut bytes = Bytes::new();
          for req in reqs {
            match maybe_resource_name {
              None => maybe_resource_name = Some(req.get_resource_name().to_owned()),
              Some(ref resource_name) => {
                if resource_name != req.get_resource_name() {
                  return Err(grpcio::Error::RpcFailure(grpcio::RpcStatus::new(
                    grpcio::RpcStatusCode::InvalidArgument,
                    Some(format!(
                      "All resource names in stream must be the same. Got {} but earlier saw {}",
                      req.get_resource_name(),
                      resource_name
                    )),
                  )));
                }
              }
            }
            if req.get_write_offset() != want_next_offset {
              return Err(grpcio::Error::RpcFailure(grpcio::RpcStatus::new(
                grpcio::RpcStatusCode::InvalidArgument,
                Some(format!(
                  "Missing chunk. Expected next offset {}, got next offset: {}",
                  want_next_offset,
                  req.get_write_offset()
                )),
              )));
            }
            want_next_offset += req.get_data().len() as i64;
            write_message_sizes
              .lock()
              .unwrap()
              .push(req.get_data().len());
            bytes.extend(req.get_data());
          }
          Ok((maybe_resource_name, bytes))
        })
        .map_err(move |err: grpcio::Error| match err {
          grpcio::Error::RpcFailure(status) => status,
          e => grpcio::RpcStatus::new(grpcio::RpcStatusCode::Unknown, Some(format!("{:?}", e))),
        })
        .and_then(
          move |(maybe_resource_name, bytes)| match maybe_resource_name {
            None => Err(grpcio::RpcStatus::new(
              grpcio::RpcStatusCode::InvalidArgument,
              Some(format!("Stream saw no messages")),
            )),
            Some(resource_name) => {
              let parts: Vec<_> = resource_name.splitn(6, "/").collect();
              if parts.len() != 6 || parts.get(1) != Some(&"uploads")
                || parts.get(3) != Some(&"blobs")
              {
                return Err(grpcio::RpcStatus::new(
                  grpcio::RpcStatusCode::InvalidArgument,
                  Some(format!("Bad resource name: {}", resource_name)),
                ));
              }
              let fingerprint = match Fingerprint::from_hex_string(parts.get(4).unwrap()) {
                Ok(f) => f,
                Err(err) => {
                  return Err(grpcio::RpcStatus::new(
                    grpcio::RpcStatusCode::InvalidArgument,
                    Some(format!(
                      "Bad fingerprint in resource name: {}: {}",
                      parts.get(4).unwrap(),
                      err
                    )),
                  ))
                }
              };
              let size = match parts.get(5).unwrap().parse::<usize>() {
                Ok(s) => s,
                Err(err) => {
                  return Err(grpcio::RpcStatus::new(
                    grpcio::RpcStatusCode::InvalidArgument,
                    Some(format!(
                      "Bad size in resource name: {}: {}",
                      parts.get(5).unwrap(),
                      err
                    )),
                  ))
                }
              };
              if size != bytes.len() {
                return Err(grpcio::RpcStatus::new(
                  grpcio::RpcStatusCode::InvalidArgument,
                  Some(format!(
                    "Size was incorrect: resource name said size={} but got {}",
                    size,
                    bytes.len()
                  )),
                ));
              }

              if should_always_fail {
                return Err(grpcio::RpcStatus::new(
                  grpcio::RpcStatusCode::Internal,
                  Some("StubCAS is configured to always fail".to_owned()),
                ));
              }

              {
                let mut blobs = blobs.lock().unwrap();
                blobs.insert(fingerprint, bytes);
              }

              let mut response = bazel_protos::bytestream::WriteResponse::new();
              response.set_committed_size(size as i64);
              Ok(response)
            }
          },
        )
        .then(move |result| match result {
          Ok(resp) => sink.success(resp),
          Err(err) => sink.fail(err),
        })
        .then(move |_| Ok(())),
    );
  }

  fn query_write_status(
    &self,
    _ctx: grpcio::RpcContext,
    _req: bazel_protos::bytestream::QueryWriteStatusRequest,
    sink: grpcio::UnarySink<bazel_protos::bytestream::QueryWriteStatusResponse>,
  ) {
    sink.fail(grpcio::RpcStatus::new(
      grpcio::RpcStatusCode::Unimplemented,
      None,
    ));
  }
}

impl bazel_protos::remote_execution_grpc::ContentAddressableStorage for StubCASResponder {
  fn find_missing_blobs(
    &self,
    _ctx: grpcio::RpcContext,
    req: bazel_protos::remote_execution::FindMissingBlobsRequest,
    sink: grpcio::UnarySink<bazel_protos::remote_execution::FindMissingBlobsResponse>,
  ) {
    if self.should_always_fail() {
      sink.fail(grpcio::RpcStatus::new(
        grpcio::RpcStatusCode::Internal,
        Some("StubCAS is configured to always fail".to_owned()),
      ));
      return;
    }
    let blobs = self.blobs.lock().unwrap();
    let mut response = bazel_protos::remote_execution::FindMissingBlobsResponse::new();
    for digest in req.get_blob_digests() {
      let hashing_digest: Digest = digest.into();
      if !blobs.contains_key(&hashing_digest.0) {
        response.mut_missing_blob_digests().push(digest.clone())
      }
    }
    sink.success(response);
  }

  fn batch_update_blobs(
    &self,
    _ctx: grpcio::RpcContext,
    _req: bazel_protos::remote_execution::BatchUpdateBlobsRequest,
    sink: grpcio::UnarySink<bazel_protos::remote_execution::BatchUpdateBlobsResponse>,
  ) {
    sink.fail(grpcio::RpcStatus::new(
      grpcio::RpcStatusCode::Unimplemented,
      None,
    ));
  }

  fn get_tree(
    &self,
    _ctx: grpcio::RpcContext,
    _req: bazel_protos::remote_execution::GetTreeRequest,
    sink: grpcio::UnarySink<bazel_protos::remote_execution::GetTreeResponse>,
  ) {
    sink.fail(grpcio::RpcStatus::new(
      grpcio::RpcStatusCode::Unimplemented,
      None,
    ));
  }
}
<|endoftext|>


<commit_before>use std::result::Result as RResult;
use std::path::Path;
use std::path::PathBuf;
use std::fs::File;
use std::io::Read;

use libimagstore::store::{FileLockEntry, Store};
use libimagref::reference::Ref;
use libimagref::flags::RefFlags;

use mailparse::{MailParseError, ParsedMail, parse_mail};

use hasher::MailHasher;
use result::Result;
use error::{MapErrInto, MailErrorKind as MEK};

struct Buffer(String);

impl Buffer {
    pub fn parsed<'a>(&'a self) -> RResult<ParsedMail<'a>, MailParseError> {
        parse_mail(self.0.as_bytes())
    }
}

impl From<String> for Buffer {
    fn from(data: String) -> Buffer {
        Buffer(data)
    }
}

pub struct Mail<'a>(Ref<'a>, Buffer);

impl<'a> Mail<'a> {

    /// Imports a mail from the Path passed
    pub fn import_from_path<P: AsRef<Path>>(store: &Store, p: P) -> Result<Mail> {
        let h = MailHasher::new();
        let f = RefFlags::default().with_content_hashing(true).with_permission_tracking(false);
        let p = PathBuf::from(p.as_ref());

        Ref::create_with_hasher(store, p, f, h)
            .map_err_into(MEK::RefCreationError)
            .and_then(|reference| {
                reference.fs_file()
                    .map_err_into(MEK::RefHandlingError)
                    .and_then(|path| File::open(path).map_err_into(MEK::IOError))
                    .and_then(|mut file| {
                        let mut s = String::new();
                        file.read_to_string(&mut s)
                            .map(|_| s)
                            .map_err_into(MEK::IOError)
                    })
                    .map(Buffer::from)
                    .map(|buffer| Mail(reference, buffer))
            })
    }

    /// Opens a mail by the passed hash
    pub fn open<S: AsRef<str>>(store: &Store, hash: S) -> Result<Option<Mail>> {
        let r = try!(Ref::get_by_hash(store, String::from(hash.as_ref()))
            .map_err_into(MEK::FetchByHashError)
            .map_err_into(MEK::FetchError));

        if r.is_none() {
            return Ok(None);
        }
        let r = r.unwrap();

        r.fs_file()
            .map_err_into(MEK::RefHandlingError)
            .and_then(|path| File::open(path).map_err_into(MEK::IOError))
            .and_then(|mut file| {
                let mut s = String::new();
                file.read_to_string(&mut s)
                    .map(|_| s)
                    .map_err_into(MEK::IOError)
            })
            .map(Buffer::from)
            .map(|buffer| Some(Mail(r, buffer)))
    }

    pub fn get_field(&self, field: &str) -> Result<Option<String>> {
        use mailparse::MailHeader;

        self.1
            .parsed()
            .map_err_into(MEK::MailParsingError)
            .map(|parsed| {
                parsed.headers
                    .iter()
                    .filter(|hdr| hdr.get_key().map(|n| n == field).unwrap_or(false))
                    .next()
                    .and_then(|field| field.get_value().ok())
            })
    }

    pub fn get_from(&self) -> Result<Option<&str>> {
        unimplemented!()
    }

    pub fn get_to(&self) -> Result<Option<&str>> {
        unimplemented!()
    }

    pub fn get_subject(&self) -> Result<Option<&str>> {
        unimplemented!()
    }

    pub fn get_message_id(&self) -> Result<Option<&str>> {
        unimplemented!()
    }

    pub fn get_in_reply_to(&self) -> Result<Option<&str>> {
        unimplemented!()
    }

}
<commit_msg>Impl get_{from,to,subject,message_id,in_reply_to}<commit_after>use std::result::Result as RResult;
use std::path::Path;
use std::path::PathBuf;
use std::fs::File;
use std::io::Read;

use libimagstore::store::{FileLockEntry, Store};
use libimagref::reference::Ref;
use libimagref::flags::RefFlags;

use mailparse::{MailParseError, ParsedMail, parse_mail};

use hasher::MailHasher;
use result::Result;
use error::{MapErrInto, MailErrorKind as MEK};

struct Buffer(String);

impl Buffer {
    pub fn parsed<'a>(&'a self) -> RResult<ParsedMail<'a>, MailParseError> {
        parse_mail(self.0.as_bytes())
    }
}

impl From<String> for Buffer {
    fn from(data: String) -> Buffer {
        Buffer(data)
    }
}

pub struct Mail<'a>(Ref<'a>, Buffer);

impl<'a> Mail<'a> {

    /// Imports a mail from the Path passed
    pub fn import_from_path<P: AsRef<Path>>(store: &Store, p: P) -> Result<Mail> {
        let h = MailHasher::new();
        let f = RefFlags::default().with_content_hashing(true).with_permission_tracking(false);
        let p = PathBuf::from(p.as_ref());

        Ref::create_with_hasher(store, p, f, h)
            .map_err_into(MEK::RefCreationError)
            .and_then(|reference| {
                reference.fs_file()
                    .map_err_into(MEK::RefHandlingError)
                    .and_then(|path| File::open(path).map_err_into(MEK::IOError))
                    .and_then(|mut file| {
                        let mut s = String::new();
                        file.read_to_string(&mut s)
                            .map(|_| s)
                            .map_err_into(MEK::IOError)
                    })
                    .map(Buffer::from)
                    .map(|buffer| Mail(reference, buffer))
            })
    }

    /// Opens a mail by the passed hash
    pub fn open<S: AsRef<str>>(store: &Store, hash: S) -> Result<Option<Mail>> {
        let r = try!(Ref::get_by_hash(store, String::from(hash.as_ref()))
            .map_err_into(MEK::FetchByHashError)
            .map_err_into(MEK::FetchError));

        if r.is_none() {
            return Ok(None);
        }
        let r = r.unwrap();

        r.fs_file()
            .map_err_into(MEK::RefHandlingError)
            .and_then(|path| File::open(path).map_err_into(MEK::IOError))
            .and_then(|mut file| {
                let mut s = String::new();
                file.read_to_string(&mut s)
                    .map(|_| s)
                    .map_err_into(MEK::IOError)
            })
            .map(Buffer::from)
            .map(|buffer| Some(Mail(r, buffer)))
    }

    pub fn get_field(&self, field: &str) -> Result<Option<String>> {
        use mailparse::MailHeader;

        self.1
            .parsed()
            .map_err_into(MEK::MailParsingError)
            .map(|parsed| {
                parsed.headers
                    .iter()
                    .filter(|hdr| hdr.get_key().map(|n| n == field).unwrap_or(false))
                    .next()
                    .and_then(|field| field.get_value().ok())
            })
    }

    pub fn get_from(&self) -> Result<Option<String>> {
        self.get_field("From")
    }

    pub fn get_to(&self) -> Result<Option<String>> {
        self.get_field("To")
    }

    pub fn get_subject(&self) -> Result<Option<String>> {
        self.get_field("Subject")
    }

    pub fn get_message_id(&self) -> Result<Option<String>> {
        self.get_field("Message-ID")
    }

    pub fn get_in_reply_to(&self) -> Result<Option<String>> {
        self.get_field("In-Reply-To")
    }

}
<|endoftext|>


<commit_before>use super::*;

enum Action {
    Next,
    End(TINFLStatus),
}

pub fn decompress_oxide(
    r: &mut tinfl_decompressor,
    in_buf: &[u8],
    out_buf: &mut Cursor<&mut [u8]>,
    flags: u32,
) -> (TINFLStatus, usize, usize) {
    let out_buf_size_mask = if flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF != 0 {
        usize::max_value()
    } else {
        out_buf.get_ref().len() - 1
    };

    if out_buf_size_mask & out_buf_size_mask.wrapping_add(1) != 0 {
        return (TINFLStatus::BadParam, 0, 0)
    }

    let mut in_buf = Cursor::new(in_buf);
    let status = loop {
        match match r.state {
            0 => {
                r.bit_buf = 0;
                r.num_bits = 0;
                r.dist = 0;
                r.counter = 0;
                r.num_extra = 0;
                r.z_header0 = 0;
                r.z_header1 = 0;
                r.z_adler32 = 1;
                r.check_adler32 = 1;
                Action::Next
            },

            1 => {
                // TODO: check that read_exact simplifies to ~one instruction
                let mut one_byte = &mut [0u8; 1];
                match in_buf.read_exact(one_byte) {
                    Err(_) => {
                        Action::End(
                            if flags & TINFL_FLAG_HAS_MORE_INPUT != 0 {
                                TINFLStatus::NeedsMoreInput
                            } else {
                                TINFLStatus::FailedCannotMakeProgress
                            }
                        )
                    },
                    Ok(_) => {
                        r.z_header0 = one_byte[0] as u32;
                        Action::Next
                    },
                }
            },

            _ => unsafe {
                let in_pos = in_buf.position() as usize;
                let mut in_len = in_buf.get_ref().len() - in_pos;
                let out_pos = out_buf.position() as usize;
                let mut out_len = out_buf.get_ref().len() - out_pos;
                let out_buf = out_buf.get_mut();
                let status = tinfl_decompress(
                    r,
                    &in_buf.get_ref()[in_pos],
                    &mut in_len,
                    &mut out_buf[0],
                    &mut out_buf[out_pos],
                    &mut out_len,
                    flags,
                );
                return (status, in_pos + in_len, out_pos + out_len);
            },
        } {
            Action::Next => r.state += 1,
            Action::End(status) => break status,
        }
    };

    if status != TINFLStatus::NeedsMoreInput && status != TINFLStatus::FailedCannotMakeProgress {
        // TODO: give back full unprocessed bytes from bit_buffer
    }

    let need_adler = flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32) != 0;
    if need_adler && status as i32 >= 0 {
        // TODO: check adler
    }

    (status, in_buf.position() as usize, out_buf.position() as usize)
}
<commit_msg>remove match match<commit_after>use super::*;

enum Action {
    Next,
    End(TINFLStatus),
}

pub fn decompress_oxide(
    r: &mut tinfl_decompressor,
    in_buf: &[u8],
    out_buf: &mut Cursor<&mut [u8]>,
    flags: u32,
) -> (TINFLStatus, usize, usize) {
    let out_buf_size_mask = if flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF != 0 {
        usize::max_value()
    } else {
        out_buf.get_ref().len() - 1
    };

    if out_buf_size_mask & out_buf_size_mask.wrapping_add(1) != 0 {
        return (TINFLStatus::BadParam, 0, 0)
    }

    let mut in_buf = Cursor::new(in_buf);
    let status = loop {
        let action = match r.state {
            0 => {
                r.bit_buf = 0;
                r.num_bits = 0;
                r.dist = 0;
                r.counter = 0;
                r.num_extra = 0;
                r.z_header0 = 0;
                r.z_header1 = 0;
                r.z_adler32 = 1;
                r.check_adler32 = 1;
                Action::Next
            },

            1 => {
                // TODO: check that read_exact simplifies to ~one instruction
                let mut one_byte = &mut [0u8; 1];
                match in_buf.read_exact(one_byte) {
                    Err(_) => {
                        Action::End(
                            if flags & TINFL_FLAG_HAS_MORE_INPUT != 0 {
                                TINFLStatus::NeedsMoreInput
                            } else {
                                TINFLStatus::FailedCannotMakeProgress
                            }
                        )
                    },
                    Ok(_) => {
                        r.z_header0 = one_byte[0] as u32;
                        Action::Next
                    },
                }
            },

            _ => unsafe {
                let in_pos = in_buf.position() as usize;
                let mut in_len = in_buf.get_ref().len() - in_pos;
                let out_pos = out_buf.position() as usize;
                let mut out_len = out_buf.get_ref().len() - out_pos;
                let out_buf = out_buf.get_mut();
                let status = tinfl_decompress(
                    r,
                    &in_buf.get_ref()[in_pos],
                    &mut in_len,
                    &mut out_buf[0],
                    &mut out_buf[out_pos],
                    &mut out_len,
                    flags,
                );
                return (status, in_pos + in_len, out_pos + out_len);
            },
        };

        match action {
            Action::Next => r.state += 1,
            Action::End(status) => break status,
        }
    };

    if status != TINFLStatus::NeedsMoreInput && status != TINFLStatus::FailedCannotMakeProgress {
        // TODO: give back full unprocessed bytes from bit_buffer
    }

    let need_adler = flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32) != 0;
    if need_adler && status as i32 >= 0 {
        // TODO: check adler
    }

    (status, in_buf.position() as usize, out_buf.position() as usize)
}
<|endoftext|>


<commit_before>
//! Implements the gerrit structure

use changes;
use error::GGRResult;
use error::GGRError;
use git2::Repository;
use git2::BranchType;
use std::io::{self, Write};
use std::process::Command;
use url;

/// Interface for Gerrit access.
pub trait GerritAccess {
    /// Returns the (path, query) information
    fn build_url(&self) -> (String, String);
}

/// `Gerrit` structure for management of several gerrit endpoints
pub struct Gerrit {
    url: url::Url,
}

impl Gerrit {
    /// Creates a new `Gerrit` object
    ///
    /// The url points to the http endpoint of an gerrit server like
    /// `http://localhost:8080/gerrit`. All other function append to this url there endpoint pathes
    /// and query parameters.
    pub fn new<S>(url: S) -> Gerrit
    where S: Into<String> {
        Gerrit {
            url: url::Url::parse(&url.into()).unwrap(),
        }
    }

    /// Convenient function to query changes from gerrit server
    ///
    /// `querylist` is used as filter for the call to gerrit. `additional_infos` gives some more
    /// information of one Change entity.
    pub fn changes(&mut self, querylist: Option<Vec<String>>, additional_infos: Option<Vec<String>>)
        -> GGRResult<changes::ChangeInfos>
    {
        let mut changes = changes::Changes::new(&self.url);

        changes.querylist = querylist;
        changes.labellist = additional_infos;

        changes.query_changes()
    }

    /// Convenient function to checkout a topic
    pub fn checkout_topic(&mut self, branchname: &str) -> GGRResult<()> {
            if let Ok(main_repo) = Repository::open(".") {
                let mut out_ok: Vec<String> = Vec::new();
                let mut out_ko: Vec<String> = Vec::new();

                println!("try checkout on main repo ... ");
                match checkout_repo(&main_repo, branchname) {
                    Ok(_) => {
                        let mut base = String::new();

                        base.push_str("OK\ngit submodule update ...");
                        let output_submodule_update = Command::new("git")
                            .arg("submodule")
                            .arg("update")
                            .arg("--recursive")
                            .arg("--init")
                            .output()?;

                        if output_submodule_update.stdout.is_empty() {
                            base.push_str(&format!("  submodule update stdout:\n{}", String::from_utf8_lossy(&output_submodule_update.stdout)));
                        }
                        if output_submodule_update.stderr.is_empty() {
                            base.push_str(&format!("  submodule update stderr:\n{}", String::from_utf8_lossy(&output_submodule_update.stderr)));
                        }
                        out_ok.push(base);
                    },
                    Err(m) => out_ko.push(format!("{} -> {}", main_repo.path().to_str().unwrap(), m.to_string().trim())),
                }

                println!();

                if let Ok(smodules) = main_repo.submodules() {
                    print!("try checkout on submodules ");
                    for smodule in smodules {
                        if let Ok(sub_repo) = smodule.open() {
                            match checkout_repo(&sub_repo, branchname) {
                                Ok(_) => {
                                    print!("+");
                                    out_ok.push(format!("{:?}", smodule.name().unwrap_or("unknown repository")))
                                },
                                Err(m) => {
                                    print!("-");
                                    out_ko.push(format!("{:?} -> {}", smodule.name().unwrap_or("unknown repository"), m.to_string().trim()))
                                },
                            };
                            let _ = io::stdout().flush();
                        }
                    }
                    println!("\n");

                    if !out_ko.is_empty() {
                        println!("Not possible to checkout '{}' on this repositories:", branchname);
                        for entry in out_ko {
                            println!("* {}", entry);
                        }
                    }

                    if !out_ok.is_empty() {
                        println!("\nSuccessfull checkout of '{}' on this repositories:", branchname);
                        for entry in out_ok {
                            println!("* {}", entry);
                        }
                    } else {
                        println!("No checkout happened");
                    }
                }
            }
        Ok(())
    }

    /// Conviention function to fetch topic `topicname` to branch `local_branch_name`.
    ///
    /// If branch exists and `force` is true, the branch is moving to new position.
    pub fn fetch_topic(&mut self, topicname: &str, local_branch_name: &str, force: bool, tracking_branch_name: Option<&str>, closed: bool) -> GGRResult<()> {
        let ofields: Vec<String> = vec!("CURRENT_REVISION".into(), "CURRENT_COMMIT".into());

        let mut query_elements = vec![format!("topic:{}", topicname)];
        if !closed {
            query_elements.push(String::from("status:open"));
        }

        let changeinfos = try!(self.changes(Some(query_elements), Some(ofields)));
        let project_tip = changeinfos.project_tip().unwrap();

        // try to fetch topic for main_repo and all submodules
        'next_ptip: for (p_name, p_tip) in project_tip {
            print!("fetch {} for {} ... ", p_name, p_tip);
            // check for root repository
            if let Ok(main_repo) = Repository::open(".") {
                // check changes on root repository
                match fetch_from_repo(&main_repo, &changeinfos, force, local_branch_name, &p_name, &p_tip, tracking_branch_name) {
                    Ok((true,m)) => {
                        println!("{}", m);
                        continue;
                    },
                    Ok((false, m)) => {
                        println!("KO\n  Error: {}", m.trim());
                    },
                    Err(r) => {
                        // hide all other errors
                        let r = r.to_string();
                        if !r.is_empty() {
                            println!("KO\nError: {}", r.to_string().trim());
                        }
                    }
                };

                // check for submodules
                if let Ok(smodules) = main_repo.submodules() {
                    for smodule in smodules {
                        if let Ok(sub_repo) = smodule.open() {
                            match fetch_from_repo(&sub_repo, &changeinfos, force, local_branch_name, &p_name, &p_tip, tracking_branch_name) {
                                Ok((true, m)) => {
                                    println!("{}", m);
                                    continue 'next_ptip;
                                },
                                Ok((false, m)) => {
                                    println!("KO\n  Error: {}", m.trim());
                                    continue;
                                },
                                Err(r) => {
                                    let r = r.to_string();
                                    if !r.is_empty() {
                                                println!("KO\nError: {}", r.to_string().trim());
                                    }
                                }
                            }
                        } else {
                            println!("{} not opened", smodule.name().unwrap());
                        }
                    }
                }
            }
        }

        Ok(())
    }

}

/// convenient function to checkout a `branch` on a `repo`. If `print_status` is true, messages are
/// printed
fn checkout_repo(repo: &Repository, branchname: &str) -> GGRResult<()> {
    if repo.is_bare() {
        return Err(GGRError::General("repository needs to be a workdir and not bare".into()));
    }

    let output_checkout = try!(Command::new("git")
        .current_dir(repo.workdir().unwrap())
        .arg("checkout")
        .arg(branchname)
        .output());

    if output_checkout.status.success() {
        return Ok(());
    }

    Err(GGRError::General(String::from_utf8_lossy(&output_checkout.stderr).into()))
}

/// convenient function to pull a `p_tip` from a `repo`, if `basename(repo.url)` same as `p_name`
/// is.
///
/// returns `true` if something is pulled, and `false` if no pull was executed. The String object
/// is a status message.
fn fetch_from_repo(repo: &Repository, ci: &changes::ChangeInfos, force: bool, local_branch_name: &str, p_name: &str, p_tip: &str, tracking_branch_name: Option<&str>) -> GGRResult<(bool, String)> {
    if repo.is_bare() {
        return Err(GGRError::General(format!("repository path '{:?}' is bare, we need a workdir", repo.path())));
    }

    for remote_name in repo.remotes().unwrap().iter() {
        if let Ok(remote) = repo.find_remote(remote_name.unwrap()) {
            let url = remote.url().unwrap().to_owned();
            let check_project_names = vec!(
                p_name.into(),
                format!("{}.git", p_name)
            );
            if check_project_names.contains(&String::from(url_to_projectname(&url).unwrap())) {
                if let Ok(entity) = ci.entity_from_commit(p_tip) {
                    if let Some(ref cur_rev) = entity.current_revision {
                        if let Some(ref revisions) = entity.revisions {
                            let reference = &revisions[cur_rev].fetch["http"].reference;
                            let force_string = if force {"+"} else { "" };
                            let refspec = format!("{}{}:{}", force_string, reference, local_branch_name);

                            if !force  && repo.find_branch(local_branch_name, BranchType::Local).is_ok() {
                                // Branch exists, but no force
                                return Ok((false, String::from("Branch exists and no force")));
                            }

                            let mut output_fetch = try!(Command::new("git")
                                .current_dir(repo.path())
                                .arg("fetch")
                                .arg(remote.name().unwrap())
                                .arg(refspec)
                                .output());

                            if output_fetch.status.success() {
                                if let Some(tracking_branch) = tracking_branch_name {
                                    let mut output_tracking = try!(Command::new("git")
                                         .current_dir(repo.path())
                                         .arg("branch")
                                         .arg("--set-upstream-to")
                                         .arg(tracking_branch)
                                         .arg(local_branch_name)
                                         .output());
                                    if !output_tracking.stdout.is_empty() {
                                        output_fetch.stdout.append(&mut String::from("\n* ").into_bytes());
                                        output_fetch.stdout.append(&mut output_tracking.stdout);
                                    }
                                    if !output_tracking.stderr.is_empty() {
                                        output_fetch.stdout.append(&mut String::from("\n* ").into_bytes());
                                        output_fetch.stdout.append(&mut output_tracking.stderr);
                                    }
                                }

                                return Ok((true, try!(String::from_utf8(output_fetch.stdout))));
                            }

                            return Ok((false, try!(String::from_utf8(output_fetch.stderr))));
                        } else {
                            return Err(GGRError::General("no revisions".into()));
                        }
                    } else {
                        return Err(GGRError::General("No cur_rev".into()));
                    }
                } else {
                    return Err(GGRError::General("no entity from commit".into()));
                }
            }
        }
    }

    Err(GGRError::General("".into()))
}

/// returns basename of a project from a url (eg.: https://localhost/test -> test)
fn url_to_projectname(url: &str) -> Option<&str> {
    if let Some(last_slash_at) = url.rfind('/') {
        let (_, remote_project_name) = url.split_at(last_slash_at+1);
        return Some(remote_project_name);
    }
    None
}

#[test]
fn test_url_to_projectname() {
    assert_eq!(url_to_projectname("http://das/haus/vom/nikolause"), Some("nikolause"));
    assert_eq!(url_to_projectname("http://."), Some("."));
    assert_eq!(url_to_projectname("nikolause"), None);
    assert_eq!(url_to_projectname("n/i/k/o/lause"), Some("lause"));
    assert_eq!(url_to_projectname(""), None);
}

<commit_msg>reformat status of submodule checkout (fixes #5)<commit_after>
//! Implements the gerrit structure

use changes;
use error::GGRResult;
use error::GGRError;
use git2::Repository;
use git2::BranchType;
use std::io::{self, Write};
use std::process::Command;
use url;

/// Interface for Gerrit access.
pub trait GerritAccess {
    /// Returns the (path, query) information
    fn build_url(&self) -> (String, String);
}

/// `Gerrit` structure for management of several gerrit endpoints
pub struct Gerrit {
    url: url::Url,
}

impl Gerrit {
    /// Creates a new `Gerrit` object
    ///
    /// The url points to the http endpoint of an gerrit server like
    /// `http://localhost:8080/gerrit`. All other function append to this url there endpoint pathes
    /// and query parameters.
    pub fn new<S>(url: S) -> Gerrit
    where S: Into<String> {
        Gerrit {
            url: url::Url::parse(&url.into()).unwrap(),
        }
    }

    /// Convenient function to query changes from gerrit server
    ///
    /// `querylist` is used as filter for the call to gerrit. `additional_infos` gives some more
    /// information of one Change entity.
    pub fn changes(&mut self, querylist: Option<Vec<String>>, additional_infos: Option<Vec<String>>)
        -> GGRResult<changes::ChangeInfos>
    {
        let mut changes = changes::Changes::new(&self.url);

        changes.querylist = querylist;
        changes.labellist = additional_infos;

        changes.query_changes()
    }

    /// Convenient function to checkout a topic
    pub fn checkout_topic(&mut self, branchname: &str) -> GGRResult<()> {
            if let Ok(main_repo) = Repository::open(".") {
                let mut out_ok: Vec<String> = Vec::new();
                let mut out_ko: Vec<String> = Vec::new();

                print!("try checkout on main repo ... ");
                match checkout_repo(&main_repo, branchname) {
                    Ok(_) => {
                        println!("OK");
                        if main_repo.submodules().ok().unwrap_or(Vec::new()).len() > 0 {
                            println!("git submodule update ...");
                            let output_submodule_update = Command::new("git")
                                .arg("submodule")
                                .arg("update")
                                .arg("--recursive")
                                .arg("--init")
                                .output()?;

                            if ! output_submodule_update.stdout.is_empty() {
                                println!("  submodule update stdout:\n{}", String::from_utf8_lossy(&output_submodule_update.stdout));
                            }
                            if ! output_submodule_update.stderr.is_empty() {
                                println!("  submodule update stderr:\n{}", String::from_utf8_lossy(&output_submodule_update.stderr));
                            }
                        }
                    },
                    Err(m) => println!("{} -> {}", main_repo.path().to_str().unwrap(), m.to_string().trim()),
                }

                if let Ok(smodules) = main_repo.submodules() {
                    print!("try checkout submodules: ");
                    if smodules.len() > 0 {
                        for smodule in smodules {
                            if let Ok(sub_repo) = smodule.open() {
                                match checkout_repo(&sub_repo, branchname) {
                                    Ok(_) => {
                                        print!("+");
                                        out_ok.push(format!("{:?}", smodule.name().unwrap_or("unknown repository")))
                                    },
                                    Err(m) => {
                                        print!("-");
                                        out_ko.push(format!("{:?} -> {}", smodule.name().unwrap_or("unknown repository"), m.to_string().trim()))
                                    },
                                };
                                let _ = io::stdout().flush();
                            }
                        }
                        println!("\n");

                        if !out_ko.is_empty() {
                            println!("Not possible to checkout '{}' on this repositories:", branchname);
                            for entry in out_ko {
                                println!("* {}", entry);
                            }
                        }

                        if !out_ok.is_empty() {
                            println!("\nSuccessfull checkout of '{}' on this repositories:", branchname);
                            for entry in out_ok {
                                println!("* {}", entry);
                            }
                        } else {
                            println!("No checkout happened");
                        }
                    } else {
                        println!("no submodules used");
                    }
                }
            }
        Ok(())
    }

    /// Conviention function to fetch topic `topicname` to branch `local_branch_name`.
    ///
    /// If branch exists and `force` is true, the branch is moving to new position.
    pub fn fetch_topic(&mut self, topicname: &str, local_branch_name: &str, force: bool, tracking_branch_name: Option<&str>, closed: bool) -> GGRResult<()> {
        let ofields: Vec<String> = vec!("CURRENT_REVISION".into(), "CURRENT_COMMIT".into());

        let mut query_elements = vec![format!("topic:{}", topicname)];
        if !closed {
            query_elements.push(String::from("status:open"));
        }

        let changeinfos = try!(self.changes(Some(query_elements), Some(ofields)));
        let project_tip = changeinfos.project_tip().unwrap();

        // try to fetch topic for main_repo and all submodules
        'next_ptip: for (p_name, p_tip) in project_tip {
            print!("fetch {} for {} ... ", p_name, p_tip);
            // check for root repository
            if let Ok(main_repo) = Repository::open(".") {
                // check changes on root repository
                match fetch_from_repo(&main_repo, &changeinfos, force, local_branch_name, &p_name, &p_tip, tracking_branch_name) {
                    Ok((true,m)) => {
                        println!("{}", m);
                        continue;
                    },
                    Ok((false, m)) => {
                        println!("KO\n  Error: {}", m.trim());
                    },
                    Err(r) => {
                        // hide all other errors
                        let r = r.to_string();
                        if !r.is_empty() {
                            println!("KO\nError: {}", r.to_string().trim());
                        }
                    }
                };

                // check for submodules
                if let Ok(smodules) = main_repo.submodules() {
                    for smodule in smodules {
                        if let Ok(sub_repo) = smodule.open() {
                            match fetch_from_repo(&sub_repo, &changeinfos, force, local_branch_name, &p_name, &p_tip, tracking_branch_name) {
                                Ok((true, m)) => {
                                    println!("{}", m);
                                    continue 'next_ptip;
                                },
                                Ok((false, m)) => {
                                    println!("KO\n  Error: {}", m.trim());
                                    continue;
                                },
                                Err(r) => {
                                    let r = r.to_string();
                                    if !r.is_empty() {
                                                println!("KO\nError: {}", r.to_string().trim());
                                    }
                                }
                            }
                        } else {
                            println!("{} not opened", smodule.name().unwrap());
                        }
                    }
                }
            }
        }

        Ok(())
    }

}

/// convenient function to checkout a `branch` on a `repo`. If `print_status` is true, messages are
/// printed
fn checkout_repo(repo: &Repository, branchname: &str) -> GGRResult<()> {
    if repo.is_bare() {
        return Err(GGRError::General("repository needs to be a workdir and not bare".into()));
    }

    let output_checkout = try!(Command::new("git")
        .current_dir(repo.workdir().unwrap())
        .arg("checkout")
        .arg(branchname)
        .output());

    if output_checkout.status.success() {
        return Ok(());
    }

    Err(GGRError::General(String::from_utf8_lossy(&output_checkout.stderr).into()))
}

/// convenient function to pull a `p_tip` from a `repo`, if `basename(repo.url)` same as `p_name`
/// is.
///
/// returns `true` if something is pulled, and `false` if no pull was executed. The String object
/// is a status message.
fn fetch_from_repo(repo: &Repository, ci: &changes::ChangeInfos, force: bool, local_branch_name: &str, p_name: &str, p_tip: &str, tracking_branch_name: Option<&str>) -> GGRResult<(bool, String)> {
    if repo.is_bare() {
        return Err(GGRError::General(format!("repository path '{:?}' is bare, we need a workdir", repo.path())));
    }

    for remote_name in repo.remotes().unwrap().iter() {
        if let Ok(remote) = repo.find_remote(remote_name.unwrap()) {
            let url = remote.url().unwrap().to_owned();
            let check_project_names = vec!(
                p_name.into(),
                format!("{}.git", p_name)
            );
            if check_project_names.contains(&String::from(url_to_projectname(&url).unwrap())) {
                if let Ok(entity) = ci.entity_from_commit(p_tip) {
                    if let Some(ref cur_rev) = entity.current_revision {
                        if let Some(ref revisions) = entity.revisions {
                            let reference = &revisions[cur_rev].fetch["http"].reference;
                            let force_string = if force {"+"} else { "" };
                            let refspec = format!("{}{}:{}", force_string, reference, local_branch_name);

                            if !force  && repo.find_branch(local_branch_name, BranchType::Local).is_ok() {
                                // Branch exists, but no force
                                return Ok((false, String::from("Branch exists and no force")));
                            }

                            let mut output_fetch = try!(Command::new("git")
                                .current_dir(repo.path())
                                .arg("fetch")
                                .arg(remote.name().unwrap())
                                .arg(refspec)
                                .output());

                            if output_fetch.status.success() {
                                if let Some(tracking_branch) = tracking_branch_name {
                                    let mut output_tracking = try!(Command::new("git")
                                         .current_dir(repo.path())
                                         .arg("branch")
                                         .arg("--set-upstream-to")
                                         .arg(tracking_branch)
                                         .arg(local_branch_name)
                                         .output());
                                    if !output_tracking.stdout.is_empty() {
                                        output_fetch.stdout.append(&mut String::from("\n* ").into_bytes());
                                        output_fetch.stdout.append(&mut output_tracking.stdout);
                                    }
                                    if !output_tracking.stderr.is_empty() {
                                        output_fetch.stdout.append(&mut String::from("\n* ").into_bytes());
                                        output_fetch.stdout.append(&mut output_tracking.stderr);
                                    }
                                }

                                return Ok((true, try!(String::from_utf8(output_fetch.stdout))));
                            }

                            return Ok((false, try!(String::from_utf8(output_fetch.stderr))));
                        } else {
                            return Err(GGRError::General("no revisions".into()));
                        }
                    } else {
                        return Err(GGRError::General("No cur_rev".into()));
                    }
                } else {
                    return Err(GGRError::General("no entity from commit".into()));
                }
            }
        }
    }

    Err(GGRError::General("".into()))
}

/// returns basename of a project from a url (eg.: https://localhost/test -> test)
fn url_to_projectname(url: &str) -> Option<&str> {
    if let Some(last_slash_at) = url.rfind('/') {
        let (_, remote_project_name) = url.split_at(last_slash_at+1);
        return Some(remote_project_name);
    }
    None
}

#[test]
fn test_url_to_projectname() {
    assert_eq!(url_to_projectname("http://das/haus/vom/nikolause"), Some("nikolause"));
    assert_eq!(url_to_projectname("http://."), Some("."));
    assert_eq!(url_to_projectname("nikolause"), None);
    assert_eq!(url_to_projectname("n/i/k/o/lause"), Some("lause"));
    assert_eq!(url_to_projectname(""), None);
}

<|endoftext|>


<commit_before>#[cfg(all(feature = "rustls_backend_marker", not(feature = "native_tls_backend_marker")))]
use std::{
    error::Error as StdError,
    fmt::{Display, Formatter, Result as FmtResult},
    io::Error as IoError,
};

use async_trait::async_trait;
use async_tungstenite::tungstenite::Message;
use flate2::read::ZlibDecoder;
use futures::stream::SplitSink;
use futures::{SinkExt, StreamExt, TryStreamExt};
use tokio::time::timeout;
use tracing::{instrument, warn};
use url::Url;

use crate::gateway::{GatewayError, WsStream};
use crate::internal::prelude::*;

#[async_trait]
pub trait ReceiverExt {
    async fn recv_json(&mut self) -> Result<Option<Value>>;
    async fn try_recv_json(&mut self) -> Result<Option<Value>>;
}

#[async_trait]
pub trait SenderExt {
    async fn send_json(&mut self, value: &Value) -> Result<()>;
}

#[async_trait]
impl ReceiverExt for WsStream {
    async fn recv_json(&mut self) -> Result<Option<Value>> {
        const TIMEOUT: tokio::time::Duration = tokio::time::Duration::from_millis(500);

        let ws_message = match timeout(TIMEOUT, self.next()).await {
            Ok(Some(Ok(v))) => Some(v),
            Ok(Some(Err(e))) => return Err(e.into()),
            Ok(None) | Err(_) => None,
        };

        convert_ws_message(ws_message)
    }

    async fn try_recv_json(&mut self) -> Result<Option<Value>> {
        convert_ws_message(self.try_next().await.ok().flatten())
    }
}

#[async_trait]
impl SenderExt for SplitSink<WsStream, Message> {
    async fn send_json(&mut self, value: &Value) -> Result<()> {
        Ok(serde_json::to_string(value)
            .map(Message::Text)
            .map_err(Error::from)
            .map(|m| self.send(m))?
            .await?)
    }
}

#[async_trait]
impl SenderExt for WsStream {
    async fn send_json(&mut self, value: &Value) -> Result<()> {
        Ok(serde_json::to_string(value)
            .map(Message::Text)
            .map_err(Error::from)
            .map(|m| self.send(m))?
            .await?)
    }
}

#[inline]
pub(crate) fn convert_ws_message(message: Option<Message>) -> Result<Option<Value>> {
    Ok(match message {
        Some(Message::Binary(bytes)) => {
            serde_json::from_reader(ZlibDecoder::new(&bytes[..])).map(Some).map_err(|why| {
                warn!("Err deserializing bytes: {:?}; bytes: {:?}", why, bytes);

                why
            })?
        },
        Some(Message::Text(payload)) => {
            serde_json::from_str(&payload).map(Some).map_err(|why| {
                warn!("Err deserializing text: {:?}; text: {}", why, payload,);

                why
            })?
        },
        Some(Message::Close(Some(frame))) => {
            return Err(Error::Gateway(GatewayError::Closed(Some(frame))));
        },
        // Ping/Pong message behaviour is internally handled by tungstenite.
        _ => None,
    })
}

/// An error that occurred while connecting over rustls
#[derive(Debug)]
#[non_exhaustive]
#[cfg(all(feature = "rustls_backend_marker", not(feature = "native_tls_backend_marker")))]
pub enum RustlsError {
    /// WebPKI X.509 Certificate Validation Error.
    WebPKI,
    /// An error with the handshake in tungstenite
    HandshakeError,
    /// Standard IO error happening while creating the tcp stream
    Io(IoError),
}

#[cfg(all(feature = "rustls_backend_marker", not(feature = "native_tls_backend_marker")))]
impl From<IoError> for RustlsError {
    fn from(e: IoError) -> Self {
        RustlsError::Io(e)
    }
}

#[cfg(all(feature = "rustls_backend_marker", not(feature = "native_tls_backend_marker")))]
impl Display for RustlsError {
    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
        match self {
            RustlsError::WebPKI => f.write_str("Failed to validate X.509 certificate"),
            RustlsError::HandshakeError => {
                f.write_str("TLS handshake failed when making the websocket connection")
            },
            RustlsError::Io(inner) => Display::fmt(&inner, f),
        }
    }
}

#[cfg(all(feature = "rustls_backend_marker", not(feature = "native_tls_backend_marker")))]
impl StdError for RustlsError {
    fn source(&self) -> Option<&(dyn StdError + 'static)> {
        match self {
            RustlsError::Io(inner) => Some(inner),
            _ => None,
        }
    }
}

#[cfg(all(feature = "rustls_backend_marker", not(feature = "native_tls_backend_marker")))]
#[instrument]
pub(crate) async fn create_rustls_client(url: Url) -> Result<WsStream> {
    let (stream, _) = async_tungstenite::tokio::connect_async_with_config::<Url>(
        url,
        Some(async_tungstenite::tungstenite::protocol::WebSocketConfig {
            max_message_size: None,
            max_frame_size: None,
            max_send_queue: None,
            accept_unmasked_frames: false,
        }),
    )
    .await
    .map_err(|_| RustlsError::HandshakeError)?;

    Ok(stream)
}

#[cfg(feature = "native_tls_backend_marker")]
#[instrument]
pub(crate) async fn create_native_tls_client(url: Url) -> Result<WsStream> {
    let (stream, _) = async_tungstenite::tokio::connect_async_with_config::<Url>(
        url.into(),
        Some(async_tungstenite::tungstenite::protocol::WebSocketConfig {
            max_message_size: None,
            max_frame_size: None,
            max_send_queue: None,
            accept_unmasked_frames: false,
        }),
    )
    .await?;

    Ok(stream)
}
<commit_msg>Conditionally instantiate `WebSocketConfig` to fix tokio 0.2 compilation (#1214)<commit_after>#[cfg(all(feature = "rustls_backend_marker", not(feature = "native_tls_backend_marker")))]
use std::{
    error::Error as StdError,
    fmt::{Display, Formatter, Result as FmtResult},
    io::Error as IoError,
};

use async_trait::async_trait;
use async_tungstenite::tungstenite::Message;
use flate2::read::ZlibDecoder;
use futures::stream::SplitSink;
use futures::{SinkExt, StreamExt, TryStreamExt};
use tokio::time::timeout;
use tracing::{instrument, warn};
use url::Url;

use crate::gateway::{GatewayError, WsStream};
use crate::internal::prelude::*;

#[async_trait]
pub trait ReceiverExt {
    async fn recv_json(&mut self) -> Result<Option<Value>>;
    async fn try_recv_json(&mut self) -> Result<Option<Value>>;
}

#[async_trait]
pub trait SenderExt {
    async fn send_json(&mut self, value: &Value) -> Result<()>;
}

#[async_trait]
impl ReceiverExt for WsStream {
    async fn recv_json(&mut self) -> Result<Option<Value>> {
        const TIMEOUT: tokio::time::Duration = tokio::time::Duration::from_millis(500);

        let ws_message = match timeout(TIMEOUT, self.next()).await {
            Ok(Some(Ok(v))) => Some(v),
            Ok(Some(Err(e))) => return Err(e.into()),
            Ok(None) | Err(_) => None,
        };

        convert_ws_message(ws_message)
    }

    async fn try_recv_json(&mut self) -> Result<Option<Value>> {
        convert_ws_message(self.try_next().await.ok().flatten())
    }
}

#[async_trait]
impl SenderExt for SplitSink<WsStream, Message> {
    async fn send_json(&mut self, value: &Value) -> Result<()> {
        Ok(serde_json::to_string(value)
            .map(Message::Text)
            .map_err(Error::from)
            .map(|m| self.send(m))?
            .await?)
    }
}

#[async_trait]
impl SenderExt for WsStream {
    async fn send_json(&mut self, value: &Value) -> Result<()> {
        Ok(serde_json::to_string(value)
            .map(Message::Text)
            .map_err(Error::from)
            .map(|m| self.send(m))?
            .await?)
    }
}

#[inline]
pub(crate) fn convert_ws_message(message: Option<Message>) -> Result<Option<Value>> {
    Ok(match message {
        Some(Message::Binary(bytes)) => {
            serde_json::from_reader(ZlibDecoder::new(&bytes[..])).map(Some).map_err(|why| {
                warn!("Err deserializing bytes: {:?}; bytes: {:?}", why, bytes);

                why
            })?
        },
        Some(Message::Text(payload)) => {
            serde_json::from_str(&payload).map(Some).map_err(|why| {
                warn!("Err deserializing text: {:?}; text: {}", why, payload,);

                why
            })?
        },
        Some(Message::Close(Some(frame))) => {
            return Err(Error::Gateway(GatewayError::Closed(Some(frame))));
        },
        // Ping/Pong message behaviour is internally handled by tungstenite.
        _ => None,
    })
}

/// An error that occurred while connecting over rustls
#[derive(Debug)]
#[non_exhaustive]
#[cfg(all(feature = "rustls_backend_marker", not(feature = "native_tls_backend_marker")))]
pub enum RustlsError {
    /// WebPKI X.509 Certificate Validation Error.
    WebPKI,
    /// An error with the handshake in tungstenite
    HandshakeError,
    /// Standard IO error happening while creating the tcp stream
    Io(IoError),
}

#[cfg(all(feature = "rustls_backend_marker", not(feature = "native_tls_backend_marker")))]
impl From<IoError> for RustlsError {
    fn from(e: IoError) -> Self {
        RustlsError::Io(e)
    }
}

#[cfg(all(feature = "rustls_backend_marker", not(feature = "native_tls_backend_marker")))]
impl Display for RustlsError {
    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
        match self {
            RustlsError::WebPKI => f.write_str("Failed to validate X.509 certificate"),
            RustlsError::HandshakeError => {
                f.write_str("TLS handshake failed when making the websocket connection")
            },
            RustlsError::Io(inner) => Display::fmt(&inner, f),
        }
    }
}

#[cfg(all(feature = "rustls_backend_marker", not(feature = "native_tls_backend_marker")))]
impl StdError for RustlsError {
    fn source(&self) -> Option<&(dyn StdError + 'static)> {
        match self {
            RustlsError::Io(inner) => Some(inner),
            _ => None,
        }
    }
}

#[cfg(any(feature = "rustls_backend", feature = "native_tls_backend"))]
fn websocket_config() -> async_tungstenite::tungstenite::protocol::WebSocketConfig {
    async_tungstenite::tungstenite::protocol::WebSocketConfig {
        max_message_size: None,
        max_frame_size: None,
        max_send_queue: None,
        accept_unmasked_frames: false,
    }
}

#[cfg(all(
    any(feature = "rustls_tokio_0_2_backend", feature = "native_tls_tokio_0_2_backend"),
    not(any(feature = "rustls_backend", feature = "native_tls_backend"))
))]
fn websocket_config() -> async_tungstenite::tungstenite::protocol::WebSocketConfig {
    async_tungstenite::tungstenite::protocol::WebSocketConfig {
        max_message_size: None,
        max_frame_size: None,
        max_send_queue: None,
    }
}

#[cfg(all(feature = "rustls_backend_marker", not(feature = "native_tls_backend_marker")))]
#[instrument]
pub(crate) async fn create_rustls_client(url: Url) -> Result<WsStream> {
    let (stream, _) =
        async_tungstenite::tokio::connect_async_with_config::<Url>(url, Some(websocket_config()))
            .await
            .map_err(|_| RustlsError::HandshakeError)?;

    Ok(stream)
}

#[cfg(feature = "native_tls_backend_marker")]
#[instrument]
pub(crate) async fn create_native_tls_client(url: Url) -> Result<WsStream> {
    let (stream, _) = async_tungstenite::tokio::connect_async_with_config::<Url>(
        url.into(),
        Some(websocket_config()),
    )
    .await?;

    Ok(stream)
}
<|endoftext|>


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

use crate::LedgerInfo;
use executor::Executor;
use failure::prelude::*;
use futures::{channel::oneshot, Future, FutureExt};
use grpcio::EnvBuilder;
use libra_config::config::NodeConfig;
use libra_logger::prelude::*;
use libra_types::crypto_proxies::ValidatorChangeEventWithProof;
use libra_types::{
    crypto_proxies::{LedgerInfoWithSignatures, ValidatorVerifier},
    transaction::TransactionListWithProof,
};
use network::proto::GetChunkResponse;
use std::{pin::Pin, sync::Arc};
use storage_client::{StorageRead, StorageReadServiceClient};
use vm_runtime::MoveVM;

/// Proxies interactions with execution and storage for state synchronization
pub trait ExecutorProxyTrait: Sync + Send {
    /// Return the latest known version
    fn get_latest_version(&self) -> Pin<Box<dyn Future<Output = Result<u64>> + Send>>;

    /// Return the latest known ledger info
    fn get_latest_ledger_info(&self) -> Pin<Box<dyn Future<Output = Result<LedgerInfo>> + Send>>;

    /// Execute and commit a batch of transactions
    fn execute_chunk(
        &self,
        txn_list_with_proof: TransactionListWithProof,
        ledger_info_with_sigs: LedgerInfoWithSignatures,
    ) -> Pin<Box<dyn Future<Output = Result<()>> + Send>>;

    /// Gets chunk of transactions
    fn get_chunk(
        &self,
        known_version: u64,
        limit: u64,
        target: LedgerInfoWithSignatures,
    ) -> Pin<Box<dyn Future<Output = Result<GetChunkResponse>> + Send>>;

    fn validate_ledger_info(&self, target: &LedgerInfoWithSignatures) -> Result<()>;

    fn get_epoch_proof(&self, start_epoch: u64) -> Result<ValidatorChangeEventWithProof>;
}

pub(crate) struct ExecutorProxy {
    storage_read_client: Arc<StorageReadServiceClient>,
    executor: Arc<Executor<MoveVM>>,
    validator_verifier: ValidatorVerifier,
}

impl ExecutorProxy {
    pub(crate) fn new(executor: Arc<Executor<MoveVM>>, config: &NodeConfig) -> Self {
        let client_env = Arc::new(EnvBuilder::new().name_prefix("grpc-coord-").build());
        let storage_read_client = Arc::new(StorageReadServiceClient::new(
            client_env,
            &config.storage.address,
            config.storage.port,
        ));
        let validator_verifier = config.consensus.consensus_peers.get_validator_verifier();
        Self {
            storage_read_client,
            executor,
            validator_verifier,
        }
    }
}

fn convert_to_future<T: Send + 'static>(
    receiver: oneshot::Receiver<Result<T>>,
) -> Pin<Box<dyn Future<Output = Result<T>> + Send>> {
    async move {
        match receiver.await {
            Ok(Ok(t)) => Ok(t),
            Ok(Err(err)) => Err(format_err!("Failed to process request: {}", err)),
            Err(oneshot::Canceled) => {
                Err(format_err!("Executor Internal error: sender is dropped."))
            }
        }
    }
        .boxed()
}

impl ExecutorProxyTrait for ExecutorProxy {
    fn get_latest_version(&self) -> Pin<Box<dyn Future<Output = Result<u64>> + Send>> {
        let client = Arc::clone(&self.storage_read_client);
        async move {
            let resp = client.get_startup_info_async().await?;
            resp.map(|r| r.ledger_info.version())
                .ok_or_else(|| format_err!("failed to fetch startup info"))
        }
            .boxed()
    }

    fn get_latest_ledger_info(&self) -> Pin<Box<dyn Future<Output = Result<LedgerInfo>> + Send>> {
        let client = Arc::clone(&self.storage_read_client);
        async move { Ok(client.update_to_latest_ledger_async(0, vec![]).await?.1) }.boxed()
    }

    fn execute_chunk(
        &self,
        txn_list_with_proof: TransactionListWithProof,
        ledger_info_with_sigs: LedgerInfoWithSignatures,
    ) -> Pin<Box<dyn Future<Output = Result<()>> + Send>> {
        convert_to_future(
            self.executor
                .execute_and_commit_chunk(txn_list_with_proof, ledger_info_with_sigs),
        )
    }

    fn get_chunk(
        &self,
        known_version: u64,
        limit: u64,
        target: LedgerInfoWithSignatures,
    ) -> Pin<Box<dyn Future<Output = Result<GetChunkResponse>> + Send>> {
        let client = Arc::clone(&self.storage_read_client);
        async move {
            let transactions = client
                .get_transactions_async(
                    known_version + 1,
                    limit,
                    target.ledger_info().version(),
                    false,
                )
                .await?;
            if transactions.transactions.is_empty() {
                error!(
                    "[state sync] can't get {} txns from version {}",
                    limit, known_version
                );
            }
            Ok(GetChunkResponse {
                ledger_info_with_sigs: Some(target.into()),
                txn_list_with_proof: Some(transactions.into()),
            })
        }
            .boxed()
    }

    fn validate_ledger_info(&self, target: &LedgerInfo) -> Result<()> {
        target.verify(&self.validator_verifier)?;
        Ok(())
    }

    fn get_epoch_proof(&self, start_epoch: u64) -> Result<ValidatorChangeEventWithProof> {
        let mut ledger_info_per_epoch = self
            .storage_read_client
            .get_latest_ledger_infos_per_epoch(start_epoch)?;
        // The latest ledger info may not carry the validator set.
        match ledger_info_per_epoch.pop() {
            Some(li) if li.ledger_info().next_validator_set().is_some() => {
                ledger_info_per_epoch.push(li);
            }
            _ => (),
        }
        Ok(ValidatorChangeEventWithProof::new(ledger_info_per_epoch))
    }
}
<commit_msg>[state sync] use synced_state as known version<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::LedgerInfo;
use executor::Executor;
use failure::prelude::*;
use futures::{channel::oneshot, Future, FutureExt};
use grpcio::EnvBuilder;
use libra_config::config::NodeConfig;
use libra_logger::prelude::*;
use libra_types::crypto_proxies::ValidatorChangeEventWithProof;
use libra_types::{
    crypto_proxies::{LedgerInfoWithSignatures, ValidatorVerifier},
    transaction::TransactionListWithProof,
};
use network::proto::GetChunkResponse;
use std::{pin::Pin, sync::Arc};
use storage_client::{StorageRead, StorageReadServiceClient};
use vm_runtime::MoveVM;

/// Proxies interactions with execution and storage for state synchronization
pub trait ExecutorProxyTrait: Sync + Send {
    /// Return the latest known version
    fn get_latest_version(&self) -> Pin<Box<dyn Future<Output = Result<u64>> + Send>>;

    /// Return the latest known ledger info
    fn get_latest_ledger_info(&self) -> Pin<Box<dyn Future<Output = Result<LedgerInfo>> + Send>>;

    /// Execute and commit a batch of transactions
    fn execute_chunk(
        &self,
        txn_list_with_proof: TransactionListWithProof,
        ledger_info_with_sigs: LedgerInfoWithSignatures,
    ) -> Pin<Box<dyn Future<Output = Result<()>> + Send>>;

    /// Gets chunk of transactions
    fn get_chunk(
        &self,
        known_version: u64,
        limit: u64,
        target: LedgerInfoWithSignatures,
    ) -> Pin<Box<dyn Future<Output = Result<GetChunkResponse>> + Send>>;

    fn validate_ledger_info(&self, target: &LedgerInfoWithSignatures) -> Result<()>;

    fn get_epoch_proof(&self, start_epoch: u64) -> Result<ValidatorChangeEventWithProof>;
}

pub(crate) struct ExecutorProxy {
    storage_read_client: Arc<StorageReadServiceClient>,
    executor: Arc<Executor<MoveVM>>,
    validator_verifier: ValidatorVerifier,
}

impl ExecutorProxy {
    pub(crate) fn new(executor: Arc<Executor<MoveVM>>, config: &NodeConfig) -> Self {
        let client_env = Arc::new(EnvBuilder::new().name_prefix("grpc-coord-").build());
        let storage_read_client = Arc::new(StorageReadServiceClient::new(
            client_env,
            &config.storage.address,
            config.storage.port,
        ));
        let validator_verifier = config.consensus.consensus_peers.get_validator_verifier();
        Self {
            storage_read_client,
            executor,
            validator_verifier,
        }
    }
}

fn convert_to_future<T: Send + 'static>(
    receiver: oneshot::Receiver<Result<T>>,
) -> Pin<Box<dyn Future<Output = Result<T>> + Send>> {
    async move {
        match receiver.await {
            Ok(Ok(t)) => Ok(t),
            Ok(Err(err)) => Err(format_err!("Failed to process request: {}", err)),
            Err(oneshot::Canceled) => {
                Err(format_err!("Executor Internal error: sender is dropped."))
            }
        }
    }
        .boxed()
}

impl ExecutorProxyTrait for ExecutorProxy {
    fn get_latest_version(&self) -> Pin<Box<dyn Future<Output = Result<u64>> + Send>> {
        let client = Arc::clone(&self.storage_read_client);
        async move {
            let resp = client.get_startup_info_async().await?;
            resp.map(|r| {
                r.synced_tree_state
                    .map_or(r.ledger_info.version(), |s| s.version)
            })
            .ok_or_else(|| format_err!("failed to fetch startup info"))
        }
            .boxed()
    }

    fn get_latest_ledger_info(&self) -> Pin<Box<dyn Future<Output = Result<LedgerInfo>> + Send>> {
        let client = Arc::clone(&self.storage_read_client);
        async move { Ok(client.update_to_latest_ledger_async(0, vec![]).await?.1) }.boxed()
    }

    fn execute_chunk(
        &self,
        txn_list_with_proof: TransactionListWithProof,
        ledger_info_with_sigs: LedgerInfoWithSignatures,
    ) -> Pin<Box<dyn Future<Output = Result<()>> + Send>> {
        convert_to_future(
            self.executor
                .execute_and_commit_chunk(txn_list_with_proof, ledger_info_with_sigs),
        )
    }

    fn get_chunk(
        &self,
        known_version: u64,
        limit: u64,
        target: LedgerInfoWithSignatures,
    ) -> Pin<Box<dyn Future<Output = Result<GetChunkResponse>> + Send>> {
        let client = Arc::clone(&self.storage_read_client);
        async move {
            let transactions = client
                .get_transactions_async(
                    known_version + 1,
                    limit,
                    target.ledger_info().version(),
                    false,
                )
                .await?;
            if transactions.transactions.is_empty() {
                error!(
                    "[state sync] can't get {} txns from version {}",
                    limit, known_version
                );
            }
            Ok(GetChunkResponse {
                ledger_info_with_sigs: Some(target.into()),
                txn_list_with_proof: Some(transactions.into()),
            })
        }
            .boxed()
    }

    fn validate_ledger_info(&self, target: &LedgerInfo) -> Result<()> {
        target.verify(&self.validator_verifier)?;
        Ok(())
    }

    fn get_epoch_proof(&self, start_epoch: u64) -> Result<ValidatorChangeEventWithProof> {
        let mut ledger_info_per_epoch = self
            .storage_read_client
            .get_latest_ledger_infos_per_epoch(start_epoch)?;
        // The latest ledger info may not carry the validator set.
        match ledger_info_per_epoch.pop() {
            Some(li) if li.ledger_info().next_validator_set().is_some() => {
                ledger_info_per_epoch.push(li);
            }
            _ => (),
        }
        Ok(ValidatorChangeEventWithProof::new(ledger_info_per_epoch))
    }
}
<|endoftext|>


<commit_before>//! Container types

use rustwlc::handle::{WlcView, WlcOutput};

/// A handle to either a view or output
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Handle {
    View(WlcView),
    Output(WlcOutput)
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ContainerType {
    /// Root container, only one exists
    Root,
    /// WlcOutput/Monitor
    Output,
    /// A workspace
    Workspace,
    /// A Container, houses views and other containers
    Container,
    /// A view (window)
    View
}

/// Layout mode for a container
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Layout {
    Horizontal,
    Vertical,
    Stacked,
    Tabbed,
    Floating
}

/// Represents an item in the container tree.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Container {
    /// Root node of the container
    Root,
    /// Output
    Output {
        /// Handle to the wlc
        handle: WlcOutput
    },
    /// Workspace
    Workspace {
        /// Name of the workspace
        name: String,
        /// Whether the workspace is focused
        ///
        /// Multiple workspaces can be focused
        focused: bool
    },
    /// Container
    Container {
        /// How the container is layed out
        layout: Layout,
        /// Whether the container is visible
        visible: bool,
        /// If the container is focused
        focused: bool,
        /// If the container is floating
        floating: bool,
    },
    /// View or window
    View {
        /// The wlc handle to the view
        handle: WlcView,
        /// Whether this view is visible
        visible: bool,
        /// Whether this view is focused
        focused: bool,
        /// Whether this view is floating
        floating: bool,
    }
}

impl Container {
    pub fn get_type(&self) -> ContainerType {
        match *self {
            Container::Root => ContainerType::Root,
            Container::Output { .. } => ContainerType::Output,
            Container::Workspace { .. } => ContainerType::Workspace,
            Container::Container { .. } => ContainerType::Container,
            Container::View { .. } => ContainerType::View
        }
    }
}
<commit_msg>Add constructor methods for Container<commit_after>//! Container types

use rustwlc::handle::{WlcView, WlcOutput};

/// A handle to either a view or output
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Handle {
    View(WlcView),
    Output(WlcOutput)
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ContainerType {
    /// Root container, only one exists
    Root,
    /// WlcOutput/Monitor
    Output,
    /// A workspace
    Workspace,
    /// A Container, houses views and other containers
    Container,
    /// A view (window)
    View
}

/// Layout mode for a container
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Layout {
    Horizontal,
    Vertical,
    Stacked,
    Tabbed,
    Floating
}

/// Represents an item in the container tree.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Container {
    /// Root node of the container
    Root,
    /// Output
    Output {
        /// Handle to the wlc
        handle: WlcOutput
    },
    /// Workspace
    Workspace {
        /// Name of the workspace
        name: String,
        /// Whether the workspace is focused
        ///
        /// Multiple workspaces can be focused
        focused: bool
    },
    /// Container
    Container {
        /// How the container is layed out
        layout: Layout,
        /// Whether the container is visible
        visible: bool,
        /// If the container is focused
        focused: bool,
        /// If the container is floating
        floating: bool,
    },
    /// View or window
    View {
        /// The wlc handle to the view
        handle: WlcView,
        /// Whether this view is visible
        visible: bool,
        /// Whether this view is focused
        focused: bool,
        /// Whether this view is floating
        floating: bool,
    }
}

impl Container {
    /// Creates a new root container
    pub fn new_root() -> Container {
        Container::Root
    }

    /// Creates a new output container with the given output
    pub fn new_output(handle: WlcOutput) -> Container {
        Container::Output {
            handle: handle
        }
    }

    /// Creates a new workspace container with the given name
    pub fn new_workspace(name: String) -> Container {
        Container::Workspace {
            name: name, focused: false
        }
    }

    /// Creates a new container
    pub fn new_container() -> Container {
        Container::Container {
            layout: Layout::Horizontal, // default layout?
            visible: false,
            focused: false,
            floating: false
        }
    }

    /// Creates a new view container with the given handle
    pub fn new_view(handle: WlcView) -> Container {
        Container::View {
            handle: handle,
            visible: false,
            focused: false,
            floating: false
        }
    }

    /// Gets the type of this container
    pub fn get_type(&self) -> ContainerType {
        match *self {
            Container::Root => ContainerType::Root,
            Container::Output { .. } => ContainerType::Output,
            Container::Workspace { .. } => ContainerType::Workspace,
            Container::Container { .. } => ContainerType::Container,
            Container::View { .. } => ContainerType::View
        }
    }
}
<|endoftext|>


<commit_before>use core::marker;

use {Future, Poll};

/// A future which is never resolved.
///
/// This future can be created with the `empty` function.
pub struct Empty<T, E> {
    _data: marker::PhantomData<(T, E)>,
}

/// Creates a future which never resolves, representing a computation that never
/// finishes.
///
/// The returned future will never resolve with a success but is still
/// susceptible to cancellation. That is, if a callback is scheduled on the
/// returned future, it is only run once the future is dropped (canceled).
pub fn empty<T, E>() -> Empty<T, E> {
    Empty { _data: marker::PhantomData }
}

impl<T, E> Future for Empty<T, E> {
    type Item = T;
    type Error = E;

    fn poll(&mut self) -> Poll<T, E> {
        Poll::NotReady
    }
}
<commit_msg>Implement Stream for Empty<commit_after>use core::marker;

use {Future, Poll};
use stream::Stream;

/// A future which is never resolved.
///
/// This future can be created with the `empty` function.
pub struct Empty<T, E> {
    _data: marker::PhantomData<(T, E)>,
}

/// Creates a future which never resolves, representing a computation that never
/// finishes.
///
/// The returned future will never resolve with a success but is still
/// susceptible to cancellation. That is, if a callback is scheduled on the
/// returned future, it is only run once the future is dropped (canceled).
pub fn empty<T, E>() -> Empty<T, E> {
    Empty { _data: marker::PhantomData }
}

impl<T, E> Future for Empty<T, E> {
    type Item = T;
    type Error = E;

    fn poll(&mut self) -> Poll<T, E> {
        Poll::NotReady
    }
}

impl<T, E> Stream for Empty<T, E>
    where T: Send + 'static,
          E: Send + 'static
{
    type Item = T;
    type Error = E;

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        Poll::Ok(None)
    }
}
<|endoftext|>