summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Wirzenius <liw@liw.fi>2023-08-15 08:04:54 +0300
committerLars Wirzenius <liw@liw.fi>2023-08-15 08:07:03 +0300
commitc82f316d140eb33cbe722c4526db6f4e11c01b84 (patch)
tree1af678aeb0e6e428039d05458675b36105552924
parentf80bdf4e47e86e5ab798f557803220dd3a989197 (diff)
downloadambient-run-c82f316d140eb33cbe722c4526db6f4e11c01b84.tar.gz
feat: show project build instruction files
Sponsored-by: author
-rw-r--r--ambient-run.md28
-rw-r--r--src/bin/ambient-run.rs17
-rw-r--r--src/error.rs3
-rw-r--r--src/lib.rs1
-rw-r--r--src/project.rs72
5 files changed, 121 insertions, 0 deletions
diff --git a/ambient-run.md b/ambient-run.md
index 6ea76c4..57b47d9 100644
--- a/ambient-run.md
+++ b/ambient-run.md
@@ -134,6 +134,34 @@ max_cpus: 8
~~~
### Show per-project configuration
+
+_Requirement:_ One can query `ambient-run` for its full per-project
+configuration. This means the union of the built-in defaults
+and the configuration in the file.
+
+_Justification:_ As a user I want to see the actual configuration
+without having to deduce it myself.
+
+_Stakeholder:_ Lars
+
+~~~scenario
+given an installed ambient-run
+given file project.yaml
+when I run ambient-run project project.yaml
+then stdout, as YAML, matches file full-project.yaml
+~~~
+
+~~~{#project.yaml .file .yaml}
+shell: |
+ cargo test
+~~~
+
+~~~{#full-project.yaml .file .yaml}
+source: .
+shell: |
+ cargo test
+~~~
+
### Show per-build configuration
## Building with ambient-run
diff --git a/src/bin/ambient-run.rs b/src/bin/ambient-run.rs
index d800630..81dbe68 100644
--- a/src/bin/ambient-run.rs
+++ b/src/bin/ambient-run.rs
@@ -1,6 +1,7 @@
use ambient_run::{
config::{canonical, default_config_file, Config},
error::AmbientRunError,
+ project::Project,
};
use clap::Parser;
use std::{
@@ -23,6 +24,7 @@ fn fallible_main() -> Result<(), AmbientRunError> {
let args = Args::parse();
match &args.cmd {
Command::Config(x) => x.run(&args),
+ Command::Project(x) => x.run(&args),
}
}
@@ -39,6 +41,7 @@ struct Args {
#[derive(Debug, Parser)]
enum Command {
Config(ConfigCommand),
+ Project(ProjectCommand),
}
#[derive(Debug, Parser)]
@@ -80,3 +83,17 @@ impl ConfigCommand {
Ok(())
}
}
+
+#[derive(Debug, Parser)]
+struct ProjectCommand {
+ /// Name of YAML file describing how to build a project.
+ filename: PathBuf,
+}
+
+impl ProjectCommand {
+ fn run(&self, _global: &Args) -> Result<(), AmbientRunError> {
+ let project = Project::load(&self.filename)?;
+ println!("{}", project.as_yaml()?);
+ Ok(())
+ }
+}
diff --git a/src/error.rs b/src/error.rs
index 6f50944..35adc8f 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -5,4 +5,7 @@
pub enum AmbientRunError {
#[error(transparent)]
Config(#[from] crate::config::ConfigError),
+
+ #[error(transparent)]
+ Project(#[from] crate::project::ProjectError),
}
diff --git a/src/lib.rs b/src/lib.rs
index ecae1e8..a57c977 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -4,3 +4,4 @@
pub mod config;
pub mod error;
+pub mod project;
diff --git a/src/project.rs b/src/project.rs
new file mode 100644
index 0000000..a9eb5dc
--- /dev/null
+++ b/src/project.rs
@@ -0,0 +1,72 @@
+//! ambient-run project build information handling.
+
+use serde::{Deserialize, Serialize};
+use std::path::{Path, PathBuf};
+
+/// Per-project build instructions.
+#[derive(Debug, Deserialize, Serialize)]
+pub struct Project {
+ source: PathBuf,
+ shell: String,
+}
+
+impl Project {
+ /// Load build instructions from named file.
+ pub fn load(filename: &Path) -> Result<Self, ProjectError> {
+ let mut project = Self::default();
+ project.add_from(filename)?;
+ Ok(project)
+ }
+
+ /// Load named project file, update self with values.
+ pub fn add_from(&mut self, filename: &Path) -> Result<(), ProjectError> {
+ eprintln!("adding from {}", filename.display());
+ let bytes = std::fs::read(filename).map_err(|e| ProjectError::Open(filename.into(), e))?;
+ let text = String::from_utf8_lossy(&bytes);
+ eprintln!("text: {:?}", text);
+ let snippet: ProjectSnippet =
+ serde_yaml::from_str(&text).map_err(|e| ProjectError::Yaml(filename.into(), e))?;
+ eprintln!("snippet: {:#?}", snippet);
+ if let Some(x) = snippet.source {
+ self.source = x;
+ }
+ if let Some(x) = snippet.shell {
+ self.shell = x;
+ }
+ Ok(())
+ }
+
+ /// Serialize the configuration into a YAML string.
+ pub fn as_yaml(&self) -> Result<String, ProjectError> {
+ serde_yaml::to_string(self).map_err(ProjectError::AsYaml)
+ }
+}
+
+impl Default for Project {
+ fn default() -> Self {
+ Self {
+ source: PathBuf::from("."),
+ shell: "echo do nothing by default\n".into(),
+ }
+ }
+}
+#[derive(Debug, Deserialize)]
+#[serde(deny_unknown_fields)]
+struct ProjectSnippet {
+ source: Option<PathBuf>,
+ shell: Option<String>,
+}
+
+/// Possible errors from configuration file handling.
+#[allow(missing_docs)]
+#[derive(Debug, thiserror::Error)]
+pub enum ProjectError {
+ #[error("failed to open project build instruction file {0}")]
+ Open(PathBuf, #[source] std::io::Error),
+
+ #[error("failed to parse project file as YAML: {0}")]
+ Yaml(PathBuf, #[source] serde_yaml::Error),
+
+ #[error("failed to serialize project build instructions as YAML")]
+ AsYaml(#[source] serde_yaml::Error),
+}