use anyhow::Result;
use std::io::{BufRead, BufReader};
use std::process::{Child, Command, Stdio};
use std::sync::mpsc;
use std::thread;
#[derive(Debug, Clone, Default)]
pub struct LoginInfo {
pub url: Option<String>,
}
pub fn is_aws_login_available() -> bool {
Command::new("aws")
.args(["login", "help"])
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()
.map(|s| s.success())
.unwrap_or(false)
}
pub fn spawn_aws_login(profile: &str, region: &str) -> Result<(Child, mpsc::Receiver<LoginInfo>)> {
let mut child = Command::new("aws")
.args(["login", "--profile", profile, "--region", region])
.stdout(Stdio::null()) .stderr(Stdio::piped()) .spawn()?;
let stderr = child.stderr.take().expect("stderr was piped");
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
let reader = BufReader::new(stderr);
let mut info = LoginInfo::default();
for line in reader.lines().map_while(Result::ok) {
if line.contains("https://") {
if let Some(url) = extract_url(&line) {
info.url = Some(url);
let _ = tx.send(info.clone());
}
}
}
});
Ok((child, rx))
}
fn extract_url(line: &str) -> Option<String> {
let start = line.find("https://")?;
let rest = &line[start..];
let end = rest.find(char::is_whitespace).unwrap_or(rest.len());
Some(rest[..end].to_string())
}
pub fn check_login_status(child: &mut Child) -> Result<Option<bool>> {
match child.try_wait()? {
None => Ok(None), Some(status) => Ok(Some(status.success())),
}
}
pub fn read_child_stderr(child: &mut Child) -> Option<String> {
use std::io::Read;
child.stderr.take().and_then(|mut stderr| {
let mut output = String::new();
stderr.read_to_string(&mut output).ok()?;
let trimmed = output.trim();
if trimmed.is_empty() {
None
} else {
Some(trimmed.to_string())
}
})
}