Skip to main content

stellar_scaffold_cli/commands/
update_env.rs

1#![allow(clippy::struct_excessive_bools)]
2use clap::Parser;
3use std::{
4    fs,
5    io::{self, BufRead},
6};
7
8#[derive(Parser, Debug, Clone)]
9pub struct Cmd {
10    /// name of env var to update
11    #[arg(long)]
12    pub name: String,
13    /// value of env var to update, if not provided stdin is used
14    #[arg(long)]
15    pub value: Option<String>,
16    /// Path to .env file
17    #[arg(long, default_value = ".env")]
18    pub env_file: std::path::PathBuf,
19}
20
21#[derive(thiserror::Error, Debug)]
22pub enum Error {
23    #[error(transparent)]
24    Io(#[from] io::Error),
25}
26
27impl Cmd {
28    pub fn run(&self) -> Result<(), Error> {
29        let file = &self.env_file;
30        let env_file = if file.exists() {
31            fs::read_to_string(file)?
32        } else {
33            String::new()
34        };
35
36        let value = self.value.clone().unwrap_or_else(|| {
37            // read from stdin
38            std::io::stdin()
39                .lock()
40                .lines()
41                .next()
42                .expect("stdin closed")
43                .expect("stdin error")
44        });
45        let name = &self.name;
46        let new_env_file =
47            replace_lines_starting_with(&env_file, &format!("{name}="), &format!("{name}={value}"));
48        fs::write(&self.env_file, new_env_file)?;
49        Ok(())
50    }
51}
52
53fn replace_lines_starting_with(input: &str, starts_with: &str, replacement: &str) -> String {
54    let mut found = false;
55    let mut v = input
56        .lines()
57        .map(|line| {
58            if line.starts_with(starts_with) {
59                found = true;
60                replacement
61            } else {
62                line
63            }
64        })
65        .collect::<Vec<&str>>();
66    if !found {
67        v.push(replacement);
68    }
69    v.join("\n")
70}