polysite/builder/
context.rs

1use super::metadata::*;
2use crate::*;
3use std::fs;
4use std::path::PathBuf;
5use tracing_error::SpanTrace;
6
7/// [`Version`] represents the compilation file version. Once a source file has been built, any
8/// subsequent builds of the same source path with the same version will be skipped.
9#[derive(PartialEq, Eq, Hash, Clone, Debug)]
10pub struct Version(String);
11impl Version {
12    pub fn get(&self) -> &str {
13        &self.0
14    }
15}
16impl Default for Version {
17    fn default() -> Self {
18        Self("default".to_string())
19    }
20}
21impl<S: AsRef<str>> From<S> for Version {
22    fn from(value: S) -> Self {
23        Self(value.as_ref().to_owned())
24    }
25}
26
27#[derive(Clone)]
28/// [`Context`] holds [`Metadata`] and [`Config`].
29/// The context is passed to [`Compiler::next_step`], and the modified context is returned.
30pub struct Context {
31    meta: Metadata,
32    config: Config,
33}
34
35impl Context {
36    pub fn new(config: Config) -> Self {
37        Self {
38            meta: Metadata::new(),
39            config,
40        }
41    }
42
43    pub fn metadata(&self) -> &Metadata {
44        &self.meta
45    }
46    pub fn metadata_mut(&mut self) -> &mut Metadata {
47        &mut self.meta
48    }
49
50    /// Get currently compiling [`Version`]
51    pub async fn version(&self) -> Option<Version> {
52        self.meta
53            .get(VERSION_META)
54            .await
55            .map(|v| v.as_str().map(|v| v.into()))
56            .flatten()
57    }
58
59    /// Get currently compiling rule name
60    pub async fn rule(&self) -> Option<String> {
61        self.meta
62            .get(RULE_META)
63            .await
64            .map(|v| v.as_str().map(|v| v.to_owned()))
65            .flatten()
66    }
67
68    /// Get currently compiling source file path
69    pub async fn source(&self) -> Option<PathBuf> {
70        self.meta
71            .get(SOURCE_FILE_META)
72            .await
73            .map(|v| v.as_str().map(|v| PathBuf::from(v)))
74            .flatten()
75    }
76    /// Get currently compiling target file path
77    pub async fn target(&self) -> Option<PathBuf> {
78        self.meta
79            .get(TARGET_FILE_META)
80            .await
81            .map(|v| v.as_str().map(|v| PathBuf::from(v)))
82            .flatten()
83    }
84    /// Get currently compiling URL path
85    pub async fn path(&self) -> Option<PathBuf> {
86        self.meta
87            .get(PATH_META)
88            .await
89            .map(|v| v.as_str().map(|v| PathBuf::from(v)))
90            .flatten()
91    }
92    /// Get currently compiling body [`Value`], which can be [`Vec<u8>`] or [`String`].
93    pub async fn body(&self) -> Option<Value> {
94        self.meta.get(BODY_META).await
95    }
96    /// Get [`Config`]
97    pub fn config(&self) -> Config {
98        self.config.clone()
99    }
100
101    /// Get source file body as bytes
102    #[tracing::instrument(skip(self))]
103    pub async fn source_body(&self) -> Result<Vec<u8>, Error> {
104        let file = self.source().await.ok_or(Error::InvalidMetadata {
105            trace: SpanTrace::capture(),
106        })?;
107        fs::read(&file).map_err(|io_error| Error::FileIo {
108            trace: SpanTrace::capture(),
109            io_error,
110        })
111    }
112    /// Get source file string
113    #[tracing::instrument(skip(self))]
114    pub async fn source_string(&self) -> Result<String, Error> {
115        String::from_utf8(self.source_body().await?).map_err(|_| Error::InvalidMetadata {
116            trace: SpanTrace::capture(),
117        })
118    }
119    /// Create target file's parent directory
120    #[tracing::instrument(skip(self))]
121    pub async fn create_target_parent_dir(&self) -> Result<PathBuf, Error> {
122        if let Some(target) = self.target().await {
123            let dir = target.parent().unwrap();
124            fs::create_dir_all(dir).map_err(|io_error| Error::FileIo {
125                trace: SpanTrace::capture(),
126                io_error,
127            })?;
128            Ok(target)
129        } else {
130            Err(Error::InvalidMetadata {
131                trace: SpanTrace::capture(),
132            })
133        }
134    }
135    /// Open target file to write
136    #[tracing::instrument(skip(self))]
137    pub async fn open_target(&self) -> Result<fs::File, Error> {
138        let target = self.create_target_parent_dir().await?;
139        fs::File::create(&target).map_err(|io_error| Error::FileIo {
140            trace: SpanTrace::capture(),
141            io_error,
142        })
143    }
144}