<commit_before>use util::{CargoResult, Config, errors};

/// Wrapper method for network call retry logic.
///
/// Retry counts provided by Config object 'net.retry'. Config shell outputs
/// a warning on per retry.
///
/// Closure must return a CargoResult.
///
/// Example:
/// use util::network;
/// cargo_result = network.with_retry(&config, || something.download());
pub fn with_retry<T, E, F>(config: &Config, mut callback: F) -> CargoResult<T>
    where F: FnMut() -> Result<T, E>,
          E: errors::NetworkError
{
    let mut remaining = config.net_retry()?;
    loop {
        match callback() {
            Ok(ret) => return Ok(ret),
            Err(ref e) if e.maybe_spurious() && remaining > 0 => {
                let msg = format!("spurious network error ({} tries \
                          remaining): {}", remaining, e);
                config.shell().warn(msg)?;
                remaining -= 1;
            }
            Err(e) => return Err(Box::new(e)),
        }
    }
}
#[test]
fn with_retry_repeats_the_call_then_works() {

    use std::error::Error;
    use util::human;
    use std::fmt;

    #[derive(Debug)]
    struct NetworkRetryError {
        error: Box<errors::CargoError>,
    }

    impl Error for NetworkRetryError {
        fn description(&self) -> &str {
            self.error.description()
        }
        fn cause(&self) -> Option<&Error> {
            self.error.cause()
        }
    }

    impl NetworkRetryError {
        fn new(error: &str) -> NetworkRetryError {
            let error = human(error.to_string());
            NetworkRetryError {
                error: error,
            }
        }
    }

    impl fmt::Display for NetworkRetryError {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            fmt::Display::fmt(&self.error, f)
        }
    }

    impl errors::CargoError for NetworkRetryError {
        fn is_human(&self) -> bool {
            false
        }
    }

    impl errors::NetworkError for NetworkRetryError {
        fn maybe_spurious(&self) -> bool {
            true
        }
    }

    let error1 = NetworkRetryError::new("one");
    let error2 = NetworkRetryError::new("two");
    let mut results: Vec<Result<(), NetworkRetryError>> = vec![Ok(()),
    Err(error1), Err(error2)];
    let config = Config::default().unwrap();
    let result = with_retry(&config, || results.pop().unwrap());
    assert_eq!(result.unwrap(), ())
}
<commit_msg>Auto merge of #3358 - tshepang:formatting, r=alexcrichton<commit_after>use util::{CargoResult, Config, errors};

/// Wrapper method for network call retry logic.
///
/// Retry counts provided by Config object `net.retry`. Config shell outputs
/// a warning on per retry.
///
/// Closure must return a CargoResult.
///
/// # Examples
///
/// ```ignore
/// use util::network;
/// cargo_result = network.with_retry(&config, || something.download());
/// ```
pub fn with_retry<T, E, F>(config: &Config, mut callback: F) -> CargoResult<T>
    where F: FnMut() -> Result<T, E>,
          E: errors::NetworkError
{
    let mut remaining = config.net_retry()?;
    loop {
        match callback() {
            Ok(ret) => return Ok(ret),
            Err(ref e) if e.maybe_spurious() && remaining > 0 => {
                let msg = format!("spurious network error ({} tries \
                          remaining): {}", remaining, e);
                config.shell().warn(msg)?;
                remaining -= 1;
            }
            Err(e) => return Err(Box::new(e)),
        }
    }
}
#[test]
fn with_retry_repeats_the_call_then_works() {

    use std::error::Error;
    use util::human;
    use std::fmt;

    #[derive(Debug)]
    struct NetworkRetryError {
        error: Box<errors::CargoError>,
    }

    impl Error for NetworkRetryError {
        fn description(&self) -> &str {
            self.error.description()
        }
        fn cause(&self) -> Option<&Error> {
            self.error.cause()
        }
    }

    impl NetworkRetryError {
        fn new(error: &str) -> NetworkRetryError {
            let error = human(error.to_string());
            NetworkRetryError {
                error: error,
            }
        }
    }

    impl fmt::Display for NetworkRetryError {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            fmt::Display::fmt(&self.error, f)
        }
    }

    impl errors::CargoError for NetworkRetryError {
        fn is_human(&self) -> bool {
            false
        }
    }

    impl errors::NetworkError for NetworkRetryError {
        fn maybe_spurious(&self) -> bool {
            true
        }
    }

    let error1 = NetworkRetryError::new("one");
    let error2 = NetworkRetryError::new("two");
    let mut results: Vec<Result<(), NetworkRetryError>> = vec![Ok(()),
    Err(error1), Err(error2)];
    let config = Config::default().unwrap();
    let result = with_retry(&config, || results.pop().unwrap());
    assert_eq!(result.unwrap(), ())
}
<|endoftext|>


<commit_before>#![feature(box_syntax)]
#![feature(asm)]
extern crate e2d2;
extern crate fnv;
extern crate time;
extern crate simd;
extern crate getopts;
extern crate rand;
use e2d2::io::*;
use e2d2::headers::*;
use e2d2::packet_batch::*;
use getopts::Options;
use std::collections::HashMap;
use std::env;
use std::time::Duration;
use std::thread;

const CONVERSION_FACTOR: f64 = 1000000000.;

#[inline]
fn rdtscp_unsafe() -> u64 {
    unsafe {
        let low: u32;
        let high: u32;
        asm!("rdtscp"
             : "={eax}" (low), "={edx}" (high)
             :
             : "{ecx}"
             : "volatile");
        ((high as u64) << 32) | (low as u64)
    }
}

#[inline]
fn rdtscp() -> u64 {
    rdtscp_unsafe()
}

#[inline]
fn lat() {
    unsafe {
        asm!("nop"
             :
             :
             :
             : "volatile");
    }
}

#[inline]
fn delay_loop(delay: u64) {
    let mut d = 0;
    while d < delay {
        lat();
        d += 1;
    }
}

fn delay<T: 'static + Batch>(parent: T, delay: u64) -> CompositionBatch {
    parent.parse::<MacHeader>()
          .transform(box move |hdr, _, _| {
              let src = hdr.src.clone();
              hdr.src = hdr.dst;
              hdr.dst = src;
              delay_loop(delay);
          })
          .compose()
}

fn recv_thread(ports: Vec<PmdPort>, queue: i32, core: i32, delay_arg: u64) {
    init_thread(core, core);
    println!("Receiving started");

    let pipelines: Vec<_> = ports.iter()
                                 .map(|port| {
                                     delay(ReceiveBatch::new(port.copy(), queue), delay_arg)
                                         .send(port.copy(), queue)
                                         .compose()
                                 }).collect();
    println!("Running {} pipelines", pipelines.len());
    let mut combined = merge(pipelines);
    loop {
        combined.process();
    }
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let program = args[0].clone();
    let mut opts = Options::new();
    opts.optflag("h", "help", "print this help menu");
    opts.optflag("", "secondary", "run as a secondary process");
    opts.optopt("n", "name", "name to use for the current process", "name");
    opts.optmulti("w", "whitelist", "Whitelist PCI", "PCI");
    opts.optmulti("v", "vdevs", "Virtual Devices to add", "PCI");
    opts.optmulti("c", "core", "Core to use", "core");
    opts.optopt("m", "master", "Master core", "master");
    opts.optopt("d", "delay", "Delay cycles", "cycles");
    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => panic!(f.to_string()),
    };
    if matches.opt_present("h") {
        print!("{}", opts.usage(&format!("Usage: {} [options]", program)));
    }
    let cores_str = matches.opt_strs("c");
    let master_core = matches.opt_str("m")
                             .unwrap_or_else(|| String::from("0"))
                             .parse()
                             .expect("Could not parse master core spec");
    let delay_arg = matches.opt_str("d")
                       .unwrap_or_else(|| String::from("100"))
                       .parse()
                       .expect("Could not parse delay");
    println!("Using master core {}", master_core);
    let name = matches.opt_str("n").unwrap_or_else(|| String::from("recv"));

    let cores: Vec<i32> = cores_str.iter()
                                   .map(|n: &String| n.parse().ok().expect(&format!("Core cannot be parsed {}", n)))
                                   .collect();

    let ports = if matches.opt_present("secondary") {
        let vdevs = matches.opt_strs("v");
        if cores.len() > vdevs.len() {
            println!("More cores than vdevs");
            std::process::exit(1);
        }
        init_system_secondary(&name,
                              master_core,
                              &[]);
        // Fix this so we don't assume Bess.
        let mut ports = Vec::with_capacity(vdevs.len());
        for (core, vdev) in cores.iter().zip(vdevs.iter()) {
            ports.push(PmdPort::new_vdev(vdev, *core).expect("Could not initialize vdev"))
        }
        ports
    } else {
        let whitelisted = matches.opt_strs("w");
        if cores.len() > whitelisted.len() {
            println!("More cores than ports");
            std::process::exit(1);
        }
        init_system_wl(&name,
                       master_core,
                       &whitelisted);
        let mut ports = Vec::with_capacity(whitelisted.len());
        for (core, wl) in cores.iter().zip(whitelisted.iter()) {
            println!("Going to use core {} for wl {}", core, wl);
        }
        for (core, port) in cores.iter().zip(0..whitelisted.len()) {
            ports.push(PmdPort::new_mq_port(port as i32, 1, 1, &[*core], &[*core])
                       .expect("Could not initialize port"))
        }
        ports
    };

    let mut ports_by_core = HashMap::<i32, Vec<PmdPort>>::with_capacity(cores.len());
    for (core, port) in cores.iter().zip(ports.iter()) {
        {
            ports_by_core.entry(*core).or_insert(vec![]).push(port.copy());
        }
    }
    const _BATCH: usize = 1 << 10;
    const _CHANNEL_SIZE: usize = 256;
    let _thread: Vec<_> = ports_by_core.iter()
                                       .map(|(core, ports)| {
                                           let c = core.clone();
                                           let p: Vec<_> = ports.iter().map(|p| p.copy()).collect();
                                           std::thread::spawn(move || recv_thread(p, 0, c, delay_arg))
                                       })
                                       .collect();
    let mut pkts_so_far = (0, 0);
    let mut last_printed = 0.;
    const MAX_PRINT_INTERVAL : f64 = 15.;
    let mut start = time::precise_time_ns() as f64 / CONVERSION_FACTOR;
    let sleep_time = Duration::from_millis(5000);
    loop {
        thread::sleep(sleep_time); // Sleep for a bit
        let now = time::precise_time_ns() as f64 / CONVERSION_FACTOR;
        if now - start > 10.0 {
            let pkts = ports_by_core.values()
                                    .map(|pvec| {
                                        pvec.iter()
                                            .map(|p| p.stats(0))
                                            .fold((0, 0), |(r, t), (rp, tp)| (r + rp, t + tp))
                                    })
                                    .fold((0, 0), |(r, t), (rp, tp)| (r + rp, t + tp));
            let start_cycles = rdtscp();
            delay_loop(100);
            let end_cycles = rdtscp();
            let delay = end_cycles - start_cycles;
            let rx_pkts = pkts.0 - pkts_so_far.0;
            if rx_pkts > 0 || now - last_printed > MAX_PRINT_INTERVAL {
                println!("{:.2} OVERALL RX {:.2} TX {:.2} CYCLE_PER_DELAY {} {} {}",
                         now - start,
                         rx_pkts as f64 / (now - start),
                         (pkts.1 - pkts_so_far.1) as f64 / (now - start),
                         start_cycles,
                         end_cycles,
                         delay);
                last_printed = now;
            }
            start = now;
            pkts_so_far = pkts;
        }
    }
}
<commit_msg>Sentinel print from main.rs<commit_after>#![feature(box_syntax)]
#![feature(asm)]
extern crate e2d2;
extern crate fnv;
extern crate time;
extern crate simd;
extern crate getopts;
extern crate rand;
use e2d2::io::*;
use e2d2::headers::*;
use e2d2::packet_batch::*;
use getopts::Options;
use std::collections::HashMap;
use std::env;
use std::time::Duration;
use std::thread;

const CONVERSION_FACTOR: f64 = 1000000000.;

#[inline]
fn rdtscp_unsafe() -> u64 {
    unsafe {
        let low: u32;
        let high: u32;
        asm!("rdtscp"
             : "={eax}" (low), "={edx}" (high)
             :
             : "{ecx}"
             : "volatile");
        ((high as u64) << 32) | (low as u64)
    }
}

#[inline]
fn rdtscp() -> u64 {
    rdtscp_unsafe()
}

#[inline]
fn lat() {
    unsafe {
        asm!("nop"
             :
             :
             :
             : "volatile");
    }
}

#[inline]
fn delay_loop(delay: u64) {
    let mut d = 0;
    while d < delay {
        lat();
        d += 1;
    }
}

fn delay<T: 'static + Batch>(parent: T, delay: u64) -> CompositionBatch {
    parent.parse::<MacHeader>()
          .transform(box move |hdr, _, _| {
              let src = hdr.src.clone();
              hdr.src = hdr.dst;
              hdr.dst = src;
              delay_loop(delay);
          })
          .compose()
}

fn recv_thread(ports: Vec<PmdPort>, queue: i32, core: i32, delay_arg: u64) {
    init_thread(core, core);
    println!("Receiving started");

    let pipelines: Vec<_> = ports.iter()
                                 .map(|port| {
                                     delay(ReceiveBatch::new(port.copy(), queue), delay_arg)
                                         .send(port.copy(), queue)
                                         .compose()
                                 }).collect();
    println!("Running {} pipelines", pipelines.len());
    let mut combined = merge(pipelines);
    loop {
        combined.process();
    }
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let program = args[0].clone();
    let mut opts = Options::new();
    opts.optflag("h", "help", "print this help menu");
    opts.optflag("", "secondary", "run as a secondary process");
    opts.optopt("n", "name", "name to use for the current process", "name");
    opts.optmulti("w", "whitelist", "Whitelist PCI", "PCI");
    opts.optmulti("v", "vdevs", "Virtual Devices to add", "PCI");
    opts.optmulti("c", "core", "Core to use", "core");
    opts.optopt("m", "master", "Master core", "master");
    opts.optopt("d", "delay", "Delay cycles", "cycles");
    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => panic!(f.to_string()),
    };
    if matches.opt_present("h") {
        print!("{}", opts.usage(&format!("Usage: {} [options]", program)));
    }
    let cores_str = matches.opt_strs("c");
    let master_core = matches.opt_str("m")
                             .unwrap_or_else(|| String::from("0"))
                             .parse()
                             .expect("Could not parse master core spec");
    let delay_arg = matches.opt_str("d")
                       .unwrap_or_else(|| String::from("100"))
                       .parse()
                       .expect("Could not parse delay");
    println!("Using master core {}", master_core);
    let name = matches.opt_str("n").unwrap_or_else(|| String::from("recv"));

    let cores: Vec<i32> = cores_str.iter()
                                   .map(|n: &String| n.parse().ok().expect(&format!("Core cannot be parsed {}", n)))
                                   .collect();

    let ports = if matches.opt_present("secondary") {
        let vdevs = matches.opt_strs("v");
        if cores.len() > vdevs.len() {
            println!("More cores than vdevs");
            std::process::exit(1);
        }
        init_system_secondary(&name,
                              master_core,
                              &[]);
        // Fix this so we don't assume Bess.
        let mut ports = Vec::with_capacity(vdevs.len());
        for (core, vdev) in cores.iter().zip(vdevs.iter()) {
            ports.push(PmdPort::new_vdev(vdev, *core).expect("Could not initialize vdev"))
        }
        ports
    } else {
        let whitelisted = matches.opt_strs("w");
        if cores.len() > whitelisted.len() {
            println!("More cores than ports");
            std::process::exit(1);
        }
        init_system_wl(&name,
                       master_core,
                       &whitelisted);
        let mut ports = Vec::with_capacity(whitelisted.len());
        for (core, wl) in cores.iter().zip(whitelisted.iter()) {
            println!("Going to use core {} for wl {}", core, wl);
        }
        for (core, port) in cores.iter().zip(0..whitelisted.len()) {
            ports.push(PmdPort::new_mq_port(port as i32, 1, 1, &[*core], &[*core])
                       .expect("Could not initialize port"))
        }
        ports
    };

    let mut ports_by_core = HashMap::<i32, Vec<PmdPort>>::with_capacity(cores.len());
    for (core, port) in cores.iter().zip(ports.iter()) {
        {
            ports_by_core.entry(*core).or_insert(vec![]).push(port.copy());
        }
    }
    const _BATCH: usize = 1 << 10;
    const _CHANNEL_SIZE: usize = 256;
    let _thread: Vec<_> = ports_by_core.iter()
                                       .map(|(core, ports)| {
                                           let c = core.clone();
                                           let p: Vec<_> = ports.iter().map(|p| p.copy()).collect();
                                           std::thread::spawn(move || recv_thread(p, 0, c, delay_arg))
                                       })
                                       .collect();
    let mut pkts_so_far = (0, 0);
    let mut last_printed = 0.;
    const MAX_PRINT_INTERVAL : f64 = 15.;
    let mut start = time::precise_time_ns() as f64 / CONVERSION_FACTOR;
    let sleep_time = Duration::from_millis(5000);
    println!("0 OVERALL RX 0.00 TX 0.00 CYCLE_PER_DELAY 0 0 0");
    loop {
        thread::sleep(sleep_time); // Sleep for a bit
        let now = time::precise_time_ns() as f64 / CONVERSION_FACTOR;
        if now - start > 10.0 {
            let pkts = ports_by_core.values()
                                    .map(|pvec| {
                                        pvec.iter()
                                            .map(|p| p.stats(0))
                                            .fold((0, 0), |(r, t), (rp, tp)| (r + rp, t + tp))
                                    })
                                    .fold((0, 0), |(r, t), (rp, tp)| (r + rp, t + tp));
            let start_cycles = rdtscp();
            delay_loop(100);
            let end_cycles = rdtscp();
            let delay = end_cycles - start_cycles;
            let rx_pkts = pkts.0 - pkts_so_far.0;
            if rx_pkts > 0 || now - last_printed > MAX_PRINT_INTERVAL {
                println!("{:.2} OVERALL RX {:.2} TX {:.2} CYCLE_PER_DELAY {} {} {}",
                         now - start,
                         rx_pkts as f64 / (now - start),
                         (pkts.1 - pkts_so_far.1) as f64 / (now - start),
                         start_cycles,
                         end_cycles,
                         delay);
                last_printed = now;
            }
            start = now;
            pkts_so_far = pkts;
        }
    }
}
<|endoftext|>


<commit_before>use crate::analysis::symbols;
use once_cell::sync::Lazy;
use regex::{Captures, Match, Regex};

const LANGUAGE_SEP_BEGIN: &str = "<!-- language=\"";
const LANGUAGE_SEP_END: &str = "\" -->";
const LANGUAGE_BLOCK_BEGIN: &str = "|[";
const LANGUAGE_BLOCK_END: &str = "\n]|";

pub fn reformat_doc(input: &str, symbols: &symbols::Info, in_type: &str) -> String {
    code_blocks_transformation(input, symbols, in_type)
}

fn try_split<'a>(src: &'a str, needle: &str) -> (&'a str, Option<&'a str>) {
    match src.find(needle) {
        Some(pos) => (&src[..pos], Some(&src[pos + needle.len()..])),
        None => (src, None),
    }
}

fn code_blocks_transformation(mut input: &str, symbols: &symbols::Info, in_type: &str) -> String {
    let mut out = String::with_capacity(input.len());

    loop {
        input = match try_split(input, LANGUAGE_BLOCK_BEGIN) {
            (before, Some(after)) => {
                out.push_str(&format(before, symbols, in_type));
                if let (before, Some(after)) =
                    try_split(get_language(after, &mut out), LANGUAGE_BLOCK_END)
                {
                    out.push_str(before);
                    out.push_str("\n```");
                    after
                } else {
                    after
                }
            }
            (before, None) => {
                out.push_str(&format(before, symbols, in_type));
                return out;
            }
        };
    }
}

fn get_language<'a>(entry: &'a str, out: &mut String) -> &'a str {
    if let (_, Some(after)) = try_split(entry, LANGUAGE_SEP_BEGIN) {
        if let (before, Some(after)) = try_split(after, LANGUAGE_SEP_END) {
            out.push_str(&format!("\n```{}", before));
            return after;
        }
    }
    out.push_str("\n```text");
    entry
}

fn format(mut input: &str, symbols: &symbols::Info, in_type: &str) -> String {
    let mut ret = String::with_capacity(input.len());
    loop {
        let (before, after) = try_split(input, "`");
        ret.push_str(&replace_c_types(before, symbols, in_type));
        if let Some(after) = after {
            ret.push('`');
            let (before, after) = try_split(after, "`");
            // don't touch anything enclosed in backticks
            ret.push_str(before);
            if let Some(after) = after {
                ret.push('`');
                input = after;
            } else {
                return ret;
            }
        } else {
            return ret;
        }
    }
}

static SYMBOL: Lazy<Regex> =
    Lazy::new(|| Regex::new(r"(^|[^\\])([@#%])(\w+\b)([:.]+[\w-]+\b)?").unwrap());
static FUNCTION: Lazy<Regex> =
    Lazy::new(|| Regex::new(r"([@#%])?(\w+\b[:.]+)?(\b[a-z0-9_]+)\(\)").unwrap());
static GDK_GTK: Lazy<Regex> = Lazy::new(|| Regex::new(r"G[dt]k[A-Z]\w+\b").unwrap());
static TAGS: Lazy<Regex> = Lazy::new(|| Regex::new(r"<[\w/-]+>").unwrap());
static SPACES: Lazy<Regex> = Lazy::new(|| Regex::new(r"[ ]{2,}").unwrap());

fn replace_c_types(entry: &str, symbols: &symbols::Info, in_type: &str) -> String {
    let lookup = |s: &str| -> String {
        symbols
            .by_c_name(s)
            .map(symbols::Symbol::full_rust_name)
            .unwrap_or_else(|| s.into())
    };

    let out = FUNCTION.replace_all(entry, |caps: &Captures<'_>| {
        let name = &caps[3];
        let sym = symbols.by_c_name(name);

        if let Some(sym) = sym {
            if sym.owner_name() == Some(in_type) {
                // `#` or `%` symbols should probably have been `@` to denote
                // that it is a reference within the current type.
                format!("[`{f}()`](Self::{f}())", f = sym.name())
            } else {
                match caps.get(1).as_ref().map(Match::as_str) {
                    // Catch invalid @ references that have a C symbol available but do not belong
                    // to the current type (and can hence not use `Self::`). For now generate XXX
                    // but with a valid global link so that the can be easily spotted in the code.
                    // assert_eq!(sym.owner_name(), Some(in_type));
                    Some("@") => format!(
                        "[`crate::{}()`] (XXX: @-reference does not belong to {}!)",
                        sym.full_rust_name(),
                        in_type,
                    ),
                    Some("#") | None => {
                        format!("[`{f}()`](crate::{f}())", f = sym.full_rust_name())
                    }
                    Some("%") => panic!("% not allowed for {:?}", caps),
                    Some(c) => panic!("Unknown symbol reference {}", c),
                }
            }
        } else if let Some(typ) = caps.get(2) {
            let typ = typ.as_str();
            if typ == in_type {
                format!("[`{f}()`](Self::{f}())", f = name)
            } else {
                format!("[`{t}{f}()`](crate::{t}{f}())", t = typ, f = name)
            }
        } else {
            format!("`{}()`", name)
        }
    });

    let out = SYMBOL.replace_all(&out, |caps: &Captures<'_>| {
        let member = caps.get(4).map(|m| m.as_str()).unwrap_or("");
        let sym = symbols.by_c_name(&caps[3]);
        match &caps[2] {
            /* References to members, enum variants or methods within the same type. */
            "@" => {
                if let Some(sym) = sym {
                    // Catch invalid @ references that have a C symbol available but do not belong
                    // to the current type (and can hence not use `Self::`). For now generate XXX
                    // but with a valid global link so that the can be easily spotted in the code.
                    // assert_eq!(sym.owner_name(), Some(in_type));
                    if sym.owner_name() != Some(in_type) {
                        format!(
                            "{}[`crate::{}{}`] (XXX: @-reference does not belong to {}!)",
                            &caps[1],
                            sym.full_rust_name(),
                            member,
                            in_type,
                        )
                    } else {
                        format!(
                            "{}[`{n}{m}`](Self::{n}{m})",
                            &caps[1],
                            n = sym.name(),
                            m = member
                        )
                    }
                } else {
                    format!("{}`{}{}`", &caps[1], &caps[3], member)
                }
            }
            "#" | "%" => {
                format!(
                    "{}`{}{}`",
                    &caps[1],
                    sym.map(symbols::Symbol::full_rust_name)
                        .unwrap_or_else(|| caps[3].into()),
                    member
                )
            }
            u => panic!("Unknown reference type {}", u),
        }
    });
    let out = GDK_GTK.replace_all(&out, |caps: &Captures<'_>| {
        format!("`{}`", lookup(&caps[0]))
    });
    let out = TAGS.replace_all(&out, "`$0`");
    SPACES.replace_all(&out, " ").into_owned()
}
<commit_msg>doc/format: Emit intra-doc-links for symbol references<commit_after>use crate::analysis::symbols;
use once_cell::sync::Lazy;
use regex::{Captures, Match, Regex};

const LANGUAGE_SEP_BEGIN: &str = "<!-- language=\"";
const LANGUAGE_SEP_END: &str = "\" -->";
const LANGUAGE_BLOCK_BEGIN: &str = "|[";
const LANGUAGE_BLOCK_END: &str = "\n]|";

pub fn reformat_doc(input: &str, symbols: &symbols::Info, in_type: &str) -> String {
    code_blocks_transformation(input, symbols, in_type)
}

fn try_split<'a>(src: &'a str, needle: &str) -> (&'a str, Option<&'a str>) {
    match src.find(needle) {
        Some(pos) => (&src[..pos], Some(&src[pos + needle.len()..])),
        None => (src, None),
    }
}

fn code_blocks_transformation(mut input: &str, symbols: &symbols::Info, in_type: &str) -> String {
    let mut out = String::with_capacity(input.len());

    loop {
        input = match try_split(input, LANGUAGE_BLOCK_BEGIN) {
            (before, Some(after)) => {
                out.push_str(&format(before, symbols, in_type));
                if let (before, Some(after)) =
                    try_split(get_language(after, &mut out), LANGUAGE_BLOCK_END)
                {
                    out.push_str(before);
                    out.push_str("\n```");
                    after
                } else {
                    after
                }
            }
            (before, None) => {
                out.push_str(&format(before, symbols, in_type));
                return out;
            }
        };
    }
}

fn get_language<'a>(entry: &'a str, out: &mut String) -> &'a str {
    if let (_, Some(after)) = try_split(entry, LANGUAGE_SEP_BEGIN) {
        if let (before, Some(after)) = try_split(after, LANGUAGE_SEP_END) {
            out.push_str(&format!("\n```{}", before));
            return after;
        }
    }
    out.push_str("\n```text");
    entry
}

fn format(mut input: &str, symbols: &symbols::Info, in_type: &str) -> String {
    let mut ret = String::with_capacity(input.len());
    loop {
        let (before, after) = try_split(input, "`");
        ret.push_str(&replace_c_types(before, symbols, in_type));
        if let Some(after) = after {
            ret.push('`');
            let (before, after) = try_split(after, "`");
            // don't touch anything enclosed in backticks
            ret.push_str(before);
            if let Some(after) = after {
                ret.push('`');
                input = after;
            } else {
                return ret;
            }
        } else {
            return ret;
        }
    }
}

static SYMBOL: Lazy<Regex> =
    Lazy::new(|| Regex::new(r"(^|[^\\])([@#%])(\w+\b)([:.]+[\w-]+\b)?").unwrap());
static FUNCTION: Lazy<Regex> =
    Lazy::new(|| Regex::new(r"([@#%])?(\w+\b[:.]+)?(\b[a-z0-9_]+)\(\)").unwrap());
static GDK_GTK: Lazy<Regex> = Lazy::new(|| Regex::new(r"G[dt]k[A-Z]\w+\b").unwrap());
static TAGS: Lazy<Regex> = Lazy::new(|| Regex::new(r"<[\w/-]+>").unwrap());
static SPACES: Lazy<Regex> = Lazy::new(|| Regex::new(r"[ ]{2,}").unwrap());

fn replace_c_types(entry: &str, symbols: &symbols::Info, in_type: &str) -> String {
    let lookup = |s: &str| -> String {
        symbols
            .by_c_name(s)
            .map(symbols::Symbol::full_rust_name)
            .unwrap_or_else(|| s.into())
    };

    let out = FUNCTION.replace_all(entry, |caps: &Captures<'_>| {
        let name = &caps[3];
        let sym = symbols.by_c_name(name);

        if let Some(sym) = sym {
            if sym.owner_name() == Some(in_type) {
                // `#` or `%` symbols should probably have been `@` to denote
                // that it is a reference within the current type.
                format!("[`{f}()`](Self::{f}())", f = sym.name())
            } else {
                match caps.get(1).as_ref().map(Match::as_str) {
                    // Catch invalid @ references that have a C symbol available but do not belong
                    // to the current type (and can hence not use `Self::`). For now generate XXX
                    // but with a valid global link so that the can be easily spotted in the code.
                    // assert_eq!(sym.owner_name(), Some(in_type));
                    Some("@") => format!(
                        "[`crate::{}()`] (XXX: @-reference does not belong to {}!)",
                        sym.full_rust_name(),
                        in_type,
                    ),
                    Some("#") | None => {
                        format!("[`{f}()`](crate::{f}())", f = sym.full_rust_name())
                    }
                    Some("%") => panic!("% not allowed for {:?}", caps),
                    Some(c) => panic!("Unknown symbol reference {}", c),
                }
            }
        } else if let Some(typ) = caps.get(2) {
            let typ = typ.as_str();
            if typ == in_type {
                format!("[`{f}()`](Self::{f}())", f = name)
            } else {
                format!("[`{t}{f}()`](crate::{t}{f}())", t = typ, f = name)
            }
        } else {
            format!("`{}()`", name)
        }
    });

    let out = SYMBOL.replace_all(&out, |caps: &Captures<'_>| {
        let member = caps.get(4).map(|m| m.as_str()).unwrap_or("");
        let sym = symbols.by_c_name(&caps[3]);

        if let Some(sym) = sym {
            if sym.owner_name() == Some(in_type) {
                // `#` or `%` symbols should probably have been `@` to denote
                // that it is a reference within the current type.
                format!(
                    "{}[`{n}{m}`](Self::{n}{m})",
                    &caps[1],
                    n = sym.name(),
                    m = member
                )
            } else {
                match &caps[2] {
                    // Catch invalid @ references that have a C symbol available but do not belong
                    // to the current type (and can hence not use `Self::`). For now generate XXX
                    // but with a valid global link so that the can be easily spotted in the code.
                    // assert_eq!(sym.owner_name(), Some(in_type));
                    "@" => format!(
                        "{}[`crate::{}{}`] (XXX: @-reference does not belong to {}!)",
                        &caps[1],
                        sym.full_rust_name(),
                        member,
                        in_type,
                    ),
                    "#" => format!(
                        "{}[`{n}{m}`](crate::{n}{m})",
                        &caps[1],
                        n = sym.full_rust_name(),
                        m = member,
                    ),
                    "%" => format!("{}[`{}{}`]", &caps[1], sym.full_rust_name(), member,),
                    c => panic!("Unknown symbol reference {}", c),
                }
            }
        } else {
            format!("{}`{}{}`", &caps[1], &caps[3], member)
        }
    });
    let out = GDK_GTK.replace_all(&out, |caps: &Captures<'_>| {
        format!("`{}`", lookup(&caps[0]))
    });
    let out = TAGS.replace_all(&out, "`$0`");
    SPACES.replace_all(&out, " ").into_owned()
}
<|endoftext|>


<commit_before>use super::gi_docgen;
use crate::{config::gobjects::GStatus, library::FunctionKind, nameutil, Env};
use once_cell::sync::Lazy;
use regex::{Captures, Match, Regex};

const LANGUAGE_SEP_BEGIN: &str = "<!-- language=\"";
const LANGUAGE_SEP_END: &str = "\" -->";
const LANGUAGE_BLOCK_BEGIN: &str = "|[";
const LANGUAGE_BLOCK_END: &str = "\n]|";

pub fn reformat_doc(input: &str, env: &Env, in_type: &str) -> String {
    code_blocks_transformation(input, env, in_type)
}

fn try_split<'a>(src: &'a str, needle: &str) -> (&'a str, Option<&'a str>) {
    match src.find(needle) {
        Some(pos) => (&src[..pos], Some(&src[pos + needle.len()..])),
        None => (src, None),
    }
}

fn code_blocks_transformation(mut input: &str, env: &Env, in_type: &str) -> String {
    let mut out = String::with_capacity(input.len());

    loop {
        input = match try_split(input, LANGUAGE_BLOCK_BEGIN) {
            (before, Some(after)) => {
                out.push_str(&format(before, env, in_type));
                if let (before, Some(after)) =
                    try_split(get_language(after, &mut out), LANGUAGE_BLOCK_END)
                {
                    out.push_str(before);
                    out.push_str("\n```");
                    after
                } else {
                    after
                }
            }
            (before, None) => {
                out.push_str(&format(before, env, in_type));
                return out;
            }
        };
    }
}

fn get_language<'a>(entry: &'a str, out: &mut String) -> &'a str {
    if let (_, Some(after)) = try_split(entry, LANGUAGE_SEP_BEGIN) {
        if let (before, Some(after)) = try_split(after, LANGUAGE_SEP_END) {
            out.push_str(&format!("\n```{}", before));
            return after;
        }
    }
    out.push_str("\n```text");
    entry
}

fn format(input: &str, env: &Env, in_type: &str) -> String {
    let mut ret = String::with_capacity(input.len());
    // We run gi_docgen first because it's super picky about the types it replaces
    let no_c_types_re = gi_docgen::replace_c_types(input, env, in_type);
    ret.push_str(&replace_c_types(&no_c_types_re, env, in_type));
    ret
}

static SYMBOL: Lazy<Regex> =
    Lazy::new(|| Regex::new(r"(^|[^\\])([@#%])(\w+\b)([:.]+[\w-]+\b)?").unwrap());
static FUNCTION: Lazy<Regex> =
    Lazy::new(|| Regex::new(r"([@#%])?(\w+\b[:.]+)?(\b[a-z0-9_]+)\(\)").unwrap());
// **note**
// The optional . at the end is to make the regex more relaxed for some weird broken cases on gtk3's docs
// it doesn't hurt other docs so please don't drop it
static GDK_GTK: Lazy<Regex> =
    Lazy::new(|| Regex::new(r"`([^\(:])?((G[dts]k|Pango)\w+\b)(\.)?`").unwrap());
static TAGS: Lazy<Regex> = Lazy::new(|| Regex::new(r"<[\w/-]+>").unwrap());
static SPACES: Lazy<Regex> = Lazy::new(|| Regex::new(r"[ ]{2,}").unwrap());

fn replace_c_types(entry: &str, env: &Env, in_type: &str) -> String {
    let symbols = env.symbols.borrow();
    let out = FUNCTION.replace_all(entry, |caps: &Captures<'_>| {
        let name = &caps[3];
        find_function(name, env)
    });

    let out = SYMBOL.replace_all(&out, |caps: &Captures<'_>| {
        let member = caps.get(4).map(|m| m.as_str()).unwrap_or("");
        let sym = symbols.by_c_name(&caps[3]);

        if let Some(sym) = sym {
            if sym.owner_name() == Some(in_type) {
                // `#` or `%` symbols should probably have been `@` to denote
                // that it is a reference within the current type.
                format!("{}[`Self::{}{}`]", &caps[1], sym.name(), member)
            } else {
                match &caps[2] {
                    // Catch invalid @ references that have a C symbol available but do not belong
                    // to the current type (and can hence not use `Self::`). For now generate XXX
                    // but with a valid global link so that the can be easily spotted in the code.
                    // assert_eq!(sym.owner_name(), Some(in_type));
                    "@" => format!(
                        "{}[`crate::{}{}`] (XXX: @-reference does not belong to {}!)",
                        &caps[1],
                        sym.full_rust_name(),
                        member,
                        in_type,
                    ),
                    "%" if sym.is_rust_prelude() => {
                        format!("{}[`{}{}`]", &caps[1], sym.full_rust_name(), member)
                    }
                    "#" | "%" => {
                        format!("{}[`crate::{}{}`]", &caps[1], sym.full_rust_name(), member)
                    }
                    c => panic!("Unknown symbol reference {}", c),
                }
            }
        } else {
            format!("{}`{}{}`", &caps[1], &caps[3], member)
        }
    });
    let out = GDK_GTK.replace_all(&out, |caps: &Captures<'_>| find_struct(&caps[2], env));
    let out = TAGS.replace_all(&out, "`$0`");
    SPACES.replace_all(&out, " ").into_owned()
}

fn find_struct(name: &str, env: &Env) -> String {
    let symbols = env.symbols.borrow();

    let symbol = if let Some(obj) = env
        .analysis
        .objects
        .iter()
        .find(|(_, o)| o.c_type == name)
        .map(|(_, o)| o)
    {
        symbols.by_tid(obj.type_id)
    } else if let Some(record) = env
        .analysis
        .records
        .iter()
        .find(|(full_name, _)| full_name == &name)
        .map(|(_, r)| r)
    {
        symbols.by_tid(record.type_id)
    } else {
        None
    };
    symbol
        .map(|sym| format!("[{name}](crate::{name})", name = sym.full_rust_name()))
        .unwrap_or_else(|| name.to_string())
}

/// Find a function in all the possible items, if not found return the original name surrounded with backsticks
/// A function can either be a struct/interface/record method, a global function or maybe a virtual function
fn find_function(name: &str, env: &Env) -> String {
    let symbols = env.symbols.borrow();
    let is_obj_func = env.analysis.objects.iter().find_map(|(_, obj_info)| {
        obj_info
            .functions
            .iter()
            .filter(|fn_info| fn_info.status != GStatus::Ignore)
            .filter(|fn_info| !fn_info.is_special() && !fn_info.is_async_finish(env))
            .find(|fn_info| fn_info.glib_name == name)
            .map(|fn_info| (obj_info, fn_info))
    });
    let is_record_func = env.analysis.records.iter().find_map(|(_, record_info)| {
        record_info
            .functions
            .iter()
            .filter(|fn_info| fn_info.status != GStatus::Ignore)
            .filter(|fn_info| !fn_info.is_special() && !fn_info.is_async_finish(env))
            .find(|fn_info| fn_info.glib_name == name)
            .map(|fn_info| (record_info, fn_info))
    });
    let is_globa_func = env.analysis.global_functions.as_ref().and_then(|info| {
        info.functions
            .iter()
            .filter(|fn_info| fn_info.status != GStatus::Ignore)
            .filter(|fn_info| !fn_info.is_special() && !fn_info.is_async_finish(env))
            .find(|fn_info| fn_info.glib_name == name)
    });

    if let Some((obj_info, fn_info)) = is_obj_func {
        let sym = symbols.by_tid(obj_info.type_id).unwrap(); // we are sure the object exists
        let (type_name, visible_type_name) =
            if obj_info.final_type || fn_info.kind == FunctionKind::Constructor {
                (obj_info.name.clone(), obj_info.name.clone())
            } else {
                let type_name = if fn_info.status == GStatus::Generate {
                    obj_info.trait_name.clone()
                } else {
                    format!("{}Manual", obj_info.trait_name)
                };
                (format!("prelude::{}", type_name), type_name)
            };
        let name = sym.full_rust_name().replace(&obj_info.name, &type_name);
        format!(
            "[{visible_type_name}::{fn_name}](crate::{name}::{fn_name})",
            name = name,
            visible_type_name = visible_type_name,
            fn_name = fn_info.codegen_name()
        )
    } else if let Some((record_info, fn_info)) = is_record_func {
        let sym = symbols.by_tid(record_info.type_id).unwrap(); // we are sure the object exists
        format!(
            "[{name}::{fn_name}](crate::{name}::{fn_name})",
            name = sym.full_rust_name(),
            fn_name = fn_info.codegen_name()
        )
    } else if let Some(fn_info) = is_globa_func {
        format!(
            "[{fn_name}()](crate::{fn_name})",
            fn_name = fn_info.codegen_name()
        )
    } else {
        format!("`{}`", name)
    }
}
<commit_msg>docs: mark functions as part of object not a trait<commit_after>use super::gi_docgen;
use crate::{config::gobjects::GStatus, library::FunctionKind, nameutil, Env};
use once_cell::sync::Lazy;
use regex::{Captures, Match, Regex};

const LANGUAGE_SEP_BEGIN: &str = "<!-- language=\"";
const LANGUAGE_SEP_END: &str = "\" -->";
const LANGUAGE_BLOCK_BEGIN: &str = "|[";
const LANGUAGE_BLOCK_END: &str = "\n]|";

pub fn reformat_doc(input: &str, env: &Env, in_type: &str) -> String {
    code_blocks_transformation(input, env, in_type)
}

fn try_split<'a>(src: &'a str, needle: &str) -> (&'a str, Option<&'a str>) {
    match src.find(needle) {
        Some(pos) => (&src[..pos], Some(&src[pos + needle.len()..])),
        None => (src, None),
    }
}

fn code_blocks_transformation(mut input: &str, env: &Env, in_type: &str) -> String {
    let mut out = String::with_capacity(input.len());

    loop {
        input = match try_split(input, LANGUAGE_BLOCK_BEGIN) {
            (before, Some(after)) => {
                out.push_str(&format(before, env, in_type));
                if let (before, Some(after)) =
                    try_split(get_language(after, &mut out), LANGUAGE_BLOCK_END)
                {
                    out.push_str(before);
                    out.push_str("\n```");
                    after
                } else {
                    after
                }
            }
            (before, None) => {
                out.push_str(&format(before, env, in_type));
                return out;
            }
        };
    }
}

fn get_language<'a>(entry: &'a str, out: &mut String) -> &'a str {
    if let (_, Some(after)) = try_split(entry, LANGUAGE_SEP_BEGIN) {
        if let (before, Some(after)) = try_split(after, LANGUAGE_SEP_END) {
            out.push_str(&format!("\n```{}", before));
            return after;
        }
    }
    out.push_str("\n```text");
    entry
}

fn format(input: &str, env: &Env, in_type: &str) -> String {
    let mut ret = String::with_capacity(input.len());
    // We run gi_docgen first because it's super picky about the types it replaces
    let no_c_types_re = gi_docgen::replace_c_types(input, env, in_type);
    ret.push_str(&replace_c_types(&no_c_types_re, env, in_type));
    ret
}

static SYMBOL: Lazy<Regex> =
    Lazy::new(|| Regex::new(r"(^|[^\\])([@#%])(\w+\b)([:.]+[\w-]+\b)?").unwrap());
static FUNCTION: Lazy<Regex> =
    Lazy::new(|| Regex::new(r"([@#%])?(\w+\b[:.]+)?(\b[a-z0-9_]+)\(\)").unwrap());
// **note**
// The optional . at the end is to make the regex more relaxed for some weird broken cases on gtk3's docs
// it doesn't hurt other docs so please don't drop it
static GDK_GTK: Lazy<Regex> =
    Lazy::new(|| Regex::new(r"`([^\(:])?((G[dts]k|Pango)\w+\b)(\.)?`").unwrap());
static TAGS: Lazy<Regex> = Lazy::new(|| Regex::new(r"<[\w/-]+>").unwrap());
static SPACES: Lazy<Regex> = Lazy::new(|| Regex::new(r"[ ]{2,}").unwrap());

fn replace_c_types(entry: &str, env: &Env, in_type: &str) -> String {
    let symbols = env.symbols.borrow();
    let out = FUNCTION.replace_all(entry, |caps: &Captures<'_>| {
        let name = &caps[3];
        find_function(name, env)
    });

    let out = SYMBOL.replace_all(&out, |caps: &Captures<'_>| {
        let member = caps.get(4).map(|m| m.as_str()).unwrap_or("");
        let sym = symbols.by_c_name(&caps[3]);

        if let Some(sym) = sym {
            if sym.owner_name() == Some(in_type) {
                // `#` or `%` symbols should probably have been `@` to denote
                // that it is a reference within the current type.
                format!("{}[`Self::{}{}`]", &caps[1], sym.name(), member)
            } else {
                match &caps[2] {
                    // Catch invalid @ references that have a C symbol available but do not belong
                    // to the current type (and can hence not use `Self::`). For now generate XXX
                    // but with a valid global link so that the can be easily spotted in the code.
                    // assert_eq!(sym.owner_name(), Some(in_type));
                    "@" => format!(
                        "{}[`crate::{}{}`] (XXX: @-reference does not belong to {}!)",
                        &caps[1],
                        sym.full_rust_name(),
                        member,
                        in_type,
                    ),
                    "%" if sym.is_rust_prelude() => {
                        format!("{}[`{}{}`]", &caps[1], sym.full_rust_name(), member)
                    }
                    "#" | "%" => {
                        format!("{}[`crate::{}{}`]", &caps[1], sym.full_rust_name(), member)
                    }
                    c => panic!("Unknown symbol reference {}", c),
                }
            }
        } else {
            format!("{}`{}{}`", &caps[1], &caps[3], member)
        }
    });
    let out = GDK_GTK.replace_all(&out, |caps: &Captures<'_>| find_struct(&caps[2], env));
    let out = TAGS.replace_all(&out, "`$0`");
    SPACES.replace_all(&out, " ").into_owned()
}

fn find_struct(name: &str, env: &Env) -> String {
    let symbols = env.symbols.borrow();

    let symbol = if let Some(obj) = env
        .analysis
        .objects
        .iter()
        .find(|(_, o)| o.c_type == name)
        .map(|(_, o)| o)
    {
        symbols.by_tid(obj.type_id)
    } else if let Some(record) = env
        .analysis
        .records
        .iter()
        .find(|(full_name, _)| full_name == &name)
        .map(|(_, r)| r)
    {
        symbols.by_tid(record.type_id)
    } else {
        None
    };
    symbol
        .map(|sym| format!("[{name}](crate::{name})", name = sym.full_rust_name()))
        .unwrap_or_else(|| name.to_string())
}

/// Find a function in all the possible items, if not found return the original name surrounded with backsticks
/// A function can either be a struct/interface/record method, a global function or maybe a virtual function
fn find_function(name: &str, env: &Env) -> String {
    let symbols = env.symbols.borrow();
    let is_obj_func = env.analysis.objects.iter().find_map(|(_, obj_info)| {
        obj_info
            .functions
            .iter()
            .filter(|fn_info| fn_info.status != GStatus::Ignore)
            .filter(|fn_info| !fn_info.is_special() && !fn_info.is_async_finish(env))
            .find(|fn_info| fn_info.glib_name == name)
            .map(|fn_info| (obj_info, fn_info))
    });
    let is_record_func = env.analysis.records.iter().find_map(|(_, record_info)| {
        record_info
            .functions
            .iter()
            .filter(|fn_info| fn_info.status != GStatus::Ignore)
            .filter(|fn_info| !fn_info.is_special() && !fn_info.is_async_finish(env))
            .find(|fn_info| fn_info.glib_name == name)
            .map(|fn_info| (record_info, fn_info))
    });
    let is_globa_func = env.analysis.global_functions.as_ref().and_then(|info| {
        info.functions
            .iter()
            .filter(|fn_info| fn_info.status != GStatus::Ignore)
            .filter(|fn_info| !fn_info.is_special() && !fn_info.is_async_finish(env))
            .find(|fn_info| fn_info.glib_name == name)
    });

    if let Some((obj_info, fn_info)) = is_obj_func {
        let sym = symbols.by_tid(obj_info.type_id).unwrap(); // we are sure the object exists
        let (type_name, visible_type_name) = if obj_info.final_type
            || fn_info.kind == FunctionKind::Constructor
            || fn_info.kind == FunctionKind::Function
        {
            (obj_info.name.clone(), obj_info.name.clone())
        } else {
            let type_name = if fn_info.status == GStatus::Generate {
                obj_info.trait_name.clone()
            } else {
                format!("{}Manual", obj_info.trait_name)
            };
            (format!("prelude::{}", type_name), type_name)
        };
        let name = sym.full_rust_name().replace(&obj_info.name, &type_name);
        format!(
            "[{visible_type_name}::{fn_name}](crate::{name}::{fn_name})",
            name = name,
            visible_type_name = visible_type_name,
            fn_name = fn_info.codegen_name()
        )
    } else if let Some((record_info, fn_info)) = is_record_func {
        let sym = symbols.by_tid(record_info.type_id).unwrap(); // we are sure the object exists
        format!(
            "[{name}::{fn_name}](crate::{name}::{fn_name})",
            name = sym.full_rust_name(),
            fn_name = fn_info.codegen_name()
        )
    } else if let Some(fn_info) = is_globa_func {
        format!(
            "[{fn_name}()](crate::{fn_name})",
            fn_name = fn_info.codegen_name()
        )
    } else {
        format!("`{}`", name)
    }
}
<|endoftext|>


<commit_before>import std._vec;

/* A codemap is a thing that maps uints to file/line/column positions
 * in a crate. This to make it possible to represent the positions
 * with single-word things, rather than passing records all over the
 * compiler.
 */

type filemap = @rec(str name,
                    uint start_pos,
                    mutable vec[uint] lines);
type codemap = @rec(mutable vec[filemap] files);
type loc = rec(str filename, uint line, uint col);

fn new_codemap() -> codemap {
    let vec[filemap] files = vec();
    ret @rec(mutable files=files);
}

fn new_filemap(str filename, uint start_pos) -> filemap {
    ret @rec(name=filename,
             start_pos=start_pos,
             mutable lines=vec(0u));
}

fn next_line(filemap file, uint pos) {
    _vec.push[uint](file.lines, pos);
}

fn lookup_pos(codemap map, uint pos) -> loc {
    auto i = _vec.len[filemap](map.files);
    while (i > 0u) {
        i -= 1u;
        auto f = map.files.(i);
        if (f.start_pos <= pos) {
            // FIXME this can be a binary search if we need to be faster
            auto line = _vec.len[uint](f.lines);
            while (line > 0u) {
                line -= 1u;
                auto line_start = f.lines.(line);
                if (line_start <= pos) {
                    ret rec(filename=f.name,
                            line=line + 1u,
                            col=pos-line_start);
                }
            }
        }
    }
    log_err #fmt("Failed to find a location for character %u", pos);
    fail;
}

//
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
//
<commit_msg>Switch to binary search in codemap.lookup_pos<commit_after>import std._vec;

/* A codemap is a thing that maps uints to file/line/column positions
 * in a crate. This to make it possible to represent the positions
 * with single-word things, rather than passing records all over the
 * compiler.
 */

type filemap = @rec(str name,
                    uint start_pos,
                    mutable vec[uint] lines);
type codemap = @rec(mutable vec[filemap] files);
type loc = rec(str filename, uint line, uint col);

fn new_codemap() -> codemap {
    let vec[filemap] files = vec();
    ret @rec(mutable files=files);
}

fn new_filemap(str filename, uint start_pos) -> filemap {
    ret @rec(name=filename,
             start_pos=start_pos,
             mutable lines=vec(0u));
}

fn next_line(filemap file, uint pos) {
    _vec.push[uint](file.lines, pos);
}

fn lookup_pos(codemap map, uint pos) -> loc {
    auto a = 0u; auto b = _vec.len[filemap](map.files);
    while (b - a > 1u) {
        auto m = (a + b) / 2u;
        if (map.files.(m).start_pos > pos) { b = m; }
        else { a = m; }
    }
    auto f = map.files.(a);
    a = 0u; b = _vec.len[uint](f.lines);
    while (b - a > 1u) {
        auto m = (a + b) / 2u;
        if (f.lines.(m) > pos) { b = m; }
        else { a = m; }
    }
    ret rec(filename=f.name,
            line=a + 1u,
            col=pos - f.lines.(a));
}

//
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
//
<|endoftext|>


<commit_before>// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use config::file_lines::FileLines;
use config::options::{IgnoreList, WidthHeuristics};

/// Trait for types that can be used in `Config`.
pub trait ConfigType: Sized {
    /// Returns hint text for use in `Config::print_docs()`. For enum types, this is a
    /// pipe-separated list of variants; for other types it returns "<type>".
    fn doc_hint() -> String;
}

impl ConfigType for bool {
    fn doc_hint() -> String {
        String::from("<boolean>")
    }
}

impl ConfigType for usize {
    fn doc_hint() -> String {
        String::from("<unsigned integer>")
    }
}

impl ConfigType for isize {
    fn doc_hint() -> String {
        String::from("<signed integer>")
    }
}

impl ConfigType for String {
    fn doc_hint() -> String {
        String::from("<string>")
    }
}

impl ConfigType for FileLines {
    fn doc_hint() -> String {
        String::from("<json>")
    }
}

impl ConfigType for WidthHeuristics {
    fn doc_hint() -> String {
        String::new()
    }
}

impl ConfigType for IgnoreList {
    fn doc_hint() -> String {
        String::from("[<string>,..]")
    }
}

/// Check if we're in a nightly build.
///
/// The environment variable `CFG_RELEASE_CHANNEL` is set during the rustc bootstrap
/// to "stable", "beta", or "nightly" depending on what toolchain is being built.
/// If we are being built as part of the stable or beta toolchains, we want
/// to disable unstable configuration options.
///
/// If we're being built by cargo (e.g. `cargo +nightly install rustfmt-nightly`),
/// `CFG_RELEASE_CHANNEL` is not set. As we only support being built against the
/// nightly compiler when installed from crates.io, default to nightly mode.
macro_rules! is_nightly_channel {
    () => {
        option_env!("CFG_RELEASE_CHANNEL")
            .map(|c| c == "nightly" || c == "dev")
            .unwrap_or(true)
    };
}

macro_rules! create_config {
    ($($i:ident: $ty:ty, $def:expr, $stb:expr, $( $dstring:expr ),+ );+ $(;)*) => (
        #[cfg(test)]
        use std::collections::HashSet;
        use std::io::Write;

        #[derive(Clone)]
        pub struct Config {
            // if a license_template_path has been specified, successfully read, parsed and compiled
            // into a regex, it will be stored here
            pub license_template: Option<Regex>,
            // For each config item, we store a bool indicating whether it has
            // been accessed and the value, and a bool whether the option was
            // manually initialised, or taken from the default,
            $($i: (Cell<bool>, bool, $ty, bool)),+
        }

        // Just like the Config struct but with each property wrapped
        // as Option<T>. This is used to parse a rustfmt.toml that doesn't
        // specify all properties of `Config`.
        // We first parse into `PartialConfig`, then create a default `Config`
        // and overwrite the properties with corresponding values from `PartialConfig`.
        #[derive(Deserialize, Serialize, Clone)]
        pub struct PartialConfig {
            $(pub $i: Option<$ty>),+
        }

        impl PartialConfig {
            pub fn to_toml(&self) -> Result<String, String> {
                // Non-user-facing options can't be specified in TOML
                let mut cloned = self.clone();
                cloned.file_lines = None;
                cloned.verbose = None;
                cloned.width_heuristics = None;

                ::toml::to_string(&cloned)
                    .map_err(|e| format!("Could not output config: {}", e.to_string()))
            }
        }

        // Macro hygiene won't allow us to make `set_$i()` methods on Config
        // for each item, so this struct is used to give the API to set values:
        // `config.set().option(false)`. It's pretty ugly. Consider replacing
        // with `config.set_option(false)` if we ever get a stable/usable
        // `concat_idents!()`.
        pub struct ConfigSetter<'a>(&'a mut Config);

        impl<'a> ConfigSetter<'a> {
            $(
            pub fn $i(&mut self, value: $ty) {
                (self.0).$i.2 = value;
                match stringify!($i) {
                    "max_width" | "use_small_heuristics" => self.0.set_heuristics(),
                    "license_template_path" => self.0.set_license_template(),
                    &_ => (),
                }
            }
            )+
        }

        // Query each option, returns true if the user set the option, false if
        // a default was used.
        pub struct ConfigWasSet<'a>(&'a Config);

        impl<'a> ConfigWasSet<'a> {
            $(
            pub fn $i(&self) -> bool {
                (self.0).$i.1
            }
            )+
        }

        impl Config {
            pub(crate) fn version_meets_requirement(&self) -> bool {
                if self.was_set().required_version() {
                    let version = env!("CARGO_PKG_VERSION");
                    let required_version = self.required_version();
                    if version != required_version {
                        println!(
                            "Error: rustfmt version ({}) doesn't match the required version ({})",
                            version,
                            required_version,
                        );
                        return false;
                    }
                }

                true
            }

            $(
            pub fn $i(&self) -> $ty {
                self.$i.0.set(true);
                self.$i.2.clone()
            }
            )+

            pub fn set(&mut self) -> ConfigSetter {
                ConfigSetter(self)
            }

            pub fn was_set(&self) -> ConfigWasSet {
                ConfigWasSet(self)
            }

            fn fill_from_parsed_config(mut self, parsed: PartialConfig, dir: &Path) -> Config {
            $(
                if let Some(val) = parsed.$i {
                    if self.$i.3 {
                        self.$i.1 = true;
                        self.$i.2 = val;
                    } else {
                        if is_nightly_channel!() {
                            self.$i.1 = true;
                            self.$i.2 = val;
                        } else {
                            eprintln!("Warning: can't set `{} = {:?}`, unstable features are only \
                                       available in nightly channel.", stringify!($i), val);
                        }
                    }
                }
            )+
                self.set_heuristics();
                self.set_license_template();
                self.set_ignore(dir);
                self
            }

            /// Returns a hash set initialized with every user-facing config option name.
            #[cfg(test)]
            pub(crate) fn hash_set() -> HashSet<String> {
                let mut hash_set = HashSet::new();
                $(
                    hash_set.insert(stringify!($i).to_owned());
                )+
                hash_set
            }

            pub(crate) fn is_valid_name(name: &str) -> bool {
                match name {
                    $(
                        stringify!($i) => true,
                    )+
                        _ => false,
                }
            }

            pub(crate) fn from_toml(toml: &str, dir: &Path) -> Result<Config, String> {
                let parsed: ::toml::Value =
                    toml.parse().map_err(|e| format!("Could not parse TOML: {}", e))?;
                let mut err: String = String::new();
                {
                    let table = parsed
                        .as_table()
                        .ok_or(String::from("Parsed config was not table"))?;
                    for key in table.keys() {
                        if !Config::is_valid_name(key) {
                            let msg = &format!("Warning: Unknown configuration option `{}`\n", key);
                            err.push_str(msg)
                        }
                    }
                }
                match parsed.try_into() {
                    Ok(parsed_config) => {
                        if !err.is_empty() {
                            eprint!("{}", err);
                        }
                        Ok(Config::default().fill_from_parsed_config(parsed_config, dir: &Path))
                    }
                    Err(e) => {
                        err.push_str("Error: Decoding config file failed:\n");
                        err.push_str(format!("{}\n", e).as_str());
                        err.push_str("Please check your config file.");
                        Err(err)
                    }
                }
            }

            pub fn used_options(&self) -> PartialConfig {
                PartialConfig {
                    $(
                        $i: if self.$i.0.get() {
                                Some(self.$i.2.clone())
                            } else {
                                None
                            },
                    )+
                }
            }

            pub fn all_options(&self) -> PartialConfig {
                PartialConfig {
                    $(
                        $i: Some(self.$i.2.clone()),
                    )+
                }
            }

            pub fn override_value(&mut self, key: &str, val: &str)
            {
                match key {
                    $(
                        stringify!($i) => {
                            self.$i.1 = true;
                            self.$i.2 = val.parse::<$ty>()
                                .expect(&format!("Failed to parse override for {} (\"{}\") as a {}",
                                                 stringify!($i),
                                                 val,
                                                 stringify!($ty)));
                        }
                    )+
                    _ => panic!("Unknown config key in override: {}", key)
                }

                match key {
                    "max_width" | "use_small_heuristics" => self.set_heuristics(),
                    "license_template_path" => self.set_license_template(),
                    &_ => (),
                }
            }

            /// Construct a `Config` from the toml file specified at `file_path`.
            ///
            /// This method only looks at the provided path, for a method that
            /// searches parents for a `rustfmt.toml` see `from_resolved_toml_path`.
            ///
            /// Return a `Config` if the config could be read and parsed from
            /// the file, Error otherwise.
            pub(super) fn from_toml_path(file_path: &Path) -> Result<Config, Error> {
                let mut file = File::open(&file_path)?;
                let mut toml = String::new();
                file.read_to_string(&mut toml)?;
                Config::from_toml(&toml, file_path.parent().unwrap())
                    .map_err(|err| Error::new(ErrorKind::InvalidData, err))
            }

            /// Resolve the config for input in `dir`.
            ///
            /// Searches for `rustfmt.toml` beginning with `dir`, and
            /// recursively checking parents of `dir` if no config file is found.
            /// If no config file exists in `dir` or in any parent, a
            /// default `Config` will be returned (and the returned path will be empty).
            ///
            /// Returns the `Config` to use, and the path of the project file if there was
            /// one.
            pub(super) fn from_resolved_toml_path(
                dir: &Path,
            ) -> Result<(Config, Option<PathBuf>), Error> {
                /// Try to find a project file in the given directory and its parents.
                /// Returns the path of a the nearest project file if one exists,
                /// or `None` if no project file was found.
                fn resolve_project_file(dir: &Path) -> Result<Option<PathBuf>, Error> {
                    let mut current = if dir.is_relative() {
                        env::current_dir()?.join(dir)
                    } else {
                        dir.to_path_buf()
                    };

                    current = fs::canonicalize(current)?;

                    loop {
                        match get_toml_path(&current) {
                            Ok(Some(path)) => return Ok(Some(path)),
                            Err(e) => return Err(e),
                            _ => ()
                        }

                        // If the current directory has no parent, we're done searching.
                        if !current.pop() {
                            return Ok(None);
                        }
                    }
                }

                match resolve_project_file(dir)? {
                    None => Ok((Config::default(), None)),
                    Some(path) => Config::from_toml_path(&path).map(|config| (config, Some(path))),
                }
            }

            pub fn is_hidden_option(name: &str) -> bool {
                const HIDE_OPTIONS: [&str; 4] =
                    ["verbose", "verbose_diff", "file_lines", "width_heuristics"];
                HIDE_OPTIONS.contains(&name)
            }

            pub fn print_docs(out: &mut Write, include_unstable: bool) {
                use std::cmp;
                let max = 0;
                $( let max = cmp::max(max, stringify!($i).len()+1); )+
                let mut space_str = String::with_capacity(max);
                for _ in 0..max {
                    space_str.push(' ');
                }
                writeln!(out, "Configuration Options:").unwrap();
                $(
                    if $stb || include_unstable {
                        let name_raw = stringify!($i);

                        if !Config::is_hidden_option(name_raw) {
                            let mut name_out = String::with_capacity(max);
                            for _ in name_raw.len()..max-1 {
                                name_out.push(' ')
                            }
                            name_out.push_str(name_raw);
                            name_out.push(' ');
                            writeln!(out,
                                    "{}{} Default: {:?}{}",
                                    name_out,
                                    <$ty>::doc_hint(),
                                    $def,
                                    if !$stb { " (unstable)" } else { "" }).unwrap();
                            $(
                                writeln!(out, "{}{}", space_str, $dstring).unwrap();
                            )+
                            writeln!(out).unwrap();
                        }
                    }
                )+
            }

            fn set_heuristics(&mut self) {
                if self.use_small_heuristics.2 == Heuristics::Default {
                    let max_width = self.max_width.2;
                    self.set().width_heuristics(WidthHeuristics::scaled(max_width));
                } else if self.use_small_heuristics.2 == Heuristics::Max {
                    let max_width = self.max_width.2;
                    self.set().width_heuristics(WidthHeuristics::set(max_width));
                } else {
                    self.set().width_heuristics(WidthHeuristics::null());
                }
            }

            fn set_license_template(&mut self) {
                if self.was_set().license_template_path() {
                    let lt_path = self.license_template_path();
                    match license::load_and_compile_template(&lt_path) {
                        Ok(re) => self.license_template = Some(re),
                        Err(msg) => eprintln!("Warning for license template file {:?}: {}",
                                              lt_path, msg),
                    }
                }
            }

            fn set_ignore(&mut self, dir: &Path) {
                self.ignore.2.add_prefix(dir);
            }

            /// Returns true if the config key was explicitely set and is the default value.
            pub fn is_default(&self, key: &str) -> bool {
                $(
                    if let stringify!($i) = key {
                        return self.$i.1 && self.$i.2 == $def;
                    }
                 )+
                false
            }
        }

        // Template for the default configuration
        impl Default for Config {
            fn default() -> Config {
                Config {
                    license_template: None,
                    $(
                        $i: (Cell::new(false), false, $def, $stb),
                    )+
                }
            }
        }
    )
}
<commit_msg>config: replace `.unwrap_or` with `.map_or`<commit_after>// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use config::file_lines::FileLines;
use config::options::{IgnoreList, WidthHeuristics};

/// Trait for types that can be used in `Config`.
pub trait ConfigType: Sized {
    /// Returns hint text for use in `Config::print_docs()`. For enum types, this is a
    /// pipe-separated list of variants; for other types it returns "<type>".
    fn doc_hint() -> String;
}

impl ConfigType for bool {
    fn doc_hint() -> String {
        String::from("<boolean>")
    }
}

impl ConfigType for usize {
    fn doc_hint() -> String {
        String::from("<unsigned integer>")
    }
}

impl ConfigType for isize {
    fn doc_hint() -> String {
        String::from("<signed integer>")
    }
}

impl ConfigType for String {
    fn doc_hint() -> String {
        String::from("<string>")
    }
}

impl ConfigType for FileLines {
    fn doc_hint() -> String {
        String::from("<json>")
    }
}

impl ConfigType for WidthHeuristics {
    fn doc_hint() -> String {
        String::new()
    }
}

impl ConfigType for IgnoreList {
    fn doc_hint() -> String {
        String::from("[<string>,..]")
    }
}

/// Check if we're in a nightly build.
///
/// The environment variable `CFG_RELEASE_CHANNEL` is set during the rustc bootstrap
/// to "stable", "beta", or "nightly" depending on what toolchain is being built.
/// If we are being built as part of the stable or beta toolchains, we want
/// to disable unstable configuration options.
///
/// If we're being built by cargo (e.g. `cargo +nightly install rustfmt-nightly`),
/// `CFG_RELEASE_CHANNEL` is not set. As we only support being built against the
/// nightly compiler when installed from crates.io, default to nightly mode.
macro_rules! is_nightly_channel {
    () => {
        option_env!("CFG_RELEASE_CHANNEL").map_or(true, |c| c == "nightly" || c == "dev")
    };
}

macro_rules! create_config {
    ($($i:ident: $ty:ty, $def:expr, $stb:expr, $( $dstring:expr ),+ );+ $(;)*) => (
        #[cfg(test)]
        use std::collections::HashSet;
        use std::io::Write;

        #[derive(Clone)]
        pub struct Config {
            // if a license_template_path has been specified, successfully read, parsed and compiled
            // into a regex, it will be stored here
            pub license_template: Option<Regex>,
            // For each config item, we store a bool indicating whether it has
            // been accessed and the value, and a bool whether the option was
            // manually initialised, or taken from the default,
            $($i: (Cell<bool>, bool, $ty, bool)),+
        }

        // Just like the Config struct but with each property wrapped
        // as Option<T>. This is used to parse a rustfmt.toml that doesn't
        // specify all properties of `Config`.
        // We first parse into `PartialConfig`, then create a default `Config`
        // and overwrite the properties with corresponding values from `PartialConfig`.
        #[derive(Deserialize, Serialize, Clone)]
        pub struct PartialConfig {
            $(pub $i: Option<$ty>),+
        }

        impl PartialConfig {
            pub fn to_toml(&self) -> Result<String, String> {
                // Non-user-facing options can't be specified in TOML
                let mut cloned = self.clone();
                cloned.file_lines = None;
                cloned.verbose = None;
                cloned.width_heuristics = None;

                ::toml::to_string(&cloned)
                    .map_err(|e| format!("Could not output config: {}", e.to_string()))
            }
        }

        // Macro hygiene won't allow us to make `set_$i()` methods on Config
        // for each item, so this struct is used to give the API to set values:
        // `config.set().option(false)`. It's pretty ugly. Consider replacing
        // with `config.set_option(false)` if we ever get a stable/usable
        // `concat_idents!()`.
        pub struct ConfigSetter<'a>(&'a mut Config);

        impl<'a> ConfigSetter<'a> {
            $(
            pub fn $i(&mut self, value: $ty) {
                (self.0).$i.2 = value;
                match stringify!($i) {
                    "max_width" | "use_small_heuristics" => self.0.set_heuristics(),
                    "license_template_path" => self.0.set_license_template(),
                    &_ => (),
                }
            }
            )+
        }

        // Query each option, returns true if the user set the option, false if
        // a default was used.
        pub struct ConfigWasSet<'a>(&'a Config);

        impl<'a> ConfigWasSet<'a> {
            $(
            pub fn $i(&self) -> bool {
                (self.0).$i.1
            }
            )+
        }

        impl Config {
            pub(crate) fn version_meets_requirement(&self) -> bool {
                if self.was_set().required_version() {
                    let version = env!("CARGO_PKG_VERSION");
                    let required_version = self.required_version();
                    if version != required_version {
                        println!(
                            "Error: rustfmt version ({}) doesn't match the required version ({})",
                            version,
                            required_version,
                        );
                        return false;
                    }
                }

                true
            }

            $(
            pub fn $i(&self) -> $ty {
                self.$i.0.set(true);
                self.$i.2.clone()
            }
            )+

            pub fn set(&mut self) -> ConfigSetter {
                ConfigSetter(self)
            }

            pub fn was_set(&self) -> ConfigWasSet {
                ConfigWasSet(self)
            }

            fn fill_from_parsed_config(mut self, parsed: PartialConfig, dir: &Path) -> Config {
            $(
                if let Some(val) = parsed.$i {
                    if self.$i.3 {
                        self.$i.1 = true;
                        self.$i.2 = val;
                    } else {
                        if is_nightly_channel!() {
                            self.$i.1 = true;
                            self.$i.2 = val;
                        } else {
                            eprintln!("Warning: can't set `{} = {:?}`, unstable features are only \
                                       available in nightly channel.", stringify!($i), val);
                        }
                    }
                }
            )+
                self.set_heuristics();
                self.set_license_template();
                self.set_ignore(dir);
                self
            }

            /// Returns a hash set initialized with every user-facing config option name.
            #[cfg(test)]
            pub(crate) fn hash_set() -> HashSet<String> {
                let mut hash_set = HashSet::new();
                $(
                    hash_set.insert(stringify!($i).to_owned());
                )+
                hash_set
            }

            pub(crate) fn is_valid_name(name: &str) -> bool {
                match name {
                    $(
                        stringify!($i) => true,
                    )+
                        _ => false,
                }
            }

            pub(crate) fn from_toml(toml: &str, dir: &Path) -> Result<Config, String> {
                let parsed: ::toml::Value =
                    toml.parse().map_err(|e| format!("Could not parse TOML: {}", e))?;
                let mut err: String = String::new();
                {
                    let table = parsed
                        .as_table()
                        .ok_or(String::from("Parsed config was not table"))?;
                    for key in table.keys() {
                        if !Config::is_valid_name(key) {
                            let msg = &format!("Warning: Unknown configuration option `{}`\n", key);
                            err.push_str(msg)
                        }
                    }
                }
                match parsed.try_into() {
                    Ok(parsed_config) => {
                        if !err.is_empty() {
                            eprint!("{}", err);
                        }
                        Ok(Config::default().fill_from_parsed_config(parsed_config, dir: &Path))
                    }
                    Err(e) => {
                        err.push_str("Error: Decoding config file failed:\n");
                        err.push_str(format!("{}\n", e).as_str());
                        err.push_str("Please check your config file.");
                        Err(err)
                    }
                }
            }

            pub fn used_options(&self) -> PartialConfig {
                PartialConfig {
                    $(
                        $i: if self.$i.0.get() {
                                Some(self.$i.2.clone())
                            } else {
                                None
                            },
                    )+
                }
            }

            pub fn all_options(&self) -> PartialConfig {
                PartialConfig {
                    $(
                        $i: Some(self.$i.2.clone()),
                    )+
                }
            }

            pub fn override_value(&mut self, key: &str, val: &str)
            {
                match key {
                    $(
                        stringify!($i) => {
                            self.$i.1 = true;
                            self.$i.2 = val.parse::<$ty>()
                                .expect(&format!("Failed to parse override for {} (\"{}\") as a {}",
                                                 stringify!($i),
                                                 val,
                                                 stringify!($ty)));
                        }
                    )+
                    _ => panic!("Unknown config key in override: {}", key)
                }

                match key {
                    "max_width" | "use_small_heuristics" => self.set_heuristics(),
                    "license_template_path" => self.set_license_template(),
                    &_ => (),
                }
            }

            /// Construct a `Config` from the toml file specified at `file_path`.
            ///
            /// This method only looks at the provided path, for a method that
            /// searches parents for a `rustfmt.toml` see `from_resolved_toml_path`.
            ///
            /// Return a `Config` if the config could be read and parsed from
            /// the file, Error otherwise.
            pub(super) fn from_toml_path(file_path: &Path) -> Result<Config, Error> {
                let mut file = File::open(&file_path)?;
                let mut toml = String::new();
                file.read_to_string(&mut toml)?;
                Config::from_toml(&toml, file_path.parent().unwrap())
                    .map_err(|err| Error::new(ErrorKind::InvalidData, err))
            }

            /// Resolve the config for input in `dir`.
            ///
            /// Searches for `rustfmt.toml` beginning with `dir`, and
            /// recursively checking parents of `dir` if no config file is found.
            /// If no config file exists in `dir` or in any parent, a
            /// default `Config` will be returned (and the returned path will be empty).
            ///
            /// Returns the `Config` to use, and the path of the project file if there was
            /// one.
            pub(super) fn from_resolved_toml_path(
                dir: &Path,
            ) -> Result<(Config, Option<PathBuf>), Error> {
                /// Try to find a project file in the given directory and its parents.
                /// Returns the path of a the nearest project file if one exists,
                /// or `None` if no project file was found.
                fn resolve_project_file(dir: &Path) -> Result<Option<PathBuf>, Error> {
                    let mut current = if dir.is_relative() {
                        env::current_dir()?.join(dir)
                    } else {
                        dir.to_path_buf()
                    };

                    current = fs::canonicalize(current)?;

                    loop {
                        match get_toml_path(&current) {
                            Ok(Some(path)) => return Ok(Some(path)),
                            Err(e) => return Err(e),
                            _ => ()
                        }

                        // If the current directory has no parent, we're done searching.
                        if !current.pop() {
                            return Ok(None);
                        }
                    }
                }

                match resolve_project_file(dir)? {
                    None => Ok((Config::default(), None)),
                    Some(path) => Config::from_toml_path(&path).map(|config| (config, Some(path))),
                }
            }

            pub fn is_hidden_option(name: &str) -> bool {
                const HIDE_OPTIONS: [&str; 4] =
                    ["verbose", "verbose_diff", "file_lines", "width_heuristics"];
                HIDE_OPTIONS.contains(&name)
            }

            pub fn print_docs(out: &mut Write, include_unstable: bool) {
                use std::cmp;
                let max = 0;
                $( let max = cmp::max(max, stringify!($i).len()+1); )+
                let mut space_str = String::with_capacity(max);
                for _ in 0..max {
                    space_str.push(' ');
                }
                writeln!(out, "Configuration Options:").unwrap();
                $(
                    if $stb || include_unstable {
                        let name_raw = stringify!($i);

                        if !Config::is_hidden_option(name_raw) {
                            let mut name_out = String::with_capacity(max);
                            for _ in name_raw.len()..max-1 {
                                name_out.push(' ')
                            }
                            name_out.push_str(name_raw);
                            name_out.push(' ');
                            writeln!(out,
                                    "{}{} Default: {:?}{}",
                                    name_out,
                                    <$ty>::doc_hint(),
                                    $def,
                                    if !$stb { " (unstable)" } else { "" }).unwrap();
                            $(
                                writeln!(out, "{}{}", space_str, $dstring).unwrap();
                            )+
                            writeln!(out).unwrap();
                        }
                    }
                )+
            }

            fn set_heuristics(&mut self) {
                if self.use_small_heuristics.2 == Heuristics::Default {
                    let max_width = self.max_width.2;
                    self.set().width_heuristics(WidthHeuristics::scaled(max_width));
                } else if self.use_small_heuristics.2 == Heuristics::Max {
                    let max_width = self.max_width.2;
                    self.set().width_heuristics(WidthHeuristics::set(max_width));
                } else {
                    self.set().width_heuristics(WidthHeuristics::null());
                }
            }

            fn set_license_template(&mut self) {
                if self.was_set().license_template_path() {
                    let lt_path = self.license_template_path();
                    match license::load_and_compile_template(&lt_path) {
                        Ok(re) => self.license_template = Some(re),
                        Err(msg) => eprintln!("Warning for license template file {:?}: {}",
                                              lt_path, msg),
                    }
                }
            }

            fn set_ignore(&mut self, dir: &Path) {
                self.ignore.2.add_prefix(dir);
            }

            /// Returns true if the config key was explicitely set and is the default value.
            pub fn is_default(&self, key: &str) -> bool {
                $(
                    if let stringify!($i) = key {
                        return self.$i.1 && self.$i.2 == $def;
                    }
                 )+
                false
            }
        }

        // Template for the default configuration
        impl Default for Config {
            fn default() -> Config {
                Config {
                    license_template: None,
                    $(
                        $i: (Cell::new(false), false, $def, $stb),
                    )+
                }
            }
        }
    )
}
<|endoftext|>