@@ -1,5 +1,14 @@ | |||
# Changelog | |||
## 0.7.0 (2019-04-28) | |||
### Breaking | |||
- Remove --base-path option, it broke `serve` on Windows and wasn't properly tested | |||
### Other | |||
- Strip wrapping whitespaces from shortcodes | |||
- Sort sitemap elements by permalink` | |||
## 0.6.0 (2019-03-25) | |||
### Breaking | |||
@@ -1,6 +1,6 @@ | |||
[package] | |||
name = "zola" | |||
version = "0.6.0" | |||
version = "0.7.0" | |||
authors = ["Vincent Prouillet <prouillet.vincent@gmail.com>"] | |||
license = "MIT" | |||
readme = "README.md" | |||
@@ -1,4 +1,5 @@ | |||
# zola (né Gutenberg) | |||
[![Build Status](https://travis-ci.com/getzola/zola.svg?branch=master)](https://travis-ci.com/getzola/zola) | |||
[![Build status](https://ci.appveyor.com/api/projects/status/i0ufvx2sdm2cmawo/branch/master?svg=true)](https://ci.appveyor.com/project/Keats/zola/branch/master) | |||
@@ -9,32 +10,31 @@ in the `docs/content` folder of the repository and the community can use [its fo | |||
## Comparisons with other static site generators | |||
| | Zola | Cobalt | Hugo | Pelican | | |||
|:-------------------------------:|:---------:|--------|------|---------| | |||
| Single binary | ✔ | ✔ | ✔ | ✕ | | |||
| Language | Rust | Rust | Go | Python | | |||
| Syntax highlighting | ✔ | ✔ | ✔ | ✔ | | |||
| Sass compilation | ✔ | ✔ | ✔ | ✔ | | |||
| Assets co-location | ✔ | ✔ | ✔ | ✔ | | |||
| Multilingual site | ✔ | ✕ | ✔ | ✔ | | |||
| Image processing | ✔ | ✕ | ✔ | ✔ | | |||
| Sane & powerful template engine | ✔ | ~ | ~ | ✔ | | |||
| Themes | ✔ | ✕ | ✔ | ✔ | | |||
| Shortcodes | ✔ | ✕ | ✔ | ✔ | | |||
| Internal links | ✔ | ✕ | ✔ | ✔ | | |||
| Link checker | ✔ | ✕ | ✕ | ✔ | | |||
| Table of contents | ✔ | ✕ | ✔ | ✔ | | |||
| Automatic header anchors | ✔ | ✕ | ✔ | ✔ | | |||
| Aliases | ✔ | ✕ | ✔ | ✔ | | |||
| Pagination | ✔ | ✕ | ✔ | ✔ | | |||
| Custom taxonomies | ✔ | ✕ | ✔ | ✕ | | |||
| Search | ✔ | ✕ | ✕ | ✔ | | |||
| Data files | ✔ | ✔ | ✔ | ✕ | | |||
| LiveReload | ✔ | ✕ | ✔ | ✔ | | |||
| Netlify support | ~ | ✕ | ✔ | ✕ | | |||
| Breadcrumbs | ✔ | ✕ | ✕ | ✔ | | |||
| Custom output formats | ✕ | ✕ | ✔ | ? | | |||
| | Zola | Cobalt | Hugo | Pelican | | |||
|:--------------------------------|:--------------------:|:--------------------:|:--------------------:|:--------------------:| | |||
| Single binary | ![yes](./is-yes.svg) | ![yes](./is-yes.svg) | ![yes](./is-yes.svg) | ![no](./is-no.svg) | | |||
| Language | Rust | Rust | Go | Python | | |||
| Syntax highlighting | ![yes](./is-yes.svg) | ![yes](./is-yes.svg) | ![yes](./is-yes.svg) | ![yes](./is-yes.svg) | | |||
| Sass compilation | ![yes](./is-yes.svg) | ![yes](./is-yes.svg) | ![yes](./is-yes.svg) | ![yes](./is-yes.svg) | | |||
| Assets co-location | ![yes](./is-yes.svg) | ![yes](./is-yes.svg) | ![yes](./is-yes.svg) | ![yes](./is-yes.svg) | | |||
| Multilingual site | ![yes](./is-yes.svg) | ![no](./is-no.svg) | ![yes](./is-yes.svg) | ![yes](./is-yes.svg) | | |||
| Image processing | ![yes](./is-yes.svg) | ![no](./is-no.svg) | ![yes](./is-yes.svg) | ![yes](./is-yes.svg) | | |||
| Sane & powerful template engine | ![yes](./is-yes.svg) | ![ehh](./is-ehh.svg) | ![ehh](./is-ehh.svg) | ![yes](./is-yes.svg) | | |||
| Themes | ![yes](./is-yes.svg) | ![no](./is-no.svg) | ![yes](./is-yes.svg) | ![yes](./is-yes.svg) | | |||
| Shortcodes | ![yes](./is-yes.svg) | ![no](./is-no.svg) | ![yes](./is-yes.svg) | ![yes](./is-yes.svg) | | |||
| Internal links | ![yes](./is-yes.svg) | ![no](./is-no.svg) | ![yes](./is-yes.svg) | ![yes](./is-yes.svg) | | |||
| Link checker | ![yes](./is-yes.svg) | ![no](./is-no.svg) | ![no](./is-no.svg) | ![yes](./is-yes.svg) | | |||
| Table of contents | ![yes](./is-yes.svg) | ![no](./is-no.svg) | ![yes](./is-yes.svg) | ![yes](./is-yes.svg) | | |||
| Automatic header anchors | ![yes](./is-yes.svg) | ![no](./is-no.svg) | ![yes](./is-yes.svg) | ![yes](./is-yes.svg) | | |||
| Aliases | ![yes](./is-yes.svg) | ![no](./is-no.svg) | ![yes](./is-yes.svg) | ![yes](./is-yes.svg) | | |||
| Pagination | ![yes](./is-yes.svg) | ![no](./is-no.svg) | ![yes](./is-yes.svg) | ![yes](./is-yes.svg) | | |||
| Custom taxonomies | ![yes](./is-yes.svg) | ![no](./is-no.svg) | ![yes](./is-yes.svg) | ![no](./is-no.svg) | | |||
| Search | ![yes](./is-yes.svg) | ![no](./is-no.svg) | ![no](./is-no.svg) | ![yes](./is-yes.svg) | | |||
| Data files | ![yes](./is-yes.svg) | ![yes](./is-yes.svg) | ![yes](./is-yes.svg) | ![no](./is-no.svg) | | |||
| LiveReload | ![yes](./is-yes.svg) | ![no](./is-no.svg) | ![yes](./is-yes.svg) | ![yes](./is-yes.svg) | | |||
| Netlify support | ![ehh](./is-ehh.svg) | ![no](./is-no.svg) | ![yes](./is-yes.svg) | ![no](./is-no.svg) | | |||
| Breadcrumbs | ![yes](./is-yes.svg) | ![no](./is-no.svg) | ![no](./is-no.svg) | ![yes](./is-yes.svg) | | |||
| Custom output formats | ![no](./is-no.svg) | ![no](./is-no.svg) | ![yes](./is-yes.svg) | ![no](./is-no.svg) | | |||
### Supported content formats | |||
@@ -45,11 +45,10 @@ in the `docs/content` folder of the repository and the community can use [its fo | |||
### Template engine explanation | |||
Cobalt gets `~` as, while based on [Liquid](https://shopify.github.io/liquid/), the Rust library doesn't implement all its features but there is no documentation on what is and isn't implemented. The errors are also cryptic. Liquid itself is not powerful enough to do some of things you can do in Jinja2, Go templates or Tera. | |||
Cobalt gets ![ehh](./is-ehh.svg) because, while based on [Liquid](https://shopify.github.io/liquid/), the Rust library doesn't implement all its features and there is no documentation on what is and isn't implemented; the errors are cryptic; and Liquid itself is not powerful enough to do some of things you can do in Jinja2, Go templates, or Tera. | |||
Hugo gets `~`. It is probably the most powerful template engine in the list after Jinja2 (hard to beat python code in templates) but personally drives me insane, to the point of writing my own template engine and static site generator. Yes, this is a bit biased. | |||
Hugo gets ![ehh](./is-ehh.svg) because while it is probably the most powerful template engine in the list, after Jinja2, it personally drives me insane, to the point of writing my own template engine and static site generator. Yes, this is a bit biased. | |||
### Pelican notes | |||
Many features of Pelican are coming from plugins, which might be tricky | |||
to use because of version mismatch or lacking documentation. Netlify supports Python | |||
and Pipenv but you still need to install your dependencies manually. | |||
Many features of Pelican are coming from plugins, which might be tricky to use because of version mismatch or lacking documentation. Netlify supports Python and Pipenv but you still need to install your dependencies manually. |
@@ -16,6 +16,7 @@ pub struct ContentParser; | |||
lazy_static! { | |||
static ref MULTIPLE_NEWLINE_RE: Regex = Regex::new(r"\n\s*\n").unwrap(); | |||
static ref OUTER_NEWLINE_RE: Regex = Regex::new(r"^\s*\n|\n\s*$").unwrap(); | |||
} | |||
fn replace_string_markers(input: &str) -> String { | |||
@@ -122,6 +123,8 @@ fn render_shortcode( | |||
// at indentation, making the output a code block. | |||
let res = MULTIPLE_NEWLINE_RE.replace_all(&res, "\n"); | |||
let res = OUTER_NEWLINE_RE.replace_all(&res, ""); | |||
Ok(res.to_string()) | |||
} | |||
@@ -411,4 +414,20 @@ Some body {{ hello() }}{%/* end */%}"#, | |||
let res = render_shortcodes("Body\n {% youtube() %}\nHello \n World{% end %}", &tera); | |||
assert_eq!(res, "Body\n Hello \n World"); | |||
} | |||
#[test] | |||
fn outer_newlines_removed_from_shortcodes_with_body() { | |||
let mut tera = Tera::default(); | |||
tera.add_raw_template("shortcodes/youtube.html", " \n {{body}} \n ").unwrap(); | |||
let res = render_shortcodes("\n{% youtube() %} \n content \n {% end %}\n", &tera); | |||
assert_eq!(res, "\n content \n"); | |||
} | |||
#[test] | |||
fn outer_newlines_removed_from_inline_shortcodes() { | |||
let mut tera = Tera::default(); | |||
tera.add_raw_template("shortcodes/youtube.html", " \n Hello, Zola. \n ").unwrap(); | |||
let res = render_shortcodes("\n{{ youtube() }}\n", &tera); | |||
assert_eq!(res, "\n Hello, Zola. \n"); | |||
} | |||
} |
@@ -66,7 +66,7 @@ pub struct Site { | |||
impl Site { | |||
/// Parse a site at the given path. Defaults to the current dir | |||
/// Passing in a path is possible using the `base-path` command line build option | |||
/// Passing in a path is only used in tests | |||
pub fn new<P: AsRef<Path>>(path: P, config_file: &str) -> Result<Site> { | |||
let path = path.as_ref(); | |||
let mut config = get_config(path, config_file); | |||
@@ -777,11 +777,15 @@ impl Site { | |||
ensure_directory_exists(&self.output_path)?; | |||
let library = self.library.read().unwrap(); | |||
let all_sitemap_entries = sitemap::find_entries( | |||
&library, | |||
&self.taxonomies[..], | |||
&self.config, | |||
); | |||
let all_sitemap_entries = { | |||
let mut all_sitemap_entries = sitemap::find_entries( | |||
&library, | |||
&self.taxonomies[..], | |||
&self.config, | |||
); | |||
all_sitemap_entries.sort(); | |||
all_sitemap_entries | |||
}; | |||
let sitemap_limit = 30000; | |||
if all_sitemap_entries.len() < sitemap_limit { | |||
@@ -5,6 +5,7 @@ use std::collections::{HashSet}; | |||
use tera::{Map, Value}; | |||
use config::{Config}; | |||
use library::{Library, Taxonomy}; | |||
use std::cmp::Ordering; | |||
/// The sitemap only needs links, potentially date and extra for pages in case of updates | |||
/// for examples so we trim down all entries to only that | |||
@@ -39,6 +40,18 @@ impl<'a> SitemapEntry<'a> { | |||
} | |||
} | |||
impl<'a> PartialOrd for SitemapEntry<'a> { | |||
fn partial_cmp(&self, other: &SitemapEntry) -> Option<Ordering> { | |||
Some(self.permalink.as_ref().cmp(other.permalink.as_ref())) | |||
} | |||
} | |||
impl<'a> Ord for SitemapEntry<'a> { | |||
fn cmp(&self, other: &SitemapEntry) -> Ordering { | |||
self.permalink.as_ref().cmp(other.permalink.as_ref()) | |||
} | |||
} | |||
/// Finds out all the links to put in a sitemap from the pages/sections/taxonomies | |||
/// There are no duplicate permalinks in the output vec | |||
pub fn find_entries<'a>(library: &'a Library, taxonomies: &'a [Taxonomy], config: &'a Config) -> Vec<SitemapEntry<'a>> { | |||
@@ -1,4 +1,4 @@ | |||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> | |||
<urlset xmlns="https://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"> | |||
{% for sitemap_entry in entries %} | |||
<url> | |||
<loc>{{ sitemap_entry.permalink | safe }}</loc> | |||
@@ -1,4 +1,4 @@ | |||
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> | |||
<sitemapindex xmlns="https://www.sitemaps.org/schemas/sitemap/0.9/siteindex.xsd"> | |||
{% for sitemap in sitemaps %} | |||
<sitemap> | |||
<loc>{{ sitemap }}</loc> | |||
@@ -431,6 +431,7 @@ mod tests { | |||
json!({ | |||
"category": { | |||
"date": "1979-05-27T07:32:00Z", | |||
"lt1": "07:32:00", | |||
"key": "value" | |||
}, | |||
}) | |||
@@ -1,3 +1,4 @@ | |||
[category] | |||
key = "value" | |||
date = 1979-05-27T07:32:00Z | |||
date = 1979-05-27T07:32:00Z | |||
lt1 = 07:32:00 |
@@ -36,14 +36,6 @@ $ zola build --base-url $DEPLOY_URL | |||
This is useful for example when you want to deploy previews of a site to a dynamic URL, such as Netlify | |||
deploy previews. | |||
You can override the default `base_path` by passing a new directory to the `base-path` flag. If no `base-path` flag | |||
is provided, zola defaults to your current working directory. This is useful if your zola project is located in | |||
a different directory from where you're executing zola from. | |||
```bash | |||
$ zola build --base-path /path/to/zola/site | |||
``` | |||
You can override the default output directory 'public' by passing a other value to the `output-dir` flag. | |||
```bash | |||
@@ -75,7 +67,6 @@ $ zola serve --interface 0.0.0.0 | |||
$ zola serve --interface 0.0.0.0 --port 2000 | |||
$ zola serve --interface 0.0.0.0 --base-url 127.0.0.1 | |||
$ zola serve --interface 0.0.0.0 --port 2000 --output-dir www/public | |||
$ zola serve --interface 0.0.0.0 --port 2000 --base-path mysite/ --output-dir mysite/www/public | |||
$ zola serve --watch-only | |||
``` | |||
@@ -0,0 +1,3 @@ | |||
<svg xmlns="http://www.w3.org/2000/svg" style="fill:#dbab09" width="16" height="16" viewBox="0 0 24 24"> | |||
<circle cx="12" cy="12" r="8"/> | |||
</svg> |
@@ -0,0 +1,3 @@ | |||
<svg xmlns="http://www.w3.org/2000/svg" style="fill:#cb2431" width="16" height="16" viewBox="0 0 24 24"> | |||
<path d="M23 20.168l-8.185-8.187 8.185-8.174-2.832-2.807-8.182 8.179-8.176-8.179-2.81 2.81 8.186 8.196-8.186 8.184 2.81 2.81 8.203-8.192 8.18 8.192z"/> | |||
</svg> |
@@ -0,0 +1,3 @@ | |||
<svg xmlns="http://www.w3.org/2000/svg" style="fill:#28a745" width="16" height="16" viewBox="0 0 24 24"> | |||
<path d="M9 21.035l-9-8.638 2.791-2.87 6.156 5.874 12.21-12.436 2.843 2.817z"/> | |||
</svg> |
@@ -30,11 +30,6 @@ pub fn build_cli() -> App<'static, 'static> { | |||
.long("base-url") | |||
.takes_value(true) | |||
.help("Force the base URL to be that value (default to the one in config.toml)"), | |||
Arg::with_name("base_path") | |||
.short("b") | |||
.long("base-path") | |||
.takes_value(true) | |||
.help("Force the base site path to a certain directory [default: the current working directory]"), | |||
Arg::with_name("output_dir") | |||
.short("o") | |||
.long("output-dir") | |||
@@ -61,11 +56,6 @@ pub fn build_cli() -> App<'static, 'static> { | |||
.default_value("public") | |||
.takes_value(true) | |||
.help("Outputs the generated site in the given path"), | |||
Arg::with_name("base_path") | |||
.short("b") | |||
.long("base-path") | |||
.takes_value(true) | |||
.help("Force the base site path to a certain directory [default: the current working directory]"), | |||
Arg::with_name("base_url") | |||
.short("u") | |||
.long("base-url") | |||
@@ -1,19 +1,12 @@ | |||
use std::env; | |||
use std::path::PathBuf; | |||
use errors::Result; | |||
use site::Site; | |||
use console; | |||
pub fn build( | |||
config_file: &str, | |||
base_path: Option<&str>, | |||
base_url: Option<&str>, | |||
output_dir: &str, | |||
) -> Result<()> { | |||
let bp = base_path.map(PathBuf::from).unwrap_or(env::current_dir().unwrap()); | |||
let mut site = Site::new(bp, config_file)?; | |||
pub fn build(config_file: &str, base_url: Option<&str>, output_dir: &str) -> Result<()> { | |||
let mut site = Site::new(env::current_dir().unwrap(), config_file)?; | |||
site.set_output_path(output_dir); | |||
if let Some(b) = base_url { | |||
site.set_base_url(b.to_string()); | |||
@@ -86,8 +86,8 @@ impl<S> Middleware<S> for NotFoundHandler { | |||
} | |||
} | |||
fn livereload_handler(_: &HttpRequest) -> &'static str { | |||
LIVE_RELOAD | |||
fn livereload_handler(_: &HttpRequest) -> HttpResponse { | |||
HttpResponse::Ok().content_type("text/javascript").body(LIVE_RELOAD) | |||
} | |||
fn rebuild_done_handling(broadcaster: &Option<Sender>, res: Result<()>, reload_path: &str) { | |||
@@ -114,15 +114,14 @@ fn rebuild_done_handling(broadcaster: &Option<Sender>, res: Result<()>, reload_p | |||
} | |||
} | |||
fn create_new_site<P: AsRef<Path>>( | |||
fn create_new_site( | |||
interface: &str, | |||
port: u16, | |||
output_dir: &str, | |||
base_path: P, | |||
base_url: &str, | |||
config_file: &str, | |||
) -> Result<(Site, String)> { | |||
let mut site = Site::new(base_path, config_file)?; | |||
let mut site = Site::new(env::current_dir().unwrap(), config_file)?; | |||
let base_address = format!("{}:{}", base_url, port); | |||
let address = format!("{}:{}", interface, port); | |||
@@ -167,15 +166,12 @@ pub fn serve( | |||
interface: &str, | |||
port: u16, | |||
output_dir: &str, | |||
base_path: Option<&str>, | |||
base_url: &str, | |||
config_file: &str, | |||
watch_only: bool, | |||
) -> Result<()> { | |||
let start = Instant::now(); | |||
let bp = base_path.map(PathBuf::from).unwrap_or(env::current_dir().unwrap()); | |||
let (mut site, address) = | |||
create_new_site(interface, port, output_dir, bp.clone(), base_url, config_file)?; | |||
let (mut site, address) = create_new_site(interface, port, output_dir, base_url, config_file)?; | |||
console::report_elapsed_time(start); | |||
// Setup watchers | |||
@@ -184,28 +180,28 @@ pub fn serve( | |||
let (tx, rx) = channel(); | |||
let mut watcher = watcher(tx, Duration::from_secs(1)).unwrap(); | |||
watcher | |||
.watch(bp.join("content/"), RecursiveMode::Recursive) | |||
.watch("content/", RecursiveMode::Recursive) | |||
.map_err(|e| ZolaError::chain("Can't watch the `content` folder. Does it exist?", e))?; | |||
watcher | |||
.watch(bp.join(config_file), RecursiveMode::Recursive) | |||
.watch(config_file, RecursiveMode::Recursive) | |||
.map_err(|e| ZolaError::chain("Can't watch the `config` file. Does it exist?", e))?; | |||
if bp.join("static").exists() { | |||
if Path::new("static").exists() { | |||
watching_static = true; | |||
watcher | |||
.watch(bp.join("static/"), RecursiveMode::Recursive) | |||
.watch("static/", RecursiveMode::Recursive) | |||
.map_err(|e| ZolaError::chain("Can't watch the `static` folder.", e))?; | |||
} | |||
if bp.join("templates").exists() { | |||
if Path::new("templates").exists() { | |||
watching_templates = true; | |||
watcher | |||
.watch(bp.join("templates/"), RecursiveMode::Recursive) | |||
.watch("templates/", RecursiveMode::Recursive) | |||
.map_err(|e| ZolaError::chain("Can't watch the `templates` folder.", e))?; | |||
} | |||
// Sass support is optional so don't make it an error to no have a sass folder | |||
let _ = watcher.watch(bp.join("sass/"), RecursiveMode::Recursive); | |||
let _ = watcher.watch("sass/", RecursiveMode::Recursive); | |||
let ws_address = format!("{}:{}", interface, site.live_reload.unwrap()); | |||
let output_path = Path::new(output_dir).to_path_buf(); | |||
@@ -262,6 +258,8 @@ pub fn serve( | |||
None | |||
}; | |||
let pwd = env::current_dir().unwrap(); | |||
let mut watchers = vec!["content", "config.toml"]; | |||
if watching_static { | |||
watchers.push("static"); | |||
@@ -275,7 +273,7 @@ pub fn serve( | |||
println!( | |||
"Listening for changes in {}{}{{{}}}", | |||
bp.display(), | |||
pwd.display(), | |||
MAIN_SEPARATOR, | |||
watchers.join(", ") | |||
); | |||
@@ -351,8 +349,7 @@ pub fn serve( | |||
if path.is_file() && is_temp_file(&path) { | |||
continue; | |||
} | |||
let (change_kind, partial_path) = | |||
detect_change_kind(&bp.canonicalize().unwrap(), &path); | |||
let (change_kind, partial_path) = detect_change_kind(&pwd, &path); | |||
// We only care about changes in non-empty folders | |||
if path.is_dir() && is_folder_empty(&path) { | |||
@@ -384,7 +381,6 @@ pub fn serve( | |||
interface, | |||
port, | |||
output_dir, | |||
bp.clone(), | |||
base_url, | |||
config_file, | |||
) | |||
@@ -405,7 +401,7 @@ pub fn serve( | |||
); | |||
let start = Instant::now(); | |||
match detect_change_kind(&bp.canonicalize().unwrap(), &path) { | |||
match detect_change_kind(&pwd, &path) { | |||
(ChangeKind::Content, _) => { | |||
console::info(&format!("-> Content changed {}", path.display())); | |||
// Force refresh | |||
@@ -424,7 +420,6 @@ pub fn serve( | |||
interface, | |||
port, | |||
output_dir, | |||
bp.clone(), | |||
base_url, | |||
config_file, | |||
) | |||
@@ -46,12 +46,7 @@ fn main() { | |||
console::info("Building site..."); | |||
let start = Instant::now(); | |||
let output_dir = matches.value_of("output_dir").unwrap(); | |||
match cmd::build( | |||
config_file, | |||
matches.value_of("base_path"), | |||
matches.value_of("base_url"), | |||
output_dir, | |||
) { | |||
match cmd::build(config_file, matches.value_of("base_url"), output_dir) { | |||
Ok(()) => console::report_elapsed_time(start), | |||
Err(e) => { | |||
console::unravel_errors("Failed to build the site", &e); | |||
@@ -84,18 +79,9 @@ fn main() { | |||
} | |||
let watch_only = matches.is_present("watch_only"); | |||
let output_dir = matches.value_of("output_dir").unwrap(); | |||
let base_path = matches.value_of("base_path"); | |||
let base_url = matches.value_of("base_url").unwrap(); | |||
console::info("Building site..."); | |||
match cmd::serve( | |||
interface, | |||
port, | |||
output_dir, | |||
base_path, | |||
base_url, | |||
config_file, | |||
watch_only, | |||
) { | |||
match cmd::serve(interface, port, output_dir, base_url, config_file, watch_only) { | |||
Ok(()) => (), | |||
Err(e) => { | |||
console::unravel_errors("", &e); | |||