logix_type/types/
executable_path.rs1use logix_vfs::LogixVfs;
2
3use crate::{
4 error::{ParseError, PathError, Result, SourceSpan, Wanted},
5 parser::LogixParser,
6 token::{Literal, StrLit, Token},
7 type_trait::{LogixTypeDescriptor, LogixValueDescriptor, Value},
8 types::{FullPath, NameOnlyPath, ValidPath},
9 LogixType,
10};
11use std::{
12 borrow::Cow,
13 ffi::OsStr,
14 fmt,
15 path::{Path, PathBuf},
16};
17
18#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
19enum Location {
20 Path(FullPath),
21 Name(NameOnlyPath),
22}
23
24#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
26pub struct ExecutableEnv<'a> {
27 pub path_env: Option<Cow<'a, OsStr>>,
28}
29
30impl<'a> ExecutableEnv<'a> {
31 const DEFAULT: ExecutableEnv<'static> = ExecutableEnv { path_env: None };
32
33 pub fn which(&self, name_or_path: impl AsRef<OsStr>) -> Option<FullPath> {
34 if let Some(path_env) = self.path_env.as_deref() {
35 which::which_in_global(name_or_path, Some(path_env))
36 .ok()?
37 .next()
38 .map(FullPath::try_from)?
39 .ok()
40 } else {
41 which::which_global(name_or_path)
42 .ok()
43 .map(FullPath::try_from)?
44 .ok()
45 }
46 }
47}
48
49#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
53pub struct ExecutablePath {
54 loc: Location,
55}
56
57impl ExecutablePath {
58 pub fn as_path(&self) -> &Path {
59 match &self.loc {
60 Location::Path(v) => v.as_path(),
61 Location::Name(v) => v.as_path(),
62 }
63 }
64
65 pub fn which(&self, env: Option<&ExecutableEnv>) -> Option<FullPath> {
66 env.unwrap_or(&ExecutableEnv::DEFAULT).which(self)
67 }
68
69 pub fn join(&self, path: impl AsRef<Path>) -> Result<Self, PathError> {
70 Ok(Self {
71 loc: match &self.loc {
72 Location::Path(v) => v.join(path).map(Location::Path)?,
73 Location::Name(v) => v.join(path).map(Location::Name)?,
74 },
75 })
76 }
77
78 pub fn with_file_name(&self, name: impl AsRef<OsStr>) -> Self {
79 Self {
80 loc: match &self.loc {
81 Location::Path(v) => Location::Path(v.with_file_name(name)),
82 Location::Name(v) => Location::Name(v.with_file_name(name)),
83 },
84 }
85 }
86
87 pub fn with_extension(&self, ext: impl AsRef<OsStr>) -> Self {
88 Self {
89 loc: match &self.loc {
90 Location::Path(v) => Location::Path(v.with_extension(ext)),
91 Location::Name(v) => Location::Name(v.with_extension(ext)),
92 },
93 }
94 }
95}
96
97impl TryFrom<PathBuf> for ExecutablePath {
98 type Error = PathError;
99
100 fn try_from(path: PathBuf) -> Result<Self, PathError> {
101 match ValidPath::try_from(path)? {
102 ValidPath::Full(path) => Ok(Self {
103 loc: Location::Path(path),
104 }),
105 ValidPath::Name(name) => Ok(Self {
106 loc: Location::Name(name),
107 }),
108 ValidPath::Rel(_) => Err(PathError::NotFullOrNameOnly),
109 }
110 }
111}
112
113impl From<ExecutablePath> for PathBuf {
114 fn from(v: ExecutablePath) -> PathBuf {
115 match v.loc {
116 Location::Path(v) => v.into(),
117 Location::Name(v) => v.into(),
118 }
119 }
120}
121
122impl_path_type_traits!(
123 ExecutablePath,
124 "The name of or full path to an executable file"
125);