use std::{
ffi::OsStr,
marker::PhantomData,
path::{Path, PathBuf},
process::{Child, Command, Stdio},
};
use super::{ExclusiveOption, SubCommand, Unselected};
use crate::global::GlobalOpts;
use crate::spawn::ParameterizedSpawn;
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct ForceSet;
impl ExclusiveOption for ForceSet {
fn inject_args(&self, command: &mut Command) {
command.arg("-f");
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct TemplateSet {
template: String,
}
impl ExclusiveOption for TemplateSet {
fn inject_args(&self, command: &mut Command) {
command.arg("-t").arg(&self.template);
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct TypeSet {
workspace_type: WorkSpaceType,
}
impl ExclusiveOption for TypeSet {
fn inject_args(&self, command: &mut Command) {
command.arg("-T").arg(self.workspace_type.as_str());
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub enum WorkSpaceType {
#[default]
Writable,
Readonly,
Partitioned,
PartitionedJnl,
Graph,
}
impl WorkSpaceType {
fn as_str(&self) -> &'static str {
match self {
WorkSpaceType::Writable => "writeable",
WorkSpaceType::Readonly => "readonly",
WorkSpaceType::Partitioned => "partitioned",
WorkSpaceType::PartitionedJnl => "partitioned-jnl",
WorkSpaceType::Graph => "graph",
}
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct GeneralMode;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ShelvedHandling {
Keep,
Delete,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct ServerIdSet;
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct ShelvedKept;
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct ShelvedDeleted;
#[doc(hidden)]
pub trait ServerIdState {
const SET: bool;
}
impl ServerIdState for Unselected {
const SET: bool = false;
}
impl ServerIdState for ServerIdSet {
const SET: bool = true;
}
#[doc(hidden)]
pub trait ShelvedState {
fn handling() -> Option<ShelvedHandling>;
fn inject_shelved(command: &mut Command);
}
impl ShelvedState for Unselected {
fn handling() -> Option<ShelvedHandling> {
None
}
fn inject_shelved(_: &mut Command) {}
}
impl ShelvedState for ShelvedKept {
fn handling() -> Option<ShelvedHandling> {
Some(ShelvedHandling::Keep)
}
fn inject_shelved(command: &mut Command) {
command.arg("-Fs");
}
}
impl ShelvedState for ShelvedDeleted {
fn handling() -> Option<ShelvedHandling> {
Some(ShelvedHandling::Delete)
}
fn inject_shelved(command: &mut Command) {
command.arg("-Fd");
}
}
#[doc(hidden)]
pub trait ServerIdCompatible: ShelvedState {}
impl ServerIdCompatible for Unselected {}
impl ServerIdCompatible for ShelvedKept {}
#[doc(hidden)]
pub trait ValidDeleteCombination {}
impl ValidDeleteCombination for (Unselected, Unselected, Unselected) {}
impl ValidDeleteCombination for (Unselected, Unselected, ForceSet) {}
impl ValidDeleteCombination for (Unselected, ShelvedKept, ForceSet) {}
impl ValidDeleteCombination for (Unselected, ShelvedDeleted, ForceSet) {}
impl ValidDeleteCombination for (ServerIdSet, Unselected, ForceSet) {}
impl ValidDeleteCombination for (ServerIdSet, ShelvedKept, ForceSet) {}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct DeleteMode<I = Unselected, S = Unselected> {
server_id: Option<String>,
serverid_state: PhantomData<I>,
shelved: S,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct StdoutMode;
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct StdoutOutput {
change: Option<String>,
}
impl ExclusiveOption for StdoutOutput {
fn inject_args(&self, command: &mut Command) {
if let Some(change) = &self.change {
command.arg("-c").arg(change);
}
command.arg("-o");
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct StreamMode<O = Unselected> {
stream: String,
output: O,
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct StreamSwitch {
stream: String,
}
impl ExclusiveOption for StreamSwitch {
fn inject_args(&self, command: &mut Command) {
command.arg("-S").arg(&self.stream);
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct TemplateSwitch {
template: String,
}
impl ExclusiveOption for TemplateSwitch {
fn inject_args(&self, command: &mut Command) {
command.arg("-t").arg(&self.template);
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct SwitchViewMode<V> {
view: V,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct StdinMode;
pub trait ClientMode<F, T, W> {
fn inject_mode_args(&self, command: &mut Command, force: &F, template: &T, workspace_type: &W);
}
impl<F, T, W> ClientMode<F, T, W> for GeneralMode
where
F: ExclusiveOption,
T: ExclusiveOption,
W: ExclusiveOption,
{
fn inject_mode_args(&self, command: &mut Command, force: &F, template: &T, workspace_type: &W) {
force.inject_args(command);
template.inject_args(command);
workspace_type.inject_args(command);
}
}
impl<I, S, F> ClientMode<F, Unselected, Unselected> for DeleteMode<I, S>
where
I: ServerIdState,
S: ShelvedState,
F: ExclusiveOption,
{
fn inject_mode_args(&self, command: &mut Command, force: &F, _: &Unselected, _: &Unselected) {
command.arg("-d");
force.inject_args(command);
if I::SET
&& let Some(server_id) = &self.server_id
{
command.arg(format!("--serverid={server_id}"));
}
S::inject_shelved(command);
}
}
impl<T, W> ClientMode<Unselected, T, W> for StdoutMode
where
T: ExclusiveOption,
W: ExclusiveOption,
{
fn inject_mode_args(
&self,
command: &mut Command,
_: &Unselected,
template: &T,
workspace_type: &W,
) {
command.arg("-o");
template.inject_args(command);
workspace_type.inject_args(command);
}
}
impl<O> ClientMode<Unselected, Unselected, Unselected> for StreamMode<O>
where
O: ExclusiveOption,
{
fn inject_mode_args(
&self,
command: &mut Command,
_: &Unselected,
_: &Unselected,
_: &Unselected,
) {
command.arg("-S").arg(&self.stream);
self.output.inject_args(command);
}
}
impl<F> ClientMode<F, Unselected, Unselected> for SwitchViewMode<StreamSwitch>
where
F: ExclusiveOption,
{
fn inject_mode_args(&self, command: &mut Command, force: &F, _: &Unselected, _: &Unselected) {
command.arg("-s");
force.inject_args(command);
self.view.inject_args(command);
}
}
impl<F> ClientMode<F, Unselected, Unselected> for SwitchViewMode<TemplateSwitch>
where
F: ExclusiveOption,
{
fn inject_mode_args(&self, command: &mut Command, force: &F, _: &Unselected, _: &Unselected) {
command.arg("-s");
force.inject_args(command);
self.view.inject_args(command);
}
}
impl<F> ClientMode<F, Unselected, Unselected> for StdinMode
where
F: ExclusiveOption,
{
fn inject_mode_args(&self, command: &mut Command, force: &F, _: &Unselected, _: &Unselected) {
command.arg("-i");
force.inject_args(command);
}
}
#[derive(Debug, Clone, Default)]
pub struct Client<M = GeneralMode, F = Unselected, T = Unselected, W = Unselected> {
bin: PathBuf,
global_opts: GlobalOpts,
mode: M,
force: F,
template: T,
workspace_type: W,
}
impl<M, F, T, W> SubCommand for Client<M, F, T, W>
where
M: ClientMode<F, T, W>,
{
fn name(&self) -> &str {
"client"
}
fn inject_local_args(&self, command: &mut Command) {
self.mode
.inject_mode_args(command, &self.force, &self.template, &self.workspace_type);
}
fn global_opts(&self) -> Option<&GlobalOpts> {
Some(&self.global_opts)
}
}
impl<F, T, W> ParameterizedSpawn<()> for Client<GeneralMode, F, T, W>
where
F: ExclusiveOption,
T: ExclusiveOption,
W: ExclusiveOption,
{
type Output = Child;
type Error = std::io::Error;
fn spawn_with(&mut self, (): ()) -> Result<Self::Output, Self::Error> {
self.setup_command(&self.bin)
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
}
}
impl<F, T, W, I> ParameterizedSpawn<(I,)> for Client<GeneralMode, F, T, W>
where
F: ExclusiveOption,
T: ExclusiveOption,
W: ExclusiveOption,
I: AsRef<OsStr>,
{
type Output = Child;
type Error = std::io::Error;
fn spawn_with(&mut self, (name,): (I,)) -> Result<Self::Output, Self::Error> {
self.setup_command(&self.bin)
.arg(name)
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
}
}
impl<T, W> ParameterizedSpawn<()> for Client<StdoutMode, Unselected, T, W>
where
T: ExclusiveOption,
W: ExclusiveOption,
{
type Output = Child;
type Error = std::io::Error;
fn spawn_with(&mut self, (): ()) -> Result<Self::Output, Self::Error> {
self.setup_command(&self.bin)
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
}
}
impl<T, W, I> ParameterizedSpawn<(I,)> for Client<StdoutMode, Unselected, T, W>
where
T: ExclusiveOption,
W: ExclusiveOption,
I: AsRef<OsStr>,
{
type Output = Child;
type Error = std::io::Error;
fn spawn_with(&mut self, (name,): (I,)) -> Result<Self::Output, Self::Error> {
self.setup_command(&self.bin)
.arg(name)
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
}
}
impl<I, S, F, A> ParameterizedSpawn<(A,)> for Client<DeleteMode<I, S>, F, Unselected, Unselected>
where
I: ServerIdState,
S: ShelvedState,
F: ExclusiveOption,
(I, S, F): ValidDeleteCombination,
A: AsRef<OsStr>,
{
type Output = Child;
type Error = std::io::Error;
fn spawn_with(&mut self, (name,): (A,)) -> Result<Self::Output, Self::Error> {
self.setup_command(&self.bin)
.arg(name)
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
}
}
impl<O> ParameterizedSpawn<()> for Client<StreamMode<O>, Unselected, Unselected, Unselected>
where
O: ExclusiveOption,
{
type Output = Child;
type Error = std::io::Error;
fn spawn_with(&mut self, (): ()) -> Result<Self::Output, Self::Error> {
self.setup_command(&self.bin)
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
}
}
impl<O, I> ParameterizedSpawn<(I,)> for Client<StreamMode<O>, Unselected, Unselected, Unselected>
where
O: ExclusiveOption,
I: AsRef<OsStr>,
{
type Output = Child;
type Error = std::io::Error;
fn spawn_with(&mut self, (name,): (I,)) -> Result<Self::Output, Self::Error> {
self.setup_command(&self.bin)
.arg(name)
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
}
}
impl<V, F> ParameterizedSpawn<()> for Client<SwitchViewMode<V>, F, Unselected, Unselected>
where
SwitchViewMode<V>: ClientMode<F, Unselected, Unselected>,
F: ExclusiveOption,
{
type Output = Child;
type Error = std::io::Error;
fn spawn_with(&mut self, (): ()) -> Result<Self::Output, Self::Error> {
self.setup_command(&self.bin)
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
}
}
impl<V, F, I> ParameterizedSpawn<(I,)> for Client<SwitchViewMode<V>, F, Unselected, Unselected>
where
SwitchViewMode<V>: ClientMode<F, Unselected, Unselected>,
F: ExclusiveOption,
I: AsRef<OsStr>,
{
type Output = Child;
type Error = std::io::Error;
fn spawn_with(&mut self, (name,): (I,)) -> Result<Self::Output, Self::Error> {
self.setup_command(&self.bin)
.arg(name)
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
}
}
impl<F> ParameterizedSpawn<()> for Client<StdinMode, F, Unselected, Unselected>
where
F: ExclusiveOption,
{
type Output = Child;
type Error = std::io::Error;
fn spawn_with(&mut self, (): ()) -> Result<Self::Output, Self::Error> {
self.setup_command(&self.bin)
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
}
}
impl<F> ParameterizedSpawn<(Stdio,)> for Client<StdinMode, F, Unselected, Unselected>
where
F: ExclusiveOption,
{
type Output = Child;
type Error = std::io::Error;
fn spawn_with(&mut self, (stdio,): (Stdio,)) -> Result<Self::Output, Self::Error> {
self.setup_command(&self.bin)
.stdin(stdio)
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
}
}
impl Client<GeneralMode, Unselected, Unselected, Unselected> {
pub fn new(bin: impl Into<PathBuf>, global_opts: GlobalOpts) -> Self {
Self {
bin: bin.into(),
global_opts,
mode: GeneralMode,
force: Unselected,
template: Unselected,
workspace_type: Unselected,
}
}
}
impl<M, F, T, W> Client<M, F, T, W> {
pub fn get_bin(&self) -> &Path {
&self.bin
}
pub fn set_bin(&mut self, bin: impl Into<PathBuf>) -> &mut Self {
self.bin = bin.into();
self
}
pub fn bin(mut self, bin: impl Into<PathBuf>) -> Self {
self.set_bin(bin);
self
}
pub fn get_global_opts(&self) -> &GlobalOpts {
&self.global_opts
}
pub fn set_global_opts(&mut self, v: GlobalOpts) -> &mut Self {
self.global_opts = v;
self
}
pub fn global_opts(mut self, v: GlobalOpts) -> Self {
self.set_global_opts(v);
self
}
}
impl<T, W> Client<GeneralMode, Unselected, T, W> {
pub fn force(self) -> Client<GeneralMode, ForceSet, T, W> {
Client {
bin: self.bin,
global_opts: self.global_opts,
mode: self.mode,
force: ForceSet,
template: self.template,
workspace_type: self.workspace_type,
}
}
}
impl<I, S> Client<DeleteMode<I, S>, Unselected, Unselected, Unselected>
where
I: ServerIdState,
S: ShelvedState,
{
pub fn force(self) -> Client<DeleteMode<I, S>, ForceSet, Unselected, Unselected> {
Client {
bin: self.bin,
global_opts: self.global_opts,
mode: self.mode,
force: ForceSet,
template: self.template,
workspace_type: self.workspace_type,
}
}
}
impl<V> Client<SwitchViewMode<V>, Unselected, Unselected, Unselected> {
pub fn force(self) -> Client<SwitchViewMode<V>, ForceSet, Unselected, Unselected> {
Client {
bin: self.bin,
global_opts: self.global_opts,
mode: self.mode,
force: ForceSet,
template: self.template,
workspace_type: self.workspace_type,
}
}
}
impl Client<StdinMode, Unselected, Unselected, Unselected> {
pub fn force(self) -> Client<StdinMode, ForceSet, Unselected, Unselected> {
Client {
bin: self.bin,
global_opts: self.global_opts,
mode: self.mode,
force: ForceSet,
template: self.template,
workspace_type: self.workspace_type,
}
}
}
impl<F, W> Client<GeneralMode, F, Unselected, W> {
pub fn template(self, template: impl Into<String>) -> Client<GeneralMode, F, TemplateSet, W> {
Client {
bin: self.bin,
global_opts: self.global_opts,
mode: self.mode,
force: self.force,
template: TemplateSet {
template: template.into(),
},
workspace_type: self.workspace_type,
}
}
}
impl<W> Client<StdoutMode, Unselected, Unselected, W> {
pub fn template(
self,
template: impl Into<String>,
) -> Client<StdoutMode, Unselected, TemplateSet, W> {
Client {
bin: self.bin,
global_opts: self.global_opts,
mode: self.mode,
force: self.force,
template: TemplateSet {
template: template.into(),
},
workspace_type: self.workspace_type,
}
}
}
impl<F, T> Client<GeneralMode, F, T, Unselected> {
pub fn workspace_type(
self,
workspace_type: WorkSpaceType,
) -> Client<GeneralMode, F, T, TypeSet> {
Client {
bin: self.bin,
global_opts: self.global_opts,
mode: self.mode,
force: self.force,
template: self.template,
workspace_type: TypeSet { workspace_type },
}
}
}
impl<T> Client<StdoutMode, Unselected, T, Unselected> {
pub fn workspace_type(
self,
workspace_type: WorkSpaceType,
) -> Client<StdoutMode, Unselected, T, TypeSet> {
Client {
bin: self.bin,
global_opts: self.global_opts,
mode: self.mode,
force: self.force,
template: self.template,
workspace_type: TypeSet { workspace_type },
}
}
}
impl<M, F, W> Client<M, F, TemplateSet, W> {
pub fn get_template(&self) -> &str {
&self.template.template
}
pub fn set_template(&mut self, template: impl Into<String>) -> &mut Self {
self.template.template = template.into();
self
}
}
impl<M, F, T> Client<M, F, T, TypeSet> {
pub fn get_workspace_type(&self) -> WorkSpaceType {
self.workspace_type.workspace_type
}
pub fn set_workspace_type(&mut self, workspace_type: WorkSpaceType) -> &mut Self {
self.workspace_type.workspace_type = workspace_type;
self
}
}
impl<F> Client<GeneralMode, F, Unselected, Unselected> {
pub fn delete(self) -> Client<DeleteMode<Unselected, Unselected>, F, Unselected, Unselected> {
Client {
bin: self.bin,
global_opts: self.global_opts,
mode: DeleteMode {
server_id: None,
serverid_state: PhantomData,
shelved: Unselected,
},
force: self.force,
template: self.template,
workspace_type: self.workspace_type,
}
}
pub fn stdin(self) -> Client<StdinMode, F, Unselected, Unselected> {
Client {
bin: self.bin,
global_opts: self.global_opts,
mode: StdinMode,
force: self.force,
template: self.template,
workspace_type: self.workspace_type,
}
}
pub fn switch_view_stream(
self,
stream: impl Into<String>,
) -> Client<SwitchViewMode<StreamSwitch>, F, Unselected, Unselected> {
Client {
bin: self.bin,
global_opts: self.global_opts,
mode: SwitchViewMode {
view: StreamSwitch {
stream: stream.into(),
},
},
force: self.force,
template: self.template,
workspace_type: self.workspace_type,
}
}
pub fn switch_view_template(
self,
template: impl Into<String>,
) -> Client<SwitchViewMode<TemplateSwitch>, F, Unselected, Unselected> {
Client {
bin: self.bin,
global_opts: self.global_opts,
mode: SwitchViewMode {
view: TemplateSwitch {
template: template.into(),
},
},
force: self.force,
template: self.template,
workspace_type: self.workspace_type,
}
}
}
impl<F> Client<GeneralMode, F, TemplateSet, Unselected> {
pub fn switch_view(self) -> Client<SwitchViewMode<TemplateSwitch>, F, Unselected, Unselected> {
Client {
bin: self.bin,
global_opts: self.global_opts,
mode: SwitchViewMode {
view: TemplateSwitch {
template: self.template.template,
},
},
force: self.force,
template: Unselected,
workspace_type: self.workspace_type,
}
}
}
impl<T, W> Client<GeneralMode, Unselected, T, W> {
pub fn stdout(self) -> Client<StdoutMode, Unselected, T, W> {
Client {
bin: self.bin,
global_opts: self.global_opts,
mode: StdoutMode,
force: self.force,
template: self.template,
workspace_type: self.workspace_type,
}
}
}
impl Client<GeneralMode, Unselected, Unselected, Unselected> {
pub fn stream(
self,
stream: impl Into<String>,
) -> Client<StreamMode<Unselected>, Unselected, Unselected, Unselected> {
Client {
bin: self.bin,
global_opts: self.global_opts,
mode: StreamMode {
stream: stream.into(),
output: Unselected,
},
force: self.force,
template: self.template,
workspace_type: self.workspace_type,
}
}
}
impl<F> Client<GeneralMode, F, Unselected, Unselected>
where
F: ExclusiveOption,
{
pub fn server_id(
self,
server_id: impl Into<String>,
) -> Client<DeleteMode<ServerIdSet, Unselected>, F, Unselected, Unselected> {
Client {
bin: self.bin,
global_opts: self.global_opts,
mode: DeleteMode {
server_id: Some(server_id.into()),
serverid_state: PhantomData,
shelved: Unselected,
},
force: self.force,
template: self.template,
workspace_type: self.workspace_type,
}
}
pub fn keep_shelved(
self,
) -> Client<DeleteMode<Unselected, ShelvedKept>, F, Unselected, Unselected> {
Client {
bin: self.bin,
global_opts: self.global_opts,
mode: DeleteMode {
server_id: None,
serverid_state: PhantomData,
shelved: ShelvedKept,
},
force: self.force,
template: self.template,
workspace_type: self.workspace_type,
}
}
pub fn delete_shelved(
self,
) -> Client<DeleteMode<Unselected, ShelvedDeleted>, F, Unselected, Unselected> {
Client {
bin: self.bin,
global_opts: self.global_opts,
mode: DeleteMode {
server_id: None,
serverid_state: PhantomData,
shelved: ShelvedDeleted,
},
force: self.force,
template: self.template,
workspace_type: self.workspace_type,
}
}
}
impl<S, F> Client<DeleteMode<Unselected, S>, F, Unselected, Unselected>
where
S: ServerIdCompatible,
F: ExclusiveOption,
{
pub fn server_id(
self,
server_id: impl Into<String>,
) -> Client<DeleteMode<ServerIdSet, S>, F, Unselected, Unselected> {
Client {
bin: self.bin,
global_opts: self.global_opts,
mode: DeleteMode {
server_id: Some(server_id.into()),
serverid_state: PhantomData,
shelved: self.mode.shelved,
},
force: self.force,
template: self.template,
workspace_type: self.workspace_type,
}
}
}
impl<S, F> Client<DeleteMode<ServerIdSet, S>, F, Unselected, Unselected>
where
S: ServerIdCompatible,
F: ExclusiveOption,
{
pub fn get_server_id(&self) -> &str {
self.mode.server_id.as_deref().expect("server ID is set")
}
pub fn set_server_id(&mut self, server_id: impl Into<String>) -> &mut Self {
self.mode.server_id = Some(server_id.into());
self
}
}
impl<I, S, F> Client<DeleteMode<I, S>, F, Unselected, Unselected>
where
I: ServerIdState,
S: ShelvedState,
F: ExclusiveOption,
{
pub fn get_shelved(&self) -> Option<ShelvedHandling> {
S::handling()
}
}
impl<I, F> Client<DeleteMode<I, Unselected>, F, Unselected, Unselected>
where
I: ServerIdState,
F: ExclusiveOption,
{
pub fn keep_shelved(self) -> Client<DeleteMode<I, ShelvedKept>, F, Unselected, Unselected> {
Client {
bin: self.bin,
global_opts: self.global_opts,
mode: DeleteMode {
server_id: self.mode.server_id,
serverid_state: PhantomData,
shelved: ShelvedKept,
},
force: self.force,
template: self.template,
workspace_type: self.workspace_type,
}
}
}
impl<F> Client<DeleteMode<Unselected, Unselected>, F, Unselected, Unselected>
where
F: ExclusiveOption,
{
pub fn delete_shelved(
self,
) -> Client<DeleteMode<Unselected, ShelvedDeleted>, F, Unselected, Unselected> {
Client {
bin: self.bin,
global_opts: self.global_opts,
mode: DeleteMode {
server_id: None,
serverid_state: PhantomData,
shelved: ShelvedDeleted,
},
force: self.force,
template: self.template,
workspace_type: self.workspace_type,
}
}
}
impl<O> Client<StreamMode<O>, Unselected, Unselected, Unselected> {
pub fn get_stream(&self) -> &str {
&self.mode.stream
}
pub fn set_stream(&mut self, stream: impl Into<String>) -> &mut Self {
self.mode.stream = stream.into();
self
}
pub fn stream(mut self, stream: impl Into<String>) -> Self {
self.set_stream(stream);
self
}
}
impl Client<StreamMode<Unselected>, Unselected, Unselected, Unselected> {
pub fn switch_view(
self,
) -> Client<SwitchViewMode<StreamSwitch>, Unselected, Unselected, Unselected> {
Client {
bin: self.bin,
global_opts: self.global_opts,
mode: SwitchViewMode {
view: StreamSwitch {
stream: self.mode.stream,
},
},
force: self.force,
template: self.template,
workspace_type: self.workspace_type,
}
}
pub fn stdout(self) -> Client<StreamMode<StdoutOutput>, Unselected, Unselected, Unselected> {
Client {
bin: self.bin,
global_opts: self.global_opts,
mode: StreamMode {
stream: self.mode.stream,
output: StdoutOutput { change: None },
},
force: self.force,
template: self.template,
workspace_type: self.workspace_type,
}
}
}
impl Client<StreamMode<StdoutOutput>, Unselected, Unselected, Unselected> {
pub fn get_change(&self) -> Option<&str> {
self.mode.output.change.as_deref()
}
pub fn set_change(&mut self, change: impl Into<String>) -> &mut Self {
self.mode.output.change = Some(change.into());
self
}
pub fn change(mut self, change: impl Into<String>) -> Self {
self.set_change(change);
self
}
}
impl<F> Client<SwitchViewMode<StreamSwitch>, F, Unselected, Unselected> {
pub fn get_stream(&self) -> &str {
&self.mode.view.stream
}
pub fn set_stream(&mut self, stream: impl Into<String>) -> &mut Self {
self.mode.view.stream = stream.into();
self
}
pub fn stream(mut self, stream: impl Into<String>) -> Self {
self.set_stream(stream);
self
}
}
impl<F> Client<SwitchViewMode<TemplateSwitch>, F, Unselected, Unselected> {
pub fn get_template(&self) -> &str {
&self.mode.view.template
}
pub fn set_template(&mut self, template: impl Into<String>) -> &mut Self {
self.mode.view.template = template.into();
self
}
pub fn template(mut self, template: impl Into<String>) -> Self {
self.set_template(template);
self
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::cmd::args_of;
#[test]
fn general_mode_without_options() {
let client = Client::new("p4", GlobalOpts::new());
assert_eq!(args_of(&client.setup_command("p4")), ["client"]);
}
#[test]
fn general_mode_with_all_options() {
let client = Client::new("p4", GlobalOpts::new())
.force()
.template("tpl")
.workspace_type(WorkSpaceType::Readonly);
assert_eq!(
args_of(&client.setup_command("p4")),
["client", "-f", "-t", "tpl", "-T", "readonly"]
);
assert_eq!(client.get_template(), "tpl");
assert_eq!(client.get_workspace_type(), WorkSpaceType::Readonly);
}
#[test]
fn workspace_type_values() {
for (ty, s) in [
(WorkSpaceType::Writable, "writeable"),
(WorkSpaceType::Readonly, "readonly"),
(WorkSpaceType::Partitioned, "partitioned"),
(WorkSpaceType::PartitionedJnl, "partitioned-jnl"),
(WorkSpaceType::Graph, "graph"),
] {
let client = Client::new("p4", GlobalOpts::new()).workspace_type(ty);
assert_eq!(args_of(&client.setup_command("p4")), ["client", "-T", s]);
}
}
#[test]
fn stdout_mode_carries_template_and_type() {
let client = Client::new("p4", GlobalOpts::new())
.template("tpl")
.workspace_type(WorkSpaceType::Graph)
.stdout();
assert_eq!(
args_of(&client.setup_command("p4")),
["client", "-o", "-t", "tpl", "-T", "graph"]
);
}
#[test]
fn delete_mode_is_terminal() {
let client = Client::new("p4", GlobalOpts::new()).delete();
assert_eq!(args_of(&client.setup_command("p4")), ["client", "-d"]);
}
#[test]
fn delete_mode_with_force_unlocks_shelved_and_server_id() {
let mut client = Client::new("p4", GlobalOpts::new())
.delete()
.force()
.server_id("edge.1667");
assert_eq!(client.get_server_id(), "edge.1667");
assert_eq!(client.get_shelved(), None);
client.set_server_id("edge.42");
let client = client.keep_shelved();
assert_eq!(client.get_server_id(), "edge.42");
assert_eq!(client.get_shelved(), Some(ShelvedHandling::Keep));
assert_eq!(
args_of(&client.setup_command("p4")),
["client", "-d", "-f", "--serverid=edge.42", "-Fs"]
);
let client = Client::new("p4", GlobalOpts::new())
.delete()
.force()
.keep_shelved()
.server_id("edge.1667");
assert_eq!(client.get_server_id(), "edge.1667");
assert_eq!(
args_of(&client.setup_command("p4")),
["client", "-d", "-f", "--serverid=edge.1667", "-Fs"]
);
let client = Client::new("p4", GlobalOpts::new())
.delete()
.force()
.delete_shelved();
assert_eq!(client.get_shelved(), Some(ShelvedHandling::Delete));
assert_eq!(
args_of(&client.setup_command("p4")),
["client", "-d", "-f", "-Fd"]
);
}
#[test]
fn force_set_in_general_mode_can_delete() {
let client = Client::new("p4", GlobalOpts::new()).force().delete();
assert_eq!(args_of(&client.setup_command("p4")), ["client", "-d", "-f"]);
}
#[test]
fn force_unlocks_one_shot_delete_transitions() {
let client = Client::new("p4", GlobalOpts::new())
.force()
.server_id("edge.1667")
.keep_shelved();
assert_eq!(client.get_server_id(), "edge.1667");
assert_eq!(client.get_shelved(), Some(ShelvedHandling::Keep));
assert_eq!(
args_of(&client.setup_command("p4")),
["client", "-d", "-f", "--serverid=edge.1667", "-Fs"]
);
let client = Client::new("p4", GlobalOpts::new())
.force()
.delete_shelved();
assert_eq!(client.get_shelved(), Some(ShelvedHandling::Delete));
assert_eq!(
args_of(&client.setup_command("p4")),
["client", "-d", "-f", "-Fd"]
);
let client = Client::new("p4", GlobalOpts::new()).force().keep_shelved();
assert_eq!(client.get_shelved(), Some(ShelvedHandling::Keep));
assert_eq!(
args_of(&client.setup_command("p4")),
["client", "-d", "-f", "-Fs"]
);
}
#[test]
fn stream_mode() {
let client = Client::new("p4", GlobalOpts::new()).stream("//stream/main");
assert_eq!(
args_of(&client.setup_command("p4")),
["client", "-S", "//stream/main"]
);
assert_eq!(client.get_stream(), "//stream/main");
let client = client.stdout();
assert_eq!(
args_of(&client.setup_command("p4")),
["client", "-S", "//stream/main", "-o"]
);
let client = client.change("4321");
assert_eq!(client.get_change(), Some("4321"));
assert_eq!(
args_of(&client.setup_command("p4")),
["client", "-S", "//stream/main", "-c", "4321", "-o"]
);
}
#[test]
fn switch_view_stream_carries_force() {
let client = Client::new("p4", GlobalOpts::new())
.force()
.switch_view_stream("//stream/dev");
assert_eq!(
args_of(&client.setup_command("p4")),
["client", "-s", "-f", "-S", "//stream/dev"]
);
assert_eq!(client.get_stream(), "//stream/dev");
}
#[test]
fn switch_view_reuses_stream() {
let client = Client::new("p4", GlobalOpts::new())
.stream("//stream/dev")
.switch_view();
assert_eq!(
args_of(&client.setup_command("p4")),
["client", "-s", "-S", "//stream/dev"]
);
assert_eq!(client.get_stream(), "//stream/dev");
let client = Client::new("p4", GlobalOpts::new())
.stream("//stream/dev")
.switch_view()
.force();
assert_eq!(
args_of(&client.setup_command("p4")),
["client", "-s", "-f", "-S", "//stream/dev"]
);
let client = Client::new("p4", GlobalOpts::new())
.stream("old")
.stream("//stream/dev")
.switch_view();
assert_eq!(
args_of(&client.setup_command("p4")),
["client", "-s", "-S", "//stream/dev"]
);
}
#[test]
fn switch_view_template() {
let client = Client::new("p4", GlobalOpts::new()).switch_view_template("tpl");
assert_eq!(
args_of(&client.setup_command("p4")),
["client", "-s", "-t", "tpl"]
);
assert_eq!(client.get_template(), "tpl");
}
#[test]
fn switch_view_reuses_template() {
let client = Client::new("p4", GlobalOpts::new())
.template("tpl")
.switch_view();
assert_eq!(
args_of(&client.setup_command("p4")),
["client", "-s", "-t", "tpl"]
);
assert_eq!(client.get_template(), "tpl");
}
#[test]
fn stdin_mode() {
let client = Client::new("p4", GlobalOpts::new()).stdin();
assert_eq!(args_of(&client.setup_command("p4")), ["client", "-i"]);
let client = Client::new("p4", GlobalOpts::new()).force().stdin();
assert_eq!(args_of(&client.setup_command("p4")), ["client", "-i", "-f"]);
}
#[test]
fn global_opts_are_injected_once() {
let client = Client::new("p4", GlobalOpts::new().port("localhost:1666"));
assert_eq!(
args_of(&client.setup_command("p4")),
["-p", "localhost:1666", "client"]
);
}
}