<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).expect("Couldn't create multipart upload");
    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).expect("Couldn't upload a file part");
        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).expect("Couldn't complete multipart upload");
    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).expect("Couldn't create bucket");
    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).expect("Couldn't delete bucket");
    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).expect("Couldn't HEAD object");
    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).expect("Couldn't GET object");
    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).expect("Couldn't GET object (range)");
    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).expect("Couldn't copy object");
    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).expect("Couldn't copy object (utf8)");
    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).expect("Couldn't delete object");
    println!("{:#?}", result);
}

fn test_list_buckets(client: &TestClient) {
    let result = client.list_buckets().expect("Couldn't list buckets");
    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).expect("Couldn't list items in bucket (v2)");
    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).expect("list objects v2 failed");
    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).expect("list objects v2 paging failed");
    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).expect("Couldn't apply bucket CORS");
    println!("{:#?}", result);
}
<commit_msg>extent object body by len not capacity<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).expect("Couldn't create multipart upload");
    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).expect("Couldn't upload a file part");
        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).expect("Couldn't complete multipart upload");
    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).expect("Couldn't create bucket");
    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).expect("Couldn't delete bucket");
    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).expect("Couldn't HEAD object");
    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).expect("Couldn't GET object");
    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[0..len]);
    }

    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).expect("Couldn't GET object (range)");
    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).expect("Couldn't copy object");
    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).expect("Couldn't copy object (utf8)");
    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).expect("Couldn't delete object");
    println!("{:#?}", result);
}

fn test_list_buckets(client: &TestClient) {
    let result = client.list_buckets().expect("Couldn't list buckets");
    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).expect("Couldn't list items in bucket (v2)");
    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).expect("list objects v2 failed");
    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).expect("list objects v2 paging failed");
    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).expect("Couldn't apply bucket CORS");
    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 cg::{self, WhereClause};
use darling::util::Override;
use quote::{ToTokens, Tokens};
use syn::{self, Data};
use synstructure::{BindingInfo, Structure, VariantInfo};

pub fn derive(input: syn::DeriveInput) -> Tokens {
    let name = &input.ident;
    let trait_path = parse_quote!(::style_traits::ToCss);
    let (impl_generics, ty_generics, mut where_clause) =
        cg::trait_parts(&input, &trait_path);

    let input_attrs = cg::parse_input_attrs::<CssInputAttrs>(&input);
    if let Data::Enum(_) = input.data {
        assert!(input_attrs.function.is_none(), "#[css(function)] is not allowed on enums");
        assert!(!input_attrs.comma, "#[css(comma)] is not allowed on enums");
    }
    let s = Structure::new(&input);

    let match_body = s.each_variant(|variant| {
        derive_variant_arm(variant, &mut where_clause)
    });

    let mut impls = quote! {
        impl #impl_generics ::style_traits::ToCss for #name #ty_generics #where_clause {
            #[allow(unused_variables)]
            #[inline]
            fn to_css<W>(
                &self,
                dest: &mut ::style_traits::CssWriter<W>,
            ) -> ::std::fmt::Result
            where
                W: ::std::fmt::Write
            {
                match *self {
                    #match_body
                }
            }
        }
    };

    if input_attrs.derive_debug {
        impls.append_all(quote! {
            impl #impl_generics ::std::fmt::Debug for #name #ty_generics #where_clause {
                fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
                    ::style_traits::ToCss::to_css(
                        self,
                        &mut ::style_traits::CssWriter::new(f),
                    )
                }
            }
        });
    }

    impls
}

fn derive_variant_arm(
    variant: &VariantInfo,
    where_clause: &mut WhereClause,
) -> Tokens {
    let bindings = variant.bindings();
    let identifier = cg::to_css_identifier(variant.ast().ident.as_ref());
    let ast = variant.ast();
    let variant_attrs = cg::parse_variant_attrs::<CssVariantAttrs>(&ast);
    let separator = if variant_attrs.comma { ", " } else { " " };

    if variant_attrs.dimension {
        assert_eq!(bindings.len(), 1);
        assert!(
            variant_attrs.function.is_none() && variant_attrs.keyword.is_none(),
            "That makes no sense"
        );
    }

    let mut expr = if let Some(keyword) = variant_attrs.keyword {
        assert!(bindings.is_empty());
        quote! {
            ::std::fmt::Write::write_str(dest, #keyword)
        }
    } else if !bindings.is_empty() {
        derive_variant_fields_expr(bindings, where_clause, separator)
    } else {
        quote! {
            ::std::fmt::Write::write_str(dest, #identifier)
        }
    };

    if variant_attrs.dimension {
        expr = quote! {
            #expr?;
            ::std::fmt::Write::write_str(dest, #identifier)
        }
    } else if let Some(function) = variant_attrs.function {
        let mut identifier = function.explicit().map_or(identifier, |name| name);
        identifier.push_str("(");
        expr = quote! {
            ::std::fmt::Write::write_str(dest, #identifier)?;
            #expr?;
            ::std::fmt::Write::write_str(dest, ")")
        }
    }
    expr
}

fn derive_variant_fields_expr(
    bindings: &[BindingInfo],
    where_clause: &mut WhereClause,
    separator: &str,
) -> Tokens {
    let mut iter = bindings.iter().filter_map(|binding| {
        let attrs = cg::parse_field_attrs::<CssFieldAttrs>(&binding.ast());
        if attrs.skip {
            return None;
        }
        Some((binding, attrs))
    }).peekable();

    let (first, attrs) = match iter.next() {
        Some(pair) => pair,
        None => return quote! { Ok(()) },
    };
    if !attrs.iterable && iter.peek().is_none() {
        if !attrs.ignore_bound {
            where_clause.add_trait_bound(&first.ast().ty);
        }
        return quote! { ::style_traits::ToCss::to_css(#first, dest) };
    }

    let mut expr = derive_single_field_expr(first, attrs, where_clause);
    for (binding, attrs) in iter {
        derive_single_field_expr(binding, attrs, where_clause).to_tokens(&mut expr)
    }

    quote! {{
        let mut writer = ::style_traits::values::SequenceWriter::new(dest, #separator);
        #expr
        Ok(())
    }}
}

fn derive_single_field_expr(
    field: &BindingInfo,
    attrs: CssFieldAttrs,
    where_clause: &mut WhereClause,
) -> Tokens {
    if attrs.iterable {
        if let Some(if_empty) = attrs.if_empty {
            return quote! {
                {
                    let mut iter = #field.iter().peekable();
                    if iter.peek().is_none() {
                        writer.item(&::style_traits::values::Verbatim(#if_empty))?;
                    } else {
                        for item in iter {
                            writer.item(&item)?;
                        }
                    }
                }
            };
        }
        quote! {
            for item in #field.iter() {
                writer.item(&item)?;
            }
        }
    } else {
        if !attrs.ignore_bound {
            where_clause.add_trait_bound(&field.ast().ty);
        }
        quote! { writer.item(#field)?; }
    }
}

#[darling(attributes(css), default)]
#[derive(Default, FromDeriveInput)]
struct CssInputAttrs {
    derive_debug: bool,
    // Here because structs variants are also their whole type definition.
    function: Option<Override<String>>,
    // Here because structs variants are also their whole type definition.
    comma: bool,
}

#[darling(attributes(css), default)]
#[derive(Default, FromVariant)]
pub struct CssVariantAttrs {
    pub function: Option<Override<String>>,
    pub comma: bool,
    pub dimension: bool,
    pub keyword: Option<String>,
    pub aliases: Option<String>,
}

#[darling(attributes(css), default)]
#[derive(Default, FromField)]
struct CssFieldAttrs {
    if_empty: Option<String>,
    ignore_bound: bool,
    iterable: bool,
    skip: bool,
}
<commit_msg>style: Introduce css(skip_if) to conditionally skip serialization of a field.<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 cg::{self, WhereClause};
use darling::util::Override;
use quote::{ToTokens, Tokens};
use syn::{self, Data, Ident};
use synstructure::{BindingInfo, Structure, VariantInfo};

pub fn derive(input: syn::DeriveInput) -> Tokens {
    let name = &input.ident;
    let trait_path = parse_quote!(::style_traits::ToCss);
    let (impl_generics, ty_generics, mut where_clause) =
        cg::trait_parts(&input, &trait_path);

    let input_attrs = cg::parse_input_attrs::<CssInputAttrs>(&input);
    if let Data::Enum(_) = input.data {
        assert!(input_attrs.function.is_none(), "#[css(function)] is not allowed on enums");
        assert!(!input_attrs.comma, "#[css(comma)] is not allowed on enums");
    }
    let s = Structure::new(&input);

    let match_body = s.each_variant(|variant| {
        derive_variant_arm(variant, &mut where_clause)
    });

    let mut impls = quote! {
        impl #impl_generics ::style_traits::ToCss for #name #ty_generics #where_clause {
            #[allow(unused_variables)]
            #[inline]
            fn to_css<W>(
                &self,
                dest: &mut ::style_traits::CssWriter<W>,
            ) -> ::std::fmt::Result
            where
                W: ::std::fmt::Write
            {
                match *self {
                    #match_body
                }
            }
        }
    };

    if input_attrs.derive_debug {
        impls.append_all(quote! {
            impl #impl_generics ::std::fmt::Debug for #name #ty_generics #where_clause {
                fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
                    ::style_traits::ToCss::to_css(
                        self,
                        &mut ::style_traits::CssWriter::new(f),
                    )
                }
            }
        });
    }

    impls
}

fn derive_variant_arm(
    variant: &VariantInfo,
    where_clause: &mut WhereClause,
) -> Tokens {
    let bindings = variant.bindings();
    let identifier = cg::to_css_identifier(variant.ast().ident.as_ref());
    let ast = variant.ast();
    let variant_attrs = cg::parse_variant_attrs::<CssVariantAttrs>(&ast);
    let separator = if variant_attrs.comma { ", " } else { " " };

    if variant_attrs.dimension {
        assert_eq!(bindings.len(), 1);
        assert!(
            variant_attrs.function.is_none() && variant_attrs.keyword.is_none(),
            "That makes no sense"
        );
    }

    let mut expr = if let Some(keyword) = variant_attrs.keyword {
        assert!(bindings.is_empty());
        quote! {
            ::std::fmt::Write::write_str(dest, #keyword)
        }
    } else if !bindings.is_empty() {
        derive_variant_fields_expr(bindings, where_clause, separator)
    } else {
        quote! {
            ::std::fmt::Write::write_str(dest, #identifier)
        }
    };

    if variant_attrs.dimension {
        expr = quote! {
            #expr?;
            ::std::fmt::Write::write_str(dest, #identifier)
        }
    } else if let Some(function) = variant_attrs.function {
        let mut identifier = function.explicit().map_or(identifier, |name| name);
        identifier.push_str("(");
        expr = quote! {
            ::std::fmt::Write::write_str(dest, #identifier)?;
            #expr?;
            ::std::fmt::Write::write_str(dest, ")")
        }
    }
    expr
}

fn derive_variant_fields_expr(
    bindings: &[BindingInfo],
    where_clause: &mut WhereClause,
    separator: &str,
) -> Tokens {
    let mut iter = bindings.iter().filter_map(|binding| {
        let attrs = cg::parse_field_attrs::<CssFieldAttrs>(&binding.ast());
        if attrs.skip {
            return None;
        }
        Some((binding, attrs))
    }).peekable();

    let (first, attrs) = match iter.next() {
        Some(pair) => pair,
        None => return quote! { Ok(()) },
    };
    if !attrs.iterable && iter.peek().is_none() {
        if !attrs.ignore_bound {
            where_clause.add_trait_bound(&first.ast().ty);
        }
        let mut expr = quote! { ::style_traits::ToCss::to_css(#first, dest) };
        if let Some(condition) = attrs.skip_if {
            expr = quote! {
                if !#first.#condition() {
                    #expr
                }
            }
        }
        return expr;
    }

    let mut expr = derive_single_field_expr(first, attrs, where_clause);
    for (binding, attrs) in iter {
        derive_single_field_expr(binding, attrs, where_clause).to_tokens(&mut expr)
    }

    quote! {{
        let mut writer = ::style_traits::values::SequenceWriter::new(dest, #separator);
        #expr
        Ok(())
    }}
}

fn derive_single_field_expr(
    field: &BindingInfo,
    attrs: CssFieldAttrs,
    where_clause: &mut WhereClause,
) -> Tokens {
    let mut expr = if attrs.iterable {
        if let Some(if_empty) = attrs.if_empty {
            return quote! {
                {
                    let mut iter = #field.iter().peekable();
                    if iter.peek().is_none() {
                        writer.item(&::style_traits::values::Verbatim(#if_empty))?;
                    } else {
                        for item in iter {
                            writer.item(&item)?;
                        }
                    }
                }
            };
        }
        quote! {
            for item in #field.iter() {
                writer.item(&item)?;
            }
        }
    } else {
        if !attrs.ignore_bound {
            where_clause.add_trait_bound(&field.ast().ty);
        }
        quote! { writer.item(#field)?; }
    };

    if let Some(condition) = attrs.skip_if {
        expr = quote! {
            if !#field.#condition() {
                #expr
            }
        }
    }

    expr
}

#[darling(attributes(css), default)]
#[derive(Default, FromDeriveInput)]
struct CssInputAttrs {
    derive_debug: bool,
    // Here because structs variants are also their whole type definition.
    function: Option<Override<String>>,
    // Here because structs variants are also their whole type definition.
    comma: bool,
}

#[darling(attributes(css), default)]
#[derive(Default, FromVariant)]
pub struct CssVariantAttrs {
    pub function: Option<Override<String>>,
    pub comma: bool,
    pub dimension: bool,
    pub keyword: Option<String>,
    pub aliases: Option<String>,
}

#[darling(attributes(css), default)]
#[derive(Default, FromField)]
struct CssFieldAttrs {
    if_empty: Option<String>,
    ignore_bound: bool,
    iterable: bool,
    skip: bool,
    skip_if: Option<Ident>,
}
<|endoftext|>


<commit_before>//! Protocol-related utilities
//!
//! This module contains several handlers to manage the Wayland protocol
//! and the clients.
//!
//! ## General structure
//!
//! Most utilities provided in this module work in the same way:
//!
//! - An `init` function or method will take the wayland display as argument and
//!   insert one or more globals into it.
//! - If you want to remove a previously inserted global, just call the
//!   `destroy()` method on the associated `Global`. If you don't plan to
//!   destroy the global at all, you don't need to bother keeping the
//!   `Global` around.
//!
//! Some of these modules require you to provide a callback that is invoked for some
//! client requests that your logic needs to handle. In most cases these callback
//! are given as input an enum specifying the event that occurred, as well as the
//! [`DispatchData`](wayland_server::DispatchData) from `wayland_server`.
//!
//! ## Provided helpers
//!
//! ### Core functionality
//!
//! The most fundamental module is the [`compositor`] module, which provides the necessary
//! logic to handle the fundamental component by which clients build their windows: surfaces.
//! Following this, the [`shell`] module contains the logic allowing clients to use their
//! surface to build concrete windows with the usual interactions. Different kind of shells
//! exist, but in general you will want to support at least the [`xdg`](shell::xdg) variant,
//! which is the standard used by most applications.
//!
//! Then, the [`seat`] module contains logic related to input handling. These helpers are used
//! to forward input (such as pointer action or keystrokes) to clients, and manage the input
//! focus of clients. Tightly coupled with it is the [`data_device`] module, which handles
//! cross-client interactions such as accessing the clipboard, or drag'n'drop actions.
//!
//! The [`shm`] module provides the necessary logic for client to provide buffers defining the
//! contents of their windows using shared memory. This is the main mechanism used by clients
//! that are not hardware accelerated. As a complement, the [`dmabuf`] module provides support
//! hardware-accelerated clients; it is tightly linked to the
//! [`backend::allocator`](crate::backend::allocator) module.
//!
//! The [`output`] module helps forwarding to clients information about the display monitors that
//! are available. This notably plays a key role in HiDPI handling, and more generally notifying
//! clients about whether they are currently visible or not (allowing them to stop drawing if they
//! are not, for example).
//!
//! ### Experimental helpers
//!
//! The [`explicit_synchronization`] module provides helpers to give clients fine-grained control
//! over the synchronization for accessing graphics buffer with the compositor, for low-latency
//! rendering. It is however still experimental, and largely untested.

use std::sync::atomic::{AtomicUsize, Ordering};

pub mod compositor;
// pub mod data_device;
// pub mod dmabuf;
// pub mod explicit_synchronization;
pub mod output;
pub mod seat;
pub mod shell;
pub mod shm;
// pub mod tablet_manager;
// pub mod viewporter;
// pub mod xdg_activation;
// pub mod xdg_foreign;

/// A global [`SerialCounter`] for use in your compositor.
///
/// Is is also used internally by some parts of Smithay.
pub static SERIAL_COUNTER: SerialCounter = SerialCounter {
    serial: AtomicUsize::new(0),
};

/// A serial type, whose comparison takes into account the wrapping-around behavior of the
/// underlying counter.
#[derive(Debug, Copy, Clone)]
pub struct Serial(u32);

impl PartialEq for Serial {
    fn eq(&self, other: &Self) -> bool {
        self.0 == other.0
    }
}

impl Eq for Serial {}

impl PartialOrd for Serial {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        let distance = if self.0 > other.0 {
            self.0 - other.0
        } else {
            other.0 - self.0
        };
        if distance < u32::MAX / 2 {
            self.0.partial_cmp(&other.0)
        } else {
            // wrap-around occured, invert comparison
            other.0.partial_cmp(&self.0)
        }
    }
}

impl From<u32> for Serial {
    fn from(n: u32) -> Self {
        Serial(n)
    }
}

impl From<Serial> for u32 {
    fn from(serial: Serial) -> u32 {
        serial.0
    }
}

/// A counter for generating serials, for use in the client protocol
///
/// A global instance of this counter is available as the `SERIAL_COUNTER`
/// static. It is recommended to only use this global counter to ensure the
/// uniqueness of serials.
///
/// The counter will wrap around on overflow, ensuring it can run for as long
/// as needed.
#[derive(Debug)]
pub struct SerialCounter {
    // TODO: replace with an AtomicU32 when stabilized
    serial: AtomicUsize,
}

impl SerialCounter {
    /// Retrieve the next serial from the counter
    pub fn next_serial(&self) -> Serial {
        Serial(self.serial.fetch_add(1, Ordering::AcqRel) as u32)
    }
}

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

    fn create_serial_counter(initial_value: u32) -> SerialCounter {
        SerialCounter {
            serial: AtomicUsize::new(initial_value as usize),
        }
    }

    #[test]
    #[allow(clippy::eq_op)]
    fn serial_equals_self() {
        let counter = create_serial_counter(0);
        let serial = counter.next_serial();
        assert!(serial == serial);
    }

    #[test]
    fn consecutive_serials() {
        let counter = create_serial_counter(0);
        let serial1 = counter.next_serial();
        let serial2 = counter.next_serial();
        assert!(serial1 < serial2);
    }

    #[test]
    fn non_consecutive_serials() {
        let skip_serials = 147;

        let counter = create_serial_counter(0);
        let serial1 = counter.next_serial();
        for _ in 0..skip_serials {
            let _ = counter.next_serial();
        }
        let serial2 = counter.next_serial();
        assert!(serial1 < serial2);
    }

    #[test]
    fn serial_wrap_around() {
        let counter = create_serial_counter(u32::MAX);
        let serial1 = counter.next_serial();
        let serial2 = counter.next_serial();

        assert!(serial1 == u32::MAX.into());
        assert!(serial2 == 0.into());

        assert!(serial1 < serial2);
    }
}
<commit_msg>wayland-frontend: Resolve SerialCounter todo<commit_after>//! Protocol-related utilities
//!
//! This module contains several handlers to manage the Wayland protocol
//! and the clients.
//!
//! ## General structure
//!
//! Most utilities provided in this module work in the same way:
//!
//! - An `init` function or method will take the wayland display as argument and
//!   insert one or more globals into it.
//! - If you want to remove a previously inserted global, just call the
//!   `destroy()` method on the associated `Global`. If you don't plan to
//!   destroy the global at all, you don't need to bother keeping the
//!   `Global` around.
//!
//! Some of these modules require you to provide a callback that is invoked for some
//! client requests that your logic needs to handle. In most cases these callback
//! are given as input an enum specifying the event that occurred, as well as the
//! [`DispatchData`](wayland_server::DispatchData) from `wayland_server`.
//!
//! ## Provided helpers
//!
//! ### Core functionality
//!
//! The most fundamental module is the [`compositor`] module, which provides the necessary
//! logic to handle the fundamental component by which clients build their windows: surfaces.
//! Following this, the [`shell`] module contains the logic allowing clients to use their
//! surface to build concrete windows with the usual interactions. Different kind of shells
//! exist, but in general you will want to support at least the [`xdg`](shell::xdg) variant,
//! which is the standard used by most applications.
//!
//! Then, the [`seat`] module contains logic related to input handling. These helpers are used
//! to forward input (such as pointer action or keystrokes) to clients, and manage the input
//! focus of clients. Tightly coupled with it is the [`data_device`] module, which handles
//! cross-client interactions such as accessing the clipboard, or drag'n'drop actions.
//!
//! The [`shm`] module provides the necessary logic for client to provide buffers defining the
//! contents of their windows using shared memory. This is the main mechanism used by clients
//! that are not hardware accelerated. As a complement, the [`dmabuf`] module provides support
//! hardware-accelerated clients; it is tightly linked to the
//! [`backend::allocator`](crate::backend::allocator) module.
//!
//! The [`output`] module helps forwarding to clients information about the display monitors that
//! are available. This notably plays a key role in HiDPI handling, and more generally notifying
//! clients about whether they are currently visible or not (allowing them to stop drawing if they
//! are not, for example).
//!
//! ### Experimental helpers
//!
//! The [`explicit_synchronization`] module provides helpers to give clients fine-grained control
//! over the synchronization for accessing graphics buffer with the compositor, for low-latency
//! rendering. It is however still experimental, and largely untested.

use std::sync::atomic::{AtomicU32, Ordering};

pub mod compositor;
// pub mod data_device;
// pub mod dmabuf;
// pub mod explicit_synchronization;
pub mod output;
pub mod seat;
pub mod shell;
pub mod shm;
// pub mod tablet_manager;
// pub mod viewporter;
// pub mod xdg_activation;
// pub mod xdg_foreign;

/// A global [`SerialCounter`] for use in your compositor.
///
/// Is is also used internally by some parts of Smithay.
pub static SERIAL_COUNTER: SerialCounter = SerialCounter {
    serial: AtomicU32::new(0),
};

/// A serial type, whose comparison takes into account the wrapping-around behavior of the
/// underlying counter.
#[derive(Debug, Copy, Clone)]
pub struct Serial(u32);

impl PartialEq for Serial {
    fn eq(&self, other: &Self) -> bool {
        self.0 == other.0
    }
}

impl Eq for Serial {}

impl PartialOrd for Serial {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        let distance = if self.0 > other.0 {
            self.0 - other.0
        } else {
            other.0 - self.0
        };
        if distance < u32::MAX / 2 {
            self.0.partial_cmp(&other.0)
        } else {
            // wrap-around occurred, invert comparison
            other.0.partial_cmp(&self.0)
        }
    }
}

impl From<u32> for Serial {
    fn from(n: u32) -> Self {
        Serial(n)
    }
}

impl From<Serial> for u32 {
    fn from(serial: Serial) -> u32 {
        serial.0
    }
}

/// A counter for generating serials, for use in the client protocol
///
/// A global instance of this counter is available as the `SERIAL_COUNTER`
/// static. It is recommended to only use this global counter to ensure the
/// uniqueness of serials.
///
/// The counter will wrap around on overflow, ensuring it can run for as long
/// as needed.
#[derive(Debug)]
pub struct SerialCounter {
    serial: AtomicU32,
}

impl SerialCounter {
    /// Retrieve the next serial from the counter
    pub fn next_serial(&self) -> Serial {
        Serial(self.serial.fetch_add(1, Ordering::AcqRel))
    }
}

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

    fn create_serial_counter(initial_value: u32) -> SerialCounter {
        SerialCounter {
            serial: AtomicU32::new(initial_value),
        }
    }

    #[test]
    #[allow(clippy::eq_op)]
    fn serial_equals_self() {
        let counter = create_serial_counter(0);
        let serial = counter.next_serial();
        assert!(serial == serial);
    }

    #[test]
    fn consecutive_serials() {
        let counter = create_serial_counter(0);
        let serial1 = counter.next_serial();
        let serial2 = counter.next_serial();
        assert!(serial1 < serial2);
    }

    #[test]
    fn non_consecutive_serials() {
        let skip_serials = 147;

        let counter = create_serial_counter(0);
        let serial1 = counter.next_serial();
        for _ in 0..skip_serials {
            let _ = counter.next_serial();
        }
        let serial2 = counter.next_serial();
        assert!(serial1 < serial2);
    }

    #[test]
    fn serial_wrap_around() {
        let counter = create_serial_counter(u32::MAX);
        let serial1 = counter.next_serial();
        let serial2 = counter.next_serial();

        assert!(serial1 == u32::MAX.into());
        assert!(serial2 == 0.into());

        assert!(serial1 < serial2);
    }
}
<|endoftext|>


<commit_before>//! # Daemon abstraction
//! This is documentation for the `daemon` module.
//!
//! Lorem Ipsum
//! functionality for building portable Rust software.

extern crate syslog;

use ::probing::Probe;
use std::thread::{spawn, sleep};
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::Duration;
use std::sync::{Arc};
use ::logging::{Logger};
use ::base::{Config};
use logging::logger::syslog::Severity as Severity;
static CPU_ANTI_HOG_MILLIS_OFFSET: u64 = 100;

#[derive(Debug)]
pub struct Worker {
    logger: Logger,
    ops: Arc<Vec<Box<Probe>>>,
    config: Config,
    should_stop: Arc<AtomicBool>,
}

impl Worker {
    pub fn new(
        logger: Logger,
        operations: Arc<Vec<Box<Probe>>>,
        c: Config
    ) -> Worker {
        Worker {
            logger: logger,
            ops: operations,
            config: c,
            should_stop: Arc::new(AtomicBool::new(false))
        }
    }

    pub fn stop(&self) -> () {
        self.should_stop.store(true, Ordering::SeqCst);
    }

    pub fn start(&self) -> () {
        let a_len = self.ops.len();
        for i in 0..a_len {
            let ops = self.ops.clone();
            let logger = self.logger.clone();
            let timeout = self.config.get_timeout() + CPU_ANTI_HOG_MILLIS_OFFSET;
            let stop_bool = self.should_stop.clone();
            spawn(move || {
                loop {
                    if stop_bool.load(Ordering::SeqCst) {
                        let msg = format!("@Thread: {} - terminating", ops[i].get_thread_id());
                        logger.log(Severity::LOG_INFO, &msg);
                        break
                    }
                    ops[i].exec();
                    sleep(Duration::from_millis(timeout));
                }
            });
        }
    }
}

impl Drop for Worker {
    fn drop(&mut self) {

        let msg = format!("Worker drop, <free({:?}>)", self);
        self.logger.log(Severity::LOG_INFO, &msg);
    }
}<commit_msg>worker subthreads join<commit_after>//! # Daemon abstraction
//! This is documentation for the `daemon` module.
//!
//! Lorem Ipsum
//! functionality for building portable Rust software.

extern crate syslog;

use ::probing::Probe;
use std::thread::{spawn, sleep, JoinHandle};
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::Duration;
use std::sync::{Arc, Mutex};
use ::logging::{Logger};
use ::base::{Config};
use logging::logger::syslog::Severity as Severity;

static CPU_ANTI_HOG_MILLIS_OFFSET: u64 = 100;

#[derive(Debug)]
pub struct Worker {
    th:Arc<Mutex<Vec<JoinHandle<()>>>>,
    logger: Logger,
    ops: Arc<Vec<Box<Probe>>>,
    config: Config,
    should_stop: Arc<AtomicBool>,
}

impl Worker {
    pub fn new(
        logger: Logger,
        operations: Arc<Vec<Box<Probe>>>,
        c: Config
    ) -> Worker {
        Worker {
            th:Arc::new(Mutex::new(vec![])),
            logger: logger,
            ops: operations,
            config: c,
            should_stop: Arc::new(AtomicBool::new(false))
        }
    }

    pub fn stop(&self) -> () {
//        let thread_handles = Arc::try_unwrap(self.th.clone());
//        let  thread_handles = self.th.clone().lock().unwrap();
        self.should_stop.store(true, Ordering::SeqCst);

//        for handle in &thread_handles {
//            let _ = handle.join();
//        }
    }

    pub fn start(&self) -> () {
        let a_len = self.ops.len();
        let mut thread_handles = self.th.lock().unwrap();

        for i in 0..a_len {
            let ops = self.ops.clone();
            let logger = self.logger.clone();
            let timeout = self.config.get_timeout() + CPU_ANTI_HOG_MILLIS_OFFSET;
            let stop_bool = self.should_stop.clone();

            thread_handles.push(
                spawn(move || {
                    loop {
                        if stop_bool.load(Ordering::SeqCst) {
                            let msg = format!("@Thread: {} - terminating", ops[i].get_thread_id());
                            logger.log(Severity::LOG_INFO, &msg);
                            break
                        }
                        ops[i].exec();
                        sleep(Duration::from_millis(timeout));
                    }
                }));
        }
    }
}

impl Drop for Worker {
    fn drop(&mut self) {
        let msg = format!("Worker drop, <free({:?}>)", self);
        self.logger.log(Severity::LOG_INFO, &msg);
    }
}<|endoftext|>


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

#![cfg(not(test))]

#[macro_use]
extern crate log;
extern crate rustfmt;
extern crate toml;
extern crate env_logger;
extern crate getopts;

use rustfmt::{WriteMode, run, run_from_stdin};
use rustfmt::config::Config;

use std::env;
use std::fs::{self, File};
use std::io::{self, Read, Write};
use std::path::{Path, PathBuf};

use getopts::{Matches, Options};

/// Rustfmt operations.
enum Operation {
    /// Format files and their child modules.
    Format(Vec<PathBuf>, WriteMode),
    /// Print the help message.
    Help,
    /// Print detailed configuration help.
    ConfigHelp,
    /// Invalid program input, including reason.
    InvalidInput(String),
    /// No file specified, read from stdin
    Stdin(String, WriteMode),
}

/// Try to find a project file in the input file directory and its parents.
fn lookup_project_file(input_file: &Path) -> io::Result<PathBuf> {
    let mut current = if input_file.is_relative() {
        try!(env::current_dir()).join(input_file)
    } else {
        input_file.to_path_buf()
    };

    // FIXME: We should canonize path to properly handle its parents,
    // but `canonicalize` function is unstable now (recently added API)
    // current = try!(fs::canonicalize(current));

    loop {
        let config_file = current.join("rustfmt.toml");
        if fs::metadata(&config_file).is_ok() {
            return Ok(config_file);
        }

        // If the current directory has no parent, we're done searching.
        if !current.pop() {
            return Err(io::Error::new(io::ErrorKind::NotFound, "Config not found"));
        }
    }
}

/// Try to find a project file. If it's found, read it.
fn lookup_and_read_project_file(input_file: &Path) -> io::Result<(PathBuf, String)> {
    let path = try!(lookup_project_file(input_file));
    let mut file = try!(File::open(&path));
    let mut toml = String::new();
    try!(file.read_to_string(&mut toml));
    Ok((path, toml))
}

fn update_config(config: &mut Config, matches: &Matches) {
    config.verbose = matches.opt_present("verbose");
}

fn execute() -> i32 {
    let mut opts = Options::new();
    opts.optflag("h", "help", "show this message");
    opts.optflag("v", "verbose", "show progress");
    opts.optopt("",
                "write-mode",
                "mode to write in (not usable when piping from stdin)",
                "[replace|overwrite|display|diff|coverage]");

    opts.optflag("",
                 "config-help",
                 "show details of rustfmt configuration options");

    let matches = match opts.parse(env::args().skip(1)) {
        Ok(m) => m,
        Err(e) => {
            print_usage(&opts, &e.to_string());
            return 1;
        }
    };

    let operation = determine_operation(&matches);

    match operation {
        Operation::InvalidInput(reason) => {
            print_usage(&opts, &reason);
            1
        }
        Operation::Help => {
            print_usage(&opts, "");
            0
        }
        Operation::ConfigHelp => {
            Config::print_docs();
            0
        }
        Operation::Stdin(input, write_mode) => {
            // try to read config from local directory
            let config = match lookup_and_read_project_file(&Path::new(".")) {
                Ok((_, toml)) => {
                    Config::from_toml(&toml)
                }
                Err(_) => Default::default(),
            };

            run_from_stdin(input, write_mode, &config);
            0
        }
        Operation::Format(files, write_mode) => {
            for file in files {
                let mut config = match lookup_and_read_project_file(&file) {
                    Ok((path, toml)) => {
                        println!("Using rustfmt config file {} for {}",
                                 path.display(),
                                 file.display());
                        Config::from_toml(&toml)
                    }
                    Err(_) => Default::default(),
                };

                update_config(&mut config, &matches);
                run(&file, write_mode, &config);
            }
            0
        }
    }
}

fn main() {
    let _ = env_logger::init();
    let exit_code = execute();

    // Make sure standard output is flushed before we exit.
    std::io::stdout().flush().unwrap();

    // Exit with given exit code.
    //
    // NOTE: This immediately terminates the process without doing any cleanup,
    // so make sure to finish all necessary cleanup before this is called.
    std::process::exit(exit_code);
}

fn print_usage(opts: &Options, reason: &str) {
    let reason = format!("{}\nusage: {} [options] <file>...",
                         reason,
                         env::current_exe().unwrap().display());
    println!("{}", opts.usage(&reason));
}

fn determine_operation(matches: &Matches) -> Operation {
    if matches.opt_present("h") {
        return Operation::Help;
    }

    if matches.opt_present("config-help") {
        return Operation::ConfigHelp;
    }

    // if no file argument is supplied, read from stdin
    if matches.free.len() == 0 {

        let mut buffer = String::new();
        match io::stdin().read_to_string(&mut buffer) {
            Ok(..) => (),
            Err(e) => return Operation::InvalidInput(e.to_string()),
        }

        // WriteMode is always plain for Stdin
        return Operation::Stdin(buffer, WriteMode::Plain);
    }

    let write_mode = match matches.opt_str("write-mode") {
        Some(mode) => {
            match mode.parse() {
                Ok(mode) => mode,
                Err(..) => return Operation::InvalidInput("Unrecognized write mode".into()),
            }
        }
        None => WriteMode::Replace,
    };

    let files: Vec<_> = matches.free.iter().map(|a| PathBuf::from(a)).collect();

    Operation::Format(files, write_mode)
}
<commit_msg>Use argv[0] for usage output<commit_after>// Copyright 2015 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.

#![cfg(not(test))]

#[macro_use]
extern crate log;
extern crate rustfmt;
extern crate toml;
extern crate env_logger;
extern crate getopts;

use rustfmt::{WriteMode, run, run_from_stdin};
use rustfmt::config::Config;

use std::env;
use std::fs::{self, File};
use std::io::{self, Read, Write};
use std::path::{Path, PathBuf};

use getopts::{Matches, Options};

/// Rustfmt operations.
enum Operation {
    /// Format files and their child modules.
    Format(Vec<PathBuf>, WriteMode),
    /// Print the help message.
    Help,
    /// Print detailed configuration help.
    ConfigHelp,
    /// Invalid program input, including reason.
    InvalidInput(String),
    /// No file specified, read from stdin
    Stdin(String, WriteMode),
}

/// Try to find a project file in the input file directory and its parents.
fn lookup_project_file(input_file: &Path) -> io::Result<PathBuf> {
    let mut current = if input_file.is_relative() {
        try!(env::current_dir()).join(input_file)
    } else {
        input_file.to_path_buf()
    };

    // FIXME: We should canonize path to properly handle its parents,
    // but `canonicalize` function is unstable now (recently added API)
    // current = try!(fs::canonicalize(current));

    loop {
        let config_file = current.join("rustfmt.toml");
        if fs::metadata(&config_file).is_ok() {
            return Ok(config_file);
        }

        // If the current directory has no parent, we're done searching.
        if !current.pop() {
            return Err(io::Error::new(io::ErrorKind::NotFound, "Config not found"));
        }
    }
}

/// Try to find a project file. If it's found, read it.
fn lookup_and_read_project_file(input_file: &Path) -> io::Result<(PathBuf, String)> {
    let path = try!(lookup_project_file(input_file));
    let mut file = try!(File::open(&path));
    let mut toml = String::new();
    try!(file.read_to_string(&mut toml));
    Ok((path, toml))
}

fn update_config(config: &mut Config, matches: &Matches) {
    config.verbose = matches.opt_present("verbose");
}

fn execute() -> i32 {
    let mut opts = Options::new();
    opts.optflag("h", "help", "show this message");
    opts.optflag("v", "verbose", "show progress");
    opts.optopt("",
                "write-mode",
                "mode to write in (not usable when piping from stdin)",
                "[replace|overwrite|display|diff|coverage]");

    opts.optflag("",
                 "config-help",
                 "show details of rustfmt configuration options");

    let matches = match opts.parse(env::args().skip(1)) {
        Ok(m) => m,
        Err(e) => {
            print_usage(&opts, &e.to_string());
            return 1;
        }
    };

    let operation = determine_operation(&matches);

    match operation {
        Operation::InvalidInput(reason) => {
            print_usage(&opts, &reason);
            1
        }
        Operation::Help => {
            print_usage(&opts, "");
            0
        }
        Operation::ConfigHelp => {
            Config::print_docs();
            0
        }
        Operation::Stdin(input, write_mode) => {
            // try to read config from local directory
            let config = match lookup_and_read_project_file(&Path::new(".")) {
                Ok((_, toml)) => {
                    Config::from_toml(&toml)
                }
                Err(_) => Default::default(),
            };

            run_from_stdin(input, write_mode, &config);
            0
        }
        Operation::Format(files, write_mode) => {
            for file in files {
                let mut config = match lookup_and_read_project_file(&file) {
                    Ok((path, toml)) => {
                        println!("Using rustfmt config file {} for {}",
                                 path.display(),
                                 file.display());
                        Config::from_toml(&toml)
                    }
                    Err(_) => Default::default(),
                };

                update_config(&mut config, &matches);
                run(&file, write_mode, &config);
            }
            0
        }
    }
}

fn main() {
    let _ = env_logger::init();
    let exit_code = execute();

    // Make sure standard output is flushed before we exit.
    std::io::stdout().flush().unwrap();

    // Exit with given exit code.
    //
    // NOTE: This immediately terminates the process without doing any cleanup,
    // so make sure to finish all necessary cleanup before this is called.
    std::process::exit(exit_code);
}

fn print_usage(opts: &Options, reason: &str) {
    let reason = format!("{}\nusage: {} [options] <file>...",
                         reason,
                         env::args_os().next().unwrap().to_string_lossy());
    println!("{}", opts.usage(&reason));
}

fn determine_operation(matches: &Matches) -> Operation {
    if matches.opt_present("h") {
        return Operation::Help;
    }

    if matches.opt_present("config-help") {
        return Operation::ConfigHelp;
    }

    // if no file argument is supplied, read from stdin
    if matches.free.len() == 0 {

        let mut buffer = String::new();
        match io::stdin().read_to_string(&mut buffer) {
            Ok(..) => (),
            Err(e) => return Operation::InvalidInput(e.to_string()),
        }

        // WriteMode is always plain for Stdin
        return Operation::Stdin(buffer, WriteMode::Plain);
    }

    let write_mode = match matches.opt_str("write-mode") {
        Some(mode) => {
            match mode.parse() {
                Ok(mode) => mode,
                Err(..) => return Operation::InvalidInput("Unrecognized write mode".into()),
            }
        }
        None => WriteMode::Replace,
    };

    let files: Vec<_> = matches.free.iter().map(|a| PathBuf::from(a)).collect();

    Operation::Format(files, write_mode)
}
<|endoftext|>


<commit_before>// Copyright © 2016, Canal TP and/or its affiliates. All rights reserved.
//
// LICENCE: This program is free software; you can redistribute it
// and/or modify it under the terms of the GNU Affero General Public
// License as published by the Free Software Foundation, either
// version 3 of the License, or (at your option) any later version.
//
// This program 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
// Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public
// License along with this program. If not, see
// <http://www.gnu.org/licenses/>.

extern crate docopt;
extern crate csv;
extern crate rustc_serialize;
extern crate curl;
extern crate mimirsbrunn;

use std::path::Path;

#[derive(RustcDecodable, RustcEncodable)]
pub struct Bano {
    pub id: String,
    pub nb: String,
    pub street: String,
    pub zip: String,
    pub city: String,
    pub src: String,
    pub lat: f64,
    pub lon: f64,
}

impl Bano {
    pub fn insee(&self) -> &str {
        assert!(self.id.len() >= 5);
        &self.id[..5]
    }
    pub fn fantoir(&self) -> &str {
        assert!(self.id.len() >= 10);
        &self.id[..10]
    }
    pub fn into_addr(self) -> mimirsbrunn::Addr {
        let street_name = format!("{}, {} {}", self.street, self.zip, self.city);
        let addr_name = format!("{} {}", self.nb, street_name);
        let street_id = format!("street:{}", self.fantoir().to_string());
        let admin = mimirsbrunn::Admin {
            id: format!("admin:{}", self.insee()),
            level: 8,
            name: self.city,
            zip_code: self.zip,
            weight: 1,
        };
        let street = mimirsbrunn::Street {
            id: street_id,
            street_name: self.street,
            name: street_name,
            administrative_region: admin,
            weight: 1,
        };
        mimirsbrunn::Addr {
            id: format!("addr:{};{}", self.lat, self.lon),
            house_number: self.nb,
            street: street,
            name: addr_name,
            coord: mimirsbrunn::Coord { lat: self.lat, lon: self.lon },
            weight: 1,
        }
    }
}

fn index_bano(files: &[String]) {
    println!("purge and create Munin...");
    mimirsbrunn::purge_and_create_munin().unwrap();
    println!("Munin purged and created.");

    for f in files.iter() {
        println!("importing {}...", f);
        let mut rdr = csv::Reader::from_file(&Path::new(&f)).unwrap().has_headers(false);

        let iter = rdr.decode().map(|r| { let b: Bano = r.unwrap(); b.into_addr() });
        let nb = mimirsbrunn::index(iter).unwrap();
        println!("importing {}: {} addresses added.", f, nb);
    }
}

#[derive(RustcDecodable, Debug)]
struct Args {
    arg_bano_files: Vec<String>
}

static USAGE: &'static str = "
Usage:
    bano2mimir <bano-files>...
";

fn main() {
    println!("c'est tipar");

    let args: Args = docopt::Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit());

    index_bano(&args.arg_bano_files);
}
<commit_msg>remove debug<commit_after>// Copyright © 2016, Canal TP and/or its affiliates. All rights reserved.
//
// LICENCE: This program is free software; you can redistribute it
// and/or modify it under the terms of the GNU Affero General Public
// License as published by the Free Software Foundation, either
// version 3 of the License, or (at your option) any later version.
//
// This program 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
// Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public
// License along with this program. If not, see
// <http://www.gnu.org/licenses/>.

extern crate docopt;
extern crate csv;
extern crate rustc_serialize;
extern crate curl;
extern crate mimirsbrunn;

use std::path::Path;

#[derive(RustcDecodable, RustcEncodable)]
pub struct Bano {
    pub id: String,
    pub nb: String,
    pub street: String,
    pub zip: String,
    pub city: String,
    pub src: String,
    pub lat: f64,
    pub lon: f64,
}

impl Bano {
    pub fn insee(&self) -> &str {
        assert!(self.id.len() >= 5);
        &self.id[..5]
    }
    pub fn fantoir(&self) -> &str {
        assert!(self.id.len() >= 10);
        &self.id[..10]
    }
    pub fn into_addr(self) -> mimirsbrunn::Addr {
        let street_name = format!("{}, {} {}", self.street, self.zip, self.city);
        let addr_name = format!("{} {}", self.nb, street_name);
        let street_id = format!("street:{}", self.fantoir().to_string());
        let admin = mimirsbrunn::Admin {
            id: format!("admin:{}", self.insee()),
            level: 8,
            name: self.city,
            zip_code: self.zip,
            weight: 1,
        };
        let street = mimirsbrunn::Street {
            id: street_id,
            street_name: self.street,
            name: street_name,
            administrative_region: admin,
            weight: 1,
        };
        mimirsbrunn::Addr {
            id: format!("addr:{};{}", self.lat, self.lon),
            house_number: self.nb,
            street: street,
            name: addr_name,
            coord: mimirsbrunn::Coord { lat: self.lat, lon: self.lon },
            weight: 1,
        }
    }
}

fn index_bano(files: &[String]) {
    println!("purge and create Munin...");
    mimirsbrunn::purge_and_create_munin().unwrap();
    println!("Munin purged and created.");

    for f in files.iter() {
        println!("importing {}...", f);
        let mut rdr = csv::Reader::from_file(&Path::new(&f)).unwrap().has_headers(false);

        let iter = rdr.decode().map(|r| { let b: Bano = r.unwrap(); b.into_addr() });
        let nb = mimirsbrunn::index(iter).unwrap();
        println!("importing {}: {} addresses added.", f, nb);
    }
}

#[derive(RustcDecodable, Debug)]
struct Args {
    arg_bano_files: Vec<String>
}

static USAGE: &'static str = "
Usage:
    bano2mimir <bano-files>...
";

fn main() {
    println!("importing bano into Mimir");

    let args: Args = docopt::Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit());

    index_bano(&args.arg_bano_files);
}
<|endoftext|>


<commit_before>use bins::error::*;
use bins::Bins;
use bins::configuration::BinsConfiguration;
use bins::engines;
use bins::FlexibleRange;
use bins::network;
use clap::{App, Arg, ArgGroup};
use hyper::Url;
use std::path::Path;
use std::process;

pub struct Arguments {
  pub all: bool,
  pub auth: bool,
  pub bin: Option<String>,
  pub copy: bool,
  pub files: Vec<String>,
  pub force: bool,
  pub input: Option<String>,
  pub json: bool,
  pub message: Option<String>,
  pub name: Option<String>,
  pub number_lines: bool,
  pub output: Option<String>,
  pub private: bool,
  pub range: Option<FlexibleRange>,
  pub raw_urls: bool,
  pub server: Option<Url>,
  pub urls: bool,
  pub write: bool
}

include!(concat!(env!("OUT_DIR"), "/git_short_tag.rs"));

fn get_name() -> String {
  option_env!("CARGO_PKG_NAME").unwrap_or("unknown_name").to_owned()
}

fn get_version() -> String {
  let version = option_env!("CARGO_PKG_VERSION").unwrap_or("unknown_version").to_owned();
  let git_tag = git_short_tag();
  format!("{}{}", version, git_tag)
}

fn get_feature_info() -> String {
  let mut features = Vec::new();
  if cfg!(feature = "clipboard_support") {
    features.push("clipboard_support");
  }
  if cfg!(feature = "file_type_checking") {
    features.push("file_type_checking");
  }
  if features.is_empty() {
    String::new()
  } else {
    format!("features: {}", features.join(", "))
  }
}

cfg_if! {
  if #[cfg(feature = "clipboard_support")] {
    fn get_clipboard_args<'a, 'b>() -> Vec<Arg<'a, 'b>> {
      vec![Arg::with_name("copy")
             .short("C")
             .long("copy")
             .help("copies the output of the command to the clipboard without a newline")
             .conflicts_with("no-copy"),
           Arg::with_name("no-copy")
             .short("c")
             .long("no-copy")
             .help("does not copy the output of the command to the clipboard")]
    }
  } else {
    fn get_clipboard_args<'a, 'b>() -> Vec<Arg<'a, 'b>> {
      Vec::new()
    }
  }
}

pub fn get_arguments(config: &BinsConfiguration) -> Result<Arguments> {
  let mut arguments = Arguments {
    all: false,
    auth: config.get_defaults_auth(),
    bin: config.get_defaults_bin().map(|s| s.to_owned()),
    copy: config.get_defaults_copy(),
    files: Vec::new(),
    force: false,
    input: None,
    json: false,
    message: None,
    name: None,
    number_lines: false,
    output: None,
    private: config.get_defaults_private(),
    range: None,
    raw_urls: false,
    server: None,
    urls: false,
    write: false
  };
  let name = get_name();
  let version = get_version();
  let feature_info = get_feature_info();
  let combined_version = if feature_info.is_empty() {
    version
  } else {
    format!("{}\n{}", version, feature_info)
  };
  let mut app = App::new(name.as_ref())
    .version(combined_version.as_ref())
    .about("A tool for pasting from the terminal")
    .arg(Arg::with_name("files")
      .help("files to paste")
      .takes_value(true)
      .multiple(true))
    .arg(Arg::with_name("message")
      .short("m")
      .long("message")
      .help("message to paste")
      .use_delimiter(false)
      .takes_value(true)
      .value_name("string"))
    .arg(Arg::with_name("private")
      .short("p")
      .long("private")
      .help("if the paste should be private")
      .conflicts_with("public"))
    .arg(Arg::with_name("public")
      .short("P")
      .long("public")
      .help("if the paste should be public"))
    .arg(Arg::with_name("auth")
      .short("a")
      .long("auth")
      .help("if authentication (like api keys and tokens) should be used")
      .conflicts_with("anon"))
    .arg(Arg::with_name("anon")
      .short("A")
      .long("anon")
      .help("if pastes should be posted without authentication"))
    .arg(Arg::with_name("bin")
      .short("b")
      .long("bin")
      .help("bin to use when uploading")
      .takes_value(true)
      .possible_values(&*engines::get_bin_names()))
    .arg(Arg::with_name("service")
      .short("s")
      .long("service")
      .help("legacy flag included for backwards compatibility. use --bin, as this will be removed in 2.0.0")
      .takes_value(true)
      .possible_values(&*engines::get_bin_names()))
    .group(ArgGroup::with_name("bin_or_service")
      .args(&["bin", "service"])
      .required(arguments.bin.is_none()))
    .arg(Arg::with_name("list-bins")
      .short("l")
      .long("list-bins")
      .help("lists available bins and exits")
      .conflicts_with_all(&["files", "message", "private", "public", "auth", "anon", "bin_or_service", "input"]))
    .arg(Arg::with_name("list-services")
      .long("list-services")
      .help("legacy flag included for backwards compatibility. use --list-bins, as this will be removed in 2.0.0")
      .conflicts_with_all(&["files", "message", "private", "public", "auth", "anon", "bin_or_service", "input"]))
    .group(ArgGroup::with_name("list-bins_or_list-services").args(&["list-bins", "list-services"]))
    .arg(Arg::with_name("input")
      .short("i")
      .long("input")
      .help("displays raw contents of input paste")
      .takes_value(true)
      .value_name("url")
      .conflicts_with_all(&["auth", "anon", "public", "private", "message", "bin_or_service"]))
    .arg(Arg::with_name("range")
      .short("n")
      .long("range")
      .help("chooses the files to get in input mode, starting from 0 (e.g. \"0\", \"0,1\", \"0-2\", \"2-0,3\")")
      .takes_value(true)
      .value_name("range")
      .use_delimiter(false)
      .requires("input")
      .conflicts_with("files"))
    .arg(Arg::with_name("all")
      .short("L")
      .long("all")
      .help("gets all files in input mode")
      .requires("input")
      .conflicts_with_all(&["files", "range"]))
    .arg(Arg::with_name("raw-urls")
      .short("r")
      .long("raw-urls")
      .help("gets the raw urls instead of the content in input mode")
      .requires("input"))
    .arg(Arg::with_name("urls")
      .short("u")
      .long("urls")
      .help("gets the urls instead of the content in input mode")
      .requires("input")
      .conflicts_with("raw-urls"))
    .arg(Arg::with_name("server")
      .short("S")
      .long("server")
      .help("specifies the server to use for the service (only support on hastebin)")
      .takes_value(true)
      .value_name("server_url"))
    .arg(Arg::with_name("name")
      .short("N")
      .long("name")
      .help("specifies a file name for --message or stdin")
      .takes_value(true)
      .value_name("name")
      .conflicts_with("files"))
    .arg(Arg::with_name("force")
      .short("f")
      .long("force")
      .help("overrides warnings about file type or size when uploading")
      .conflicts_with("input"))
    .arg(Arg::with_name("number_lines")
      .short("e")
      .long("number-lines")
      .help("display line numbers for each file in input mode")
      .requires("input"))
    .arg(Arg::with_name("write")
      .short("w")
      .long("write")
      .help("writes pastes to files in input mode")
      .requires("input"))
    .arg(Arg::with_name("output")
      .short("o")
      .long("output")
      .help("specifies where to save files in write mode")
      .takes_value(true)
      .value_name("dir")
      .requires("write"))
    .arg(Arg::with_name("json")
      .short("j")
      .long("json")
      .help("output a json object instead of normal values")
      .conflicts_with_all(&["write", "urls", "raw-urls"]));
  for arg in get_clipboard_args() {
    app = app.arg(arg);
  }
  let res = app.get_matches();
  if res.is_present("list-bins_or_list-services") {
    println!("{}", engines::get_bin_names().join("\n"));
    process::exit(0);
  }
  if let Some(files) = res.values_of("files") {
    arguments.files = files.map(|s| s.to_owned()).collect();
  }
  if let Some(message) = res.value_of("message") {
    arguments.message = Some(message.to_owned());
  }
  if let Some(bin) = res.value_of("bin_or_service") {
    arguments.bin = Some(bin.to_owned());
  }
  if let Some(input) = res.value_of("input") {
    arguments.input = Some(input.to_owned());
  }
  if let Some(range) = res.value_of("range") {
    arguments.range = Some(try!(FlexibleRange::parse(range)));
  }
  if let Some(server) = res.value_of("server") {
    if let Some(ref bin) = arguments.bin {
      if bin.to_lowercase() != "hastebin" {
        return Err("--server may only be used if --service is hastebin".into());
      }
    }
    arguments.server = Some(try!(network::parse_url(server).chain_err(|| "invalid --server")));
  }
  if let Some(name) = res.value_of("name") {
    let name = try!(Bins::sanitize_path(Path::new(name)));
    arguments.name = Some(name.to_owned());
  }
  if let Some(output) = res.value_of("output") {
    arguments.output = Some(output.to_owned());
  }
  arguments.all = res.is_present("all");
  arguments.force = res.is_present("force");
  arguments.json = res.is_present("json");
  arguments.number_lines = res.is_present("number_lines");
  arguments.raw_urls = res.is_present("raw-urls");
  arguments.urls = res.is_present("urls");
  arguments.write = res.is_present("write");
  if res.is_present("private") {
    arguments.private = true;
  } else if res.is_present("public") {
    arguments.private = false;
  }
  if res.is_present("anon") {
    arguments.auth = false;
  } else if res.is_present("auth") {
    arguments.auth = true;
  }
  if res.is_present("copy") {
    arguments.copy = true;
  } else if res.is_present("no-copy") {
    arguments.copy = false;
  }
  Ok(arguments)
}
<commit_msg>refactor: use Option<String> instead of String for feature info<commit_after>use bins::error::*;
use bins::Bins;
use bins::configuration::BinsConfiguration;
use bins::engines;
use bins::FlexibleRange;
use bins::network;
use clap::{App, Arg, ArgGroup};
use hyper::Url;
use std::path::Path;
use std::process;

pub struct Arguments {
  pub all: bool,
  pub auth: bool,
  pub bin: Option<String>,
  pub copy: bool,
  pub files: Vec<String>,
  pub force: bool,
  pub input: Option<String>,
  pub json: bool,
  pub message: Option<String>,
  pub name: Option<String>,
  pub number_lines: bool,
  pub output: Option<String>,
  pub private: bool,
  pub range: Option<FlexibleRange>,
  pub raw_urls: bool,
  pub server: Option<Url>,
  pub urls: bool,
  pub write: bool
}

include!(concat!(env!("OUT_DIR"), "/git_short_tag.rs"));

fn get_name() -> String {
  option_env!("CARGO_PKG_NAME").unwrap_or("unknown_name").to_owned()
}

fn get_version() -> String {
  let version = option_env!("CARGO_PKG_VERSION").unwrap_or("unknown_version").to_owned();
  let git_tag = git_short_tag();
  format!("{}{}", version, git_tag)
}

fn get_feature_info() -> Option<String> {
  let mut features = Vec::new();
  if cfg!(feature = "clipboard_support") {
    features.push("clipboard_support");
  }
  if cfg!(feature = "file_type_checking") {
    features.push("file_type_checking");
  }
  if features.is_empty() {
    None
  } else {
    Some(format!("features: {}", features.join(", ")))
  }
}

cfg_if! {
  if #[cfg(feature = "clipboard_support")] {
    fn get_clipboard_args<'a, 'b>() -> Vec<Arg<'a, 'b>> {
      vec![Arg::with_name("copy")
             .short("C")
             .long("copy")
             .help("copies the output of the command to the clipboard without a newline")
             .conflicts_with("no-copy"),
           Arg::with_name("no-copy")
             .short("c")
             .long("no-copy")
             .help("does not copy the output of the command to the clipboard")]
    }
  } else {
    fn get_clipboard_args<'a, 'b>() -> Vec<Arg<'a, 'b>> {
      Vec::new()
    }
  }
}

pub fn get_arguments(config: &BinsConfiguration) -> Result<Arguments> {
  let mut arguments = Arguments {
    all: false,
    auth: config.get_defaults_auth(),
    bin: config.get_defaults_bin().map(|s| s.to_owned()),
    copy: config.get_defaults_copy(),
    files: Vec::new(),
    force: false,
    input: None,
    json: false,
    message: None,
    name: None,
    number_lines: false,
    output: None,
    private: config.get_defaults_private(),
    range: None,
    raw_urls: false,
    server: None,
    urls: false,
    write: false
  };
  let name = get_name();
  let version = get_version();
  let feature_info = get_feature_info();
  let combined_version = match feature_info {
    Some(i) => format!("{}\n{}", version, i),
    None => version
  };
  let mut app = App::new(name.as_ref())
    .version(combined_version.as_ref())
    .about("A tool for pasting from the terminal")
    .arg(Arg::with_name("files")
      .help("files to paste")
      .takes_value(true)
      .multiple(true))
    .arg(Arg::with_name("message")
      .short("m")
      .long("message")
      .help("message to paste")
      .use_delimiter(false)
      .takes_value(true)
      .value_name("string"))
    .arg(Arg::with_name("private")
      .short("p")
      .long("private")
      .help("if the paste should be private")
      .conflicts_with("public"))
    .arg(Arg::with_name("public")
      .short("P")
      .long("public")
      .help("if the paste should be public"))
    .arg(Arg::with_name("auth")
      .short("a")
      .long("auth")
      .help("if authentication (like api keys and tokens) should be used")
      .conflicts_with("anon"))
    .arg(Arg::with_name("anon")
      .short("A")
      .long("anon")
      .help("if pastes should be posted without authentication"))
    .arg(Arg::with_name("bin")
      .short("b")
      .long("bin")
      .help("bin to use when uploading")
      .takes_value(true)
      .possible_values(&*engines::get_bin_names()))
    .arg(Arg::with_name("service")
      .short("s")
      .long("service")
      .help("legacy flag included for backwards compatibility. use --bin, as this will be removed in 2.0.0")
      .takes_value(true)
      .possible_values(&*engines::get_bin_names()))
    .group(ArgGroup::with_name("bin_or_service")
      .args(&["bin", "service"])
      .required(arguments.bin.is_none()))
    .arg(Arg::with_name("list-bins")
      .short("l")
      .long("list-bins")
      .help("lists available bins and exits")
      .conflicts_with_all(&["files", "message", "private", "public", "auth", "anon", "bin_or_service", "input"]))
    .arg(Arg::with_name("list-services")
      .long("list-services")
      .help("legacy flag included for backwards compatibility. use --list-bins, as this will be removed in 2.0.0")
      .conflicts_with_all(&["files", "message", "private", "public", "auth", "anon", "bin_or_service", "input"]))
    .group(ArgGroup::with_name("list-bins_or_list-services").args(&["list-bins", "list-services"]))
    .arg(Arg::with_name("input")
      .short("i")
      .long("input")
      .help("displays raw contents of input paste")
      .takes_value(true)
      .value_name("url")
      .conflicts_with_all(&["auth", "anon", "public", "private", "message", "bin_or_service"]))
    .arg(Arg::with_name("range")
      .short("n")
      .long("range")
      .help("chooses the files to get in input mode, starting from 0 (e.g. \"0\", \"0,1\", \"0-2\", \"2-0,3\")")
      .takes_value(true)
      .value_name("range")
      .use_delimiter(false)
      .requires("input")
      .conflicts_with("files"))
    .arg(Arg::with_name("all")
      .short("L")
      .long("all")
      .help("gets all files in input mode")
      .requires("input")
      .conflicts_with_all(&["files", "range"]))
    .arg(Arg::with_name("raw-urls")
      .short("r")
      .long("raw-urls")
      .help("gets the raw urls instead of the content in input mode")
      .requires("input"))
    .arg(Arg::with_name("urls")
      .short("u")
      .long("urls")
      .help("gets the urls instead of the content in input mode")
      .requires("input")
      .conflicts_with("raw-urls"))
    .arg(Arg::with_name("server")
      .short("S")
      .long("server")
      .help("specifies the server to use for the service (only support on hastebin)")
      .takes_value(true)
      .value_name("server_url"))
    .arg(Arg::with_name("name")
      .short("N")
      .long("name")
      .help("specifies a file name for --message or stdin")
      .takes_value(true)
      .value_name("name")
      .conflicts_with("files"))
    .arg(Arg::with_name("force")
      .short("f")
      .long("force")
      .help("overrides warnings about file type or size when uploading")
      .conflicts_with("input"))
    .arg(Arg::with_name("number_lines")
      .short("e")
      .long("number-lines")
      .help("display line numbers for each file in input mode")
      .requires("input"))
    .arg(Arg::with_name("write")
      .short("w")
      .long("write")
      .help("writes pastes to files in input mode")
      .requires("input"))
    .arg(Arg::with_name("output")
      .short("o")
      .long("output")
      .help("specifies where to save files in write mode")
      .takes_value(true)
      .value_name("dir")
      .requires("write"))
    .arg(Arg::with_name("json")
      .short("j")
      .long("json")
      .help("output a json object instead of normal values")
      .conflicts_with_all(&["write", "urls", "raw-urls"]));
  for arg in get_clipboard_args() {
    app = app.arg(arg);
  }
  let res = app.get_matches();
  if res.is_present("list-bins_or_list-services") {
    println!("{}", engines::get_bin_names().join("\n"));
    process::exit(0);
  }
  if let Some(files) = res.values_of("files") {
    arguments.files = files.map(|s| s.to_owned()).collect();
  }
  if let Some(message) = res.value_of("message") {
    arguments.message = Some(message.to_owned());
  }
  if let Some(bin) = res.value_of("bin_or_service") {
    arguments.bin = Some(bin.to_owned());
  }
  if let Some(input) = res.value_of("input") {
    arguments.input = Some(input.to_owned());
  }
  if let Some(range) = res.value_of("range") {
    arguments.range = Some(try!(FlexibleRange::parse(range)));
  }
  if let Some(server) = res.value_of("server") {
    if let Some(ref bin) = arguments.bin {
      if bin.to_lowercase() != "hastebin" {
        return Err("--server may only be used if --service is hastebin".into());
      }
    }
    arguments.server = Some(try!(network::parse_url(server).chain_err(|| "invalid --server")));
  }
  if let Some(name) = res.value_of("name") {
    let name = try!(Bins::sanitize_path(Path::new(name)));
    arguments.name = Some(name.to_owned());
  }
  if let Some(output) = res.value_of("output") {
    arguments.output = Some(output.to_owned());
  }
  arguments.all = res.is_present("all");
  arguments.force = res.is_present("force");
  arguments.json = res.is_present("json");
  arguments.number_lines = res.is_present("number_lines");
  arguments.raw_urls = res.is_present("raw-urls");
  arguments.urls = res.is_present("urls");
  arguments.write = res.is_present("write");
  if res.is_present("private") {
    arguments.private = true;
  } else if res.is_present("public") {
    arguments.private = false;
  }
  if res.is_present("anon") {
    arguments.auth = false;
  } else if res.is_present("auth") {
    arguments.auth = true;
  }
  if res.is_present("copy") {
    arguments.copy = true;
  } else if res.is_present("no-copy") {
    arguments.copy = false;
  }
  Ok(arguments)
}
<|endoftext|>