<commit_before>#![feature(asm)]

use std::{fmt, slice, str};
use std::ops::Deref;

enum RequestType {
    BasicInformation            = 0x00000000,
    VersionInformation          = 0x00000001,
    ExtendedFunctionInformation = 0x80000000,
    BrandString1                = 0x80000002,
    BrandString2                = 0x80000003,
    BrandString3                = 0x80000004,
}

fn cpuid(code: RequestType) -> (u32, u32, u32, u32) {
    let res1;
    let res2;
    let res3;
    let res4;

    unsafe {
        asm!("cpuid"
             : // output operands
             "={eax}"(res1),
             "={ebx}"(res2),
             "={ecx}"(res3),
             "={edx}"(res4)
             : // input operands
             "{eax}"(code as u32)
             : // clobbers
             : // options
        );
    }

    (res1, res2, res3, res4)
}

// This matches the Intel Architecture guide, with bits 31 -> 0.
// The bit positions are inclusive.
fn bits_of(val: u32, start_bit: u8, end_bit: u8) -> u32 {
    let mut silly = 0;

    for _ in start_bit..end_bit+1 {
        silly <<= 1;
        silly |= 1;
    }

    (val >> start_bit) & silly
}

macro_rules! bit {
    ($reg:ident, $idx:expr, $name:ident) => {
        pub fn $name(self) -> bool {
            ((self.$reg >> $idx) & 1) != 0
        }
    }
}

/// Exposes the processor feature flags.
///
/// Each method corresponds to a single capability. Method names match
/// the feature mnemonic listed in the Intel Instruction Set
/// Reference.
#[derive(Copy, Clone, Debug)]
pub struct FeatureInformation {
    ecx: u32,
    edx: u32,
}

impl FeatureInformation {
    bit!(ecx,  0, sse3);
    bit!(ecx,  1, pclmulqdq);
    bit!(ecx,  2, dtes64);
    bit!(ecx,  3, monitor);
    bit!(ecx,  4, ds_cpl);
    bit!(ecx,  5, vmx);
    bit!(ecx,  6, smx);
    bit!(ecx,  7, eist);
    bit!(ecx,  8, tm2);
    bit!(ecx,  9, ssse3);
    bit!(ecx, 10, cnxt_id);
    bit!(ecx, 11, sdbg);
    bit!(ecx, 12, fma);
    bit!(ecx, 13, cmpxchg16b);
    bit!(ecx, 14, xtpr_update_control);
    bit!(ecx, 15, pdcm);
    // 16 - reserved
    bit!(ecx, 17, pcid);
    bit!(ecx, 18, dca);
    bit!(ecx, 19, sse4_1);
    bit!(ecx, 20, sse4_2);
    bit!(ecx, 21, x2apic);
    bit!(ecx, 22, movbe);
    bit!(ecx, 23, popcnt);
    bit!(ecx, 24, tsc_deadline);
    bit!(ecx, 25, aesni);
    bit!(ecx, 26, xsave);
    bit!(ecx, 27, osxsave);
    bit!(ecx, 28, avx);
    bit!(ecx, 29, f16c);
    bit!(ecx, 30, rdrand);
    // 31 - unused

    bit!(edx,  0, fpu);
    bit!(edx,  1, vme);
    bit!(edx,  2, de);
    bit!(edx,  3, pse);
    bit!(edx,  4, tsc);
    bit!(edx,  5, msr);
    bit!(edx,  6, pae);
    bit!(edx,  7, mce);
    bit!(edx,  8, cx8);
    bit!(edx,  9, apic);
    // reserved
    bit!(edx, 11, sep);
    bit!(edx, 12, mtrr);
    bit!(edx, 13, pge);
    bit!(edx, 14, mca);
    bit!(edx, 15, cmov);
    bit!(edx, 16, pat);
    bit!(edx, 17, pse_36);
    bit!(edx, 18, psn);
    bit!(edx, 19, clfsh);
    // reserved
    bit!(edx, 21, ds);
    bit!(edx, 22, acpi);
    bit!(edx, 23, mmx);
    bit!(edx, 24, fxsr);
    bit!(edx, 25, sse);
    bit!(edx, 26, sse2);
    bit!(edx, 27, ss);
    bit!(edx, 28, htt);
    bit!(edx, 29, tm);
    // reserved
    bit!(edx, 31, pbe);
}

macro_rules! dump {
    ($me:expr, $f: expr, $name: ident) => {
        try!(writeln!($f, "{}: {}", stringify!($name), $me.$name()));
    }
}

impl fmt::Display for FeatureInformation {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        dump!(self, f, sse3);
        dump!(self, f, pclmulqdq);
        dump!(self, f, dtes64);
        dump!(self, f, monitor);
        dump!(self, f, ds_cpl);
        dump!(self, f, vmx);
        dump!(self, f, smx);
        dump!(self, f, eist);
        dump!(self, f, tm2);
        dump!(self, f, ssse3);
        dump!(self, f, cnxt_id);
        dump!(self, f, sdbg);
        dump!(self, f, fma);
        dump!(self, f, cmpxchg16b);
        dump!(self, f, xtpr_update_control);
        dump!(self, f, pdcm);
        dump!(self, f, pcid);
        dump!(self, f, dca);
        dump!(self, f, sse4_1);
        dump!(self, f, sse4_2);
        dump!(self, f, x2apic);
        dump!(self, f, movbe);
        dump!(self, f, popcnt);
        dump!(self, f, tsc_deadline);
        dump!(self, f, aesni);
        dump!(self, f, xsave);
        dump!(self, f, osxsave);
        dump!(self, f, avx);
        dump!(self, f, f16c);
        dump!(self, f, rdrand);
        dump!(self, f, fpu);
        dump!(self, f, vme);
        dump!(self, f, de);
        dump!(self, f, pse);
        dump!(self, f, tsc);
        dump!(self, f, msr);
        dump!(self, f, pae);
        dump!(self, f, mce);
        dump!(self, f, cx8);
        dump!(self, f, apic);
        dump!(self, f, sep);
        dump!(self, f, mtrr);
        dump!(self, f, pge);
        dump!(self, f, mca);
        dump!(self, f, cmov);
        dump!(self, f, pat);
        dump!(self, f, pse_36);
        dump!(self, f, psn);
        dump!(self, f, clfsh);
        dump!(self, f, ds);
        dump!(self, f, acpi);
        dump!(self, f, mmx);
        dump!(self, f, fxsr);
        dump!(self, f, sse);
        dump!(self, f, sse2);
        dump!(self, f, ss);
        dump!(self, f, htt);
        dump!(self, f, tm);
        dump!(self, f, pbe);
        Ok(())
    }
}

pub fn feature_information() -> FeatureInformation {
    let (_, _, c, d) = cpuid(RequestType::VersionInformation);
    FeatureInformation { ecx: c, edx: d }
}

fn as_bytes(v: &u32) -> &[u8] {
    let start = v as *const u32 as *const u8;
    // TODO: use u32::BYTES
    unsafe { slice::from_raw_parts(start, 4) }
}

// 3 calls of 4 registers of 4 bytes
const BRAND_STRING_LENGTH: usize = 3 * 4 * 4;

pub struct BrandString {
    bytes: [u8; BRAND_STRING_LENGTH],
}

impl BrandString {
    fn new() -> BrandString {
        BrandString { bytes: [0; BRAND_STRING_LENGTH] }
    }
}

impl Deref for BrandString {
    type Target = str;

    fn deref(&self) -> &str {
        let nul_terminator = self.bytes.iter().position(|&b| b == 0).unwrap_or(0);
        let usable_bytes = &self.bytes[..nul_terminator];
        unsafe { str::from_utf8_unchecked(usable_bytes) }
    }
}

impl fmt::Display for BrandString {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        (self as &str).fmt(f)
    }
}

pub fn brand_string() -> BrandString {
    // Should check supported (EAX Return Value of 0x80000000 ≥ 0x80000004)

    fn append_bytes(a: RequestType, bytes: &mut [u8]) {
        let (a, b, c, d) = cpuid(a);

        let result_bytes =
            as_bytes(&a).iter()
            .chain(as_bytes(&b).iter())
            .chain(as_bytes(&c).iter())
            .chain(as_bytes(&d).iter());

        for (output, input) in bytes.iter_mut().zip(result_bytes) {
            *output = *input
        }
    }

    let mut brand_string = BrandString::new();
    append_bytes(RequestType::BrandString1, &mut brand_string.bytes[0..]);
    append_bytes(RequestType::BrandString2, &mut brand_string.bytes[16..]);
    append_bytes(RequestType::BrandString3, &mut brand_string.bytes[32..]);
    brand_string
}

#[test]
fn basic_genuine_intel() {
    // let (a,b,c,d) = cpuid(RequestType::BasicInformation);

    // assert_eq!(b"Genu", b);
    // assert_eq!(b"ntel", c);
    // assert_eq!(b"ineI", d);
}

#[test]
fn brand_string_contains_intel() {
    assert!(brand_string().contains("Intel(R)"))
}
<commit_msg>Actually test the base CPUID instruction<commit_after>#![feature(asm)]

use std::{fmt, slice, str};
use std::ops::Deref;

enum RequestType {
    BasicInformation            = 0x00000000,
    VersionInformation          = 0x00000001,
    ExtendedFunctionInformation = 0x80000000,
    BrandString1                = 0x80000002,
    BrandString2                = 0x80000003,
    BrandString3                = 0x80000004,
}

fn cpuid(code: RequestType) -> (u32, u32, u32, u32) {
    let res1;
    let res2;
    let res3;
    let res4;

    unsafe {
        asm!("cpuid"
             : // output operands
             "={eax}"(res1),
             "={ebx}"(res2),
             "={ecx}"(res3),
             "={edx}"(res4)
             : // input operands
             "{eax}"(code as u32)
             : // clobbers
             : // options
        );
    }

    (res1, res2, res3, res4)
}

// This matches the Intel Architecture guide, with bits 31 -> 0.
// The bit positions are inclusive.
fn bits_of(val: u32, start_bit: u8, end_bit: u8) -> u32 {
    let mut silly = 0;

    for _ in start_bit..end_bit+1 {
        silly <<= 1;
        silly |= 1;
    }

    (val >> start_bit) & silly
}

macro_rules! bit {
    ($reg:ident, $idx:expr, $name:ident) => {
        pub fn $name(self) -> bool {
            ((self.$reg >> $idx) & 1) != 0
        }
    }
}

/// Exposes the processor feature flags.
///
/// Each method corresponds to a single capability. Method names match
/// the feature mnemonic listed in the Intel Instruction Set
/// Reference.
#[derive(Copy, Clone, Debug)]
pub struct FeatureInformation {
    ecx: u32,
    edx: u32,
}

impl FeatureInformation {
    bit!(ecx,  0, sse3);
    bit!(ecx,  1, pclmulqdq);
    bit!(ecx,  2, dtes64);
    bit!(ecx,  3, monitor);
    bit!(ecx,  4, ds_cpl);
    bit!(ecx,  5, vmx);
    bit!(ecx,  6, smx);
    bit!(ecx,  7, eist);
    bit!(ecx,  8, tm2);
    bit!(ecx,  9, ssse3);
    bit!(ecx, 10, cnxt_id);
    bit!(ecx, 11, sdbg);
    bit!(ecx, 12, fma);
    bit!(ecx, 13, cmpxchg16b);
    bit!(ecx, 14, xtpr_update_control);
    bit!(ecx, 15, pdcm);
    // 16 - reserved
    bit!(ecx, 17, pcid);
    bit!(ecx, 18, dca);
    bit!(ecx, 19, sse4_1);
    bit!(ecx, 20, sse4_2);
    bit!(ecx, 21, x2apic);
    bit!(ecx, 22, movbe);
    bit!(ecx, 23, popcnt);
    bit!(ecx, 24, tsc_deadline);
    bit!(ecx, 25, aesni);
    bit!(ecx, 26, xsave);
    bit!(ecx, 27, osxsave);
    bit!(ecx, 28, avx);
    bit!(ecx, 29, f16c);
    bit!(ecx, 30, rdrand);
    // 31 - unused

    bit!(edx,  0, fpu);
    bit!(edx,  1, vme);
    bit!(edx,  2, de);
    bit!(edx,  3, pse);
    bit!(edx,  4, tsc);
    bit!(edx,  5, msr);
    bit!(edx,  6, pae);
    bit!(edx,  7, mce);
    bit!(edx,  8, cx8);
    bit!(edx,  9, apic);
    // reserved
    bit!(edx, 11, sep);
    bit!(edx, 12, mtrr);
    bit!(edx, 13, pge);
    bit!(edx, 14, mca);
    bit!(edx, 15, cmov);
    bit!(edx, 16, pat);
    bit!(edx, 17, pse_36);
    bit!(edx, 18, psn);
    bit!(edx, 19, clfsh);
    // reserved
    bit!(edx, 21, ds);
    bit!(edx, 22, acpi);
    bit!(edx, 23, mmx);
    bit!(edx, 24, fxsr);
    bit!(edx, 25, sse);
    bit!(edx, 26, sse2);
    bit!(edx, 27, ss);
    bit!(edx, 28, htt);
    bit!(edx, 29, tm);
    // reserved
    bit!(edx, 31, pbe);
}

macro_rules! dump {
    ($me:expr, $f: expr, $name: ident) => {
        try!(writeln!($f, "{}: {}", stringify!($name), $me.$name()));
    }
}

impl fmt::Display for FeatureInformation {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        dump!(self, f, sse3);
        dump!(self, f, pclmulqdq);
        dump!(self, f, dtes64);
        dump!(self, f, monitor);
        dump!(self, f, ds_cpl);
        dump!(self, f, vmx);
        dump!(self, f, smx);
        dump!(self, f, eist);
        dump!(self, f, tm2);
        dump!(self, f, ssse3);
        dump!(self, f, cnxt_id);
        dump!(self, f, sdbg);
        dump!(self, f, fma);
        dump!(self, f, cmpxchg16b);
        dump!(self, f, xtpr_update_control);
        dump!(self, f, pdcm);
        dump!(self, f, pcid);
        dump!(self, f, dca);
        dump!(self, f, sse4_1);
        dump!(self, f, sse4_2);
        dump!(self, f, x2apic);
        dump!(self, f, movbe);
        dump!(self, f, popcnt);
        dump!(self, f, tsc_deadline);
        dump!(self, f, aesni);
        dump!(self, f, xsave);
        dump!(self, f, osxsave);
        dump!(self, f, avx);
        dump!(self, f, f16c);
        dump!(self, f, rdrand);
        dump!(self, f, fpu);
        dump!(self, f, vme);
        dump!(self, f, de);
        dump!(self, f, pse);
        dump!(self, f, tsc);
        dump!(self, f, msr);
        dump!(self, f, pae);
        dump!(self, f, mce);
        dump!(self, f, cx8);
        dump!(self, f, apic);
        dump!(self, f, sep);
        dump!(self, f, mtrr);
        dump!(self, f, pge);
        dump!(self, f, mca);
        dump!(self, f, cmov);
        dump!(self, f, pat);
        dump!(self, f, pse_36);
        dump!(self, f, psn);
        dump!(self, f, clfsh);
        dump!(self, f, ds);
        dump!(self, f, acpi);
        dump!(self, f, mmx);
        dump!(self, f, fxsr);
        dump!(self, f, sse);
        dump!(self, f, sse2);
        dump!(self, f, ss);
        dump!(self, f, htt);
        dump!(self, f, tm);
        dump!(self, f, pbe);
        Ok(())
    }
}

pub fn feature_information() -> FeatureInformation {
    let (_, _, c, d) = cpuid(RequestType::VersionInformation);
    FeatureInformation { ecx: c, edx: d }
}

fn as_bytes(v: &u32) -> &[u8] {
    let start = v as *const u32 as *const u8;
    // TODO: use u32::BYTES
    unsafe { slice::from_raw_parts(start, 4) }
}

// 3 calls of 4 registers of 4 bytes
const BRAND_STRING_LENGTH: usize = 3 * 4 * 4;

pub struct BrandString {
    bytes: [u8; BRAND_STRING_LENGTH],
}

impl BrandString {
    fn new() -> BrandString {
        BrandString { bytes: [0; BRAND_STRING_LENGTH] }
    }
}

impl Deref for BrandString {
    type Target = str;

    fn deref(&self) -> &str {
        let nul_terminator = self.bytes.iter().position(|&b| b == 0).unwrap_or(0);
        let usable_bytes = &self.bytes[..nul_terminator];
        unsafe { str::from_utf8_unchecked(usable_bytes) }
    }
}

impl fmt::Display for BrandString {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        (self as &str).fmt(f)
    }
}

pub fn brand_string() -> BrandString {
    // Should check supported (EAX Return Value of 0x80000000 ≥ 0x80000004)

    fn append_bytes(a: RequestType, bytes: &mut [u8]) {
        let (a, b, c, d) = cpuid(a);

        let result_bytes =
            as_bytes(&a).iter()
            .chain(as_bytes(&b).iter())
            .chain(as_bytes(&c).iter())
            .chain(as_bytes(&d).iter());

        for (output, input) in bytes.iter_mut().zip(result_bytes) {
            *output = *input
        }
    }

    let mut brand_string = BrandString::new();
    append_bytes(RequestType::BrandString1, &mut brand_string.bytes[0..]);
    append_bytes(RequestType::BrandString2, &mut brand_string.bytes[16..]);
    append_bytes(RequestType::BrandString3, &mut brand_string.bytes[32..]);
    brand_string
}

#[test]
fn basic_genuine_intel() {
    let (_, b, c, d) = cpuid(RequestType::BasicInformation);

    assert_eq!(b"Genu", as_bytes(&b));
    assert_eq!(b"ntel", as_bytes(&c));
    assert_eq!(b"ineI", as_bytes(&d));
}

#[test]
fn brand_string_contains_intel() {
    assert!(brand_string().contains("Intel(R)"))
}
<|endoftext|>


<commit_before>//! # Feature flags
//!
//! This crate exposes a number of features. These can be enabled or disabled as shown
//! [in Cargo's documentation](https://doc.rust-lang.org/cargo/reference/features.html). Features
//! are _disabled_ by default unless otherwise noted.
//!
//! Reliance on a given feature is always indicated alongside the item definition.
//!
//! - `std` (_enabled by default, implicitly enables `alloc`_)
//!
//!   This enables a number of features that depend on the standard library. [`Instant`] is the
//!   primary item that requires this feature, though some   others methods may rely on [`Instant`]
//!   internally.
//!
//! - `alloc` (_enabled by default via `std`_)
//!
//!   Enables a number of features that require the ability to dynamically allocate memory.
//!
//! - `macros`
//!
//!   Enables macros that provide compile-time verification of values and intuitive syntax.
//!
//! - `formatting` (_implicitly enables `std`_)
//!
//!   Enables formatting of most structs.
//!
//! - `parsing`
//!
//!   Enables parsing of most structs.
//!
//! - `local-offset` (_implicitly enables `std`_)
//!
//!   This feature enables a number of methods that allow obtaining the system's UTC offset.
//!
//! - `large-dates`
//!
//!   By default, only years within the ±9999 range (inclusive) are supported. If you need support
//!   for years outside this range, consider enabling this feature; the supported range will be
//!   increased to ±999,999.
//!
//!   Note that enabling this feature has some costs, as it means forgoing some optimizations.
//!   Ambiguities may be introduced when parsing that would not otherwise exist.
//!
//!   If you are using this feature, **please leave a comment**
//!   [on this discussion](https://github.com/time-rs/time/discussions/306) with your use case. If
//!   there is not sufficient demand for this feature, it will be dropped in a future release.
//!
//! - `serde`
//!
//!   Enables [serde](https://docs.rs/serde) support for all types.
//!
//! - `serde-human-readable` (_implicitly enables `serde`, `formatting`, and `parsing`_)
//!
//!   Allows serde representations to use a human-readable format. This is determined by the
//!   serializer, not the user. If this feature is not enabled or if the serializer requests a
//!   non-human-readable format, a format optimized for binary representation will be used.
//!
//! - `rand`
//!
//!   Enables [rand](https://docs.rs/rand) support for all types.
//!
//! - `quickcheck` (_implicitly enables `alloc`_)
//!
//!   Enables [quickcheck](https://docs.rs/quickcheck) support for all types except [`Instant`].
//!
//! One pseudo-feature flag that is only available to end users is the `unsound_local_offset` cfg.
//! As the name indicates, using the feature is unsound, and [may cause unexpected segmentation
//! faults](https://github.com/time-rs/time/issues/293). Unlike other flags, this is deliberately
//! only available to end users; this is to ensure that a user doesn't have unsound behavior without
//! knowing it. To enable this behavior, you must use `RUSTFLAGS="--cfg unsound_local_offset" cargo
//! build` or similar. Note: This flag is _not tested anywhere_, including in the regular test of
//! the powerset of all feature flags. Use at your own risk. Without this flag, any method that
//! requires the local offset will return the `Err` variant.

#![doc(html_playground_url = "https://play.rust-lang.org")]
#![cfg_attr(__time_03_docs, feature(doc_cfg))]
#![cfg_attr(
    __time_03_docs,
    deny(rustdoc::broken_intra_doc_links, rustdoc::private_intra_doc_links)
)]
#![cfg_attr(not(feature = "std"), no_std)]
#![deny(
    anonymous_parameters,
    clippy::all,
    const_err,
    illegal_floating_point_literal_pattern,
    late_bound_lifetime_arguments,
    path_statements,
    patterns_in_fns_without_body,
    rust_2018_idioms,
    trivial_casts,
    trivial_numeric_casts,
    unreachable_pub,
    unsafe_code,
    unused_extern_crates
)]
#![warn(
    clippy::dbg_macro,
    clippy::decimal_literal_representation,
    clippy::get_unwrap,
    clippy::missing_docs_in_private_items,
    clippy::nursery,
    clippy::pedantic,
    clippy::print_stdout,
    clippy::todo,
    clippy::unimplemented,
    clippy::unwrap_in_result,
    clippy::unwrap_used,
    clippy::use_debug,
    missing_copy_implementations,
    missing_debug_implementations,
    unused_qualifications,
    variant_size_differences
)]
#![allow(
    clippy::cast_lossless,
    clippy::cast_possible_truncation,
    clippy::cast_possible_wrap,
    clippy::cast_precision_loss,
    clippy::cast_sign_loss,
    clippy::enum_glob_use,
    clippy::map_err_ignore,
    clippy::missing_errors_doc,
    clippy::must_use_candidate,
    clippy::redundant_pub_crate,
    unstable_name_collisions
)]
#![doc(html_favicon_url = "https://avatars0.githubusercontent.com/u/55999857")]
#![doc(html_logo_url = "https://avatars0.githubusercontent.com/u/55999857")]
#![doc(test(attr(deny(warnings))))]

#[allow(unused_extern_crates)]
#[cfg(feature = "alloc")]
extern crate alloc;

// region: macros
macro_rules! __impl_assign {
    ($sym:tt $op:ident $fn:ident $target:ty : $($(#[$attr:meta])* $t:ty),+) => {$(
        #[allow(unused_qualifications)]
        $(#[$attr])*
        impl core::ops::$op<$t> for $target {
            fn $fn(&mut self, rhs: $t) {
                *self = *self $sym rhs;
            }
        }
    )+};
}

/// Implement `AddAssign` for the provided types.
macro_rules! impl_add_assign {
    ($target:ty : $($(#[$attr:meta])* $t:ty),+ $(,)?) => {
        __impl_assign!(+ AddAssign add_assign $target : $($(#[$attr])* $t),+);
    };
}

/// Implement `SubAssign` for the provided types.
macro_rules! impl_sub_assign {
    ($target:ty : $($(#[$attr:meta])* $t:ty),+ $(,)?) => {
        __impl_assign!(- SubAssign sub_assign $target : $($(#[$attr])* $t),+);
    };
}

/// Implement `MulAssign` for the provided types.
macro_rules! impl_mul_assign {
    ($target:ty : $($(#[$attr:meta])* $t:ty),+ $(,)?) => {
        __impl_assign!(* MulAssign mul_assign $target : $($(#[$attr])* $t),+);
    };
}

/// Implement `DivAssign` for the provided types.
macro_rules! impl_div_assign {
    ($target:ty : $($(#[$attr:meta])* $t:ty),+ $(,)?) => {
        __impl_assign!(/ DivAssign div_assign $target : $($(#[$attr])* $t),+);
    };
}

/// Division of integers, rounding the resulting value towards negative infinity.
macro_rules! div_floor {
    ($a:expr, $b:expr) => {{
        let _a = $a;
        let _b = $b;

        let (_quotient, _remainder) = (_a / _b, _a % _b);

        if (_remainder > 0 && _b < 0) || (_remainder < 0 && _b > 0) {
            _quotient - 1
        } else {
            _quotient
        }
    }};
}

/// Euclidean remainder. Useful for `const` contexts.
macro_rules! rem_euclid {
    ($a:expr, $b:expr) => {{
        let _a = $a;
        let _b = $b;
        let r = _a % _b;
        if r < 0 {
            if _b < 0 { r - _b } else { r + _b }
        } else {
            r
        }
    }};
}

/// Cascade an out-of-bounds value.
macro_rules! cascade {
    (@ordinal ordinal) => {};
    (@year year) => {};

    // Cascade an out-of-bounds value from "from" to "to".
    ($(!$from_not_mut:ident)? $from:ident in $min:literal.. $max:literal => $to:tt) => {
        #[allow(unused_comparisons, unused_assignments)]
        if $from >= $max {
            $from -= $max - $min;
            $to += 1;
        } else if $from < $min {
            $from += $max - $min;
            $to -= 1;
        }
    };

    // Special case the ordinal-to-year cascade, as it has different behavior.
    ($ordinal:ident => $year:ident) => {
        // We need to actually capture the idents. Without this, macro hygiene causes errors.
        cascade!(@ordinal $ordinal);
        cascade!(@year $year);
        #[allow(unused_assignments)]
        if $ordinal > crate::util::days_in_year($year) {
            $year += 1;
            $ordinal = 1;
        } else if $ordinal == 0 {
            $year -= 1;
            $ordinal = crate::util::days_in_year($year);
        }
    };
}

/// Returns `Err(error::ComponentRange)` if the value is not in range.
macro_rules! ensure_value_in_range {
    ($value:ident in $start:expr => $end:expr) => {{
        let _start = $start;
        let _end = $end;
        #[allow(trivial_numeric_casts, unused_comparisons)]
        if $value < _start || $value > _end {
            return Err(crate::error::ComponentRange {
                name: stringify!($value),
                minimum: _start as _,
                maximum: _end as _,
                value: $value as _,
                conditional_range: false,
            });
        }
    }};

    ($value:ident conditionally in $start:expr => $end:expr) => {{
        let _start = $start;
        let _end = $end;
        #[allow(trivial_numeric_casts, unused_comparisons)]
        if $value < _start || $value > _end {
            return Err(crate::error::ComponentRange {
                name: stringify!($value),
                minimum: _start as _,
                maximum: _end as _,
                value: $value as _,
                conditional_range: true,
            });
        }
    }};
}

/// Try to unwrap an expression, returning if not possible.
///
/// This is similar to the `?` operator, but does not perform `.into()`. Because of this, it is
/// usable in `const` contexts.
macro_rules! const_try {
    ($e:expr) => {
        match $e {
            Ok(value) => value,
            Err(error) => return Err(error),
        }
    };
}

/// Try to unwrap an expression, returning if not possible.
///
/// This is similar to the `?` operator, but is usable in `const` contexts.
macro_rules! const_try_opt {
    ($e:expr) => {
        match $e {
            Some(value) => value,
            None => return None,
        }
    };
}
// endregion macros

/// The [`Date`] struct and its associated `impl`s.
mod date;
/// The [`Duration`] struct and its associated `impl`s.
mod duration;
/// Various error types returned by methods in the time crate.
pub mod error;
/// Extension traits.
pub mod ext;
#[cfg(any(feature = "formatting", feature = "parsing"))]
#[cfg_attr(
    __time_03_docs,
    doc(cfg(any(feature = "formatting", feature = "parsing")))
)]
pub mod format_description;
#[cfg(feature = "formatting")]
#[cfg_attr(__time_03_docs, doc(cfg(feature = "formatting")))]
pub mod formatting;
/// The [`Instant`] struct and its associated `impl`s.
#[cfg(feature = "std")]
#[cfg_attr(__time_03_docs, doc(cfg(feature = "std")))]
mod instant;
/// Macros to construct statically known values.
#[cfg(feature = "macros")]
#[cfg_attr(__time_03_docs, doc(cfg(feature = "macros")))]
pub mod macros;
mod month;
/// The [`OffsetDateTime`] struct and its associated `impl`s.
mod offset_date_time;
#[cfg(feature = "parsing")]
#[cfg_attr(__time_03_docs, doc(cfg(feature = "parsing")))]
pub mod parsing;
/// The [`PrimitiveDateTime`] struct and its associated `impl`s.
mod primitive_date_time;
#[cfg(feature = "quickcheck")]
#[cfg_attr(__time_03_docs, doc(cfg(feature = "quickcheck")))]
mod quickcheck;
#[cfg(feature = "rand")]
#[cfg_attr(__time_03_docs, doc(cfg(feature = "rand")))]
mod rand;
#[cfg(feature = "serde")]
#[cfg_attr(__time_03_docs, doc(cfg(feature = "serde")))]
#[allow(missing_copy_implementations, missing_debug_implementations)]
pub mod serde;
#[cfg(test)]
mod tests;
/// The [`Time`] struct and its associated `impl`s.
mod time;
/// The [`UtcOffset`] struct and its associated `impl`s.
mod utc_offset;
pub mod util;
/// Days of the week.
mod weekday;

pub use crate::date::Date;
pub use crate::duration::Duration;
pub use crate::error::Error;
#[cfg(feature = "std")]
pub use crate::instant::Instant;
pub use crate::month::Month;
pub use crate::offset_date_time::OffsetDateTime;
pub use crate::primitive_date_time::PrimitiveDateTime;
pub use crate::time::Time;
pub use crate::utc_offset::UtcOffset;
pub use crate::weekday::Weekday;

/// An alias for [`std::result::Result`] with a generic error from the time crate.
pub type Result<T> = core::result::Result<T, Error>;
<commit_msg>Remove outdated info<commit_after>//! # Feature flags
//!
//! This crate exposes a number of features. These can be enabled or disabled as shown
//! [in Cargo's documentation](https://doc.rust-lang.org/cargo/reference/features.html). Features
//! are _disabled_ by default unless otherwise noted.
//!
//! Reliance on a given feature is always indicated alongside the item definition.
//!
//! - `std` (_enabled by default, implicitly enables `alloc`_)
//!
//!   This enables a number of features that depend on the standard library.
//!
//! - `alloc` (_enabled by default via `std`_)
//!
//!   Enables a number of features that require the ability to dynamically allocate memory.
//!
//! - `macros`
//!
//!   Enables macros that provide compile-time verification of values and intuitive syntax.
//!
//! - `formatting` (_implicitly enables `std`_)
//!
//!   Enables formatting of most structs.
//!
//! - `parsing`
//!
//!   Enables parsing of most structs.
//!
//! - `local-offset` (_implicitly enables `std`_)
//!
//!   This feature enables a number of methods that allow obtaining the system's UTC offset.
//!
//! - `large-dates`
//!
//!   By default, only years within the ±9999 range (inclusive) are supported. If you need support
//!   for years outside this range, consider enabling this feature; the supported range will be
//!   increased to ±999,999.
//!
//!   Note that enabling this feature has some costs, as it means forgoing some optimizations.
//!   Ambiguities may be introduced when parsing that would not otherwise exist.
//!
//!   If you are using this feature, **please leave a comment**
//!   [on this discussion](https://github.com/time-rs/time/discussions/306) with your use case. If
//!   there is not sufficient demand for this feature, it will be dropped in a future release.
//!
//! - `serde`
//!
//!   Enables [serde](https://docs.rs/serde) support for all types.
//!
//! - `serde-human-readable` (_implicitly enables `serde`, `formatting`, and `parsing`_)
//!
//!   Allows serde representations to use a human-readable format. This is determined by the
//!   serializer, not the user. If this feature is not enabled or if the serializer requests a
//!   non-human-readable format, a format optimized for binary representation will be used.
//!
//! - `rand`
//!
//!   Enables [rand](https://docs.rs/rand) support for all types.
//!
//! - `quickcheck` (_implicitly enables `alloc`_)
//!
//!   Enables [quickcheck](https://docs.rs/quickcheck) support for all types except [`Instant`].
//!
//! One pseudo-feature flag that is only available to end users is the `unsound_local_offset` cfg.
//! As the name indicates, using the feature is unsound, and [may cause unexpected segmentation
//! faults](https://github.com/time-rs/time/issues/293). Unlike other flags, this is deliberately
//! only available to end users; this is to ensure that a user doesn't have unsound behavior without
//! knowing it. To enable this behavior, you must use `RUSTFLAGS="--cfg unsound_local_offset" cargo
//! build` or similar. Note: This flag is _not tested anywhere_, including in the regular test of
//! the powerset of all feature flags. Use at your own risk. Without this flag, any method that
//! requires the local offset will return the `Err` variant.

#![doc(html_playground_url = "https://play.rust-lang.org")]
#![cfg_attr(__time_03_docs, feature(doc_cfg))]
#![cfg_attr(
    __time_03_docs,
    deny(rustdoc::broken_intra_doc_links, rustdoc::private_intra_doc_links)
)]
#![cfg_attr(not(feature = "std"), no_std)]
#![deny(
    anonymous_parameters,
    clippy::all,
    const_err,
    illegal_floating_point_literal_pattern,
    late_bound_lifetime_arguments,
    path_statements,
    patterns_in_fns_without_body,
    rust_2018_idioms,
    trivial_casts,
    trivial_numeric_casts,
    unreachable_pub,
    unsafe_code,
    unused_extern_crates
)]
#![warn(
    clippy::dbg_macro,
    clippy::decimal_literal_representation,
    clippy::get_unwrap,
    clippy::missing_docs_in_private_items,
    clippy::nursery,
    clippy::pedantic,
    clippy::print_stdout,
    clippy::todo,
    clippy::unimplemented,
    clippy::unwrap_in_result,
    clippy::unwrap_used,
    clippy::use_debug,
    missing_copy_implementations,
    missing_debug_implementations,
    unused_qualifications,
    variant_size_differences
)]
#![allow(
    clippy::cast_lossless,
    clippy::cast_possible_truncation,
    clippy::cast_possible_wrap,
    clippy::cast_precision_loss,
    clippy::cast_sign_loss,
    clippy::enum_glob_use,
    clippy::map_err_ignore,
    clippy::missing_errors_doc,
    clippy::must_use_candidate,
    clippy::redundant_pub_crate,
    unstable_name_collisions
)]
#![doc(html_favicon_url = "https://avatars0.githubusercontent.com/u/55999857")]
#![doc(html_logo_url = "https://avatars0.githubusercontent.com/u/55999857")]
#![doc(test(attr(deny(warnings))))]

#[allow(unused_extern_crates)]
#[cfg(feature = "alloc")]
extern crate alloc;

// region: macros
macro_rules! __impl_assign {
    ($sym:tt $op:ident $fn:ident $target:ty : $($(#[$attr:meta])* $t:ty),+) => {$(
        #[allow(unused_qualifications)]
        $(#[$attr])*
        impl core::ops::$op<$t> for $target {
            fn $fn(&mut self, rhs: $t) {
                *self = *self $sym rhs;
            }
        }
    )+};
}

/// Implement `AddAssign` for the provided types.
macro_rules! impl_add_assign {
    ($target:ty : $($(#[$attr:meta])* $t:ty),+ $(,)?) => {
        __impl_assign!(+ AddAssign add_assign $target : $($(#[$attr])* $t),+);
    };
}

/// Implement `SubAssign` for the provided types.
macro_rules! impl_sub_assign {
    ($target:ty : $($(#[$attr:meta])* $t:ty),+ $(,)?) => {
        __impl_assign!(- SubAssign sub_assign $target : $($(#[$attr])* $t),+);
    };
}

/// Implement `MulAssign` for the provided types.
macro_rules! impl_mul_assign {
    ($target:ty : $($(#[$attr:meta])* $t:ty),+ $(,)?) => {
        __impl_assign!(* MulAssign mul_assign $target : $($(#[$attr])* $t),+);
    };
}

/// Implement `DivAssign` for the provided types.
macro_rules! impl_div_assign {
    ($target:ty : $($(#[$attr:meta])* $t:ty),+ $(,)?) => {
        __impl_assign!(/ DivAssign div_assign $target : $($(#[$attr])* $t),+);
    };
}

/// Division of integers, rounding the resulting value towards negative infinity.
macro_rules! div_floor {
    ($a:expr, $b:expr) => {{
        let _a = $a;
        let _b = $b;

        let (_quotient, _remainder) = (_a / _b, _a % _b);

        if (_remainder > 0 && _b < 0) || (_remainder < 0 && _b > 0) {
            _quotient - 1
        } else {
            _quotient
        }
    }};
}

/// Euclidean remainder. Useful for `const` contexts.
macro_rules! rem_euclid {
    ($a:expr, $b:expr) => {{
        let _a = $a;
        let _b = $b;
        let r = _a % _b;
        if r < 0 {
            if _b < 0 { r - _b } else { r + _b }
        } else {
            r
        }
    }};
}

/// Cascade an out-of-bounds value.
macro_rules! cascade {
    (@ordinal ordinal) => {};
    (@year year) => {};

    // Cascade an out-of-bounds value from "from" to "to".
    ($(!$from_not_mut:ident)? $from:ident in $min:literal.. $max:literal => $to:tt) => {
        #[allow(unused_comparisons, unused_assignments)]
        if $from >= $max {
            $from -= $max - $min;
            $to += 1;
        } else if $from < $min {
            $from += $max - $min;
            $to -= 1;
        }
    };

    // Special case the ordinal-to-year cascade, as it has different behavior.
    ($ordinal:ident => $year:ident) => {
        // We need to actually capture the idents. Without this, macro hygiene causes errors.
        cascade!(@ordinal $ordinal);
        cascade!(@year $year);
        #[allow(unused_assignments)]
        if $ordinal > crate::util::days_in_year($year) {
            $year += 1;
            $ordinal = 1;
        } else if $ordinal == 0 {
            $year -= 1;
            $ordinal = crate::util::days_in_year($year);
        }
    };
}

/// Returns `Err(error::ComponentRange)` if the value is not in range.
macro_rules! ensure_value_in_range {
    ($value:ident in $start:expr => $end:expr) => {{
        let _start = $start;
        let _end = $end;
        #[allow(trivial_numeric_casts, unused_comparisons)]
        if $value < _start || $value > _end {
            return Err(crate::error::ComponentRange {
                name: stringify!($value),
                minimum: _start as _,
                maximum: _end as _,
                value: $value as _,
                conditional_range: false,
            });
        }
    }};

    ($value:ident conditionally in $start:expr => $end:expr) => {{
        let _start = $start;
        let _end = $end;
        #[allow(trivial_numeric_casts, unused_comparisons)]
        if $value < _start || $value > _end {
            return Err(crate::error::ComponentRange {
                name: stringify!($value),
                minimum: _start as _,
                maximum: _end as _,
                value: $value as _,
                conditional_range: true,
            });
        }
    }};
}

/// Try to unwrap an expression, returning if not possible.
///
/// This is similar to the `?` operator, but does not perform `.into()`. Because of this, it is
/// usable in `const` contexts.
macro_rules! const_try {
    ($e:expr) => {
        match $e {
            Ok(value) => value,
            Err(error) => return Err(error),
        }
    };
}

/// Try to unwrap an expression, returning if not possible.
///
/// This is similar to the `?` operator, but is usable in `const` contexts.
macro_rules! const_try_opt {
    ($e:expr) => {
        match $e {
            Some(value) => value,
            None => return None,
        }
    };
}
// endregion macros

/// The [`Date`] struct and its associated `impl`s.
mod date;
/// The [`Duration`] struct and its associated `impl`s.
mod duration;
/// Various error types returned by methods in the time crate.
pub mod error;
/// Extension traits.
pub mod ext;
#[cfg(any(feature = "formatting", feature = "parsing"))]
#[cfg_attr(
    __time_03_docs,
    doc(cfg(any(feature = "formatting", feature = "parsing")))
)]
pub mod format_description;
#[cfg(feature = "formatting")]
#[cfg_attr(__time_03_docs, doc(cfg(feature = "formatting")))]
pub mod formatting;
/// The [`Instant`] struct and its associated `impl`s.
#[cfg(feature = "std")]
#[cfg_attr(__time_03_docs, doc(cfg(feature = "std")))]
mod instant;
/// Macros to construct statically known values.
#[cfg(feature = "macros")]
#[cfg_attr(__time_03_docs, doc(cfg(feature = "macros")))]
pub mod macros;
mod month;
/// The [`OffsetDateTime`] struct and its associated `impl`s.
mod offset_date_time;
#[cfg(feature = "parsing")]
#[cfg_attr(__time_03_docs, doc(cfg(feature = "parsing")))]
pub mod parsing;
/// The [`PrimitiveDateTime`] struct and its associated `impl`s.
mod primitive_date_time;
#[cfg(feature = "quickcheck")]
#[cfg_attr(__time_03_docs, doc(cfg(feature = "quickcheck")))]
mod quickcheck;
#[cfg(feature = "rand")]
#[cfg_attr(__time_03_docs, doc(cfg(feature = "rand")))]
mod rand;
#[cfg(feature = "serde")]
#[cfg_attr(__time_03_docs, doc(cfg(feature = "serde")))]
#[allow(missing_copy_implementations, missing_debug_implementations)]
pub mod serde;
#[cfg(test)]
mod tests;
/// The [`Time`] struct and its associated `impl`s.
mod time;
/// The [`UtcOffset`] struct and its associated `impl`s.
mod utc_offset;
pub mod util;
/// Days of the week.
mod weekday;

pub use crate::date::Date;
pub use crate::duration::Duration;
pub use crate::error::Error;
#[cfg(feature = "std")]
pub use crate::instant::Instant;
pub use crate::month::Month;
pub use crate::offset_date_time::OffsetDateTime;
pub use crate::primitive_date_time::PrimitiveDateTime;
pub use crate::time::Time;
pub use crate::utc_offset::UtcOffset;
pub use crate::weekday::Weekday;

/// An alias for [`std::result::Result`] with a generic error from the time crate.
pub type Result<T> = core::result::Result<T, Error>;
<|endoftext|>


<commit_before>pub trait Array<T>
{
    // taken from the slice impl
    fn len(&self) -> usize;
    fn is_empty(&self) -> bool;
    fn first(&self) -> Option<&T>;
    fn first_mut(&mut self) -> Option<&mut T>;
    fn last(&self) -> Option<&T>;
    fn last_mut(&mut self) -> Option<&mut T>;
    fn get(&self, index: usize) -> Option<&T>;
    fn get_mut(&mut self, index: usize) -> Option<&mut T>;
    fn as_ptr(&self) -> *const T;
    fn as_mut_ptr(&mut self) -> *mut T;
    // convenience methods
    fn as_slice(&self) -> &[T];
    fn as_mut_slice(&mut self) -> &mut [T];
    fn map<F>(self, f: F) -> Self where T: Copy, F: FnMut(T) -> T;
    fn fold<A, F>(self, acc: A, f: F) -> A where T: Copy, F: FnMut(A, T) -> A;
}

// for arrays with 1+ elements
macro_rules! impl_array
{
    (@replace $t:tt $sub:expr) => ($sub);

    (@count $($e:expr)*) => { 0usize $(+ impl_array!(@replace $e 1usize))* };

    ($($idx:expr)+) => {
        impl <T> ::Array<T> for [T; impl_array!(@count $($idx)+)]
        {
            fn len(&self) -> usize { impl_array!(@count $($idx)+) }
            fn is_empty(&self) -> bool { false }
            fn first(&self) -> Option<&T> { Some(&self[0]) }
            fn first_mut(&mut self) -> Option<&mut T> { Some(&mut self[0]) }
            fn last(&self) -> Option<&T> { Some(&self[impl_array!(@count $($idx)+) - 1]) }
            fn last_mut(&mut self) -> Option<&mut T> { Some(&mut self[impl_array!(@count $($idx)+) - 1]) }
            fn get(&self, index: usize) -> Option<&T> { if index < impl_array!(@count $($idx)+) { Some(&self[index]) } else { None } }
            fn get_mut(&mut self, index: usize) -> Option<&mut T> { if index < impl_array!(@count $($idx)+) { Some(&mut self[index]) } else { None } }
            fn as_ptr(&self) -> *const T { &self[0] }
            fn as_mut_ptr(&mut self) -> *mut T { &mut self[0] }
            fn as_slice(&self) -> &[T] { self }
            fn as_mut_slice(&mut self) -> &mut [T] { self }
            fn map<F>(self, mut f: F) -> Self where T: Copy, F: FnMut(T) -> T { [$(f(self[$idx])),+] }
            fn fold<A, F>(self, mut acc: A, mut f: F) -> A where T: Copy, F: FnMut(A, T) -> A { $(acc = f(acc, self[$idx]);)+ acc }
        }
    };
}

// workaround to not being able to cast `&mut [T; 0]` to `*mut T` directly
fn get_mut_ptr<T>(a: &mut [T; 0]) -> *mut T { a.as_mut_ptr() }

// special case for the empty array
impl <T> Array<T> for [T; 0]
{
    fn len(&self) -> usize { 0 }
    fn is_empty(&self) -> bool { true }
    fn first(&self) -> Option<&T> { None }
    fn first_mut(&mut self) -> Option<&mut T> { None }
    fn last(&self) -> Option<&T> { None }
    fn last_mut(&mut self) -> Option<&mut T> { None }
    fn get(&self, _index: usize) -> Option<&T> { None }
    fn get_mut(&mut self, _index: usize) -> Option<&mut T> { None }
    fn as_ptr(&self) -> *const T { self as _ }
    fn as_mut_ptr(&mut self) -> *mut T { get_mut_ptr(self) }
    fn as_slice(&self) -> &[T] { self }
    fn as_mut_slice(&mut self) -> &mut [T] { self }
    fn map<F>(self, _f: F) -> Self where T: Copy, F: FnMut(T) -> T { self }
    fn fold<A, F>(self, acc: A, _f: F) -> A where T: Copy, F: FnMut(A, T) -> A { acc }
}

impl_array!(0);
impl_array!(0 1);
impl_array!(0 1 2);
impl_array!(0 1 2 3);
impl_array!(0 1 2 3 4);
impl_array!(0 1 2 3 4 5);
impl_array!(0 1 2 3 4 5 6);
impl_array!(0 1 2 3 4 5 6 7);
impl_array!(0 1 2 3 4 5 6 7 8);
impl_array!(0 1 2 3 4 5 6 7 8 9);
impl_array!(0 1 2 3 4 5 6 7 8 9 10);
impl_array!(0 1 2 3 4 5 6 7 8 9 10 11);
impl_array!(0 1 2 3 4 5 6 7 8 9 10 11 12);
impl_array!(0 1 2 3 4 5 6 7 8 9 10 11 12 13);
impl_array!(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14);
impl_array!(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15);
impl_array!(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16);

#[cfg(test)]
mod tests
{
    use super::Array;
    use std::ops::Add;

    #[test]
    fn methods()
    {
        let mut arr: [i32; 0] = [];
        assert_eq!(arr.len(), 0);
        assert_eq!(arr.is_empty(), true);
        assert_eq!(arr.first(), None);
        assert_eq!(arr.first_mut(), None);
        assert_eq!(arr.last(), None);
        assert_eq!(arr.last_mut(), None);
        assert_eq!(arr.get(1), None);
        assert_eq!(arr.get_mut(1), None);
        assert_eq!(arr.map(|a| a * 2), []);
        assert_eq!(arr.fold(0, |a, n| a + n), 0);

        let mut arr = [1, 2, 3, 4];
        assert_eq!(arr.len(), 4);
        assert_eq!(arr.is_empty(), false);
        assert_eq!(arr.first(), Some(&1));
        assert_eq!(arr.first_mut(), Some(&mut 1));
        assert_eq!(arr.last(), Some(&4));
        assert_eq!(arr.last_mut(), Some(&mut 4));
        assert_eq!(arr.get(1), Some(&2));
        assert_eq!(arr.get(42), None);
        assert_eq!(arr.get_mut(1), Some(&mut 2));
        assert_eq!(arr.get_mut(42), None);
        assert_eq!(arr.map(|a| a * 2), [2, 4, 6, 8]);
        assert_eq!(arr.fold(0, |a, n| a + n), 10);
    }

    fn sum<T, V>(arr: T, val: V) -> T
        where T: Array<V>, V: Add<Output=V> + Copy
    {
        arr.map(|n| n + val)
    }

    #[test]
    fn generics()
    {
        assert_eq!(sum([], 1), []);
        assert_eq!(sum([1], 10), [11]);
        assert_eq!(sum([1, 2], 20), [21, 22]);
        assert_eq!(sum([1, 2, 3], 30), [31, 32, 33]);
        assert_eq!(sum([1, 2, 3, 4], 40), [41, 42, 43, 44]);
    }

    #[test]
    fn slice()
    {
        assert_eq!([1, 2, 3].as_slice(), &[1, 2, 3]);
        assert_eq!([4, 5, 6].as_mut_slice(), &mut [4, 5, 6]);

        let mut arr = [1, -5, 8, 42, 33];
        arr.as_mut_slice().sort();
        assert_eq!(arr, [-5, 1, 8, 33, 42]);
    }
}
<commit_msg>improved the macros<commit_after>pub trait Array<T>
{
    // taken from the slice impl
    fn len(&self) -> usize;
    fn is_empty(&self) -> bool;
    fn first(&self) -> Option<&T>;
    fn first_mut(&mut self) -> Option<&mut T>;
    fn last(&self) -> Option<&T>;
    fn last_mut(&mut self) -> Option<&mut T>;
    fn get(&self, index: usize) -> Option<&T>;
    fn get_mut(&mut self, index: usize) -> Option<&mut T>;
    fn as_ptr(&self) -> *const T;
    fn as_mut_ptr(&mut self) -> *mut T;
    // convenience methods
    fn as_slice(&self) -> &[T];
    fn as_mut_slice(&mut self) -> &mut [T];
    fn map<F>(self, f: F) -> Self where T: Copy, F: FnMut(T) -> T;
    fn fold<A, F>(self, acc: A, f: F) -> A where T: Copy, F: FnMut(A, T) -> A;
}

// for arrays with 1+ elements
macro_rules! impl_array
{
    (@do_impl $count:tt $($idx:tt)+) => {
        impl<T> $crate::Array<T> for [T; $count]
        {
            fn len(&self) -> usize { $count }
            fn is_empty(&self) -> bool { false }
            fn first(&self) -> Option<&T> { Some(&self[0]) }
            fn first_mut(&mut self) -> Option<&mut T> { Some(&mut self[0]) }
            fn last(&self) -> Option<&T> { Some(&self[$count - 1]) }
            fn last_mut(&mut self) -> Option<&mut T> { Some(&mut self[$count - 1]) }
            fn get(&self, index: usize) -> Option<&T> { if index < $count { Some(&self[index]) } else { None } }
            fn get_mut(&mut self, index: usize) -> Option<&mut T> { if index < $count { Some(&mut self[index]) } else { None } }
            fn as_ptr(&self) -> *const T { &self[0] }
            fn as_mut_ptr(&mut self) -> *mut T { &mut self[0] }
            fn as_slice(&self) -> &[T] { self }
            fn as_mut_slice(&mut self) -> &mut [T] { self }
            fn map<F>(self, mut f: F) -> Self where T: Copy, F: FnMut(T) -> T { [$( f(self[$count - $idx - 1]) ),+] }
            fn fold<A, F>(self, mut acc: A, mut f: F) -> A where T: Copy, F: FnMut(A, T) -> A { $( acc = f(acc, self[$count - $idx - 1]); )+ acc }
        }
    };

    ($count:tt $idx:tt) => { impl_array!(@do_impl $count $idx); };
    ($head:tt $($tail:tt)+) => { impl_array!(@do_impl $head $($tail)+); impl_array!($($tail)+); };
}

// implement sizes from 32 to 1
impl_array!(32 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0);

// special case for the empty array
impl<T> Array<T> for [T; 0]
{
    fn len(&self) -> usize { 0 }
    fn is_empty(&self) -> bool { true }
    fn first(&self) -> Option<&T> { None }
    fn first_mut(&mut self) -> Option<&mut T> { None }
    fn last(&self) -> Option<&T> { None }
    fn last_mut(&mut self) -> Option<&mut T> { None }
    fn get(&self, _index: usize) -> Option<&T> { None }
    fn get_mut(&mut self, _index: usize) -> Option<&mut T> { None }
    fn as_ptr(&self) -> *const T { self as _ }
    fn as_mut_ptr(&mut self) -> *mut T { get_mut_ptr(self) }
    fn as_slice(&self) -> &[T] { self }
    fn as_mut_slice(&mut self) -> &mut [T] { self }
    fn map<F>(self, _f: F) -> Self where T: Copy, F: FnMut(T) -> T { self }
    fn fold<A, F>(self, acc: A, _f: F) -> A where T: Copy, F: FnMut(A, T) -> A { acc }
}

// workaround to not being able to cast `&mut [T; 0]` to `*mut T` directly
fn get_mut_ptr<T>(a: &mut [T; 0]) -> *mut T { a.as_mut_ptr() }

#[cfg(test)]
mod tests
{
    use super::Array;
    use std::ops::Add;

    #[test]
    fn methods()
    {
        let mut arr: [i32; 0] = [];
        assert_eq!(arr.len(), 0);
        assert_eq!(arr.is_empty(), true);
        assert_eq!(arr.first(), None);
        assert_eq!(arr.first_mut(), None);
        assert_eq!(arr.last(), None);
        assert_eq!(arr.last_mut(), None);
        assert_eq!(arr.get(1), None);
        assert_eq!(arr.get_mut(1), None);
        assert_eq!(arr.map(|a| a * 2), []);
        assert_eq!(arr.fold(0, |a, n| a + n), 0);

        let mut arr = [1, 2, 3, 4];
        assert_eq!(arr.len(), 4);
        assert_eq!(arr.is_empty(), false);
        assert_eq!(arr.first(), Some(&1));
        assert_eq!(arr.first_mut(), Some(&mut 1));
        assert_eq!(arr.last(), Some(&4));
        assert_eq!(arr.last_mut(), Some(&mut 4));
        assert_eq!(arr.get(1), Some(&2));
        assert_eq!(arr.get(42), None);
        assert_eq!(arr.get_mut(1), Some(&mut 2));
        assert_eq!(arr.get_mut(42), None);
        assert_eq!(arr.map(|a| a * 2), [2, 4, 6, 8]);
        assert_eq!(arr.fold(0, |a, n| a + n), 10);
    }

    fn sum<T, V>(arr: T, val: V) -> T
        where T: Array<V>, V: Add<Output=V> + Copy
    {
        arr.map(|n| n + val)
    }

    #[test]
    fn generics()
    {
        assert_eq!(sum([], 1), []);
        assert_eq!(sum([1], 10), [11]);
        assert_eq!(sum([1, 2], 20), [21, 22]);
        assert_eq!(sum([1, 2, 3], 30), [31, 32, 33]);
        assert_eq!(sum([1, 2, 3, 4], 40), [41, 42, 43, 44]);
    }

    #[test]
    fn slice()
    {
        assert_eq!([1, 2, 3].as_slice(), &[1, 2, 3]);
        assert_eq!([4, 5, 6].as_mut_slice(), &mut [4, 5, 6]);

        let mut arr = [1, -5, 8, 42, 33];
        arr.as_mut_slice().sort();
        assert_eq!(arr, [-5, 1, 8, 33, 42]);
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 Corey Farwell
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//	http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

extern crate xml;

use std::ascii::AsciiExt;
use std::io;
use xml::{Element, ElementBuilder, Parser, Xml};

trait ElementUtils {
    fn tag_with_text(&mut self, child_name: &'static str, child_body: &str);
    fn tag_with_optional_text(&mut self, child_name: &'static str, child_body: &Option<String>);
}

impl ElementUtils for Element {
    fn tag_with_text(&mut self, child_name: &'static str, child_body: &str) {
        self.tag(elem_with_text(child_name, child_body));
    }

    fn tag_with_optional_text(&mut self, child_name: &'static str, child_body: &Option<String>) {
        if let Some(ref c) = *child_body {
            self.tag_with_text(child_name, &c);
        }
    }
}

fn elem_with_text(tag_name: &'static str, chars: &str) -> Element {
    let mut elem = Element::new(tag_name.to_string(), None, vec![]);
    elem.text(chars.to_string());
    elem
}

trait ViaXml {
    fn to_xml(&self) -> Element;
    fn from_xml(element: Element) -> Result<Self, &'static str>;
}

/// RSS
///
/// "At the top level, a RSS document is a \<rss\> element, with a mandatory attribute called
/// version, that specifies the version of RSS that the document conforms to. If it conforms to
/// this specification, the version attribute must be 2.0."
///
/// [RSS 2.0 Specification § RSS]
/// (http://cyber.law.harvard.edu/rss/rss.html#whatIsRss)
#[derive(Default)]
pub struct Rss(pub Channel);

impl ViaXml for Rss {
    fn to_xml(&self) -> Element {
        let mut rss = Element::new("rss".to_string(), None, vec![("version".to_string(), None, "2.0".to_string())]);

        let &Rss(ref channel) = self;
        rss.tag(channel.to_xml());

        rss
    }

    fn from_xml(rss_elem: Element) -> Result<Self, &'static str> {
        if rss_elem.name.to_ascii_lowercase() != "rss" {
            panic!("Expected <rss>, found <{}>", rss_elem.name);
        }

        let channel_elem = match rss_elem.get_child("channel", None) {
            Some(elem) => elem,
            None => return Err("No <channel> element found in <rss>"),
        };

        let channel = try!(ViaXml::from_xml(channel_elem.clone()));

        Ok(Rss(channel))
    }
}

impl Rss {
    pub fn to_string(&self) -> String {
        let mut ret = format!("{}", Xml::PINode("xml version='1.0' encoding='UTF-8'".to_string()));
        ret.push_str(&format!("{}", self.to_xml()));
        ret
    }

    pub fn from_reader(reader: &mut io::Read) -> Result<Self, &'static str> {
        let mut rss_string = String::new();

        if let Err(..) = reader.read_to_string(&mut rss_string) {
            return Err("Error reading string from reader");
        }

        let mut parser = Parser::new();
        parser.feed_str(&rss_string);

        let mut builder = ElementBuilder::new();

        for event in parser {
            if let Some(Ok(element)) = builder.handle_event(event) {
                return ViaXml::from_xml(element);
            }
        }

        Err("RSS read error")
    }
}

/// Channel
///
/// "Subordinate to the \<rss\> element is a single \<channel\> element, which contains information
/// about the channel (metadata) and its contents."
///
/// [RSS 2.0 Specification § Channel]
/// (http://cyber.law.harvard.edu/rss/rss.html#requiredChannelElements)
///
/// ## Examples
///
/// ```
/// use rss::Channel;
/// use std::default::Default;
///
/// let channel = Channel {
///     title: "My Blog".to_string(),
///     link: "http://myblog.com".to_string(),
///     description: "Where I write stuff".to_string(),
///     items: vec![],
///     ..Default::default()
/// };
/// ```
#[derive(Default)]
pub struct Channel {
    pub title: String,
    pub link: String,
    pub description: String,
    pub items: Vec<Item>,
    pub language: Option<String>,
    pub copyright: Option<String>,
    pub managing_editor: Option<String>,
    pub web_master: Option<String>,
    pub pub_date: Option<String>,
    pub last_build_date: Option<String>,
    pub categories: Vec<Category>,
    pub generator: Option<String>,
    pub docs: Option<String>,
    // pub cloud:
    pub ttl: Option<String>,  // TODO: change this to Option<i32>?
    pub image: Option<String>,
    pub rating: Option<String>,
    // pub text_input:
    pub skip_hours: Option<String>,
    pub skip_days: Option<String>,
}

impl ViaXml for Channel {
    fn to_xml(&self) -> Element {
        let mut channel = Element::new("channel".to_string(), None, vec![]);

        channel.tag_with_text("title", &self.title);
        channel.tag_with_text("link", &self.link);
        channel.tag_with_text("description", &self.description);

        for item in &self.items {
            channel.tag(item.to_xml());
        }

        channel.tag_with_optional_text("language", &self.language);
        channel.tag_with_optional_text("copyright", &self.copyright);
        channel.tag_with_optional_text("managingEditor", &self.managing_editor);
        channel.tag_with_optional_text("webMaster", &self.web_master);
        channel.tag_with_optional_text("pubDate", &self.pub_date);
        channel.tag_with_optional_text("lastBuildDate", &self.last_build_date);
        channel.tag_with_optional_text("generator", &self.generator);
        channel.tag_with_optional_text("docs", &self.docs);
        channel.tag_with_optional_text("ttl", &self.ttl);
        channel.tag_with_optional_text("image", &self.image);
        channel.tag_with_optional_text("rating", &self.rating);
        channel.tag_with_optional_text("skipHours", &self.skip_hours);
        channel.tag_with_optional_text("skipDays", &self.skip_days);

        for category in &self.categories {
            channel.tag(category.to_xml());
        }

        channel
    }

    fn from_xml(element: Element) -> Result<Self, &'static str> {
        let title = match element.get_child("title", None) {
            Some(element) => element.content_str(),
            None => return Err("<channel> is missing required <title> element"),
        };

        let link = match element.get_child("link", None) {
            Some(element) => element.content_str(),
            None => return Err("<channel> is missing required <link> element"),
        };

        let description = match element.get_child("description", None) {
            Some(element) => element.content_str(),
            None => return Err("<channel> is missing required <description> element"),
        };

        let items = element.get_children("item", None)
            .map(|e| ViaXml::from_xml(e.clone()).unwrap())
            .collect();

        let language = element.get_child("language", None).map(Element::content_str);
        let copyright = element.get_child("copyright", None).map(Element::content_str);
        let managing_editor = element.get_child("managing_editor", None).map(Element::content_str);
        let web_master = element.get_child("managing_editor", None).map(Element::content_str);
        let pub_date = element.get_child("pub_date", None).map(Element::content_str);
        let last_build_date = element.get_child("last_build_date", None).map(Element::content_str);

        let categories = element.get_children("category", None)
            .map(|e| ViaXml::from_xml(e.clone()).unwrap())
            .collect();

        let generator = element.get_child("generator", None).map(Element::content_str);
        let docs = element.get_child("docs", None).map(Element::content_str);
        let ttl = element.get_child("ttl", None).map(Element::content_str);
        let image = element.get_child("image", None).map(Element::content_str);
        let rating = element.get_child("rating", None).map(Element::content_str);
        let skip_hours = element.get_child("skip_hours", None).map(Element::content_str);
        let skip_days = element.get_child("skip_days", None).map(Element::content_str);

        Ok(Channel {
            title: title,
            link: link,
            description: description,
            items: items,
            language: language,
            copyright: copyright,
            managing_editor: managing_editor,
            web_master: web_master,
            pub_date: pub_date,
            last_build_date: last_build_date,
            categories: categories,
            generator: generator,
            docs: docs,
            ttl: ttl,
            image: image,
            rating: rating,
            skip_hours: skip_hours,
            skip_days: skip_days,
        })
    }
}

/// Item
///
/// [RSS 2.0 Specification § Item]
/// (http://cyber.law.harvard.edu/rss/rss.html#hrelementsOfLtitemgt)
#[derive(Default)]
pub struct Item {
    pub title: Option<String>,
    pub link: Option<String>,
    pub description: Option<String>,
    pub author: Option<String>,
    pub categories: Vec<Category>,
    pub comments: Option<String>,
    // pub enclosure
    // pub guid
    // pub pubDate
    // pub source
}

impl ViaXml for Item {
    fn to_xml(&self) -> Element {
        let mut item = Element::new("item".to_string(), None, vec![]);

        item.tag_with_optional_text("title", &self.title);
        item.tag_with_optional_text("link", &self.link);
        item.tag_with_optional_text("description", &self.description);
        item.tag_with_optional_text("author", &self.author);
        item.tag_with_optional_text("comments", &self.comments);

        for category in &self.categories {
            item.tag(category.to_xml());
        }

        item
    }

    fn from_xml(element: Element) -> Result<Self, &'static str> {
        let title = element.get_child("title", None).map(Element::content_str);
        let link = element.get_child("link", None).map(Element::content_str);
        let description = element.get_child("description", None).map(Element::content_str);
        let author = element.get_child("author", None).map(Element::content_str);
        let comments = element.get_child("comments", None).map(Element::content_str);

        let categories = element.get_children("category", None)
            .map(|e| ViaXml::from_xml(e.clone()).unwrap())
            .collect();

        Ok(Item {
            title: title,
            link: link,
            description: description,
            categories: categories,
            author: author,
            comments: comments,
        })
    }
}

/// Category
///
/// [RSS 2.0 Specification § Category]
/// (http://cyber.law.harvard.edu/rss/rss.html#ltcategorygtSubelementOfLtitemgt)
#[derive(Default)]
pub struct Category {
    pub domain: Option<String>,
    pub value: String,
}

impl ViaXml for Category {
    fn to_xml(&self) -> Element {
        let mut category = match self.domain {
            Some(ref d) => Element::new("category".to_string(), None, vec![("domain".to_string(), None, d.clone())]),
            None => Element::new("category".to_string(), None, vec![]),
        };
        category.text(self.value.clone());
        category
    }

    fn from_xml(elem: Element) -> Result<Self, &'static str> {
        let domain = elem.get_attribute("domain", None).map(|s| s.to_string());
        let value = elem.content_str();

        Ok(Category {
            domain: domain,
            value: value,
        })
    }
}

#[cfg(test)]
mod test {
    use std::default::Default;
    use std::fs::File;
    use super::{Rss, Item, Channel};

    #[test]
    fn test_basic_to_string() {
        let item = Item {
            title: Some("My first post!".to_string()),
            link: Some("http://myblog.com/post1".to_string()),
            description: Some("This is my first post".to_string()),
            ..Default::default()
        };

        let channel = Channel {
            title: "My Blog".to_string(),
            link: "http://myblog.com".to_string(),
            description: "Where I write stuff".to_string(),
            items: vec![item],
            ..Default::default()
        };

        let rss = Rss(channel);
        assert_eq!(rss.to_string(), "<?xml version=\'1.0\' encoding=\'UTF-8\'?><rss version=\'2.0\'><channel><title>My Blog</title><link>http://myblog.com</link><description>Where I write stuff</description><item><title>My first post!</title><link>http://myblog.com/post1</link><description>This is my first post</description></item></channel></rss>");
    }

    #[test]
    fn test_from_file() {
        let mut file = File::open("test-data/pinboard.xml").unwrap();
        let rss = Rss::from_reader(&mut file).unwrap();
        assert!(rss.to_string().len() > 0);
    }

    #[test]
    fn test_read_no_channels() {
        let rss_str = "<rss></rss>";
        assert!(Rss::from_reader(&mut rss_str.as_bytes()).is_err());
    }

    #[test]
    fn test_read_one_channel_no_properties() {
        let rss_str = "\
            <rss>\
                <channel>\
                </channel>\
            </rss>";
        assert!(Rss::from_reader(&mut rss_bytes.as_bytes()).is_err());
    }

    #[test]
    fn test_read_one_channel() {
        let rss_str = "\
            <rss>\
                <channel>\
                    <title>Hello world!</title>\
                    <description></description>\
                    <link></link>\
                </channel>\
            </rss>";
        let Rss(channel) = Rss::from_reader(&mut rss_str.as_bytes()).unwrap();
        assert_eq!("Hello world!", channel.title);
    }

    // Ensure reader ignores the PI XML node and continues to parse the RSS
    #[test]
    fn test_read_with_pinode() {
        let rss_str = "\
            <?xml version=\'1.0\' encoding=\'UTF-8\'?>\
            <rss>\
                <channel>\
                    <title>Title</title>\
                    <link></link>\
                    <description></description>\
                </channel>\
            </rss>";
        let Rss(channel) = Rss::from_reader(&mut rss_str.as_bytes()).unwrap();
        assert_eq!("Title", channel.title);
    }
}
<commit_msg>Fix unresolved variable<commit_after>// Copyright 2015 Corey Farwell
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//	http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

extern crate xml;

use std::ascii::AsciiExt;
use std::io;
use xml::{Element, ElementBuilder, Parser, Xml};

trait ElementUtils {
    fn tag_with_text(&mut self, child_name: &'static str, child_body: &str);
    fn tag_with_optional_text(&mut self, child_name: &'static str, child_body: &Option<String>);
}

impl ElementUtils for Element {
    fn tag_with_text(&mut self, child_name: &'static str, child_body: &str) {
        self.tag(elem_with_text(child_name, child_body));
    }

    fn tag_with_optional_text(&mut self, child_name: &'static str, child_body: &Option<String>) {
        if let Some(ref c) = *child_body {
            self.tag_with_text(child_name, &c);
        }
    }
}

fn elem_with_text(tag_name: &'static str, chars: &str) -> Element {
    let mut elem = Element::new(tag_name.to_string(), None, vec![]);
    elem.text(chars.to_string());
    elem
}

trait ViaXml {
    fn to_xml(&self) -> Element;
    fn from_xml(element: Element) -> Result<Self, &'static str>;
}

/// RSS
///
/// "At the top level, a RSS document is a \<rss\> element, with a mandatory attribute called
/// version, that specifies the version of RSS that the document conforms to. If it conforms to
/// this specification, the version attribute must be 2.0."
///
/// [RSS 2.0 Specification § RSS]
/// (http://cyber.law.harvard.edu/rss/rss.html#whatIsRss)
#[derive(Default)]
pub struct Rss(pub Channel);

impl ViaXml for Rss {
    fn to_xml(&self) -> Element {
        let mut rss = Element::new("rss".to_string(), None, vec![("version".to_string(), None, "2.0".to_string())]);

        let &Rss(ref channel) = self;
        rss.tag(channel.to_xml());

        rss
    }

    fn from_xml(rss_elem: Element) -> Result<Self, &'static str> {
        if rss_elem.name.to_ascii_lowercase() != "rss" {
            panic!("Expected <rss>, found <{}>", rss_elem.name);
        }

        let channel_elem = match rss_elem.get_child("channel", None) {
            Some(elem) => elem,
            None => return Err("No <channel> element found in <rss>"),
        };

        let channel = try!(ViaXml::from_xml(channel_elem.clone()));

        Ok(Rss(channel))
    }
}

impl Rss {
    pub fn to_string(&self) -> String {
        let mut ret = format!("{}", Xml::PINode("xml version='1.0' encoding='UTF-8'".to_string()));
        ret.push_str(&format!("{}", self.to_xml()));
        ret
    }

    pub fn from_reader(reader: &mut io::Read) -> Result<Self, &'static str> {
        let mut rss_string = String::new();

        if let Err(..) = reader.read_to_string(&mut rss_string) {
            return Err("Error reading string from reader");
        }

        let mut parser = Parser::new();
        parser.feed_str(&rss_string);

        let mut builder = ElementBuilder::new();

        for event in parser {
            if let Some(Ok(element)) = builder.handle_event(event) {
                return ViaXml::from_xml(element);
            }
        }

        Err("RSS read error")
    }
}

/// Channel
///
/// "Subordinate to the \<rss\> element is a single \<channel\> element, which contains information
/// about the channel (metadata) and its contents."
///
/// [RSS 2.0 Specification § Channel]
/// (http://cyber.law.harvard.edu/rss/rss.html#requiredChannelElements)
///
/// ## Examples
///
/// ```
/// use rss::Channel;
/// use std::default::Default;
///
/// let channel = Channel {
///     title: "My Blog".to_string(),
///     link: "http://myblog.com".to_string(),
///     description: "Where I write stuff".to_string(),
///     items: vec![],
///     ..Default::default()
/// };
/// ```
#[derive(Default)]
pub struct Channel {
    pub title: String,
    pub link: String,
    pub description: String,
    pub items: Vec<Item>,
    pub language: Option<String>,
    pub copyright: Option<String>,
    pub managing_editor: Option<String>,
    pub web_master: Option<String>,
    pub pub_date: Option<String>,
    pub last_build_date: Option<String>,
    pub categories: Vec<Category>,
    pub generator: Option<String>,
    pub docs: Option<String>,
    // pub cloud:
    pub ttl: Option<String>,  // TODO: change this to Option<i32>?
    pub image: Option<String>,
    pub rating: Option<String>,
    // pub text_input:
    pub skip_hours: Option<String>,
    pub skip_days: Option<String>,
}

impl ViaXml for Channel {
    fn to_xml(&self) -> Element {
        let mut channel = Element::new("channel".to_string(), None, vec![]);

        channel.tag_with_text("title", &self.title);
        channel.tag_with_text("link", &self.link);
        channel.tag_with_text("description", &self.description);

        for item in &self.items {
            channel.tag(item.to_xml());
        }

        channel.tag_with_optional_text("language", &self.language);
        channel.tag_with_optional_text("copyright", &self.copyright);
        channel.tag_with_optional_text("managingEditor", &self.managing_editor);
        channel.tag_with_optional_text("webMaster", &self.web_master);
        channel.tag_with_optional_text("pubDate", &self.pub_date);
        channel.tag_with_optional_text("lastBuildDate", &self.last_build_date);
        channel.tag_with_optional_text("generator", &self.generator);
        channel.tag_with_optional_text("docs", &self.docs);
        channel.tag_with_optional_text("ttl", &self.ttl);
        channel.tag_with_optional_text("image", &self.image);
        channel.tag_with_optional_text("rating", &self.rating);
        channel.tag_with_optional_text("skipHours", &self.skip_hours);
        channel.tag_with_optional_text("skipDays", &self.skip_days);

        for category in &self.categories {
            channel.tag(category.to_xml());
        }

        channel
    }

    fn from_xml(element: Element) -> Result<Self, &'static str> {
        let title = match element.get_child("title", None) {
            Some(element) => element.content_str(),
            None => return Err("<channel> is missing required <title> element"),
        };

        let link = match element.get_child("link", None) {
            Some(element) => element.content_str(),
            None => return Err("<channel> is missing required <link> element"),
        };

        let description = match element.get_child("description", None) {
            Some(element) => element.content_str(),
            None => return Err("<channel> is missing required <description> element"),
        };

        let items = element.get_children("item", None)
            .map(|e| ViaXml::from_xml(e.clone()).unwrap())
            .collect();

        let language = element.get_child("language", None).map(Element::content_str);
        let copyright = element.get_child("copyright", None).map(Element::content_str);
        let managing_editor = element.get_child("managing_editor", None).map(Element::content_str);
        let web_master = element.get_child("managing_editor", None).map(Element::content_str);
        let pub_date = element.get_child("pub_date", None).map(Element::content_str);
        let last_build_date = element.get_child("last_build_date", None).map(Element::content_str);

        let categories = element.get_children("category", None)
            .map(|e| ViaXml::from_xml(e.clone()).unwrap())
            .collect();

        let generator = element.get_child("generator", None).map(Element::content_str);
        let docs = element.get_child("docs", None).map(Element::content_str);
        let ttl = element.get_child("ttl", None).map(Element::content_str);
        let image = element.get_child("image", None).map(Element::content_str);
        let rating = element.get_child("rating", None).map(Element::content_str);
        let skip_hours = element.get_child("skip_hours", None).map(Element::content_str);
        let skip_days = element.get_child("skip_days", None).map(Element::content_str);

        Ok(Channel {
            title: title,
            link: link,
            description: description,
            items: items,
            language: language,
            copyright: copyright,
            managing_editor: managing_editor,
            web_master: web_master,
            pub_date: pub_date,
            last_build_date: last_build_date,
            categories: categories,
            generator: generator,
            docs: docs,
            ttl: ttl,
            image: image,
            rating: rating,
            skip_hours: skip_hours,
            skip_days: skip_days,
        })
    }
}

/// Item
///
/// [RSS 2.0 Specification § Item]
/// (http://cyber.law.harvard.edu/rss/rss.html#hrelementsOfLtitemgt)
#[derive(Default)]
pub struct Item {
    pub title: Option<String>,
    pub link: Option<String>,
    pub description: Option<String>,
    pub author: Option<String>,
    pub categories: Vec<Category>,
    pub comments: Option<String>,
    // pub enclosure
    // pub guid
    // pub pubDate
    // pub source
}

impl ViaXml for Item {
    fn to_xml(&self) -> Element {
        let mut item = Element::new("item".to_string(), None, vec![]);

        item.tag_with_optional_text("title", &self.title);
        item.tag_with_optional_text("link", &self.link);
        item.tag_with_optional_text("description", &self.description);
        item.tag_with_optional_text("author", &self.author);
        item.tag_with_optional_text("comments", &self.comments);

        for category in &self.categories {
            item.tag(category.to_xml());
        }

        item
    }

    fn from_xml(element: Element) -> Result<Self, &'static str> {
        let title = element.get_child("title", None).map(Element::content_str);
        let link = element.get_child("link", None).map(Element::content_str);
        let description = element.get_child("description", None).map(Element::content_str);
        let author = element.get_child("author", None).map(Element::content_str);
        let comments = element.get_child("comments", None).map(Element::content_str);

        let categories = element.get_children("category", None)
            .map(|e| ViaXml::from_xml(e.clone()).unwrap())
            .collect();

        Ok(Item {
            title: title,
            link: link,
            description: description,
            categories: categories,
            author: author,
            comments: comments,
        })
    }
}

/// Category
///
/// [RSS 2.0 Specification § Category]
/// (http://cyber.law.harvard.edu/rss/rss.html#ltcategorygtSubelementOfLtitemgt)
#[derive(Default)]
pub struct Category {
    pub domain: Option<String>,
    pub value: String,
}

impl ViaXml for Category {
    fn to_xml(&self) -> Element {
        let mut category = match self.domain {
            Some(ref d) => Element::new("category".to_string(), None, vec![("domain".to_string(), None, d.clone())]),
            None => Element::new("category".to_string(), None, vec![]),
        };
        category.text(self.value.clone());
        category
    }

    fn from_xml(elem: Element) -> Result<Self, &'static str> {
        let domain = elem.get_attribute("domain", None).map(|s| s.to_string());
        let value = elem.content_str();

        Ok(Category {
            domain: domain,
            value: value,
        })
    }
}

#[cfg(test)]
mod test {
    use std::default::Default;
    use std::fs::File;
    use super::{Rss, Item, Channel};

    #[test]
    fn test_basic_to_string() {
        let item = Item {
            title: Some("My first post!".to_string()),
            link: Some("http://myblog.com/post1".to_string()),
            description: Some("This is my first post".to_string()),
            ..Default::default()
        };

        let channel = Channel {
            title: "My Blog".to_string(),
            link: "http://myblog.com".to_string(),
            description: "Where I write stuff".to_string(),
            items: vec![item],
            ..Default::default()
        };

        let rss = Rss(channel);
        assert_eq!(rss.to_string(), "<?xml version=\'1.0\' encoding=\'UTF-8\'?><rss version=\'2.0\'><channel><title>My Blog</title><link>http://myblog.com</link><description>Where I write stuff</description><item><title>My first post!</title><link>http://myblog.com/post1</link><description>This is my first post</description></item></channel></rss>");
    }

    #[test]
    fn test_from_file() {
        let mut file = File::open("test-data/pinboard.xml").unwrap();
        let rss = Rss::from_reader(&mut file).unwrap();
        assert!(rss.to_string().len() > 0);
    }

    #[test]
    fn test_read_no_channels() {
        let rss_str = "<rss></rss>";
        assert!(Rss::from_reader(&mut rss_str.as_bytes()).is_err());
    }

    #[test]
    fn test_read_one_channel_no_properties() {
        let rss_str = "\
            <rss>\
                <channel>\
                </channel>\
            </rss>";
        assert!(Rss::from_reader(&mut rss_str.as_bytes()).is_err());
    }

    #[test]
    fn test_read_one_channel() {
        let rss_str = "\
            <rss>\
                <channel>\
                    <title>Hello world!</title>\
                    <description></description>\
                    <link></link>\
                </channel>\
            </rss>";
        let Rss(channel) = Rss::from_reader(&mut rss_str.as_bytes()).unwrap();
        assert_eq!("Hello world!", channel.title);
    }

    // Ensure reader ignores the PI XML node and continues to parse the RSS
    #[test]
    fn test_read_with_pinode() {
        let rss_str = "\
            <?xml version=\'1.0\' encoding=\'UTF-8\'?>\
            <rss>\
                <channel>\
                    <title>Title</title>\
                    <link></link>\
                    <description></description>\
                </channel>\
            </rss>";
        let Rss(channel) = Rss::from_reader(&mut rss_str.as_bytes()).unwrap();
        assert_eq!("Title", channel.title);
    }
}
<|endoftext|>