mirror of
https://github.com/mfontanini/presenterm.git
synced 2026-09-01 23:33:21 -06:00
feat: allow passing in env file to snippet
This commit is contained in:
parent
874cea8c70
commit
894ce397da
@ -66,6 +66,7 @@ impl SnippetExecutor {
|
||||
&self,
|
||||
language: &SnippetLanguage,
|
||||
spec: &SnippetExecutorSpec,
|
||||
custom_env: HashMap<String, String>,
|
||||
) -> Result<LanguageSnippetExecutor, UnsupportedExecution> {
|
||||
let language_config = self
|
||||
.executors
|
||||
@ -79,10 +80,14 @@ impl SnippetExecutor {
|
||||
})?
|
||||
}
|
||||
};
|
||||
|
||||
let mut env = config.environment.clone();
|
||||
env.extend(custom_env);
|
||||
Ok(LanguageSnippetExecutor {
|
||||
hidden_line_prefix: language_config.hidden_line_prefix.clone(),
|
||||
config,
|
||||
cwd: self.cwd.clone(),
|
||||
env,
|
||||
})
|
||||
}
|
||||
|
||||
@ -126,6 +131,7 @@ pub(crate) struct LanguageSnippetExecutor {
|
||||
hidden_line_prefix: Option<String>,
|
||||
config: SnippetExecutorConfig,
|
||||
cwd: PathBuf,
|
||||
env: HashMap<String, String>,
|
||||
}
|
||||
|
||||
impl LanguageSnippetExecutor {
|
||||
@ -141,7 +147,7 @@ impl LanguageSnippetExecutor {
|
||||
state.clone(),
|
||||
script_dir,
|
||||
self.config.commands.clone(),
|
||||
self.config.environment.clone(),
|
||||
self.env.clone(),
|
||||
self.cwd.clone(),
|
||||
output_type,
|
||||
);
|
||||
@ -177,7 +183,7 @@ impl LanguageSnippetExecutor {
|
||||
let mut command = portable_pty::CommandBuilder::new(command);
|
||||
command.args(args);
|
||||
command.cwd(&self.cwd);
|
||||
for (key, value) in &self.config.environment {
|
||||
for (key, value) in &self.env {
|
||||
command.env(key, value);
|
||||
}
|
||||
Ok(PtySnippetContext { command, _temp: script_dir })
|
||||
@ -190,7 +196,7 @@ impl LanguageSnippetExecutor {
|
||||
let (command, args) = commands.split_first().expect("no commands");
|
||||
let child = process::Command::new(command)
|
||||
.args(args)
|
||||
.envs(&self.config.environment)
|
||||
.envs(&self.env)
|
||||
.current_dir(&self.cwd)
|
||||
.stderr(Stdio::piped())
|
||||
.spawn()
|
||||
@ -419,7 +425,9 @@ echo 'bye'"
|
||||
..Default::default()
|
||||
},
|
||||
};
|
||||
let executor = SnippetExecutor::default().language_executor(&snippet.language, &Default::default()).unwrap();
|
||||
let executor = SnippetExecutor::default()
|
||||
.language_executor(&snippet.language, &Default::default(), Default::default())
|
||||
.unwrap();
|
||||
let handle = executor.execute_async(&snippet).expect("execution failed");
|
||||
let state = loop {
|
||||
let state = handle.state.lock().unwrap();
|
||||
@ -447,7 +455,9 @@ echo 'hello world'
|
||||
..Default::default()
|
||||
},
|
||||
};
|
||||
let executor = SnippetExecutor::default().language_executor(&snippet.language, &Default::default()).unwrap();
|
||||
let executor = SnippetExecutor::default()
|
||||
.language_executor(&snippet.language, &Default::default(), Default::default())
|
||||
.unwrap();
|
||||
let handle = executor.execute_async(&snippet).expect("execution failed");
|
||||
let state = loop {
|
||||
let state = handle.state.lock().unwrap();
|
||||
@ -476,7 +486,9 @@ echo 'hello world'
|
||||
..Default::default()
|
||||
},
|
||||
};
|
||||
let executor = SnippetExecutor::default().language_executor(&snippet.language, &Default::default()).unwrap();
|
||||
let executor = SnippetExecutor::default()
|
||||
.language_executor(&snippet.language, &Default::default(), Default::default())
|
||||
.unwrap();
|
||||
let handle = executor.execute_async(&snippet).expect("execution failed");
|
||||
let state = loop {
|
||||
let state = handle.state.lock().unwrap();
|
||||
|
||||
@ -289,6 +289,7 @@ impl SnippetParser {
|
||||
HighlightedLines(lines) => attributes.highlight_groups = lines,
|
||||
Width(width) => attributes.width = Some(width),
|
||||
ExpectedExecutionResult(result) => attributes.expected_execution_result = result,
|
||||
Env(path) => attributes.env_file = Some(path),
|
||||
};
|
||||
processed_attributes.push(discriminant);
|
||||
input = rest;
|
||||
@ -352,6 +353,7 @@ impl SnippetParser {
|
||||
}
|
||||
},
|
||||
"pty" => SnippetAttribute::ExecPty(SnippetExecutorSpec::default(), parameter.parse()?),
|
||||
"env" => SnippetAttribute::Env(parameter.to_string().into()),
|
||||
_ => return Err(SnippetBlockParseError::InvalidToken(Self::next_identifier(input).into())),
|
||||
}
|
||||
}
|
||||
@ -471,6 +473,7 @@ enum SnippetAttribute {
|
||||
AcquireTerminal(SnippetExecutorSpec),
|
||||
ExpectedExecutionResult(ExpectedSnippetExecutionResult),
|
||||
Id(String),
|
||||
Env(PathBuf),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
||||
@ -717,6 +720,9 @@ pub(crate) struct SnippetAttributes {
|
||||
|
||||
/// The identifier for a snippet.
|
||||
pub(crate) id: Option<String>,
|
||||
|
||||
/// The environment file to load.
|
||||
pub(crate) env_file: Option<PathBuf>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
||||
|
||||
@ -122,6 +122,9 @@ pub(crate) enum InvalidPresentation {
|
||||
|
||||
#[error("snippet id '{0}' already exists")]
|
||||
SnippetAlreadyExists(String),
|
||||
|
||||
#[error("invalid env file format: expected <key>=<value>")]
|
||||
InvalidEnvFile,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
||||
@ -25,7 +25,7 @@ use crate::{
|
||||
},
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
use std::{cell::RefCell, collections::HashMap, path::Path, rc::Rc};
|
||||
|
||||
impl PresentationBuilder<'_, '_> {
|
||||
pub(crate) fn push_code(&mut self, info: String, code: String, source_position: SourcePosition) -> BuildResult {
|
||||
@ -61,7 +61,11 @@ impl PresentationBuilder<'_, '_> {
|
||||
(SnippetExecutorSpec::Default, SnippetExecution::Exec(args)) => &args.spec,
|
||||
_ => spec,
|
||||
};
|
||||
let executor = self.snippet_executor.language_executor(&snippet.language, spec)?;
|
||||
let env = match &snippet.attributes.env_file {
|
||||
Some(path) => self.load_env(path, source_position)?,
|
||||
None => HashMap::new(),
|
||||
};
|
||||
let executor = self.snippet_executor.language_executor(&snippet.language, spec, env)?;
|
||||
self.push_validator(&snippet, &executor);
|
||||
}
|
||||
|
||||
@ -85,7 +89,11 @@ impl PresentationBuilder<'_, '_> {
|
||||
self.push_execution_disabled_operation(exec_type);
|
||||
}
|
||||
SnippetExecution::Exec(args) => {
|
||||
let executor = self.snippet_executor.language_executor(&snippet.language, &args.spec)?;
|
||||
let env = match &snippet.attributes.env_file {
|
||||
Some(path) => self.load_env(path, source_position)?,
|
||||
None => HashMap::new(),
|
||||
};
|
||||
let executor = self.snippet_executor.language_executor(&snippet.language, &args.spec, env)?;
|
||||
match args.repr {
|
||||
SnippetRepr::Image => {
|
||||
self.push_code_as_image(snippet, executor)?;
|
||||
@ -404,6 +412,31 @@ impl PresentationBuilder<'_, '_> {
|
||||
let operation = ValidateSnippetOperation::new(snippet.clone(), executor.clone());
|
||||
self.chunk_operations.push(RenderOperation::RenderAsync(Rc::new(operation)));
|
||||
}
|
||||
|
||||
fn load_env(&self, path: &Path, source_position: SourcePosition) -> Result<HashMap<String, String>, BuildError> {
|
||||
let base = self.resource_base_path();
|
||||
let contents = self.resources.external_text_file(path, &base).map_err(|e| {
|
||||
self.invalid_presentation(
|
||||
source_position,
|
||||
InvalidPresentation::IncludeMarkdown { path: path.to_path_buf(), error: e },
|
||||
)
|
||||
})?;
|
||||
let mut vars = HashMap::new();
|
||||
for line in contents.lines() {
|
||||
if line.starts_with('#') {
|
||||
continue;
|
||||
}
|
||||
let line = line.trim();
|
||||
if line.is_empty() {
|
||||
continue;
|
||||
}
|
||||
let Some((key, value)) = line.split_once('=') else {
|
||||
return Err(self.invalid_presentation(source_position, InvalidPresentation::InvalidEnvFile));
|
||||
};
|
||||
vars.insert(key.to_string(), value.to_string());
|
||||
}
|
||||
Ok(vars)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -889,4 +922,25 @@ echo "{qr}"
|
||||
let expected: Vec<_> = [empty.as_str()].into_iter().chain(qr.lines()).chain([empty.as_str()]).collect();
|
||||
assert_eq!(lines, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn env_file() {
|
||||
let temp = tempfile::NamedTempFile::new().expect("failed to create tempfile");
|
||||
let path = temp.path();
|
||||
fs::write(path, "FOO=42\nBAR=1337\n\nTAR=hi\n# a comment\n").unwrap();
|
||||
|
||||
let path = path.to_string_lossy();
|
||||
let input = format!(
|
||||
"
|
||||
```bash +exec_replace +env:{path}
|
||||
echo \"$FOO\"
|
||||
echo \"$BAR\"
|
||||
echo \"$TAR\"
|
||||
```
|
||||
"
|
||||
);
|
||||
let lines = Test::new(input).render().rows(5).columns(5).into_lines();
|
||||
let expected = &[" ", "42 ", "1337 ", "hi ", " "];
|
||||
assert_eq!(lines, expected);
|
||||
}
|
||||
}
|
||||
|
||||
@ -346,7 +346,9 @@ mod tests {
|
||||
..Default::default()
|
||||
},
|
||||
};
|
||||
let executor = SnippetExecutor::default().language_executor(&snippet.language, &Default::default()).unwrap();
|
||||
let executor = SnippetExecutor::default()
|
||||
.language_executor(&snippet.language, &Default::default(), Default::default())
|
||||
.unwrap();
|
||||
let policy = RenderAsyncStartPolicy::OnDemand;
|
||||
let handle = SnippetHandle::new(snippet, executor, policy);
|
||||
RunSnippetTrigger::new(handle)
|
||||
|
||||
@ -127,7 +127,9 @@ mod tests {
|
||||
language: SnippetLanguage::Rust,
|
||||
attributes: SnippetAttributes { expected_execution_result, ..Default::default() },
|
||||
};
|
||||
let executor = SnippetExecutor::default().language_executor(&snippet.language, &Default::default()).unwrap();
|
||||
let executor = SnippetExecutor::default()
|
||||
.language_executor(&snippet.language, &Default::default(), Default::default())
|
||||
.unwrap();
|
||||
let state = Arc::new(Mutex::new(State::default()));
|
||||
let mut pollable =
|
||||
OperationPollable { snippet: snippet.clone(), executor: executor.clone(), state: state.clone() };
|
||||
@ -154,7 +156,9 @@ mod tests {
|
||||
language: SnippetLanguage::Rust,
|
||||
attributes: SnippetAttributes { expected_execution_result, ..Default::default() },
|
||||
};
|
||||
let executor = SnippetExecutor::default().language_executor(&snippet.language, &Default::default()).unwrap();
|
||||
let executor = SnippetExecutor::default()
|
||||
.language_executor(&snippet.language, &Default::default(), Default::default())
|
||||
.unwrap();
|
||||
let state = Arc::new(Mutex::new(State::default()));
|
||||
let mut pollable =
|
||||
OperationPollable { snippet: snippet.clone(), executor: executor.clone(), state: state.clone() };
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user