cli/
types.rs

1//! # types
2//!
3//! Defines the various types and aliases used by cargo-make.
4//!
5
6#[cfg(test)]
7#[path = "types_test.rs"]
8mod types_test;
9
10use crate::legacy;
11use crate::plugin::types::Plugins;
12use ci_info::types::CiInfo;
13use git_info::types::GitInfo;
14use indexmap::{IndexMap, IndexSet};
15use regex::Regex;
16use rust_info::types::RustInfo;
17use std::collections::HashMap;
18
19/// Returns the platform name
20pub fn get_platform_name() -> String {
21    if cfg!(windows) {
22        "windows".to_string()
23    } else if cfg!(target_os = "macos") || cfg!(target_os = "ios") {
24        "mac".to_string()
25    } else {
26        "linux".to_string()
27    }
28}
29
30fn get_namespaced_task_name(namespace: &str, task: &str) -> String {
31    let mut namespaced_task = String::new();
32
33    if namespace.len() > 0 {
34        namespaced_task.push_str(namespace);
35        namespaced_task.push_str("::");
36    }
37    namespaced_task.push_str(task);
38
39    namespaced_task
40}
41
42fn extend_script_value(
43    current_script_value: Option<ScriptValue>,
44    new_script_value: Option<ScriptValue>,
45) -> Option<ScriptValue> {
46    match current_script_value {
47        Some(ref current_value) => match new_script_value {
48            Some(ref new_value) => match current_value {
49                ScriptValue::Sections(current_sections) => match new_value {
50                    ScriptValue::Sections(new_sections) => {
51                        let pre = if new_sections.pre.is_some() {
52                            new_sections.pre.clone()
53                        } else {
54                            current_sections.pre.clone()
55                        };
56                        let main = if new_sections.main.is_some() {
57                            new_sections.main.clone()
58                        } else {
59                            current_sections.main.clone()
60                        };
61                        let post = if new_sections.post.is_some() {
62                            new_sections.post.clone()
63                        } else {
64                            current_sections.post.clone()
65                        };
66
67                        Some(ScriptValue::Sections(ScriptSections { pre, main, post }))
68                    }
69                    _ => current_script_value,
70                },
71                _ => new_script_value,
72            },
73            None => current_script_value,
74        },
75        None => new_script_value,
76    }
77}
78
79#[derive(Debug, Clone)]
80/// Holds CLI args
81pub struct CliArgs {
82    /// The command name
83    pub command: String,
84    /// The external Makefile.toml path
85    pub build_file: Option<String>,
86    /// The task to invoke
87    pub task: String,
88    /// The profile name
89    pub profile: Option<String>,
90    /// Log level name
91    pub log_level: String,
92    /// Disables colorful output
93    pub disable_color: bool,
94    /// Task completion for given shell
95    pub completion: Option<String>,
96    /// Current working directory
97    pub cwd: Option<String>,
98    /// Environment variables
99    pub env: Option<Vec<String>>,
100    /// Environment variables file
101    pub env_file: Option<String>,
102    /// Prevent workspace support
103    pub disable_workspace: bool,
104    /// Prevent on error flow even if defined in config section
105    pub disable_on_error: bool,
106    /// Allow invocation of private tasks
107    pub allow_private: bool,
108    /// If true, the init and end tasks are skipped
109    pub skip_init_end_tasks: bool,
110    /// Skip tasks that match the provided pattern
111    pub skip_tasks_pattern: Option<String>,
112    /// Only print the execution plan
113    pub print_only: bool,
114    /// List all known steps
115    pub list_all_steps: bool,
116    /// List steps for a given category
117    pub list_category_steps: Option<String>,
118    /// Diff flows
119    pub diff_execution_plan: bool,
120    /// Disables the update check during startup
121    pub disable_check_for_updates: bool,
122    /// Allows access unsupported experimental predefined tasks
123    pub experimental: bool,
124    /// additional command line arguments
125    pub arguments: Option<Vec<String>>,
126    /// Output format
127    pub output_format: String,
128    /// Output file name
129    pub output_file: Option<String>,
130    /// Print time summary at end of the flow
131    pub print_time_summary: bool,
132    /// Hide any minor tasks such as pre/post hooks
133    pub hide_uninteresting: bool,
134}
135
136impl CliArgs {
137    /// Creates and returns a new instance.
138    pub fn new() -> CliArgs {
139        CliArgs {
140            command: "".to_string(),
141            build_file: None,
142            task: "default".to_string(),
143            profile: None,
144            log_level: "info".to_string(),
145            disable_color: false,
146            completion: None,
147            cwd: None,
148            env: None,
149            env_file: None,
150            disable_workspace: false,
151            disable_on_error: false,
152            allow_private: false,
153            skip_init_end_tasks: false,
154            skip_tasks_pattern: None,
155            print_only: false,
156            list_all_steps: false,
157            list_category_steps: None,
158            diff_execution_plan: false,
159            disable_check_for_updates: false,
160            experimental: false,
161            arguments: None,
162            output_format: "default".to_string(),
163            output_file: None,
164            print_time_summary: false,
165            hide_uninteresting: false,
166        }
167    }
168}
169
170#[derive(Debug)]
171pub(crate) struct RunTaskOptions {
172    pub(crate) plugins_enabled: bool,
173}
174
175#[derive(Serialize, Deserialize, Debug, Clone, Default)]
176/// Holds persisted data used by cargo-make
177pub struct Cache {
178    /// File from which the cache file was loaded from
179    #[serde(skip)]
180    pub file_name: Option<String>,
181    /// Holds last update check with returned no updates result
182    pub last_update_check: Option<u64>,
183}
184
185impl Cache {
186    /// Returns new instance
187    pub fn new() -> Cache {
188        Default::default()
189    }
190}
191
192#[derive(Serialize, Deserialize, Debug, Clone, Default)]
193/// Holds configuration info for cargo-make
194pub struct GlobalConfig {
195    /// File from which the global config was loaded from
196    #[serde(skip)]
197    pub file_name: Option<String>,
198    /// Default log level
199    pub log_level: Option<String>,
200    /// Default output coloring
201    pub disable_color: Option<bool>,
202    /// Default task name
203    pub default_task_name: Option<String>,
204    /// Update check minimum time from the previous check (always, daily, weekly, monthly)
205    pub update_check_minimum_interval: Option<String>,
206    /// True to search for project root in parent directories if current cwd is not a project root
207    pub search_project_root: Option<bool>,
208}
209
210impl GlobalConfig {
211    /// Returns new instance
212    pub fn new() -> GlobalConfig {
213        GlobalConfig {
214            search_project_root: Some(false),
215            ..Default::default()
216        }
217    }
218}
219
220#[derive(Serialize, Deserialize, Debug, Clone, Default)]
221/// Holds crate workspace info, see <http://doc.crates.io/manifest.html#the-workspace-section>
222pub struct Workspace {
223    /// members paths
224    pub members: Option<Vec<String>>,
225    /// exclude paths
226    pub exclude: Option<Vec<String>>,
227    /// workspace level dependencies
228    pub dependencies: Option<IndexMap<String, CrateDependency>>,
229    /// root package
230    pub package: Option<PackageInfo>,
231}
232
233impl Workspace {
234    /// Creates and returns a new instance.
235    pub fn new() -> Workspace {
236        Default::default()
237    }
238}
239
240#[derive(Serialize, Deserialize, Debug, Clone, Default)]
241/// Holds crate package information loaded from the Cargo.toml file package section.
242pub struct PackageInfo {
243    /// name
244    pub name: Option<String>,
245    /// version
246    pub version: Option<String>,
247    /// description
248    pub description: Option<String>,
249    /// license
250    pub license: Option<String>,
251    /// documentation link
252    pub documentation: Option<String>,
253    /// homepage link
254    pub homepage: Option<String>,
255    /// repository link
256    pub repository: Option<String>,
257}
258
259impl PackageInfo {
260    /// Creates and returns a new instance.
261    pub fn new() -> PackageInfo {
262        Default::default()
263    }
264}
265
266#[derive(Serialize, Deserialize, Debug, Clone)]
267/// Holds crate dependency info.
268pub struct CrateDependencyInfo {
269    /// Holds the dependency path
270    pub path: Option<String>,
271}
272
273#[derive(Serialize, Deserialize, Debug, Clone)]
274#[serde(untagged)]
275/// Holds crate dependency info.
276pub enum CrateDependency {
277    /// Holds the dependency version
278    Version(String),
279    /// Hold dependency info
280    Info(CrateDependencyInfo),
281}
282
283#[derive(Deserialize, Debug, Clone, Default)]
284/// Holds crate information loaded from the Cargo.toml file.
285pub struct CrateInfo {
286    /// package info
287    pub package: Option<PackageInfo>,
288    /// workspace info
289    pub workspace: Option<Workspace>,
290    /// crate dependencies
291    pub dependencies: Option<IndexMap<String, CrateDependency>>,
292}
293
294impl CrateInfo {
295    /// Creates and returns a new instance.
296    pub fn new() -> CrateInfo {
297        Default::default()
298    }
299}
300
301#[derive(Debug, Clone)]
302/// Holds env information
303pub struct EnvInfo {
304    /// Rust info
305    pub rust_info: RustInfo,
306    /// Crate info
307    pub crate_info: CrateInfo,
308    /// Git info
309    pub git_info: GitInfo,
310    /// CI info
311    pub ci_info: CiInfo,
312}
313
314#[derive(Debug, Clone)]
315/// Holds flow information
316pub struct FlowInfo {
317    /// The flow config object
318    pub config: Config,
319    /// The main task of the flow
320    pub task: String,
321    /// The env info
322    pub env_info: EnvInfo,
323    /// Prevent workspace support
324    pub disable_workspace: bool,
325    /// Prevent on error flow even if defined in config section
326    pub disable_on_error: bool,
327    /// Allow invocation of private tasks
328    pub allow_private: bool,
329    /// If true, the init and end tasks are skipped
330    pub skip_init_end_tasks: bool,
331    /// Skip tasks that match the provided pattern
332    pub skip_tasks_pattern: Option<Regex>,
333    /// additional command line arguments
334    pub cli_arguments: Option<Vec<String>>,
335}
336
337#[derive(Debug, Clone, Default)]
338/// Holds mutable flow state
339pub struct FlowState {
340    /// timing info for summary
341    pub time_summary: Vec<(String, u128)>,
342    /// forced plugin name
343    pub forced_plugin: Option<String>,
344}
345
346impl FlowState {
347    /// Creates and returns a new instance.
348    pub fn new() -> FlowState {
349        Default::default()
350    }
351}
352
353#[derive(Serialize, Deserialize, Debug, Clone)]
354/// Rust version condition structure
355pub struct RustVersionCondition {
356    /// min version number
357    pub min: Option<String>,
358    /// max version number
359    pub max: Option<String>,
360    /// specific version number
361    pub equal: Option<String>,
362}
363
364#[derive(Serialize, Deserialize, Debug, Clone)]
365/// Files modified (input/output) condition structure
366pub struct FilesFilesModifiedCondition {
367    /// input files
368    pub input: Vec<String>,
369    /// output files
370    pub output: Vec<String>,
371}
372
373#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
374/// Control how condition checks are evaluated
375pub enum ConditionType {
376    /// All conditions must pass
377    And,
378    /// Any condition must pass
379    Or,
380    /// Any condition group must pass, but each group will be validated as an AND
381    GroupOr,
382}
383
384#[derive(Serialize, Deserialize, Debug, Clone, Default)]
385/// Holds condition attributes
386pub struct TaskCondition {
387    /// condition type (AND/OR) by default AND
388    pub condition_type: Option<ConditionType>,
389    /// Failure message
390    pub fail_message: Option<String>,
391    /// Profile names (development, ...)
392    pub profiles: Option<Vec<String>>,
393    /// As defined in the cfg target_os
394    pub os: Option<Vec<String>>,
395    /// Platform names (linux, windows, mac)
396    pub platforms: Option<Vec<String>>,
397    /// Channel names (stable, beta, nightly)
398    pub channels: Option<Vec<String>>,
399    /// Environment variables which must be defined
400    pub env_set: Option<Vec<String>>,
401    /// Environment variables which must not be defined
402    pub env_not_set: Option<Vec<String>>,
403    /// Environment variables and their values
404    pub env: Option<IndexMap<String, String>>,
405    /// Environment variables and the values which they must not be defined as
406    pub env_not: Option<IndexMap<String, String>>,
407    /// Environment variables which are defined as true
408    pub env_true: Option<Vec<String>>,
409    /// Environment variables which are defined as false
410    pub env_false: Option<Vec<String>>,
411    /// Environment variables and the values which they are required to contain
412    pub env_contains: Option<IndexMap<String, String>>,
413    /// Rust version condition
414    pub rust_version: Option<RustVersionCondition>,
415    /// Files exist
416    pub files_exist: Option<Vec<String>>,
417    /// Files which do not exist
418    pub files_not_exist: Option<Vec<String>>,
419    /// Files modified since last execution
420    pub files_modified: Option<FilesFilesModifiedCondition>,
421}
422
423impl TaskCondition {
424    pub fn get_condition_type(&self) -> ConditionType {
425        match self.condition_type {
426            Some(ref value) => value.clone(),
427            None => ConditionType::And,
428        }
429    }
430}
431
432#[derive(Serialize, Deserialize, Debug, Clone)]
433/// Env file path and attributes
434pub struct EnvFileInfo {
435    /// The file path as string
436    pub path: String,
437    /// The path base directory (relative paths are from this base path)
438    pub base_path: Option<String>,
439    /// The profile name this file is relevant to
440    pub profile: Option<String>,
441    /// If true, only set the env vars if not already defined
442    pub defaults_only: Option<bool>,
443}
444
445impl EnvFileInfo {
446    /// Creates and returns a new instance.
447    pub fn new(path: String) -> EnvFileInfo {
448        EnvFileInfo {
449            path,
450            base_path: None,
451            profile: None,
452            defaults_only: None,
453        }
454    }
455}
456
457#[derive(Serialize, Deserialize, Debug, Clone)]
458#[serde(untagged)]
459/// Holds the env file path and attributes
460pub enum EnvFile {
461    /// The file path as string
462    Path(String),
463    /// Extended info object for env file
464    Info(EnvFileInfo),
465}
466
467#[derive(Serialize, Deserialize, Debug, Clone)]
468/// Env value provided by a script
469pub struct EnvValueScript {
470    /// The script to execute to get the env value
471    pub script: Vec<String>,
472    /// True/False to enable multi line env values
473    pub multi_line: Option<bool>,
474    /// The condition to validate
475    pub condition: Option<TaskCondition>,
476    /// The explicit environment variables this script depends on
477    pub depends_on: Option<Vec<String>>,
478}
479
480#[derive(Serialize, Deserialize, Debug, Clone)]
481/// Env value provided by decoding other values
482pub struct EnvValueDecode {
483    /// The source value (can be an env expression)
484    pub source: String,
485    /// The default value in case no decode mapping was found, if not provided it will default to the source value
486    pub default_value: Option<String>,
487    /// The decoding mapping
488    pub mapping: HashMap<String, String>,
489    /// The condition to validate
490    pub condition: Option<TaskCondition>,
491}
492
493#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
494/// Enables to unset env variables
495pub struct EnvValueUnset {
496    /// If true, the env variable will be unset, else ignored
497    pub unset: bool,
498}
499
500#[derive(Serialize, Deserialize, Debug, Clone)]
501/// Env value set if condition is met
502pub struct EnvValueConditioned {
503    /// The value to set (can be an env expression)
504    pub value: String,
505    /// The condition to validate
506    pub condition: Option<TaskCondition>,
507}
508
509#[derive(Serialize, Deserialize, Debug, Clone)]
510/// Env value holding a list of paths based on given glob definitions
511pub struct EnvValuePathGlob {
512    /// The glob used to fetch all paths
513    pub glob: String,
514    /// True to include files (default is true if undefined)
515    pub include_files: Option<bool>,
516    /// True to include directories (default is true if undefined)
517    pub include_dirs: Option<bool>,
518    /// Enables to respect ignore files
519    pub ignore_type: Option<String>,
520}
521
522#[derive(Serialize, Deserialize, Debug, Clone)]
523#[serde(untagged)]
524/// Holds the env value or script
525pub enum EnvValue {
526    /// The value as string
527    Value(String),
528    /// The value as boolean
529    Boolean(bool),
530    /// The value as number
531    Number(isize),
532    /// The value as a list of strings
533    List(Vec<String>),
534    /// Unset env
535    Unset(EnvValueUnset),
536    /// Script which will return the value
537    Script(EnvValueScript),
538    /// Env decoding info
539    Decode(EnvValueDecode),
540    /// Conditional env value
541    Conditional(EnvValueConditioned),
542    /// Path glob
543    PathGlob(EnvValuePathGlob),
544    /// Profile env
545    Profile(IndexMap<String, EnvValue>),
546}
547
548/// Arguments used to check whether a crate or rustup component is installed.
549///
550/// Deserialize into an array of strings. Allows both a single string (which will
551/// become a single-element array) or a sequence of strings.
552#[derive(Debug, Serialize, Clone, PartialEq, Eq)]
553#[serde(transparent)]
554pub struct TestArg {
555    /// Content of the arguments
556    pub inner: Vec<String>,
557}
558
559impl std::ops::Deref for TestArg {
560    type Target = Vec<String>;
561    fn deref(&self) -> &Self::Target {
562        &self.inner
563    }
564}
565
566impl std::ops::DerefMut for TestArg {
567    fn deref_mut(&mut self) -> &mut Self::Target {
568        &mut self.inner
569    }
570}
571
572impl<'de> serde::de::Deserialize<'de> for TestArg {
573    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
574    where
575        D: serde::de::Deserializer<'de>,
576    {
577        struct StringVecVisitor;
578        impl<'de> serde::de::Visitor<'de> for StringVecVisitor {
579            type Value = TestArg;
580
581            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
582                formatter.write_str("A string or an array of strings")
583            }
584
585            fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
586            where
587                E: serde::de::Error,
588            {
589                Ok(TestArg {
590                    inner: vec![s.to_string()],
591                })
592            }
593
594            fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
595            where
596                A: serde::de::SeqAccess<'de>,
597            {
598                let mut v = Vec::with_capacity(seq.size_hint().unwrap_or(0));
599                while let Some(s) = seq.next_element()? {
600                    v.push(s);
601                }
602
603                Ok(TestArg { inner: v })
604            }
605        }
606        deserializer.deserialize_any(StringVecVisitor)
607    }
608}
609
610#[derive(Serialize, Deserialize, Debug, Clone)]
611/// Holds instructions how to install the cargo plugin
612pub struct InstallCargoPluginInfo {
613    /// The provided crate to install
614    pub crate_name: Option<String>,
615    /// Minimal version
616    pub min_version: Option<String>,
617    /// Optional alternate 'install' command
618    pub install_command: Option<String>,
619    /// Optional add force flag (if needed), default is true
620    pub force: Option<bool>,
621}
622
623impl PartialEq for InstallCargoPluginInfo {
624    fn eq(&self, other: &InstallCargoPluginInfo) -> bool {
625        let mut same = match self.crate_name {
626            Some(ref crate_name) => match other.crate_name {
627                Some(ref other_crate_name) => crate_name == other_crate_name,
628                None => false,
629            },
630            None => match other.crate_name {
631                None => true,
632                _ => false,
633            },
634        };
635        if !same {
636            return false;
637        }
638
639        same = match self.min_version {
640            Some(ref min_version) => match other.min_version {
641                Some(ref other_min_version) => min_version == other_min_version,
642                None => false,
643            },
644            None => match other.min_version {
645                None => true,
646                _ => false,
647            },
648        };
649        if !same {
650            return false;
651        }
652
653        same = match self.install_command {
654            Some(ref install_command) => match other.install_command {
655                Some(ref other_install_command) => install_command == other_install_command,
656                None => false,
657            },
658            None => match other.install_command {
659                None => true,
660                _ => false,
661            },
662        };
663        if !same {
664            return false;
665        }
666
667        match self.force {
668            Some(ref force) => match other.force {
669                Some(ref other_force) => force == other_force,
670                None => false,
671            },
672            None => match other.force {
673                None => true,
674                _ => false,
675            },
676        }
677    }
678}
679
680#[derive(Serialize, Deserialize, Debug, Clone)]
681/// Holds instructions how to install the crate
682pub struct InstallCrateInfo {
683    /// The provided crate to install
684    pub crate_name: String,
685    /// If defined, the component to install via rustup
686    pub rustup_component_name: Option<String>,
687    /// The binary file name to be used to test if the crate is already installed
688    pub binary: String,
689    /// Test arguments that will be used to check that the crate is installed.
690    pub test_arg: TestArg,
691    /// Minimal version
692    pub min_version: Option<String>,
693    /// Exact version
694    pub version: Option<String>,
695    /// Optional alternate 'install' command
696    pub install_command: Option<String>,
697    /// Optional add force flag (if needed), default is true
698    pub force: Option<bool>,
699}
700
701impl PartialEq for InstallCrateInfo {
702    fn eq(&self, other: &InstallCrateInfo) -> bool {
703        if self.crate_name != other.crate_name
704            || self.binary != other.binary
705            || self.test_arg != other.test_arg
706        {
707            return false;
708        }
709
710        let mut same = match self.rustup_component_name {
711            Some(ref rustup_component_name) => match other.rustup_component_name {
712                Some(ref other_rustup_component_name) => {
713                    if rustup_component_name == other_rustup_component_name {
714                        true
715                    } else {
716                        false
717                    }
718                }
719                None => false,
720            },
721            None => match other.rustup_component_name {
722                None => true,
723                _ => false,
724            },
725        };
726        if !same {
727            return false;
728        }
729
730        same = match self.min_version {
731            Some(ref min_version) => match other.min_version {
732                Some(ref other_min_version) => min_version == other_min_version,
733                None => false,
734            },
735            None => match other.min_version {
736                None => true,
737                _ => false,
738            },
739        };
740        if !same {
741            return false;
742        }
743
744        same = match self.version {
745            Some(ref version) => match other.version {
746                Some(ref other_version) => version == other_version,
747                None => false,
748            },
749            None => match other.version {
750                None => true,
751                _ => false,
752            },
753        };
754        if !same {
755            return false;
756        }
757
758        same = match self.install_command {
759            Some(ref install_command) => match other.install_command {
760                Some(ref other_install_command) => install_command == other_install_command,
761                None => false,
762            },
763            None => match other.install_command {
764                None => true,
765                _ => false,
766            },
767        };
768        if !same {
769            return false;
770        }
771
772        match self.force {
773            Some(ref force) => match other.force {
774                Some(ref other_force) => force == other_force,
775                None => false,
776            },
777            None => match other.force {
778                None => true,
779                _ => false,
780            },
781        }
782    }
783}
784
785#[derive(Serialize, Deserialize, Debug, Clone)]
786/// Holds instructions how to install a rustup component
787pub struct InstallRustupComponentInfo {
788    /// The component to install via rustup
789    pub rustup_component_name: String,
790    /// The binary file name to be used to test if the crate is already installed
791    pub binary: Option<String>,
792    /// Test argument that will be used to check that the crate is installed
793    pub test_arg: Option<TestArg>,
794}
795
796impl PartialEq for InstallRustupComponentInfo {
797    fn eq(&self, other: &InstallRustupComponentInfo) -> bool {
798        if self.rustup_component_name != other.rustup_component_name {
799            false
800        } else {
801            let same = match self.binary {
802                Some(ref value) => match other.binary {
803                    Some(ref other_value) => value == other_value,
804                    None => false,
805                },
806                None => match other.binary {
807                    None => true,
808                    _ => false,
809                },
810            };
811
812            if same {
813                self.test_arg == other.test_arg
814            } else {
815                false
816            }
817        }
818    }
819}
820
821#[derive(Serialize, Deserialize, Debug, Clone)]
822#[serde(untagged)]
823/// Install crate name or params
824pub enum InstallCrate {
825    /// Enables to prevent installation flow
826    Enabled(bool),
827    /// The value as string
828    Value(String),
829    /// Install crate params
830    CrateInfo(InstallCrateInfo),
831    /// Install rustup component params
832    RustupComponentInfo(InstallRustupComponentInfo),
833    /// Install cargo plugin info
834    CargoPluginInfo(InstallCargoPluginInfo),
835}
836
837impl PartialEq for InstallCrate {
838    fn eq(&self, other: &InstallCrate) -> bool {
839        match self {
840            InstallCrate::Enabled(value) => match other {
841                InstallCrate::Enabled(other_value) => value == other_value,
842                _ => false,
843            },
844            InstallCrate::Value(value) => match other {
845                InstallCrate::Value(other_value) => value == other_value,
846                _ => false,
847            },
848            InstallCrate::CargoPluginInfo(info) => match other {
849                InstallCrate::CargoPluginInfo(other_info) => info == other_info,
850                _ => false,
851            },
852            InstallCrate::CrateInfo(info) => match other {
853                InstallCrate::CrateInfo(other_info) => info == other_info,
854                _ => false,
855            },
856            InstallCrate::RustupComponentInfo(info) => match other {
857                InstallCrate::RustupComponentInfo(other_info) => info == other_info,
858                _ => false,
859            },
860        }
861    }
862}
863
864#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
865#[serde(untagged)]
866/// Holds the run task name/s
867pub enum RunTaskName {
868    /// Single task name
869    Single(String),
870    /// Multiple task names
871    Multiple(Vec<String>),
872}
873
874#[derive(Serialize, Deserialize, Debug, Clone)]
875/// Holds the run task information
876pub struct RunTaskDetails {
877    /// The task name
878    pub name: RunTaskName,
879    /// True to fork the task to a new sub process
880    pub fork: Option<bool>,
881    /// True to run all tasks in parallel (default false)
882    pub parallel: Option<bool>,
883    /// Cleanup task name
884    pub cleanup_task: Option<String>,
885}
886
887#[derive(Serialize, Deserialize, Debug, Clone)]
888/// Holds the run task routing information
889pub struct RunTaskRoutingInfo {
890    /// The task name
891    pub name: RunTaskName,
892    /// True to fork the task to a new sub process
893    pub fork: Option<bool>,
894    /// True to run all tasks in parallel (default false)
895    pub parallel: Option<bool>,
896    /// Cleanup task name
897    pub cleanup_task: Option<String>,
898    /// if provided all condition values must be met in order for the task to be invoked
899    pub condition: Option<TaskCondition>,
900    /// if script exit code is not 0, the task will not be invoked
901    pub condition_script: Option<ConditionScriptValue>,
902    /// The script runner arguments before the script file path
903    pub condition_script_runner_args: Option<Vec<String>>,
904}
905
906#[derive(Serialize, Deserialize, Debug, Clone)]
907#[serde(untagged)]
908/// Run task info
909pub enum RunTaskInfo {
910    /// Task name
911    Name(String),
912    /// Run Task Info
913    Details(RunTaskDetails),
914    /// Task conditional selector
915    Routing(Vec<RunTaskRoutingInfo>),
916}
917
918#[derive(Serialize, Deserialize, Debug, Clone)]
919/// Holds watch options
920pub struct WatchOptions {
921    /// Watch version to install if not already installed
922    pub version: Option<String>,
923    /// Postpone first run until a file changes
924    pub postpone: Option<bool>,
925    /// Ignore a glob/gitignore-style pattern
926    pub ignore_pattern: Option<MaybeArray<String>>,
927    /// Do not use .gitignore files
928    pub no_git_ignore: Option<bool>,
929    /// Show paths that changed
930    pub why: Option<bool>,
931    /// Select which files/folders to watch
932    pub watch: Option<Vec<String>>,
933}
934
935#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
936#[serde(untagged)]
937/// Could be an array or single value
938pub enum MaybeArray<T> {
939    /// Single value
940    Single(T),
941    /// Multiple values
942    Multiple(Vec<T>),
943}
944
945impl PartialEq for WatchOptions {
946    fn eq(&self, other: &WatchOptions) -> bool {
947        let mut same = match self.version {
948            Some(ref value) => match other.version {
949                Some(ref other_value) => value == other_value,
950                None => false,
951            },
952            None => match other.version {
953                None => true,
954                _ => false,
955            },
956        };
957
958        same = if same {
959            match self.postpone {
960                Some(ref value) => match other.postpone {
961                    Some(ref other_value) => value == other_value,
962                    None => false,
963                },
964                None => match other.postpone {
965                    None => true,
966                    _ => false,
967                },
968            }
969        } else {
970            false
971        };
972
973        same = if same {
974            match self.ignore_pattern {
975                Some(ref value) => match other.ignore_pattern {
976                    Some(ref other_value) => value == other_value,
977                    None => false,
978                },
979                None => match other.ignore_pattern {
980                    None => true,
981                    _ => false,
982                },
983            }
984        } else {
985            false
986        };
987
988        same = if same {
989            match self.no_git_ignore {
990                Some(ref value) => match other.no_git_ignore {
991                    Some(ref other_value) => value == other_value,
992                    None => false,
993                },
994                None => match other.no_git_ignore {
995                    None => true,
996                    _ => false,
997                },
998            }
999        } else {
1000            false
1001        };
1002
1003        if same {
1004            match self.watch {
1005                Some(ref value) => match other.watch {
1006                    Some(ref other_value) => value == other_value,
1007                    None => false,
1008                },
1009                None => match other.watch {
1010                    None => true,
1011                    _ => false,
1012                },
1013            }
1014        } else {
1015            false
1016        }
1017    }
1018}
1019
1020#[derive(Serialize, Deserialize, Debug, Clone)]
1021#[serde(untagged)]
1022/// Holds watch options or simple true/false value
1023pub enum TaskWatchOptions {
1024    /// True/False to enable/disable watch
1025    Boolean(bool),
1026    /// Extended configuration for watch
1027    Options(WatchOptions),
1028}
1029
1030impl PartialEq for TaskWatchOptions {
1031    fn eq(&self, other: &TaskWatchOptions) -> bool {
1032        match self {
1033            TaskWatchOptions::Boolean(value) => match other {
1034                TaskWatchOptions::Boolean(other_value) => value == other_value,
1035                _ => false,
1036            },
1037            TaskWatchOptions::Options(info) => match other {
1038                TaskWatchOptions::Options(other_info) => info == other_info,
1039                _ => false,
1040            },
1041        }
1042    }
1043}
1044
1045#[derive(Serialize, Deserialize, Debug, Clone)]
1046#[serde(untagged)]
1047/// Holds deprecation info such as true/false/message
1048pub enum DeprecationInfo {
1049    /// True/False flag (true is deprecated)
1050    Boolean(bool),
1051    /// Deprecation message
1052    Message(String),
1053}
1054
1055impl PartialEq for DeprecationInfo {
1056    fn eq(&self, other: &DeprecationInfo) -> bool {
1057        match self {
1058            DeprecationInfo::Boolean(value) => match other {
1059                DeprecationInfo::Boolean(other_value) => value == other_value,
1060                _ => false,
1061            },
1062            DeprecationInfo::Message(message) => match other {
1063                DeprecationInfo::Message(other_message) => message == other_message,
1064                _ => false,
1065            },
1066        }
1067    }
1068}
1069
1070#[derive(Serialize, Deserialize, Debug, Clone)]
1071/// Script file name
1072pub struct FileScriptValue {
1073    /// Script file name
1074    pub file: String,
1075    /// True for absolute path (default false)
1076    pub absolute_path: Option<bool>,
1077}
1078
1079#[derive(Serialize, Deserialize, Debug, Clone)]
1080/// Script content split to parts to enable a more fine tuned extension capability
1081pub struct ScriptSections {
1082    /// Script section
1083    pub pre: Option<String>,
1084    /// Script section
1085    pub main: Option<String>,
1086    /// Script section
1087    pub post: Option<String>,
1088}
1089
1090#[derive(Serialize, Deserialize, Debug, Clone)]
1091#[serde(untagged)]
1092/// Script value (text, file name, ...)
1093pub enum ScriptValue {
1094    /// The script text as single line
1095    SingleLine(String),
1096    /// The script text lines
1097    Text(Vec<String>),
1098    /// Script file name
1099    File(FileScriptValue),
1100    /// Script content split to multiple parts to enable fine tuned extension
1101    Sections(ScriptSections),
1102}
1103
1104#[derive(Serialize, Deserialize, Debug, Clone)]
1105#[serde(untagged)]
1106/// Condition script value (not as advanced as normal script value)
1107pub enum ConditionScriptValue {
1108    /// The script text as single line
1109    SingleLine(String),
1110    /// The script text lines
1111    Text(Vec<String>),
1112}
1113
1114#[derive(Serialize, Deserialize, Debug, Clone, Default)]
1115/// Holds a single task configuration such as command and dependencies list
1116pub struct Task {
1117    /// if true, it should ignore all data in base task
1118    pub clear: Option<bool>,
1119    /// Task description
1120    pub description: Option<String>,
1121    /// Category name used to document the task
1122    pub category: Option<String>,
1123    /// if true, the command/script of this task will not be invoked, dependencies however will be
1124    pub disabled: Option<bool>,
1125    /// if true, the task is hidden from the list of available tasks and also cannot be invoked directly from cli
1126    pub private: Option<bool>,
1127    /// if not false, this task is defined as deprecated
1128    pub deprecated: Option<DeprecationInfo>,
1129    /// Extend any task based on the defined name
1130    pub extend: Option<String>,
1131    /// set to false to notify cargo-make that this is not a workspace and should not call task for every member (same as --no-workspace CLI flag)
1132    pub workspace: Option<bool>,
1133    /// Optional plugin used to execute the task
1134    pub plugin: Option<String>,
1135    /// set to true to watch for file changes and invoke the task operation
1136    pub watch: Option<TaskWatchOptions>,
1137    /// if provided all condition values must be met in order for the task to be invoked (will not stop dependencies)
1138    pub condition: Option<TaskCondition>,
1139    /// if script exit code is not 0, the command/script of this task will not be invoked, dependencies however will be
1140    pub condition_script: Option<ConditionScriptValue>,
1141    /// The script runner arguments before the script file path
1142    pub condition_script_runner_args: Option<Vec<String>>,
1143    /// if true, any error while executing the task will be printed but will not break the build
1144    pub ignore_errors: Option<bool>,
1145    /// DEPRECATED, replaced with ignore_errors
1146    pub force: Option<bool>,
1147    /// The env files to setup before running the task commands
1148    pub env_files: Option<Vec<EnvFile>>,
1149    /// The env vars to setup before running the task commands
1150    pub env: Option<IndexMap<String, EnvValue>>,
1151    /// The working directory for the task to execute its command/script
1152    pub cwd: Option<String>,
1153    /// if defined, task points to another task and all other properties are ignored
1154    pub alias: Option<String>,
1155    /// acts like alias if runtime OS is Linux (takes precedence over alias)
1156    pub linux_alias: Option<String>,
1157    /// acts like alias if runtime OS is Windows (takes precedence over alias)
1158    pub windows_alias: Option<String>,
1159    /// acts like alias if runtime OS is Mac (takes precedence over alias)
1160    pub mac_alias: Option<String>,
1161    /// if defined, the provided crate will be installed (if needed) before running the task
1162    pub install_crate: Option<InstallCrate>,
1163    /// additional cargo install arguments
1164    pub install_crate_args: Option<Vec<String>>,
1165    /// if defined, the provided script will be executed before running the task
1166    pub install_script: Option<ScriptValue>,
1167    /// The command to execute
1168    pub command: Option<String>,
1169    /// The command args
1170    pub args: Option<Vec<String>>,
1171    /// If command is not defined, and script is defined, the provided script will be executed
1172    pub script: Option<ScriptValue>,
1173    /// The script runner (defaults to cmd in windows and sh for other platforms)
1174    pub script_runner: Option<String>,
1175    /// The script runner arguments before the script file path
1176    pub script_runner_args: Option<Vec<String>>,
1177    /// The script file extension
1178    pub script_extension: Option<String>,
1179    /// The task name to execute
1180    pub run_task: Option<RunTaskInfo>,
1181    /// A list of tasks to execute before this task
1182    pub dependencies: Option<Vec<DependencyIdentifier>>,
1183    /// The rust toolchain used to invoke the command or install the needed crates/components
1184    pub toolchain: Option<ToolchainSpecifier>,
1185    /// override task if runtime OS is Linux (takes precedence over alias)
1186    pub linux: Option<PlatformOverrideTask>,
1187    /// override task if runtime OS is Windows (takes precedence over alias)
1188    pub windows: Option<PlatformOverrideTask>,
1189    /// override task if runtime OS is Mac (takes precedence over alias)
1190    pub mac: Option<PlatformOverrideTask>,
1191}
1192
1193/// A toolchain, defined either as a string (following the rustup syntax)
1194/// or a ToolchainBoundedSpecifier.
1195#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
1196#[serde(untagged)]
1197pub enum ToolchainSpecifier {
1198    /// A string specifying the channel name of the toolchain
1199    Simple(String),
1200    /// A toolchain with a minimum version bound
1201    Bounded(ToolchainBoundedSpecifier),
1202}
1203
1204impl From<String> for ToolchainSpecifier {
1205    fn from(channel: String) -> Self {
1206        Self::Simple(channel)
1207    }
1208}
1209
1210impl From<&str> for ToolchainSpecifier {
1211    fn from(channel: &str) -> Self {
1212        channel.to_string().into()
1213    }
1214}
1215
1216impl std::fmt::Display for ToolchainSpecifier {
1217    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1218        match self {
1219            Self::Simple(ref channel) => write!(formatter, "{}", channel),
1220            Self::Bounded(ref spec) => write!(formatter, "{}", spec),
1221        }
1222    }
1223}
1224
1225impl ToolchainSpecifier {
1226    /// Return the channel of the toolchain to look for
1227    pub fn channel(&self) -> &str {
1228        match self {
1229            Self::Simple(ref channel) => &channel,
1230            Self::Bounded(ToolchainBoundedSpecifier { ref channel, .. }) => channel,
1231        }
1232    }
1233
1234    /// Return the minimal version, if any, to look for
1235    pub fn min_version(&self) -> Option<&str> {
1236        match self {
1237            Self::Simple(_) => None,
1238            Self::Bounded(ToolchainBoundedSpecifier {
1239                ref min_version, ..
1240            }) => Some(min_version),
1241        }
1242    }
1243}
1244
1245/// A toolchain with a minimum version bound
1246#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
1247pub struct ToolchainBoundedSpecifier {
1248    /// The channel of the toolchain to use
1249    pub channel: String,
1250    /// The minimum version to match
1251    pub min_version: String,
1252}
1253
1254impl std::fmt::Display for ToolchainBoundedSpecifier {
1255    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1256        write!(formatter, "{} >= {}", self.channel, self.min_version)
1257    }
1258}
1259
1260/// A dependency, defined either as a string or as a Dependency object
1261#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
1262#[serde(untagged)]
1263pub enum DependencyIdentifier {
1264    /// A full dependency definition (potentially in a different file)
1265    Definition(TaskIdentifier),
1266    /// A string dependency definition (its name in the current file)
1267    Name(String),
1268}
1269
1270impl From<&str> for DependencyIdentifier {
1271    fn from(name: &str) -> Self {
1272        DependencyIdentifier::Name(name.to_string())
1273    }
1274}
1275
1276impl DependencyIdentifier {
1277    /// Get the name of a dependency
1278    pub fn name(&self) -> &str {
1279        match self {
1280            DependencyIdentifier::Definition(identifier) => &identifier.name,
1281            DependencyIdentifier::Name(name) => name,
1282        }
1283    }
1284
1285    /// Adorn the TaskIdentifier with a namespace
1286    pub fn with_namespace(self, namespace: &str) -> Self {
1287        match self {
1288            DependencyIdentifier::Definition(mut identifier) => {
1289                identifier.name = get_namespaced_task_name(namespace, &identifier.name);
1290                DependencyIdentifier::Definition(identifier)
1291            }
1292            DependencyIdentifier::Name(name) => {
1293                DependencyIdentifier::Name(get_namespaced_task_name(namespace, &name))
1294            }
1295        }
1296    }
1297}
1298
1299/// An identifier for a task
1300#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
1301pub struct TaskIdentifier {
1302    /// The task name to execute
1303    pub name: String,
1304    /// The path to the makefile the task resides in
1305    pub path: Option<String>,
1306}
1307
1308impl std::fmt::Display for TaskIdentifier {
1309    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1310        match &self.path {
1311            Some(path) => write!(formatter, "{}:{}", path, self.name),
1312            None => write!(formatter, "{}", self.name),
1313        }
1314    }
1315}
1316
1317impl TaskIdentifier {
1318    /// Create a new TaskIdentifier referencing a task in the current file
1319    pub fn from_name(name: &str) -> Self {
1320        Self {
1321            name: name.to_string(),
1322            path: None,
1323        }
1324    }
1325}
1326
1327impl Into<TaskIdentifier> for DependencyIdentifier {
1328    fn into(self) -> TaskIdentifier {
1329        match self {
1330            DependencyIdentifier::Definition(identifier) => identifier,
1331            DependencyIdentifier::Name(name) => TaskIdentifier { name, path: None },
1332        }
1333    }
1334}
1335
1336impl Task {
1337    /// Creates and returns a new instance.
1338    pub fn new() -> Task {
1339        Default::default()
1340    }
1341
1342    /// Apply modifications
1343    pub fn apply(self: &mut Task, modify_config: &ModifyConfig) {
1344        match modify_config.private {
1345            Some(value) => {
1346                if value {
1347                    self.private = Some(true);
1348                }
1349            }
1350            None => (),
1351        };
1352
1353        match modify_config.namespace {
1354            Some(ref namespace) => {
1355                if namespace.len() > 0 {
1356                    if self.extend.is_some() {
1357                        self.extend = Some(get_namespaced_task_name(
1358                            namespace,
1359                            &self.extend.clone().unwrap(),
1360                        ));
1361                    }
1362
1363                    if self.alias.is_some() {
1364                        self.alias = Some(get_namespaced_task_name(
1365                            namespace,
1366                            &self.alias.clone().unwrap(),
1367                        ));
1368                    }
1369
1370                    if self.linux_alias.is_some() {
1371                        self.linux_alias = Some(get_namespaced_task_name(
1372                            namespace,
1373                            &self.linux_alias.clone().unwrap(),
1374                        ));
1375                    }
1376
1377                    if self.windows_alias.is_some() {
1378                        self.windows_alias = Some(get_namespaced_task_name(
1379                            namespace,
1380                            &self.windows_alias.clone().unwrap(),
1381                        ));
1382                    }
1383
1384                    if self.mac_alias.is_some() {
1385                        self.mac_alias = Some(get_namespaced_task_name(
1386                            namespace,
1387                            &self.mac_alias.clone().unwrap(),
1388                        ));
1389                    }
1390
1391                    if self.run_task.is_some() {
1392                        let mut run_task = self.run_task.clone().unwrap();
1393
1394                        run_task = match run_task {
1395                            RunTaskInfo::Name(value) => {
1396                                RunTaskInfo::Name(get_namespaced_task_name(namespace, &value))
1397                            }
1398                            RunTaskInfo::Details(mut run_task_details) => {
1399                                match run_task_details.name {
1400                                    RunTaskName::Single(ref name) => {
1401                                        run_task_details.name = RunTaskName::Single(
1402                                            get_namespaced_task_name(namespace, name),
1403                                        )
1404                                    }
1405                                    RunTaskName::Multiple(ref names) => {
1406                                        let mut updated_names = vec![];
1407                                        for name in names {
1408                                            updated_names
1409                                                .push(get_namespaced_task_name(namespace, name));
1410                                        }
1411
1412                                        run_task_details.name =
1413                                            RunTaskName::Multiple(updated_names);
1414                                    }
1415                                };
1416
1417                                RunTaskInfo::Details(run_task_details)
1418                            }
1419                            RunTaskInfo::Routing(mut routing_info_vector) => {
1420                                for routing_info in &mut routing_info_vector {
1421                                    match routing_info.name {
1422                                        RunTaskName::Single(ref name) => {
1423                                            routing_info.name = RunTaskName::Single(
1424                                                get_namespaced_task_name(namespace, name),
1425                                            )
1426                                        }
1427                                        RunTaskName::Multiple(ref names) => {
1428                                            let mut updated_names = vec![];
1429                                            for name in names {
1430                                                updated_names.push(get_namespaced_task_name(
1431                                                    namespace, name,
1432                                                ));
1433                                            }
1434
1435                                            routing_info.name =
1436                                                RunTaskName::Multiple(updated_names);
1437                                        }
1438                                    };
1439                                }
1440
1441                                RunTaskInfo::Routing(routing_info_vector)
1442                            }
1443                        };
1444
1445                        self.run_task = Some(run_task);
1446                    }
1447
1448                    if let Some(dependencies) = &self.dependencies {
1449                        self.dependencies = Some(
1450                            dependencies
1451                                .iter()
1452                                .map(|identifier| identifier.to_owned().with_namespace(namespace))
1453                                .collect(),
1454                        );
1455                    }
1456                }
1457            }
1458            None => (),
1459        };
1460    }
1461
1462    /// Copies values from the task into self.
1463    ///
1464    /// # Arguments
1465    ///
1466    /// * `task` - The task to copy from
1467    pub fn extend(self: &mut Task, task: &Task) {
1468        let override_values = match task.clear {
1469            Some(value) => value,
1470            None => false,
1471        };
1472
1473        if task.clear.is_some() {
1474            self.clear = task.clear.clone();
1475        }
1476
1477        if task.description.is_some() {
1478            self.description = task.description.clone();
1479        } else if override_values {
1480            self.description = None;
1481        }
1482
1483        if task.category.is_some() {
1484            self.category = task.category.clone();
1485        } else if override_values {
1486            self.category = None;
1487        }
1488
1489        if task.disabled.is_some() {
1490            self.disabled = task.disabled.clone();
1491        } else if override_values {
1492            self.disabled = None;
1493        }
1494
1495        if task.private.is_some() {
1496            self.private = task.private.clone();
1497        } else if override_values {
1498            self.private = None;
1499        }
1500
1501        if task.deprecated.is_some() {
1502            self.deprecated = task.deprecated.clone();
1503        } else if override_values {
1504            self.deprecated = None;
1505        }
1506
1507        if task.extend.is_some() {
1508            self.extend = task.extend.clone();
1509        } else if override_values {
1510            self.extend = None;
1511        }
1512
1513        if task.workspace.is_some() {
1514            self.workspace = task.workspace.clone();
1515        } else if override_values {
1516            self.workspace = None;
1517        }
1518
1519        if task.plugin.is_some() {
1520            self.plugin = task.plugin.clone();
1521        } else if override_values {
1522            self.plugin = None;
1523        }
1524
1525        if task.watch.is_some() {
1526            self.watch = task.watch.clone();
1527        } else if override_values {
1528            self.watch = None;
1529        }
1530
1531        if task.condition.is_some() {
1532            self.condition = task.condition.clone();
1533        } else if override_values {
1534            self.condition = None;
1535        }
1536
1537        if task.condition_script.is_some() {
1538            self.condition_script = task.condition_script.clone();
1539        } else if override_values {
1540            self.condition_script = None;
1541        }
1542
1543        if task.condition_script_runner_args.is_some() {
1544            self.condition_script_runner_args = task.condition_script_runner_args.clone();
1545        } else if override_values {
1546            self.condition_script_runner_args = None;
1547        }
1548
1549        if task.ignore_errors.is_some() {
1550            self.ignore_errors = task.ignore_errors.clone();
1551        } else if override_values {
1552            self.ignore_errors = None;
1553        }
1554
1555        if task.force.is_some() {
1556            self.force = task.force.clone();
1557        } else if override_values {
1558            self.force = None;
1559        }
1560
1561        if task.env_files.is_some() {
1562            self.env_files = task.env_files.clone();
1563        } else if override_values {
1564            self.env_files = None;
1565        }
1566
1567        if task.env.is_some() {
1568            self.env = task.env.clone();
1569        } else if override_values {
1570            self.env = None;
1571        }
1572
1573        if task.cwd.is_some() {
1574            self.cwd = task.cwd.clone();
1575        } else if override_values {
1576            self.cwd = None;
1577        }
1578
1579        if task.alias.is_some() {
1580            self.alias = task.alias.clone();
1581        } else if override_values {
1582            self.alias = None;
1583        }
1584
1585        if task.linux_alias.is_some() {
1586            self.linux_alias = task.linux_alias.clone();
1587        } else if override_values {
1588            self.linux_alias = None;
1589        }
1590
1591        if task.windows_alias.is_some() {
1592            self.windows_alias = task.windows_alias.clone();
1593        } else if override_values {
1594            self.windows_alias = None;
1595        }
1596
1597        if task.mac_alias.is_some() {
1598            self.mac_alias = task.mac_alias.clone();
1599        } else if override_values {
1600            self.mac_alias = None;
1601        }
1602
1603        if task.install_crate.is_some() {
1604            self.install_crate = task.install_crate.clone();
1605        } else if override_values {
1606            self.install_crate = None;
1607        }
1608
1609        if task.install_crate_args.is_some() {
1610            self.install_crate_args = task.install_crate_args.clone();
1611        } else if override_values {
1612            self.install_crate_args = None;
1613        }
1614
1615        if task.install_script.is_some() {
1616            self.install_script =
1617                extend_script_value(self.install_script.clone(), task.install_script.clone());
1618        } else if override_values {
1619            self.install_script = None;
1620        }
1621
1622        if task.command.is_some() {
1623            self.command = task.command.clone();
1624        } else if override_values {
1625            self.command = None;
1626        }
1627
1628        if task.args.is_some() {
1629            self.args = task.args.clone();
1630        } else if override_values {
1631            self.args = None;
1632        }
1633
1634        if task.script.is_some() {
1635            self.script = extend_script_value(self.script.clone(), task.script.clone());
1636        } else if override_values {
1637            self.script = None;
1638        }
1639
1640        if task.script_runner.is_some() {
1641            self.script_runner = task.script_runner.clone();
1642        } else if override_values {
1643            self.script_runner = None;
1644        }
1645
1646        if task.script_runner_args.is_some() {
1647            self.script_runner_args = task.script_runner_args.clone();
1648        } else if override_values {
1649            self.script_runner_args = None;
1650        }
1651
1652        if task.script_extension.is_some() {
1653            self.script_extension = task.script_extension.clone();
1654        } else if override_values {
1655            self.script_extension = None;
1656        }
1657
1658        if task.run_task.is_some() {
1659            self.run_task = task.run_task.clone();
1660        } else if override_values {
1661            self.run_task = None;
1662        }
1663
1664        if task.dependencies.is_some() {
1665            self.dependencies = task.dependencies.clone();
1666        } else if override_values {
1667            self.dependencies = None;
1668        }
1669
1670        if task.toolchain.is_some() {
1671            self.toolchain = task.toolchain.clone();
1672        } else if override_values {
1673            self.toolchain = None;
1674        }
1675
1676        if task.linux.is_some() {
1677            self.linux = task.linux.clone();
1678        } else if override_values {
1679            self.linux = None;
1680        }
1681
1682        if task.windows.is_some() {
1683            self.windows = task.windows.clone();
1684        } else if override_values {
1685            self.windows = None;
1686        }
1687
1688        if task.mac.is_some() {
1689            self.mac = task.mac.clone();
1690        } else if override_values {
1691            self.mac = None;
1692        }
1693    }
1694
1695    /// Returns true if the task ignore_errors attribute is defined and true
1696    pub fn should_ignore_errors(self: &Task) -> bool {
1697        match self.ignore_errors {
1698            Some(value) => value,
1699            None => match self.force {
1700                Some(value) => {
1701                    legacy::show_deprecated_attriute_warning("force", "ignore_errors");
1702
1703                    value
1704                }
1705                None => false,
1706            },
1707        }
1708    }
1709
1710    /// Returns the override task definition based on the current platform.
1711    fn get_override(self: &Task) -> Option<PlatformOverrideTask> {
1712        let platform_name = get_platform_name();
1713        if platform_name == "windows" {
1714            match self.windows {
1715                Some(ref value) => Some(value.clone()),
1716                _ => None,
1717            }
1718        } else if platform_name == "mac" {
1719            match self.mac {
1720                Some(ref value) => Some(value.clone()),
1721                _ => None,
1722            }
1723        } else {
1724            match self.linux {
1725                Some(ref value) => Some(value.clone()),
1726                _ => None,
1727            }
1728        }
1729    }
1730
1731    /// Returns a new task based on the override information and current platform.
1732    pub fn get_normalized_task(self: &mut Task) -> Task {
1733        match self.get_override() {
1734            Some(ref mut override_task) => {
1735                override_task.extend(self);
1736
1737                Task {
1738                    clear: self.clear.clone(),
1739                    description: self.description.clone(),
1740                    category: self.category.clone(),
1741                    disabled: override_task.disabled.clone(),
1742                    private: override_task.private.clone(),
1743                    deprecated: override_task.deprecated.clone(),
1744                    extend: override_task.extend.clone(),
1745                    workspace: self.workspace.clone(),
1746                    plugin: override_task.plugin.clone(),
1747                    watch: override_task.watch.clone(),
1748                    condition: override_task.condition.clone(),
1749                    condition_script: override_task.condition_script.clone(),
1750                    condition_script_runner_args: override_task
1751                        .condition_script_runner_args
1752                        .clone(),
1753                    ignore_errors: override_task.ignore_errors.clone(),
1754                    force: override_task.force.clone(),
1755                    env_files: override_task.env_files.clone(),
1756                    env: override_task.env.clone(),
1757                    cwd: override_task.cwd.clone(),
1758                    alias: None,
1759                    linux_alias: None,
1760                    windows_alias: None,
1761                    mac_alias: None,
1762                    install_crate: override_task.install_crate.clone(),
1763                    install_crate_args: override_task.install_crate_args.clone(),
1764                    install_script: override_task.install_script.clone(),
1765                    command: override_task.command.clone(),
1766                    args: override_task.args.clone(),
1767                    script: override_task.script.clone(),
1768                    script_runner: override_task.script_runner.clone(),
1769                    script_runner_args: override_task.script_runner_args.clone(),
1770                    script_extension: override_task.script_extension.clone(),
1771                    run_task: override_task.run_task.clone(),
1772                    dependencies: override_task.dependencies.clone(),
1773                    toolchain: override_task.toolchain.clone(),
1774                    linux: None,
1775                    windows: None,
1776                    mac: None,
1777                }
1778            }
1779            None => self.clone(),
1780        }
1781    }
1782
1783    /// Returns the alias value based on the current platform and task definition.
1784    pub fn get_alias(self: &Task) -> Option<String> {
1785        let alias = if cfg!(windows) {
1786            match self.windows_alias {
1787                Some(ref value) => Some(value),
1788                _ => None,
1789            }
1790        } else if cfg!(target_os = "macos") || cfg!(target_os = "ios") {
1791            match self.mac_alias {
1792                Some(ref value) => Some(value),
1793                _ => None,
1794            }
1795        } else {
1796            match self.linux_alias {
1797                Some(ref value) => Some(value),
1798                _ => None,
1799            }
1800        };
1801
1802        match alias {
1803            Some(os_alias) => Some(os_alias.clone()),
1804            _ => match self.alias {
1805                Some(ref alias) => Some(alias.clone()),
1806                _ => None,
1807            },
1808        }
1809    }
1810
1811    /// Returns the amount of actions defined on the task
1812    pub fn get_actions_count(self: &Task) -> u8 {
1813        let mut actions_count = 0;
1814
1815        if self.run_task.is_some() {
1816            actions_count = actions_count + 1;
1817        }
1818        if self.command.is_some() {
1819            actions_count = actions_count + 1;
1820        }
1821        if self.script.is_some() {
1822            actions_count = actions_count + 1;
1823        }
1824
1825        actions_count
1826    }
1827
1828    /// Returns true if the task has any actions on its own
1829    /// or if it modifies the environment in any way.
1830    pub fn is_actionable(self: &Task) -> bool {
1831        if self.disabled.unwrap_or(false) {
1832            return false;
1833        }
1834
1835        let actions_count = self.get_actions_count();
1836        if actions_count > 0 {
1837            return true;
1838        }
1839
1840        if self.install_crate.is_some() || self.install_script.is_some() {
1841            return true;
1842        }
1843
1844        let mut actionable = match self.env {
1845            Some(ref value) => value.len() > 0,
1846            None => false,
1847        };
1848        if actionable {
1849            return true;
1850        }
1851
1852        actionable = match self.env_files {
1853            Some(ref value) => value.len() > 0,
1854            None => false,
1855        };
1856        if actionable {
1857            return true;
1858        }
1859
1860        actionable = match self.dependencies {
1861            Some(ref value) => value.len() > 0,
1862            None => false,
1863        };
1864        if actionable {
1865            return true;
1866        }
1867
1868        actionable = match self.watch {
1869            Some(ref options) => match options {
1870                TaskWatchOptions::Boolean(value) => *value,
1871                _ => true,
1872            },
1873            None => false,
1874        };
1875
1876        actionable
1877    }
1878
1879    /// Returns true if the task is valid
1880    pub fn is_valid(self: &Task) -> bool {
1881        let actions_count = self.get_actions_count();
1882
1883        if actions_count <= 1 {
1884            true
1885        } else {
1886            false
1887        }
1888    }
1889}
1890
1891#[derive(Serialize, Deserialize, Debug, Clone)]
1892/// Holds a single task configuration for a specific platform as an override of another task
1893pub struct PlatformOverrideTask {
1894    /// if true, it should ignore all data in base task
1895    pub clear: Option<bool>,
1896    /// if true, the command/script of this task will not be invoked, dependencies however will be
1897    pub disabled: Option<bool>,
1898    /// if true, the task is hidden from the list of available tasks and also cannot be invoked directly from cli
1899    pub private: Option<bool>,
1900    /// if not false, this task is defined as deprecated
1901    pub deprecated: Option<DeprecationInfo>,
1902    /// Extend any task based on the defined name
1903    pub extend: Option<String>,
1904    /// Optional plugin used to execute the task
1905    pub plugin: Option<String>,
1906    /// set to true to watch for file changes and invoke the task operation
1907    pub watch: Option<TaskWatchOptions>,
1908    /// if provided all condition values must be met in order for the task to be invoked (will not stop dependencies)
1909    pub condition: Option<TaskCondition>,
1910    /// if script exit code is not 0, the command/script of this task will not be invoked, dependencies however will be
1911    pub condition_script: Option<ConditionScriptValue>,
1912    /// The script runner arguments before the script file path
1913    pub condition_script_runner_args: Option<Vec<String>>,
1914    /// if true, any error while executing the task will be printed but will not break the build
1915    pub ignore_errors: Option<bool>,
1916    /// DEPRECATED, replaced with ignore_errors
1917    pub force: Option<bool>,
1918    /// The env files to setup before running the task commands
1919    pub env_files: Option<Vec<EnvFile>>,
1920    /// The env vars to setup before running the task commands
1921    pub env: Option<IndexMap<String, EnvValue>>,
1922    /// The working directory for the task to execute its command/script
1923    pub cwd: Option<String>,
1924    /// if defined, the provided crate will be installed (if needed) before running the task
1925    pub install_crate: Option<InstallCrate>,
1926    /// additional cargo install arguments
1927    pub install_crate_args: Option<Vec<String>>,
1928    /// if defined, the provided script will be executed before running the task
1929    pub install_script: Option<ScriptValue>,
1930    /// The command to execute
1931    pub command: Option<String>,
1932    /// The command args
1933    pub args: Option<Vec<String>>,
1934    /// If command is not defined, and script is defined, the provided script will be executed
1935    pub script: Option<ScriptValue>,
1936    /// The script runner (defaults to cmd in windows and sh for other platforms)
1937    pub script_runner: Option<String>,
1938    /// The script runner arguments before the script file path
1939    pub script_runner_args: Option<Vec<String>>,
1940    /// The script file extension
1941    pub script_extension: Option<String>,
1942    /// The task name to execute
1943    pub run_task: Option<RunTaskInfo>,
1944    /// A list of tasks to execute before this task
1945    pub dependencies: Option<Vec<DependencyIdentifier>>,
1946    /// The rust toolchain used to invoke the command or install the needed crates/components
1947    pub toolchain: Option<ToolchainSpecifier>,
1948}
1949
1950impl PlatformOverrideTask {
1951    /// Copies values from the task into self.
1952    ///
1953    /// # Arguments
1954    ///
1955    /// * `task` - The task to copy from
1956    pub fn extend(self: &mut PlatformOverrideTask, task: &mut Task) {
1957        let copy_values = match self.clear {
1958            Some(value) => !value,
1959            None => true,
1960        };
1961
1962        if copy_values {
1963            if self.disabled.is_none() && task.disabled.is_some() {
1964                self.disabled = task.disabled.clone();
1965            }
1966
1967            if self.private.is_none() && task.private.is_some() {
1968                self.private = task.private.clone();
1969            }
1970
1971            if self.deprecated.is_none() && task.deprecated.is_some() {
1972                self.deprecated = task.deprecated.clone();
1973            }
1974
1975            if self.extend.is_none() && task.extend.is_some() {
1976                self.extend = task.extend.clone();
1977            }
1978
1979            if self.plugin.is_none() && task.plugin.is_some() {
1980                self.plugin = task.plugin.clone();
1981            }
1982
1983            if self.watch.is_none() && task.watch.is_some() {
1984                self.watch = task.watch.clone();
1985            }
1986
1987            if self.condition.is_none() && task.condition.is_some() {
1988                self.condition = task.condition.clone();
1989            }
1990
1991            if self.condition_script.is_none() && task.condition_script.is_some() {
1992                self.condition_script = task.condition_script.clone();
1993            }
1994
1995            if self.condition_script_runner_args.is_none()
1996                && task.condition_script_runner_args.is_some()
1997            {
1998                self.condition_script_runner_args = task.condition_script_runner_args.clone();
1999            }
2000
2001            if self.ignore_errors.is_none() && task.ignore_errors.is_some() {
2002                self.ignore_errors = task.ignore_errors.clone();
2003            }
2004
2005            if self.force.is_none() && task.force.is_some() {
2006                self.force = task.force.clone();
2007            }
2008
2009            if self.env_files.is_none() && task.env_files.is_some() {
2010                self.env_files = task.env_files.clone();
2011            }
2012
2013            if self.env.is_none() && task.env.is_some() {
2014                self.env = task.env.clone();
2015            }
2016
2017            if self.cwd.is_none() && task.cwd.is_some() {
2018                self.cwd = task.cwd.clone();
2019            }
2020
2021            if self.install_crate.is_none() && task.install_crate.is_some() {
2022                self.install_crate = task.install_crate.clone();
2023            }
2024
2025            if self.install_crate_args.is_none() && task.install_crate_args.is_some() {
2026                self.install_crate_args = task.install_crate_args.clone();
2027            }
2028
2029            if self.install_script.is_none() && task.install_script.is_some() {
2030                self.install_script =
2031                    extend_script_value(self.install_script.clone(), task.install_script.clone());
2032            }
2033
2034            if self.command.is_none() && task.command.is_some() {
2035                self.command = task.command.clone();
2036            }
2037
2038            if self.args.is_none() && task.args.is_some() {
2039                self.args = task.args.clone();
2040            }
2041
2042            if self.script.is_none() && task.script.is_some() {
2043                self.script = extend_script_value(None, task.script.clone());
2044            }
2045
2046            if self.script_runner.is_none() && task.script_runner.is_some() {
2047                self.script_runner = task.script_runner.clone();
2048            }
2049
2050            if self.script_runner_args.is_none() && task.script_runner_args.is_some() {
2051                self.script_runner_args = task.script_runner_args.clone();
2052            }
2053
2054            if self.script_extension.is_none() && task.script_extension.is_some() {
2055                self.script_extension = task.script_extension.clone();
2056            }
2057
2058            if self.run_task.is_none() && task.run_task.is_some() {
2059                self.run_task = task.run_task.clone();
2060            }
2061
2062            if self.dependencies.is_none() && task.dependencies.is_some() {
2063                self.dependencies = task.dependencies.clone();
2064            }
2065
2066            if self.toolchain.is_none() && task.toolchain.is_some() {
2067                self.toolchain = task.toolchain.clone();
2068            }
2069        }
2070    }
2071}
2072
2073#[derive(Serialize, Deserialize, Debug, Clone)]
2074/// Extend with more fine tuning options
2075pub struct ExtendOptions {
2076    /// Path to another makefile
2077    pub path: String,
2078    /// Enable optional extend (default to false)
2079    pub optional: Option<bool>,
2080    /// Relative to option, sub as current makefile, git root, crate root, workspace root, etc...
2081    /// Possible values: (makefile, git, crate, workspace)
2082    pub relative: Option<String>,
2083}
2084
2085#[derive(Serialize, Deserialize, Debug, Clone)]
2086#[serde(untagged)]
2087/// Holds makefile extend value
2088pub enum Extend {
2089    /// Path to another makefile
2090    Path(String),
2091    /// Extend options for more fine tune control
2092    Options(ExtendOptions),
2093    /// Multiple extends list
2094    List(Vec<ExtendOptions>),
2095}
2096
2097#[derive(Serialize, Deserialize, Debug, Clone)]
2098/// Holds properties to modify the core tasks
2099pub struct ModifyConfig {
2100    /// If true, all core tasks will be set to private (default false)
2101    pub private: Option<bool>,
2102    /// If set to some value, all core tasks are modified to: namespace::name for example default::build
2103    pub namespace: Option<String>,
2104}
2105
2106impl ModifyConfig {
2107    /// Returns true if config modifications is needed based on the current state
2108    pub fn is_modifications_defined(self: &ModifyConfig) -> bool {
2109        if self.private.unwrap_or(false) {
2110            true
2111        } else {
2112            match self.namespace {
2113                Some(ref value) => value.len() > 0,
2114                None => false,
2115            }
2116        }
2117    }
2118
2119    /// Returns the namespace prefix for task names
2120    pub fn get_namespace_prefix(self: &ModifyConfig) -> String {
2121        match self.namespace {
2122            Some(ref value) => get_namespaced_task_name(value, ""),
2123            None => "".to_string(),
2124        }
2125    }
2126}
2127
2128#[derive(Serialize, Deserialize, Debug, Clone, Copy, Hash, PartialEq, Eq)]
2129#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
2130/// Unstable cargo-make feature
2131pub enum UnstableFeature {
2132    /// Gracefully shutdown and then kill the running command on Ctrl+C signal
2133    CtrlCHandling,
2134}
2135
2136impl UnstableFeature {
2137    /// Creates the env. variable name associated to the feature
2138    pub fn to_env_name(&self) -> String {
2139        let mut feature = serde_json::to_string(&self).unwrap();
2140        feature = feature.replace("\"", "");
2141        format!("CARGO_MAKE_UNSTABLE_FEATURE_{feature}", feature = feature)
2142    }
2143
2144    /// Is the corresponding env. variable set?
2145    pub fn is_env_set(&self) -> bool {
2146        envmnt::is(self.to_env_name())
2147    }
2148}
2149
2150#[derive(Serialize, Deserialize, Debug, Clone, Default)]
2151/// Holds the configuration found in the makefile toml config section.
2152pub struct ConfigSection {
2153    /// If true, the default core tasks will not be loaded
2154    pub skip_core_tasks: Option<bool>,
2155    /// Modify core tasks config
2156    pub modify_core_tasks: Option<ModifyConfig>,
2157    /// Init task name which will be invoked at the start of every run
2158    pub init_task: Option<String>,
2159    /// End task name which will be invoked at the end of every run
2160    pub end_task: Option<String>,
2161    /// The name of the task to run in case of any error during the invocation of the flow
2162    pub on_error_task: Option<String>,
2163    /// The name of the task which runs legacy migration flows
2164    pub legacy_migration_task: Option<String>,
2165    /// Additional profile names to load
2166    pub additional_profiles: Option<Vec<String>>,
2167    /// Minimum cargo-make/makers version
2168    pub min_version: Option<String>,
2169    /// The task.workspace default value
2170    pub default_to_workspace: Option<bool>,
2171    /// do not load git env info (save on perf)
2172    pub skip_git_env_info: Option<bool>,
2173    /// do not load rust env info (save on perf)
2174    pub skip_rust_env_info: Option<bool>,
2175    /// do not load current crate env info (save on perf)
2176    pub skip_crate_env_info: Option<bool>,
2177    /// True to reduce console output for non CI execution
2178    pub reduce_output: Option<bool>,
2179    /// True to print time summary at the end of the flow
2180    pub time_summary: Option<bool>,
2181    /// Automatically load cargo aliases as cargo-make tasks
2182    pub load_cargo_aliases: Option<bool>,
2183    /// If true (default false) disable all automatic/defined installation instructions
2184    pub disable_install: Option<bool>,
2185    /// The project information member (used by workspaces)
2186    pub main_project_member: Option<String>,
2187    /// Invoked while loading the descriptor file but before loading any extended descriptor
2188    pub load_script: Option<ScriptValue>,
2189    /// acts like load_script if runtime OS is Linux (takes precedence over load_script)
2190    pub linux_load_script: Option<ScriptValue>,
2191    /// acts like load_script if runtime OS is Windows (takes precedence over load_script)
2192    pub windows_load_script: Option<ScriptValue>,
2193    /// acts like load_script if runtime OS is Mac (takes precedence over load_script)
2194    pub mac_load_script: Option<ScriptValue>,
2195    /// Enables unstable cargo-make features
2196    pub unstable_features: Option<IndexSet<UnstableFeature>>,
2197}
2198
2199impl ConfigSection {
2200    /// Creates and returns a new instance.
2201    pub fn new() -> ConfigSection {
2202        Default::default()
2203    }
2204
2205    /// Apply modifications
2206    pub fn apply(self: &mut ConfigSection, modify_config: &ModifyConfig) {
2207        match modify_config.namespace {
2208            Some(ref namespace) => {
2209                if self.init_task.is_some() {
2210                    self.init_task = Some(get_namespaced_task_name(
2211                        namespace,
2212                        &self.init_task.clone().unwrap(),
2213                    ));
2214                }
2215
2216                if self.end_task.is_some() {
2217                    self.end_task = Some(get_namespaced_task_name(
2218                        namespace,
2219                        &self.end_task.clone().unwrap(),
2220                    ));
2221                }
2222
2223                if self.on_error_task.is_some() {
2224                    self.on_error_task = Some(get_namespaced_task_name(
2225                        namespace,
2226                        &self.on_error_task.clone().unwrap(),
2227                    ));
2228                }
2229
2230                if self.legacy_migration_task.is_some() {
2231                    self.legacy_migration_task = Some(get_namespaced_task_name(
2232                        namespace,
2233                        &self.legacy_migration_task.clone().unwrap(),
2234                    ));
2235                }
2236            }
2237            None => (),
2238        }
2239    }
2240
2241    /// Copies values from the config section into self.
2242    ///
2243    /// # Arguments
2244    ///
2245    /// * `task` - The task to copy from
2246    pub fn extend(self: &mut ConfigSection, extended: &mut ConfigSection) {
2247        if extended.skip_core_tasks.is_some() {
2248            self.skip_core_tasks = extended.skip_core_tasks.clone();
2249        }
2250
2251        if extended.modify_core_tasks.is_some() {
2252            self.modify_core_tasks = extended.modify_core_tasks.clone();
2253        }
2254
2255        if extended.init_task.is_some() {
2256            self.init_task = extended.init_task.clone();
2257        }
2258
2259        if extended.end_task.is_some() {
2260            self.end_task = extended.end_task.clone();
2261        }
2262
2263        if extended.on_error_task.is_some() {
2264            self.on_error_task = extended.on_error_task.clone();
2265        }
2266
2267        if extended.legacy_migration_task.is_some() {
2268            self.legacy_migration_task = extended.legacy_migration_task.clone();
2269        }
2270
2271        if extended.additional_profiles.is_some() {
2272            self.additional_profiles = extended.additional_profiles.clone();
2273        }
2274
2275        if extended.min_version.is_some() {
2276            self.min_version = extended.min_version.clone();
2277        }
2278
2279        if extended.default_to_workspace.is_some() {
2280            self.default_to_workspace = extended.default_to_workspace.clone();
2281        }
2282
2283        if extended.skip_git_env_info.is_some() {
2284            self.skip_git_env_info = extended.skip_git_env_info.clone();
2285        }
2286
2287        if extended.skip_rust_env_info.is_some() {
2288            self.skip_rust_env_info = extended.skip_rust_env_info.clone();
2289        }
2290
2291        if extended.skip_crate_env_info.is_some() {
2292            self.skip_crate_env_info = extended.skip_crate_env_info.clone();
2293        }
2294
2295        if extended.reduce_output.is_some() {
2296            self.reduce_output = extended.reduce_output.clone();
2297        }
2298
2299        if extended.time_summary.is_some() {
2300            self.time_summary = extended.time_summary.clone();
2301        }
2302
2303        if extended.load_cargo_aliases.is_some() {
2304            self.load_cargo_aliases = extended.load_cargo_aliases.clone();
2305        }
2306
2307        if extended.disable_install.is_some() {
2308            self.disable_install = extended.disable_install.clone();
2309        }
2310
2311        if extended.main_project_member.is_some() {
2312            self.main_project_member = extended.main_project_member.clone();
2313        }
2314
2315        if extended.load_script.is_some() {
2316            self.load_script =
2317                extend_script_value(self.load_script.clone(), extended.load_script.clone());
2318        }
2319
2320        if extended.linux_load_script.is_some() {
2321            self.linux_load_script = extend_script_value(
2322                self.linux_load_script.clone(),
2323                extended.linux_load_script.clone(),
2324            );
2325        }
2326
2327        if extended.windows_load_script.is_some() {
2328            self.windows_load_script = extend_script_value(
2329                self.windows_load_script.clone(),
2330                extended.windows_load_script.clone(),
2331            );
2332        }
2333
2334        if extended.mac_load_script.is_some() {
2335            self.mac_load_script = extend_script_value(
2336                self.mac_load_script.clone(),
2337                extended.mac_load_script.clone(),
2338            );
2339        }
2340
2341        if let Some(extended_unstable_features) = extended.unstable_features.clone() {
2342            if let Some(unstable_features) = &mut self.unstable_features {
2343                unstable_features.extend(extended_unstable_features);
2344            } else {
2345                self.unstable_features = Some(extended_unstable_features);
2346            }
2347        }
2348    }
2349
2350    /// Returns the load script based on the current platform
2351    pub fn get_load_script(self: &ConfigSection) -> Option<ScriptValue> {
2352        let platform_name = get_platform_name();
2353
2354        if platform_name == "windows" {
2355            if self.windows_load_script.is_some() {
2356                self.windows_load_script.clone()
2357            } else {
2358                self.load_script.clone()
2359            }
2360        } else if platform_name == "mac" {
2361            if self.mac_load_script.is_some() {
2362                self.mac_load_script.clone()
2363            } else {
2364                self.load_script.clone()
2365            }
2366        } else {
2367            if self.linux_load_script.is_some() {
2368                self.linux_load_script.clone()
2369            } else {
2370                self.load_script.clone()
2371            }
2372        }
2373    }
2374}
2375
2376#[derive(Serialize, Deserialize, Debug, Clone, Default)]
2377/// Holds the entire configuration such as task definitions and env vars
2378pub struct Config {
2379    /// Runtime config
2380    pub config: ConfigSection,
2381    /// The env files to setup before running the flow
2382    pub env_files: Vec<EnvFile>,
2383    /// The env vars to setup before running the flow
2384    pub env: IndexMap<String, EnvValue>,
2385    /// The env scripts to execute before running the flow
2386    pub env_scripts: Vec<String>,
2387    /// All task definitions
2388    pub tasks: IndexMap<String, Task>,
2389    /// All plugin definitions
2390    pub plugins: Option<Plugins>,
2391}
2392
2393impl Config {
2394    /// Apply modifications
2395    pub fn apply(self: &mut Config, modify_config: &ModifyConfig) {
2396        self.config.apply(&modify_config);
2397
2398        let namespace = match modify_config.namespace {
2399            Some(ref namespace) => namespace,
2400            None => "",
2401        };
2402
2403        let mut modified_tasks = IndexMap::<String, Task>::new();
2404
2405        for (key, value) in self.tasks.iter() {
2406            let namespaced_task = get_namespaced_task_name(namespace, &key);
2407            let mut task = value.clone();
2408
2409            task.apply(&modify_config);
2410
2411            modified_tasks.insert(namespaced_task, task);
2412        }
2413
2414        self.tasks = modified_tasks;
2415    }
2416}
2417
2418#[derive(Serialize, Deserialize, Debug, Clone, Default)]
2419/// Holds the entire externally read configuration such as task definitions and env vars where all values are optional
2420pub struct ExternalConfig {
2421    /// Path to another toml file to extend
2422    pub extend: Option<Extend>,
2423    /// Runtime config
2424    pub config: Option<ConfigSection>,
2425    /// The env files to setup before running the flow
2426    pub env_files: Option<Vec<EnvFile>>,
2427    /// The env vars to setup before running the flow
2428    pub env: Option<IndexMap<String, EnvValue>>,
2429    /// The env scripts to execute before running the flow
2430    pub env_scripts: Option<Vec<String>>,
2431    /// All task definitions
2432    pub tasks: Option<IndexMap<String, Task>>,
2433    /// All plugin definitions
2434    pub plugins: Option<Plugins>,
2435}
2436
2437impl ExternalConfig {
2438    /// Creates and returns a new instance.
2439    pub fn new() -> ExternalConfig {
2440        Default::default()
2441    }
2442}
2443
2444#[derive(Serialize, Clone, Debug)]
2445/// Execution plan step to execute
2446pub struct Step {
2447    /// The task name
2448    pub name: String,
2449    /// The task config
2450    pub config: Task,
2451}
2452
2453#[derive(Debug)]
2454/// Execution plan which defines all steps to run and the order to run them
2455pub struct ExecutionPlan {
2456    /// A list of steps to execute
2457    pub steps: Vec<Step>,
2458}
2459
2460#[derive(Debug)]
2461/// Command info
2462pub struct CommandSpec {
2463    /// The command to execute
2464    pub command: String,
2465    /// The command args
2466    pub args: Option<Vec<String>>,
2467}