/* * Copyright (C) 2021 Aravinth Manivannan * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ use std::collections::HashMap; use crate::data::*; use crate::proxy::StringUtils; use get_post::*; #[derive(Eq, PartialEq)] enum PostitionType { Start, End, } #[derive(Default, Eq, PartialEq)] struct ListState { in_uli: bool, in_oli: bool, } struct Markup<'a, 'b> { markup: &'a GetPostPostContentBodyModelParagraphsMarkups, p: &'a GetPostPostContentBodyModelParagraphs, pos_type: PostitionType, gists: &'b Option>, } impl<'a, 'b> Markup<'a, 'b> { fn start( p: &GetPostPostContentBodyModelParagraphs, gists: &'b Option>, pindex: usize, state: &mut ListState, ) -> String { let list = Self::list_close(p, state); let resp = if p.type_ == "IMG" { let metadata = p.metadata.as_ref().unwrap(); format!( r#"
"#, metadata.original_width.as_ref().unwrap(), crate::V1_API_ROUTES.proxy.get_medium_asset(&metadata.id) ) } else if p.type_ == "P" { "

".into() } else if p.type_ == "PRE" { "

".into()
        } else if p.type_ == "BQ" || p.type_ == "PQ" {
            "
".into() } else if p.type_ == "H1" { "

".into() } else if p.type_ == "H2" { "

".into() } else if p.type_ == "H3" { if pindex == 0 { log::debug!("caught heading"); "".into() } else { "

".into() } } else if p.type_ == "H4" { "

".into() } else if p.type_ == "H5" { "

".into() } else if p.type_ == "H6" { "
".into() } else if p.type_ == "IFRAME" { let src = &p .iframe .as_ref() .unwrap() .media_resource .as_ref() .unwrap() .href; if src.contains("gist.github.com") { let gist_id = crate::data::Data::get_gist_id(src); let (_, gist) = gists .as_ref() .unwrap() .iter() .find(|(id, _)| id == gist_id) .as_ref() .unwrap(); let mut gists = String::default(); for file in &gist.files { gists += &format!( r#"
{}
"#, file.get_html_content() ); } format!( r#"
{gists} See gist on GitHub"#, &gist.html_url ) } else { format!(r#"".into() } } else if p.type_ == "OLI" || p.type_ == "ULI" { "".into() } else if p.type_ == "MIXTAPE_EMBED" { "

".into() } else { "".into() }; if state.in_oli { if p.type_ != "OLI" { state.in_oli = false; format!("{resp}") } else { resp } } else if state.in_uli { if p.type_ != "ULI" { state.in_uli = false; format!("{resp}") } else { resp } } else { resp } } fn list_close( p: &GetPostPostContentBodyModelParagraphs, state: &mut ListState, ) -> Option { if state.in_oli && p.type_ != "OLI" { state.in_oli = false; return Some("".to_string()); }; if state.in_uli && p.type_ != "ULI" { state.in_uli = false; return Some("".to_string()); }; None } fn apply_markup(&self, _pindex: usize) -> String { if self.markup.type_ == "A" { if let Some(anchor_type) = &self.markup.anchor_type { if anchor_type == "LINK" { if self.pos_type == PostitionType::Start { format!( r#""#, self.markup.href.as_ref().unwrap() ) } else { "".into() } } else if anchor_type == "USER" { if self.pos_type == PostitionType::Start { format!( r#""#, self.markup.user_id.as_ref().unwrap() ) } else { "".into() } } else { // log::error!("unknown markup.anchor_type: {:?} post id {}", anchor_type, id); if self.pos_type == PostitionType::Start { "".into() } else { "".into() } } } else { // log::error!("unknown markup.anchor_type: {:?} post id {}", anchor_type, id); if self.pos_type == PostitionType::Start { "".into() } else { "".into() } } } else if self.markup.type_ == "PRE" { if self.pos_type == PostitionType::Start { "
".into()
            } else {
                "
".into() } } else if self.markup.type_ == "EM" { if self.pos_type == PostitionType::Start { "".into() } else { "".into() } } else if self.markup.type_ == "STRONG" { if self.pos_type == PostitionType::Start { "".into() } else { "".into() } } else if self.markup.type_ == "CODE" { if self.pos_type == PostitionType::Start { "".into() } else { "".into() } } else { // log::error!("unknown markup.type_: {:?} post id {}", markup.type_, id); if self.pos_type == PostitionType::Start { log::info!("Unknown type"); r#"

From LibMedium: LibMedium is built by reverse engineering the Meduim's internal API. This post contains markup(formatting rules) that we are unaware of. Please report this URL on our bug tracker so that we can improve page rendering.
Alternatively, you can also email me at realaravinth at batsense dot net!

"# .into() } else { "".into() } } } } #[derive(Default)] struct PositionMap<'a, 'b> { map: HashMap>>, arr: Vec, } impl<'a, 'b> PositionMap<'a, 'b> { fn insert_if_not_exists(&mut self, pos: i64, m: Markup<'a, 'b>) { if let Some(markups) = self.map.get_mut(&pos) { markups.push(m); } else { self.map.insert(pos, vec![m]); self.arr.push(pos); } } } pub fn apply_markup( data: &PostResp, gists: &Option>, ) -> Vec { let mut paragraphs: Vec = Vec::with_capacity(data.content.body_model.paragraphs.len()); let mut state = ListState::default(); let mut no_render_html = false; for (pindex, p) in data.content.body_model.paragraphs.iter().enumerate() { let mut pos = PositionMap::default(); if p.type_ == "H3" && pindex == 0 { log::debug!("FOUND TOP LEVEL H3. Breaking"); continue; } for m in p.markups.iter() { let start_markup = Markup { markup: m, p, gists, pos_type: PostitionType::Start, }; pos.insert_if_not_exists(m.start, start_markup); let end_markup = Markup { markup: m, p, gists, pos_type: PostitionType::End, }; pos.insert_if_not_exists(m.end, end_markup); } let mut cur = 0; fn incr_cur(cur: usize, point: i64) -> usize { let incr = point as usize - cur; let post_incr = cur + incr; log::debug!( "cur before incr: {cur}, incr by: {}, post incr: {}", incr, post_incr ); post_incr } let mut content = String::with_capacity(p.text.len()); let start = &Markup::start(p, gists, pindex, &mut state); content += start; if start == "
" {
            no_render_html = true;
        }
        pos.arr.sort();
        let mut page = String::default();
        if let Some(first) = pos.arr.first() {
            page += p.text.slice(cur..*first as usize);
            cur = incr_cur(cur, *first);
            for point in pos.arr.iter() {
                if cur != *point as usize {
                    page += p.text.slice(cur..*point as usize);
                }
                //           }
                let pos_markups = pos.map.get(point).unwrap();
                for m in pos_markups.iter() {
                    page += &m.apply_markup(pindex);
                }
                cur = incr_cur(cur, *point);
            }
            log::debug!("LAST");
            page += p.text.slice(cur..);
            let end = &Markup::end(p, pindex, &mut state);
            if end == "
" { no_render_html = false; } content += &page; content += end; } else { log::debug!("LAST WITH NO MARKUP"); page += p.text.slice(cur..); if no_render_html { page = page.replace("<", "<").replace(">", ">"); } content += &page; content += &Markup::end(p, pindex, &mut state); } paragraphs.push(content); } paragraphs }