<commit_before>// Copyright 2016 PingCAP, Inc.
//
// 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,
// See the License for the specific language governing permissions and
// limitations under the License.

use std::fmt::{self, Formatter, Display};
use std::error;
use std::fs::File;
use std::sync::Arc;
use std::sync::mpsc::SyncSender;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::time::Instant;
use std::str;

use rocksdb::{DB, Writable, WriteBatch};
use kvproto::raft_serverpb::{RaftApplyState, RegionLocalState, PeerState};
use kvproto::eraftpb::Snapshot as RaftSnapshot;

use util::worker::Runnable;
use util::codec::bytes::CompactBytesDecoder;
use util::{escape, HandyRwLock, rocksdb};
use raftstore::store::engine::{Mutable, Snapshot, Iterable};
use raftstore::store::peer_storage::{JOB_STATUS_FINISHED, JOB_STATUS_CANCELLED, JOB_STATUS_FAILED,
                                     JOB_STATUS_CANCELLING, JOB_STATUS_PENDING, JOB_STATUS_RUNNING};
use raftstore::store::{self, SnapManager, SnapKey, SnapEntry, keys, Peekable};
use storage::CF_RAFT;

use super::metrics::*;

/// region related task.
pub enum Task {
    Gen {
        region_id: u64,
        notifier: SyncSender<RaftSnapshot>,
    },
    Apply {
        region_id: u64,
        status: Arc<AtomicUsize>,
    },
    /// Destroy data between [start_key, end_key).
    ///
    /// The deletion may and may not succeed.
    Destroy {
        region_id: u64,
        start_key: Vec<u8>,
        end_key: Vec<u8>,
    },
}

impl Task {
    pub fn destroy(region_id: u64, start_key: Vec<u8>, end_key: Vec<u8>) -> Task {
        Task::Destroy {
            region_id: region_id,
            start_key: start_key,
            end_key: end_key,
        }
    }
}

impl Display for Task {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        match *self {
            Task::Gen { region_id, .. } => write!(f, "Snap gen for {}", region_id),
            Task::Apply { region_id, .. } => write!(f, "Snap apply for {}", region_id),
            Task::Destroy { region_id, ref start_key, ref end_key } => {
                write!(f,
                       "Destroy {} [{}, {})",
                       region_id,
                       escape(&start_key),
                       escape(&end_key))
            }
        }
    }
}

quick_error! {
    #[derive(Debug)]
    enum Error {
        Abort {
            description("abort")
            display("abort")
        }
        Other(err: Box<error::Error + Sync + Send>) {
            from()
            cause(err.as_ref())
            description(err.description())
            display("snap failed {:?}", err)
        }
    }
}

#[inline]
fn check_abort(status: &AtomicUsize) -> Result<(), Error> {
    if status.load(Ordering::Relaxed) == JOB_STATUS_CANCELLING {
        return Err(Error::Abort);
    }
    Ok(())
}

// TODO: use threadpool to do task concurrently
pub struct Runner {
    db: Arc<DB>,
    batch_size: usize,
    mgr: SnapManager,
}

impl Runner {
    pub fn new(db: Arc<DB>, mgr: SnapManager, batch_size: usize) -> Runner {
        Runner {
            db: db,
            mgr: mgr,
            batch_size: batch_size,
        }
    }

    fn generate_snap(&self,
                     region_id: u64,
                     notifier: SyncSender<RaftSnapshot>)
                     -> Result<(), Error> {
        // do we need to check leader here?
        let raw_snap = Snapshot::new(self.db.clone());

        let snap = box_try!(store::do_snapshot(self.mgr.clone(), &raw_snap, region_id));
        if let Err(e) = notifier.try_send(snap) {
            info!("[region {}] failed to notify snap result, maybe leadership has changed, \
                   ignore: {:?}",
                  region_id,
                  e);
        }
        Ok(())
    }

    fn handle_gen(&self, region_id: u64, notifier: SyncSender<RaftSnapshot>) {
        SNAP_COUNTER_VEC.with_label_values(&["generate", "all"]).inc();
        let gen_histogram = SNAP_HISTOGRAM.with_label_values(&["generate"]);
        let timer = gen_histogram.start_timer();

        if let Err(e) = self.generate_snap(region_id, notifier) {
            error!("[region {}] failed to generate snap: {:?}!!!", region_id, e);
            return;
        }

        SNAP_COUNTER_VEC.with_label_values(&["generate", "success"]).inc();
        timer.observe_duration();
    }

    fn delete_all_in_range(&self,
                           start_key: &[u8],
                           end_key: &[u8],
                           abort: &AtomicUsize)
                           -> Result<(), Error> {
        let mut wb = WriteBatch::new();
        let mut size_cnt = 0;
        for cf in self.db.cf_names() {
            try!(check_abort(&abort));
            let handle = box_try!(rocksdb::get_cf_handle(&self.db, cf));

            let mut it = box_try!(self.db.new_iterator_cf(cf, Some(end_key), false));

            try!(check_abort(&abort));
            it.seek(start_key.into());
            while it.valid() {
                {
                    let key = it.key();
                    if key >= end_key {
                        break;
                    }

                    box_try!(wb.delete_cf(handle, key));
                    size_cnt += key.len();
                    if size_cnt >= self.batch_size {
                        // Can't use write_without_wal here.
                        // Otherwise it may cause dirty data when applying snapshot.
                        box_try!(self.db.write(wb));
                        wb = WriteBatch::new();
                    }
                };
                try!(check_abort(&abort));
                if !it.next() {
                    break;
                }
            }
        }

        if wb.count() > 0 {
            box_try!(self.db.write(wb));
        }
        Ok(())
    }

    fn apply_snap(&self, region_id: u64, abort: Arc<AtomicUsize>) -> Result<(), Error> {
        info!("[region {}] begin apply snap data", region_id);
        try!(check_abort(&abort));
        let region_key = keys::region_state_key(region_id);
        let mut region_state: RegionLocalState = match box_try!(self.db.get_msg(&region_key)) {
            Some(state) => state,
            None => return Err(box_err!("failed to get region_state from {}", escape(&region_key))),
        };

        // clear up origin data.
        let start_key = keys::enc_start_key(region_state.get_region());
        let end_key = keys::enc_end_key(region_state.get_region());
        box_try!(self.delete_all_in_range(&start_key, &end_key, &abort));

        let state_key = keys::apply_state_key(region_id);
        let apply_state: RaftApplyState = match box_try!(self.db.get_msg_cf(CF_RAFT, &state_key)) {
            Some(state) => state,
            None => return Err(box_err!("failed to get raftstate from {}", escape(&state_key))),
        };
        let term = apply_state.get_truncated_state().get_term();
        let idx = apply_state.get_truncated_state().get_index();
        let snap_key = SnapKey::new(region_id, term, idx);
        let snap_file = box_try!(self.mgr.rl().get_snap_file(&snap_key, false));
        self.mgr.wl().register(snap_key.clone(), SnapEntry::Applying);
        defer!({
            self.mgr.wl().deregister(&snap_key, &SnapEntry::Applying);
        });
        if !snap_file.exists() {
            return Err(box_err!("missing snap file {}", snap_file.path().display()));
        }
        try!(check_abort(&abort));
        box_try!(snap_file.validate());
        let mut reader = box_try!(File::open(snap_file.path()));

        let timer = Instant::now();
        // Write the snapshot into the region.
        loop {
            // TODO: avoid too many allocation
            try!(check_abort(&abort));
            let cf = box_try!(reader.decode_compact_bytes());
            if cf.is_empty() {
                break;
            }
            let handle = box_try!(rocksdb::get_cf_handle(&self.db,
                                                         unsafe { str::from_utf8_unchecked(&cf) }));
            let mut wb = WriteBatch::new();
            let mut batch_size = 0;
            loop {
                try!(check_abort(&abort));
                let key = box_try!(reader.decode_compact_bytes());
                if key.is_empty() {
                    box_try!(self.db.write(wb));
                    break;
                }
                batch_size += key.len();
                let value = box_try!(reader.decode_compact_bytes());
                batch_size += value.len();
                box_try!(wb.put_cf(handle, &key, &value));
                if batch_size >= self.batch_size {
                    box_try!(self.db.write(wb));
                    wb = WriteBatch::new();
                    batch_size = 0;
                }
            }
        }

        region_state.set_state(PeerState::Normal);
        box_try!(self.db.put_msg(&region_key, &region_state));
        snap_file.delete();
        info!("[region {}] apply new data takes {:?}",
              region_id,
              timer.elapsed());
        Ok(())
    }

    fn handle_apply(&self, region_id: u64, status: Arc<AtomicUsize>) {
        status.compare_and_swap(JOB_STATUS_PENDING, JOB_STATUS_RUNNING, Ordering::SeqCst);
        SNAP_COUNTER_VEC.with_label_values(&["apply", "all"]).inc();
        let apply_histogram = SNAP_HISTOGRAM.with_label_values(&["apply"]);
        let timer = apply_histogram.start_timer();

        match self.apply_snap(region_id, status.clone()) {
            Ok(()) => {
                status.swap(JOB_STATUS_FINISHED, Ordering::SeqCst);
                SNAP_COUNTER_VEC.with_label_values(&["apply", "success"]).inc();
            }
            Err(Error::Abort) => {
                warn!("applying snapshot for region {} is aborted.", region_id);
                assert!(status.swap(JOB_STATUS_CANCELLED, Ordering::SeqCst) ==
                        JOB_STATUS_CANCELLING);
                SNAP_COUNTER_VEC.with_label_values(&["apply", "abort"]).inc();
            }
            Err(e) => {
                error!("failed to apply snap: {:?}!!!", e);
                status.swap(JOB_STATUS_FAILED, Ordering::SeqCst);
                SNAP_COUNTER_VEC.with_label_values(&["apply", "fail"]).inc();
            }
        }

        timer.observe_duration();
    }

    fn handle_destroy(&mut self, region_id: u64, start_key: Vec<u8>, end_key: Vec<u8>) {
        info!("[region {}] deleting data in [{}, {})",
              region_id,
              escape(&start_key),
              escape(&end_key));
        let status = AtomicUsize::new(JOB_STATUS_PENDING);
        if let Err(e) = self.delete_all_in_range(&start_key, &end_key, &status) {
            error!("failed to delete data in [{}, {}): {:?}",
                   escape(&start_key),
                   escape(&end_key),
                   e);
        }
    }
}

impl Runnable<Task> for Runner {
    fn run(&mut self, task: Task) {
        match task {
            Task::Gen { region_id, notifier } => self.handle_gen(region_id, notifier),
            Task::Apply { region_id, status } => self.handle_apply(region_id, status),
            Task::Destroy { region_id, start_key, end_key } => {
                self.handle_destroy(region_id, start_key, end_key)
            }
        }
    }
}
<commit_msg>worker: clean up size_cnt  after write. (#1399)<commit_after>// Copyright 2016 PingCAP, Inc.
//
// 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,
// See the License for the specific language governing permissions and
// limitations under the License.

use std::fmt::{self, Formatter, Display};
use std::error;
use std::fs::File;
use std::sync::Arc;
use std::sync::mpsc::SyncSender;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::time::Instant;
use std::str;

use rocksdb::{DB, Writable, WriteBatch};
use kvproto::raft_serverpb::{RaftApplyState, RegionLocalState, PeerState};
use kvproto::eraftpb::Snapshot as RaftSnapshot;

use util::worker::Runnable;
use util::codec::bytes::CompactBytesDecoder;
use util::{escape, HandyRwLock, rocksdb};
use raftstore::store::engine::{Mutable, Snapshot, Iterable};
use raftstore::store::peer_storage::{JOB_STATUS_FINISHED, JOB_STATUS_CANCELLED, JOB_STATUS_FAILED,
                                     JOB_STATUS_CANCELLING, JOB_STATUS_PENDING, JOB_STATUS_RUNNING};
use raftstore::store::{self, SnapManager, SnapKey, SnapEntry, keys, Peekable};
use storage::CF_RAFT;

use super::metrics::*;

/// region related task.
pub enum Task {
    Gen {
        region_id: u64,
        notifier: SyncSender<RaftSnapshot>,
    },
    Apply {
        region_id: u64,
        status: Arc<AtomicUsize>,
    },
    /// Destroy data between [start_key, end_key).
    ///
    /// The deletion may and may not succeed.
    Destroy {
        region_id: u64,
        start_key: Vec<u8>,
        end_key: Vec<u8>,
    },
}

impl Task {
    pub fn destroy(region_id: u64, start_key: Vec<u8>, end_key: Vec<u8>) -> Task {
        Task::Destroy {
            region_id: region_id,
            start_key: start_key,
            end_key: end_key,
        }
    }
}

impl Display for Task {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        match *self {
            Task::Gen { region_id, .. } => write!(f, "Snap gen for {}", region_id),
            Task::Apply { region_id, .. } => write!(f, "Snap apply for {}", region_id),
            Task::Destroy { region_id, ref start_key, ref end_key } => {
                write!(f,
                       "Destroy {} [{}, {})",
                       region_id,
                       escape(&start_key),
                       escape(&end_key))
            }
        }
    }
}

quick_error! {
    #[derive(Debug)]
    enum Error {
        Abort {
            description("abort")
            display("abort")
        }
        Other(err: Box<error::Error + Sync + Send>) {
            from()
            cause(err.as_ref())
            description(err.description())
            display("snap failed {:?}", err)
        }
    }
}

#[inline]
fn check_abort(status: &AtomicUsize) -> Result<(), Error> {
    if status.load(Ordering::Relaxed) == JOB_STATUS_CANCELLING {
        return Err(Error::Abort);
    }
    Ok(())
}

// TODO: use threadpool to do task concurrently
pub struct Runner {
    db: Arc<DB>,
    batch_size: usize,
    mgr: SnapManager,
}

impl Runner {
    pub fn new(db: Arc<DB>, mgr: SnapManager, batch_size: usize) -> Runner {
        Runner {
            db: db,
            mgr: mgr,
            batch_size: batch_size,
        }
    }

    fn generate_snap(&self,
                     region_id: u64,
                     notifier: SyncSender<RaftSnapshot>)
                     -> Result<(), Error> {
        // do we need to check leader here?
        let raw_snap = Snapshot::new(self.db.clone());

        let snap = box_try!(store::do_snapshot(self.mgr.clone(), &raw_snap, region_id));
        if let Err(e) = notifier.try_send(snap) {
            info!("[region {}] failed to notify snap result, maybe leadership has changed, \
                   ignore: {:?}",
                  region_id,
                  e);
        }
        Ok(())
    }

    fn handle_gen(&self, region_id: u64, notifier: SyncSender<RaftSnapshot>) {
        SNAP_COUNTER_VEC.with_label_values(&["generate", "all"]).inc();
        let gen_histogram = SNAP_HISTOGRAM.with_label_values(&["generate"]);
        let timer = gen_histogram.start_timer();

        if let Err(e) = self.generate_snap(region_id, notifier) {
            error!("[region {}] failed to generate snap: {:?}!!!", region_id, e);
            return;
        }

        SNAP_COUNTER_VEC.with_label_values(&["generate", "success"]).inc();
        timer.observe_duration();
    }

    fn delete_all_in_range(&self,
                           start_key: &[u8],
                           end_key: &[u8],
                           abort: &AtomicUsize)
                           -> Result<(), Error> {
        let mut wb = WriteBatch::new();
        let mut size_cnt = 0;
        for cf in self.db.cf_names() {
            try!(check_abort(&abort));
            let handle = box_try!(rocksdb::get_cf_handle(&self.db, cf));

            let mut it = box_try!(self.db.new_iterator_cf(cf, Some(end_key), false));

            try!(check_abort(&abort));
            it.seek(start_key.into());
            while it.valid() {
                {
                    let key = it.key();
                    if key >= end_key {
                        break;
                    }

                    box_try!(wb.delete_cf(handle, key));
                    size_cnt += key.len();
                    if size_cnt >= self.batch_size {
                        // Can't use write_without_wal here.
                        // Otherwise it may cause dirty data when applying snapshot.
                        box_try!(self.db.write(wb));
                        wb = WriteBatch::new();
                        size_cnt = 0;
                    }
                };
                try!(check_abort(&abort));
                if !it.next() {
                    break;
                }
            }
        }

        if wb.count() > 0 {
            box_try!(self.db.write(wb));
        }
        Ok(())
    }

    fn apply_snap(&self, region_id: u64, abort: Arc<AtomicUsize>) -> Result<(), Error> {
        info!("[region {}] begin apply snap data", region_id);
        try!(check_abort(&abort));
        let region_key = keys::region_state_key(region_id);
        let mut region_state: RegionLocalState = match box_try!(self.db.get_msg(&region_key)) {
            Some(state) => state,
            None => return Err(box_err!("failed to get region_state from {}", escape(&region_key))),
        };

        // clear up origin data.
        let start_key = keys::enc_start_key(region_state.get_region());
        let end_key = keys::enc_end_key(region_state.get_region());
        box_try!(self.delete_all_in_range(&start_key, &end_key, &abort));

        let state_key = keys::apply_state_key(region_id);
        let apply_state: RaftApplyState = match box_try!(self.db.get_msg_cf(CF_RAFT, &state_key)) {
            Some(state) => state,
            None => return Err(box_err!("failed to get raftstate from {}", escape(&state_key))),
        };
        let term = apply_state.get_truncated_state().get_term();
        let idx = apply_state.get_truncated_state().get_index();
        let snap_key = SnapKey::new(region_id, term, idx);
        let snap_file = box_try!(self.mgr.rl().get_snap_file(&snap_key, false));
        self.mgr.wl().register(snap_key.clone(), SnapEntry::Applying);
        defer!({
            self.mgr.wl().deregister(&snap_key, &SnapEntry::Applying);
        });
        if !snap_file.exists() {
            return Err(box_err!("missing snap file {}", snap_file.path().display()));
        }
        try!(check_abort(&abort));
        box_try!(snap_file.validate());
        let mut reader = box_try!(File::open(snap_file.path()));

        let timer = Instant::now();
        // Write the snapshot into the region.
        loop {
            // TODO: avoid too many allocation
            try!(check_abort(&abort));
            let cf = box_try!(reader.decode_compact_bytes());
            if cf.is_empty() {
                break;
            }
            let handle = box_try!(rocksdb::get_cf_handle(&self.db,
                                                         unsafe { str::from_utf8_unchecked(&cf) }));
            let mut wb = WriteBatch::new();
            let mut batch_size = 0;
            loop {
                try!(check_abort(&abort));
                let key = box_try!(reader.decode_compact_bytes());
                if key.is_empty() {
                    box_try!(self.db.write(wb));
                    break;
                }
                batch_size += key.len();
                let value = box_try!(reader.decode_compact_bytes());
                batch_size += value.len();
                box_try!(wb.put_cf(handle, &key, &value));
                if batch_size >= self.batch_size {
                    box_try!(self.db.write(wb));
                    wb = WriteBatch::new();
                    batch_size = 0;
                }
            }
        }

        region_state.set_state(PeerState::Normal);
        box_try!(self.db.put_msg(&region_key, &region_state));
        snap_file.delete();
        info!("[region {}] apply new data takes {:?}",
              region_id,
              timer.elapsed());
        Ok(())
    }

    fn handle_apply(&self, region_id: u64, status: Arc<AtomicUsize>) {
        status.compare_and_swap(JOB_STATUS_PENDING, JOB_STATUS_RUNNING, Ordering::SeqCst);
        SNAP_COUNTER_VEC.with_label_values(&["apply", "all"]).inc();
        let apply_histogram = SNAP_HISTOGRAM.with_label_values(&["apply"]);
        let timer = apply_histogram.start_timer();

        match self.apply_snap(region_id, status.clone()) {
            Ok(()) => {
                status.swap(JOB_STATUS_FINISHED, Ordering::SeqCst);
                SNAP_COUNTER_VEC.with_label_values(&["apply", "success"]).inc();
            }
            Err(Error::Abort) => {
                warn!("applying snapshot for region {} is aborted.", region_id);
                assert!(status.swap(JOB_STATUS_CANCELLED, Ordering::SeqCst) ==
                        JOB_STATUS_CANCELLING);
                SNAP_COUNTER_VEC.with_label_values(&["apply", "abort"]).inc();
            }
            Err(e) => {
                error!("failed to apply snap: {:?}!!!", e);
                status.swap(JOB_STATUS_FAILED, Ordering::SeqCst);
                SNAP_COUNTER_VEC.with_label_values(&["apply", "fail"]).inc();
            }
        }

        timer.observe_duration();
    }

    fn handle_destroy(&mut self, region_id: u64, start_key: Vec<u8>, end_key: Vec<u8>) {
        info!("[region {}] deleting data in [{}, {})",
              region_id,
              escape(&start_key),
              escape(&end_key));
        let status = AtomicUsize::new(JOB_STATUS_PENDING);
        if let Err(e) = self.delete_all_in_range(&start_key, &end_key, &status) {
            error!("failed to delete data in [{}, {}): {:?}",
                   escape(&start_key),
                   escape(&end_key),
                   e);
        }
    }
}

impl Runnable<Task> for Runner {
    fn run(&mut self, task: Task) {
        match task {
            Task::Gen { region_id, notifier } => self.handle_gen(region_id, notifier),
            Task::Apply { region_id, status } => self.handle_apply(region_id, status),
            Task::Destroy { region_id, start_key, end_key } => {
                self.handle_destroy(region_id, start_key, end_key)
            }
        }
    }
}
<|endoftext|>


<commit_before>use cortex_m;
use stm32f0x2::{USART1 as UART1, USART3 as UART3, GPIOA, GPIOB, NVIC, RCC};
use stm32f0x2::interrupt::Interrupt::{USART1, USART3_4};

const FREQUENCY: u32 = 48000000;

pub enum Uarts {
    Uart1,
    Uart3,
}

pub enum NBits {
    _8bits,
    _9bits,
}

pub enum StopBits {
    _0b5,
    _1b,
    _1b5,
    _2b,
}

pub enum Parity {
    /// No Parity
    None,
    /// Even Parity
    Even,
    /// Odd Parity
    Odd,
}

pub struct Uart {
    uart: Uarts,
}

impl Uart {
    pub fn setup(
        uart: Uarts,
        baudrate: u32,
        nbits: NBits,
        nbstopbits: StopBits,
        parity: Parity,
    ) -> Uart {
        match uart {
            Uarts::Uart1 => {
                cortex_m::interrupt::free(|cs| {
                    let rcc = RCC.borrow(cs);
                    let nvic = NVIC.borrow(cs);
                    let gpioa = GPIOA.borrow(cs);
                    let uart1 = UART1.borrow(cs);
                    // Enable GPIOA Clock into the Advanced High-performance Bus
                    rcc.ahbenr.modify(|_, w| w.iopaen().enabled());
                    // Enable USART1 Clock  into the Advanced Peripheral Bus 2
                    rcc.apb2enr.modify(|_, w| w.usart1en().enabled());

                    // Configure speed of PA9/PA10 (refer to the datasheet for the frequency, the power supply and load conditions)
                    gpioa
                        .ospeedr
                        .modify(|_, w| w.ospeedr9().high_speed().ospeedr10().high_speed());
                    // Use pull-up on PA9/10
                    gpioa
                        .pupdr
                        .modify(|_, w| w.pupdr9().pull_up().pupdr10().pull_up());
                    // Configure PA9/PA10 Alternate Function 1 -> USART1 (PA9 -> TX & PA10 -> RX )
                    gpioa.afrh.modify(|_, w| w.afrh9().af1().afrh10().af1());
                    // PA9 & PA10 as Alternate function (USART1 peripheral)
                    gpioa
                        .moder
                        .modify(|_, w| w.moder9().alternate().moder10().alternate());
                    // PA9 & PA10 push-pull configuration
                    gpioa
                        .otyper
                        .modify(|_, w| w.ot9().push_pull().ot10().push_pull());

                    // Configure UART1 : Word length
                    match nbits {
                        NBits::_8bits => {
                            uart1.cr1.modify(|_, w| w.m()._8bits());
                        }
                        NBits::_9bits => {
                            uart1.cr1.modify(|_, w| w.m()._9bits());
                        }
                    }
                    // Configure UART1 : Parity
                    match parity {
                        Parity::None => {
                            uart1.cr1.modify(|_, w| w.pce().disabled());
                        }
                        Parity::Even => {
                            uart1.cr1.modify(|_, w| w.pce().enabled());
                            uart1.cr1.modify(|_, w| w.ps().even());
                        }
                        Parity::Odd => {
                            uart1.cr1.modify(|_, w| w.pce().enabled());
                            uart1.cr1.modify(|_, w| w.ps().odd());
                        }
                    }
                    // Configure UART1 : Transfert Direction - Oversampling - RX Interrupt
                    uart1.cr1.modify(|_, w| {
                        w.te()
                            .enabled()
                            .re()
                            .enabled()
                            .over8()
                            .over8()
                            .rxneie()
                            .enabled()
                    });
                    // Configure UART1 : stop bit
                    match nbstopbits {
                        StopBits::_0b5 => {
                            uart1.cr2.modify(|_, w| w.stop().half_stop());
                        }
                        StopBits::_1b => {
                            uart1.cr2.modify(|_, w| w.stop()._1stop());
                        }
                        StopBits::_1b5 => {
                            uart1.cr2.modify(|_, w| w.stop()._1half_stop());
                        }
                        StopBits::_2b => {
                            uart1.cr2.modify(|_, w| w.stop()._2stop());
                        }
                    }

                    // Configure UART1 : disable hardware flow control - Overrun interrupt
                    uart1.cr3.modify(|_, w| {
                        w.rtse()
                            .disabled()
                            .ctse()
                            .disabled()
                            .ctsie()
                            .disabled()
                            .ovrdis()
                            .disabled()
                    });
                    // Configure UART1 : baudrate
                    uart1.brr.modify(|_, w| {
                        w.div_fraction()
                            .bits((FREQUENCY / (baudrate / 2)) as u8 & 0x0F)
                    });
                    uart1.brr.modify(|_, w| {
                        w.div_mantissa()
                            .bits(((FREQUENCY / (baudrate / 2)) >> 4) as u16)
                    });
                    // Configure UART1 : Asynchronous mode
                    uart1
                        .cr2
                        .modify(|_, w| w.linen().disabled().clken().disabled());
                    // UART1 enabled
                    uart1.cr1.modify(|_, w| w.ue().enabled());
                    // Interrupt UART1 activated into NVIC
                    nvic.enable(USART1);
                    nvic.clear_pending(USART1);
                });
                Uart { uart }
            }
            Uarts::Uart3 => {
                cortex_m::interrupt::free(|cs| {
                    let rcc = RCC.borrow(cs);
                    let nvic = NVIC.borrow(cs);
                    let gpiob = GPIOB.borrow(cs);
                    let uart3 = UART3.borrow(cs);
                    // Enable GPIOB Clock into the Advanced High-performance Bus
                    rcc.ahbenr.modify(|_, w| w.iopben().enabled());
                    // Enable USART3 Clock  into the Advanced Peripheral Bus 2
                    rcc.apb1enr.modify(|_, w| w.usart3en().enabled());

                    // Configure speed of PB10/PB11 (refer to the datasheet for the frequency, the power supply and load conditions)
                    gpiob
                        .ospeedr
                        .modify(|_, w| w.ospeedr10().high_speed().ospeedr11().high_speed());
                    // Use pull-up on PB10/11
                    gpiob
                        .pupdr
                        .modify(|_, w| w.pupdr10().pull_up().pupdr11().pull_up());
                    // Configure PB10/PB11 Alternate Function 1 -> USART3 (PB10 -> TX & PB11 -> RX )
                    gpiob.afrh.modify(|_, w| w.afrh10().af4().afrh11().af4());
                    // PA9 & PA10 as Alternate function (USART3 Peripheral)
                    gpiob
                        .moder
                        .modify(|_, w| w.moder10().alternate().moder11().alternate());
                    // PA9 & PA10 push-pull configuration
                    gpiob
                        .otyper
                        .modify(|_, w| w.ot10().push_pull().ot11().push_pull());

                    // Configure UART : Word length
                    match nbits {
                        NBits::_8bits => {
                            uart3.cr1.modify(|_, w| w.m()._8bits());
                        }
                        NBits::_9bits => {
                            uart3.cr1.modify(|_, w| w.m()._9bits());
                        }
                    }
                    // Configure UART3 : Parity
                    match parity {
                        Parity::None => {
                            uart3.cr1.modify(|_, w| w.pce().disabled());
                        }
                        Parity::Even => {
                            uart3.cr1.modify(|_, w| w.pce().enabled());
                            uart3.cr1.modify(|_, w| w.ps().even());
                        }
                        Parity::Odd => {
                            uart3.cr1.modify(|_, w| w.pce().enabled());
                            uart3.cr1.modify(|_, w| w.ps().odd());
                        }
                    }
                    // Configure UART3 : Transfert Direction - Oversampling - RX Interrupt
                    uart3.cr1.modify(|_, w| {
                        w.te()
                            .enabled()
                            .re()
                            .enabled()
                            .over8()
                            .over8()
                            .rxneie()
                            .enabled()
                    });
                    // Configure UART3 : 1 stop bit
                    match nbstopbits {
                        StopBits::_0b5 => {
                            uart3.cr2.modify(|_, w| w.stop().half_stop());
                        }
                        StopBits::_1b => {
                            uart3.cr2.modify(|_, w| w.stop()._1stop());
                        }
                        StopBits::_1b5 => {
                            uart3.cr2.modify(|_, w| w.stop()._1half_stop());
                        }
                        StopBits::_2b => {
                            uart3.cr2.modify(|_, w| w.stop()._2stop());
                        }
                    }

                    // Configure UART3 : disable hardware flow control - Overrun interrupt
                    uart3.cr3.modify(|_, w| {
                        w.rtse()
                            .disabled()
                            .ctse()
                            .disabled()
                            .ctsie()
                            .disabled()
                            .ovrdis()
                            .disabled()
                    });
                    // Configure UART3 : baudrate
                    uart3.brr.modify(|_, w| {
                        w.div_fraction()
                            .bits((FREQUENCY / (baudrate / 2)) as u8 & 0x0F)
                    });
                    uart3.brr.modify(|_, w| {
                        w.div_mantissa()
                            .bits(((FREQUENCY / (baudrate / 2)) >> 4) as u16)
                    });
                    // Configure UART3 : Asynchronous mode
                    uart3
                        .cr2
                        .modify(|_, w| w.linen().disabled().clken().disabled());
                    // UART3 enabled
                    uart3.cr1.modify(|_, w| w.ue().enabled());
                    // Interrupt UART3 (and USART4) activated into NVIC
                    nvic.enable(USART3_4);
                    nvic.clear_pending(USART3_4);
                });
                Uart { uart }
            }
        }
    }

    pub fn send(&self, byte: u8) {
        match self.uart {
            Uarts::Uart1 => cortex_m::interrupt::free(|cs| {
                let uart1 = UART1.borrow(cs);
                // the byte is stored in the UART1 transmit register
                uart1.tdr.write(|w| w.tdr().bits(byte as u16));
            }),
            Uarts::Uart3 => cortex_m::interrupt::free(|cs| {
                let uart3 = UART3.borrow(cs);
                // the byte is stored in the UART3 transmit register
                uart3.tdr.write(|w| w.tdr().bits(byte as u16));
            }),
        }
    }

    pub fn transmit_complete(&self) -> bool {
        match self.uart {
            Uarts::Uart1 => cortex_m::interrupt::free(|cs| {
                let uart1 = UART1.borrow(cs);
                // Check the Transmit Completed flag status
                if uart1.isr.read().tc().bit_is_set() {
                    // if transmit completed, clear the transmit completed flag and return true
                    uart1.icr.write(|w| w.tccf().clear_bit());
                    true
                } else {
                    false
                }
            }),
            Uarts::Uart3 => cortex_m::interrupt::free(|cs| {
                let uart3 = UART3.borrow(cs);
                // Check the Transmit Completed flag status
                if uart3.isr.read().tc().bit_is_set() {
                    // if transmit completed, clear the transmit completed flag and return true
                    uart3.icr.write(|w| w.tccf().clear_bit());
                    true
                } else {
                    false
                }
            }),
        }
    }

    pub fn read_data(&self) -> Option<u8> {
        match self.uart {
            Uarts::Uart1 => cortex_m::interrupt::free(|cs| {
                let uart1 = UART1.borrow(cs);
                // Check the Receive Not Empty flag flag status
                if uart1.isr.read().rxne().bit_is_set() {
                    // if true, read the received data and finish the rxne flag cleared procedure
                    Some(uart1.rdr.read().rdr().bits() as u8)
                } else {
                    None
                }
            }),
            Uarts::Uart3 => cortex_m::interrupt::free(|cs| {
                let uart3 = UART3.borrow(cs);
                // Check the Receive Not Empty flag flag status
                if uart3.isr.read().rxne().bit_is_set() {
                    // if true, read the received data and finish the rxne flag cleared procedure
                    Some(uart3.rdr.read().rdr().bits() as u8)
                } else {
                    None
                }
            }),
        }
    }
}
<commit_msg>Add support for usart4.<commit_after>use cortex_m;
use stm32f0x2::{USART1 as UART1, USART3 as UART3, USART4 as UART4, GPIOA, GPIOB, NVIC, RCC};
use stm32f0x2::interrupt::Interrupt::{USART1, USART3_4};

const FREQUENCY: u32 = 48_000_000;

pub enum Uarts {
    Uart1,
    Uart3,
    Uart4,
}

pub enum NBits {
    _8bits,
    _9bits,
}

pub enum StopBits {
    _0b5,
    _1b,
    _1b5,
    _2b,
}

pub enum Parity {
    /// No Parity
    None,
    /// Even Parity
    Even,
    /// Odd Parity
    Odd,
}

pub struct Uart {
    uart: Uarts,
}

impl Uart {
    pub fn setup(
        uart: Uarts,
        baudrate: u32,
        nbits: NBits,
        nbstopbits: StopBits,
        parity: Parity,
    ) -> Uart {
        match uart {
            Uarts::Uart1 => {
                cortex_m::interrupt::free(|cs| {
                    let rcc = RCC.borrow(cs);
                    let nvic = NVIC.borrow(cs);
                    let gpioa = GPIOA.borrow(cs);
                    let uart1 = UART1.borrow(cs);
                    // Enable GPIOA Clock into the Advanced High-performance Bus
                    rcc.ahbenr.modify(|_, w| w.iopaen().enabled());
                    // Enable USART1 Clock  into the Advanced Peripheral Bus 2
                    rcc.apb2enr.modify(|_, w| w.usart1en().enabled());

                    // Configure speed of PA9/PA10 (refer to the datasheet for the frequency, the power supply and load conditions)
                    gpioa
                        .ospeedr
                        .modify(|_, w| w.ospeedr9().high_speed().ospeedr10().high_speed());
                    // Use pull-up on PA9/10
                    gpioa
                        .pupdr
                        .modify(|_, w| w.pupdr9().pull_up().pupdr10().pull_up());
                    // Configure PA9/PA10 Alternate Function 1 -> USART1 (PA9 -> TX & PA10 -> RX )
                    gpioa.afrh.modify(|_, w| w.afrh9().af1().afrh10().af1());
                    // PA9 & PA10 as Alternate function (USART1 peripheral)
                    gpioa
                        .moder
                        .modify(|_, w| w.moder9().alternate().moder10().alternate());
                    // PA9 & PA10 push-pull configuration
                    gpioa
                        .otyper
                        .modify(|_, w| w.ot9().push_pull().ot10().push_pull());

                    // Configure UART1 : Word length
                    match nbits {
                        NBits::_8bits => {
                            uart1.cr1.modify(|_, w| w.m()._8bits());
                        }
                        NBits::_9bits => {
                            uart1.cr1.modify(|_, w| w.m()._9bits());
                        }
                    }
                    // Configure UART1 : Parity
                    match parity {
                        Parity::None => {
                            uart1.cr1.modify(|_, w| w.pce().disabled());
                        }
                        Parity::Even => {
                            uart1.cr1.modify(|_, w| w.pce().enabled());
                            uart1.cr1.modify(|_, w| w.ps().even());
                        }
                        Parity::Odd => {
                            uart1.cr1.modify(|_, w| w.pce().enabled());
                            uart1.cr1.modify(|_, w| w.ps().odd());
                        }
                    }
                    // Configure UART1 : Transfert Direction - Oversampling - RX Interrupt
                    uart1.cr1.modify(|_, w| {
                        w.te()
                            .enabled()
                            .re()
                            .enabled()
                            .over8()
                            .over8()
                            .rxneie()
                            .enabled()
                    });
                    // Configure UART1 : stop bit
                    match nbstopbits {
                        StopBits::_0b5 => {
                            uart1.cr2.modify(|_, w| w.stop().half_stop());
                        }
                        StopBits::_1b => {
                            uart1.cr2.modify(|_, w| w.stop()._1stop());
                        }
                        StopBits::_1b5 => {
                            uart1.cr2.modify(|_, w| w.stop()._1half_stop());
                        }
                        StopBits::_2b => {
                            uart1.cr2.modify(|_, w| w.stop()._2stop());
                        }
                    }

                    // Configure UART1 : disable hardware flow control - Overrun interrupt
                    uart1.cr3.modify(|_, w| {
                        w.rtse()
                            .disabled()
                            .ctse()
                            .disabled()
                            .ctsie()
                            .disabled()
                            .ovrdis()
                            .disabled()
                    });
                    // Configure UART1 : baudrate
                    uart1.brr.modify(|_, w| {
                        w.div_fraction()
                            .bits((FREQUENCY / (baudrate / 2)) as u8 & 0x0F)
                    });
                    uart1.brr.modify(|_, w| {
                        w.div_mantissa()
                            .bits(((FREQUENCY / (baudrate / 2)) >> 4) as u16)
                    });
                    // Configure UART1 : Asynchronous mode
                    uart1
                        .cr2
                        .modify(|_, w| w.linen().disabled().clken().disabled());
                    // UART1 enabled
                    uart1.cr1.modify(|_, w| w.ue().enabled());
                    // Interrupt UART1 activated into NVIC
                    nvic.enable(USART1);
                    nvic.clear_pending(USART1);
                });
                Uart { uart }
            }
            Uarts::Uart3 => {
                cortex_m::interrupt::free(|cs| {
                    let rcc = RCC.borrow(cs);
                    let nvic = NVIC.borrow(cs);
                    let gpiob = GPIOB.borrow(cs);
                    let uart3 = UART3.borrow(cs);
                    // Enable GPIOB Clock into the Advanced High-performance Bus
                    rcc.ahbenr.modify(|_, w| w.iopben().enabled());
                    // Enable USART3 Clock  into the Advanced Peripheral Bus 2
                    rcc.apb1enr.modify(|_, w| w.usart3en().enabled());

                    // Configure speed of PB10/PB11 (refer to the datasheet for the frequency, the power supply and load conditions)
                    gpiob
                        .ospeedr
                        .modify(|_, w| w.ospeedr10().high_speed().ospeedr11().high_speed());
                    // Use pull-up on PB10/11
                    gpiob
                        .pupdr
                        .modify(|_, w| w.pupdr10().pull_up().pupdr11().pull_up());
                    // Configure PB10/PB11 Alternate Function 1 -> USART3 (PB10 -> TX & PB11 -> RX )
                    gpiob.afrh.modify(|_, w| w.afrh10().af4().afrh11().af4());
                    // PA9 & PA10 as Alternate function (USART3 Peripheral)
                    gpiob
                        .moder
                        .modify(|_, w| w.moder10().alternate().moder11().alternate());
                    // PA9 & PA10 push-pull configuration
                    gpiob
                        .otyper
                        .modify(|_, w| w.ot10().push_pull().ot11().push_pull());

                    // Configure UART : Word length
                    match nbits {
                        NBits::_8bits => {
                            uart3.cr1.modify(|_, w| w.m()._8bits());
                        }
                        NBits::_9bits => {
                            uart3.cr1.modify(|_, w| w.m()._9bits());
                        }
                    }
                    // Configure UART3 : Parity
                    match parity {
                        Parity::None => {
                            uart3.cr1.modify(|_, w| w.pce().disabled());
                        }
                        Parity::Even => {
                            uart3.cr1.modify(|_, w| w.pce().enabled());
                            uart3.cr1.modify(|_, w| w.ps().even());
                        }
                        Parity::Odd => {
                            uart3.cr1.modify(|_, w| w.pce().enabled());
                            uart3.cr1.modify(|_, w| w.ps().odd());
                        }
                    }
                    // Configure UART3 : Transfert Direction - Oversampling - RX Interrupt
                    uart3.cr1.modify(|_, w| {
                        w.te()
                            .enabled()
                            .re()
                            .enabled()
                            .over8()
                            .over8()
                            .rxneie()
                            .enabled()
                    });
                    // Configure UART3 : 1 stop bit
                    match nbstopbits {
                        StopBits::_0b5 => {
                            uart3.cr2.modify(|_, w| w.stop().half_stop());
                        }
                        StopBits::_1b => {
                            uart3.cr2.modify(|_, w| w.stop()._1stop());
                        }
                        StopBits::_1b5 => {
                            uart3.cr2.modify(|_, w| w.stop()._1half_stop());
                        }
                        StopBits::_2b => {
                            uart3.cr2.modify(|_, w| w.stop()._2stop());
                        }
                    }

                    // Configure UART3 : disable hardware flow control - Overrun interrupt
                    uart3.cr3.modify(|_, w| {
                        w.rtse()
                            .disabled()
                            .ctse()
                            .disabled()
                            .ctsie()
                            .disabled()
                            .ovrdis()
                            .disabled()
                    });
                    // Configure UART3 : baudrate
                    uart3.brr.modify(|_, w| {
                        w.div_fraction()
                            .bits((FREQUENCY / (baudrate / 2)) as u8 & 0x0F)
                    });
                    uart3.brr.modify(|_, w| {
                        w.div_mantissa()
                            .bits(((FREQUENCY / (baudrate / 2)) >> 4) as u16)
                    });
                    // Configure UART3 : Asynchronous mode
                    uart3
                        .cr2
                        .modify(|_, w| w.linen().disabled().clken().disabled());
                    // UART3 enabled
                    uart3.cr1.modify(|_, w| w.ue().enabled());
                    // Interrupt UART3 (and USART4) activated into NVIC
                    nvic.enable(USART3_4);
                    nvic.clear_pending(USART3_4);
                });
                Uart { uart }
            }
            Uarts::Uart4 => {
                cortex_m::interrupt::free(|cs| {
                    let rcc = RCC.borrow(cs);
                    let nvic = NVIC.borrow(cs);
                    let gpioa = GPIOA.borrow(cs);
                    let uart = UART4.borrow(cs);
                    // Enable GPIOA Clock into the Advanced High-performance Bus
                    rcc.ahbenr.modify(|_, w| w.iopaen().enabled());
                    // Enable USART4 Clock into the Advanced Peripheral Bus 1
                    rcc.apb1enr.modify(|_, w| w.usart4en().enabled());

                    // Configure speed of PA0/PA1 (refer to the datasheet for the frequency, the power supply and load conditions)
                    gpioa
                        .ospeedr
                        .modify(|_, w| w.ospeedr0().high_speed().ospeedr1().high_speed());
                    // Use pull-up on PA0/1
                    gpioa
                        .pupdr
                        .modify(|_, w| w.pupdr0().pull_up().pupdr1().pull_up());
                    // Configure PA0/PA1 Alternate Function 4 -> USART4 (PA0 -> TX & PA1 -> RX)
                    gpioa.afrl.modify(|_, w| w.afrl0().af4().afrl1().af4());
                    // PA0 & PA1 as Alternate function (USART4 peripheral)
                    gpioa
                        .moder
                        .modify(|_, w| w.moder0().alternate().moder1().alternate());
                    // PA0 & PA1 push-pull configuration
                    gpioa
                        .otyper
                        .modify(|_, w| w.ot0().push_pull().ot1().push_pull());

                    // Configure UART1 : Word length
                    match nbits {
                        NBits::_8bits => {
                            uart.cr1.modify(|_, w| w.m()._8bits());
                        }
                        NBits::_9bits => {
                            uart.cr1.modify(|_, w| w.m()._9bits());
                        }
                    }
                    // Configure UART1 : Parity
                    match parity {
                        Parity::None => {
                            uart.cr1.modify(|_, w| w.pce().disabled());
                        }
                        Parity::Even => {
                            uart.cr1.modify(|_, w| w.pce().enabled());
                            uart.cr1.modify(|_, w| w.ps().even());
                        }
                        Parity::Odd => {
                            uart.cr1.modify(|_, w| w.pce().enabled());
                            uart.cr1.modify(|_, w| w.ps().odd());
                        }
                    }
                    // Configure UART4: Transfert Direction - Oversampling - RX Interrupt
                    uart.cr1.modify(|_, w| {
                        w.te()
                            .enabled()
                            .re()
                            .enabled()
                            .over8()
                            .over8()
                            .rxneie()
                            .enabled()
                    });
                    // Configure UART1 : stop bit
                    match nbstopbits {
                        StopBits::_0b5 => {
                            uart.cr2.modify(|_, w| w.stop().half_stop());
                        }
                        StopBits::_1b => {
                            uart.cr2.modify(|_, w| w.stop()._1stop());
                        }
                        StopBits::_1b5 => {
                            uart.cr2.modify(|_, w| w.stop()._1half_stop());
                        }
                        StopBits::_2b => {
                            uart.cr2.modify(|_, w| w.stop()._2stop());
                        }
                    }

                    // Configure UART4: disable hardware flow control - Overrun interrupt
                    uart.cr3.modify(|_, w| {
                        w.rtse()
                            .disabled()
                            .ctse()
                            .disabled()
                            .ctsie()
                            .disabled()
                            .ovrdis()
                            .disabled()
                    });
                    // Configure UART4: baudrate
                    uart.brr.modify(|_, w| {
                        w.div_fraction()
                            .bits((FREQUENCY / (baudrate / 2)) as u8 & 0x0F)
                    });
                    uart.brr.modify(|_, w| {
                        w.div_mantissa()
                            .bits(((FREQUENCY / (baudrate / 2)) >> 4) as u16)
                    });
                    // Configure UART4: Asynchronous mode
                    uart.cr2
                        .modify(|_, w| w.linen().disabled().clken().disabled());
                    // UART4 enabled
                    uart.cr1.modify(|_, w| w.ue().enabled());

                    // Interrupt UART4 activated into NVIC
                    nvic.enable(USART3_4);
                    nvic.clear_pending(USART3_4);
                });
                Uart { uart }
            }
        }
    }

    pub fn send(&self, byte: u8) {
        match self.uart {
            Uarts::Uart1 => cortex_m::interrupt::free(|cs| {
                let uart1 = UART1.borrow(cs);
                // the byte is stored in the UART1 transmit register
                uart1.tdr.write(|w| w.tdr().bits(byte as u16));
            }),
            Uarts::Uart3 => cortex_m::interrupt::free(|cs| {
                let uart3 = UART3.borrow(cs);
                // the byte is stored in the UART3 transmit register
                uart3.tdr.write(|w| w.tdr().bits(byte as u16));
            }),
            Uarts::Uart4 => cortex_m::interrupt::free(|cs| {
                let uart4 = UART4.borrow(cs);
                // the byte is stored in the UART4 transmit register
                uart4.tdr.write(|w| w.tdr().bits(byte as u16));
            }),
        }
    }

    pub fn transmit_complete(&self) -> bool {
        match self.uart {
            Uarts::Uart1 => cortex_m::interrupt::free(|cs| {
                let uart1 = UART1.borrow(cs);
                // Check the Transmit Completed flag status
                if uart1.isr.read().tc().bit_is_set() {
                    // if transmit completed, clear the transmit completed flag and return true
                    uart1.icr.write(|w| w.tccf().clear_bit());
                    true
                } else {
                    false
                }
            }),
            Uarts::Uart3 => cortex_m::interrupt::free(|cs| {
                let uart3 = UART3.borrow(cs);
                // Check the Transmit Completed flag status
                if uart3.isr.read().tc().bit_is_set() {
                    // if transmit completed, clear the transmit completed flag and return true
                    uart3.icr.write(|w| w.tccf().clear_bit());
                    true
                } else {
                    false
                }
            }),
            Uarts::Uart4 => cortex_m::interrupt::free(|cs| {
                let uart4 = UART4.borrow(cs);
                // Check the Transmit Completed flag status
                if uart4.isr.read().tc().bit_is_set() {
                    // if transmit completed, clear the transmit completed flag and return true
                    uart4.icr.write(|w| w.tccf().clear_bit());
                    true
                } else {
                    false
                }
            }),
        }
    }

    pub fn read_data(&self) -> Option<u8> {
        match self.uart {
            Uarts::Uart1 => cortex_m::interrupt::free(|cs| {
                let uart1 = UART1.borrow(cs);
                // Check the Receive Not Empty flag flag status
                if uart1.isr.read().rxne().bit_is_set() {
                    // if true, read the received data and finish the rxne flag cleared procedure
                    Some(uart1.rdr.read().rdr().bits() as u8)
                } else {
                    None
                }
            }),
            Uarts::Uart3 => cortex_m::interrupt::free(|cs| {
                let uart3 = UART3.borrow(cs);
                // Check the Receive Not Empty flag flag status
                if uart3.isr.read().rxne().bit_is_set() {
                    // if true, read the received data and finish the rxne flag cleared procedure
                    Some(uart3.rdr.read().rdr().bits() as u8)
                } else {
                    None
                }
            }),
            Uarts::Uart4 => cortex_m::interrupt::free(|cs| {
                let uart4 = UART4.borrow(cs);
                // Check the Receive Not Empty flag flag status
                if uart4.isr.read().rxne().bit_is_set() {
                    // if true, read the received data and finish the rxne flag cleared procedure
                    Some(uart4.rdr.read().rdr().bits() as u8)
                } else {
                    None
                }
            }),
        }
    }
}
<|endoftext|>


<commit_before>use std::ffi::OsString;
use std::fs::File;
use std::path::Path;
use std::process::exit;
use std::{env, fs, io};

use nmk::env_name::{EDITOR, LD_LIBRARY_PATH, NMK_HOME, PATH, VIMINIT, ZDOTDIR};
use nmk::home::NmkHome;
use nmk::time::{human_time, seconds_since_build};

use crate::cmdline::Opt;
use crate::core::set_env;
use crate::pathenv::PathVec;
use crate::terminal;
use crate::tmux::{make_config_context, Tmux};

fn setup_environment(nmk_home: &Path) {
    let zdotdir = nmk_home.join("zsh");
    set_env(NMK_HOME, nmk_home);
    set_env(ZDOTDIR, zdotdir);

    let init_vim = nmk_home.join("vim").join("init.vim");
    if let Some(path) = init_vim.to_str() {
        set_env(VIMINIT, format!(r"source\ {}", path));
    }
}

fn setup_preferred_editor() {
    const PREFERRED_EDITORS: &[&str] = &["nvim", "vim"];

    match env::var_os(EDITOR)
        .into_iter()
        .chain(PREFERRED_EDITORS.iter().map(OsString::from))
        .find(|bin| which::which(bin).is_ok())
    {
        Some(editor) => {
            log::debug!("using {:?} as preferred editor", editor);
            set_env(EDITOR, editor);
        }
        None => env::remove_var(EDITOR),
    }
}

fn setup_path(nmk_home: &Path) {
    let mut bin_path = PathVec::parse(env::var_os(PATH).expect("$PATH not found"));
    bin_path.push_front(nmk_home.join("vendor").join("bin"));
    bin_path.push_front(nmk_home.join("bin"));
    bin_path = bin_path.unique().no_version_managers();
    log::debug!("{} = {:#?}", PATH, bin_path);
    set_env(PATH, bin_path.make());
}

fn setup_ld_library_path(nmk_home: &Path) {
    let vendored_lib_dir = nmk_home.join("vendor").join("lib");
    if vendored_lib_dir.exists() {
        let mut lib_path = match env::var_os(LD_LIBRARY_PATH) {
            Some(value) => PathVec::parse(value),
            None => PathVec::new(),
        };
        lib_path.push_front(vendored_lib_dir);
        log::debug!("{} = {:#?}", LD_LIBRARY_PATH, lib_path);
        let next_ld = lib_path.make();
        set_env(LD_LIBRARY_PATH, next_ld);
    }
}

fn display_message_of_the_day() {
    let mut stdout = std::io::stdout();
    ["/var/run/motd.dynamic", "/etc/motd"]
        .iter()
        .map(Path::new)
        .filter(|p| p.exists())
        .flat_map(File::open)
        .for_each(|mut f| {
            io::copy(&mut f, &mut stdout).expect("fail to print motd");
        });
}

const DAY_SECS: i64 = 24 * 60 * 60;

fn check_for_update_suggest() {
    if let Some(secs) = seconds_since_build() {
        if secs > 45 * DAY_SECS {
            println!("\nnmk: I's been {} since build.\n", human_time(secs));
        }
    }
}

pub fn main(opt: Opt) -> ! {
    if opt.motd {
        display_message_of_the_day();
        check_for_update_suggest()
    }

    let nmk_home = NmkHome::find().expect("Unable to locate NMK_HOME");
    assert!(nmk_home.exists(), "{:?} doesn't exist", nmk_home);

    log::debug!("Dotfiles directory: {:?}", nmk_home);
    setup_ld_library_path(&nmk_home);
    setup_path(&nmk_home);
    setup_environment(&nmk_home);
    setup_preferred_editor();
    crate::zsh::setup(&opt, &nmk_home);
    if opt.login {
        crate::zsh::exec_login_shell(&opt);
    } else {
        let tmux = Tmux::new(nmk_home);
        log::debug!("tmux path = {:?}", tmux.bin);
        log::debug!("tmux version = {}", tmux.version.as_str());
        let is_color_term = opt.force_256_color || terminal::support_256_color();
        let tmp_config;
        let config = match opt.tmux_conf {
            Some(ref config) => config,
            None => {
                let context = make_config_context(&opt, is_color_term);
                tmp_config = tmux
                    .render_config_in_temp_dir(&opt, context)
                    .expect("Unable to create temporary config file");
                if opt.render {
                    print_config_then_remove(&tmp_config).expect("Unable to print config");
                    exit(0);
                }
                set_env("NMK_TMP_TMUX_CONF", &tmp_config);
                &tmp_config
            }
        };
        tmux.exec(&opt, config, is_color_term);
    }
}

fn print_config_then_remove(config: &Path) -> io::Result<()> {
    io::copy(&mut File::open(&config)?, &mut io::stdout())?;
    fs::remove_file(config)
}
<commit_msg>Avoid allocation for PREFERRED_EDITORS<commit_after>use std::ffi::OsStr;
use std::fs::File;
use std::path::Path;
use std::process::exit;
use std::{env, fs, io};

use nmk::env_name::{EDITOR, LD_LIBRARY_PATH, NMK_HOME, PATH, VIMINIT, ZDOTDIR};
use nmk::home::NmkHome;
use nmk::time::{human_time, seconds_since_build};

use crate::cmdline::Opt;
use crate::core::set_env;
use crate::pathenv::PathVec;
use crate::terminal;
use crate::tmux::{make_config_context, Tmux};

fn setup_environment(nmk_home: &Path) {
    let zdotdir = nmk_home.join("zsh");
    set_env(NMK_HOME, nmk_home);
    set_env(ZDOTDIR, zdotdir);

    let init_vim = nmk_home.join("vim").join("init.vim");
    if let Some(path) = init_vim.to_str() {
        set_env(VIMINIT, format!(r"source\ {}", path));
    }
}

fn setup_preferred_editor() {
    const PREFERRED_EDITORS: &[&str] = &["nvim", "vim"];

    match env::var_os(EDITOR)
        .as_deref()
        .into_iter()
        .chain(PREFERRED_EDITORS.iter().map(OsStr::new))
        .find(|bin| which::which(bin).is_ok())
    {
        Some(editor) => {
            log::debug!("using {:?} as preferred editor", editor);
            set_env(EDITOR, editor);
        }
        None => env::remove_var(EDITOR),
    }
}

fn setup_path(nmk_home: &Path) {
    let mut bin_path = PathVec::parse(env::var_os(PATH).expect("$PATH not found"));
    bin_path.push_front(nmk_home.join("vendor").join("bin"));
    bin_path.push_front(nmk_home.join("bin"));
    bin_path = bin_path.unique().no_version_managers();
    log::debug!("{} = {:#?}", PATH, bin_path);
    set_env(PATH, bin_path.make());
}

fn setup_ld_library_path(nmk_home: &Path) {
    let vendored_lib_dir = nmk_home.join("vendor").join("lib");
    if vendored_lib_dir.exists() {
        let mut lib_path = match env::var_os(LD_LIBRARY_PATH) {
            Some(value) => PathVec::parse(value),
            None => PathVec::new(),
        };
        lib_path.push_front(vendored_lib_dir);
        log::debug!("{} = {:#?}", LD_LIBRARY_PATH, lib_path);
        let next_ld = lib_path.make();
        set_env(LD_LIBRARY_PATH, next_ld);
    }
}

fn display_message_of_the_day() {
    let mut stdout = std::io::stdout();
    ["/var/run/motd.dynamic", "/etc/motd"]
        .iter()
        .map(Path::new)
        .filter(|p| p.exists())
        .flat_map(File::open)
        .for_each(|mut f| {
            io::copy(&mut f, &mut stdout).expect("fail to print motd");
        });
}

const DAY_SECS: i64 = 24 * 60 * 60;

fn check_for_update_suggest() {
    if let Some(secs) = seconds_since_build() {
        if secs > 45 * DAY_SECS {
            println!("\nnmk: I's been {} since build.\n", human_time(secs));
        }
    }
}

pub fn main(opt: Opt) -> ! {
    if opt.motd {
        display_message_of_the_day();
        check_for_update_suggest()
    }

    let nmk_home = NmkHome::find().expect("Unable to locate NMK_HOME");
    assert!(nmk_home.exists(), "{:?} doesn't exist", nmk_home);

    log::debug!("Dotfiles directory: {:?}", nmk_home);
    setup_ld_library_path(&nmk_home);
    setup_path(&nmk_home);
    setup_environment(&nmk_home);
    setup_preferred_editor();
    crate::zsh::setup(&opt, &nmk_home);
    if opt.login {
        crate::zsh::exec_login_shell(&opt);
    } else {
        let tmux = Tmux::new(nmk_home);
        log::debug!("tmux path = {:?}", tmux.bin);
        log::debug!("tmux version = {}", tmux.version.as_str());
        let is_color_term = opt.force_256_color || terminal::support_256_color();
        let tmp_config;
        let config = match opt.tmux_conf {
            Some(ref config) => config,
            None => {
                let context = make_config_context(&opt, is_color_term);
                tmp_config = tmux
                    .render_config_in_temp_dir(&opt, context)
                    .expect("Unable to create temporary config file");
                if opt.render {
                    print_config_then_remove(&tmp_config).expect("Unable to print config");
                    exit(0);
                }
                set_env("NMK_TMP_TMUX_CONF", &tmp_config);
                &tmp_config
            }
        };
        tmux.exec(&opt, config, is_color_term);
    }
}

fn print_config_then_remove(config: &Path) -> io::Result<()> {
    io::copy(&mut File::open(&config)?, &mut io::stdout())?;
    fs::remove_file(config)
}
<|endoftext|>


<commit_before>use au = gfx::geometry;
use content::content_task::ContentTask;
use dom::cow;
use dom::element::*;
use dom::event::{Event, ReflowEvent};
use dom::node::{Comment, Doctype, DoctypeData, Element, Node, NodeScope, Text};
use resource::image_cache_task::ImageCacheTask;
use resource::image_cache_task;
use resource::resource_task::{Done, Load, Payload, ResourceTask};

use core::comm::{Chan, Port};
use cssparse::{InlineProvenance, StylesheetProvenance, UrlProvenance, spawn_css_parser};
use hubbub::hubbub;
use hubbub::Attribute;
use newcss::stylesheet::Stylesheet;
use std::net::url::Url;
use std::net::url;

type JSResult = ~[~[u8]];

enum CSSMessage {
    CSSTaskNewFile(StylesheetProvenance),
    CSSTaskExit
}

enum JSMessage {
    JSTaskNewFile(Url),
    JSTaskExit
}

struct HtmlParserResult {
    root: Node,
    style_port: comm::Port<Option<Stylesheet>>,
    js_port: comm::Port<JSResult>,
}

/**
Runs a task that coordinates parsing links to css stylesheets.

This function should be spawned in a separate task and spins waiting
for the html builder to find links to css stylesheets and sends off
tasks to parse each link.  When the html process finishes, it notifies
the listener, who then collects the css rules from each task it
spawned, collates them, and sends them to the given result channel.

# Arguments

* `to_parent` - A channel on which to send back the full set of rules.
* `from_parent` - A port on which to receive new links.

*/
fn css_link_listener(to_parent : comm::Chan<Option<Stylesheet>>,
                     from_parent : comm::Port<CSSMessage>,
                     resource_task: ResourceTask) {
    let mut result_vec = ~[];

    loop {
        match from_parent.recv() {
            CSSTaskNewFile(move provenance) => {
                result_vec.push(spawn_css_parser(move provenance, copy resource_task));
            }
            CSSTaskExit => {
                break;
            }
        }
    }

    // Send the sheets back in order
    // FIXME: Shouldn't wait until after we've recieved CSSTaskExit to start sending these
    do vec::consume(move result_vec) |_i, port| {
        to_parent.send(Some(port.recv()));
    }
    to_parent.send(None);
}

fn js_script_listener(to_parent : comm::Chan<~[~[u8]]>, from_parent : comm::Port<JSMessage>,
                      resource_task: ResourceTask) {
    let mut result_vec = ~[];

    loop {
        match from_parent.recv() {
            JSTaskNewFile(move url) => {
                let result_port = comm::Port();
                let result_chan = comm::Chan(&result_port);
                do task::spawn |move url| {
                    let input_port = Port();
                    // TODO: change copy to move once we can move into closures
                    resource_task.send(Load(copy url, input_port.chan()));

                    let mut buf = ~[];
                    loop {
                        match input_port.recv() {
                            Payload(move data) => {
                                buf += data;
                            }
                            Done(Ok(*)) => {
                                result_chan.send(move buf);
                                break;
                            }
                            Done(Err(*)) => {
                                error!("error loading script %s", url.to_str());
                            }
                        }
                    }
                }
                vec::push(&mut result_vec, result_port);
            }
            JSTaskExit => {
                break;
            }
        }
    }

    let js_scripts = vec::map(result_vec, |result_port| result_port.recv());
    to_parent.send(move js_scripts);
}

fn build_element_kind(tag: &str) -> ~ElementKind {
    // TODO (Issue #85): use atoms
    if      tag == ~"a" { ~HTMLAnchorElement }
    else if tag == ~"aside" { ~HTMLAsideElement }
    else if tag == ~"br" { ~HTMLBRElement }
    else if tag == ~"body" { ~HTMLBodyElement }
    else if tag == ~"bold" { ~HTMLBoldElement }
    else if tag == ~"div" { ~HTMLDivElement }
    else if tag == ~"font" { ~HTMLFontElement }
    else if tag == ~"form" { ~HTMLFormElement }
    else if tag == ~"hr" { ~HTMLHRElement }
    else if tag == ~"head" { ~HTMLHeadElement }
    else if tag == ~"h1" { ~HTMLHeadingElement(Heading1) }
    else if tag == ~"h2" { ~HTMLHeadingElement(Heading2) }
    else if tag == ~"h3" { ~HTMLHeadingElement(Heading3) }
    else if tag == ~"h4" { ~HTMLHeadingElement(Heading4) }
    else if tag == ~"h5" { ~HTMLHeadingElement(Heading5) }
    else if tag == ~"h6" { ~HTMLHeadingElement(Heading6) }
    else if tag == ~"html" { ~HTMLHtmlElement }
    else if tag == ~"img" { ~HTMLImageElement(HTMLImageData()) }
    else if tag == ~"input" { ~HTMLInputElement }
    else if tag == ~"i" { ~HTMLItalicElement }
    else if tag == ~"link" { ~HTMLLinkElement }
    else if tag == ~"li" { ~HTMLListItemElement }
    else if tag == ~"meta" { ~HTMLMetaElement }
    else if tag == ~"ol" { ~HTMLOListElement }
    else if tag == ~"option" { ~HTMLOptionElement }
    else if tag == ~"p" { ~HTMLParagraphElement }
    else if tag == ~"script" { ~HTMLScriptElement }
    else if tag == ~"section" { ~HTMLSectionElement }
    else if tag == ~"select" { ~HTMLSelectElement }
    else if tag == ~"small" { ~HTMLSmallElement }
    else if tag == ~"span" { ~HTMLSpanElement }
    else if tag == ~"style" { ~HTMLStyleElement }
    else if tag == ~"tbody" { ~HTMLTableBodyElement }
    else if tag == ~"td" { ~HTMLTableCellElement }
    else if tag == ~"table" { ~HTMLTableElement }
    else if tag == ~"tr" { ~HTMLTableRowElement }
    else if tag == ~"title" { ~HTMLTitleElement }
    else if tag == ~"ul" { ~HTMLUListElement }
    else { ~UnknownElement }
}

pub fn parse_html(scope: NodeScope,
                  url: Url,
                  resource_task: ResourceTask,
                  image_cache_task: ImageCacheTask) -> HtmlParserResult unsafe {
    // Spawn a CSS parser to receive links to CSS style sheets.
    let (css_port, css_chan): (comm::Port<Option<Stylesheet>>, comm::Chan<CSSMessage>) =
            do task::spawn_conversation |css_port: comm::Port<CSSMessage>,
                                         css_chan: comm::Chan<Option<Stylesheet>>| {
        css_link_listener(css_chan, css_port, resource_task);
    };

    // Spawn a JS parser to receive JavaScript.
    let (js_port, js_chan): (comm::Port<JSResult>, comm::Chan<JSMessage>) =
            do task::spawn_conversation |js_port: comm::Port<JSMessage>,
                                         js_chan: comm::Chan<JSResult>| {
        js_script_listener(js_chan, js_port, resource_task);
    };

    let (scope, url) = (@copy scope, @move url);

    // Build the root node.
    let root = scope.new_node(Element(ElementData(~"html", ~HTMLDivElement)));
    debug!("created new node");
    let parser = hubbub::Parser("UTF-8", false);
    debug!("created parser");
    parser.set_document_node(cast::transmute(cow::unwrap(root)));
    parser.enable_scripting(true);

    // Performs various actions necessary after appending has taken place. Currently, this consists
    // of processing inline stylesheets, but in the future it might perform prefetching, etc.
    let append_hook: @fn(Node, Node) = |parent_node, child_node| {
        do scope.read(&parent_node) |parent_node_contents| {
            do scope.read(&child_node) |child_node_contents| {
                match (parent_node_contents.kind, child_node_contents.kind) {
                    (~Element(ref element), ~Text(ref data)) => {
                        match element.kind {
                            ~HTMLStyleElement => {
                                debug!("found inline CSS stylesheet");
                                let url = url::from_str("http://example.com/"); // FIXME
                                let provenance = InlineProvenance(result::unwrap(move url),
                                                                  copy *data);
                                css_chan.send(CSSTaskNewFile(provenance));
                            }
                            _ => {} // Nothing to do.
                        }
                    }
                    _ => {} // Nothing to do.
                }
            }
        }
    };

    parser.set_tree_handler(@hubbub::TreeHandler {
        create_comment: |data: ~str| {
            debug!("create comment");
            let new_node = scope.new_node(Comment(move data));
            unsafe { cast::transmute(cow::unwrap(new_node)) }
        },
        create_doctype: |doctype: ~hubbub::Doctype| {
            debug!("create doctype");
            // TODO: remove copying here by using struct pattern matching to
            // move all ~strs at once (blocked on Rust #3845, #3846, #3847)
            let public_id = match doctype.public_id {
                None => None,
                Some(id) => Some(copy id)
            };
            let system_id = match doctype.system_id {
                None => None,
                Some(id) => Some(copy id)
            };
            let data = DoctypeData(copy doctype.name, move public_id, move system_id,
                                   copy doctype.force_quirks);
            let new_node = scope.new_node(Doctype(move data));
            unsafe { cast::transmute(cow::unwrap(new_node)) }
        },
        create_element: |tag: ~hubbub::Tag, move image_cache_task| {
            debug!("create element");
            // TODO: remove copying here by using struct pattern matching to
            // move all ~strs at once (blocked on Rust #3845, #3846, #3847)
            let elem_kind = build_element_kind(tag.name);
            let elem = ElementData(copy tag.name, move elem_kind);

            debug!("-- attach attrs");
            for tag.attributes.each |attr| {
                elem.attrs.push(~Attr(copy attr.name, copy attr.value));
            }

            // Spawn additional parsing, network loads, etc. from tag and attrs
            match elem.kind {
                //Handle CSS style sheets from <link> elements
                ~HTMLLinkElement => {
                    match (elem.get_attr(~"rel"), elem.get_attr(~"href")) {
                        (Some(move rel), Some(move href)) => {
                            if rel == ~"stylesheet" {
                                debug!("found CSS stylesheet: %s", href);
                                css_chan.send(CSSTaskNewFile(UrlProvenance(make_url(move href,
                                                                           Some(copy *url)))));
                            }
                        }
                        _ => {}
                    }
                },
                ~HTMLImageElement(ref d) => {
                    do elem.get_attr(~"src").iter |img_url_str| {
                        let img_url = make_url(copy *img_url_str, Some(copy *url));
                        d.image = Some(copy img_url);
                        // inform the image cache to load this, but don't store a handle.
                        // TODO (Issue #84): don't prefetch if we are within a <noscript> tag.
                        image_cache_task.send(image_cache_task::Prefetch(move img_url));
                    }
                }
                //TODO (Issue #86): handle inline styles ('style' attr)
                _ => {}
            }
            let node = scope.new_node(Element(move elem));
            unsafe { cast::transmute(cow::unwrap(node)) }
        },
        create_text: |data: ~str| {
            debug!("create text");
            let new_node = scope.new_node(Text(move data));
            unsafe { cast::transmute(cow::unwrap(new_node)) }
        },
        ref_node: |_node| {},
        unref_node: |_node| {},
        append_child: |parent: hubbub::NodeDataPtr, child: hubbub::NodeDataPtr| unsafe {
            debug!("append child %x %x", cast::transmute(parent), cast::transmute(child));
            unsafe {
                let p: Node = cow::wrap(cast::transmute(parent));
                let c: Node = cow::wrap(cast::transmute(child));
                scope.add_child(p, c);
                append_hook(p, c);
            }
            child
        },
        insert_before: |_parent, _child| {
            debug!("insert before");
            0u
        },
        remove_child: |_parent, _child| {
            debug!("remove child");
            0u
        },
        clone_node: |node, deep| {
            debug!("clone node");
            unsafe {
                if deep { error!("-- deep clone unimplemented"); }
                let n: Node = cow::wrap(cast::transmute(node));
                let data = n.read(|read_data| copy *read_data.kind);
                let new_node = scope.new_node(move data);
                unsafe { cast::transmute(cow::unwrap(new_node)) }
            }
        },
        reparent_children: |_node, _new_parent| {
            debug!("reparent children");
            0u
        },
        get_parent: |_node, _element_only| {
            debug!("get parent");
            0u
        },
        has_children: |_node| {
            debug!("has children");
            false
        },
        form_associate: |_form, _node| {
            debug!("form associate");
        },
        add_attributes: |_node, _attributes| {
            debug!("add attributes");
        },
        set_quirks_mode: |_mode| {
            debug!("set quirks mode");
        },
        encoding_change: |_encname| {
            debug!("encoding change");
        },
        complete_script: |script| {
            // A little function for holding this lint attr
            #[allow(non_implicitly_copyable_typarams)]
            fn complete_script(scope: &NodeScope, script: hubbub::NodeDataPtr, url: &Url, js_chan: &comm::Chan<JSMessage>) unsafe {
                do scope.read(&cow::wrap(cast::transmute(script))) |node_contents| {
                    match *node_contents.kind {
                        Element(element) if element.tag_name == ~"script" => {
                            match element.get_attr(~"src") {
                                Some(move src) => {
                                    debug!("found script: %s", src);
                                    let new_url = make_url(move src, Some(copy *url));
                                    js_chan.send(JSTaskNewFile(move new_url));
                                }
                                None => {}
                            }
                        }
                        _ => {}
                    }
                }
            }
            complete_script(scope, script, url, &js_chan);
            debug!("complete script");
        }
    });
    debug!("set tree handler");

    let input_port = Port();
    resource_task.send(Load(copy *url, input_port.chan()));
    debug!("loaded page");
    loop {
        match input_port.recv() {
            Payload(data) => {
                debug!("received data");
                parser.parse_chunk(data);
            }
            Done(*) => {
                break;
            }
        }
    }

    css_chan.send(CSSTaskExit);
    js_chan.send(JSTaskExit);

    return HtmlParserResult { root: root, style_port: css_port, js_port: js_port };
}

<commit_msg>Eliminate some copies<commit_after>use au = gfx::geometry;
use content::content_task::ContentTask;
use dom::cow;
use dom::element::*;
use dom::event::{Event, ReflowEvent};
use dom::node::{Comment, Doctype, DoctypeData, Element, Node, NodeScope, Text};
use resource::image_cache_task::ImageCacheTask;
use resource::image_cache_task;
use resource::resource_task::{Done, Load, Payload, ResourceTask};

use core::comm::{Chan, Port};
use cssparse::{InlineProvenance, StylesheetProvenance, UrlProvenance, spawn_css_parser};
use hubbub::hubbub;
use hubbub::Attribute;
use newcss::stylesheet::Stylesheet;
use std::net::url::Url;
use std::net::url;

type JSResult = ~[~[u8]];

enum CSSMessage {
    CSSTaskNewFile(StylesheetProvenance),
    CSSTaskExit
}

enum JSMessage {
    JSTaskNewFile(Url),
    JSTaskExit
}

struct HtmlParserResult {
    root: Node,
    style_port: comm::Port<Option<Stylesheet>>,
    js_port: comm::Port<JSResult>,
}

/**
Runs a task that coordinates parsing links to css stylesheets.

This function should be spawned in a separate task and spins waiting
for the html builder to find links to css stylesheets and sends off
tasks to parse each link.  When the html process finishes, it notifies
the listener, who then collects the css rules from each task it
spawned, collates them, and sends them to the given result channel.

# Arguments

* `to_parent` - A channel on which to send back the full set of rules.
* `from_parent` - A port on which to receive new links.

*/
fn css_link_listener(to_parent : comm::Chan<Option<Stylesheet>>,
                     from_parent : comm::Port<CSSMessage>,
                     resource_task: ResourceTask) {
    let mut result_vec = ~[];

    loop {
        match from_parent.recv() {
            CSSTaskNewFile(move provenance) => {
                result_vec.push(spawn_css_parser(move provenance, copy resource_task));
            }
            CSSTaskExit => {
                break;
            }
        }
    }

    // Send the sheets back in order
    // FIXME: Shouldn't wait until after we've recieved CSSTaskExit to start sending these
    do vec::consume(move result_vec) |_i, port| {
        to_parent.send(Some(port.recv()));
    }
    to_parent.send(None);
}

fn js_script_listener(to_parent : comm::Chan<~[~[u8]]>, from_parent : comm::Port<JSMessage>,
                      resource_task: ResourceTask) {
    let mut result_vec = ~[];

    loop {
        match from_parent.recv() {
            JSTaskNewFile(move url) => {
                let result_port = comm::Port();
                let result_chan = comm::Chan(&result_port);
                do task::spawn |move url| {
                    let input_port = Port();
                    // TODO: change copy to move once we can move into closures
                    resource_task.send(Load(copy url, input_port.chan()));

                    let mut buf = ~[];
                    loop {
                        match input_port.recv() {
                            Payload(move data) => {
                                buf += data;
                            }
                            Done(Ok(*)) => {
                                result_chan.send(move buf);
                                break;
                            }
                            Done(Err(*)) => {
                                error!("error loading script %s", url.to_str());
                            }
                        }
                    }
                }
                vec::push(&mut result_vec, result_port);
            }
            JSTaskExit => {
                break;
            }
        }
    }

    let js_scripts = vec::map(result_vec, |result_port| result_port.recv());
    to_parent.send(move js_scripts);
}

fn build_element_kind(tag: &str) -> ~ElementKind {
    // TODO (Issue #85): use atoms
    if      tag == ~"a" { ~HTMLAnchorElement }
    else if tag == ~"aside" { ~HTMLAsideElement }
    else if tag == ~"br" { ~HTMLBRElement }
    else if tag == ~"body" { ~HTMLBodyElement }
    else if tag == ~"bold" { ~HTMLBoldElement }
    else if tag == ~"div" { ~HTMLDivElement }
    else if tag == ~"font" { ~HTMLFontElement }
    else if tag == ~"form" { ~HTMLFormElement }
    else if tag == ~"hr" { ~HTMLHRElement }
    else if tag == ~"head" { ~HTMLHeadElement }
    else if tag == ~"h1" { ~HTMLHeadingElement(Heading1) }
    else if tag == ~"h2" { ~HTMLHeadingElement(Heading2) }
    else if tag == ~"h3" { ~HTMLHeadingElement(Heading3) }
    else if tag == ~"h4" { ~HTMLHeadingElement(Heading4) }
    else if tag == ~"h5" { ~HTMLHeadingElement(Heading5) }
    else if tag == ~"h6" { ~HTMLHeadingElement(Heading6) }
    else if tag == ~"html" { ~HTMLHtmlElement }
    else if tag == ~"img" { ~HTMLImageElement(HTMLImageData()) }
    else if tag == ~"input" { ~HTMLInputElement }
    else if tag == ~"i" { ~HTMLItalicElement }
    else if tag == ~"link" { ~HTMLLinkElement }
    else if tag == ~"li" { ~HTMLListItemElement }
    else if tag == ~"meta" { ~HTMLMetaElement }
    else if tag == ~"ol" { ~HTMLOListElement }
    else if tag == ~"option" { ~HTMLOptionElement }
    else if tag == ~"p" { ~HTMLParagraphElement }
    else if tag == ~"script" { ~HTMLScriptElement }
    else if tag == ~"section" { ~HTMLSectionElement }
    else if tag == ~"select" { ~HTMLSelectElement }
    else if tag == ~"small" { ~HTMLSmallElement }
    else if tag == ~"span" { ~HTMLSpanElement }
    else if tag == ~"style" { ~HTMLStyleElement }
    else if tag == ~"tbody" { ~HTMLTableBodyElement }
    else if tag == ~"td" { ~HTMLTableCellElement }
    else if tag == ~"table" { ~HTMLTableElement }
    else if tag == ~"tr" { ~HTMLTableRowElement }
    else if tag == ~"title" { ~HTMLTitleElement }
    else if tag == ~"ul" { ~HTMLUListElement }
    else { ~UnknownElement }
}

pub fn parse_html(scope: NodeScope,
                  url: Url,
                  resource_task: ResourceTask,
                  image_cache_task: ImageCacheTask) -> HtmlParserResult unsafe {
    // Spawn a CSS parser to receive links to CSS style sheets.
    let (css_port, css_chan): (comm::Port<Option<Stylesheet>>, comm::Chan<CSSMessage>) =
            do task::spawn_conversation |css_port: comm::Port<CSSMessage>,
                                         css_chan: comm::Chan<Option<Stylesheet>>| {
        css_link_listener(css_chan, css_port, resource_task);
    };

    // Spawn a JS parser to receive JavaScript.
    let (js_port, js_chan): (comm::Port<JSResult>, comm::Chan<JSMessage>) =
            do task::spawn_conversation |js_port: comm::Port<JSMessage>,
                                         js_chan: comm::Chan<JSResult>| {
        js_script_listener(js_chan, js_port, resource_task);
    };

    let (scope, url) = (@copy scope, @move url);

    // Build the root node.
    let root = scope.new_node(Element(ElementData(~"html", ~HTMLDivElement)));
    debug!("created new node");
    let parser = hubbub::Parser("UTF-8", false);
    debug!("created parser");
    parser.set_document_node(cast::transmute(cow::unwrap(root)));
    parser.enable_scripting(true);

    // Performs various actions necessary after appending has taken place. Currently, this consists
    // of processing inline stylesheets, but in the future it might perform prefetching, etc.
    let append_hook: @fn(Node, Node) = |parent_node, child_node| {
        do scope.read(&parent_node) |parent_node_contents| {
            do scope.read(&child_node) |child_node_contents| {
                match (&parent_node_contents.kind, &child_node_contents.kind) {
                    (&~Element(ref element), &~Text(ref data)) => {
                        match element.kind {
                            ~HTMLStyleElement => {
                                debug!("found inline CSS stylesheet");
                                let url = url::from_str("http://example.com/"); // FIXME
                                let provenance = InlineProvenance(result::unwrap(move url),
                                                                  copy *data);
                                css_chan.send(CSSTaskNewFile(move provenance));
                            }
                            _ => {} // Nothing to do.
                        }
                    }
                    _ => {} // Nothing to do.
                }
            }
        }
    };

    parser.set_tree_handler(@hubbub::TreeHandler {
        create_comment: |data: ~str| {
            debug!("create comment");
            let new_node = scope.new_node(Comment(move data));
            unsafe { cast::transmute(cow::unwrap(new_node)) }
        },
        create_doctype: |doctype: ~hubbub::Doctype| {
            debug!("create doctype");
            // TODO: remove copying here by using struct pattern matching to
            // move all ~strs at once (blocked on Rust #3845, #3846, #3847)
            let public_id = match doctype.public_id {
                None => None,
                Some(id) => Some(copy id)
            };
            let system_id = match doctype.system_id {
                None => None,
                Some(id) => Some(copy id)
            };
            let data = DoctypeData(copy doctype.name, move public_id, move system_id,
                                   copy doctype.force_quirks);
            let new_node = scope.new_node(Doctype(move data));
            unsafe { cast::transmute(cow::unwrap(new_node)) }
        },
        create_element: |tag: ~hubbub::Tag, move image_cache_task| {
            debug!("create element");
            // TODO: remove copying here by using struct pattern matching to
            // move all ~strs at once (blocked on Rust #3845, #3846, #3847)
            let elem_kind = build_element_kind(tag.name);
            let elem = ElementData(copy tag.name, move elem_kind);

            debug!("-- attach attrs");
            for tag.attributes.each |attr| {
                elem.attrs.push(~Attr(copy attr.name, copy attr.value));
            }

            // Spawn additional parsing, network loads, etc. from tag and attrs
            match elem.kind {
                //Handle CSS style sheets from <link> elements
                ~HTMLLinkElement => {
                    match (elem.get_attr(~"rel"), elem.get_attr(~"href")) {
                        (Some(move rel), Some(move href)) => {
                            if rel == ~"stylesheet" {
                                debug!("found CSS stylesheet: %s", href);
                                css_chan.send(CSSTaskNewFile(UrlProvenance(make_url(move href,
                                                                           Some(copy *url)))));
                            }
                        }
                        _ => {}
                    }
                },
                ~HTMLImageElement(ref d) => {
                    do elem.get_attr(~"src").iter |img_url_str| {
                        let img_url = make_url(copy *img_url_str, Some(copy *url));
                        d.image = Some(copy img_url);
                        // inform the image cache to load this, but don't store a handle.
                        // TODO (Issue #84): don't prefetch if we are within a <noscript> tag.
                        image_cache_task.send(image_cache_task::Prefetch(move img_url));
                    }
                }
                //TODO (Issue #86): handle inline styles ('style' attr)
                _ => {}
            }
            let node = scope.new_node(Element(move elem));
            unsafe { cast::transmute(cow::unwrap(node)) }
        },
        create_text: |data: ~str| {
            debug!("create text");
            let new_node = scope.new_node(Text(move data));
            unsafe { cast::transmute(cow::unwrap(new_node)) }
        },
        ref_node: |_node| {},
        unref_node: |_node| {},
        append_child: |parent: hubbub::NodeDataPtr, child: hubbub::NodeDataPtr| unsafe {
            debug!("append child %x %x", cast::transmute(parent), cast::transmute(child));
            unsafe {
                let p: Node = cow::wrap(cast::transmute(parent));
                let c: Node = cow::wrap(cast::transmute(child));
                scope.add_child(p, c);
                append_hook(p, c);
            }
            child
        },
        insert_before: |_parent, _child| {
            debug!("insert before");
            0u
        },
        remove_child: |_parent, _child| {
            debug!("remove child");
            0u
        },
        clone_node: |node, deep| {
            debug!("clone node");
            unsafe {
                if deep { error!("-- deep clone unimplemented"); }
                let n: Node = cow::wrap(cast::transmute(node));
                let data = n.read(|read_data| copy *read_data.kind);
                let new_node = scope.new_node(move data);
                unsafe { cast::transmute(cow::unwrap(new_node)) }
            }
        },
        reparent_children: |_node, _new_parent| {
            debug!("reparent children");
            0u
        },
        get_parent: |_node, _element_only| {
            debug!("get parent");
            0u
        },
        has_children: |_node| {
            debug!("has children");
            false
        },
        form_associate: |_form, _node| {
            debug!("form associate");
        },
        add_attributes: |_node, _attributes| {
            debug!("add attributes");
        },
        set_quirks_mode: |_mode| {
            debug!("set quirks mode");
        },
        encoding_change: |_encname| {
            debug!("encoding change");
        },
        complete_script: |script| {
            // A little function for holding this lint attr
            #[allow(non_implicitly_copyable_typarams)]
            fn complete_script(scope: &NodeScope, script: hubbub::NodeDataPtr, url: &Url, js_chan: &comm::Chan<JSMessage>) unsafe {
                do scope.read(&cow::wrap(cast::transmute(script))) |node_contents| {
                    match *node_contents.kind {
                        Element(element) if element.tag_name == ~"script" => {
                            match element.get_attr(~"src") {
                                Some(move src) => {
                                    debug!("found script: %s", src);
                                    let new_url = make_url(move src, Some(copy *url));
                                    js_chan.send(JSTaskNewFile(move new_url));
                                }
                                None => {}
                            }
                        }
                        _ => {}
                    }
                }
            }
            complete_script(scope, script, url, &js_chan);
            debug!("complete script");
        }
    });
    debug!("set tree handler");

    let input_port = Port();
    resource_task.send(Load(copy *url, input_port.chan()));
    debug!("loaded page");
    loop {
        match input_port.recv() {
            Payload(data) => {
                debug!("received data");
                parser.parse_chunk(data);
            }
            Done(*) => {
                break;
            }
        }
    }

    css_chan.send(CSSTaskExit);
    js_chan.send(JSTaskExit);

    return HtmlParserResult { root: root, style_port: css_port, js_port: js_port };
}

<|endoftext|>