Scp.java

package com.github.dakusui.actionunit.actions.cmd.unix;

import com.github.dakusui.actionunit.actions.cmd.CommandLineComposer;
import com.github.dakusui.actionunit.actions.cmd.Commander;
import com.github.dakusui.actionunit.actions.cmd.CommanderConfig;
import com.github.dakusui.actionunit.core.Context;
import com.github.dakusui.actionunit.core.context.ContextFunction;
import com.github.dakusui.printables.PrintableFunction;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;

import static com.github.dakusui.actionunit.core.context.ContextFunctions.immediateOf;
import static com.github.dakusui.actionunit.utils.Checks.requireState;
import static java.util.Objects.requireNonNull;

public class Scp extends Commander<Scp> {
  private ContextFunction<Target>         destination;
  private List<Function<Context, Target>> files;
  private Function<String, SshOptions>    sshOptionsResolver;

  public Scp(CommanderConfig config) {
    super(config, "scp");
    this.files = new LinkedList<>();
    sshOptionsResolver(config.sshOptionsResolver());
  }

  public Scp sshOptionsResolver(Function<String, SshOptions> resolver) {
    this.sshOptionsResolver = requireNonNull(resolver);
    return this;
  }

  public Scp recursive() {
    return this.addOption("-r");
  }

  public Scp file(Function<Context, Target> target) {
    this.files.add(requireNonNull(target));
    return this;
  }

  public Scp file(Target target) {
    return this.file(immediateOf(requireNonNull(target)));
  }

  public Scp to(Target target) {
    this.destination = immediateOf(requireNonNull(target));
    return this;
  }

  @Override
  public CommandLineComposer buildCommandLineComposer() {
    Scp cloned = this.clone();
    CommandLineComposer.Builder commandLineComposerBuilder = cloned.commandLineComposerBuilderIfSet();
    Function<Target, String> formatTarget = PrintableFunction.of(Target::format).describe("Target::format");
    for (Function<Context, Target> each : files) {
      commandLineComposerBuilder
          .append(" ", false)
          .append(each.andThen(formatTarget), true);
    }
    return commandLineComposerBuilder
        .append(" ", false)
        .append(requireState(Objects::nonNull, cloned.destination).andThen(formatTarget), true)
        .build();
  }

  @Override
  public Scp clone() {
    Scp ret = super.clone();
    ret.sshOptionsResolver.apply("{remotehost}").formatOptionsWith(SshOptions.Formatter.forScp()).forEach(ret::addOption);
    ret.files = new ArrayList<>(ret.files);
    return ret;
  }

  public interface Target {
    interface Account {
      Optional<String> user();

      String host();

      default String format() {
        return user()
            .map(v -> String.format("%s@%s", v, host()))
            .orElseGet(this::host);
      }

      static Account create(String user, String host) {
        requireNonNull(user);
        requireNonNull(host);
        return new Account() {
          @Override
          public Optional<String> user() {
            return Optional.of(user);
          }

          @Override
          public String host() {
            return host;
          }

          @Override
          public String toString() {
            return format();
          }
        };
      }

      static Account create(String host) {
        requireNonNull(host);
        return new Account() {
          @Override
          public Optional<String> user() {
            return Optional.empty();
          }

          @Override
          public String host() {
            return host;
          }

          @Override
          public String toString() {
            return format();
          }
        };
      }
    }

    Optional<Account> account();

    String path();

    default String format() {
      return account().isPresent() ?
          String.format("%s:%s", account().get().format(), path()) :
          path();
    }

    static Target of(String path) {
      requireNonNull(path);
      return new Target() {
        @Override
        public Optional<Account> account() {
          return Optional.empty();
        }

        @Override
        public String path() {
          return path;
        }

        @Override
        public String toString() {
          return format();
        }
      };
    }

    static Target of(String host, String path) {
      requireNonNull(host);
      requireNonNull(path);
      return new Target() {
        @Override
        public Optional<Account> account() {
          return Optional.of(Account.create(host));
        }

        @Override
        public String path() {
          return path;
        }

        @Override
        public String toString() {
          return format();
        }
      };
    }

    static Target of(String user, String host, String path) {
      requireNonNull(user);
      requireNonNull(host);
      requireNonNull(path);
      return new Target() {
        @Override
        public Optional<Account> account() {
          return Optional.of(Account.create(user, host));
        }

        @Override
        public String path() {
          return path;
        }

        @Override
        public String toString() {
          return format();
        }
      };
    }
  }
}