<commit_before>use clap::Clap;

use prjoxide::bba::bbafile::*;
use prjoxide::bba::bbastruct::*;
use prjoxide::bba::idstring::*;
use prjoxide::bba::tileloc::*;
use prjoxide::bba::tiletype::*;
use prjoxide::bba::timing::*;

use prjoxide::bitstream::*;
use prjoxide::chip::*;
use prjoxide::database::*;
use prjoxide::fasmparse::*;

use std::convert::TryInto;
use std::fs::File;
use std::io::*;

use include_dir::{include_dir, Dir};

const DATABASE_DIR: Dir = include_dir!("../../database");

#[derive(Clap)]
#[clap(version = "0.1", author = "David Shah <dave@ds0.me>")]
struct Opts {
    #[clap(subcommand)]
    subcmd: SubCommand,
}

#[derive(Clap)]
enum SubCommand {
    #[clap(about = "pack FASM into a bitstream")]
    Pack(Pack),
    #[clap(about = "unpack a bitstream into FASM")]
    Unpack(Unpack),
    #[clap(about = "export a BBA file for the nextpnr build")]
    BBAExport(BBAExport),
    #[cfg(feature = "interchange")]
    #[clap(about = "export a FPGA interchange file (not yet implemented)")]
    InterchangeExport(InterchangeExport),
}

#[derive(Clap)]
struct Pack {
    #[clap(long, about = "create background programmable bitstream (advanced)")]
    background: bool,
    #[clap(about = "input FASM file")]
    fasm: String,
    #[clap(about = "output bitstream")]
    bitstream: String,

}

impl Pack {
    pub fn run(&self) -> Result<()> {
        let mut db = Database::new_builtin(DATABASE_DIR);
        let parsed_fasm = ParsedFasm::parse(&self.fasm).unwrap();

        let mut chip = Chip::from_fasm(&mut db, &parsed_fasm, None);

        if self.background {
            chip.settings.insert("background".to_string(), "1".to_string());
        }
        let bs = BitstreamParser::serialise_chip(&chip);
        let mut outfile = File::create(&self.bitstream).unwrap();
        outfile.write_all(&bs)?;
        Ok(())
    }
}

#[derive(Clap)]
struct Unpack {
    #[clap(about = "input bitstream")]
    bitstream: String,
    #[clap(about = "output FASM file")]
    fasm: String,
}

impl Unpack {
    pub fn run(&self) -> Result<()> {
        let mut db = Database::new_builtin(DATABASE_DIR);
        let chip = BitstreamParser::parse_file(&mut db, &self.bitstream).unwrap();

        let mut outfile = File::create(&self.fasm)?;

        writeln!(outfile, "{{ oxide.device=\"{}\" }}", chip.device)?;
        writeln!(outfile, "{{ oxide.device_variant=\"{}\" }}", chip.variant)?;
        writeln!(outfile, "")?;

        for metadata in chip.metadata.iter() {
            writeln!(outfile, "{{ oxide.meta=\"{}\" }}", metadata)?;
        }
        if !chip.metadata.is_empty() {
            writeln!(outfile, "")?;
        }

        for tile in chip.tiles {
            tile.write_fasm(&mut db, &mut outfile);
        }

        for (addr, val) in chip.ipconfig.iter() {
            writeln!(outfile, "IP_UNKNOWN.0x{:08X}[7:0] = 8'h{:02X};", addr, val)?;
        }

        Ok(())
    }
}

#[derive(Clap)]
struct BBAExport {
    #[clap(about = "device family name")]
    family: String,
    #[clap(about = "path to nextpnr constids.inc")]
    constids: String,
    #[clap(about = "path to output bba file")]
    bba: String,
}

impl BBAExport {
    pub fn run(&self) -> Result<()> {
        let mut ids = IdStringDB::from_constids(&self.constids)?;
        let outfile = File::create(&self.bba)?;

        if self.family != "LIFCL" {
            // TODO: multiple family and device support
            panic!("unsupported family {}", &self.family);
        }

        let speed_grades = vec!["4", "5", "6", "10", "11", "12", "M"];
        let devices = vec!["LIFCL-40", "LFD2NX-40", "LIFCL-17"];
        let mut db = Database::new_builtin(DATABASE_DIR);

        let tts = TileTypes::new(&mut db, &mut ids, "LIFCL", &devices);

        let mut lgrids = Vec::new();
        let mut empty_chips = Vec::new();
        for device in devices.iter() {
            let empty_chip = Chip::from_name(&mut db, device);
            let mut lgrid = LocationGrid::new(&empty_chip, &mut db, &tts);
            lgrid.stamp_neighbours();
            lgrids.push(lgrid);
            empty_chips.push(empty_chip);
        }

        let mut lts = LocationTypes::from_locs(&mut lgrids);
        lts.import_wires(&mut ids, &tts);

        let mut bba_str = BufWriter::new(outfile);
        let mut bba = BBAWriter::new(&mut bba_str);
        bba.pre("#include \"nextpnr.h\"")?;
        bba.pre("#include \"embed.h\"")?;
        bba.pre("NEXTPNR_NAMESPACE_BEGIN")?;
        bba.post(&format!("EmbeddedFile chipdb_file_{0}(\"nexus/chipdb-{0}.bin\", chipdb_blob_{0});", &self.family))?;
        bba.post("NEXTPNR_NAMESPACE_END")?;
        bba.push(&format!("chipdb_blob_{}", &self.family))?;
        bba.ref_label("db")?;

        let mut bba_tmg = BBATiming::new(&speed_grades);
        let mut bba_s = BBAStructs::new(&mut bba);
        lts.write_locs_bba(&mut bba_s, &mut ids, &mut bba_tmg, &tts)?;
        for (i, lgrid) in lgrids.iter().enumerate() {
            lgrid.write_grid_bba(&mut bba_s, i.try_into().unwrap(), &mut ids, &empty_chips[i])?;
            lgrid.write_chip_iodb(&mut bba_s, i.try_into().unwrap(), &mut ids)?;
        }

        bba_s.list_begin("chips")?;
        for (i, lgrid) in lgrids.iter().enumerate() {
            lgrid.write_chip_bba(&mut bba_s, i.try_into().unwrap(), &empty_chips[i])?;
        }

        bba_tmg.import(&self.family, &mut db, &mut ids);

        bba_tmg.write_bba(&mut bba_s)?;

        ids.write_bba(&mut bba_s)?;
        bba_s.list_begin("db")?;
        bba_s.database(devices.len(), "LIFCL", "chips", lts.types.len(), bba_tmg.speed_grades.len(), "chip_tts")?;

        bba_s.out.pop()?;
        Ok(())
    }
}

#[derive(Clap)]
#[cfg(feature = "interchange")]
struct InterchangeExport {
    #[clap(about = "device name")]
    device: String,
    #[clap(about = "path to output interchange file")]
    interchange: String,
}

#[cfg(feature = "interchange")]
impl InterchangeExport {
    pub fn run(&self) -> Result<()> {
        let mut ids = IdStringDB::new();
        let mut db = Database::new_builtin(DATABASE_DIR);
        let c = Chip::from_name(&mut db, &self.device);
        let g = prjoxide::interchange_gen::routing_graph::GraphBuilder::run(&mut ids, &c, &mut db);
        prjoxide::interchange_gen::writer::write(&c, &mut db, &mut ids, &g, &self.interchange).unwrap();
        Ok(())
    }
}

fn main() -> Result<()> {
    let opts: Opts = Opts::parse();
    match opts.subcmd {
        SubCommand::Pack(t) => {
            t.run()
        }
        SubCommand::Unpack(t) => {
            t.run()
        }
        SubCommand::BBAExport(t) => {
            t.run()
        }
        #[cfg(feature = "interchange")]
        SubCommand::InterchangeExport(t) => {
            t.run()
        }
    }

}
<commit_msg>Update author<commit_after>use clap::Clap;

use prjoxide::bba::bbafile::*;
use prjoxide::bba::bbastruct::*;
use prjoxide::bba::idstring::*;
use prjoxide::bba::tileloc::*;
use prjoxide::bba::tiletype::*;
use prjoxide::bba::timing::*;

use prjoxide::bitstream::*;
use prjoxide::chip::*;
use prjoxide::database::*;
use prjoxide::fasmparse::*;

use std::convert::TryInto;
use std::fs::File;
use std::io::*;

use include_dir::{include_dir, Dir};

const DATABASE_DIR: Dir = include_dir!("../../database");

#[derive(Clap)]
#[clap(version = "0.1", author = "gatecat <gatecat@ds0.me>")]
struct Opts {
    #[clap(subcommand)]
    subcmd: SubCommand,
}

#[derive(Clap)]
enum SubCommand {
    #[clap(about = "pack FASM into a bitstream")]
    Pack(Pack),
    #[clap(about = "unpack a bitstream into FASM")]
    Unpack(Unpack),
    #[clap(about = "export a BBA file for the nextpnr build")]
    BBAExport(BBAExport),
    #[cfg(feature = "interchange")]
    #[clap(about = "export a FPGA interchange file (not yet implemented)")]
    InterchangeExport(InterchangeExport),
}

#[derive(Clap)]
struct Pack {
    #[clap(long, about = "create background programmable bitstream (advanced)")]
    background: bool,
    #[clap(about = "input FASM file")]
    fasm: String,
    #[clap(about = "output bitstream")]
    bitstream: String,

}

impl Pack {
    pub fn run(&self) -> Result<()> {
        let mut db = Database::new_builtin(DATABASE_DIR);
        let parsed_fasm = ParsedFasm::parse(&self.fasm).unwrap();

        let mut chip = Chip::from_fasm(&mut db, &parsed_fasm, None);

        if self.background {
            chip.settings.insert("background".to_string(), "1".to_string());
        }
        let bs = BitstreamParser::serialise_chip(&chip);
        let mut outfile = File::create(&self.bitstream).unwrap();
        outfile.write_all(&bs)?;
        Ok(())
    }
}

#[derive(Clap)]
struct Unpack {
    #[clap(about = "input bitstream")]
    bitstream: String,
    #[clap(about = "output FASM file")]
    fasm: String,
}

impl Unpack {
    pub fn run(&self) -> Result<()> {
        let mut db = Database::new_builtin(DATABASE_DIR);
        let chip = BitstreamParser::parse_file(&mut db, &self.bitstream).unwrap();

        let mut outfile = File::create(&self.fasm)?;

        writeln!(outfile, "{{ oxide.device=\"{}\" }}", chip.device)?;
        writeln!(outfile, "{{ oxide.device_variant=\"{}\" }}", chip.variant)?;
        writeln!(outfile, "")?;

        for metadata in chip.metadata.iter() {
            writeln!(outfile, "{{ oxide.meta=\"{}\" }}", metadata)?;
        }
        if !chip.metadata.is_empty() {
            writeln!(outfile, "")?;
        }

        for tile in chip.tiles {
            tile.write_fasm(&mut db, &mut outfile);
        }

        for (addr, val) in chip.ipconfig.iter() {
            writeln!(outfile, "IP_UNKNOWN.0x{:08X}[7:0] = 8'h{:02X};", addr, val)?;
        }

        Ok(())
    }
}

#[derive(Clap)]
struct BBAExport {
    #[clap(about = "device family name")]
    family: String,
    #[clap(about = "path to nextpnr constids.inc")]
    constids: String,
    #[clap(about = "path to output bba file")]
    bba: String,
}

impl BBAExport {
    pub fn run(&self) -> Result<()> {
        let mut ids = IdStringDB::from_constids(&self.constids)?;
        let outfile = File::create(&self.bba)?;

        if self.family != "LIFCL" {
            // TODO: multiple family and device support
            panic!("unsupported family {}", &self.family);
        }

        let speed_grades = vec!["4", "5", "6", "10", "11", "12", "M"];
        let devices = vec!["LIFCL-40", "LFD2NX-40", "LIFCL-17"];
        let mut db = Database::new_builtin(DATABASE_DIR);

        let tts = TileTypes::new(&mut db, &mut ids, "LIFCL", &devices);

        let mut lgrids = Vec::new();
        let mut empty_chips = Vec::new();
        for device in devices.iter() {
            let empty_chip = Chip::from_name(&mut db, device);
            let mut lgrid = LocationGrid::new(&empty_chip, &mut db, &tts);
            lgrid.stamp_neighbours();
            lgrids.push(lgrid);
            empty_chips.push(empty_chip);
        }

        let mut lts = LocationTypes::from_locs(&mut lgrids);
        lts.import_wires(&mut ids, &tts);

        let mut bba_str = BufWriter::new(outfile);
        let mut bba = BBAWriter::new(&mut bba_str);
        bba.pre("#include \"nextpnr.h\"")?;
        bba.pre("#include \"embed.h\"")?;
        bba.pre("NEXTPNR_NAMESPACE_BEGIN")?;
        bba.post(&format!("EmbeddedFile chipdb_file_{0}(\"nexus/chipdb-{0}.bin\", chipdb_blob_{0});", &self.family))?;
        bba.post("NEXTPNR_NAMESPACE_END")?;
        bba.push(&format!("chipdb_blob_{}", &self.family))?;
        bba.ref_label("db")?;

        let mut bba_tmg = BBATiming::new(&speed_grades);
        let mut bba_s = BBAStructs::new(&mut bba);
        lts.write_locs_bba(&mut bba_s, &mut ids, &mut bba_tmg, &tts)?;
        for (i, lgrid) in lgrids.iter().enumerate() {
            lgrid.write_grid_bba(&mut bba_s, i.try_into().unwrap(), &mut ids, &empty_chips[i])?;
            lgrid.write_chip_iodb(&mut bba_s, i.try_into().unwrap(), &mut ids)?;
        }

        bba_s.list_begin("chips")?;
        for (i, lgrid) in lgrids.iter().enumerate() {
            lgrid.write_chip_bba(&mut bba_s, i.try_into().unwrap(), &empty_chips[i])?;
        }

        bba_tmg.import(&self.family, &mut db, &mut ids);

        bba_tmg.write_bba(&mut bba_s)?;

        ids.write_bba(&mut bba_s)?;
        bba_s.list_begin("db")?;
        bba_s.database(devices.len(), "LIFCL", "chips", lts.types.len(), bba_tmg.speed_grades.len(), "chip_tts")?;

        bba_s.out.pop()?;
        Ok(())
    }
}

#[derive(Clap)]
#[cfg(feature = "interchange")]
struct InterchangeExport {
    #[clap(about = "device name")]
    device: String,
    #[clap(about = "path to output interchange file")]
    interchange: String,
}

#[cfg(feature = "interchange")]
impl InterchangeExport {
    pub fn run(&self) -> Result<()> {
        let mut ids = IdStringDB::new();
        let mut db = Database::new_builtin(DATABASE_DIR);
        let c = Chip::from_name(&mut db, &self.device);
        let g = prjoxide::interchange_gen::routing_graph::GraphBuilder::run(&mut ids, &c, &mut db);
        prjoxide::interchange_gen::writer::write(&c, &mut db, &mut ids, &g, &self.interchange).unwrap();
        Ok(())
    }
}

fn main() -> Result<()> {
    let opts: Opts = Opts::parse();
    match opts.subcmd {
        SubCommand::Pack(t) => {
            t.run()
        }
        SubCommand::Unpack(t) => {
            t.run()
        }
        SubCommand::BBAExport(t) => {
            t.run()
        }
        #[cfg(feature = "interchange")]
        SubCommand::InterchangeExport(t) => {
            t.run()
        }
    }

}
<|endoftext|>


<commit_before>use std::fmt;
use std::cmp;
use std::clone;
use std::os;
use term::color;

fn col_cmd(c: &fmt::Show) -> String{
    format!("\\[{}[{}\\]", '\x1B', c)
}

pub fn col(c: u16) -> String {
    col_cmd(&format!("{}m", c + 30))
}

fn bcol(c: u16) -> String{
    col_cmd(&format!("1;{}m", c + 30))
}

pub fn reset() -> String{
    col_cmd(&"0m")
}

pub struct PromptBuffer<'a> {
    plugins: Vec<Box<PromptBufferPlugin+'a>>,
    path: Path
}

enum PromptLineType {
    Boxed,
    Free
}

impl Copy for PromptLineType {}

struct PromptBox {
    color: u16,
    text: String,
    is_bold: bool
}

impl clone::Clone for PromptBox {
    fn clone(&self) -> PromptBox {
        PromptBox {
            color: self.color,
            text: self.text.clone(),
            is_bold: self.is_bold
        }
    }
}

impl fmt::Show for PromptBox {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}{}{}", if self.is_bold { bcol(self.color) } else { col(self.color) }, self.text, reset())
    }
}

pub struct PromptLine {
    level: u8,
    line_type: PromptLineType,
    parts: Vec<PromptBox>,
}

impl PromptLine {
    fn new() -> PromptLine {
        PromptLine {
            level: 0,
            line_type: PromptLineType::Boxed,
            parts: Vec::new(),
        }
    }

    fn new_free() -> PromptLine {
        let mut r = PromptLine::new();
        r.line_type = PromptLineType::Free;
        r
    }
}

impl clone::Clone for PromptLine {
    fn clone(&self) -> PromptLine {
        PromptLine {
            level: self.level,
            line_type: self.line_type,
            parts: self.parts.clone(),
        }
    }
}

const TOP       : int = 8;
const BOTTOM    : int = 4;
const LEFT      : int = 2;
const RIGHT     : int = 1;

fn get_line(flags: int) -> char {
    return match flags {
        0b1111 => '┼',
        0b1110 => '┤',
        0b1101 => '├',
        0b1100 => '│',
        0b1011 => '┴',
        0b1010 => '┘',
        0b1001 => '└',
        0b0110 => '┐',
        0b0101 => '┌',
        0b0111 => '┬',
        0b0011 => '─',
        _      => ' '
    }
}

fn trail_off() -> String {
    let mut retval = String::new();
    for _ in range(0i,10i) {
        retval = format!("{}{}", retval, get_line(LEFT|RIGHT));
    }
    retval
}

pub struct PromptLineBuilder {
    line: PromptLine
}

impl PromptLineBuilder {
    pub fn new() -> PromptLineBuilder {
        PromptLineBuilder {
            line: PromptLine::new()
        }
    }

    pub fn new_free() -> PromptLineBuilder {
        PromptLineBuilder {
            line: PromptLine::new_free()
        }
    }

    pub fn indent_by(mut self, amt: u8) -> PromptLineBuilder {
        self.line.level += amt;

        self
    }

    pub fn indent(self) -> PromptLineBuilder {
        self.indent_by(1)
    }

    fn add_block(mut self, s: &fmt::Show, c: u16, bold: bool) -> PromptLineBuilder {
        self.line.parts.push(
            PromptBox {
                color: c,
                text: format!("{}", s),
                is_bold: bold
            }
        );

        self
    }

    pub fn block(self, s: &fmt::Show) -> PromptLineBuilder {
        self.add_block(s, color::MAGENTA, false)
    }

    pub fn colored_block(self, s: &fmt::Show, c: u16) -> PromptLineBuilder {
        self.add_block(s, c, false)
    }

    pub fn bold_colored_block(self, s: &fmt::Show, c: u16) -> PromptLineBuilder {
        self.add_block(s, c, true)
    }

    pub fn build(self) -> PromptLine {
        self.line
    }
}

impl<'a> PromptBuffer<'a> {
    pub fn new() -> PromptBuffer<'a> {
        PromptBuffer {
            plugins: Vec::new(),
            path: os::make_absolute(&Path::new(".")).unwrap()
        }
    }

    pub fn start(&self, lines: &mut Vec<PromptLine>) {
        lines.push(PromptLineBuilder::new()
            .block(&"\\w")
            .block(&"\\H")
            .build());
    }

    pub fn add_plugin(&mut self, plugin: Box<PromptBufferPlugin+'a>) {
        self.plugins.push(plugin);
    }

    pub fn set_path(&mut self, p: Path) {
        self.path = p;
    }

    pub fn to_string(&mut self) -> String {
        let mut retval = String::new();
        let mut lines = Vec::new();

        self.start(&mut lines);

        let mut pl = self.plugins.as_mut_slice();
        for i in range(0, pl.len()) {
            pl[i].run(&self.path, &mut lines);
        }

        for ix in range(0, lines.len()) {
            let ref line = lines[ix];
            let current = line.level;
            let (after, start, end) = if ix + 1 < lines.len() {
                let a = lines[ix + 1].level;
                (a, cmp::min(current, a), cmp::max(current, a))
            } else {
                (0, 0, current)
            };

            let mut line_text = String::new();

            for _ in range(0, start) {
                line_text = format!(" {}", line_text);
            }

            for i in range(start, end + 1) {
                line_text = format!("{}{}", line_text,
                    get_line(
                        if i == current && ix > 0 { TOP } else { 0 } |
                        if i == after { BOTTOM } else { 0 } |
                        if i > start { LEFT } else { 0 } |
                        match line.line_type {
                            PromptLineType::Boxed => RIGHT,
                            PromptLineType::Free => if i == current {
                                0
                            } else {
                                RIGHT
                            }
                        }
                    )
                );
            }

            for b in line.parts.iter() {
                line_text = match line.line_type {
                    PromptLineType::Boxed => format!("{}{}{}{}{}",
                        line_text,
                        get_line(LEFT|RIGHT),
                        get_line(LEFT|TOP|BOTTOM),
                        b,
                        get_line(TOP|BOTTOM|RIGHT)),
                    PromptLineType::Free => format!("{} {}", line_text, b)
                };
            }

            match line.line_type {
                PromptLineType::Boxed => {
                    line_text = format!("{}{}", line_text, trail_off());
                },
                _ => {}
            }

            retval = format!("{}{}\n", retval, line_text);
        }

        format!("{}{}{}{} ", retval, get_line(TOP|RIGHT), get_line(LEFT|RIGHT), PromptBox {
            text: "\\$".to_string(),
            color: color::RED,
            is_bold: false
        })
    }

    pub fn print(&mut self) {
        println!("{}", self.to_string());
    }
}

pub trait PromptBufferPlugin {
    fn run(&mut self, path: &Path, lines: &mut Vec<PromptLine>);
}
<commit_msg>Clean up propmt_buffer<commit_after>use std::fmt;
use std::cmp;
use std::os;
use term::color;

fn col_cmd(c: &fmt::Show) -> String{
    format!("\\[{}[{}\\]", '\x1B', c)
}

pub fn col(c: u16) -> String {
    col_cmd(&format!("{}m", c + 30))
}

pub fn bcol(c: u16) -> String {
    col_cmd(&format!("1;{}m", c + 30))
}

pub fn reset() -> String {
    col_cmd(&"0m")
}

#[deriving(Clone)]
enum PromptLineType {
    Boxed,
    Free
}

/// PromptBox
///
/// The smallest component of a prompt line
///
/// Contains a color, text, and "is bold" flag
#[deriving(Clone)]
struct PromptBox {
    color: color::Color,
    text: String,
    is_bold: bool
}

impl fmt::Show for PromptBox {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}{}{}", if self.is_bold { bcol(self.color) } else { col(self.color) }, self.text, reset())
    }
}

/// PromptLine
///
/// The small pieces used to display prompt lines
#[deriving(Clone)]
pub struct PromptLine {
    level: u8,
    line_type: PromptLineType,
    parts: Vec<PromptBox>,
}

impl PromptLine {
    fn new() -> PromptLine {
        PromptLine {
            level: 0,
            line_type: PromptLineType::Boxed,
            parts: Vec::new(),
        }
    }

    fn new_free() -> PromptLine {
        let mut r = PromptLine::new();
        r.line_type = PromptLineType::Free;
        r
    }
}

/// PromptLineBuilder
///
/// Used to easily construct PromptLines
pub struct PromptLineBuilder {
    line: PromptLine
}

impl PromptLineBuilder {
    pub fn new() -> PromptLineBuilder {
        PromptLineBuilder {
            line: PromptLine::new()
        }
    }

    pub fn new_free() -> PromptLineBuilder {
        PromptLineBuilder {
            line: PromptLine::new_free()
        }
    }

    pub fn indent_by(mut self, amt: u8) -> PromptLineBuilder {
        self.line.level += amt;

        self
    }

    pub fn indent(self) -> PromptLineBuilder {
        self.indent_by(1)
    }

    fn add_block(mut self, s: &fmt::Show, c: u16, bold: bool) -> PromptLineBuilder {
        self.line.parts.push(
            PromptBox {
                color: c,
                text: format!("{}", s),
                is_bold: bold
            }
        );

        self
    }

    pub fn block(self, s: &fmt::Show) -> PromptLineBuilder {
        self.add_block(s, color::MAGENTA, false)
    }

    pub fn colored_block(self, s: &fmt::Show, c: u16) -> PromptLineBuilder {
        self.add_block(s, c, false)
    }

    pub fn bold_colored_block(self, s: &fmt::Show, c: u16) -> PromptLineBuilder {
        self.add_block(s, c, true)
    }

    pub fn build(self) -> PromptLine {
        self.line
    }
}

const TOP       : int = 8;
const BOTTOM    : int = 4;
const LEFT      : int = 2;
const RIGHT     : int = 1;

/// PromptBuffer
///
/// Used to contain a list of PromptLines
/// Knows how to format a serise of PromptLines in a pretty way
pub struct PromptBuffer<'a> {
    plugins: Vec<Box<PromptBufferPlugin+'a>>,
    path: Path
}

impl<'a> PromptBuffer<'a> {
    pub fn new() -> PromptBuffer<'a> {
        PromptBuffer {
            plugins: Vec::new(),
            path: os::make_absolute(&Path::new(".")).unwrap()
        }
    }

    fn get_line(flags: int) -> char {
        return match flags {
            0b1111 => '┼',
            0b1110 => '┤',
            0b1101 => '├',
            0b1100 => '│',
            0b1011 => '┴',
            0b1010 => '┘',
            0b1001 => '└',
            0b0110 => '┐',
            0b0101 => '┌',
            0b0111 => '┬',
            0b0011 => '─',
            _      => ' '
        }
    }

    fn trail_off() -> String {
        let mut retval = String::new();
        for _ in range(0i,10i) {
            retval = format!("{}{}", retval, PromptBuffer::get_line(LEFT|RIGHT));
        }
        retval
    }

    pub fn start(&self, lines: &mut Vec<PromptLine>) {
        lines.push(PromptLineBuilder::new()
            .block(&"\\w")
            .block(&"\\H")
            .build());
    }

    pub fn add_plugin(&mut self, plugin: Box<PromptBufferPlugin+'a>) {
        self.plugins.push(plugin);
    }

    pub fn set_path(&mut self, p: Path) {
        self.path = p;
    }

    pub fn to_string(&mut self) -> String {
        let mut retval = String::new();
        let mut lines = Vec::new();

        self.start(&mut lines);

        let mut pl = self.plugins.as_mut_slice();
        for i in range(0, pl.len()) {
            pl[i].run(&self.path, &mut lines);
        }

        for ix in range(0, lines.len()) {
            let ref line = lines[ix];
            let current = line.level;
            let (after, start, end) = if ix + 1 < lines.len() {
                let a = lines[ix + 1].level;
                (a, cmp::min(current, a), cmp::max(current, a))
            } else {
                (0, 0, current)
            };

            let mut line_text = String::new();

            for _ in range(0, start) {
                line_text = format!(" {}", line_text);
            }

            for i in range(start, end + 1) {
                line_text = format!("{}{}", line_text,
                    PromptBuffer::get_line(
                        if i == current && ix > 0 { TOP } else { 0 } |
                        if i == after { BOTTOM } else { 0 } |
                        if i > start { LEFT } else { 0 } |
                        match line.line_type {
                            PromptLineType::Boxed => RIGHT,
                            PromptLineType::Free => if i == current {
                                0
                            } else {
                                RIGHT
                            }
                        }
                    )
                );
            }

            for b in line.parts.iter() {
                line_text = match line.line_type {
                    PromptLineType::Boxed => format!("{}{}{}{}{}",
                        line_text,
                        PromptBuffer::get_line(LEFT|RIGHT),
                        PromptBuffer::get_line(LEFT|TOP|BOTTOM),
                        b,
                        PromptBuffer::get_line(TOP|BOTTOM|RIGHT)),
                    PromptLineType::Free => format!("{} {}", line_text, b)
                };
            }

            match line.line_type {
                PromptLineType::Boxed => {
                    line_text = format!("{}{}", line_text, PromptBuffer::trail_off());
                },
                _ => {}
            }

            retval = format!("{}{}\n", retval, line_text);
        }

        format!("{}{}{}{} ",
            retval,
            PromptBuffer::get_line(TOP|RIGHT), PromptBuffer::get_line(LEFT|RIGHT),
            PromptBox {
                text: "\\$".to_string(),
                color: color::RED,
                is_bold: false
            })
    }

    pub fn print(&mut self) {
        println!("{}", self.to_string());
    }
}

pub trait PromptBufferPlugin {
    fn run(&mut self, path: &Path, lines: &mut Vec<PromptLine>);
}
<|endoftext|>


<commit_before>use super::ffi;
use super::unlock_notify;
use super::StatementStatus;
#[cfg(feature = "modern_sqlite")]
use crate::util::SqliteMallocString;
use std::ffi::CStr;
use std::os::raw::c_int;
use std::ptr;

// Private newtype for raw sqlite3_stmts that finalize themselves when dropped.
#[derive(Debug)]
pub struct RawStatement(*mut ffi::sqlite3_stmt, bool, crate::util::ParamIndexCache);

impl RawStatement {
    pub unsafe fn new(stmt: *mut ffi::sqlite3_stmt, tail: bool) -> RawStatement {
        RawStatement(stmt, tail, Default::default())
    }

    pub fn is_null(&self) -> bool {
        self.0.is_null()
    }

    pub unsafe fn ptr(&self) -> *mut ffi::sqlite3_stmt {
        self.0
    }

    pub fn column_count(&self) -> usize {
        unsafe { ffi::sqlite3_column_count(self.0) as usize }
    }

    pub fn column_type(&self, idx: usize) -> c_int {
        unsafe { ffi::sqlite3_column_type(self.0, idx as c_int) }
    }

    pub fn column_decltype(&self, idx: usize) -> Option<&CStr> {
        unsafe {
            let decltype = ffi::sqlite3_column_decltype(self.0, idx as c_int);
            if decltype.is_null() {
                None
            } else {
                Some(CStr::from_ptr(decltype))
            }
        }
    }

    pub fn column_name(&self, idx: usize) -> Option<&CStr> {
        let idx = idx as c_int;
        if idx < 0 || idx >= self.column_count() as c_int {
            return None;
        }
        unsafe {
            let ptr = ffi::sqlite3_column_name(self.0, idx);
            // If ptr is null here, it's an OOM, so there's probably nothing
            // meaningful we can do. Just assert instead of returning None.
            assert!(
                !ptr.is_null(),
                "Null pointer from sqlite3_column_name: Out of memory?"
            );
            Some(CStr::from_ptr(ptr))
        }
    }

    pub fn step(&self) -> c_int {
        if cfg!(feature = "unlock_notify") {
            let db = unsafe { ffi::sqlite3_db_handle(self.0) };
            let mut rc;
            loop {
                rc = unsafe { ffi::sqlite3_step(self.0) };
                if unsafe { !unlock_notify::is_locked(db, rc) } {
                    break;
                }
                rc = unsafe { unlock_notify::wait_for_unlock_notify(db) };
                if rc != ffi::SQLITE_OK {
                    break;
                }
                self.reset();
            }
            rc
        } else {
            unsafe { ffi::sqlite3_step(self.0) }
        }
    }

    pub fn reset(&self) -> c_int {
        unsafe { ffi::sqlite3_reset(self.0) }
    }

    pub fn bind_parameter_count(&self) -> usize {
        unsafe { ffi::sqlite3_bind_parameter_count(self.0) as usize }
    }

    pub fn bind_parameter_index(&self, name: &str) -> Option<usize> {
        self.2.get_or_insert_with(name, |param_cstr| {
            let r = unsafe { ffi::sqlite3_bind_parameter_index(self.0, param_cstr.as_ptr()) };
            match r {
                0 => None,
                i => Some(i as usize),
            }
        })
    }

    pub fn clear_bindings(&self) -> c_int {
        unsafe { ffi::sqlite3_clear_bindings(self.0) }
    }

    pub fn sql(&self) -> Option<&CStr> {
        if self.0.is_null() {
            None
        } else {
            Some(unsafe { CStr::from_ptr(ffi::sqlite3_sql(self.0)) })
        }
    }

    pub fn finalize(mut self) -> c_int {
        self.finalize_()
    }

    fn finalize_(&mut self) -> c_int {
        let r = unsafe { ffi::sqlite3_finalize(self.0) };
        self.0 = ptr::null_mut();
        r
    }

    #[cfg(feature = "modern_sqlite")] // 3.7.4
    pub fn readonly(&self) -> bool {
        unsafe { ffi::sqlite3_stmt_readonly(self.0) != 0 }
    }

    #[cfg(feature = "modern_sqlite")] // 3.14.0
    pub(crate) fn expanded_sql(&self) -> Option<SqliteMallocString> {
        unsafe { SqliteMallocString::from_raw(ffi::sqlite3_expanded_sql(self.0)) }
    }

    pub fn get_status(&self, status: StatementStatus, reset: bool) -> i32 {
        assert!(!self.0.is_null());
        unsafe { ffi::sqlite3_stmt_status(self.0, status as i32, reset as i32) }
    }

    pub fn has_tail(&self) -> bool {
        self.1
    }
}

impl Drop for RawStatement {
    fn drop(&mut self) {
        self.finalize_();
    }
}
<commit_msg>Name fields of `RawStatement`<commit_after>use super::ffi;
use super::unlock_notify;
use super::StatementStatus;
#[cfg(feature = "modern_sqlite")]
use crate::util::SqliteMallocString;
use std::ffi::CStr;
use std::os::raw::c_int;
use std::ptr;

// Private newtype for raw sqlite3_stmts that finalize themselves when dropped.
#[derive(Debug)]
pub struct RawStatement {
    ptr: *mut ffi::sqlite3_stmt,
    tail: bool,
    cache: crate::util::ParamIndexCache,
}

impl RawStatement {
    pub unsafe fn new(stmt: *mut ffi::sqlite3_stmt, tail: bool) -> RawStatement {
        RawStatement {
            ptr: stmt,
            tail,
            cache: Default::default(),
        }
    }

    pub fn is_null(&self) -> bool {
        self.ptr.is_null()
    }

    pub unsafe fn ptr(&self) -> *mut ffi::sqlite3_stmt {
        self.ptr
    }

    pub fn column_count(&self) -> usize {
        unsafe { ffi::sqlite3_column_count(self.ptr) as usize }
    }

    pub fn column_type(&self, idx: usize) -> c_int {
        unsafe { ffi::sqlite3_column_type(self.ptr, idx as c_int) }
    }

    pub fn column_decltype(&self, idx: usize) -> Option<&CStr> {
        unsafe {
            let decltype = ffi::sqlite3_column_decltype(self.ptr, idx as c_int);
            if decltype.is_null() {
                None
            } else {
                Some(CStr::from_ptr(decltype))
            }
        }
    }

    pub fn column_name(&self, idx: usize) -> Option<&CStr> {
        let idx = idx as c_int;
        if idx < 0 || idx >= self.column_count() as c_int {
            return None;
        }
        unsafe {
            let ptr = ffi::sqlite3_column_name(self.ptr, idx);
            // If ptr is null here, it's an OOM, so there's probably nothing
            // meaningful we can do. Just assert instead of returning None.
            assert!(
                !ptr.is_null(),
                "Null pointer from sqlite3_column_name: Out of memory?"
            );
            Some(CStr::from_ptr(ptr))
        }
    }

    pub fn step(&self) -> c_int {
        if cfg!(feature = "unlock_notify") {
            let db = unsafe { ffi::sqlite3_db_handle(self.ptr) };
            let mut rc;
            loop {
                rc = unsafe { ffi::sqlite3_step(self.ptr) };
                if unsafe { !unlock_notify::is_locked(db, rc) } {
                    break;
                }
                rc = unsafe { unlock_notify::wait_for_unlock_notify(db) };
                if rc != ffi::SQLITE_OK {
                    break;
                }
                self.reset();
            }
            rc
        } else {
            unsafe { ffi::sqlite3_step(self.ptr) }
        }
    }

    pub fn reset(&self) -> c_int {
        unsafe { ffi::sqlite3_reset(self.ptr) }
    }

    pub fn bind_parameter_count(&self) -> usize {
        unsafe { ffi::sqlite3_bind_parameter_count(self.ptr) as usize }
    }

    pub fn bind_parameter_index(&self, name: &str) -> Option<usize> {
        self.cache.get_or_insert_with(name, |param_cstr| {
            let r = unsafe { ffi::sqlite3_bind_parameter_index(self.ptr, param_cstr.as_ptr()) };
            match r {
                0 => None,
                i => Some(i as usize),
            }
        })
    }

    pub fn clear_bindings(&self) -> c_int {
        unsafe { ffi::sqlite3_clear_bindings(self.ptr) }
    }

    pub fn sql(&self) -> Option<&CStr> {
        if self.ptr.is_null() {
            None
        } else {
            Some(unsafe { CStr::from_ptr(ffi::sqlite3_sql(self.ptr)) })
        }
    }

    pub fn finalize(mut self) -> c_int {
        self.finalize_()
    }

    fn finalize_(&mut self) -> c_int {
        let r = unsafe { ffi::sqlite3_finalize(self.ptr) };
        self.ptr = ptr::null_mut();
        r
    }

    #[cfg(feature = "modern_sqlite")] // 3.7.4
    pub fn readonly(&self) -> bool {
        unsafe { ffi::sqlite3_stmt_readonly(self.ptr) != 0 }
    }

    #[cfg(feature = "modern_sqlite")] // 3.14.0
    pub(crate) fn expanded_sql(&self) -> Option<SqliteMallocString> {
        unsafe { SqliteMallocString::from_raw(ffi::sqlite3_expanded_sql(self.ptr)) }
    }

    pub fn get_status(&self, status: StatementStatus, reset: bool) -> i32 {
        assert!(!self.ptr.is_null());
        unsafe { ffi::sqlite3_stmt_status(self.ptr, status as i32, reset as i32) }
    }

    pub fn has_tail(&self) -> bool {
        self.tail
    }
}

impl Drop for RawStatement {
    fn drop(&mut self) {
        self.finalize_();
    }
}
<|endoftext|>


<commit_before>extern crate mio;
extern crate rand;

use mio::{EventLoop,Token,TryWrite, Interest, PollOpt};
use mio::tcp::*;
use mio::udp::*;
use mio::buf::*;
use rand::{thread_rng, Rng};
use std::fs::File;
use std::io::*;

trait QuoteProviderImpl {
  fn get_quote(&self, usize) -> String;
  fn get_random_quote(&self) -> String;
  fn new() -> Self;
}

struct QuoteProvider {
  quotes: Vec<String>,
}

impl QuoteProviderImpl for QuoteProvider {

  fn get_quote(&self, index: usize) -> String {
    //todo: pointers not clones?
    self.quotes[index].clone()
  }

  fn get_random_quote(&self) -> String {
    //todo: cache rng for improved perf
    let mut rng = thread_rng();
    let index: usize = rng.gen_range(0, self.quotes.len());
    self.get_quote(index)
  }

  fn new() -> QuoteProvider {
    let mut quotes: Vec<String> = Vec::new();
    println!("Reading wisdom...");
    //todo: check exists
    //todo: pass in as arg
    let path = "../../src/server/wisdom.txt";
    let result = File::open(path);
    match result {
        Ok(file) => {
            let reader = BufReader::new(file);
            for line in reader.lines() {
                match line {
                    Ok (line_str) => {
                        if line_str.len() > 0 {
                            quotes.push(line_str + "\n");
                        } else {
                            println!("Skipped blank line in file.");
                        }
                    },
                    Err(e) => println!("Invalid line. Skipping... {:?}",e)
                }
            }

        },
        Err(e) => println!("Failed to open wisdom file {:?}. {:?}", path, e)
    }

    QuoteProvider {quotes: quotes}
  }
}

const TCP_SERVER: mio::Token = mio::Token(0);
const UDP_SERVER: mio::Token = mio::Token(1);

struct QotdServer {
    tcp_server: TcpListener,
    udp_server: UdpSocket,
    quote_provider: QuoteProvider
}

impl mio::Handler for QotdServer {
    type Timeout = ();
    type Message = ();

    #[allow(unused_variables)]
    fn readable(&mut self, event_loop: &mut mio::EventLoop<QotdServer>, token: mio::Token, hint: mio::ReadHint) {
        match token {
            TCP_SERVER => {
                println!("the server socket is ready to accept a TCP connection");
                match self.tcp_server.accept() {
                    Ok(Some(mut connection)) => {
                        println!("accepted a socket {:?}", connection);
                        let quote = self.quote_provider.get_random_quote();
                        match connection.write_slice(quote.as_bytes()) {
                            Ok(Some(_)) => println!("Wrote to tcp"),
                            Ok(None) => println!("Didn't write anything to tcp"),
                            Err(e) => println!("Failed to write. {:?}", e)

                        }
                        drop(connection);
                    }
                    Ok(None) => {
                        println!("the server socket wasn't actually ready")
                    }
                    Err(e) => {
                        println!("listener.accept() errored: {}", e);
                        event_loop.shutdown();
                    }
                }
            }
            UDP_SERVER => {
                println!("the server socket is ready to accept a UDP connection");
                let mut buf = [0; 128];
                match self.udp_server.recv_from(&mut MutSliceBuf::wrap(&mut buf)) {
                    Ok(Some(addr)) => {
                        let quote = self.quote_provider.get_random_quote();
                        let mut quote_buf = SliceBuf::wrap(&mut quote.as_bytes());
                        match self.udp_server.send_to(&mut quote_buf, &addr) {
                            Ok(Some(_)) => println!("Wrote to udp"),
                            Ok(None) => println!("Failed to write any bytes"),
                            Err(e) => println!("Faile to write to udp. {:?}", e)
                        }
                    }
                    Ok(None) => println!("Couldn't write to socket? Or was it just empty?"),
                    Err(e) => println!("couldn't receive a datagram: {}", e)
                }
            }
            _ => panic!("Received unknown token"),
        }
    }
}

fn main() {

    let quote_provider: QuoteProvider = QuoteProviderImpl::new();
    println!("Sample quote: {:?}", quote_provider.get_random_quote());

    let address = "0.0.0.0:6567".parse().unwrap();
    let tcp_server = TcpListener::bind(&address).unwrap();
    let udp_server = UdpSocket::v4().unwrap();
    let _ = udp_server.bind(&address);

    let mut event_loop = mio::EventLoop::new().unwrap();
    let _ = event_loop.register_opt(&tcp_server, TCP_SERVER, Interest::readable(), PollOpt::edge());
    let _ = event_loop.register_opt(&udp_server, UDP_SERVER, Interest::readable(), PollOpt::edge());

    println!("running qotd server");
    let mut qotd_server = QotdServer
    {
        tcp_server: tcp_server,
        udp_server: udp_server,
        quote_provider: quote_provider
    };
    let _ = event_loop.run(&mut qotd_server);

    drop(qotd_server.udp_server);
    drop(qotd_server.tcp_server);

}<commit_msg>started refactoring into methods and impls<commit_after>extern crate mio;
extern crate rand;

use mio::{EventLoop,Token,TryWrite, Interest, PollOpt};
use mio::tcp::*;
use mio::udp::*;
use mio::buf::*;
use rand::{thread_rng, Rng};
use std::fs::File;
use std::io::*;

trait QuoteProviderImpl {
  fn get_quote(&self, usize) -> String;
  fn get_random_quote(&self) -> String;
  fn new() -> Self;
}

struct QuoteProvider {
  quotes: Vec<String>,
}

impl QuoteProviderImpl for QuoteProvider {

  fn get_quote(&self, index: usize) -> String {
    //todo: pointers not clones?
    self.quotes[index].clone()
  }

  fn get_random_quote(&self) -> String {
    //todo: cache rng for improved perf
    let mut rng = thread_rng();
    let index: usize = rng.gen_range(0, self.quotes.len());
    self.get_quote(index)
  }

  fn new() -> QuoteProvider {
    let mut quotes: Vec<String> = Vec::new();
    println!("Reading wisdom...");
    //todo: check exists
    //todo: pass in as arg
    let path = "../../src/server/wisdom.txt";
    let result = File::open(path);
    match result {
        Ok(file) => {
            let reader = BufReader::new(file);
            for line in reader.lines() {
                match line {
                    Ok (line_str) => {
                        if line_str.len() > 0 {
                            quotes.push(line_str + "\n");
                        } else {
                            println!("Skipped blank line in file.");
                        }
                    },
                    Err(e) => println!("Invalid line. Skipping... {:?}",e)
                }
            }

        },
        Err(e) => println!("Failed to open wisdom file {:?}. {:?}", path, e)
    }

    QuoteProvider {quotes: quotes}
  }
}

const TCP_SERVER: mio::Token = mio::Token(0);
const UDP_SERVER: mio::Token = mio::Token(1);

struct QotdServer {
    tcp_server: TcpListener,
    udp_server: UdpSocket,
    quote_provider: QuoteProvider
}

impl QotdServer {
    fn accept_tcp_connection(quote_provider: &QuoteProvider, tcp_listener: &TcpListener) {

        let stream_result = tcp_listener.accept();
        match stream_result {
            Ok(Some(mut connection)) => {
                println!("accepted a socket {:?}", connection);
                let quote = quote_provider.get_random_quote();
                //try!(connection.write_slice(quote.as_bytes()));
                let r = connection.write_slice(quote.as_bytes()) ;
                QotdServer::log_socket_write(r);
                drop(connection);
            }
            Ok(None) => {
                println!("the server socket wasn't actually ready")
            }
            Err(e) => {
                println!("listener.accept() errored: {}", e);
            }
        }
    }

    fn accept_udp_connection(quote_provider: &QuoteProvider, udp_socket: &UdpSocket) {
        println!("the server socket is ready to accept a UDP connection");
        let mut buf = [0; 128];
        match udp_socket.recv_from(&mut MutSliceBuf::wrap(&mut buf)) {
            Ok(Some(addr)) => {
                let quote = quote_provider.get_random_quote();
                let mut quote_buf = SliceBuf::wrap(&mut quote.as_bytes());
                let r = udp_socket.send_to(&mut quote_buf, &addr);
                QotdServer::log_socket_write(r);
            }
            Ok(None) => println!("Couldn't write to socket? Or was it just empty?"),
            Err(e) => println!("couldn't receive a datagram: {}", e)
        }
    }

    fn log_socket_write<T>(result: Result<Option<T>>) where T: std::fmt::Debug {
        match result {
            Ok(Some(bytes)) => println!("Wrote {:?} bytes to socket ", bytes),
            Ok(None) => println!("Didn't write any bytes"),
            Err(e) => println!("Failed to write. {:?}", e)
        }
    }
}

impl mio::Handler for QotdServer {
    type Timeout = ();
    type Message = ();

    #[allow(unused_variables)]
    fn readable(&mut self, event_loop: &mut mio::EventLoop<QotdServer>, token: mio::Token, hint: mio::ReadHint) {
        match token {
            TCP_SERVER => QotdServer::accept_tcp_connection(&self.quote_provider, &self.tcp_server),
            UDP_SERVER => QotdServer::accept_udp_connection(&self.quote_provider, &self.udp_server),
            _ => panic!("Received unknown token"),
        }
    }
}

macro_rules! vecxx {
    ( $( $x:expr ),* ) => {
        {
            let mut temp_vec = Vec::new();
            $(
                temp_vec.push($x);
            )*
            temp_vec
        }
    };
}

fn main() {

    let quote_provider: QuoteProvider = QuoteProviderImpl::new();
    println!("Sample quote: {:?}", quote_provider.get_random_quote());

    let address = "0.0.0.0:6567".parse().unwrap();
    let tcp_server = TcpListener::bind(&address).unwrap();
    let udp_server = UdpSocket::v4().unwrap();
    let _ = udp_server.bind(&address);

    let mut event_loop = mio::EventLoop::new().unwrap();
    let _ = event_loop.register_opt(&tcp_server, TCP_SERVER, Interest::readable(), PollOpt::edge());
    let _ = event_loop.register_opt(&udp_server, UDP_SERVER, Interest::readable(), PollOpt::edge());

    println!("running qotd server");
    let mut qotd_server = QotdServer
    {
        tcp_server: tcp_server,
        udp_server: udp_server,
        quote_provider: quote_provider
    };
    let _ = event_loop.run(&mut qotd_server);

    drop(qotd_server.udp_server);
    drop(qotd_server.tcp_server);

}<|endoftext|>


<commit_before>use std::iter;

use rustc_middle::mir;
use rustc_target::abi::Size;

use crate::*;

impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
    fn emulate_foreign_item_by_name(
        &mut self,
        link_name: &str,
        args: &[OpTy<'tcx, Tag>],
        dest: PlaceTy<'tcx, Tag>,
        _ret: mir::BasicBlock,
    ) -> InterpResult<'tcx, bool> {
        let this = self.eval_context_mut();

        // Windows API stubs.
        // HANDLE = isize
        // DWORD = ULONG = u32
        // BOOL = i32
        match link_name {
            // Environment related shims
            "GetEnvironmentVariableW" => {
                let result = this.GetEnvironmentVariableW(args[0], args[1], args[2])?;
                this.write_scalar(Scalar::from_u32(result), dest)?;
            }
            "SetEnvironmentVariableW" => {
                let result = this.SetEnvironmentVariableW(args[0], args[1])?;
                this.write_scalar(Scalar::from_i32(result), dest)?;
            }
            "GetEnvironmentStringsW" => {
                let result = this.GetEnvironmentStringsW()?;
                this.write_scalar(result, dest)?;
            }
            "FreeEnvironmentStringsW" => {
                let result = this.FreeEnvironmentStringsW(args[0])?;
                this.write_scalar(Scalar::from_i32(result), dest)?;
            }
            "GetCurrentDirectoryW" => {
                let result = this.GetCurrentDirectoryW(args[0], args[1])?;
                this.write_scalar(Scalar::from_u32(result), dest)?;
            }
            "SetCurrentDirectoryW" => {
                let result = this.SetCurrentDirectoryW(args[0])?;
                this.write_scalar(Scalar::from_i32(result), dest)?;
            }

            // File related shims
            "GetStdHandle" => {
                let which = this.read_scalar(args[0])?.to_i32()?;
                // We just make this the identity function, so we know later in `WriteFile`
                // which one it is.
                this.write_scalar(Scalar::from_machine_isize(which.into(), this), dest)?;
            }
            "WriteFile" => {
                let handle = this.read_scalar(args[0])?.to_machine_isize(this)?;
                let buf = this.read_scalar(args[1])?.not_undef()?;
                let n = this.read_scalar(args[2])?.to_u32()?;
                let written_place = this.deref_operand(args[3])?;
                // Spec says to always write `0` first.
                this.write_null(written_place.into())?;
                let written = if handle == -11 || handle == -12 {
                    // stdout/stderr
                    use std::io::{self, Write};

                    let buf_cont = this.memory.read_bytes(buf, Size::from_bytes(u64::from(n)))?;
                    let res = if handle == -11 {
                        io::stdout().write(buf_cont)
                    } else {
                        io::stderr().write(buf_cont)
                    };
                    res.ok().map(|n| n as u32)
                } else {
                    throw_unsup_format!("on Windows, writing to anything except stdout/stderr is not supported")
                };
                // If there was no error, write back how much was written.
                if let Some(n) = written {
                    this.write_scalar(Scalar::from_u32(n), written_place.into())?;
                }
                // Return whether this was a success.
                this.write_scalar(
                    Scalar::from_i32(if written.is_some() { 1 } else { 0 }),
                    dest,
                )?;
            }

            // Allocation
            "HeapAlloc" => {
                let _handle = this.read_scalar(args[0])?.to_machine_isize(this)?;
                let flags = this.read_scalar(args[1])?.to_u32()?;
                let size = this.read_scalar(args[2])?.to_machine_usize(this)?;
                let zero_init = (flags & 0x00000008) != 0; // HEAP_ZERO_MEMORY
                let res = this.malloc(size, zero_init, MiriMemoryKind::WinHeap);
                this.write_scalar(res, dest)?;
            }
            "HeapFree" => {
                let _handle = this.read_scalar(args[0])?.to_machine_isize(this)?;
                let _flags = this.read_scalar(args[1])?.to_u32()?;
                let ptr = this.read_scalar(args[2])?.not_undef()?;
                this.free(ptr, MiriMemoryKind::WinHeap)?;
                this.write_scalar(Scalar::from_i32(1), dest)?;
            }
            "HeapReAlloc" => {
                let _handle = this.read_scalar(args[0])?.to_machine_isize(this)?;
                let _flags = this.read_scalar(args[1])?.to_u32()?;
                let ptr = this.read_scalar(args[2])?.not_undef()?;
                let size = this.read_scalar(args[3])?.to_machine_usize(this)?;
                let res = this.realloc(ptr, size, MiriMemoryKind::WinHeap)?;
                this.write_scalar(res, dest)?;
            }

            // errno
            "SetLastError" => {
                this.set_last_error(this.read_scalar(args[0])?.not_undef()?)?;
            }
            "GetLastError" => {
                let last_error = this.get_last_error()?;
                this.write_scalar(last_error, dest)?;
            }

            // Querying system information
            "GetSystemInfo" => {
                let system_info = this.deref_operand(args[0])?;
                // Initialize with `0`.
                this.memory.write_bytes(
                    system_info.ptr,
                    iter::repeat(0u8).take(system_info.layout.size.bytes() as usize),
                )?;
                // Set number of processors.
                let dword_size = Size::from_bytes(4);
                let num_cpus = this.mplace_field(system_info, 6)?;
                this.write_scalar(Scalar::from_int(NUM_CPUS, dword_size), num_cpus.into())?;
            }

            // Thread-local storage
            "TlsAlloc" => {
                // This just creates a key; Windows does not natively support TLS destructors.

                // Create key and return it.
                let key = this.machine.tls.create_tls_key(None, dest.layout.size)?;
                this.write_scalar(Scalar::from_uint(key, dest.layout.size), dest)?;
            }
            "TlsGetValue" => {
                let key = u128::from(this.read_scalar(args[0])?.to_u32()?);
                let ptr = this.machine.tls.load_tls(key, this)?;
                this.write_scalar(ptr, dest)?;
            }
            "TlsSetValue" => {
                let key = u128::from(this.read_scalar(args[0])?.to_u32()?);
                let new_ptr = this.read_scalar(args[1])?.not_undef()?;
                this.machine.tls.store_tls(key, this.test_null(new_ptr)?)?;

                // Return success (`1`).
                this.write_scalar(Scalar::from_i32(1), dest)?;
            }

            // Access to command-line arguments
            "GetCommandLineW" => {
                this.write_scalar(
                    this.machine.cmd_line.expect("machine must be initialized"),
                    dest,
                )?;
            }

            // Time related shims
            "GetSystemTimeAsFileTime" => {
                this.GetSystemTimeAsFileTime(args[0])?;
            }
            "QueryPerformanceCounter" => {
                let result = this.QueryPerformanceCounter(args[0])?;
                this.write_scalar(Scalar::from_i32(result), dest)?;
            }
            "QueryPerformanceFrequency" => {
                let result = this.QueryPerformanceFrequency(args[0])?;
                this.write_scalar(Scalar::from_i32(result), dest)?;
            }

            // Miscellaneous
            "SystemFunction036" => {
                // The actual name of 'RtlGenRandom'
                let ptr = this.read_scalar(args[0])?.not_undef()?;
                let len = this.read_scalar(args[1])?.to_u32()?;
                this.gen_random(ptr, len.into())?;
                this.write_scalar(Scalar::from_bool(true), dest)?;
            }
            "GetConsoleScreenBufferInfo" => {
                // `term` needs this, so we fake it.
                let _console = this.read_scalar(args[0])?.to_machine_isize(this)?;
                let _buffer_info = this.deref_operand(args[1])?;
                // Indicate an error.
                // FIXME: we should set last_error, but to what?
                this.write_null(dest)?;
            }
            "GetConsoleMode" => {
                // Windows "isatty" (in libtest) needs this, so we fake it.
                let _console = this.read_scalar(args[0])?.to_machine_isize(this)?;
                let _mode = this.deref_operand(args[1])?;
                // Indicate an error.
                // FIXME: we should set last_error, but to what?
                this.write_null(dest)?;
            }

            // Better error for attempts to create a thread
            "CreateThread" => {
                throw_unsup_format!("Miri does not support threading");
            }

            // Incomplete shims that we "stub out" just to get pre-main initialziation code to work.
            // These shims are enabled only when the caller is in the standard library.
            "GetProcessHeap" if this.frame().instance.to_string().starts_with("std::sys::windows::") => {
                // Just fake a HANDLE
                this.write_scalar(Scalar::from_machine_isize(1, this), dest)?;
            }
            | "GetModuleHandleW"
            | "GetProcAddress"
            | "SetConsoleTextAttribute" if this.frame().instance.to_string().starts_with("std::sys::windows::")
            => {
                // Pretend these do not exist / nothing happened, by returning zero.
                this.write_null(dest)?;
            }
            "AddVectoredExceptionHandler" if this.frame().instance.to_string().starts_with("std::sys::windows::") => {
                // Any non zero value works for the stdlib. This is just used for stack overflows anyway.
                this.write_scalar(Scalar::from_machine_usize(1, this), dest)?;
            }
            | "InitializeCriticalSection"
            | "EnterCriticalSection"
            | "LeaveCriticalSection"
            | "DeleteCriticalSection" if this.frame().instance.to_string().starts_with("std::sys::windows::")
            => {
                // Nothing to do, not even a return value.
                // (Windows locks are reentrant, and we have only 1 thread,
                // so not doing any futher checks here is at least not incorrect.)
            }
            "TryEnterCriticalSection" if this.frame().instance.to_string().starts_with("std::sys::windows::")
            => {
                // There is only one thread, so this always succeeds and returns TRUE
                this.write_scalar(Scalar::from_int(1, dest.layout.size), dest)?;
            }

            _ => throw_unsup_format!("can't call foreign function: {}", link_name),
        }

        Ok(true)
    }
}

<commit_msg>Set explicit return value size for windows shim<commit_after>use std::iter;

use rustc_middle::mir;
use rustc_target::abi::Size;

use crate::*;

impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
    fn emulate_foreign_item_by_name(
        &mut self,
        link_name: &str,
        args: &[OpTy<'tcx, Tag>],
        dest: PlaceTy<'tcx, Tag>,
        _ret: mir::BasicBlock,
    ) -> InterpResult<'tcx, bool> {
        let this = self.eval_context_mut();

        // Windows API stubs.
        // HANDLE = isize
        // DWORD = ULONG = u32
        // BOOL = i32
        match link_name {
            // Environment related shims
            "GetEnvironmentVariableW" => {
                let result = this.GetEnvironmentVariableW(args[0], args[1], args[2])?;
                this.write_scalar(Scalar::from_u32(result), dest)?;
            }
            "SetEnvironmentVariableW" => {
                let result = this.SetEnvironmentVariableW(args[0], args[1])?;
                this.write_scalar(Scalar::from_i32(result), dest)?;
            }
            "GetEnvironmentStringsW" => {
                let result = this.GetEnvironmentStringsW()?;
                this.write_scalar(result, dest)?;
            }
            "FreeEnvironmentStringsW" => {
                let result = this.FreeEnvironmentStringsW(args[0])?;
                this.write_scalar(Scalar::from_i32(result), dest)?;
            }
            "GetCurrentDirectoryW" => {
                let result = this.GetCurrentDirectoryW(args[0], args[1])?;
                this.write_scalar(Scalar::from_u32(result), dest)?;
            }
            "SetCurrentDirectoryW" => {
                let result = this.SetCurrentDirectoryW(args[0])?;
                this.write_scalar(Scalar::from_i32(result), dest)?;
            }

            // File related shims
            "GetStdHandle" => {
                let which = this.read_scalar(args[0])?.to_i32()?;
                // We just make this the identity function, so we know later in `WriteFile`
                // which one it is.
                this.write_scalar(Scalar::from_machine_isize(which.into(), this), dest)?;
            }
            "WriteFile" => {
                let handle = this.read_scalar(args[0])?.to_machine_isize(this)?;
                let buf = this.read_scalar(args[1])?.not_undef()?;
                let n = this.read_scalar(args[2])?.to_u32()?;
                let written_place = this.deref_operand(args[3])?;
                // Spec says to always write `0` first.
                this.write_null(written_place.into())?;
                let written = if handle == -11 || handle == -12 {
                    // stdout/stderr
                    use std::io::{self, Write};

                    let buf_cont = this.memory.read_bytes(buf, Size::from_bytes(u64::from(n)))?;
                    let res = if handle == -11 {
                        io::stdout().write(buf_cont)
                    } else {
                        io::stderr().write(buf_cont)
                    };
                    res.ok().map(|n| n as u32)
                } else {
                    throw_unsup_format!("on Windows, writing to anything except stdout/stderr is not supported")
                };
                // If there was no error, write back how much was written.
                if let Some(n) = written {
                    this.write_scalar(Scalar::from_u32(n), written_place.into())?;
                }
                // Return whether this was a success.
                this.write_scalar(
                    Scalar::from_i32(if written.is_some() { 1 } else { 0 }),
                    dest,
                )?;
            }

            // Allocation
            "HeapAlloc" => {
                let _handle = this.read_scalar(args[0])?.to_machine_isize(this)?;
                let flags = this.read_scalar(args[1])?.to_u32()?;
                let size = this.read_scalar(args[2])?.to_machine_usize(this)?;
                let zero_init = (flags & 0x00000008) != 0; // HEAP_ZERO_MEMORY
                let res = this.malloc(size, zero_init, MiriMemoryKind::WinHeap);
                this.write_scalar(res, dest)?;
            }
            "HeapFree" => {
                let _handle = this.read_scalar(args[0])?.to_machine_isize(this)?;
                let _flags = this.read_scalar(args[1])?.to_u32()?;
                let ptr = this.read_scalar(args[2])?.not_undef()?;
                this.free(ptr, MiriMemoryKind::WinHeap)?;
                this.write_scalar(Scalar::from_i32(1), dest)?;
            }
            "HeapReAlloc" => {
                let _handle = this.read_scalar(args[0])?.to_machine_isize(this)?;
                let _flags = this.read_scalar(args[1])?.to_u32()?;
                let ptr = this.read_scalar(args[2])?.not_undef()?;
                let size = this.read_scalar(args[3])?.to_machine_usize(this)?;
                let res = this.realloc(ptr, size, MiriMemoryKind::WinHeap)?;
                this.write_scalar(res, dest)?;
            }

            // errno
            "SetLastError" => {
                this.set_last_error(this.read_scalar(args[0])?.not_undef()?)?;
            }
            "GetLastError" => {
                let last_error = this.get_last_error()?;
                this.write_scalar(last_error, dest)?;
            }

            // Querying system information
            "GetSystemInfo" => {
                let system_info = this.deref_operand(args[0])?;
                // Initialize with `0`.
                this.memory.write_bytes(
                    system_info.ptr,
                    iter::repeat(0u8).take(system_info.layout.size.bytes() as usize),
                )?;
                // Set number of processors.
                let dword_size = Size::from_bytes(4);
                let num_cpus = this.mplace_field(system_info, 6)?;
                this.write_scalar(Scalar::from_int(NUM_CPUS, dword_size), num_cpus.into())?;
            }

            // Thread-local storage
            "TlsAlloc" => {
                // This just creates a key; Windows does not natively support TLS destructors.

                // Create key and return it.
                let key = this.machine.tls.create_tls_key(None, dest.layout.size)?;
                this.write_scalar(Scalar::from_uint(key, dest.layout.size), dest)?;
            }
            "TlsGetValue" => {
                let key = u128::from(this.read_scalar(args[0])?.to_u32()?);
                let ptr = this.machine.tls.load_tls(key, this)?;
                this.write_scalar(ptr, dest)?;
            }
            "TlsSetValue" => {
                let key = u128::from(this.read_scalar(args[0])?.to_u32()?);
                let new_ptr = this.read_scalar(args[1])?.not_undef()?;
                this.machine.tls.store_tls(key, this.test_null(new_ptr)?)?;

                // Return success (`1`).
                this.write_scalar(Scalar::from_i32(1), dest)?;
            }

            // Access to command-line arguments
            "GetCommandLineW" => {
                this.write_scalar(
                    this.machine.cmd_line.expect("machine must be initialized"),
                    dest,
                )?;
            }

            // Time related shims
            "GetSystemTimeAsFileTime" => {
                this.GetSystemTimeAsFileTime(args[0])?;
            }
            "QueryPerformanceCounter" => {
                let result = this.QueryPerformanceCounter(args[0])?;
                this.write_scalar(Scalar::from_i32(result), dest)?;
            }
            "QueryPerformanceFrequency" => {
                let result = this.QueryPerformanceFrequency(args[0])?;
                this.write_scalar(Scalar::from_i32(result), dest)?;
            }

            // Miscellaneous
            "SystemFunction036" => {
                // The actual name of 'RtlGenRandom'
                let ptr = this.read_scalar(args[0])?.not_undef()?;
                let len = this.read_scalar(args[1])?.to_u32()?;
                this.gen_random(ptr, len.into())?;
                this.write_scalar(Scalar::from_bool(true), dest)?;
            }
            "GetConsoleScreenBufferInfo" => {
                // `term` needs this, so we fake it.
                let _console = this.read_scalar(args[0])?.to_machine_isize(this)?;
                let _buffer_info = this.deref_operand(args[1])?;
                // Indicate an error.
                // FIXME: we should set last_error, but to what?
                this.write_null(dest)?;
            }
            "GetConsoleMode" => {
                // Windows "isatty" (in libtest) needs this, so we fake it.
                let _console = this.read_scalar(args[0])?.to_machine_isize(this)?;
                let _mode = this.deref_operand(args[1])?;
                // Indicate an error.
                // FIXME: we should set last_error, but to what?
                this.write_null(dest)?;
            }

            // Better error for attempts to create a thread
            "CreateThread" => {
                throw_unsup_format!("Miri does not support threading");
            }

            // Incomplete shims that we "stub out" just to get pre-main initialziation code to work.
            // These shims are enabled only when the caller is in the standard library.
            "GetProcessHeap" if this.frame().instance.to_string().starts_with("std::sys::windows::") => {
                // Just fake a HANDLE
                this.write_scalar(Scalar::from_machine_isize(1, this), dest)?;
            }
            | "GetModuleHandleW"
            | "GetProcAddress"
            | "SetConsoleTextAttribute" if this.frame().instance.to_string().starts_with("std::sys::windows::")
            => {
                // Pretend these do not exist / nothing happened, by returning zero.
                this.write_null(dest)?;
            }
            "AddVectoredExceptionHandler" if this.frame().instance.to_string().starts_with("std::sys::windows::") => {
                // Any non zero value works for the stdlib. This is just used for stack overflows anyway.
                this.write_scalar(Scalar::from_machine_usize(1, this), dest)?;
            }
            | "InitializeCriticalSection"
            | "EnterCriticalSection"
            | "LeaveCriticalSection"
            | "DeleteCriticalSection" if this.frame().instance.to_string().starts_with("std::sys::windows::")
            => {
                // Nothing to do, not even a return value.
                // (Windows locks are reentrant, and we have only 1 thread,
                // so not doing any futher checks here is at least not incorrect.)
            }
            "TryEnterCriticalSection" if this.frame().instance.to_string().starts_with("std::sys::windows::")
            => {
                // There is only one thread, so this always succeeds and returns TRUE
                this.write_scalar(Scalar::from_i32(1), dest)?;
            }

            _ => throw_unsup_format!("can't call foreign function: {}", link_name),
        }

        Ok(true)
    }
}

<|endoftext|>


<commit_before>//! Module contianing the core functionality for OAuth2 Authentication.
use crate::authenticator_delegate::{
    AuthenticatorDelegate, DefaultAuthenticatorDelegate, FlowDelegate,
};
use crate::device::DeviceFlow;
use crate::error::Error;
use crate::installed::{InstalledFlow, InstalledFlowReturnMethod};
use crate::refresh::RefreshFlow;
use crate::storage::{self, Storage};
use crate::types::{ApplicationSecret, Token};
use private::AuthFlow;

use std::borrow::Cow;
use std::io;
use std::path::PathBuf;
use std::sync::Mutex;
use std::time::Duration;

/// Authenticator is responsible for fetching tokens, handling refreshing tokens,
/// and optionally persisting tokens to disk.
pub struct Authenticator<C> {
    hyper_client: hyper::Client<C>,
    app_secret: ApplicationSecret,
    auth_delegate: Box<dyn AuthenticatorDelegate>,
    storage: Storage,
    auth_flow: AuthFlow,
}

impl<C> Authenticator<C>
where
    C: hyper::client::connect::Connect + 'static,
{
    /// Return the current token for the provided scopes.
    pub async fn token<'a, T>(&'a self, scopes: &'a [T]) -> Result<Token, Error>
    where
        T: AsRef<str>,
    {
        let scope_key = storage::ScopeHash::new(scopes);
        match self.storage.get(scope_key, scopes) {
            Some(t) if !t.expired() => {
                // unexpired token found
                Ok(t)
            }
            Some(Token {
                refresh_token: Some(refresh_token),
                ..
            }) => {
                // token is expired but has a refresh token.
                let token = match RefreshFlow::refresh_token(
                    &self.hyper_client,
                    &self.app_secret,
                    &refresh_token,
                )
                .await
                {
                    Err(err) => {
                        self.auth_delegate.token_refresh_failed(&err);
                        return Err(err.into());
                    }
                    Ok(token) => token,
                };
                self.storage
                    .set(scope_key, scopes, Some(token.clone()))
                    .await;
                Ok(token)
            }
            None
            | Some(Token {
                refresh_token: None,
                ..
            }) => {
                // no token in the cache or the token returned does not contain a refresh token.
                let t = self
                    .auth_flow
                    .token(&self.hyper_client, &self.app_secret, scopes)
                    .await?;
                self.storage.set(scope_key, scopes, Some(t.clone())).await;
                Ok(t)
            }
        }
    }
}

/// Configure an Authenticator using the builder pattern.
pub struct AuthenticatorBuilder<C, F> {
    hyper_client_builder: C,
    app_secret: ApplicationSecret,
    auth_delegate: Box<dyn AuthenticatorDelegate>,
    storage_type: StorageType,
    auth_flow: F,
}

/// Create an authenticator that uses the installed flow.
/// ```
/// # async fn foo() {
/// # use yup_oauth2::InstalledFlowReturnMethod;
/// # let custom_flow_delegate = yup_oauth2::authenticator_delegate::DefaultFlowDelegate;
/// # let app_secret = yup_oauth2::read_application_secret("/tmp/foo").unwrap();
///     let authenticator = yup_oauth2::InstalledFlowAuthenticator::builder(
///         app_secret,
///         InstalledFlowReturnMethod::HTTPRedirect,
///     )
///     .build()
///     .await
///     .expect("failed to create authenticator");
/// # }
/// ```
pub struct InstalledFlowAuthenticator;
impl InstalledFlowAuthenticator {
    /// Use the builder pattern to create an Authenticator that uses the installed flow.
    pub fn builder(
        app_secret: ApplicationSecret,
        method: InstalledFlowReturnMethod,
    ) -> AuthenticatorBuilder<DefaultHyperClient, InstalledFlow> {
        AuthenticatorBuilder::<DefaultHyperClient, _>::with_auth_flow(
            app_secret,
            InstalledFlow::new(method),
        )
    }
}

/// Create an authenticator that uses the device flow.
/// ```
/// # async fn foo() {
/// # let app_secret = yup_oauth2::read_application_secret("/tmp/foo").unwrap();
///     let authenticator = yup_oauth2::DeviceFlowAuthenticator::builder(app_secret)
///         .build()
///         .await
///         .expect("failed to create authenticator");
/// # }
/// ```
pub struct DeviceFlowAuthenticator;
impl DeviceFlowAuthenticator {
    /// Use the builder pattern to create an Authenticator that uses the device flow.
    pub fn builder(
        app_secret: ApplicationSecret,
    ) -> AuthenticatorBuilder<DefaultHyperClient, DeviceFlow> {
        AuthenticatorBuilder::<DefaultHyperClient, _>::with_auth_flow(app_secret, DeviceFlow::new())
    }
}

/// Methods available when building any Authenticator.
/// ```
/// # async fn foo() {
/// # let custom_hyper_client = hyper::Client::new();
/// # let custom_auth_delegate = yup_oauth2::authenticator_delegate::DefaultAuthenticatorDelegate;
/// # let app_secret = yup_oauth2::read_application_secret("/tmp/foo").unwrap();
///     let authenticator = yup_oauth2::DeviceFlowAuthenticator::builder(app_secret)
///         .hyper_client(custom_hyper_client)
///         .persist_tokens_to_disk("/tmp/tokenfile.json")
///         .auth_delegate(Box::new(custom_auth_delegate))
///         .build()
///         .await
///         .expect("failed to create authenticator");
/// # }
/// ```
impl<C, F> AuthenticatorBuilder<C, F> {
    /// Create the authenticator.
    pub async fn build(self) -> io::Result<Authenticator<C::Connector>>
    where
        C: HyperClientBuilder,
        F: Into<AuthFlow>,
    {
        let hyper_client = self.hyper_client_builder.build_hyper_client();
        let storage = match self.storage_type {
            StorageType::Memory => Storage::Memory {
                tokens: Mutex::new(storage::JSONTokens::new()),
            },
            StorageType::Disk(path) => Storage::Disk(storage::DiskStorage::new(path).await?),
        };

        Ok(Authenticator {
            hyper_client,
            app_secret: self.app_secret,
            storage,
            auth_delegate: self.auth_delegate,
            auth_flow: self.auth_flow.into(),
        })
    }

    fn with_auth_flow(
        app_secret: ApplicationSecret,
        auth_flow: F,
    ) -> AuthenticatorBuilder<DefaultHyperClient, F> {
        AuthenticatorBuilder {
            hyper_client_builder: DefaultHyperClient,
            app_secret,
            auth_delegate: Box::new(DefaultAuthenticatorDelegate),
            storage_type: StorageType::Memory,
            auth_flow,
        }
    }

    /// Use the provided hyper client.
    pub fn hyper_client<NewC>(
        self,
        hyper_client: hyper::Client<NewC>,
    ) -> AuthenticatorBuilder<hyper::Client<NewC>, F> {
        AuthenticatorBuilder {
            hyper_client_builder: hyper_client,
            app_secret: self.app_secret,
            auth_delegate: self.auth_delegate,
            storage_type: self.storage_type,
            auth_flow: self.auth_flow,
        }
    }

    /// Persist tokens to disk in the provided filename.
    pub fn persist_tokens_to_disk<P: Into<PathBuf>>(self, path: P) -> AuthenticatorBuilder<C, F> {
        AuthenticatorBuilder {
            storage_type: StorageType::Disk(path.into()),
            ..self
        }
    }

    /// Use the provided authenticator delegate.
    pub fn auth_delegate(
        self,
        auth_delegate: Box<dyn AuthenticatorDelegate>,
    ) -> AuthenticatorBuilder<C, F> {
        AuthenticatorBuilder {
            auth_delegate,
            ..self
        }
    }
}

/// Methods available when building a device flow Authenticator.
/// ```
/// # async fn foo() {
/// # let custom_flow_delegate = yup_oauth2::authenticator_delegate::DefaultFlowDelegate;
/// # let app_secret = yup_oauth2::read_application_secret("/tmp/foo").unwrap();
///     let authenticator = yup_oauth2::DeviceFlowAuthenticator::builder(app_secret)
///         .device_code_url("foo")
///         .flow_delegate(Box::new(custom_flow_delegate))
///         .wait_duration(std::time::Duration::from_secs(120))
///         .grant_type("foo")
///         .build()
///         .await
///         .expect("failed to create authenticator");
/// # }
/// ```
impl<C> AuthenticatorBuilder<C, DeviceFlow> {
    /// Use the provided device code url.
    pub fn device_code_url(self, url: impl Into<Cow<'static, str>>) -> Self {
        AuthenticatorBuilder {
            auth_flow: DeviceFlow {
                device_code_url: url.into(),
                ..self.auth_flow
            },
            ..self
        }
    }

    /// Use the provided FlowDelegate.
    pub fn flow_delegate(self, flow_delegate: Box<dyn FlowDelegate>) -> Self {
        AuthenticatorBuilder {
            auth_flow: DeviceFlow {
                flow_delegate,
                ..self.auth_flow
            },
            ..self
        }
    }

    /// Use the provided wait duration.
    pub fn wait_duration(self, wait_duration: Duration) -> Self {
        AuthenticatorBuilder {
            auth_flow: DeviceFlow {
                wait_duration,
                ..self.auth_flow
            },
            ..self
        }
    }

    /// Use the provided grant type.
    pub fn grant_type(self, grant_type: impl Into<Cow<'static, str>>) -> Self {
        AuthenticatorBuilder {
            auth_flow: DeviceFlow {
                grant_type: grant_type.into(),
                ..self.auth_flow
            },
            ..self
        }
    }
}

/// Methods available when building an installed flow Authenticator.
/// ```
/// # async fn foo() {
/// # use yup_oauth2::InstalledFlowReturnMethod;
/// # let custom_flow_delegate = yup_oauth2::authenticator_delegate::DefaultFlowDelegate;
/// # let app_secret = yup_oauth2::read_application_secret("/tmp/foo").unwrap();
///     let authenticator = yup_oauth2::InstalledFlowAuthenticator::builder(
///         app_secret,
///         InstalledFlowReturnMethod::HTTPRedirect,
///     )
///     .flow_delegate(Box::new(custom_flow_delegate))
///     .build()
///     .await
///     .expect("failed to create authenticator");
/// # }
/// ```
impl<C> AuthenticatorBuilder<C, InstalledFlow> {
    /// Use the provided FlowDelegate.
    pub fn flow_delegate(self, flow_delegate: Box<dyn FlowDelegate>) -> Self {
        AuthenticatorBuilder {
            auth_flow: InstalledFlow {
                flow_delegate,
                ..self.auth_flow
            },
            ..self
        }
    }
}

mod private {
    use crate::device::DeviceFlow;
    use crate::error::Error;
    use crate::installed::InstalledFlow;
    use crate::types::{ApplicationSecret, Token};

    pub enum AuthFlow {
        DeviceFlow(DeviceFlow),
        InstalledFlow(InstalledFlow),
    }

    impl From<DeviceFlow> for AuthFlow {
        fn from(device_flow: DeviceFlow) -> AuthFlow {
            AuthFlow::DeviceFlow(device_flow)
        }
    }

    impl From<InstalledFlow> for AuthFlow {
        fn from(installed_flow: InstalledFlow) -> AuthFlow {
            AuthFlow::InstalledFlow(installed_flow)
        }
    }

    impl AuthFlow {
        pub(crate) async fn token<'a, C, T>(
            &'a self,
            hyper_client: &'a hyper::Client<C>,
            app_secret: &'a ApplicationSecret,
            scopes: &'a [T],
        ) -> Result<Token, Error>
        where
            T: AsRef<str>,
            C: hyper::client::connect::Connect + 'static,
        {
            match self {
                AuthFlow::DeviceFlow(device_flow) => {
                    device_flow.token(hyper_client, app_secret, scopes).await
                }
                AuthFlow::InstalledFlow(installed_flow) => {
                    installed_flow.token(hyper_client, app_secret, scopes).await
                }
            }
        }
    }
}

/// A trait implemented for any hyper::Client as well as teh DefaultHyperClient.
pub trait HyperClientBuilder {
    /// The hyper connector that the resulting hyper client will use.
    type Connector: hyper::client::connect::Connect + 'static;

    /// Create a hyper::Client
    fn build_hyper_client(self) -> hyper::Client<Self::Connector>;
}

/// The builder value used when the default hyper client should be used.
pub struct DefaultHyperClient;
impl HyperClientBuilder for DefaultHyperClient {
    type Connector = hyper_rustls::HttpsConnector<hyper::client::connect::HttpConnector>;

    fn build_hyper_client(self) -> hyper::Client<Self::Connector> {
        hyper::Client::builder()
            .keep_alive(false)
            .build::<_, hyper::Body>(hyper_rustls::HttpsConnector::new())
    }
}

impl<C> HyperClientBuilder for hyper::Client<C>
where
    C: hyper::client::connect::Connect + 'static,
{
    type Connector = C;

    fn build_hyper_client(self) -> hyper::Client<C> {
        self
    }
}

enum StorageType {
    Memory,
    Disk(PathBuf),
}
<commit_msg>Add header styling to the AuthenticatorBuilder rustdoc<commit_after>//! Module contianing the core functionality for OAuth2 Authentication.
use crate::authenticator_delegate::{
    AuthenticatorDelegate, DefaultAuthenticatorDelegate, FlowDelegate,
};
use crate::device::DeviceFlow;
use crate::error::Error;
use crate::installed::{InstalledFlow, InstalledFlowReturnMethod};
use crate::refresh::RefreshFlow;
use crate::storage::{self, Storage};
use crate::types::{ApplicationSecret, Token};
use private::AuthFlow;

use std::borrow::Cow;
use std::io;
use std::path::PathBuf;
use std::sync::Mutex;
use std::time::Duration;

/// Authenticator is responsible for fetching tokens, handling refreshing tokens,
/// and optionally persisting tokens to disk.
pub struct Authenticator<C> {
    hyper_client: hyper::Client<C>,
    app_secret: ApplicationSecret,
    auth_delegate: Box<dyn AuthenticatorDelegate>,
    storage: Storage,
    auth_flow: AuthFlow,
}

impl<C> Authenticator<C>
where
    C: hyper::client::connect::Connect + 'static,
{
    /// Return the current token for the provided scopes.
    pub async fn token<'a, T>(&'a self, scopes: &'a [T]) -> Result<Token, Error>
    where
        T: AsRef<str>,
    {
        let scope_key = storage::ScopeHash::new(scopes);
        match self.storage.get(scope_key, scopes) {
            Some(t) if !t.expired() => {
                // unexpired token found
                Ok(t)
            }
            Some(Token {
                refresh_token: Some(refresh_token),
                ..
            }) => {
                // token is expired but has a refresh token.
                let token = match RefreshFlow::refresh_token(
                    &self.hyper_client,
                    &self.app_secret,
                    &refresh_token,
                )
                .await
                {
                    Err(err) => {
                        self.auth_delegate.token_refresh_failed(&err);
                        return Err(err.into());
                    }
                    Ok(token) => token,
                };
                self.storage
                    .set(scope_key, scopes, Some(token.clone()))
                    .await;
                Ok(token)
            }
            None
            | Some(Token {
                refresh_token: None,
                ..
            }) => {
                // no token in the cache or the token returned does not contain a refresh token.
                let t = self
                    .auth_flow
                    .token(&self.hyper_client, &self.app_secret, scopes)
                    .await?;
                self.storage.set(scope_key, scopes, Some(t.clone())).await;
                Ok(t)
            }
        }
    }
}

/// Configure an Authenticator using the builder pattern.
pub struct AuthenticatorBuilder<C, F> {
    hyper_client_builder: C,
    app_secret: ApplicationSecret,
    auth_delegate: Box<dyn AuthenticatorDelegate>,
    storage_type: StorageType,
    auth_flow: F,
}

/// Create an authenticator that uses the installed flow.
/// ```
/// # async fn foo() {
/// # use yup_oauth2::InstalledFlowReturnMethod;
/// # let custom_flow_delegate = yup_oauth2::authenticator_delegate::DefaultFlowDelegate;
/// # let app_secret = yup_oauth2::read_application_secret("/tmp/foo").unwrap();
///     let authenticator = yup_oauth2::InstalledFlowAuthenticator::builder(
///         app_secret,
///         InstalledFlowReturnMethod::HTTPRedirect,
///     )
///     .build()
///     .await
///     .expect("failed to create authenticator");
/// # }
/// ```
pub struct InstalledFlowAuthenticator;
impl InstalledFlowAuthenticator {
    /// Use the builder pattern to create an Authenticator that uses the installed flow.
    pub fn builder(
        app_secret: ApplicationSecret,
        method: InstalledFlowReturnMethod,
    ) -> AuthenticatorBuilder<DefaultHyperClient, InstalledFlow> {
        AuthenticatorBuilder::<DefaultHyperClient, _>::with_auth_flow(
            app_secret,
            InstalledFlow::new(method),
        )
    }
}

/// Create an authenticator that uses the device flow.
/// ```
/// # async fn foo() {
/// # let app_secret = yup_oauth2::read_application_secret("/tmp/foo").unwrap();
///     let authenticator = yup_oauth2::DeviceFlowAuthenticator::builder(app_secret)
///         .build()
///         .await
///         .expect("failed to create authenticator");
/// # }
/// ```
pub struct DeviceFlowAuthenticator;
impl DeviceFlowAuthenticator {
    /// Use the builder pattern to create an Authenticator that uses the device flow.
    pub fn builder(
        app_secret: ApplicationSecret,
    ) -> AuthenticatorBuilder<DefaultHyperClient, DeviceFlow> {
        AuthenticatorBuilder::<DefaultHyperClient, _>::with_auth_flow(app_secret, DeviceFlow::new())
    }
}

/// ## Methods available when building any Authenticator.
/// ```
/// # async fn foo() {
/// # let custom_hyper_client = hyper::Client::new();
/// # let custom_auth_delegate = yup_oauth2::authenticator_delegate::DefaultAuthenticatorDelegate;
/// # let app_secret = yup_oauth2::read_application_secret("/tmp/foo").unwrap();
///     let authenticator = yup_oauth2::DeviceFlowAuthenticator::builder(app_secret)
///         .hyper_client(custom_hyper_client)
///         .persist_tokens_to_disk("/tmp/tokenfile.json")
///         .auth_delegate(Box::new(custom_auth_delegate))
///         .build()
///         .await
///         .expect("failed to create authenticator");
/// # }
/// ```
impl<C, F> AuthenticatorBuilder<C, F> {
    /// Create the authenticator.
    pub async fn build(self) -> io::Result<Authenticator<C::Connector>>
    where
        C: HyperClientBuilder,
        F: Into<AuthFlow>,
    {
        let hyper_client = self.hyper_client_builder.build_hyper_client();
        let storage = match self.storage_type {
            StorageType::Memory => Storage::Memory {
                tokens: Mutex::new(storage::JSONTokens::new()),
            },
            StorageType::Disk(path) => Storage::Disk(storage::DiskStorage::new(path).await?),
        };

        Ok(Authenticator {
            hyper_client,
            app_secret: self.app_secret,
            storage,
            auth_delegate: self.auth_delegate,
            auth_flow: self.auth_flow.into(),
        })
    }

    fn with_auth_flow(
        app_secret: ApplicationSecret,
        auth_flow: F,
    ) -> AuthenticatorBuilder<DefaultHyperClient, F> {
        AuthenticatorBuilder {
            hyper_client_builder: DefaultHyperClient,
            app_secret,
            auth_delegate: Box::new(DefaultAuthenticatorDelegate),
            storage_type: StorageType::Memory,
            auth_flow,
        }
    }

    /// Use the provided hyper client.
    pub fn hyper_client<NewC>(
        self,
        hyper_client: hyper::Client<NewC>,
    ) -> AuthenticatorBuilder<hyper::Client<NewC>, F> {
        AuthenticatorBuilder {
            hyper_client_builder: hyper_client,
            app_secret: self.app_secret,
            auth_delegate: self.auth_delegate,
            storage_type: self.storage_type,
            auth_flow: self.auth_flow,
        }
    }

    /// Persist tokens to disk in the provided filename.
    pub fn persist_tokens_to_disk<P: Into<PathBuf>>(self, path: P) -> AuthenticatorBuilder<C, F> {
        AuthenticatorBuilder {
            storage_type: StorageType::Disk(path.into()),
            ..self
        }
    }

    /// Use the provided authenticator delegate.
    pub fn auth_delegate(
        self,
        auth_delegate: Box<dyn AuthenticatorDelegate>,
    ) -> AuthenticatorBuilder<C, F> {
        AuthenticatorBuilder {
            auth_delegate,
            ..self
        }
    }
}

/// ## Methods available when building a device flow Authenticator.
/// ```
/// # async fn foo() {
/// # let custom_flow_delegate = yup_oauth2::authenticator_delegate::DefaultFlowDelegate;
/// # let app_secret = yup_oauth2::read_application_secret("/tmp/foo").unwrap();
///     let authenticator = yup_oauth2::DeviceFlowAuthenticator::builder(app_secret)
///         .device_code_url("foo")
///         .flow_delegate(Box::new(custom_flow_delegate))
///         .wait_duration(std::time::Duration::from_secs(120))
///         .grant_type("foo")
///         .build()
///         .await
///         .expect("failed to create authenticator");
/// # }
/// ```
impl<C> AuthenticatorBuilder<C, DeviceFlow> {
    /// Use the provided device code url.
    pub fn device_code_url(self, url: impl Into<Cow<'static, str>>) -> Self {
        AuthenticatorBuilder {
            auth_flow: DeviceFlow {
                device_code_url: url.into(),
                ..self.auth_flow
            },
            ..self
        }
    }

    /// Use the provided FlowDelegate.
    pub fn flow_delegate(self, flow_delegate: Box<dyn FlowDelegate>) -> Self {
        AuthenticatorBuilder {
            auth_flow: DeviceFlow {
                flow_delegate,
                ..self.auth_flow
            },
            ..self
        }
    }

    /// Use the provided wait duration.
    pub fn wait_duration(self, wait_duration: Duration) -> Self {
        AuthenticatorBuilder {
            auth_flow: DeviceFlow {
                wait_duration,
                ..self.auth_flow
            },
            ..self
        }
    }

    /// Use the provided grant type.
    pub fn grant_type(self, grant_type: impl Into<Cow<'static, str>>) -> Self {
        AuthenticatorBuilder {
            auth_flow: DeviceFlow {
                grant_type: grant_type.into(),
                ..self.auth_flow
            },
            ..self
        }
    }
}

/// ## Methods available when building an installed flow Authenticator.
/// ```
/// # async fn foo() {
/// # use yup_oauth2::InstalledFlowReturnMethod;
/// # let custom_flow_delegate = yup_oauth2::authenticator_delegate::DefaultFlowDelegate;
/// # let app_secret = yup_oauth2::read_application_secret("/tmp/foo").unwrap();
///     let authenticator = yup_oauth2::InstalledFlowAuthenticator::builder(
///         app_secret,
///         InstalledFlowReturnMethod::HTTPRedirect,
///     )
///     .flow_delegate(Box::new(custom_flow_delegate))
///     .build()
///     .await
///     .expect("failed to create authenticator");
/// # }
/// ```
impl<C> AuthenticatorBuilder<C, InstalledFlow> {
    /// Use the provided FlowDelegate.
    pub fn flow_delegate(self, flow_delegate: Box<dyn FlowDelegate>) -> Self {
        AuthenticatorBuilder {
            auth_flow: InstalledFlow {
                flow_delegate,
                ..self.auth_flow
            },
            ..self
        }
    }
}

mod private {
    use crate::device::DeviceFlow;
    use crate::error::Error;
    use crate::installed::InstalledFlow;
    use crate::types::{ApplicationSecret, Token};

    pub enum AuthFlow {
        DeviceFlow(DeviceFlow),
        InstalledFlow(InstalledFlow),
    }

    impl From<DeviceFlow> for AuthFlow {
        fn from(device_flow: DeviceFlow) -> AuthFlow {
            AuthFlow::DeviceFlow(device_flow)
        }
    }

    impl From<InstalledFlow> for AuthFlow {
        fn from(installed_flow: InstalledFlow) -> AuthFlow {
            AuthFlow::InstalledFlow(installed_flow)
        }
    }

    impl AuthFlow {
        pub(crate) async fn token<'a, C, T>(
            &'a self,
            hyper_client: &'a hyper::Client<C>,
            app_secret: &'a ApplicationSecret,
            scopes: &'a [T],
        ) -> Result<Token, Error>
        where
            T: AsRef<str>,
            C: hyper::client::connect::Connect + 'static,
        {
            match self {
                AuthFlow::DeviceFlow(device_flow) => {
                    device_flow.token(hyper_client, app_secret, scopes).await
                }
                AuthFlow::InstalledFlow(installed_flow) => {
                    installed_flow.token(hyper_client, app_secret, scopes).await
                }
            }
        }
    }
}

/// A trait implemented for any hyper::Client as well as teh DefaultHyperClient.
pub trait HyperClientBuilder {
    /// The hyper connector that the resulting hyper client will use.
    type Connector: hyper::client::connect::Connect + 'static;

    /// Create a hyper::Client
    fn build_hyper_client(self) -> hyper::Client<Self::Connector>;
}

/// The builder value used when the default hyper client should be used.
pub struct DefaultHyperClient;
impl HyperClientBuilder for DefaultHyperClient {
    type Connector = hyper_rustls::HttpsConnector<hyper::client::connect::HttpConnector>;

    fn build_hyper_client(self) -> hyper::Client<Self::Connector> {
        hyper::Client::builder()
            .keep_alive(false)
            .build::<_, hyper::Body>(hyper_rustls::HttpsConnector::new())
    }
}

impl<C> HyperClientBuilder for hyper::Client<C>
where
    C: hyper::client::connect::Connect + 'static,
{
    type Connector = C;

    fn build_hyper_client(self) -> hyper::Client<C> {
        self
    }
}

enum StorageType {
    Memory,
    Disk(PathBuf),
}
<|endoftext|>