Skip to content

Commit

Permalink
many changes
Browse files Browse the repository at this point in the history
  • Loading branch information
Jeremy Moeglich committed Oct 22, 2024
1 parent 9cf2eee commit 88b37a6
Show file tree
Hide file tree
Showing 7 changed files with 210 additions and 92 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "weaveconfig"
version = "0.2.0"
version = "0.3.0"
edition = "2021"
description = "A unified configuration tool for monorepos"
readme = "README.md"
Expand Down
208 changes: 156 additions & 52 deletions src/apply_resolved.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
use std::{
collections::HashMap,
collections::{HashMap, HashSet},
path::{Path, PathBuf},
};

use anyhow::Context;
use futures::{stream::FuturesUnordered, StreamExt};
use serde_json::{Map, Value};

use crate::{
get_environment_value::get_environment_value, map_path::map_path,
resolve_spaces::ResolvedSpace, template_file::template_file,
ts_binding::generate_binding::generate_binding, write_json_file::write_json_file,
get_environment_value::get_environment_value,
map_path::map_path,
resolve_spaces::ResolvedSpace,
space_graph::{CopyTree, ToCopy},
template_file::template_file,
ts_binding::generate_binding::generate_binding,
write_json_file::write_json_file,
};

async fn gen_folder(real_path: &PathBuf) -> Result<PathBuf, anyhow::Error> {
Expand Down Expand Up @@ -62,65 +67,164 @@ async fn write_gitignore(gen_folder: &PathBuf) -> Result<(), anyhow::Error> {
Ok(())
}

// Function to write files and directories to be copied
async fn write_to_copy(space: &ResolvedSpace, real_path: &Path) -> Result<(), anyhow::Error> {
// Prepare variables once to avoid cloning multiple times
let variables = space.variables.clone().unwrap_or_default();
// Copy the tree structure with files and directories
copy_tree(
&space.files_to_copy,
real_path,
None,
&space.variables,
&space.environments,
)
.await
.with_context(|| format!("Failed to copy tree structure for: {}", real_path.display()))?;

for to_copy in &space.files_to_copy {
// Determine the relative destination path
let dest_relative = to_copy
.path
.strip_prefix(&space.path)
.with_context(|| format!("Failed to strip prefix for {}", to_copy.path.display()))?;

// Read the file content asynchronously
let content = tokio::fs::read_to_string(&to_copy.path)
.await
.with_context(|| format!("Failed to read file: {}", to_copy.path.display()))?;

// Compute the full destination path
let mapped_dist = real_path.join(dest_relative);

// Create parent directories if they don't exist
if let Some(parent) = mapped_dist.parent() {
tokio::fs::create_dir_all(parent)
.await
.with_context(|| format!("Failed to create directories: {}", parent.display()))?;
}

if to_copy.for_each_env {
// Iterate over each environment and write templated files
for env in &space.environments {
let env_variables = get_environment_value(&variables, env)
.with_context(|| format!("Failed to get variables for environment: {}", env))?;
Ok(())
}

let templated_content =
template_file(&content, &env_variables).with_context(|| {
format!(
"Failed to template file for environment {}: {}",
env,
to_copy.path.display()
// Recursive function to copy a tree of files and directories
async fn copy_tree(
copytree: &CopyTree,
copy_into: &Path,
env: Option<&str>,
variables: &Option<Map<String, Value>>,
environments: &HashSet<String>,
) -> Result<(), anyhow::Error> {
for to_copy in &copytree.to_copy {
let prefix = "_forenv";
// Check if the file/directory name needs environment-specific substitution
if needs_substitution(
&to_copy
.last_segment()
.with_context(|| format!("Failed to get last segment for {:?}", to_copy))?,
prefix,
) {
match env {
// If environment is specified, copy with that environment
Some(env) => {
copy_tocopy_with_env(to_copy, copy_into, Some(env), variables, environments)
.await
.with_context(|| {
format!("Failed to copy {:?} with environment: {}", to_copy, env)
})?;
}
// If no environment is specified, copy for all environments
None => {
for env in environments {
// Get environment-specific variables
let variables = match variables {
Some(variables) => {
Some(get_environment_value(variables, env).with_context(|| {
format!(
"Failed to get environment value for '{}' in {:?}",
env, variables
)
})?)
}
None => None,
};
copy_tocopy_with_env(
to_copy,
copy_into,
Some(env),
&variables,
environments,
)
})?;

let dest = mapped_dist.with_file_name(format!("{}.{}", env, to_copy.dest_filename));

tokio::fs::write(&dest, templated_content)
.await
.with_context(|| format!("Failed to write file: {}", dest.display()))?;
.await
.with_context(|| {
format!("Failed to copy {:?} for environment: {}", to_copy, env)
})?;
}
}
}
} else {
// Template the content once and write to the destination
let templated_content = template_file(&content, &variables)
.with_context(|| format!("Failed to template file: {}", to_copy.path.display()))?;
// If no environment substitution is needed, copy without environment
copy_tocopy_with_env(to_copy, copy_into, None, variables, environments)
.await
.with_context(|| {
format!(
"Failed to copy {:?} without environment substitution",
to_copy
)
})?;
}
}

let dest = mapped_dist.with_file_name(&to_copy.dest_filename);
Ok(())
}

tokio::fs::write(&dest, templated_content)
// Function to copy a single file or directory with environment-specific handling
async fn copy_tocopy_with_env(
to_copy: &ToCopy,
copy_into: &Path,
env: Option<&str>,
variables: &Option<Map<String, Value>>,
environments: &HashSet<String>,
) -> Result<(), anyhow::Error> {
let last_segment = to_copy
.last_segment()
.with_context(|| "Failed to get last segment")?;
// Substitute environment in the file/directory name if needed
let substituted_name = match env {
Some(env) => substitute_path_segment(last_segment, "_forenv", env),
None => last_segment.to_string(),
};
let destination = copy_into.join(substituted_name);

match to_copy {
ToCopy::File(file) => {
// Read file content
let content = tokio::fs::read_to_string(&file)
.await
.with_context(|| format!("Failed to write file: {}", dest.display()))?;
.with_context(|| format!("Failed to read file: {:?}", file))?;
// Apply variable substitution if variables are provided
let content = if let Some(variables) = variables {
template_file(&content, variables)
.with_context(|| "Failed to apply variable substitution")?
} else {
content
};
// Write the processed content to the destination
tokio::fs::write(&destination, content)
.await
.with_context(|| format!("Failed to write to destination: {:?}", destination))?;
}
ToCopy::Directory { subtree, .. } => {
// Create the directory if it doesn't exist
if !destination.exists() {
tokio::fs::create_dir(&destination)
.await
.with_context(|| format!("Failed to create directory: {:?}", destination))?;
}
// Recursively copy the subdirectory
Box::pin(copy_tree(
subtree,
&destination,
env,
variables,
environments,
))
.await
.with_context(|| {
format!("Failed to recursively copy subdirectory: {:?}", destination)
})?;
}
}

Ok(())
}

// Function to substitute environment in a path segment
fn substitute_path_segment(segment: &str, from: &str, to: &str) -> String {
if needs_substitution(segment, from) {
segment.replacen(from, to, 1)
} else {
segment.to_string()
}
}

// Function to check if a segment needs environment substitution
fn needs_substitution(segment: &str, from: &str) -> bool {
segment.starts_with(from)
}
30 changes: 8 additions & 22 deletions src/file_graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ pub struct Directory {
pub path: PathBuf,
pub parent_directory: Option<PathBuf>,
pub space: Option<SpaceNode>,
pub rest_to_copy: Vec<FileToCopy>,
pub rest_to_copy: Vec<PathBuf>
}

#[derive(Debug, Clone, PartialEq)]
Expand All @@ -20,12 +20,7 @@ pub struct SpaceNode {
pub variables: Option<serde_json::Map<String, serde_json::Value>>,
}

#[derive(Debug, Clone, PartialEq)]
pub struct FileToCopy {
pub path: PathBuf,
pub dest_filename: String,
pub for_each_env: bool,
}
const FORENV_PREFIX: &str = "_forenv";

/// Creates a graph of the weaveconfig configuration.
/// The root of the graph is typically the `weaveconfig` directory within the project root.
Expand All @@ -41,7 +36,7 @@ pub async fn traverse_directory(
path,
parent_directory: None,
space: None,
rest_to_copy: Vec::new(),
rest_to_copy: Vec::new()
};

locate_directories(&mut root_directory).await?;
Expand Down Expand Up @@ -76,7 +71,7 @@ async fn locate_directories(directory: &mut Directory) -> Result<(), anyhow::Err
path: entry_path.clone(),
parent_directory: Some(parent_path.clone()),
space: None,
rest_to_copy: Vec::new(),
rest_to_copy: Vec::new()
};

if let Err(e) = locate_directories(&mut sub_directory).await {
Expand Down Expand Up @@ -143,7 +138,7 @@ async fn locate_directories(directory: &mut Directory) -> Result<(), anyhow::Err
enum FileType {
Space(SpaceSchema),
Variables(serde_json::Map<String, serde_json::Value>),
Rest(FileToCopy),
Rest(PathBuf),
}

async fn process_file(file_path: PathBuf) -> Result<FileType, anyhow::Error> {
Expand Down Expand Up @@ -195,25 +190,16 @@ async fn process_file(file_path: PathBuf) -> Result<FileType, anyhow::Error> {
map.insert(prefix, serde_json::Value::Object(variables));
Ok(FileType::Variables(map))
}
segments if segments.first() == Some(&"_forenv") => {
let dest_filename = segments[1..].join(".");
Ok(FileType::Rest(FileToCopy {
path: file_path,
dest_filename,
for_each_env: true,
}))
segments if segments.first() == Some(&FORENV_PREFIX) => {
Ok(FileType::Rest(file_path))
}
_ => Err(anyhow!(
"Invalid file name format: '{}'. Expected '_space.json', '_env.json', '_<prefix>_env.json' or '_forenv.<rest>'.",
file_name
)),
}
} else {
Ok(FileType::Rest(FileToCopy {
path: file_path.clone(),
dest_filename: file_name.to_string(),
for_each_env: false,
}))
Ok(FileType::Rest(file_path))
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/get_environment_value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ use serde_json::{Map, Value};

use crate::template_file::value_type;

/// Get the environment variables for a given environment
/// This involves merging the key of the environment into the root overwriting any existing keys
pub fn get_environment_value(
variables: &Map<String, Value>,
environment: &str,
Expand Down
5 changes: 2 additions & 3 deletions src/resolve_spaces.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use crate::{
file_graph::FileToCopy,
merging::merge_map_consume,
space_graph::{GenerateSpace, SpaceGraph},
space_graph::{CopyTree, GenerateSpace, SpaceGraph},
};
use anyhow::{Context, Result};
use serde_json::{Map, Value};
Expand All @@ -15,7 +14,7 @@ pub struct ResolvedSpace {
pub variables: Option<Map<String, Value>>,
pub environments: HashSet<String>,
pub path: PathBuf,
pub files_to_copy: Vec<FileToCopy>,
pub files_to_copy: CopyTree,
pub generate: GenerateSpace,
}

Expand Down
Loading

0 comments on commit 88b37a6

Please sign in to comment.