<commit_before>use std::{fmt, iter::FromIterator, sync::Arc};

use hir::{ExpandResult, MacroFile};
use ide_db::base_db::{
    salsa::debug::{DebugQueryTable, TableEntry},
    CrateId, FileId, FileTextQuery, SourceDatabase, SourceRootId,
};
use ide_db::{
    symbol_index::{LibrarySymbolsQuery, SymbolIndex},
    RootDatabase,
};
use itertools::Itertools;
use profile::{memory_usage, Bytes};
use rustc_hash::FxHashMap;
use stdx::format_to;
use syntax::{ast, Parse, SyntaxNode};

fn syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats {
    ide_db::base_db::ParseQuery.in_db(db).entries::<SyntaxTreeStats>()
}
fn macro_syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats {
    hir::db::ParseMacroExpansionQuery.in_db(db).entries::<SyntaxTreeStats>()
}

// Feature: Status
//
// Shows internal statistic about memory usage of rust-analyzer.
//
// |===
// | Editor  | Action Name
//
// | VS Code | **Rust Analyzer: Status**
// |===
// image::https://user-images.githubusercontent.com/48062697/113065584-05f34500-91b1-11eb-98cc-5c196f76be7f.gif[]
pub(crate) fn status(db: &RootDatabase, file_id: Option<FileId>) -> String {
    let mut buf = String::new();
    let count = profile::countme::get_all();
    format_to!(buf, "{}\n", FileTextQuery.in_db(db).entries::<FilesStats>());
    format_to!(buf, "{}\n", LibrarySymbolsQuery.in_db(db).entries::<LibrarySymbolsStats>());
    format_to!(buf, "{}\n", syntax_tree_stats(db));
    format_to!(buf, "{} (Macros)\n", macro_syntax_tree_stats(db));
    format_to!(buf, "{} Total\n", memory_usage());
    format_to!(
        buf,
        "\nCounts:\n{}",
        if count.to_string().contains("all counts are zero") {
            String::from("All counts are zero\n")
        } else {
            count.to_string()
        }
    );

    if let Some(file_id) = file_id {
        format_to!(buf, "\nFile info:\n");
        let krate = crate::parent_module::crate_for(db, file_id).pop();
        match krate {
            Some(krate) => {
                let crate_graph = db.crate_graph();
                let display_crate = |krate: CrateId| match &crate_graph[krate].display_name {
                    Some(it) => format!("{}({:?})", it, krate),
                    None => format!("{:?}", krate),
                };
                format_to!(buf, "crate: {}\n", display_crate(krate));
                let deps = crate_graph[krate]
                    .dependencies
                    .iter()
                    .map(|dep| format!("{}={:?}", dep.name, dep.crate_id))
                    .format(", ");
                format_to!(buf, "deps: {}\n", deps);
            }
            None => format_to!(buf, "does not belong to any crate"),
        }
    }

    buf.trim().to_string()
}

#[derive(Default)]
struct FilesStats {
    total: usize,
    size: Bytes,
}

impl fmt::Display for FilesStats {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(fmt, "{} ({}) files", self.total, self.size)
    }
}

impl FromIterator<TableEntry<FileId, Arc<String>>> for FilesStats {
    fn from_iter<T>(iter: T) -> FilesStats
    where
        T: IntoIterator<Item = TableEntry<FileId, Arc<String>>>,
    {
        let mut res = FilesStats::default();
        for entry in iter {
            res.total += 1;
            res.size += entry.value.unwrap().len();
        }
        res
    }
}

#[derive(Default)]
pub(crate) struct SyntaxTreeStats {
    total: usize,
    pub(crate) retained: usize,
}

impl fmt::Display for SyntaxTreeStats {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(fmt, "{} trees, {} retained", self.total, self.retained)
    }
}

impl FromIterator<TableEntry<FileId, Parse<ast::SourceFile>>> for SyntaxTreeStats {
    fn from_iter<T>(iter: T) -> SyntaxTreeStats
    where
        T: IntoIterator<Item = TableEntry<FileId, Parse<ast::SourceFile>>>,
    {
        let mut res = SyntaxTreeStats::default();
        for entry in iter {
            res.total += 1;
            res.retained += entry.value.is_some() as usize;
        }
        res
    }
}

impl<M> FromIterator<TableEntry<MacroFile, ExpandResult<Option<(Parse<SyntaxNode>, M)>>>>
    for SyntaxTreeStats
{
    fn from_iter<T>(iter: T) -> SyntaxTreeStats
    where
        T: IntoIterator<Item = TableEntry<MacroFile, ExpandResult<Option<(Parse<SyntaxNode>, M)>>>>,
    {
        let mut res = SyntaxTreeStats::default();
        for entry in iter {
            res.total += 1;
            res.retained += entry.value.is_some() as usize;
        }
        res
    }
}

#[derive(Default)]
struct LibrarySymbolsStats {
    total: usize,
    size: Bytes,
}

impl fmt::Display for LibrarySymbolsStats {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(fmt, "{} ({}) index symbols", self.total, self.size)
    }
}

impl FromIterator<TableEntry<(), Arc<FxHashMap<SourceRootId, SymbolIndex>>>>
    for LibrarySymbolsStats
{
    fn from_iter<T>(iter: T) -> LibrarySymbolsStats
    where
        T: IntoIterator<Item = TableEntry<(), Arc<FxHashMap<SourceRootId, SymbolIndex>>>>,
    {
        let mut res = LibrarySymbolsStats::default();
        for entry in iter {
            let value = entry.value.unwrap();
            for symbols in value.values() {
                res.total += symbols.len();
                res.size += symbols.memory_size();
            }
        }
        res
    }
}
<commit_msg>clean up some wording<commit_after>use std::{fmt, iter::FromIterator, sync::Arc};

use hir::{ExpandResult, MacroFile};
use ide_db::base_db::{
    salsa::debug::{DebugQueryTable, TableEntry},
    CrateId, FileId, FileTextQuery, SourceDatabase, SourceRootId,
};
use ide_db::{
    symbol_index::{LibrarySymbolsQuery, SymbolIndex},
    RootDatabase,
};
use itertools::Itertools;
use profile::{memory_usage, Bytes};
use rustc_hash::FxHashMap;
use stdx::format_to;
use syntax::{ast, Parse, SyntaxNode};

fn syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats {
    ide_db::base_db::ParseQuery.in_db(db).entries::<SyntaxTreeStats>()
}
fn macro_syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats {
    hir::db::ParseMacroExpansionQuery.in_db(db).entries::<SyntaxTreeStats>()
}

// Feature: Status
//
// Shows internal statistic about memory usage of rust-analyzer.
//
// |===
// | Editor  | Action Name
//
// | VS Code | **Rust Analyzer: Status**
// |===
// image::https://user-images.githubusercontent.com/48062697/113065584-05f34500-91b1-11eb-98cc-5c196f76be7f.gif[]
pub(crate) fn status(db: &RootDatabase, file_id: Option<FileId>) -> String {
    let mut buf = String::new();
    let count = profile::countme::get_all();
    format_to!(buf, "{}\n", FileTextQuery.in_db(db).entries::<FilesStats>());
    format_to!(buf, "{}\n", LibrarySymbolsQuery.in_db(db).entries::<LibrarySymbolsStats>());
    format_to!(buf, "{}\n", syntax_tree_stats(db));
    format_to!(buf, "{} (Macros)\n", macro_syntax_tree_stats(db));
    format_to!(buf, "{} in total\n", memory_usage());
    format_to!(
        buf,
        "\nCounts:\n{}",
        if count.to_string().contains("all counts are zero") {
            String::from("All counts are zero\n")
        } else {
            count.to_string()
        }
    );

    if let Some(file_id) = file_id {
        format_to!(buf, "\nFile info:\n");
        let krate = crate::parent_module::crate_for(db, file_id).pop();
        match krate {
            Some(krate) => {
                let crate_graph = db.crate_graph();
                let display_crate = |krate: CrateId| match &crate_graph[krate].display_name {
                    Some(it) => format!("{}({:?})", it, krate),
                    None => format!("{:?}", krate),
                };
                format_to!(buf, "crate: {}\n", display_crate(krate));
                let deps = crate_graph[krate]
                    .dependencies
                    .iter()
                    .map(|dep| format!("{}={:?}", dep.name, dep.crate_id))
                    .format(", ");
                format_to!(buf, "deps: {}\n", deps);
            }
            None => format_to!(buf, "Does not belong to any crate"),
        }
    }

    buf.trim().to_string()
}

#[derive(Default)]
struct FilesStats {
    total: usize,
    size: Bytes,
}

impl fmt::Display for FilesStats {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(fmt, "{} of files", self.size)
    }
}

impl FromIterator<TableEntry<FileId, Arc<String>>> for FilesStats {
    fn from_iter<T>(iter: T) -> FilesStats
    where
        T: IntoIterator<Item = TableEntry<FileId, Arc<String>>>,
    {
        let mut res = FilesStats::default();
        for entry in iter {
            res.total += 1;
            res.size += entry.value.unwrap().len();
        }
        res
    }
}

#[derive(Default)]
pub(crate) struct SyntaxTreeStats {
    total: usize,
    pub(crate) retained: usize,
}

impl fmt::Display for SyntaxTreeStats {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(fmt, "{} trees, {} preserved", self.total, self.retained)
    }
}

impl FromIterator<TableEntry<FileId, Parse<ast::SourceFile>>> for SyntaxTreeStats {
    fn from_iter<T>(iter: T) -> SyntaxTreeStats
    where
        T: IntoIterator<Item = TableEntry<FileId, Parse<ast::SourceFile>>>,
    {
        let mut res = SyntaxTreeStats::default();
        for entry in iter {
            res.total += 1;
            res.retained += entry.value.is_some() as usize;
        }
        res
    }
}

impl<M> FromIterator<TableEntry<MacroFile, ExpandResult<Option<(Parse<SyntaxNode>, M)>>>>
    for SyntaxTreeStats
{
    fn from_iter<T>(iter: T) -> SyntaxTreeStats
    where
        T: IntoIterator<Item = TableEntry<MacroFile, ExpandResult<Option<(Parse<SyntaxNode>, M)>>>>,
    {
        let mut res = SyntaxTreeStats::default();
        for entry in iter {
            res.total += 1;
            res.retained += entry.value.is_some() as usize;
        }
        res
    }
}

#[derive(Default)]
struct LibrarySymbolsStats {
    total: usize,
    size: Bytes,
}

impl fmt::Display for LibrarySymbolsStats {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(fmt, "{} of index symbols", self.size)
    }
}

impl FromIterator<TableEntry<(), Arc<FxHashMap<SourceRootId, SymbolIndex>>>>
    for LibrarySymbolsStats
{
    fn from_iter<T>(iter: T) -> LibrarySymbolsStats
    where
        T: IntoIterator<Item = TableEntry<(), Arc<FxHashMap<SourceRootId, SymbolIndex>>>>,
    {
        let mut res = LibrarySymbolsStats::default();
        for entry in iter {
            let value = entry.value.unwrap();
            for symbols in value.values() {
                res.total += symbols.len();
                res.size += symbols.memory_size();
            }
        }
        res
    }
}
<|endoftext|>


<commit_before>#![allow(non_snake_case)]

use std::cmp;
use std::mem;

use rgsl::randist::multinomial::multinomial_pdf;

use bio::stats::combinatorics::combinations;
use bio::stats::logprobs::{Prob, LogProb};
use bio::stats::logprobs;

use io::codebook::Codebook;

pub struct Params {
    N: u8,
    m: u8,
    p0: Prob,
    p1: Prob,
    codebook: Codebook
}

pub trait Model: Sync {

    fn params(&self) -> &Params;

    /// Probability to make exactly i 1->0 and j 0->1 errors.
    fn xi(&self, i: u8, j: u8) -> Prob {
        self.params().p1.powi(i as i32) * self.params().p0.powi(j as i32) *
        (1.0 - self.params().p1).powi((self.params().m - i) as i32) * (1.0 - self.params().p0).powi((self.params().N - self.params().m - j) as i32)
    }

    /// Number of possibilities to select i ones and j zeros.
    fn psi(&self, i: u8, j: u8) -> f64 {
        combinations(self.params().m as u64, i as u64) * combinations((self.params().N - self.params().m) as u64, j as u64)
    }

    /// Probability to see an exact readout given that we have a call.
    fn prob_call_exact(&self) -> Prob;

    /// Probability to see a readout with one mismatch given that we have a call.
    fn prob_call_mismatch(&self) -> Prob;

    /// Probability to see an exact readout given that we have a miscall.
    fn prob_miscall_exact(&self, feature: &str) -> Prob;

    /// Probability to see a readout with one mismatch given that we have a miscall.
    fn prob_miscall_mismatch(&self, feature: &str) -> Prob;

    /// Probability to miss or miscall a readout because of too many errors.
    fn prob_missed(&self) -> Prob {
        1.0 - self.prob_call_exact() - self.prob_call_mismatch()
    }

    fn prob_miscall(&self, feature: &str) -> Prob {
        self.prob_miscall_exact(feature) + self.prob_miscall_mismatch(feature)
    }

    /// Probability to completely miss a readout (not miscalled).
    fn prob_nocall(&self, feature: &str) -> Prob {
        1.0 - self.prob_miscall_exact(feature) - self.prob_miscall_mismatch(feature)
    }
}

pub struct MHD4 {
    params: Params
}

impl Model for MHD4 {

    fn params(&self) -> &Params {
        &self.params
    }

    /// Probability to see an exact readout given that we have a call.
    fn prob_call_exact(&self) -> Prob {
        self.xi(0, 0)
    }

    /// Probability to see a readout with one mismatch given that we have a call.
    fn prob_call_mismatch(&self) -> Prob {
        (self.params().m as f64 * self.xi(1, 0) + (self.params().N - self.params().m) as f64 * self.xi(0, 1))
    }

    /// Probability to see an exact readout given that we have a miscall.
    fn prob_miscall_exact(&self, feature: &str) -> Prob {
        self.params().codebook.neighbors(feature, 4) as f64 * self.xi(2, 2)
    }

    /// Probability to see a readout with one mismatch given that we have a miscall.
    fn prob_miscall_mismatch(&self, feature: &str) -> Prob {
        let n = self.params().codebook.neighbors(feature, 4) as f64;

        n * 2 as f64 * self.xi(2, 1) + n * 2 as f64 * self.xi(1, 2) +
        n * (self.params().m - 2) as f64 * self.xi(3, 2) + n * (self.params().N - self.params().m - 2) as f64 * self.xi(2, 3)
    }
}

pub struct MHD2 {
    params: Params
}

impl Model for MHD2 {

    fn params(&self) -> &Params {
        &self.params
    }

    /// Probability to see an exact readout given that we have a call.
    fn prob_call_exact(&self) -> Prob {
        self.xi(0, 0)
    }

    /// Probability to see a readout with one mismatch given that we have a call.
    fn prob_call_mismatch(&self) -> Prob {
        0.0
    }

    /// Probability to see an exact readout given that we have a miscall.
    fn prob_miscall_exact(&self, feature: &str) -> Prob {
        self.params().codebook.neighbors(feature, 2) as f64 * self.xi(1, 1) +
        self.params().codebook.neighbors(feature, 4) as f64 * self.xi(2, 2)
    }

    /// Probability to see a readout with one mismatch given that we have a miscall.
    #[allow(unused_variables)]
    fn prob_miscall_mismatch(&self, feature: &str) -> Prob {
        0.0
    }
}

pub fn new_model(N: u8, m: u8, p0: Prob, p1: Prob, dist: u8, codebook: Codebook) -> Box<Model> {
    let params = Params { N: N, m: m, p0: p0, p1: p1, codebook: codebook};
    let model: Box<Model> = match dist {
        4 => Box::new(MHD4 { params: params }),
        2 => Box::new(MHD2 { params: params }),
        _ => panic!("Hamming distances other than 2 and 4 are unsupported at the moment.")
    };
    model
}

/// Readout probabilities.
pub struct Readout {
    prob_call_exact: Prob,
    prob_call_mismatch: Prob,
    prob_miscall_exact: Prob,
    prob_miscall_mismatch: Prob,
    prob_missed: Prob,
    prob_nocall: Prob,
    prob_miscall: Prob,
    margin: u32
}

impl Readout {
    pub fn new(feature: &str, model: &Box<Model>, window_width: u32) -> Self {

        Readout {
            prob_call_exact: model.prob_call_exact(),
            prob_call_mismatch: model.prob_call_mismatch(),
            prob_miscall_exact: model.prob_miscall_exact(feature),
            prob_miscall_mismatch: model.prob_miscall_mismatch(feature),
            prob_missed: model.prob_missed(),
            prob_nocall: model.prob_nocall(feature),
            prob_miscall: model.prob_miscall(feature),
            margin: window_width / 2
        }
    }

    pub fn window(&self, count: u32, count_exact: u32) -> (u32, u32) {
        let prob_call = 1.0 - self.prob_miscall;
        //let n_0 = (count - count_exact) as f64 / (self.prob_call_mismatch * (1.0 - self.prob_miscall) + self.prob_miscall_mismatch);
        //let n_1 = count_exact as f64 / (self.prob_call_exact * (1.0 - self.prob_miscall) + self.prob_miscall_exact);
        //let n_ = count as f64 / (self.prob_call_exact * prob_call +  self.prob_call_mismatch * prob_call + self.prob_miscall_exact + self.prob_miscall_mismatch);

        // estimate n from exact readouts
        let n_0 = count_exact as f64 / (self.prob_call_exact * prob_call + self.prob_miscall_exact);
        let n_1 = {
            // estimate n from corrected readouts
            let p = self.prob_call_mismatch * prob_call + self.prob_miscall_mismatch;
            if p > 0.0 {
                let n_1 = (count - count_exact) as f64 / p;
                n_1
            } else {
                n_0
            }
        };

        let est_x = |n: f64| {
            (
                n * self.prob_call_exact * prob_call +
                n * self.prob_call_mismatch * prob_call +
                n * self.prob_missed
            ).round() as i32
        };

        // estimate lower and upper bound of x
        // with MHD2, this is the same and the estimate is equivalent to the MAP
        let mut x_0 = est_x(n_0);
        let mut x_1 = est_x(n_1);
        if x_0 > x_1 {
            mem::swap(&mut x_0, &mut x_1);
        }

        (cmp::max(x_0 - self.margin as i32, 0) as u32, x_1 as u32 + self.margin)
    }

    pub fn likelihood(&self, x: u32, count: u32, count_exact: u32) -> LogProb {
        let count = count;
        let count_exact = count_exact;
        assert!(count >= count_exact);

        let mut summands = Vec::new();
        let prob_call = 1.0 - self.prob_miscall; // Pr(E=e) = 1 - Pr(E!=e)
        let probs = [
            self.prob_call_exact * prob_call, // Pr(H=0, E=e) = Pr(H=0 | E=e) * Pr(E=e)
            self.prob_call_mismatch * prob_call,
            self.prob_missed * prob_call,
            self.prob_miscall_exact,
            self.prob_miscall_mismatch
        ];

        for i in 0..(cmp::min(x, count) + 1) {
            let jmax = cmp::min(count_exact, i);
            // i - j <= count - count_exact
            let jmin = if count_exact + i > count { count_exact + i - count } else { 0 };
            for j in jmin..(jmax + 1) {
                //let n = combinations((count - count_exact) as u64, (i - j) as u64).ln() + combinations(count_exact as u64, j as u64).ln();
                let k = x - i;
                let exact_miscalls = count_exact - j;
                let mismatch_miscalls = count - count_exact - (i - j);

                let p = multinomial_pdf(&probs, &[j, i - j, k, exact_miscalls, mismatch_miscalls]).ln();
                summands.push(p);
            }
        }
        let likelihood = logprobs::sum(&summands);
        assert!(!likelihood.is_nan());
        likelihood
    }

    pub fn expected_total(&self, total_count: u32) -> f64 {
        // c = x - x * p_nocall
        // x = c / (1 - p_nocall)
        total_count as f64 / (1.0 - self.prob_nocall)
    }
}

#[cfg(test)]
mod tests {
    #![allow(non_upper_case_globals)]

    use super::*;
    use nalgebra::ApproxEq;
    use io;

    fn setup_mhd4() -> Box<Model> {
        new_model(16, 4, 0.04, 0.1, 4, io::codebook::Reader::from_file("test/codebook/simulated-MHD4.txt", 4).unwrap().codebook())
    }

    fn setup_mhd2() -> Box<Model> {
        new_model(14, 4, 0.04, 0.1, 2, io::codebook::Reader::from_file("test/codebook/simulated-MHD2.txt", 2).unwrap().codebook())
    }

    #[test]
    fn test_prob_call_exact() {
        let model = setup_mhd4();
        let p = model.prob_call_exact();
        println!("{}", p);
        assert!(p.approx_eq(&0.4019988717840602));
    }

    #[test]
    fn test_prob_call_mismatch() {
        let model = setup_mhd4();
        let p = model.prob_call_mismatch();
        println!("{}", p);
        assert!(p.approx_eq(&0.3796656011293904));
    }

    #[test]
    fn test_prob_miscall_exact() {
        let model = setup_mhd4();
        let p = model.prob_miscall_exact("COL5A1");
        println!("{}", p);
        assert!(p.approx_eq(&0.00031018431464819473));
    }

    #[test]
    fn test_prob_miscall_mismatch() {
        let model = setup_mhd4();
        let p = model.prob_miscall_mismatch("COL5A1");
        println!("{}", p);
        assert!(p.approx_eq(&0.020670338078917206));
    }

    #[test]
    fn test_prob_missed() {
        let model = setup_mhd4();
        let p = model.prob_missed();
        println!("{}", p);
        assert!(p.approx_eq(&0.21833552708654924));
    }

    #[test]
    fn test_mhd2() {
        let model = setup_mhd2();
        println!("{}", model.prob_call_exact());
        println!("{}", model.prob_call_mismatch());
        println!("{}", model.prob_miscall_exact("COL7A1"));
        println!("{}", model.prob_miscall_mismatch("COL7A1"));
        println!("{}", model.prob_missed());
    }

    #[test]
    fn test_xi() {
        let model = setup_mhd4();
        let p = model.xi(0, 0);
        assert!(p.approx_eq(&0.4019988717840602));
        let p = model.xi(1, 0);
        assert!(p.approx_eq(&0.04466654130934002));
        let p = model.xi(0, 1);
        assert!(p.approx_eq(&0.01674995299100251));
        let p = model.xi(2, 2);
        assert!(p.approx_eq(&8.616230962449852e-06));
        let p = model.xi(2, 1);
        assert!(p.approx_eq(&0.00020678954309879646));
        let p = model.xi(1, 2);
        assert!(p.approx_eq(&7.754607866204867e-05));
    }

    #[test]
    fn test_window() {
        let model = setup_mhd4();
        let readout = Readout::new("COL5A1", &model, 100);
        let (lower, upper) = readout.window(175, 75);
        println!("{} {}", lower, upper);
        for x in lower..upper {
            println!("{}={}", x, readout.likelihood(x, 175, 75));
        }
        assert!(readout.likelihood(lower, 175, 75).exp().approx_eq(&0.0));
        assert!(readout.likelihood(upper, 175, 75).exp().approx_eq(&0.0));
    }
}
<commit_msg>Average over lower and upper bound for PMF window.<commit_after>#![allow(non_snake_case)]

use std::cmp;
use std::mem;

use rgsl::randist::multinomial::multinomial_pdf;

use bio::stats::combinatorics::combinations;
use bio::stats::logprobs::{Prob, LogProb};
use bio::stats::logprobs;

use io::codebook::Codebook;

pub struct Params {
    N: u8,
    m: u8,
    p0: Prob,
    p1: Prob,
    codebook: Codebook
}

pub trait Model: Sync {

    fn params(&self) -> &Params;

    /// Probability to make exactly i 1->0 and j 0->1 errors.
    fn xi(&self, i: u8, j: u8) -> Prob {
        self.params().p1.powi(i as i32) * self.params().p0.powi(j as i32) *
        (1.0 - self.params().p1).powi((self.params().m - i) as i32) * (1.0 - self.params().p0).powi((self.params().N - self.params().m - j) as i32)
    }

    /// Number of possibilities to select i ones and j zeros.
    fn psi(&self, i: u8, j: u8) -> f64 {
        combinations(self.params().m as u64, i as u64) * combinations((self.params().N - self.params().m) as u64, j as u64)
    }

    /// Probability to see an exact readout given that we have a call.
    fn prob_call_exact(&self) -> Prob;

    /// Probability to see a readout with one mismatch given that we have a call.
    fn prob_call_mismatch(&self) -> Prob;

    /// Probability to see an exact readout given that we have a miscall.
    fn prob_miscall_exact(&self, feature: &str) -> Prob;

    /// Probability to see a readout with one mismatch given that we have a miscall.
    fn prob_miscall_mismatch(&self, feature: &str) -> Prob;

    /// Probability to miss or miscall a readout because of too many errors.
    fn prob_missed(&self) -> Prob {
        1.0 - self.prob_call_exact() - self.prob_call_mismatch()
    }

    fn prob_miscall(&self, feature: &str) -> Prob {
        self.prob_miscall_exact(feature) + self.prob_miscall_mismatch(feature)
    }

    /// Probability to completely miss a readout (not miscalled).
    fn prob_nocall(&self, feature: &str) -> Prob {
        1.0 - self.prob_miscall_exact(feature) - self.prob_miscall_mismatch(feature)
    }
}

pub struct MHD4 {
    params: Params
}

impl Model for MHD4 {

    fn params(&self) -> &Params {
        &self.params
    }

    /// Probability to see an exact readout given that we have a call.
    fn prob_call_exact(&self) -> Prob {
        self.xi(0, 0)
    }

    /// Probability to see a readout with one mismatch given that we have a call.
    fn prob_call_mismatch(&self) -> Prob {
        (self.params().m as f64 * self.xi(1, 0) + (self.params().N - self.params().m) as f64 * self.xi(0, 1))
    }

    /// Probability to see an exact readout given that we have a miscall.
    fn prob_miscall_exact(&self, feature: &str) -> Prob {
        self.params().codebook.neighbors(feature, 4) as f64 * self.xi(2, 2)
    }

    /// Probability to see a readout with one mismatch given that we have a miscall.
    fn prob_miscall_mismatch(&self, feature: &str) -> Prob {
        let n = self.params().codebook.neighbors(feature, 4) as f64;

        n * 2 as f64 * self.xi(2, 1) + n * 2 as f64 * self.xi(1, 2) +
        n * (self.params().m - 2) as f64 * self.xi(3, 2) + n * (self.params().N - self.params().m - 2) as f64 * self.xi(2, 3)
    }
}

pub struct MHD2 {
    params: Params
}

impl Model for MHD2 {

    fn params(&self) -> &Params {
        &self.params
    }

    /// Probability to see an exact readout given that we have a call.
    fn prob_call_exact(&self) -> Prob {
        self.xi(0, 0)
    }

    /// Probability to see a readout with one mismatch given that we have a call.
    fn prob_call_mismatch(&self) -> Prob {
        0.0
    }

    /// Probability to see an exact readout given that we have a miscall.
    fn prob_miscall_exact(&self, feature: &str) -> Prob {
        self.params().codebook.neighbors(feature, 2) as f64 * self.xi(1, 1) +
        self.params().codebook.neighbors(feature, 4) as f64 * self.xi(2, 2)
    }

    /// Probability to see a readout with one mismatch given that we have a miscall.
    #[allow(unused_variables)]
    fn prob_miscall_mismatch(&self, feature: &str) -> Prob {
        0.0
    }
}

pub fn new_model(N: u8, m: u8, p0: Prob, p1: Prob, dist: u8, codebook: Codebook) -> Box<Model> {
    let params = Params { N: N, m: m, p0: p0, p1: p1, codebook: codebook};
    let model: Box<Model> = match dist {
        4 => Box::new(MHD4 { params: params }),
        2 => Box::new(MHD2 { params: params }),
        _ => panic!("Hamming distances other than 2 and 4 are unsupported at the moment.")
    };
    model
}

/// Readout probabilities.
pub struct Readout {
    prob_call_exact: Prob,
    prob_call_mismatch: Prob,
    prob_miscall_exact: Prob,
    prob_miscall_mismatch: Prob,
    prob_missed: Prob,
    prob_nocall: Prob,
    prob_miscall: Prob,
    prob_call: Prob,
    margin: u32
}

impl Readout {
    pub fn new(feature: &str, model: &Box<Model>, window_width: u32) -> Self {
        let prob_miscall = model.prob_miscall(feature);
        Readout {
            prob_call_exact: model.prob_call_exact(),
            prob_call_mismatch: model.prob_call_mismatch(),
            prob_miscall_exact: model.prob_miscall_exact(feature),
            prob_miscall_mismatch: model.prob_miscall_mismatch(feature),
            prob_missed: model.prob_missed(),
            prob_nocall: model.prob_nocall(feature),
            prob_miscall: prob_miscall,
            prob_call: 1.0 - prob_miscall,
            margin: window_width / 2
        }
    }

    fn est_x(&self, n: f64) -> i32 {
        (
            n * self.prob_call_exact * self.prob_call +
            n * self.prob_call_mismatch * self.prob_call +
            n * self.prob_missed * self.prob_call
        ).round() as i32
    }

    /// Upper and lower bound for MAP
    pub fn map_bounds(&self, count: u32, count_exact: u32) -> (u32, u32) {
        // estimate n from exact readouts
        let n_0 = count_exact as f64 / (self.prob_call_exact * self.prob_call + self.prob_miscall_exact);
        let n_1 = {
            // estimate n from corrected readouts
            let p = self.prob_call_mismatch * self.prob_call + self.prob_miscall_mismatch;
            if p > 0.0 {
                let n_1 = (count - count_exact) as f64 / p;
                n_1
            } else {
                n_0
            }
        };

        // estimate lower and upper bound of x
        // with MHD2, this is the same and the estimate is equivalent to the MAP
        let mut x_0 = self.est_x(n_0);
        let mut x_1 = self.est_x(n_1);
        if x_0 > x_1 {
            mem::swap(&mut x_0, &mut x_1);
        }

        (x_0 as u32, x_1 as u32)
    }

    /// Prior window for calculating expression PMF
    pub fn window(&self, count: u32, count_exact: u32) -> (u32, u32) {
        let n_avg = count as f64 / (
            self.prob_call_exact * self.prob_call +
            self.prob_call_mismatch * self.prob_call +
            self.prob_miscall_exact +
            self.prob_miscall_mismatch
        );
        let x = self.est_x(n_avg);

        (cmp::max(x - self.margin as i32, 0) as u32, x as u32 + self.margin)
    }

    pub fn likelihood(&self, x: u32, count: u32, count_exact: u32) -> LogProb {
        let count = count;
        let count_exact = count_exact;
        assert!(count >= count_exact);

        let mut summands = Vec::new();
        let probs = [
            self.prob_call_exact * self.prob_call, // Pr(H=0, E=e) = Pr(H=0 | E=e) * Pr(E=e)
            self.prob_call_mismatch * self.prob_call,
            self.prob_missed * self.prob_call,
            self.prob_miscall_exact,
            self.prob_miscall_mismatch
        ];

        for i in 0..(cmp::min(x, count) + 1) {
            let jmax = cmp::min(count_exact, i);
            // i - j <= count - count_exact
            let jmin = if count_exact + i > count { count_exact + i - count } else { 0 };
            for j in jmin..(jmax + 1) {
                //let n = combinations((count - count_exact) as u64, (i - j) as u64).ln() + combinations(count_exact as u64, j as u64).ln();
                let k = x - i;
                let exact_miscalls = count_exact - j;
                let mismatch_miscalls = count - count_exact - (i - j);

                let p = multinomial_pdf(&probs, &[j, i - j, k, exact_miscalls, mismatch_miscalls]).ln();
                summands.push(p);
            }
        }
        let likelihood = logprobs::sum(&summands);
        assert!(!likelihood.is_nan());
        likelihood
    }

    pub fn expected_total(&self, total_count: u32) -> f64 {
        // c = x - x * p_nocall
        // x = c / (1 - p_nocall)
        total_count as f64 / (1.0 - self.prob_nocall)
    }
}

#[cfg(test)]
mod tests {
    #![allow(non_upper_case_globals)]

    use super::*;
    use nalgebra::ApproxEq;
    use io;

    fn setup_mhd4() -> Box<Model> {
        new_model(16, 4, 0.04, 0.1, 4, io::codebook::Reader::from_file("test/codebook/simulated-MHD4.txt", 4).unwrap().codebook())
    }

    fn setup_mhd2() -> Box<Model> {
        new_model(14, 4, 0.04, 0.1, 2, io::codebook::Reader::from_file("test/codebook/simulated-MHD2.txt", 2).unwrap().codebook())
    }

    #[test]
    fn test_prob_call_exact() {
        let model = setup_mhd4();
        let p = model.prob_call_exact();
        println!("{}", p);
        assert!(p.approx_eq(&0.4019988717840602));
    }

    #[test]
    fn test_prob_call_mismatch() {
        let model = setup_mhd4();
        let p = model.prob_call_mismatch();
        println!("{}", p);
        assert!(p.approx_eq(&0.3796656011293904));
    }

    #[test]
    fn test_prob_miscall_exact() {
        let model = setup_mhd4();
        let p = model.prob_miscall_exact("COL5A1");
        println!("{}", p);
        assert!(p.approx_eq(&0.00031018431464819473));
    }

    #[test]
    fn test_prob_miscall_mismatch() {
        let model = setup_mhd4();
        let p = model.prob_miscall_mismatch("COL5A1");
        println!("{}", p);
        assert!(p.approx_eq(&0.020670338078917206));
    }

    #[test]
    fn test_prob_missed() {
        let model = setup_mhd4();
        let p = model.prob_missed();
        println!("{}", p);
        assert!(p.approx_eq(&0.21833552708654924));
    }

    #[test]
    fn test_mhd2() {
        let model = setup_mhd2();
        println!("{}", model.prob_call_exact());
        println!("{}", model.prob_call_mismatch());
        println!("{}", model.prob_miscall_exact("COL7A1"));
        println!("{}", model.prob_miscall_mismatch("COL7A1"));
        println!("{}", model.prob_missed());
    }

    #[test]
    fn test_xi() {
        let model = setup_mhd4();
        let p = model.xi(0, 0);
        assert!(p.approx_eq(&0.4019988717840602));
        let p = model.xi(1, 0);
        assert!(p.approx_eq(&0.04466654130934002));
        let p = model.xi(0, 1);
        assert!(p.approx_eq(&0.01674995299100251));
        let p = model.xi(2, 2);
        assert!(p.approx_eq(&8.616230962449852e-06));
        let p = model.xi(2, 1);
        assert!(p.approx_eq(&0.00020678954309879646));
        let p = model.xi(1, 2);
        assert!(p.approx_eq(&7.754607866204867e-05));
    }

    #[test]
    fn test_window() {
        let model = setup_mhd4();
        let readout = Readout::new("COL5A1", &model, 100);
        let (lower, upper) = readout.window(175, 75);
        println!("{} {}", lower, upper);
        for x in lower..upper {
            println!("{}={}", x, readout.likelihood(x, 175, 75));
        }
        assert!(readout.likelihood(lower, 175, 75).exp().approx_eq(&0.0));
        assert!(readout.likelihood(upper, 175, 75).exp().approx_eq(&0.0));
    }
}
<|endoftext|>


<commit_before>use nix::libc::c_void;

use super::{PixelFormat, SwapBuffersError};

#[cfg_attr(feature = "cargo-clippy", allow(clippy))]
#[allow(missing_docs)]
pub(crate) mod ffi {
    include!(concat!(env!("OUT_DIR"), "/gl_bindings.rs"));
}

pub use self::ffi::Gles2;

/// Trait that describes objects that have an OpenGL context
/// and can be used to render upon
pub trait GLGraphicsBackend {
    /// Swaps buffers at the end of a frame.
    fn swap_buffers(&self) -> Result<(), SwapBuffersError>;

    /// Returns the address of an OpenGL function.
    ///
    /// Supposes that the context has been made current before this function is called.
    unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void;

    /// Returns the dimensions of the window, or screen, etc in points.
    ///
    /// That are the scaled pixels of the underlying graphics backend.
    /// For nested compositors this will respect the scaling of the root compositor.
    /// For drawing directly onto hardware this unit will be equal to actual pixels.
    fn get_framebuffer_dimensions(&self) -> (u32, u32);

    /// Returns true if the OpenGL context is the current one in the thread.
    fn is_current(&self) -> bool;

    /// Makes the OpenGL context the current context in the current thread.
    ///
    /// # Unsafety
    ///
    /// This function is marked unsafe, because the context cannot be made current
    /// on multiple threads.
    unsafe fn make_current(&self) -> Result<(), SwapBuffersError>;

    /// Returns the pixel format of the main framebuffer of the context.
    fn get_pixel_format(&self) -> PixelFormat;
}

/// Loads a Raw GLES Interface for a given `GLGraphicsBackend`
///
/// This remains valid as long as the underlying `GLGraphicsBackend` is alive
/// and may only be used in combination with the backend. Using this with any
/// other gl context may cause undefined behavior.
pub fn load_raw_gl<B: GLGraphicsBackend>(backend: &B) -> Gles2 {
    Gles2::load_with(|s| unsafe { backend.get_proc_address(s) as *const _ })
}
<commit_msg>docs: add graphics/gl<commit_after>//! Gl rendering types

use nix::libc::c_void;

use super::{PixelFormat, SwapBuffersError};

#[cfg_attr(feature = "cargo-clippy", allow(clippy))]
#[allow(missing_docs)]
pub(crate) mod ffi {
    include!(concat!(env!("OUT_DIR"), "/gl_bindings.rs"));
}

pub use self::ffi::Gles2;

/// Trait that describes objects that have an OpenGL context
/// and can be used to render upon
pub trait GLGraphicsBackend {
    /// Swaps buffers at the end of a frame.
    fn swap_buffers(&self) -> Result<(), SwapBuffersError>;

    /// Returns the address of an OpenGL function.
    ///
    /// Supposes that the context has been made current before this function is called.
    unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void;

    /// Returns the dimensions of the window, or screen, etc in points.
    ///
    /// That are the scaled pixels of the underlying graphics backend.
    /// For nested compositors this will respect the scaling of the root compositor.
    /// For drawing directly onto hardware this unit will be equal to actual pixels.
    fn get_framebuffer_dimensions(&self) -> (u32, u32);

    /// Returns true if the OpenGL context is the current one in the thread.
    fn is_current(&self) -> bool;

    /// Makes the OpenGL context the current context in the current thread.
    ///
    /// # Unsafety
    ///
    /// This function is marked unsafe, because the context cannot be made current
    /// on multiple threads.
    unsafe fn make_current(&self) -> Result<(), SwapBuffersError>;

    /// Returns the pixel format of the main framebuffer of the context.
    fn get_pixel_format(&self) -> PixelFormat;
}

/// Loads a Raw GLES Interface for a given `GLGraphicsBackend`
///
/// This remains valid as long as the underlying `GLGraphicsBackend` is alive
/// and may only be used in combination with the backend. Using this with any
/// other gl context may cause undefined behavior.
pub fn load_raw_gl<B: GLGraphicsBackend>(backend: &B) -> Gles2 {
    Gles2::load_with(|s| unsafe { backend.get_proc_address(s) as *const _ })
}
<|endoftext|>


<commit_before>//!
//! Abstraction of different session APIs.
//!
//! Sessions provide a way for multiple graphical systems to run in parallel by providing
//! mechanisms to switch between and handle device access and permissions for every running
//! instance.
//!
//! They are crucial to allow unprivileged processes to use graphical or input devices.
//!
//! The following mechanisms are currently provided:
//!     - direct - legacy tty / virtual terminal kernel API
//!
use nix::fcntl::OFlag;
use std::{
    cell::RefCell,
    os::unix::io::RawFd,
    path::Path,
    rc::Rc,
    sync::{Arc, Mutex},
};

/// General session interface.
///
/// Provides a way to open and close devices and change the active vt.
pub trait Session {
    /// Error type of the implementation
    type Error: AsErrno;

    /// Opens a device at the given `path` with the given flags.
    ///
    /// Returns a raw file descriptor
    fn open(&mut self, path: &Path, flags: OFlag) -> Result<RawFd, Self::Error>;
    /// Close a previously opened file descriptor
    fn close(&mut self, fd: RawFd) -> Result<(), Self::Error>;

    /// Change the currently active virtual terminal
    fn change_vt(&mut self, vt: i32) -> Result<(), Self::Error>;

    /// Check if this session is currently active
    fn is_active(&self) -> bool;
    /// Which seat this session is on
    fn seat(&self) -> String;
}

/// Interface for registering for notifications for a given session.
///
/// Part of the session API which allows to get notified, when the given session
/// gets paused or becomes active again. Any object implementing the `SessionObserver` trait
/// may be registered.
pub trait SessionNotifier {
    /// Id type of registered observers
    type Id: PartialEq + Eq;

    /// Registers a given `SessionObserver`.
    ///
    /// Returns an id of the inserted observer, can be used to remove it again.
    fn register<S: SessionObserver + 'static, A: AsSessionObserver<S>>(&mut self, signal: &mut A)
        -> Self::Id;
    /// Removes an observer by its given id from `SessionNotifier::register`.
    fn unregister(&mut self, signal: Self::Id);

    /// Check if this session is currently active
    fn is_active(&self) -> bool;
    /// Which seat this session is on
    fn seat(&self) -> &str;
}

/// Trait describing the ability to return a `SessionObserver` related to Self.
///
/// The returned `SessionObserver` is responsible to handle the `pause` and `activate` signals.
pub trait AsSessionObserver<S: SessionObserver + 'static> {
    /// Create a `SessionObserver` linked to this object
    fn observer(&mut self) -> S;
}

impl<T: SessionObserver + Clone + 'static> AsSessionObserver<T> for T {
    fn observer(&mut self) -> T {
        self.clone()
    }
}

/// Trait describing the ability to be notified when the session pauses or becomes active again.
///
/// It might be impossible to interact with devices while the session is disabled.
/// This interface provides callbacks for when that happens.
pub trait SessionObserver {
    /// Session/Device is about to be paused.
    ///
    /// If only a specific device shall be closed a device number in the form of
    /// (major, minor) is provided. All observers not using the specified device should
    /// ignore the signal in that case.
    fn pause(&mut self, device: Option<(u32, u32)>);
    /// Session/Device got active again
    ///
    /// If only a specific device shall be activated again a device number in the form of
    /// `(major, major, Option<RawFd>)` is provided. Optionally the session may decide to replace
    /// the currently open file descriptor of the device with a new one. In that case the old one
    /// should not be used anymore and be closed. All observers not using the specified device should
    /// ignore the signal in that case.
    fn activate(&mut self, device: Option<(u32, u32, Option<RawFd>)>);
}

impl Session for () {
    type Error = ();

    fn open(&mut self, _path: &Path, _flags: OFlag) -> Result<RawFd, Self::Error> {
        Err(())
    }
    fn close(&mut self, _fd: RawFd) -> Result<(), Self::Error> {
        Err(())
    }

    fn change_vt(&mut self, _vt: i32) -> Result<(), Self::Error> {
        Err(())
    }

    fn is_active(&self) -> bool {
        false
    }
    fn seat(&self) -> String {
        String::from("seat0")
    }
}

impl<S: Session> Session for Rc<RefCell<S>> {
    type Error = S::Error;

    fn open(&mut self, path: &Path, flags: OFlag) -> Result<RawFd, Self::Error> {
        self.borrow_mut().open(path, flags)
    }

    fn close(&mut self, fd: RawFd) -> Result<(), Self::Error> {
        self.borrow_mut().close(fd)
    }

    fn change_vt(&mut self, vt: i32) -> Result<(), Self::Error> {
        self.borrow_mut().change_vt(vt)
    }

    fn is_active(&self) -> bool {
        self.borrow().is_active()
    }

    fn seat(&self) -> String {
        self.borrow().seat()
    }
}

impl<S: Session> Session for Arc<Mutex<S>> {
    type Error = S::Error;

    fn open(&mut self, path: &Path, flags: OFlag) -> Result<RawFd, Self::Error> {
        self.lock().unwrap().open(path, flags)
    }

    fn close(&mut self, fd: RawFd) -> Result<(), Self::Error> {
        self.lock().unwrap().close(fd)
    }

    fn change_vt(&mut self, vt: i32) -> Result<(), Self::Error> {
        self.lock().unwrap().change_vt(vt)
    }

    fn is_active(&self) -> bool {
        self.lock().unwrap().is_active()
    }

    fn seat(&self) -> String {
        self.lock().unwrap().seat()
    }
}

/// Allows errors to be described by an error number
pub trait AsErrno: ::std::fmt::Debug {
    /// Returns the error number representing this error if any
    fn as_errno(&self) -> Option<i32>;
}

impl AsErrno for () {
    fn as_errno(&self) -> Option<i32> {
        None
    }
}

pub mod auto;
mod dbus;
pub mod direct;
pub use self::dbus::*;
<commit_msg>session: export OFlag<commit_after>//!
//! Abstraction of different session APIs.
//!
//! Sessions provide a way for multiple graphical systems to run in parallel by providing
//! mechanisms to switch between and handle device access and permissions for every running
//! instance.
//!
//! They are crucial to allow unprivileged processes to use graphical or input devices.
//!
//! The following mechanisms are currently provided:
//!     - direct - legacy tty / virtual terminal kernel API
//!
pub use nix::fcntl::OFlag;
use std::{
    cell::RefCell,
    os::unix::io::RawFd,
    path::Path,
    rc::Rc,
    sync::{Arc, Mutex},
};

/// General session interface.
///
/// Provides a way to open and close devices and change the active vt.
pub trait Session {
    /// Error type of the implementation
    type Error: AsErrno;

    /// Opens a device at the given `path` with the given flags.
    ///
    /// Returns a raw file descriptor
    fn open(&mut self, path: &Path, flags: OFlag) -> Result<RawFd, Self::Error>;
    /// Close a previously opened file descriptor
    fn close(&mut self, fd: RawFd) -> Result<(), Self::Error>;

    /// Change the currently active virtual terminal
    fn change_vt(&mut self, vt: i32) -> Result<(), Self::Error>;

    /// Check if this session is currently active
    fn is_active(&self) -> bool;
    /// Which seat this session is on
    fn seat(&self) -> String;
}

/// Interface for registering for notifications for a given session.
///
/// Part of the session API which allows to get notified, when the given session
/// gets paused or becomes active again. Any object implementing the `SessionObserver` trait
/// may be registered.
pub trait SessionNotifier {
    /// Id type of registered observers
    type Id: PartialEq + Eq;

    /// Registers a given `SessionObserver`.
    ///
    /// Returns an id of the inserted observer, can be used to remove it again.
    fn register<S: SessionObserver + 'static, A: AsSessionObserver<S>>(&mut self, signal: &mut A)
        -> Self::Id;
    /// Removes an observer by its given id from `SessionNotifier::register`.
    fn unregister(&mut self, signal: Self::Id);

    /// Check if this session is currently active
    fn is_active(&self) -> bool;
    /// Which seat this session is on
    fn seat(&self) -> &str;
}

/// Trait describing the ability to return a `SessionObserver` related to Self.
///
/// The returned `SessionObserver` is responsible to handle the `pause` and `activate` signals.
pub trait AsSessionObserver<S: SessionObserver + 'static> {
    /// Create a `SessionObserver` linked to this object
    fn observer(&mut self) -> S;
}

impl<T: SessionObserver + Clone + 'static> AsSessionObserver<T> for T {
    fn observer(&mut self) -> T {
        self.clone()
    }
}

/// Trait describing the ability to be notified when the session pauses or becomes active again.
///
/// It might be impossible to interact with devices while the session is disabled.
/// This interface provides callbacks for when that happens.
pub trait SessionObserver {
    /// Session/Device is about to be paused.
    ///
    /// If only a specific device shall be closed a device number in the form of
    /// (major, minor) is provided. All observers not using the specified device should
    /// ignore the signal in that case.
    fn pause(&mut self, device: Option<(u32, u32)>);
    /// Session/Device got active again
    ///
    /// If only a specific device shall be activated again a device number in the form of
    /// `(major, major, Option<RawFd>)` is provided. Optionally the session may decide to replace
    /// the currently open file descriptor of the device with a new one. In that case the old one
    /// should not be used anymore and be closed. All observers not using the specified device should
    /// ignore the signal in that case.
    fn activate(&mut self, device: Option<(u32, u32, Option<RawFd>)>);
}

impl Session for () {
    type Error = ();

    fn open(&mut self, _path: &Path, _flags: OFlag) -> Result<RawFd, Self::Error> {
        Err(())
    }
    fn close(&mut self, _fd: RawFd) -> Result<(), Self::Error> {
        Err(())
    }

    fn change_vt(&mut self, _vt: i32) -> Result<(), Self::Error> {
        Err(())
    }

    fn is_active(&self) -> bool {
        false
    }
    fn seat(&self) -> String {
        String::from("seat0")
    }
}

impl<S: Session> Session for Rc<RefCell<S>> {
    type Error = S::Error;

    fn open(&mut self, path: &Path, flags: OFlag) -> Result<RawFd, Self::Error> {
        self.borrow_mut().open(path, flags)
    }

    fn close(&mut self, fd: RawFd) -> Result<(), Self::Error> {
        self.borrow_mut().close(fd)
    }

    fn change_vt(&mut self, vt: i32) -> Result<(), Self::Error> {
        self.borrow_mut().change_vt(vt)
    }

    fn is_active(&self) -> bool {
        self.borrow().is_active()
    }

    fn seat(&self) -> String {
        self.borrow().seat()
    }
}

impl<S: Session> Session for Arc<Mutex<S>> {
    type Error = S::Error;

    fn open(&mut self, path: &Path, flags: OFlag) -> Result<RawFd, Self::Error> {
        self.lock().unwrap().open(path, flags)
    }

    fn close(&mut self, fd: RawFd) -> Result<(), Self::Error> {
        self.lock().unwrap().close(fd)
    }

    fn change_vt(&mut self, vt: i32) -> Result<(), Self::Error> {
        self.lock().unwrap().change_vt(vt)
    }

    fn is_active(&self) -> bool {
        self.lock().unwrap().is_active()
    }

    fn seat(&self) -> String {
        self.lock().unwrap().seat()
    }
}

/// Allows errors to be described by an error number
pub trait AsErrno: ::std::fmt::Debug {
    /// Returns the error number representing this error if any
    fn as_errno(&self) -> Option<i32>;
}

impl AsErrno for () {
    fn as_errno(&self) -> Option<i32> {
        None
    }
}

pub mod auto;
mod dbus;
pub mod direct;
pub use self::dbus::*;
<|endoftext|>


<commit_before>use std::env;
use std::process::{Command, Stdio};
use std::io::Write;

fn get_systemd_unit_property(unit: &str, prop: &str) -> String {
    String::from_utf8_lossy(&Command::new("systemctl")
                            .arg("show")
                            .arg(unit)
                            .arg("--property")
                            .arg(prop)
                            .output()
                            .unwrap()
                            .stdout[prop.len() + 1..])
        .trim_right_matches('\n')
        .to_owned()
}

fn main() {
    let unit = match env::args().nth(1) {
        Some(unit) => unit,
        None => {
            println!("Usage: mail-on-failure <unit>");
            return;
        }
    };

    let mut user = get_systemd_unit_property(&*unit, "User");
    if user.len() == 0 {
        user = "root".to_owned();
    }

    let job_env = get_systemd_unit_property(&*unit, "Environment");
    for pair in job_env.split(' ') {
        let mut p = pair.splitn(2, '=');
        if let (Some(name), Some(value)) = (p.next(), p.next()) {
            if name == "MAILTO" {
                user = value.to_owned();
                break;
            }
        }
    }

    if user.len() == 0 {
        return;
    }

    let mut hostname = String::from_utf8_lossy(&Command::new("uname")
        .arg("-n")
        .output()
        .unwrap()
        .stdout[..])
        .trim_right_matches('\n')
        .to_owned();

    if hostname.len() == 0 {
        hostname = "localhost".to_owned();
    }

    let mut head = String::new();
    head.push_str("From: root (systemd-cron)\nTo: ");
    head.push_str(&*user);
    head.push_str("\nSubject: [");
    head.push_str(&*hostname);
    head.push_str("] job ");
    head.push_str(&*unit);
    head.push_str(r###" failed
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Auto-Submitted: auto-generated

"###);

    let status = Command::new("systemctl")
        .arg("status")
        .arg(&*unit)
        .output()
        .unwrap();

    let mut mailer = Command::new("sendmail")
        .arg("-i")
        .arg("-B8BITMIME")
        .arg(&*user)
        .stdin(Stdio::piped())
        .spawn()
        .unwrap();

    println!("{}", head);
    println!("{}", String::from_utf8_lossy(&*status.stdout));
    if let Some(ref mut stdin) = mailer.stdin {
        let _ = stdin.write_all(head.as_bytes()).and_then(|_|
                stdin.write_all(&*status.stdout));
    }

    mailer.wait().unwrap();
}
<commit_msg>log errors during email sending<commit_after>#[macro_use]
extern crate log;
extern crate kernlog;

use std::env;
use std::process::{Command, Stdio};
use std::io::Write;

macro_rules! try_log {
    ($exp:expr) => {
        match $exp {
            Ok(v) => v,
            Err(e) => { warn!("{}", e); return; }
        }
    }
}

fn get_systemd_unit_property(unit: &str, prop: &str) -> String {
    String::from_utf8_lossy(&try_log!(Command::new("systemctl")
                            .arg("show")
                            .arg(unit)
                            .arg("--property")
                            .arg(prop)
                            .output())
                            .stdout[prop.len() + 1..])
        .trim_right_matches('\n')
        .to_owned()
}

fn main() {
    kernlog::init().unwrap();

    let unit = match env::args().nth(1) {
        Some(unit) => unit,
        None => {
            println!("Usage: mail-on-failure <unit>");
            return;
        }
    };

    let mut user = get_systemd_unit_property(&*unit, "User");
    if user.len() == 0 {
        user = "root".to_owned();
    }

    let job_env = get_systemd_unit_property(&*unit, "Environment");
    for pair in job_env.split(' ') {
        let mut p = pair.splitn(2, '=');
        if let (Some(name), Some(value)) = (p.next(), p.next()) {
            if name == "MAILTO" {
                user = value.to_owned();
                break;
            }
        }
    }

    if user.len() == 0 {
        return;
    }

    let mut hostname = String::from_utf8_lossy(&try_log!(Command::new("uname")
        .arg("-n")
        .output())
        .stdout[..])
        .trim_right_matches('\n')
        .to_owned();

    if hostname.len() == 0 {
        hostname = "localhost".to_owned();
    }

    let mut head = String::new();
    head.push_str("From: root (systemd-cron)\nTo: ");
    head.push_str(&*user);
    head.push_str("\nSubject: [");
    head.push_str(&*hostname);
    head.push_str("] job ");
    head.push_str(&*unit);
    head.push_str(r###" failed
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Auto-Submitted: auto-generated

"###);

    let status = Command::new("systemctl")
        .arg("status")
        .arg(&*unit)
        .output()
        .unwrap();

    let mut mailer = try_log!(Command::new("sendmail")
        .arg("-i")
        .arg("-B8BITMIME")
        .arg(&*user)
        .stdin(Stdio::piped())
        .spawn());

    if let Some(ref mut stdin) = mailer.stdin {
        try_log!(stdin.write_all(head.as_bytes()).and_then(|_|
                 stdin.write_all(&*status.stdout)));
    }

    mailer.wait().unwrap();
}
<|endoftext|>


<commit_before>//! Shim which is passed to Cargo as "rustc" when running the bootstrap.
//!
//! This shim will take care of some various tasks that our build process
//! requires that Cargo can't quite do through normal configuration:
//!
//! 1. When compiling build scripts and build dependencies, we need a guaranteed
//!    full standard library available. The only compiler which actually has
//!    this is the snapshot, so we detect this situation and always compile with
//!    the snapshot compiler.
//! 2. We pass a bunch of `--cfg` and other flags based on what we're compiling
//!    (and this slightly differs based on a whether we're using a snapshot or
//!    not), so we do that all here.
//!
//! This may one day be replaced by RUSTFLAGS, but the dynamic nature of
//! switching compilers for the bootstrap and for build scripts will probably
//! never get replaced.

use std::env;
use std::path::PathBuf;
use std::process::Command;
use std::str::FromStr;
use std::time::Instant;

fn main() {
    let args = env::args_os().skip(1).collect::<Vec<_>>();

    // Detect whether or not we're a build script depending on whether --target
    // is passed (a bit janky...)
    let target = args.windows(2).find(|w| &*w[0] == "--target").and_then(|w| w[1].to_str());
    let version = args.iter().find(|w| &**w == "-vV");

    let verbose = match env::var("RUSTC_VERBOSE") {
        Ok(s) => usize::from_str(&s).expect("RUSTC_VERBOSE should be an integer"),
        Err(_) => 0,
    };

    // Use a different compiler for build scripts, since there may not yet be a
    // libstd for the real compiler to use. However, if Cargo is attempting to
    // determine the version of the compiler, the real compiler needs to be
    // used. Currently, these two states are differentiated based on whether
    // --target and -vV is/isn't passed.
    let (rustc, libdir) = if target.is_none() && version.is_none() {
        ("RUSTC_SNAPSHOT", "RUSTC_SNAPSHOT_LIBDIR")
    } else {
        ("RUSTC_REAL", "RUSTC_LIBDIR")
    };
    let stage = env::var("RUSTC_STAGE").expect("RUSTC_STAGE was not set");
    let sysroot = env::var_os("RUSTC_SYSROOT").expect("RUSTC_SYSROOT was not set");
    let on_fail = env::var_os("RUSTC_ON_FAIL").map(Command::new);

    let rustc = env::var_os(rustc).unwrap_or_else(|| panic!("{:?} was not set", rustc));
    let libdir = env::var_os(libdir).unwrap_or_else(|| panic!("{:?} was not set", libdir));
    let mut dylib_path = bootstrap::util::dylib_path();
    dylib_path.insert(0, PathBuf::from(&libdir));

    let mut cmd = Command::new(rustc);
    cmd.args(&args).env(bootstrap::util::dylib_path_var(), env::join_paths(&dylib_path).unwrap());

    // Get the name of the crate we're compiling, if any.
    let crate_name =
        args.windows(2).find(|args| args[0] == "--crate-name").and_then(|args| args[1].to_str());

    if let Some(crate_name) = crate_name {
        if let Some(target) = env::var_os("RUSTC_TIME") {
            if target == "all"
                || target.into_string().unwrap().split(',').any(|c| c.trim() == crate_name)
            {
                cmd.arg("-Ztime");
            }
        }
    }

    // Print backtrace in case of ICE
    if env::var("RUSTC_BACKTRACE_ON_ICE").is_ok() && env::var("RUST_BACKTRACE").is_err() {
        cmd.env("RUST_BACKTRACE", "1");
    }

    if let Ok(lint_flags) = env::var("RUSTC_LINT_FLAGS") {
        cmd.args(lint_flags.split_whitespace());
    }

    if target.is_some() {
        // The stage0 compiler has a special sysroot distinct from what we
        // actually downloaded, so we just always pass the `--sysroot` option,
        // unless one is already set.
        if !args.iter().any(|arg| arg == "--sysroot") {
            cmd.arg("--sysroot").arg(&sysroot);
        }

        // If we're compiling specifically the `panic_abort` crate then we pass
        // the `-C panic=abort` option. Note that we do not do this for any
        // other crate intentionally as this is the only crate for now that we
        // ship with panic=abort.
        //
        // This... is a bit of a hack how we detect this. Ideally this
        // information should be encoded in the crate I guess? Would likely
        // require an RFC amendment to RFC 1513, however.
        //
        // `compiler_builtins` are unconditionally compiled with panic=abort to
        // workaround undefined references to `rust_eh_unwind_resume` generated
        // otherwise, see issue https://github.com/rust-lang/rust/issues/43095.
        if crate_name == Some("panic_abort")
            || crate_name == Some("compiler_builtins") && stage != "0"
        {
            cmd.arg("-C").arg("panic=abort");
        }
    } else {
        // FIXME(rust-lang/cargo#5754) we shouldn't be using special env vars
        // here, but rather Cargo should know what flags to pass rustc itself.

        // Override linker if necessary.
        if let Ok(host_linker) = env::var("RUSTC_HOST_LINKER") {
            cmd.arg(format!("-Clinker={}", host_linker));
        }
        if env::var_os("RUSTC_HOST_FUSE_LD_LLD").is_some() {
            cmd.arg("-Clink-args=-fuse-ld=lld");
        }

        if let Ok(s) = env::var("RUSTC_HOST_CRT_STATIC") {
            if s == "true" {
                cmd.arg("-C").arg("target-feature=+crt-static");
            }
            if s == "false" {
                cmd.arg("-C").arg("target-feature=-crt-static");
            }
        }
    }

    if let Ok(map) = env::var("RUSTC_DEBUGINFO_MAP") {
        cmd.arg("--remap-path-prefix").arg(&map);
    }

    // Force all crates compiled by this compiler to (a) be unstable and (b)
    // allow the `rustc_private` feature to link to other unstable crates
    // also in the sysroot. We also do this for host crates, since those
    // may be proc macros, in which case we might ship them.
    if env::var_os("RUSTC_FORCE_UNSTABLE").is_some() && (stage != "0" || target.is_some()) {
        cmd.arg("-Z").arg("force-unstable-if-unmarked");
    }

    if verbose > 1 {
        eprintln!(
            "rustc command: {:?}={:?} {:?}",
            bootstrap::util::dylib_path_var(),
            env::join_paths(&dylib_path).unwrap(),
            cmd,
        );
        eprintln!("sysroot: {:?}", sysroot);
        eprintln!("libdir: {:?}", libdir);
    }

    let start = Instant::now();
    let status = {
        let errmsg = format!("\nFailed to run:\n{:?}\n-------------", cmd);
        cmd.status().expect(&errmsg)
    };

    if env::var_os("RUSTC_PRINT_STEP_TIMINGS").is_some() {
        if let Some(crate_name) = crate_name {
            let dur = start.elapsed();
            let is_test = args.iter().any(|a| a == "--test");
            eprintln!(
                "[RUSTC-TIMING] {} test:{} {}.{:03}",
                crate_name,
                is_test,
                dur.as_secs(),
                dur.subsec_millis()
            );
        }
    }

    if status.success() {
        std::process::exit(0);
        // note: everything below here is unreachable. do not put code that
        // should run on success, after this block.
    }
    if verbose > 0 {
        println!("\nDid not run successfully: {}\n{:?}\n-------------", status, cmd);
    }

    if let Some(mut on_fail) = on_fail {
        on_fail.status().expect("Could not run the on_fail command");
    }

    // Preserve the exit code. In case of signal, exit with 0xfe since it's
    // awkward to preserve this status in a cross-platform way.
    match status.code() {
        Some(i) => std::process::exit(i),
        None => {
            eprintln!("rustc exited with {}", status);
            std::process::exit(0xfe);
        }
    }
}
<commit_msg>Print out env vars related to Rust on (sufficiently verbose) rustc invocations.<commit_after>//! Shim which is passed to Cargo as "rustc" when running the bootstrap.
//!
//! This shim will take care of some various tasks that our build process
//! requires that Cargo can't quite do through normal configuration:
//!
//! 1. When compiling build scripts and build dependencies, we need a guaranteed
//!    full standard library available. The only compiler which actually has
//!    this is the snapshot, so we detect this situation and always compile with
//!    the snapshot compiler.
//! 2. We pass a bunch of `--cfg` and other flags based on what we're compiling
//!    (and this slightly differs based on a whether we're using a snapshot or
//!    not), so we do that all here.
//!
//! This may one day be replaced by RUSTFLAGS, but the dynamic nature of
//! switching compilers for the bootstrap and for build scripts will probably
//! never get replaced.

use std::env;
use std::path::PathBuf;
use std::process::Command;
use std::str::FromStr;
use std::time::Instant;

fn main() {
    let args = env::args_os().skip(1).collect::<Vec<_>>();

    // Detect whether or not we're a build script depending on whether --target
    // is passed (a bit janky...)
    let target = args.windows(2).find(|w| &*w[0] == "--target").and_then(|w| w[1].to_str());
    let version = args.iter().find(|w| &**w == "-vV");

    let verbose = match env::var("RUSTC_VERBOSE") {
        Ok(s) => usize::from_str(&s).expect("RUSTC_VERBOSE should be an integer"),
        Err(_) => 0,
    };

    // Use a different compiler for build scripts, since there may not yet be a
    // libstd for the real compiler to use. However, if Cargo is attempting to
    // determine the version of the compiler, the real compiler needs to be
    // used. Currently, these two states are differentiated based on whether
    // --target and -vV is/isn't passed.
    let (rustc, libdir) = if target.is_none() && version.is_none() {
        ("RUSTC_SNAPSHOT", "RUSTC_SNAPSHOT_LIBDIR")
    } else {
        ("RUSTC_REAL", "RUSTC_LIBDIR")
    };
    let stage = env::var("RUSTC_STAGE").expect("RUSTC_STAGE was not set");
    let sysroot = env::var_os("RUSTC_SYSROOT").expect("RUSTC_SYSROOT was not set");
    let on_fail = env::var_os("RUSTC_ON_FAIL").map(Command::new);

    let rustc = env::var_os(rustc).unwrap_or_else(|| panic!("{:?} was not set", rustc));
    let libdir = env::var_os(libdir).unwrap_or_else(|| panic!("{:?} was not set", libdir));
    let mut dylib_path = bootstrap::util::dylib_path();
    dylib_path.insert(0, PathBuf::from(&libdir));

    let mut cmd = Command::new(rustc);
    cmd.args(&args).env(bootstrap::util::dylib_path_var(), env::join_paths(&dylib_path).unwrap());

    // Get the name of the crate we're compiling, if any.
    let crate_name =
        args.windows(2).find(|args| args[0] == "--crate-name").and_then(|args| args[1].to_str());

    if let Some(crate_name) = crate_name {
        if let Some(target) = env::var_os("RUSTC_TIME") {
            if target == "all"
                || target.into_string().unwrap().split(',').any(|c| c.trim() == crate_name)
            {
                cmd.arg("-Ztime");
            }
        }
    }

    // Print backtrace in case of ICE
    if env::var("RUSTC_BACKTRACE_ON_ICE").is_ok() && env::var("RUST_BACKTRACE").is_err() {
        cmd.env("RUST_BACKTRACE", "1");
    }

    if let Ok(lint_flags) = env::var("RUSTC_LINT_FLAGS") {
        cmd.args(lint_flags.split_whitespace());
    }

    if target.is_some() {
        // The stage0 compiler has a special sysroot distinct from what we
        // actually downloaded, so we just always pass the `--sysroot` option,
        // unless one is already set.
        if !args.iter().any(|arg| arg == "--sysroot") {
            cmd.arg("--sysroot").arg(&sysroot);
        }

        // If we're compiling specifically the `panic_abort` crate then we pass
        // the `-C panic=abort` option. Note that we do not do this for any
        // other crate intentionally as this is the only crate for now that we
        // ship with panic=abort.
        //
        // This... is a bit of a hack how we detect this. Ideally this
        // information should be encoded in the crate I guess? Would likely
        // require an RFC amendment to RFC 1513, however.
        //
        // `compiler_builtins` are unconditionally compiled with panic=abort to
        // workaround undefined references to `rust_eh_unwind_resume` generated
        // otherwise, see issue https://github.com/rust-lang/rust/issues/43095.
        if crate_name == Some("panic_abort")
            || crate_name == Some("compiler_builtins") && stage != "0"
        {
            cmd.arg("-C").arg("panic=abort");
        }
    } else {
        // FIXME(rust-lang/cargo#5754) we shouldn't be using special env vars
        // here, but rather Cargo should know what flags to pass rustc itself.

        // Override linker if necessary.
        if let Ok(host_linker) = env::var("RUSTC_HOST_LINKER") {
            cmd.arg(format!("-Clinker={}", host_linker));
        }
        if env::var_os("RUSTC_HOST_FUSE_LD_LLD").is_some() {
            cmd.arg("-Clink-args=-fuse-ld=lld");
        }

        if let Ok(s) = env::var("RUSTC_HOST_CRT_STATIC") {
            if s == "true" {
                cmd.arg("-C").arg("target-feature=+crt-static");
            }
            if s == "false" {
                cmd.arg("-C").arg("target-feature=-crt-static");
            }
        }
    }

    if let Ok(map) = env::var("RUSTC_DEBUGINFO_MAP") {
        cmd.arg("--remap-path-prefix").arg(&map);
    }

    // Force all crates compiled by this compiler to (a) be unstable and (b)
    // allow the `rustc_private` feature to link to other unstable crates
    // also in the sysroot. We also do this for host crates, since those
    // may be proc macros, in which case we might ship them.
    if env::var_os("RUSTC_FORCE_UNSTABLE").is_some() && (stage != "0" || target.is_some()) {
        cmd.arg("-Z").arg("force-unstable-if-unmarked");
    }

    if verbose > 1 {
        let rust_env_vars =
            env::vars().filter(|(k, _)| k.starts_with("RUST") || k.starts_with("CARGO"));
        for (i, (k, v)) in rust_env_vars.enumerate() {
            eprintln!("rustc env[{}]: {:?}={:?}", i, k, v);
        }
        eprintln!("rustc working directory: {}", env::current_dir().unwrap().display());
        eprintln!(
            "rustc command: {:?}={:?} {:?}",
            bootstrap::util::dylib_path_var(),
            env::join_paths(&dylib_path).unwrap(),
            cmd,
        );
        eprintln!("sysroot: {:?}", sysroot);
        eprintln!("libdir: {:?}", libdir);
    }

    let start = Instant::now();
    let status = {
        let errmsg = format!("\nFailed to run:\n{:?}\n-------------", cmd);
        cmd.status().expect(&errmsg)
    };

    if env::var_os("RUSTC_PRINT_STEP_TIMINGS").is_some() {
        if let Some(crate_name) = crate_name {
            let dur = start.elapsed();
            let is_test = args.iter().any(|a| a == "--test");
            eprintln!(
                "[RUSTC-TIMING] {} test:{} {}.{:03}",
                crate_name,
                is_test,
                dur.as_secs(),
                dur.subsec_millis()
            );
        }
    }

    if status.success() {
        std::process::exit(0);
        // note: everything below here is unreachable. do not put code that
        // should run on success, after this block.
    }
    if verbose > 0 {
        println!("\nDid not run successfully: {}\n{:?}\n-------------", status, cmd);
    }

    if let Some(mut on_fail) = on_fail {
        on_fail.status().expect("Could not run the on_fail command");
    }

    // Preserve the exit code. In case of signal, exit with 0xfe since it's
    // awkward to preserve this status in a cross-platform way.
    match status.code() {
        Some(i) => std::process::exit(i),
        None => {
            eprintln!("rustc exited with {}", status);
            std::process::exit(0xfe);
        }
    }
}
<|endoftext|>


<commit_before>// "Tifflin" Kernel
// - By John Hodge (thePowersGang)
//
// Modules/fs_fat/lib.rs
//! FAT (12/16/32) Filesystemd river
#![feature(no_std,core,linkage)]
#![no_std]
#[macro_use] extern crate core;
#[macro_use] extern crate kernel;
use kernel::prelude::*;

use kernel::vfs::{self, mount, node};
use kernel::metadevs::storage::{self,VolumeHandle,SizePrinter};
use kernel::lib::mem::aref::{ArefInner,ArefBorrow};

module_define!{FS_FAT, [VFS], init}

const FAT16_MIN_CLUSTERS: usize = 4085;
const FAT32_MIN_CLUSTERS: usize = 65525;

/// FAT Legacy (pre 32) root cluster base. Just has to be above the max cluster num for FAT16
const FATL_ROOT_CLUSTER: u32 = 0x00FF0000;

const FAT12_EOC: u16 = 0x0FFF;
const FAT16_EOC: u16 = 0xFFFF;
const FAT32_EOC: u32 = 0x00FFFFFF;

/// on-disk structures
mod on_disk;
/// Directory IO
mod dir;
/// File IO
mod file;

#[derive(Copy,Clone,Debug)]
enum Size
{
	Fat12,
	Fat16,
	Fat32,
}

/// Driver strucutre
struct Driver;

struct Filesystem
{
	inner: ArefInner<FilesystemInner>
}
impl ::core::ops::Deref for Filesystem {
	type Target = FilesystemInner;
	fn deref(&self) -> &FilesystemInner { &self.inner }
}
struct FilesystemInner
{
	vh: VolumeHandle,
	ty: Size,

	spc: usize,
	cluster_size: usize,
	/// Total number of data clusters
	cluster_count: usize,
	first_data_sector: usize,

	root_first_cluster: u32,
	root_sector_count: u32,
}

/// Inodes IDs destrucure into two 28-bit cluster IDs, and a 16-bit dir offset
#[derive(Debug)]
struct InodeRef
{
	dir_first_cluster: u32,
	dir_offset: u16,
	first_cluster: u32,
}

/// Iterable cluster list
enum ClusterList {
	Range(::core::ops::Range<u32>),
	Chained(ArefBorrow<FilesystemInner>, u32),
}

static S_DRIVER: Driver = Driver;

fn init()
{
	let h = mount::DriverRegistration::new("fat", &S_DRIVER);
	// TODO: Remember the registration for unloading
	::core::mem::forget(h);
}

impl mount::Driver for Driver
{
	fn detect(&self, vol: &VolumeHandle) -> vfs::Result<usize> {
		let bs = {
			let mut bs = [0u8; 512];
			try!( vol.read_blocks(0, &mut bs) );
			on_disk::BootSect::read(&bs)
			};

		let bps = bs.common().bps;
		let spc = bs.common().spc;
		let media_desc = bs.common().media_descriptor;

		if bps == 0 || spc == 0 || media_desc < 0xf0 {
			Ok(0)
		}
		else {
			Ok(1)
		}
	}
	fn mount(&self, vol: VolumeHandle) -> vfs::Result<Box<mount::Filesystem>> {
		let bs = {
			let mut bs = [0u8; 512];
			try!( vol.read_blocks(0, &mut bs) );
			on_disk::BootSect::read(&mut &bs[..])
			};
		let bs_c = bs.common();
		if bs_c.bps != 512 {
			return Err(vfs::Error::Unknown("TODO: non 512-byte sector FAT"))
		}
		if bs_c.fat_count == 0 {
			return Err(vfs::Error::Unknown("FAT Count is 0"));
		}

		log_debug!("Label: {:?}", ::kernel::lib::RawString(&bs.tail_common().label));

		let bps = bs_c.bps as usize;
		let spc = bs_c.spc as usize;

		let root_dir_sectors = (bs_c.files_in_root as usize*32 + bps - 1) / bps;
		let fat_size = if bs_c.fat_size_16 > 0 {
				bs_c.fat_size_16 as usize
			}
			else {
				todo!("FAT32 FAT size field");
			};
		let total_sectors = if bs_c.total_sectors_16 > 0 {
				bs_c.total_sectors_16 as usize
			}
			else {
				bs_c.total_sectors_32 as usize
			};

		// Calcualte some quantities
		let spare_fat_sectors = (bs_c.fat_count as usize - 1) * fat_size;
		let first_data_sector = bs_c.reserved_sect_count as usize
			+ fat_size + spare_fat_sectors
			+ root_dir_sectors;
		let cluster_count = (total_sectors - first_data_sector - spare_fat_sectors) / spc;

		// Determine the FAT type
		let fat_type = if cluster_count < FAT16_MIN_CLUSTERS {
				Size::Fat12
			}
			else if cluster_count < FAT32_MIN_CLUSTERS {
				Size::Fat16
			}
			else {
				Size::Fat32
			};
		log_debug!("{:?} {} sectors, Size {}", fat_type, total_sectors,
			SizePrinter((total_sectors*bs_c.bps as usize) as u64));

		Ok(Box::new(Filesystem {
			// SAFE: Saving to a Box, so won't move
			inner: unsafe { ArefInner::new(FilesystemInner {
				ty: fat_type,
				spc: spc,
				cluster_size: spc * vol.block_size(),
				cluster_count: cluster_count,
				first_data_sector: first_data_sector,
				root_first_cluster: match fat_type {
					Size::Fat32 => bs.info32().unwrap().root_cluster,
					_ => FATL_ROOT_CLUSTER as u32,
					},
				root_sector_count: root_dir_sectors as u32,

				vh: vol,
				}) },
			}))
	}
}

type Cluster = Vec<u8>;

impl FilesystemInner
{
	/// Load a cluster from disk
	// TODO: V V V
	// - Should this function lock the cluster somehow to prevent accidental overlap?
	// - Could also cache somehow (with a refcount) along with the 'writing' flag
	fn read_cluster(&self, cluster: u32, dst: &mut [u8]) -> Result<(), storage::IoError> {
		log_trace!("Filesystem::read_cluster({:#x})", cluster);
		assert_eq!(dst.len(), self.cluster_size);
		// For now, just read the bytes, screw caching
		let sector = if !is!(self.ty, Size::Fat32) && cluster >= FATL_ROOT_CLUSTER {
				// Root directory (for FAT12/16, where it was not a normal file)
				let rc = cluster - FATL_ROOT_CLUSTER;
				assert!( (rc as u64 * self.spc as u64) < self.root_sector_count as u64);
				(self.first_data_sector - self.root_sector_count as usize) as u64
				+ (rc * self.spc as u32) as u64
			}
			else {
				// Anything else
				assert!(cluster >= 2);
				assert!(cluster - 2 < self.cluster_count as u32);
				self.first_data_sector as u64 + (cluster as u64 - 2) * self.spc as u64
			};
		log_debug!("cluster = {:#x}, sector = 0x{:x}", cluster, sector);
		try!(self.vh.read_blocks(sector, dst));
		//::kernel::logging::hex_dump("FAT Cluster", &buf);
		Ok( () )
	}
	fn load_cluster(&self, cluster: u32) -> Result<Cluster, storage::IoError> {
		let mut buf: Vec<u8> = ::core::iter::repeat(0).take(self.spc * self.vh.block_size()).collect();
		try!(self.read_cluster(cluster, &mut buf));
		Ok( buf )
	}

	/// Obtain the next cluster in a chain
	fn get_next_cluster(&self, cluster: u32) -> Option<u32> {
		todo!("get_next_cluster({})", cluster);
	}
}

impl mount::Filesystem for Filesystem
{
	fn root_inode(&self) -> node::InodeId {
		(InodeRef {
			first_cluster: self.root_first_cluster,
			dir_first_cluster: 0,
			dir_offset: 0,
			}).to_id()
	}
	fn get_node_by_inode(&self, id: node::InodeId) -> Option<node::Node> {
		let r = InodeRef::from(id);
		if r.first_cluster == self.root_first_cluster {
			Some(node::Node::Dir(dir::DirNode::new_boxed(self.inner.borrow(), r.first_cluster)))
		}
		else {
			// Reading from the directory starting at r.dir_first_cluster
			// locate the file with cluster equal to r.first_cluster.
			// And use that to create the node
			let dn = dir::DirNode::new(self.inner.borrow(), r.dir_first_cluster);
			dn.find_node(r.first_cluster)
		}
	}
}

impl InodeRef
{
	fn new(c: u32, dir_c: u32) -> InodeRef {
		assert!(c     <= 0x00FF_FFFF);
		assert!(dir_c <= 0x00FF_FFFF);
		InodeRef {
			first_cluster: c,
			dir_first_cluster: dir_c,
			dir_offset: 0,
		}
	}
	fn to_id(&self) -> node::InodeId {
		assert!(self.first_cluster <= 0x00FF_FFFF);
		assert!(self.dir_first_cluster <= 0x00FF_FFFF);
		//assert!(v.dir_offset <= 0xFFFF);
		(self.first_cluster as u64)
		| (self.dir_first_cluster as u64) << 24
		| (self.dir_offset as u64) << 48
	}
}

impl From<node::InodeId> for InodeRef {
	fn from(v: node::InodeId) -> InodeRef {
		InodeRef {
			first_cluster: (v & 0x00FF_FFFF) as u32,
			dir_first_cluster: ((v >> 24) & 0x00FF_FFFF) as u32,
			dir_offset: (v >> 48) as u16,
		}
	}
}

impl ClusterList {
	pub fn chained(fs: ArefBorrow<FilesystemInner>, start: u32) -> ClusterList {
		ClusterList::Chained(fs, start)
	}
}
impl ::core::iter::Iterator for ClusterList {
	type Item = u32;
	fn next(&mut self) -> Option<u32> {
		match *self
		{
		ClusterList::Range(ref mut r) => r.next(),
		ClusterList::Chained(ref fs, ref mut next) =>
			if *next == 0 {
				None
			}
			else {
				let rv = *next;
				*next = fs.get_next_cluster(*next).unwrap_or(0);
				Some( rv )
			},
		}
	}
}
<commit_msg>Modules/FAT - Add FAT read (i.e. cluster chain following)<commit_after>// "Tifflin" Kernel
// - By John Hodge (thePowersGang)
//
// Modules/fs_fat/lib.rs
//! FAT (12/16/32) Filesystemd river
#![feature(no_std,core,linkage)]
#![no_std]
#[macro_use] extern crate core;
#[macro_use] extern crate kernel;
use kernel::prelude::*;

use kernel::vfs::{self, mount, node};
use kernel::metadevs::storage::{self,VolumeHandle,SizePrinter};
use kernel::lib::mem::aref::{ArefInner,ArefBorrow};

module_define!{FS_FAT, [VFS], init}

const FAT16_MIN_CLUSTERS: usize = 4085;
const FAT32_MIN_CLUSTERS: usize = 65525;

/// FAT Legacy (pre 32) root cluster base. Just has to be above the max cluster num for FAT16
const FATL_ROOT_CLUSTER: u32 = 0x00FF0000;

const FAT12_EOC: u16 = 0x0FFF;
const FAT16_EOC: u16 = 0xFFFF;
const FAT32_EOC: u32 = 0x00FFFFFF;

/// on-disk structures
mod on_disk;
/// Directory IO
mod dir;
/// File IO
mod file;

#[derive(Copy,Clone,Debug)]
enum Size
{
	Fat12,
	Fat16,
	Fat32,
}

/// Driver strucutre
struct Driver;

struct Filesystem
{
	inner: ArefInner<FilesystemInner>
}
impl ::core::ops::Deref for Filesystem {
	type Target = FilesystemInner;
	fn deref(&self) -> &FilesystemInner { &self.inner }
}
struct FilesystemInner
{
	vh: VolumeHandle,
	ty: Size,

	spc: usize,
	cluster_size: usize,
	/// Total number of data clusters
	cluster_count: usize,
	first_fat_sector: usize,
	first_data_sector: usize,

	root_first_cluster: u32,
	root_sector_count: u32,
}

/// Inodes IDs destrucure into two 28-bit cluster IDs, and a 16-bit dir offset
#[derive(Debug)]
struct InodeRef
{
	dir_first_cluster: u32,
	dir_offset: u16,
	first_cluster: u32,
}

/// Iterable cluster list
enum ClusterList {
	Range(::core::ops::Range<u32>),
	Chained(ArefBorrow<FilesystemInner>, u32),
}

static S_DRIVER: Driver = Driver;

fn init()
{
	let h = mount::DriverRegistration::new("fat", &S_DRIVER);
	// TODO: Remember the registration for unloading
	::core::mem::forget(h);
}

impl mount::Driver for Driver
{
	fn detect(&self, vol: &VolumeHandle) -> vfs::Result<usize> {
		let bs = {
			let mut bs = [0u8; 512];
			try!( vol.read_blocks(0, &mut bs) );
			on_disk::BootSect::read(&bs)
			};

		let bps = bs.common().bps;
		let spc = bs.common().spc;
		let media_desc = bs.common().media_descriptor;

		if bps == 0 || spc == 0 || media_desc < 0xf0 {
			Ok(0)
		}
		else {
			Ok(1)
		}
	}
	fn mount(&self, vol: VolumeHandle) -> vfs::Result<Box<mount::Filesystem>> {
		let bs = {
			let mut bs = [0u8; 512];
			try!( vol.read_blocks(0, &mut bs) );
			on_disk::BootSect::read(&mut &bs[..])
			};
		let bs_c = bs.common();
		if bs_c.bps != 512 {
			return Err(vfs::Error::Unknown("TODO: non 512-byte sector FAT"))
		}
		if bs_c.fat_count == 0 {
			return Err(vfs::Error::Unknown("FAT Count is 0"));
		}

		log_debug!("Label: {:?}", ::kernel::lib::RawString(&bs.tail_common().label));

		let bps = bs_c.bps as usize;
		let spc = bs_c.spc as usize;

		let root_dir_sectors = (bs_c.files_in_root as usize*32 + bps - 1) / bps;
		let fat_size = if bs_c.fat_size_16 > 0 {
				bs_c.fat_size_16 as usize
			}
			else {
				todo!("FAT32 FAT size field");
			};
		let total_sectors = if bs_c.total_sectors_16 > 0 {
				bs_c.total_sectors_16 as usize
			}
			else {
				bs_c.total_sectors_32 as usize
			};

		// Calcualte some quantities
		let spare_fat_sectors = (bs_c.fat_count as usize - 1) * fat_size;
		let first_data_sector = bs_c.reserved_sect_count as usize
			+ fat_size + spare_fat_sectors
			+ root_dir_sectors;
		let cluster_count = (total_sectors - first_data_sector - spare_fat_sectors) / spc;

		// Determine the FAT type
		let fat_type = if cluster_count < FAT16_MIN_CLUSTERS {
				Size::Fat12
			}
			else if cluster_count < FAT32_MIN_CLUSTERS {
				Size::Fat16
			}
			else {
				Size::Fat32
			};
		log_debug!("{:?} {} sectors, Size {}", fat_type, total_sectors,
			SizePrinter((total_sectors*bs_c.bps as usize) as u64));

		Ok(Box::new(Filesystem {
			// SAFE: Saving to a Box, so won't move
			inner: unsafe { ArefInner::new(FilesystemInner {
				ty: fat_type,
				spc: spc,
				cluster_size: spc * vol.block_size(),
				cluster_count: cluster_count,
				first_fat_sector: bs_c.reserved_sect_count as usize,
				first_data_sector: first_data_sector,
				root_first_cluster: match fat_type {
					Size::Fat32 => bs.info32().unwrap().root_cluster,
					_ => FATL_ROOT_CLUSTER as u32,
					},
				root_sector_count: root_dir_sectors as u32,

				vh: vol,
				}) },
			}))
	}
}

type Cluster = Vec<u8>;

impl FilesystemInner
{
	/// Load a cluster from disk
	// TODO: V V V
	// - Should this function lock the cluster somehow to prevent accidental overlap?
	// - Could also cache somehow (with a refcount) along with the 'writing' flag
	fn read_cluster(&self, cluster: u32, dst: &mut [u8]) -> Result<(), storage::IoError> {
		log_trace!("Filesystem::read_cluster({:#x})", cluster);
		assert_eq!(dst.len(), self.cluster_size);
		// For now, just read the bytes, screw caching
		let sector = if !is!(self.ty, Size::Fat32) && cluster >= FATL_ROOT_CLUSTER {
				// Root directory (for FAT12/16, where it was not a normal file)
				let rc = cluster - FATL_ROOT_CLUSTER;
				assert!( (rc as u64 * self.spc as u64) < self.root_sector_count as u64);
				(self.first_data_sector - self.root_sector_count as usize) as u64
				+ (rc * self.spc as u32) as u64
			}
			else {
				// Anything else
				assert!(cluster >= 2);
				assert!(cluster - 2 < self.cluster_count as u32);
				self.first_data_sector as u64 + (cluster as u64 - 2) * self.spc as u64
			};
		log_debug!("cluster = {:#x}, sector = 0x{:x}", cluster, sector);
		try!(self.vh.read_blocks(sector, dst));
		//::kernel::logging::hex_dump("FAT Cluster", &buf);
		Ok( () )
	}
	fn load_cluster(&self, cluster: u32) -> Result<Cluster, storage::IoError> {
		let mut buf: Vec<u8> = ::core::iter::repeat(0).take(self.spc * self.vh.block_size()).collect();
		try!(self.read_cluster(cluster, &mut buf));
		Ok( buf )
	}

	/// Obtain the next cluster in a chain
	fn get_next_cluster(&self, cluster: u32) -> Option<u32> {
		use kernel::lib::byteorder::{ReadBytesExt,LittleEndian};
		// - Determine what sector contains the requested FAT entry
		let bs = self.vh.block_size();
		let (fat_sector,ofs) = match self.ty
			{
			Size::Fat12 => {
				let cps = bs / 3 * 2;	// 2 per 3 bytes
				(cluster as usize / cps, (cluster as usize % cps) / 2 * 3 )
				},
			Size::Fat16 => {
				let cps = bs / 2;
				(cluster as usize / cps, (cluster as usize % cps) * 2)
				},
			Size::Fat32 => {
				let cps = bs / 4;
				(cluster as usize / cps, (cluster as usize % cps) * 2)
				},
			};
		// - Read a single sector from the FAT
		let mut sector_data = Vec::from_fn(bs, |_|0u8);
		match self.vh.read_blocks( (self.first_fat_sector + fat_sector) as u64, &mut sector_data )
		{
		Ok(_) => {},
		Err(_) => return None,
		}
		// - Extract the entry
		match self.ty
		{
		Size::Fat12 => {
			// FAT12 has special handling because it packs 2 entries into 24 bytes
			let val = {
				let v24 = (&sector_data[ofs..]).read_uint::<LittleEndian>(3).unwrap();
				if cluster % 2 == 0 { v24 & 0xFFF } else { v24 >> 12 }
				} as u16;
			if val == FAT12_EOC { None } else { Some(val as u32) }
			},
		Size::Fat16 => {
			let val = (&sector_data[ofs..]).read_u16::<LittleEndian>().unwrap();
			if val == FAT16_EOC { None } else { Some(val as u32) }
			},
		Size::Fat32 => {
			let val = (&sector_data[ofs..]).read_u32::<LittleEndian>().unwrap();
			if val == FAT32_EOC { None } else { Some(val as u32) }
			},
		}
	}
}

impl mount::Filesystem for Filesystem
{
	fn root_inode(&self) -> node::InodeId {
		(InodeRef {
			first_cluster: self.root_first_cluster,
			dir_first_cluster: 0,
			dir_offset: 0,
			}).to_id()
	}
	fn get_node_by_inode(&self, id: node::InodeId) -> Option<node::Node> {
		let r = InodeRef::from(id);
		if r.first_cluster == self.root_first_cluster {
			Some(node::Node::Dir(dir::DirNode::new_boxed(self.inner.borrow(), r.first_cluster)))
		}
		else {
			// Reading from the directory starting at r.dir_first_cluster
			// locate the file with cluster equal to r.first_cluster.
			// And use that to create the node
			let dn = dir::DirNode::new(self.inner.borrow(), r.dir_first_cluster);
			dn.find_node(r.first_cluster)
		}
	}
}

impl InodeRef
{
	fn new(c: u32, dir_c: u32) -> InodeRef {
		assert!(c     <= 0x00FF_FFFF);
		assert!(dir_c <= 0x00FF_FFFF);
		InodeRef {
			first_cluster: c,
			dir_first_cluster: dir_c,
			dir_offset: 0,
		}
	}
	fn to_id(&self) -> node::InodeId {
		assert!(self.first_cluster <= 0x00FF_FFFF);
		assert!(self.dir_first_cluster <= 0x00FF_FFFF);
		//assert!(v.dir_offset <= 0xFFFF);
		(self.first_cluster as u64)
		| (self.dir_first_cluster as u64) << 24
		| (self.dir_offset as u64) << 48
	}
}

impl From<node::InodeId> for InodeRef {
	fn from(v: node::InodeId) -> InodeRef {
		InodeRef {
			first_cluster: (v & 0x00FF_FFFF) as u32,
			dir_first_cluster: ((v >> 24) & 0x00FF_FFFF) as u32,
			dir_offset: (v >> 48) as u16,
		}
	}
}

impl ClusterList {
	pub fn chained(fs: ArefBorrow<FilesystemInner>, start: u32) -> ClusterList {
		ClusterList::Chained(fs, start)
	}
}
impl ::core::iter::Iterator for ClusterList {
	type Item = u32;
	fn next(&mut self) -> Option<u32> {
		match *self
		{
		ClusterList::Range(ref mut r) => r.next(),
		ClusterList::Chained(ref fs, ref mut next) =>
			if *next == 0 {
				None
			}
			else {
				let rv = *next;
				*next = fs.get_next_cluster(*next).unwrap_or(0);
				Some( rv )
			},
		}
	}
}
<|endoftext|>


<commit_before>// Unlike C/C++, there's no restriction on the order of function definitions
fn main() {
    // We can use this function here, and define it somewhere later
    fizzbuzz_to(100);
}

// Function that returns a boolean value
fn is_divisible_by(lhs: u32, rhs: u32) -> bool {
    // Corner case, early return
    if rhs == 0 {
        return false;
    }

    // This is an expression, the `return` keyword is not necessary here
    lhs % rhs == 0
}

// Functions that "don't" return a value, actually return the unit type `()`
fn fizzbuzz(n: u32) -> () {
    if is_divisible_by(n, 15) {
        println!("fizzbuzz");
    } else if is_divisible_by(n, 3) {
        println!("fizz");
    } else if is_divisible_by(n, 5) {
        println!("buzz");
    } else {
        println!("{}", n);
    }
}

// When a function returns `()`, the return type can be omitted from the
// signature
fn fizzbuzz_to(n: u32) {
    for n in 1..(n + 1) {
        fizzbuzz(n);
    }
}
<commit_msg>Removed parentheses from the previous fix for fn.rs<commit_after>// Unlike C/C++, there's no restriction on the order of function definitions
fn main() {
    // We can use this function here, and define it somewhere later
    fizzbuzz_to(100);
}

// Function that returns a boolean value
fn is_divisible_by(lhs: u32, rhs: u32) -> bool {
    // Corner case, early return
    if rhs == 0 {
        return false;
    }

    // This is an expression, the `return` keyword is not necessary here
    lhs % rhs == 0
}

// Functions that "don't" return a value, actually return the unit type `()`
fn fizzbuzz(n: u32) -> () {
    if is_divisible_by(n, 15) {
        println!("fizzbuzz");
    } else if is_divisible_by(n, 3) {
        println!("fizz");
    } else if is_divisible_by(n, 5) {
        println!("buzz");
    } else {
        println!("{}", n);
    }
}

// When a function returns `()`, the return type can be omitted from the
// signature
fn fizzbuzz_to(n: u32) {
    for n in 1..n + 1 {
        fizzbuzz(n);
    }
}
<|endoftext|>


<commit_before>// Unlike C/C++, no restriction on the order of definitions
fn main() {
    fizzbuzz_to(100);
}

// Function that returns a boolean value
fn is_divisible_by(lhs: uint, rhs: uint) -> bool {
    // Corner case, early return
    if rhs == 0 {
        return false;
    }

    // This is an expression, the `return` keyword is not necessary here
    lhs % rhs == 0
}

// Functions that "don't" return a value, actually return the unit type `()`
fn fizzbuzz(n: uint) -> () {
    if is_divisible_by(n, 15) {
        println!("fizzbuzz");
    } else if is_divisible_by(n, 3) {
        println!("fizz");
    } else if is_divisible_by(n, 5) {
        println!("buzz");
    } else {
        println!("{}", n);
    }
}

// When a function returns `()`, the return type can be omitted from the
// signature
fn fizzbuzz_to(n: uint) {
    for n in range(1, n + 1) {
        fizzbuzz(n);
    }
}
<commit_msg>fn: Add more details about the order of function definitions<commit_after>// Unlike C/C++, there's no restriction on the order of function definitions
fn main() {
    // We can use this function here, and define it somewhere later
    fizzbuzz_to(100);
}

// Function that returns a boolean value
fn is_divisible_by(lhs: uint, rhs: uint) -> bool {
    // Corner case, early return
    if rhs == 0 {
        return false;
    }

    // This is an expression, the `return` keyword is not necessary here
    lhs % rhs == 0
}

// Functions that "don't" return a value, actually return the unit type `()`
fn fizzbuzz(n: uint) -> () {
    if is_divisible_by(n, 15) {
        println!("fizzbuzz");
    } else if is_divisible_by(n, 3) {
        println!("fizz");
    } else if is_divisible_by(n, 5) {
        println!("buzz");
    } else {
        println!("{}", n);
    }
}

// When a function returns `()`, the return type can be omitted from the
// signature
fn fizzbuzz_to(n: uint) {
    for n in range(1, n + 1) {
        fizzbuzz(n);
    }
}
<|endoftext|>


<commit_before>extern crate cursive;

use cursive::Cursive;
use cursive::align::HAlign;
use cursive::view::Boxable;
use cursive::views::{Dialog, Panel, TextView};

fn main() {
    // Read some long text from a file.
    let content = include_str!("../assets/lorem.txt");

    let mut siv = Cursive::default();

    // We can quit by pressing q
    siv.add_global_callback('q', |s| s.quit());

    // The text is too long to fit on a line, so the view will wrap lines,
    // and will adapt to the terminal size.
    siv.add_fullscreen_layer(
        Dialog::around(Panel::new(TextView::new(content)))
            // This is the alignment for the button
            .h_align(HAlign::Center)
            .button("Quit", |s| s.quit())
            .title("Unicode and wide-character support")
            .full_screen(),
    );
    // Show a popup on top of the view.
    siv.add_layer(Dialog::info(
        "Try resizing the terminal!\n(Press 'q' to \
         quit when you're done.)",
    ));

    siv.run();
}
<commit_msg>lorem example: remove unnecessary full_screen()<commit_after>extern crate cursive;

use cursive::align::HAlign;
use cursive::views::{Dialog, Panel, TextView};
use cursive::Cursive;

fn main() {
    // Read some long text from a file.
    let content = include_str!("../assets/lorem.txt");

    let mut siv = Cursive::default();

    // We can quit by pressing q
    siv.add_global_callback('q', |s| s.quit());

    // The text is too long to fit on a line, so the view will wrap lines,
    // and will adapt to the terminal size.
    siv.add_fullscreen_layer(
        Dialog::around(Panel::new(TextView::new(content)))
            .title("Unicode and wide-character support")
            // This is the alignment for the button
            .h_align(HAlign::Center)
            .button("Quit", |s| s.quit()),
    );
    // Show a popup on top of the view.
    siv.add_layer(Dialog::info(
        "Try resizing the terminal!\n(Press 'q' to \
         quit when you're done.)",
    ));

    siv.run();
}
<|endoftext|>