// Path: src/main/java/mirror/Utils.java
// public static MirrorStub withTimeout(MirrorStub s) {
//   // over VPN, ~100k files can take 30 seconds.
//   return s.withDeadlineAfter(10, TimeUnit.MINUTES);
// }
// 
// Path: src/main/java/mirror/tasks/TaskFactory.java
// public interface TaskFactory {
// 
//   default TaskHandle runTask(TaskLogic logic) {
//     return runTask(logic, null);
//   }
// 
//   TaskHandle runTask(TaskLogic logic, Runnable onFailure);
// 
//   void stopTask(TaskLogic logic);
// 
//   default TaskPool newTaskPool() {
//     return new TaskPool(this);
//   }
// 
// }
// 
// Path: src/main/java/mirror/tasks/TaskLogic.java
// public interface TaskLogic {
// 
//   /** Run one interaction, and return how long the task wants to sleep. */
//   Duration runOneLoop() throws InterruptedException;
// 
//   /** Called on the task thread, before we start calling {@link #runOneLoop()} in a loop. */
//   default void onStart() throws InterruptedException {
//   }
// 
//   default void onFailure() throws InterruptedException {
//   }
// 
//   /** Called on the task thread, after we've interrupted/stopped calling {@link #runOneLoop()}. */
//   default void onStop() throws InterruptedException {
//   }
// 
//   /**
//    * Called off the task thread, when we're trying to interrupt the task.
//    *
//    * Most threads shouldn't need this because they'll shut down
//    * when we call thread.interrupt().
//    */
//   default void onInterrupt() {
//   }
// 
//   default String getName() {
//     String name = getClass().getSimpleName();
//     // lambdas don't have simple names
//     if (name.equals("")) {
//       name = StringUtils.substringAfterLast(getClass().getName(), ".");
//     }
//     return name;
//   }
// }


import static mirror.Utils.withTimeout;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.time.Duration;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.util.concurrent.SettableFuture;
import io.grpc.ManagedChannel;
import io.grpc.stub.ClientCallStreamObserver;
import io.grpc.stub.ClientResponseObserver;
import io.grpc.stub.StreamObserver;
import mirror.MirrorGrpc.MirrorStub;
import mirror.tasks.TaskFactory;
import mirror.tasks.TaskLogic;
package mirror;






public class MirrorClient {

  private static final Logger log = LoggerFactory.getLogger(MirrorClient.class);

  private final MirrorPaths paths;
// Path: src/main/java/mirror/Utils.java
// public static MirrorStub withTimeout(MirrorStub s) {
//   // over VPN, ~100k files can take 30 seconds.
//   return s.withDeadlineAfter(10, TimeUnit.MINUTES);
// }
// 
// Path: src/main/java/mirror/tasks/TaskFactory.java
// public interface TaskFactory {
// 
//   default TaskHandle runTask(TaskLogic logic) {
//     return runTask(logic, null);
//   }
// 
//   TaskHandle runTask(TaskLogic logic, Runnable onFailure);
// 
//   void stopTask(TaskLogic logic);
// 
//   default TaskPool newTaskPool() {
//     return new TaskPool(this);
//   }
// 
// }
// 
// Path: src/main/java/mirror/tasks/TaskLogic.java
// public interface TaskLogic {
// 
//   /** Run one interaction, and return how long the task wants to sleep. */
//   Duration runOneLoop() throws InterruptedException;
// 
//   /** Called on the task thread, before we start calling {@link #runOneLoop()} in a loop. */
//   default void onStart() throws InterruptedException {
//   }
// 
//   default void onFailure() throws InterruptedException {
//   }
// 
//   /** Called on the task thread, after we've interrupted/stopped calling {@link #runOneLoop()}. */
//   default void onStop() throws InterruptedException {
//   }
// 
//   /**
//    * Called off the task thread, when we're trying to interrupt the task.
//    *
//    * Most threads shouldn't need this because they'll shut down
//    * when we call thread.interrupt().
//    */
//   default void onInterrupt() {
//   }
// 
//   default String getName() {
//     String name = getClass().getSimpleName();
//     // lambdas don't have simple names
//     if (name.equals("")) {
//       name = StringUtils.substringAfterLast(getClass().getName(), ".");
//     }
//     return name;
//   }
// }


// Path: src/main/java/mirror/MirrorClient.java
import static mirror.Utils.withTimeout;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.time.Duration;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.util.concurrent.SettableFuture;
import io.grpc.ManagedChannel;
import io.grpc.stub.ClientCallStreamObserver;
import io.grpc.stub.ClientResponseObserver;
import io.grpc.stub.StreamObserver;
import mirror.MirrorGrpc.MirrorStub;
import mirror.tasks.TaskFactory;
import mirror.tasks.TaskLogic;

package mirror;






public class MirrorClient {

  private static final Logger log = LoggerFactory.getLogger(MirrorClient.class);

  private final MirrorPaths paths;
  private final TaskFactory taskFactory;