<commit_before>use elapsed::measure_time;

use fall_tree::{Language, NodeType, File, ERROR, WHITESPACE, TextRange,
                FileStats, ImmutableNode, TextUnit, ReparseRegion};
use lex::{Token, LexRule, tokenize};

#[derive(Clone, Copy, Debug)]
pub struct TokenSequence<'a> {
    text: &'a str,
    start: usize,
    non_ws_indexes: &'a [usize],
    original_tokens: &'a [Token],
}

#[derive(Debug)]
pub enum Node {
    Leaf(NodeType, usize),
    Composite {
        ty: Option<NodeType>,
        children: Vec<Node>,
        layer: Option<u32>,
    }
}

impl Node {
    pub fn error() -> Node {
        Self::composite(Some(ERROR))
    }

    pub fn composite(ty: Option<NodeType>) -> Node {
        Node::Composite { ty: ty, children: Vec::new(), layer: None }
    }

    pub fn layer(layer: u32) -> Node {
        Node::Composite { ty: None, children: Vec::new(), layer: Some(layer) }
    }

    pub fn success<'t>(ts: TokenSequence<'t>) -> (Node, TokenSequence<'t>) {
        (Self::composite(None), ts)
    }
}

impl<'a> TokenSequence<'a> {
    pub fn prefix(&self, suffix: TokenSequence<'a>) -> TokenSequence<'a> {
        TokenSequence {
            text: self.text,
            start: self.start,
            non_ws_indexes: &self.non_ws_indexes[..suffix.start - self.start],
            original_tokens: self.original_tokens
        }
    }

    pub fn current(&self) -> Option<Token> {
        self.non_ws_indexes.first().map(|&idx| {
            self.original_tokens[idx]
        })
    }

    pub fn bump(&self) -> (Node, TokenSequence<'a>) {
        let token = self.current().expect("Can't bump an empty token sequence");
        let node = Node::Leaf(token.ty, self.non_ws_indexes[0]);
        let rest = TokenSequence {
            text: self.text,
            start: self.start + 1,
            non_ws_indexes: &self.non_ws_indexes[1..],
            original_tokens: self.original_tokens,
        };
        (node, rest)
    }
}

impl Node {
    pub fn push_child(&mut self, child: Node) {
        match *self {
            Node::Composite { ref mut children, .. } => children.push(child),
            Node::Leaf(..) => panic!("Can't add children to a leaf node"),
        }
    }

    pub fn debug(&self, tokens: &TokenSequence) -> String {
        let (l, r) = match (self.left_idx(), self.right_idx()) {
            (Some(l), Some(r)) => (l, r),
            _ => return "EMPTY-NODE".to_owned()
        };
        let mut result = String::new();
        let mut start = tokens.original_tokens[..l].iter().map(|t| t.len).sum::<TextUnit>();
        for t in tokens.original_tokens[l..r].iter() {
            result += &tokens.text[TextRange::from_len(start, t.len)];
            start += t.len;
        }
        result
    }

    fn right_idx(&self) -> Option<usize> {
        match *self {
            Node::Leaf(_, idx) => Some(idx),
            Node::Composite { ref children, .. } => children.iter().rev().filter_map(|n| {
                n.right_idx()
            }).next(),
        }
    }

    fn left_idx(&self) -> Option<usize> {
        match *self {
            Node::Leaf(_, idx) => Some(idx),
            Node::Composite { ref children, .. } => match children.first() {
                None => None,
                Some(child) => child.left_idx()
            },
        }
    }
}

pub fn parse(
    lang: Language,
    text: String,
    tokenizer: &[LexRule],
    parser: &Fn(TokenSequence, &mut FileStats) -> Node
) -> File {
    let mut stats = FileStats::new();
    let (lex_time, owned_tokens) = measure_time(|| tokenize(&text, tokenizer).collect::<Vec<_>>());
    stats.lexing_time = lex_time.duration();
    let non_ws_indexes: Vec<usize> = owned_tokens.iter().enumerate().filter_map(|(i, t)| {
        if t.ty == WHITESPACE { None } else { Some(i) }
    }).collect();
    let (parse_time, node) = {
        let tokens = TokenSequence {
            text: &text,
            start: 0,
            non_ws_indexes: &non_ws_indexes,
            original_tokens: &owned_tokens,
        };
        measure_time(|| parser(tokens, &mut stats))
    };
    stats.parsing_time = parse_time.duration();
    let pre_node = to_pre_node(node, &owned_tokens);

    let node = pre_node.into_immutable_node();
    File::new(lang, text, stats, node)
}

#[derive(Debug)]
struct PreNode {
    ty: NodeType,
    len: TextUnit,
    children: Vec<PreNode>,
    reparse_region: Option<ReparseRegion>,
    first: Option<usize>,
    last: Option<usize>
}

impl PreNode {
    fn push_child(&mut self, child: PreNode, tokens: &[Token]) {
        match (self.last, child.first) {
            (Some(l), Some(r)) if l + 1 < r => {
                for idx in l + 1..r {
                    let t = tokens[idx];
                    assert!(t.ty == WHITESPACE, "expected whitespace, got {:?}", t.ty);
                    self.push_child_raw(token_pre_node(idx, t))
                }
            }
            _ => {}
        }
        self.push_child_raw(child)
    }

    fn push_child_raw(&mut self, child: PreNode) {
        self.last = child.last.or(self.last);
        self.first = self.first.or(child.first);
        self.len += child.len;
        self.children.push(child);
    }

    fn into_immutable_node(self) -> ImmutableNode {
        if self.children.is_empty() {
            return ImmutableNode::new_leaf(self.ty, self.len)
        }
        let mut node = ImmutableNode::new(self.ty, self.reparse_region);
        for child in self.children {
            node.push_child(child.into_immutable_node());
        }
        node
    }
}

fn token_pre_node(idx: usize, t: Token) -> PreNode {
    PreNode {
        ty: t.ty,
        len: t.len,
        children: Vec::new(),
        reparse_region: None,
        first: Some(idx),
        last: Some(idx),
    }
}

fn to_pre_node(file_node: Node, tokens: &[Token]) -> PreNode {
    let (ty, children) = match file_node {
        Node::Composite { ty, children , .. } => (ty.unwrap(), children),
        _ => panic!("Root node must be composite")
    };
    let mut result = PreNode {
        ty: ty,
        len: TextUnit::zero(),
        children: Vec::new(),
        reparse_region: None,
        first: None,
        last: None,
    };

    for (i, &t) in tokens.iter().enumerate() {
        if t.ty != WHITESPACE {
            break
        }
        result.push_child_raw(token_pre_node(i, t))
    }

    for child in children {
        add_child(&mut result, &child, tokens)
    }
    if let Some(idx) = result.last {
        for idx in idx + 1..tokens.len() {
            let t = tokens[idx];
            assert!(t.ty == WHITESPACE);
            result.push_child_raw(token_pre_node(idx, t))
        }
    }
    result
}

fn add_child(parent: &mut PreNode, node: &Node, tokens: &[Token]) {
    match *node {
        Node::Leaf(_, idx) => {
            parent.push_child(token_pre_node(idx, tokens[idx]), tokens)
        }
        Node::Composite { ty, ref children, layer } => {
            if ty.is_none() {
                let mut children = children.iter();

                let first_child = children.next().unwrap();
                add_child(parent, first_child, tokens);

                let start: TextUnit = parent.children[..parent.children.len() - 1].iter()
                    .map(|child| child.len)
                    .sum();

                for child in children {
                    add_child(parent, child, tokens)
                }

                let end: TextUnit = parent.children.iter()
                    .map(|child| child.len)
                    .sum();

                if let Some(layer) = layer {
                    parent.reparse_region = Some(ReparseRegion {
                        range: TextRange::from_to(start, end),
                        parser_id: layer,
                    })
                }
                return
            }
            let ty = ty.unwrap();
            let mut p = PreNode {
                ty: ty,
                len: TextUnit::zero(),
                children: Vec::new(),
                reparse_region: None,
                first: None,
                last: None,
            };
            for child in children {
                add_child(&mut p, child, tokens);
            }
            parent.push_child(p, tokens)
        }
    }
}
<commit_msg>Rename<commit_after>use elapsed::measure_time;

use fall_tree::{Language, NodeType, File, ERROR, WHITESPACE, TextRange,
                FileStats, ImmutableNode, TextUnit, ReparseRegion};
use lex::{Token, LexRule, tokenize};

#[derive(Clone, Copy, Debug)]
pub struct TokenSequence<'a> {
    text: &'a str,
    start: usize,
    non_ws_indexes: &'a [usize],
    original_tokens: &'a [Token],
}

#[derive(Debug)]
pub enum Node {
    Leaf(NodeType, usize),
    Composite {
        ty: Option<NodeType>,
        children: Vec<Node>,
        layer: Option<u32>,
    }
}

impl Node {
    pub fn error() -> Node {
        Self::composite(Some(ERROR))
    }

    pub fn composite(ty: Option<NodeType>) -> Node {
        Node::Composite { ty: ty, children: Vec::new(), layer: None }
    }

    pub fn layer(layer: u32) -> Node {
        Node::Composite { ty: None, children: Vec::new(), layer: Some(layer) }
    }

    pub fn success<'t>(ts: TokenSequence<'t>) -> (Node, TokenSequence<'t>) {
        (Self::composite(None), ts)
    }
}

impl<'a> TokenSequence<'a> {
    pub fn prefix(&self, suffix: TokenSequence<'a>) -> TokenSequence<'a> {
        TokenSequence {
            text: self.text,
            start: self.start,
            non_ws_indexes: &self.non_ws_indexes[..suffix.start - self.start],
            original_tokens: self.original_tokens
        }
    }

    pub fn current(&self) -> Option<Token> {
        self.non_ws_indexes.first().map(|&idx| {
            self.original_tokens[idx]
        })
    }

    pub fn bump(&self) -> (Node, TokenSequence<'a>) {
        let token = self.current().expect("Can't bump an empty token sequence");
        let node = Node::Leaf(token.ty, self.non_ws_indexes[0]);
        let rest = TokenSequence {
            text: self.text,
            start: self.start + 1,
            non_ws_indexes: &self.non_ws_indexes[1..],
            original_tokens: self.original_tokens,
        };
        (node, rest)
    }
}

impl Node {
    pub fn push_child(&mut self, child: Node) {
        match *self {
            Node::Composite { ref mut children, .. } => children.push(child),
            Node::Leaf(..) => panic!("Can't add children to a leaf node"),
        }
    }

    pub fn debug(&self, tokens: &TokenSequence) -> String {
        let (l, r) = match (self.left_idx(), self.right_idx()) {
            (Some(l), Some(r)) => (l, r),
            _ => return "EMPTY-NODE".to_owned()
        };
        let mut result = String::new();
        let mut start = tokens.original_tokens[..l].iter().map(|t| t.len).sum::<TextUnit>();
        for t in tokens.original_tokens[l..r].iter() {
            result += &tokens.text[TextRange::from_len(start, t.len)];
            start += t.len;
        }
        result
    }

    fn right_idx(&self) -> Option<usize> {
        match *self {
            Node::Leaf(_, idx) => Some(idx),
            Node::Composite { ref children, .. } => children.iter().rev().filter_map(|n| {
                n.right_idx()
            }).next(),
        }
    }

    fn left_idx(&self) -> Option<usize> {
        match *self {
            Node::Leaf(_, idx) => Some(idx),
            Node::Composite { ref children, .. } => match children.first() {
                None => None,
                Some(child) => child.left_idx()
            },
        }
    }
}

pub fn parse(
    lang: Language,
    text: String,
    tokenizer: &[LexRule],
    parser: &Fn(TokenSequence, &mut FileStats) -> Node
) -> File {
    let mut stats = FileStats::new();
    let (lex_time, owned_tokens) = measure_time(|| tokenize(&text, tokenizer).collect::<Vec<_>>());
    stats.lexing_time = lex_time.duration();
    let non_ws_indexes: Vec<usize> = owned_tokens.iter().enumerate().filter_map(|(i, t)| {
        if t.ty == WHITESPACE { None } else { Some(i) }
    }).collect();
    let (parse_time, node) = {
        let tokens = TokenSequence {
            text: &text,
            start: 0,
            non_ws_indexes: &non_ws_indexes,
            original_tokens: &owned_tokens,
        };
        measure_time(|| parser(tokens, &mut stats))
    };
    stats.parsing_time = parse_time.duration();
    let pre_node = to_pre_node(node, &owned_tokens);

    let node = pre_node.into_immutable_node();
    File::new(lang, text, stats, node)
}

#[derive(Debug)]
struct WsNode {
    ty: NodeType,
    len: TextUnit,
    children: Vec<WsNode>,
    reparse_region: Option<ReparseRegion>,
    first: Option<usize>,
    last: Option<usize>
}

impl WsNode {
    fn push_child(&mut self, child: WsNode, tokens: &[Token]) {
        match (self.last, child.first) {
            (Some(l), Some(r)) if l + 1 < r => {
                for idx in l + 1..r {
                    let t = tokens[idx];
                    assert!(t.ty == WHITESPACE, "expected whitespace, got {:?}", t.ty);
                    self.push_child_raw(token_pre_node(idx, t))
                }
            }
            _ => {}
        }
        self.push_child_raw(child)
    }

    fn push_child_raw(&mut self, child: WsNode) {
        self.last = child.last.or(self.last);
        self.first = self.first.or(child.first);
        self.len += child.len;
        self.children.push(child);
    }

    fn into_immutable_node(self) -> ImmutableNode {
        if self.children.is_empty() {
            return ImmutableNode::new_leaf(self.ty, self.len)
        }
        let mut node = ImmutableNode::new(self.ty, self.reparse_region);
        for child in self.children {
            node.push_child(child.into_immutable_node());
        }
        node
    }
}

fn token_pre_node(idx: usize, t: Token) -> WsNode {
    WsNode {
        ty: t.ty,
        len: t.len,
        children: Vec::new(),
        reparse_region: None,
        first: Some(idx),
        last: Some(idx),
    }
}

fn to_pre_node(file_node: Node, tokens: &[Token]) -> WsNode {
    let (ty, children) = match file_node {
        Node::Composite { ty, children , .. } => (ty.unwrap(), children),
        _ => panic!("Root node must be composite")
    };
    let mut result = WsNode {
        ty: ty,
        len: TextUnit::zero(),
        children: Vec::new(),
        reparse_region: None,
        first: None,
        last: None,
    };

    for (i, &t) in tokens.iter().enumerate() {
        if t.ty != WHITESPACE {
            break
        }
        result.push_child_raw(token_pre_node(i, t))
    }

    for child in children {
        add_child(&mut result, &child, tokens)
    }
    if let Some(idx) = result.last {
        for idx in idx + 1..tokens.len() {
            let t = tokens[idx];
            assert!(t.ty == WHITESPACE);
            result.push_child_raw(token_pre_node(idx, t))
        }
    }
    result
}

fn add_child(parent: &mut WsNode, node: &Node, tokens: &[Token]) {
    match *node {
        Node::Leaf(_, idx) => {
            parent.push_child(token_pre_node(idx, tokens[idx]), tokens)
        }
        Node::Composite { ty, ref children, layer } => {
            if ty.is_none() {
                let mut children = children.iter();

                let first_child = children.next().unwrap();
                add_child(parent, first_child, tokens);

                let start: TextUnit = parent.children[..parent.children.len() - 1].iter()
                    .map(|child| child.len)
                    .sum();

                for child in children {
                    add_child(parent, child, tokens)
                }

                let end: TextUnit = parent.children.iter()
                    .map(|child| child.len)
                    .sum();

                if let Some(layer) = layer {
                    parent.reparse_region = Some(ReparseRegion {
                        range: TextRange::from_to(start, end),
                        parser_id: layer,
                    })
                }
                return
            }
            let ty = ty.unwrap();
            let mut p = WsNode {
                ty: ty,
                len: TextUnit::zero(),
                children: Vec::new(),
                reparse_region: None,
                first: None,
                last: None,
            };
            for child in children {
                add_child(&mut p, child, tokens);
            }
            parent.push_child(p, tokens)
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2019 DDN. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

pub(crate) mod corosync_conf;

use crate::{agent_error::ImlAgentError, systemd};
use elementtree::Element;
use futures::{future::try_join_all, try_join};
use iml_cmd::{CheckedCommandExt, Command};
use iml_fs::file_exists;
use iml_wire_types::{
    ComponentState, ConfigState, ResourceAgentInfo, ResourceAgentType, ServiceState,
};
use std::ffi::OsStr;

fn create(elem: &Element) -> ResourceAgentInfo {
    ResourceAgentInfo {
        agent: {
            ResourceAgentType::new(
                elem.get_attr("class"),
                elem.get_attr("provider"),
                elem.get_attr("type"),
            )
        },
        id: elem.get_attr("id").unwrap_or_default().to_string(),
        args: elem
            .find_all("instance_attributes")
            .map(|e| {
                e.find_all("nvpair").map(|nv| {
                    (
                        nv.get_attr("name").unwrap_or_default().to_string(),
                        nv.get_attr("value").unwrap_or_default().to_string(),
                    )
                })
            })
            .flatten()
            .collect(),
    }
}

pub(crate) async fn crm_attribute(args: Vec<String>) -> Result<String, ImlAgentError> {
    let o = Command::new("crm_attribute")
        .args(args)
        .checked_output()
        .await?;

    Ok(String::from_utf8_lossy(&o.stdout).to_string())
}

pub(crate) async fn crm_resource<I, S>(args: I) -> Result<String, ImlAgentError>
where
    I: IntoIterator<Item = S>,
    S: AsRef<OsStr>,
{
    let o = Command::new("crm_resource")
        .args(args)
        .checked_output()
        .await?;

    Ok(String::from_utf8_lossy(&o.stdout).to_string())
}

fn process_resource(output: &[u8]) -> Result<ResourceAgentInfo, ImlAgentError> {
    let element = Element::from_reader(output)?;

    Ok(create(&element))
}

pub async fn get_ha_resource_list(_: ()) -> Result<Vec<ResourceAgentInfo>, ImlAgentError> {
    let resources = crm_resource(&["--list-raw"]).await?;

    let xs = resources
        .trim()
        .lines()
        .filter(|res| {
            // This filters out cloned resources which show up as:
            // clones-resource:0
            // clones-resource:1
            // ...
            // ":" is not valid in normal resource ids so only ":0" should be processed
            if let Some(n) = res.split(':').nth(1) {
                n.parse() == Ok(0)
            } else {
                true
            }
        })
        .map(|res| async move {
            let output = crm_resource(&["--query-xml", "--resource", res]).await?;
            if let Some(xml) = output.split("xml:").last() {
                process_resource(xml.as_bytes())
            } else {
                Err(ImlAgentError::MarkerNotFound)
            }
        });
    try_join_all(xs).await
}

async fn systemd_unit_servicestate(name: &str) -> Result<ServiceState, ImlAgentError> {
    let n = format!("{}.service", name);
    match systemd::get_run_state(n).await {
        Ok(s) => Ok(ServiceState::Configured(s)),
        Err(err) => {
            tracing::debug!("Get Run State of {} failed: {:?}", name, err);
            Ok(ServiceState::Unconfigured)
        }
    }
}

async fn check_corosync() -> Result<ComponentState<bool>, ImlAgentError> {
    let mut corosync = ComponentState::<bool> {
        ..Default::default()
    };

    if file_exists("/etc/corosync/corosync.conf").await {
        let expected = r#"totem.interface.0.mcastaddr (str) = 226.94.0.1
totem.interface.1.mcastaddr (str) = 226.94.1.1
"#
        .as_bytes();
        let output = Command::new("corosync-cmapctl")
            .args(&["totem.interface.0.mcastaddr", "totem.interface.1.mcastaddr"])
            .checked_output()
            .await?;

        corosync.config = if output.stdout == expected {
            ConfigState::IML
        } else {
            ConfigState::Unknown
        };
        corosync.service = systemd_unit_servicestate("corosync").await?;
    }

    Ok(corosync)
}

async fn check_pacemaker() -> Result<ComponentState<bool>, ImlAgentError> {
    let mut pacemaker = ComponentState::<bool> {
        ..Default::default()
    };

    if file_exists("/var/lib/pacemaker/cib/cib.xml").await {
        pacemaker.service = systemd_unit_servicestate("pacemaker").await?;
        let resources = get_ha_resource_list(()).await?;
        if resources.is_empty() {
            pacemaker.config = ConfigState::Default;
        } else {
            pacemaker.config = ConfigState::Unknown;
        }
        for res in resources {
            if res.id == "st-fencing" && res.agent.ocftype == "fence_chroma" {
                pacemaker.config = ConfigState::IML;
                break;
            }
        }
    }

    Ok(pacemaker)
}

async fn check_pcs() -> Result<ComponentState<bool>, ImlAgentError> {
    let mut pcs = ComponentState::<bool> {
        ..Default::default()
    };

    if file_exists("/var/lib/pcsd/tokens").await {
        pcs.config = ConfigState::IML;
        pcs.service = systemd_unit_servicestate("pcsd").await?;
    }

    Ok(pcs)
}

pub async fn check_ha(
    _: (),
) -> Result<
    (
        ComponentState<bool>,
        ComponentState<bool>,
        ComponentState<bool>,
    ),
    ImlAgentError,
> {
    let corosync = check_corosync();
    let pacemaker = check_pacemaker();
    let pcs = check_pcs();

    try_join!(corosync, pacemaker, pcs)
}

pub(crate) async fn pcs(args: Vec<String>) -> Result<String, ImlAgentError> {
    let o = Command::new("pcs").args(args).checked_output().await?;

    Ok(String::from_utf8_lossy(&o.stdout).to_string())
}

#[cfg(test)]
mod tests {
    use super::{process_resource, ResourceAgentInfo, ResourceAgentType};
    use std::collections::HashMap;

    #[test]
    fn test_ha_only_fence_chroma() {
        let testxml = include_bytes!("fixtures/crm-resource-chroma-stonith.xml");

        assert_eq!(
            process_resource(testxml).unwrap(),
            ResourceAgentInfo {
                agent: ResourceAgentType::new("stonith", None, "fence_chroma"),
                id: "st-fencing".to_string(),
                args: HashMap::new()
            }
        );
    }

    #[test]
    fn test_iml() {
        let testxml = include_bytes!("fixtures/crm-resource-iml-mgs.xml");

        let r1 = ResourceAgentInfo {
            agent: ResourceAgentType::new("ocf", Some("lustre"), "Lustre"),
            id: "MGS".to_string(),
            args: vec![
                ("mountpoint", "/mnt/MGS"),
                (
                    "target",
                    "/dev/disk/by-id/scsi-36001405c20616f7b8b2492d8913a4d24",
                ),
            ]
            .iter()
            .map(|(k, v)| (k.to_string(), v.to_string()))
            .collect(),
        };

        assert_eq!(process_resource(testxml).unwrap(), r1);
    }

    #[test]
    fn test_es_ost() {
        let testxml = include_bytes!("fixtures/crm-resource-es-ost.xml");

        let r1 = ResourceAgentInfo {
            agent: ResourceAgentType::new("ocf", Some("ddn"), "lustre-server"),
            id: "ost0001-es01a".to_string(),
            args: vec![
                ("lustre_resource_type", "ost"),
                ("manage_directory", "1"),
                ("device", "/dev/ddn/es01a_ost0001"),
                ("directory", "/lustre/es01a/ost0001"),
            ]
            .iter()
            .map(|(k, v)| (k.to_string(), v.to_string()))
            .collect(),
        };

        assert_eq!(process_resource(testxml).unwrap(), r1);
    }
}
<commit_msg>Return empty list if resource list is empty in get_ha_resource_list (#1629)<commit_after>// Copyright (c) 2019 DDN. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

pub(crate) mod corosync_conf;

use crate::{agent_error::ImlAgentError, systemd};
use elementtree::Element;
use futures::{future::try_join_all, try_join};
use iml_cmd::{CheckedCommandExt, Command};
use iml_fs::file_exists;
use iml_wire_types::{
    ComponentState, ConfigState, ResourceAgentInfo, ResourceAgentType, ServiceState,
};
use std::ffi::OsStr;

fn create(elem: &Element) -> ResourceAgentInfo {
    ResourceAgentInfo {
        agent: {
            ResourceAgentType::new(
                elem.get_attr("class"),
                elem.get_attr("provider"),
                elem.get_attr("type"),
            )
        },
        id: elem.get_attr("id").unwrap_or_default().to_string(),
        args: elem
            .find_all("instance_attributes")
            .map(|e| {
                e.find_all("nvpair").map(|nv| {
                    (
                        nv.get_attr("name").unwrap_or_default().to_string(),
                        nv.get_attr("value").unwrap_or_default().to_string(),
                    )
                })
            })
            .flatten()
            .collect(),
    }
}

pub(crate) async fn crm_attribute(args: Vec<String>) -> Result<String, ImlAgentError> {
    let o = Command::new("crm_attribute")
        .args(args)
        .checked_output()
        .await?;

    Ok(String::from_utf8_lossy(&o.stdout).to_string())
}

pub(crate) async fn crm_resource<I, S>(args: I) -> Result<String, ImlAgentError>
where
    I: IntoIterator<Item = S>,
    S: AsRef<OsStr>,
{
    let o = Command::new("crm_resource")
        .args(args)
        .checked_output()
        .await?;

    Ok(String::from_utf8_lossy(&o.stdout).to_string())
}

fn process_resource(output: &[u8]) -> Result<ResourceAgentInfo, ImlAgentError> {
    let element = Element::from_reader(output)?;

    Ok(create(&element))
}

pub async fn get_ha_resource_list(_: ()) -> Result<Vec<ResourceAgentInfo>, ImlAgentError> {
    let resources = match crm_resource(&["--list-raw"]).await {
        Ok(res) => res,
        Err(e) => {
            tracing::info!("Failed to get resource list: {:?}", e);
            return Ok(vec![]);
        }
    };

    let xs = resources
        .trim()
        .lines()
        .filter(|res| {
            // This filters out cloned resources which show up as:
            // clones-resource:0
            // clones-resource:1
            // ...
            // ":" is not valid in normal resource ids so only ":0" should be processed
            if let Some(n) = res.split(':').nth(1) {
                n.parse() == Ok(0)
            } else {
                true
            }
        })
        .map(|res| async move {
            let output = crm_resource(&["--query-xml", "--resource", res]).await?;
            if let Some(xml) = output.split("xml:").last() {
                process_resource(xml.as_bytes())
            } else {
                Err(ImlAgentError::MarkerNotFound)
            }
        });
    try_join_all(xs).await
}

async fn systemd_unit_servicestate(name: &str) -> Result<ServiceState, ImlAgentError> {
    let n = format!("{}.service", name);
    match systemd::get_run_state(n).await {
        Ok(s) => Ok(ServiceState::Configured(s)),
        Err(err) => {
            tracing::debug!("Get Run State of {} failed: {:?}", name, err);
            Ok(ServiceState::Unconfigured)
        }
    }
}

async fn check_corosync() -> Result<ComponentState<bool>, ImlAgentError> {
    let mut corosync = ComponentState::<bool> {
        ..Default::default()
    };

    if file_exists("/etc/corosync/corosync.conf").await {
        let expected = r#"totem.interface.0.mcastaddr (str) = 226.94.0.1
totem.interface.1.mcastaddr (str) = 226.94.1.1
"#
        .as_bytes();
        let output = Command::new("corosync-cmapctl")
            .args(&["totem.interface.0.mcastaddr", "totem.interface.1.mcastaddr"])
            .checked_output()
            .await?;

        corosync.config = if output.stdout == expected {
            ConfigState::IML
        } else {
            ConfigState::Unknown
        };
        corosync.service = systemd_unit_servicestate("corosync").await?;
    }

    Ok(corosync)
}

async fn check_pacemaker() -> Result<ComponentState<bool>, ImlAgentError> {
    let mut pacemaker = ComponentState::<bool> {
        ..Default::default()
    };

    if file_exists("/var/lib/pacemaker/cib/cib.xml").await {
        pacemaker.service = systemd_unit_servicestate("pacemaker").await?;
        let resources = get_ha_resource_list(()).await?;
        if resources.is_empty() {
            pacemaker.config = ConfigState::Default;
        } else {
            pacemaker.config = ConfigState::Unknown;
        }
        for res in resources {
            if res.id == "st-fencing" && res.agent.ocftype == "fence_chroma" {
                pacemaker.config = ConfigState::IML;
                break;
            }
        }
    }

    Ok(pacemaker)
}

async fn check_pcs() -> Result<ComponentState<bool>, ImlAgentError> {
    let mut pcs = ComponentState::<bool> {
        ..Default::default()
    };

    if file_exists("/var/lib/pcsd/tokens").await {
        pcs.config = ConfigState::IML;
        pcs.service = systemd_unit_servicestate("pcsd").await?;
    }

    Ok(pcs)
}

pub async fn check_ha(
    _: (),
) -> Result<
    (
        ComponentState<bool>,
        ComponentState<bool>,
        ComponentState<bool>,
    ),
    ImlAgentError,
> {
    let corosync = check_corosync();
    let pacemaker = check_pacemaker();
    let pcs = check_pcs();

    try_join!(corosync, pacemaker, pcs)
}

pub(crate) async fn pcs(args: Vec<String>) -> Result<String, ImlAgentError> {
    let o = Command::new("pcs").args(args).checked_output().await?;

    Ok(String::from_utf8_lossy(&o.stdout).to_string())
}

#[cfg(test)]
mod tests {
    use super::{process_resource, ResourceAgentInfo, ResourceAgentType};
    use std::collections::HashMap;

    #[test]
    fn test_ha_only_fence_chroma() {
        let testxml = include_bytes!("fixtures/crm-resource-chroma-stonith.xml");

        assert_eq!(
            process_resource(testxml).unwrap(),
            ResourceAgentInfo {
                agent: ResourceAgentType::new("stonith", None, "fence_chroma"),
                id: "st-fencing".to_string(),
                args: HashMap::new()
            }
        );
    }

    #[test]
    fn test_iml() {
        let testxml = include_bytes!("fixtures/crm-resource-iml-mgs.xml");

        let r1 = ResourceAgentInfo {
            agent: ResourceAgentType::new("ocf", Some("lustre"), "Lustre"),
            id: "MGS".to_string(),
            args: vec![
                ("mountpoint", "/mnt/MGS"),
                (
                    "target",
                    "/dev/disk/by-id/scsi-36001405c20616f7b8b2492d8913a4d24",
                ),
            ]
            .iter()
            .map(|(k, v)| (k.to_string(), v.to_string()))
            .collect(),
        };

        assert_eq!(process_resource(testxml).unwrap(), r1);
    }

    #[test]
    fn test_es_ost() {
        let testxml = include_bytes!("fixtures/crm-resource-es-ost.xml");

        let r1 = ResourceAgentInfo {
            agent: ResourceAgentType::new("ocf", Some("ddn"), "lustre-server"),
            id: "ost0001-es01a".to_string(),
            args: vec![
                ("lustre_resource_type", "ost"),
                ("manage_directory", "1"),
                ("device", "/dev/ddn/es01a_ost0001"),
                ("directory", "/lustre/es01a/ost0001"),
            ]
            .iter()
            .map(|(k, v)| (k.to_string(), v.to_string()))
            .collect(),
        };

        assert_eq!(process_resource(testxml).unwrap(), r1);
    }
}
<|endoftext|>


<commit_before>//! Note: This crate is considered internal API of the `json_typegen` tools. If you want to use this
//! crate directly, and thus care about its stability, please
//! [open an issue](https://github.com/evestera/json_typegen/issues/new) to let me know.

#[macro_use]
extern crate error_chain;

#[cfg(feature = "local-samples")]
use std::fs::File;

use lazy_static::lazy_static;
use regex::Regex;
use serde_json::Value;

mod generation;
mod hints;
mod inference;
mod options;
pub mod parse;
mod shape;
mod util;

use crate::hints::Hints;
pub use crate::options::Options;
pub use crate::options::OutputMode;

mod errors {
    error_chain! {
        types {
            JTError, ErrorKind, ResultExt;
        }

        links {
        }

        foreign_links {
            ReqwestError(::reqwest::Error) #[cfg(feature = "remote-samples")];
            IoError(::std::io::Error) #[cfg(feature = "local-samples")];
            JsonError(::serde_json::Error);
        }

        errors {
            MissingSource {
                description("No source for sample specified")
            }
            ExistingType(t: String) {
                display("No code generated, JSON matches existing type {}", t)
            }
        }
    }
}

pub use crate::errors::*;

enum SampleSource<'a> {
    Url(&'a str),
    File(&'a str),
    Text(&'a str),
}

/// Generate code from a `json_typegen` macro invocation
pub fn codegen_from_macro(input: &str) -> Result<String, JTError> {
    let macro_input = parse::full_macro(input)?;

    codegen(
        &macro_input.name,
        &macro_input.sample_source,
        macro_input.options,
    )
}

/// Generate code from the arguments to a `json_typegen` macro invocation
pub fn codegen_from_macro_input(input: &str) -> Result<String, JTError> {
    let macro_input = parse::macro_input(input)?;

    codegen(
        &macro_input.name,
        &macro_input.sample_source,
        macro_input.options,
    )
}

/// The main code generation function for `json_typegen`
pub fn codegen(name: &str, input: &str, mut options: Options) -> Result<String, JTError> {
    let source = infer_source_type(input);
    let sample = get_and_parse_sample(&source)?;
    let name = handle_pub_in_name(name, &mut options);

    let mut hints_vec = Vec::new();
    std::mem::swap(&mut options.hints, &mut hints_vec);

    let mut hints = Hints::new();
    for (pointer, hint) in hints_vec.iter() {
        hints.add(pointer, hint);
    }

    let shape = inference::value_to_shape(&sample, &hints);

    let generated_code = if options.runnable {
        generation::rust::rust_program(name, &shape, options)
    } else {
        let (name, defs) = match options.output_mode {
            OutputMode::Rust => generation::rust::rust_types(name, &shape, options),
            OutputMode::JsonSchema => generation::json_schema::json_schema(name, &shape, options),
            OutputMode::Kotlin => generation::kotlin::kotlin_types(name, &shape, options),
            OutputMode::Shape => generation::shape::shape_string(name, &shape, options),
            OutputMode::Typescript => {
                generation::typescript::typescript_types(name, &shape, options)
            }
        };
        defs.ok_or_else(|| JTError::from(ErrorKind::ExistingType(name.to_string())))?
    };

    Ok(generated_code)
}

/// Parse "names" like `pub(crate) Foo` into a name and a visibility option
fn handle_pub_in_name<'a>(name: &'a str, options: &mut Options) -> &'a str {
    lazy_static! {
        static ref PUB_RE: Regex = Regex::new(
            r"(?x)
                pub ( \( (?P<restriction> [^)]+ ) \) )?
                \s+
                (?P<name> .+ )
            "
        )
        .unwrap();
    }
    match PUB_RE.captures(name) {
        Some(captures) => {
            options.type_visibility = match captures.name("restriction") {
                Some(restriction) => format!("pub({})", restriction.as_str()),
                None => "pub".into(),
            };
            captures.name("name").unwrap().as_str()
        }
        None => {
            // If there is no visibility specified here, we want to use whatever is set elsewhere
            name
        }
    }
}

fn infer_source_type(s: &str) -> SampleSource {
    let s = s.trim();
    if s.starts_with('{') || s.starts_with('[') {
        return SampleSource::Text(s);
    }
    if s.starts_with("http://") || s.starts_with("https://") {
        return SampleSource::Url(s);
    }
    SampleSource::File(s)
}

fn get_and_parse_sample(source: &SampleSource) -> Result<Value, JTError> {
    let parse_result = match *source {
        #[cfg(feature = "remote-samples")]
        SampleSource::Url(url) => serde_json::de::from_reader(reqwest::get(url)?),
        #[cfg(not(feature = "remote-samples"))]
        SampleSource::Url(_) => {
            return Err("Remote samples disabled".into());
        }

        #[cfg(feature = "local-samples")]
        SampleSource::File(path) => serde_json::de::from_reader(File::open(path)?),
        #[cfg(not(feature = "local-samples"))]
        SampleSource::File(_) => {
            return Err("Local samples disabled".into());
        }

        SampleSource::Text(text) => serde_json::from_str(text),
    };
    Ok(parse_result.chain_err(|| "Unable to parse JSON sample")?)
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_handle_pub_in_name() {
        let mut options = Options::default();
        let name = handle_pub_in_name("Foo", &mut options);
        assert_eq!(name, "Foo");
        assert_eq!(options.type_visibility, Options::default().type_visibility);
        let name = handle_pub_in_name("pub Foo", &mut options);
        assert_eq!(name, "Foo");
        assert_eq!(options.type_visibility, "pub".to_string());
        let name = handle_pub_in_name("pub(crate) Foo Bar", &mut options);
        assert_eq!(name, "Foo Bar");
        assert_eq!(options.type_visibility, "pub(crate)".to_string());
        let name = handle_pub_in_name("pub(some::path) Foo", &mut options);
        assert_eq!(name, "Foo");
        assert_eq!(options.type_visibility, "pub(some::path)".to_string());
    }
}
<commit_msg>Ensure a final newline in code output.<commit_after>//! Note: This crate is considered internal API of the `json_typegen` tools. If you want to use this
//! crate directly, and thus care about its stability, please
//! [open an issue](https://github.com/evestera/json_typegen/issues/new) to let me know.

#[macro_use]
extern crate error_chain;

#[cfg(feature = "local-samples")]
use std::fs::File;

use lazy_static::lazy_static;
use regex::Regex;
use serde_json::Value;

mod generation;
mod hints;
mod inference;
mod options;
pub mod parse;
mod shape;
mod util;

use crate::hints::Hints;
pub use crate::options::Options;
pub use crate::options::OutputMode;

mod errors {
    error_chain! {
        types {
            JTError, ErrorKind, ResultExt;
        }

        links {
        }

        foreign_links {
            ReqwestError(::reqwest::Error) #[cfg(feature = "remote-samples")];
            IoError(::std::io::Error) #[cfg(feature = "local-samples")];
            JsonError(::serde_json::Error);
        }

        errors {
            MissingSource {
                description("No source for sample specified")
            }
            ExistingType(t: String) {
                display("No code generated, JSON matches existing type {}", t)
            }
        }
    }
}

pub use crate::errors::*;

enum SampleSource<'a> {
    Url(&'a str),
    File(&'a str),
    Text(&'a str),
}

/// Generate code from a `json_typegen` macro invocation
pub fn codegen_from_macro(input: &str) -> Result<String, JTError> {
    let macro_input = parse::full_macro(input)?;

    codegen(
        &macro_input.name,
        &macro_input.sample_source,
        macro_input.options,
    )
}

/// Generate code from the arguments to a `json_typegen` macro invocation
pub fn codegen_from_macro_input(input: &str) -> Result<String, JTError> {
    let macro_input = parse::macro_input(input)?;

    codegen(
        &macro_input.name,
        &macro_input.sample_source,
        macro_input.options,
    )
}

/// The main code generation function for `json_typegen`
pub fn codegen(name: &str, input: &str, mut options: Options) -> Result<String, JTError> {
    let source = infer_source_type(input);
    let sample = get_and_parse_sample(&source)?;
    let name = handle_pub_in_name(name, &mut options);

    let mut hints_vec = Vec::new();
    std::mem::swap(&mut options.hints, &mut hints_vec);

    let mut hints = Hints::new();
    for (pointer, hint) in hints_vec.iter() {
        hints.add(pointer, hint);
    }

    let shape = inference::value_to_shape(&sample, &hints);

    let mut generated_code = if options.runnable {
        generation::rust::rust_program(name, &shape, options)
    } else {
        let (name, defs) = match options.output_mode {
            OutputMode::Rust => generation::rust::rust_types(name, &shape, options),
            OutputMode::JsonSchema => generation::json_schema::json_schema(name, &shape, options),
            OutputMode::Kotlin => generation::kotlin::kotlin_types(name, &shape, options),
            OutputMode::Shape => generation::shape::shape_string(name, &shape, options),
            OutputMode::Typescript => {
                generation::typescript::typescript_types(name, &shape, options)
            }
        };
        defs.ok_or_else(|| JTError::from(ErrorKind::ExistingType(name.to_string())))?
    };

    if !generated_code.ends_with("\n") {
        generated_code.push('\n');
    }

    Ok(generated_code)
}

/// Parse "names" like `pub(crate) Foo` into a name and a visibility option
fn handle_pub_in_name<'a>(name: &'a str, options: &mut Options) -> &'a str {
    lazy_static! {
        static ref PUB_RE: Regex = Regex::new(
            r"(?x)
                pub ( \( (?P<restriction> [^)]+ ) \) )?
                \s+
                (?P<name> .+ )
            "
        )
        .unwrap();
    }
    match PUB_RE.captures(name) {
        Some(captures) => {
            options.type_visibility = match captures.name("restriction") {
                Some(restriction) => format!("pub({})", restriction.as_str()),
                None => "pub".into(),
            };
            captures.name("name").unwrap().as_str()
        }
        None => {
            // If there is no visibility specified here, we want to use whatever is set elsewhere
            name
        }
    }
}

fn infer_source_type(s: &str) -> SampleSource {
    let s = s.trim();
    if s.starts_with('{') || s.starts_with('[') {
        return SampleSource::Text(s);
    }
    if s.starts_with("http://") || s.starts_with("https://") {
        return SampleSource::Url(s);
    }
    SampleSource::File(s)
}

fn get_and_parse_sample(source: &SampleSource) -> Result<Value, JTError> {
    let parse_result = match *source {
        #[cfg(feature = "remote-samples")]
        SampleSource::Url(url) => serde_json::de::from_reader(reqwest::get(url)?),
        #[cfg(not(feature = "remote-samples"))]
        SampleSource::Url(_) => {
            return Err("Remote samples disabled".into());
        }

        #[cfg(feature = "local-samples")]
        SampleSource::File(path) => serde_json::de::from_reader(File::open(path)?),
        #[cfg(not(feature = "local-samples"))]
        SampleSource::File(_) => {
            return Err("Local samples disabled".into());
        }

        SampleSource::Text(text) => serde_json::from_str(text),
    };
    Ok(parse_result.chain_err(|| "Unable to parse JSON sample")?)
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_handle_pub_in_name() {
        let mut options = Options::default();
        let name = handle_pub_in_name("Foo", &mut options);
        assert_eq!(name, "Foo");
        assert_eq!(options.type_visibility, Options::default().type_visibility);
        let name = handle_pub_in_name("pub Foo", &mut options);
        assert_eq!(name, "Foo");
        assert_eq!(options.type_visibility, "pub".to_string());
        let name = handle_pub_in_name("pub(crate) Foo Bar", &mut options);
        assert_eq!(name, "Foo Bar");
        assert_eq!(options.type_visibility, "pub(crate)".to_string());
        let name = handle_pub_in_name("pub(some::path) Foo", &mut options);
        assert_eq!(name, "Foo");
        assert_eq!(options.type_visibility, "pub(some::path)".to_string());
    }
}
<|endoftext|>


<commit_before>// Here we check that type ascription is syntactically invalid when
// not in the top position of a ascribing a let binding or function parameter.

// This has no effect.
// We include it to demonstrate that this is the case:
#![feature(type_ascription)]

fn main() {}

fn _ok() {
    let _a @ _b: u8 = 0; // OK.
    fn _f(_a @ _b: u8) {} // OK.
}

#[cfg(FALSE)]
fn case_1() {
    let a: u8 @ b = 0;
    //~^ ERROR expected one of `!`
}

#[cfg(FALSE)]
fn case_2() {
    let a @ (b: u8);
    //~^ ERROR expected one of `!`
    //~| ERROR expected one of `)`
}

#[cfg(FALSE)]
fn case_3() {
    let a: T1 @ Outer(b: T2);
    //~^ ERROR expected one of `!`
}
<commit_msg>Rollup merge of #89777 - pierwill:fix-88233, r=Mark-Simulacrum<commit_after>// Here we check that type ascription is syntactically invalid when
// not in the top position of an ascribing `let` binding or function parameter.

// This has no effect.
// We include it to demonstrate that this is the case:
#![feature(type_ascription)]

fn main() {}

fn _ok() {
    let _a @ _b: u8 = 0; // OK.
    fn _f(_a @ _b: u8) {} // OK.
}

#[cfg(FALSE)]
fn case_1() {
    let a: u8 @ b = 0;
    //~^ ERROR expected one of `!`
}

#[cfg(FALSE)]
fn case_2() {
    let a @ (b: u8);
    //~^ ERROR expected one of `!`
    //~| ERROR expected one of `)`
}

#[cfg(FALSE)]
fn case_3() {
    let a: T1 @ Outer(b: T2);
    //~^ ERROR expected one of `!`
}
<|endoftext|>


<commit_before>// edition:2018
// run-pass
// revisions: migrate mir
//[mir]compile-flags: -Z borrowck=mir

trait Trait<'a, 'b> { }
impl<T> Trait<'_, '_> for T { }

// Here we wind up selecting `'a` and `'b` in the hidden type because
// those are the types that appear inth e original values.

existential type Foo<'a, 'b>: Trait<'a, 'b>;

fn upper_bounds<'a, 'b>(a: &'a u8, b: &'b u8) -> Foo<'a, 'b> {
    // In this simple case, you have a hidden type `(&'0 u8, &'1 u8)` and constraints like
    //
    // ```
    // 'a: '0
    // 'b: '1
    // '0 in ['a, 'b]
    // '1 in ['a, 'b]
    // ```
    //
    // We use the fact that `'a: 0'` must hold (combined with the in
    // constraint) to determine that `'0 = 'a` must be the answer.
    (a, b)
}

fn main() { }
<commit_msg>test with explicit existential type<commit_after>// edition:2018
// run-pass
// revisions: migrate mir
//[mir]compile-flags: -Z borrowck=mir

#![feature(existential_type)]

trait Trait<'a, 'b> { }
impl<T> Trait<'_, '_> for T { }

// Here we wind up selecting `'a` and `'b` in the hidden type because
// those are the types that appear inth e original values.

existential type Foo<'a, 'b>: Trait<'a, 'b>;

fn upper_bounds<'a, 'b>(a: &'a u8, b: &'b u8) -> Foo<'a, 'b> {
    // In this simple case, you have a hidden type `(&'0 u8, &'1 u8)` and constraints like
    //
    // ```
    // 'a: '0
    // 'b: '1
    // '0 in ['a, 'b]
    // '1 in ['a, 'b]
    // ```
    //
    // We use the fact that `'a: 0'` must hold (combined with the in
    // constraint) to determine that `'0 = 'a` must be the answer.
    (a, b)
}

fn main() { }
<|endoftext|>


<commit_before>//! `PUT /_matrix/client/*/rooms/{roomId}/state/{eventType}/{stateKey}`

pub mod v3 {
    //! `/v3/` ([spec])
    //!
    //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#put_matrixclientv3roomsroomidstateeventtypestatekey

    use ruma_common::{
        api::ruma_api,
        events::{AnyStateEventContent, StateEventContent, StateEventType},
        serde::{Incoming, Raw},
        MilliSecondsSinceUnixEpoch, OwnedEventId, RoomId,
    };
    use serde_json::value::to_raw_value as to_raw_json_value;

    ruma_api! {
        metadata: {
            description: "Send a state event to a room associated with a given state key.",
            method: PUT,
            name: "send_state_event",
            r0_path: "/_matrix/client/r0/rooms/:room_id/state/:event_type/:state_key",
            stable_path: "/_matrix/client/v3/rooms/:room_id/state/:event_type/:state_key",
            rate_limited: false,
            authentication: AccessToken,
            added: 1.0,
        }

        response: {
            /// A unique identifier for the event.
            pub event_id: OwnedEventId,
        }

        error: crate::Error
    }

    /// Data for a request to the `send_state_event` API endpoint.
    ///
    /// Send a state event to a room associated with a given state key.
    #[derive(Clone, Debug, Incoming)]
    #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
    #[incoming_derive(!Deserialize)]
    pub struct Request<'a> {
        /// The room to set the state in.
        pub room_id: &'a RoomId,

        /// The type of event to send.
        pub event_type: StateEventType,

        /// The state_key for the state to send.
        pub state_key: &'a str,

        /// The event content to send.
        pub body: Raw<AnyStateEventContent>,

        /// Timestamp to use for the `origin_server_ts` of the event.
        ///
        /// This is called [timestamp massaging] and can only be used by Appservices.
        ///
        /// Note that this does not change the position of the event in the timeline.
        ///
        /// [timestamp massaging]: https://spec.matrix.org/v1.3/application-service-api/#timestamp-massaging
        pub timestamp: Option<MilliSecondsSinceUnixEpoch>,
    }

    impl<'a> Request<'a> {
        /// Creates a new `Request` with the given room id, state key and event content.
        ///
        /// # Errors
        ///
        /// Since `Request` stores the request body in serialized form, this function can fail if
        /// `T`s [`Serialize`][serde::Serialize] implementation can fail.
        pub fn new<T>(
            room_id: &'a RoomId,
            state_key: &'a str,
            content: &'a T,
        ) -> serde_json::Result<Self>
        where
            T: StateEventContent,
        {
            Ok(Self {
                room_id,
                state_key,
                event_type: content.event_type(),
                body: Raw::from_json(to_raw_json_value(content)?),
                timestamp: None,
            })
        }

        /// Creates a new `Request` with the given room id, event type, state key and raw event
        /// content.
        pub fn new_raw(
            room_id: &'a RoomId,
            event_type: StateEventType,
            state_key: &'a str,
            body: Raw<AnyStateEventContent>,
        ) -> Self {
            Self { room_id, event_type, state_key, body, timestamp: None }
        }
    }

    impl Response {
        /// Creates a new `Response` with the given event id.
        pub fn new(event_id: OwnedEventId) -> Self {
            Self { event_id }
        }
    }

    #[cfg(feature = "client")]
    impl<'a> ruma_common::api::OutgoingRequest for Request<'a> {
        type EndpointError = crate::Error;
        type IncomingResponse = Response;

        const METADATA: ruma_common::api::Metadata = METADATA;

        fn try_into_http_request<T: Default + bytes::BufMut>(
            self,
            base_url: &str,
            access_token: ruma_common::api::SendAccessToken<'_>,
            considering_versions: &'_ [ruma_common::api::MatrixVersion],
        ) -> Result<http::Request<T>, ruma_common::api::error::IntoHttpError> {
            use std::borrow::Cow;

            use http::header::{self, HeaderValue};
            use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};

            let room_id_percent = utf8_percent_encode(self.room_id.as_str(), NON_ALPHANUMERIC);
            let event_type = self.event_type.to_string();
            let event_type_percent = utf8_percent_encode(&event_type, NON_ALPHANUMERIC);

            let mut url = format!(
                "{}{}",
                base_url.strip_suffix('/').unwrap_or(base_url),
                ruma_common::api::select_path(
                    considering_versions,
                    &METADATA,
                    None,
                    Some(format_args!(
                        "/_matrix/client/r0/rooms/{room_id_percent}/state/{event_type_percent}",
                    )),
                    Some(format_args!(
                        "/_matrix/client/v3/rooms/{room_id_percent}/state/{event_type_percent}",
                    )),
                )?
            );

            // Last URL segment is optional, that is why this trait impl is not generated.
            if !self.state_key.is_empty() {
                url.push('/');
                url.push_str(&Cow::from(utf8_percent_encode(self.state_key, NON_ALPHANUMERIC)));
            }

            let request_query = RequestQuery { timestamp: self.timestamp };
            url.push('?');
            url.push_str(&ruma_common::serde::urlencoded::to_string(request_query)?);

            let http_request = http::Request::builder()
                .method(http::Method::PUT)
                .uri(url)
                .header(header::CONTENT_TYPE, "application/json")
                .header(
                    header::AUTHORIZATION,
                    HeaderValue::from_str(&format!(
                        "Bearer {}",
                        access_token
                            .get_required_for_endpoint()
                            .ok_or(ruma_common::api::error::IntoHttpError::NeedsAuthentication)?
                    ))?,
                )
                .body(ruma_common::serde::json_to_buf(&self.body)?)?;

            Ok(http_request)
        }
    }

    #[cfg(feature = "server")]
    impl ruma_common::api::IncomingRequest for IncomingRequest {
        type EndpointError = crate::Error;
        type OutgoingResponse = Response;

        const METADATA: ruma_common::api::Metadata = METADATA;

        fn try_from_http_request<B, S>(
            request: http::Request<B>,
            path_args: &[S],
        ) -> Result<Self, ruma_common::api::error::FromHttpRequestError>
        where
            B: AsRef<[u8]>,
            S: AsRef<str>,
        {
            use ruma_common::OwnedRoomId;

            // FIXME: find a way to make this if-else collapse with serde recognizing trailing
            // Option
            let (room_id, event_type, state_key): (OwnedRoomId, StateEventType, String) =
                if path_args.len() == 3 {
                    serde::Deserialize::deserialize(serde::de::value::SeqDeserializer::<
                        _,
                        serde::de::value::Error,
                    >::new(
                        path_args.iter().map(::std::convert::AsRef::as_ref),
                    ))?
                } else {
                    let (a, b) =
                        serde::Deserialize::deserialize(serde::de::value::SeqDeserializer::<
                            _,
                            serde::de::value::Error,
                        >::new(
                            path_args.iter().map(::std::convert::AsRef::as_ref),
                        ))?;

                    (a, b, "".into())
                };

            let request_query: RequestQuery =
                ruma_common::serde::urlencoded::from_str(request.uri().query().unwrap_or(""))?;

            let body = serde_json::from_slice(request.body().as_ref())?;

            Ok(Self { room_id, event_type, state_key, body, timestamp: request_query.timestamp })
        }
    }

    /// Data in the request's query string.
    #[derive(Debug)]
    #[cfg_attr(feature = "client", derive(serde::Serialize))]
    #[cfg_attr(feature = "server", derive(serde::Deserialize))]
    struct RequestQuery {
        /// Timestamp to use for the `origin_server_ts` of the event.
        #[serde(
            alias = "org.matrix.msc3316.ts",
            rename = "ts",
            skip_serializing_if = "Option::is_none"
        )]
        pub timestamp: Option<MilliSecondsSinceUnixEpoch>,
    }
}
<commit_msg>client-api: Use a stronger type for send_state_event::Request::new<commit_after>//! `PUT /_matrix/client/*/rooms/{roomId}/state/{eventType}/{stateKey}`

pub mod v3 {
    //! `/v3/` ([spec])
    //!
    //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#put_matrixclientv3roomsroomidstateeventtypestatekey

    use std::borrow::Borrow;

    use ruma_common::{
        api::ruma_api,
        events::{AnyStateEventContent, StateEventContent, StateEventType},
        serde::{Incoming, Raw},
        MilliSecondsSinceUnixEpoch, OwnedEventId, RoomId,
    };
    use serde_json::value::to_raw_value as to_raw_json_value;

    ruma_api! {
        metadata: {
            description: "Send a state event to a room associated with a given state key.",
            method: PUT,
            name: "send_state_event",
            r0_path: "/_matrix/client/r0/rooms/:room_id/state/:event_type/:state_key",
            stable_path: "/_matrix/client/v3/rooms/:room_id/state/:event_type/:state_key",
            rate_limited: false,
            authentication: AccessToken,
            added: 1.0,
        }

        response: {
            /// A unique identifier for the event.
            pub event_id: OwnedEventId,
        }

        error: crate::Error
    }

    /// Data for a request to the `send_state_event` API endpoint.
    ///
    /// Send a state event to a room associated with a given state key.
    #[derive(Clone, Debug, Incoming)]
    #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
    #[incoming_derive(!Deserialize)]
    pub struct Request<'a> {
        /// The room to set the state in.
        pub room_id: &'a RoomId,

        /// The type of event to send.
        pub event_type: StateEventType,

        /// The state_key for the state to send.
        pub state_key: &'a str,

        /// The event content to send.
        pub body: Raw<AnyStateEventContent>,

        /// Timestamp to use for the `origin_server_ts` of the event.
        ///
        /// This is called [timestamp massaging] and can only be used by Appservices.
        ///
        /// Note that this does not change the position of the event in the timeline.
        ///
        /// [timestamp massaging]: https://spec.matrix.org/v1.3/application-service-api/#timestamp-massaging
        pub timestamp: Option<MilliSecondsSinceUnixEpoch>,
    }

    impl<'a> Request<'a> {
        /// Creates a new `Request` with the given room id, state key and event content.
        ///
        /// # Errors
        ///
        /// Since `Request` stores the request body in serialized form, this function can fail if
        /// `T`s [`Serialize`][serde::Serialize] implementation can fail.
        pub fn new<T, K>(
            room_id: &'a RoomId,
            state_key: &'a K,
            content: &'a T,
        ) -> serde_json::Result<Self>
        where
            T: StateEventContent,
            T::StateKey: Borrow<K>,
            K: AsRef<str> + ?Sized,
        {
            Ok(Self {
                room_id,
                state_key: state_key.as_ref(),
                event_type: content.event_type(),
                body: Raw::from_json(to_raw_json_value(content)?),
                timestamp: None,
            })
        }

        /// Creates a new `Request` with the given room id, event type, state key and raw event
        /// content.
        pub fn new_raw(
            room_id: &'a RoomId,
            event_type: StateEventType,
            state_key: &'a str,
            body: Raw<AnyStateEventContent>,
        ) -> Self {
            Self { room_id, event_type, state_key, body, timestamp: None }
        }
    }

    impl Response {
        /// Creates a new `Response` with the given event id.
        pub fn new(event_id: OwnedEventId) -> Self {
            Self { event_id }
        }
    }

    #[cfg(feature = "client")]
    impl<'a> ruma_common::api::OutgoingRequest for Request<'a> {
        type EndpointError = crate::Error;
        type IncomingResponse = Response;

        const METADATA: ruma_common::api::Metadata = METADATA;

        fn try_into_http_request<T: Default + bytes::BufMut>(
            self,
            base_url: &str,
            access_token: ruma_common::api::SendAccessToken<'_>,
            considering_versions: &'_ [ruma_common::api::MatrixVersion],
        ) -> Result<http::Request<T>, ruma_common::api::error::IntoHttpError> {
            use std::borrow::Cow;

            use http::header::{self, HeaderValue};
            use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};

            let room_id_percent = utf8_percent_encode(self.room_id.as_str(), NON_ALPHANUMERIC);
            let event_type = self.event_type.to_string();
            let event_type_percent = utf8_percent_encode(&event_type, NON_ALPHANUMERIC);

            let mut url = format!(
                "{}{}",
                base_url.strip_suffix('/').unwrap_or(base_url),
                ruma_common::api::select_path(
                    considering_versions,
                    &METADATA,
                    None,
                    Some(format_args!(
                        "/_matrix/client/r0/rooms/{room_id_percent}/state/{event_type_percent}",
                    )),
                    Some(format_args!(
                        "/_matrix/client/v3/rooms/{room_id_percent}/state/{event_type_percent}",
                    )),
                )?
            );

            // Last URL segment is optional, that is why this trait impl is not generated.
            if !self.state_key.is_empty() {
                url.push('/');
                url.push_str(&Cow::from(utf8_percent_encode(self.state_key, NON_ALPHANUMERIC)));
            }

            let request_query = RequestQuery { timestamp: self.timestamp };
            url.push('?');
            url.push_str(&ruma_common::serde::urlencoded::to_string(request_query)?);

            let http_request = http::Request::builder()
                .method(http::Method::PUT)
                .uri(url)
                .header(header::CONTENT_TYPE, "application/json")
                .header(
                    header::AUTHORIZATION,
                    HeaderValue::from_str(&format!(
                        "Bearer {}",
                        access_token
                            .get_required_for_endpoint()
                            .ok_or(ruma_common::api::error::IntoHttpError::NeedsAuthentication)?
                    ))?,
                )
                .body(ruma_common::serde::json_to_buf(&self.body)?)?;

            Ok(http_request)
        }
    }

    #[cfg(feature = "server")]
    impl ruma_common::api::IncomingRequest for IncomingRequest {
        type EndpointError = crate::Error;
        type OutgoingResponse = Response;

        const METADATA: ruma_common::api::Metadata = METADATA;

        fn try_from_http_request<B, S>(
            request: http::Request<B>,
            path_args: &[S],
        ) -> Result<Self, ruma_common::api::error::FromHttpRequestError>
        where
            B: AsRef<[u8]>,
            S: AsRef<str>,
        {
            use ruma_common::OwnedRoomId;

            // FIXME: find a way to make this if-else collapse with serde recognizing trailing
            // Option
            let (room_id, event_type, state_key): (OwnedRoomId, StateEventType, String) =
                if path_args.len() == 3 {
                    serde::Deserialize::deserialize(serde::de::value::SeqDeserializer::<
                        _,
                        serde::de::value::Error,
                    >::new(
                        path_args.iter().map(::std::convert::AsRef::as_ref),
                    ))?
                } else {
                    let (a, b) =
                        serde::Deserialize::deserialize(serde::de::value::SeqDeserializer::<
                            _,
                            serde::de::value::Error,
                        >::new(
                            path_args.iter().map(::std::convert::AsRef::as_ref),
                        ))?;

                    (a, b, "".into())
                };

            let request_query: RequestQuery =
                ruma_common::serde::urlencoded::from_str(request.uri().query().unwrap_or(""))?;

            let body = serde_json::from_slice(request.body().as_ref())?;

            Ok(Self { room_id, event_type, state_key, body, timestamp: request_query.timestamp })
        }
    }

    /// Data in the request's query string.
    #[derive(Debug)]
    #[cfg_attr(feature = "client", derive(serde::Serialize))]
    #[cfg_attr(feature = "server", derive(serde::Deserialize))]
    struct RequestQuery {
        /// Timestamp to use for the `origin_server_ts` of the event.
        #[serde(
            alias = "org.matrix.msc3316.ts",
            rename = "ts",
            skip_serializing_if = "Option::is_none"
        )]
        pub timestamp: Option<MilliSecondsSinceUnixEpoch>,
    }
}
<|endoftext|>


<commit_before>/**
Katy Williamson kcw4sn
Ben Terner bt3ze
@virginia.edu

Final Project for CS4414 - Operating Systems
A parallel MST implementation on images in Rust

*/

/*
some documentation:
Pixel object is composed of r,g,b values, a color, and an x,y index that takes the place of its adjacency list
Point is just an (x,y) value to denote a pixel's location
Edge contains two points: source and dest, along with the edge cost between them

*/

extern mod extra;
use std::{os, num, hashmap} ;
use extra::{arc,priority_queue};

struct Pixel{
    r: int,
    b: int,
    g: int,
    color: int,
    x: int,
    y: int
}

impl Pixel {
    fn new(red: int, blue: int, green: int, x: int, y: int) -> Pixel {
        Pixel{
            r: red,
            b: blue,
            g: green,
            color: -1,
            x: x,
            y: y
        }
    }
}

impl Clone for Pixel{
    fn clone(&self) -> Pixel {
        Pixel{
            r: self.r,
            b: self.b,
            g: self.g,
            color: self.color,
            x: self.x,
            y: self.y
        }
    }
}

struct Point {
    x: int,
    y: int
}

impl Point{
    fn new(x: int, y: int)-> Point {
        Point {
            x: x,
            y: y
        }
    }
}

struct Edge {
    source: Point,
    dest: Point,
    cost: float
}

impl Edge {
    fn new(s: Point, d: Point, cost: float) -> Edge {
        Edge {
            source: s,
            dest: d,
            cost: cost
        }
    }
}

impl Ord for Edge {
    fn lt(&self, other: &Edge) -> bool {
        self.cost < other.cost
    }
}

fn edgeCost(a: &Pixel, b: &Pixel) -> float {
    num::sqrt( (
            (a.r-b.r)*(a.r-b.r) + (a.g-b.g)*(a.g-b.g) + (a.b-b.b)*(a.b-b.b) ) as float)
}

fn readImage( filename: &~str) -> ~ [ ~[Pixel]] {
    let mut retval: ~[~[Pixel]] = ~[];

    let file: &str = filename.to_owned();
    let file2: ~str = file.to_owned();
    let split_filename: ~[&str] = file.split_iter('.').collect();
    let file_data: &str = split_filename[0] + ".txt";

    println(fmt!("The image name is [%?]", file));
    println(fmt!("The image data will be stored at [%?]", file_data));

    //read in the .txt associated with the file title
    let path = &PosixPath(file_data);

    match std::io::file_reader(path) {
	Ok(reader) => {
	    //run the java program to process the image
/*	    let program: &str = "java ImageReader";
	    let argv: ~[~str] = ~[file2];
	    let mut prog = std::run::Process::new(program, argv,
		std::run::ProcessOptions {
			env: None,
			dir: None,
			in_fd: None,
			out_fd: None,
			err_fd: None
		}
	    );
	    prog.finish();
*/
	    //parse out the array dimensions
    	    let dimensions_str: &str = reader.read_line();
    	    println(fmt!("dimensons: %s",dimensions_str));
	    let mut h: int = 0;
	    let mut w: int = 0;
	    match dimensions_str[0] as char {
		'H' => {
		    let split_dim: ~[&str] = dimensions_str.split_iter('W').collect();
		    h = from_str(split_dim[0].slice(1,split_dim[0].len())).unwrap();
		    w = from_str(split_dim[1]).unwrap();
		    println(fmt!("Dimensions passed: H %? W %?",h,w));
		    //let retval: ~[ ~[Pixel, ..w], ..h];
		}
		_ => {println("Dimensions corrupted! Abort");}
	    }

	    //fill in the retVal array with pixel data
            //let mut rowfirst = false;
	    for row in range(0,h){
//                if(!rowfirst) {
                retval.push(~[]);
                //rowfirst = true; }
		for col in range(0,w){
	    	    if !reader.eof() {
			let line: &str = reader.read_line();
			//println(line);
			let rpos:uint = match line.find('R') { Some(n)=>n,None=>line.len() };
			let gpos:uint = match line.find('G') { Some(n)=>n,None=>line.len() };
			let bpos:uint = match line.find('B') { Some(n)=>n,None=>line.len() };

			if rpos == line.len() || gpos == line.len() || bpos == line.len() {
			    println("Pixel data corrupted! Abort");
			}

			let R:int = from_str(line.slice(rpos+1,gpos)).unwrap();
			let G:int = from_str(line.slice(gpos+1,bpos)).unwrap();
			let B:int = from_str(line.slice(bpos+1,line.len())).unwrap();

			//println(fmt!("R %? G %? B %?",R,G,B));

			//fn new(red: int, blue: int, green: int, x: int, y: int) -> Pixel {
			retval[row].push(Pixel::new(R,G,B,col,row));

		    }
		    else {
			println("Data file corrupted! Abort");
		    }
		}
	    }
	},
	Err(err)   => { fail!(err) }
    }

    retval
}

fn main(){
    let argv =  os::args();
    println(argv.to_str());

    let pixels: ~[ ~[Pixel] ] = readImage(&argv[1]);
    let height = pixels.len() as int;
    let width = pixels[0].len() as int;

    println(fmt!("Dimensions received: H %? W %?",height,width));

    let mut find_arcs: ~[ ~[arc::RWArc<Pixel>] ] =  ~[];
    for h in range(0,height){
	find_arcs.push(~[]);
        for w in range(0,width){
 //           println(fmt!("pix %?",pixels[h][w]));
	    find_arcs[h].push(arc::RWArc::new(pixels[h][w]));
	}
    }

    let arcs = find_arcs; // now we have an immutable array that can be shared

    let corners = [ (0,0),(width-1,0),(0,height-1),(width-1,height-1) ];

    let mut color_index = 0;
    for &corner in corners.iter(){
        let (a,b) = corner;
        do arcs[a][b].write |pix| {
            pix.color = color_index;
        }
        color_index+=1;
    }

    for &corner in corners.iter() {
        let (a,b) = corner;
        let shared_arcs = arcs.clone();
        do spawn { // split into threads here
            let c = a;
            let d = b;
            let mut x=a;
            let mut y=b;

            let mut queue: priority_queue::PriorityQueue<Edge> =  priority_queue::PriorityQueue::new();
            let mut visited: hashmap::HashMap<(int,int),bool> = hashmap::HashMap::new();
            let mut newvisit: bool = true;

            println(fmt!("start at (%i,%i)",c,d));

            loop { // iterate through items in the queue, which contains all of the edges/vertices
                if(newvisit){
                   println(fmt!("(%i,%i) : (%i,%i)",c,d,x,y));
                    let neighbors = [ (x,y-1),(x+1,y), (x,y+1), (x-1,y)];
                    for &coord in neighbors.iter() {
                        let (w,z) = coord;
                        if w >=0 && w < width && z >=0 && z < height { // bounds checking
                            // if neighbor at coord has not been or colored
                            let mut newvertex = false;
                            let mut uncolored = false;
                            do shared_arcs[z][w].read |dest| {
                                if dest.color < 0 {
                                    uncolored = true; }
                            }
                            if uncolored {
                                do shared_arcs[z][w].write |dest| {
                                    if dest.color < 0 {
                                        do shared_arcs[y][x].read |src| {
                                            dest.color = src.color;
                                        }
                                        newvertex = true;
                                    }
                                }
                            }
                            if newvertex { // then we have a new vertex
                                do shared_arcs[y][x].read |src| {
                                    do shared_arcs[z][w].read |dest| {
                                        queue.push( Edge::new(Point::new(x,y),Point::new(w,z),edgeCost(src,dest)));
                                    }
                                }
                            }
                        }
                    }
                }

                let edge = queue.maybe_pop();
                match edge {
                    Some(e) => {
//                        println(fmt!("(%i,%i) pop (%i,%i)-%f-(%i,%i)",c,d,e.source.x,e.source.y,e.cost,e.dest.x,e.dest.y));
                        // not in any tree
                        let coord = (e.dest.x,e.dest.y);
//                        println(fmt!("before runtime error? %?",coord));
                        if !visited.contains_key(&coord){
 // use visited hashmap to figure out if we're at a new vertex.
                            visited.insert(coord,true);
                            x = e.dest.x;
                            y = e.dest.y;
                            newvisit = true;
//                            println(fmt!("%? %f",coord,e.cost));
                        } else {
                            newvisit = false;
                        }
                    },
                    None => { break; } // this should execute at the end when there are no more unvisited or uncolored nodes for a thread
                }
            }

        }
    }
}
<commit_msg>appears to be working as expected.<commit_after>/**
Katy Williamson kcw4sn
Ben Terner bt3ze
@virginia.edu

Final Project for CS4414 - Operating Systems
A parallel MST implementation on images in Rust

*/

/*
some documentation:
Pixel object is composed of r,g,b values, a color, and an x,y index that takes the place of its adjacency list
Point is just an (x,y) value to denote a pixel's location
Edge contains two points: source and dest, along with the edge cost between them

*/

extern mod extra;
use std::{os, num, hashmap} ;
use extra::{arc,priority_queue};

struct Pixel{
    r: int,
    b: int,
    g: int,
    color: int,
    x: int,
    y: int
}

impl Pixel {
    fn new(red: int, blue: int, green: int, x: int, y: int) -> Pixel {
        Pixel{
            r: red,
            b: blue,
            g: green,
            color: -1,
            x: x,
            y: y
        }
    }
}

impl Clone for Pixel{
    fn clone(&self) -> Pixel {
        Pixel{
            r: self.r,
            b: self.b,
            g: self.g,
            color: self.color,
            x: self.x,
            y: self.y
        }
    }
}

struct Point {
    x: int,
    y: int
}

impl Point{
    fn new(x: int, y: int)-> Point {
        Point {
            x: x,
            y: y
        }
    }
}

struct Edge {
    source: Point,
    dest: Point,
    cost: float
}

impl Edge {
    fn new(s: Point, d: Point, cost: float) -> Edge {
        Edge {
            source: s,
            dest: d,
            cost: cost
        }
    }
}

impl Ord for Edge {
    fn lt(&self, other: &Edge) -> bool {
        self.cost < other.cost
    }
}

fn edgeCost(a: &Pixel, b: &Pixel) -> float {
    num::sqrt( (
            (a.r-b.r)*(a.r-b.r) + (a.g-b.g)*(a.g-b.g) + (a.b-b.b)*(a.b-b.b) ) as float)
}

fn readImage( filename: &~str) -> ~ [ ~[Pixel]] {
    let mut retval: ~[~[Pixel]] = ~[];

    let file: &str = filename.to_owned();
    let file2: ~str = file.to_owned();
    let split_filename: ~[&str] = file.split_iter('.').collect();
    let file_data: &str = split_filename[0] + ".txt";

    println(fmt!("The image name is [%?]", file));
    println(fmt!("The image data will be stored at [%?]", file_data));

    //read in the .txt associated with the file title
    let path = &PosixPath(file_data);

    match std::io::file_reader(path) {
	Ok(reader) => {
	    //run the java program to process the image
/*	    let program: &str = "java ImageReader";
	    let argv: ~[~str] = ~[file2];
	    let mut prog = std::run::Process::new(program, argv,
		std::run::ProcessOptions {
			env: None,
			dir: None,
			in_fd: None,
			out_fd: None,
			err_fd: None
		}
	    );
	    prog.finish();
*/
	    //parse out the array dimensions
    	    let dimensions_str: &str = reader.read_line();
    	    println(fmt!("dimensons: %s",dimensions_str));
	    let mut h: int = 0;
	    let mut w: int = 0;
	    match dimensions_str[0] as char {
		'H' => {
		    let split_dim: ~[&str] = dimensions_str.split_iter('W').collect();
		    h = from_str(split_dim[0].slice(1,split_dim[0].len())).unwrap();
		    w = from_str(split_dim[1]).unwrap();
		    println(fmt!("Dimensions passed: H %? W %?",h,w));
		    //let retval: ~[ ~[Pixel, ..w], ..h];
		}
		_ => {println("Dimensions corrupted! Abort");}
	    }

	    //fill in the retVal array with pixel data
            //let mut rowfirst = false;
	    for row in range(0,h){
//                if(!rowfirst) {
                retval.push(~[]);
                //rowfirst = true; }
		for col in range(0,w){
	    	    if !reader.eof() {
			let line: &str = reader.read_line();
			//println(line);
			let rpos:uint = match line.find('R') { Some(n)=>n,None=>line.len() };
			let gpos:uint = match line.find('G') { Some(n)=>n,None=>line.len() };
			let bpos:uint = match line.find('B') { Some(n)=>n,None=>line.len() };

			if rpos == line.len() || gpos == line.len() || bpos == line.len() {
			    println("Pixel data corrupted! Abort");
			}

			let R:int = from_str(line.slice(rpos+1,gpos)).unwrap();
			let G:int = from_str(line.slice(gpos+1,bpos)).unwrap();
			let B:int = from_str(line.slice(bpos+1,line.len())).unwrap();

			//println(fmt!("R %? G %? B %?",R,G,B));

			//fn new(red: int, blue: int, green: int, x: int, y: int) -> Pixel {
			retval[row].push(Pixel::new(R,G,B,col,row));

		    }
		    else {
			println("Data file corrupted! Abort");
		    }
		}
	    }
	},
	Err(err)   => { fail!(err) }
    }

    retval
}

fn main(){
    let argv =  os::args();
    println(argv.to_str());

    let pixels: ~[ ~[Pixel] ] = readImage(&argv[1]);
    let height = pixels.len() as int;
    let width = pixels[0].len() as int;

    println(fmt!("Dimensions received: H %? W %?",height,width));

    let mut find_arcs: ~[ ~[arc::RWArc<Pixel>] ] =  ~[];
    for h in range(0,height){
	find_arcs.push(~[]);
        for w in range(0,width){
 //           println(fmt!("pix %?",pixels[h][w]));
	    find_arcs[h].push(arc::RWArc::new(pixels[h][w]));
	}
    }

    let arcs = find_arcs; // now we have an immutable array that can be shared

    let corners = [ (0,0),(width-1,0),(0,height-1),(width-1,height-1) ];

    let mut color_index = 0;
    for &corner in corners.iter(){
        let (a,b) = corner;
        do arcs[b][a].write |pix| {
            pix.color = color_index;
        }
        color_index+=1;
    }

    for &corner in corners.iter() {
        let (a,b) = corner;
        let shared_arcs = arcs.clone();
        do spawn { // split into threads here
            let c = a;
            let d = b;
            let mut x=a;
            let mut y=b;

            let mut queue: priority_queue::PriorityQueue<Edge> =  priority_queue::PriorityQueue::new();
            let mut visited: hashmap::HashMap<(int,int),bool> = hashmap::HashMap::new();
            let mut newvisit: bool = true;

            println(fmt!("start at (%i,%i)",c,d));

            loop { // iterate through items in the queue, which contains all of the edges/vertices
                if(newvisit){
                   println(fmt!("(%i,%i) : (%i,%i)",c,d,x,y));
                    let neighbors = [ (x,y-1),(x+1,y), (x,y+1), (x-1,y)];
                    for &coord in neighbors.iter() {
                        let (w,z) = coord;
                        if w >=0 && w < width && z >=0 && z < height { // bounds checking
                            // if neighbor at coord has not been or colored
                            let mut newvertex = false;
                            let mut uncolored = false;
                            do shared_arcs[z][w].read |dest| {
                                if dest.color < 0 {
                                    uncolored = true; }
                            }
                            if uncolored {
                                do shared_arcs[z][w].write |dest| {
                                    if dest.color < 0 {
                                        do shared_arcs[y][x].read |src| {
                                            dest.color = src.color;
                                        }
                                        newvertex = true;
                                    }
                                }
                            }
                            if newvertex { // then we have a new vertex
                                do shared_arcs[y][x].read |src| {
                                    do shared_arcs[z][w].read |dest| {
                                        queue.push( Edge::new(Point::new(x,y),Point::new(w,z),edgeCost(src,dest)));
                                    }
                                }
                            }
                        }
                    }
                }

                let edge = queue.maybe_pop();
                match edge {
                    Some(e) => {
//                        println(fmt!("(%i,%i) pop (%i,%i)-%f-(%i,%i)",c,d,e.source.x,e.source.y,e.cost,e.dest.x,e.dest.y));
                        // not in any tree
                        let coord = (e.dest.x,e.dest.y);
//                        println(fmt!("before runtime error? %?",coord));
                        if !visited.contains_key(&coord){
 // use visited hashmap to figure out if we're at a new vertex.
                            visited.insert(coord,true);
                            x = e.dest.x;
                            y = e.dest.y;
                            newvisit = true;
//                            println(fmt!("%? %f",coord,e.cost));
                        } else {
                            newvisit = false;
                        }
                    },
                    None => { break; } // this should execute at the end when there are no more unvisited or uncolored nodes for a thread
                }
            }

        }
    }
}
<|endoftext|>