1use apple_sdk::Platform;
4use std::{
5 path::{Path, PathBuf},
6 str::FromStr,
7};
8use thiserror::Error;
9
10#[non_exhaustive]
11#[derive(Debug, Error)]
12pub enum SdkPathError {
13 #[error("apple_sdk::Error")]
14 AppleSdkError(apple_sdk::Error),
15 #[error("path is not sdk path")]
16 InvalidPath(PathBuf),
17 #[error("xcrun lookup failed")]
18 XcrunError(std::io::Error),
19}
20
21#[derive(Debug, Clone)]
22pub struct SdkPath(PathBuf);
23
24impl SdkPath {
25 pub fn path(&self) -> &Path {
26 &self.0
27 }
28}
29
30impl TryFrom<&Platform> for SdkPath {
31 type Error = SdkPathError;
32
33 fn try_from(platform: &Platform) -> Result<Self, Self::Error> {
34 use std::process::Command;
35 let output = Command::new("xcrun")
36 .args(&[
37 "--sdk",
38 &platform.filesystem_name().to_lowercase(),
39 "--show-sdk-path",
40 ])
41 .output()
42 .map_err(SdkPathError::XcrunError)?
43 .stdout;
44 let path = std::str::from_utf8(&output)
45 .expect("invalid output from `xcrun`")
46 .trim_end();
47 Ok(Self(PathBuf::from(path)))
48 }
49}
50
51impl TryFrom<PathBuf> for SdkPath {
52 type Error = SdkPathError;
53 fn try_from(path: PathBuf) -> Result<Self, Self::Error> {
54 let s = path
55 .to_str()
56 .expect("sdk path is always convertible to utf-8 string");
57 if !s.ends_with(".sdk") {
58 return Err(SdkPathError::InvalidPath(path));
59 }
60 let path = std::path::PathBuf::from(s);
61 if !path.exists() {
62 return Err(SdkPathError::InvalidPath(path));
63 }
64 Ok(Self(path))
65 }
66}
67
68impl TryFrom<&str> for SdkPath {
69 type Error = SdkPathError;
70 fn try_from(s: &str) -> Result<Self, Self::Error> {
71 let platform = Platform::from_str(s).map_err(SdkPathError::AppleSdkError)?;
72 SdkPath::try_from(&platform)
73 }
74}