diff --git a/src/cli/translate/extract_svgs.rs b/src/cli/translate/extract_svgs.rs deleted file mode 100644 index 8b1378917..000000000 --- a/src/cli/translate/extract_svgs.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/cli/translate/mod.rs b/src/cli/translate/mod.rs index 81b5092ed..a717d632a 100644 --- a/src/cli/translate/mod.rs +++ b/src/cli/translate/mod.rs @@ -1,15 +1,14 @@ +use anyhow::Result; use html_parser::Dom; +use html_parser::Element; use html_parser::Node; -use std::fmt::Write; +#[allow(unused_imports)] +use std::fmt::{Display, Formatter}; use std::io::Read; use std::path::PathBuf; use std::process::exit; use structopt::StructOpt; -pub mod extract_svgs; -pub mod to_component; -pub mod translate; - /// Build the Rust WASM app and all of its assets. #[derive(Clone, Debug, StructOpt)] #[structopt(name = "translate")] @@ -21,11 +20,7 @@ pub struct Translate { /// Input file #[structopt(short, long)] - pub source: Option, - - /// Input file - #[structopt(short, long, parse(from_os_str))] - pub input: Option, + pub file: Option, /// Output file, stdout if not present #[structopt(parse(from_os_str))] @@ -35,23 +30,17 @@ pub struct Translate { impl Translate { pub fn translate(self) -> anyhow::Result<()> { let Translate { - component: as_component, - input, + component, output, - source, + file, } = self; - let contents; - let temp = input.map(|f| { - std::fs::read_to_string(&f).unwrap_or_else(|e| { + let contents = match file { + Some(input) => std::fs::read_to_string(&input).unwrap_or_else(|e| { log::error!("Cloud not read input file: {}.", e); exit(0); - }) - }); - if let Some(..) = temp { - if let Some(s) = source { - contents = s; - } else { + }), + None => { if atty::is(atty::Stream::Stdin) { log::error!("No input file, source, or stdin to translate from."); exit(0); @@ -60,28 +49,21 @@ impl Translate { let mut buffer = String::new(); std::io::stdin().read_to_string(&mut buffer).unwrap(); - contents = buffer.trim().to_string(); + buffer.trim().to_string() } - } else { - contents = temp.unwrap(); - } + }; - // parse the input as html and prepare the output - let dom = Dom::parse(&contents)?; - let mut out_buf = String::new(); - - if as_component { - writeln!(out_buf, "use dioxus::prelude::*;")?; - writeln!(out_buf, "fn component(cx: Scope) -> Element {{")?; - writeln!(out_buf, " cx.render(rsx!(")?; - } - for child in &dom.children { - render_child(&mut out_buf, child, if as_component { 2 } else { 0 })?; - } - if as_component { - writeln!(out_buf, " ))")?; - writeln!(out_buf, "}}")?; - } + let out_buf = match component { + true => format!("{}", convert_html_to_component(&contents).unwrap()), + false => { + let mut buf = String::new(); + let dom = Dom::parse(&contents).unwrap(); + for child in &dom.children { + simple_render_child(&mut buf, child, 0)?; + } + buf + } + }; if let Some(output) = output { std::fs::write(&output, out_buf)?; @@ -93,7 +75,8 @@ impl Translate { } } -fn render_child(f: &mut impl Write, child: &Node, il: u32) -> std::fmt::Result { +/// render without handling svgs or anything +fn simple_render_child(f: &mut impl std::fmt::Write, child: &Node, il: u32) -> std::fmt::Result { write_tabs(f, il)?; match child { Node::Text(t) => writeln!(f, "\"{}\"", t)?, @@ -131,9 +114,8 @@ fn render_child(f: &mut impl Write, child: &Node, il: u32) -> std::fmt::Result { None => writeln!(f, "{}: \"\",", new_name)?, } } else { - match name.as_str() { - "for" | "async" | "type" | "as" => write!(f, "r#")?, - _ => {} + if matches!(name.as_str(), "for" | "async" | "type" | "as") { + write!(f, "r#")? } match value { @@ -145,7 +127,7 @@ fn render_child(f: &mut impl Write, child: &Node, il: u32) -> std::fmt::Result { // now the children for child in &el.children { - render_child(f, child, il + 1)?; + simple_render_child(f, child, il + 1)?; } // close the tag @@ -156,9 +138,171 @@ fn render_child(f: &mut impl Write, child: &Node, il: u32) -> std::fmt::Result { Ok(()) } -fn write_tabs(f: &mut impl Write, num: u32) -> std::fmt::Result { +pub fn convert_html_to_component(html: &str) -> Result { + Ok(ComponentRenderer { + dom: Dom::parse(html)?, + icon_index: 0, + }) +} + +#[allow(dead_code)] +pub struct ComponentRenderer { + dom: Dom, + icon_index: usize, +} + +impl Display for ComponentRenderer { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + writeln!( + f, + r##" +fn component(cx: Scope) -> Element {{ + cx.render(rsx!("## + )?; + let mut svg_nodes = vec![]; + + let mut svg_idx = 0; + for child in &self.dom.children { + render_child(f, child, 2, &mut svg_nodes, true, &mut svg_idx)?; + } + write!( + f, + r##" )) +}}"## + )?; + + if svg_idx == 0 { + return Ok(()); + } + + writeln!(f, "\n\nmod icons {{")?; + + let mut id = 0; + while let Some(svg) = svg_nodes.pop() { + writeln!( + f, + r##" pub(super) fn icon_{}(cx: Scope) -> Element {{ + cx.render(rsx!("##, + id + )?; + write_tabs(f, 3)?; + + render_element(f, svg, 3, &mut svg_nodes, false, &mut 0)?; + writeln!(f, "\t\t))\n\t}}\n")?; + id += 1; + } + + writeln!(f, "}}")?; + + Ok(()) + } +} + +fn render_child<'a>( + f: &mut Formatter<'_>, + child: &'a Node, + il: u32, + svg_buffer: &mut Vec<&'a Element>, + skip_svg: bool, + svg_idx: &mut usize, +) -> std::fmt::Result { + write_tabs(f, il)?; + match child { + Node::Text(t) => writeln!(f, "\"{}\"", t)?, + Node::Comment(e) => writeln!(f, "/* {} */", e)?, + Node::Element(el) => render_element(f, el, il, svg_buffer, skip_svg, svg_idx)?, + }; + Ok(()) +} + +fn render_element<'a>( + f: &mut Formatter<'_>, + el: &'a Element, + il: u32, + svg_buffer: &mut Vec<&'a Element>, + skip_svg: bool, + svg_idx: &mut usize, +) -> std::fmt::Result { + if el.name == "svg" && skip_svg { + svg_buffer.push(el); + // todo: attach the right icon ID + writeln!(f, "icons::icon_{} {{}}", svg_idx)?; + *svg_idx += 1; + return Ok(()); + } + + // open the tag + write!(f, "{} {{ ", &el.name)?; + + // todo: dioxus will eventually support classnames + // for now, just write them with a space between each + let class_iter = &mut el.classes.iter(); + if let Some(first_class) = class_iter.next() { + write!(f, "class: \"{}", first_class)?; + for next_class in class_iter { + write!(f, " {}", next_class)?; + } + write!(f, "\",")?; + } + writeln!(f)?; + + // write the attributes + if let Some(id) = &el.id { + write_tabs(f, il + 1)?; + writeln!(f, "id: \"{}\",", id)?; + } + + for (name, value) in &el.attributes { + write_tabs(f, il + 1)?; + + use convert_case::{Case, Casing}; + if name.chars().any(|ch| ch.is_ascii_uppercase() || ch == '-') { + let new_name = name.to_case(Case::Snake); + match value { + Some(val) => writeln!(f, "{}: \"{}\",", new_name, val)?, + None => writeln!(f, "{}: \"\",", new_name)?, + } + } else { + if matches!(name.as_str(), "for" | "async" | "type" | "as") { + write!(f, "r#")? + } + + match value { + Some(val) => writeln!(f, "{}: \"{}\",", name, val)?, + None => writeln!(f, "{}: \"\",", name)?, + } + } + } + + // now the children + for child in &el.children { + render_child(f, child, il + 1, svg_buffer, skip_svg, svg_idx)?; + } + + // close the tag + write_tabs(f, il)?; + writeln!(f, "}}")?; + Ok(()) +} + +fn write_tabs(f: &mut impl std::fmt::Write, num: u32) -> std::fmt::Result { for _ in 0..num { write!(f, " ")? } Ok(()) } + +#[test] +fn generates_svgs() { + use std::io::Write; + + let st = include_str!("../../../tests/svg.html"); + + let out = format!("{:}", convert_html_to_component(st).unwrap()); + dbg!(&out); + + std::fs::File::create("svg_rsx.rs") + .unwrap() + .write_all(out.as_bytes()) + .unwrap(); +} diff --git a/src/cli/translate/to_component.rs b/src/cli/translate/to_component.rs deleted file mode 100644 index c83777ccb..000000000 --- a/src/cli/translate/to_component.rs +++ /dev/null @@ -1,183 +0,0 @@ -//! Intelligently converts html to rsx with appropraite transformations and extractions. -//! -//! - [*] Creates a component -//! - [ ] Extracts svgs -//! - [ ] Attempts to extract lists - -#[allow(unused_imports)] -use std::{ - fmt::{Display, Formatter}, - io::Write, -}; - -use anyhow::Result; - -use html_parser::{Dom, Element, Node}; - -pub fn convert_html_to_component(html: &str) -> Result { - Ok(ComponentRenderer { - dom: Dom::parse(html)?, - icon_index: 0, - }) -} - -#[allow(dead_code)] -pub struct ComponentRenderer { - dom: Dom, - icon_index: usize, -} - -impl Display for ComponentRenderer { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - writeln!( - f, - r##" -fn component(cx: Scope) -> Element {{ - cx.render(rsx!("## - )?; - let mut svg_nodes = vec![]; - - let mut svg_idx = 0; - for child in &self.dom.children { - render_child(f, child, 2, &mut svg_nodes, true, &mut svg_idx)?; - } - write!( - f, - r##" )) -}}"## - )?; - - if svg_idx == 0 { - return Ok(()); - } - - writeln!(f, "\n\nmod icons {{")?; - - let mut id = 0; - while let Some(svg) = svg_nodes.pop() { - writeln!( - f, - r##" pub(super) fn icon_{}(cx: Scope) -> Element {{ - cx.render(rsx!("##, - id - )?; - write_tabs(f, 3)?; - - render_element(f, svg, 3, &mut svg_nodes, false, &mut 0)?; - writeln!(f, "\t\t))\n\t}}\n")?; - id += 1; - } - - writeln!(f, "}}")?; - - Ok(()) - } -} - -fn render_child<'a>( - f: &mut Formatter<'_>, - child: &'a Node, - il: u32, - svg_buffer: &mut Vec<&'a Element>, - skip_svg: bool, - svg_idx: &mut usize, -) -> std::fmt::Result { - write_tabs(f, il)?; - match child { - Node::Text(t) => writeln!(f, "\"{}\"", t)?, - Node::Comment(e) => writeln!(f, "/* {} */", e)?, - Node::Element(el) => render_element(f, el, il, svg_buffer, skip_svg, svg_idx)?, - }; - Ok(()) -} - -fn render_element<'a>( - f: &mut Formatter<'_>, - el: &'a Element, - il: u32, - svg_buffer: &mut Vec<&'a Element>, - skip_svg: bool, - svg_idx: &mut usize, -) -> std::fmt::Result { - if el.name == "svg" && skip_svg { - svg_buffer.push(el); - // todo: attach the right icon ID - writeln!(f, "icons::icon_{} {{}}", svg_idx)?; - *svg_idx += 1; - return Ok(()); - } - - // open the tag - write!(f, "{} {{ ", &el.name)?; - - // todo: dioxus will eventually support classnames - // for now, just write them with a space between each - let class_iter = &mut el.classes.iter(); - if let Some(first_class) = class_iter.next() { - write!(f, "class: \"{}", first_class)?; - for next_class in class_iter { - write!(f, " {}", next_class)?; - } - write!(f, "\",")?; - } - writeln!(f)?; - - // write the attributes - if let Some(id) = &el.id { - write_tabs(f, il + 1)?; - writeln!(f, "id: \"{}\",", id)?; - } - - for (name, value) in &el.attributes { - write_tabs(f, il + 1)?; - - use convert_case::{Case, Casing}; - if name.chars().any(|ch| ch.is_ascii_uppercase() || ch == '-') { - let new_name = name.to_case(Case::Snake); - match value { - Some(val) => writeln!(f, "{}: \"{}\",", new_name, val)?, - None => writeln!(f, "{}: \"\",", new_name)?, - } - } else { - match name.as_str() { - "for" | "async" | "type" | "as" => write!(f, "r#")?, - _ => {} - } - - match value { - Some(val) => writeln!(f, "{}: \"{}\",", name, val)?, - None => writeln!(f, "{}: \"\",", name)?, - } - } - } - - // now the children - for child in &el.children { - render_child(f, child, il + 1, svg_buffer, skip_svg, svg_idx)?; - } - - // close the tag - write_tabs(f, il)?; - writeln!(f, "}}")?; - Ok(()) -} - -fn write_tabs(f: &mut Formatter, num: u32) -> std::fmt::Result { - for _ in 0..num { - write!(f, " ")? - } - Ok(()) -} - -#[test] -fn generates_svgs() { - let st = include_str!("../../../tests/svg.html"); - - let out = format!("{:}", convert_html_to_component(st).unwrap()); - dbg!(&out); - - std::fs::File::create("svg_rsx.rs") - .unwrap() - .write_all(out.as_bytes()) - .unwrap(); -} diff --git a/src/cli/translate/translate.rs b/src/cli/translate/translate.rs deleted file mode 100644 index 8b1378917..000000000 --- a/src/cli/translate/translate.rs +++ /dev/null @@ -1 +0,0 @@ -