<commit_before>use rusoto_core::signature;
use rusoto_core::signature::SignedRequest;
use rusoto_core::param::{Params, ServiceParams};
use rusoto_core::region::Region;
use rusoto_core::credential::AwsCredentials;
use generated::{GetObjectRequest, PutObjectRequest, DeleteObjectRequest};
use std::time::Duration;
/// URL encodes an S3 object key. This is necessary for `copy_object` and `upload_part_copy`,
/// which require the `copy_source` field to be URL encoded.
///
/// # Examples
///
/// ```
/// use rusoto_s3::CopyObjectRequest;
///
/// let request = CopyObjectRequest {
///     bucket: "my-bucket".to_owned(),
///     key: "my-key".to_owned(),
///     copy_source: rusoto_s3::util::encode_key("other-buckét/key-to-cöpy"),
///     ..Default::default()
/// };
/// ```
pub fn encode_key<T: AsRef<str>>(key: T) -> String {
    signature::encode_uri_path(key.as_ref())
}

macro_rules! add_headers {
    (
        $input:ident , $req:ident ; $p:ident , $e:expr ; $( $t:tt )*
    ) => (
        {
            if let Some(ref $p) = $input.$p {
                $req.add_header($e, &$p.to_string());
            }
            add_headers! { $input, $req; $( $t )* }
        }
    );
    (
        $input:pat , $req:expr ;
    ) => ({
    });
}

macro_rules! add_params {
    (
        $input:ident , $params:ident ; $p:ident , $e:expr ; $( $t:tt )*
    ) => (
        {
            if let Some(ref $p) = $input.$p {
                $params.put($e, &$p);
            }
            add_params! { $input, $params; $( $t )* }
        }
    );
    (
        $input:pat , $req:expr ;
    ) => ({
    });
}

pub struct PreSignedRequestOption {
    pub expires_in: Duration
}

impl Default for PreSignedRequestOption {
    fn default() -> Self {
        Self {
            expires_in: Duration::from_secs(3600)
        }
    }
}

pub trait PreSignedRequest {
    /// http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html
    fn get_presigned_url(&self, region: &Region, credentials: &AwsCredentials, option: &PreSignedRequestOption) -> String;
}

impl PreSignedRequest for GetObjectRequest {
    /// https://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectGET.html
    fn get_presigned_url(&self, region: &Region, credentials: &AwsCredentials, option: &PreSignedRequestOption) -> String {
        let request_uri = format!("/{bucket}/{key}", bucket = self.bucket, key = self.key);
        let mut request = SignedRequest::new("GET", "s3", &region, &request_uri);
        let mut params = Params::new();

        add_headers!(
            self, request;
            range, "Range";
            if_modified_since, "If-Modified-Since";
            if_unmodified_since, "If-Unmodified-Since";
            if_match, "If-Match";
            if_none_match, "If-None-Match";
            sse_customer_algorithm, "x-amz-server-side-encryption-customer-algorithm";
            sse_customer_key, "x-amz-server-side-encryption-customer-key";
            sse_customer_key_md5, "x-amz-server-side-encryption-customer-key-MD5";
        );

        add_params!(
            self, params;
            part_number, "partNumber";
            response_content_type, "response-content-type";
            response_content_language, "response-content-language";
            response_expires, "response-expires";
            response_cache_control, "response-cache-control";
            response_content_disposition, "response-content-disposition";
            response_content_encoding, "response-content-encoding";
            version_id, "versionId";
        );

        request.set_params(params);
        request.generate_presigned_url(credentials, &option.expires_in)
    }
}

impl PreSignedRequest for PutObjectRequest {
    /// https://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectPUT.html
    fn get_presigned_url(&self, region: &Region, credentials: &AwsCredentials, option: &PreSignedRequestOption) -> String {
        let request_uri = format!("/{bucket}/{key}", bucket = self.bucket, key = self.key);
        let mut request = SignedRequest::new("PUT", "s3", &region, &request_uri);

        add_headers!(
            self, request;
            cache_control, "Cache-Control";
            content_disposition, "Content-Disposition";
            content_encoding, "Content-Encoding";
            content_length, "Content-Length";
            content_md5, "Content-MD5";
            content_type, "Content-Type";
            // AWS document has Expect parameter but PutObjectRequest does'nt have it.
            //expect, "Expect";
            expires, "Expires";
            storage_class, "x-amz-storage-class";
            tagging, "x-amz-tagging";
            website_redirect_location, "x-amz-website-redirect-location";
            acl, "x-amz-acl";
            grant_read, "x-amz-grant-read";
            // AWS document has x-amz-grant-write parameter but PutObjectRequest does'nt have it.
            //grant_write, "x-amz-grant-write";
            grant_read_acp, "x-amz-grant-read-acp";
            grant_write_acp, "x-amz-grant-write-acp";
            grant_full_control, "x-amz-grant-full-control";
            server_side_encryption, "x-amz-server-side-encryption";
            ssekms_key_id, "x-amz-server-side-encryption-aws-kms-key-id";
            // AWS document has x-amz-server-side-encryption-context parameter but PutObjectRequest does'nt have it.
            //kms_context, "x-amz-server-side-encryption-context";
            sse_customer_algorithm, "x-amz-server-side-encryption-customer-algorithm";
            sse_customer_key, "x-amz-server-side-encryption-customer-key";
            sse_customer_key_md5, "x-amz-server-side-encryption-customer-key-MD5";
        );

        if let Some(ref metadata) = self.metadata {
            for (header_name, header_value) in metadata.iter() {
                let header = format!("x-amz-meta-{}", header_name);
                request.add_header(header, header_value);
            }
        }

        request.generate_presigned_url(credentials, &option.expires_in)
    }
}

impl PreSignedRequest for DeleteObjectRequest {
    /// https://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectDELETE.html
    fn get_presigned_url(&self, region: &Region, credentials: &AwsCredentials, option: &PreSignedRequestOption) -> String {
        let request_uri = format!("/{bucket}/{key}", bucket = self.bucket, key = self.key);
        let mut request = SignedRequest::new("DELETE", "s3", &region, &request_uri);
        let mut params = Params::new();

        add_headers!(
            self, request;
            mfa, "x-amz-mfa";
        );

        add_params!(
            self, params;
            version_id, "versionId";
        );

        request.set_params(params);
        request.generate_presigned_url(credentials, &option.expires_in)
    }
}
<commit_msg>first stab at implemented PreSignedRequest for UploadPartRequest<commit_after>use rusoto_core::signature;
use rusoto_core::signature::SignedRequest;
use rusoto_core::param::{Params, ServiceParams};
use rusoto_core::region::Region;
use rusoto_core::credential::AwsCredentials;
use generated::{GetObjectRequest, PutObjectRequest, DeleteObjectRequest, UploadPartRequest};
use std::time::Duration;
/// URL encodes an S3 object key. This is necessary for `copy_object` and `upload_part_copy`,
/// which require the `copy_source` field to be URL encoded.
///
/// # Examples
///
/// ```
/// use rusoto_s3::CopyObjectRequest;
///
/// let request = CopyObjectRequest {
///     bucket: "my-bucket".to_owned(),
///     key: "my-key".to_owned(),
///     copy_source: rusoto_s3::util::encode_key("other-buckét/key-to-cöpy"),
///     ..Default::default()
/// };
/// ```
pub fn encode_key<T: AsRef<str>>(key: T) -> String {
    signature::encode_uri_path(key.as_ref())
}

macro_rules! add_headers {
    (
        $input:ident , $req:ident ; $p:ident , $e:expr ; $( $t:tt )*
    ) => (
        {
            if let Some(ref $p) = $input.$p {
                $req.add_header($e, &$p.to_string());
            }
            add_headers! { $input, $req; $( $t )* }
        }
    );
    (
        $input:pat , $req:expr ;
    ) => ({
    });
}

macro_rules! add_params {
    (
        $input:ident , $params:ident ; $p:ident , $e:expr ; $( $t:tt )*
    ) => (
        {
            if let Some(ref $p) = $input.$p {
                $params.put($e, &$p);
            }
            add_params! { $input, $params; $( $t )* }
        }
    );
    (
        $input:pat , $req:expr ;
    ) => ({
    });
}

pub struct PreSignedRequestOption {
    pub expires_in: Duration
}

impl Default for PreSignedRequestOption {
    fn default() -> Self {
        Self {
            expires_in: Duration::from_secs(3600)
        }
    }
}

pub trait PreSignedRequest {
    /// http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html
    fn get_presigned_url(&self, region: &Region, credentials: &AwsCredentials, option: &PreSignedRequestOption) -> String;
}

impl PreSignedRequest for GetObjectRequest {
    /// https://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectGET.html
    fn get_presigned_url(&self, region: &Region, credentials: &AwsCredentials, option: &PreSignedRequestOption) -> String {
        let request_uri = format!("/{bucket}/{key}", bucket = self.bucket, key = self.key);
        let mut request = SignedRequest::new("GET", "s3", &region, &request_uri);
        let mut params = Params::new();

        add_headers!(
            self, request;
            range, "Range";
            if_modified_since, "If-Modified-Since";
            if_unmodified_since, "If-Unmodified-Since";
            if_match, "If-Match";
            if_none_match, "If-None-Match";
            sse_customer_algorithm, "x-amz-server-side-encryption-customer-algorithm";
            sse_customer_key, "x-amz-server-side-encryption-customer-key";
            sse_customer_key_md5, "x-amz-server-side-encryption-customer-key-MD5";
        );

        add_params!(
            self, params;
            part_number, "partNumber";
            response_content_type, "response-content-type";
            response_content_language, "response-content-language";
            response_expires, "response-expires";
            response_cache_control, "response-cache-control";
            response_content_disposition, "response-content-disposition";
            response_content_encoding, "response-content-encoding";
            version_id, "versionId";
        );

        request.set_params(params);
        request.generate_presigned_url(credentials, &option.expires_in)
    }
}

impl PreSignedRequest for PutObjectRequest {
    /// https://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectPUT.html
    fn get_presigned_url(&self, region: &Region, credentials: &AwsCredentials, option: &PreSignedRequestOption) -> String {
        let request_uri = format!("/{bucket}/{key}", bucket = self.bucket, key = self.key);
        let mut request = SignedRequest::new("PUT", "s3", &region, &request_uri);

        add_headers!(
            self, request;
            cache_control, "Cache-Control";
            content_disposition, "Content-Disposition";
            content_encoding, "Content-Encoding";
            content_length, "Content-Length";
            content_md5, "Content-MD5";
            content_type, "Content-Type";
            // AWS document has Expect parameter but PutObjectRequest does'nt have it.
            //expect, "Expect";
            expires, "Expires";
            storage_class, "x-amz-storage-class";
            tagging, "x-amz-tagging";
            website_redirect_location, "x-amz-website-redirect-location";
            acl, "x-amz-acl";
            grant_read, "x-amz-grant-read";
            // AWS document has x-amz-grant-write parameter but PutObjectRequest does'nt have it.
            //grant_write, "x-amz-grant-write";
            grant_read_acp, "x-amz-grant-read-acp";
            grant_write_acp, "x-amz-grant-write-acp";
            grant_full_control, "x-amz-grant-full-control";
            server_side_encryption, "x-amz-server-side-encryption";
            ssekms_key_id, "x-amz-server-side-encryption-aws-kms-key-id";
            // AWS document has x-amz-server-side-encryption-context parameter but PutObjectRequest does'nt have it.
            //kms_context, "x-amz-server-side-encryption-context";
            sse_customer_algorithm, "x-amz-server-side-encryption-customer-algorithm";
            sse_customer_key, "x-amz-server-side-encryption-customer-key";
            sse_customer_key_md5, "x-amz-server-side-encryption-customer-key-MD5";
        );

        if let Some(ref metadata) = self.metadata {
            for (header_name, header_value) in metadata.iter() {
                let header = format!("x-amz-meta-{}", header_name);
                request.add_header(header, header_value);
            }
        }

        request.generate_presigned_url(credentials, &option.expires_in)
    }
}

impl PreSignedRequest for DeleteObjectRequest {
    /// https://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectDELETE.html
    fn get_presigned_url(&self, region: &Region, credentials: &AwsCredentials, option: &PreSignedRequestOption) -> String {
        let request_uri = format!("/{bucket}/{key}", bucket = self.bucket, key = self.key);
        let mut request = SignedRequest::new("DELETE", "s3", &region, &request_uri);
        let mut params = Params::new();

        add_headers!(
            self, request;
            mfa, "x-amz-mfa";
        );

        add_params!(
            self, params;
            version_id, "versionId";
        );

        request.set_params(params);
        request.generate_presigned_url(credentials, &option.expires_in)
    }
}

impl PreSignedRequest for UploadPartRequest {
    /// https://docs.aws.amazon.com/AmazonS3/latest/API/mpUploadUploadPart.html
    fn get_presigned_url(&self, region: &Region, credentials: &AwsCredentials, option: &PreSignedRequestOption) -> String {
        let request_uri = format!("/{bucket}/{key}", bucket = self.bucket, key = self.key);
        let mut request = SignedRequest::new("PUT", "s3", &region, &request_uri);

        request.add_param("partNumber", &self.part_number.to_string());
        request.add_param("uploadId", &self.upload_id);

        add_headers!(
            self, request;
            content_length, "Content-Length";
            content_md5, "Content-MD5";
            sse_customer_algorithm, "x-amz-server-side-encryption-customer-algorithm";
            sse_customer_key, "x-amz-server-side-encryption-customer-key";
            sse_customer_key_md5, "x-amz-server-side-encryption-customer-key-MD5";
            request_payer, "x-amz-request-payer";
        );

        request.generate_presigned_url(credentials, &option.expires_in)
    }
}
<|endoftext|>


<commit_before>/// Creates a [`Vec`] containing the arguments.
///
/// `vec!` allows `Vec`s to be defined with the same syntax as array expressions.
/// There are two forms of this macro:
///
/// - Create a [`Vec`] containing a given list of elements:
///
/// ```
/// let v = vec![1, 2, 3];
/// assert_eq!(v[0], 1);
/// assert_eq!(v[1], 2);
/// assert_eq!(v[2], 3);
/// ```
///
/// - Create a [`Vec`] from a given element and size:
///
/// ```
/// let v = vec![1; 3];
/// assert_eq!(v, [1, 1, 1]);
/// ```
///
/// Note that unlike array expressions this syntax supports all elements
/// which implement [`Clone`] and the number of elements doesn't have to be
/// a constant.
///
/// This will use `clone` to duplicate an expression, so one should be careful
/// using this with types having a nonstandard `Clone` implementation. For
/// example, `vec![Rc::new(1); 5]` will create a vector of five references
/// to the same boxed integer value, not five references pointing to independently
/// boxed integers.
///
/// [`Vec`]: ../std/vec/struct.Vec.html
/// [`Clone`]: ../std/clone/trait.Clone.html
#[cfg(not(test))]
#[macro_export]
#[stable(feature = "rust1", since = "1.0.0")]
#[allow_internal_unstable(box_syntax)]
macro_rules! vec {
    () => (
        $crate::vec::Vec::new()
    );
    ($elem:expr; $n:expr) => (
        $crate::vec::from_elem($elem, $n)
    );
    ($($x:expr),*) => (
        <[_]>::into_vec(box [$($x),*])
    );
    ($($x:expr,)*) => ($crate::vec![$($x),*])
}

// HACK(japaric): with cfg(test) the inherent `[T]::into_vec` method, which is
// required for this macro definition, is not available. Instead use the
// `slice::into_vec`  function which is only available with cfg(test)
// NB see the slice::hack module in slice.rs for more information
#[cfg(test)]
macro_rules! vec {
    () => (
        $crate::vec::Vec::new()
    );
    ($elem:expr; $n:expr) => (
        $crate::vec::from_elem($elem, $n)
    );
    ($($x:expr),*) => (
        $crate::slice::into_vec(box [$($x),*])
    );
    ($($x:expr,)*) => (vec![$($x),*])
}

/// Creates a `String` using interpolation of runtime expressions.
///
/// The first argument `format!` receives is a format string. This must be a string
/// literal. The power of the formatting string is in the `{}`s contained.
///
/// Additional parameters passed to `format!` replace the `{}`s within the
/// formatting string in the order given unless named or positional parameters
/// are used; see [`std::fmt`][fmt] for more information.
///
/// A common use for `format!` is concatenation and interpolation of strings.
/// The same convention is used with [`print!`] and [`write!`] macros,
/// depending on the intended destination of the string.
///
/// To convert a single value to a string, use the [`to_string`] method. This
/// will use the [`Display`] formatting trait.
///
/// [fmt]: ../std/fmt/index.html
/// [`print!`]: ../std/macro.print.html
/// [`write!`]: ../std/macro.write.html
/// [`to_string`]: ../std/string/trait.ToString.html
/// [`Display`]: ../std/fmt/trait.Display.html
///
/// # Panics
///
/// `format!` panics if a formatting trait implementation returns an error.
/// This indicates an incorrect implementation
/// since `fmt::Write for String` never returns an error itself.
///
/// # Examples
///
/// ```
/// format!("test");
/// format!("hello {}", "world!");
/// format!("x = {}, y = {y}", 10, y = 30);
/// ```
#[macro_export]
#[stable(feature = "rust1", since = "1.0.0")]
macro_rules! format {
    ($($arg:tt)*) => {{
        let res = $crate::fmt::format($crate::__export::format_args!($($arg)*));
        res
    }}
}
<commit_msg>Rollup merge of #70949 - WaffleLapkin:simlify_vec_macro, r=petrochenkov<commit_after>/// Creates a [`Vec`] containing the arguments.
///
/// `vec!` allows `Vec`s to be defined with the same syntax as array expressions.
/// There are two forms of this macro:
///
/// - Create a [`Vec`] containing a given list of elements:
///
/// ```
/// let v = vec![1, 2, 3];
/// assert_eq!(v[0], 1);
/// assert_eq!(v[1], 2);
/// assert_eq!(v[2], 3);
/// ```
///
/// - Create a [`Vec`] from a given element and size:
///
/// ```
/// let v = vec![1; 3];
/// assert_eq!(v, [1, 1, 1]);
/// ```
///
/// Note that unlike array expressions this syntax supports all elements
/// which implement [`Clone`] and the number of elements doesn't have to be
/// a constant.
///
/// This will use `clone` to duplicate an expression, so one should be careful
/// using this with types having a nonstandard `Clone` implementation. For
/// example, `vec![Rc::new(1); 5]` will create a vector of five references
/// to the same boxed integer value, not five references pointing to independently
/// boxed integers.
///
/// [`Vec`]: ../std/vec/struct.Vec.html
/// [`Clone`]: ../std/clone/trait.Clone.html
#[cfg(not(test))]
#[macro_export]
#[stable(feature = "rust1", since = "1.0.0")]
#[allow_internal_unstable(box_syntax)]
macro_rules! vec {
    () => (
        $crate::vec::Vec::new()
    );
    ($elem:expr; $n:expr) => (
        $crate::vec::from_elem($elem, $n)
    );
    ($($x:expr),+ $(,)?) => (
        <[_]>::into_vec(box [$($x),+])
    );
}

// HACK(japaric): with cfg(test) the inherent `[T]::into_vec` method, which is
// required for this macro definition, is not available. Instead use the
// `slice::into_vec`  function which is only available with cfg(test)
// NB see the slice::hack module in slice.rs for more information
#[cfg(test)]
macro_rules! vec {
    () => (
        $crate::vec::Vec::new()
    );
    ($elem:expr; $n:expr) => (
        $crate::vec::from_elem($elem, $n)
    );
    ($($x:expr),*) => (
        $crate::slice::into_vec(box [$($x),*])
    );
    ($($x:expr,)*) => (vec![$($x),*])
}

/// Creates a `String` using interpolation of runtime expressions.
///
/// The first argument `format!` receives is a format string. This must be a string
/// literal. The power of the formatting string is in the `{}`s contained.
///
/// Additional parameters passed to `format!` replace the `{}`s within the
/// formatting string in the order given unless named or positional parameters
/// are used; see [`std::fmt`][fmt] for more information.
///
/// A common use for `format!` is concatenation and interpolation of strings.
/// The same convention is used with [`print!`] and [`write!`] macros,
/// depending on the intended destination of the string.
///
/// To convert a single value to a string, use the [`to_string`] method. This
/// will use the [`Display`] formatting trait.
///
/// [fmt]: ../std/fmt/index.html
/// [`print!`]: ../std/macro.print.html
/// [`write!`]: ../std/macro.write.html
/// [`to_string`]: ../std/string/trait.ToString.html
/// [`Display`]: ../std/fmt/trait.Display.html
///
/// # Panics
///
/// `format!` panics if a formatting trait implementation returns an error.
/// This indicates an incorrect implementation
/// since `fmt::Write for String` never returns an error itself.
///
/// # Examples
///
/// ```
/// format!("test");
/// format!("hello {}", "world!");
/// format!("x = {}, y = {y}", 10, y = 30);
/// ```
#[macro_export]
#[stable(feature = "rust1", since = "1.0.0")]
macro_rules! format {
    ($($arg:tt)*) => {{
        let res = $crate::fmt::format($crate::__export::format_args!($($arg)*));
        res
    }}
}
<|endoftext|>


<commit_before>// Copyright 2013 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 std::cell::RefCell;
use std::io::Process;
use std::local_data;
use std::os;
use std::str;

use collections::HashSet;
use testing;
use extra::tempfile::TempDir;
use rustc::back::link;
use rustc::driver::driver;
use rustc::driver::session;
use rustc::metadata::creader::Loader;
use getopts;
use syntax::diagnostic;
use syntax::parse;

use core;
use clean;
use clean::Clean;
use fold::DocFolder;
use html::markdown;
use passes;
use visit_ast::RustdocVisitor;

pub fn run(input: &str, matches: &getopts::Matches) -> int {
    let parsesess = parse::new_parse_sess();
    let input_path = Path::new(input);
    let input = driver::FileInput(input_path.clone());
    let libs = matches.opt_strs("L").map(|s| Path::new(s.as_slice()));
    let libs = @RefCell::new(libs.move_iter().collect());

    let sessopts = @session::Options {
        maybe_sysroot: Some(@os::self_exe_path().unwrap().dir_path()),
        addl_lib_search_paths: libs,
        crate_types: ~[session::CrateTypeDylib],
        .. (*session::basic_options()).clone()
    };

    let diagnostic_handler = diagnostic::mk_handler();
    let span_diagnostic_handler =
        diagnostic::mk_span_handler(diagnostic_handler, parsesess.cm);

    let sess = driver::build_session_(sessopts,
                                      Some(input_path),
                                      parsesess.cm,
                                      span_diagnostic_handler);

    let cfg = driver::build_configuration(sess);
    let krate = driver::phase_1_parse_input(sess, cfg, &input);
    let loader = &mut Loader::new(sess);
    let (krate, _) = driver::phase_2_configure_and_expand(sess, loader, krate);

    let ctx = @core::DocContext {
        krate: krate,
        tycx: None,
        sess: sess,
    };
    local_data::set(super::ctxtkey, ctx);

    let mut v = RustdocVisitor::new(ctx, None);
    v.visit(&ctx.krate);
    let krate = v.clean();
    let (krate, _) = passes::unindent_comments(krate);
    let (krate, _) = passes::collapse_docs(krate);

    let mut collector = Collector {
        tests: ~[],
        names: ~[],
        cnt: 0,
        libs: libs,
        cratename: krate.name.to_owned(),
    };
    collector.fold_crate(krate);

    let args = matches.opt_strs("test-args");
    let mut args = args.iter().flat_map(|s| s.words()).map(|s| s.to_owned());
    let mut args = args.to_owned_vec();
    args.unshift(~"rustdoctest");

    testing::test_main(args, collector.tests);

    0
}

fn runtest(test: &str, cratename: &str, libs: HashSet<Path>, should_fail: bool) {
    let test = maketest(test, cratename);
    let parsesess = parse::new_parse_sess();
    let input = driver::StrInput(test);

    let sessopts = @session::Options {
        maybe_sysroot: Some(@os::self_exe_path().unwrap().dir_path()),
        addl_lib_search_paths: @RefCell::new(libs),
        crate_types: ~[session::CrateTypeExecutable],
        output_types: ~[link::OutputTypeExe],
        cg: session::CodegenOptions {
            prefer_dynamic: true,
            .. session::basic_codegen_options()
        },
        .. (*session::basic_options()).clone()
    };

    let diagnostic_handler = diagnostic::mk_handler();
    let span_diagnostic_handler =
        diagnostic::mk_span_handler(diagnostic_handler, parsesess.cm);

    let sess = driver::build_session_(sessopts,
                                      None,
                                      parsesess.cm,
                                      span_diagnostic_handler);

    let outdir = TempDir::new("rustdoctest").expect("rustdoc needs a tempdir");
    let out = Some(outdir.path().clone());
    let cfg = driver::build_configuration(sess);
    driver::compile_input(sess, cfg, &input, &out, &None);

    let exe = outdir.path().join("rust_out");
    let out = Process::output(exe.as_str().unwrap(), []);
    match out {
        Err(e) => fail!("couldn't run the test: {}", e),
        Ok(out) => {
            if should_fail && out.status.success() {
                fail!("test executable succeeded when it should have failed");
            } else if !should_fail && !out.status.success() {
                fail!("test executable failed:\n{}", str::from_utf8(out.error));
            }
        }
    }
}

fn maketest(s: &str, cratename: &str) -> ~str {
    let mut prog = ~r"
#[deny(warnings)];
#[allow(unused_variable, dead_assignment, unused_mut, attribute_usage, dead_code)];
";
    if !s.contains("extern crate") {
        if s.contains("extra") {
            prog.push_str("extern crate extra;\n");
        }
        if s.contains(cratename) {
            prog.push_str(format!("extern crate {};\n", cratename));
        }
    }
    if s.contains("fn main") {
        prog.push_str(s);
    } else {
        prog.push_str("fn main() {\n");
        prog.push_str(s);
        prog.push_str("\n}");
    }

    return prog;
}

pub struct Collector {
    priv tests: ~[testing::TestDescAndFn],
    priv names: ~[~str],
    priv libs: @RefCell<HashSet<Path>>,
    priv cnt: uint,
    priv cratename: ~str,
}

impl Collector {
    pub fn add_test(&mut self, test: &str, should_fail: bool) {
        let test = test.to_owned();
        let name = format!("{}_{}", self.names.connect("::"), self.cnt);
        self.cnt += 1;
        let libs = self.libs.borrow();
        let libs = (*libs.get()).clone();
        let cratename = self.cratename.to_owned();
        debug!("Creating test {}: {}", name, test);
        self.tests.push(testing::TestDescAndFn {
            desc: testing::TestDesc {
                name: testing::DynTestName(name),
                ignore: false,
                should_fail: false, // compiler failures are test failures
            },
            testfn: testing::DynTestFn(proc() {
                runtest(test, cratename, libs, should_fail);
            }),
        });
    }
}

impl DocFolder for Collector {
    fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
        let pushed = match item.name {
            Some(ref name) if name.len() == 0 => false,
            Some(ref name) => { self.names.push(name.to_owned()); true }
            None => false
        };
        match item.doc_value() {
            Some(doc) => {
                self.cnt = 0;
                markdown::find_testable_code(doc, self);
            }
            None => {}
        }
        let ret = self.fold_item_recur(item);
        if pushed {
            self.names.pop();
        }
        return ret;
    }
}
<commit_msg>Provide a more helpful error for tests that fail due to noexec<commit_after>// Copyright 2013 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 std::cell::RefCell;
use std::io;
use std::io::Process;
use std::local_data;
use std::os;
use std::str;

use collections::HashSet;
use testing;
use extra::tempfile::TempDir;
use rustc::back::link;
use rustc::driver::driver;
use rustc::driver::session;
use rustc::metadata::creader::Loader;
use getopts;
use syntax::diagnostic;
use syntax::parse;

use core;
use clean;
use clean::Clean;
use fold::DocFolder;
use html::markdown;
use passes;
use visit_ast::RustdocVisitor;

pub fn run(input: &str, matches: &getopts::Matches) -> int {
    let parsesess = parse::new_parse_sess();
    let input_path = Path::new(input);
    let input = driver::FileInput(input_path.clone());
    let libs = matches.opt_strs("L").map(|s| Path::new(s.as_slice()));
    let libs = @RefCell::new(libs.move_iter().collect());

    let sessopts = @session::Options {
        maybe_sysroot: Some(@os::self_exe_path().unwrap().dir_path()),
        addl_lib_search_paths: libs,
        crate_types: ~[session::CrateTypeDylib],
        .. (*session::basic_options()).clone()
    };

    let diagnostic_handler = diagnostic::mk_handler();
    let span_diagnostic_handler =
        diagnostic::mk_span_handler(diagnostic_handler, parsesess.cm);

    let sess = driver::build_session_(sessopts,
                                      Some(input_path),
                                      parsesess.cm,
                                      span_diagnostic_handler);

    let cfg = driver::build_configuration(sess);
    let krate = driver::phase_1_parse_input(sess, cfg, &input);
    let loader = &mut Loader::new(sess);
    let (krate, _) = driver::phase_2_configure_and_expand(sess, loader, krate);

    let ctx = @core::DocContext {
        krate: krate,
        tycx: None,
        sess: sess,
    };
    local_data::set(super::ctxtkey, ctx);

    let mut v = RustdocVisitor::new(ctx, None);
    v.visit(&ctx.krate);
    let krate = v.clean();
    let (krate, _) = passes::unindent_comments(krate);
    let (krate, _) = passes::collapse_docs(krate);

    let mut collector = Collector {
        tests: ~[],
        names: ~[],
        cnt: 0,
        libs: libs,
        cratename: krate.name.to_owned(),
    };
    collector.fold_crate(krate);

    let args = matches.opt_strs("test-args");
    let mut args = args.iter().flat_map(|s| s.words()).map(|s| s.to_owned());
    let mut args = args.to_owned_vec();
    args.unshift(~"rustdoctest");

    testing::test_main(args, collector.tests);

    0
}

fn runtest(test: &str, cratename: &str, libs: HashSet<Path>, should_fail: bool) {
    let test = maketest(test, cratename);
    let parsesess = parse::new_parse_sess();
    let input = driver::StrInput(test);

    let sessopts = @session::Options {
        maybe_sysroot: Some(@os::self_exe_path().unwrap().dir_path()),
        addl_lib_search_paths: @RefCell::new(libs),
        crate_types: ~[session::CrateTypeExecutable],
        output_types: ~[link::OutputTypeExe],
        cg: session::CodegenOptions {
            prefer_dynamic: true,
            .. session::basic_codegen_options()
        },
        .. (*session::basic_options()).clone()
    };

    let diagnostic_handler = diagnostic::mk_handler();
    let span_diagnostic_handler =
        diagnostic::mk_span_handler(diagnostic_handler, parsesess.cm);

    let sess = driver::build_session_(sessopts,
                                      None,
                                      parsesess.cm,
                                      span_diagnostic_handler);

    let outdir = TempDir::new("rustdoctest").expect("rustdoc needs a tempdir");
    let out = Some(outdir.path().clone());
    let cfg = driver::build_configuration(sess);
    driver::compile_input(sess, cfg, &input, &out, &None);

    let exe = outdir.path().join("rust_out");
    let out = Process::output(exe.as_str().unwrap(), []);
    match out {
        Err(e) => fail!("couldn't run the test: {}{}", e,
                        if e.kind == io::PermissionDenied {
                            " - maybe your tempdir is mounted with noexec?"
                        } else { "" }),
        Ok(out) => {
            if should_fail && out.status.success() {
                fail!("test executable succeeded when it should have failed");
            } else if !should_fail && !out.status.success() {
                fail!("test executable failed:\n{}", str::from_utf8(out.error));
            }
        }
    }
}

fn maketest(s: &str, cratename: &str) -> ~str {
    let mut prog = ~r"
#[deny(warnings)];
#[allow(unused_variable, dead_assignment, unused_mut, attribute_usage, dead_code)];
";
    if !s.contains("extern crate") {
        if s.contains("extra") {
            prog.push_str("extern crate extra;\n");
        }
        if s.contains(cratename) {
            prog.push_str(format!("extern crate {};\n", cratename));
        }
    }
    if s.contains("fn main") {
        prog.push_str(s);
    } else {
        prog.push_str("fn main() {\n");
        prog.push_str(s);
        prog.push_str("\n}");
    }

    return prog;
}

pub struct Collector {
    priv tests: ~[testing::TestDescAndFn],
    priv names: ~[~str],
    priv libs: @RefCell<HashSet<Path>>,
    priv cnt: uint,
    priv cratename: ~str,
}

impl Collector {
    pub fn add_test(&mut self, test: &str, should_fail: bool) {
        let test = test.to_owned();
        let name = format!("{}_{}", self.names.connect("::"), self.cnt);
        self.cnt += 1;
        let libs = self.libs.borrow();
        let libs = (*libs.get()).clone();
        let cratename = self.cratename.to_owned();
        debug!("Creating test {}: {}", name, test);
        self.tests.push(testing::TestDescAndFn {
            desc: testing::TestDesc {
                name: testing::DynTestName(name),
                ignore: false,
                should_fail: false, // compiler failures are test failures
            },
            testfn: testing::DynTestFn(proc() {
                runtest(test, cratename, libs, should_fail);
            }),
        });
    }
}

impl DocFolder for Collector {
    fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
        let pushed = match item.name {
            Some(ref name) if name.len() == 0 => false,
            Some(ref name) => { self.names.push(name.to_owned()); true }
            None => false
        };
        match item.doc_value() {
            Some(doc) => {
                self.cnt = 0;
                markdown::find_testable_code(doc, self);
            }
            None => {}
        }
        let ret = self.fold_item_recur(item);
        if pushed {
            self.names.pop();
        }
        return ret;
    }
}
<|endoftext|>


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

/*! Non-blocking access to stdin, stdout, and stderr.

This module provides bindings to the local event loop's TTY interface, using it
to offer synchronous but non-blocking versions of stdio. These handles can be
inspected for information about terminal dimensions or for related information
about the stream or terminal to which it is attached.

# Example

```rust
# #![allow(unused_must_use)]
use std::io;

let mut out = io::stdout();
out.write(b"Hello, world!");
```

*/

use failure::local_stderr;
use fmt;
use io::{Reader, Writer, IoResult, IoError, OtherIoError,
         standard_error, EndOfFile, LineBufferedWriter, BufferedReader};
use iter::Iterator;
use kinds::Send;
use libc;
use option::{Option, Some, None};
use boxed::Box;
use result::{Ok, Err};
use rt;
use rt::local::Local;
use rt::task::Task;
use rt::rtio::{DontClose, IoFactory, LocalIo, RtioFileStream, RtioTTY};
use slice::ImmutableSlice;
use str::StrSlice;
use uint;

// And so begins the tale of acquiring a uv handle to a stdio stream on all
// platforms in all situations. Our story begins by splitting the world into two
// categories, windows and unix. Then one day the creators of unix said let
// there be redirection! And henceforth there was redirection away from the
// console for standard I/O streams.
//
// After this day, the world split into four factions:
//
// 1. Unix with stdout on a terminal.
// 2. Unix with stdout redirected.
// 3. Windows with stdout on a terminal.
// 4. Windows with stdout redirected.
//
// Many years passed, and then one day the nation of libuv decided to unify this
// world. After months of toiling, uv created three ideas: TTY, Pipe, File.
// These three ideas propagated throughout the lands and the four great factions
// decided to settle among them.
//
// The groups of 1, 2, and 3 all worked very hard towards the idea of TTY. Upon
// doing so, they even enhanced themselves further then their Pipe/File
// brethren, becoming the dominant powers.
//
// The group of 4, however, decided to work independently. They abandoned the
// common TTY belief throughout, and even abandoned the fledgling Pipe belief.
// The members of the 4th faction decided to only align themselves with File.
//
// tl;dr; TTY works on everything but when windows stdout is redirected, in that
//        case pipe also doesn't work, but magically file does!
enum StdSource {
    TTY(Box<RtioTTY + Send>),
    File(Box<RtioFileStream + Send>),
}

fn src<T>(fd: libc::c_int, readable: bool, f: |StdSource| -> T) -> T {
    LocalIo::maybe_raise(|io| {
        Ok(match io.tty_open(fd, readable) {
            Ok(tty) => f(TTY(tty)),
            Err(_) => f(File(io.fs_from_raw_fd(fd, DontClose))),
        })
    }).map_err(IoError::from_rtio_error).unwrap()
}

local_data_key!(local_stdout: Box<Writer + Send>)

/// Creates a new non-blocking handle to the stdin of the current process.
///
/// The returned handled is buffered by default with a `BufferedReader`. If
/// buffered access is not desired, the `stdin_raw` function is provided to
/// provided unbuffered access to stdin.
///
/// Care should be taken when creating multiple handles to the stdin of a
/// process. Because this is a buffered reader by default, it's possible for
/// pending input to be unconsumed in one reader and unavailable to other
/// readers. It is recommended that only one handle at a time is created for the
/// stdin of a process.
///
/// See `stdout()` for more notes about this function.
pub fn stdin() -> BufferedReader<StdReader> {
    // The default buffer capacity is 64k, but apparently windows doesn't like
    // 64k reads on stdin. See #13304 for details, but the idea is that on
    // windows we use a slightly smaller buffer that's been seen to be
    // acceptable.
    if cfg!(windows) {
        BufferedReader::with_capacity(8 * 1024, stdin_raw())
    } else {
        BufferedReader::new(stdin_raw())
    }
}

/// Creates a new non-blocking handle to the stdin of the current process.
///
/// Unlike `stdin()`, the returned reader is *not* a buffered reader.
///
/// See `stdout()` for more notes about this function.
pub fn stdin_raw() -> StdReader {
    src(libc::STDIN_FILENO, true, |src| StdReader { inner: src })
}

/// Creates a line-buffered handle to the stdout of the current process.
///
/// Note that this is a fairly expensive operation in that at least one memory
/// allocation is performed. Additionally, this must be called from a runtime
/// task context because the stream returned will be a non-blocking object using
/// the local scheduler to perform the I/O.
///
/// Care should be taken when creating multiple handles to an output stream for
/// a single process. While usage is still safe, the output may be surprising if
/// no synchronization is performed to ensure a sane output.
pub fn stdout() -> LineBufferedWriter<StdWriter> {
    LineBufferedWriter::new(stdout_raw())
}

/// Creates an unbuffered handle to the stdout of the current process
///
/// See notes in `stdout()` for more information.
pub fn stdout_raw() -> StdWriter {
    src(libc::STDOUT_FILENO, false, |src| StdWriter { inner: src })
}

/// Creates a line-buffered handle to the stderr of the current process.
///
/// See `stdout()` for notes about this function.
pub fn stderr() -> LineBufferedWriter<StdWriter> {
    LineBufferedWriter::new(stderr_raw())
}

/// Creates an unbuffered handle to the stderr of the current process
///
/// See notes in `stdout()` for more information.
pub fn stderr_raw() -> StdWriter {
    src(libc::STDERR_FILENO, false, |src| StdWriter { inner: src })
}

/// Resets the task-local stdout handle to the specified writer
///
/// This will replace the current task's stdout handle, returning the old
/// handle. All future calls to `print` and friends will emit their output to
/// this specified handle.
///
/// Note that this does not need to be called for all new tasks; the default
/// output handle is to the process's stdout stream.
pub fn set_stdout(stdout: Box<Writer + Send>) -> Option<Box<Writer + Send>> {
    local_stdout.replace(Some(stdout)).and_then(|mut s| {
        let _ = s.flush();
        Some(s)
    })
}

/// Resets the task-local stderr handle to the specified writer
///
/// This will replace the current task's stderr handle, returning the old
/// handle. Currently, the stderr handle is used for printing failure messages
/// during task failure.
///
/// Note that this does not need to be called for all new tasks; the default
/// output handle is to the process's stderr stream.
pub fn set_stderr(stderr: Box<Writer + Send>) -> Option<Box<Writer + Send>> {
    local_stderr.replace(Some(stderr)).and_then(|mut s| {
        let _ = s.flush();
        Some(s)
    })
}

// Helper to access the local task's stdout handle
//
// Note that this is not a safe function to expose because you can create an
// aliased pointer very easily:
//
//  with_task_stdout(|io1| {
//      with_task_stdout(|io2| {
//          // io1 aliases io2
//      })
//  })
fn with_task_stdout(f: |&mut Writer| -> IoResult<()>) {
    let result = if Local::exists(None::<Task>) {
        let mut my_stdout = local_stdout.replace(None).unwrap_or_else(|| {
            box stdout() as Box<Writer + Send>
        });
        let result = f(&mut *my_stdout);
        local_stdout.replace(Some(my_stdout));
        result
    } else {
        let mut io = rt::Stdout;
        f(&mut io as &mut Writer)
    };
    match result {
        Ok(()) => {}
        Err(e) => fail!("failed printing to stdout: {}", e),
    }
}

/// Flushes the local task's stdout handle.
///
/// By default, this stream is a line-buffering stream, so flushing may be
/// necessary to ensure that all output is printed to the screen (if there are
/// no newlines printed).
///
/// Note that logging macros do not use this stream. Using the logging macros
/// will emit output to stderr, and while they are line buffered the log
/// messages are always terminated in a newline (no need to flush).
pub fn flush() {
    with_task_stdout(|io| io.flush())
}

/// Prints a string to the stdout of the current process. No newline is emitted
/// after the string is printed.
pub fn print(s: &str) {
    with_task_stdout(|io| io.write(s.as_bytes()))
}

/// Prints a string to the stdout of the current process. A literal
/// `\n` character is printed to the console after the string.
pub fn println(s: &str) {
    with_task_stdout(|io| {
        io.write(s.as_bytes()).and_then(|()| io.write([b'\n']))
    })
}

/// Similar to `print`, but takes a `fmt::Arguments` structure to be compatible
/// with the `format_args!` macro.
pub fn print_args(fmt: &fmt::Arguments) {
    with_task_stdout(|io| write!(io, "{}", fmt))
}

/// Similar to `println`, but takes a `fmt::Arguments` structure to be
/// compatible with the `format_args!` macro.
pub fn println_args(fmt: &fmt::Arguments) {
    with_task_stdout(|io| writeln!(io, "{}", fmt))
}

/// Representation of a reader of a standard input stream
pub struct StdReader {
    inner: StdSource
}

impl StdReader {
    /// Returns whether this stream is attached to a TTY instance or not.
    pub fn isatty(&self) -> bool {
        match self.inner {
            TTY(..) => true,
            File(..) => false,
        }
    }
}

impl Reader for StdReader {
    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
        let ret = match self.inner {
            TTY(ref mut tty) => {
                // Flush the task-local stdout so that weird issues like a
                // print!'d prompt not being shown until after the user hits
                // enter.
                flush();
                tty.read(buf)
            },
            File(ref mut file) => file.read(buf).map(|i| i as uint),
        }.map_err(IoError::from_rtio_error);
        match ret {
            // When reading a piped stdin, libuv will return 0-length reads when
            // stdin reaches EOF. For pretty much all other streams it will
            // return an actual EOF error, but apparently for stdin it's a
            // little different. Hence, here we convert a 0 length read to an
            // end-of-file indicator so the caller knows to stop reading.
            Ok(0) => { Err(standard_error(EndOfFile)) }
            ret @ Ok(..) | ret @ Err(..) => ret,
        }
    }
}

/// Representation of a writer to a standard output stream
pub struct StdWriter {
    inner: StdSource
}

impl StdWriter {
    /// Gets the size of this output window, if possible. This is typically used
    /// when the writer is attached to something like a terminal, this is used
    /// to fetch the dimensions of the terminal.
    ///
    /// If successful, returns `Ok((width, height))`.
    ///
    /// # Error
    ///
    /// This function will return an error if the output stream is not actually
    /// connected to a TTY instance, or if querying the TTY instance fails.
    pub fn winsize(&mut self) -> IoResult<(int, int)> {
        match self.inner {
            TTY(ref mut tty) => {
                tty.get_winsize().map_err(IoError::from_rtio_error)
            }
            File(..) => {
                Err(IoError {
                    kind: OtherIoError,
                    desc: "stream is not a tty",
                    detail: None,
                })
            }
        }
    }

    /// Controls whether this output stream is a "raw stream" or simply a normal
    /// stream.
    ///
    /// # Error
    ///
    /// This function will return an error if the output stream is not actually
    /// connected to a TTY instance, or if querying the TTY instance fails.
    pub fn set_raw(&mut self, raw: bool) -> IoResult<()> {
        match self.inner {
            TTY(ref mut tty) => {
                tty.set_raw(raw).map_err(IoError::from_rtio_error)
            }
            File(..) => {
                Err(IoError {
                    kind: OtherIoError,
                    desc: "stream is not a tty",
                    detail: None,
                })
            }
        }
    }

    /// Returns whether this stream is attached to a TTY instance or not.
    pub fn isatty(&self) -> bool {
        match self.inner {
            TTY(..) => true,
            File(..) => false,
        }
    }
}

impl Writer for StdWriter {
    fn write(&mut self, buf: &[u8]) -> IoResult<()> {
        // As with stdin on windows, stdout often can't handle writes of large
        // sizes. For an example, see #14940. For this reason, chunk the output
        // buffer on windows, but on unix we can just write the whole buffer all
        // at once.
        let max_size = if cfg!(windows) {64 * 1024} else {uint::MAX};
        for chunk in buf.chunks(max_size) {
            try!(match self.inner {
                TTY(ref mut tty) => tty.write(chunk),
                File(ref mut file) => file.write(chunk),
            }.map_err(IoError::from_rtio_error))
        }
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    iotest!(fn smoke() {
        // Just make sure we can acquire handles
        stdin();
        stdout();
        stderr();
    })

    iotest!(fn capture_stdout() {
        use io::{ChanReader, ChanWriter};

        let (tx, rx) = channel();
        let (mut r, w) = (ChanReader::new(rx), ChanWriter::new(tx));
        spawn(proc() {
            set_stdout(box w);
            println!("hello!");
        });
        assert_eq!(r.read_to_string().unwrap(), "hello!\n".to_string());
    })

    iotest!(fn capture_stderr() {
        use realstd::comm::channel;
        use realstd::io::{Writer, ChanReader, ChanWriter, Reader};

        let (tx, rx) = channel();
        let (mut r, w) = (ChanReader::new(rx), ChanWriter::new(tx));
        spawn(proc() {
            ::realstd::io::stdio::set_stderr(box w);
            fail!("my special message");
        });
        let s = r.read_to_string().unwrap();
        assert!(s.as_slice().contains("my special message"));
    })
}
<commit_msg>auto merge of #16952 : alexcrichton/rust/windows-large-console-write, r=brson<commit_after>// Copyright 2013 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.

/*! Non-blocking access to stdin, stdout, and stderr.

This module provides bindings to the local event loop's TTY interface, using it
to offer synchronous but non-blocking versions of stdio. These handles can be
inspected for information about terminal dimensions or for related information
about the stream or terminal to which it is attached.

# Example

```rust
# #![allow(unused_must_use)]
use std::io;

let mut out = io::stdout();
out.write(b"Hello, world!");
```

*/

use failure::local_stderr;
use fmt;
use io::{Reader, Writer, IoResult, IoError, OtherIoError,
         standard_error, EndOfFile, LineBufferedWriter, BufferedReader};
use iter::Iterator;
use kinds::Send;
use libc;
use option::{Option, Some, None};
use boxed::Box;
use result::{Ok, Err};
use rt;
use rt::local::Local;
use rt::task::Task;
use rt::rtio::{DontClose, IoFactory, LocalIo, RtioFileStream, RtioTTY};
use slice::ImmutableSlice;
use str::StrSlice;
use uint;

// And so begins the tale of acquiring a uv handle to a stdio stream on all
// platforms in all situations. Our story begins by splitting the world into two
// categories, windows and unix. Then one day the creators of unix said let
// there be redirection! And henceforth there was redirection away from the
// console for standard I/O streams.
//
// After this day, the world split into four factions:
//
// 1. Unix with stdout on a terminal.
// 2. Unix with stdout redirected.
// 3. Windows with stdout on a terminal.
// 4. Windows with stdout redirected.
//
// Many years passed, and then one day the nation of libuv decided to unify this
// world. After months of toiling, uv created three ideas: TTY, Pipe, File.
// These three ideas propagated throughout the lands and the four great factions
// decided to settle among them.
//
// The groups of 1, 2, and 3 all worked very hard towards the idea of TTY. Upon
// doing so, they even enhanced themselves further then their Pipe/File
// brethren, becoming the dominant powers.
//
// The group of 4, however, decided to work independently. They abandoned the
// common TTY belief throughout, and even abandoned the fledgling Pipe belief.
// The members of the 4th faction decided to only align themselves with File.
//
// tl;dr; TTY works on everything but when windows stdout is redirected, in that
//        case pipe also doesn't work, but magically file does!
enum StdSource {
    TTY(Box<RtioTTY + Send>),
    File(Box<RtioFileStream + Send>),
}

fn src<T>(fd: libc::c_int, readable: bool, f: |StdSource| -> T) -> T {
    LocalIo::maybe_raise(|io| {
        Ok(match io.tty_open(fd, readable) {
            Ok(tty) => f(TTY(tty)),
            Err(_) => f(File(io.fs_from_raw_fd(fd, DontClose))),
        })
    }).map_err(IoError::from_rtio_error).unwrap()
}

local_data_key!(local_stdout: Box<Writer + Send>)

/// Creates a new non-blocking handle to the stdin of the current process.
///
/// The returned handled is buffered by default with a `BufferedReader`. If
/// buffered access is not desired, the `stdin_raw` function is provided to
/// provided unbuffered access to stdin.
///
/// Care should be taken when creating multiple handles to the stdin of a
/// process. Because this is a buffered reader by default, it's possible for
/// pending input to be unconsumed in one reader and unavailable to other
/// readers. It is recommended that only one handle at a time is created for the
/// stdin of a process.
///
/// See `stdout()` for more notes about this function.
pub fn stdin() -> BufferedReader<StdReader> {
    // The default buffer capacity is 64k, but apparently windows doesn't like
    // 64k reads on stdin. See #13304 for details, but the idea is that on
    // windows we use a slightly smaller buffer that's been seen to be
    // acceptable.
    if cfg!(windows) {
        BufferedReader::with_capacity(8 * 1024, stdin_raw())
    } else {
        BufferedReader::new(stdin_raw())
    }
}

/// Creates a new non-blocking handle to the stdin of the current process.
///
/// Unlike `stdin()`, the returned reader is *not* a buffered reader.
///
/// See `stdout()` for more notes about this function.
pub fn stdin_raw() -> StdReader {
    src(libc::STDIN_FILENO, true, |src| StdReader { inner: src })
}

/// Creates a line-buffered handle to the stdout of the current process.
///
/// Note that this is a fairly expensive operation in that at least one memory
/// allocation is performed. Additionally, this must be called from a runtime
/// task context because the stream returned will be a non-blocking object using
/// the local scheduler to perform the I/O.
///
/// Care should be taken when creating multiple handles to an output stream for
/// a single process. While usage is still safe, the output may be surprising if
/// no synchronization is performed to ensure a sane output.
pub fn stdout() -> LineBufferedWriter<StdWriter> {
    LineBufferedWriter::new(stdout_raw())
}

/// Creates an unbuffered handle to the stdout of the current process
///
/// See notes in `stdout()` for more information.
pub fn stdout_raw() -> StdWriter {
    src(libc::STDOUT_FILENO, false, |src| StdWriter { inner: src })
}

/// Creates a line-buffered handle to the stderr of the current process.
///
/// See `stdout()` for notes about this function.
pub fn stderr() -> LineBufferedWriter<StdWriter> {
    LineBufferedWriter::new(stderr_raw())
}

/// Creates an unbuffered handle to the stderr of the current process
///
/// See notes in `stdout()` for more information.
pub fn stderr_raw() -> StdWriter {
    src(libc::STDERR_FILENO, false, |src| StdWriter { inner: src })
}

/// Resets the task-local stdout handle to the specified writer
///
/// This will replace the current task's stdout handle, returning the old
/// handle. All future calls to `print` and friends will emit their output to
/// this specified handle.
///
/// Note that this does not need to be called for all new tasks; the default
/// output handle is to the process's stdout stream.
pub fn set_stdout(stdout: Box<Writer + Send>) -> Option<Box<Writer + Send>> {
    local_stdout.replace(Some(stdout)).and_then(|mut s| {
        let _ = s.flush();
        Some(s)
    })
}

/// Resets the task-local stderr handle to the specified writer
///
/// This will replace the current task's stderr handle, returning the old
/// handle. Currently, the stderr handle is used for printing failure messages
/// during task failure.
///
/// Note that this does not need to be called for all new tasks; the default
/// output handle is to the process's stderr stream.
pub fn set_stderr(stderr: Box<Writer + Send>) -> Option<Box<Writer + Send>> {
    local_stderr.replace(Some(stderr)).and_then(|mut s| {
        let _ = s.flush();
        Some(s)
    })
}

// Helper to access the local task's stdout handle
//
// Note that this is not a safe function to expose because you can create an
// aliased pointer very easily:
//
//  with_task_stdout(|io1| {
//      with_task_stdout(|io2| {
//          // io1 aliases io2
//      })
//  })
fn with_task_stdout(f: |&mut Writer| -> IoResult<()>) {
    let result = if Local::exists(None::<Task>) {
        let mut my_stdout = local_stdout.replace(None).unwrap_or_else(|| {
            box stdout() as Box<Writer + Send>
        });
        let result = f(&mut *my_stdout);
        local_stdout.replace(Some(my_stdout));
        result
    } else {
        let mut io = rt::Stdout;
        f(&mut io as &mut Writer)
    };
    match result {
        Ok(()) => {}
        Err(e) => fail!("failed printing to stdout: {}", e),
    }
}

/// Flushes the local task's stdout handle.
///
/// By default, this stream is a line-buffering stream, so flushing may be
/// necessary to ensure that all output is printed to the screen (if there are
/// no newlines printed).
///
/// Note that logging macros do not use this stream. Using the logging macros
/// will emit output to stderr, and while they are line buffered the log
/// messages are always terminated in a newline (no need to flush).
pub fn flush() {
    with_task_stdout(|io| io.flush())
}

/// Prints a string to the stdout of the current process. No newline is emitted
/// after the string is printed.
pub fn print(s: &str) {
    with_task_stdout(|io| io.write(s.as_bytes()))
}

/// Prints a string to the stdout of the current process. A literal
/// `\n` character is printed to the console after the string.
pub fn println(s: &str) {
    with_task_stdout(|io| {
        io.write(s.as_bytes()).and_then(|()| io.write([b'\n']))
    })
}

/// Similar to `print`, but takes a `fmt::Arguments` structure to be compatible
/// with the `format_args!` macro.
pub fn print_args(fmt: &fmt::Arguments) {
    with_task_stdout(|io| write!(io, "{}", fmt))
}

/// Similar to `println`, but takes a `fmt::Arguments` structure to be
/// compatible with the `format_args!` macro.
pub fn println_args(fmt: &fmt::Arguments) {
    with_task_stdout(|io| writeln!(io, "{}", fmt))
}

/// Representation of a reader of a standard input stream
pub struct StdReader {
    inner: StdSource
}

impl StdReader {
    /// Returns whether this stream is attached to a TTY instance or not.
    pub fn isatty(&self) -> bool {
        match self.inner {
            TTY(..) => true,
            File(..) => false,
        }
    }
}

impl Reader for StdReader {
    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
        let ret = match self.inner {
            TTY(ref mut tty) => {
                // Flush the task-local stdout so that weird issues like a
                // print!'d prompt not being shown until after the user hits
                // enter.
                flush();
                tty.read(buf)
            },
            File(ref mut file) => file.read(buf).map(|i| i as uint),
        }.map_err(IoError::from_rtio_error);
        match ret {
            // When reading a piped stdin, libuv will return 0-length reads when
            // stdin reaches EOF. For pretty much all other streams it will
            // return an actual EOF error, but apparently for stdin it's a
            // little different. Hence, here we convert a 0 length read to an
            // end-of-file indicator so the caller knows to stop reading.
            Ok(0) => { Err(standard_error(EndOfFile)) }
            ret @ Ok(..) | ret @ Err(..) => ret,
        }
    }
}

/// Representation of a writer to a standard output stream
pub struct StdWriter {
    inner: StdSource
}

impl StdWriter {
    /// Gets the size of this output window, if possible. This is typically used
    /// when the writer is attached to something like a terminal, this is used
    /// to fetch the dimensions of the terminal.
    ///
    /// If successful, returns `Ok((width, height))`.
    ///
    /// # Error
    ///
    /// This function will return an error if the output stream is not actually
    /// connected to a TTY instance, or if querying the TTY instance fails.
    pub fn winsize(&mut self) -> IoResult<(int, int)> {
        match self.inner {
            TTY(ref mut tty) => {
                tty.get_winsize().map_err(IoError::from_rtio_error)
            }
            File(..) => {
                Err(IoError {
                    kind: OtherIoError,
                    desc: "stream is not a tty",
                    detail: None,
                })
            }
        }
    }

    /// Controls whether this output stream is a "raw stream" or simply a normal
    /// stream.
    ///
    /// # Error
    ///
    /// This function will return an error if the output stream is not actually
    /// connected to a TTY instance, or if querying the TTY instance fails.
    pub fn set_raw(&mut self, raw: bool) -> IoResult<()> {
        match self.inner {
            TTY(ref mut tty) => {
                tty.set_raw(raw).map_err(IoError::from_rtio_error)
            }
            File(..) => {
                Err(IoError {
                    kind: OtherIoError,
                    desc: "stream is not a tty",
                    detail: None,
                })
            }
        }
    }

    /// Returns whether this stream is attached to a TTY instance or not.
    pub fn isatty(&self) -> bool {
        match self.inner {
            TTY(..) => true,
            File(..) => false,
        }
    }
}

impl Writer for StdWriter {
    fn write(&mut self, buf: &[u8]) -> IoResult<()> {
        // As with stdin on windows, stdout often can't handle writes of large
        // sizes. For an example, see #14940. For this reason, chunk the output
        // buffer on windows, but on unix we can just write the whole buffer all
        // at once.
        //
        // For some other references, it appears that this problem has been
        // encountered by others [1] [2]. We choose the number 8KB just because
        // libuv does the same.
        //
        // [1]: https://tahoe-lafs.org/trac/tahoe-lafs/ticket/1232
        // [2]: http://www.mail-archive.com/log4net-dev@logging.apache.org/msg00661.html
        let max_size = if cfg!(windows) {8192} else {uint::MAX};
        for chunk in buf.chunks(max_size) {
            try!(match self.inner {
                TTY(ref mut tty) => tty.write(chunk),
                File(ref mut file) => file.write(chunk),
            }.map_err(IoError::from_rtio_error))
        }
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    iotest!(fn smoke() {
        // Just make sure we can acquire handles
        stdin();
        stdout();
        stderr();
    })

    iotest!(fn capture_stdout() {
        use io::{ChanReader, ChanWriter};

        let (tx, rx) = channel();
        let (mut r, w) = (ChanReader::new(rx), ChanWriter::new(tx));
        spawn(proc() {
            set_stdout(box w);
            println!("hello!");
        });
        assert_eq!(r.read_to_string().unwrap(), "hello!\n".to_string());
    })

    iotest!(fn capture_stderr() {
        use realstd::comm::channel;
        use realstd::io::{Writer, ChanReader, ChanWriter, Reader};

        let (tx, rx) = channel();
        let (mut r, w) = (ChanReader::new(rx), ChanWriter::new(tx));
        spawn(proc() {
            ::realstd::io::stdio::set_stderr(box w);
            fail!("my special message");
        });
        let s = r.read_to_string().unwrap();
        assert!(s.as_slice().contains("my special message"));
    })
}
<|endoftext|>


<commit_before>
fn compute_kmp_table(word: String) -> Vec<i32> {
	let mut table : Vec<i32> = vec![0; word.len()];
	let mut pos = 2;
	let mut cnd = 0;

	let word_chars : Vec<char> = word.chars().collect::<Vec<char>>();

	table[0] = -1;
	table[1] = 0;

	while pos < word.len() {
		if word_chars[pos - 1] == word_chars[cnd] {
			table[pos] = (cnd + 1) as i32;
			cnd += 1;
			pos += 1;
		} else if cnd > 0 {
			cnd = (table[cnd]) as usize;
		} else {
			table[pos] = 0;
			pos += 1;
		}
	}

	table
}

#[test]
fn compute_kmp_table_test_1() {
	let word : String = "ABCDABD".to_string();
	let expected_res : Vec<i32> = vec![-1, 0, 0, 0, 0, 1, 2];
	let res : Vec<i32> = compute_kmp_table(word);

    assert_eq!(res.len(), expected_res.len());

    for i in 0..res.len() {
    	assert_eq!(res[i], expected_res[i]);
    }
}<commit_msg>Knut Morris Pratt search implemented.<commit_after>fn compute_kmp_table(word: &String) -> Vec<i32> {
    let mut table : Vec<i32> = vec![0; word.len()];
    let mut pos = 2;
    let mut cnd = 0;

    let word_chars : Vec<char> = word.chars().collect::<Vec<char>>();

    table[0] = -1;
    table[1] = 0;

    while pos < word.len() {
        if word_chars[pos - 1] == word_chars[cnd] {
            table[pos] = (cnd + 1) as i32;
            cnd += 1;
            pos += 1;
        } else if cnd > 0 {
            cnd = (table[cnd]) as usize;
        } else {
            table[pos] = 0;
            pos += 1;
        }
    }

    table
}

fn kmp_serch(word: & String, text: & String) -> usize {
    let mut m : usize = 0;
    let mut i : usize = 0;
    let table : Vec<i32> = compute_kmp_table(&word);
    let text_len : usize = text.len();
    let word_len : usize = word.len();

    let word_chars : Vec<char> = word.chars().collect::<Vec<char>>();
    let text_chars : Vec<char> = text.chars().collect::<Vec<char>>();

    while m + i < text_len {
        if word_chars[i] == text_chars[m + i] {
            if i == word_len - 1 {
                return m;
            }
            i += 1;
        } else {
            if table[i] > -1 {
                m += i - (table[i] as usize);
                i = table[i] as usize;
            } else {
                i = 0;
                m += 1;
            }
        }
    }
    text_len // no match found
}

#[test]
fn compute_kmp_table_test_1() {
    let word : String = "ABCDABD".to_string();
    let expected_res : Vec<i32> = vec![-1, 0, 0, 0, 0, 1, 2];
    let res : Vec<i32> = compute_kmp_table(&word);

    assert_eq!(res.len(), expected_res.len());

    for i in 0..res.len() {
        assert_eq!(res[i], expected_res[i]);
    }
}

#[test]
fn kmp_serch_1() {
    let word : String = "ABCDABD".to_string();
    let text : String = "ABC ABCDAB ABCDABCDABDE".to_string();
    let expected_res : usize = 15;

    let res : usize = kmp_serch(&word, &text);

    assert_eq!(res, expected_res);

}<|endoftext|>


<commit_before>use std::fs::File;
use std::io::prelude::*;
use std::path::Path;

fn process_file(p: &str) -> ((u32, u32, u32, u32, u32, u32), [[f64; 4]; 4]) {
    let path = Path::new(p);
    let mut file = File::open(&path).expect("Unable to open");

    let mut s = String::new();
    file.read_to_string(&mut s).expect("not able to read");

    let mut ctr = (0, 0, 0, 0, 0, s.len() as u32);
    let mut is_beginning = true;
    let mut trans_mtrx: [[f64; 4]; 4] = [[0.0; 4]; 4];
    let mut prev_pos = 0;
    let mut curr_pos = 0;

    for c in s.chars() {
        match c {
            'A' => {
                ctr.0 += 1;
                curr_pos = 0;
            }
            'C' => {
                ctr.1 += 1;
                curr_pos = 1;
            }
            'G' => {
                ctr.2 += 1;
                curr_pos = 2;
            }
            'T' => {
                ctr.3 += 1;
                curr_pos = 3;
            }
            _ => println!("Not a valid letter"),
        }

        if is_beginning {
            ctr.4 = curr_pos as u32;
            is_beginning = false;
        } else {
            trans_mtrx[prev_pos][curr_pos] += 1.0;
            prev_pos = curr_pos;
        }
    }

    match s.pop().unwrap() {
        'A' => ctr.0 -= 1,
        'C' => ctr.1 -= 1,
        'G' => ctr.2 -= 1,
        'T' => ctr.3 -= 1,
        _ => println!("Not a valid letter"),
    }

    (ctr, trans_mtrx)
}

fn calc_trans_mtrx(nums: [u32; 4], trans_mtrx: &mut [[f64; 4]; 4]) {
    for i in 0..4 {
        for j in 0..4 {
            trans_mtrx[i][j] /= nums[i] as f64;
        }
    }
}

fn prob(first: u32, length: u32, prob_1: f64, prob_2: f64, prob_3: f64) -> f64 {
    (first as f64 / length as f64) * prob_1 * prob_2 * prob_3
}

fn main() {
    // let path = ;
    let (tuple, mut trans_mtrx) = process_file("practica_07.txt");
    calc_trans_mtrx([tuple.0, tuple.1, tuple.2, tuple.3], &mut trans_mtrx);

    let mut file = File::create("EPuma_Practica_07B.txt").expect("Couldn't open write file");
    // println!("{}\n",
    //          prob(tuple.0,
    //               tuple.5,
    //               trans_mtrx[0][1],
    //               trans_mtrx[1][2],
    //               trans_mtrx[2][3]));
    write!(file,
           "P(ATCA): {}\n",
           prob(tuple.0,
                tuple.5,
                trans_mtrx[0][3],
                trans_mtrx[3][1],
                trans_mtrx[1][0]))
            .expect("Couldn't write in file");
    write!(file,
           "P(CAGG): {}\n",
           prob(tuple.1 + 1,
                tuple.5,
                trans_mtrx[1][0],
                trans_mtrx[0][2],
                trans_mtrx[2][2]))
            .expect("Couldn't write in file");
    write!(file,
           "P(GAAT): {}\n",
           prob(tuple.2,
                tuple.5,
                trans_mtrx[2][0],
                trans_mtrx[0][0],
                trans_mtrx[0][3]))
            .expect("Couldn't write in file");
    write!(file,
           "P(TGAC): {}\n",
           prob(tuple.3,
                tuple.5,
                trans_mtrx[3][2],
                trans_mtrx[2][0],
                trans_mtrx[0][1]))
            .expect("Couldn't write in file");
}
<commit_msg>Message<commit_after>use std::fs::File;
use std::io::prelude::*;
use std::path::Path;

//Intento de push

fn process_file(p: &str) -> ((u32, u32, u32, u32, u32, u32), [[f64; 4]; 4]) {
    let path = Path::new(p);
    let mut file = File::open(&path).expect("Unable to open");

    let mut s = String::new();
    file.read_to_string(&mut s).expect("not able to read");

    let mut ctr = (0, 0, 0, 0, 0, s.len() as u32);
    let mut is_beginning = true;
    let mut trans_mtrx: [[f64; 4]; 4] = [[0.0; 4]; 4];
    let mut prev_pos = 0;
    let mut curr_pos = 0;

    for c in s.chars() {
        match c {
            'A' => {
                ctr.0 += 1;
                curr_pos = 0;
            }
            'C' => {
                ctr.1 += 1;
                curr_pos = 1;
            }
            'G' => {
                ctr.2 += 1;
                curr_pos = 2;
            }
            'T' => {
                ctr.3 += 1;
                curr_pos = 3;
            }
            _ => println!("Not a valid letter"),
        }

        if is_beginning {
            ctr.4 = curr_pos as u32;
            is_beginning = false;
        } else {
            trans_mtrx[prev_pos][curr_pos] += 1.0;
            prev_pos = curr_pos;
        }
    }

    match s.pop().unwrap() {
        'A' => ctr.0 -= 1,
        'C' => ctr.1 -= 1,
        'G' => ctr.2 -= 1,
        'T' => ctr.3 -= 1,
        _ => println!("Not a valid letter"),
    }

    (ctr, trans_mtrx)
}

fn calc_trans_mtrx(nums: [u32; 4], trans_mtrx: &mut [[f64; 4]; 4]) {
    for i in 0..4 {
        for j in 0..4 {
            trans_mtrx[i][j] /= nums[i] as f64;
        }
    }
}

fn prob(first: u32, length: u32, prob_1: f64, prob_2: f64, prob_3: f64) -> f64 {
    (first as f64 / length as f64) * prob_1 * prob_2 * prob_3
}

fn main() {
    // let path = ;
    let (tuple, mut trans_mtrx) = process_file("practica_07.txt");
    calc_trans_mtrx([tuple.0, tuple.1, tuple.2, tuple.3], &mut trans_mtrx);

    let mut file = File::create("EPuma_Practica_07B.txt").expect("Couldn't open write file");
    // println!("{}\n",
    //          prob(tuple.0,
    //               tuple.5,
    //               trans_mtrx[0][1],
    //               trans_mtrx[1][2],
    //               trans_mtrx[2][3]));
    write!(file,
           "P(ATCA): {}\n",
           prob(tuple.0,
                tuple.5,
                trans_mtrx[0][3],
                trans_mtrx[3][1],
                trans_mtrx[1][0]))
            .expect("Couldn't write in file");
    write!(file,
           "P(CAGG): {}\n",
           prob(tuple.1 + 1,
                tuple.5,
                trans_mtrx[1][0],
                trans_mtrx[0][2],
                trans_mtrx[2][2]))
            .expect("Couldn't write in file");
    write!(file,
           "P(GAAT): {}\n",
           prob(tuple.2,
                tuple.5,
                trans_mtrx[2][0],
                trans_mtrx[0][0],
                trans_mtrx[0][3]))
            .expect("Couldn't write in file");
    write!(file,
           "P(TGAC): {}\n",
           prob(tuple.3,
                tuple.5,
                trans_mtrx[3][2],
                trans_mtrx[2][0],
                trans_mtrx[0][1]))
            .expect("Couldn't write in file");
}
<|endoftext|>


<commit_before>//! Module that defines `SyntaxError`.

use std::fmt;

use crate::{TextRange, TextUnit};

/// Represents the result of unsuccessful tokenization, parsing
/// or semmantical analyzis.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct SyntaxError(String, TextRange);

// FIXME: there was an unused SyntaxErrorKind previously (before this enum was removed)
// It was introduced in this PR: https://github.com/rust-analyzer/rust-analyzer/pull/846/files#diff-827da9b03b8f9faa1bade5cdd44d5dafR95
// but it was not removed by a mistake.
//
// So, we need to find a place where to stick validation for attributes in match clauses.
// Code before refactor:
// InvalidMatchInnerAttr => {
//    write!(f, "Inner attributes are only allowed directly after the opening brace of the match expression")
// }

impl SyntaxError {
    pub fn new(message: impl Into<String>, range: TextRange) -> Self {
        Self(message.into(), range)
    }
    pub fn new_at_offset(message: impl Into<String>, offset: TextUnit) -> Self {
        Self(message.into(), TextRange::offset_len(offset, 1.into()))
    }

    pub fn message(&self) -> &str {
        &self.0
    }
    pub fn range(&self) -> &TextRange {
        &self.1
    }

    pub fn with_range(mut self, range: TextRange) -> Self {
        self.1 = range;
        self
    }
}

impl fmt::Display for SyntaxError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.0.fmt(f)
    }
}
<commit_msg>Change semmantical analyzis to tree validation as per @matklad<commit_after>//! Module that defines `SyntaxError`.

use std::fmt;

use crate::{TextRange, TextUnit};

/// Represents the result of unsuccessful tokenization, parsing
/// or tree validation.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct SyntaxError(String, TextRange);

// FIXME: there was an unused SyntaxErrorKind previously (before this enum was removed)
// It was introduced in this PR: https://github.com/rust-analyzer/rust-analyzer/pull/846/files#diff-827da9b03b8f9faa1bade5cdd44d5dafR95
// but it was not removed by a mistake.
//
// So, we need to find a place where to stick validation for attributes in match clauses.
// Code before refactor:
// InvalidMatchInnerAttr => {
//    write!(f, "Inner attributes are only allowed directly after the opening brace of the match expression")
// }

impl SyntaxError {
    pub fn new(message: impl Into<String>, range: TextRange) -> Self {
        Self(message.into(), range)
    }
    pub fn new_at_offset(message: impl Into<String>, offset: TextUnit) -> Self {
        Self(message.into(), TextRange::offset_len(offset, 1.into()))
    }

    pub fn message(&self) -> &str {
        &self.0
    }
    pub fn range(&self) -> &TextRange {
        &self.1
    }

    pub fn with_range(mut self, range: TextRange) -> Self {
        self.1 = range;
        self
    }
}

impl fmt::Display for SyntaxError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.0.fmt(f)
    }
}
<|endoftext|>


<commit_before>//! Transforms markdown
use ide_db::helpers::rust_doc::is_rust_fence;

const RUSTDOC_FENCE: &str = "```";

pub(crate) fn format_docs(src: &str) -> String {
    let mut processed_lines = Vec::new();
    let mut in_code_block = false;
    let mut is_rust = false;

    for mut line in src.lines() {
        if in_code_block && is_rust && code_line_ignored_by_rustdoc(line) {
            continue;
        }

        if let Some(header) = line.strip_prefix(RUSTDOC_FENCE) {
            in_code_block ^= true;

            if in_code_block {
                is_rust = is_rust_fence(header);

                if is_rust {
                    line = "```rust";
                }
            }
        }

        processed_lines.push(line);
    }
    processed_lines.join("\n")
}

fn code_line_ignored_by_rustdoc(line: &str) -> bool {
    let trimmed = line.trim();
    trimmed == "#" || trimmed.starts_with("# ") || trimmed.starts_with("#\t")
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_format_docs_adds_rust() {
        let comment = "```\nfn some_rust() {}\n```";
        assert_eq!(format_docs(comment), "```rust\nfn some_rust() {}\n```");
    }

    #[test]
    fn test_format_docs_handles_plain_text() {
        let comment = "```text\nthis is plain text\n```";
        assert_eq!(format_docs(comment), "```text\nthis is plain text\n```");
    }

    #[test]
    fn test_format_docs_handles_non_rust() {
        let comment = "```sh\nsupposedly shell code\n```";
        assert_eq!(format_docs(comment), "```sh\nsupposedly shell code\n```");
    }

    #[test]
    fn test_format_docs_handles_rust_alias() {
        let comment = "```ignore\nlet z = 55;\n```";
        assert_eq!(format_docs(comment), "```rust\nlet z = 55;\n```");
    }

    #[test]
    fn test_format_docs_handles_complex_code_block_attrs() {
        let comment = "```rust,no_run\nlet z = 55;\n```";
        assert_eq!(format_docs(comment), "```rust\nlet z = 55;\n```");
    }

    #[test]
    fn test_format_docs_handles_error_codes() {
        let comment = "```compile_fail,E0641\nlet b = 0 as *const _;\n```";
        assert_eq!(format_docs(comment), "```rust\nlet b = 0 as *const _;\n```");
    }

    #[test]
    fn test_format_docs_skips_comments_in_rust_block() {
        let comment =
            "```rust\n # skip1\n# skip2\n#stay1\nstay2\n#\n #\n   #    \n #\tskip3\n\t#\t\n```";
        assert_eq!(format_docs(comment), "```rust\n#stay1\nstay2\n```");
    }

    #[test]
    fn test_format_docs_does_not_skip_lines_if_plain_text() {
        let comment =
            "```text\n # stay1\n# stay2\n#stay3\nstay4\n#\n #\n   #    \n #\tstay5\n\t#\t\n```";
        assert_eq!(
            format_docs(comment),
            "```text\n # stay1\n# stay2\n#stay3\nstay4\n#\n #\n   #    \n #\tstay5\n\t#\t\n```",
        );
    }

    #[test]
    fn test_format_docs_keeps_comments_outside_of_rust_block() {
        let comment = " # stay1\n# stay2\n#stay3\nstay4\n#\n #\n   #    \n #\tstay5\n\t#\t";
        assert_eq!(format_docs(comment), comment);
    }

    #[test]
    fn test_format_docs_preserves_newlines() {
        let comment = "this\nis\nmultiline";
        assert_eq!(format_docs(comment), comment);
    }

    #[test]
    fn test_code_blocks_in_comments_marked_as_rust() {
        let comment = r#"```rust
fn main(){}
```
Some comment.
```
let a = 1;
```"#;

        assert_eq!(
            format_docs(comment),
            "```rust\nfn main(){}\n```\nSome comment.\n```rust\nlet a = 1;\n```"
        );
    }

    #[test]
    fn test_code_blocks_in_comments_marked_as_text() {
        let comment = r#"```text
filler
text
```
Some comment.
```
let a = 1;
```"#;

        assert_eq!(
            format_docs(comment),
            "```text\nfiller\ntext\n```\nSome comment.\n```rust\nlet a = 1;\n```"
        );
    }
}
<commit_msg>Fix: only shows one # when we encounter ##<commit_after>//! Transforms markdown
use std::borrow::Cow;

use ide_db::helpers::rust_doc::is_rust_fence;

const RUSTDOC_FENCE: &str = "```";

pub(crate) fn format_docs(src: &str) -> String {
    let mut processed_lines = Vec::new();
    let mut in_code_block = false;
    let mut is_rust = false;

    for line in src.lines() {
        let mut line = Cow::from(line);
        if in_code_block && is_rust && code_line_ignored_by_rustdoc(&line) {
            continue;
        }

        if let Some(header) = line.strip_prefix(RUSTDOC_FENCE) {
            in_code_block ^= true;

            if in_code_block {
                is_rust = is_rust_fence(header);

                if is_rust {
                    line = Cow::Borrowed("```rust");
                }
            }
        }

        if in_code_block {
            let trimmed = line.trim();
            if trimmed.starts_with("##") {
                line = Cow::Owned(line.replacen("##", "#", 1));
            }
        }

        processed_lines.push(line);
    }
    processed_lines.join("\n")
}

fn code_line_ignored_by_rustdoc(line: &str) -> bool {
    let trimmed = line.trim();
    trimmed == "#" || trimmed.starts_with("# ") || trimmed.starts_with("#\t")
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_format_docs_adds_rust() {
        let comment = "```\nfn some_rust() {}\n```";
        assert_eq!(format_docs(comment), "```rust\nfn some_rust() {}\n```");
    }

    #[test]
    fn test_format_docs_handles_plain_text() {
        let comment = "```text\nthis is plain text\n```";
        assert_eq!(format_docs(comment), "```text\nthis is plain text\n```");
    }

    #[test]
    fn test_format_docs_handles_non_rust() {
        let comment = "```sh\nsupposedly shell code\n```";
        assert_eq!(format_docs(comment), "```sh\nsupposedly shell code\n```");
    }

    #[test]
    fn test_format_docs_handles_rust_alias() {
        let comment = "```ignore\nlet z = 55;\n```";
        assert_eq!(format_docs(comment), "```rust\nlet z = 55;\n```");
    }

    #[test]
    fn test_format_docs_handles_complex_code_block_attrs() {
        let comment = "```rust,no_run\nlet z = 55;\n```";
        assert_eq!(format_docs(comment), "```rust\nlet z = 55;\n```");
    }

    #[test]
    fn test_format_docs_handles_error_codes() {
        let comment = "```compile_fail,E0641\nlet b = 0 as *const _;\n```";
        assert_eq!(format_docs(comment), "```rust\nlet b = 0 as *const _;\n```");
    }

    #[test]
    fn test_format_docs_skips_comments_in_rust_block() {
        let comment =
            "```rust\n # skip1\n# skip2\n#stay1\nstay2\n#\n #\n   #    \n #\tskip3\n\t#\t\n```";
        assert_eq!(format_docs(comment), "```rust\n#stay1\nstay2\n```");
    }

    #[test]
    fn test_format_docs_does_not_skip_lines_if_plain_text() {
        let comment =
            "```text\n # stay1\n# stay2\n#stay3\nstay4\n#\n #\n   #    \n #\tstay5\n\t#\t\n```";
        assert_eq!(
            format_docs(comment),
            "```text\n # stay1\n# stay2\n#stay3\nstay4\n#\n #\n   #    \n #\tstay5\n\t#\t\n```",
        );
    }

    #[test]
    fn test_format_docs_keeps_comments_outside_of_rust_block() {
        let comment = " # stay1\n# stay2\n#stay3\nstay4\n#\n #\n   #    \n #\tstay5\n\t#\t";
        assert_eq!(format_docs(comment), comment);
    }

    #[test]
    fn test_format_docs_preserves_newlines() {
        let comment = "this\nis\nmultiline";
        assert_eq!(format_docs(comment), comment);
    }

    #[test]
    fn test_code_blocks_in_comments_marked_as_rust() {
        let comment = r#"```rust
fn main(){}
```
Some comment.
```
let a = 1;
```"#;

        assert_eq!(
            format_docs(comment),
            "```rust\nfn main(){}\n```\nSome comment.\n```rust\nlet a = 1;\n```"
        );
    }

    #[test]
    fn test_code_blocks_in_comments_marked_as_text() {
        let comment = r#"```text
filler
text
```
Some comment.
```
let a = 1;
```"#;

        assert_eq!(
            format_docs(comment),
            "```text\nfiller\ntext\n```\nSome comment.\n```rust\nlet a = 1;\n```"
        );
    }

    #[test]
    fn test_format_docs_handles_escape_double_hashes() {
        let comment = r#"```rust
let s = "foo
## bar # baz";
```"#;

        assert_eq!(format_docs(comment), "```rust\nlet s = \"foo\n# bar # baz\";\n```");
    }
}
<|endoftext|>