diff options
author | Lars Wirzenius <liw@liw.fi> | 2023-08-15 08:04:54 +0300 |
---|---|---|
committer | Lars Wirzenius <liw@liw.fi> | 2023-08-15 08:07:03 +0300 |
commit | c82f316d140eb33cbe722c4526db6f4e11c01b84 (patch) | |
tree | 1af678aeb0e6e428039d05458675b36105552924 | |
parent | f80bdf4e47e86e5ab798f557803220dd3a989197 (diff) | |
download | ambient-run-c82f316d140eb33cbe722c4526db6f4e11c01b84.tar.gz |
feat: show project build instruction files
Sponsored-by: author
-rw-r--r-- | ambient-run.md | 28 | ||||
-rw-r--r-- | src/bin/ambient-run.rs | 17 | ||||
-rw-r--r-- | src/error.rs | 3 | ||||
-rw-r--r-- | src/lib.rs | 1 | ||||
-rw-r--r-- | src/project.rs | 72 |
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), } @@ -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), +} |