<commit_before>#![cfg(feature = "s3")]
extern crate rusoto_core;
extern crate rusoto_s3;
extern crate time;
extern crate hyper;
extern crate env_logger;
extern crate log;

use hyper::Client;
use std::fs::File;
use std::io::Read;
use time::get_time;

use rusoto_core::{DefaultCredentialsProvider, Region};
use rusoto_s3::{S3, S3Client, HeadObjectRequest, CopyObjectRequest, GetObjectRequest,
                 PutObjectRequest, DeleteObjectRequest, PutBucketCorsRequest, CORSConfiguration,
                 CORSRule, CreateBucketRequest, DeleteBucketRequest, CreateMultipartUploadRequest,
                 UploadPartRequest, CompleteMultipartUploadRequest, CompletedMultipartUpload,
                 CompletedPart, ListObjectsV2Request};
use rusoto_core::default_tls_client;

type TestClient = S3Client<DefaultCredentialsProvider, Client>;

// Rust is in bad need of an integration test harness
// This creates the S3 resources needed for a suite of tests,
// executes those tests, and then destroys the resources
#[test]
fn test_all_the_things() {
    let _ = env_logger::init();

    let client = S3Client::new(default_tls_client().unwrap(),
                               DefaultCredentialsProvider::new().unwrap(),
                               Region::UsEast1);

    let test_bucket = format!("rusoto_test_bucket_{}", get_time().sec);
    let filename = format!("test_file_{}", get_time().sec);
    let utf8_filename = format!("test[über]file@{}", get_time().sec);
    let binary_filename = format!("test_file_b{}", get_time().sec);
    let multipart_filename = format!("test_multipart_file_{}", get_time().sec);

    // get a list of list_buckets
    test_list_buckets(&client);

    // create a bucket for these tests
    test_create_bucket(&client, &test_bucket);

    // list items v2
    list_items_in_bucket(&client, &test_bucket);

    // do a multipart upload
    test_multipart_upload(&client, &test_bucket, &multipart_filename);

    // modify the bucket's CORS properties
    test_put_bucket_cors(&client, &test_bucket);

    // PUT an object (no_credentials is an arbitrary choice)
    test_put_object_with_filename(&client,
                                  &test_bucket,
                                  &filename,
                                  &"tests/sample-data/no_credentials");

    // HEAD the object that was PUT
    test_head_object(&client, &test_bucket, &filename);

    // GET the object
    test_get_object(&client, &test_bucket, &filename);
    test_get_object_range(&client, &test_bucket, &filename);
    // copy the object to change its settings
    test_copy_object(&client, &test_bucket, &filename);

    // UTF8 filenames
    test_put_object_with_filename(&client,
                                  &test_bucket,
                                  &utf8_filename,
                                  &"tests/sample-data/no_credentials");

    test_copy_object_utf8(&client, &test_bucket, &utf8_filename);

    test_delete_object(&client, &test_bucket, &utf8_filename);

    // Binary objects:
    test_put_object_with_filename(&client,
                                  &test_bucket,
                                  &binary_filename,
                                  &"tests/sample-data/binary-file");
    test_get_object(&client, &test_bucket, &binary_filename);

    // paging test requires three items in the bucket, put another item there:
    // PUT an object (no_credentials is an arbitrary choice)
    let another_filename = format!("foo{}", filename);
    test_put_object_with_filename(&client,
                                  &test_bucket,
                                  &another_filename,
                                  &"tests/sample-data/no_credentials");

    // list items with paging
    list_items_in_bucket_paged(&client, &test_bucket);

    test_delete_object(&client, &test_bucket, &binary_filename);
    test_delete_object(&client, &test_bucket, &another_filename);

    // DELETE the object
    test_delete_object(&client, &test_bucket, &filename);

    // delete the test bucket
    test_delete_bucket(&client, &test_bucket);
}

fn test_multipart_upload(client: &TestClient, bucket: &str, filename: &str) {
    let create_multipart_req = CreateMultipartUploadRequest {
        bucket: bucket.to_owned(),
        key: filename.to_owned(),
        ..Default::default()
    };

    // start the multipart upload and note the upload_id generated
    let response = client.create_multipart_upload(&create_multipart_req).unwrap();
    println!("{:#?}", response);
    let upload_id = response.upload_id.unwrap();

    // create 2 upload parts
    let create_upload_part = |body: Vec<u8>, part_number: i64| -> UploadPartRequest {
        UploadPartRequest {
            body: Some(body),
            bucket: bucket.to_owned(),
            key: filename.to_owned(),
            upload_id: upload_id.to_owned(),
            part_number: part_number,
            ..Default::default()
        }
    };

    // minimum size for a non-final multipart upload part is 5MB
    let part_req1 = create_upload_part(vec!['a' as u8; 1024 * 1024 * 5], 1);
    let part_req2 = create_upload_part("foo".as_bytes().to_vec(), 2);

    // upload 2 parts and note the etags generated for them
    let mut completed_parts = Vec::new();
    for req in [part_req1, part_req2].into_iter() {
        let response = client.upload_part(&req).unwrap();
        println!("{:#?}", response);
        completed_parts.push(CompletedPart {
            e_tag: response.e_tag.clone(),
            part_number: Some(req.part_number),
        });
    }

    // complete the multipart upload with the etags of the parts
    let completed_upload = CompletedMultipartUpload { parts: Some(completed_parts) };

    let complete_req = CompleteMultipartUploadRequest {
        bucket: bucket.to_owned(),
        key: filename.to_owned(),
        upload_id: upload_id.to_owned(),
        multipart_upload: Some(completed_upload),
        ..Default::default()
    };

    let response = client.complete_multipart_upload(&complete_req).unwrap();
    println!("{:#?}", response);

    // delete the completed file
    test_delete_object(client, bucket, filename);
}

fn test_create_bucket(client: &TestClient, bucket: &str) {
    let create_bucket_req = CreateBucketRequest { bucket: bucket.to_owned(), ..Default::default() };

    let result = client.create_bucket(&create_bucket_req).unwrap();
    println!("{:#?}", result);
}

fn test_delete_bucket(client: &TestClient, bucket: &str) {
    let delete_bucket_req = DeleteBucketRequest { bucket: bucket.to_owned(), ..Default::default() };

    let result = client.delete_bucket(&delete_bucket_req).unwrap();
    println!("{:#?}", result);
}

fn test_put_object_with_filename(client: &TestClient,
                                 bucket: &str,
                                 dest_filename: &str,
                                 local_filename: &str) {
    let mut f = File::open(local_filename).unwrap();
    let mut contents: Vec<u8> = Vec::new();
    match f.read_to_end(&mut contents) {
        Err(why) => panic!("Error opening file to send to S3: {}", why),
        Ok(_) => {
            let req = PutObjectRequest {
                bucket: bucket.to_owned(),
                key: dest_filename.to_owned(),
                body: Some(contents),
                ..Default::default()
            };
            let result = client.put_object(&req);
            println!("{:#?}", result);
        }
    }
}

fn test_head_object(client: &TestClient, bucket: &str, filename: &str) {
    let head_req = HeadObjectRequest {
        bucket: bucket.to_owned(),
        key: filename.to_owned(),
        ..Default::default()
    };

    let result = client.head_object(&head_req).unwrap();
    println!("{:#?}", result);
}

fn test_get_object(client: &TestClient, bucket: &str, filename: &str) {
    let get_req = GetObjectRequest {
        bucket: bucket.to_owned(),
        key: filename.to_owned(),
        ..Default::default()
    };

    let result = client.get_object(&get_req).unwrap();
    println!("get object result: {:#?}", result);

    let mut body: Vec<u8> = Vec::new();
    assert!(result.body.unwrap().read_to_end(&mut body).is_ok());
    assert!(body.len() > 0);
}

fn test_get_object_range(client: &TestClient, bucket: &str, filename: &str) {
    let get_req = GetObjectRequest {
        bucket: bucket.to_owned(),
        key: filename.to_owned(),
        range: Some("bytes=0-1".to_owned()),
        ..Default::default()
    };

    let result = client.get_object(&get_req).unwrap();
    println!("\nget object range result: {:#?}", result);
    assert_eq!(result.content_length.unwrap(), 2);
}

fn test_copy_object(client: &TestClient, bucket: &str, filename: &str) {
    let req = CopyObjectRequest {
        bucket: bucket.to_owned(),
        key: filename.to_owned(),
        copy_source: format!("{}/{}", bucket, filename),
        cache_control: Some("max-age=123".to_owned()),
        content_type: Some("application/json".to_owned()),
        metadata_directive: Some("REPLACE".to_owned()),
        ..Default::default()
    };

    let result = client.copy_object(&req).unwrap();
    println!("{:#?}", result);
}

fn test_copy_object_utf8(client: &TestClient, bucket: &str, filename: &str) {
    let req = CopyObjectRequest {
        bucket: bucket.to_owned(),
        key: format!("{}", filename.to_owned()),
        copy_source: rusoto_s3::util::encode_key(format!("{}/{}", bucket, filename)),
        cache_control: Some("max-age=123".to_owned()),
        content_type: Some("application/json".to_owned()),
        metadata_directive: Some("REPLACE".to_owned()),
        ..Default::default()
    };

    let result = client.copy_object(&req).unwrap();
    println!("{:#?}", result);
}

fn test_delete_object(client: &TestClient, bucket: &str, filename: &str) {
    let del_req = DeleteObjectRequest {
        bucket: bucket.to_owned(),
        key: filename.to_owned(),
        ..Default::default()
    };

    let result = client.delete_object(&del_req).unwrap();
    println!("{:#?}", result);
}

fn test_list_buckets(client: &TestClient) {
    let result = client.list_buckets().unwrap();
    println!("\nbuckets available: {:#?}", result);
}

fn list_items_in_bucket(client: &TestClient, bucket: &str) {
    let list_obj_req = ListObjectsV2Request {
        bucket: bucket.to_owned(),
        start_after: Some("foo".to_owned()),
        ..Default::default()
    };
    let result = client.list_objects_v2(&list_obj_req).unwrap();
    println!("Items in bucket: {:#?}", result);
}

// Assuming there's already more than three item in our test bucket:
fn list_items_in_bucket_paged(client: &TestClient, bucket: &str) {
    let mut list_obj_req = ListObjectsV2Request {
        bucket: bucket.to_owned(),
        max_keys: Some(1),
        ..Default::default()
    };
    let result = client.list_objects_v2(&list_obj_req).unwrap();
    println!("Items in bucket, page 1: {:#?}", result);
    assert!(result.next_continuation_token.is_some());

    list_obj_req.continuation_token = result.next_continuation_token;
    let result = client.list_objects_v2(&list_obj_req).unwrap();
    println!("Items in bucket, page 2: {:#?}", result);
    // For the second call it the token is in `continuation_token` not `next_continuation_token`
    assert!(result.continuation_token.is_some());
}

fn test_put_bucket_cors(client: &TestClient, bucket: &str) {
    let cors_rules =
        vec![CORSRule {
                 allowed_methods: vec!["PUT".to_owned(), "POST".to_owned(), "DELETE".to_owned()],
                 allowed_origins: vec!["http://www.example.com".to_owned()],
                 allowed_headers: Some(vec!["*".to_owned()]),
                 max_age_seconds: Some(3000),
                 expose_headers: Some(vec!["x-amz-server-side-encryption".to_owned()]),
                 ..Default::default()
             }];

    let cors_configuration = CORSConfiguration { cors_rules: cors_rules };

    let req = PutBucketCorsRequest {
        bucket: bucket.to_owned(),
        cors_configuration: cors_configuration,
        ..Default::default()
    };

    let result = client.put_bucket_cors(&req).unwrap();
    println!("{:#?}", result);
}
<commit_msg>Make S3 GetObject test read in chunks<commit_after>#![cfg(feature = "s3")]
extern crate rusoto_core;
extern crate rusoto_s3;
extern crate time;
extern crate hyper;
extern crate env_logger;
extern crate log;

use hyper::Client;
use std::fs::File;
use std::io::Read;
use time::get_time;

use rusoto_core::{DefaultCredentialsProvider, Region};
use rusoto_s3::{S3, S3Client, HeadObjectRequest, CopyObjectRequest, GetObjectRequest,
                 PutObjectRequest, DeleteObjectRequest, PutBucketCorsRequest, CORSConfiguration,
                 CORSRule, CreateBucketRequest, DeleteBucketRequest, CreateMultipartUploadRequest,
                 UploadPartRequest, CompleteMultipartUploadRequest, CompletedMultipartUpload,
                 CompletedPart, ListObjectsV2Request};
use rusoto_core::default_tls_client;

type TestClient = S3Client<DefaultCredentialsProvider, Client>;

// Rust is in bad need of an integration test harness
// This creates the S3 resources needed for a suite of tests,
// executes those tests, and then destroys the resources
#[test]
fn test_all_the_things() {
    let _ = env_logger::init();

    let client = S3Client::new(default_tls_client().unwrap(),
                               DefaultCredentialsProvider::new().unwrap(),
                               Region::UsEast1);

    let test_bucket = format!("rusoto_test_bucket_{}", get_time().sec);
    let filename = format!("test_file_{}", get_time().sec);
    let utf8_filename = format!("test[über]file@{}", get_time().sec);
    let binary_filename = format!("test_file_b{}", get_time().sec);
    let multipart_filename = format!("test_multipart_file_{}", get_time().sec);

    // get a list of list_buckets
    test_list_buckets(&client);

    // create a bucket for these tests
    test_create_bucket(&client, &test_bucket);

    // list items v2
    list_items_in_bucket(&client, &test_bucket);

    // do a multipart upload
    test_multipart_upload(&client, &test_bucket, &multipart_filename);

    // modify the bucket's CORS properties
    test_put_bucket_cors(&client, &test_bucket);

    // PUT an object (no_credentials is an arbitrary choice)
    test_put_object_with_filename(&client,
                                  &test_bucket,
                                  &filename,
                                  &"tests/sample-data/no_credentials");

    // HEAD the object that was PUT
    test_head_object(&client, &test_bucket, &filename);

    // GET the object
    test_get_object(&client, &test_bucket, &filename);
    test_get_object_range(&client, &test_bucket, &filename);
    // copy the object to change its settings
    test_copy_object(&client, &test_bucket, &filename);

    // UTF8 filenames
    test_put_object_with_filename(&client,
                                  &test_bucket,
                                  &utf8_filename,
                                  &"tests/sample-data/no_credentials");

    test_copy_object_utf8(&client, &test_bucket, &utf8_filename);

    test_delete_object(&client, &test_bucket, &utf8_filename);

    // Binary objects:
    test_put_object_with_filename(&client,
                                  &test_bucket,
                                  &binary_filename,
                                  &"tests/sample-data/binary-file");
    test_get_object(&client, &test_bucket, &binary_filename);

    // paging test requires three items in the bucket, put another item there:
    // PUT an object (no_credentials is an arbitrary choice)
    let another_filename = format!("foo{}", filename);
    test_put_object_with_filename(&client,
                                  &test_bucket,
                                  &another_filename,
                                  &"tests/sample-data/no_credentials");

    // list items with paging
    list_items_in_bucket_paged(&client, &test_bucket);

    test_delete_object(&client, &test_bucket, &binary_filename);
    test_delete_object(&client, &test_bucket, &another_filename);

    // DELETE the object
    test_delete_object(&client, &test_bucket, &filename);

    // delete the test bucket
    test_delete_bucket(&client, &test_bucket);
}

fn test_multipart_upload(client: &TestClient, bucket: &str, filename: &str) {
    let create_multipart_req = CreateMultipartUploadRequest {
        bucket: bucket.to_owned(),
        key: filename.to_owned(),
        ..Default::default()
    };

    // start the multipart upload and note the upload_id generated
    let response = client.create_multipart_upload(&create_multipart_req).unwrap();
    println!("{:#?}", response);
    let upload_id = response.upload_id.unwrap();

    // create 2 upload parts
    let create_upload_part = |body: Vec<u8>, part_number: i64| -> UploadPartRequest {
        UploadPartRequest {
            body: Some(body),
            bucket: bucket.to_owned(),
            key: filename.to_owned(),
            upload_id: upload_id.to_owned(),
            part_number: part_number,
            ..Default::default()
        }
    };

    // minimum size for a non-final multipart upload part is 5MB
    let part_req1 = create_upload_part(vec!['a' as u8; 1024 * 1024 * 5], 1);
    let part_req2 = create_upload_part("foo".as_bytes().to_vec(), 2);

    // upload 2 parts and note the etags generated for them
    let mut completed_parts = Vec::new();
    for req in [part_req1, part_req2].into_iter() {
        let response = client.upload_part(&req).unwrap();
        println!("{:#?}", response);
        completed_parts.push(CompletedPart {
            e_tag: response.e_tag.clone(),
            part_number: Some(req.part_number),
        });
    }

    // complete the multipart upload with the etags of the parts
    let completed_upload = CompletedMultipartUpload { parts: Some(completed_parts) };

    let complete_req = CompleteMultipartUploadRequest {
        bucket: bucket.to_owned(),
        key: filename.to_owned(),
        upload_id: upload_id.to_owned(),
        multipart_upload: Some(completed_upload),
        ..Default::default()
    };

    let response = client.complete_multipart_upload(&complete_req).unwrap();
    println!("{:#?}", response);

    // delete the completed file
    test_delete_object(client, bucket, filename);
}

fn test_create_bucket(client: &TestClient, bucket: &str) {
    let create_bucket_req = CreateBucketRequest { bucket: bucket.to_owned(), ..Default::default() };

    let result = client.create_bucket(&create_bucket_req).unwrap();
    println!("{:#?}", result);
}

fn test_delete_bucket(client: &TestClient, bucket: &str) {
    let delete_bucket_req = DeleteBucketRequest { bucket: bucket.to_owned(), ..Default::default() };

    let result = client.delete_bucket(&delete_bucket_req).unwrap();
    println!("{:#?}", result);
}

fn test_put_object_with_filename(client: &TestClient,
                                 bucket: &str,
                                 dest_filename: &str,
                                 local_filename: &str) {
    let mut f = File::open(local_filename).unwrap();
    let mut contents: Vec<u8> = Vec::new();
    match f.read_to_end(&mut contents) {
        Err(why) => panic!("Error opening file to send to S3: {}", why),
        Ok(_) => {
            let req = PutObjectRequest {
                bucket: bucket.to_owned(),
                key: dest_filename.to_owned(),
                body: Some(contents),
                ..Default::default()
            };
            let result = client.put_object(&req);
            println!("{:#?}", result);
        }
    }
}

fn test_head_object(client: &TestClient, bucket: &str, filename: &str) {
    let head_req = HeadObjectRequest {
        bucket: bucket.to_owned(),
        key: filename.to_owned(),
        ..Default::default()
    };

    let result = client.head_object(&head_req).unwrap();
    println!("{:#?}", result);
}

fn test_get_object(client: &TestClient, bucket: &str, filename: &str) {
    let get_req = GetObjectRequest {
        bucket: bucket.to_owned(),
        key: filename.to_owned(),
        ..Default::default()
    };

    let result = client.get_object(&get_req).unwrap();
    println!("get object result: {:#?}", result);

    let mut body: Vec<u8> = Vec::new();
    let mut buf: [u8; 5] = [0; 5];
    let mut stream = result.body.unwrap();

    while let Ok(len) = stream.read(&mut buf) {
        if len == 0 {
            break;
        }

        println!("read {} bytes", len);
        body.extend_from_slice(&buf);
    }

    assert!(body.len() > 0);
}

fn test_get_object_range(client: &TestClient, bucket: &str, filename: &str) {
    let get_req = GetObjectRequest {
        bucket: bucket.to_owned(),
        key: filename.to_owned(),
        range: Some("bytes=0-1".to_owned()),
        ..Default::default()
    };

    let result = client.get_object(&get_req).unwrap();
    println!("\nget object range result: {:#?}", result);
    assert_eq!(result.content_length.unwrap(), 2);
}

fn test_copy_object(client: &TestClient, bucket: &str, filename: &str) {
    let req = CopyObjectRequest {
        bucket: bucket.to_owned(),
        key: filename.to_owned(),
        copy_source: format!("{}/{}", bucket, filename),
        cache_control: Some("max-age=123".to_owned()),
        content_type: Some("application/json".to_owned()),
        metadata_directive: Some("REPLACE".to_owned()),
        ..Default::default()
    };

    let result = client.copy_object(&req).unwrap();
    println!("{:#?}", result);
}

fn test_copy_object_utf8(client: &TestClient, bucket: &str, filename: &str) {
    let req = CopyObjectRequest {
        bucket: bucket.to_owned(),
        key: format!("{}", filename.to_owned()),
        copy_source: rusoto_s3::util::encode_key(format!("{}/{}", bucket, filename)),
        cache_control: Some("max-age=123".to_owned()),
        content_type: Some("application/json".to_owned()),
        metadata_directive: Some("REPLACE".to_owned()),
        ..Default::default()
    };

    let result = client.copy_object(&req).unwrap();
    println!("{:#?}", result);
}

fn test_delete_object(client: &TestClient, bucket: &str, filename: &str) {
    let del_req = DeleteObjectRequest {
        bucket: bucket.to_owned(),
        key: filename.to_owned(),
        ..Default::default()
    };

    let result = client.delete_object(&del_req).unwrap();
    println!("{:#?}", result);
}

fn test_list_buckets(client: &TestClient) {
    let result = client.list_buckets().unwrap();
    println!("\nbuckets available: {:#?}", result);
}

fn list_items_in_bucket(client: &TestClient, bucket: &str) {
    let list_obj_req = ListObjectsV2Request {
        bucket: bucket.to_owned(),
        start_after: Some("foo".to_owned()),
        ..Default::default()
    };
    let result = client.list_objects_v2(&list_obj_req).unwrap();
    println!("Items in bucket: {:#?}", result);
}

// Assuming there's already more than three item in our test bucket:
fn list_items_in_bucket_paged(client: &TestClient, bucket: &str) {
    let mut list_obj_req = ListObjectsV2Request {
        bucket: bucket.to_owned(),
        max_keys: Some(1),
        ..Default::default()
    };
    let result = client.list_objects_v2(&list_obj_req).unwrap();
    println!("Items in bucket, page 1: {:#?}", result);
    assert!(result.next_continuation_token.is_some());

    list_obj_req.continuation_token = result.next_continuation_token;
    let result = client.list_objects_v2(&list_obj_req).unwrap();
    println!("Items in bucket, page 2: {:#?}", result);
    // For the second call it the token is in `continuation_token` not `next_continuation_token`
    assert!(result.continuation_token.is_some());
}

fn test_put_bucket_cors(client: &TestClient, bucket: &str) {
    let cors_rules =
        vec![CORSRule {
                 allowed_methods: vec!["PUT".to_owned(), "POST".to_owned(), "DELETE".to_owned()],
                 allowed_origins: vec!["http://www.example.com".to_owned()],
                 allowed_headers: Some(vec!["*".to_owned()]),
                 max_age_seconds: Some(3000),
                 expose_headers: Some(vec!["x-amz-server-side-encryption".to_owned()]),
                 ..Default::default()
             }];

    let cors_configuration = CORSConfiguration { cors_rules: cors_rules };

    let req = PutBucketCorsRequest {
        bucket: bucket.to_owned(),
        cors_configuration: cors_configuration,
        ..Default::default()
    };

    let result = client.put_bucket_cors(&req).unwrap();
    println!("{:#?}", result);
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use std::ascii::AsciiExt;

use dom::attr::Attr;
use dom::attr::AttrHelpers;
use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
use dom::bindings::codegen::Bindings::EventTargetBinding::EventTargetMethods;
use dom::bindings::codegen::Bindings::HTMLScriptElementBinding;
use dom::bindings::codegen::Bindings::HTMLScriptElementBinding::HTMLScriptElementMethods;
use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
use dom::bindings::codegen::InheritTypes::{HTMLScriptElementDerived, HTMLScriptElementCast};
use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLElementCast, NodeCast};
use dom::bindings::codegen::InheritTypes::EventTargetCast;
use dom::bindings::global::Window;
use dom::bindings::js::{JSRef, Temporary, OptionalRootable};
use dom::bindings::utils::{Reflectable, Reflector};
use dom::document::Document;
use dom::element::{ElementTypeId, Element, AttributeHandlers, ElementCreator};
use dom::eventtarget::{EventTarget, EventTargetTypeId};
use dom::event::{Event, Bubbles, NotCancelable, EventHelpers};
use dom::htmlelement::HTMLElement;
use dom::node::{Node, NodeHelpers, NodeTypeId, window_from_node, CloneChildrenFlag};
use dom::virtualmethods::VirtualMethods;
use dom::window::WindowHelpers;

use encoding::all::UTF_8;
use encoding::types::{Encoding, DecoderTrap};
use servo_net::resource_task::load_whole_resource;
use servo_util::str::{DOMString, HTML_SPACE_CHARACTERS, StaticStringVec};
use std::cell::Cell;
use string_cache::Atom;
use url::UrlParser;

#[dom_struct]
pub struct HTMLScriptElement {
    htmlelement: HTMLElement,

    /// https://html.spec.whatwg.org/multipage/scripting.html#already-started
    already_started: Cell<bool>,

    /// https://html.spec.whatwg.org/multipage/scripting.html#parser-inserted
    parser_inserted: Cell<bool>,

    /// https://html.spec.whatwg.org/multipage/scripting.html#non-blocking
    ///
    /// (currently unused)
    non_blocking: Cell<bool>,

    /// https://html.spec.whatwg.org/multipage/scripting.html#ready-to-be-parser-executed
    ///
    /// (currently unused)
    ready_to_be_parser_executed: Cell<bool>,
}

impl HTMLScriptElementDerived for EventTarget {
    fn is_htmlscriptelement(&self) -> bool {
        *self.type_id() == EventTargetTypeId::Node(NodeTypeId::Element(ElementTypeId::HTMLScriptElement))
    }
}

impl HTMLScriptElement {
    fn new_inherited(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>,
                     creator: ElementCreator) -> HTMLScriptElement {
        HTMLScriptElement {
            htmlelement: HTMLElement::new_inherited(ElementTypeId::HTMLScriptElement, localName, prefix, document),
            already_started: Cell::new(false),
            parser_inserted: Cell::new(creator == ElementCreator::ParserCreated),
            non_blocking: Cell::new(creator != ElementCreator::ParserCreated),
            ready_to_be_parser_executed: Cell::new(false),
        }
    }

    #[allow(unrooted_must_root)]
    pub fn new(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>,
               creator: ElementCreator) -> Temporary<HTMLScriptElement> {
        let element = HTMLScriptElement::new_inherited(localName, prefix, document, creator);
        Node::reflect_node(box element, document, HTMLScriptElementBinding::Wrap)
    }
}

pub trait HTMLScriptElementHelpers {
    /// Prepare a script (<http://www.whatwg.org/html/#prepare-a-script>)
    fn prepare(self);

    /// Prepare a script, steps 6 and 7.
    fn is_javascript(self) -> bool;

    /// Set the "already started" flag (<https://whatwg.org/html/#already-started>)
    fn mark_already_started(self);
}

/// Supported script types as defined by
/// <http://whatwg.org/html/#support-the-scripting-language>.
static SCRIPT_JS_MIMES: StaticStringVec = &[
    "application/ecmascript",
    "application/javascript",
    "application/x-ecmascript",
    "application/x-javascript",
    "text/ecmascript",
    "text/javascript",
    "text/javascript1.0",
    "text/javascript1.1",
    "text/javascript1.2",
    "text/javascript1.3",
    "text/javascript1.4",
    "text/javascript1.5",
    "text/jscript",
    "text/livescript",
    "text/x-ecmascript",
    "text/x-javascript",
];

impl<'a> HTMLScriptElementHelpers for JSRef<'a, HTMLScriptElement> {
    fn prepare(self) {
        // https://html.spec.whatwg.org/multipage/scripting.html#prepare-a-script
        // Step 1.
        if self.already_started.get() {
            return;
        }
        // Step 2.
        let was_parser_inserted = self.parser_inserted.get();
        self.parser_inserted.set(false);

        // Step 3.
        let element: JSRef<Element> = ElementCast::from_ref(self);
        if was_parser_inserted && element.has_attribute(&atom!("async")) {
            self.non_blocking.set(true);
        }
        // Step 4.
        let text = self.Text();
        if text.len() == 0 && !element.has_attribute(&atom!("src")) {
            return;
        }
        // Step 5.
        let node: JSRef<Node> = NodeCast::from_ref(self);
        if !node.is_in_doc() {
            return;
        }
        // Step 6, 7.
        if !self.is_javascript() {
            return;
        }
        // Step 8.
        if was_parser_inserted {
            self.parser_inserted.set(true);
            self.non_blocking.set(false);
        }
        // Step 9.
        self.already_started.set(true);

        // Step 10.
        // TODO: If the element is flagged as "parser-inserted", but the element's node document is
        // not the Document of the parser that created the element, then abort these steps.

        // Step 11.
        // TODO: If scripting is disabled for the script element, then the user agent must abort
        // these steps at this point. The script is not executed.

        // Step 12.
        // TODO: If the script element has an `event` attribute and a `for` attribute, then run
        // these substeps...

        // Step 13.
        // TODO: If the script element has a `charset` attribute, then let the script block's
        // character encoding for this script element be the result of getting an encoding from the
        // value of the `charset` attribute.

        // Step 14 and 15.
        // TODO: Add support for the `defer` and `async` attributes.  (For now, we fetch all
        // scripts synchronously and execute them immediately.)
        let window = window_from_node(self).root();
        let page = window.page();
        let base_url = page.get_url();

        let (source, url) = match element.get_attribute(ns!(""), &atom!("src")).root() {
            Some(src) => {
                if src.deref().Value().is_empty() {
                    // TODO: queue a task to fire a simple event named `error` at the element
                    return;
                }
                match UrlParser::new().base_url(&base_url).parse(src.deref().Value().as_slice()) {
                    Ok(url) => {
                        // TODO: Do a potentially CORS-enabled fetch with the mode being the current
                        // state of the element's `crossorigin` content attribute, the origin being
                        // the origin of the script element's node document, and the default origin
                        // behaviour set to taint.
                        match load_whole_resource(&page.resource_task, url) {
                            Ok((metadata, bytes)) => {
                                // TODO: use the charset from step 13.
                                let source = UTF_8.decode(bytes.as_slice(), DecoderTrap::Replace).unwrap();
                                (source, metadata.final_url)
                            }
                            Err(_) => {
                                error!("error loading script {}", src.deref().Value());
                                return;
                            }
                        }
                    }
                    Err(_) => {
                        // TODO: queue a task to fire a simple event named `error` at the element
                        error!("error parsing URL for script {}", src.deref().Value());
                        return;
                    }
                }
            }
            None => (text, base_url)
        };

        window.evaluate_script_with_result(source.as_slice(), url.serialize().as_slice());

        let event = Event::new(Window(*window),
                               "load".to_string(),
                               Bubbles, NotCancelable).root();
        event.set_trusted(true);
        let target: JSRef<EventTarget> = EventTargetCast::from_ref(self);
        target.DispatchEvent(*event).ok();
    }

    fn is_javascript(self) -> bool {
        let element: JSRef<Element> = ElementCast::from_ref(self);
        match element.get_attribute(ns!(""), &atom!("type")).root().map(|s| s.Value()) {
            Some(ref s) if s.is_empty() => {
                // type attr exists, but empty means js
                debug!("script type empty, inferring js");
                true
            },
            Some(ref s) => {
                debug!("script type={:s}", *s);
                SCRIPT_JS_MIMES.contains(&s.to_ascii_lower().as_slice().trim_chars(HTML_SPACE_CHARACTERS))
            },
            None => {
                debug!("no script type");
                match element.get_attribute(ns!(""), &atom!("language"))
                             .root()
                             .map(|s| s.Value()) {
                    Some(ref s) if s.is_empty() => {
                        debug!("script language empty, inferring js");
                        true
                    },
                    Some(ref s) => {
                        debug!("script language={:s}", *s);
                        SCRIPT_JS_MIMES.contains(&format!("text/{}", s).to_ascii_lower().as_slice())
                    },
                    None => {
                        debug!("no script type or language, inferring js");
                        true
                    }
                }
            }
        }
    }

    fn mark_already_started(self) {
        self.already_started.set(true);
    }
}

impl<'a> VirtualMethods for JSRef<'a, HTMLScriptElement> {
    fn super_type<'a>(&'a self) -> Option<&'a VirtualMethods> {
        let htmlelement: &JSRef<HTMLElement> = HTMLElementCast::from_borrowed_ref(self);
        Some(htmlelement as &VirtualMethods)
    }

    fn after_set_attr(&self, attr: JSRef<Attr>) {
        match self.super_type() {
            Some(ref s) => s.after_set_attr(attr),
            _ => (),
        }
        let node: JSRef<Node> = NodeCast::from_ref(*self);
        if attr.local_name() == &atom!("src") && !self.parser_inserted.get() && node.is_in_doc() {
            self.prepare();
        }
    }

    fn child_inserted(&self, child: JSRef<Node>) {
        match self.super_type() {
            Some(ref s) => s.child_inserted(child),
            _ => (),
        }
        let node: JSRef<Node> = NodeCast::from_ref(*self);
        if !self.parser_inserted.get() && node.is_in_doc() {
            self.prepare();
        }
    }

    fn bind_to_tree(&self, tree_in_doc: bool) {
        match self.super_type() {
            Some(ref s) => s.bind_to_tree(tree_in_doc),
            _ => ()
        }

        if tree_in_doc && !self.parser_inserted.get() {
            self.prepare();
        }
    }

    fn cloning_steps(&self, copy: JSRef<Node>, maybe_doc: Option<JSRef<Document>>,
                     clone_children: CloneChildrenFlag) {
        match self.super_type() {
            Some(ref s) => s.cloning_steps(copy, maybe_doc, clone_children),
            _ => (),
        }

        // https://whatwg.org/html/#already-started
        if self.already_started.get() {
            let copy_elem: JSRef<HTMLScriptElement> = HTMLScriptElementCast::to_ref(copy).unwrap();
            copy_elem.mark_already_started();
        }
    }
}

impl<'a> HTMLScriptElementMethods for JSRef<'a, HTMLScriptElement> {
    make_url_getter!(Src)

    make_setter!(SetSrc, "src")

    // http://www.whatwg.org/html/#dom-script-text
    fn Text(self) -> DOMString {
        let node: JSRef<Node> = NodeCast::from_ref(self);
        Node::collect_text_contents(node.children())
    }

    // http://www.whatwg.org/html/#dom-script-text
    fn SetText(self, value: DOMString) {
        let node: JSRef<Node> = NodeCast::from_ref(self);
        node.SetTextContent(Some(value))
    }
}

impl Reflectable for HTMLScriptElement {
    fn reflector<'a>(&'a self) -> &'a Reflector {
        self.htmlelement.reflector()
    }
}
<commit_msg>update past rustup and use DoesNotBubble instead of Bubbles<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use std::ascii::AsciiExt;

use dom::attr::Attr;
use dom::attr::AttrHelpers;
use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
use dom::bindings::codegen::Bindings::HTMLScriptElementBinding;
use dom::bindings::codegen::Bindings::HTMLScriptElementBinding::HTMLScriptElementMethods;
use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
use dom::bindings::codegen::InheritTypes::{HTMLScriptElementDerived, HTMLScriptElementCast};
use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLElementCast, NodeCast};
use dom::bindings::codegen::InheritTypes::EventTargetCast;
use dom::bindings::global::Window;
use dom::bindings::js::{JSRef, Temporary, OptionalRootable};
use dom::bindings::utils::{Reflectable, Reflector};
use dom::document::Document;
use dom::element::{ElementTypeId, Element, AttributeHandlers, ElementCreator};
use dom::eventtarget::{EventTarget, EventTargetTypeId, EventTargetHelpers};
use dom::event::{Event, EventBubbles, EventCancelable, EventHelpers};
use dom::htmlelement::HTMLElement;
use dom::node::{Node, NodeHelpers, NodeTypeId, window_from_node, CloneChildrenFlag};
use dom::virtualmethods::VirtualMethods;
use dom::window::WindowHelpers;

use encoding::all::UTF_8;
use encoding::types::{Encoding, DecoderTrap};
use servo_net::resource_task::load_whole_resource;
use servo_util::str::{DOMString, HTML_SPACE_CHARACTERS, StaticStringVec};
use std::cell::Cell;
use string_cache::Atom;
use url::UrlParser;

#[dom_struct]
pub struct HTMLScriptElement {
    htmlelement: HTMLElement,

    /// https://html.spec.whatwg.org/multipage/scripting.html#already-started
    already_started: Cell<bool>,

    /// https://html.spec.whatwg.org/multipage/scripting.html#parser-inserted
    parser_inserted: Cell<bool>,

    /// https://html.spec.whatwg.org/multipage/scripting.html#non-blocking
    ///
    /// (currently unused)
    non_blocking: Cell<bool>,

    /// https://html.spec.whatwg.org/multipage/scripting.html#ready-to-be-parser-executed
    ///
    /// (currently unused)
    ready_to_be_parser_executed: Cell<bool>,
}

impl HTMLScriptElementDerived for EventTarget {
    fn is_htmlscriptelement(&self) -> bool {
        *self.type_id() == EventTargetTypeId::Node(NodeTypeId::Element(ElementTypeId::HTMLScriptElement))
    }
}

impl HTMLScriptElement {
    fn new_inherited(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>,
                     creator: ElementCreator) -> HTMLScriptElement {
        HTMLScriptElement {
            htmlelement: HTMLElement::new_inherited(ElementTypeId::HTMLScriptElement, localName, prefix, document),
            already_started: Cell::new(false),
            parser_inserted: Cell::new(creator == ElementCreator::ParserCreated),
            non_blocking: Cell::new(creator != ElementCreator::ParserCreated),
            ready_to_be_parser_executed: Cell::new(false),
        }
    }

    #[allow(unrooted_must_root)]
    pub fn new(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>,
               creator: ElementCreator) -> Temporary<HTMLScriptElement> {
        let element = HTMLScriptElement::new_inherited(localName, prefix, document, creator);
        Node::reflect_node(box element, document, HTMLScriptElementBinding::Wrap)
    }
}

pub trait HTMLScriptElementHelpers {
    /// Prepare a script (<http://www.whatwg.org/html/#prepare-a-script>)
    fn prepare(self);

    /// Prepare a script, steps 6 and 7.
    fn is_javascript(self) -> bool;

    /// Set the "already started" flag (<https://whatwg.org/html/#already-started>)
    fn mark_already_started(self);
}

/// Supported script types as defined by
/// <http://whatwg.org/html/#support-the-scripting-language>.
static SCRIPT_JS_MIMES: StaticStringVec = &[
    "application/ecmascript",
    "application/javascript",
    "application/x-ecmascript",
    "application/x-javascript",
    "text/ecmascript",
    "text/javascript",
    "text/javascript1.0",
    "text/javascript1.1",
    "text/javascript1.2",
    "text/javascript1.3",
    "text/javascript1.4",
    "text/javascript1.5",
    "text/jscript",
    "text/livescript",
    "text/x-ecmascript",
    "text/x-javascript",
];

impl<'a> HTMLScriptElementHelpers for JSRef<'a, HTMLScriptElement> {
    fn prepare(self) {
        // https://html.spec.whatwg.org/multipage/scripting.html#prepare-a-script
        // Step 1.
        if self.already_started.get() {
            return;
        }
        // Step 2.
        let was_parser_inserted = self.parser_inserted.get();
        self.parser_inserted.set(false);

        // Step 3.
        let element: JSRef<Element> = ElementCast::from_ref(self);
        if was_parser_inserted && element.has_attribute(&atom!("async")) {
            self.non_blocking.set(true);
        }
        // Step 4.
        let text = self.Text();
        if text.len() == 0 && !element.has_attribute(&atom!("src")) {
            return;
        }
        // Step 5.
        let node: JSRef<Node> = NodeCast::from_ref(self);
        if !node.is_in_doc() {
            return;
        }
        // Step 6, 7.
        if !self.is_javascript() {
            return;
        }
        // Step 8.
        if was_parser_inserted {
            self.parser_inserted.set(true);
            self.non_blocking.set(false);
        }
        // Step 9.
        self.already_started.set(true);

        // Step 10.
        // TODO: If the element is flagged as "parser-inserted", but the element's node document is
        // not the Document of the parser that created the element, then abort these steps.

        // Step 11.
        // TODO: If scripting is disabled for the script element, then the user agent must abort
        // these steps at this point. The script is not executed.

        // Step 12.
        // TODO: If the script element has an `event` attribute and a `for` attribute, then run
        // these substeps...

        // Step 13.
        // TODO: If the script element has a `charset` attribute, then let the script block's
        // character encoding for this script element be the result of getting an encoding from the
        // value of the `charset` attribute.

        // Step 14 and 15.
        // TODO: Add support for the `defer` and `async` attributes.  (For now, we fetch all
        // scripts synchronously and execute them immediately.)
        let window = window_from_node(self).root();
        let page = window.page();
        let base_url = page.get_url();

        let (source, url) = match element.get_attribute(ns!(""), &atom!("src")).root() {
            Some(src) => {
                if src.deref().Value().is_empty() {
                    // TODO: queue a task to fire a simple event named `error` at the element
                    return;
                }
                match UrlParser::new().base_url(&base_url).parse(src.deref().Value().as_slice()) {
                    Ok(url) => {
                        // TODO: Do a potentially CORS-enabled fetch with the mode being the current
                        // state of the element's `crossorigin` content attribute, the origin being
                        // the origin of the script element's node document, and the default origin
                        // behaviour set to taint.
                        match load_whole_resource(&page.resource_task, url) {
                            Ok((metadata, bytes)) => {
                                // TODO: use the charset from step 13.
                                let source = UTF_8.decode(bytes.as_slice(), DecoderTrap::Replace).unwrap();
                                (source, metadata.final_url)
                            }
                            Err(_) => {
                                error!("error loading script {}", src.deref().Value());
                                return;
                            }
                        }
                    }
                    Err(_) => {
                        // TODO: queue a task to fire a simple event named `error` at the element
                        error!("error parsing URL for script {}", src.deref().Value());
                        return;
                    }
                }
            }
            None => (text, base_url)
        };

        window.evaluate_script_with_result(source.as_slice(), url.serialize().as_slice());

        let event = Event::new(Window(*window),
                               "load".to_string(),
                               EventBubbles::DoesNotBubble,
                               EventCancelable::NotCancelable).root();
        event.set_trusted(true);
        let target: JSRef<EventTarget> = EventTargetCast::from_ref(self);
        target.dispatch_event(*event);
    }

    fn is_javascript(self) -> bool {
        let element: JSRef<Element> = ElementCast::from_ref(self);
        match element.get_attribute(ns!(""), &atom!("type")).root().map(|s| s.Value()) {
            Some(ref s) if s.is_empty() => {
                // type attr exists, but empty means js
                debug!("script type empty, inferring js");
                true
            },
            Some(ref s) => {
                debug!("script type={:s}", *s);
                SCRIPT_JS_MIMES.contains(&s.to_ascii_lower().as_slice().trim_chars(HTML_SPACE_CHARACTERS))
            },
            None => {
                debug!("no script type");
                match element.get_attribute(ns!(""), &atom!("language"))
                             .root()
                             .map(|s| s.Value()) {
                    Some(ref s) if s.is_empty() => {
                        debug!("script language empty, inferring js");
                        true
                    },
                    Some(ref s) => {
                        debug!("script language={:s}", *s);
                        SCRIPT_JS_MIMES.contains(&format!("text/{}", s).to_ascii_lower().as_slice())
                    },
                    None => {
                        debug!("no script type or language, inferring js");
                        true
                    }
                }
            }
        }
    }

    fn mark_already_started(self) {
        self.already_started.set(true);
    }
}

impl<'a> VirtualMethods for JSRef<'a, HTMLScriptElement> {
    fn super_type<'a>(&'a self) -> Option<&'a VirtualMethods> {
        let htmlelement: &JSRef<HTMLElement> = HTMLElementCast::from_borrowed_ref(self);
        Some(htmlelement as &VirtualMethods)
    }

    fn after_set_attr(&self, attr: JSRef<Attr>) {
        match self.super_type() {
            Some(ref s) => s.after_set_attr(attr),
            _ => (),
        }
        let node: JSRef<Node> = NodeCast::from_ref(*self);
        if attr.local_name() == &atom!("src") && !self.parser_inserted.get() && node.is_in_doc() {
            self.prepare();
        }
    }

    fn child_inserted(&self, child: JSRef<Node>) {
        match self.super_type() {
            Some(ref s) => s.child_inserted(child),
            _ => (),
        }
        let node: JSRef<Node> = NodeCast::from_ref(*self);
        if !self.parser_inserted.get() && node.is_in_doc() {
            self.prepare();
        }
    }

    fn bind_to_tree(&self, tree_in_doc: bool) {
        match self.super_type() {
            Some(ref s) => s.bind_to_tree(tree_in_doc),
            _ => ()
        }

        if tree_in_doc && !self.parser_inserted.get() {
            self.prepare();
        }
    }

    fn cloning_steps(&self, copy: JSRef<Node>, maybe_doc: Option<JSRef<Document>>,
                     clone_children: CloneChildrenFlag) {
        match self.super_type() {
            Some(ref s) => s.cloning_steps(copy, maybe_doc, clone_children),
            _ => (),
        }

        // https://whatwg.org/html/#already-started
        if self.already_started.get() {
            let copy_elem: JSRef<HTMLScriptElement> = HTMLScriptElementCast::to_ref(copy).unwrap();
            copy_elem.mark_already_started();
        }
    }
}

impl<'a> HTMLScriptElementMethods for JSRef<'a, HTMLScriptElement> {
    make_url_getter!(Src)

    make_setter!(SetSrc, "src")

    // http://www.whatwg.org/html/#dom-script-text
    fn Text(self) -> DOMString {
        let node: JSRef<Node> = NodeCast::from_ref(self);
        Node::collect_text_contents(node.children())
    }

    // http://www.whatwg.org/html/#dom-script-text
    fn SetText(self, value: DOMString) {
        let node: JSRef<Node> = NodeCast::from_ref(self);
        node.SetTextContent(Some(value))
    }
}

impl Reflectable for HTMLScriptElement {
    fn reflector<'a>(&'a self) -> &'a Reflector {
        self.htmlelement.reflector()
    }
}
<|endoftext|>


<commit_before>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

extern crate clap;
#[macro_use] extern crate version;
#[macro_use] extern crate log;
extern crate walkdir;
extern crate toml;
extern crate toml_query;

extern crate libimagrt;
extern crate libimagerror;

use std::env;
use std::process::exit;
use std::process::Command;
use std::process::Stdio;
use std::io::ErrorKind;
use std::collections::BTreeMap;

use walkdir::WalkDir;
use clap::{Arg, ArgMatches, AppSettings, SubCommand};
use toml::Value;
use toml_query::read::TomlValueReadExt;

use libimagrt::runtime::Runtime;
use libimagerror::trace::trace_error;

/// Returns the helptext, putting the Strings in cmds as possible
/// subcommands into it
fn help_text(cmds: Vec<String>) -> String {
    format!(r#"

     _
    (_)_ __ ___   __ _  __ _
    | | '_ \` _ \/ _\`|/ _\`|
    | | | | | | | (_| | (_| |
    |_|_| |_| |_|\__,_|\__, |
                       |___/
    -------------------------

    Usage: imag [--version | --versions | -h | --help] <command> <args...>

    imag - the personal information management suite for the commandline

    imag is a PIM suite for the commandline. It consists of several commands,
    called "modules". Each module implements one PIM aspect and all of these
    modules can be used independently.

    Available commands:

    {imagbins}

    Call a command with 'imag <command> <args>'
    Each command can be called with "--help" to get the respective helptext.

    Please visit https://github.com/matthiasbeyer/imag to view the source code,
    follow the development of imag or maybe even contribute to imag.

    imag is free software. It is released under the terms of LGPLv2.1

    (c) 2016 Matthias Beyer and contributors"#,
        imagbins = cmds
            .into_iter()
            .map(|cmd| format!("\t{}\n", cmd))
            .fold(String::new(), |s, c| {
                let s = s + c.as_str();
                s
            }))
}

/// Returns the list of imag-* executables found in $PATH
fn get_commands() -> Vec<String> {
    match env::var("PATH") {
        Err(e) => {
            println!("PATH error: {:?}", e);
            exit(1);
        },

        Ok(path) => path
            .split(":")
            .flat_map(|elem| {
                WalkDir::new(elem)
                    .max_depth(1)
                    .into_iter()
                    .filter(|path| match *path {
                        Ok(ref p) => p.file_name().to_str().map_or(false, |f| f.starts_with("imag-")),
                        Err(_)    => false,
                    })
                    .filter_map(Result::ok)
                    .filter_map(|path| path
                        .file_name()
                       .to_str()
                       .and_then(|s| s.splitn(2, "-").nth(1).map(String::from))
                    )
            })
            .collect()
    }
}

fn main() {
    // Initialize the Runtime and build the CLI
    let appname  = "imag";
    let version  = &version!();
    let about    = "imag - the PIM suite for the commandline";
    let commands = get_commands();
    let helptext = help_text(commands.clone());
    let app      = Runtime::get_default_cli_builder(appname, version, about)
        .settings(&[AppSettings::AllowExternalSubcommands, AppSettings::ArgRequiredElseHelp])
        .arg(Arg::with_name("version")
             .long("version")
             .takes_value(false)
             .required(false)
             .multiple(false)
             .help("Get the version of imag"))
        .arg(Arg::with_name("versions")
             .long("versions")
             .takes_value(false)
             .required(false)
             .multiple(false)
             .help("Get the versions of the imag commands"))
        .subcommand(SubCommand::with_name("help").help("Show help"))
        .help(helptext.as_str());
    let rt = Runtime::new(app)
        .unwrap_or_else(|e| {
            println!("Runtime couldn't be setup. Exiting");
            trace_error(&e);
            exit(1);
        });
    let matches = rt.cli();

    debug!("matches: {:?}", matches);

    // Begin checking for arguments

    if matches.is_present("version") {
        debug!("Showing version");
        println!("imag {}", &version!()[..]);
        exit(0);
    }

    if matches.is_present("versions") {
        debug!("Showing versions");
        commands
            .iter()
            .map(|command| {
                match Command::new(format!("imag-{}", command))
                    .arg("--version")
                    .output()
                    .map(|v| v.stdout)
                {
                    Ok(s) => match String::from_utf8(s) {
                        Ok(s) => format!("{:10} -> {}", command, s),
                        Err(e) => format!("UTF8 Error while working with output of imag{}: {:?}", command, e),
                    },
                    Err(e) => format!("Failed calling imag-{} -> {:?}", command, e),
                }
            })
            .fold((), |_, line| {
                // The amount of newlines may differ depending on the subprocess
                println!("{}", line.trim());
            });

        exit(0);
    }

    let aliases = match fetch_aliases(&rt) {
        Ok(aliases) => aliases,
        Err(e)      => {
            println!("Error while fetching aliases from configuration file");
            debug!("Error = {:?}", e);
            println!("Aborting");
            exit(1);
        }
    };

    // Matches any subcommand given
    match matches.subcommand() {
        (subcommand, Some(scmd)) => {
            // Get all given arguments and further subcommands to pass to
            // the imag-<> binary
            // Providing no arguments is OK, and is therefore ignored here
            let mut subcommand_args : Vec<String> = match scmd.values_of("") {
                Some(values) => values.map(String::from).collect(),
                None => Vec::new()
            };

            forward_commandline_arguments(&matches, &mut subcommand_args);

            let subcommand = String::from(subcommand);
            let subcommand = aliases.get(&subcommand).cloned().unwrap_or(subcommand);

            debug!("Calling 'imag-{}' with args: {:?}", subcommand, subcommand_args);

            // Create a Command, and pass it the gathered arguments
            match Command::new(format!("imag-{}", subcommand))
                .stdin(Stdio::inherit())
                .stdout(Stdio::inherit())
                .stderr(Stdio::inherit())
                .args(&subcommand_args[..])
                .spawn()
                .and_then(|mut c| c.wait())
            {
                Ok(exit_status) => {
                    if !exit_status.success() {
                        debug!("{} exited with non-zero exit code: {:?}", subcommand, exit_status);
                        println!("{} exited with non-zero exit code", subcommand);
                        exit(exit_status.code().unwrap_or(1));
                    }
                    debug!("Successful exit!");
                },

                Err(e) => {
                    debug!("Error calling the subcommand");
                    match e.kind() {
                        ErrorKind::NotFound => {
                            println!("No such command: 'imag-{}'", subcommand);
                            println!("See 'imag --help' for available subcommands");
                            exit(1);
                        },
                        ErrorKind::PermissionDenied => {
                            println!("No permission to execute: 'imag-{}'", subcommand);
                            exit(1);
                        },
                        _ => {
                            println!("Error spawning: {:?}", e);
                            exit(1);
                        }
                    }
                }
            }
        },
        // Calling for example 'imag --versions' will lead here, as this option does not exit.
        // There's nothing to do in such a case
        _ => {},
    }
}

fn fetch_aliases(rt: &Runtime) -> Result<BTreeMap<String, String>, String> {
    let cfg   = rt.config().ok_or_else(|| String::from("No configuration found"))?;
    let value = cfg
        .read("imag.aliases")
        .map_err(|_| String::from("Reading from config failed"));

    match value? {
        None                         => Ok(BTreeMap::new()),
        Some(&Value::Table(ref tbl)) => {
            let mut alias_mappings = BTreeMap::new();

            for (k, v) in tbl {
                match v {
                    &Value::String(ref alias)      => {
                        alias_mappings.insert(alias.clone(), k.clone());
                    },
                    &Value::Array(ref aliases) => {
                        for alias in aliases {
                            match alias {
                                &Value::String(ref s) => {
                                    alias_mappings.insert(s.clone(), k.clone());
                                },
                                _ => {
                                    let e = format!("Not all values are a String in 'imag.aliases.{}'", k);
                                    return Err(e);
                                }
                            }
                        }
                    },

                    _ => {
                        let msg = format!("Type Error: 'imag.aliases.{}' is not a table or string", k);
                        return Err(msg);
                    },
                }
            }

            Ok(alias_mappings)
        },

        Some(_) => Err(String::from("Type Error: 'imag.aliases' is not a table")),
    }
}

fn forward_commandline_arguments(m: &ArgMatches, scmd: &mut Vec<String>) {
    let push = |flag: Option<&str>, val_name: &str, m: &ArgMatches, v: &mut Vec<String>| {
        let _ = m
            .value_of(val_name)
            .map(|val| {
                let flag = format!("--{}", flag.unwrap_or(val_name));
                v.insert(0, String::from(val));
                v.insert(0, flag);
            });
    };

    push(Some("verbose"),
         Runtime::arg_verbosity_name(), m , scmd);

    push(Some("debug"),
         Runtime::arg_debugging_name(), m , scmd);

    push(Some("no-color"),
         Runtime::arg_no_color_output_name(), m , scmd);

    push(Some("config"),
         Runtime::arg_config_name(), m , scmd);

    push(Some("override-config"),
         Runtime::arg_config_override_name(), m , scmd);

    push(Some("rtp"),
         Runtime::arg_runtimepath_name(), m , scmd);

    push(Some("store"),
         Runtime::arg_storepath_name(), m , scmd);

    push(Some("editor"),
         Runtime::arg_editor_name(), m , scmd);

    push(Some("generate-commandline-completion"),
         Runtime::arg_generate_compl(), m , scmd);

    push(None , Runtime::arg_logdest_name()                         , m , scmd);

}

<commit_msg>Use App::after_help() for printing runtime-generated help output<commit_after>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

extern crate clap;
#[macro_use] extern crate version;
#[macro_use] extern crate log;
extern crate walkdir;
extern crate toml;
extern crate toml_query;

extern crate libimagrt;
extern crate libimagerror;

use std::env;
use std::process::exit;
use std::process::Command;
use std::process::Stdio;
use std::io::ErrorKind;
use std::collections::BTreeMap;

use walkdir::WalkDir;
use clap::{Arg, ArgMatches, AppSettings, SubCommand};
use toml::Value;
use toml_query::read::TomlValueReadExt;

use libimagrt::runtime::Runtime;
use libimagerror::trace::trace_error;

/// Returns the helptext, putting the Strings in cmds as possible
/// subcommands into it
fn help_text(cmds: Vec<String>) -> String {
    format!(r#"

     _
    (_)_ __ ___   __ _  __ _
    | | '_ \` _ \/ _\`|/ _\`|
    | | | | | | | (_| | (_| |
    |_|_| |_| |_|\__,_|\__, |
                       |___/
    -------------------------

    Usage: imag [--version | --versions | -h | --help] <command> <args...>

    imag - the personal information management suite for the commandline

    imag is a PIM suite for the commandline. It consists of several commands,
    called "modules". Each module implements one PIM aspect and all of these
    modules can be used independently.

    Available commands:

    {imagbins}

    Call a command with 'imag <command> <args>'
    Each command can be called with "--help" to get the respective helptext.

    Please visit https://github.com/matthiasbeyer/imag to view the source code,
    follow the development of imag or maybe even contribute to imag.

    imag is free software. It is released under the terms of LGPLv2.1

    (c) 2016 Matthias Beyer and contributors"#,
        imagbins = cmds
            .into_iter()
            .map(|cmd| format!("\t{}\n", cmd))
            .fold(String::new(), |s, c| {
                let s = s + c.as_str();
                s
            }))
}

/// Returns the list of imag-* executables found in $PATH
fn get_commands() -> Vec<String> {
    match env::var("PATH") {
        Err(e) => {
            println!("PATH error: {:?}", e);
            exit(1);
        },

        Ok(path) => path
            .split(":")
            .flat_map(|elem| {
                WalkDir::new(elem)
                    .max_depth(1)
                    .into_iter()
                    .filter(|path| match *path {
                        Ok(ref p) => p.file_name().to_str().map_or(false, |f| f.starts_with("imag-")),
                        Err(_)    => false,
                    })
                    .filter_map(Result::ok)
                    .filter_map(|path| path
                        .file_name()
                       .to_str()
                       .and_then(|s| s.splitn(2, "-").nth(1).map(String::from))
                    )
            })
            .collect()
    }
}

fn main() {
    // Initialize the Runtime and build the CLI
    let appname  = "imag";
    let version  = &version!();
    let about    = "imag - the PIM suite for the commandline";
    let commands = get_commands();
    let helptext = help_text(commands.clone());
    let app      = Runtime::get_default_cli_builder(appname, version, about)
        .settings(&[AppSettings::AllowExternalSubcommands, AppSettings::ArgRequiredElseHelp])
        .arg(Arg::with_name("version")
             .long("version")
             .takes_value(false)
             .required(false)
             .multiple(false)
             .help("Get the version of imag"))
        .arg(Arg::with_name("versions")
             .long("versions")
             .takes_value(false)
             .required(false)
             .multiple(false)
             .help("Get the versions of the imag commands"))
        .subcommand(SubCommand::with_name("help").help("Show help"))
        .after_help(helptext.as_str());
    let rt = Runtime::new(app)
        .unwrap_or_else(|e| {
            println!("Runtime couldn't be setup. Exiting");
            trace_error(&e);
            exit(1);
        });
    let matches = rt.cli();

    debug!("matches: {:?}", matches);

    // Begin checking for arguments

    if matches.is_present("version") {
        debug!("Showing version");
        println!("imag {}", &version!()[..]);
        exit(0);
    }

    if matches.is_present("versions") {
        debug!("Showing versions");
        commands
            .iter()
            .map(|command| {
                match Command::new(format!("imag-{}", command))
                    .arg("--version")
                    .output()
                    .map(|v| v.stdout)
                {
                    Ok(s) => match String::from_utf8(s) {
                        Ok(s) => format!("{:10} -> {}", command, s),
                        Err(e) => format!("UTF8 Error while working with output of imag{}: {:?}", command, e),
                    },
                    Err(e) => format!("Failed calling imag-{} -> {:?}", command, e),
                }
            })
            .fold((), |_, line| {
                // The amount of newlines may differ depending on the subprocess
                println!("{}", line.trim());
            });

        exit(0);
    }

    let aliases = match fetch_aliases(&rt) {
        Ok(aliases) => aliases,
        Err(e)      => {
            println!("Error while fetching aliases from configuration file");
            debug!("Error = {:?}", e);
            println!("Aborting");
            exit(1);
        }
    };

    // Matches any subcommand given
    match matches.subcommand() {
        (subcommand, Some(scmd)) => {
            // Get all given arguments and further subcommands to pass to
            // the imag-<> binary
            // Providing no arguments is OK, and is therefore ignored here
            let mut subcommand_args : Vec<String> = match scmd.values_of("") {
                Some(values) => values.map(String::from).collect(),
                None => Vec::new()
            };

            forward_commandline_arguments(&matches, &mut subcommand_args);

            let subcommand = String::from(subcommand);
            let subcommand = aliases.get(&subcommand).cloned().unwrap_or(subcommand);

            debug!("Calling 'imag-{}' with args: {:?}", subcommand, subcommand_args);

            // Create a Command, and pass it the gathered arguments
            match Command::new(format!("imag-{}", subcommand))
                .stdin(Stdio::inherit())
                .stdout(Stdio::inherit())
                .stderr(Stdio::inherit())
                .args(&subcommand_args[..])
                .spawn()
                .and_then(|mut c| c.wait())
            {
                Ok(exit_status) => {
                    if !exit_status.success() {
                        debug!("{} exited with non-zero exit code: {:?}", subcommand, exit_status);
                        println!("{} exited with non-zero exit code", subcommand);
                        exit(exit_status.code().unwrap_or(1));
                    }
                    debug!("Successful exit!");
                },

                Err(e) => {
                    debug!("Error calling the subcommand");
                    match e.kind() {
                        ErrorKind::NotFound => {
                            println!("No such command: 'imag-{}'", subcommand);
                            println!("See 'imag --help' for available subcommands");
                            exit(1);
                        },
                        ErrorKind::PermissionDenied => {
                            println!("No permission to execute: 'imag-{}'", subcommand);
                            exit(1);
                        },
                        _ => {
                            println!("Error spawning: {:?}", e);
                            exit(1);
                        }
                    }
                }
            }
        },
        // Calling for example 'imag --versions' will lead here, as this option does not exit.
        // There's nothing to do in such a case
        _ => {},
    }
}

fn fetch_aliases(rt: &Runtime) -> Result<BTreeMap<String, String>, String> {
    let cfg   = rt.config().ok_or_else(|| String::from("No configuration found"))?;
    let value = cfg
        .read("imag.aliases")
        .map_err(|_| String::from("Reading from config failed"));

    match value? {
        None                         => Ok(BTreeMap::new()),
        Some(&Value::Table(ref tbl)) => {
            let mut alias_mappings = BTreeMap::new();

            for (k, v) in tbl {
                match v {
                    &Value::String(ref alias)      => {
                        alias_mappings.insert(alias.clone(), k.clone());
                    },
                    &Value::Array(ref aliases) => {
                        for alias in aliases {
                            match alias {
                                &Value::String(ref s) => {
                                    alias_mappings.insert(s.clone(), k.clone());
                                },
                                _ => {
                                    let e = format!("Not all values are a String in 'imag.aliases.{}'", k);
                                    return Err(e);
                                }
                            }
                        }
                    },

                    _ => {
                        let msg = format!("Type Error: 'imag.aliases.{}' is not a table or string", k);
                        return Err(msg);
                    },
                }
            }

            Ok(alias_mappings)
        },

        Some(_) => Err(String::from("Type Error: 'imag.aliases' is not a table")),
    }
}

fn forward_commandline_arguments(m: &ArgMatches, scmd: &mut Vec<String>) {
    let push = |flag: Option<&str>, val_name: &str, m: &ArgMatches, v: &mut Vec<String>| {
        let _ = m
            .value_of(val_name)
            .map(|val| {
                let flag = format!("--{}", flag.unwrap_or(val_name));
                v.insert(0, String::from(val));
                v.insert(0, flag);
            });
    };

    push(Some("verbose"),
         Runtime::arg_verbosity_name(), m , scmd);

    push(Some("debug"),
         Runtime::arg_debugging_name(), m , scmd);

    push(Some("no-color"),
         Runtime::arg_no_color_output_name(), m , scmd);

    push(Some("config"),
         Runtime::arg_config_name(), m , scmd);

    push(Some("override-config"),
         Runtime::arg_config_override_name(), m , scmd);

    push(Some("rtp"),
         Runtime::arg_runtimepath_name(), m , scmd);

    push(Some("store"),
         Runtime::arg_storepath_name(), m , scmd);

    push(Some("editor"),
         Runtime::arg_editor_name(), m , scmd);

    push(Some("generate-commandline-completion"),
         Runtime::arg_generate_compl(), m , scmd);

    push(None , Runtime::arg_logdest_name()                         , m , scmd);

}

<|endoftext|>


<commit_before>//! This example demonstrates how to roll your own event loop,
//! if for some reason you want to do that instead of using the `EventHandler`
//! trait to do that for you.
//!
//! This is how `ggez::event::run()` works, mostly, (if you want to see which parts were left out
//! of this example, check [event.rs](https://github.com/ggez/ggez/blob/master/src/event.rs),
//! it really is not doing anything magical.  But, if you want a bit more power over
//! the control flow of your game, this is how you get it.
//!
//! It is functionally identical to the `super_simple.rs` example apart from that.

use ggez::event;
use ggez::event::winit_event::{Event, KeyboardInput, WindowEvent};
use ggez::graphics::{self, Color, DrawMode};
use ggez::GameResult;
use winit::event_loop::ControlFlow;

pub fn main() -> GameResult {
    let cb = ggez::ContextBuilder::new("eventloop", "ggez");
    let (mut ctx, events_loop) = cb.build()?;

    let mut position: f32 = 1.0;

    // Handle events. Refer to `winit` docs for more information.
    events_loop.run(move |mut event, _window_target, control_flow| {
        if !ctx.continuing {
            *control_flow = ControlFlow::Exit;
            return;
        }

        *control_flow = ControlFlow::Poll;

        let ctx = &mut ctx;

        // This tells `ggez` to update it's internal states, should the event require that.
        // These include cursor position, view updating on resize, etc.
        event::process_event(ctx, &mut event);
        match event {
            Event::WindowEvent { event, .. } => match event {
                WindowEvent::CloseRequested => event::quit(ctx),
                WindowEvent::KeyboardInput {
                    input:
                        KeyboardInput {
                            virtual_keycode: Some(keycode),
                            ..
                        },
                    ..
                } => {
                    if let event::KeyCode::Escape = keycode {
                        *control_flow = winit::event_loop::ControlFlow::Exit
                    }
                }
                // `CloseRequested` and `KeyboardInput` events won't appear here.
                x => println!("Other window event fired: {:?}", x),
            },
            Event::MainEventsCleared => {
                // Tell the timer stuff a frame has happened.
                // Without this the FPS timer functions and such won't work.
                ctx.timer.tick();

                // Update
                position += 1.0;

                // Draw
                graphics::clear(ctx, [0.1, 0.2, 0.3, 1.0].into());
                let circle = graphics::Mesh::new_circle(
                    ctx,
                    DrawMode::fill(),
                    glam::Vec2::new(0.0, 0.0),
                    100.0,
                    2.0,
                    Color::WHITE,
                )
                .unwrap();
                graphics::draw(ctx, &circle, (glam::Vec2::new(position, 380.0),)).unwrap();
                graphics::present(ctx).unwrap();

                // reset the mouse delta for the next frame
                // necessary because it's calculated cumulatively each cycle
                ctx.mouse.reset_delta();

                // Copy the state of the keyboard into the KeyboardContext and
                // the mouse into the MouseContext.
                // Not required for this example but important if you want to
                // use the functions keyboard::is_key_just_pressed/released and
                // mouse::is_button_just_pressed/released.
                ctx.keyboard_context.save_keyboard_state();
                ctx.mouse_context.save_mouse_state();

                ggez::timer::yield_now();
            }

            x => println!("Device event fired: {:?}", x),
        }
    });
}
<commit_msg>port eventloop example<commit_after>//! This example demonstrates how to roll your own event loop,
//! if for some reason you want to do that instead of using the `EventHandler`
//! trait to do that for you.
//!
//! This is how `ggez::event::run()` works, mostly, (if you want to see which parts were left out
//! of this example, check [event.rs](https://github.com/ggez/ggez/blob/master/src/event.rs),
//! it really is not doing anything magical.  But, if you want a bit more power over
//! the control flow of your game, this is how you get it.
//!
//! It is functionally identical to the `super_simple.rs` example apart from that.

use ggez::event;
use ggez::event::winit_event::{Event, KeyboardInput, WindowEvent};
use ggez::graphics::{self, Color, DrawMode};
use ggez::GameResult;
use winit::event_loop::ControlFlow;

pub fn main() -> GameResult {
    let cb = ggez::ContextBuilder::new("eventloop", "ggez");
    let (mut ctx, events_loop) = cb.build()?;

    let mut position: f32 = 1.0;

    // Handle events. Refer to `winit` docs for more information.
    events_loop.run(move |mut event, _window_target, control_flow| {
        if !ctx.continuing {
            *control_flow = ControlFlow::Exit;
            return;
        }

        *control_flow = ControlFlow::Poll;

        let ctx = &mut ctx;

        // This tells `ggez` to update it's internal states, should the event require that.
        // These include cursor position, view updating on resize, etc.
        event::process_event(ctx, &mut event);
        match event {
            Event::WindowEvent { event, .. } => match event {
                WindowEvent::CloseRequested => event::quit(ctx),
                WindowEvent::KeyboardInput {
                    input:
                        KeyboardInput {
                            virtual_keycode: Some(keycode),
                            ..
                        },
                    ..
                } => {
                    if let event::KeyCode::Escape = keycode {
                        *control_flow = winit::event_loop::ControlFlow::Exit
                    }
                }
                // `CloseRequested` and `KeyboardInput` events won't appear here.
                x => println!("Other window event fired: {:?}", x),
            },
            Event::MainEventsCleared => {
                // Tell the timer stuff a frame has happened.
                // Without this the FPS timer functions and such won't work.
                ctx.timer.tick();

                // Update
                position += 1.0;

                // Draw
                ctx.gfx.begin_frame().unwrap();
                let circle = graphics::Mesh::new_circle(
                    &ctx.gfx,
                    DrawMode::fill(),
                    glam::Vec2::new(0.0, 0.0),
                    100.0,
                    2.0,
                    Color::WHITE,
                )
                .unwrap();
                let mut canvas = graphics::Canvas::from_frame(
                    &mut ctx.gfx,
                    graphics::Color::from([0.1, 0.2, 0.3, 1.0]),
                )
                .unwrap();
                canvas.draw_mesh(&circle, None, (glam::Vec2::new(position, 380.0),));
                canvas.finish();
                ctx.gfx.end_frame().unwrap();

                // reset the mouse delta for the next frame
                // necessary because it's calculated cumulatively each cycle
                ctx.mouse.reset_delta();

                // Copy the state of the keyboard into the KeyboardContext and
                // the mouse into the MouseContext.
                // Not required for this example but important if you want to
                // use the functions keyboard::is_key_just_pressed/released and
                // mouse::is_button_just_pressed/released.
                ctx.keyboard.save_keyboard_state();
                ctx.mouse.save_mouse_state();

                ggez::timer::yield_now();
            }

            x => println!("Device event fired: {:?}", x),
        }
    });
}
<|endoftext|>