mod template;
#[cfg(test)]
mod tests;
pub use self::template::Template;
pub use self::template::{
AVAILABLE_SPECS, DEFAULT_P2P_PORT, DEFAULT_RPC_PORT, DEFAULT_SPEC, TemplateContext,
};
pub use std::io::{Error, Result};
use ckb_types::H256;
use includedir::Files;
use serde::{Deserialize, Serialize};
use std::borrow::Cow;
use std::fmt;
use std::fs;
use std::io::{BufReader, Cursor, Read};
use std::path::{Path, PathBuf};
use ckb_system_scripts::BUNDLED_CELL;
mod bundled {
#![allow(missing_docs, clippy::unreadable_literal)]
include!(concat!(env!("OUT_DIR"), "/bundled.rs"));
}
pub use bundled::BUNDLED;
include!(concat!(env!("OUT_DIR"), "/code_hashes.rs"));
pub const CKB_CONFIG_FILE_NAME: &str = "ckb.toml";
pub const MINER_CONFIG_FILE_NAME: &str = "ckb-miner.toml";
pub const SPEC_DEV_FILE_NAME: &str = "specs/dev.toml";
pub const DB_OPTIONS_FILE_NAME: &str = "default.db-options";
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Resource {
Bundled {
bundled: String,
},
FileSystem {
file: PathBuf,
},
Raw {
raw: String,
},
}
impl fmt::Display for Resource {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Resource::Bundled { bundled } => write!(f, "Bundled({bundled})"),
Resource::FileSystem { file } => write!(f, "FileSystem({})", file.display()),
Resource::Raw { raw } => write!(f, "Raw({})", raw),
}
}
}
impl Resource {
pub fn bundled(bundled: String) -> Resource {
Resource::Bundled { bundled }
}
pub fn file_system(file: PathBuf) -> Resource {
Resource::FileSystem { file }
}
pub fn raw(raw: String) -> Resource {
Resource::Raw { raw }
}
pub fn ckb_config<P: AsRef<Path>>(root_dir: P) -> Resource {
Resource::file_system(root_dir.as_ref().join(CKB_CONFIG_FILE_NAME))
}
pub fn miner_config<P: AsRef<Path>>(root_dir: P) -> Resource {
Resource::file_system(root_dir.as_ref().join(MINER_CONFIG_FILE_NAME))
}
pub fn db_options<P: AsRef<Path>>(root_dir: P) -> Resource {
Resource::file_system(root_dir.as_ref().join(DB_OPTIONS_FILE_NAME))
}
pub fn bundled_ckb_config() -> Resource {
Resource::bundled(CKB_CONFIG_FILE_NAME.to_string())
}
pub fn bundled_miner_config() -> Resource {
Resource::bundled(MINER_CONFIG_FILE_NAME.to_string())
}
pub fn bundled_db_options() -> Resource {
Resource::bundled(DB_OPTIONS_FILE_NAME.to_string())
}
pub fn exported_in<P: AsRef<Path>>(root_dir: P) -> bool {
BUNDLED
.file_names()
.chain(BUNDLED_CELL.file_names())
.any(|name| join_bundled_key(root_dir.as_ref().to_path_buf(), name).exists())
}
pub fn is_bundled(&self) -> bool {
matches!(self, Resource::Bundled { .. })
}
pub fn exists(&self) -> bool {
match self {
Resource::Bundled { bundled } => {
SourceFiles::new(&BUNDLED_CELL, &BUNDLED).is_available(bundled)
}
Resource::FileSystem { file } => file.exists(),
Resource::Raw { .. } => true,
}
}
pub fn parent(&self) -> Option<&Path> {
match self {
Resource::FileSystem { file } => file.parent(),
_ => None,
}
}
pub fn absolutize<P: AsRef<Path>>(&mut self, base: P) {
if let Resource::FileSystem { file: path } = self
&& path.is_relative()
{
*path = base.as_ref().join(&path)
}
}
pub fn get(&self) -> Result<Cow<'static, [u8]>> {
match self {
Resource::Bundled { bundled } => SourceFiles::new(&BUNDLED_CELL, &BUNDLED).get(bundled),
Resource::FileSystem { file } => Ok(Cow::Owned(fs::read(file)?)),
Resource::Raw { raw } => Ok(Cow::Owned(raw.to_owned().into_bytes())),
}
}
pub fn read(&self) -> Result<Box<dyn Read>> {
match self {
Resource::Bundled { bundled } => {
SourceFiles::new(&BUNDLED_CELL, &BUNDLED).read(bundled)
}
Resource::FileSystem { file } => Ok(Box::new(BufReader::new(fs::File::open(file)?))),
Resource::Raw { raw } => Ok(Box::new(Cursor::new(raw.to_owned().into_bytes()))),
}
}
pub fn export<P: AsRef<Path>>(&self, context: &TemplateContext<'_>, root_dir: P) -> Result<()> {
let key = match self {
Resource::Bundled { bundled } => bundled,
_ => return Ok(()),
};
let target = join_bundled_key(root_dir.as_ref().to_path_buf(), key);
let template = Template::new(from_utf8(self.get()?)?);
if let Some(dir) = target.parent() {
fs::create_dir_all(dir)?;
}
let mut f = fs::File::create(&target)?;
template.render_to(&mut f, context)?;
Ok(())
}
}
struct SourceFiles<'a> {
system_cells: &'a Files,
config: &'a Files,
}
impl<'a> SourceFiles<'a> {
fn new(system_cells: &'a Files, config: &'a Files) -> Self {
SourceFiles {
system_cells,
config,
}
}
fn get(&self, path: &str) -> Result<Cow<'static, [u8]>> {
self.config
.get(path)
.or_else(|_| self.system_cells.get(path))
}
fn read(&self, path: &str) -> Result<Box<dyn Read>> {
self.config
.read(path)
.or_else(|_| self.system_cells.read(path))
}
fn is_available(&self, path: &str) -> bool {
self.config.is_available(path) || self.system_cells.is_available(path)
}
}
fn from_utf8(data: Cow<[u8]>) -> Result<String> {
String::from_utf8(data.to_vec()).map_err(Error::other)
}
fn join_bundled_key(mut root_dir: PathBuf, key: &str) -> PathBuf {
key.split('/')
.for_each(|component| root_dir.push(component));
root_dir
}