revive_solc_json_interface/standard_json/input/
source.rs

1//! The `solc --standard-json` input source.
2
3use std::path::Path;
4use std::path::PathBuf;
5
6use serde::Deserialize;
7use serde::Serialize;
8
9/// The `solc --standard-json` input source.
10#[derive(Clone, Debug, Serialize, Deserialize)]
11#[serde(rename_all = "camelCase")]
12pub struct Source {
13    /// The source code file content.
14    #[serde(skip_serializing_if = "Option::is_none")]
15    pub content: Option<String>,
16    /// The source file URLs.
17    #[serde(skip_serializing_if = "Option::is_none")]
18    pub urls: Option<Vec<String>>,
19}
20
21impl Source {
22    /// Reads the source from the file system.
23    pub fn try_read(path: &Path) -> anyhow::Result<Self> {
24        let content = if path.to_string_lossy() == "-" {
25            std::io::read_to_string(std::io::stdin())
26                .map_err(|error| anyhow::anyhow!("<stdin> reading: {error}"))
27        } else {
28            std::fs::read_to_string(path)
29                .map_err(|error| anyhow::anyhow!("File {path:?} reading: {error}"))
30        }?;
31
32        Ok(Self {
33            content: Some(content),
34            urls: None,
35        })
36    }
37
38    /// Tries to resolve the source code.
39    ///
40    /// At the moment only one URL pointing to the file system is supported.
41    pub fn try_resolve(&mut self) -> anyhow::Result<()> {
42        match (self.content.as_ref(), self.urls.as_ref()) {
43            (Some(_), None) => Ok(()),
44            (None, Some(urls)) => {
45                let mut errors = Vec::with_capacity(urls.len());
46                for url in urls.iter() {
47                    let url_path = PathBuf::from(url);
48                    match Source::try_read(url_path.as_path()) {
49                        Ok(resolved) => {
50                            *self = resolved;
51                            break;
52                        }
53                        Err(error) => errors.push(error),
54                    }
55                }
56                if !errors.is_empty() {
57                    anyhow::bail!(
58                        "{}",
59                        errors
60                            .into_iter()
61                            .map(|error| error.to_string())
62                            .collect::<Vec<String>>()
63                            .join("\n")
64                    );
65                }
66                Ok(())
67            }
68            (Some(_), Some(_)) => anyhow::bail!("Both `content` and `urls` cannot be set."),
69            (None, None) => anyhow::bail!("Either `content` or `urls` must be set."),
70        }
71    }
72
73    /// Takes ownership of the source code and returns it.
74    pub fn take_content(&mut self) -> Option<String> {
75        self.content.take()
76    }
77
78    /// Returns the source code reference, if the source has been previously read or resolved.
79    pub fn content(&self) -> Option<&str> {
80        self.content.as_deref()
81    }
82}
83
84impl From<String> for Source {
85    fn from(content: String) -> Self {
86        Self {
87            content: Some(content),
88            urls: None,
89        }
90    }
91}
92
93impl From<&Path> for Source {
94    fn from(path: &Path) -> Self {
95        Self {
96            content: None,
97            urls: Some(vec![path.to_string_lossy().to_string()]),
98        }
99    }
100}