1
//! The module which holds the higher and generic abstractions over a source file
2

            
3
use crate::domain::target::TargetIdentifier;
4
use crate::project_model::compiler::StdLibMode;
5
use color_eyre::Report;
6
use serde::{Deserialize, Serialize};
7
use std::borrow::Cow;
8
use std::fmt::{Debug, Display};
9
use std::path::PathBuf;
10
use std::process::ExitStatus;
11
use transient::{Any, Inv};
12

            
13
/// Represents any kind of translation unit and the generic operations
14
/// applicable to all the implementors
15
pub trait TranslationUnit<'a>: AsTranslationUnit<'a> + Any<Inv<'a>> + Display + Debug {
16
    /// Returns the full path of the [`TranslationUnit`] behind the invocation, including
17
    /// the file stem and the extension
18
    ///
19
    /// # Examples
20
    ///
21
    /// ```
22
    /// use std::borrow::Cow;
23
    /// use std::path::PathBuf;
24
    /// use zork::domain::translation_unit::TranslationUnit;
25
    /// use zork::project_model::sourceset::SourceFile;
26
    ///
27
    /// let source_file = SourceFile {
28
    ///     path: PathBuf::from("/usr/include"),
29
    ///     file_stem: Cow::from("std"),
30
    ///     extension: Cow::from("h"),
31
    /// };
32
    ///
33
    /// assert_eq!(source_file.path(), PathBuf::from("/usr/include/std.h"));
34
    ///
35
    /// let source_file_compat = SourceFile {
36
    ///     path: PathBuf::from("/usr/include"),
37
    ///     file_stem: Cow::from("std.compat"),
38
    ///     extension: Cow::from("h"),
39
    /// };
40
    ///
41
    /// assert_eq!(source_file_compat.path(), PathBuf::from("/usr/include/std.compat.h"));
42
    /// ```
43
15
    fn path(&self) -> PathBuf {
44
15
        self.parent().join(self.filename())
45
15
    }
46

            
47
    /// Returns only the path to the directory where the translation unit lives on the fs
48
    fn parent(&self) -> &PathBuf;
49

            
50
    /// Outputs the declared file stem (filename without extension) for this translation unit
51
    fn file_stem(&self) -> &Cow<'_, str>;
52

            
53
    /// Outputs the declared extension for `self`
54
    fn extension(&self) -> &Cow<'_, str>;
55

            
56
    /// Outputs the file stem concatenated with the extension for a given tu
57
25
    fn filename(&self) -> String {
58
25
        format!("{}.{}", self.file_stem(), self.extension())
59
25
    }
60
}
61

            
62
/// Base trait for downcasting all the implementors of [`TranslationUnit`] when they are hidden
63
/// behind an opaque type
64
pub trait AsTranslationUnit<'a> {
65
    fn as_any(&self) -> &dyn Any<Inv<'a>>;
66
}
67

            
68
// Blanket implementation of [`AsTranslationUnit`] for all types implementing TranslationUnit
69
impl<'a, T: TranslationUnit<'a> + 'a> AsTranslationUnit<'a> for T {
70
11
    fn as_any(&self) -> &dyn Any<Inv<'a>> {
71
        self
72
11
    }
73
}
74

            
75
#[macro_export]
76
macro_rules! impl_translation_unit_for {
77
    ($t:ty) => {
78
        impl<'a> TranslationUnit<'a> for $t {
79
25
            fn parent(&self) -> &PathBuf {
80
                &self.path
81
25
            }
82

            
83
33
            fn file_stem(&self) -> &Cow<'_, str> {
84
33
                &self.file_stem
85
33
            }
86

            
87
25
            fn extension(&self) -> &Cow<'_, str> {
88
25
                &self.extension
89
25
            }
90
        }
91
    };
92
}
93

            
94
/// The different type of translation units that `Zork++` is able to deal with
95
#[derive(Debug)]
96
pub enum TranslationUnitKind<'a> {
97
    ModuleInterface,
98
    ModuleImplementation,
99
    SourceFile(&'a TargetIdentifier<'a>),
100
    ModularStdLib(StdLibMode),
101
    SystemHeader,
102
}
103

            
104
/// The different states of a translation unit in the whole lifecycle of
105
/// the build process and across different iterations of the same
106
50
#[derive(Debug, Default, Serialize, Deserialize, Clone, Copy, Eq, PartialEq)]
107
pub enum TranslationUnitStatus {
108
    /// A command that is executed correctly
109
    Success,
110
    /// A skipped command due to previous successful iterations
111
    Cached,
112
    /// A command which is return code indicates an unsuccessful execution
113
    Failed,
114
    /// Whenever a translation unit must be rebuilt
115
    #[default]
116
12
    PendingToBuild,
117
    /// The associated [`TranslationUnit`] has been deleted from the user's configuration and therefore,
118
    /// it should be removed from the cache as well as its generated byproducts
119
    ToDelete,
120
    /// The execution failed, returning a [`color_eyre::Result`] with the Err variant
121
    Error,
122
}
123

            
124
impl From<color_eyre::Result<ExitStatus, Report>> for TranslationUnitStatus {
125
    fn from(value: color_eyre::Result<ExitStatus, Report>) -> Self {
126
        helpers::handle_command_execution_result(&value)
127
    }
128
}
129

            
130
impl From<&color_eyre::Result<ExitStatus, Report>> for TranslationUnitStatus {
131
15
    fn from(value: &color_eyre::Result<ExitStatus, Report>) -> Self {
132
15
        helpers::handle_command_execution_result(value)
133
15
    }
134
}
135

            
136
mod helpers {
137
    use crate::domain::translation_unit::TranslationUnitStatus;
138
    use std::process::ExitStatus;
139

            
140
    /// Convenient way of handle a command execution result avoiding duplicate code
141
15
    pub(crate) fn handle_command_execution_result(
142
        value: &color_eyre::Result<ExitStatus>,
143
    ) -> TranslationUnitStatus {
144
15
        match value {
145
15
            Ok(r) => {
146
15
                if r.success() {
147
15
                    TranslationUnitStatus::Success
148
                } else {
149
                    TranslationUnitStatus::Failed
150
                }
151
            }
152
            Err(_) => TranslationUnitStatus::Error,
153
        }
154
15
    }
155
}