1use std::{
2 borrow::Cow,
3 env, fs,
4 path::{Path, PathBuf},
5 str::FromStr,
6};
7use url::Url;
8
9use anyhow::{anyhow, Context, Error, Result};
10
11#[derive(Clone, Debug, Eq, PartialEq)]
13pub enum Source {
14 String(String),
16
17 Local(PathBuf),
19 }
22
23impl Source {
24 pub fn parse<S>(source: S) -> Result<Self, Error>
39 where
40 S: AsRef<str>,
41 {
42 let source = source.as_ref().trim();
43
44 if source.starts_with('[') || source.starts_with("\n") {
45 return Ok(Source::String(source.to_owned()));
46 }
47 let root = env::current_dir()?.canonicalize()?;
48 Source::with_root(root, source)
49 }
50
51 fn with_root<P, S>(root: P, source: S) -> Result<Self, Error>
55 where
56 P: AsRef<Path>,
57 S: AsRef<str>,
58 {
59 let base = Url::from_directory_path(&root).map_err(|_| {
60 anyhow!(
61 "root path '{:?}' is not absolute",
62 root.as_ref().as_os_str()
63 )
64 })?;
65 let url = base.join(source.as_ref())?;
66
67 match url.scheme() {
68 "file" => Ok(Source::local(url.path())),
69 _ => Err(anyhow!("unsupported URL '{}'", url)),
71 }
72 }
73
74 fn local<P>(path: P) -> Self
76 where
77 P: AsRef<Path>,
78 {
79 Source::Local(path.as_ref().into())
80 }
81
82 pub fn get(&self) -> Result<String> {
86 match self {
87 Source::Local(path) => get_local_contract(path),
88 Source::String(abi) => Ok(abi.clone()),
89 }
90 }
91}
92
93fn get_local_contract(path: &Path) -> Result<String> {
94 let path = if path.is_relative() {
95 let absolute_path = path.canonicalize().with_context(|| {
96 format!(
97 "unable to canonicalize file from working dir {} with path {}",
98 env::current_dir()
99 .map(|cwd| cwd.display().to_string())
100 .unwrap_or_else(|err| format!("??? ({})", err)),
101 path.display(),
102 )
103 })?;
104 Cow::Owned(absolute_path)
105 } else {
106 Cow::Borrowed(path)
107 };
108
109 let json = fs::read_to_string(&path).context(format!(
110 "failed to read artifact JSON file with path {}",
111 &path.display()
112 ))?;
113 Ok(json)
114}
115
116impl FromStr for Source {
117 type Err = Error;
118
119 fn from_str(s: &str) -> Result<Self> {
120 Source::parse(s)
121 }
122}