1use std::collections::HashMap;
6use std::path::Path;
7
8use crate::Result;
9use derive_more::Display;
10use serde::{Deserialize, Serialize};
11use xvc_walker::AbsolutePath;
12
13#[derive(Display, Clone, Debug, Deserialize, PartialEq, Serialize)]
15#[display("CoreConfig(xvc_repo_version: {xvc_repo_version}, verbosity: {verbosity})")]
16#[serde(deny_unknown_fields)]
17pub struct CoreConfig {
18 pub xvc_repo_version: u8,
20 pub verbosity: String,
22}
23
24#[derive(Display, Clone, Debug, Deserialize, PartialEq, Serialize)]
26#[display("GitConfig(use_git: {use_git}, command: {command}, auto_commit: {auto_commit}, auto_stage: {auto_stage})")]
27#[serde(deny_unknown_fields)]
28pub struct GitConfig {
29 pub use_git: bool,
31 pub command: String,
33 pub auto_commit: bool,
35 pub auto_stage: bool,
37}
38
39#[derive(Display, Clone, Debug, Deserialize, PartialEq, Serialize)]
41#[display("CacheConfig(algorithm: {algorithm})")]
42#[serde(deny_unknown_fields)]
43pub struct CacheConfig {
44 pub algorithm: String,
46}
47
48#[derive(Display, Clone, Debug, Deserialize, PartialEq, Serialize)]
50#[display("FileTrackConfig(no_commit: {no_commit}, force: {force}, text_or_binary: {text_or_binary}, no_parallel: {no_parallel}, include_git_files: {include_git_files})")]
51#[serde(deny_unknown_fields)]
52pub struct FileTrackConfig {
53 pub no_commit: bool,
55 pub force: bool,
57 pub text_or_binary: String,
59 pub no_parallel: bool,
61 pub include_git_files: bool,
63}
64
65#[derive(Display, Clone, Debug, Deserialize, PartialEq, Serialize)]
67#[display("FileListConfig(format: {format}, sort: {sort}, show_dot_files: {show_dot_files}, no_summary: {no_summary}, recursive: {recursive}, include_git_files: {include_git_files})")]
68#[serde(deny_unknown_fields)]
69pub struct FileListConfig {
70 pub format: String,
72 pub sort: String,
74 pub show_dot_files: bool,
76 pub no_summary: bool,
78 pub recursive: bool,
80 pub include_git_files: bool,
82}
83
84#[derive(Display, Clone, Debug, Deserialize, PartialEq, Serialize)]
86#[display("FileCarryInConfig(force: {force}, no_parallel: {no_parallel})")]
87#[serde(deny_unknown_fields)]
88pub struct FileCarryInConfig {
89 pub force: bool,
91 pub no_parallel: bool,
93}
94
95#[derive(Display, Clone, Debug, Deserialize, PartialEq, Serialize)]
97#[display("FileRecheckConfig(method: {method})")]
98#[serde(deny_unknown_fields)]
99pub struct FileRecheckConfig {
100 pub method: String,
102}
103
104#[derive(Display, Clone, Debug, Deserialize, PartialEq, Serialize)]
106#[display("FileConfig(track: {track}, list: {list}, carry_in: {carry_in}, recheck: {recheck})")]
107#[serde(deny_unknown_fields)]
108pub struct FileConfig {
109 pub track: FileTrackConfig,
111 pub list: FileListConfig,
113 #[serde(rename = "carry-in")]
115 pub carry_in: FileCarryInConfig,
116 pub recheck: FileRecheckConfig,
118}
119
120#[derive(Display, Clone, Debug, Deserialize, PartialEq, Serialize)]
122#[display("PipelineConfig(current_pipeline: {current_pipeline}, default: {default}, default_params_file: {default_params_file}, process_pool_size: {process_pool_size})")]
123#[serde(deny_unknown_fields)]
124pub struct PipelineConfig {
125 pub current_pipeline: String,
127 pub default: String,
129 pub default_params_file: String,
131 pub process_pool_size: u32,
133}
134
135#[derive(Display, Clone, Debug, Deserialize, PartialEq, Serialize)]
137#[display("CheckIgnoreConfig(details: {details})")]
138#[serde(deny_unknown_fields)]
139pub struct CheckIgnoreConfig {
140 pub details: bool,
142}
143
144#[derive(Display, Clone, Debug, Deserialize, PartialEq, Serialize)]
146#[display("XvcConfiguration(core: {core}, git: {git}, cache: {cache}, file: {file}, pipeline: {pipeline}, check_ignore: {check_ignore})")]
147#[serde(deny_unknown_fields)]
148pub struct XvcConfiguration {
149 pub core: CoreConfig,
151 pub git: GitConfig,
153 pub cache: CacheConfig,
155 pub file: FileConfig,
157 pub pipeline: PipelineConfig,
159 #[serde(rename = "check-ignore")]
161 pub check_ignore: CheckIgnoreConfig,
162}
163
164#[derive(Display, Clone, Debug, Deserialize, PartialEq, Serialize, Default)]
166#[display("OptionalCoreConfig(xvc_repo_version: {xvc_repo_version:?}, verbosity: {verbosity:?})")]
167#[serde(deny_unknown_fields)]
168pub struct OptionalCoreConfig {
169 pub xvc_repo_version: Option<u8>,
171 pub verbosity: Option<String>,
173 pub guid: Option<String>,
176}
177
178#[derive(Display, Clone, Debug, Deserialize, PartialEq, Serialize, Default)]
180#[display("OptionalGitConfig(use_git: {use_git:?}, command: {command:?}, auto_commit: {auto_commit:?}, auto_stage: {auto_stage:?})")]
181#[serde(deny_unknown_fields)]
182pub struct OptionalGitConfig {
183 pub use_git: Option<bool>,
185 pub command: Option<String>,
187 pub auto_commit: Option<bool>,
189 pub auto_stage: Option<bool>,
191}
192
193#[derive(Display, Clone, Debug, Deserialize, PartialEq, Serialize, Default)]
195#[display("OptionalCacheConfig(algorithm: {algorithm:?})")]
196#[serde(deny_unknown_fields)]
197pub struct OptionalCacheConfig {
198 pub algorithm: Option<String>,
200}
201
202#[derive(Display, Clone, Debug, Deserialize, PartialEq, Serialize, Default)]
204#[display("OptionalFileTrackConfig(no_commit: {no_commit:?}, force: {force:?}, text_or_binary: {text_or_binary:?}, no_parallel: {no_parallel:?}, include_git_files: {include_git_files:?})")]
205#[serde(deny_unknown_fields)]
206pub struct OptionalFileTrackConfig {
207 pub no_commit: Option<bool>,
209 pub force: Option<bool>,
211 pub text_or_binary: Option<String>,
213 pub no_parallel: Option<bool>,
215 pub include_git_files: Option<bool>,
217}
218
219#[derive(Display, Clone, Debug, Deserialize, PartialEq, Serialize, Default)]
221#[display("OptionalFileListConfig(format: {format:?}, sort: {sort:?}, show_dot_files: {show_dot_files:?}, no_summary: {no_summary:?}, recursive: {recursive:?}, include_git_files: {include_git_files:?})")]
222#[serde(deny_unknown_fields)]
223pub struct OptionalFileListConfig {
224 pub format: Option<String>,
226 pub sort: Option<String>,
228 pub show_dot_files: Option<bool>,
230 pub no_summary: Option<bool>,
232 pub recursive: Option<bool>,
234 pub include_git_files: Option<bool>,
236}
237
238#[derive(Display, Clone, Debug, Deserialize, PartialEq, Serialize, Default)]
240#[display("OptionalFileCarryInConfig(force: {force:?}, no_parallel: {no_parallel:?})")]
241#[serde(deny_unknown_fields)]
242pub struct OptionalFileCarryInConfig {
243 pub force: Option<bool>,
245 pub no_parallel: Option<bool>,
247}
248
249#[derive(Display, Clone, Debug, Deserialize, PartialEq, Serialize, Default)]
251#[display("OptionalFileRecheckConfig(method: {method:?})")]
252#[serde(deny_unknown_fields)]
253pub struct OptionalFileRecheckConfig {
254 pub method: Option<String>,
256}
257
258#[derive(Display, Clone, Debug, Deserialize, PartialEq, Serialize, Default)]
260#[display("OptionalFileConfig(track: {track:?}, list: {list:?}, carry_in: {carry_in:?}, recheck: {recheck:?})")]
261#[serde(deny_unknown_fields)]
262pub struct OptionalFileConfig {
263 pub track: Option<OptionalFileTrackConfig>,
265 pub list: Option<OptionalFileListConfig>,
267 #[serde(rename = "carry-in")]
269 pub carry_in: Option<OptionalFileCarryInConfig>,
270 pub recheck: Option<OptionalFileRecheckConfig>,
272}
273
274#[derive(Display, Clone, Debug, Deserialize, PartialEq, Serialize, Default)]
276#[display("OptionalPipelineConfig(current_pipeline: {current_pipeline:?}, default: {default:?}, default_params_file: {default_params_file:?}, process_pool_size: {process_pool_size:?})")]
277#[serde(deny_unknown_fields)]
278pub struct OptionalPipelineConfig {
279 pub current_pipeline: Option<String>,
281 pub default: Option<String>,
283 pub default_params_file: Option<String>,
285 pub process_pool_size: Option<u32>,
287}
288
289#[derive(Display, Clone, Debug, Deserialize, PartialEq, Serialize, Default)]
291#[display("OptionalCheckIgnoreConfig(details: {details:?})")]
292#[serde(deny_unknown_fields)]
293pub struct OptionalCheckIgnoreConfig {
294 pub details: Option<bool>,
296}
297
298#[derive(Display, Clone, Debug, Deserialize, PartialEq, Serialize, Default)]
300#[display("XvcOptionalConfiguration(core: {core:?}, git: {git:?}, cache: {cache:?}, file: {file:?}, pipeline: {pipeline:?}, check_ignore: {check_ignore:?})")]
301#[serde(deny_unknown_fields)]
302pub struct XvcOptionalConfiguration {
303 pub core: Option<OptionalCoreConfig>,
305 pub git: Option<OptionalGitConfig>,
307 pub cache: Option<OptionalCacheConfig>,
309 pub file: Option<OptionalFileConfig>,
311 pub pipeline: Option<OptionalPipelineConfig>,
313 pub check_ignore: Option<OptionalCheckIgnoreConfig>,
315}
316
317impl XvcOptionalConfiguration {
318 pub fn from_file(file_path: &Path) -> Result<Self> {
328 let s =
329 std::fs::read_to_string(&file_path).map_err(|e| crate::Error::IoError { source: e })?;
330 let c: XvcOptionalConfiguration =
331 toml::from_str(&s).map_err(|e| crate::Error::TomlDeserializationError { source: e })?;
332 Ok(c)
333 }
334
335 fn parse_bool(s: &str) -> Option<bool> {
336 match s {
337 "1" | "TRUE" | "True" | "true" => Some(true),
338 "0" | "FALSE" | "False" | "false" => Some(false),
339 _ => None,
340 }
341 }
342
343 pub fn from_hash_map(prefix: &str, values: &HashMap<String, String>) -> Self {
355 let mut config = Self {
356 core: None,
357 git: None,
358 cache: None,
359 file: None,
360 pipeline: None,
361 check_ignore: None,
362 };
363
364 for (key, value) in values.iter() {
365 if !key.starts_with(prefix) {
366 continue;
367 }
368
369 let key_str = &key[prefix.len()..].to_lowercase();
370 match key_str.as_str() {
371 "core.xvc_repo_version" => {
373 if let Ok(val) = value.parse::<u8>() {
374 config
375 .core
376 .get_or_insert_with(Default::default)
377 .xvc_repo_version = Some(val);
378 }
379 }
380 "core.verbosity" => {
381 config.core.get_or_insert_with(Default::default).verbosity =
382 Some(value.to_string());
383 }
384 "git.use_git" => {
386 if let Some(val) = Self::parse_bool(value) {
387 config.git.get_or_insert_with(Default::default).use_git = Some(val);
388 }
389 }
390 "git.command" => {
391 config.git.get_or_insert_with(Default::default).command =
392 Some(value.to_string());
393 }
394 "git.auto_commit" => {
395 if let Some(val) = Self::parse_bool(value) {
396 config.git.get_or_insert_with(Default::default).auto_commit = Some(val);
397 }
398 }
399 "git.auto_stage" => {
400 if let Some(val) = Self::parse_bool(value) {
401 config.git.get_or_insert_with(Default::default).auto_stage = Some(val);
402 }
403 }
404 "cache.algorithm" => {
406 config.cache.get_or_insert_with(Default::default).algorithm =
407 Some(value.to_string());
408 }
409 "file.track.no_commit" => {
411 if let Some(val) = Self::parse_bool(value) {
412 config
413 .file
414 .get_or_insert_with(Default::default)
415 .track
416 .get_or_insert_with(Default::default)
417 .no_commit = Some(val);
418 }
419 }
420 "file.track.force" => {
421 if let Some(val) = Self::parse_bool(value) {
422 config
423 .file
424 .get_or_insert_with(Default::default)
425 .track
426 .get_or_insert_with(Default::default)
427 .force = Some(val);
428 }
429 }
430 "file.track.text_or_binary" => {
431 config
432 .file
433 .get_or_insert_with(Default::default)
434 .track
435 .get_or_insert_with(Default::default)
436 .text_or_binary = Some(value.to_string());
437 }
438 "file.track.no_parallel" => {
439 if let Some(val) = Self::parse_bool(value) {
440 config
441 .file
442 .get_or_insert_with(Default::default)
443 .track
444 .get_or_insert_with(Default::default)
445 .no_parallel = Some(val);
446 }
447 }
448 "file.track.include_git_files" => {
449 if let Some(val) = Self::parse_bool(value) {
450 config
451 .file
452 .get_or_insert_with(Default::default)
453 .track
454 .get_or_insert_with(Default::default)
455 .include_git_files = Some(val);
456 }
457 }
458 "file.list.format" => {
460 config
461 .file
462 .get_or_insert_with(Default::default)
463 .list
464 .get_or_insert_with(Default::default)
465 .format = Some(value.to_string());
466 }
467 "file.list.sort" => {
468 config
469 .file
470 .get_or_insert_with(Default::default)
471 .list
472 .get_or_insert_with(Default::default)
473 .sort = Some(value.to_string());
474 }
475 "file.list.show_dot_files" => {
476 if let Some(val) = Self::parse_bool(value) {
477 config
478 .file
479 .get_or_insert_with(Default::default)
480 .list
481 .get_or_insert_with(Default::default)
482 .show_dot_files = Some(val);
483 }
484 }
485 "file.list.no_summary" => {
486 if let Some(val) = Self::parse_bool(value) {
487 config
488 .file
489 .get_or_insert_with(Default::default)
490 .list
491 .get_or_insert_with(Default::default)
492 .no_summary = Some(val);
493 }
494 }
495 "file.list.recursive" => {
496 if let Some(val) = Self::parse_bool(value) {
497 config
498 .file
499 .get_or_insert_with(Default::default)
500 .list
501 .get_or_insert_with(Default::default)
502 .recursive = Some(val);
503 }
504 }
505 "file.list.include_git_files" => {
506 if let Some(val) = Self::parse_bool(value) {
507 config
508 .file
509 .get_or_insert_with(Default::default)
510 .list
511 .get_or_insert_with(Default::default)
512 .include_git_files = Some(val);
513 }
514 }
515 "file.carry_in.force" => {
517 if let Some(val) = Self::parse_bool(value) {
518 config
519 .file
520 .get_or_insert_with(Default::default)
521 .carry_in
522 .get_or_insert_with(Default::default)
523 .force = Some(val);
524 }
525 }
526 "file.carry_in.no_parallel" => {
527 if let Some(val) = Self::parse_bool(value) {
528 config
529 .file
530 .get_or_insert_with(Default::default)
531 .carry_in
532 .get_or_insert_with(Default::default)
533 .no_parallel = Some(val);
534 }
535 }
536 "file.recheck.method" => {
538 config
539 .file
540 .get_or_insert_with(Default::default)
541 .recheck
542 .get_or_insert_with(Default::default)
543 .method = Some(value.to_string());
544 }
545 "pipeline.current_pipeline" => {
547 config
548 .pipeline
549 .get_or_insert_with(Default::default)
550 .current_pipeline = Some(value.to_string());
551 }
552 "pipeline.default" => {
553 config.pipeline.get_or_insert_with(Default::default).default =
554 Some(value.to_string());
555 }
556 "pipeline.default_params_file" => {
557 config
558 .pipeline
559 .get_or_insert_with(Default::default)
560 .default_params_file = Some(value.to_string());
561 }
562 "pipeline.process_pool_size" => {
563 if let Ok(val) = value.parse::<u32>() {
564 config
565 .pipeline
566 .get_or_insert_with(Default::default)
567 .process_pool_size = Some(val);
568 }
569 }
570 "check_ignore.details" => {
572 if let Some(val) = Self::parse_bool(value) {
573 config
574 .check_ignore
575 .get_or_insert_with(Default::default)
576 .details = Some(val);
577 }
578 }
579 _ => {} }
581 }
582 config
583 }
584
585 pub fn from_env() -> Self {
592 let env_vars: HashMap<String, String> = std::env::vars().collect();
593 Self::from_hash_map("XVC_", &env_vars)
594 }
595}
596
597#[derive(Debug, Clone)]
601pub struct XvcConfigParams {
602 pub default_configuration: String,
606 pub current_dir: AbsolutePath,
610 pub include_system_config: bool,
613 pub include_user_config: bool,
616 pub project_config_path: Option<AbsolutePath>,
620 pub local_config_path: Option<AbsolutePath>,
624 pub include_environment_config: bool,
631 pub command_line_config: Option<Vec<String>>,
633}
634
635impl XvcConfigParams {
636 pub fn new(default_configuration: String, current_dir: AbsolutePath) -> Self {
647 Self {
648 default_configuration,
649 current_dir,
650 include_system_config: true,
651 include_user_config: true,
652 project_config_path: None,
653 local_config_path: None,
654 include_environment_config: true,
655 command_line_config: None,
656 }
657 }
658
659 pub fn include_system_config(mut self, include_system_config: bool) -> Self {
669 self.include_system_config = include_system_config;
670 self
671 }
672
673 pub fn include_user_config(mut self, include_user_config: bool) -> Self {
683 self.include_user_config = include_user_config;
684 self
685 }
686
687 pub fn project_config_path(mut self, project_config_path: Option<AbsolutePath>) -> Self {
697 self.project_config_path = project_config_path;
698 self
699 }
700
701 pub fn local_config_path(mut self, local_config_path: Option<AbsolutePath>) -> Self {
711 self.local_config_path = local_config_path;
712 self
713 }
714
715 pub fn include_environment_config(mut self, include_environment_config: bool) -> Self {
725 self.include_environment_config = include_environment_config;
726 self
727 }
728
729 pub fn command_line_config(mut self, command_line_config: Option<Vec<String>>) -> Self {
739 self.command_line_config = command_line_config;
740 self
741 }
742}
743
744pub fn default_config() -> XvcConfiguration {
753 XvcConfiguration {
754 core: CoreConfig {
755 xvc_repo_version: 2,
756 verbosity: "error".to_string(),
757 },
758 git: GitConfig {
759 use_git: true,
760 command: "git".to_string(),
761 auto_commit: true,
762 auto_stage: false,
763 },
764 cache: CacheConfig {
765 algorithm: "blake3".to_string(),
766 },
767 file: FileConfig {
768 track: FileTrackConfig {
769 no_commit: false,
770 force: false,
771 text_or_binary: "auto".to_string(),
772 no_parallel: false,
773 include_git_files: false,
774 },
775 list: FileListConfig {
776 format: "{{aft}}{{rrm}} {{asz}} {{ats}} {{rcd8}} {{acd8}} {{name}}".to_string(),
777 sort: "name-desc".to_string(),
778 show_dot_files: false,
779 no_summary: false,
780 recursive: false,
781 include_git_files: false,
782 },
783 carry_in: FileCarryInConfig {
784 force: false,
785 no_parallel: false,
786 },
787 recheck: FileRecheckConfig {
788 method: "copy".to_string(),
789 },
790 },
791 pipeline: PipelineConfig {
792 current_pipeline: "default".to_string(),
793 default: "default".to_string(),
794 default_params_file: "params.yaml".to_string(),
795 process_pool_size: 4,
796 },
797 check_ignore: CheckIgnoreConfig { details: false },
798 }
799}
800
801pub fn merge_configs(
815 config: &XvcConfiguration,
816 opt_config: &XvcOptionalConfiguration,
817) -> XvcConfiguration {
818 let core = CoreConfig {
819 xvc_repo_version: opt_config
820 .core
821 .clone()
822 .and_then(|c| c.xvc_repo_version)
823 .unwrap_or(config.core.xvc_repo_version),
824 verbosity: opt_config
825 .core
826 .clone()
827 .and_then(|c| c.verbosity)
828 .unwrap_or(config.core.verbosity.clone()),
829 };
830
831 let git = GitConfig {
832 use_git: opt_config
833 .git
834 .clone()
835 .and_then(|g| g.use_git)
836 .unwrap_or(config.git.use_git),
837 command: opt_config
838 .git
839 .clone()
840 .and_then(|g| g.command)
841 .unwrap_or(config.git.command.clone()),
842 auto_commit: opt_config
843 .git
844 .clone()
845 .and_then(|g| g.auto_commit)
846 .unwrap_or(config.git.auto_commit),
847 auto_stage: opt_config
848 .git
849 .clone()
850 .and_then(|g| g.auto_stage)
851 .unwrap_or(config.git.auto_stage),
852 };
853
854 let cache = CacheConfig {
855 algorithm: opt_config
856 .cache
857 .clone()
858 .and_then(|g| g.algorithm)
859 .unwrap_or(config.cache.algorithm.clone()),
860 };
861
862 let opt_track = opt_config.file.clone().and_then(|f| f.track);
863 let track = FileTrackConfig {
864 no_commit: opt_track
865 .clone()
866 .and_then(|t| t.no_commit)
867 .unwrap_or(config.file.track.no_commit),
868 force: opt_track
869 .clone()
870 .and_then(|t| t.force)
871 .unwrap_or(config.file.track.force),
872 text_or_binary: opt_track
873 .clone()
874 .and_then(|t| t.text_or_binary)
875 .unwrap_or(config.file.track.text_or_binary.clone()),
876 no_parallel: opt_track
877 .clone()
878 .and_then(|t| t.no_parallel)
879 .unwrap_or(config.file.track.no_parallel),
880 include_git_files: opt_track
881 .and_then(|t| t.include_git_files)
882 .unwrap_or(config.file.track.include_git_files),
883 };
884
885 let opt_list = opt_config.file.clone().and_then(|f| f.list);
886 let list = FileListConfig {
887 format: opt_list
888 .clone()
889 .and_then(|l| l.format)
890 .unwrap_or(config.file.list.format.clone()),
891 sort: opt_list
892 .clone()
893 .and_then(|l| l.sort)
894 .unwrap_or(config.file.list.sort.clone()),
895 show_dot_files: opt_list
896 .clone()
897 .and_then(|l| l.show_dot_files)
898 .unwrap_or(config.file.list.show_dot_files),
899 no_summary: opt_list
900 .clone()
901 .and_then(|l| l.no_summary)
902 .unwrap_or(config.file.list.no_summary),
903 recursive: opt_list
904 .clone()
905 .and_then(|l| l.recursive)
906 .unwrap_or(config.file.list.recursive),
907 include_git_files: opt_list
908 .and_then(|l| l.include_git_files)
909 .unwrap_or(config.file.list.include_git_files),
910 };
911
912 let opt_carry_in = opt_config.file.clone().and_then(|f| f.carry_in);
913 let carry_in = FileCarryInConfig {
914 force: opt_carry_in
915 .clone()
916 .and_then(|c| c.force)
917 .unwrap_or(config.file.carry_in.force),
918 no_parallel: opt_carry_in
919 .and_then(|c| c.no_parallel)
920 .unwrap_or(config.file.carry_in.no_parallel),
921 };
922
923 let opt_recheck = opt_config.file.clone().and_then(|f| f.recheck);
924 let recheck = FileRecheckConfig {
925 method: opt_recheck
926 .and_then(|r| r.method)
927 .unwrap_or(config.file.recheck.method.clone()),
928 };
929
930 let file = FileConfig {
931 track,
932 list,
933 carry_in,
934 recheck,
935 };
936
937 let pipeline = PipelineConfig {
938 current_pipeline: opt_config
939 .pipeline
940 .clone()
941 .and_then(|p| p.current_pipeline)
942 .unwrap_or(config.pipeline.current_pipeline.clone()),
943 default: opt_config
944 .pipeline
945 .clone()
946 .and_then(|p| p.default)
947 .unwrap_or(config.pipeline.default.clone()),
948 default_params_file: opt_config
949 .pipeline
950 .clone()
951 .and_then(|p| p.default_params_file)
952 .unwrap_or(config.pipeline.default_params_file.clone()),
953 process_pool_size: opt_config
954 .pipeline
955 .clone()
956 .and_then(|p| p.process_pool_size)
957 .unwrap_or(config.pipeline.process_pool_size),
958 };
959
960 let check_ignore = CheckIgnoreConfig {
961 details: opt_config
962 .check_ignore
963 .clone()
964 .and_then(|c| c.details)
965 .unwrap_or(config.check_ignore.details),
966 };
967
968 XvcConfiguration {
969 core,
970 git,
971 cache,
972 file,
973 pipeline,
974 check_ignore,
975 }
976}
977
978pub fn initial_xvc_configuration_file(config: &XvcConfiguration) -> Result<String> {
991 Ok(format!(
992 r##"
993[core]
994xvc_repo_version = {xvc_repo_version}
995# Default verbosity level.
996# One of "error", "warn", "info"
997verbosity = "{verbosity}"
998
999[git]
1000# Automate git operations.
1001# Turning this off leads Xvc to behave as if it's not in a Git repository.
1002# Not recommended unless you're really not using Git
1003use_git = {use_git}
1004# Command to run Git process.
1005# You can set this to an absolute path to specify an executable
1006# If set to a non-absolute path, the executable will be searched in $PATH.
1007command = "{git_command}"
1008
1009# Commit changes in .xvc/ directory after commands.
1010# You can set this to false if you want to commit manually.
1011auto_commit = {auto_commit}
1012
1013# Stage changes in .xvc/ directory without committing.
1014# auto_commit implies auto_stage.
1015# If you want to commit manually but don't want to stage after individual Xvc commands, you can set this to true.
1016auto_stage = {auto_stage}
1017
1018[cache]
1019# The hash algorithm used for the cache.
1020# It may take blake3, blake2, sha2 or sha3 as values.
1021# All algorithms are selected to produce 256-bit hashes, so sha2 means SHA2-256, blake2 means BLAKE2s, etc.
1022# The cache path is produced by prepending algorithm name to the cache.
1023# Blake3 files are in .xvc/b3/, while sha2 files are in .xvc/s2/ etc.
1024algorithm = "{cache_algorithm}"
1025
1026[file]
1027
1028[file.track]
1029
1030# Don't move file content to cache after xvc file track
1031no_commit = {file_track_no_commit}
1032# Force to track files even if they are already tracked.
1033force = {file_track_force}
1034
1035# Xvc calculates file content digest differently for text and binary files.
1036# This option controls whether to treat files as text or binary.
1037# It may take auto, text or binary as values.
1038# Auto check each file individually and treat it as text if it's text.
1039text_or_binary = "{file_track_text_or_binary}"
1040
1041# Don't use parallelism in track operations.
1042# Note that some of the operations are implemented in parallel by default, and this option affects some heavier operations.
1043no_parallel = {file_track_no_parallel}
1044
1045# Track files that are tracked by Git.
1046include_git_files = {file_track_include_git_files}
1047
1048[file.list]
1049
1050# Format for `xvc file list` rows. You can reorder or remove columns.
1051# The following are the keys for each row:
1052# - {{acd64}}: actual content digest. All 64 digits from the workspace file's content.
1053# - {{acd8}}: actual content digest. First 8 digits the file content digest.
1054# - {{aft}}: actual file type. Whether the entry is a file (F), directory (D),
1055# symlink (S), hardlink (H) or reflink (R).
1056# - {{asz}}: actual size. The size of the workspace file in bytes. It uses MB,
1057# GB and TB to represent sizes larger than 1MB.
1058# - {{ats}}: actual timestamp. The timestamp of the workspace file.
1059# - {{cst}}: cache status. One of "=", ">", "<", "X", or "?" to show
1060# whether the file timestamp is the same as the cached timestamp, newer,
1061# older, not cached or not tracked.
1062# - {{name}}: The name of the file or directory.
1063# - {{rcd64}}: recorded content digest. All 64 digits.
1064# - {{rcd8}}: recorded content digest. First 8 digits.
1065# - {{rrm}}: recorded recheck method. Whether the entry is linked to the workspace
1066# as a copy (C), symlink (S), hardlink (H) or reflink (R).
1067# - {{rsz}}: recorded size. The size of the cached content in bytes. It uses
1068# MB, GB and TB to represent sizes larger than 1MB.
1069# - {{rts}}: recorded timestamp. The timestamp of the cached content.
1070#
1071# There are no escape sequences in the format string.
1072# If you want to add a tab, type it to the string.
1073# If you want to add a literal double curly brace, open an issue.
1074format = "{file_list_format}"
1075
1076# Default sort order for `xvc file list`.
1077# Valid values are
1078# none, name-asc, name-desc, size-asc, size-desc, ts-asc, ts-desc.
1079sort = "{file_list_sort}"
1080
1081# Show dot files like .gitignore
1082show_dot_files = {file_list_show_dot_files}
1083
1084# Do not show a summary for as the final row for `xvc file list`.
1085no_summary = {file_list_no_summary}
1086
1087# List files recursively always.
1088recursive = {file_list_recursive}
1089
1090# List files tracked by Git.
1091include_git_files = {file_list_include_git_files}
1092
1093[file.carry-in]
1094# Carry-in the files to cache always, even if they are already present.
1095force = {file_carry_in_force}
1096
1097# Don't use parallel move/copy in carry-in
1098no_parallel = {file_carry_in_no_parallel}
1099
1100[file.recheck]
1101# The recheck method for Xvc. It may take copy, hardlink, symlink, reflink as values.
1102# The default is copy to make sure the options is portable.
1103# Copy duplicates the file content, while hardlink, symlink and reflink only create a new path to the file.
1104# Note that hardlink and symlink are read-only as they link the files in cache.
1105method = "{file_recheck_method}"
1106
1107[pipeline]
1108# Name of the current pipeline to run
1109current_pipeline = "{pipeline_current_pipeline}"
1110# Name of the default pipeline
1111default = "{pipeline_default}"
1112# Name of the default params file name
1113default_params_file = "{pipeline_default_params_file}"
1114# Number of command processes to run concurrently
1115process_pool_size = {pipeline_process_pool_size}
1116
1117[check_ignore]
1118# Show details by default
1119details = {check_ignore_details}
1120
1121"##,
1122 xvc_repo_version = config.core.xvc_repo_version,
1123 verbosity = config.core.verbosity,
1124 use_git = config.git.use_git,
1125 git_command = config.git.command,
1126 auto_commit = config.git.auto_commit,
1127 auto_stage = config.git.auto_stage,
1128 cache_algorithm = config.cache.algorithm,
1129 file_track_no_commit = config.file.track.no_commit,
1130 file_track_force = config.file.track.force,
1131 file_track_text_or_binary = config.file.track.text_or_binary,
1132 file_track_no_parallel = config.file.track.no_parallel,
1133 file_track_include_git_files = config.file.track.include_git_files,
1134 file_list_format = config.file.list.format,
1135 file_list_sort = config.file.list.sort,
1136 file_list_show_dot_files = config.file.list.show_dot_files,
1137 file_list_no_summary = config.file.list.no_summary,
1138 file_list_recursive = config.file.list.recursive,
1139 file_list_include_git_files = config.file.list.include_git_files,
1140 file_carry_in_force = config.file.carry_in.force,
1141 file_carry_in_no_parallel = config.file.carry_in.no_parallel,
1142 file_recheck_method = config.file.recheck.method,
1143 pipeline_current_pipeline = config.pipeline.current_pipeline,
1144 pipeline_default = config.pipeline.default,
1145 pipeline_default_params_file = config.pipeline.default_params_file,
1146 pipeline_process_pool_size = config.pipeline.process_pool_size,
1147 check_ignore_details = config.check_ignore.details,
1148 ))
1149}
1150
1151pub fn blank_optional_config() -> XvcOptionalConfiguration {
1159 XvcOptionalConfiguration {
1160 core: None,
1161 git: None,
1162 cache: None,
1163 file: None,
1164 pipeline: None,
1165 check_ignore: None,
1166 }
1167}
1168
1169impl XvcConfiguration {
1170 pub fn from_file(file_path: &Path) -> Result<Self> {
1180 let s =
1181 std::fs::read_to_string(&file_path).map_err(|e| crate::Error::IoError { source: e })?;
1182 let c: XvcConfiguration =
1183 toml::from_str(&s).map_err(|e| crate::Error::TomlDeserializationError { source: e })?;
1184 Ok(c)
1185 }
1186
1187 pub fn merge_with_optional(&self, opt_config: &XvcOptionalConfiguration) -> Self {
1197 merge_configs(self, opt_config)
1198 }
1199}