jumpcut 1.0.0

JumpCut is a library and CLI for converting Fountain-formatted text files into FDX, HTML, JSON, text, and PDF formats.
Documentation
use crate::{ElementText, TextRun};
use serde::{Deserialize, Serialize};

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct StyledRun {
    pub text: String,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub styles: Vec<String>,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct StyledText {
    pub plain_text: String,
    pub runs: Vec<StyledRun>,
}

impl StyledText {
    pub fn from_element_text(text: &ElementText) -> Option<Self> {
        match text {
            ElementText::Plain(_) => None,
            ElementText::Styled(runs) => Some(Self {
                plain_text: runs.iter().map(|run| run.content.as_str()).collect(),
                runs: runs.iter().map(styled_run_from_text_run).collect(),
            }),
        }
    }

    pub fn slice(&self, start_offset: usize, end_offset: usize) -> Self {
        if start_offset >= end_offset {
            return Self {
                plain_text: String::new(),
                runs: Vec::new(),
            };
        }

        let plain_text = self.plain_text[start_offset..end_offset].to_string();
        let mut runs = Vec::new();
        let mut run_start = 0usize;

        for run in &self.runs {
            let run_end = run_start + run.text.len();
            let slice_start = run_start.max(start_offset);
            let slice_end = run_end.min(end_offset);

            if slice_start < slice_end {
                runs.push(StyledRun {
                    text: run.text[(slice_start - run_start)..(slice_end - run_start)].to_string(),
                    styles: run.styles.clone(),
                });
            }

            run_start = run_end;
        }

        Self { plain_text, runs }
    }
}

fn styled_run_from_text_run(run: &TextRun) -> StyledRun {
    let mut styles: Vec<String> = run.text_style.iter().cloned().collect();
    styles.sort();

    StyledRun {
        text: run.content.clone(),
        styles,
    }
}