index links and translation links separately
This commit is contained in:
parent
6492faa1bc
commit
ca40e71d1b
|
@ -43,6 +43,10 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div id="sidebar" class="sidebar">
|
<div id="sidebar" class="sidebar">
|
||||||
|
<div class="translation-indexes">
|
||||||
|
{{#translation-indexes}}{{/translation-indexes}}
|
||||||
|
</div>
|
||||||
|
|
||||||
{{#toc}}{{/toc}}
|
{{#toc}}{{/toc}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -55,8 +59,8 @@
|
||||||
<i id="theme-toggle" class="fa fa-paint-brush"></i>
|
<i id="theme-toggle" class="fa fa-paint-brush"></i>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="translations">
|
<div class="translation-links">
|
||||||
{{#translations}}{{/translations}}
|
{{#translation-links}}{{/translation-links}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1 class="menu-title">{{ title }}</h1>
|
<h1 class="menu-title">{{ title }}</h1>
|
||||||
|
|
|
@ -54,12 +54,30 @@
|
||||||
img { max-width: 100%; }
|
img { max-width: 100%; }
|
||||||
}
|
}
|
||||||
|
|
||||||
div.translations {
|
div.translation-indexes {
|
||||||
float: left
|
|
||||||
height: 50px
|
|
||||||
ul {
|
ul {
|
||||||
margin: 10px 0 0 0
|
margin: 10px 0 0 0
|
||||||
display: inline-block
|
padding: 0
|
||||||
|
text-align: center
|
||||||
|
list-style-type: none
|
||||||
|
li {
|
||||||
|
display: inline-block
|
||||||
|
padding: 0.2em 0.5em
|
||||||
|
a {
|
||||||
|
text-decoration: none
|
||||||
|
&:hover { text-decoration: underline; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
div.translation-links {
|
||||||
|
float: left
|
||||||
|
height: 50px
|
||||||
|
ul {
|
||||||
|
margin: 10px 0 0 0
|
||||||
|
padding: 0 0 0 1em
|
||||||
|
text-align: left
|
||||||
list-style-type: none
|
list-style-type: none
|
||||||
li {
|
li {
|
||||||
display: inline-block
|
display: inline-block
|
||||||
|
|
|
@ -151,23 +151,40 @@ table thead td {
|
||||||
.content img {
|
.content img {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
div.translations {
|
div.translation-indexes ul {
|
||||||
float: left;
|
|
||||||
height: 50px;
|
|
||||||
}
|
|
||||||
div.translations ul {
|
|
||||||
margin: 10px 0 0 0;
|
margin: 10px 0 0 0;
|
||||||
display: inline-block;
|
padding: 0;
|
||||||
|
text-align: center;
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
}
|
}
|
||||||
div.translations ul li {
|
div.translation-indexes ul li {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: 0.2em 0.5em;
|
padding: 0.2em 0.5em;
|
||||||
}
|
}
|
||||||
div.translations ul li a {
|
div.translation-indexes ul li a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
div.translations ul li a:hover {
|
div.translation-indexes ul li a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
div.translation-links {
|
||||||
|
float: left;
|
||||||
|
height: 50px;
|
||||||
|
}
|
||||||
|
div.translation-links ul {
|
||||||
|
margin: 10px 0 0 0;
|
||||||
|
padding: 0 0 0 1em;
|
||||||
|
text-align: left;
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
|
div.translation-links ul li {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0.2em 0.5em;
|
||||||
|
}
|
||||||
|
div.translation-links ul li a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
div.translation-links ul li a:hover {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
.menu-bar {
|
.menu-bar {
|
||||||
|
|
|
@ -69,6 +69,9 @@ pub struct Chapter {
|
||||||
/// Links to the corresponding translations.
|
/// Links to the corresponding translations.
|
||||||
pub translation_links: Option<Vec<TranslationLink>>,
|
pub translation_links: Option<Vec<TranslationLink>>,
|
||||||
|
|
||||||
|
/// An identifier string that can allow linking translations with different paths.
|
||||||
|
pub translation_id: Option<String>,
|
||||||
|
|
||||||
/// The author of the chapter, or the book.
|
/// The author of the chapter, or the book.
|
||||||
pub authors: Option<Vec<Author>>,
|
pub authors: Option<Vec<Author>>,
|
||||||
|
|
||||||
|
@ -93,6 +96,7 @@ impl Default for Chapter {
|
||||||
src_path: None,
|
src_path: None,
|
||||||
dest_path: None,
|
dest_path: None,
|
||||||
translation_links: None,
|
translation_links: None,
|
||||||
|
translation_id: None,
|
||||||
authors: None,
|
authors: None,
|
||||||
translators: None,
|
translators: None,
|
||||||
description: None,
|
description: None,
|
||||||
|
@ -196,6 +200,10 @@ impl Chapter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(a) = data.get("translation_id") {
|
||||||
|
self.translation_id = Some(a.to_string().replace("\"", ""));
|
||||||
|
}
|
||||||
|
|
||||||
// Author name as a hash key.
|
// Author name as a hash key.
|
||||||
if let Some(a) = data.get("author") {
|
if let Some(a) = data.get("author") {
|
||||||
if let Some(b) = a.as_str() {
|
if let Some(b) = a.as_str() {
|
||||||
|
@ -294,24 +302,35 @@ impl Chapter {
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct TranslationLink {
|
pub struct TranslationLink {
|
||||||
|
/// Language code, such as 'en' or 'fr'.
|
||||||
pub code: String,
|
pub code: String,
|
||||||
pub link: String,
|
/// The `<a href="">` link to the translation. `None` indicates that the
|
||||||
|
/// language is part of the book, but there isn't a translation for this
|
||||||
|
/// chapter.
|
||||||
|
pub link: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for TranslationLink {
|
impl Default for TranslationLink {
|
||||||
fn default() -> TranslationLink {
|
fn default() -> TranslationLink {
|
||||||
TranslationLink {
|
TranslationLink {
|
||||||
code: "".to_string(),
|
code: "--".to_string(),
|
||||||
link: "".to_string(),
|
link: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TranslationLink {
|
impl TranslationLink {
|
||||||
pub fn new(code: String, link: String) -> TranslationLink {
|
pub fn new(code: String) -> TranslationLink {
|
||||||
TranslationLink {
|
TranslationLink {
|
||||||
code: code,
|
code: code,
|
||||||
link: link,
|
link: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_with_link(code: String, link: String) -> TranslationLink {
|
||||||
|
TranslationLink {
|
||||||
|
code: code,
|
||||||
|
link: Some(link),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -323,7 +342,9 @@ impl From<toml::Table> for TranslationLink {
|
||||||
link.code = x.to_string().replace("\"", "");
|
link.code = x.to_string().replace("\"", "");
|
||||||
}
|
}
|
||||||
if let Some(x) = data.get("link") {
|
if let Some(x) = data.get("link") {
|
||||||
link.link = x.to_string().replace("\"", "");
|
link.link = Some(x.to_string().replace("\"", ""));
|
||||||
|
} else {
|
||||||
|
link.link = None;
|
||||||
}
|
}
|
||||||
link
|
link
|
||||||
}
|
}
|
||||||
|
|
|
@ -448,7 +448,7 @@ impl MDBook {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// prepare a Vec of default links to point to the index.html of each translation
|
/// prepare a Vec of default links to point to the index.html of each translation
|
||||||
fn translation_index_links(&mut self) -> Vec<TranslationLink> {
|
pub fn translation_index_links(&self) -> Option<Vec<TranslationLink>> {
|
||||||
let mut default_links: Vec<TranslationLink> = vec![];
|
let mut default_links: Vec<TranslationLink> = vec![];
|
||||||
|
|
||||||
let mut keys = self.translations.keys()
|
let mut keys = self.translations.keys()
|
||||||
|
@ -456,6 +456,11 @@ impl MDBook {
|
||||||
.collect::<Vec<String>>();
|
.collect::<Vec<String>>();
|
||||||
keys.sort();
|
keys.sort();
|
||||||
|
|
||||||
|
if keys.len() < 2 {
|
||||||
|
// There is only one language. No need to display translation links.
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
for key in keys {
|
for key in keys {
|
||||||
let book = self.translations.get(&key).unwrap();
|
let book = self.translations.get(&key).unwrap();
|
||||||
|
|
||||||
|
@ -463,25 +468,37 @@ impl MDBook {
|
||||||
let a = book.config.dest.strip_prefix(&z).unwrap();
|
let a = book.config.dest.strip_prefix(&z).unwrap();
|
||||||
let b = a.join("index.html");
|
let b = a.join("index.html");
|
||||||
let c = b.to_str().unwrap();
|
let c = b.to_str().unwrap();
|
||||||
let link = TranslationLink::new(key, c.to_string());
|
let link = TranslationLink::new_with_link(key, c.to_string());
|
||||||
default_links.push(link);
|
default_links.push(link);
|
||||||
}
|
}
|
||||||
|
|
||||||
default_links
|
Some(default_links)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_translation_links(&mut self, content: &TocContent) -> TocContent {
|
fn set_translation_links(&mut self, content: &TocContent) -> TocContent {
|
||||||
let default_links = self.translation_index_links();
|
let mut final_links: BTreeMap<String, TranslationLink> = BTreeMap::new();
|
||||||
|
|
||||||
let mut newcontent: TocContent = content.clone();
|
let mut newcontent: TocContent = content.clone();
|
||||||
|
|
||||||
|
// Start by adding the code of each language but no links. These will
|
||||||
|
// render as gray <span> tags.
|
||||||
|
for key in self.translations.keys() {
|
||||||
|
final_links.insert(key.clone(), TranslationLink::new(key.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Take the links parsed from the chapter's TOML header
|
||||||
|
|
||||||
match newcontent.chapter.translation_links {
|
match newcontent.chapter.translation_links {
|
||||||
Some(_) => {},
|
Some(links) => {
|
||||||
None => {
|
for i in links.iter() {
|
||||||
newcontent.chapter.translation_links = Some(default_links);
|
final_links.insert(i.clone().code, i.clone());
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
None => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let a: Vec<TranslationLink> = final_links.values().map(|x| x.clone()).collect();
|
||||||
|
newcontent.chapter.translation_links = Some(a);
|
||||||
|
|
||||||
newcontent
|
newcontent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use renderer::html_handlebars::helpers;
|
use renderer::html_handlebars::helpers;
|
||||||
use renderer::Renderer;
|
use renderer::Renderer;
|
||||||
use book::{MDBook, Book};
|
use book::{MDBook, Book};
|
||||||
use book::chapter::Chapter;
|
use book::chapter::{Chapter, TranslationLink};
|
||||||
use book::toc::{TocItem, TocContent};
|
use book::toc::{TocItem, TocContent};
|
||||||
use utils;
|
use utils;
|
||||||
use FILES;
|
use FILES;
|
||||||
|
@ -127,6 +127,8 @@ impl Renderer for HtmlHandlebars {
|
||||||
debug!("[fn]: render");
|
debug!("[fn]: render");
|
||||||
let mut handlebars = Handlebars::new();
|
let mut handlebars = Handlebars::new();
|
||||||
|
|
||||||
|
let translation_indexes = book_project.translation_index_links();
|
||||||
|
|
||||||
// Render the chapters of each book
|
// Render the chapters of each book
|
||||||
for (key, book) in &book_project.translations {
|
for (key, book) in &book_project.translations {
|
||||||
|
|
||||||
|
@ -164,7 +166,8 @@ impl Renderer for HtmlHandlebars {
|
||||||
handlebars.register_helper("toc", Box::new(helpers::toc::RenderToc));
|
handlebars.register_helper("toc", Box::new(helpers::toc::RenderToc));
|
||||||
handlebars.register_helper("previous", Box::new(helpers::navigation::previous));
|
handlebars.register_helper("previous", Box::new(helpers::navigation::previous));
|
||||||
handlebars.register_helper("next", Box::new(helpers::navigation::next));
|
handlebars.register_helper("next", Box::new(helpers::navigation::next));
|
||||||
handlebars.register_helper("translations", Box::new(helpers::translations::TranslationsHelper));
|
handlebars.register_helper("translation-links", Box::new(helpers::translations::TranslationLinksHelper));
|
||||||
|
handlebars.register_helper("translation-indexes", Box::new(helpers::translations::TranslationIndexesHelper));
|
||||||
|
|
||||||
// Check if book's dest directory exists
|
// Check if book's dest directory exists
|
||||||
|
|
||||||
|
@ -196,12 +199,12 @@ impl Renderer for HtmlHandlebars {
|
||||||
// almost the same as process_chapter(), but we have to
|
// almost the same as process_chapter(), but we have to
|
||||||
// manipulate path_to_root in data and rendered_path
|
// manipulate path_to_root in data and rendered_path
|
||||||
|
|
||||||
let mut data = try!(make_data(&book, &chapter, &book_project.livereload_script));
|
let mut data = try!(make_data(&book, &chapter, &translation_indexes, &book_project.livereload_script));
|
||||||
|
|
||||||
data.remove("path_to_root");
|
data.remove("path_to_root");
|
||||||
data.insert("path_to_root".to_owned(), "".to_json());
|
data.insert("path_to_root".to_owned(), "".to_json());
|
||||||
|
|
||||||
// Rendere the handlebars template with the data
|
// Render the handlebars template with the data
|
||||||
debug!("[*]: Render template");
|
debug!("[*]: Render template");
|
||||||
let rendered_content = try!(handlebars.render("page", &data));
|
let rendered_content = try!(handlebars.render("page", &data));
|
||||||
|
|
||||||
|
@ -221,7 +224,7 @@ impl Renderer for HtmlHandlebars {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render a file for every entry in the book
|
// Render a file for every entry in the book
|
||||||
try!(self.process_items(&book.toc, &book, &book_project.livereload_script, &handlebars));
|
try!(self.process_items(&book.toc, &book, &translation_indexes, &book_project.livereload_script, &handlebars));
|
||||||
|
|
||||||
// Write print.html
|
// Write print.html
|
||||||
if let Some(content) = self.collect_print_content_markdown(&book.toc, &book) {
|
if let Some(content) = self.collect_print_content_markdown(&book.toc, &book) {
|
||||||
|
@ -231,7 +234,7 @@ impl Renderer for HtmlHandlebars {
|
||||||
chapter.set_dest_path(PathBuf::from("print.html"));
|
chapter.set_dest_path(PathBuf::from("print.html"));
|
||||||
chapter.content = Some(content);
|
chapter.content = Some(content);
|
||||||
|
|
||||||
try!(self.process_chapter(&chapter, &book, &None, &handlebars));
|
try!(self.process_chapter(&chapter, &book, &None, &None, &handlebars));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,6 +247,7 @@ impl HtmlHandlebars {
|
||||||
fn process_items(&self,
|
fn process_items(&self,
|
||||||
items: &Vec<TocItem>,
|
items: &Vec<TocItem>,
|
||||||
book: &Book,
|
book: &Book,
|
||||||
|
translation_indexes: &Option<Vec<TranslationLink>>,
|
||||||
livereload_script: &Option<String>,
|
livereload_script: &Option<String>,
|
||||||
handlebars: &Handlebars)
|
handlebars: &Handlebars)
|
||||||
-> Result<(), Box<Error>> {
|
-> Result<(), Box<Error>> {
|
||||||
|
@ -254,11 +258,11 @@ impl HtmlHandlebars {
|
||||||
TocItem::Unnumbered(ref i) |
|
TocItem::Unnumbered(ref i) |
|
||||||
TocItem::Unlisted(ref i) => {
|
TocItem::Unlisted(ref i) => {
|
||||||
if let Some(_) = i.chapter.get_dest_path() {
|
if let Some(_) = i.chapter.get_dest_path() {
|
||||||
try!(self.process_chapter(&i.chapter, book, livereload_script, handlebars));
|
try!(self.process_chapter(&i.chapter, book, translation_indexes, livereload_script, handlebars));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(ref subs) = i.sub_items {
|
if let Some(ref subs) = i.sub_items {
|
||||||
try!(self.process_items(&subs, book, livereload_script, handlebars));
|
try!(self.process_items(&subs, book, translation_indexes, livereload_script, handlebars));
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
|
@ -303,11 +307,12 @@ impl HtmlHandlebars {
|
||||||
fn process_chapter(&self,
|
fn process_chapter(&self,
|
||||||
chapter: &Chapter,
|
chapter: &Chapter,
|
||||||
book: &Book,
|
book: &Book,
|
||||||
|
translation_indexes: &Option<Vec<TranslationLink>>,
|
||||||
livereload_script: &Option<String>,
|
livereload_script: &Option<String>,
|
||||||
handlebars: &Handlebars)
|
handlebars: &Handlebars)
|
||||||
-> Result<(), Box<Error>> {
|
-> Result<(), Box<Error>> {
|
||||||
|
|
||||||
let data = try!(make_data(book, chapter, livereload_script));
|
let data = try!(make_data(book, chapter, translation_indexes, livereload_script));
|
||||||
|
|
||||||
// Render the handlebars template with the data
|
// Render the handlebars template with the data
|
||||||
debug!("[*]: Render template");
|
debug!("[*]: Render template");
|
||||||
|
@ -339,6 +344,7 @@ impl HtmlHandlebars {
|
||||||
|
|
||||||
fn make_data(book: &Book,
|
fn make_data(book: &Book,
|
||||||
chapter: &Chapter,
|
chapter: &Chapter,
|
||||||
|
translation_indexes: &Option<Vec<TranslationLink>>,
|
||||||
livereload_script: &Option<String>)
|
livereload_script: &Option<String>)
|
||||||
-> Result<serde_json::Map<String, serde_json::Value>, Box<Error>> {
|
-> Result<serde_json::Map<String, serde_json::Value>, Box<Error>> {
|
||||||
|
|
||||||
|
@ -402,8 +408,12 @@ fn make_data(book: &Book,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(ref links) = *translation_indexes {
|
||||||
|
data.insert("translation-indexes".to_owned(), links.to_json());
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(ref links) = chapter.translation_links {
|
if let Some(ref links) = chapter.translation_links {
|
||||||
data.insert("translation_links".to_owned(), links.to_json());
|
data.insert("translation-links".to_owned(), links.to_json());
|
||||||
}
|
}
|
||||||
|
|
||||||
let chapters = try!(items_to_chapters(&book.toc, &book));
|
let chapters = try!(items_to_chapters(&book.toc, &book));
|
||||||
|
|
|
@ -5,19 +5,55 @@ use serde_json;
|
||||||
use handlebars::{Handlebars, HelperDef, RenderError, RenderContext, Helper, Context};
|
use handlebars::{Handlebars, HelperDef, RenderError, RenderContext, Helper, Context};
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct TranslationsHelper;
|
pub struct TranslationLinksHelper;
|
||||||
|
|
||||||
impl HelperDef for TranslationsHelper {
|
impl HelperDef for TranslationLinksHelper {
|
||||||
fn call(&self, c: &Context, _h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
|
fn call(&self, c: &Context, _h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
|
||||||
|
|
||||||
let translation_links = c.navigate(rc.get_path(), &VecDeque::new(), "translation_links");
|
let translation_links = c.navigate(rc.get_path(), &VecDeque::new(), "translation-links");
|
||||||
let decoded: Vec<BTreeMap<String, String>> = serde_json::from_str(&translation_links.to_string()).unwrap();
|
let decoded: Vec<BTreeMap<String, String>> = serde_json::from_str(&translation_links.to_string()).unwrap();
|
||||||
|
|
||||||
if decoded.len() == 0 {
|
if decoded.len() == 0 {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
try!(rc.writer.write("<ul class=\"translations\">".as_bytes()));
|
try!(rc.writer.write("<ul class=\"translation-links\">".as_bytes()));
|
||||||
|
|
||||||
|
for item in decoded {
|
||||||
|
let empty = "".to_string();
|
||||||
|
let link = item.get("link").unwrap_or(&empty);
|
||||||
|
let code = item.get("code").unwrap_or(&empty);
|
||||||
|
|
||||||
|
// None value in the link becomes an empty string
|
||||||
|
let text = if link.len() == 0 {
|
||||||
|
format!("<li><span>{}</span></li>", code)
|
||||||
|
} else {
|
||||||
|
format!("<li><a href=\"{}\">{}</a></li>", link, code)
|
||||||
|
};
|
||||||
|
|
||||||
|
try!(rc.writer.write(text.as_bytes()));
|
||||||
|
}
|
||||||
|
|
||||||
|
try!(rc.writer.write("</ul>".as_bytes()));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct TranslationIndexesHelper;
|
||||||
|
|
||||||
|
impl HelperDef for TranslationIndexesHelper {
|
||||||
|
fn call(&self, c: &Context, _h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
|
||||||
|
|
||||||
|
let translation_indexes = c.navigate(rc.get_path(), &VecDeque::new(), "translation-indexes");
|
||||||
|
let decoded: Vec<BTreeMap<String, String>> = serde_json::from_str(&translation_indexes.to_string()).unwrap();
|
||||||
|
|
||||||
|
if decoded.len() == 0 {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
try!(rc.writer.write("<ul class=\"translation-indexes\">".as_bytes()));
|
||||||
|
|
||||||
for item in decoded {
|
for item in decoded {
|
||||||
let empty = "".to_string();
|
let empty = "".to_string();
|
||||||
|
|
Loading…
Reference in New Issue