notation_model 0.6.0

Fun notation - runtime models
Documentation
use std::collections::HashMap;
use std::fmt::Display;
use std::sync::{Arc, Weak};

use notation_proto::prelude::Chord;

use crate::prelude::{
    Fretboard4, Fretboard6, ModelEntry, SliceBegin, SliceEnd, Tab, TabChord, TrackKind,
};

#[derive(Copy, Clone, Debug, Default)]
pub struct TrackProps {
    pub index: usize,
}

#[derive(Debug)]
pub struct Track {
    pub tab: Weak<Tab>,
    pub id: String,
    pub kind: TrackKind,
    pub entries: Vec<Arc<ModelEntry>>,
    pub props: TrackProps,
}
impl Display for Track {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "<Track>({:?}, {}, [{}])",
            self.kind,
            self.id,
            self.entries.len()
        )
    }
}
impl Track {
    pub fn new(
        tab: Weak<Tab>,
        index: usize,
        id: String,
        kind: TrackKind,
        entries: Vec<Arc<ModelEntry>>,
    ) -> Self {
        let props = TrackProps { index };
        Self {
            tab,
            id,
            kind,
            entries,
            props,
        }
    }
    pub fn new_arc(tab: Weak<Tab>, index: usize, v: notation_proto::prelude::Track) -> Arc<Self> {
        Arc::<Self>::new_cyclic(|weak_self| {
            let entries = ModelEntry::new_entries(v.entries, weak_self);
            Self::new(tab, index, v.id, v.kind, entries)
        })
    }
}
impl Track {
    pub fn tab(&self) -> Option<Arc<Tab>> {
        self.tab.upgrade().map(|x| x.clone())
    }
    pub fn index_of_mark(&self, begin: usize, mark: &String) -> Option<usize> {
        for i in begin..self.entries.len() {
            let entry = self.entries.get(i);
            if entry.is_some() && entry.unwrap().proto.is_mark_string(mark) {
                return Some(i);
            }
        }
        None
    }
    pub fn get_entries(&self, begin: &SliceBegin, end: &SliceEnd) -> Vec<Arc<ModelEntry>> {
        let (index, count) = match (begin, end) {
            (SliceBegin::Mark(x), SliceEnd::Mark(y)) => match self.index_of_mark(0, x) {
                Some(index) => {
                    let index = index + 1;
                    match self.index_of_mark(index, y) {
                        Some(end) => (index, end - index),
                        None => (index, 0),
                    }
                }
                None => (0, 0),
            },
            (SliceBegin::Mark(x), SliceEnd::Count(y)) => match self.index_of_mark(0, x) {
                Some(index) => (index + 1, *y),
                None => (0, 0),
            },
            (SliceBegin::Index(x), SliceEnd::Mark(y)) => match self.index_of_mark(*x, y) {
                Some(end) => (*x, end - 1 - *x),
                None => (*x, 0),
            },
            (SliceBegin::Index(x), SliceEnd::Count(y)) => (*x, *y),
        };
        let mut entries = vec![];
        for i in index..(index + count) {
            let entry = self.entries.get(i);
            if entry.is_some() {
                entries.push(entry.unwrap().clone());
            }
        }
        entries
    }
    pub fn get_tab_chords(&self) -> Vec<TabChord> {
        let mut chord_entries: HashMap<Chord, Vec<Arc<ModelEntry>>> = HashMap::new();
        for entry in self.entries.iter() {
            if let Some(chord) = entry
                .proto
                .as_core()
                .and_then(|x| x.as_chord())
                .map(|z| z.to_owned())
            {
                if !chord_entries.contains_key(&chord) {
                    chord_entries.insert(chord, Vec::new());
                }
                chord_entries.get_mut(&chord).unwrap().push(entry.clone());
            }
        }
        let mut chords = chord_entries
            .into_iter()
            .map(|(chord, entries)| {
                let bars = TabChord::calc_bars(self.tab(), chord);
                TabChord {
                    chord,
                    entries,
                    bars,
                }
            })
            .collect::<Vec<TabChord>>();
        let scale = self.tab().map(|t| t.meta.scale);
        chords.sort_by(|a, b| {
            let chord_a = scale
                .map(|s| s.calc_chord_for_sort(&a.chord))
                .unwrap_or(a.chord);
            let chord_b = scale
                .map(|s| s.calc_chord_for_sort(&b.chord))
                .unwrap_or(b.chord);
            chord_a.cmp(&chord_b)
        });
        chords
    }
}

macro_rules! impl_get_fretboard {
    ($name:ident, $strings:literal, $as_fretted:ident, $fretboard:ident) => {
        impl Track {
            pub fn $name(&self) -> Option<$fretboard> {
                self.get_entry(&|x: &ModelEntry| {
                    x.$as_fretted()
                        .and_then(|x| x.as_fretboard().map(|z| z.to_owned()))
                })
            }
        }
    };
}

impl_get_fretboard!(get_fretboard6, 6, as_fretted6, Fretboard6);
impl_get_fretboard!(get_fretboard4, 4, as_fretted4, Fretboard4);