use std::ffi::OsStr;
use std::fmt;
use std::ops::Deref;
use std::path::{self, PathBuf};
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct Path(PathBuf);
impl Path {
pub fn empty() -> Self {
Self(PathBuf::new())
}
pub fn from_path_buf(p: PathBuf) -> Self {
Self(p)
}
pub fn into_path_buf(self) -> PathBuf {
self.0
}
pub fn join(&self, component: impl AsRef<path::Path>) -> Self {
Self(self.0.join(component))
}
pub fn parent(&self) -> Option<Self> {
self.0.parent().map(|p| Self(p.to_owned()))
}
pub fn file_name(&self) -> Option<&OsStr> {
self.0.file_name()
}
pub fn file_stem(&self) -> Option<&OsStr> {
self.0.file_stem()
}
pub fn extension(&self) -> Option<&OsStr> {
self.0.extension()
}
pub fn with_extension(&self, ext: impl AsRef<OsStr>) -> Self {
Self(self.0.with_extension(ext))
}
pub fn is_absolute(&self) -> bool {
self.0.is_absolute()
}
pub fn is_relative(&self) -> bool {
self.0.is_relative()
}
pub fn is_file(&self) -> bool {
self.0.is_file()
}
pub fn is_dir(&self) -> bool {
self.0.is_dir()
}
pub fn exists(&self) -> bool {
self.0.exists()
}
pub fn display(&self) -> path::Display<'_> {
self.0.display()
}
pub fn asset() -> Self {
if let Ok(v) = std::env::var("OA_ASSET") {
return Self(PathBuf::from(v));
}
if let Some(root) = find_source_root() {
let candidate = root.join("sdk/asset");
if candidate.is_dir() {
return Self(candidate);
}
}
Self(PathBuf::from("asset"))
}
pub fn asset_rel(relative: impl AsRef<path::Path>) -> Self {
Self::asset().join(relative)
}
pub fn var() -> Self {
if let Ok(v) = std::env::var("OA_VAR") {
return Self(PathBuf::from(v));
}
if let Some(root) = find_source_root() {
let candidate = root.join("var");
if candidate.is_dir() {
return Self(candidate);
}
}
Self(PathBuf::from("var"))
}
pub fn var_rel(relative: impl AsRef<path::Path>) -> Self {
Self::var().join(relative)
}
pub fn data() -> Self {
if let Ok(v) = std::env::var("OA_DATA") {
return Self(PathBuf::from(v));
}
Self::var().join("data")
}
pub fn data_rel(relative: impl AsRef<path::Path>) -> Self {
Self::data().join(relative)
}
pub fn home() -> Self {
if let Ok(v) = std::env::var("HOME") {
return Self(PathBuf::from(v));
}
if let Ok(v) = std::env::var("USERPROFILE") {
return Self(PathBuf::from(v));
}
Self(std::env::current_dir().unwrap_or_else(|_| PathBuf::from(".")))
}
pub fn temp() -> Self {
for var in ["TMPDIR", "TEMP", "TMP"] {
if let Ok(v) = std::env::var(var) {
return Self(PathBuf::from(v));
}
}
#[cfg(unix)]
return Self(PathBuf::from("/tmp"));
#[cfg(not(unix))]
Self(std::env::current_dir().unwrap_or_else(|_| PathBuf::from(".")))
}
}
impl Deref for Path {
type Target = path::Path;
fn deref(&self) -> &path::Path {
&self.0
}
}
impl AsRef<path::Path> for Path {
fn as_ref(&self) -> &path::Path {
&self.0
}
}
impl std::borrow::Borrow<path::Path> for Path {
fn borrow(&self) -> &path::Path {
&self.0
}
}
impl From<PathBuf> for Path {
fn from(p: PathBuf) -> Self {
Self(p)
}
}
impl From<Path> for PathBuf {
fn from(p: Path) -> PathBuf {
p.0
}
}
impl From<&path::Path> for Path {
fn from(p: &path::Path) -> Self {
Self(p.to_owned())
}
}
impl From<&str> for Path {
fn from(s: &str) -> Self {
Self(PathBuf::from(s))
}
}
impl From<String> for Path {
fn from(s: String) -> Self {
Self(PathBuf::from(s))
}
}
impl AsRef<OsStr> for Path {
fn as_ref(&self) -> &OsStr {
self.0.as_os_str()
}
}
impl fmt::Debug for Path {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.0, f)
}
}
impl fmt::Display for Path {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0.display(), f)
}
}
fn find_source_root() -> Option<PathBuf> {
let cwd = std::env::current_dir().ok()?;
let mut dir: &path::Path = &cwd;
loop {
if dir.join("sdk/asset").is_dir() || dir.join("Cargo.toml").is_file() {
return Some(dir.to_owned());
}
dir = dir.parent()?;
}
}