<commit_before>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

use std::io::Write as _;

use byteorder::{BigEndian, WriteBytesExt as _};

/// Size of a gzchunked block.
const BLOCK_SIZE: usize = 10 << 20;

/// A wrapper type for gzip compression level.
#[derive(Clone, Copy, Debug)]
pub struct Compression(flate2::Compression);

/// A type for defining gzip compression level for gzchunked.
impl Compression {

    pub fn new(level: u32) -> Compression {
        Compression(flate2::Compression::new(level))
    }

    pub fn none() -> Compression {
        Compression(flate2::Compression::none())
    }

    pub fn best() -> Compression {
        Compression(flate2::Compression::best())
    }
}

impl Default for Compression {

    fn default() -> Compression {
        Compression(flate2::Compression::new(5))
    }
}

/// A gzchunked streaming encoder.
pub struct Encoder {
    encoder: flate2::write::GzEncoder<Vec<u8>>,
    compression: Compression,
}

impl Encoder {

    /// Creates a new encoder with specified gzip compression level.
    pub fn new(compression: Compression) -> Encoder {
        Encoder {
            encoder: flate2::write::GzEncoder::new(Vec::new(), compression.0),
            compression,
        }
    }

    /// Writes next data chunk into the stream.
    pub fn write(&mut self, buf: &[u8]) -> std::io::Result<()> {
        self.encoder.write_u64::<BigEndian>(buf.len() as u64)?;
        self.encoder.write_all(buf)?;
        Ok(())
    }

    /// Attempts to retrieve next gzipped block.
    /// Returns `Ok(None)` if there's not enough data for a whole block.
    pub fn try_next_chunk(&mut self) -> std::io::Result<Option<Vec<u8>>> {
        self.encoder.flush()?;
        if self.encoder.get_ref().len() < BLOCK_SIZE {
            return Ok(None)
        }

        Ok(Some(self.next_chunk()?))
    }

    /// Retrieves next gzipped block without checking its size.
    pub fn next_chunk(&mut self) -> std::io::Result<Vec<u8>> {
        self.encoder.flush()?;
        let new_encoder = flate2::write::GzEncoder::new(Vec::new(), self.compression.0);
        let old_encoder = std::mem::replace(&mut self.encoder, new_encoder);
        let encoded_data = old_encoder.finish()?;

        Ok(encoded_data)
    }
}

/// Encodes the given iterator over binary entries into the gzchunked format.
///
/// This is a streaming encoder that lazily encodes the data and can be used to
/// effectively process megabytes of data. The function returns an iterator that
/// yields parts of the gzchunked file.
///
/// This variant uses default encoding settings. To customize them, one can use
/// the [`encode_with_opts`] function.
///
/// [`encode_with_opts`]: fn.encode_with_opts.html
///
/// # Examples
///
/// ```no_run
/// use std::fs::File;
/// use std::io::Write as _;
///
/// let items: Vec<&[u8]> = vec!(b"foo", b"bar", b"baz");
///
/// let chunks = rrg::gzchunked::encode(items.into_iter());
/// for (idx, chunk) in chunks.enumerate() {
///     let mut file = File::open(format!("output.gzc.{}", idx)).unwrap();
///     file.write_all(chunk.unwrap().as_slice());
/// }
/// ```
pub fn encode<'a, I>(iter: I) -> Encode<I>
where
    I: Iterator<Item=&'a [u8]>,
{
    encode_with_opts(iter, EncodeOpts::default())
}

/// Encodes the given iterator over binary entries into the gzchunked format.
///
/// This is a variant of the [`encode`] function that allows customization of
/// encoding parameters. Refer to its documentation for more details.
///
/// [`encode`]: fn.encode.html
pub fn encode_with_opts<'a, I>(iter: I, opts: EncodeOpts) -> Encode<I>
where
    I: Iterator<Item=&'a [u8]>,
{
    Encode::with_opts(iter, opts)
}

/// Options and flags that configure encoding into the gzchuned format.
#[derive(Clone, Copy, Debug)]
pub struct EncodeOpts {
    /// Compression level used for the gzip encoding.
    pub compression: Compression,
    /// A rough file size limit for parts of the output file.
    pub part_size: u64,
}

impl Default for EncodeOpts {

    fn default() -> EncodeOpts {
        EncodeOpts {
            compression: Compression::default(),
            part_size: 1 * 1024 * 1024, // 1 MiB.
        }
    }
}

/// Streaming encoder for the gzchunked format.
///
/// It implements the `Iterator` trait, lazily polling the underlying entry
/// iterator as more parts are needed.
///
/// Instances of this type can be constructed using the [`encode_with_opts`] or
/// [`encode`] function.
///
/// [`encode`]: fn.encode.html
/// [`encode_with_opts`]: fn.encode_with_opts.html
pub struct Encode<I> {
    chunked: crate::chunked::Encode<I>,
    opts: EncodeOpts,
}

impl<'a, I> Encode<I>
where
    I: Iterator<Item = &'a [u8]>,
{
    /// Creates a new encoder instance with the specified options.
    fn with_opts(iter: I, opts: EncodeOpts) -> Encode<I> {
        Encode {
            chunked: crate::chunked::encode(iter),
            opts: opts,
        }
    }

    /// Obtains the next part of the output file (if available).
    fn pull(&mut self) -> std::io::Result<Option<Vec<u8>>> {
        use crate::io::copy_until;

        let compression = self.opts.compression.0;
        let part_size = self.opts.part_size;

        let mut encoder = flate2::write::GzEncoder::new(vec!(), compression);
        let len = copy_until(&mut self.chunked, &mut encoder, |_, encoder| {
            encoder.get_ref().len() as u64 >= part_size
        })?;

        if len == 0 {
            Ok(None)
        } else {
            Ok(Some(encoder.finish()?))
        }
    }
}

impl<'a, I> Iterator for Encode<I>
where
    I: Iterator<Item = &'a [u8]>,
{
    type Item = std::io::Result<Vec<u8>>;

    fn next(&mut self) -> Option<std::io::Result<Vec<u8>>> {
        self.pull().transpose()
    }
}
<commit_msg>Rename one of the gzchunked encoder methods.<commit_after>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

use std::io::Write as _;

use byteorder::{BigEndian, WriteBytesExt as _};

/// Size of a gzchunked block.
const BLOCK_SIZE: usize = 10 << 20;

/// A wrapper type for gzip compression level.
#[derive(Clone, Copy, Debug)]
pub struct Compression(flate2::Compression);

/// A type for defining gzip compression level for gzchunked.
impl Compression {

    pub fn new(level: u32) -> Compression {
        Compression(flate2::Compression::new(level))
    }

    pub fn none() -> Compression {
        Compression(flate2::Compression::none())
    }

    pub fn best() -> Compression {
        Compression(flate2::Compression::best())
    }
}

impl Default for Compression {

    fn default() -> Compression {
        Compression(flate2::Compression::new(5))
    }
}

/// A gzchunked streaming encoder.
pub struct Encoder {
    encoder: flate2::write::GzEncoder<Vec<u8>>,
    compression: Compression,
}

impl Encoder {

    /// Creates a new encoder with specified gzip compression level.
    pub fn new(compression: Compression) -> Encoder {
        Encoder {
            encoder: flate2::write::GzEncoder::new(Vec::new(), compression.0),
            compression,
        }
    }

    /// Writes next data chunk into the stream.
    pub fn write(&mut self, buf: &[u8]) -> std::io::Result<()> {
        self.encoder.write_u64::<BigEndian>(buf.len() as u64)?;
        self.encoder.write_all(buf)?;
        Ok(())
    }

    /// Attempts to retrieve next gzipped block.
    /// Returns `Ok(None)` if there's not enough data for a whole block.
    pub fn try_next_chunk(&mut self) -> std::io::Result<Option<Vec<u8>>> {
        self.encoder.flush()?;
        if self.encoder.get_ref().len() < BLOCK_SIZE {
            return Ok(None)
        }

        Ok(Some(self.next_chunk()?))
    }

    /// Retrieves next gzipped block without checking its size.
    pub fn next_chunk(&mut self) -> std::io::Result<Vec<u8>> {
        self.encoder.flush()?;
        let new_encoder = flate2::write::GzEncoder::new(Vec::new(), self.compression.0);
        let old_encoder = std::mem::replace(&mut self.encoder, new_encoder);
        let encoded_data = old_encoder.finish()?;

        Ok(encoded_data)
    }
}

/// Encodes the given iterator over binary entries into the gzchunked format.
///
/// This is a streaming encoder that lazily encodes the data and can be used to
/// effectively process megabytes of data. The function returns an iterator that
/// yields parts of the gzchunked file.
///
/// This variant uses default encoding settings. To customize them, one can use
/// the [`encode_with_opts`] function.
///
/// [`encode_with_opts`]: fn.encode_with_opts.html
///
/// # Examples
///
/// ```no_run
/// use std::fs::File;
/// use std::io::Write as _;
///
/// let items: Vec<&[u8]> = vec!(b"foo", b"bar", b"baz");
///
/// let chunks = rrg::gzchunked::encode(items.into_iter());
/// for (idx, chunk) in chunks.enumerate() {
///     let mut file = File::open(format!("output.gzc.{}", idx)).unwrap();
///     file.write_all(chunk.unwrap().as_slice());
/// }
/// ```
pub fn encode<'a, I>(iter: I) -> Encode<I>
where
    I: Iterator<Item=&'a [u8]>,
{
    encode_with_opts(iter, EncodeOpts::default())
}

/// Encodes the given iterator over binary entries into the gzchunked format.
///
/// This is a variant of the [`encode`] function that allows customization of
/// encoding parameters. Refer to its documentation for more details.
///
/// [`encode`]: fn.encode.html
pub fn encode_with_opts<'a, I>(iter: I, opts: EncodeOpts) -> Encode<I>
where
    I: Iterator<Item=&'a [u8]>,
{
    Encode::with_opts(iter, opts)
}

/// Options and flags that configure encoding into the gzchuned format.
#[derive(Clone, Copy, Debug)]
pub struct EncodeOpts {
    /// Compression level used for the gzip encoding.
    pub compression: Compression,
    /// A rough file size limit for parts of the output file.
    pub part_size: u64,
}

impl Default for EncodeOpts {

    fn default() -> EncodeOpts {
        EncodeOpts {
            compression: Compression::default(),
            part_size: 1 * 1024 * 1024, // 1 MiB.
        }
    }
}

/// Streaming encoder for the gzchunked format.
///
/// It implements the `Iterator` trait, lazily polling the underlying entry
/// iterator as more parts are needed.
///
/// Instances of this type can be constructed using the [`encode_with_opts`] or
/// [`encode`] function.
///
/// [`encode`]: fn.encode.html
/// [`encode_with_opts`]: fn.encode_with_opts.html
pub struct Encode<I> {
    chunked: crate::chunked::Encode<I>,
    opts: EncodeOpts,
}

impl<'a, I> Encode<I>
where
    I: Iterator<Item = &'a [u8]>,
{
    /// Creates a new encoder instance with the specified options.
    fn with_opts(iter: I, opts: EncodeOpts) -> Encode<I> {
        Encode {
            chunked: crate::chunked::encode(iter),
            opts: opts,
        }
    }

    /// Obtains the next part of the output file (if available).
    fn next_part(&mut self) -> std::io::Result<Option<Vec<u8>>> {
        use crate::io::copy_until;

        let compression = self.opts.compression.0;
        let part_size = self.opts.part_size;

        let mut encoder = flate2::write::GzEncoder::new(vec!(), compression);
        let len = copy_until(&mut self.chunked, &mut encoder, |_, encoder| {
            encoder.get_ref().len() as u64 >= part_size
        })?;

        if len == 0 {
            Ok(None)
        } else {
            Ok(Some(encoder.finish()?))
        }
    }
}

impl<'a, I> Iterator for Encode<I>
where
    I: Iterator<Item = &'a [u8]>,
{
    type Item = std::io::Result<Vec<u8>>;

    fn next(&mut self) -> Option<std::io::Result<Vec<u8>>> {
        self.next_part().transpose()
    }
}
<|endoftext|>


<commit_before>use std::fs::File;
use std::io::Read;

use read_token::{NumberSettings, ReadToken};

use super::io::io_error;

use Variable;

/// Loads data from a file.
pub fn load_file(file: &str) -> Result<Variable, String> {
    let mut data_file = try!(File::open(file).map_err(|err| io_error("open", file, &err)));
    let mut d = String::new();
    try!(data_file.read_to_string(&mut d).map_err(|err| io_error("read", file, &err)));
    load_data(&d)
}

/// Loads data from text.
pub fn load_data(data: &str) -> Result<Variable, String> {
    let mut read = ReadToken::new(data, 0);
    expr(&mut read)
}

static NUMBER_SETTINGS: NumberSettings = NumberSettings {
    allow_underscore: true,
};

const SEPS: &'static str = &"(){}[],.:;\n\"\\";

fn expr(read: &mut ReadToken) -> Result<Variable, String> {
    use std::sync::Arc;

    if let Some(range) = read.tag("{") {
        // Object.
        *read = read.consume(range.length);
        return object(read);
    }
    if let Some(range) = read.tag("[") {
        // Array.
        *read = read.consume(range.length);
        return array(read);
    }
    if let Some(range) = read.tag("(") {
        // Vec4.
        *read = read.consume(range.length);
        return vec4(read);
    }
    if let Some(range) = read.tag("#") {
        use read_color::rgb_maybe_a;

        // Color.
        *read = read.consume(range.length);
        let (range, _) = read.until_any_or_whitespace(SEPS);
        let val = read.raw_string(range.length);
        if let Some((rgb, a)) = rgb_maybe_a(&mut val.chars()) {
            let v = [rgb[0] as f32 / 255.0, rgb[1] as f32 / 255.0, rgb[2] as f32 / 255.0,
                     a.unwrap_or(255) as f32 / 255.0];
            return Ok(Variable::Vec4(v));
        } else {
            return Err("Expected hex color".into());
        }
    }
    if let Some(range) = read.tag("link") {
        // Link.
        *read = read.consume(range.length);
        return link(read);
    }
    // Text.
    if let Some(range) = read.string() {
        match read.parse_string(range.length) {
            Ok(s) => {
                *read = read.consume(range.length);
                return Ok(Variable::Text(Arc::new(s)));
            }
            Err(err_range) => return Err(format!("{}", err_range.data)),
        }
    }
    // Number.
    if let Some(range) = read.number(&NUMBER_SETTINGS) {
        match read.parse_number(&NUMBER_SETTINGS, range.length) {
            Ok(val) => {
                *read = read.consume(range.length);
                return Ok(Variable::f64(val));
            }
            Err(err) => return Err(format!("{}", err)),
        }
    }
    // Boolean.
    if let Some(range) = read.tag("false") {
        *read = read.consume(range.length);
        return Ok(Variable::bool(false));
    }
    if let Some(range) = read.tag("true") {
        *read = read.consume(range.length);
        return Ok(Variable::bool(true));
    }
    Err("Not implemented".into())
}

fn object(read: &mut ReadToken) -> Result<Variable, String> {
    use std::sync::Arc;
    use std::collections::HashMap;

    let mut res: HashMap<Arc<String>, Variable> = HashMap::new();
    let mut was_comma = false;
    loop {
        opt_w(read);

        if let Some(range) = read.tag("}") {
            *read = read.consume(range.length);
            break;
        }

        if res.len() > 0 && !was_comma {
            return Err("Expected `,`".into());
        }

        let (range, _) = read.until_any_or_whitespace(SEPS);
        let key: Arc<String>;
        if range.length == 0 {
            return Err("Expected key".into());
        } else {
            key = Arc::new(read.raw_string(range.length));
            *read = read.consume(range.length);
        };

        opt_w(read);

        if let Some(range) = read.tag(":") {
            *read = read.consume(range.length);
        } else {
            return Err("Expected `:`".into());
        }

        opt_w(read);

        res.insert(key, try!(expr(read)));

        was_comma = comma(read);
    }
    Ok(Variable::Object(Arc::new(res)))
}

fn array(read: &mut ReadToken) -> Result<Variable, String> {
    use std::sync::Arc;

    let mut res = vec![];
    let mut was_comma = false;
    loop {
        opt_w(read);

        if let Some(range) = read.tag("]") {
            *read = read.consume(range.length);
            break;
        }

        if res.len() > 0 && !was_comma {
            return Err("Expected `,`".into());
        }

        res.push(try!(expr(read)));
        was_comma = comma(read);
    }
    Ok(Variable::Array(Arc::new(res)))
}

fn link(read: &mut ReadToken) -> Result<Variable, String> {
    use Link;

    opt_w(read);

    if let Some(range) = read.tag("{") {
        *read = read.consume(range.length);
    } else {
        return Err("Expected `{`".into());
    }

    let mut link = Link::new();

    opt_w(read);

    loop {
        opt_w(read);

        if let Some(range) = read.tag("}") {
            *read = read.consume(range.length);
            break;
        }

        match link.push(&try!(expr(read))) {
            Ok(()) => {}
            Err(err) => return Err(err),
        };
    }
    Ok(Variable::Link(Box::new(link)))
}

fn vec4(read: &mut ReadToken) -> Result<Variable, String> {
    let x = if let Some(range) = read.number(&NUMBER_SETTINGS) {
        match read.parse_number(&NUMBER_SETTINGS, range.length) {
            Ok(x) => {
                *read = read.consume(range.length);
                x
            }
            Err(err) => return Err(format!("{}", err)),
        }
    } else {
        return Err("Expected x component".into());
    };
    comma(read);
    let y = if let Some(range) = read.number(&NUMBER_SETTINGS) {
        match read.parse_number(&NUMBER_SETTINGS, range.length) {
            Ok(y) => {
                *read = read.consume(range.length);
                y
            }
            Err(err) => return Err(format!("{}", err)),
        }
    } else {
        return Err("Expected y component".into());
    };
    let (z, w) = if comma(read) {
        if let Some(range) = read.number(&NUMBER_SETTINGS) {
            match read.parse_number(&NUMBER_SETTINGS, range.length) {
                Ok(z) => {
                    *read = read.consume(range.length);
                    comma(read);
                    if let Some(range) = read.number(&NUMBER_SETTINGS) {
                        match read.parse_number(&NUMBER_SETTINGS, range.length) {
                            Ok(w) => (z, w),
                            Err(err) => return Err(format!("{}", err)),
                        }
                    } else { (z, 0.0) }
                }
                Err(err) => return Err(format!("{}", err)),
            }
        } else { (0.0, 0.0) }
    } else { (0.0, 0.0) };
    opt_w(read);
    if let Some(range) = read.tag(")") {
        *read = read.consume(range.length);
    }
    Ok(Variable::Vec4([x as f32, y as f32, z as f32, w as f32]))
}

/// Reads optional whitespace including comments.
fn opt_w(read: &mut ReadToken) {
    let range = read.whitespace();
    if range.length > 0 {
        *read = read.consume(range.length);
    }
}

/// Reads comma.
fn comma(read: &mut ReadToken) -> bool {
    let mut res = false;
    opt_w(read);
    if let Some(range) = read.tag(",") {
        *read = read.consume(range.length);
        res = true;
    }
    opt_w(read);
    res
}
<commit_msg>Reuse strings from `HashSet`<commit_after>use std::fs::File;
use std::io::Read;
use std::collections::HashSet;
use std::sync::Arc;

use read_token::{NumberSettings, ReadToken};

use super::io::io_error;

use Variable;

type Strings = HashSet<Arc<String>>;

/// Loads data from a file.
pub fn load_file(file: &str) -> Result<Variable, String> {
    let mut data_file = try!(File::open(file).map_err(|err| io_error("open", file, &err)));
    let mut d = String::new();
    try!(data_file.read_to_string(&mut d).map_err(|err| io_error("read", file, &err)));
    load_data(&d)
}

/// Loads data from text.
pub fn load_data(data: &str) -> Result<Variable, String> {
    let mut read = ReadToken::new(data, 0);
    let mut strings: Strings = HashSet::new();
    expr(&mut read, &mut strings)
}

static NUMBER_SETTINGS: NumberSettings = NumberSettings {
    allow_underscore: true,
};

const SEPS: &'static str = &"(){}[],.:;\n\"\\";

fn expr(
    read: &mut ReadToken,
    strings: &mut Strings
) -> Result<Variable, String> {
    if let Some(range) = read.tag("{") {
        // Object.
        *read = read.consume(range.length);
        return object(read, strings);
    }
    if let Some(range) = read.tag("[") {
        // Array.
        *read = read.consume(range.length);
        return array(read, strings);
    }
    if let Some(range) = read.tag("(") {
        // Vec4.
        *read = read.consume(range.length);
        return vec4(read);
    }
    if let Some(range) = read.tag("#") {
        use read_color::rgb_maybe_a;

        // Color.
        *read = read.consume(range.length);
        let (range, _) = read.until_any_or_whitespace(SEPS);
        let val = read.raw_string(range.length);
        if let Some((rgb, a)) = rgb_maybe_a(&mut val.chars()) {
            let v = [rgb[0] as f32 / 255.0, rgb[1] as f32 / 255.0, rgb[2] as f32 / 255.0,
                     a.unwrap_or(255) as f32 / 255.0];
            return Ok(Variable::Vec4(v));
        } else {
            return Err("Expected hex color".into());
        }
    }
    if let Some(range) = read.tag("link") {
        // Link.
        *read = read.consume(range.length);
        return link(read, strings);
    }
    // Text.
    if let Some(range) = read.string() {
        match read.parse_string(range.length) {
            Ok(s) => {
                *read = read.consume(range.length);
                return Ok(Variable::Text(
                    if let Some(s) = strings.get(&s) {
                        s.clone()
                    } else {
                        Arc::new(s)
                    }
                ));
            }
            Err(err_range) => return Err(format!("{}", err_range.data)),
        }
    }
    // Number.
    if let Some(range) = read.number(&NUMBER_SETTINGS) {
        match read.parse_number(&NUMBER_SETTINGS, range.length) {
            Ok(val) => {
                *read = read.consume(range.length);
                return Ok(Variable::f64(val));
            }
            Err(err) => return Err(format!("{}", err)),
        }
    }
    // Boolean.
    if let Some(range) = read.tag("false") {
        *read = read.consume(range.length);
        return Ok(Variable::bool(false));
    }
    if let Some(range) = read.tag("true") {
        *read = read.consume(range.length);
        return Ok(Variable::bool(true));
    }
    Err("Not implemented".into())
}

fn object(
    read: &mut ReadToken,
    strings: &mut Strings
) -> Result<Variable, String> {
    use std::collections::HashMap;

    let mut res: HashMap<Arc<String>, Variable> = HashMap::new();
    let mut was_comma = false;
    loop {
        opt_w(read);

        if let Some(range) = read.tag("}") {
            *read = read.consume(range.length);
            break;
        }

        if res.len() > 0 && !was_comma {
            return Err("Expected `,`".into());
        }

        let (range, _) = read.until_any_or_whitespace(SEPS);
        let key: Arc<String>;
        if range.length == 0 {
            return Err("Expected key".into());
        } else {
            let k = read.raw_string(range.length);
            // Use reference to existing string to reduce memory.
            key = if let Some(s) = strings.get(&k) {
                s.clone()
            } else {
                Arc::new(k)
            };
            *read = read.consume(range.length);
        };

        opt_w(read);

        if let Some(range) = read.tag(":") {
            *read = read.consume(range.length);
        } else {
            return Err("Expected `:`".into());
        }

        opt_w(read);

        res.insert(key, try!(expr(read, strings)));

        was_comma = comma(read);
    }
    Ok(Variable::Object(Arc::new(res)))
}

fn array(
    read: &mut ReadToken,
    strings: &mut Strings
) -> Result<Variable, String> {
    use std::sync::Arc;

    let mut res = vec![];
    let mut was_comma = false;
    loop {
        opt_w(read);

        if let Some(range) = read.tag("]") {
            *read = read.consume(range.length);
            break;
        }

        if res.len() > 0 && !was_comma {
            return Err("Expected `,`".into());
        }

        res.push(try!(expr(read, strings)));
        was_comma = comma(read);
    }
    Ok(Variable::Array(Arc::new(res)))
}

fn link(
    read: &mut ReadToken,
    strings: &mut Strings
) -> Result<Variable, String> {
    use Link;

    opt_w(read);

    if let Some(range) = read.tag("{") {
        *read = read.consume(range.length);
    } else {
        return Err("Expected `{`".into());
    }

    let mut link = Link::new();

    opt_w(read);

    loop {
        opt_w(read);

        if let Some(range) = read.tag("}") {
            *read = read.consume(range.length);
            break;
        }

        match link.push(&try!(expr(read, strings))) {
            Ok(()) => {}
            Err(err) => return Err(err),
        };
    }
    Ok(Variable::Link(Box::new(link)))
}

fn vec4(read: &mut ReadToken) -> Result<Variable, String> {
    let x = if let Some(range) = read.number(&NUMBER_SETTINGS) {
        match read.parse_number(&NUMBER_SETTINGS, range.length) {
            Ok(x) => {
                *read = read.consume(range.length);
                x
            }
            Err(err) => return Err(format!("{}", err)),
        }
    } else {
        return Err("Expected x component".into());
    };
    comma(read);
    let y = if let Some(range) = read.number(&NUMBER_SETTINGS) {
        match read.parse_number(&NUMBER_SETTINGS, range.length) {
            Ok(y) => {
                *read = read.consume(range.length);
                y
            }
            Err(err) => return Err(format!("{}", err)),
        }
    } else {
        return Err("Expected y component".into());
    };
    let (z, w) = if comma(read) {
        if let Some(range) = read.number(&NUMBER_SETTINGS) {
            match read.parse_number(&NUMBER_SETTINGS, range.length) {
                Ok(z) => {
                    *read = read.consume(range.length);
                    comma(read);
                    if let Some(range) = read.number(&NUMBER_SETTINGS) {
                        match read.parse_number(&NUMBER_SETTINGS, range.length) {
                            Ok(w) => (z, w),
                            Err(err) => return Err(format!("{}", err)),
                        }
                    } else { (z, 0.0) }
                }
                Err(err) => return Err(format!("{}", err)),
            }
        } else { (0.0, 0.0) }
    } else { (0.0, 0.0) };
    opt_w(read);
    if let Some(range) = read.tag(")") {
        *read = read.consume(range.length);
    }
    Ok(Variable::Vec4([x as f32, y as f32, z as f32, w as f32]))
}

/// Reads optional whitespace including comments.
fn opt_w(read: &mut ReadToken) {
    let range = read.whitespace();
    if range.length > 0 {
        *read = read.consume(range.length);
    }
}

/// Reads comma.
fn comma(read: &mut ReadToken) -> bool {
    let mut res = false;
    opt_w(read);
    if let Some(range) = read.tag(",") {
        *read = read.consume(range.length);
        res = true;
    }
    opt_w(read);
    res
}
<|endoftext|>


<commit_before>// Copyright 2014 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.

//! Traits for conversions between types.
//!
//! The traits in this module provide a general way to talk about conversions from one type to
//! another. They follow the standard Rust conventions of `as`/`into`/`from`.
//!
//! Like many traits, these are often used as bounds for generic functions, to support arguments of
//! multiple types.
//!
//! See each trait for usage examples.

#![stable(feature = "rust1", since = "1.0.0")]

use marker::Sized;

/// A cheap, reference-to-reference conversion.
///
/// # Examples
///
/// Both `String` and `&str` implement `AsRef<str>`:
///
/// ```
/// fn is_hello<T: AsRef<str>>(s: T) {
///    assert_eq!("hello", s.as_ref());
/// }
///
/// let s = "hello";
/// is_hello(s);
///
/// let s = "hello".to_string();
/// is_hello(s);
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub trait AsRef<T: ?Sized> {
    /// Performs the conversion.
    #[stable(feature = "rust1", since = "1.0.0")]
    fn as_ref(&self) -> &T;
}

/// A cheap, mutable reference-to-mutable reference conversion.
#[stable(feature = "rust1", since = "1.0.0")]
pub trait AsMut<T: ?Sized> {
    /// Performs the conversion.
    #[stable(feature = "rust1", since = "1.0.0")]
    fn as_mut(&mut self) -> &mut T;
}

/// A conversion that consumes `self`, which may or may not be expensive.
///
/// # Examples
///
/// `String` implements `Into<Vec<u8>>`:
///
/// ```
/// fn is_hello<T: Into<Vec<u8>>>(s: T) {
///    let bytes = b"hello".to_vec();
///    assert_eq!(bytes, s.into());
/// }
///
/// let s = "hello".to_string();
/// is_hello(s);
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub trait Into<T>: Sized {
    /// Performs the conversion.
    #[stable(feature = "rust1", since = "1.0.0")]
    fn into(self) -> T;
}

/// Construct `Self` via a conversion.
///
/// # Examples
///
/// `String` implements `From<&str>`:
///
/// ```
/// let s = "hello";
/// let string = "hello".to_string();
///
/// let other_string: String = From::from(s);
///
/// assert_eq!(string, other_string);
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub trait From<T> {
    /// Performs the conversion.
    #[stable(feature = "rust1", since = "1.0.0")]
    fn from(T) -> Self;
}

////////////////////////////////////////////////////////////////////////////////
// GENERIC IMPLS
////////////////////////////////////////////////////////////////////////////////

// As lifts over &
#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, T: ?Sized, U: ?Sized> AsRef<U> for &'a T where T: AsRef<U> {
    fn as_ref(&self) -> &U {
        <T as AsRef<U>>::as_ref(*self)
    }
}

// As lifts over &mut
#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, T: ?Sized, U: ?Sized> AsRef<U> for &'a mut T where T: AsRef<U> {
    fn as_ref(&self) -> &U {
        <T as AsRef<U>>::as_ref(*self)
    }
}

// FIXME (#23442): replace the above impls for &/&mut with the following more general one:
// // As lifts over Deref
// impl<D: ?Sized + Deref, U: ?Sized> AsRef<U> for D where D::Target: AsRef<U> {
//     fn as_ref(&self) -> &U {
//         self.deref().as_ref()
//     }
// }

// AsMut lifts over &mut
#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, T: ?Sized, U: ?Sized> AsMut<U> for &'a mut T where T: AsMut<U> {
    fn as_mut(&mut self) -> &mut U {
        (*self).as_mut()
    }
}

// FIXME (#23442): replace the above impl for &mut with the following more general one:
// // AsMut lifts over DerefMut
// impl<D: ?Sized + Deref, U: ?Sized> AsMut<U> for D where D::Target: AsMut<U> {
//     fn as_mut(&mut self) -> &mut U {
//         self.deref_mut().as_mut()
//     }
// }

// From implies Into
#[stable(feature = "rust1", since = "1.0.0")]
impl<T, U> Into<U> for T where U: From<T> {
    fn into(self) -> U {
        U::from(self)
    }
}

// From (and thus Into) is reflexive
#[stable(feature = "rust1", since = "1.0.0")]
impl<T> From<T> for T {
    fn from(t: T) -> T { t }
}

////////////////////////////////////////////////////////////////////////////////
// CONCRETE IMPLS
////////////////////////////////////////////////////////////////////////////////

#[stable(feature = "rust1", since = "1.0.0")]
impl<T> AsRef<[T]> for [T] {
    fn as_ref(&self) -> &[T] {
        self
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<T> AsMut<[T]> for [T] {
    fn as_mut(&mut self) -> &mut [T] {
        self
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl AsRef<str> for str {
    fn as_ref(&self) -> &str {
        self
    }
}
<commit_msg>Make From::from example more idiomatic / simpler<commit_after>// Copyright 2014 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.

//! Traits for conversions between types.
//!
//! The traits in this module provide a general way to talk about conversions from one type to
//! another. They follow the standard Rust conventions of `as`/`into`/`from`.
//!
//! Like many traits, these are often used as bounds for generic functions, to support arguments of
//! multiple types.
//!
//! See each trait for usage examples.

#![stable(feature = "rust1", since = "1.0.0")]

use marker::Sized;

/// A cheap, reference-to-reference conversion.
///
/// # Examples
///
/// Both `String` and `&str` implement `AsRef<str>`:
///
/// ```
/// fn is_hello<T: AsRef<str>>(s: T) {
///    assert_eq!("hello", s.as_ref());
/// }
///
/// let s = "hello";
/// is_hello(s);
///
/// let s = "hello".to_string();
/// is_hello(s);
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub trait AsRef<T: ?Sized> {
    /// Performs the conversion.
    #[stable(feature = "rust1", since = "1.0.0")]
    fn as_ref(&self) -> &T;
}

/// A cheap, mutable reference-to-mutable reference conversion.
#[stable(feature = "rust1", since = "1.0.0")]
pub trait AsMut<T: ?Sized> {
    /// Performs the conversion.
    #[stable(feature = "rust1", since = "1.0.0")]
    fn as_mut(&mut self) -> &mut T;
}

/// A conversion that consumes `self`, which may or may not be expensive.
///
/// # Examples
///
/// `String` implements `Into<Vec<u8>>`:
///
/// ```
/// fn is_hello<T: Into<Vec<u8>>>(s: T) {
///    let bytes = b"hello".to_vec();
///    assert_eq!(bytes, s.into());
/// }
///
/// let s = "hello".to_string();
/// is_hello(s);
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub trait Into<T>: Sized {
    /// Performs the conversion.
    #[stable(feature = "rust1", since = "1.0.0")]
    fn into(self) -> T;
}

/// Construct `Self` via a conversion.
///
/// # Examples
///
/// `String` implements `From<&str>`:
///
/// ```
/// let string = "hello".to_string();
/// let other_string = String::from("hello");
///
/// assert_eq!(string, other_string);
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub trait From<T> {
    /// Performs the conversion.
    #[stable(feature = "rust1", since = "1.0.0")]
    fn from(T) -> Self;
}

////////////////////////////////////////////////////////////////////////////////
// GENERIC IMPLS
////////////////////////////////////////////////////////////////////////////////

// As lifts over &
#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, T: ?Sized, U: ?Sized> AsRef<U> for &'a T where T: AsRef<U> {
    fn as_ref(&self) -> &U {
        <T as AsRef<U>>::as_ref(*self)
    }
}

// As lifts over &mut
#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, T: ?Sized, U: ?Sized> AsRef<U> for &'a mut T where T: AsRef<U> {
    fn as_ref(&self) -> &U {
        <T as AsRef<U>>::as_ref(*self)
    }
}

// FIXME (#23442): replace the above impls for &/&mut with the following more general one:
// // As lifts over Deref
// impl<D: ?Sized + Deref, U: ?Sized> AsRef<U> for D where D::Target: AsRef<U> {
//     fn as_ref(&self) -> &U {
//         self.deref().as_ref()
//     }
// }

// AsMut lifts over &mut
#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, T: ?Sized, U: ?Sized> AsMut<U> for &'a mut T where T: AsMut<U> {
    fn as_mut(&mut self) -> &mut U {
        (*self).as_mut()
    }
}

// FIXME (#23442): replace the above impl for &mut with the following more general one:
// // AsMut lifts over DerefMut
// impl<D: ?Sized + Deref, U: ?Sized> AsMut<U> for D where D::Target: AsMut<U> {
//     fn as_mut(&mut self) -> &mut U {
//         self.deref_mut().as_mut()
//     }
// }

// From implies Into
#[stable(feature = "rust1", since = "1.0.0")]
impl<T, U> Into<U> for T where U: From<T> {
    fn into(self) -> U {
        U::from(self)
    }
}

// From (and thus Into) is reflexive
#[stable(feature = "rust1", since = "1.0.0")]
impl<T> From<T> for T {
    fn from(t: T) -> T { t }
}

////////////////////////////////////////////////////////////////////////////////
// CONCRETE IMPLS
////////////////////////////////////////////////////////////////////////////////

#[stable(feature = "rust1", since = "1.0.0")]
impl<T> AsRef<[T]> for [T] {
    fn as_ref(&self) -> &[T] {
        self
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<T> AsMut<[T]> for [T] {
    fn as_mut(&mut self) -> &mut [T] {
        self
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl AsRef<str> for str {
    fn as_ref(&self) -> &str {
        self
    }
}
<|endoftext|>


<commit_before>//
//  SOS: the Stupid Operating System
//  by Eliza Weisman (eliza@elizas.website)
//
//  Copyright (c) 2015-2017 Eliza Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! 64-bit IDT gate implementation
use ::segment;
use super::{GateFlags};
use super::super::{InterruptHandler, ErrorCodeHandler};

use core::{convert, mem};
use core::marker::PhantomData;

impl GateFlags {

    /// Returns a new trap gate
    pub const fn new_trap() -> Self {
        GateFlags { bits: super::TRAP_GATE_32.bits | super::PRESENT.bits }
    }

    /// Returns a new call gate
    pub const fn new_task() -> Self {
        GateFlags { bits: super::TASK_GATE_32.bits | super::PRESENT.bits }
    }

    /// Returns a new interrupt gate
    pub const fn new_interrupt() -> Self {
        GateFlags { bits: super::INT_GATE_32.bits | super::PRESENT.bits }
    }

}

/// An IDT entry is called a gate.
///
/// Based on [code](http://wiki.osdev.org/Interrupt_Descriptor_Table#Structure)
/// from the OS Dev Wiki.
///
/// Refer also to "6.14.1 64-Bit Mode IDT"  and "Table 3-2. System-Segment and
/// Gate-Descriptor Types" in the _Intel® 64 and IA-32 Architectures
/// Software Developer’s Manual_
#[repr(C, packed)]
#[derive(Copy, Clone)]
pub struct Gate<H = InterruptHandler>
    { /// bits 0 - 15 of the offset
      pub offset_lower: u16
    , /// code segment selector (GDT or LDT)
      pub selector: segment::Selector
    , /// always zero
      _zero: u8
    , /// indicates the gate's type and attributes.
      /// the second half indicates the type:
      ///   + `0b1100`: Call gate
      ///   + `0b1110`: Interrupt gate
      ///   + `0b1111`: Trap Gate
      pub flags: GateFlags
    , /// bits 16 - 31 of the offset
      pub offset_mid: u16
    , /// bits 32 - 63 of the offset
      pub offset_upper: u32
    , /// always zero (according to the spec, this is "reserved")
      _reserved: u32
    , _handler_type: PhantomData<H>
    }

impl<H> Gate<H> {

    /// Creates a new IDT gate marked as `absent`.
    ///
    /// This is basically just for filling the new IDT table
    /// with valid (but useless) gates upon init.
    ///
    /// Actually triggering an absent interrupt will send a General Protection
    /// fault (13).
    pub const fn absent() -> Self {
       Gate { offset_lower: 0
            , selector: segment::Selector::from_raw(0)
            , _zero: 0
            , flags: GateFlags { bits: 0 }
            , offset_mid: 0
            , offset_upper: 0
            , _reserved: 0
            , _handler_type: PhantomData
            }
    }

    /// Set the handler function corresponding to this gate.
    #[inline]
    pub fn set_handler<F>(&mut self, handler: F) -> &mut Self
    where Self: convert::From<F> {
        *self = Self::from(handler);
        self
    }

}

impl<H> Default for Gate<H> {
    fn default() -> Self {
        Gate { offset_lower: 0
             , selector: segment::Selector::from_raw(0)
             , _zero: 0
             , flags: GateFlags { bits: 0 }
             , offset_mid: 0
             , offset_upper: 0
             , _reserved: 0
             , _handler_type: PhantomData
             }
    }
}

impl convert::From<InterruptHandler> for Gate<InterruptHandler> {

    /// Creates a new IDT gate pointing at the given handler function.
    ///
    /// The `handler` function must have been created with valid interrupt
    /// calling conventions.
    fn from(handler: InterruptHandler) -> Self {
        unsafe { // trust me on this, `mem::transmute()` is glorious black magic
                let (low, mid, high): (u16, u16, u32) = mem::transmute(handler);

            Gate { offset_lower: low
                 , offset_mid: mid
                 , offset_upper: high
                 , selector: segment::Selector::from_cs()
                 , flags: GateFlags::new_interrupt()
                 , ..Default::default()
                 }
        }
    }
}

impl convert::From<ErrorCodeHandler> for Gate<ErrorCodeHandler> {

    /// Creates a new IDT gate pointing at the given handler function.
    ///
    /// The `handler` function must have been created with valid interrupt
    /// calling conventions.
    fn from(handler: ErrorCodeHandler) -> Self {
        unsafe { // trust me on this, `mem::transmute()` is glorious black magic
                let (low, mid, high): (u16, u16, u32) = mem::transmute(handler);

            Gate { offset_lower: low
                 , offset_mid: mid
                 , offset_upper: high
                 , selector: segment::Selector::from_cs()
                 , flags: GateFlags::new_interrupt()
                 , ..Default::default()
                 }
        }
    }
}

impl convert::From<*const u8> for Gate {

    /// Creates a new IDT gate pointing at the given handler function.
    ///
    /// The `handler` function must have been created with valid interrupt
    /// calling conventions.
    ///
    /// This should probably not be used, if it can possibly be avoided.
    //  TODO: it would be really nice if we didn't need this any more.
    //        after the Revolution, once handlers are created in Rust-land with
    //        naked functions...
    fn from(handler: *const u8) -> Self {
        unsafe {
            let (low, mid, high): (u16, u16, u32) = mem::transmute(handler);

            Gate { offset_lower: low
                 , offset_mid: mid
                 , offset_upper: high
                 , selector: segment::Selector::from_cs()
                 , flags: GateFlags::new_interrupt()
                 , ..Default::default()
                 }
        }
    }
}
<commit_msg>feat(gate): interrupt gates dereference to flags field<commit_after>//
//  SOS: the Stupid Operating System
//  by Eliza Weisman (eliza@elizas.website)
//
//  Copyright (c) 2015-2017 Eliza Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! 64-bit IDT gate implementation
use ::segment;
use super::{GateFlags};
use super::super::{InterruptHandler, ErrorCodeHandler};

use core::{convert, mem, ops};
use core::marker::PhantomData;

impl GateFlags {

    /// Returns a new trap gate
    pub const fn new_trap() -> Self {
        GateFlags { bits: super::TRAP_GATE_32.bits | super::PRESENT.bits }
    }

    /// Returns a new call gate
    pub const fn new_task() -> Self {
        GateFlags { bits: super::TASK_GATE_32.bits | super::PRESENT.bits }
    }

    /// Returns a new interrupt gate
    pub const fn new_interrupt() -> Self {
        GateFlags { bits: super::INT_GATE_32.bits | super::PRESENT.bits }
    }

}

/// An IDT entry is called a gate.
///
/// Based on [code](http://wiki.osdev.org/Interrupt_Descriptor_Table#Structure)
/// from the OS Dev Wiki.
///
/// Refer also to "6.14.1 64-Bit Mode IDT"  and "Table 3-2. System-Segment and
/// Gate-Descriptor Types" in the _Intel® 64 and IA-32 Architectures
/// Software Developer’s Manual_
#[repr(C, packed)]
#[derive(Copy, Clone)]
pub struct Gate<H = InterruptHandler>
    { /// bits 0 - 15 of the offset
      pub offset_lower: u16
    , /// code segment selector (GDT or LDT)
      pub selector: segment::Selector
    , /// always zero
      _zero: u8
    , /// indicates the gate's type and attributes.
      /// the second half indicates the type:
      ///   + `0b1100`: Call gate
      ///   + `0b1110`: Interrupt gate
      ///   + `0b1111`: Trap Gate
      pub flags: GateFlags
    , /// bits 16 - 31 of the offset
      pub offset_mid: u16
    , /// bits 32 - 63 of the offset
      pub offset_upper: u32
    , /// always zero (according to the spec, this is "reserved")
      _reserved: u32
    , _handler_type: PhantomData<H>
    }

impl<H> Gate<H> {

    /// Creates a new IDT gate marked as `absent`.
    ///
    /// This is basically just for filling the new IDT table
    /// with valid (but useless) gates upon init.
    ///
    /// Actually triggering an absent interrupt will send a General Protection
    /// fault (13).
    pub const fn absent() -> Self {
       Gate { offset_lower: 0
            , selector: segment::Selector::from_raw(0)
            , _zero: 0
            , flags: GateFlags { bits: 0 }
            , offset_mid: 0
            , offset_upper: 0
            , _reserved: 0
            , _handler_type: PhantomData
            }
    }

    /// Set the handler function corresponding to this gate.
    #[inline]
    pub fn set_handler<F>(&mut self, handler: F) -> &mut Self
    where Self: convert::From<F> {
        *self = Self::from(handler);
        self
    }

}

impl<H> Default for Gate<H> {
    fn default() -> Self {
        Gate { offset_lower: 0
             , selector: segment::Selector::from_raw(0)
             , _zero: 0
             , flags: GateFlags { bits: 0 }
             , offset_mid: 0
             , offset_upper: 0
             , _reserved: 0
             , _handler_type: PhantomData
             }
    }
}

impl<H> ops::Deref for Gate<H> {
    type Target = GateFlags;

    #[inline] fn deref(&self) -> &Self::Target { &self.flags }
}

impl<H> ops::DerefMut for Gate<H> {
    #[inline] fn deref_mut(&mut self) -> &mut Self::Target { &mut self.flags }
}

impl convert::From<InterruptHandler> for Gate<InterruptHandler> {

    /// Creates a new IDT gate pointing at the given handler function.
    ///
    /// The `handler` function must have been created with valid interrupt
    /// calling conventions.
    fn from(handler: InterruptHandler) -> Self {
        unsafe { // trust me on this, `mem::transmute()` is glorious black magic
                let (low, mid, high): (u16, u16, u32) = mem::transmute(handler);

            Gate { offset_lower: low
                 , offset_mid: mid
                 , offset_upper: high
                 , selector: segment::Selector::from_cs()
                 , flags: GateFlags::new_interrupt()
                 , ..Default::default()
                 }
        }
    }
}

impl convert::From<ErrorCodeHandler> for Gate<ErrorCodeHandler> {

    /// Creates a new IDT gate pointing at the given handler function.
    ///
    /// The `handler` function must have been created with valid interrupt
    /// calling conventions.
    fn from(handler: ErrorCodeHandler) -> Self {
        unsafe { // trust me on this, `mem::transmute()` is glorious black magic
                let (low, mid, high): (u16, u16, u32) = mem::transmute(handler);

            Gate { offset_lower: low
                 , offset_mid: mid
                 , offset_upper: high
                 , selector: segment::Selector::from_cs()
                 , flags: GateFlags::new_interrupt()
                 , ..Default::default()
                 }
        }
    }
}

impl convert::From<*const u8> for Gate {

    /// Creates a new IDT gate pointing at the given handler function.
    ///
    /// The `handler` function must have been created with valid interrupt
    /// calling conventions.
    ///
    /// This should probably not be used, if it can possibly be avoided.
    //  TODO: it would be really nice if we didn't need this any more.
    //        after the Revolution, once handlers are created in Rust-land with
    //        naked functions...
    fn from(handler: *const u8) -> Self {
        unsafe {
            let (low, mid, high): (u16, u16, u32) = mem::transmute(handler);

            Gate { offset_lower: low
                 , offset_mid: mid
                 , offset_upper: high
                 , selector: segment::Selector::from_cs()
                 , flags: GateFlags::new_interrupt()
                 , ..Default::default()
                 }
        }
    }
}
<|endoftext|>


<commit_before>use std::collections::BTreeMap;

use js_int::UInt;
use ruma_common::MilliSecondsSinceUnixEpoch;
use ruma_events::{pdu::EventHash, EventType};
use ruma_identifiers::{EventId, RoomId, ServerName, ServerSigningKeyId, UserId};
use serde_json::value::Value as JsonValue;

/// Abstraction of a PDU so users can have their own PDU types.
pub trait Event {
    /// The `EventId` of this event.
    fn event_id(&self) -> &EventId;

    /// The `RoomId` of this event.
    fn room_id(&self) -> &RoomId;

    /// The `UserId` of this event.
    fn sender(&self) -> &UserId;

    /// The time of creation on the originating server.
    fn origin_server_ts(&self) -> MilliSecondsSinceUnixEpoch;

    /// The kind of event.
    fn event_type(&self) -> &EventType;

    /// The `UserId` of this PDU.
    fn content(&self) -> serde_json::Value;

    /// The state key for this event.
    fn state_key(&self) -> Option<&str>;

    /// The events before this event.
    // Requires GATs to avoid boxing (and TAIT for making it convenient).
    fn prev_events(&self) -> Box<dyn DoubleEndedIterator<Item = &EventId> + '_>;

    /// The maximum number of `prev_events` plus 1.
    ///
    /// This is only used in state resolution version 1.
    fn depth(&self) -> &UInt;

    /// All the authenticating events for this event.
    // Requires GATs to avoid boxing (and TAIT for making it convenient).
    fn auth_events(&self) -> Box<dyn DoubleEndedIterator<Item = &EventId> + '_>;

    /// If this event is a redaction event this is the event it redacts.
    fn redacts(&self) -> Option<&EventId>;

    /// The `unsigned` content of this event.
    fn unsigned(&self) -> &BTreeMap<String, JsonValue>;

    /// The content hash of this PDU.
    fn hashes(&self) -> &EventHash;

    /// A map of server names to another map consisting of the signing key id and finally the
    /// signature.
    fn signatures(&self) -> BTreeMap<Box<ServerName>, BTreeMap<ServerSigningKeyId, String>>;
}
<commit_msg>state-res: Fix some docs<commit_after>use std::collections::BTreeMap;

use js_int::UInt;
use ruma_common::MilliSecondsSinceUnixEpoch;
use ruma_events::{pdu::EventHash, EventType};
use ruma_identifiers::{EventId, RoomId, ServerName, ServerSigningKeyId, UserId};
use serde_json::value::Value as JsonValue;

/// Abstraction of a PDU so users can have their own PDU types.
pub trait Event {
    /// The `EventId` of this event.
    fn event_id(&self) -> &EventId;

    /// The `RoomId` of this event.
    fn room_id(&self) -> &RoomId;

    /// The `UserId` of this event.
    fn sender(&self) -> &UserId;

    /// The time of creation on the originating server.
    fn origin_server_ts(&self) -> MilliSecondsSinceUnixEpoch;

    /// The event type.
    fn event_type(&self) -> &EventType;

    /// The event's content.
    fn content(&self) -> serde_json::Value;

    /// The state key for this event.
    fn state_key(&self) -> Option<&str>;

    /// The events before this event.
    // Requires GATs to avoid boxing (and TAIT for making it convenient).
    fn prev_events(&self) -> Box<dyn DoubleEndedIterator<Item = &EventId> + '_>;

    /// The maximum number of `prev_events` plus 1.
    ///
    /// This is only used in state resolution version 1.
    fn depth(&self) -> &UInt;

    /// All the authenticating events for this event.
    // Requires GATs to avoid boxing (and TAIT for making it convenient).
    fn auth_events(&self) -> Box<dyn DoubleEndedIterator<Item = &EventId> + '_>;

    /// If this event is a redaction event this is the event it redacts.
    fn redacts(&self) -> Option<&EventId>;

    /// The `unsigned` content of this event.
    fn unsigned(&self) -> &BTreeMap<String, JsonValue>;

    /// The content hash of this PDU.
    fn hashes(&self) -> &EventHash;

    /// A map of server names to another map consisting of the signing key id and finally the
    /// signature.
    fn signatures(&self) -> BTreeMap<Box<ServerName>, BTreeMap<ServerSigningKeyId, String>>;
}
<|endoftext|>


<commit_before>// Copyright 2012-2014 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.
pub use self::MaybeTyped::*;

use rustc_driver::driver;
use rustc::session::{self, config};
use rustc::session::config::{Input, UnstableFeatures};
use rustc::session::search_paths::SearchPaths;
use rustc::middle::{privacy, ty};
use rustc::lint;
use rustc_trans::back::link;
use rustc_resolve as resolve;

use syntax::{ast, ast_map, codemap, diagnostic};

use std::cell::RefCell;
use std::collections::{HashMap, HashSet};

use visit_ast::RustdocVisitor;
use clean;
use clean::Clean;

/// Are we generating documentation (`Typed`) or tests (`NotTyped`)?
pub enum MaybeTyped<'tcx> {
    Typed(ty::ctxt<'tcx>),
    NotTyped(session::Session)
}

pub type ExternalPaths = RefCell<Option<HashMap<ast::DefId,
                                                (Vec<String>, clean::TypeKind)>>>;

pub struct DocContext<'tcx> {
    pub krate: &'tcx ast::Crate,
    pub maybe_typed: MaybeTyped<'tcx>,
    pub input: Input,
    pub external_paths: ExternalPaths,
    pub external_traits: RefCell<Option<HashMap<ast::DefId, clean::Trait>>>,
    pub external_typarams: RefCell<Option<HashMap<ast::DefId, String>>>,
    pub inlined: RefCell<Option<HashSet<ast::DefId>>>,
    pub populated_crate_impls: RefCell<HashSet<ast::CrateNum>>,
}

impl<'tcx> DocContext<'tcx> {
    pub fn sess<'a>(&'a self) -> &'a session::Session {
        match self.maybe_typed {
            Typed(ref tcx) => &tcx.sess,
            NotTyped(ref sess) => sess
        }
    }

    pub fn tcx_opt<'a>(&'a self) -> Option<&'a ty::ctxt<'tcx>> {
        match self.maybe_typed {
            Typed(ref tcx) => Some(tcx),
            NotTyped(_) => None
        }
    }

    pub fn tcx<'a>(&'a self) -> &'a ty::ctxt<'tcx> {
        let tcx_opt = self.tcx_opt();
        tcx_opt.expect("tcx not present")
    }
}

pub struct CrateAnalysis {
    pub exported_items: privacy::ExportedItems,
    pub public_items: privacy::PublicItems,
    pub external_paths: ExternalPaths,
    pub external_traits: RefCell<Option<HashMap<ast::DefId, clean::Trait>>>,
    pub external_typarams: RefCell<Option<HashMap<ast::DefId, String>>>,
    pub inlined: RefCell<Option<HashSet<ast::DefId>>>,
}

pub type Externs = HashMap<String, Vec<String>>;

pub fn run_core(search_paths: SearchPaths, cfgs: Vec<String>, externs: Externs,
                input: Input, triple: Option<String>)
                -> (clean::Crate, CrateAnalysis) {

    // Parse, resolve, and typecheck the given crate.

    let cpath = match input {
        Input::File(ref p) => Some(p.clone()),
        _ => None
    };

    let warning_lint = lint::builtin::WARNINGS.name_lower();

    let sessopts = config::Options {
        maybe_sysroot: None,
        search_paths: search_paths,
        crate_types: vec!(config::CrateTypeRlib),
        lint_opts: vec!((warning_lint, lint::Allow)),
        externs: externs,
        target_triple: triple.unwrap_or(config::host_triple().to_string()),
        cfg: config::parse_cfgspecs(cfgs),
        // Ensure that rustdoc works even if rustc is feature-staged
        unstable_features: UnstableFeatures::Default,
        ..config::basic_options().clone()
    };

    let codemap = codemap::CodeMap::new();
    let diagnostic_handler = diagnostic::default_handler(diagnostic::Auto, None);
    let span_diagnostic_handler =
        diagnostic::mk_span_handler(diagnostic_handler, codemap);

    let sess = session::build_session_(sessopts, cpath,
                                       span_diagnostic_handler);

    let cfg = config::build_configuration(&sess);

    let krate = driver::phase_1_parse_input(&sess, cfg, &input);

    let name = link::find_crate_name(Some(&sess), krate.attrs.as_slice(),
                                     &input);

    let krate = driver::phase_2_configure_and_expand(&sess, krate, name.as_slice(), None)
                    .expect("phase_2_configure_and_expand aborted in rustdoc!");

    let mut forest = ast_map::Forest::new(krate);
    let ast_map = driver::assign_node_ids_and_map(&sess, &mut forest);

    let arenas = ty::CtxtArenas::new();
    let ty::CrateAnalysis {
        exported_items, public_items, ty_cx, ..
    } = driver::phase_3_run_analysis_passes(sess,
                                            ast_map,
                                            &arenas,
                                            name,
                                            resolve::MakeGlobMap::No);

    let ctxt = DocContext {
        krate: ty_cx.map.krate(),
        maybe_typed: Typed(ty_cx),
        input: input,
        external_traits: RefCell::new(Some(HashMap::new())),
        external_typarams: RefCell::new(Some(HashMap::new())),
        external_paths: RefCell::new(Some(HashMap::new())),
        inlined: RefCell::new(Some(HashSet::new())),
        populated_crate_impls: RefCell::new(HashSet::new()),
    };
    debug!("crate: {:?}", ctxt.krate);

    let analysis = CrateAnalysis {
        exported_items: exported_items,
        public_items: public_items,
        external_paths: RefCell::new(None),
        external_traits: RefCell::new(None),
        external_typarams: RefCell::new(None),
        inlined: RefCell::new(None),
    };

    let krate = {
        let mut v = RustdocVisitor::new(&ctxt, Some(&analysis));
        v.visit(ctxt.krate);
        v.clean(&ctxt)
    };

    let external_paths = ctxt.external_paths.borrow_mut().take();
    *analysis.external_paths.borrow_mut() = external_paths;
    let map = ctxt.external_traits.borrow_mut().take();
    *analysis.external_traits.borrow_mut() = map;
    let map = ctxt.external_typarams.borrow_mut().take();
    *analysis.external_typarams.borrow_mut() = map;
    let map = ctxt.inlined.borrow_mut().take();
    *analysis.inlined.borrow_mut() = map;
    (krate, analysis)
}
<commit_msg>rustdoc: Add some re-exports<commit_after>// Copyright 2012-2014 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.
pub use self::MaybeTyped::*;

use rustc_driver::driver;
use rustc::session::{self, config};
use rustc::session::config::UnstableFeatures;
use rustc::middle::{privacy, ty};
use rustc::lint;
use rustc_trans::back::link;
use rustc_resolve as resolve;

use syntax::{ast, ast_map, codemap, diagnostic};

use std::cell::RefCell;
use std::collections::{HashMap, HashSet};

use visit_ast::RustdocVisitor;
use clean;
use clean::Clean;

pub use rustc::session::config::Input;
pub use rustc::session::search_paths::SearchPaths;

/// Are we generating documentation (`Typed`) or tests (`NotTyped`)?
pub enum MaybeTyped<'tcx> {
    Typed(ty::ctxt<'tcx>),
    NotTyped(session::Session)
}

pub type ExternalPaths = RefCell<Option<HashMap<ast::DefId,
                                                (Vec<String>, clean::TypeKind)>>>;

pub struct DocContext<'tcx> {
    pub krate: &'tcx ast::Crate,
    pub maybe_typed: MaybeTyped<'tcx>,
    pub input: Input,
    pub external_paths: ExternalPaths,
    pub external_traits: RefCell<Option<HashMap<ast::DefId, clean::Trait>>>,
    pub external_typarams: RefCell<Option<HashMap<ast::DefId, String>>>,
    pub inlined: RefCell<Option<HashSet<ast::DefId>>>,
    pub populated_crate_impls: RefCell<HashSet<ast::CrateNum>>,
}

impl<'tcx> DocContext<'tcx> {
    pub fn sess<'a>(&'a self) -> &'a session::Session {
        match self.maybe_typed {
            Typed(ref tcx) => &tcx.sess,
            NotTyped(ref sess) => sess
        }
    }

    pub fn tcx_opt<'a>(&'a self) -> Option<&'a ty::ctxt<'tcx>> {
        match self.maybe_typed {
            Typed(ref tcx) => Some(tcx),
            NotTyped(_) => None
        }
    }

    pub fn tcx<'a>(&'a self) -> &'a ty::ctxt<'tcx> {
        let tcx_opt = self.tcx_opt();
        tcx_opt.expect("tcx not present")
    }
}

pub struct CrateAnalysis {
    pub exported_items: privacy::ExportedItems,
    pub public_items: privacy::PublicItems,
    pub external_paths: ExternalPaths,
    pub external_traits: RefCell<Option<HashMap<ast::DefId, clean::Trait>>>,
    pub external_typarams: RefCell<Option<HashMap<ast::DefId, String>>>,
    pub inlined: RefCell<Option<HashSet<ast::DefId>>>,
}

pub type Externs = HashMap<String, Vec<String>>;

pub fn run_core(search_paths: SearchPaths, cfgs: Vec<String>, externs: Externs,
                input: Input, triple: Option<String>)
                -> (clean::Crate, CrateAnalysis) {

    // Parse, resolve, and typecheck the given crate.

    let cpath = match input {
        Input::File(ref p) => Some(p.clone()),
        _ => None
    };

    let warning_lint = lint::builtin::WARNINGS.name_lower();

    let sessopts = config::Options {
        maybe_sysroot: None,
        search_paths: search_paths,
        crate_types: vec!(config::CrateTypeRlib),
        lint_opts: vec!((warning_lint, lint::Allow)),
        externs: externs,
        target_triple: triple.unwrap_or(config::host_triple().to_string()),
        cfg: config::parse_cfgspecs(cfgs),
        // Ensure that rustdoc works even if rustc is feature-staged
        unstable_features: UnstableFeatures::Default,
        ..config::basic_options().clone()
    };

    let codemap = codemap::CodeMap::new();
    let diagnostic_handler = diagnostic::default_handler(diagnostic::Auto, None);
    let span_diagnostic_handler =
        diagnostic::mk_span_handler(diagnostic_handler, codemap);

    let sess = session::build_session_(sessopts, cpath,
                                       span_diagnostic_handler);

    let cfg = config::build_configuration(&sess);

    let krate = driver::phase_1_parse_input(&sess, cfg, &input);

    let name = link::find_crate_name(Some(&sess), krate.attrs.as_slice(),
                                     &input);

    let krate = driver::phase_2_configure_and_expand(&sess, krate, name.as_slice(), None)
                    .expect("phase_2_configure_and_expand aborted in rustdoc!");

    let mut forest = ast_map::Forest::new(krate);
    let ast_map = driver::assign_node_ids_and_map(&sess, &mut forest);

    let arenas = ty::CtxtArenas::new();
    let ty::CrateAnalysis {
        exported_items, public_items, ty_cx, ..
    } = driver::phase_3_run_analysis_passes(sess,
                                            ast_map,
                                            &arenas,
                                            name,
                                            resolve::MakeGlobMap::No);

    let ctxt = DocContext {
        krate: ty_cx.map.krate(),
        maybe_typed: Typed(ty_cx),
        input: input,
        external_traits: RefCell::new(Some(HashMap::new())),
        external_typarams: RefCell::new(Some(HashMap::new())),
        external_paths: RefCell::new(Some(HashMap::new())),
        inlined: RefCell::new(Some(HashSet::new())),
        populated_crate_impls: RefCell::new(HashSet::new()),
    };
    debug!("crate: {:?}", ctxt.krate);

    let analysis = CrateAnalysis {
        exported_items: exported_items,
        public_items: public_items,
        external_paths: RefCell::new(None),
        external_traits: RefCell::new(None),
        external_typarams: RefCell::new(None),
        inlined: RefCell::new(None),
    };

    let krate = {
        let mut v = RustdocVisitor::new(&ctxt, Some(&analysis));
        v.visit(ctxt.krate);
        v.clean(&ctxt)
    };

    let external_paths = ctxt.external_paths.borrow_mut().take();
    *analysis.external_paths.borrow_mut() = external_paths;
    let map = ctxt.external_traits.borrow_mut().take();
    *analysis.external_traits.borrow_mut() = map;
    let map = ctxt.external_typarams.borrow_mut().take();
    *analysis.external_typarams.borrow_mut() = map;
    let map = ctxt.inlined.borrow_mut().take();
    *analysis.inlined.borrow_mut() = map;
    (krate, analysis)
}
<|endoftext|>


<commit_before>// Copyright 2012-2014 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 rustc_lint;
use rustc_driver::{self, driver, target_features, abort_on_err};
use rustc::session::{self, config};
use rustc::hir::def_id::{DefId, CrateNum};
use rustc::hir::def::Def;
use rustc::middle::cstore::CrateStore;
use rustc::middle::privacy::AccessLevels;
use rustc::ty::{self, TyCtxt, AllArenas};
use rustc::hir::map as hir_map;
use rustc::lint::{self, LintPass};
use rustc::session::config::ErrorOutputType;
use rustc::util::nodemap::{FxHashMap, FxHashSet};
use rustc_resolve as resolve;
use rustc_metadata::creader::CrateLoader;
use rustc_metadata::cstore::CStore;
use rustc_target::spec::TargetTriple;

use syntax::ast::NodeId;
use syntax::codemap;
use syntax::edition::Edition;
use syntax::feature_gate::UnstableFeatures;
use syntax::json::JsonEmitter;
use errors;
use errors::emitter::{Emitter, EmitterWriter};

use std::cell::{RefCell, Cell};
use std::mem;
use rustc_data_structures::sync::{self, Lrc};
use std::rc::Rc;
use std::path::PathBuf;

use visit_ast::RustdocVisitor;
use clean;
use clean::Clean;
use html::render::RenderInfo;

pub use rustc::session::config::{Input, CodegenOptions};
pub use rustc::session::search_paths::SearchPaths;

pub type ExternalPaths = FxHashMap<DefId, (Vec<String>, clean::TypeKind)>;

pub struct DocContext<'a, 'tcx: 'a, 'rcx: 'a> {
    pub tcx: TyCtxt<'a, 'tcx, 'tcx>,
    pub resolver: &'a RefCell<resolve::Resolver<'rcx>>,
    /// The stack of module NodeIds up till this point
    pub mod_ids: RefCell<Vec<NodeId>>,
    pub crate_name: Option<String>,
    pub cstore: Rc<CrateStore>,
    pub populated_all_crate_impls: Cell<bool>,
    // Note that external items for which `doc(hidden)` applies to are shown as
    // non-reachable while local items aren't. This is because we're reusing
    // the access levels from crateanalysis.
    /// Later on moved into `clean::Crate`
    pub access_levels: RefCell<AccessLevels<DefId>>,
    /// Later on moved into `html::render::CACHE_KEY`
    pub renderinfo: RefCell<RenderInfo>,
    /// Later on moved through `clean::Crate` into `html::render::CACHE_KEY`
    pub external_traits: RefCell<FxHashMap<DefId, clean::Trait>>,
    /// Used while populating `external_traits` to ensure we don't process the same trait twice at
    /// the same time.
    pub active_extern_traits: RefCell<Vec<DefId>>,
    // The current set of type and lifetime substitutions,
    // for expanding type aliases at the HIR level:

    /// Table type parameter definition -> substituted type
    pub ty_substs: RefCell<FxHashMap<Def, clean::Type>>,
    /// Table node id of lifetime parameter definition -> substituted lifetime
    pub lt_substs: RefCell<FxHashMap<DefId, clean::Lifetime>>,
    /// Table DefId of `impl Trait` in argument position -> bounds
    pub impl_trait_bounds: RefCell<FxHashMap<DefId, Vec<clean::GenericBound>>>,
    pub send_trait: Option<DefId>,
    pub fake_def_ids: RefCell<FxHashMap<CrateNum, DefId>>,
    pub all_fake_def_ids: RefCell<FxHashSet<DefId>>,
    /// Maps (type_id, trait_id) -> auto trait impl
    pub generated_synthetics: RefCell<FxHashSet<(DefId, DefId)>>
}

impl<'a, 'tcx, 'rcx> DocContext<'a, 'tcx, 'rcx> {
    pub fn sess(&self) -> &session::Session {
        &self.tcx.sess
    }

    /// Call the closure with the given parameters set as
    /// the substitutions for a type alias' RHS.
    pub fn enter_alias<F, R>(&self,
                             ty_substs: FxHashMap<Def, clean::Type>,
                             lt_substs: FxHashMap<DefId, clean::Lifetime>,
                             f: F) -> R
    where F: FnOnce() -> R {
        let (old_tys, old_lts) =
            (mem::replace(&mut *self.ty_substs.borrow_mut(), ty_substs),
             mem::replace(&mut *self.lt_substs.borrow_mut(), lt_substs));
        let r = f();
        *self.ty_substs.borrow_mut() = old_tys;
        *self.lt_substs.borrow_mut() = old_lts;
        r
    }
}

pub trait DocAccessLevels {
    fn is_doc_reachable(&self, did: DefId) -> bool;
}

impl DocAccessLevels for AccessLevels<DefId> {
    fn is_doc_reachable(&self, did: DefId) -> bool {
        self.is_public(did)
    }
}

/// Creates a new diagnostic `Handler` that can be used to emit warnings and errors.
///
/// If the given `error_format` is `ErrorOutputType::Json` and no `CodeMap` is given, a new one
/// will be created for the handler.
pub fn new_handler(error_format: ErrorOutputType, codemap: Option<Lrc<codemap::CodeMap>>)
    -> errors::Handler
{
    // rustdoc doesn't override (or allow to override) anything from this that is relevant here, so
    // stick to the defaults
    let sessopts = config::basic_options();
    let emitter: Box<dyn Emitter + sync::Send> = match error_format {
        ErrorOutputType::HumanReadable(color_config) => Box::new(
            EmitterWriter::stderr(
                color_config,
                codemap.map(|cm| cm as _),
                false,
                sessopts.debugging_opts.teach,
            ).ui_testing(sessopts.debugging_opts.ui_testing)
        ),
        ErrorOutputType::Json(pretty) => {
            let codemap = codemap.unwrap_or_else(
                || Lrc::new(codemap::CodeMap::new(sessopts.file_path_mapping())));
            Box::new(
                JsonEmitter::stderr(
                    None,
                    codemap,
                    pretty,
                ).ui_testing(sessopts.debugging_opts.ui_testing)
            )
        },
        ErrorOutputType::Short(color_config) => Box::new(
            EmitterWriter::stderr(
                color_config,
                codemap.map(|cm| cm as _),
                true,
                false)
        ),
    };

    errors::Handler::with_emitter_and_flags(
        emitter,
        errors::HandlerFlags {
            can_emit_warnings: true,
            treat_err_as_bug: false,
            external_macro_backtrace: false,
            ..Default::default()
        },
    )
}

pub fn run_core(search_paths: SearchPaths,
                cfgs: Vec<String>,
                externs: config::Externs,
                input: Input,
                triple: Option<TargetTriple>,
                maybe_sysroot: Option<PathBuf>,
                allow_warnings: bool,
                crate_name: Option<String>,
                force_unstable_if_unmarked: bool,
                edition: Edition,
                cg: CodegenOptions,
                error_format: ErrorOutputType,
                cmd_lints: Vec<(String, lint::Level)>,
                lint_cap: Option<lint::Level>,
                describe_lints: bool) -> (clean::Crate, RenderInfo)
{
    // Parse, resolve, and typecheck the given crate.

    let cpath = match input {
        Input::File(ref p) => Some(p.clone()),
        _ => None
    };

    let intra_link_resolution_failure_name = lint::builtin::INTRA_DOC_LINK_RESOLUTION_FAILURE.name;
    let warnings_lint_name = lint::builtin::WARNINGS.name;
    let missing_docs = rustc_lint::builtin::MISSING_DOCS.name;
    let lints = lint::builtin::HardwiredLints.get_lints()
                    .into_iter()
                    .chain(rustc_lint::SoftLints.get_lints().into_iter())
                    .filter_map(|lint| {
                        if lint.name == warnings_lint_name ||
                           lint.name == intra_link_resolution_failure_name {
                            None
                        } else {
                            Some((lint.name_lower(), lint::Allow))
                        }
                    })
                    .chain(cmd_lints.into_iter())
                    .collect::<Vec<_>>();

    let host_triple = TargetTriple::from_triple(config::host_triple());
    // plays with error output here!
    let sessopts = config::Options {
        maybe_sysroot,
        search_paths,
        crate_types: vec![config::CrateTypeRlib],
        lint_opts: if !allow_warnings {
            lints
        } else {
            vec![]
        },
        lint_cap: Some(lint_cap.unwrap_or_else(|| lint::Forbid)),
        cg,
        externs,
        target_triple: triple.unwrap_or(host_triple),
        // Ensure that rustdoc works even if rustc is feature-staged
        unstable_features: UnstableFeatures::Allow,
        actually_rustdoc: true,
        debugging_opts: config::DebuggingOptions {
            force_unstable_if_unmarked,
            ..config::basic_debugging_options()
        },
        error_format,
        edition,
        describe_lints,
        ..config::basic_options()
    };
    driver::spawn_thread_pool(sessopts, move |sessopts| {
        let codemap = Lrc::new(codemap::CodeMap::new(sessopts.file_path_mapping()));
        let diagnostic_handler = new_handler(error_format, Some(codemap.clone()));

        let mut sess = session::build_session_(
            sessopts, cpath, diagnostic_handler, codemap,
        );

        lint::builtin::HardwiredLints.get_lints()
                                     .into_iter()
                                     .chain(rustc_lint::SoftLints.get_lints().into_iter())
                                     .filter_map(|lint| {
                                         // We don't want to whitelist *all* lints so let's
                                         // ignore those ones.
                                         if lint.name == warnings_lint_name ||
                                            lint.name == intra_link_resolution_failure_name ||
                                            lint.name == missing_docs {
                                             None
                                         } else {
                                             Some(lint)
                                         }
                                     })
                                     .for_each(|l| {
                                         sess.driver_lint_caps.insert(lint::LintId::of(l),
                                                                      lint::Allow);
                                     });

        let codegen_backend = rustc_driver::get_codegen_backend(&sess);
        let cstore = Rc::new(CStore::new(codegen_backend.metadata_loader()));
        rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess));

        let mut cfg = config::build_configuration(&sess, config::parse_cfgspecs(cfgs));
        target_features::add_configuration(&mut cfg, &sess, &*codegen_backend);
        sess.parse_sess.config = cfg;

        let control = &driver::CompileController::basic();

        let krate = panictry!(driver::phase_1_parse_input(control, &sess, &input));

        let name = match crate_name {
            Some(ref crate_name) => crate_name.clone(),
            None => ::rustc_codegen_utils::link::find_crate_name(Some(&sess), &krate.attrs, &input),
        };

        let mut crate_loader = CrateLoader::new(&sess, &cstore, &name);

        let resolver_arenas = resolve::Resolver::arenas();
        let result = driver::phase_2_configure_and_expand_inner(&sess,
                                                        &cstore,
                                                        krate,
                                                        None,
                                                        &name,
                                                        None,
                                                        resolve::MakeGlobMap::No,
                                                        &resolver_arenas,
                                                        &mut crate_loader,
                                                        |_| Ok(()));
        let driver::InnerExpansionResult {
            mut hir_forest,
            mut resolver,
            ..
        } = abort_on_err(result, &sess);

        resolver.ignore_extern_prelude_feature = true;

        // We need to hold on to the complete resolver, so we clone everything
        // for the analysis passes to use. Suboptimal, but necessary in the
        // current architecture.
        let defs = resolver.definitions.clone();
        let resolutions = ty::Resolutions {
            freevars: resolver.freevars.clone(),
            export_map: resolver.export_map.clone(),
            trait_map: resolver.trait_map.clone(),
            maybe_unused_trait_imports: resolver.maybe_unused_trait_imports.clone(),
            maybe_unused_extern_crates: resolver.maybe_unused_extern_crates.clone(),
        };
        let analysis = ty::CrateAnalysis {
            access_levels: Lrc::new(AccessLevels::default()),
            name: name.to_string(),
            glob_map: if resolver.make_glob_map { Some(resolver.glob_map.clone()) } else { None },
        };

        let arenas = AllArenas::new();
        let hir_map = hir_map::map_crate(&sess, &*cstore, &mut hir_forest, &defs);
        let output_filenames = driver::build_output_filenames(&input,
                                                            &None,
                                                            &None,
                                                            &[],
                                                            &sess);

        let resolver = RefCell::new(resolver);
        abort_on_err(driver::phase_3_run_analysis_passes(&*codegen_backend,
                                                        control,
                                                        &sess,
                                                        &*cstore,
                                                        hir_map,
                                                        analysis,
                                                        resolutions,
                                                        &arenas,
                                                        &name,
                                                        &output_filenames,
                                                        |tcx, analysis, _, result| {
            if let Err(_) = result {
                sess.fatal("Compilation failed, aborting rustdoc");
            }

            let ty::CrateAnalysis { access_levels, .. } = analysis;

            // Convert from a NodeId set to a DefId set since we don't always have easy access
            // to the map from defid -> nodeid
            let access_levels = AccessLevels {
                map: access_levels.map.iter()
                                    .map(|(&k, &v)| (tcx.hir.local_def_id(k), v))
                                    .collect()
            };

            let send_trait = if crate_name == Some("core".to_string()) {
                clean::get_trait_def_id(&tcx, &["marker", "Send"], true)
            } else {
                clean::get_trait_def_id(&tcx, &["core", "marker", "Send"], false)
            };

            let ctxt = DocContext {
                tcx,
                resolver: &resolver,
                crate_name,
                cstore: cstore.clone(),
                populated_all_crate_impls: Cell::new(false),
                access_levels: RefCell::new(access_levels),
                external_traits: Default::default(),
                active_extern_traits: Default::default(),
                renderinfo: Default::default(),
                ty_substs: Default::default(),
                lt_substs: Default::default(),
                impl_trait_bounds: Default::default(),
                mod_ids: Default::default(),
                send_trait: send_trait,
                fake_def_ids: RefCell::new(FxHashMap()),
                all_fake_def_ids: RefCell::new(FxHashSet()),
                generated_synthetics: RefCell::new(FxHashSet()),
            };
            debug!("crate: {:?}", tcx.hir.krate());

            let krate = {
                let mut v = RustdocVisitor::new(&*cstore, &ctxt);
                v.visit(tcx.hir.krate());
                v.clean(&ctxt)
            };

            (krate, ctxt.renderinfo.into_inner())
        }), &sess)
    })
}
<commit_msg>Rollup merge of #52233 - GuillaumeGomez:rustdoc-lint-handling, r=oli-obk<commit_after>// Copyright 2012-2014 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 rustc_lint;
use rustc_driver::{self, driver, target_features, abort_on_err};
use rustc::session::{self, config};
use rustc::hir::def_id::{DefId, CrateNum};
use rustc::hir::def::Def;
use rustc::middle::cstore::CrateStore;
use rustc::middle::privacy::AccessLevels;
use rustc::ty::{self, TyCtxt, AllArenas};
use rustc::hir::map as hir_map;
use rustc::lint::{self, LintPass};
use rustc::session::config::ErrorOutputType;
use rustc::util::nodemap::{FxHashMap, FxHashSet};
use rustc_resolve as resolve;
use rustc_metadata::creader::CrateLoader;
use rustc_metadata::cstore::CStore;
use rustc_target::spec::TargetTriple;

use syntax::ast::NodeId;
use syntax::codemap;
use syntax::edition::Edition;
use syntax::feature_gate::UnstableFeatures;
use syntax::json::JsonEmitter;
use errors;
use errors::emitter::{Emitter, EmitterWriter};

use std::cell::{RefCell, Cell};
use std::mem;
use rustc_data_structures::sync::{self, Lrc};
use std::rc::Rc;
use std::path::PathBuf;

use visit_ast::RustdocVisitor;
use clean;
use clean::Clean;
use html::render::RenderInfo;

pub use rustc::session::config::{Input, CodegenOptions};
pub use rustc::session::search_paths::SearchPaths;

pub type ExternalPaths = FxHashMap<DefId, (Vec<String>, clean::TypeKind)>;

pub struct DocContext<'a, 'tcx: 'a, 'rcx: 'a> {
    pub tcx: TyCtxt<'a, 'tcx, 'tcx>,
    pub resolver: &'a RefCell<resolve::Resolver<'rcx>>,
    /// The stack of module NodeIds up till this point
    pub mod_ids: RefCell<Vec<NodeId>>,
    pub crate_name: Option<String>,
    pub cstore: Rc<CrateStore>,
    pub populated_all_crate_impls: Cell<bool>,
    // Note that external items for which `doc(hidden)` applies to are shown as
    // non-reachable while local items aren't. This is because we're reusing
    // the access levels from crateanalysis.
    /// Later on moved into `clean::Crate`
    pub access_levels: RefCell<AccessLevels<DefId>>,
    /// Later on moved into `html::render::CACHE_KEY`
    pub renderinfo: RefCell<RenderInfo>,
    /// Later on moved through `clean::Crate` into `html::render::CACHE_KEY`
    pub external_traits: RefCell<FxHashMap<DefId, clean::Trait>>,
    /// Used while populating `external_traits` to ensure we don't process the same trait twice at
    /// the same time.
    pub active_extern_traits: RefCell<Vec<DefId>>,
    // The current set of type and lifetime substitutions,
    // for expanding type aliases at the HIR level:

    /// Table type parameter definition -> substituted type
    pub ty_substs: RefCell<FxHashMap<Def, clean::Type>>,
    /// Table node id of lifetime parameter definition -> substituted lifetime
    pub lt_substs: RefCell<FxHashMap<DefId, clean::Lifetime>>,
    /// Table DefId of `impl Trait` in argument position -> bounds
    pub impl_trait_bounds: RefCell<FxHashMap<DefId, Vec<clean::GenericBound>>>,
    pub send_trait: Option<DefId>,
    pub fake_def_ids: RefCell<FxHashMap<CrateNum, DefId>>,
    pub all_fake_def_ids: RefCell<FxHashSet<DefId>>,
    /// Maps (type_id, trait_id) -> auto trait impl
    pub generated_synthetics: RefCell<FxHashSet<(DefId, DefId)>>
}

impl<'a, 'tcx, 'rcx> DocContext<'a, 'tcx, 'rcx> {
    pub fn sess(&self) -> &session::Session {
        &self.tcx.sess
    }

    /// Call the closure with the given parameters set as
    /// the substitutions for a type alias' RHS.
    pub fn enter_alias<F, R>(&self,
                             ty_substs: FxHashMap<Def, clean::Type>,
                             lt_substs: FxHashMap<DefId, clean::Lifetime>,
                             f: F) -> R
    where F: FnOnce() -> R {
        let (old_tys, old_lts) =
            (mem::replace(&mut *self.ty_substs.borrow_mut(), ty_substs),
             mem::replace(&mut *self.lt_substs.borrow_mut(), lt_substs));
        let r = f();
        *self.ty_substs.borrow_mut() = old_tys;
        *self.lt_substs.borrow_mut() = old_lts;
        r
    }
}

pub trait DocAccessLevels {
    fn is_doc_reachable(&self, did: DefId) -> bool;
}

impl DocAccessLevels for AccessLevels<DefId> {
    fn is_doc_reachable(&self, did: DefId) -> bool {
        self.is_public(did)
    }
}

/// Creates a new diagnostic `Handler` that can be used to emit warnings and errors.
///
/// If the given `error_format` is `ErrorOutputType::Json` and no `CodeMap` is given, a new one
/// will be created for the handler.
pub fn new_handler(error_format: ErrorOutputType, codemap: Option<Lrc<codemap::CodeMap>>)
    -> errors::Handler
{
    // rustdoc doesn't override (or allow to override) anything from this that is relevant here, so
    // stick to the defaults
    let sessopts = config::basic_options();
    let emitter: Box<dyn Emitter + sync::Send> = match error_format {
        ErrorOutputType::HumanReadable(color_config) => Box::new(
            EmitterWriter::stderr(
                color_config,
                codemap.map(|cm| cm as _),
                false,
                sessopts.debugging_opts.teach,
            ).ui_testing(sessopts.debugging_opts.ui_testing)
        ),
        ErrorOutputType::Json(pretty) => {
            let codemap = codemap.unwrap_or_else(
                || Lrc::new(codemap::CodeMap::new(sessopts.file_path_mapping())));
            Box::new(
                JsonEmitter::stderr(
                    None,
                    codemap,
                    pretty,
                ).ui_testing(sessopts.debugging_opts.ui_testing)
            )
        },
        ErrorOutputType::Short(color_config) => Box::new(
            EmitterWriter::stderr(
                color_config,
                codemap.map(|cm| cm as _),
                true,
                false)
        ),
    };

    errors::Handler::with_emitter_and_flags(
        emitter,
        errors::HandlerFlags {
            can_emit_warnings: true,
            treat_err_as_bug: false,
            external_macro_backtrace: false,
            ..Default::default()
        },
    )
}

pub fn run_core(search_paths: SearchPaths,
                cfgs: Vec<String>,
                externs: config::Externs,
                input: Input,
                triple: Option<TargetTriple>,
                maybe_sysroot: Option<PathBuf>,
                allow_warnings: bool,
                crate_name: Option<String>,
                force_unstable_if_unmarked: bool,
                edition: Edition,
                cg: CodegenOptions,
                error_format: ErrorOutputType,
                cmd_lints: Vec<(String, lint::Level)>,
                lint_cap: Option<lint::Level>,
                describe_lints: bool) -> (clean::Crate, RenderInfo)
{
    // Parse, resolve, and typecheck the given crate.

    let cpath = match input {
        Input::File(ref p) => Some(p.clone()),
        _ => None
    };

    let intra_link_resolution_failure_name = lint::builtin::INTRA_DOC_LINK_RESOLUTION_FAILURE.name;
    let warnings_lint_name = lint::builtin::WARNINGS.name;
    let missing_docs = rustc_lint::builtin::MISSING_DOCS.name;

    // In addition to those specific lints, we also need to whitelist those given through
    // command line, otherwise they'll get ignored and we don't want that.
    let mut whitelisted_lints = vec![warnings_lint_name.to_owned(),
                                     intra_link_resolution_failure_name.to_owned(),
                                     missing_docs.to_owned()];

    for (lint, _) in &cmd_lints {
        whitelisted_lints.push(lint.clone());
    }

    let lints = lint::builtin::HardwiredLints.get_lints()
                    .into_iter()
                    .chain(rustc_lint::SoftLints.get_lints().into_iter())
                    .filter_map(|lint| {
                        if lint.name == warnings_lint_name ||
                           lint.name == intra_link_resolution_failure_name {
                            None
                        } else {
                            Some((lint.name_lower(), lint::Allow))
                        }
                    })
                    .chain(cmd_lints.into_iter())
                    .collect::<Vec<_>>();

    let host_triple = TargetTriple::from_triple(config::host_triple());
    // plays with error output here!
    let sessopts = config::Options {
        maybe_sysroot,
        search_paths,
        crate_types: vec![config::CrateTypeRlib],
        lint_opts: if !allow_warnings {
            lints
        } else {
            vec![]
        },
        lint_cap: Some(lint_cap.unwrap_or_else(|| lint::Forbid)),
        cg,
        externs,
        target_triple: triple.unwrap_or(host_triple),
        // Ensure that rustdoc works even if rustc is feature-staged
        unstable_features: UnstableFeatures::Allow,
        actually_rustdoc: true,
        debugging_opts: config::DebuggingOptions {
            force_unstable_if_unmarked,
            ..config::basic_debugging_options()
        },
        error_format,
        edition,
        describe_lints,
        ..config::basic_options()
    };
    driver::spawn_thread_pool(sessopts, move |sessopts| {
        let codemap = Lrc::new(codemap::CodeMap::new(sessopts.file_path_mapping()));
        let diagnostic_handler = new_handler(error_format, Some(codemap.clone()));

        let mut sess = session::build_session_(
            sessopts, cpath, diagnostic_handler, codemap,
        );

        lint::builtin::HardwiredLints.get_lints()
                                     .into_iter()
                                     .chain(rustc_lint::SoftLints.get_lints().into_iter())
                                     .filter_map(|lint| {
                                         // We don't want to whitelist *all* lints so let's
                                         // ignore those ones.
                                         if whitelisted_lints.iter().any(|l| &lint.name == l) {
                                             None
                                         } else {
                                             Some(lint)
                                         }
                                     })
                                     .for_each(|l| {
                                         sess.driver_lint_caps.insert(lint::LintId::of(l),
                                                                      lint::Allow);
                                     });

        let codegen_backend = rustc_driver::get_codegen_backend(&sess);
        let cstore = Rc::new(CStore::new(codegen_backend.metadata_loader()));
        rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess));

        let mut cfg = config::build_configuration(&sess, config::parse_cfgspecs(cfgs));
        target_features::add_configuration(&mut cfg, &sess, &*codegen_backend);
        sess.parse_sess.config = cfg;

        let control = &driver::CompileController::basic();

        let krate = panictry!(driver::phase_1_parse_input(control, &sess, &input));

        let name = match crate_name {
            Some(ref crate_name) => crate_name.clone(),
            None => ::rustc_codegen_utils::link::find_crate_name(Some(&sess), &krate.attrs, &input),
        };

        let mut crate_loader = CrateLoader::new(&sess, &cstore, &name);

        let resolver_arenas = resolve::Resolver::arenas();
        let result = driver::phase_2_configure_and_expand_inner(&sess,
                                                        &cstore,
                                                        krate,
                                                        None,
                                                        &name,
                                                        None,
                                                        resolve::MakeGlobMap::No,
                                                        &resolver_arenas,
                                                        &mut crate_loader,
                                                        |_| Ok(()));
        let driver::InnerExpansionResult {
            mut hir_forest,
            mut resolver,
            ..
        } = abort_on_err(result, &sess);

        resolver.ignore_extern_prelude_feature = true;

        // We need to hold on to the complete resolver, so we clone everything
        // for the analysis passes to use. Suboptimal, but necessary in the
        // current architecture.
        let defs = resolver.definitions.clone();
        let resolutions = ty::Resolutions {
            freevars: resolver.freevars.clone(),
            export_map: resolver.export_map.clone(),
            trait_map: resolver.trait_map.clone(),
            maybe_unused_trait_imports: resolver.maybe_unused_trait_imports.clone(),
            maybe_unused_extern_crates: resolver.maybe_unused_extern_crates.clone(),
        };
        let analysis = ty::CrateAnalysis {
            access_levels: Lrc::new(AccessLevels::default()),
            name: name.to_string(),
            glob_map: if resolver.make_glob_map { Some(resolver.glob_map.clone()) } else { None },
        };

        let arenas = AllArenas::new();
        let hir_map = hir_map::map_crate(&sess, &*cstore, &mut hir_forest, &defs);
        let output_filenames = driver::build_output_filenames(&input,
                                                            &None,
                                                            &None,
                                                            &[],
                                                            &sess);

        let resolver = RefCell::new(resolver);
        abort_on_err(driver::phase_3_run_analysis_passes(&*codegen_backend,
                                                        control,
                                                        &sess,
                                                        &*cstore,
                                                        hir_map,
                                                        analysis,
                                                        resolutions,
                                                        &arenas,
                                                        &name,
                                                        &output_filenames,
                                                        |tcx, analysis, _, result| {
            if let Err(_) = result {
                sess.fatal("Compilation failed, aborting rustdoc");
            }

            let ty::CrateAnalysis { access_levels, .. } = analysis;

            // Convert from a NodeId set to a DefId set since we don't always have easy access
            // to the map from defid -> nodeid
            let access_levels = AccessLevels {
                map: access_levels.map.iter()
                                    .map(|(&k, &v)| (tcx.hir.local_def_id(k), v))
                                    .collect()
            };

            let send_trait = if crate_name == Some("core".to_string()) {
                clean::get_trait_def_id(&tcx, &["marker", "Send"], true)
            } else {
                clean::get_trait_def_id(&tcx, &["core", "marker", "Send"], false)
            };

            let ctxt = DocContext {
                tcx,
                resolver: &resolver,
                crate_name,
                cstore: cstore.clone(),
                populated_all_crate_impls: Cell::new(false),
                access_levels: RefCell::new(access_levels),
                external_traits: Default::default(),
                active_extern_traits: Default::default(),
                renderinfo: Default::default(),
                ty_substs: Default::default(),
                lt_substs: Default::default(),
                impl_trait_bounds: Default::default(),
                mod_ids: Default::default(),
                send_trait: send_trait,
                fake_def_ids: RefCell::new(FxHashMap()),
                all_fake_def_ids: RefCell::new(FxHashSet()),
                generated_synthetics: RefCell::new(FxHashSet()),
            };
            debug!("crate: {:?}", tcx.hir.krate());

            let krate = {
                let mut v = RustdocVisitor::new(&*cstore, &ctxt);
                v.visit(tcx.hir.krate());
                v.clean(&ctxt)
            };

            (krate, ctxt.renderinfo.into_inner())
        }), &sess)
    })
}
<|endoftext|>