<commit_before>// Rust TNetStrings serialization library.

use std;
import option::{some, none};
import std::map;
import std::rand;

export t;
export to_str;
export from_str;

/*
Tag: t

Represents a tnetstring value.
*/
tag t {
    /* Variant: str */
    str(str);
    /* Variant: int */
    int(int);
    /* Variant: float */
    float(float);
    /* Variant: bool */
    bool(bool);
    /* Variant: null */
    null;
    /* Variant: map */
    map(map::hashmap<str, t>);
    /* Variant: vec */
    vec(@[t]);
}

/*
Function: to_str

Serializes a tnetstring value into a string.
*/
fn to_str(t: t) -> str {
    alt t {
        str(s) { #fmt("%u:%s,", str::byte_len(s), s) }
        int(i) {
            let s = int::str(i);
            #fmt("%u:%s#", str::byte_len(s), s)
        }
        float(f) {
            let s = #fmt("%f", f);
            #fmt("%u:%s^", str::byte_len(s), s)
        }
        bool(b) {
            let s = bool::to_str(b);
            #fmt("%u:%s!", str::byte_len(s), s)
        }
        map(m) {
            let result = [];
            vec::reserve(result, m.size() * 2u);
            m.items({ |k, v|
                vec::push(result, to_str(str(k)));
                vec::push(result, to_str(v));
            });
            let payload = str::concat(result);
            #fmt("%u:%s}", str::byte_len(payload), payload)
        }
        vec(@l) {
            let result = vec::map(l, { |e| to_str(e) });
            let payload = str::concat(result);
            #fmt("%u:%s]", str::byte_len(payload), payload)
        }
        null { "0:~" }
    }
}

/*
Function: from_str

Deserializes a tnetstring value from a string.
*/
fn from_str(data: str) -> (option::t<t>, str) {
    let (payload, payload_type, remain) = from_str_payload(data);

    let value = alt payload_type {
        '#' { some(int(int::from_str(payload))) }
        '}' { some(map(from_str_map(payload))) }
        ']' { some(vec(from_str_vec(payload))) }
        '!' { some(bool(bool::from_str(payload))) }
        '^' { some(float(float::from_str(payload))) }
        '~' {
            assert str::byte_len(payload) == 0u;
            some(null)
        }
        ',' { some(str(payload)) }
        _ {
            let s = str::from_char(payload_type);
            fail #fmt("Invalid payload type: %s", str::escape(s))
        }
    };

    ret (value, remain);
}

fn from_str_payload(data: str) -> (str, char, str) {
    assert data != "";

    let parts = str::splitn(data, ':' as u8, 1u);
    let (length, extra) = alt vec::len(parts) {
        0u { ("", "") }
        1u { (parts[0], "") }
        2u { (parts[0], parts[1]) }
        _ { fail }
    };
    let length = uint::from_str(length);

    let payload = str::slice(extra, 0u, length);
    let extra = str::slice(extra, length, str::byte_len(extra));
    assert extra != "";

    let payload_type = str::char_at(extra, 0u);
    let remain = str::slice(extra, 1u, str::byte_len(extra));

    assert str::byte_len(payload) == length;
    ret (payload, payload_type, remain);
}

fn from_str_vec(data: str) -> @[t] {
    if str::byte_len(data) == 0u { ret @[]; }

    let (value, extra) = from_str(data);
    let result = [option::get(value)];

    while extra != "" {
        let (value, e) = from_str(extra);
        extra = e;
        vec::push(result, option::get(value));
    }

    ret @result;
}

fn from_str_pair(data: str) -> (t, t, str) {
    let (key, extra) = from_str(data);
    assert option::is_some(key);
    assert extra != "";

    let (value, extra) = from_str(extra);
    assert option::is_some(value);

    ret (option::get(key), option::get(value), extra);
}

fn from_str_map(data: str) -> map::hashmap<str, t> {
    if str::byte_len(data) == 0u { ret map::new_str_hash(); }

    let (key, value, extra) = from_str_pair(data);

    let key = alt key {
        str(key) { key }
        _ { fail "Keys can only be strings." }
    };

    let result = map::new_str_hash();
    result.insert(key, value);

    while extra != "" {
        let (key, value, e) = from_str_pair(extra);
        extra = e;

        let key = alt key {
            str(key) { key }
            _ { fail "Keys can only be strings." }
        };

        result.insert(key, value);
    }

    ret result;
}

fn eq(t0: t, t1: t) -> bool {
    alt (t0, t1) {
        (str(s0), str(s1)) { s0 == s1 }
        (int(i0), int(i1)) { i0 == i1 }
        (float(f0), float(f1)) { f0 == f1 }
        (bool(b0), bool(b1)) { b0 == b1 }
        (null., null.) { true }
        (map(d0), map(d1)) {
            if d0.size() == d1.size() {
                let equal = true;
                d0.items() { |k, v|
                    if !d1.contains_key(k) || !eq(d1.get(k), v) {
                        equal = false;
                    }
                };
                equal
            } else {
                false
            }
        }
        (vec(l0), vec(l1)) {
            vec::all2(*l0, *l1, eq)
        }
        _ { false }
    }
}

#[cfg(test)]
mod tests {
    // Tests inspired by https://github.com/rfk/tnetstring.

    fn test(string: str, expected: t) {
        let (actual, rest) = from_str(string);
        assert option::is_some(actual);
        assert rest == "";

        let actual = option::get(actual);
        assert eq(actual, expected);
        assert to_str(expected) == string;
    }

    #[test]
    fn test_format() {
        test("11:hello world,", str("hello world"));
        test("0:}", map(map::new_str_hash()));
        test("0:]", vec(@[]));

        let d = map::new_str_hash();
        d.insert("hello",
                vec(@[
                    int(12345678901),
                    str("this"),
                    bool(true),
                    null,
                    str("\x00\x00\x00\x00")]));

        test("51:5:hello,39:11:12345678901#4:this,4:true!0:~4:\x00\x00\x00" +
            "\x00,]}", map(d));

        test("5:12345#", int(12345));
        test("12:this is cool,", str("this is cool"));
        test("0:,", str(""));
        test("0:~", null);
        test("4:true!", bool(true));
        test("5:false!", bool(false));
        test("10:\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00,",
            str("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"));
        test("24:5:12345#5:67890#5:xxxxx,]",
            vec(@[int(12345), int(67890), str("xxxxx")]));
        test("18:3:0.1^3:0.2^3:0.4^]",
           vec(@[float(0.1), float(0.2), float(0.4)]));
        test("243:238:233:228:223:218:213:208:203:198:193:188:183:178:173:" +
            "168:163:158:153:148:143:138:133:128:123:118:113:108:103:99:95:" +
            "91:87:83:79:75:71:67:63:59:55:51:47:43:39:35:31:27:23:19:15:" +
            "11:hello-there,]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]" +
            "]]]]",
            vec(
                @[vec(@[vec(@[vec(@[vec(@[vec(@[vec(@[vec(@[vec(
                @[vec(@[vec(@[vec(@[vec(@[vec(@[vec(@[vec(@[vec(
                @[vec(@[vec(@[vec(@[vec(@[vec(@[vec(@[vec(@[vec(
                @[vec(@[vec(@[vec(@[vec(@[vec(@[vec(@[vec(@[vec(
                @[vec(@[vec(@[vec(@[vec(@[vec(@[vec(@[vec(@[vec(
                @[vec(@[vec(@[vec(@[vec(@[vec(@[vec(@[vec(@[vec(
                @[vec(@[vec(@[
                    str("hello-there")
                ])])])])])])])])])])])])])])])])])])])])])])])])])])])])
                ])])])])])])])])])])])])])])])])])])])])])])]));
    }

    #[test]
    fn test_random() {
        fn randint(rng: rand::rng, a: u32, b: u32) -> u32 {
            (rng.next() % (b - a + 1u32)) + a
        }

        fn get_random_object(rng: rand::rng, depth: u32) -> t {
            if randint(rng, depth, 10u32) <= 4u32 {
                if randint(rng, 0u32, 1u32) == 0u32 {
                    let n = randint(rng, 0u32, 10u32);
                    vec(@vec::init_fn(
                        { |_i| get_random_object(rng, depth + 1u32) },
                        n as uint))
                } else {
                    let d = map::new_str_hash();

                    let i = randint(rng, 0u32, 10u32);
                    while i != 0u32 {
                        let s = rng.gen_str(randint(rng, 0u32, 100u32) as uint);
                        d.insert(s, get_random_object(rng, depth + 1u32));
                        i -= 1u32;
                    }
                    map(d)
                }
            } else {
                alt randint(rng, 0u32, 4u32) {
                    0u32 { null }
                    1u32 { bool(true) }
                    2u32 { bool(false) }
                    3u32 {
                        if randint(rng, 0u32, 1u32) == 0u32 {
                            int(rng.next() as int)
                        } else {
                            int(-rng.next() as int)
                        }
                    }
                    /*
                    Disable the float tests for now since it's hard to exactly
                    compare floats.
                    4u32 {
                        if randint(rng, 0u32, 1u32) == 0u32 {
                            float(rng.next_float())
                        } else {
                            float(-rng.next_float())
                        }
                    }
                    */
                    4u32 {
                        str(rng.gen_str(randint(rng, 0u32, 100u32) as uint))
                    }
                    _ { fail }
                }
            }
        }

        let rng = rand::mk_rng();

        let i = 500u;
        while i != 0u {
            let v0 = get_random_object(rng, 0u32);

            alt from_str(to_str(v0)) {
                (some(v1), "") {
                    if !eq(v0, v1) {
                        log_err v0;
                        log_err v1;
                    }
                    assert eq(v0, v1);
                }
                _ { fail; }
            }
            i -= 1u;
        }
    }
}
<commit_msg>Export the tnetstring tags.<commit_after>// Rust TNetStrings serialization library.

use std;
import option::{some, none};
import std::map;
import std::rand;

export t;
export to_str;
export from_str;
export str;
export int;
export float;
export bool;
export null;
export map;
export vec;

/*
Tag: t

Represents a tnetstring value.
*/
tag t {
    /* Variant: str */
    str(str);
    /* Variant: int */
    int(int);
    /* Variant: float */
    float(float);
    /* Variant: bool */
    bool(bool);
    /* Variant: null */
    null;
    /* Variant: map */
    map(map::hashmap<str, t>);
    /* Variant: vec */
    vec(@[t]);
}

/*
Function: to_str

Serializes a tnetstring value into a string.
*/
fn to_str(t: t) -> str {
    alt t {
        str(s) { #fmt("%u:%s,", str::byte_len(s), s) }
        int(i) {
            let s = int::str(i);
            #fmt("%u:%s#", str::byte_len(s), s)
        }
        float(f) {
            let s = #fmt("%f", f);
            #fmt("%u:%s^", str::byte_len(s), s)
        }
        bool(b) {
            let s = bool::to_str(b);
            #fmt("%u:%s!", str::byte_len(s), s)
        }
        map(m) {
            let result = [];
            vec::reserve(result, m.size() * 2u);
            m.items({ |k, v|
                vec::push(result, to_str(str(k)));
                vec::push(result, to_str(v));
            });
            let payload = str::concat(result);
            #fmt("%u:%s}", str::byte_len(payload), payload)
        }
        vec(@l) {
            let result = vec::map(l, { |e| to_str(e) });
            let payload = str::concat(result);
            #fmt("%u:%s]", str::byte_len(payload), payload)
        }
        null { "0:~" }
    }
}

/*
Function: from_str

Deserializes a tnetstring value from a string.
*/
fn from_str(data: str) -> (option::t<t>, str) {
    let (payload, payload_type, remain) = from_str_payload(data);

    let value = alt payload_type {
        '#' { some(int(int::from_str(payload))) }
        '}' { some(map(from_str_map(payload))) }
        ']' { some(vec(from_str_vec(payload))) }
        '!' { some(bool(bool::from_str(payload))) }
        '^' { some(float(float::from_str(payload))) }
        '~' {
            assert str::byte_len(payload) == 0u;
            some(null)
        }
        ',' { some(str(payload)) }
        _ {
            let s = str::from_char(payload_type);
            fail #fmt("Invalid payload type: %s", str::escape(s))
        }
    };

    ret (value, remain);
}

fn from_str_payload(data: str) -> (str, char, str) {
    assert data != "";

    let parts = str::splitn(data, ':' as u8, 1u);
    let (length, extra) = alt vec::len(parts) {
        0u { ("", "") }
        1u { (parts[0], "") }
        2u { (parts[0], parts[1]) }
        _ { fail }
    };
    let length = uint::from_str(length);

    let payload = str::slice(extra, 0u, length);
    let extra = str::slice(extra, length, str::byte_len(extra));
    assert extra != "";

    let payload_type = str::char_at(extra, 0u);
    let remain = str::slice(extra, 1u, str::byte_len(extra));

    assert str::byte_len(payload) == length;
    ret (payload, payload_type, remain);
}

fn from_str_vec(data: str) -> @[t] {
    if str::byte_len(data) == 0u { ret @[]; }

    let (value, extra) = from_str(data);
    let result = [option::get(value)];

    while extra != "" {
        let (value, e) = from_str(extra);
        extra = e;
        vec::push(result, option::get(value));
    }

    ret @result;
}

fn from_str_pair(data: str) -> (t, t, str) {
    let (key, extra) = from_str(data);
    assert option::is_some(key);
    assert extra != "";

    let (value, extra) = from_str(extra);
    assert option::is_some(value);

    ret (option::get(key), option::get(value), extra);
}

fn from_str_map(data: str) -> map::hashmap<str, t> {
    if str::byte_len(data) == 0u { ret map::new_str_hash(); }

    let (key, value, extra) = from_str_pair(data);

    let key = alt key {
        str(key) { key }
        _ { fail "Keys can only be strings." }
    };

    let result = map::new_str_hash();
    result.insert(key, value);

    while extra != "" {
        let (key, value, e) = from_str_pair(extra);
        extra = e;

        let key = alt key {
            str(key) { key }
            _ { fail "Keys can only be strings." }
        };

        result.insert(key, value);
    }

    ret result;
}

fn eq(t0: t, t1: t) -> bool {
    alt (t0, t1) {
        (str(s0), str(s1)) { s0 == s1 }
        (int(i0), int(i1)) { i0 == i1 }
        (float(f0), float(f1)) { f0 == f1 }
        (bool(b0), bool(b1)) { b0 == b1 }
        (null., null.) { true }
        (map(d0), map(d1)) {
            if d0.size() == d1.size() {
                let equal = true;
                d0.items() { |k, v|
                    if !d1.contains_key(k) || !eq(d1.get(k), v) {
                        equal = false;
                    }
                };
                equal
            } else {
                false
            }
        }
        (vec(l0), vec(l1)) {
            vec::all2(*l0, *l1, eq)
        }
        _ { false }
    }
}

#[cfg(test)]
mod tests {
    // Tests inspired by https://github.com/rfk/tnetstring.

    fn test(string: str, expected: t) {
        let (actual, rest) = from_str(string);
        assert option::is_some(actual);
        assert rest == "";

        let actual = option::get(actual);
        assert eq(actual, expected);
        assert to_str(expected) == string;
    }

    #[test]
    fn test_format() {
        test("11:hello world,", str("hello world"));
        test("0:}", map(map::new_str_hash()));
        test("0:]", vec(@[]));

        let d = map::new_str_hash();
        d.insert("hello",
                vec(@[
                    int(12345678901),
                    str("this"),
                    bool(true),
                    null,
                    str("\x00\x00\x00\x00")]));

        test("51:5:hello,39:11:12345678901#4:this,4:true!0:~4:\x00\x00\x00" +
            "\x00,]}", map(d));

        test("5:12345#", int(12345));
        test("12:this is cool,", str("this is cool"));
        test("0:,", str(""));
        test("0:~", null);
        test("4:true!", bool(true));
        test("5:false!", bool(false));
        test("10:\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00,",
            str("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"));
        test("24:5:12345#5:67890#5:xxxxx,]",
            vec(@[int(12345), int(67890), str("xxxxx")]));
        test("18:3:0.1^3:0.2^3:0.4^]",
           vec(@[float(0.1), float(0.2), float(0.4)]));
        test("243:238:233:228:223:218:213:208:203:198:193:188:183:178:173:" +
            "168:163:158:153:148:143:138:133:128:123:118:113:108:103:99:95:" +
            "91:87:83:79:75:71:67:63:59:55:51:47:43:39:35:31:27:23:19:15:" +
            "11:hello-there,]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]" +
            "]]]]",
            vec(
                @[vec(@[vec(@[vec(@[vec(@[vec(@[vec(@[vec(@[vec(
                @[vec(@[vec(@[vec(@[vec(@[vec(@[vec(@[vec(@[vec(
                @[vec(@[vec(@[vec(@[vec(@[vec(@[vec(@[vec(@[vec(
                @[vec(@[vec(@[vec(@[vec(@[vec(@[vec(@[vec(@[vec(
                @[vec(@[vec(@[vec(@[vec(@[vec(@[vec(@[vec(@[vec(
                @[vec(@[vec(@[vec(@[vec(@[vec(@[vec(@[vec(@[vec(
                @[vec(@[vec(@[
                    str("hello-there")
                ])])])])])])])])])])])])])])])])])])])])])])])])])])])])
                ])])])])])])])])])])])])])])])])])])])])])])]));
    }

    #[test]
    fn test_random() {
        fn randint(rng: rand::rng, a: u32, b: u32) -> u32 {
            (rng.next() % (b - a + 1u32)) + a
        }

        fn get_random_object(rng: rand::rng, depth: u32) -> t {
            if randint(rng, depth, 10u32) <= 4u32 {
                if randint(rng, 0u32, 1u32) == 0u32 {
                    let n = randint(rng, 0u32, 10u32);
                    vec(@vec::init_fn(
                        { |_i| get_random_object(rng, depth + 1u32) },
                        n as uint))
                } else {
                    let d = map::new_str_hash();

                    let i = randint(rng, 0u32, 10u32);
                    while i != 0u32 {
                        let s = rng.gen_str(randint(rng, 0u32, 100u32) as uint);
                        d.insert(s, get_random_object(rng, depth + 1u32));
                        i -= 1u32;
                    }
                    map(d)
                }
            } else {
                alt randint(rng, 0u32, 4u32) {
                    0u32 { null }
                    1u32 { bool(true) }
                    2u32 { bool(false) }
                    3u32 {
                        if randint(rng, 0u32, 1u32) == 0u32 {
                            int(rng.next() as int)
                        } else {
                            int(-rng.next() as int)
                        }
                    }
                    /*
                    Disable the float tests for now since it's hard to exactly
                    compare floats.
                    4u32 {
                        if randint(rng, 0u32, 1u32) == 0u32 {
                            float(rng.next_float())
                        } else {
                            float(-rng.next_float())
                        }
                    }
                    */
                    4u32 {
                        str(rng.gen_str(randint(rng, 0u32, 100u32) as uint))
                    }
                    _ { fail }
                }
            }
        }

        let rng = rand::mk_rng();

        let i = 500u;
        while i != 0u {
            let v0 = get_random_object(rng, 0u32);

            alt from_str(to_str(v0)) {
                (some(v1), "") {
                    if !eq(v0, v1) {
                        log_err v0;
                        log_err v1;
                    }
                    assert eq(v0, v1);
                }
                _ { fail; }
            }
            i -= 1u;
        }
    }
}
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::collections::BTreeMap;
use std::borrow::Borrow;
use std::rc::Rc;
use std::cell::RefCell;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use std::error::Error;

use dbus::{Connection, NameFlag};
use dbus::tree::{Factory, Tree, Property, MethodFn, MethodErr, EmitsChangedSignal};
use dbus::MessageItem;

use froyo::Froyo;
use blockdev::BlockMember;
use types::FroyoResult;

#[derive(Debug, Clone)]
pub struct DbusContext<'a> {
    name_prop: Arc<Property<MethodFn<'a>>>,
    pub remaining_prop: Arc<Property<MethodFn<'a>>>,
    pub total_prop: Arc<Property<MethodFn<'a>>>,
    pub status_prop: Arc<Property<MethodFn<'a>>>,
    pub running_status_prop: Arc<Property<MethodFn<'a>>>,
    pub block_devices_prop: Arc<Property<MethodFn<'a>>>,
}

impl<'a> DbusContext<'a> {
    pub fn update_one(prop: &Arc<Property<MethodFn<'a>>>, m: MessageItem)
                      -> FroyoResult<()> {
        prop.set_value(m);
        // TODO: result is signals we need to be sending for PropertyChanged???
        Ok(())
    }

    pub fn get_block_devices_msgitem(block_devs: &BTreeMap<String, BlockMember>)
                                     -> MessageItem {
        let mut msg_vec = Vec::new();
        for bd in block_devs.values() {
            let (bd_path, bd_status) = match *bd {
                BlockMember::Present(ref bd) =>
                // TODO: in-use vs not-in-use
                    (RefCell::borrow(&bd).path.to_string_lossy().into_owned(), 0u32),
                BlockMember::Absent(ref sbd) =>
                    (sbd.path.to_string_lossy().into_owned(), 3u32),
            };

            let entry = MessageItem::Struct(vec![bd_path.into(), bd_status.into()]);
            msg_vec.push(entry);
        }

        MessageItem::new_array(msg_vec)
            .expect("Froyodev with no blockdev members???")
    }
}

pub fn get_tree<'a>(c: &Connection, froyos: &mut Rc<RefCell<Vec<Rc<RefCell<Froyo<'a>>>>>>)
                       -> FroyoResult<Tree<MethodFn<'a>>> {
    c.register_name("org.freedesktop.Froyo1", NameFlag::ReplaceExisting as u32).unwrap();

    let f = Factory::new_fn();

    let froyos_closed_over = froyos.clone();
    let mut froyos = RefCell::borrow_mut(&froyos);

    let create_method = f.method("Create", move |m,_,_| {
        let mut items = m.get_items();
        if items.len() < 3 {
            return Err(MethodErr::no_arg())
        }

        let force: bool = try!(items.pop().ok_or_else(MethodErr::no_arg)
                               .and_then(|i| i.inner()
                                         .map_err(|_| MethodErr::invalid_arg(&i))));
        let blockdevs = match try!(items.pop().ok_or_else(MethodErr::no_arg)) {
            MessageItem::Array(x, _) => x,
            x => return Err(MethodErr::invalid_arg(&x)),
        };
        let blockdevs = blockdevs.into_iter()
            .map(|x| PathBuf::from(x.inner::<&str>().unwrap()))
            .collect::<Vec<_>>();

        let name = try!(items.pop().ok_or_else(MethodErr::no_arg)
                        .and_then(|i| i.inner::<&str>()
                                  .map_err(|_| MethodErr::invalid_arg(&i))
                                  .map(|i| i.to_owned())));

        let froyo = match Froyo::new(&name, &blockdevs, force) {
            Ok(x) => x,
            Err(_) => return Err(MethodErr::failed(&"Froyo create failed")),
        };
        try!(froyo.save_state()
             .map_err(|err| {
                 let msg = format!("Saving state failed: {}",
                                   err.description());
                 MethodErr::failed(&msg)
             }));

        let s = format!("/org/freedesktop/froyo/{}", froyo.id);
        let mr = m.method_return().append(s);

        RefCell::borrow_mut(&froyos_closed_over).push(Rc::new(RefCell::new(froyo)));

        Ok(vec![mr])
    })
        .in_arg(("name", "s"))
        .in_arg(("blockdevs", "as"))
        .in_arg(("force", "b"))
        .out_arg(("obj_path", "s"));

    let tree = f.tree();
    let tree = tree
        .add(f.object_path("/org/freedesktop/froyo")
             .introspectable()
             .object_manager()
             .add(f.interface("org.freedesktop.FroyoService1")
                  .add_m(create_method)
             ));

    let tree = froyos
        .iter_mut()
        .fold(tree, |tree, froyo| {

            let mut iface = f.interface("org.freedesktop.FroyoDevice1");
            let name_p = iface.add_p_ref(f.property(
                "Name", RefCell::borrow(&*froyo).name.to_owned()));
            let p_closed_over = name_p.clone();
            let froyo_closed_over = froyo.clone();
            let mut iface = iface.add_m(
                f.method("SetName", move |m,_,_| {
                    let mut items = m.get_items();
                    if items.len() < 1 {
                        return Err(MethodErr::no_arg())
                    }

                    let name = try!(items.pop().ok_or_else(MethodErr::no_arg)
                                    .and_then(|i| i.inner::<&str>()
                                              .map_err(|_| MethodErr::invalid_arg(&i))
                                              .map(|i| i.to_owned())));

                    let mut froyo = RefCell::borrow_mut(&*froyo_closed_over);
                    froyo.name = name.clone();
                    try!(froyo.save_state()
                         .map_err(|err| {
                             let msg = format!("Saving state failed: {}",
                                               err.description());
                             MethodErr::failed(&msg)
                         }));

                    try!(p_closed_over.set_value(name.into())
                         .map_err(|_| MethodErr::invalid_arg(&"name")));
                    Ok(vec![m.method_return()])
                })
                    .in_arg(("new_name", "s")));

            let rem_p = iface.add_p_ref(f.property("RemainingSectors", 0u64)
                                        .emits_changed(EmitsChangedSignal::False));
            let tot_p = iface.add_p_ref(f.property("TotalSectors", 0u64)
                                        .emits_changed(EmitsChangedSignal::False));
            let status_p = iface.add_p_ref(f.property("Status", 0u32));
            let running_status_p = iface.add_p_ref(f.property("RunningStatus", 0u32));

            let froyo_closed_over = froyo.clone();
            let iface = iface.add_m(
                f.method("AddBlockDevice", move |m,_,_| {
                    let mut items = m.get_items();
                    if items.len() < 2 {
                        return Err(MethodErr::no_arg())
                    }

                    let force: bool = try!(
                        items.pop()
                            .ok_or_else(MethodErr::no_arg)
                            .and_then(
                                |i| i.inner().map_err(|_| MethodErr::invalid_arg(&i))));

                    let new_dev = try!(
                        items.pop()
                            .ok_or_else(MethodErr::no_arg)
                            .and_then(|i| i.inner::<&str>()
                                      .map_err(|_| MethodErr::invalid_arg(&i))
                                      .map(|i| i.to_owned())));

                    let mut froyo = RefCell::borrow_mut(&*froyo_closed_over);
                    try!(froyo.add_block_device(Path::new(&new_dev), force)
                        .map_err(|err| {
                            let msg = format!("Adding block device failed: {}",
                                              err.description());
                            MethodErr::failed(&msg)
                        }));
                    try!(froyo.save_state()
                         .map_err(|err| {
                             let msg = format!("Saving state failed: {}",
                                               err.description());
                             MethodErr::failed(&msg)
                         }));
                    Ok(vec![m.method_return()])
                })
                    .in_arg(("device_path", "s"))
                    .in_arg(("force", "b")));

            let froyo_closed_over = froyo.clone();
            let iface = iface.add_m(
                f.method("RemoveBlockDevice", move |m,_,_| {
                    let mut items = m.get_items();
                    if items.len() < 1 {
                        return Err(MethodErr::no_arg())
                    }

                    let wipe: bool = try!(
                        items.pop()
                            .ok_or_else(MethodErr::no_arg)
                            .and_then(
                                |i| i.inner().map_err(|_| MethodErr::invalid_arg(&i))));

                    let removing_dev = try!(
                        items.pop()
                            .ok_or_else(MethodErr::no_arg)
                            .and_then(|i| i.inner::<&str>()
                                      .map_err(|_| MethodErr::invalid_arg(&i))
                                      .map(|i| i.to_owned())));

                    let mut froyo = RefCell::borrow_mut(&*froyo_closed_over);
                    try!(froyo.remove_block_device(Path::new(&removing_dev), wipe)
                        .map_err(|err| {
                            let msg = format!("Removing block device failed: {}",
                                              err.description());
                            MethodErr::failed(&msg)
                        }));
                    try!(froyo.save_state()
                         .map_err(|err| {
                             let msg = format!("Saving state failed: {}",
                                               err.description());
                             MethodErr::failed(&msg)
                         }));
                    Ok(vec![m.method_return()])
                })
                    .in_arg(("device_path", "s"))
                    .in_arg(("wipe", "b")));

            let froyo_closed_over = froyo.clone();
            let mut iface = iface.add_m(
                f.method("Reshape", move |m,_,_| {
                    let mut froyo = RefCell::borrow_mut(&*froyo_closed_over);
                    try!(froyo.reshape()
                         .map_err(|err| {
                             let msg = format!("Reshape failed: {}",
                                               err.description());
                             MethodErr::failed(&msg)
                         }));
                    Ok(vec![m.method_return()])
                }));

            let mut froyo = RefCell::borrow_mut(froyo);

            // Need to actually get values b/c I can't figure out how to
            // get a 0-length array of struct
            let bdev_msg = DbusContext::get_block_devices_msgitem(&froyo.block_devs);
            let block_devices_p = iface.add_p_ref(f.property("BlockDevices", bdev_msg));

            let path = format!("/org/freedesktop/froyo/{}", froyo.id);
            froyo.dbus_context = Some(DbusContext {
                name_prop: name_p,
                remaining_prop: rem_p,
                total_prop: tot_p,
                status_prop: status_p,
                running_status_prop: running_status_p,
                block_devices_prop: block_devices_p,
            });

            tree.add(f.object_path(path)
                     .introspectable()
                     .add(iface))
        });

    for froyo in &*froyos {
        try!(RefCell::borrow(&froyo).update_dbus());
    }

    tree.set_registered(&c, true).unwrap();

    Ok(tree)
}
<commit_msg>Fix BlockDevices to report in-use vs not-in-use<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::collections::BTreeMap;
use std::borrow::Borrow;
use std::rc::Rc;
use std::cell::RefCell;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use std::error::Error;

use dbus::{Connection, NameFlag};
use dbus::tree::{Factory, Tree, Property, MethodFn, MethodErr, EmitsChangedSignal};
use dbus::MessageItem;

use froyo::Froyo;
use blockdev::BlockMember;
use types::FroyoResult;

#[derive(Debug, Clone)]
pub struct DbusContext<'a> {
    name_prop: Arc<Property<MethodFn<'a>>>,
    pub remaining_prop: Arc<Property<MethodFn<'a>>>,
    pub total_prop: Arc<Property<MethodFn<'a>>>,
    pub status_prop: Arc<Property<MethodFn<'a>>>,
    pub running_status_prop: Arc<Property<MethodFn<'a>>>,
    pub block_devices_prop: Arc<Property<MethodFn<'a>>>,
}

impl<'a> DbusContext<'a> {
    pub fn update_one(prop: &Arc<Property<MethodFn<'a>>>, m: MessageItem)
                      -> FroyoResult<()> {
        prop.set_value(m);
        // TODO: result is signals we need to be sending for PropertyChanged???
        Ok(())
    }

    pub fn get_block_devices_msgitem(block_devs: &BTreeMap<String, BlockMember>)
                                     -> MessageItem {
        let mut msg_vec = Vec::new();
        for bd in block_devs.values() {
            let (bd_path, bd_status) = match *bd {
                BlockMember::Present(ref bd) => {
                    let bd = RefCell::borrow(&bd);
                    let status = match bd.linear_devs.len() {
                        0 => 1u32, // not in use
                        _ => 0u32, // in use
                    };
                    (bd.path.to_string_lossy().into_owned(), status)
                },
                BlockMember::Absent(ref sbd) =>
                    (sbd.path.to_string_lossy().into_owned(), 3u32),
            };

            let entry = MessageItem::Struct(vec![bd_path.into(), bd_status.into()]);
            msg_vec.push(entry);
        }

        MessageItem::new_array(msg_vec)
            .expect("Froyodev with no blockdev members???")
    }
}

pub fn get_tree<'a>(c: &Connection, froyos: &mut Rc<RefCell<Vec<Rc<RefCell<Froyo<'a>>>>>>)
                       -> FroyoResult<Tree<MethodFn<'a>>> {
    c.register_name("org.freedesktop.Froyo1", NameFlag::ReplaceExisting as u32).unwrap();

    let f = Factory::new_fn();

    let froyos_closed_over = froyos.clone();
    let mut froyos = RefCell::borrow_mut(&froyos);

    let create_method = f.method("Create", move |m,_,_| {
        let mut items = m.get_items();
        if items.len() < 3 {
            return Err(MethodErr::no_arg())
        }

        let force: bool = try!(items.pop().ok_or_else(MethodErr::no_arg)
                               .and_then(|i| i.inner()
                                         .map_err(|_| MethodErr::invalid_arg(&i))));
        let blockdevs = match try!(items.pop().ok_or_else(MethodErr::no_arg)) {
            MessageItem::Array(x, _) => x,
            x => return Err(MethodErr::invalid_arg(&x)),
        };
        let blockdevs = blockdevs.into_iter()
            .map(|x| PathBuf::from(x.inner::<&str>().unwrap()))
            .collect::<Vec<_>>();

        let name = try!(items.pop().ok_or_else(MethodErr::no_arg)
                        .and_then(|i| i.inner::<&str>()
                                  .map_err(|_| MethodErr::invalid_arg(&i))
                                  .map(|i| i.to_owned())));

        let froyo = match Froyo::new(&name, &blockdevs, force) {
            Ok(x) => x,
            Err(_) => return Err(MethodErr::failed(&"Froyo create failed")),
        };
        try!(froyo.save_state()
             .map_err(|err| {
                 let msg = format!("Saving state failed: {}",
                                   err.description());
                 MethodErr::failed(&msg)
             }));

        let s = format!("/org/freedesktop/froyo/{}", froyo.id);
        let mr = m.method_return().append(s);

        RefCell::borrow_mut(&froyos_closed_over).push(Rc::new(RefCell::new(froyo)));

        Ok(vec![mr])
    })
        .in_arg(("name", "s"))
        .in_arg(("blockdevs", "as"))
        .in_arg(("force", "b"))
        .out_arg(("obj_path", "s"));

    let tree = f.tree();
    let tree = tree
        .add(f.object_path("/org/freedesktop/froyo")
             .introspectable()
             .object_manager()
             .add(f.interface("org.freedesktop.FroyoService1")
                  .add_m(create_method)
             ));

    let tree = froyos
        .iter_mut()
        .fold(tree, |tree, froyo| {

            let mut iface = f.interface("org.freedesktop.FroyoDevice1");
            let name_p = iface.add_p_ref(f.property(
                "Name", RefCell::borrow(&*froyo).name.to_owned()));
            let p_closed_over = name_p.clone();
            let froyo_closed_over = froyo.clone();
            let mut iface = iface.add_m(
                f.method("SetName", move |m,_,_| {
                    let mut items = m.get_items();
                    if items.len() < 1 {
                        return Err(MethodErr::no_arg())
                    }

                    let name = try!(items.pop().ok_or_else(MethodErr::no_arg)
                                    .and_then(|i| i.inner::<&str>()
                                              .map_err(|_| MethodErr::invalid_arg(&i))
                                              .map(|i| i.to_owned())));

                    let mut froyo = RefCell::borrow_mut(&*froyo_closed_over);
                    froyo.name = name.clone();
                    try!(froyo.save_state()
                         .map_err(|err| {
                             let msg = format!("Saving state failed: {}",
                                               err.description());
                             MethodErr::failed(&msg)
                         }));

                    try!(p_closed_over.set_value(name.into())
                         .map_err(|_| MethodErr::invalid_arg(&"name")));
                    Ok(vec![m.method_return()])
                })
                    .in_arg(("new_name", "s")));

            let rem_p = iface.add_p_ref(f.property("RemainingSectors", 0u64)
                                        .emits_changed(EmitsChangedSignal::False));
            let tot_p = iface.add_p_ref(f.property("TotalSectors", 0u64)
                                        .emits_changed(EmitsChangedSignal::False));
            let status_p = iface.add_p_ref(f.property("Status", 0u32));
            let running_status_p = iface.add_p_ref(f.property("RunningStatus", 0u32));

            let froyo_closed_over = froyo.clone();
            let iface = iface.add_m(
                f.method("AddBlockDevice", move |m,_,_| {
                    let mut items = m.get_items();
                    if items.len() < 2 {
                        return Err(MethodErr::no_arg())
                    }

                    let force: bool = try!(
                        items.pop()
                            .ok_or_else(MethodErr::no_arg)
                            .and_then(
                                |i| i.inner().map_err(|_| MethodErr::invalid_arg(&i))));

                    let new_dev = try!(
                        items.pop()
                            .ok_or_else(MethodErr::no_arg)
                            .and_then(|i| i.inner::<&str>()
                                      .map_err(|_| MethodErr::invalid_arg(&i))
                                      .map(|i| i.to_owned())));

                    let mut froyo = RefCell::borrow_mut(&*froyo_closed_over);
                    try!(froyo.add_block_device(Path::new(&new_dev), force)
                        .map_err(|err| {
                            let msg = format!("Adding block device failed: {}",
                                              err.description());
                            MethodErr::failed(&msg)
                        }));
                    try!(froyo.save_state()
                         .map_err(|err| {
                             let msg = format!("Saving state failed: {}",
                                               err.description());
                             MethodErr::failed(&msg)
                         }));
                    Ok(vec![m.method_return()])
                })
                    .in_arg(("device_path", "s"))
                    .in_arg(("force", "b")));

            let froyo_closed_over = froyo.clone();
            let iface = iface.add_m(
                f.method("RemoveBlockDevice", move |m,_,_| {
                    let mut items = m.get_items();
                    if items.len() < 1 {
                        return Err(MethodErr::no_arg())
                    }

                    let wipe: bool = try!(
                        items.pop()
                            .ok_or_else(MethodErr::no_arg)
                            .and_then(
                                |i| i.inner().map_err(|_| MethodErr::invalid_arg(&i))));

                    let removing_dev = try!(
                        items.pop()
                            .ok_or_else(MethodErr::no_arg)
                            .and_then(|i| i.inner::<&str>()
                                      .map_err(|_| MethodErr::invalid_arg(&i))
                                      .map(|i| i.to_owned())));

                    let mut froyo = RefCell::borrow_mut(&*froyo_closed_over);
                    try!(froyo.remove_block_device(Path::new(&removing_dev), wipe)
                        .map_err(|err| {
                            let msg = format!("Removing block device failed: {}",
                                              err.description());
                            MethodErr::failed(&msg)
                        }));
                    try!(froyo.save_state()
                         .map_err(|err| {
                             let msg = format!("Saving state failed: {}",
                                               err.description());
                             MethodErr::failed(&msg)
                         }));
                    Ok(vec![m.method_return()])
                })
                    .in_arg(("device_path", "s"))
                    .in_arg(("wipe", "b")));

            let froyo_closed_over = froyo.clone();
            let mut iface = iface.add_m(
                f.method("Reshape", move |m,_,_| {
                    let mut froyo = RefCell::borrow_mut(&*froyo_closed_over);
                    try!(froyo.reshape()
                         .map_err(|err| {
                             let msg = format!("Reshape failed: {}",
                                               err.description());
                             MethodErr::failed(&msg)
                         }));
                    Ok(vec![m.method_return()])
                }));

            let mut froyo = RefCell::borrow_mut(froyo);

            // Need to actually get values b/c I can't figure out how to
            // get a 0-length array of struct
            let bdev_msg = DbusContext::get_block_devices_msgitem(&froyo.block_devs);
            let block_devices_p = iface.add_p_ref(f.property("BlockDevices", bdev_msg));

            let path = format!("/org/freedesktop/froyo/{}", froyo.id);
            froyo.dbus_context = Some(DbusContext {
                name_prop: name_p,
                remaining_prop: rem_p,
                total_prop: tot_p,
                status_prop: status_p,
                running_status_prop: running_status_p,
                block_devices_prop: block_devices_p,
            });

            tree.add(f.object_path(path)
                     .introspectable()
                     .add(iface))
        });

    for froyo in &*froyos {
        try!(RefCell::borrow(&froyo).update_dbus());
    }

    tree.set_registered(&c, true).unwrap();

    Ok(tree)
}
<|endoftext|>


<commit_before>// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::ffi::{CStr, CString};
use std::fmt;
use std::ptr;
use super::error::SBError;
use super::platform::SBPlatform;
use super::stream::SBStream;
use super::target::SBTarget;
use sys;

/// Creates [`SBTarget`]s, provides access to them and manages
/// the overall debugging experience.
///
/// # Initialization and Teardown
///
/// LLDB must be initialized before the functionality is used. This
/// is done with `SBDebugger::initialize()`:
///
/// ```
/// use lldb::SBDebugger;
///
/// SBDebugger::initialize();
/// ```
///
/// Similarly, it must be terminated after you are done using it:
///
/// ```
/// use lldb::SBDebugger;
///
/// SBDebugger::initialize();
/// // Use LLDB functionality ...
/// SBDebugger::terminate();
/// ```
///
/// Once you've initialized LLDB, you're ready to create an instance
/// of `SBDebugger`:
///
/// ```
/// use lldb::SBDebugger;
///
/// SBDebugger::initialize();
///
/// let debugger = SBDebugger::create(false);
/// // ... configure the debugger if needed ...
/// // ... create a target and do stuff ...
///
/// SBDebugger::terminate();
/// ```
///
/// # Configuration
///
/// ## Async Mode
///
/// While it is best to use LLDB in asynchronous mode, it does offer a
/// synchronous mode, which can be easier to use for quick experiments
/// or scripts.
///
/// In synchronous mode, calls to the LLDB API do not return until the
/// underlying action has been completed. This means that the thread
/// from which you call LLDB will be blocked during that time, so this
/// is not an ideal way to use LLDB for building interactive tools
/// or a new user interface.
///
/// In asynchronous mode, calls to the LLDB API will return immediately
/// without waiting for the action to complete. This means that actions
/// like launching a target, continuing the execution of a process and
/// so on won't be completed immediately and you must process events
/// to see what the results of an action are.
///
/// Synchronous mode can be enabled by using [`set_async`] and passing it
/// a `false` value. You can see if you're in asynchronous mode or not
/// by calling [`async`].
///
/// # Target Management
///
/// The `SBDebugger` instance tracks the various targets that are
/// currently known to the debugger.
///
/// Typically, you create a target with [`create_target`],
/// [`create_target_simple`] or one of the related methods.
///
/// Sometimes, you'll want to create a target without an associated
/// executable. A common use case for this is to attach to a process
/// by name or process ID where you don't know the executable in advance.
/// The most convenient way to do this is:
///
/// ```
/// # use lldb::SBDebugger;
/// # SBDebugger::initialize();
/// let debugger = SBDebugger::create(false);
/// if let Some(target) = debugger.create_target_simple("") {
///     println!("Got a target: {:?}", target);
///     // Now, maybe we'd like to attach to something.
/// }
/// # SBDebugger::terminate();
/// ```
///
/// You can iterate over these targets which have been created by
/// using [`targets`]:
///
/// ```no_run
/// # use lldb::{SBDebugger, SBTarget};
/// # fn look_at_targets(debugger: SBDebugger) {
/// // Iterate over the targets...
/// for target in debugger.targets() {
///     println!("Hello {:?}!", target);
/// }
/// // Or collect them into a vector!
/// let targets = debugger.targets().collect::<Vec<SBTarget>>();
/// # }
/// ```
///
/// # Platform Management
///
/// ...
///
/// [`SBTarget`]: struct.SBTarget.html
/// [`set_async`]: #method.set_async
/// [`async`]: #method.async
/// [`create_target`]: #method.create_target
/// [`create_target_simple`]: #method.create_target_simple
/// [`targets`]: #method.targets
pub struct SBDebugger {
    /// The underlying raw `SBDebuggerRef`.
    pub raw: sys::SBDebuggerRef,
}

impl SBDebugger {
    /// Initialize LLDB.
    ///
    /// This should be called before LLDB functionality is used.
    pub fn initialize() {
        unsafe { sys::SBDebuggerInitialize() };
    }

    /// Tear down LLDB.
    ///
    /// This should be called once the application no longer needs
    /// to use LLDB functionality. Typically, this is called as the
    /// application exits.
    pub fn terminate() {
        unsafe { sys::SBDebuggerTerminate() };
    }

    /// Create a new instance of `SBDebugger`.
    ///
    /// If `source_init_files` is `true`, then `~/.lldbinit` will
    /// be processed.
    pub fn create(source_init_files: bool) -> SBDebugger {
        SBDebugger { raw: unsafe { sys::SBDebuggerCreate2(source_init_files as u8) } }
    }

    /// Get whether or not the debugger is in async mode.
    ///
    /// When in async mode, the debugger returns immediately when
    /// stepping or continuing without waiting for the process
    /// to change state.
    pub fn async(&self) -> bool {
        unsafe { sys::SBDebuggerGetAsync(self.raw) != 0 }
    }

    /// Set the debugger to be in async mode or not.
    ///
    /// When in async mode, the debugger returns immediately when
    /// stepping or continuing without waiting for the process
    /// to change state.
    pub fn set_async(&mut self, async: bool) {
        unsafe { sys::SBDebuggerSetAsync(self.raw, async as u8) }
    }

    /// Get the LLDB version string.
    pub fn version() -> String {
        unsafe {
            match CStr::from_ptr(sys::SBDebuggerGetVersionString()).to_str() {
                Ok(s) => s.to_owned(),
                _ => panic!("No version string?"),
            }
        }
    }

    /// Create a target.
    ///
    /// The executable name may be an empty string to create
    /// an empty target.
    pub fn create_target(&self,
                         executable: &str,
                         target_triple: Option<&str>,
                         platform_name: Option<&str>,
                         add_dependent_modules: bool)
                         -> Result<SBTarget, SBError> {
        let executable = CString::new(executable).unwrap();
        let target_triple = target_triple.map(|s| CString::new(s).unwrap());
        let platform_name = platform_name.map(|s| CString::new(s).unwrap());
        let error = SBError::new();
        let target = unsafe {
            sys::SBDebuggerCreateTarget(self.raw,
                                        executable.as_ptr(),
                                        target_triple.map_or(ptr::null(), |s| s.as_ptr()),
                                        platform_name.map_or(ptr::null(), |s| s.as_ptr()),
                                        add_dependent_modules as u8,
                                        error.raw)
        };
        if error.is_success() {
            Ok(SBTarget::wrap(target))
        } else {
            Err(error)
        }
    }

    /// Create a target from just an executable name.
    ///
    /// The executable name may be an empty string to create
    /// an empty target.
    ///
    /// Using [`create_target`] is preferred in most cases as
    /// that provides access to an `SBError` to inform the caller
    /// about what might have gone wrong.
    pub fn create_target_simple(&self, executable: &str) -> Option<SBTarget> {
        let executable = CString::new(executable).unwrap();
        SBTarget::maybe_wrap(unsafe { sys::SBDebuggerCreateTarget2(self.raw, executable.as_ptr()) })
    }

    /// Get an iterator over the [targets] known to this debugger instance.
    ///
    /// [targets]: struct.SBTarget.html
    pub fn targets(&self) -> SBDebuggerTargetIter {
        SBDebuggerTargetIter {
            debugger: self,
            idx: 0,
        }
    }

    /// Get the currently selected [`SBTarget`].
    ///
    /// [SBTarget]: struct.SBTarget.html
    pub fn selected_target(&self) -> Option<SBTarget> {
        SBTarget::maybe_wrap(unsafe { sys::SBDebuggerGetSelectedTarget(self.raw) })
    }

    /// Set the selected [`SBTarget`].
    ///
    /// [SBTarget]: struct.SBTarget.html
    pub fn set_selected_target(&mut self, target: &SBTarget) {
        unsafe { sys::SBDebuggerSetSelectedTarget(self.raw, target.raw) };
    }

    /// Get the currently selected [`SBPlatform`].
    ///
    /// [`SBPlatform`]: struct.SBPlatform.html
    pub fn selected_platform(&self) -> SBPlatform {
        unsafe { SBPlatform { raw: sys::SBDebuggerGetSelectedPlatform(self.raw) } }
    }

    /// Set the selected [`SBPlatform`].
    ///
    /// [`SBPlatform`]: struct.SBPlatform.html
    pub fn set_selected_platform(&mut self, platform: &SBPlatform) {
        unsafe { sys::SBDebuggerSetSelectedPlatform(self.raw, platform.raw) };
    }
}

/// Iterate over the [targets] known to a [debugger].
///
/// [targets]: struct.SBTarget.html
/// [debugger]: struct.SBDebugger.html
pub struct SBDebuggerTargetIter<'d> {
    debugger: &'d SBDebugger,
    idx: usize,
}

impl<'d> Iterator for SBDebuggerTargetIter<'d> {
    type Item = SBTarget;

    fn next(&mut self) -> Option<SBTarget> {
        if self.idx < unsafe { sys::SBDebuggerGetNumTargets(self.debugger.raw) as usize } {
            let r = Some(SBTarget {
                             raw: unsafe {
                                 sys::SBDebuggerGetTargetAtIndex(self.debugger.raw, self.idx as u32)
                             },
                         });
            self.idx += 1;
            r
        } else {
            None
        }
    }
}

impl fmt::Debug for SBDebugger {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        let stream = SBStream::new();
        unsafe { sys::SBDebuggerGetDescription(self.raw, stream.raw) };
        write!(fmt, "SBDebugger {{ {} }}", stream.data())
    }
}

impl Drop for SBDebugger {
    fn drop(&mut self) {
        unsafe { sys::DisposeSBDebugger(self.raw) };
    }
}

impl ::juniper::Context for SBDebugger {}

#[cfg(feature = "graphql")]
graphql_object!(SBDebugger: SBDebugger | &self | {
    field targets() -> Vec<SBTarget> {
        self.targets().collect()
    }

    field selected_target() -> Option<SBTarget> {
        self.selected_target()
    }

    field selected_platform() -> SBPlatform {
        self.selected_platform()
    }
});

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

    #[test]
    fn it_works() {
        assert!(!SBDebugger::version().is_empty());
    }
}
<commit_msg>Only impl juniper::Context when 'graphql' feature enabled.<commit_after>// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::ffi::{CStr, CString};
use std::fmt;
use std::ptr;
use super::error::SBError;
use super::platform::SBPlatform;
use super::stream::SBStream;
use super::target::SBTarget;
use sys;

/// Creates [`SBTarget`]s, provides access to them and manages
/// the overall debugging experience.
///
/// # Initialization and Teardown
///
/// LLDB must be initialized before the functionality is used. This
/// is done with `SBDebugger::initialize()`:
///
/// ```
/// use lldb::SBDebugger;
///
/// SBDebugger::initialize();
/// ```
///
/// Similarly, it must be terminated after you are done using it:
///
/// ```
/// use lldb::SBDebugger;
///
/// SBDebugger::initialize();
/// // Use LLDB functionality ...
/// SBDebugger::terminate();
/// ```
///
/// Once you've initialized LLDB, you're ready to create an instance
/// of `SBDebugger`:
///
/// ```
/// use lldb::SBDebugger;
///
/// SBDebugger::initialize();
///
/// let debugger = SBDebugger::create(false);
/// // ... configure the debugger if needed ...
/// // ... create a target and do stuff ...
///
/// SBDebugger::terminate();
/// ```
///
/// # Configuration
///
/// ## Async Mode
///
/// While it is best to use LLDB in asynchronous mode, it does offer a
/// synchronous mode, which can be easier to use for quick experiments
/// or scripts.
///
/// In synchronous mode, calls to the LLDB API do not return until the
/// underlying action has been completed. This means that the thread
/// from which you call LLDB will be blocked during that time, so this
/// is not an ideal way to use LLDB for building interactive tools
/// or a new user interface.
///
/// In asynchronous mode, calls to the LLDB API will return immediately
/// without waiting for the action to complete. This means that actions
/// like launching a target, continuing the execution of a process and
/// so on won't be completed immediately and you must process events
/// to see what the results of an action are.
///
/// Synchronous mode can be enabled by using [`set_async`] and passing it
/// a `false` value. You can see if you're in asynchronous mode or not
/// by calling [`async`].
///
/// # Target Management
///
/// The `SBDebugger` instance tracks the various targets that are
/// currently known to the debugger.
///
/// Typically, you create a target with [`create_target`],
/// [`create_target_simple`] or one of the related methods.
///
/// Sometimes, you'll want to create a target without an associated
/// executable. A common use case for this is to attach to a process
/// by name or process ID where you don't know the executable in advance.
/// The most convenient way to do this is:
///
/// ```
/// # use lldb::SBDebugger;
/// # SBDebugger::initialize();
/// let debugger = SBDebugger::create(false);
/// if let Some(target) = debugger.create_target_simple("") {
///     println!("Got a target: {:?}", target);
///     // Now, maybe we'd like to attach to something.
/// }
/// # SBDebugger::terminate();
/// ```
///
/// You can iterate over these targets which have been created by
/// using [`targets`]:
///
/// ```no_run
/// # use lldb::{SBDebugger, SBTarget};
/// # fn look_at_targets(debugger: SBDebugger) {
/// // Iterate over the targets...
/// for target in debugger.targets() {
///     println!("Hello {:?}!", target);
/// }
/// // Or collect them into a vector!
/// let targets = debugger.targets().collect::<Vec<SBTarget>>();
/// # }
/// ```
///
/// # Platform Management
///
/// ...
///
/// [`SBTarget`]: struct.SBTarget.html
/// [`set_async`]: #method.set_async
/// [`async`]: #method.async
/// [`create_target`]: #method.create_target
/// [`create_target_simple`]: #method.create_target_simple
/// [`targets`]: #method.targets
pub struct SBDebugger {
    /// The underlying raw `SBDebuggerRef`.
    pub raw: sys::SBDebuggerRef,
}

impl SBDebugger {
    /// Initialize LLDB.
    ///
    /// This should be called before LLDB functionality is used.
    pub fn initialize() {
        unsafe { sys::SBDebuggerInitialize() };
    }

    /// Tear down LLDB.
    ///
    /// This should be called once the application no longer needs
    /// to use LLDB functionality. Typically, this is called as the
    /// application exits.
    pub fn terminate() {
        unsafe { sys::SBDebuggerTerminate() };
    }

    /// Create a new instance of `SBDebugger`.
    ///
    /// If `source_init_files` is `true`, then `~/.lldbinit` will
    /// be processed.
    pub fn create(source_init_files: bool) -> SBDebugger {
        SBDebugger { raw: unsafe { sys::SBDebuggerCreate2(source_init_files as u8) } }
    }

    /// Get whether or not the debugger is in async mode.
    ///
    /// When in async mode, the debugger returns immediately when
    /// stepping or continuing without waiting for the process
    /// to change state.
    pub fn async(&self) -> bool {
        unsafe { sys::SBDebuggerGetAsync(self.raw) != 0 }
    }

    /// Set the debugger to be in async mode or not.
    ///
    /// When in async mode, the debugger returns immediately when
    /// stepping or continuing without waiting for the process
    /// to change state.
    pub fn set_async(&mut self, async: bool) {
        unsafe { sys::SBDebuggerSetAsync(self.raw, async as u8) }
    }

    /// Get the LLDB version string.
    pub fn version() -> String {
        unsafe {
            match CStr::from_ptr(sys::SBDebuggerGetVersionString()).to_str() {
                Ok(s) => s.to_owned(),
                _ => panic!("No version string?"),
            }
        }
    }

    /// Create a target.
    ///
    /// The executable name may be an empty string to create
    /// an empty target.
    pub fn create_target(&self,
                         executable: &str,
                         target_triple: Option<&str>,
                         platform_name: Option<&str>,
                         add_dependent_modules: bool)
                         -> Result<SBTarget, SBError> {
        let executable = CString::new(executable).unwrap();
        let target_triple = target_triple.map(|s| CString::new(s).unwrap());
        let platform_name = platform_name.map(|s| CString::new(s).unwrap());
        let error = SBError::new();
        let target = unsafe {
            sys::SBDebuggerCreateTarget(self.raw,
                                        executable.as_ptr(),
                                        target_triple.map_or(ptr::null(), |s| s.as_ptr()),
                                        platform_name.map_or(ptr::null(), |s| s.as_ptr()),
                                        add_dependent_modules as u8,
                                        error.raw)
        };
        if error.is_success() {
            Ok(SBTarget::wrap(target))
        } else {
            Err(error)
        }
    }

    /// Create a target from just an executable name.
    ///
    /// The executable name may be an empty string to create
    /// an empty target.
    ///
    /// Using [`create_target`] is preferred in most cases as
    /// that provides access to an `SBError` to inform the caller
    /// about what might have gone wrong.
    pub fn create_target_simple(&self, executable: &str) -> Option<SBTarget> {
        let executable = CString::new(executable).unwrap();
        SBTarget::maybe_wrap(unsafe { sys::SBDebuggerCreateTarget2(self.raw, executable.as_ptr()) })
    }

    /// Get an iterator over the [targets] known to this debugger instance.
    ///
    /// [targets]: struct.SBTarget.html
    pub fn targets(&self) -> SBDebuggerTargetIter {
        SBDebuggerTargetIter {
            debugger: self,
            idx: 0,
        }
    }

    /// Get the currently selected [`SBTarget`].
    ///
    /// [SBTarget]: struct.SBTarget.html
    pub fn selected_target(&self) -> Option<SBTarget> {
        SBTarget::maybe_wrap(unsafe { sys::SBDebuggerGetSelectedTarget(self.raw) })
    }

    /// Set the selected [`SBTarget`].
    ///
    /// [SBTarget]: struct.SBTarget.html
    pub fn set_selected_target(&mut self, target: &SBTarget) {
        unsafe { sys::SBDebuggerSetSelectedTarget(self.raw, target.raw) };
    }

    /// Get the currently selected [`SBPlatform`].
    ///
    /// [`SBPlatform`]: struct.SBPlatform.html
    pub fn selected_platform(&self) -> SBPlatform {
        unsafe { SBPlatform { raw: sys::SBDebuggerGetSelectedPlatform(self.raw) } }
    }

    /// Set the selected [`SBPlatform`].
    ///
    /// [`SBPlatform`]: struct.SBPlatform.html
    pub fn set_selected_platform(&mut self, platform: &SBPlatform) {
        unsafe { sys::SBDebuggerSetSelectedPlatform(self.raw, platform.raw) };
    }
}

/// Iterate over the [targets] known to a [debugger].
///
/// [targets]: struct.SBTarget.html
/// [debugger]: struct.SBDebugger.html
pub struct SBDebuggerTargetIter<'d> {
    debugger: &'d SBDebugger,
    idx: usize,
}

impl<'d> Iterator for SBDebuggerTargetIter<'d> {
    type Item = SBTarget;

    fn next(&mut self) -> Option<SBTarget> {
        if self.idx < unsafe { sys::SBDebuggerGetNumTargets(self.debugger.raw) as usize } {
            let r = Some(SBTarget {
                             raw: unsafe {
                                 sys::SBDebuggerGetTargetAtIndex(self.debugger.raw, self.idx as u32)
                             },
                         });
            self.idx += 1;
            r
        } else {
            None
        }
    }
}

impl fmt::Debug for SBDebugger {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        let stream = SBStream::new();
        unsafe { sys::SBDebuggerGetDescription(self.raw, stream.raw) };
        write!(fmt, "SBDebugger {{ {} }}", stream.data())
    }
}

impl Drop for SBDebugger {
    fn drop(&mut self) {
        unsafe { sys::DisposeSBDebugger(self.raw) };
    }
}

#[cfg(feature = "graphql")]
impl ::juniper::Context for SBDebugger {}

#[cfg(feature = "graphql")]
graphql_object!(SBDebugger: SBDebugger | &self | {
    field targets() -> Vec<SBTarget> {
        self.targets().collect()
    }

    field selected_target() -> Option<SBTarget> {
        self.selected_target()
    }

    field selected_platform() -> SBPlatform {
        self.selected_platform()
    }
});

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

    #[test]
    fn it_works() {
        assert!(!SBDebugger::version().is_empty());
    }
}
<|endoftext|>


<commit_before>use lang::Lang;
use script::Script;
use script::detect_script;
use info::Info;
use options::Options;
use detect;

/// Configurable structure that holds detection options and provides functions
/// to detect language and script.
/// # Examples
/// Specifying a whitelist:
///
/// ```
/// use whatlang::{Detector, Lang};
///
/// const WHITELIST : &'static [Lang] = &[Lang::Eng, Lang::Rus];
///
/// // Create detector with whitelist
/// let detector = Detector::with_whitelist(WHITELIST);
/// let lang = detector.detect_lang("That is not Russian");
/// assert_eq!(lang, Some(Lang::Eng));
/// ```
///
/// Specifying a blacklist:
///
/// ```
/// use whatlang::{Detector, Lang};
///
/// const BLACKLIST: &'static [Lang] = &[Lang::Eng, Lang::Ita];
///
/// let detector = Detector::with_blacklist(BLACKLIST);
/// let lang = detector.detect_lang("Jen la trinkejo fermitis, ni iras tra mallumo kaj pluvo.");
/// assert_eq!(lang, Some(Lang::Epo));
/// ```
pub struct Detector<'a> {
    options: Options<'a>,
}

impl<'a> Detector<'a> {
    pub fn new() -> Self {
        Detector { options: Options::None }
    }

    pub fn with_whitelist(whitelist: &'a [Lang]) -> Self {
        let opts = Options::Whitelist(whitelist);
        Detector { options: opts }
    }

    pub fn with_blacklist(blacklist: &'a [Lang]) -> Self {
        let opts = Options::Blacklist(blacklist);
        Detector { options: opts }
    }

    pub fn detect(&self, text: &str) -> Option<Info> {
        detect::detect_with_options(text, self.options)
    }

    pub fn detect_lang(&self, text: &str) -> Option<Lang> {
        detect::detect_lang_with_options(text, self.options)
    }

    pub fn detect_script(&self, text: &str) -> Option<Script> {
        detect_script(text)
    }
}

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

    #[test]
    fn test_detect_script() {
        // Russian, Cyrillic
        assert_eq!(Detector::new().detect_script("Кириллица"), Some(Script::Cyrillic));
    }

    #[test]
    fn test_detect_lang() {
        // Esperanto
        let text = "Ĉiuj redaktantoj de Esperanta Vikipedio estas volontuloj. Ili partoprenas en la kunlaborema komunumo, sen estro, kie la anoj kunordigas siajn strebojn kadre de temaj projektoj kaj pluraj diskutejoj. Ili sekvas la bazajn regulojn establitaj de la komunumo, ekzemple kontrolebleco de la informo aŭ la menciindeco de la temo.";
        assert_eq!(Detector::new().detect_lang(text), Some(Lang::Epo));
    }

    #[test]
    fn test_detect() {
        // Esperanto
        let text = "Ĉiuj redaktantoj de Esperanta Vikipedio estas volontuloj. Ili partoprenas en la kunlaborema komunumo, sen estro, kie la anoj kunordigas siajn strebojn kadre de temaj projektoj kaj pluraj diskutejoj. Ili sekvas la bazajn regulojn establitaj de la komunumo, ekzemple kontrolebleco de la informo aŭ la menciindeco de la temo.";
        let output = Detector::new().detect(text);
        assert_eq!(output.is_some(), true);

        let info = output.unwrap();
        assert_eq!(info.lang, Lang::Epo);
        assert_eq!(info.script, Script::Latin);
    }
}
<commit_msg>Implement Debug, Clonet and Default traits for Detector<commit_after>use lang::Lang;
use script::Script;
use script::detect_script;
use info::Info;
use options::Options;
use detect;

/// Configurable structure that holds detection options and provides functions
/// to detect language and script.
/// # Examples
/// Specifying a whitelist:
///
/// ```
/// use whatlang::{Detector, Lang};
///
/// const WHITELIST : &'static [Lang] = &[Lang::Eng, Lang::Rus];
///
/// // Create detector with whitelist
/// let detector = Detector::with_whitelist(WHITELIST);
/// let lang = detector.detect_lang("That is not Russian");
/// assert_eq!(lang, Some(Lang::Eng));
/// ```
///
/// Specifying a blacklist:
///
/// ```
/// use whatlang::{Detector, Lang};
///
/// const BLACKLIST: &'static [Lang] = &[Lang::Eng, Lang::Ita];
///
/// let detector = Detector::with_blacklist(BLACKLIST);
/// let lang = detector.detect_lang("Jen la trinkejo fermitis, ni iras tra mallumo kaj pluvo.");
/// assert_eq!(lang, Some(Lang::Epo));
/// ```
#[derive(Debug, Clone, Default)]
pub struct Detector<'a> {
    options: Options<'a>,
}

impl<'a> Detector<'a> {
    pub fn new() -> Self {
        Detector { options: Options::None }
    }

    pub fn with_whitelist(whitelist: &'a [Lang]) -> Self {
        let opts = Options::Whitelist(whitelist);
        Detector { options: opts }
    }

    pub fn with_blacklist(blacklist: &'a [Lang]) -> Self {
        let opts = Options::Blacklist(blacklist);
        Detector { options: opts }
    }

    pub fn detect(&self, text: &str) -> Option<Info> {
        detect::detect_with_options(text, self.options)
    }

    pub fn detect_lang(&self, text: &str) -> Option<Lang> {
        detect::detect_lang_with_options(text, self.options)
    }

    pub fn detect_script(&self, text: &str) -> Option<Script> {
        detect_script(text)
    }
}

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

    #[test]
    fn test_detect_script() {
        // Russian, Cyrillic
        assert_eq!(Detector::new().detect_script("Кириллица"), Some(Script::Cyrillic));
    }

    #[test]
    fn test_detect_lang() {
        // Esperanto
        let text = "Ĉiuj redaktantoj de Esperanta Vikipedio estas volontuloj. Ili partoprenas en la kunlaborema komunumo, sen estro, kie la anoj kunordigas siajn strebojn kadre de temaj projektoj kaj pluraj diskutejoj. Ili sekvas la bazajn regulojn establitaj de la komunumo, ekzemple kontrolebleco de la informo aŭ la menciindeco de la temo.";
        assert_eq!(Detector::new().detect_lang(text), Some(Lang::Epo));
    }

    #[test]
    fn test_detect() {
        // Esperanto
        let text = "Ĉiuj redaktantoj de Esperanta Vikipedio estas volontuloj. Ili partoprenas en la kunlaborema komunumo, sen estro, kie la anoj kunordigas siajn strebojn kadre de temaj projektoj kaj pluraj diskutejoj. Ili sekvas la bazajn regulojn establitaj de la komunumo, ekzemple kontrolebleco de la informo aŭ la menciindeco de la temo.";
        let output = Detector::new().detect(text);
        assert_eq!(output.is_some(), true);

        let info = output.unwrap();
        assert_eq!(info.lang, Lang::Epo);
        assert_eq!(info.script, Script::Latin);
    }
}
<|endoftext|>


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

//! SQL Parser
//!
//! Note that most SQL parsing is now delegated to the sqlparser crate, which handles ANSI SQL but
//! this module contains DataFusion-specific SQL extensions.

use sqlparser::dialect::*;
use sqlparser::sqlast::*;
use sqlparser::sqlparser::*;
use sqlparser::sqltokenizer::*;

macro_rules! parser_err {
    ($MSG:expr) => {
        Err(ParserError::ParserError($MSG.to_string()))
    };
}

#[derive(Debug, Clone)]
pub enum FileType {
    NdJson,
    Parquet,
    CSV,
}

#[derive(Debug, Clone)]
pub enum DFASTNode {
    /// ANSI SQL AST node
    ANSI(ASTNode),
    /// DDL for creating an external table in DataFusion
    CreateExternalTable {
        /// Table name
        name: String,
        /// Optional schema
        columns: Vec<SQLColumnDef>,
        /// File type (Parquet, NDJSON, CSV)
        file_type: FileType,
        /// Header row?
        header_row: bool,
        /// Path to file
        location: String,
    },
}

/// SQL Parser
pub struct DFParser {
    parser: Parser,
}

impl DFParser {
    /// Parse the specified tokens
    pub fn new(sql: String) -> Result<Self, ParserError> {
        let dialect = GenericSqlDialect {};
        let mut tokenizer = Tokenizer::new(&dialect, &sql);
        let tokens = tokenizer.tokenize()?;
        Ok(DFParser {
            parser: Parser::new(tokens),
        })
    }

    /// Parse a SQL statement and produce an Abstract Syntax Tree (AST)
    pub fn parse_sql(sql: String) -> Result<DFASTNode, ParserError> {
        let mut parser = DFParser::new(sql)?;
        parser.parse()
    }

    /// Parse a new expression
    pub fn parse(&mut self) -> Result<DFASTNode, ParserError> {
        self.parse_expr(0)
    }

    /// Parse tokens until the precedence changes
    fn parse_expr(&mut self, precedence: u8) -> Result<DFASTNode, ParserError> {
        let mut expr = self.parse_prefix()?;
        loop {
            let next_precedence = self.parser.get_next_precedence()?;
            if precedence >= next_precedence {
                break;
            }

            if let Some(infix_expr) = self.parse_infix(expr.clone(), next_precedence)? {
                expr = infix_expr;
            }
        }
        Ok(expr)
    }

    /// Parse an expression prefix
    fn parse_prefix(&mut self) -> Result<DFASTNode, ParserError> {
        if self
            .parser
            .parse_keywords(vec!["CREATE", "EXTERNAL", "TABLE"])
        {
            match self.parser.next_token() {
                Some(Token::Identifier(id)) => {
                    // parse optional column list (schema)
                    let mut columns = vec![];
                    if self.parser.consume_token(&Token::LParen)? {
                        loop {
                            if let Some(Token::Identifier(column_name)) = self.parser.next_token() {
                                if let Ok(data_type) = self.parser.parse_data_type() {
                                    let allow_null =
                                        if self.parser.parse_keywords(vec!["NOT", "NULL"]) {
                                            false
                                        } else if self.parser.parse_keyword("NULL") {
                                            true
                                        } else {
                                            true
                                        };

                                    match self.parser.peek_token() {
                                        Some(Token::Comma) => {
                                            self.parser.next_token();
                                            columns.push(SQLColumnDef {
                                                name: column_name,
                                                data_type: data_type,
                                                allow_null,
                                                default: None,
                                                is_primary: false,
                                                is_unique: false,
                                            });
                                        }
                                        Some(Token::RParen) => {
                                            self.parser.next_token();
                                            columns.push(SQLColumnDef {
                                                name: column_name,
                                                data_type: data_type,
                                                allow_null,
                                                default: None,
                                                is_primary: false,
                                                is_unique: false,
                                            });
                                            break;
                                        }
                                        _ => {
                                            return parser_err!(
                                                "Expected ',' or ')' after column definition"
                                            );
                                        }
                                    }
                                } else {
                                    return parser_err!(
                                        "Error parsing data type in column definition"
                                    );
                                }
                            } else {
                                return parser_err!("Error parsing column name");
                            }
                        }
                    }

                    //println!("Parsed {} column defs", columns.len());

                    let mut headers = true;
                    let file_type: FileType =
                        if self.parser.parse_keywords(vec!["STORED", "AS", "CSV"]) {
                            if self.parser.parse_keywords(vec!["WITH", "HEADER", "ROW"]) {
                                headers = true;
                            } else if self.parser.parse_keywords(vec!["WITHOUT", "HEADER", "ROW"]) {
                                headers = false;
                            }
                            FileType::CSV
                        } else if self.parser.parse_keywords(vec!["STORED", "AS", "NDJSON"]) {
                            FileType::NdJson
                        } else if self.parser.parse_keywords(vec!["STORED", "AS", "PARQUET"]) {
                            FileType::Parquet
                        } else {
                            return parser_err!(format!(
                                "Expected 'STORED AS' clause, found {:?}",
                                self.parser.peek_token()
                            ));
                        };

                    let location: String = if self.parser.parse_keywords(vec!["LOCATION"]) {
                        self.parser.parse_literal_string()?
                    } else {
                        return parser_err!("Missing 'LOCATION' clause");
                    };

                    Ok(DFASTNode::CreateExternalTable {
                        name: id,
                        columns,
                        file_type,
                        header_row: headers,
                        location,
                    })
                }
                _ => parser_err!(format!(
                    "Unexpected token after CREATE EXTERNAL TABLE: {:?}",
                    self.parser.peek_token()
                )),
            }
        } else {
            Ok(DFASTNode::ANSI(self.parser.parse_prefix()?))
        }
    }

    pub fn parse_infix(
        &mut self,
        _expr: DFASTNode,
        _precedence: u8,
    ) -> Result<Option<DFASTNode>, ParserError> {
        //        match expr {
        //            DFASTNode::ANSI(ansi) => {
        //                //DFASTNode::ANSI(self.parser.parse_infix(ansi, precedence)?)
        //            })

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

//! SQL Parser
//!
//! Note that most SQL parsing is now delegated to the sqlparser crate, which handles ANSI SQL but
//! this module contains DataFusion-specific SQL extensions.

use sqlparser::dialect::*;
use sqlparser::sqlast::*;
use sqlparser::sqlparser::*;
use sqlparser::sqltokenizer::*;

macro_rules! parser_err {
    ($MSG:expr) => {
        Err(ParserError::ParserError($MSG.to_string()))
    };
}

#[derive(Debug, Clone)]
pub enum FileType {
    NdJson,
    Parquet,
    CSV,
}

#[derive(Debug, Clone)]
pub enum DFASTNode {
    /// ANSI SQL AST node
    ANSI(ASTNode),
    /// DDL for creating an external table in DataFusion
    CreateExternalTable {
        /// Table name
        name: String,
        /// Optional schema
        columns: Vec<SQLColumnDef>,
        /// File type (Parquet, NDJSON, CSV)
        file_type: FileType,
        /// Header row?
        header_row: bool,
        /// Path to file
        location: String,
    },
}

/// SQL Parser
pub struct DFParser {
    parser: Parser,
}

impl DFParser {
    /// Parse the specified tokens
    pub fn new(sql: String) -> Result<Self, ParserError> {
        let dialect = GenericSqlDialect {};
        let mut tokenizer = Tokenizer::new(&dialect, &sql);
        let tokens = tokenizer.tokenize()?;
        Ok(DFParser {
            parser: Parser::new(tokens),
        })
    }

    /// Parse a SQL statement and produce an Abstract Syntax Tree (AST)
    pub fn parse_sql(sql: String) -> Result<DFASTNode, ParserError> {
        let mut parser = DFParser::new(sql)?;
        parser.parse()
    }

    /// Parse a new expression
    pub fn parse(&mut self) -> Result<DFASTNode, ParserError> {
        self.parse_expr(0)
    }

    /// Parse tokens until the precedence changes
    fn parse_expr(&mut self, precedence: u8) -> Result<DFASTNode, ParserError> {
        let mut expr = self.parse_prefix()?;
        loop {
            let next_precedence = self.parser.get_next_precedence()?;
            if precedence >= next_precedence {
                break;
            }

            if let Some(infix_expr) = self.parse_infix(expr.clone(), next_precedence)? {
                expr = infix_expr;
            }
        }
        Ok(expr)
    }

    /// Parse an expression prefix
    fn parse_prefix(&mut self) -> Result<DFASTNode, ParserError> {
        if self
            .parser
            .parse_keywords(vec!["CREATE", "EXTERNAL", "TABLE"])
        {
            match self.parser.next_token() {
                Some(Token::Identifier(id)) => {
                    // parse optional column list (schema)
                    let mut columns = vec![];
                    if self.parser.consume_token(&Token::LParen) {
                        loop {
                            if let Some(Token::Identifier(column_name)) = self.parser.next_token() {
                                if let Ok(data_type) = self.parser.parse_data_type() {
                                    let allow_null =
                                        if self.parser.parse_keywords(vec!["NOT", "NULL"]) {
                                            false
                                        } else if self.parser.parse_keyword("NULL") {
                                            true
                                        } else {
                                            true
                                        };

                                    match self.parser.peek_token() {
                                        Some(Token::Comma) => {
                                            self.parser.next_token();
                                            columns.push(SQLColumnDef {
                                                name: column_name,
                                                data_type: data_type,
                                                allow_null,
                                                default: None,
                                                is_primary: false,
                                                is_unique: false,
                                            });
                                        }
                                        Some(Token::RParen) => {
                                            self.parser.next_token();
                                            columns.push(SQLColumnDef {
                                                name: column_name,
                                                data_type: data_type,
                                                allow_null,
                                                default: None,
                                                is_primary: false,
                                                is_unique: false,
                                            });
                                            break;
                                        }
                                        _ => {
                                            return parser_err!(
                                                "Expected ',' or ')' after column definition"
                                            );
                                        }
                                    }
                                } else {
                                    return parser_err!(
                                        "Error parsing data type in column definition"
                                    );
                                }
                            } else {
                                return parser_err!("Error parsing column name");
                            }
                        }
                    }

                    //println!("Parsed {} column defs", columns.len());

                    let mut headers = true;
                    let file_type: FileType =
                        if self.parser.parse_keywords(vec!["STORED", "AS", "CSV"]) {
                            if self.parser.parse_keywords(vec!["WITH", "HEADER", "ROW"]) {
                                headers = true;
                            } else if self.parser.parse_keywords(vec!["WITHOUT", "HEADER", "ROW"]) {
                                headers = false;
                            }
                            FileType::CSV
                        } else if self.parser.parse_keywords(vec!["STORED", "AS", "NDJSON"]) {
                            FileType::NdJson
                        } else if self.parser.parse_keywords(vec!["STORED", "AS", "PARQUET"]) {
                            FileType::Parquet
                        } else {
                            return parser_err!(format!(
                                "Expected 'STORED AS' clause, found {:?}",
                                self.parser.peek_token()
                            ));
                        };

                    let location: String = if self.parser.parse_keywords(vec!["LOCATION"]) {
                        self.parser.parse_literal_string()?
                    } else {
                        return parser_err!("Missing 'LOCATION' clause");
                    };

                    Ok(DFASTNode::CreateExternalTable {
                        name: id,
                        columns,
                        file_type,
                        header_row: headers,
                        location,
                    })
                }
                _ => parser_err!(format!(
                    "Unexpected token after CREATE EXTERNAL TABLE: {:?}",
                    self.parser.peek_token()
                )),
            }
        } else {
            Ok(DFASTNode::ANSI(self.parser.parse_prefix()?))
        }
    }

    pub fn parse_infix(
        &mut self,
        _expr: DFASTNode,
        _precedence: u8,
    ) -> Result<Option<DFASTNode>, ParserError> {
        //        match expr {
        //            DFASTNode::ANSI(ansi) => {
        //                //DFASTNode::ANSI(self.parser.parse_infix(ansi, precedence)?)
        //            })

        unimplemented!()
    }
}
<|endoftext|>