<commit_before>///!
///! Define types which allow us to serialize/de-serialize
///! messages when interacting with the todoist API.
///!
///! Types manually converted from documentation here:
///!  - [Todoist API](https://developer.todoist.com/)
///!
///! Note: These are using v7 definition of the todoist API.
///!
///! We attribute each of the structs with:
///!
///! - `Serialize` and `Deserialize` impls for serialization.
///! - 'Debug ' impl for ... debugging.
///!
use serde::{Deserialize, Serialize};

/// Struct to be used for objects which we don't
/// yet support. It doesn't deserialize anything.
///
#[derive(Serialize, Deserialize, Debug)]
pub struct NotYetUsedStruct {}

#[derive(Serialize, Deserialize, Debug)]
pub struct LabelStruct {}

// Automatically generate:
// - `Serialize` and `Deserialize` impls for serialization.
// - 'Debug ' impl for ... debugging.
#[derive(Serialize, Deserialize, Debug)]
pub struct ItemStruct {
    /// The id of the task.
    pub id: u64,

    /// The owner of the task.
    pub user_id: u64,

    /// The project that the task resides in.
    pub project_id: u64,

    /// The parent id of the task.
    pub parent_id: Option<u64>,

    /// The text of the task.
    pub content: String,

    /// The date of the task, added in free form text,
    /// for example it can be every day @ 10 (or null or an empty string if not set).
    pub date_string: String,

    /// The language of the date_string.
    /// Valid languages are: en, da, pl, zh, ko, de, pt, ja, it, fr, sv, ru, es, nl
    pub date_lang: String,

    /// The date of the task in the format:
    /// Mon 07 Aug 2006 12:34:56 +0000 (or null if not set).
    ///
    /// For all day task (i.e. task due “Today”),
    /// the time part will be set as xx:xx:59.
    pub due_date_utc: Option<String>,

    /// The priority of the task between: 1..4.
    /// - 1 is natural
    /// - 4 is very urgent
    pub priority: u8,

    /// The indent of the task between: 1..4.
    /// - 1 is top-level
    pub indent: u8,

    /// The order of the task inside the project.
    /// smallest value is placed at the top.
    pub item_order: u64,

    /// The order of the task inside the Today or Next 7 days view
    /// (a number, where the smallest value would place the task at the top).
    pub day_order: i64,

    /// Whether the task's sub-tasks are collapsed.
    pub collapsed: u8,

    /// The tasks label ids.
    pub labels: Vec<u64>,

    /// The id of the user who assigned the current task.
    /// Only makes sense for shared projects.
    pub assigned_by_uid: Option<u64>,

    /// The id of user who is responsible for the current task.
    /// Only makes sense for shared projects.
    pub responsible_uid: Option<u64>,

    /// Whether the task is marked as completed.
    pub checked: u8,

    /// Whether the task hsa been marked as completed is marked to be moved to history.
    pub in_history: u8,

    /// Whether the task is marked as deleted.
    pub is_deleted: u8,

    /// Whether the task is marked as archived.
    pub is_archived: u8,

    /// A special id for shared tasks. Can be ignored.
    pub sync_id: Option<u64>,

    /// The date when the task was created.
    pub date_added: Option<String>,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct UserFeaturesStruct {
    karma_disabled: bool,

    restriction: u8,

    karma_vacation: bool,

    beta: u8,

    has_push_reminders: bool,

    dateist_inline_disabled: bool,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct TimeZoneStruct {
    /// Hours difference from GMT.
    hours: i8,

    /// Minutes difference from GMT.
    minutes: i8,

    /// Time difference from GMT as a string.
    gmt_string: String,

    /// Whether daylight saving time applies.
    is_dst: u8,

    /// The timezone name.
    timezone: String,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct UserStruct {
    /// URL to the small size of the user avatar.
    pub avatar_small: String,

    /// The users avatar in a medium size.
    pub avatar_medium: String,

    /// The users avatar in a big size.
    pub avatar_big: String,

    /// The users avatar in yet another resolution.
    pub avatar_s640: String,

    /// The users start page.
    pub start_page: String,

    /// What features the user has set.
    pub features: UserFeaturesStruct,

    /// How many tasks the user has completed today.
    pub completed_today: u32,

    /// Is this a premium user.
    pub is_premium: bool,

    /// If projects should be shown in oldest dates first (value = 0)
    /// or oldest dates last (value = 1)
    pub sort_order: u8,

    /// The users full name.
    pub full_name: String,

    /// The default time in minutes for the automatic reminders.
    pub auto_reminder: u32,

    /// The date the user joined.
    pub join_date: Option<String>,

    /// The users identification number.
    pub id: u64,

    /// The day of the next week, that tasks will be postponed to.
    /// (Between 1 and y, where Monday is 1 and 7 is Sunday)
    pub next_week: u8,

    /// The total number tasks the user has completed.
    pub completed_count: u64,

    /// The daily goal of tasks.
    pub daily_goal: u32,

    /// The currently selected Todoist theme (between 0 and 10).
    pub theme: u8,

    /// The users email address.
    pub email: String,

    /// The first day of the week (between 1 (Monday) and 7 (Sunday))
    pub start_day: u8,

    /// Users timezone information.
    pub tz_info: TimeZoneStruct,

    /// Whether to use the DD-MM-YYYY date format (if set to 0),
    /// or the MM-DD-YYYY format (if set to 1).
    pub date_format: u8,

    /// The project the user has selected to use as their inbox.
    pub inbox_project: u64,

    /// Whether to use a 24h format such as 13:00 (if set to 0) when displaying time,
    /// or a 12h format such as 1:00pm (if set to 1).
    pub time_format: u8,

    /// The id of the user's avatar.
    pub image_id: String,

    /// The user's karma trend.
    pub karma_trend: String,

    /// The id o the user's business account.
    pub business_account_id: Option<u64>,

    /// The users mobile phone number.
    pub mobile_number: Option<String>,

    /// The users mobile host number.
    pub mobile_host: Option<String>,

    /// The date when the users's Premium subscrpition ends.
    pub premium_until: Option<String>,

    /// The users authentication token.
    pub token: String,

    /// The user's karma score.
    pub karma: u64,

    /// Whether the user is a business account admin.
    pub is_biz_admin: bool,

    /// The default reminder for the user. Reminders are only possible for Premium users.
    /// The default reminder can be one of the following: email to send reminders by email,
    /// mobile to send reminders to mobile devices via SMS, push to send reminders to smart
    /// devices using push notifications (one of the Android or iOS official clients must be
    /// installed on the client side to receive these notifications),
    /// no_default to turn off sending default reminders.
    pub default_reminder: Option<String>,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct FilterStruct {
    /// The presentation order of the filter.
    item_order: u8,

    /// The name of the filter.
    name: String,

    /// The query specifier for this filter.
    query: String,

    /// The color to present this filter as.
    color: u8,

    /// Is the filter deleted.
    is_deleted: u8,

    /// The identifier of this filter.
    id: u64,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct ReminderStruct {
    /// The item id that this reminder is for.
    item_id: u64,

    /// The alias name for the location.
    name: String,

    /// The service to get the reminder for.
    service: String,

    /// The type of reminder, 'relative' for time-based reminder in minutes.
    /// 'absolute' for a time-based reminder with a specific time and date in the future.
    /// 'location' for a location-based reminder.
    /// type: String,

    /// The date of the task, added in free form text,
    /// for example it can be every day @ 10 (or null or an empty string if not set).
    date_string: String,

    /// The language of the date_string.
    /// Valid languages are: en, da, pl, zh, ko, de, pt, ja, it, fr, sv, ru, es, nl
    date_lang: String,

    /// Is the reminder deleted (0,1).
    is_deleted: u8,

    /// The identifier of this filter.
    id: u64,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct ProjectStruct {
    /// The name of the project.
    pub name: String,

    /// The id of the parent project.
    pub parent_id: Option<u64>,

    /// The color to present this filter as.
    pub color: u8,

    /// Whether the project's sub-projects are collapsed (0,1)
    pub collapsed: u8,

    /// This project is marked as the inbox project.
    pub inbox_project: Option<bool>,

    /// This project has more notes.
    pub has_more_notes: Option<bool>,

    /// The item order (0,1).
    pub item_order: u8,

    // The indent of the time (1..4) where 1 is top level.
    pub indent: u8,

    /// The identifier of this filter.
    pub id: u64,

    /// Is the project deleted (0,1).
    pub is_deleted: u8,

    /// Whether the project is marked as archived (0,1)
    pub is_archived: u8,
}

// Automatically generate:
// - `Serialize` and `Deserialize` impls for serialization.
// - 'Debug ' impl for ... debugging.
#[derive(Serialize, Deserialize, Debug)]
pub struct SyncStruct {
    /// Collaborators
    /// Note: Not yet used, so we don't parse it yet.
    pub collaborators: Option<Vec<NotYetUsedStruct>>,

    /// Collaborator states.
    /// Note: Not yet used, so we don't parse it yet.
    pub collaborator_states: Option<Vec<NotYetUsedStruct>>,

    // Specifies the order of items in daily agenda.
    pub day_orders: Option<NotYetUsedStruct>,

    /// An array of filters.
    pub filters: Option<Vec<FilterStruct>>,

    /// Whether the response contains all data (full sync), or incremental.
    pub full_sync: bool,

    /// An array of items.
    pub items: Option<Vec<ItemStruct>>,

    /// An array of labels.
    pub labels: Option<Vec<LabelStruct>>,

    /// An array of live notifications.
    pub live_notifications: Option<Vec<NotYetUsedStruct>>,

    /// The last live notification the user saw.
    /// Used for implementing unread notifications.
    pub live_notifications_last_read_id: Option<u64>,

    /// An array of notes.
    /// Note: Not yet used, so we don't parse it yet.
    pub notes: Option<Vec<NotYetUsedStruct>>,

    /// An array of projects.
    pub projects: Option<Vec<ProjectStruct>>,

    /// An array of reminders.
    pub reminders: Option<Vec<ReminderStruct>>,

    /// A new synchronization token.
    /// Used by the client on the next sync request to do incremental sync.
    pub sync_token: String,

    /// The users information.
    pub user: Option<UserStruct>,
}

#[cfg(test)]
use serde_json;

#[test]
fn item_deserialize_test() {
    let json_item = r#"{
                      "id": 33511505,
                      "user_id": 1_855_589,
                      "project_id": 128_501_470,
                      "content": "Task1",
                      "date_string": "",
                      "date_lang": "en",
                      "due_date_utc": null,
                      "indent": 1,
                      "priority": 1,
                      "item_order": 1,
                      "day_order": -1,
                      "collapsed": 0,
                      "labels": [12_839_231, 18_391_839],
                      "assigned_by_uid": 1_855_589,
                      "responsible_uid": null,
                      "checked": 0,
                      "in_history": 0,
                      "is_deleted": 0,
                      "is_archived": 0,
                      "sync_id": null,
                      "date_added": "Fri 26 Sep 2014 08:25:05 +0000"
                    }"#;

    let item: ItemStruct = serde_json::from_str(&json_item).unwrap();

    assert_eq!(item.id, 33_511_505);
    assert_eq!(item.user_id, 1_855_589);
    assert_eq!(item.content, "Task1");
    assert_eq!(item.date_string, "");
    assert_eq!(item.date_lang, "en");
    assert_eq!(item.due_date_utc, None);
    assert_eq!(item.indent, 1);
    assert_eq!(item.priority, 1);
    assert_eq!(item.item_order, 1);
    assert_eq!(item.day_order, -1);
    assert_eq!(item.collapsed, 0);
    assert_eq!(item.labels, vec![12_839_231, 18_391_839]);
    assert_eq!(item.assigned_by_uid.unwrap(), 1_855_589);
    assert_eq!(item.responsible_uid, None);
    assert_eq!(item.checked, 0);
    assert_eq!(item.in_history, 0);
    assert_eq!(item.is_deleted, 0);
    assert_eq!(item.is_archived, 0);
    assert_eq!(item.sync_id, None);
    assert_eq!(item.date_added.unwrap(), "Fri 26 Sep 2014 08:25:05 +0000");
}
<commit_msg>Tests: Fix test I broken when going crazy with clippy lints :)<commit_after>///!
///! Define types which allow us to serialize/de-serialize
///! messages when interacting with the todoist API.
///!
///! Types manually converted from documentation here:
///!  - [Todoist API](https://developer.todoist.com/)
///!
///! Note: These are using v7 definition of the todoist API.
///!
///! We attribute each of the structs with:
///!
///! - `Serialize` and `Deserialize` impls for serialization.
///! - 'Debug ' impl for ... debugging.
///!
use serde::{Deserialize, Serialize};

/// Struct to be used for objects which we don't
/// yet support. It doesn't deserialize anything.
///
#[derive(Serialize, Deserialize, Debug)]
pub struct NotYetUsedStruct {}

#[derive(Serialize, Deserialize, Debug)]
pub struct LabelStruct {}

// Automatically generate:
// - `Serialize` and `Deserialize` impls for serialization.
// - 'Debug ' impl for ... debugging.
#[derive(Serialize, Deserialize, Debug)]
pub struct ItemStruct {
    /// The id of the task.
    pub id: u64,

    /// The owner of the task.
    pub user_id: u64,

    /// The project that the task resides in.
    pub project_id: u64,

    /// The parent id of the task.
    pub parent_id: Option<u64>,

    /// The text of the task.
    pub content: String,

    /// The date of the task, added in free form text,
    /// for example it can be every day @ 10 (or null or an empty string if not set).
    pub date_string: String,

    /// The language of the date_string.
    /// Valid languages are: en, da, pl, zh, ko, de, pt, ja, it, fr, sv, ru, es, nl
    pub date_lang: String,

    /// The date of the task in the format:
    /// Mon 07 Aug 2006 12:34:56 +0000 (or null if not set).
    ///
    /// For all day task (i.e. task due “Today”),
    /// the time part will be set as xx:xx:59.
    pub due_date_utc: Option<String>,

    /// The priority of the task between: 1..4.
    /// - 1 is natural
    /// - 4 is very urgent
    pub priority: u8,

    /// The indent of the task between: 1..4.
    /// - 1 is top-level
    pub indent: u8,

    /// The order of the task inside the project.
    /// smallest value is placed at the top.
    pub item_order: u64,

    /// The order of the task inside the Today or Next 7 days view
    /// (a number, where the smallest value would place the task at the top).
    pub day_order: i64,

    /// Whether the task's sub-tasks are collapsed.
    pub collapsed: u8,

    /// The tasks label ids.
    pub labels: Vec<u64>,

    /// The id of the user who assigned the current task.
    /// Only makes sense for shared projects.
    pub assigned_by_uid: Option<u64>,

    /// The id of user who is responsible for the current task.
    /// Only makes sense for shared projects.
    pub responsible_uid: Option<u64>,

    /// Whether the task is marked as completed.
    pub checked: u8,

    /// Whether the task hsa been marked as completed is marked to be moved to history.
    pub in_history: u8,

    /// Whether the task is marked as deleted.
    pub is_deleted: u8,

    /// Whether the task is marked as archived.
    pub is_archived: u8,

    /// A special id for shared tasks. Can be ignored.
    pub sync_id: Option<u64>,

    /// The date when the task was created.
    pub date_added: Option<String>,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct UserFeaturesStruct {
    karma_disabled: bool,

    restriction: u8,

    karma_vacation: bool,

    beta: u8,

    has_push_reminders: bool,

    dateist_inline_disabled: bool,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct TimeZoneStruct {
    /// Hours difference from GMT.
    hours: i8,

    /// Minutes difference from GMT.
    minutes: i8,

    /// Time difference from GMT as a string.
    gmt_string: String,

    /// Whether daylight saving time applies.
    is_dst: u8,

    /// The timezone name.
    timezone: String,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct UserStruct {
    /// URL to the small size of the user avatar.
    pub avatar_small: String,

    /// The users avatar in a medium size.
    pub avatar_medium: String,

    /// The users avatar in a big size.
    pub avatar_big: String,

    /// The users avatar in yet another resolution.
    pub avatar_s640: String,

    /// The users start page.
    pub start_page: String,

    /// What features the user has set.
    pub features: UserFeaturesStruct,

    /// How many tasks the user has completed today.
    pub completed_today: u32,

    /// Is this a premium user.
    pub is_premium: bool,

    /// If projects should be shown in oldest dates first (value = 0)
    /// or oldest dates last (value = 1)
    pub sort_order: u8,

    /// The users full name.
    pub full_name: String,

    /// The default time in minutes for the automatic reminders.
    pub auto_reminder: u32,

    /// The date the user joined.
    pub join_date: Option<String>,

    /// The users identification number.
    pub id: u64,

    /// The day of the next week, that tasks will be postponed to.
    /// (Between 1 and y, where Monday is 1 and 7 is Sunday)
    pub next_week: u8,

    /// The total number tasks the user has completed.
    pub completed_count: u64,

    /// The daily goal of tasks.
    pub daily_goal: u32,

    /// The currently selected Todoist theme (between 0 and 10).
    pub theme: u8,

    /// The users email address.
    pub email: String,

    /// The first day of the week (between 1 (Monday) and 7 (Sunday))
    pub start_day: u8,

    /// Users timezone information.
    pub tz_info: TimeZoneStruct,

    /// Whether to use the DD-MM-YYYY date format (if set to 0),
    /// or the MM-DD-YYYY format (if set to 1).
    pub date_format: u8,

    /// The project the user has selected to use as their inbox.
    pub inbox_project: u64,

    /// Whether to use a 24h format such as 13:00 (if set to 0) when displaying time,
    /// or a 12h format such as 1:00pm (if set to 1).
    pub time_format: u8,

    /// The id of the user's avatar.
    pub image_id: String,

    /// The user's karma trend.
    pub karma_trend: String,

    /// The id o the user's business account.
    pub business_account_id: Option<u64>,

    /// The users mobile phone number.
    pub mobile_number: Option<String>,

    /// The users mobile host number.
    pub mobile_host: Option<String>,

    /// The date when the users's Premium subscrpition ends.
    pub premium_until: Option<String>,

    /// The users authentication token.
    pub token: String,

    /// The user's karma score.
    pub karma: u64,

    /// Whether the user is a business account admin.
    pub is_biz_admin: bool,

    /// The default reminder for the user. Reminders are only possible for Premium users.
    /// The default reminder can be one of the following: email to send reminders by email,
    /// mobile to send reminders to mobile devices via SMS, push to send reminders to smart
    /// devices using push notifications (one of the Android or iOS official clients must be
    /// installed on the client side to receive these notifications),
    /// no_default to turn off sending default reminders.
    pub default_reminder: Option<String>,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct FilterStruct {
    /// The presentation order of the filter.
    item_order: u8,

    /// The name of the filter.
    name: String,

    /// The query specifier for this filter.
    query: String,

    /// The color to present this filter as.
    color: u8,

    /// Is the filter deleted.
    is_deleted: u8,

    /// The identifier of this filter.
    id: u64,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct ReminderStruct {
    /// The item id that this reminder is for.
    item_id: u64,

    /// The alias name for the location.
    name: String,

    /// The service to get the reminder for.
    service: String,

    /// The type of reminder, 'relative' for time-based reminder in minutes.
    /// 'absolute' for a time-based reminder with a specific time and date in the future.
    /// 'location' for a location-based reminder.
    /// type: String,

    /// The date of the task, added in free form text,
    /// for example it can be every day @ 10 (or null or an empty string if not set).
    date_string: String,

    /// The language of the date_string.
    /// Valid languages are: en, da, pl, zh, ko, de, pt, ja, it, fr, sv, ru, es, nl
    date_lang: String,

    /// Is the reminder deleted (0,1).
    is_deleted: u8,

    /// The identifier of this filter.
    id: u64,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct ProjectStruct {
    /// The name of the project.
    pub name: String,

    /// The id of the parent project.
    pub parent_id: Option<u64>,

    /// The color to present this filter as.
    pub color: u8,

    /// Whether the project's sub-projects are collapsed (0,1)
    pub collapsed: u8,

    /// This project is marked as the inbox project.
    pub inbox_project: Option<bool>,

    /// This project has more notes.
    pub has_more_notes: Option<bool>,

    /// The item order (0,1).
    pub item_order: u8,

    // The indent of the time (1..4) where 1 is top level.
    pub indent: u8,

    /// The identifier of this filter.
    pub id: u64,

    /// Is the project deleted (0,1).
    pub is_deleted: u8,

    /// Whether the project is marked as archived (0,1)
    pub is_archived: u8,
}

// Automatically generate:
// - `Serialize` and `Deserialize` impls for serialization.
// - 'Debug ' impl for ... debugging.
#[derive(Serialize, Deserialize, Debug)]
pub struct SyncStruct {
    /// Collaborators
    /// Note: Not yet used, so we don't parse it yet.
    pub collaborators: Option<Vec<NotYetUsedStruct>>,

    /// Collaborator states.
    /// Note: Not yet used, so we don't parse it yet.
    pub collaborator_states: Option<Vec<NotYetUsedStruct>>,

    // Specifies the order of items in daily agenda.
    pub day_orders: Option<NotYetUsedStruct>,

    /// An array of filters.
    pub filters: Option<Vec<FilterStruct>>,

    /// Whether the response contains all data (full sync), or incremental.
    pub full_sync: bool,

    /// An array of items.
    pub items: Option<Vec<ItemStruct>>,

    /// An array of labels.
    pub labels: Option<Vec<LabelStruct>>,

    /// An array of live notifications.
    pub live_notifications: Option<Vec<NotYetUsedStruct>>,

    /// The last live notification the user saw.
    /// Used for implementing unread notifications.
    pub live_notifications_last_read_id: Option<u64>,

    /// An array of notes.
    /// Note: Not yet used, so we don't parse it yet.
    pub notes: Option<Vec<NotYetUsedStruct>>,

    /// An array of projects.
    pub projects: Option<Vec<ProjectStruct>>,

    /// An array of reminders.
    pub reminders: Option<Vec<ReminderStruct>>,

    /// A new synchronization token.
    /// Used by the client on the next sync request to do incremental sync.
    pub sync_token: String,

    /// The users information.
    pub user: Option<UserStruct>,
}

#[cfg(test)]
use serde_json;

#[test]
fn item_deserialize_test() {
    let json_item = r#"{
                      "id": 33511505,
                      "user_id": 1855589,
                      "project_id": 128501470,
                      "content": "Task1",
                      "date_string": "",
                      "date_lang": "en",
                      "due_date_utc": null,
                      "indent": 1,
                      "priority": 1,
                      "item_order": 1,
                      "day_order": -1,
                      "collapsed": 0,
                      "labels": [12839231, 18391839],
                      "assigned_by_uid": 1855589,
                      "responsible_uid": null,
                      "checked": 0,
                      "in_history": 0,
                      "is_deleted": 0,
                      "is_archived": 0,
                      "sync_id": null,
                      "date_added": "Fri 26 Sep 2014 08:25:05 +0000"
                    }"#;

    let item: ItemStruct = serde_json::from_str(&json_item).unwrap();

    assert_eq!(item.id, 33_511_505);
    assert_eq!(item.user_id, 1_855_589);
    assert_eq!(item.content, "Task1");
    assert_eq!(item.date_string, "");
    assert_eq!(item.date_lang, "en");
    assert_eq!(item.due_date_utc, None);
    assert_eq!(item.indent, 1);
    assert_eq!(item.priority, 1);
    assert_eq!(item.item_order, 1);
    assert_eq!(item.day_order, -1);
    assert_eq!(item.collapsed, 0);
    assert_eq!(item.labels, vec![12_839_231, 18_391_839]);
    assert_eq!(item.assigned_by_uid.unwrap(), 1_855_589);
    assert_eq!(item.responsible_uid, None);
    assert_eq!(item.checked, 0);
    assert_eq!(item.in_history, 0);
    assert_eq!(item.is_deleted, 0);
    assert_eq!(item.is_archived, 0);
    assert_eq!(item.sync_id, None);
    assert_eq!(item.date_added.unwrap(), "Fri 26 Sep 2014 08:25:05 +0000");
}
<|endoftext|>


<commit_before>// Copyright © 2017 Cormac O'Brien
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software
// and associated documentation files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
// BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

//! Quake PAK archive manipulation.

extern crate byteorder;

use std::collections::hash_map::Iter;
use std::collections::HashMap;
use std::error::Error;
use std::fmt;
use std::fs;
use std::io;
use std::io::{Read, Seek, SeekFrom};
use std::path::Path;
use std::string;
use byteorder::{LittleEndian, ReadBytesExt};

const PAK_MAGIC: [u8; 4] = [b'P', b'A', b'C', b'K'];
const PAK_ENTRY_SIZE: usize = 64;

#[derive(Debug)]
pub enum PakError {
    Io(io::Error),
    Utf8(string::FromUtf8Error),
    Invalid,
    Other,
}

impl fmt::Display for PakError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.description())
    }
}

impl Error for PakError {
    fn description(&self) -> &str {
        match *self {
            PakError::Io(ref e) => e.description(),
            PakError::Utf8(ref e) => e.description(),
            PakError::Invalid => "Not a valid PAK file",
            PakError::Other => "Unknown error",
        }
    }

    fn cause(&self) -> Option<&Error> {
        match *self {
            PakError::Io(ref i) => Some(i),
            PakError::Utf8(ref i) => Some(i),
            PakError::Invalid => None,
            PakError::Other => None,
        }
    }
}

impl From<io::Error> for PakError {
    fn from(err: io::Error) -> PakError {
        PakError::Io(err)
    }
}

impl From<string::FromUtf8Error> for PakError {
    fn from(err: string::FromUtf8Error) -> PakError {
        PakError::Utf8(err)
    }
}

/// A virtual file tree loaded from a PAK archive.
pub struct Pak(HashMap<String, Box<[u8]>>);

impl Pak {
    pub fn new() -> Pak {
        Pak(HashMap::new())
    }

    /// Attempts to load a virtual file tree from a PAK archive.
    ///
    /// # Examples
    /// ```
    /// use pak::Pak;
    ///
    /// let pak0 = Pak::load("pak0.pak");
    /// ```
    pub fn add<P: AsRef<Path>>(&mut self, path: P) -> Result<(), PakError> {
        debug!("Opening {}", path.as_ref().to_str().unwrap());

        let mut infile = try!(fs::File::open(path));

        let mut magic = [0u8; 4];
        try!(infile.read(&mut magic));

        if magic != PAK_MAGIC {
            return Err(PakError::Invalid);
        }

        // Locate the file table

        let wad_offset = match try!(infile.read_i32::<LittleEndian>()) {
            o if o <= 0 => return Err(PakError::Invalid),
            o => o as u32,
        };

        let wad_size = match try!(infile.read_i32::<LittleEndian>()) {
            s if s <= 0 => return Err(PakError::Invalid),
            s => s as u32,
        };

        for i in 0..(wad_size as usize / PAK_ENTRY_SIZE) {
            let entry_offset = wad_offset as u64 + (i * PAK_ENTRY_SIZE) as u64;
            try!(infile.seek(SeekFrom::Start(entry_offset)));

            let mut path_bytes = [0u8; 56];
            try!(infile.read(&mut path_bytes));

            let file_offset = match try!(infile.read_i32::<LittleEndian>()) {
                o if o <= 0 => return Err(PakError::Invalid),
                o => o as u32,
            };

            let file_size = match try!(infile.read_i32::<LittleEndian>()) {
                s if s <= 0 => return Err(PakError::Invalid),
                s => s as u32,
            };

            let last = {
                let mut _last: usize = 0;
                while path_bytes[_last] != 0 {
                    _last += 1;
                }
                _last
            };

            let path = try!(String::from_utf8(path_bytes[0..last].to_vec()));

            try!(infile.seek(SeekFrom::Start(file_offset as u64)));

            let mut data: Vec<u8> = Vec::with_capacity(file_size as usize);
            try!((&mut infile).take(file_size as u64).read_to_end(&mut data));

            self.0.insert(path, data.into_boxed_slice());
        }
        Ok(())
    }

    /// Opens a file in the file tree for reading.
    ///
    /// # Examples
    /// ```
    /// use pak::Pak;
    ///
    /// let pak0 = Pak::load("pak0.pak");
    /// let progs_dat = pak0.open("progs.dat");
    /// ```
    pub fn open(&self, path: &str) -> Option<&[u8]> {
        match self.0.get(path) {
            Some(data) => Some(&data),
            None => None,
        }
    }

    pub fn iter<'a>(&self) -> Iter<String, Box<[u8]>> {
        self.0.iter()
    }
}
<commit_msg>Fix doctests in pak.rs<commit_after>// Copyright © 2017 Cormac O'Brien
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software
// and associated documentation files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
// BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

//! Quake PAK archive manipulation.

extern crate byteorder;

use std::collections::hash_map::Iter;
use std::collections::HashMap;
use std::error::Error;
use std::fmt;
use std::fs;
use std::io;
use std::io::{Read, Seek, SeekFrom};
use std::path::Path;
use std::string;
use byteorder::{LittleEndian, ReadBytesExt};

const PAK_MAGIC: [u8; 4] = [b'P', b'A', b'C', b'K'];
const PAK_ENTRY_SIZE: usize = 64;

#[derive(Debug)]
pub enum PakError {
    Io(io::Error),
    Utf8(string::FromUtf8Error),
    Invalid,
    Other,
}

impl fmt::Display for PakError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.description())
    }
}

impl Error for PakError {
    fn description(&self) -> &str {
        match *self {
            PakError::Io(ref e) => e.description(),
            PakError::Utf8(ref e) => e.description(),
            PakError::Invalid => "Not a valid PAK file",
            PakError::Other => "Unknown error",
        }
    }

    fn cause(&self) -> Option<&Error> {
        match *self {
            PakError::Io(ref i) => Some(i),
            PakError::Utf8(ref i) => Some(i),
            PakError::Invalid => None,
            PakError::Other => None,
        }
    }
}

impl From<io::Error> for PakError {
    fn from(err: io::Error) -> PakError {
        PakError::Io(err)
    }
}

impl From<string::FromUtf8Error> for PakError {
    fn from(err: string::FromUtf8Error) -> PakError {
        PakError::Utf8(err)
    }
}

/// A virtual file tree loaded from a PAK archive.
pub struct Pak(HashMap<String, Box<[u8]>>);

impl Pak {
    /// Constructs a new `Pak` object.
    ///
    /// The virtual file tree is initially empty.
    ///
    /// # Examples
    /// ```
    /// # extern crate richter;
    /// use richter::pak::Pak;
    ///
    /// # fn main() {
    /// let pak = Pak::new();
    /// # }
    /// ```
    pub fn new() -> Pak {
        Pak(HashMap::new())
    }

    /// Adds the data from the specified PAK archive to the virtual file tree.
    ///
    /// # Examples
    /// ```no_run
    /// # extern crate richter;
    /// use richter::pak::Pak;
    ///
    /// # fn main() {
    /// let mut pak = Pak::new();
    /// pak.add("pak0.pak").unwrap();
    /// # }
    /// ```
    pub fn add<P: AsRef<Path>>(&mut self, path: P) -> Result<(), PakError> {
        debug!("Opening {}", path.as_ref().to_str().unwrap());

        let mut infile = try!(fs::File::open(path));

        let mut magic = [0u8; 4];
        try!(infile.read(&mut magic));

        if magic != PAK_MAGIC {
            return Err(PakError::Invalid);
        }

        // Locate the file table

        let wad_offset = match try!(infile.read_i32::<LittleEndian>()) {
            o if o <= 0 => return Err(PakError::Invalid),
            o => o as u32,
        };

        let wad_size = match try!(infile.read_i32::<LittleEndian>()) {
            s if s <= 0 => return Err(PakError::Invalid),
            s => s as u32,
        };

        for i in 0..(wad_size as usize / PAK_ENTRY_SIZE) {
            let entry_offset = wad_offset as u64 + (i * PAK_ENTRY_SIZE) as u64;
            try!(infile.seek(SeekFrom::Start(entry_offset)));

            let mut path_bytes = [0u8; 56];
            try!(infile.read(&mut path_bytes));

            let file_offset = match try!(infile.read_i32::<LittleEndian>()) {
                o if o <= 0 => return Err(PakError::Invalid),
                o => o as u32,
            };

            let file_size = match try!(infile.read_i32::<LittleEndian>()) {
                s if s <= 0 => return Err(PakError::Invalid),
                s => s as u32,
            };

            let last = {
                let mut _last: usize = 0;
                while path_bytes[_last] != 0 {
                    _last += 1;
                }
                _last
            };

            let path = try!(String::from_utf8(path_bytes[0..last].to_vec()));

            try!(infile.seek(SeekFrom::Start(file_offset as u64)));

            let mut data: Vec<u8> = Vec::with_capacity(file_size as usize);
            try!((&mut infile).take(file_size as u64).read_to_end(&mut data));

            self.0.insert(path, data.into_boxed_slice());
        }
        Ok(())
    }

    /// Opens a file in the file tree for reading.
    ///
    /// # Examples
    /// ```no_run
    /// # extern crate richter;
    /// use richter::pak::Pak;
    ///
    /// # fn main() {
    /// let mut pak = Pak::new();
    /// pak.add("pak0.pak").unwrap();
    /// let progs_dat = pak.open("progs.dat").unwrap();
    /// # }
    /// ```
    pub fn open(&self, path: &str) -> Option<&[u8]> {
        match self.0.get(path) {
            Some(data) => Some(&data),
            None => None,
        }
    }

    pub fn iter<'a>(&self) -> Iter<String, Box<[u8]>> {
        self.0.iter()
    }
}
<|endoftext|>


<commit_before>mod field;
mod generator;

pub use self::field::*;
pub use self::generator::*;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Clue {
    NoClue,
    Empty,
    Up(i32),
    Left(i32),
    Down(i32),
    Right(i32),
}

impl Clue {
    pub fn same_shape(self, other: Clue) -> bool {
        match (self, other) {
            (Clue::Up(_), Clue::Up(_))
            | (Clue::Left(_), Clue::Left(_))
            | (Clue::Down(_), Clue::Down(_))
            | (Clue::Right(_), Clue::Right(_)) => true,
            _ => false,
        }
    }
    pub fn clue_number(self) -> i32 {
        match self {
            Clue::NoClue | Clue::Empty => -1,
            Clue::Up(n) | Clue::Left(n) | Clue::Down(n) | Clue::Right(n) => n,
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Cell {
    Clue,
    Undecided,
    Line,
    Blocked,
}

impl Cell {
    pub fn is_blocking(self) -> bool {
        match self {
            Cell::Clue | Cell::Blocked => true,
            _ => false,
        }
    }
}

#[derive(Clone, Copy)]
pub struct Technique {
    two_by_two: bool,
    two_by_three: bool,
    one_in_three_orthogonal_either: bool,
    one_in_three_remote: bool,
    inout_advanced: bool,
    local_parity: bool,
}

impl Technique {
    pub fn new() -> Technique {
        Technique {
            two_by_two: true,
            two_by_three: true,
            one_in_three_orthogonal_either: true,
            one_in_three_remote: true,
            inout_advanced: true,
            local_parity: true,
        }
    }
}
<commit_msg>Yajilin: make fields of Technique pub<commit_after>mod field;
mod generator;

pub use self::field::*;
pub use self::generator::*;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Clue {
    NoClue,
    Empty,
    Up(i32),
    Left(i32),
    Down(i32),
    Right(i32),
}

impl Clue {
    pub fn same_shape(self, other: Clue) -> bool {
        match (self, other) {
            (Clue::Up(_), Clue::Up(_))
            | (Clue::Left(_), Clue::Left(_))
            | (Clue::Down(_), Clue::Down(_))
            | (Clue::Right(_), Clue::Right(_)) => true,
            _ => false,
        }
    }
    pub fn clue_number(self) -> i32 {
        match self {
            Clue::NoClue | Clue::Empty => -1,
            Clue::Up(n) | Clue::Left(n) | Clue::Down(n) | Clue::Right(n) => n,
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Cell {
    Clue,
    Undecided,
    Line,
    Blocked,
}

impl Cell {
    pub fn is_blocking(self) -> bool {
        match self {
            Cell::Clue | Cell::Blocked => true,
            _ => false,
        }
    }
}

#[derive(Clone, Copy)]
pub struct Technique {
    pub two_by_two: bool,
    pub two_by_three: bool,
    pub one_in_three_orthogonal_either: bool,
    pub one_in_three_remote: bool,
    pub inout_advanced: bool,
    pub local_parity: bool,
}

impl Technique {
    pub fn new() -> Technique {
        Technique {
            two_by_two: true,
            two_by_three: true,
            one_in_three_orthogonal_either: true,
            one_in_three_remote: true,
            inout_advanced: true,
            local_parity: true,
        }
    }
}
<|endoftext|>


<commit_before>extern crate prodbg_ui;

use std::mem::transmute;
use std::os::raw::{c_void};

//use prodbg_ui::*;
//

struct MyPlugin {
    _dummy: i32,
}

impl MyPlugin {
    fn new() -> Self {
        MyPlugin {
            _dummy: 0,
        }
    }

    fn pressed_button(&mut self) {
        println!("Pressed button");
    }

    fn create_ui(&mut self, ui: &prodbg_ui::ui_gen::Ui) {
        let button = ui.push_button_create();
        button.set_title("Rust: Test");

        extern "C" fn temp_call(target: *mut std::os::raw::c_void) {
            unsafe {
                let app = target as *mut MyPlugin;
                MyPlugin::pressed_button(&mut *app);
            }
        }

        unsafe {
            let object = (*(*button.get_obj()).base).object;
            prodbg_ui::connect(object, b"2released()\0", &self, temp_call);
        }

        //button.on_pressed(self, MyPlugin::pressed_button);
    }
}

macro_rules! connect_released {
    ($sender:expr, $data:expr, $call_type:ident, $callback:path) => {
        {
            extern "C" fn temp_call(target: *mut std::os::raw::c_void) {
                unsafe {
                    let app = target as *mut $call_type;
                    $callback(&mut *app);
                }
            }

            unsafe {
                let object = (*(*$sender.get_obj()).base).object;
                wrui::connect(object, b"2released()\0", $data, temp_call);
            }
        }
    }
}

#[no_mangle]
pub fn init_plugin(ui_ptr: *const prodbg_ui::ffi_gen::PU) {
    // This stuff here is really hidden from the plugin
    // but we do this temporary when testing out the apis
    let ui = prodbg_ui::ui_gen::Ui::new(ui_ptr);

    // This is some tricker to (later on) send back a instance of the plugin to C and then on the
    // Rust side again cast it back to a Rust instance

    let instance: *mut c_void  = unsafe { transmute(Box::new(MyPlugin::new())) };
    let plugin: &mut MyPlugin = unsafe { &mut *(instance as *mut MyPlugin) };

    plugin.create_ui(&ui);

    println!("Rust: memory_view_2");
}
<commit_msg>Working helper macro for creating button<commit_after>extern crate prodbg_ui;

use std::mem::transmute;
use std::os::raw::{c_void};

//use prodbg_ui::*;
//

struct MyPlugin {
    _dummy: i32,
}

macro_rules! create_button {
    ($ui:tt, $data:tt, $data_type:ty, $($rest:tt)*) => {{
        let button = $ui.push_button_create();
        create_button_inner!(button, $data, $data_type, $($rest)*);
        button
    }}
}

macro_rules! create_button_inner {
    ($w:expr, ) => {};
    ($w:expr, $data:tt, ) => {};
    ($w:expr, $data:tt, $data_type:ty, ) => {};

    ($w:expr, $data:tt, $data_type:ty, title: $value:expr, $($rest:tt)*) => {{
        $w.set_title($value);
        create_button_inner!($w, $data, $data_type, $($rest)*);
    }};

    ($w:expr, $data:tt, $data_type:ty, on_released: $callback:path, $($rest:tt)*) => {{
        extern "C" fn temp_call(target: *mut std::os::raw::c_void) {
            unsafe {
                let app = target as *mut $data_type;
                $callback(&mut *app);
            }
        }

        unsafe {
            let object = (*(*$w.get_obj()).base).object;
            prodbg_ui::connect(object, b"2released()\0", $data, temp_call);
        }

        create_button_inner!($w, $data, $data_type, $($rest)*);
    }};
}

impl MyPlugin {
    fn new() -> Self {
        MyPlugin {
            _dummy: 0,
        }
    }

    fn pressed_button(&mut self) {
        println!("Pressed button");
    }

    fn create_ui(&mut self, ui: &prodbg_ui::ui_gen::Ui) {
        let _button = create_button! {
            ui, self, MyPlugin,
            title: "foo",
            on_released: MyPlugin::pressed_button,
        };
    }

        /*
        let button = ui.push_button_create();
        button.set_title("Rust: Test");

        extern "C" fn temp_call(target: *mut std::os::raw::c_void) {
            unsafe {
                let app = target as *mut MyPlugin;
                MyPlugin::pressed_button(&mut *app);
            }
        }

        unsafe {
            let object = (*(*button.get_obj()).base).object;
            prodbg_ui::connect(object, b"2released()\0", &self, temp_call);
        }

        //button.on_pressed(self, MyPlugin::pressed_button);
    }
    */
}

macro_rules! connect_released {
    ($sender:expr, $data:expr, $call_type:ident, $callback:path) => {
        {
            extern "C" fn temp_call(target: *mut std::os::raw::c_void) {
                unsafe {
                    let app = target as *mut $call_type;
                    $callback(&mut *app);
                }
            }

            unsafe {
                let object = (*(*$sender.get_obj()).base).object;
                wrui::connect(object, b"2released()\0", $data, temp_call);
            }
        }
    }
}

#[no_mangle]
pub fn init_plugin(ui_ptr: *const prodbg_ui::ffi_gen::PU) {
    // This stuff here is really hidden from the plugin
    // but we do this temporary when testing out the apis
    let ui = prodbg_ui::ui_gen::Ui::new(ui_ptr);

    // This is some tricker to (later on) send back a instance of the plugin to C and then on the
    // Rust side again cast it back to a Rust instance

    let instance: *mut c_void  = unsafe { transmute(Box::new(MyPlugin::new())) };
    let plugin: &mut MyPlugin = unsafe { &mut *(instance as *mut MyPlugin) };

    plugin.create_ui(&ui);

    println!("Rust: memory_view_2");
}
<|endoftext|>


<commit_before>use value::Value;
use block::BasicBlock;
use ffi::core;

/// A PHI node represents a value which is selected based on the predecessor of the current block.
pub type PhiNode = Value;

impl PhiNode {
    /// Adds an incoming value to the end of this PHI node.
    pub fn add_incoming(&self, val: &Value, block: &BasicBlock) {
        let mut values = vec![val.into()];
        let mut blocks = vec![block.into()];
        unsafe { core::LLVMAddIncoming(self.into(), values.as_mut_ptr(), blocks.as_mut_ptr(), 1) }.into()
    }

    /// Counts the number of incoming values attached to this PHI node.
    pub fn count_incoming(&self) -> u32 {
        unsafe { core::LLVMCountIncoming(self.into()) }
    }

    /// Gets an incoming value from this PHI node from a specific index.
    pub fn get_incoming_value(&self, index: u32) -> &Value {
        unsafe { core::LLVMGetIncomingValue(self.into(), index) }.into()
    }

    /// Gets an incoming basic block from this PHI node from a specific index.
    pub fn get_incoming_block(&self, index: u32) -> &BasicBlock {
        unsafe { core::LLVMGetIncomingBlock(self.into(), index) }.into()
    }
}
<commit_msg>Make PHI nodes more type safe.<commit_after>use value::Value;
use block::BasicBlock;
use ffi::prelude::LLVMValueRef;
use ffi::core;
use util::CastFrom;
use std::ops::Deref;
use std::{mem, ptr};

/// A PHI node represents a value which is selected based on the predecessor of the current block.
pub struct PhiNode;

native_ref!(&PhiNode = LLVMValueRef);
impl PhiNode {
    /// Adds an incoming value to the end of this PHI node.
    pub fn add_incoming(&self, val: &Value, block: &BasicBlock) {
        let mut values = vec![val.into()];
        let mut blocks = vec![block.into()];
        unsafe { core::LLVMAddIncoming(self.into(), values.as_mut_ptr(), blocks.as_mut_ptr(), 1) }.into()
    }

    /// Counts the number of incoming values attached to this PHI node.
    pub fn count_incoming(&self) -> u32 {
        unsafe { core::LLVMCountIncoming(self.into()) }
    }

    /// Gets an incoming value from this PHI node from a specific index.
    pub fn get_incoming_value(&self, index: u32) -> &Value {
        unsafe { core::LLVMGetIncomingValue(self.into(), index) }.into()
    }

    /// Gets an incoming basic block from this PHI node from a specific index.
    pub fn get_incoming_block(&self, index: u32) -> &BasicBlock {
        unsafe { core::LLVMGetIncomingBlock(self.into(), index) }.into()
    }
}
impl CastFrom for PhiNode {
    type From = Value;
    fn cast(value: &Value) -> Option<&PhiNode> {
        unsafe {
            let node = core::LLVMIsAPHINode(value.into());
            if node == ptr::null_mut() {
                None
            } else {
                Some(node.into())
            }
        }
    }
}
deref!(PhiNode, Value);
<|endoftext|>


<commit_before>//! Write las points.
//!
//! A `Writer` uses a `Header` for its configuration:
//!
//! ```
//! use std::io::Cursor;
//! use las::{Writer, Header};
//! let mut header = Header::from((1, 4));
//! let writer = Writer::new(Cursor::new(Vec::new()), header).unwrap();
//! ```
//!
//! The set of optional fields on the point format and the points must match exactly:
//!
//! ```
//! use std::io::Cursor;
//! use las::{Builder, Writer, Point};
//! use las::point::Format;
//! use las::Color;
//!
//! let mut builder = Builder::default();
//! builder.point_format = Format::new(1).unwrap();
//! let mut writer = Writer::new(Cursor::new(Vec::new()), builder.into_header().unwrap()).unwrap();
//!
//! let mut point = Point::default(); // default points don't have any optional attributes
//! assert!(writer.write(point.clone()).is_err());
//!
//! point.gps_time = Some(42.); // point format 1 requires gps time
//! writer.write(point.clone()).unwrap();
//!
//! point.color = Some(Color::new(1, 2, 3));
//! assert!(writer.write(point).is_err()); // the point's color would be lost
//! ```

use {Header, Point, Result};
use point::Format;
use std::fs::File;
use std::io::{BufWriter, Cursor, Seek, SeekFrom, Write};
use std::path::Path;

quick_error! {
    /// Writer errors.
    #[derive(Debug)]
    pub enum Error {
        /// The writer is closed.
        Closed {
            description("the writer is closed")
        }
        /// The attributes of the point format and point do not match.
        PointAttributes(format: Format, point: Point) {
            description("the attributes of the point format and point do not match")
            display("the attributes of point format {:?} does not match point {:?}", format, point)
        }
    }
}

/// Writes LAS data.
///
/// The LAS header needs to be re-written when the writer closes. For convenience, this is done via
/// the `Drop` implementation of the writer. One consequence is that if the header re-write fails
/// during the drop, a panic will result. If you want to check for errors instead of panicing, use
/// `close` explicitly.
///
/// ```
/// use std::io::Cursor;
/// use las::Writer;
/// {
///     let mut writer = Writer::default();
///     writer.close().unwrap();
/// } // <- `close` is not called
/// ```
#[derive(Debug)]
pub struct Writer<W: Seek + Write> {
    closed: bool,
    header: Header,
    start: u64,
    write: W,
}

impl<W: Seek + Write> Writer<W> {
    /// Creates a new writer.
    ///
    /// The header that is passed in will have various fields zero'd, e.g. bounds, number of
    /// points, etc.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::io::Cursor;
    /// use las::Writer;
    /// let writer = Writer::new(Cursor::new(Vec::new()), Default::default());
    /// ```
    pub fn new(mut write: W, mut header: Header) -> Result<Writer<W>> {
        let start = write.seek(SeekFrom::Current(0))?;
        header.clear();
        header.clone().into_raw().and_then(|raw_header| {
            raw_header.write_to(&mut write)
        })?;
        for vlr in header.vlrs() {
            (*vlr).clone().into_raw(false).and_then(|raw_vlr| {
                raw_vlr.write_to(&mut write)
            })?;
        }
        if !header.vlr_padding().is_empty() {
            write.write_all(&header.vlr_padding())?;
        }
        Ok(Writer {
            closed: false,
            header: header,
            start: start,
            write: write,
        })
    }

    /// Writes a point.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::io::Cursor;
    /// use las::Writer;
    ///
    /// let mut writer = Writer::default();
    /// writer.write(Default::default()).unwrap();
    /// ```
    pub fn write(&mut self, point: Point) -> Result<()> {
        if self.closed {
            return Err(Error::Closed.into());
        }
        if !point.matches(self.header.point_format()) {
            return Err(
                Error::PointAttributes(self.header.point_format(), point).into(),
            );
        }
        self.header.add_point(&point);
        point.into_raw(self.header.transforms()).and_then(
            |raw_point| {
                raw_point.write_to(&mut self.write, self.header.point_format())
            },
        )?;
        Ok(())
    }

    /// Close this writer.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::io::Cursor;
    /// use las::Writer;
    /// let mut writer = Writer::default();
    /// writer.close().unwrap();
    /// assert!(writer.close().is_err());
    /// ```
    pub fn close(&mut self) -> Result<()> {
        if self.closed {
            return Err(Error::Closed.into());
        }
        self.write.write_all(self.header.point_padding())?;
        for raw_evlr in self.header.evlrs().into_iter().map(|evlr| {
            evlr.clone().into_raw(true)
        })
        {
            raw_evlr?.write_to(&mut self.write)?;
        }
        self.write.seek(SeekFrom::Start(self.start))?;
        self.header.clone().into_raw().and_then(|raw_header| {
            raw_header.write_to(&mut self.write)
        })?;
        self.write.seek(SeekFrom::Start(self.start))?;
        self.closed = true;
        Ok(())
    }
}

impl<W: Write + Seek + Clone> Writer<W> {
    /// Closes this writer and returns its inner `Write`, seeked to the beginning of the las data.
    ///
    /// # Examples
    ///
    /// ```
    /// use las::Writer;
    /// let writer = Writer::default();
    /// let cursor = writer.into_inner().unwrap();
    /// ```
    pub fn into_inner(mut self) -> Result<W> {
        if !self.closed {
            self.close()?;
        }
        self.write.seek(SeekFrom::Start(self.start))?;
        Ok(self.write.clone())
    }
}

impl Writer<BufWriter<File>> {
    /// Creates a new writer for a path.
    ///
    /// # Examples
    ///
    /// ```
    /// use las::Writer;
    /// let writer = Writer::from_path("/dev/null", Default::default());
    /// ```
    pub fn from_path<P: AsRef<Path>>(path: P, header: Header) -> Result<Writer<BufWriter<File>>> {
        File::create(path).map_err(::Error::from).and_then(|file| {
            Writer::new(BufWriter::new(file), header)
        })
    }
}

impl Default for Writer<Cursor<Vec<u8>>> {
    fn default() -> Writer<Cursor<Vec<u8>>> {
        Writer::new(Cursor::new(Vec::new()), Header::default()).unwrap()
    }
}

impl<W: Seek + Write> Drop for Writer<W> {
    fn drop(&mut self) {
        if !self.closed {
            self.close().expect("Error when dropping the writer");
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use Version;
    use byteorder::{LittleEndian, ReadBytesExt};
    use header::Builder;
    use point::Format;
    use std::io::Cursor;

    fn writer(format: Format, version: Version) -> Writer<Cursor<Vec<u8>>> {
        let mut builder = Builder::default();
        builder.point_format = format;
        builder.version = version;
        Writer::new(Cursor::new(Vec::new()), builder.into_header().unwrap()).unwrap()
    }

    #[test]
    fn las_1_0_point_data_start_signature() {
        let mut builder = Builder::default();
        builder.version = (1, 0).into();
        builder.vlrs.push(Default::default());
        let mut writer = Writer::new(Cursor::new(Vec::new()), builder.into_header().unwrap())
            .unwrap();
        writer.write(Default::default()).unwrap();
        let mut cursor = writer.into_inner().unwrap();
        cursor.set_position(281);
        assert_eq!(0xCCDD, cursor.read_u16::<LittleEndian>().unwrap());
    }

    #[test]
    fn already_closed() {
        let mut writer = Writer::default();
        writer.close().unwrap();
        assert!(writer.close().is_err());
        assert!(writer.write(Default::default()).is_err());
    }

    #[test]
    fn missing_extra_bytes() {
        let format = Format {
            extra_bytes: 1,
            ..Default::default()
        };
        let mut writer = writer(format, Version::new(1, 4));
        assert!(writer.write(Default::default()).is_err());
    }

    #[test]
    fn missing_gps_time() {
        let format = Format::new(1).unwrap();
        let mut writer = writer(format, Version::new(1, 2));
        assert!(writer.write(Default::default()).is_err());
    }

    #[test]
    fn missing_color() {
        let format = Format::new(2).unwrap();
        let mut writer = writer(format, Version::new(1, 2));
        assert!(writer.write(Default::default()).is_err());
    }

    #[test]
    fn missing_nir() {
        let format = Format::new(8).unwrap();
        let mut writer = writer(format, Version::new(1, 4));
        let point = Point {
            gps_time: Some(0.),
            color: Some(Default::default()),
            ..Default::default()
        };
        assert!(writer.write(point).is_err());
    }

    #[test]
    fn missing_waveform() {
        let format = Format::new(4).unwrap();
        let mut writer = writer(format, Version::new(1, 4));
        assert!(writer.write(Default::default()).is_err());
    }

    #[test]
    fn write_not_at_start() {
        use Reader;
        use byteorder::WriteBytesExt;

        let mut cursor = Cursor::new(Vec::new());
        cursor.write_u8(42).unwrap();
        let mut writer = Writer::new(cursor, Default::default()).unwrap();
        let point = Point::default();
        writer.write(point.clone()).unwrap();
        let mut reader = Reader::new(writer.into_inner().unwrap()).unwrap();
        assert_eq!(point, reader.read().unwrap().unwrap());
    }
}
<commit_msg>Add Writer::header<commit_after>//! Write las points.
//!
//! A `Writer` uses a `Header` for its configuration:
//!
//! ```
//! use std::io::Cursor;
//! use las::{Writer, Header};
//! let mut header = Header::from((1, 4));
//! let writer = Writer::new(Cursor::new(Vec::new()), header).unwrap();
//! ```
//!
//! The set of optional fields on the point format and the points must match exactly:
//!
//! ```
//! use std::io::Cursor;
//! use las::{Builder, Writer, Point};
//! use las::point::Format;
//! use las::Color;
//!
//! let mut builder = Builder::default();
//! builder.point_format = Format::new(1).unwrap();
//! let mut writer = Writer::new(Cursor::new(Vec::new()), builder.into_header().unwrap()).unwrap();
//!
//! let mut point = Point::default(); // default points don't have any optional attributes
//! assert!(writer.write(point.clone()).is_err());
//!
//! point.gps_time = Some(42.); // point format 1 requires gps time
//! writer.write(point.clone()).unwrap();
//!
//! point.color = Some(Color::new(1, 2, 3));
//! assert!(writer.write(point).is_err()); // the point's color would be lost
//! ```

use {Header, Point, Result};
use point::Format;
use std::fs::File;
use std::io::{BufWriter, Cursor, Seek, SeekFrom, Write};
use std::path::Path;

quick_error! {
    /// Writer errors.
    #[derive(Debug)]
    pub enum Error {
        /// The writer is closed.
        Closed {
            description("the writer is closed")
        }
        /// The attributes of the point format and point do not match.
        PointAttributes(format: Format, point: Point) {
            description("the attributes of the point format and point do not match")
            display("the attributes of point format {:?} does not match point {:?}", format, point)
        }
    }
}

/// Writes LAS data.
///
/// The LAS header needs to be re-written when the writer closes. For convenience, this is done via
/// the `Drop` implementation of the writer. One consequence is that if the header re-write fails
/// during the drop, a panic will result. If you want to check for errors instead of panicing, use
/// `close` explicitly.
///
/// ```
/// use std::io::Cursor;
/// use las::Writer;
/// {
///     let mut writer = Writer::default();
///     writer.close().unwrap();
/// } // <- `close` is not called
/// ```
#[derive(Debug)]
pub struct Writer<W: Seek + Write> {
    closed: bool,
    header: Header,
    start: u64,
    write: W,
}

impl<W: Seek + Write> Writer<W> {
    /// Creates a new writer.
    ///
    /// The header that is passed in will have various fields zero'd, e.g. bounds, number of
    /// points, etc.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::io::Cursor;
    /// use las::Writer;
    /// let writer = Writer::new(Cursor::new(Vec::new()), Default::default());
    /// ```
    pub fn new(mut write: W, mut header: Header) -> Result<Writer<W>> {
        let start = write.seek(SeekFrom::Current(0))?;
        header.clear();
        header.clone().into_raw().and_then(|raw_header| {
            raw_header.write_to(&mut write)
        })?;
        for vlr in header.vlrs() {
            (*vlr).clone().into_raw(false).and_then(|raw_vlr| {
                raw_vlr.write_to(&mut write)
            })?;
        }
        if !header.vlr_padding().is_empty() {
            write.write_all(&header.vlr_padding())?;
        }
        Ok(Writer {
            closed: false,
            header: header,
            start: start,
            write: write,
        })
    }

    /// Returns a reference to this writer's header.
    ///
    /// # Examples
    ///
    /// ```
    /// use las::Writer;
    /// let writer = Writer::default();
    /// let header = writer.header();
    /// ```
    pub fn header(&self) -> &Header {
        &self.header
    }

    /// Writes a point.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::io::Cursor;
    /// use las::Writer;
    ///
    /// let mut writer = Writer::default();
    /// writer.write(Default::default()).unwrap();
    /// ```
    pub fn write(&mut self, point: Point) -> Result<()> {
        if self.closed {
            return Err(Error::Closed.into());
        }
        if !point.matches(self.header.point_format()) {
            return Err(
                Error::PointAttributes(self.header.point_format(), point).into(),
            );
        }
        self.header.add_point(&point);
        point.into_raw(self.header.transforms()).and_then(
            |raw_point| {
                raw_point.write_to(&mut self.write, self.header.point_format())
            },
        )?;
        Ok(())
    }

    /// Close this writer.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::io::Cursor;
    /// use las::Writer;
    /// let mut writer = Writer::default();
    /// writer.close().unwrap();
    /// assert!(writer.close().is_err());
    /// ```
    pub fn close(&mut self) -> Result<()> {
        if self.closed {
            return Err(Error::Closed.into());
        }
        self.write.write_all(self.header.point_padding())?;
        for raw_evlr in self.header.evlrs().into_iter().map(|evlr| {
            evlr.clone().into_raw(true)
        })
        {
            raw_evlr?.write_to(&mut self.write)?;
        }
        self.write.seek(SeekFrom::Start(self.start))?;
        self.header.clone().into_raw().and_then(|raw_header| {
            raw_header.write_to(&mut self.write)
        })?;
        self.write.seek(SeekFrom::Start(self.start))?;
        self.closed = true;
        Ok(())
    }
}

impl<W: Write + Seek + Clone> Writer<W> {
    /// Closes this writer and returns its inner `Write`, seeked to the beginning of the las data.
    ///
    /// # Examples
    ///
    /// ```
    /// use las::Writer;
    /// let writer = Writer::default();
    /// let cursor = writer.into_inner().unwrap();
    /// ```
    pub fn into_inner(mut self) -> Result<W> {
        if !self.closed {
            self.close()?;
        }
        self.write.seek(SeekFrom::Start(self.start))?;
        Ok(self.write.clone())
    }
}

impl Writer<BufWriter<File>> {
    /// Creates a new writer for a path.
    ///
    /// # Examples
    ///
    /// ```
    /// use las::Writer;
    /// let writer = Writer::from_path("/dev/null", Default::default());
    /// ```
    pub fn from_path<P: AsRef<Path>>(path: P, header: Header) -> Result<Writer<BufWriter<File>>> {
        File::create(path).map_err(::Error::from).and_then(|file| {
            Writer::new(BufWriter::new(file), header)
        })
    }
}

impl Default for Writer<Cursor<Vec<u8>>> {
    fn default() -> Writer<Cursor<Vec<u8>>> {
        Writer::new(Cursor::new(Vec::new()), Header::default()).unwrap()
    }
}

impl<W: Seek + Write> Drop for Writer<W> {
    fn drop(&mut self) {
        if !self.closed {
            self.close().expect("Error when dropping the writer");
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use Version;
    use byteorder::{LittleEndian, ReadBytesExt};
    use header::Builder;
    use point::Format;
    use std::io::Cursor;

    fn writer(format: Format, version: Version) -> Writer<Cursor<Vec<u8>>> {
        let mut builder = Builder::default();
        builder.point_format = format;
        builder.version = version;
        Writer::new(Cursor::new(Vec::new()), builder.into_header().unwrap()).unwrap()
    }

    #[test]
    fn las_1_0_point_data_start_signature() {
        let mut builder = Builder::default();
        builder.version = (1, 0).into();
        builder.vlrs.push(Default::default());
        let mut writer = Writer::new(Cursor::new(Vec::new()), builder.into_header().unwrap())
            .unwrap();
        writer.write(Default::default()).unwrap();
        let mut cursor = writer.into_inner().unwrap();
        cursor.set_position(281);
        assert_eq!(0xCCDD, cursor.read_u16::<LittleEndian>().unwrap());
    }

    #[test]
    fn already_closed() {
        let mut writer = Writer::default();
        writer.close().unwrap();
        assert!(writer.close().is_err());
        assert!(writer.write(Default::default()).is_err());
    }

    #[test]
    fn missing_extra_bytes() {
        let format = Format {
            extra_bytes: 1,
            ..Default::default()
        };
        let mut writer = writer(format, Version::new(1, 4));
        assert!(writer.write(Default::default()).is_err());
    }

    #[test]
    fn missing_gps_time() {
        let format = Format::new(1).unwrap();
        let mut writer = writer(format, Version::new(1, 2));
        assert!(writer.write(Default::default()).is_err());
    }

    #[test]
    fn missing_color() {
        let format = Format::new(2).unwrap();
        let mut writer = writer(format, Version::new(1, 2));
        assert!(writer.write(Default::default()).is_err());
    }

    #[test]
    fn missing_nir() {
        let format = Format::new(8).unwrap();
        let mut writer = writer(format, Version::new(1, 4));
        let point = Point {
            gps_time: Some(0.),
            color: Some(Default::default()),
            ..Default::default()
        };
        assert!(writer.write(point).is_err());
    }

    #[test]
    fn missing_waveform() {
        let format = Format::new(4).unwrap();
        let mut writer = writer(format, Version::new(1, 4));
        assert!(writer.write(Default::default()).is_err());
    }

    #[test]
    fn write_not_at_start() {
        use Reader;
        use byteorder::WriteBytesExt;

        let mut cursor = Cursor::new(Vec::new());
        cursor.write_u8(42).unwrap();
        let mut writer = Writer::new(cursor, Default::default()).unwrap();
        let point = Point::default();
        writer.write(point.clone()).unwrap();
        let mut reader = Reader::new(writer.into_inner().unwrap()).unwrap();
        assert_eq!(point, reader.read().unwrap().unwrap());
    }
}
<|endoftext|>


<commit_before>#![allow(unused_imports)]

use core::intrinsics;

// NOTE These functions are implemented using assembly because they using a custom
// calling convention which can't be implemented using a normal Rust function

// NOTE These functions are never mangled as they are not tested against compiler-rt
// and mangling ___chkstk would break the `jmp ___chkstk` instruction in __alloca

#[cfg(windows)]
#[naked]
#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
pub unsafe fn ___chkstk_ms() {
    asm!("push   %rcx
          push   %rax
          cmp    $$0x1000,%rax
          lea    24(%rsp),%rcx
          jb     1f
          2:
          sub    $$0x1000,%rcx
          test   %rcx,(%rcx)
          sub    $$0x1000,%rax
          cmp    $$0x1000,%rax
          ja     2b
          1:
          sub    %rax,%rcx
          test   %rcx,(%rcx)
          pop    %rax
          pop    %rcx
          ret");
    intrinsics::unreachable();
}

#[cfg(windows)]
#[naked]
#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
pub unsafe fn __alloca() {
    asm!("mov    %rcx,%rax  // x64 _alloca is a normal function with parameter in rcx
          jmp    ___chkstk  // Jump to ___chkstk since fallthrough may be unreliable");
    intrinsics::unreachable();
}

#[cfg(windows)]
#[naked]
#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
pub unsafe fn ___chkstk() {
    asm!("push   %rcx
          cmp    $$0x1000,%rax
          lea    16(%rsp),%rcx  // rsp before calling this routine -> rcx
          jb     1f
          2:
          sub    $$0x1000,%rcx
          test   %rcx,(%rcx)
          sub    $$0x1000,%rax
          cmp    $$0x1000,%rax
          ja     2b
          1:
          sub    %rax,%rcx
          test   %rcx,(%rcx)

          lea    8(%rsp),%rax   // load pointer to the return address into rax
          mov    %rcx,%rsp      // install the new top of stack pointer into rsp
          mov    -8(%rax),%rcx  // restore rcx
          push   (%rax)         // push return address onto the stack
          sub    %rsp,%rax      // restore the original value in rax
          ret");
    intrinsics::unreachable();
}
<commit_msg>Don't include custom chkstk on MSVC<commit_after>#![allow(unused_imports)]

use core::intrinsics;

// NOTE These functions are implemented using assembly because they using a custom
// calling convention which can't be implemented using a normal Rust function

// NOTE These functions are never mangled as they are not tested against compiler-rt
// and mangling ___chkstk would break the `jmp ___chkstk` instruction in __alloca

#[cfg(all(windows, target_env = "gnu"))]
#[naked]
#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
pub unsafe fn ___chkstk_ms() {
    asm!("push   %rcx
          push   %rax
          cmp    $$0x1000,%rax
          lea    24(%rsp),%rcx
          jb     1f
          2:
          sub    $$0x1000,%rcx
          test   %rcx,(%rcx)
          sub    $$0x1000,%rax
          cmp    $$0x1000,%rax
          ja     2b
          1:
          sub    %rax,%rcx
          test   %rcx,(%rcx)
          pop    %rax
          pop    %rcx
          ret");
    intrinsics::unreachable();
}

#[cfg(all(windows, target_env = "gnu"))]
#[naked]
#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
pub unsafe fn __alloca() {
    asm!("mov    %rcx,%rax  // x64 _alloca is a normal function with parameter in rcx
          jmp    ___chkstk  // Jump to ___chkstk since fallthrough may be unreliable");
    intrinsics::unreachable();
}

#[cfg(all(windows, target_env = "gnu"))]
#[naked]
#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
pub unsafe fn ___chkstk() {
    asm!("push   %rcx
          cmp    $$0x1000,%rax
          lea    16(%rsp),%rcx  // rsp before calling this routine -> rcx
          jb     1f
          2:
          sub    $$0x1000,%rcx
          test   %rcx,(%rcx)
          sub    $$0x1000,%rax
          cmp    $$0x1000,%rax
          ja     2b
          1:
          sub    %rax,%rcx
          test   %rcx,(%rcx)

          lea    8(%rsp),%rax   // load pointer to the return address into rax
          mov    %rcx,%rsp      // install the new top of stack pointer into rsp
          mov    -8(%rax),%rcx  // restore rcx
          push   (%rax)         // push return address onto the stack
          sub    %rsp,%rax      // restore the original value in rax
          ret");
    intrinsics::unreachable();
}
<|endoftext|>


<commit_before>extern crate c_ares_sys;
extern crate libc;

use ctypes;
use error::AresError;
use types::AddressFamily;
use std::mem;
use std::net::{
    Ipv4Addr,
    Ipv6Addr,
    SocketAddrV4,
    SocketAddrV6,
};

// Convert an error code from the library into a more strongly typed AresError.
pub fn ares_error(code: libc::c_int) -> AresError {
    match code {
        c_ares_sys::ARES_ENODATA => AresError::ENODATA,
        c_ares_sys::ARES_EFORMERR => AresError::EFORMERR,
        c_ares_sys::ARES_ESERVFAIL => AresError::ESERVFAIL,
        c_ares_sys::ARES_ENOTFOUND => AresError::ENOTFOUND,
        c_ares_sys::ARES_ENOTIMP => AresError::ENOTIMP,
        c_ares_sys::ARES_EREFUSED => AresError::EREFUSED,
        c_ares_sys::ARES_EBADQUERY => AresError::EBADQUERY,
        c_ares_sys::ARES_EBADNAME => AresError::EBADNAME,
        c_ares_sys::ARES_EBADFAMILY => AresError::EBADFAMILY,
        c_ares_sys::ARES_EBADRESP => AresError::EBADRESP,
        c_ares_sys::ARES_ECONNREFUSED => AresError::ECONNREFUSED,
        c_ares_sys::ARES_ETIMEOUT => AresError::ETIMEOUT,
        c_ares_sys::ARES_EOF => AresError::EOF,
        c_ares_sys::ARES_EFILE => AresError::EFILE,
        c_ares_sys::ARES_ENOMEM => AresError::ENOMEM,
        c_ares_sys::ARES_EDESTRUCTION => AresError::EDESTRUCTION,
        c_ares_sys::ARES_EBADSTR => AresError::EBADSTR,
        c_ares_sys::ARES_EBADFLAGS => AresError::EBADFLAGS,
        c_ares_sys::ARES_ENONAME => AresError::ENONAME,
        c_ares_sys::ARES_EBADHINTS => AresError::EBADHINTS,
        c_ares_sys::ARES_ENOTINITIALIZED => AresError::ENOTINITIALIZED,
        c_ares_sys::ARES_ELOADIPHLPAPI => AresError::ELOADIPHLPAPI,
        c_ares_sys::ARES_EADDRGETNETWORKPARAMS =>
            AresError::EADDRGETNETWORKPARAMS,
        c_ares_sys::ARES_ECANCELLED => AresError::ECANCELLED,
        _ => AresError::UNKNOWN,
    }
}

// Convert an address family into a more strongly typed AddressFamily.
pub fn address_family(family: libc::c_int) -> Option<AddressFamily> {
    match family {
        ctypes::AF_INET => Some(AddressFamily::INET),
        ctypes::AF_INET6 => Some(AddressFamily::INET6),
        _ => None,
    }
}

// Get the u32 value from an Ipv4Addr.
fn ipv4_as_value(ipv4: &Ipv4Addr) -> u32 {
    ipv4.octets()
        .iter()
        .fold(0, |v, &o| (v << 8) | o as u32)
        .to_be()
}

// Get an in_addr from an Ipv4Addr.
#[cfg(unix)]
pub fn ipv4_as_in_addr(ipv4: &Ipv4Addr) -> ctypes::in_addr {
    ctypes::in_addr { s_addr: ipv4_as_value(ipv4) }
}

#[cfg(windows)]
pub fn ipv4_as_in_addr(ipv4: &Ipv4Addr) -> ctypes::in_addr {
    ctypes::in_addr { S_un: ipv4_as_value(ipv4) }
}

// Get an in6_addr from an Ipv6Addr.
pub fn ipv6_as_in6_addr(ipv6: &Ipv6Addr) -> ctypes::in6_addr {
    let segments = ipv6.segments();
    let mut in6_addr: ctypes::in6_addr = unsafe { mem::uninitialized() };
    in6_addr.s6_addr[0] = (segments[0] >> 8) as u8;
    in6_addr.s6_addr[1] = segments[0] as u8;
    in6_addr.s6_addr[2] = (segments[1] >> 8) as u8;
    in6_addr.s6_addr[3] = segments[1] as u8;
    in6_addr.s6_addr[4] = (segments[2] >> 8) as u8;
    in6_addr.s6_addr[5] = segments[2] as u8;
    in6_addr.s6_addr[6] = (segments[3] >> 8) as u8;
    in6_addr.s6_addr[7] = segments[3] as u8;
    in6_addr.s6_addr[8] = (segments[4] >> 8) as u8;
    in6_addr.s6_addr[9] = segments[4] as u8;
    in6_addr.s6_addr[10] = (segments[5] >> 8) as u8;
    in6_addr.s6_addr[11] = segments[5] as u8;
    in6_addr.s6_addr[12] = (segments[6] >> 8) as u8;
    in6_addr.s6_addr[13] = segments[6] as u8;
    in6_addr.s6_addr[14] = (segments[7] >> 8) as u8;
    in6_addr.s6_addr[15] = segments[7] as u8;
    in6_addr
}

// Get a sockaddr_in from a SocketAddrV4.
pub fn socket_addrv4_as_sockaddr_in(
    sock_v4: &SocketAddrV4) -> ctypes::sockaddr_in {
    let in_addr = ipv4_as_in_addr(sock_v4.ip());
    ctypes::sockaddr_in {
        sin_family: ctypes::AF_INET as ctypes::sa_family_t,
        sin_port: sock_v4.port().to_be(),
        sin_addr: in_addr,
        sin_zero: [0; 8],
    }
}

// Get a sockaddr_in6 from a SocketAddrV6.
#[cfg(unix)]
pub fn socket_addrv6_as_sockaddr_in6(
    sock_v6: &SocketAddrV6) -> ctypes::sockaddr_in6 {
    let in6_addr = ipv6_as_in6_addr(sock_v6.ip());
    ctypes::sockaddr_in6 {
        sin6_family: ctypes::AF_INET6 as ctypes::sa_family_t,
        sin6_port: sock_v6.port().to_be(),
        sin6_addr: in6_addr,
        sin6_flowinfo: sock_v6.flowinfo().to_be(),
        sin6_scope_id: sock_v6.scope_id().to_be(),
    }
}

#[cfg(windows)]
pub fn socket_addrv6_as_sockaddr_in6(
    sock_v6: &SocketAddrV6) -> ctypes::sockaddr_in6 {
    let in6_addr = ipv6_as_in6_addr(sock_v6.ip());
    ctypes::sockaddr_in6 {
        sin6_family: ctypes::AF_INET6 as i16,
        sin6_port: sock_v6.port().to_be(),
        sin6_addr: in6_addr,
        sin6_flowinfo: sock_v6.flowinfo().to_be(),
        sin6_scope_id: sock_v6.scope_id().to_be(),
    }
}
<commit_msg>Fix OS X build?<commit_after>extern crate c_ares_sys;
extern crate libc;

use ctypes;
use error::AresError;
use types::AddressFamily;
use std::mem;
use std::net::{
    Ipv4Addr,
    Ipv6Addr,
    SocketAddrV4,
    SocketAddrV6,
};

// Convert an error code from the library into a more strongly typed AresError.
pub fn ares_error(code: libc::c_int) -> AresError {
    match code {
        c_ares_sys::ARES_ENODATA => AresError::ENODATA,
        c_ares_sys::ARES_EFORMERR => AresError::EFORMERR,
        c_ares_sys::ARES_ESERVFAIL => AresError::ESERVFAIL,
        c_ares_sys::ARES_ENOTFOUND => AresError::ENOTFOUND,
        c_ares_sys::ARES_ENOTIMP => AresError::ENOTIMP,
        c_ares_sys::ARES_EREFUSED => AresError::EREFUSED,
        c_ares_sys::ARES_EBADQUERY => AresError::EBADQUERY,
        c_ares_sys::ARES_EBADNAME => AresError::EBADNAME,
        c_ares_sys::ARES_EBADFAMILY => AresError::EBADFAMILY,
        c_ares_sys::ARES_EBADRESP => AresError::EBADRESP,
        c_ares_sys::ARES_ECONNREFUSED => AresError::ECONNREFUSED,
        c_ares_sys::ARES_ETIMEOUT => AresError::ETIMEOUT,
        c_ares_sys::ARES_EOF => AresError::EOF,
        c_ares_sys::ARES_EFILE => AresError::EFILE,
        c_ares_sys::ARES_ENOMEM => AresError::ENOMEM,
        c_ares_sys::ARES_EDESTRUCTION => AresError::EDESTRUCTION,
        c_ares_sys::ARES_EBADSTR => AresError::EBADSTR,
        c_ares_sys::ARES_EBADFLAGS => AresError::EBADFLAGS,
        c_ares_sys::ARES_ENONAME => AresError::ENONAME,
        c_ares_sys::ARES_EBADHINTS => AresError::EBADHINTS,
        c_ares_sys::ARES_ENOTINITIALIZED => AresError::ENOTINITIALIZED,
        c_ares_sys::ARES_ELOADIPHLPAPI => AresError::ELOADIPHLPAPI,
        c_ares_sys::ARES_EADDRGETNETWORKPARAMS =>
            AresError::EADDRGETNETWORKPARAMS,
        c_ares_sys::ARES_ECANCELLED => AresError::ECANCELLED,
        _ => AresError::UNKNOWN,
    }
}

// Convert an address family into a more strongly typed AddressFamily.
pub fn address_family(family: libc::c_int) -> Option<AddressFamily> {
    match family {
        ctypes::AF_INET => Some(AddressFamily::INET),
        ctypes::AF_INET6 => Some(AddressFamily::INET6),
        _ => None,
    }
}

// Get the u32 value from an Ipv4Addr.
fn ipv4_as_value(ipv4: &Ipv4Addr) -> u32 {
    ipv4.octets()
        .iter()
        .fold(0, |v, &o| (v << 8) | o as u32)
        .to_be()
}

// Get an in_addr from an Ipv4Addr.
#[cfg(unix)]
pub fn ipv4_as_in_addr(ipv4: &Ipv4Addr) -> ctypes::in_addr {
    ctypes::in_addr { s_addr: ipv4_as_value(ipv4) }
}

#[cfg(windows)]
pub fn ipv4_as_in_addr(ipv4: &Ipv4Addr) -> ctypes::in_addr {
    ctypes::in_addr { S_un: ipv4_as_value(ipv4) }
}

// Get an in6_addr from an Ipv6Addr.
pub fn ipv6_as_in6_addr(ipv6: &Ipv6Addr) -> ctypes::in6_addr {
    let segments = ipv6.segments();
    let mut in6_addr: ctypes::in6_addr = unsafe { mem::uninitialized() };
    in6_addr.s6_addr[0] = (segments[0] >> 8) as u8;
    in6_addr.s6_addr[1] = segments[0] as u8;
    in6_addr.s6_addr[2] = (segments[1] >> 8) as u8;
    in6_addr.s6_addr[3] = segments[1] as u8;
    in6_addr.s6_addr[4] = (segments[2] >> 8) as u8;
    in6_addr.s6_addr[5] = segments[2] as u8;
    in6_addr.s6_addr[6] = (segments[3] >> 8) as u8;
    in6_addr.s6_addr[7] = segments[3] as u8;
    in6_addr.s6_addr[8] = (segments[4] >> 8) as u8;
    in6_addr.s6_addr[9] = segments[4] as u8;
    in6_addr.s6_addr[10] = (segments[5] >> 8) as u8;
    in6_addr.s6_addr[11] = segments[5] as u8;
    in6_addr.s6_addr[12] = (segments[6] >> 8) as u8;
    in6_addr.s6_addr[13] = segments[6] as u8;
    in6_addr.s6_addr[14] = (segments[7] >> 8) as u8;
    in6_addr.s6_addr[15] = segments[7] as u8;
    in6_addr
}

// Get a sockaddr_in from a SocketAddrV4.
#[cfg(any(target_os = "macos",
          target_os = "ios",
          target_os = "freebsd",
          target_os = "dragonfly",
          target_os = "openbsd",
          target_os = "netbsd",
          target_os = "bitrig"))]
pub fn socket_addrv4_as_sockaddr_in(
    sock_v4: &SocketAddrV4) -> ctypes::sockaddr_in {
    let in_addr = ipv4_as_in_addr(sock_v4.ip());
    ctypes::sockaddr_in {
        sin_len: mem::size_of::<ctypes::sockaddr_in>(),
        sin_family: ctypes::AF_INET as ctypes::sa_family_t,
        sin_port: sock_v4.port().to_be(),
        sin_addr: in_addr,
        sin_zero: [0; 8],
    }
}

#[cfg(not(any(target_os = "macos",
              target_os = "ios",
              target_os = "freebsd",
              target_os = "dragonfly",
              target_os = "openbsd",
              target_os = "netbsd",
              target_os = "bitrig")))]
pub fn socket_addrv4_as_sockaddr_in(
    sock_v4: &SocketAddrV4) -> ctypes::sockaddr_in {
    let in_addr = ipv4_as_in_addr(sock_v4.ip());
    ctypes::sockaddr_in {
        sin_family: ctypes::AF_INET as ctypes::sa_family_t,
        sin_port: sock_v4.port().to_be(),
        sin_addr: in_addr,
        sin_zero: [0; 8],
    }
}

// Get a sockaddr_in6 from a SocketAddrV6.
#[cfg(any(target_os = "macos",
          target_os = "ios",
          target_os = "freebsd",
          target_os = "dragonfly",
          target_os = "openbsd",
          target_os = "netbsd",
          target_os = "bitrig"))]
pub fn socket_addrv6_as_sockaddr_in6(
    sock_v6: &SocketAddrV6) -> ctypes::sockaddr_in6 {
    let in6_addr = ipv6_as_in6_addr(sock_v6.ip());
    ctypes::sockaddr_in6 {
        sin6_len: mem::size_of::<ctypes::sockaddr_in6>(),
        sin6_family: ctypes::AF_INET6 as ctypes::sa_family_t,
        sin6_port: sock_v6.port().to_be(),
        sin6_addr: in6_addr,
        sin6_flowinfo: sock_v6.flowinfo().to_be(),
        sin6_scope_id: sock_v6.scope_id().to_be(),
    }
}

#[cfg(all(unix,not(any(target_os = "macos",
                       target_os = "ios",
                       target_os = "freebsd",
                       target_os = "dragonfly",
                       target_os = "openbsd",
                       target_os = "netbsd",
                       target_os = "bitrig"))))]
pub fn socket_addrv6_as_sockaddr_in6(
    sock_v6: &SocketAddrV6) -> ctypes::sockaddr_in6 {
    let in6_addr = ipv6_as_in6_addr(sock_v6.ip());
    ctypes::sockaddr_in6 {
        sin6_family: ctypes::AF_INET6 as ctypes::sa_family_t,
        sin6_port: sock_v6.port().to_be(),
        sin6_addr: in6_addr,
        sin6_flowinfo: sock_v6.flowinfo().to_be(),
        sin6_scope_id: sock_v6.scope_id().to_be(),
    }
}

#[cfg(windows)]
pub fn socket_addrv6_as_sockaddr_in6(
    sock_v6: &SocketAddrV6) -> ctypes::sockaddr_in6 {
    let in6_addr = ipv6_as_in6_addr(sock_v6.ip());
    ctypes::sockaddr_in6 {
        sin6_family: ctypes::AF_INET6 as i16,
        sin6_port: sock_v6.port().to_be(),
        sin6_addr: in6_addr,
        sin6_flowinfo: sock_v6.flowinfo().to_be(),
        sin6_scope_id: sock_v6.scope_id().to_be(),
    }
}
<|endoftext|>


<commit_before>#![doc(html_root_url = "https://urschrei.github.io/lonlat_bng/")]
//! This module provides utilities to the conversions module
use conversions::MIN_X_SHIFT;
use conversions::MIN_Y_SHIFT;
use conversions::MIN_Z_SHIFT;

use std;
use std::fmt;
use ostn02_phf::ostn02_lookup;

// fn helmert(lon_vec: [&f64], lat_vec: [&f64]) -> (Vec<f64>, Vec<f64>) {
//     let t_array = Vec3::new(TX, TY, TZ);
//     let params = Mat3::new(1. + s, RZS, -RYS, -RZS, 1. + s, RXS, RYS, -RXS, 1. + s);
//     // let zeros: Vec<&f64> = vec![&1.; lon_vec.len()];
//     let test = vec![1, 2, 3, 4, 5, 6];
//     // let mut combined: Vec<&f64> = lon_vec.extend(lat_vec.iter()).collect();
//     // combined.extend(zeros.iter().cloned());
//     // let tmat = DMat::from_row_vec(3, zeros.len(), &combined);
//     let tmat = DMat::from_row_vec(3, 2, &test);
//     // build input vector of x, y, and z columns
//     // let inp = DMat::from_row_vec(3, 1, vec![lon_vec, lat_vec, h_vec]);
//     (vec![1.], vec![2.])
// }

/// Bounds checking for input values
pub fn check<T>(to_check: T, bounds: (T, T)) -> Result<T, ()>
    where T: std::cmp::PartialOrd + fmt::Display + Copy
{
    match to_check {
        to_check if bounds.0 <= to_check && to_check <= bounds.1 => Ok(to_check),
        _ => Err(()),
    }
}

/// Round Easting, Northing, and Orthometric height to the nearest millimetre
pub fn round_to_nearest_mm(x: f64, y: f64, z: f64) -> (f64, f64, f64) {
    let new_x = (x * 1000.).round() as f64 / 1000.;
    let new_y = (y * 1000.).round() as f64 / 1000.;
    let new_z = (z * 1000.).round() as f64 / 1000.;
    (new_x, new_y, new_z)
}

/// Round a float to nine decimal places
pub fn round_to_eight(x: f64, y: f64) -> (f64, f64) {
    let new_x = (x * 100000000.).round() as f64 / 100000000.;
    let new_y = (y * 100000000.).round() as f64 / 100000000.;
    (new_x, new_y)
}

/// Try to get OSTN02 shift parameters, and calculate offsets
pub fn get_ostn_ref(x: &i32, y: &i32) -> Result<(f64, f64, f64), ()> {
    let key = format!("{:03x}{:03x}", y, x);
    // some or None, so try! this
    let result = try!(ostn02_lookup(&*key).ok_or(()));
    Ok((result.0 as f64 / 1000. + MIN_X_SHIFT,
        result.1 as f64 / 1000. + MIN_Y_SHIFT,
        result.2 as f64 / 1000. + MIN_Z_SHIFT))

}

// Input values must be valid ETRS89 grid references
// See p20 of the transformation user guide at
// https://www.ordnancesurvey.co.uk/business-and-government/help-and-support/navigation-technology/os-net/formats-for-developers.html
pub fn ostn02_shifts(x: &f64, y: &f64) -> Result<(f64, f64, f64), ()> {
    let e_index = (*x / 1000.) as i32;
    let n_index = (*y / 1000.) as i32;

    // eastings and northings of the south-west corner of the cell
    let x0 = e_index * 1000;
    let y0 = n_index * 1000;

    // The easting, northing and geoid shifts for the four corners of the cell
    // any of these could be Err, so use try!
    let s0: (f64, f64, f64) = try!(get_ostn_ref(&(e_index + 0), &(n_index + 0)));
    let s1: (f64, f64, f64) = try!(get_ostn_ref(&(e_index + 1), &(n_index + 0)));
    let s2: (f64, f64, f64) = try!(get_ostn_ref(&(e_index + 0), &(n_index + 1)));
    let s3: (f64, f64, f64) = try!(get_ostn_ref(&(e_index + 1), &(n_index + 1)));

    // offset within square
    let dx = *x - (x0 as f64);
    let dy = *y - (y0 as f64);

    let t = dx / 1000.;
    let u = dy / 1000.;

    let f0 = (1. - t as f64) * (1. - u as f64);
    let f1 = t as f64 * (1. - u as f64);
    let f2 = (1. - t as f64) * u as f64;
    let f3 = t as f64 * u as f64;

    // bilinear interpolation, to obtain the actual shifts
    let se = f0 * s0.0 + f1 * s1.0 + f2 * s2.0 + f3 * s3.0;
    let sn = f0 * s0.1 + f1 * s1.1 + f2 * s2.1 + f3 * s3.1;
    let sg = f0 * s0.2 + f1 * s1.2 + f2 * s2.2 + f3 * s3.2;

    Ok(round_to_nearest_mm(se, sn, sg))

}

#[cfg(test)]
mod tests {
    use super::get_ostn_ref;
    use super::ostn02_shifts;
    use super::check;

    #[test]
    // original coordinates are 651307.003, 313255.686
    fn test_ostn_hashmap_retrieval() {
        let eastings = 651;
        let northings = 313;
        let expected = (102.775, -78.244, 44.252);
        assert_eq!(expected, get_ostn_ref(&eastings, &northings).unwrap());
    }

    #[test]
    #[should_panic]
    fn test_failed_ostn_hashmap_retrieval() {
        // we're try!ing this in the shift calculation function, so an Err is fine
        let eastings = 999;
        let northings = 999;
        let expected = (1., 1., 1.);
        assert_eq!(expected, get_ostn_ref(&eastings, &northings).unwrap());
    }

    #[test]
    fn test_ostn02_shift_incorporation() {
        // these are the input values and corrections on p20-21
        let eastings = 651307.003;
        let northings = 313255.686;
        let expected = (102.789, -78.238, 44.244);
        assert_eq!(expected, ostn02_shifts(&eastings, &northings).unwrap());
    }

    #[test]
    #[should_panic]
    fn test_min_lon_extents() {
        let max_lon = 1.768960;
        let min_lon = -6.379880;
        // below min_lon
        check(&-6.379881, (&min_lon, &max_lon)).unwrap();
    }

    #[test]
    #[should_panic]
    fn test_min_lat_extents() {
        let max_lat = 55.811741;
        let min_lat = 49.871159;
        // below min lat
        check(&49.871158, (&min_lat, &max_lat)).unwrap();
    }

    #[test]
    #[should_panic]
    fn test_max_lon_extents() {
        let max_lon = 1.768960;
        let min_lon = -6.379880;
        // above max lon
        check(&1.768961, (&min_lon, &max_lon)).unwrap();
    }

    #[test]
    #[should_panic]
    fn test_max_lat_extents() {
        let max_lat = 55.811741;
        let min_lat = 49.871159;
        // above max lat
        check(&55.811742, (&min_lat, &max_lat)).unwrap();
    }

}
<commit_msg>Removing some implicit derefs, adding some comments<commit_after>#![doc(html_root_url = "https://urschrei.github.io/lonlat_bng/")]
//! This module provides utilities to the conversions module
use conversions::MIN_X_SHIFT;
use conversions::MIN_Y_SHIFT;
use conversions::MIN_Z_SHIFT;

use std;
use std::fmt;
use ostn02_phf::ostn02_lookup;

// fn helmert(lon_vec: [&f64], lat_vec: [&f64]) -> (Vec<f64>, Vec<f64>) {
//     let t_array = Vec3::new(TX, TY, TZ);
//     let params = Mat3::new(1. + s, RZS, -RYS, -RZS, 1. + s, RXS, RYS, -RXS, 1. + s);
//     // let zeros: Vec<&f64> = vec![&1.; lon_vec.len()];
//     let test = vec![1, 2, 3, 4, 5, 6];
//     // let mut combined: Vec<&f64> = lon_vec.extend(lat_vec.iter()).collect();
//     // combined.extend(zeros.iter().cloned());
//     // let tmat = DMat::from_row_vec(3, zeros.len(), &combined);
//     let tmat = DMat::from_row_vec(3, 2, &test);
//     // build input vector of x, y, and z columns
//     // let inp = DMat::from_row_vec(3, 1, vec![lon_vec, lat_vec, h_vec]);
//     (vec![1.], vec![2.])
// }

/// Bounds checking for input values
pub fn check<T>(to_check: T, bounds: (T, T)) -> Result<T, ()>
    where T: std::cmp::PartialOrd + fmt::Display + Copy
{
    match to_check {
        to_check if bounds.0 <= to_check && to_check <= bounds.1 => Ok(to_check),
        _ => Err(()),
    }
}

/// Round Easting, Northing, and Orthometric height to the nearest millimetre
pub fn round_to_nearest_mm(x: f64, y: f64, z: f64) -> (f64, f64, f64) {
    let new_x = (x * 1000.).round() as f64 / 1000.;
    let new_y = (y * 1000.).round() as f64 / 1000.;
    let new_z = (z * 1000.).round() as f64 / 1000.;
    (new_x, new_y, new_z)
}

/// Round a float to nine decimal places
pub fn round_to_eight(x: f64, y: f64) -> (f64, f64) {
    let new_x = (x * 100000000.).round() as f64 / 100000000.;
    let new_y = (y * 100000000.).round() as f64 / 100000000.;
    (new_x, new_y)
}

/// Try to get OSTN02 shift parameters, and calculate offsets
pub fn get_ostn_ref(x: &i32, y: &i32) -> Result<(f64, f64, f64), ()> {
    let key = format!("{:03x}{:03x}", y, x);
    // Some or None, so convert to Result, which we can try!
    let result = try!(ostn02_lookup(&*key).ok_or(()));
    Ok((result.0 as f64 / 1000. + MIN_X_SHIFT,
        result.1 as f64 / 1000. + MIN_Y_SHIFT,
        result.2 as f64 / 1000. + MIN_Z_SHIFT))

}

// Input values must be valid ETRS89 grid references
// See p20 of the transformation user guide at
// https://www.ordnancesurvey.co.uk/business-and-government/help-and-support/navigation-technology/os-net/formats-for-developers.html
pub fn ostn02_shifts(x: &f64, y: &f64) -> Result<(f64, f64, f64), ()> {
    let e_index = (*x / 1000.) as i32;
    let n_index = (*y / 1000.) as i32;

    // eastings and northings of the south-west corner of the cell
    let x0 = e_index * 1000;
    let y0 = n_index * 1000;

    // The easting, northing and geoid shifts for the four corners of the cell
    // any of these could be Err, so use try!

    // bottom-left grid intersection
    let s0: (f64, f64, f64) = try!(get_ostn_ref(&(e_index), &(n_index)));
    // bottom-right
    let s1: (f64, f64, f64) = try!(get_ostn_ref(&(e_index + 1), &(n_index + 0)));
    // top-left
    let s2: (f64, f64, f64) = try!(get_ostn_ref(&(e_index + 0), &(n_index + 1)));
    // top-right
    let s3: (f64, f64, f64) = try!(get_ostn_ref(&(e_index + 1), &(n_index + 1)));

    // offset within square
    let dx = x - (x0 as f64);
    let dy = y - (y0 as f64);

    let t = dx / 1000.;
    let u = dy / 1000.;

    let f0 = (1. - t as f64) * (1. - u as f64);
    let f1 = t as f64 * (1. - u as f64);
    let f2 = (1. - t as f64) * u as f64;
    let f3 = t as f64 * u as f64;

    // bilinear interpolation, to obtain the actual shifts
    let se = f0 * s0.0 + f1 * s1.0 + f2 * s2.0 + f3 * s3.0;
    let sn = f0 * s0.1 + f1 * s1.1 + f2 * s2.1 + f3 * s3.1;
    let sg = f0 * s0.2 + f1 * s1.2 + f2 * s2.2 + f3 * s3.2;

    Ok(round_to_nearest_mm(se, sn, sg))

}

#[cfg(test)]
mod tests {
    use super::get_ostn_ref;
    use super::ostn02_shifts;
    use super::check;

    #[test]
    // original coordinates are 651307.003, 313255.686
    fn test_ostn_hashmap_retrieval() {
        let eastings = 651;
        let northings = 313;
        let expected = (102.775, -78.244, 44.252);
        assert_eq!(expected, get_ostn_ref(&eastings, &northings).unwrap());
    }

    #[test]
    #[should_panic]
    fn test_failed_ostn_hashmap_retrieval() {
        // we're try!ing this in the shift calculation function, so an Err is fine
        let eastings = 999;
        let northings = 999;
        let expected = (1., 1., 1.);
        assert_eq!(expected, get_ostn_ref(&eastings, &northings).unwrap());
    }

    #[test]
    fn test_ostn02_shift_incorporation() {
        // these are the input values and corrections on p20-21
        let eastings = 651307.003;
        let northings = 313255.686;
        let expected = (102.789, -78.238, 44.244);
        assert_eq!(expected, ostn02_shifts(&eastings, &northings).unwrap());
    }

    #[test]
    #[should_panic]
    fn test_min_lon_extents() {
        let max_lon = 1.768960;
        let min_lon = -6.379880;
        // below min_lon
        check(&-6.379881, (&min_lon, &max_lon)).unwrap();
    }

    #[test]
    #[should_panic]
    fn test_min_lat_extents() {
        let max_lat = 55.811741;
        let min_lat = 49.871159;
        // below min lat
        check(&49.871158, (&min_lat, &max_lat)).unwrap();
    }

    #[test]
    #[should_panic]
    fn test_max_lon_extents() {
        let max_lon = 1.768960;
        let min_lon = -6.379880;
        // above max lon
        check(&1.768961, (&min_lon, &max_lon)).unwrap();
    }

    #[test]
    #[should_panic]
    fn test_max_lat_extents() {
        let max_lat = 55.811741;
        let min_lat = 49.871159;
        // above max lat
        check(&55.811742, (&min_lat, &max_lat)).unwrap();
    }

}
<|endoftext|>