ckb_capsule/
project_context.rs1use crate::config::{Config, Deployment};
3use crate::version::Version;
4use anyhow::{anyhow, Result};
5use log::error;
6use std::env;
7use std::fs;
8use std::path::{Path, PathBuf};
9use std::str::FromStr;
10
11pub const CONTRACTS_DIR: &str = "contracts";
12const CONTRACTS_BUILD_DIR: &str = "build";
13const MIGRATIONS_DIR: &str = "migrations";
14pub const CONFIG_FILE: &str = "capsule.toml";
15pub const CARGO_CONFIG_FILE: &str = "Cargo.toml";
16
17#[derive(Debug, Copy, Clone, PartialEq, Eq)]
18pub enum BuildEnv {
19 Debug,
20 Release,
21}
22
23impl FromStr for BuildEnv {
24 type Err = &'static str;
25
26 fn from_str(s: &str) -> Result<Self, Self::Err> {
27 match s.to_lowercase().as_str() {
28 "debug" => Ok(BuildEnv::Debug),
29 "release" => Ok(BuildEnv::Release),
30 _ => Err("no match"),
31 }
32 }
33}
34
35#[derive(Debug, Copy, Clone)]
36pub struct BuildConfig {
37 pub build_env: BuildEnv,
38 pub always_debug: bool,
39 pub remap: bool,
41}
42
43#[derive(Debug, Copy, Clone)]
44pub enum DeployEnv {
45 Dev,
46 Production,
47}
48
49impl FromStr for DeployEnv {
50 type Err = &'static str;
51
52 fn from_str(s: &str) -> Result<Self, Self::Err> {
53 match s.to_lowercase().as_str() {
54 "dev" => Ok(DeployEnv::Dev),
55 "production" => Ok(DeployEnv::Production),
56 _ => Err("no match"),
57 }
58 }
59}
60
61#[derive(Clone)]
62pub struct Context {
63 pub project_path: PathBuf,
64 pub config: Config,
65 pub use_docker_host: bool,
66 pub docker_env_file: String,
67 pub rustup_dir: Option<String>,
68}
69
70impl Context {
71 pub fn load() -> Result<Context> {
72 Self::load_from_path(env::current_dir()?)
73 }
74
75 pub fn load_from_path<P: AsRef<Path>>(path: P) -> Result<Context> {
76 let mut project_path = PathBuf::new();
77 project_path.push(&path);
78 let content = {
79 let mut config_path = project_path.clone();
80 config_path.push(CONFIG_FILE);
81 read_config_file(config_path)?
82 };
83 let config: Config = toml_edit::de::from_str(&content).expect("parse config");
84 let capsule_version = Version::current();
85 let project_version: Version = config.version.parse()?;
86 if !capsule_version.is_compatible(&project_version) {
87 return Err(anyhow!(
88 "Incompatible capsule version {}, this project requires a version that's compatible with {}",
89 capsule_version.to_string(),
90 project_version.to_string()
91 ));
92 }
93 Ok(Context {
94 config,
95 project_path,
96 use_docker_host: false,
97 docker_env_file: String::new(),
98 rustup_dir: None,
99 })
100 }
101
102 pub fn workspace_dir(&self) -> Result<PathBuf> {
103 let mut path = self.project_path.clone();
104 if let Some(workspace_dir) = self.config.rust.workspace_dir.as_ref() {
105 match workspace_dir.to_str() {
106 Some(".") => {}
107 Some(CONTRACTS_DIR) => {
108 path.push(workspace_dir);
109 }
110 dir => {
111 return Err(anyhow!("Invalid `workspace_dir` config: {:?}, only allowed \".\" or \"contracts\".", dir));
112 }
113 }
114 }
115 Ok(path)
116 }
117
118 pub fn contracts_path(&self) -> PathBuf {
119 let mut path = self.project_path.clone();
120 path.push(CONTRACTS_DIR);
121 path
122 }
123
124 pub fn contracts_build_dir(&self) -> PathBuf {
125 let mut path = self.project_path.clone();
126 path.push(CONTRACTS_BUILD_DIR);
127 path
128 }
129
130 pub fn contracts_build_path(&self, env: BuildEnv) -> PathBuf {
131 let mut path = self.contracts_build_dir();
132 let prefix = match env {
133 BuildEnv::Debug => "debug",
134 BuildEnv::Release => "release",
135 };
136 path.push(prefix);
137 path
138 }
139
140 pub fn migrations_path(&self, env: DeployEnv) -> PathBuf {
141 let mut path = self.project_path.clone();
142 path.push(MIGRATIONS_DIR);
143 let prefix = match env {
144 DeployEnv::Production => "production",
145 DeployEnv::Dev => "dev",
146 };
147 path.push(prefix);
148 path
149 }
150
151 pub fn load_deployment(&self) -> Result<Deployment> {
152 let mut path = self.project_path.clone();
153 path.push(
154 self.config
155 .deployment
156 .as_deref()
157 .unwrap_or_else(|| Path::new("deployment.toml")),
158 );
159 match toml_edit::de::from_str(&fs::read_to_string(&path)?) {
160 Ok(deployment) => Ok(deployment),
161 Err(err) => {
162 error!("failed to parse {:?}", path);
163 Err(err.into())
164 }
165 }
166 }
167}
168
169pub fn read_config_file<P: AsRef<Path> + std::fmt::Debug>(path: P) -> Result<String> {
170 match fs::read_to_string(&path) {
171 Ok(content) => Ok(content),
172 Err(err) => Err(anyhow!(
173 "Can't found {:?}, current directory is not a project. error: {:?}",
174 path,
175 err
176 )),
177 }
178}
179
180pub fn write_config_file<P: AsRef<Path>>(path: P, content: String) -> Result<()> {
181 fs::write(path, content)?;
182 Ok(())
183}