fix: translation service

This commit is contained in:
Jonathan Kelley committed 2022-02-06 16:28:17 -05:00
1 parent 51bc2ff0a4
commit 025efc9120
4 files changed
+192 -233

No files matched your search

-1
View File
@@ -1 +0,0 @@
+192 -48
View File
@@ -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<String>,
/// Input file
#[structopt(short, long, parse(from_os_str))]
pub input: Option<PathBuf>,
pub file: Option<String>,
/// 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<ComponentRenderer> {
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();
}
-183
View File
@@ -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<ComponentRenderer> {
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();
}
-1
View File
@@ -1 +0,0 @@