config.rs 4.6 KB

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