///|
pub(all) struct CastMember {
name : String
first_line : Int
scenes : Array[Int]
turns : Int
spoken_lines : Int
words : Int
} derive(Eq, Debug, ToJson)
///|
/// Whitespace-delimited tokens, NOT linguistic word segmentation or duration.
pub fn word_count(text : String) -> Int {
let mut n = 0
let mut inside = false
for c in plain_text(text).to_array() {
if c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\u{3000}' {
inside = false
} else if !inside {
n += 1
inside = true
}
}
n
}
///|
/// Exact character identity; extensions do not split a role. Case is not folded.
pub fn cast(doc : Document) -> Array[CastMember] {
let out : Array[CastMember] = []
let indices : Map[String, Int] = Map([])
let ss = scenes(doc)
for turn in turns(doc) {
let scene = scene_at_line(ss, turn.line)
let mut words = 0
for text in turn.dialogue {
words += word_count(text)
}
match indices.get(turn.character) {
Some(i) => {
let prev = out[i]
let scene_ids = prev.scenes.copy()
if !scene_ids.contains(scene) {
scene_ids.push(scene)
}
out[i] = {
..prev,
scenes: scene_ids,
turns: prev.turns + 1,
spoken_lines: prev.spoken_lines + turn.dialogue.length(),
words: prev.words + words,
}
}
None => {
indices.set(turn.character, out.length())
out.push({
name: turn.character,
first_line: turn.line,
scenes: [scene],
turns: 1,
spoken_lines: turn.dialogue.length(),
words,
})
}
}
}
out
}