use std::net::{IpAddr, Ipv6Addr};
use crate::{error::Error, Location, Region};
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct Placement {
#[doc(alias = "FLY_APP_NAME")]
pub app: String,
#[doc(alias = "FLY_PROCESS_GROUP")]
pub process_group: Option<String>,
#[doc(alias = "FLY_PUBLIC_IP")]
pub public_ip: Option<Ipv6Addr>,
#[doc(alias = "FLY_PRIVATE_IP")]
pub private_ip: Ipv6Addr,
#[doc(alias = "FLY_ALLOC_ID")]
pub allocation: String,
pub machine: Option<Machine>,
#[doc(alias = "FLY_REGION")]
pub location: Location,
}
impl Placement {
#[cfg(feature = "environment")]
#[cfg_attr(docsrs, doc(cfg(feature = "environment")))]
pub fn current() -> Result<Self, Error> {
let app = var("FLY_APP_NAME")?;
let process_group = std::env::var("FLY_PROCESS_GROUP").ok();
let public_ip = public_address();
let private_ip = environment_address().ok_or(Error::Unavailable)?;
let allocation = var("FLY_ALLOC_ID")?;
let machine = Machine::current().ok();
let region_code = var("FLY_REGION")?;
#[cfg(feature = "regions")]
let location: Location = region_code.parse().expect("invalid $FLY_REGION");
#[cfg(not(feature = "regions"))]
let location = region_code;
Ok(Self {
app,
process_group,
public_ip,
private_ip,
allocation,
machine,
location,
})
}
#[cfg(feature = "regions")]
pub fn region(&self) -> Option<Region> {
match self.location {
Location::Region(region) => Some(region),
_ => None,
}
}
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct Machine {
#[doc(alias = "FLY_MACHINE_ID")]
pub id: String,
#[doc(alias = "FLY_IMAGE_REF")]
pub image: Option<String>,
#[doc(alias = "FLY_MACHINE_VERSION")]
pub version: String,
#[doc(alias = "FLY_VM_MEMORY_MB")]
pub memory: Option<usize>,
}
impl Machine {
#[cfg(feature = "environment")]
#[cfg_attr(docsrs, doc(cfg(feature = "environment")))]
pub fn current() -> Result<Self, Error> {
let id = var("FLY_MACHINE_ID")?;
let image = std::env::var("FLY_IMAGE_REF").ok();
let version = var("FLY_MACHINE_VERSION")?;
let memory = std::env::var("FLY_VM_MEMORY_MB")
.ok()
.and_then(|value| value.parse::<usize>().ok());
Ok(Self {
id,
image,
version,
memory,
})
}
}
#[cfg(feature = "environment")]
#[cfg_attr(docsrs, doc(cfg(feature = "environment")))]
pub fn hosted() -> bool {
use std::env::var;
matches!((var("FLY_APP_NAME"), var("FLY_PRIVATE_IP")), (Ok(_), Ok(_)))
}
#[cfg(any(feature = "detect", feature = "environment"))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "detect", feature = "environment"))))]
pub fn private_address() -> Option<Ipv6Addr> {
#[cfg(feature = "environment")]
let ip = environment_address();
#[cfg(not(feature = "environment"))]
let ip: Option<Ipv6Addr> = None;
#[cfg(feature = "detect")]
let ip = ip.or_else(detect_address);
ip
}
#[cfg(feature = "environment")]
#[cfg_attr(docsrs, doc(cfg(feature = "environment")))]
pub fn public_address() -> Option<Ipv6Addr> {
std::env::var("FLY_PUBLIC_IP")
.ok()
.and_then(|value| value.parse::<Ipv6Addr>().ok())
}
#[cfg(feature = "environment")]
fn environment_address() -> Option<Ipv6Addr> {
let ip = std::env::var("FLY_PRIVATE_IP").ok();
match ip {
Some(ip) if !ip.is_empty() => ip.parse::<Ipv6Addr>().ok(),
_ => None,
}
}
#[cfg(feature = "detect")]
fn detect_address() -> Option<Ipv6Addr> {
let interfaces = if_addrs::get_if_addrs().ok()?;
interfaces
.into_iter()
.filter_map(|interface| match interface.ip() {
IpAddr::V6(ip) if ip.segments()[0] == 0xfdaa => Some(ip),
_ => None,
})
.next()
}
#[cfg(feature = "environment")]
fn var(name: &'static str) -> Result<String, Error> {
match std::env::var(name) {
Ok(value) => Ok(value),
_ => Err(Error::Unavailable),
}
}