config.rs 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. use anchor_syn::idl::Idl;
  2. use anyhow::{anyhow, Error, Result};
  3. use serde::{Deserialize, Serialize};
  4. use serum_common::client::Cluster;
  5. use solana_sdk::signature::Keypair;
  6. use std::fs::{self, File};
  7. use std::io::prelude::*;
  8. use std::path::Path;
  9. use std::path::PathBuf;
  10. use std::str::FromStr;
  11. #[derive(Debug, Default)]
  12. pub struct Config {
  13. pub cluster: Cluster,
  14. pub wallet: WalletPath,
  15. pub test: Option<Test>,
  16. }
  17. impl Config {
  18. // Searches all parent directories for an Anchor.toml file.
  19. pub fn discover() -> Result<Option<(Self, PathBuf, Option<PathBuf>)>> {
  20. // Set to true if we ever see a Cargo.toml file when traversing the
  21. // parent directories.
  22. let mut cargo_toml = None;
  23. let _cwd = std::env::current_dir()?;
  24. let mut cwd_opt = Some(_cwd.as_path());
  25. while let Some(cwd) = cwd_opt {
  26. let files = fs::read_dir(cwd)?;
  27. // Cargo.toml file for this directory level.
  28. let mut cargo_toml_level = None;
  29. let mut anchor_toml = None;
  30. for f in files {
  31. let p = f?.path();
  32. if let Some(filename) = p.file_name() {
  33. if filename.to_str() == Some("Cargo.toml") {
  34. cargo_toml_level = Some(p);
  35. } else if filename.to_str() == Some("Anchor.toml") {
  36. let mut cfg_file = File::open(&p)?;
  37. let mut cfg_contents = String::new();
  38. cfg_file.read_to_string(&mut cfg_contents)?;
  39. let cfg = cfg_contents.parse()?;
  40. anchor_toml = Some((cfg, p));
  41. }
  42. }
  43. }
  44. if let Some((cfg, parent)) = anchor_toml {
  45. return Ok(Some((cfg, parent, cargo_toml)));
  46. }
  47. if cargo_toml.is_none() {
  48. cargo_toml = cargo_toml_level;
  49. }
  50. cwd_opt = cwd.parent();
  51. }
  52. Ok(None)
  53. }
  54. pub fn wallet_kp(&self) -> Result<Keypair> {
  55. solana_sdk::signature::read_keypair_file(&self.wallet.to_string())
  56. .map_err(|_| anyhow!("Unable to read keypair file"))
  57. }
  58. }
  59. // Pubkey serializes as a byte array so use this type a hack to serialize
  60. // into base 58 strings.
  61. #[derive(Debug, Serialize, Deserialize)]
  62. struct _Config {
  63. cluster: String,
  64. wallet: String,
  65. test: Option<Test>,
  66. }
  67. impl ToString for Config {
  68. fn to_string(&self) -> String {
  69. let cfg = _Config {
  70. cluster: format!("{}", self.cluster),
  71. wallet: self.wallet.to_string(),
  72. test: self.test.clone(),
  73. };
  74. toml::to_string(&cfg).expect("Must be well formed")
  75. }
  76. }
  77. impl FromStr for Config {
  78. type Err = Error;
  79. fn from_str(s: &str) -> Result<Self, Self::Err> {
  80. let cfg: _Config = toml::from_str(s)
  81. .map_err(|e| anyhow::format_err!("Unable to deserialize config: {}", e.to_string()))?;
  82. Ok(Config {
  83. cluster: cfg.cluster.parse()?,
  84. wallet: shellexpand::tilde(&cfg.wallet).parse()?,
  85. test: cfg.test,
  86. })
  87. }
  88. }
  89. #[derive(Debug, Clone, Serialize, Deserialize)]
  90. pub struct Test {
  91. pub genesis: Vec<GenesisEntry>,
  92. }
  93. #[derive(Debug, Clone, Serialize, Deserialize)]
  94. pub struct GenesisEntry {
  95. // Base58 pubkey string.
  96. pub address: String,
  97. // Filepath to the compiled program to embed into the genesis.
  98. pub program: String,
  99. }
  100. // TODO: this should read idl dir instead of parsing source.
  101. pub fn read_all_programs() -> Result<Vec<Program>> {
  102. let files = fs::read_dir("programs")?;
  103. let mut r = vec![];
  104. for f in files {
  105. let path = f?.path();
  106. let idl = anchor_syn::parser::file::parse(path.join("src/lib.rs"))?;
  107. let lib_name = extract_lib_name(&path.join("Cargo.toml"))?;
  108. r.push(Program {
  109. lib_name,
  110. path,
  111. idl,
  112. });
  113. }
  114. Ok(r)
  115. }
  116. pub fn extract_lib_name(path: impl AsRef<Path>) -> Result<String> {
  117. let mut toml = File::open(path)?;
  118. let mut contents = String::new();
  119. toml.read_to_string(&mut contents)?;
  120. let cargo_toml: toml::Value = contents.parse()?;
  121. match cargo_toml {
  122. toml::Value::Table(t) => match t.get("lib") {
  123. None => Err(anyhow!("lib not found in Cargo.toml")),
  124. Some(lib) => match lib
  125. .get("name")
  126. .ok_or_else(|| anyhow!("lib name not found in Cargo.toml"))?
  127. {
  128. toml::Value::String(n) => Ok(n.to_string()),
  129. _ => Err(anyhow!("lib name must be a string")),
  130. },
  131. },
  132. _ => Err(anyhow!("Invalid Cargo.toml")),
  133. }
  134. }
  135. #[derive(Debug, Clone)]
  136. pub struct Program {
  137. pub lib_name: String,
  138. pub path: PathBuf,
  139. pub idl: Idl,
  140. }
  141. impl Program {
  142. pub fn anchor_keypair_path(&self) -> PathBuf {
  143. std::env::current_dir()
  144. .expect("Must have current dir")
  145. .join(format!(
  146. "target/deploy/anchor-{}-keypair.json",
  147. self.lib_name
  148. ))
  149. }
  150. pub fn binary_path(&self) -> PathBuf {
  151. std::env::current_dir()
  152. .expect("Must have current dir")
  153. .join(format!("target/deploy/{}.so", self.lib_name))
  154. }
  155. }
  156. serum_common::home_path!(WalletPath, ".config/solana/id.json");