svn/
options.rs

1//! Builder-style option types for higher-level operations.
2
3use crate::{Depth, DirentField, PropertyList};
4
5#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
6#[derive(Clone, Debug, PartialEq, Eq)]
7/// Options for [`crate::RaSvnSession::get_file_with_options`].
8pub struct GetFileOptions {
9    /// Revision to fetch.
10    pub rev: u64,
11    /// Whether to request file properties.
12    pub want_props: bool,
13    /// Whether to request inherited properties.
14    ///
15    /// Note: the standard Subversion client uses a separate `get-iprops` call
16    /// instead of setting `want-iprops` in `get-file`, to work around
17    /// compatibility issues with some `svnserve` versions.
18    pub want_iprops: bool,
19    /// Maximum number of bytes to stream.
20    pub max_bytes: u64,
21}
22
23impl GetFileOptions {
24    /// Creates options with no properties requested.
25    pub fn new(rev: u64, max_bytes: u64) -> Self {
26        Self {
27            rev,
28            want_props: false,
29            want_iprops: false,
30            max_bytes,
31        }
32    }
33
34    /// Requests file properties in the response.
35    #[must_use]
36    pub fn with_props(mut self) -> Self {
37        self.want_props = true;
38        self
39    }
40
41    /// Requests inherited properties (if supported by the server).
42    #[must_use]
43    pub fn with_iprops(mut self) -> Self {
44        self.want_iprops = true;
45        self
46    }
47}
48
49#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
50#[derive(Clone, Debug, PartialEq, Eq)]
51/// Which revision properties to request for `log` operations.
52pub enum LogRevProps {
53    /// Request all revision properties supported by the server.
54    All,
55    /// Request only a specific set of revision property names.
56    Custom(Vec<String>),
57}
58
59#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
60#[derive(Clone, Debug, PartialEq, Eq)]
61/// Options for [`crate::RaSvnSession::log_with_options`].
62pub struct LogOptions {
63    /// Target paths (repository-relative) to include in the log query.
64    pub target_paths: Vec<String>,
65    /// Start revision (inclusive). `None` uses the server default.
66    pub start_rev: Option<u64>,
67    /// End revision (inclusive). `None` uses the server default.
68    pub end_rev: Option<u64>,
69    /// Whether to include changed paths in each log entry.
70    pub changed_paths: bool,
71    /// Whether to require the target path to exist at the requested revisions.
72    pub strict_node: bool,
73    /// Maximum number of entries to return (`0` means unlimited).
74    pub limit: u64,
75    /// Whether to include merged revisions.
76    pub include_merged_revisions: bool,
77    /// Which revision properties to request.
78    pub revprops: LogRevProps,
79}
80
81impl Default for LogOptions {
82    fn default() -> Self {
83        Self {
84            target_paths: Vec::new(),
85            start_rev: None,
86            end_rev: None,
87            changed_paths: true,
88            strict_node: true,
89            limit: 0,
90            include_merged_revisions: false,
91            revprops: LogRevProps::All,
92        }
93    }
94}
95
96impl LogOptions {
97    /// Convenience constructor for a revision range.
98    #[must_use]
99    pub fn between(start_rev: u64, end_rev: u64) -> Self {
100        Self {
101            start_rev: Some(start_rev),
102            end_rev: Some(end_rev),
103            ..Self::default()
104        }
105    }
106}
107
108#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
109#[derive(Clone, Debug, PartialEq, Eq)]
110/// Options for [`crate::RaSvnSession::list_with_options`].
111pub struct ListOptions {
112    /// Repository path to list (directory).
113    pub path: String,
114    /// Revision to list at; `None` uses the server default (usually HEAD).
115    pub rev: Option<u64>,
116    /// Listing depth.
117    pub depth: Depth,
118    /// Optional fields to request (server must support the `list` capability).
119    pub fields: Vec<DirentField>,
120    /// Optional glob patterns to filter entries (server must support `list`).
121    pub patterns: Vec<String>,
122}
123
124impl ListOptions {
125    /// Creates list options for a path at a given depth.
126    pub fn new(path: impl Into<String>, depth: Depth) -> Self {
127        Self {
128            path: path.into(),
129            rev: None,
130            depth,
131            fields: Vec::new(),
132            patterns: Vec::new(),
133        }
134    }
135
136    /// Sets the revision to list at.
137    #[must_use]
138    pub fn with_rev(mut self, rev: u64) -> Self {
139        self.rev = Some(rev);
140        self
141    }
142
143    /// Requests additional entry fields from the server.
144    #[must_use]
145    pub fn with_fields(mut self, fields: Vec<DirentField>) -> Self {
146        self.fields = fields;
147        self
148    }
149
150    /// Adds glob patterns to filter entries on the server side.
151    #[must_use]
152    pub fn with_patterns(mut self, patterns: Vec<String>) -> Self {
153        self.patterns = patterns;
154        self
155    }
156}
157
158#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
159#[derive(Clone, Debug, PartialEq, Eq)]
160/// Options for [`crate::RaSvnSession::update`].
161pub struct UpdateOptions {
162    /// Revision to update to; `None` usually means HEAD.
163    pub rev: Option<u64>,
164    /// Update target path (repository-relative).
165    pub target: String,
166    /// Update depth.
167    pub depth: Depth,
168    /// Whether to request copyfrom arguments from the server.
169    pub send_copyfrom_args: bool,
170    /// Whether to ignore ancestry when applying the report.
171    pub ignore_ancestry: bool,
172}
173
174impl UpdateOptions {
175    /// Creates update options for a target and depth.
176    pub fn new(target: impl Into<String>, depth: Depth) -> Self {
177        Self {
178            rev: None,
179            target: target.into(),
180            depth,
181            send_copyfrom_args: true,
182            ignore_ancestry: false,
183        }
184    }
185
186    /// Sets the revision to update to.
187    #[must_use]
188    pub fn with_rev(mut self, rev: u64) -> Self {
189        self.rev = Some(rev);
190        self
191    }
192
193    /// Disables copyfrom arguments (for compatibility with older servers).
194    #[must_use]
195    pub fn without_copyfrom_args(mut self) -> Self {
196        self.send_copyfrom_args = false;
197        self
198    }
199
200    /// Ignores ancestry when applying the update.
201    #[must_use]
202    pub fn ignore_ancestry(mut self) -> Self {
203        self.ignore_ancestry = true;
204        self
205    }
206}
207
208#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
209#[derive(Clone, Debug, PartialEq, Eq)]
210/// Options for [`crate::RaSvnSession::switch`].
211pub struct SwitchOptions {
212    /// Revision to switch to; `None` usually means HEAD.
213    pub rev: Option<u64>,
214    /// Working target path (repository-relative).
215    pub target: String,
216    /// URL to switch to.
217    pub switch_url: String,
218    /// Switch depth.
219    pub depth: Depth,
220    /// Whether to request copyfrom arguments from the server.
221    pub send_copyfrom_args: bool,
222    /// Whether to ignore ancestry when applying the report.
223    pub ignore_ancestry: bool,
224}
225
226impl SwitchOptions {
227    /// Creates switch options for a target, URL, and depth.
228    pub fn new(target: impl Into<String>, switch_url: impl Into<String>, depth: Depth) -> Self {
229        Self {
230            rev: None,
231            target: target.into(),
232            switch_url: switch_url.into(),
233            depth,
234            send_copyfrom_args: true,
235            ignore_ancestry: false,
236        }
237    }
238
239    /// Sets the revision to switch to.
240    #[must_use]
241    pub fn with_rev(mut self, rev: u64) -> Self {
242        self.rev = Some(rev);
243        self
244    }
245
246    /// Disables copyfrom arguments (for compatibility with older servers).
247    #[must_use]
248    pub fn without_copyfrom_args(mut self) -> Self {
249        self.send_copyfrom_args = false;
250        self
251    }
252
253    /// Ignores ancestry when applying the switch.
254    #[must_use]
255    pub fn ignore_ancestry(mut self) -> Self {
256        self.ignore_ancestry = true;
257        self
258    }
259}
260
261#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
262#[derive(Clone, Debug, PartialEq, Eq)]
263/// Options for [`crate::RaSvnSession::status`].
264pub struct StatusOptions {
265    /// Target path (repository-relative).
266    pub target: String,
267    /// Revision to compare against; `None` usually means HEAD.
268    pub rev: Option<u64>,
269    /// Status depth.
270    pub depth: Depth,
271}
272
273impl StatusOptions {
274    /// Creates status options for a target and depth.
275    pub fn new(target: impl Into<String>, depth: Depth) -> Self {
276        Self {
277            target: target.into(),
278            rev: None,
279            depth,
280        }
281    }
282
283    /// Sets the revision to compare against.
284    #[must_use]
285    pub fn with_rev(mut self, rev: u64) -> Self {
286        self.rev = Some(rev);
287        self
288    }
289}
290
291#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
292#[derive(Clone, Debug, PartialEq, Eq)]
293/// Options for [`crate::RaSvnSession::diff`].
294pub struct DiffOptions {
295    /// Revision to diff against; `None` usually means HEAD.
296    pub rev: Option<u64>,
297    /// Target path (repository-relative).
298    pub target: String,
299    /// Whether to ignore ancestry.
300    pub ignore_ancestry: bool,
301    /// URL to diff against.
302    pub versus_url: String,
303    /// Whether to request text deltas.
304    pub text_deltas: bool,
305    /// Diff depth.
306    pub depth: Depth,
307}
308
309impl DiffOptions {
310    /// Creates diff options for a target, versus URL, and depth.
311    pub fn new(target: impl Into<String>, versus_url: impl Into<String>, depth: Depth) -> Self {
312        Self {
313            rev: None,
314            target: target.into(),
315            ignore_ancestry: false,
316            versus_url: versus_url.into(),
317            text_deltas: true,
318            depth,
319        }
320    }
321
322    /// Sets the revision to diff against.
323    #[must_use]
324    pub fn with_rev(mut self, rev: u64) -> Self {
325        self.rev = Some(rev);
326        self
327    }
328
329    /// Ignores ancestry when producing the diff.
330    #[must_use]
331    pub fn ignore_ancestry(mut self) -> Self {
332        self.ignore_ancestry = true;
333        self
334    }
335
336    /// Enables or disables requesting text deltas.
337    #[must_use]
338    pub fn with_text_deltas(mut self, text_deltas: bool) -> Self {
339        self.text_deltas = text_deltas;
340        self
341    }
342}
343
344#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
345#[derive(Clone, Debug, PartialEq, Eq)]
346/// Options for [`crate::RaSvnSession::replay`].
347pub struct ReplayOptions {
348    /// Revision to replay.
349    pub revision: u64,
350    /// Low water mark revision.
351    pub low_water_mark: u64,
352    /// Whether to send deltas.
353    pub send_deltas: bool,
354}
355
356impl ReplayOptions {
357    /// Creates replay options for a single revision.
358    pub fn new(revision: u64) -> Self {
359        Self {
360            revision,
361            low_water_mark: 0,
362            send_deltas: true,
363        }
364    }
365
366    /// Sets the low water mark.
367    #[must_use]
368    pub fn with_low_water_mark(mut self, low_water_mark: u64) -> Self {
369        self.low_water_mark = low_water_mark;
370        self
371    }
372
373    /// Sets whether to request deltas.
374    #[must_use]
375    pub fn with_send_deltas(mut self, send_deltas: bool) -> Self {
376        self.send_deltas = send_deltas;
377        self
378    }
379}
380
381#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
382#[derive(Clone, Debug, PartialEq, Eq)]
383/// Options for [`crate::RaSvnSession::replay_range`].
384pub struct ReplayRangeOptions {
385    /// Start revision (inclusive).
386    pub start_rev: u64,
387    /// End revision (inclusive).
388    pub end_rev: u64,
389    /// Low water mark revision.
390    pub low_water_mark: u64,
391    /// Whether to send deltas.
392    pub send_deltas: bool,
393}
394
395impl ReplayRangeOptions {
396    /// Creates replay-range options for a revision range.
397    pub fn new(start_rev: u64, end_rev: u64) -> Self {
398        Self {
399            start_rev,
400            end_rev,
401            low_water_mark: 0,
402            send_deltas: true,
403        }
404    }
405
406    /// Sets the low water mark.
407    #[must_use]
408    pub fn with_low_water_mark(mut self, low_water_mark: u64) -> Self {
409        self.low_water_mark = low_water_mark;
410        self
411    }
412
413    /// Sets whether to request deltas.
414    #[must_use]
415    pub fn with_send_deltas(mut self, send_deltas: bool) -> Self {
416        self.send_deltas = send_deltas;
417        self
418    }
419}
420
421#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
422#[derive(Clone, Debug, PartialEq, Eq, Default)]
423/// Options for [`crate::RaSvnSession::lock`].
424pub struct LockOptions {
425    /// Optional lock comment.
426    pub comment: Option<String>,
427    /// Whether to steal an existing lock.
428    pub steal_lock: bool,
429    /// Optional revision the lock is expected to apply to.
430    pub current_rev: Option<u64>,
431}
432
433impl LockOptions {
434    /// Creates default lock options.
435    pub fn new() -> Self {
436        Self::default()
437    }
438
439    /// Sets a lock comment.
440    #[must_use]
441    pub fn with_comment(mut self, comment: impl Into<String>) -> Self {
442        self.comment = Some(comment.into());
443        self
444    }
445
446    /// Enables stealing an existing lock.
447    #[must_use]
448    pub fn steal_lock(mut self) -> Self {
449        self.steal_lock = true;
450        self
451    }
452
453    /// Sets a current revision constraint for the lock request.
454    #[must_use]
455    pub fn with_current_rev(mut self, current_rev: u64) -> Self {
456        self.current_rev = Some(current_rev);
457        self
458    }
459}
460
461#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
462#[derive(Clone, Debug, PartialEq, Eq, Default)]
463/// Options for [`crate::RaSvnSession::lock_many`].
464pub struct LockManyOptions {
465    /// Optional lock comment.
466    pub comment: Option<String>,
467    /// Whether to steal existing locks.
468    pub steal_lock: bool,
469}
470
471impl LockManyOptions {
472    /// Creates default lock-many options.
473    pub fn new() -> Self {
474        Self::default()
475    }
476
477    /// Sets a lock comment.
478    #[must_use]
479    pub fn with_comment(mut self, comment: impl Into<String>) -> Self {
480        self.comment = Some(comment.into());
481        self
482    }
483
484    /// Enables stealing existing locks.
485    #[must_use]
486    pub fn steal_lock(mut self) -> Self {
487        self.steal_lock = true;
488        self
489    }
490}
491
492#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
493#[derive(Clone, Debug, PartialEq, Eq)]
494/// A lock target for [`crate::RaSvnSession::lock_many`].
495pub struct LockTarget {
496    /// Repository path to lock.
497    pub path: String,
498    /// Optional current revision constraint.
499    pub current_rev: Option<u64>,
500}
501
502impl LockTarget {
503    /// Creates a lock target for a path.
504    pub fn new(path: impl Into<String>) -> Self {
505        Self {
506            path: path.into(),
507            current_rev: None,
508        }
509    }
510
511    /// Sets a current revision constraint for this target.
512    #[must_use]
513    pub fn with_current_rev(mut self, current_rev: u64) -> Self {
514        self.current_rev = Some(current_rev);
515        self
516    }
517}
518
519#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
520#[derive(Clone, Debug, PartialEq, Eq, Default)]
521/// Options for [`crate::RaSvnSession::unlock`].
522pub struct UnlockOptions {
523    /// Optional lock token.
524    pub token: Option<String>,
525    /// Whether to break the lock (force unlock).
526    pub break_lock: bool,
527}
528
529impl UnlockOptions {
530    /// Creates default unlock options.
531    pub fn new() -> Self {
532        Self::default()
533    }
534
535    /// Sets the lock token.
536    #[must_use]
537    pub fn with_token(mut self, token: impl Into<String>) -> Self {
538        self.token = Some(token.into());
539        self
540    }
541
542    /// Enables breaking the lock (force unlock).
543    #[must_use]
544    pub fn break_lock(mut self) -> Self {
545        self.break_lock = true;
546        self
547    }
548}
549
550#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
551#[derive(Clone, Debug, PartialEq, Eq, Default)]
552/// Options for [`crate::RaSvnSession::unlock_many`].
553pub struct UnlockManyOptions {
554    /// Whether to break locks (force unlock).
555    pub break_lock: bool,
556}
557
558impl UnlockManyOptions {
559    /// Creates default unlock-many options.
560    pub fn new() -> Self {
561        Self::default()
562    }
563
564    /// Enables breaking locks (force unlock).
565    #[must_use]
566    pub fn break_lock(mut self) -> Self {
567        self.break_lock = true;
568        self
569    }
570}
571
572#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
573#[derive(Clone, Debug, PartialEq, Eq)]
574/// An unlock target for [`crate::RaSvnSession::unlock_many`].
575pub struct UnlockTarget {
576    /// Repository path to unlock.
577    pub path: String,
578    /// Optional lock token.
579    pub token: Option<String>,
580}
581
582impl UnlockTarget {
583    /// Creates an unlock target for a path.
584    pub fn new(path: impl Into<String>) -> Self {
585        Self {
586            path: path.into(),
587            token: None,
588        }
589    }
590
591    /// Sets the lock token for this target.
592    #[must_use]
593    pub fn with_token(mut self, token: impl Into<String>) -> Self {
594        self.token = Some(token.into());
595        self
596    }
597}
598
599#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
600#[derive(Clone, Debug, PartialEq, Eq)]
601/// A path/token pair to include in [`crate::CommitOptions::lock_tokens`].
602pub struct CommitLockToken {
603    /// Locked repository path.
604    pub path: String,
605    /// Lock token to present during commit.
606    pub token: String,
607}
608
609impl CommitLockToken {
610    /// Creates a path/token pair.
611    pub fn new(path: impl Into<String>, token: impl Into<String>) -> Self {
612        Self {
613            path: path.into(),
614            token: token.into(),
615        }
616    }
617}
618
619#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
620#[derive(Clone, Debug, PartialEq, Eq)]
621/// Options for [`crate::RaSvnSession::commit`].
622///
623/// `rev_props` is for additional revision properties; `svn:log` is always
624/// derived from `log_message`.
625pub struct CommitOptions {
626    /// Commit log message (used to set `svn:log`).
627    pub log_message: String,
628    /// Lock tokens to present during commit.
629    pub lock_tokens: Vec<CommitLockToken>,
630    /// Whether to keep locks after a successful commit.
631    pub keep_locks: bool,
632    /// Additional revision properties to set during commit.
633    pub rev_props: PropertyList,
634}
635
636impl CommitOptions {
637    /// Creates commit options with a required log message.
638    pub fn new(log_message: impl Into<String>) -> Self {
639        Self {
640            log_message: log_message.into(),
641            lock_tokens: Vec::new(),
642            keep_locks: false,
643            rev_props: PropertyList::new(),
644        }
645    }
646
647    /// Sets lock tokens to be included in the commit.
648    #[must_use]
649    pub fn with_lock_tokens(mut self, lock_tokens: Vec<CommitLockToken>) -> Self {
650        self.lock_tokens = lock_tokens;
651        self
652    }
653
654    /// Requests that locks be kept after the commit.
655    #[must_use]
656    pub fn keep_locks(mut self) -> Self {
657        self.keep_locks = true;
658        self
659    }
660
661    /// Sets additional revision properties.
662    #[must_use]
663    pub fn with_rev_props(mut self, rev_props: PropertyList) -> Self {
664        self.rev_props = rev_props;
665        self
666    }
667}