<commit_before>// Take a look at the license at the top of the repository in the LICENSE file.

use crate::ComponentExt;

use std::collections::HashMap;
use std::fs::{metadata, read_dir, File};
use std::io::Read;
use std::path::{Path, PathBuf};

#[doc = include_str!("../../md_doc/component.md")]
pub struct Component {
    temperature: f32,
    max: f32,
    critical: Option<f32>,
    label: String,
    input_file: PathBuf,
}

fn get_file_line(file: &Path, capacity: usize) -> Option<String> {
    let mut reader = String::with_capacity(capacity);
    if let Ok(mut f) = File::open(file) {
        if f.read_to_string(&mut reader).is_ok() {
            Some(reader)
        } else {
            None
        }
    } else {
        None
    }
}

fn is_file<T: AsRef<Path>>(path: T) -> bool {
    metadata(path).ok().map(|m| m.is_file()).unwrap_or(false)
}

fn append_files(components: &mut Vec<Component>, folder: &Path) {
    let mut matchings: HashMap<u32, Vec<String>> = HashMap::with_capacity(10);

    if let Ok(dir) = read_dir(folder) {
        for entry in dir.flatten() {
            let entry = entry.path();
            if entry.is_dir()
                || !entry
                    .file_name()
                    .and_then(|x| x.to_str())
                    .unwrap_or("")
                    .starts_with("temp")
            {
                continue;
            }
            if let Some(entry) = entry.file_name() {
                if let Some(entry) = entry.to_str() {
                    let mut parts = entry.split('_');
                    if let Some(Some(id)) = parts.next().map(|s| s[4..].parse::<u32>().ok()) {
                        matchings
                            .entry(id)
                            .or_insert_with(|| Vec::with_capacity(5))
                            .push(
                                parts
                                    .next()
                                    .map(|s| format!("_{}", s))
                                    .unwrap_or_else(String::new),
                            );
                    }
                }
            }
        }
        for (key, val) in &matchings {
            let mut found_input = None;
            let mut found_label = None;
            for (pos, v) in val.iter().enumerate() {
                match v.as_str() {
                    // raspberry has empty string for temperature input
                    "_input" | "" => {
                        found_input = Some(pos);
                    }
                    "_label" => {
                        found_label = Some(pos);
                    }
                    _ => {}
                }
            }
            if let (Some(_), Some(found_input)) = (found_label, found_input) {
                let mut p_label = folder.to_path_buf();
                let mut p_input = folder.to_path_buf();
                let mut p_crit = folder.to_path_buf();
                let mut p_max = folder.to_path_buf();

                p_label.push(&format!("temp{}_label", key));
                p_input.push(&format!("temp{}{}", key, val[found_input]));
                p_max.push(&format!("temp{}_max", key));
                p_crit.push(&format!("temp{}_crit", key));
                if is_file(&p_input) {
                    let label = get_file_line(p_label.as_path(), 10)
                        .unwrap_or_else(|| format!("Component {}", key)) // needed for raspberry pi
                        .replace('\n', "");
                    let max = get_file_line(p_max.as_path(), 10).map(|max| {
                        max.replace('\n', "").parse::<f32>().unwrap_or(100_000f32) / 1000f32
                    });
                    let crit = get_file_line(p_crit.as_path(), 10).map(|crit| {
                        crit.replace('\n', "").parse::<f32>().unwrap_or(100_000f32) / 1000f32
                    });
                    components.push(Component::new(label, p_input, max, crit));
                }
            }
        }
    }
}

impl Component {
    /// Creates a new component with the given information.
    pub(crate) fn new(
        label: String,
        input_file: PathBuf,
        max: Option<f32>,
        critical: Option<f32>,
    ) -> Component {
        let mut c = Component {
            temperature: 0f32,
            label,
            input_file,
            max: max.unwrap_or(0.0),
            critical,
        };
        c.refresh();
        c
    }
}

impl ComponentExt for Component {
    fn temperature(&self) -> f32 {
        self.temperature
    }

    fn max(&self) -> f32 {
        self.max
    }

    fn critical(&self) -> Option<f32> {
        self.critical
    }

    fn label(&self) -> &str {
        &self.label
    }

    fn refresh(&mut self) {
        if let Some(content) = get_file_line(self.input_file.as_path(), 10) {
            self.temperature = content
                .replace('\n', "")
                .parse::<f32>()
                .unwrap_or(100_000f32)
                / 1000f32;
            if self.temperature > self.max {
                self.max = self.temperature;
            }
        }
    }
}

pub(crate) fn get_components() -> Vec<Component> {
    let mut components = Vec::with_capacity(10);
    if let Ok(dir) = read_dir(Path::new("/sys/class/hwmon/")) {
        for entry in dir.flatten() {
            let entry = entry.path();
            if !entry.is_dir()
                || !entry
                    .file_name()
                    .and_then(|x| x.to_str())
                    .unwrap_or("")
                    .starts_with("hwmon")
            {
                continue;
            }
            append_files(&mut components, &entry);
        }
        components.sort_by(|c1, c2| c1.label.to_lowercase().cmp(&c2.label.to_lowercase()));
    }
    components
}
<commit_msg>[linux-hwmon] Attempt to read new data from hwmon on linux<commit_after>// Take a look at the license at the top of the repository in the LICENSE file.

// Information about values readable from `hwmon` sysfs.
//
// Values in /sys/class/hwmonN are `c_long` or `c_ulong`
// transposed to rust we only read `u32` or `i32` values.
use crate::ComponentExt;

use std::collections::HashMap;
use std::fs::{read_dir, File};
use std::io::Read;
use std::path::{Path, PathBuf};

#[doc = include_str!("../../md_doc/component.md")]
#[derive(Default)]
pub struct Component {
    /// Optional associated device of a `Component`.
    device_model: Option<String>,
    /// The chip name.
    ///
    /// Kernel documentation extract:
    /// ```txt
    /// This should be a short, lowercase string, not containing
    /// whitespace, dashes, or the wildcard character '*'.
    /// This attribute represents the chip name. It is the only
    /// mandatory attribute.
    /// I2C devices get this attribute created automatically.
    /// ```
    name: String,
    /// Temperature current value
    /// - Read in: `temp[1-*]_input`.
    /// - Unit: read as millidegree Celsius converted to Celsius.
    temperature: Option<f32>,
    /// Maximum value computed by sysinfo
    max: Option<f32>,
    /// Max threshold provided by the chip/kernel
    /// - Read in:`temp[1-*]_max`
    /// - Unit: read as millidegree Celsius converted to Celsius.
    threshold_max: Option<f32>,
    /// Min threshold provided by the chip/kernel.
    /// - Read in:`temp[1-*]_min`
    /// - Unit: read as millidegree Celsius converted to Celsius.
    threshold_min: Option<f32>,
    /// Critical threshold provided by the chip/kernel previous user write.
    /// Read in `temp[1-*]_crit`:
    /// Typically greater than corresponding temp_max values.
    /// - Unit: read as millidegree Celsius converted to Celsius.
    threshold_critical: Option<f32>,
    /// Sensor type, not common but can exist!
    ///
    /// Read in: `temp[1-*]_type` Sensor type selection.
    /// Values integer:
    /// - 1: CPU embedded diode
    /// - 2: 3904 transistor
    /// - 3: thermal diode
    /// - 4: thermistor
    /// - 5: AMD AMDSI
    /// - 6: Intel PECI
    /// Not all types are supported by all chips
    sensor_type: Option<TermalSensorType>,
    /// Component Label
    ///
    /// For formating detail see `Component::label` function docstring.
    ///
    /// ## Linux implementation details
    ///
    /// read n: `temp[1-*]_label` Suggested temperature channel label.
    /// Value: Text string
    ///
    /// Should only be created if the driver has hints about what
    /// this temperature channel is being used for, and user-space
    /// doesn't. In all other cases, the label is provided by user-space.
    label: String,
    // TODO: not used now.
    // Historical minimum temperature
    // - Read in:`temp[1-*]_lowest
    // - Unit: millidegree Celsius
    //
    // Temperature critical min value, typically lower than
    // corresponding temp_min values.
    // - Read in:`temp[1-*]_lcrit`
    // - Unit: millidegree Celsius
    //
    // Temperature emergency max value, for chips supporting more than
    // two upper temperature limits. Must be equal or greater than
    // corresponding temp_crit values.
    // - temp[1-*]_emergency
    // - Unit: millidegree Celsius
    /// File to read current temperature shall be `temp[1-*]_input`
    /// It may be absent but we don't continue if absent.
    input_file: Option<PathBuf>,
    /// `temp[1-*]_highest file` to read if disponnible highest value.
    highest_file: Option<PathBuf>,
}

// Read arbitrary data from sysfs.
fn get_file_line(file: &Path, capacity: usize) -> Option<String> {
    let mut reader = String::with_capacity(capacity);
    let mut f = File::open(file).ok()?;
    f.read_to_string(&mut reader).ok()?;
    reader.truncate(reader.trim_end().len());
    Some(reader)
}

/// Designed at first for reading an `i32` or `u32` aka `c_long`
/// from a `/sys/class/hwmon` sysfs file.
fn read_number_from_file<N>(file: &Path) -> Option<N>
where
    N: std::str::FromStr,
{
    let mut reader = [0u8; 32];
    let mut f = File::open(file).ok()?;
    let n = f.read(&mut reader).ok()?;
    // parse and trim would complain about `\0`.
    let number = &reader[..n];
    let number = std::str::from_utf8(number).ok()?;
    let number = number.trim();
    // Assert that we cleaned a little bit that string.
    if cfg!(feature = "debug") {
        assert!(!number.contains('\n') && !number.contains('\0'));
    }
    number.parse().ok()
}

// Read a temperature from a `tempN_item` sensor form the sysfs.
// number returned will be in mili-celsius.
//
// Don't call it on `label`, `name` or `type` file.
#[inline]
fn get_temperature_from_file(file: &Path) -> Option<f32> {
    let temp = read_number_from_file(file);
    convert_temp_celsius(temp)
}

/// Takes a raw temperature in mili-celsius and convert it to celsius
#[inline]
fn convert_temp_celsius(temp: Option<i32>) -> Option<f32> {
    temp.map(|n| (n as f32) / 1000f32)
}

/// Information about thermal sensor. It may be unavailable as it's
/// kernel module and chip dependant.
enum TermalSensorType {
    /// 1: CPU embedded diode
    CPUEmbeddedDiode,
    /// 2: 3904 transistor
    Transistor3904,
    /// 3: thermal diode
    ThermalDiode,
    /// 4: thermistor
    Thermistor,
    /// 5: AMD AMDSI
    AMDAMDSI,
    /// 6: Intel PECI
    IntelPECI,
    /// Not all types are supported by all chips so we keep space for
    /// unknown sensors.
    Unknown(u8),
}

impl From<u8> for TermalSensorType {
    fn from(input: u8) -> Self {
        match input {
            0 => Self::CPUEmbeddedDiode,
            1 => Self::Transistor3904,
            3 => Self::ThermalDiode,
            4 => Self::Thermistor,
            5 => Self::AMDAMDSI,
            6 => Self::IntelPECI,
            n => Self::Unknown(n),
        }
    }
}

/// Check given `item` dispatch to read the right `file` with the right parsing and store data in
/// given `component`. `id` is provided for `label` creation.
fn fill_component(component: &mut Component, item: &str, folder: &Path, file: &str) {
    let hwmon_file = folder.join(file);
    match item {
        "type" => {
            component.sensor_type =
                read_number_from_file::<u8>(&hwmon_file).map(TermalSensorType::from)
        }
        "input" => {
            let temperature = get_temperature_from_file(&hwmon_file);
            component.input_file = Some(hwmon_file);
            component.temperature = temperature;
            // Maximum know try to get it from `highest` if not available
            // use current temperature
            if component.max.is_none() {
                component.max = temperature;
            }
        }
        "label" => component.label = get_file_line(&hwmon_file, 10).unwrap_or_default(),
        "highest" => {
            component.max = get_temperature_from_file(&hwmon_file).or(component.temperature);
            component.highest_file = Some(hwmon_file);
        }
        "max" => component.threshold_max = get_temperature_from_file(&hwmon_file),
        "min" => component.threshold_min = get_temperature_from_file(&hwmon_file),
        "crit" => component.threshold_critical = get_temperature_from_file(&hwmon_file),
        _ => {
            sysinfo_debug!(
                "This hwmon-temp file is still not supported! Contributions are appreciated.;) {:?}",
                hwmon_file,
            );
        }
    }
}

impl Component {
    /// Read out `hwmon` info (hardware monitor) from `folder`
    /// to get values' path to be used on refresh as well as files containing `max`,
    /// `critical value` and `label`. Then we store everything into `components`.
    ///
    /// Note that a thermal [Component] must have a way to read its temperature.
    /// If not, it will be ignored and not added into `components`.
    ///
    /// ## What is read:
    ///
    /// - Mandatory: `name` the name of the `hwmon`.
    /// - Mandatory: `tempN_input` Drop [Component] if missing
    /// - Optional: sensor `label`, in the general case content of `tempN_label`
    ///   see below for special cases
    /// - Optional: `label`
    /// - Optional: `/device/model`
    /// - Optional: hightest historic value in `tempN_hightest`.
    /// - Optional: max threshold value defined in `tempN_max`
    /// - Optional: critical threshold value defined in `tempN_crit`
    ///
    /// Where `N` is a u32 associated to a sensor like `temp1_max`, `temp1_input`.
    ///
    /// ## Doc to Linux kernel API.
    ///
    /// Kernel hwmon API: https://www.kernel.org/doc/html/latest/hwmon/hwmon-kernel-api.html
    /// DriveTemp kernel API: https://docs.kernel.org/gpu/amdgpu/thermal.html#hwmon-interfaces
    /// Amdgpu hwmon interface: https://www.kernel.org/doc/html/latest/hwmon/drivetemp.html
    fn from_hwmon(components: &mut Vec<Component>, folder: &Path) -> Option<()> {
        let dir = read_dir(folder).ok()?;
        let mut matchings: HashMap<u32, Component> = HashMap::with_capacity(10);
        for entry in dir.flatten() {
            let entry = entry.path();
            let filename = entry.file_name().and_then(|x| x.to_str()).unwrap_or("");
            if entry.is_dir() || !filename.starts_with("temp") {
                continue;
            }

            let (id, item) = filename.split_once('_')?;
            let id = id.get(4..)?.parse::<u32>().ok()?;

            let component = matchings.entry(id).or_insert_with(Component::default);
            let name = get_file_line(&folder.join("name"), 16);
            component.name = name.unwrap_or_default();
            let device_model = get_file_line(&folder.join("device/model"), 16);
            component.device_model = device_model;
            fill_component(component, item, folder, filename);
        }
        let compo = matchings
            .into_iter()
            .map(|(id, mut c)| {
                // sysinfo expose a generic interface with a `label`.
                // Problem: a lot of sensors don't have a label or a device model! ¯\_(ツ)_/¯
                // So let's pretend we have a unique label!
                // See the table in `Component::label` documentation for the table detail.
                c.label = c.format_label("temp", id);
                c
            })
            // Remove components without `tempN_input` file termal. `Component` doesn't support this kind of sensors yet
            .filter(|c| c.input_file.is_some());

        components.extend(compo);
        Some(())
    }

    /// Compute a label out of available information.
    /// See the table in `Component::label`'s documentation.
    fn format_label(&self, class: &str, id: u32) -> String {
        let Component {
            device_model,
            name,
            label,
            ..
        } = self;
        let has_label = !label.is_empty();
        match (has_label, device_model) {
            (true, Some(device_model)) => {
                format!("{} {} {} {}{}", name, label, device_model, class, id)
            }
            (true, None) => format!("{} {}", name, label),
            (false, Some(device_model)) => format!("{} {}", name, device_model),
            (false, None) => format!("{} {}{}", name, class, id),
        }
    }
}

impl ComponentExt for Component {
    fn temperature(&self) -> f32 {
        self.temperature.unwrap_or(f32::NAN)
    }

    fn max(&self) -> f32 {
        self.max.unwrap_or(f32::NAN)
    }

    fn critical(&self) -> Option<f32> {
        self.threshold_critical
    }

    fn label(&self) -> &str {
        &self.label
    }

    fn refresh(&mut self) {
        let current = self
            .input_file
            .as_ref()
            .and_then(|file| get_temperature_from_file(file.as_path()));
        // tries to read out kernel highest if not compute something from temperature.
        let max = self
            .highest_file
            .as_ref()
            .and_then(|file| get_temperature_from_file(file.as_path()))
            .or_else(|| {
                let last = self.temperature?;
                let current = current?;
                Some(last.max(current))
            });
        self.max = max;
        self.temperature = current;
    }
}

pub(crate) fn get_components() -> Vec<Component> {
    let mut components = Vec::with_capacity(10);
    if let Ok(dir) = read_dir(Path::new("/sys/class/hwmon/")) {
        for entry in dir.flatten() {
            let entry = entry.path();
            if !entry.is_dir()
                || !entry
                    .file_name()
                    .and_then(|x| x.to_str())
                    .unwrap_or("")
                    .starts_with("hwmon")
            {
                continue;
            }
            Component::from_hwmon(&mut components, &entry);
        }
        components.sort_by(|c1, c2| c1.label.to_lowercase().cmp(&c2.label.to_lowercase()));
    }
    components
}
<|endoftext|>


<commit_before>//! Utility methods and constructors for Lua objects

use std::fmt::Display;
use std::convert::From;
use rlua::{self, Lua, Table, UserData, AnyUserData, Value,
           FromLua, ToLua, Function};
use super::signal;
use super::class::Class;
use super::property::Property;

/// All Lua objects can be cast to this.
#[derive(Clone, Debug)]
pub struct Object<'lua> {
    table: Table<'lua>
}

impl <'lua> From<Table<'lua>> for Object<'lua> {
    fn from(table: Table<'lua>) -> Self {
        Object { table }
    }
}

/// Construct a new object, used when using the default Objectable::new.
pub struct ObjectBuilder<'lua>{
    lua: &'lua Lua,
    object: Object<'lua>
}

impl <'lua> ObjectBuilder<'lua> {
    #[allow(dead_code)]
    pub fn add_to_meta(self, new_meta: Table<'lua>) -> rlua::Result<Self> {
        let meta = self.object.table.get_metatable()
            .expect("Object had no meta table");
        for entry in new_meta.pairs::<rlua::Value, rlua::Value>() {
            let (key, value) = entry?;
            meta.set(key, value)?;
        }
        self.object.table.set_metatable(Some(meta));
        Ok(self)
    }

    pub fn property(self, prop: Property<'lua>) -> rlua::Result<Self> {
        let meta = self.object.table.get_metatable()
            .expect("Object had no meta table!");
        let properties = meta.get::<_, Table>("properties")?;
        let length = properties.len().unwrap_or(0) + 1;
        properties.set(length, prop)?;
        Ok(self)
    }

    #[allow(dead_code)]
    pub fn add_to_signals(self, name: String, func: rlua::Function)
                          -> rlua::Result<Self> {
        signal::connect_signal(self.lua, self.object.clone(), name, &[func])?;
        Ok(self)
    }

    pub fn build(self) -> Object<'lua> {
        self.object
    }
}

/// Trait implemented by all objects that represent OO lua objects.
///
/// This trait allows casting an object gotten back from the Lua runtime
/// into a concrete object so that Rust can do things with it.
///
/// You can't do anything to the object until it has been converted into a
/// canonical form using this trait.
pub trait Objectable<'lua, T, S: UserData + Default + Display + Clone> {
    fn cast(obj: Object<'lua>) -> rlua::Result<T> {
        let data = obj.table.get::<_, Table>("data")?.get::<_, AnyUserData>("data")?;
        if data.is::<S>() {
            Ok(Self::_wrap(obj.table))
        } else {
            use rlua::Error::RuntimeError;
            Err(RuntimeError("Could not cast object to concrete type".into()))
        }
    }

    /// Given the internal object table, constructs a concrete object out
    /// of it.
    ///
    /// This is only used internally, which is why it's prefixed with a "_"
    /// Please do not use it outside of object.rs.
    fn _wrap(table: Table<'lua>) -> T;

    fn state(&self) -> rlua::Result<S> {
        self.get_table().get::<_, Table>("data")?.get::<_, S>("data")
    }

    fn set_state(&self, data: S) -> rlua::Result<()> {
        self.get_table().get::<_, Table>("data")?.set("data", data)
    }

    /// Gets the internal table for the concrete object.
    /// Used internally by cast, though there's nothing wrong with it being
    /// used outside of internal object use.
    fn get_table(&self) -> Table<'lua>;

    fn allocate(lua: &'lua Lua, class: Class) -> rlua::Result<ObjectBuilder<'lua>>
    {
        let object = S::default();
        // TODO FIXME
        // we put a wrapper table here,
        // but in awesome they use lua_setuservalue.
        // I don't think this is equivilent...it just forces things to "work"
        let wrapper_table = lua.create_table();
        wrapper_table.set("data", object)?;
        let object_table = lua.create_table();
        object_table.set("data", wrapper_table)?;
        let meta = lua.create_table();
        meta.set("__class", class)?;
        meta.set("properties", Vec::<Property>::new().to_lua(lua)?)?;
        meta.set("signals", lua.create_table())?;
        meta.set("connect_signal",
                 lua.create_function(connect_signal))?;
        meta.set("disconnect_signal",
                 lua.create_function(disconnect_signal))?;
        meta.set("emit_signal", lua.create_function(emit_signal))?;
        meta.set("__index", lua.create_function(default_index))?;
        meta.set("__newindex", lua.create_function(default_newindex))?;
        meta.set("__tostring", lua.create_function(|_, object_table: Table| {
            Ok(format!("{}", object_table.get::<_, Table>("data")?.get::<_, S>("data")?))
        }))?;
        object_table.set_metatable(Some(meta));
        let object = Object { table: object_table };
        Ok(ObjectBuilder { object, lua })
    }
}

impl <'lua> ToLua<'lua> for Object<'lua> {
    fn to_lua(self, _: &'lua Lua) -> rlua::Result<Value<'lua>> {
        Ok(Value::Table(self.table))
    }
}

impl <'lua> Object<'lua> {
    /// Coerces a concrete object into a generic object.
    ///
    /// This requires a check to ensure data integrity, and it's often useless.
    /// Please don't use this method unless you need to.
    #[allow(dead_code)]
    #[deprecated]
    pub fn to_object<T, S, O>(obj: O) -> Self
        where S: Default + Display + Clone + UserData,
              O: Objectable<'lua, T, S>
    {
        let table = obj.get_table();
        let has_data = table.contains_key("data")
            .expect("Could not get data field of object table");
        assert_eq!(has_data, true);
        Object { table }
    }

    pub fn signals(&self) -> rlua::Result<rlua::Table<'lua>> {
        self.table.get::<_, Table>("signals")
    }

    pub fn table(self) -> rlua::Table<'lua> {
        self.table
    }
}

/// Default indexing of an Awesome object.
///
/// Automatically looks up contents in meta table, so instead of overriding this
/// it's easier to just add the required data in the meta table.
pub fn default_index<'lua>(lua: &'lua Lua,
                           (obj_table, index): (Table<'lua>, Value<'lua>))
                           -> rlua::Result<Value<'lua>> {
    // Look up in metatable first
    let meta = obj_table.get_metatable()
        .expect("Object had no metatable");
    if meta.get::<_, Table>("__class").is_ok() {
        if let Ok(val) = meta.raw_get::<_, Value>(index.clone()) {
            match val {
                Value::Nil => {},
                val => return Ok(val)
            }
        }
    }
    let index = String::from_lua(index, lua)?;
    match index.as_str() {
        "valid" => {
            Ok(Value::Boolean(
                if let Ok(class) = meta.get::<_, Table>("__class") {
                    let class: Class = class.into();
                    class.checker()?
                        .map(|checker| checker(obj_table.into()))
                        .unwrap_or(true)
                } else {
                    false
                }))
        },
        "data" => {
            obj_table.get("data")
        },
        index => {
            // Try see if there is a property of the class with the name
            if let Ok(class) = meta.get::<_, Table>("__class") {
                let props = class.get::<_, Vec<Property>>("properties")?;
                for prop in props {
                    if prop.name.as_str() == index {
                        // Property exists and has an index callback
                        if let Some(index) = prop.cb_index {
                            return index.call(obj_table)
                        }
                    }
                }
                match class.get::<_, Function>("__index_miss_handler") {
                    Ok(function) => {
                        return function.bind(obj_table)?.call(index)
                    },
                    Err(_) => {}
                }
            }
            // TODO property miss handler if index doesn't exst
            Ok(rlua::Value::Nil)
        }
    }
}

/// Default new indexing (assignment) of an Awesome object.
///
/// Automatically looks up contents in meta table, so instead of overriding this
/// it's easier to just add the required data in the meta table.
pub fn default_newindex<'lua>(_: &'lua Lua,
                              (obj_table, index, val):
                              (Table<'lua>, String, Value<'lua>))
                              -> rlua::Result<Value<'lua>> {
    // Look up in metatable first
    if let Some(meta) = obj_table.get_metatable() {
        if let Ok(val) = meta.raw_get::<_, Value>(index.clone()) {
            match val {
                Value::Nil => {},
                val => return Ok(val)
            }
        }
        let class = meta.get::<_, Table>("__class")?;
        let props = class.get::<_, Vec<Property>>("properties")?;
        for prop in props {
            if prop.name.as_str() == index {
                // Property exists and has a newindex callback
                if let Some(newindex) = prop.cb_newindex {
                    return newindex.bind(obj_table)?.call(val)
                }
            }
        }
        match class.get::<_, Function>("__newindex_miss_handler") {
            Ok(function) => {
                return function.bind(obj_table)?.call(index)
            },
            Err(_) => {}
        }
        // TODO property miss handler if index doesn't exst
    }
    Ok(Value::Nil)
}

fn connect_signal(lua: &Lua, (obj_table, signal, func): (Table, String, Function))
                  -> rlua::Result<()> {
    signal::connect_signal(lua, obj_table.into(), signal, &[func])
}

fn disconnect_signal(lua: &Lua, (obj_table, signal): (Table, String))
                     -> rlua::Result<()> {
    signal::disconnect_signal(lua, obj_table.into(), signal)
}

fn emit_signal(lua: &Lua, (obj_table, signal, args): (Table, String, Value))
               -> rlua::Result<()> {
    signal::emit_signal(lua, obj_table.into(), signal, args)
}
<commit_msg>Removed property setting for objects<commit_after>//! Utility methods and constructors for Lua objects

use std::fmt::Display;
use std::convert::From;
use rlua::{self, Lua, Table, UserData, AnyUserData, Value,
           FromLua, ToLua, Function};
use super::signal;
use super::class::Class;
use super::property::Property;

/// All Lua objects can be cast to this.
#[derive(Clone, Debug)]
pub struct Object<'lua> {
    table: Table<'lua>
}

impl <'lua> From<Table<'lua>> for Object<'lua> {
    fn from(table: Table<'lua>) -> Self {
        Object { table }
    }
}

/// Construct a new object, used when using the default Objectable::new.
pub struct ObjectBuilder<'lua>{
    lua: &'lua Lua,
    object: Object<'lua>
}

impl <'lua> ObjectBuilder<'lua> {
    #[allow(dead_code)]
    pub fn add_to_meta(self, new_meta: Table<'lua>) -> rlua::Result<Self> {
        let meta = self.object.table.get_metatable()
            .expect("Object had no meta table");
        for entry in new_meta.pairs::<rlua::Value, rlua::Value>() {
            let (key, value) = entry?;
            meta.set(key, value)?;
        }
        self.object.table.set_metatable(Some(meta));
        Ok(self)
    }

    #[allow(dead_code)]
    pub fn add_to_signals(self, name: String, func: rlua::Function)
                          -> rlua::Result<Self> {
        signal::connect_signal(self.lua, self.object.clone(), name, &[func])?;
        Ok(self)
    }

    pub fn build(self) -> Object<'lua> {
        self.object
    }
}

/// Trait implemented by all objects that represent OO lua objects.
///
/// This trait allows casting an object gotten back from the Lua runtime
/// into a concrete object so that Rust can do things with it.
///
/// You can't do anything to the object until it has been converted into a
/// canonical form using this trait.
pub trait Objectable<'lua, T, S: UserData + Default + Display + Clone> {
    fn cast(obj: Object<'lua>) -> rlua::Result<T> {
        let data = obj.table.get::<_, Table>("data")?.get::<_, AnyUserData>("data")?;
        if data.is::<S>() {
            Ok(Self::_wrap(obj.table))
        } else {
            use rlua::Error::RuntimeError;
            Err(RuntimeError("Could not cast object to concrete type".into()))
        }
    }

    /// Given the internal object table, constructs a concrete object out
    /// of it.
    ///
    /// This is only used internally, which is why it's prefixed with a "_"
    /// Please do not use it outside of object.rs.
    fn _wrap(table: Table<'lua>) -> T;

    fn state(&self) -> rlua::Result<S> {
        self.get_table().get::<_, Table>("data")?.get::<_, S>("data")
    }

    fn set_state(&self, data: S) -> rlua::Result<()> {
        self.get_table().get::<_, Table>("data")?.set("data", data)
    }

    /// Gets the internal table for the concrete object.
    /// Used internally by cast, though there's nothing wrong with it being
    /// used outside of internal object use.
    fn get_table(&self) -> Table<'lua>;

    fn allocate(lua: &'lua Lua, class: Class) -> rlua::Result<ObjectBuilder<'lua>>
    {
        let object = S::default();
        // TODO FIXME
        // we put a wrapper table here,
        // but in awesome they use lua_setuservalue.
        // I don't think this is equivilent...it just forces things to "work"
        let wrapper_table = lua.create_table();
        wrapper_table.set("data", object)?;
        let object_table = lua.create_table();
        object_table.set("data", wrapper_table)?;
        let meta = lua.create_table();
        meta.set("__class", class)?;
        meta.set("properties", Vec::<Property>::new().to_lua(lua)?)?;
        meta.set("signals", lua.create_table())?;
        meta.set("connect_signal",
                 lua.create_function(connect_signal))?;
        meta.set("disconnect_signal",
                 lua.create_function(disconnect_signal))?;
        meta.set("emit_signal", lua.create_function(emit_signal))?;
        meta.set("__index", lua.create_function(default_index))?;
        meta.set("__newindex", lua.create_function(default_newindex))?;
        meta.set("__tostring", lua.create_function(|_, object_table: Table| {
            Ok(format!("{}", object_table.get::<_, Table>("data")?.get::<_, S>("data")?))
        }))?;
        object_table.set_metatable(Some(meta));
        let object = Object { table: object_table };
        Ok(ObjectBuilder { object, lua })
    }
}

impl <'lua> ToLua<'lua> for Object<'lua> {
    fn to_lua(self, _: &'lua Lua) -> rlua::Result<Value<'lua>> {
        Ok(Value::Table(self.table))
    }
}

impl <'lua> Object<'lua> {
    /// Coerces a concrete object into a generic object.
    ///
    /// This requires a check to ensure data integrity, and it's often useless.
    /// Please don't use this method unless you need to.
    #[allow(dead_code)]
    #[deprecated]
    pub fn to_object<T, S, O>(obj: O) -> Self
        where S: Default + Display + Clone + UserData,
              O: Objectable<'lua, T, S>
    {
        let table = obj.get_table();
        let has_data = table.contains_key("data")
            .expect("Could not get data field of object table");
        assert_eq!(has_data, true);
        Object { table }
    }

    pub fn signals(&self) -> rlua::Result<rlua::Table<'lua>> {
        self.table.get::<_, Table>("signals")
    }

    pub fn table(self) -> rlua::Table<'lua> {
        self.table
    }
}

/// Default indexing of an Awesome object.
///
/// Automatically looks up contents in meta table, so instead of overriding this
/// it's easier to just add the required data in the meta table.
pub fn default_index<'lua>(lua: &'lua Lua,
                           (obj_table, index): (Table<'lua>, Value<'lua>))
                           -> rlua::Result<Value<'lua>> {
    // Look up in metatable first
    let meta = obj_table.get_metatable()
        .expect("Object had no metatable");
    if meta.get::<_, Table>("__class").is_ok() {
        if let Ok(val) = meta.raw_get::<_, Value>(index.clone()) {
            match val {
                Value::Nil => {},
                val => return Ok(val)
            }
        }
    }
    let index = String::from_lua(index, lua)?;
    match index.as_str() {
        "valid" => {
            Ok(Value::Boolean(
                if let Ok(class) = meta.get::<_, Table>("__class") {
                    let class: Class = class.into();
                    class.checker()?
                        .map(|checker| checker(obj_table.into()))
                        .unwrap_or(true)
                } else {
                    false
                }))
        },
        "data" => {
            obj_table.get("data")
        },
        index => {
            // Try see if there is a property of the class with the name
            if let Ok(class) = meta.get::<_, Table>("__class") {
                let props = class.get::<_, Vec<Property>>("properties")?;
                for prop in props {
                    if prop.name.as_str() == index {
                        // Property exists and has an index callback
                        if let Some(index) = prop.cb_index {
                            return index.call(obj_table)
                        }
                    }
                }
                match class.get::<_, Function>("__index_miss_handler") {
                    Ok(function) => {
                        return function.bind(obj_table)?.call(index)
                    },
                    Err(_) => {}
                }
            }
            // TODO property miss handler if index doesn't exst
            Ok(rlua::Value::Nil)
        }
    }
}

/// Default new indexing (assignment) of an Awesome object.
///
/// Automatically looks up contents in meta table, so instead of overriding this
/// it's easier to just add the required data in the meta table.
pub fn default_newindex<'lua>(_: &'lua Lua,
                              (obj_table, index, val):
                              (Table<'lua>, String, Value<'lua>))
                              -> rlua::Result<Value<'lua>> {
    // Look up in metatable first
    if let Some(meta) = obj_table.get_metatable() {
        if let Ok(val) = meta.raw_get::<_, Value>(index.clone()) {
            match val {
                Value::Nil => {},
                val => return Ok(val)
            }
        }
        let class = meta.get::<_, Table>("__class")?;
        let props = class.get::<_, Vec<Property>>("properties")?;
        for prop in props {
            if prop.name.as_str() == index {
                // Property exists and has a newindex callback
                if let Some(newindex) = prop.cb_newindex {
                    return newindex.bind(obj_table)?.call(val)
                }
            }
        }
        match class.get::<_, Function>("__newindex_miss_handler") {
            Ok(function) => {
                return function.bind(obj_table)?.call(index)
            },
            Err(_) => {}
        }
        // TODO property miss handler if index doesn't exst
    }
    Ok(Value::Nil)
}

fn connect_signal(lua: &Lua, (obj_table, signal, func): (Table, String, Function))
                  -> rlua::Result<()> {
    signal::connect_signal(lua, obj_table.into(), signal, &[func])
}

fn disconnect_signal(lua: &Lua, (obj_table, signal): (Table, String))
                     -> rlua::Result<()> {
    signal::disconnect_signal(lua, obj_table.into(), signal)
}

fn emit_signal(lua: &Lua, (obj_table, signal, args): (Table, String, Value))
               -> rlua::Result<()> {
    signal::emit_signal(lua, obj_table.into(), signal, args)
}
<|endoftext|>


<commit_before>//! Error types.

use byteorder;
use phf;
use std::error;
use std::convert::From;
use std::fmt;
use std::io;
use std::result;
use std::collections::HashMap;

use {Result, DbErrorNew};
use types::Type;

include!(concat!(env!("OUT_DIR"), "/sqlstate.rs"));

/// A Postgres error or notice.
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct DbError {
    /// The field contents are ERROR, FATAL, or PANIC (in an error message),
    /// or WARNING, NOTICE, DEBUG, INFO, or LOG (in a notice message), or a
    /// localized translation of one of these.
    pub severity: String,

    /// The SQLSTATE code for the error.
    pub code: SqlState,

    /// The primary human-readable error message. This should be accurate but
    /// terse (typically one line).
    pub message: String,

    /// An optional secondary error message carrying more detail about the
    /// problem. Might run to multiple lines.
    pub detail: Option<String>,

    /// An optional suggestion what to do about the problem. This is intended
    /// to differ from Detail in that it offers advice (potentially
    /// inappropriate) rather than hard facts. Might run to multiple lines.
    pub hint: Option<String>,

    /// An optional error cursor position into either the original query string
    /// or an internally generated query.
    pub position: Option<ErrorPosition>,

    /// An indication of the context in which the error occurred. Presently
    /// this includes a call stack traceback of active procedural language
    /// functions and internally-generated queries. The trace is one entry per
    /// line, most recent first.
    pub where_: Option<String>,

    /// If the error was associated with a specific database object, the name
    /// of the schema containing that object, if any. (PostgreSQL 9.3+)
    pub schema: Option<String>,

    /// If the error was associated with a specific table, the name of the
    /// table. (Refer to the schema name field for the name of the table's
    /// schema.) (PostgreSQL 9.3+)
    pub table: Option<String>,

    /// If the error was associated with a specific table column, the name of
    /// the column. (Refer to the schema and table name fields to identify the
    /// table.) (PostgreSQL 9.3+)
    pub column: Option<String>,

    /// If the error was associated with a specific data type, the name of the
    /// data type. (Refer to the schema name field for the name of the data
    /// type's schema.) (PostgreSQL 9.3+)
    pub datatype: Option<String>,

    /// If the error was associated with a specific constraint, the name of the
    /// constraint. Refer to fields listed above for the associated table or
    /// domain. (For this purpose, indexes are treated as constraints, even if
    /// they weren't created with constraint syntax.) (PostgreSQL 9.3+)
    pub constraint: Option<String>,

    /// The file name of the source-code location where the error was reported.
    pub file: String,

    /// The line number of the source-code location where the error was
    /// reported.
    pub line: u32,

    /// The name of the source-code routine reporting the error.
    pub routine: String,

    _p: (),
}

impl DbErrorNew for DbError {
    fn new_raw(fields: Vec<(u8, String)>) -> result::Result<DbError, ()> {
        let mut map: HashMap<_, _> = fields.into_iter().collect();
        Ok(DbError {
            severity: try!(map.remove(&b'S').ok_or(())),
            code: SqlState::from_code(try!(map.remove(&b'C').ok_or(()))),
            message: try!(map.remove(&b'M').ok_or(())),
            detail: map.remove(&b'D'),
            hint: map.remove(&b'H'),
            position: match map.remove(&b'P') {
                Some(pos) => Some(ErrorPosition::Normal(try!(pos.parse().map_err(|_| ())))),
                None => {
                    match map.remove(&b'p') {
                        Some(pos) => {
                            Some(ErrorPosition::Internal {
                                position: try!(pos.parse().map_err(|_| ())),
                                query: try!(map.remove(&b'q').ok_or(())),
                            })
                        }
                        None => None,
                    }
                }
            },
            where_: map.remove(&b'W'),
            schema: map.remove(&b's'),
            table: map.remove(&b't'),
            column: map.remove(&b'c'),
            datatype: map.remove(&b'd'),
            constraint: map.remove(&b'n'),
            file: try!(map.remove(&b'F').ok_or(())),
            line: try!(map.remove(&b'L').and_then(|l| l.parse().ok()).ok_or(())),
            routine: try!(map.remove(&b'R').ok_or(())),
            _p: (),
        })
    }

    fn new_connect<T>(fields: Vec<(u8, String)>) -> result::Result<T, ConnectError> {
        match DbError::new_raw(fields) {
            Ok(err) => Err(ConnectError::DbError(Box::new(err))),
            Err(()) => Err(ConnectError::IoError(::bad_response())),
        }
    }

    fn new<T>(fields: Vec<(u8, String)>) -> Result<T> {
        match DbError::new_raw(fields) {
            Ok(err) => Err(Error::DbError(Box::new(err))),
            Err(()) => Err(Error::IoError(::bad_response())),
        }
    }
}

impl fmt::Display for DbError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(fmt, "{}: {}", self.severity, self.message)
    }
}

impl error::Error for DbError {
    fn description(&self) -> &str {
        &self.message
    }
}

/// Reasons a new Postgres connection could fail.
#[derive(Debug)]
pub enum ConnectError {
    /// An error creating `ConnectParams`.
    BadConnectParams(Box<error::Error + Sync + Send>),
    /// The `ConnectParams` was missing a user.
    MissingUser,
    /// An error from the Postgres server itself.
    DbError(Box<DbError>),
    /// A password was required but not provided in the `ConnectParams`.
    MissingPassword,
    /// The Postgres server requested an authentication method not supported
    /// by the driver.
    UnsupportedAuthentication,
    /// The Postgres server does not support SSL encryption.
    NoSslSupport,
    /// An error initializing the SSL session.
    SslError(Box<error::Error + Sync + Send>),
    /// An error communicating with the server.
    IoError(io::Error),
}

impl fmt::Display for ConnectError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        try!(fmt.write_str(error::Error::description(self)));
        match *self {
            ConnectError::BadConnectParams(ref msg) => write!(fmt, ": {}", msg),
            ConnectError::DbError(ref err) => write!(fmt, ": {}", err),
            ConnectError::SslError(ref err) => write!(fmt, ": {}", err),
            ConnectError::IoError(ref err) => write!(fmt, ": {}", err),
            _ => Ok(()),
        }
    }
}

impl error::Error for ConnectError {
    fn description(&self) -> &str {
        match *self {
            ConnectError::BadConnectParams(_) => "Error creating `ConnectParams`",
            ConnectError::MissingUser => "User missing in `ConnectParams`",
            ConnectError::DbError(_) => "Error reported by Postgres",
            ConnectError::MissingPassword => {
                "The server requested a password but none was provided"
            }
            ConnectError::UnsupportedAuthentication => {
                "The server requested an unsupported authentication method"
            }
            ConnectError::NoSslSupport => "The server does not support SSL",
            ConnectError::SslError(_) => "Error initiating SSL session",
            ConnectError::IoError(_) => "Error communicating with the server",
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            ConnectError::BadConnectParams(ref err) => Some(&**err),
            ConnectError::DbError(ref err) => Some(&**err),
            ConnectError::SslError(ref err) => Some(&**err),
            ConnectError::IoError(ref err) => Some(err),
            _ => None,
        }
    }
}

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

impl From<DbError> for ConnectError {
    fn from(err: DbError) -> ConnectError {
        ConnectError::DbError(Box::new(err))
    }
}

impl From<byteorder::Error> for ConnectError {
    fn from(err: byteorder::Error) -> ConnectError {
        ConnectError::IoError(From::from(err))
    }
}

/// Represents the position of an error in a query.
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum ErrorPosition {
    /// A position in the original query.
    Normal(u32),
    /// A position in an internally generated query.
    Internal {
        /// The byte position.
        position: u32,
        /// A query generated by the Postgres server.
        query: String,
    },
}

/// An error encountered when communicating with the Postgres server.
#[derive(Debug)]
pub enum Error {
    /// An error reported by the Postgres server.
    DbError(Box<DbError>),
    /// An error communicating with the Postgres server.
    IoError(io::Error),
    /// An attempt was made to convert between incompatible Rust and Postgres
    /// types.
    WrongType(Type),
    /// An attempt was made to read from a column that does not exist.
    InvalidColumn,
    /// An error converting between Postgres and Rust types.
    Conversion(Box<error::Error + Sync + Send>),
}

impl fmt::Display for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        try!(fmt.write_str(error::Error::description(self)));
        match *self {
            Error::DbError(ref err) => write!(fmt, ": {}", err),
            Error::IoError(ref err) => write!(fmt, ": {}", err),
            Error::WrongType(ref ty) => write!(fmt, ": saw type {:?}", ty),
            Error::Conversion(ref err) => write!(fmt, ": {}", err),
            _ => Ok(()),
        }
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::DbError(_) => "Error reported by Postgres",
            Error::IoError(_) => "Error communicating with the server",
            Error::WrongType(_) => "Unexpected type",
            Error::InvalidColumn => "Invalid column",
            Error::Conversion(_) => "Error converting between Postgres and Rust types",
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            Error::DbError(ref err) => Some(&**err),
            Error::IoError(ref err) => Some(err),
            Error::Conversion(ref err) => Some(&**err),
            _ => None,
        }
    }
}

impl From<DbError> for Error {
    fn from(err: DbError) -> Error {
        Error::DbError(Box::new(err))
    }
}

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

impl From<byteorder::Error> for Error {
    fn from(err: byteorder::Error) -> Error {
        Error::IoError(From::from(err))
    }
}

impl From<Error> for io::Error {
    fn from(err: Error) -> io::Error {
        io::Error::new(io::ErrorKind::Other, err)
    }
}
<commit_msg>Manually implement Debug for DbError<commit_after>//! Error types.

use byteorder;
use phf;
use std::error;
use std::convert::From;
use std::fmt;
use std::io;
use std::result;
use std::collections::HashMap;

use {Result, DbErrorNew};
use types::Type;

include!(concat!(env!("OUT_DIR"), "/sqlstate.rs"));

/// A Postgres error or notice.
#[derive(Clone, PartialEq, Eq)]
pub struct DbError {
    /// The field contents are ERROR, FATAL, or PANIC (in an error message),
    /// or WARNING, NOTICE, DEBUG, INFO, or LOG (in a notice message), or a
    /// localized translation of one of these.
    pub severity: String,

    /// The SQLSTATE code for the error.
    pub code: SqlState,

    /// The primary human-readable error message. This should be accurate but
    /// terse (typically one line).
    pub message: String,

    /// An optional secondary error message carrying more detail about the
    /// problem. Might run to multiple lines.
    pub detail: Option<String>,

    /// An optional suggestion what to do about the problem. This is intended
    /// to differ from Detail in that it offers advice (potentially
    /// inappropriate) rather than hard facts. Might run to multiple lines.
    pub hint: Option<String>,

    /// An optional error cursor position into either the original query string
    /// or an internally generated query.
    pub position: Option<ErrorPosition>,

    /// An indication of the context in which the error occurred. Presently
    /// this includes a call stack traceback of active procedural language
    /// functions and internally-generated queries. The trace is one entry per
    /// line, most recent first.
    pub where_: Option<String>,

    /// If the error was associated with a specific database object, the name
    /// of the schema containing that object, if any. (PostgreSQL 9.3+)
    pub schema: Option<String>,

    /// If the error was associated with a specific table, the name of the
    /// table. (Refer to the schema name field for the name of the table's
    /// schema.) (PostgreSQL 9.3+)
    pub table: Option<String>,

    /// If the error was associated with a specific table column, the name of
    /// the column. (Refer to the schema and table name fields to identify the
    /// table.) (PostgreSQL 9.3+)
    pub column: Option<String>,

    /// If the error was associated with a specific data type, the name of the
    /// data type. (Refer to the schema name field for the name of the data
    /// type's schema.) (PostgreSQL 9.3+)
    pub datatype: Option<String>,

    /// If the error was associated with a specific constraint, the name of the
    /// constraint. Refer to fields listed above for the associated table or
    /// domain. (For this purpose, indexes are treated as constraints, even if
    /// they weren't created with constraint syntax.) (PostgreSQL 9.3+)
    pub constraint: Option<String>,

    /// The file name of the source-code location where the error was reported.
    pub file: String,

    /// The line number of the source-code location where the error was
    /// reported.
    pub line: u32,

    /// The name of the source-code routine reporting the error.
    pub routine: String,

    _p: (),
}

impl DbErrorNew for DbError {
    fn new_raw(fields: Vec<(u8, String)>) -> result::Result<DbError, ()> {
        let mut map: HashMap<_, _> = fields.into_iter().collect();
        Ok(DbError {
            severity: try!(map.remove(&b'S').ok_or(())),
            code: SqlState::from_code(try!(map.remove(&b'C').ok_or(()))),
            message: try!(map.remove(&b'M').ok_or(())),
            detail: map.remove(&b'D'),
            hint: map.remove(&b'H'),
            position: match map.remove(&b'P') {
                Some(pos) => Some(ErrorPosition::Normal(try!(pos.parse().map_err(|_| ())))),
                None => {
                    match map.remove(&b'p') {
                        Some(pos) => {
                            Some(ErrorPosition::Internal {
                                position: try!(pos.parse().map_err(|_| ())),
                                query: try!(map.remove(&b'q').ok_or(())),
                            })
                        }
                        None => None,
                    }
                }
            },
            where_: map.remove(&b'W'),
            schema: map.remove(&b's'),
            table: map.remove(&b't'),
            column: map.remove(&b'c'),
            datatype: map.remove(&b'd'),
            constraint: map.remove(&b'n'),
            file: try!(map.remove(&b'F').ok_or(())),
            line: try!(map.remove(&b'L').and_then(|l| l.parse().ok()).ok_or(())),
            routine: try!(map.remove(&b'R').ok_or(())),
            _p: (),
        })
    }

    fn new_connect<T>(fields: Vec<(u8, String)>) -> result::Result<T, ConnectError> {
        match DbError::new_raw(fields) {
            Ok(err) => Err(ConnectError::DbError(Box::new(err))),
            Err(()) => Err(ConnectError::IoError(::bad_response())),
        }
    }

    fn new<T>(fields: Vec<(u8, String)>) -> Result<T> {
        match DbError::new_raw(fields) {
            Ok(err) => Err(Error::DbError(Box::new(err))),
            Err(()) => Err(Error::IoError(::bad_response())),
        }
    }
}

// manual impl to leave out _p
impl fmt::Debug for DbError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.debug_struct("DbError")
           .field("severity", &self.severity)
           .field("code", &self.code)
           .field("message", &self.message)
           .field("detail", &self.detail)
           .field("hint", &self.hint)
           .field("position", &self.position)
           .field("where_", &self.where_)
           .field("schema", &self.schema)
           .field("table", &self.table)
           .field("column", &self.column)
           .field("datatype", &self.datatype)
           .field("constraint", &self.constraint)
           .field("file", &self.file)
           .field("line", &self.line)
           .field("routine", &self.routine)
           .finish()
    }
}

impl fmt::Display for DbError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(fmt, "{}: {}", self.severity, self.message)
    }
}

impl error::Error for DbError {
    fn description(&self) -> &str {
        &self.message
    }
}

/// Reasons a new Postgres connection could fail.
#[derive(Debug)]
pub enum ConnectError {
    /// An error creating `ConnectParams`.
    BadConnectParams(Box<error::Error + Sync + Send>),
    /// The `ConnectParams` was missing a user.
    MissingUser,
    /// An error from the Postgres server itself.
    DbError(Box<DbError>),
    /// A password was required but not provided in the `ConnectParams`.
    MissingPassword,
    /// The Postgres server requested an authentication method not supported
    /// by the driver.
    UnsupportedAuthentication,
    /// The Postgres server does not support SSL encryption.
    NoSslSupport,
    /// An error initializing the SSL session.
    SslError(Box<error::Error + Sync + Send>),
    /// An error communicating with the server.
    IoError(io::Error),
}

impl fmt::Display for ConnectError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        try!(fmt.write_str(error::Error::description(self)));
        match *self {
            ConnectError::BadConnectParams(ref msg) => write!(fmt, ": {}", msg),
            ConnectError::DbError(ref err) => write!(fmt, ": {}", err),
            ConnectError::SslError(ref err) => write!(fmt, ": {}", err),
            ConnectError::IoError(ref err) => write!(fmt, ": {}", err),
            _ => Ok(()),
        }
    }
}

impl error::Error for ConnectError {
    fn description(&self) -> &str {
        match *self {
            ConnectError::BadConnectParams(_) => "Error creating `ConnectParams`",
            ConnectError::MissingUser => "User missing in `ConnectParams`",
            ConnectError::DbError(_) => "Error reported by Postgres",
            ConnectError::MissingPassword => {
                "The server requested a password but none was provided"
            }
            ConnectError::UnsupportedAuthentication => {
                "The server requested an unsupported authentication method"
            }
            ConnectError::NoSslSupport => "The server does not support SSL",
            ConnectError::SslError(_) => "Error initiating SSL session",
            ConnectError::IoError(_) => "Error communicating with the server",
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            ConnectError::BadConnectParams(ref err) => Some(&**err),
            ConnectError::DbError(ref err) => Some(&**err),
            ConnectError::SslError(ref err) => Some(&**err),
            ConnectError::IoError(ref err) => Some(err),
            _ => None,
        }
    }
}

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

impl From<DbError> for ConnectError {
    fn from(err: DbError) -> ConnectError {
        ConnectError::DbError(Box::new(err))
    }
}

impl From<byteorder::Error> for ConnectError {
    fn from(err: byteorder::Error) -> ConnectError {
        ConnectError::IoError(From::from(err))
    }
}

/// Represents the position of an error in a query.
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum ErrorPosition {
    /// A position in the original query.
    Normal(u32),
    /// A position in an internally generated query.
    Internal {
        /// The byte position.
        position: u32,
        /// A query generated by the Postgres server.
        query: String,
    },
}

/// An error encountered when communicating with the Postgres server.
#[derive(Debug)]
pub enum Error {
    /// An error reported by the Postgres server.
    DbError(Box<DbError>),
    /// An error communicating with the Postgres server.
    IoError(io::Error),
    /// An attempt was made to convert between incompatible Rust and Postgres
    /// types.
    WrongType(Type),
    /// An attempt was made to read from a column that does not exist.
    InvalidColumn,
    /// An error converting between Postgres and Rust types.
    Conversion(Box<error::Error + Sync + Send>),
}

impl fmt::Display for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        try!(fmt.write_str(error::Error::description(self)));
        match *self {
            Error::DbError(ref err) => write!(fmt, ": {}", err),
            Error::IoError(ref err) => write!(fmt, ": {}", err),
            Error::WrongType(ref ty) => write!(fmt, ": saw type {:?}", ty),
            Error::Conversion(ref err) => write!(fmt, ": {}", err),
            _ => Ok(()),
        }
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::DbError(_) => "Error reported by Postgres",
            Error::IoError(_) => "Error communicating with the server",
            Error::WrongType(_) => "Unexpected type",
            Error::InvalidColumn => "Invalid column",
            Error::Conversion(_) => "Error converting between Postgres and Rust types",
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            Error::DbError(ref err) => Some(&**err),
            Error::IoError(ref err) => Some(err),
            Error::Conversion(ref err) => Some(&**err),
            _ => None,
        }
    }
}

impl From<DbError> for Error {
    fn from(err: DbError) -> Error {
        Error::DbError(Box::new(err))
    }
}

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

impl From<byteorder::Error> for Error {
    fn from(err: byteorder::Error) -> Error {
        Error::IoError(From::from(err))
    }
}

impl From<Error> for io::Error {
    fn from(err: Error) -> io::Error {
        io::Error::new(io::ErrorKind::Other, err)
    }
}
<|endoftext|>


<commit_before>/// Incremental Graph Representations

use std::fmt::Debug;
use std::hash::Hash;
use std::rc::Rc;

use adapton::engine::{cell, ns, name_of_str, name_unit, Name};
use adapton::collections::{list_of_tree, tree_fold_seq, tree_of_list, Dir2, List, ListIntro,
                           MapIntro, MapElim, SetIntro, Tree, TreeIntro};
use adapton::collections::trie::{trie_fold_seq, Set, Trie, TrieIntro};

/// Representation of a graph as a list of edges, where edges are
/// a pair of node ids.
#[derive(Debug,PartialEq,Eq,Hash,Clone)]
pub struct Graph<Node> {
    edge_tree: Tree<(Node, Node)>,
}

/// Representation of a graph as finite map from node ids to
/// an outgoing adjacency list of node ids.
#[derive(Debug,PartialEq,Eq,Hash,Clone)]
pub struct AdjacencyGraph<Node>
    where Node: Debug + Clone + Hash + PartialEq + Eq + 'static
{
    adjacency_map: Trie<(Node, Tree<Node>)>,
}

trait NamedGraph<Node>: Debug + Clone + Hash + PartialEq + Eq {
    fn name(nm: Name, g: Self) -> Self;
}

/// Produce a graph.
pub trait GraphIntro<Node>: Debug + Clone + Hash + PartialEq + Eq {
    /// Yields an empty graph, i.e. no vertices or edges.
    fn empty() -> Self;
    /// Adds the edge `(src, dst)` named `nm` to the graph `graph`.
    fn add_edge(graph: Self, nm: Name, src: Node, dst: Node) -> Self;
}

/// Reduce a graph to a value.
pub trait GraphElim<Node>: Debug + Clone + Hash + PartialEq + Eq {
    /// Returns a set of the edges in the graph `graph`.
    fn edges(graph: &Self) -> Set<(Node, Node)>;
    /// Returns a set of the vertices of the graph `graph`.
    fn vertices(graph: &Self) -> Set<Node>;
    /// Returns a graph whose set of edges are the reversed edges of `graph`.
    fn reverse_edges(graph: &Self) -> Self;
}

impl<Node: Debug + Clone + Hash + PartialEq + Eq + 'static> NamedGraph<Node> for Graph<Node> {
    fn name(nm: Name, g: Graph<Node>) -> Graph<Node> {
        let edge_list = ns(name_of_str("list_of_tree"),
                           move || list_of_tree(g.edge_tree, Dir2::Left));
        let el = List::name_art(Some(nm), edge_list);
        let edge_tree = ns(name_of_str("tree_of_list"),
                           || tree_of_list::<_, _, Tree<_>, _>(Dir2::Left, el));
        Graph::<Node> { edge_tree: edge_tree }
    }
}

impl<Node: Debug + Clone + Hash + PartialEq + Eq + 'static> GraphIntro<Node> for Graph<Node> {
    fn empty() -> Graph<Node> {
        Graph::<Node> { edge_tree: Tree::nil() }
    }

    fn add_edge(graph: Graph<Node>, nm: Name, src: Node, dst: Node) -> Graph<Node> {
        let edge_list = ns(name_of_str("list_of_tree"),
                           move || list_of_tree(graph.edge_tree, Dir2::Left));
        let el = List::name_art(Some(nm), edge_list);
        let el = List::cons((src, dst), el);
        let edge_tree = ns(name_of_str("tree_of_list"),
                           || tree_of_list::<_, _, Tree<_>, _>(Dir2::Left, el));
        Graph::<Node> { edge_tree: edge_tree }
    }
}

impl<Node: Debug + Clone + Hash + PartialEq + Eq + 'static> GraphElim<Node> for Graph<Node> {
    fn edges(graph: &Graph<Node>) -> Set<(Node, Node)> {
        tree_fold_seq(graph.edge_tree.clone(),
                      Dir2::Left,
                      SetIntro::empty(),
                      Rc::new(|e, set| SetIntro::add(set, e)),
                      Rc::new(|_, set| set),
                      Rc::new(|nm: Name, _, set| {
                          TrieIntro::name(nm.clone(), TrieIntro::art(cell(nm, set)))
                      }))
    }

    fn vertices(graph: &Graph<Node>) -> Set<Node> {
        let edge_trie = ns(name_of_str("edge_trie_vertices"), || Self::edges(graph));
        trie_fold_seq(edge_trie,
                      SetIntro::empty(),
                      Rc::new(|((src,dst), ()), set| {
                          let add_src = SetIntro::add(set, src);
                          SetIntro::add(add_src, dst)
                      }),
                      Rc::new(move |set| set),
                      Rc::new(move |n: Name, set| {
                          Set::name(n.clone(), Set::art(cell(n, set)))
                      }))
    }

    fn reverse_edges(graph: &Graph<Node>) -> Graph<Node> {
        let es = Self::edges(graph);
        trie_fold_seq(es,
                      Self::empty(),
                      Rc::new(|((src, dst), ()), g| Self::add_edge(g, name_unit(), dst, src)),
                      Rc::new(|g| g),
                      Rc::new(|nm: Name, g| Self::name(nm, g)))
    }
}

impl<Node: Debug + Clone + Hash + PartialEq + Eq + 'static> NamedGraph<Node>
    for AdjacencyGraph<Node> {
    fn name(nm: Name, g: AdjacencyGraph<Node>) -> AdjacencyGraph<Node> {
        let adj = TrieIntro::name(nm.clone(),
                                  TrieIntro::art(cell(nm, g.adjacency_map)));
        AdjacencyGraph { adjacency_map: adj }
    }
}

impl<Node: Debug + Clone + Hash + PartialEq + Eq + 'static>
    GraphIntro<Node> for AdjacencyGraph<Node> {
        fn empty() -> AdjacencyGraph<Node> {
            AdjacencyGraph::<Node> {
                adjacency_map: MapIntro::empty(),
            }
        }

        fn add_edge(graph: AdjacencyGraph<Node>,
                    nm: Name, src: Node, dst: Node) -> AdjacencyGraph<Node> {
            let src_adj_list = MapElim::find(&graph.adjacency_map, &src);
            match src_adj_list {
                None => {
                    // let adj = List::name_art(Some(nm.clone()), List::nil());
                    let adj = List::cons(dst, List::nil());
                    let edge_tree = tree_of_list::<_, _, Tree<_>, _>(Dir2::Left, adj);
                    AdjacencyGraph::<Node> {
                        adjacency_map: TrieIntro::extend(nm, graph.adjacency_map,
                                                         (src, edge_tree)),
                    }
                }
                Some(adj_nodes) => {
                    let adj = list_of_tree(adj_nodes, Dir2::Left);
                    // let adj = List::name_art(Some(nm.clone()), adj);
                    let adj = List::cons(dst, adj);
                    let edge_tree = tree_of_list::<_, _, Tree<_>, _>(Dir2::Left, adj);
                    AdjacencyGraph::<Node> {
                        adjacency_map: TrieIntro::extend(nm, graph.adjacency_map,
                                                         (src, edge_tree)),
                    }
                }
            }
        }
    }

impl<Node: Debug + Copy + Clone + Hash + PartialEq + Eq + 'static>
    GraphElim<Node> for AdjacencyGraph<Node> {
        fn edges(graph: &AdjacencyGraph<Node>) -> Set<(Node, Node)> {
            trie_fold_seq(graph.adjacency_map.clone(), SetIntro::empty(),
                          Rc::new(|(src, dsts), set|
                                  tree_fold_seq(dsts, Dir2::Left, set,
                                                Rc::new(move |dst, set|
                                                        SetIntro::add(set, (src, dst))),
                                                Rc::new(|_, set| set),
                                                Rc::new(|nm: Name, _, set| {
                                                    TrieIntro::name(nm.clone(),
                                                                    TrieIntro::art(cell(nm,
                                                                                set)))
                                                }))),
                          Rc::new(|set| set),
                          Rc::new(|nm: Name, set|
                                  TrieIntro::name(nm.clone(),
                                                  TrieIntro::art(cell(nm, set)))))
        }

        fn vertices(graph: &AdjacencyGraph<Node>) -> Set<Node> {
            trie_fold_seq(graph.adjacency_map.clone(), SetIntro::empty(),
                          Rc::new(|(src, dsts), set| {
                              let src_set = SetIntro::add(set, src);
                              tree_fold_seq(dsts, Dir2::Left, src_set,
                                            Rc::new(|dst, set| SetIntro::add(set, dst)),
                                            Rc::new(|_, set| set),
                                            Rc::new(|nm: Name, _, set|
                                                    TrieIntro::name(nm.clone(),
                                                                    TrieIntro::art(cell(nm, set)))))
                          }),
                          Rc::new(|set| set),
                          Rc::new(|nm: Name, set| TrieIntro::name(nm.clone(),
                                                                  TrieIntro::art(cell(nm, set)))))
        }

        fn reverse_edges(graph: &AdjacencyGraph<Node>) -> AdjacencyGraph<Node> {
            let es = Self::edges(graph);
            trie_fold_seq(es,
                          Self::empty(),
                          Rc::new(|((src, dst), ()), g| Self::add_edge(g, name_unit(), dst, src)),
                          Rc::new(|g| g),
                          Rc::new(|nm: Name, g| Self::name(nm, g)))
        }
    }

pub fn adjacency_of_edge_list<X: Hash + Clone + Debug + PartialEq + Eq>(el_graph: &Graph<X>)
                                                                        -> AdjacencyGraph<X> {
    let adj_graph = AdjacencyGraph::empty();
    tree_fold_seq(el_graph.edge_tree.clone(),
                  Dir2::Left,
                  adj_graph,
                  Rc::new(|(src, dst), g| AdjacencyGraph::add_edge(g, name_unit(), src, dst)),
                  Rc::new(|_, set| set),
                  Rc::new(|nm: Name, _, g: AdjacencyGraph<_>| {
                      let adj = TrieIntro::name(nm.clone(),
                                                TrieIntro::art(cell(nm, g.adjacency_map)));
                      AdjacencyGraph { adjacency_map: adj }
                  }))
}

pub fn edge_list_of_adjacency
    <X: Hash + Clone + Debug + PartialEq + Eq>(adj_graph: &AdjacencyGraph<X>)
                                               -> Graph<X> {
    let edge_list = Graph::empty();
    trie_fold_seq(adj_graph.adjacency_map.clone(),
                  edge_list,
                  Rc::new(|(src, dsts): (X, Tree<X>), graph: Graph<X>| {
        let edge_tree = tree_fold_seq(dsts,
                                      Dir2::Left,
                                      graph.edge_tree,
                                      Rc::new(move |dst, g| {
            let edge_list = ns(name_of_str("list_of_tree"),
                               move || list_of_tree(g, Dir2::Left));
            let el = List::cons((src.clone(), dst), edge_list);
            ns(name_of_str("tree_of_list"),
               || tree_of_list::<_, _, Tree<_>, _>(Dir2::Left, el))
        }),
                                      Rc::new(|_, g| g),
                                      Rc::new(|nm: Name, _, g| {
            let edge_list = ns(name_of_str("list_of_tree"),
                               move || list_of_tree(g, Dir2::Left));
            let el = List::name_art(Some(nm), edge_list);
            ns(name_of_str("tree_of_list"),
               || tree_of_list::<_, _, Tree<_>, _>(Dir2::Left, el))
        }));
        Graph::<X> { edge_tree: edge_tree }
    }),
                  Rc::new(|g| g),
                  Rc::new(|nm: Name, g: Graph<X>| Graph::name(nm, g)))
}
<commit_msg>Small refactoring<commit_after>/// Incremental Graph Representations

use std::fmt::Debug;
use std::hash::Hash;
use std::rc::Rc;

use adapton::engine::{cell, ns, name_of_str, name_unit, Name};
use adapton::collections::{list_of_tree, tree_fold_seq, tree_of_list, Dir2, List, ListIntro,
                           MapIntro, MapElim, SetIntro, Tree, TreeIntro};
use adapton::collections::trie::{trie_fold_seq, Set, Trie, TrieIntro};

/// Representation of a graph as a list of edges, where edges are
/// a pair of node ids.
#[derive(Debug,PartialEq,Eq,Hash,Clone)]
pub struct Graph<Node> {
    edge_tree: Tree<(Node, Node)>,
}

/// Representation of a graph as finite map from node ids to
/// an outgoing adjacency list of node ids.
#[derive(Debug,PartialEq,Eq,Hash,Clone)]
pub struct AdjacencyGraph<Node>
    where Node: Debug + Clone + Hash + PartialEq + Eq + 'static
{
    adjacency_map: Trie<(Node, Tree<Node>)>,
}

trait NamedGraph<Node>: Debug + Clone + Hash + PartialEq + Eq {
    fn name(nm: Name, g: Self) -> Self;
}

/// Produce a graph.
pub trait GraphIntro<Node>: Debug + Clone + Hash + PartialEq + Eq {
    /// Yields an empty graph, i.e. no vertices or edges.
    fn empty() -> Self;
    /// Adds the edge `(src, dst)` named `nm` to the graph `graph`.
    fn add_edge(graph: Self, nm: Name, src: Node, dst: Node) -> Self;
}

/// Reduce a graph to a value.
pub trait GraphElim<Node>: Debug + Clone + Hash + PartialEq + Eq {
    /// Returns a set of the edges in the graph `graph`.
    fn edges(graph: &Self) -> Set<(Node, Node)>;
    /// Returns a set of the vertices of the graph `graph`.
    fn vertices(graph: &Self) -> Set<Node>;
    /// Returns a graph whose set of edges are the reversed edges of `graph`.
    fn reverse_edges(graph: &Self) -> Self;
}

impl<Node: Debug + Clone + Hash + PartialEq + Eq + 'static> NamedGraph<Node> for Graph<Node> {
    fn name(nm: Name, g: Graph<Node>) -> Graph<Node> {
        let edge_list = ns(name_of_str("list_of_tree"),
                           move || list_of_tree(g.edge_tree, Dir2::Left));
        let el = List::name_art(Some(nm), edge_list);
        let edge_tree = ns(name_of_str("tree_of_list"),
                           || tree_of_list::<_, _, Tree<_>, _>(Dir2::Left, el));
        Graph::<Node> { edge_tree: edge_tree }
    }
}

impl<Node: Debug + Clone + Hash + PartialEq + Eq + 'static> GraphIntro<Node> for Graph<Node> {
    fn empty() -> Graph<Node> {
        Graph::<Node> { edge_tree: Tree::nil() }
    }

    fn add_edge(graph: Graph<Node>, nm: Name, src: Node, dst: Node) -> Graph<Node> {
        let edge_list = ns(name_of_str("list_of_tree"),
                           move || list_of_tree(graph.edge_tree, Dir2::Left));
        let el = List::name_art(Some(nm), edge_list);
        let el = List::cons((src, dst), el);
        let edge_tree = ns(name_of_str("tree_of_list"),
                           || tree_of_list::<_, _, Tree<_>, _>(Dir2::Left, el));
        Graph::<Node> { edge_tree: edge_tree }
    }
}

impl<Node: Debug + Clone + Hash + PartialEq + Eq + 'static> GraphElim<Node> for Graph<Node> {
    fn edges(graph: &Graph<Node>) -> Set<(Node, Node)> {
        tree_fold_seq(graph.edge_tree.clone(),
                      Dir2::Left,
                      SetIntro::empty(),
                      Rc::new(|e, set| SetIntro::add(set, e)),
                      Rc::new(|_, set| set),
                      Rc::new(|nm: Name, _, set| {
                          TrieIntro::name(nm.clone(), TrieIntro::art(cell(nm, set)))
                      }))
    }

    fn vertices(graph: &Graph<Node>) -> Set<Node> {
        let edge_trie = ns(name_of_str("edge_trie_vertices"), || Self::edges(graph));
        trie_fold_seq(edge_trie,
                      SetIntro::empty(),
                      Rc::new(|((src,dst), ()), set| {
                          let add_src = SetIntro::add(set, src);
                          SetIntro::add(add_src, dst)
                      }),
                      Rc::new(move |set| set),
                      Rc::new(move |n: Name, set| {
                          Set::name(n.clone(), Set::art(cell(n, set)))
                      }))
    }

    fn reverse_edges(graph: &Graph<Node>) -> Graph<Node> {
        let es = Self::edges(graph);
        trie_fold_seq(es,
                      Self::empty(),
                      Rc::new(|((src, dst), ()), g| Self::add_edge(g, name_unit(), dst, src)),
                      Rc::new(|g| g),
                      Rc::new(|nm: Name, g| Self::name(nm, g)))
    }
}

impl<Node: Debug + Clone + Hash + PartialEq + Eq + 'static> NamedGraph<Node>
    for AdjacencyGraph<Node> {
    fn name(nm: Name, g: AdjacencyGraph<Node>) -> AdjacencyGraph<Node> {
        let adj = TrieIntro::name(nm.clone(),
                                  TrieIntro::art(cell(nm, g.adjacency_map)));
        AdjacencyGraph { adjacency_map: adj }
    }
}

impl<Node: Debug + Clone + Hash + PartialEq + Eq + 'static>
    GraphIntro<Node> for AdjacencyGraph<Node> {
        fn empty() -> AdjacencyGraph<Node> {
            AdjacencyGraph::<Node> {
                adjacency_map: MapIntro::empty(),
            }
        }

        fn add_edge(graph: AdjacencyGraph<Node>,
                    nm: Name, src: Node, dst: Node) -> AdjacencyGraph<Node> {
            let src_adj_list = MapElim::find(&graph.adjacency_map, &src);
            match src_adj_list {
                None => {
                    // let adj = List::name_art(Some(nm.clone()), List::nil());
                    let adj = List::cons(dst, List::nil());
                    let edge_tree = tree_of_list::<_, _, Tree<_>, _>(Dir2::Left, adj);
                    AdjacencyGraph::<Node> {
                        adjacency_map: TrieIntro::extend(nm, graph.adjacency_map,
                                                         (src, edge_tree)),
                    }
                }
                Some(adj_nodes) => {
                    let adj = list_of_tree(adj_nodes, Dir2::Left);
                    // let adj = List::name_art(Some(nm.clone()), adj);
                    let adj = List::cons(dst, adj);
                    let edge_tree = tree_of_list::<_, _, Tree<_>, _>(Dir2::Left, adj);
                    AdjacencyGraph::<Node> {
                        adjacency_map: TrieIntro::extend(nm, graph.adjacency_map,
                                                         (src, edge_tree)),
                    }
                }
            }
        }
    }

impl<Node: Debug + Copy + Clone + Hash + PartialEq + Eq + 'static>
    GraphElim<Node> for AdjacencyGraph<Node> {
        fn edges(graph: &AdjacencyGraph<Node>) -> Set<(Node, Node)> {
            trie_fold_seq(graph.adjacency_map.clone(), SetIntro::empty(),
                          Rc::new(|(src, dsts), set|
                                  tree_fold_seq(dsts, Dir2::Left, set,
                                                Rc::new(move |dst, set|
                                                        SetIntro::add(set, (src, dst))),
                                                Rc::new(|_, set| set),
                                                Rc::new(|nm: Name, _, set| {
                                                    TrieIntro::name(nm.clone(),
                                                                    TrieIntro::art(cell(nm,
                                                                                set)))
                                                }))),
                          Rc::new(|set| set),
                          Rc::new(|nm: Name, set|
                                  TrieIntro::name(nm.clone(),
                                                  TrieIntro::art(cell(nm, set)))))
        }

        fn vertices(graph: &AdjacencyGraph<Node>) -> Set<Node> {
            trie_fold_seq(graph.adjacency_map.clone(), SetIntro::empty(),
                          Rc::new(|(src, dsts), set| {
                              let src_set = SetIntro::add(set, src);
                              tree_fold_seq(dsts, Dir2::Left, src_set,
                                            Rc::new(|dst, set| SetIntro::add(set, dst)),
                                            Rc::new(|_, set| set),
                                            Rc::new(|nm: Name, _, set|
                                                    TrieIntro::name(nm.clone(),
                                                                    TrieIntro::art(cell(nm, set)))))
                          }),
                          Rc::new(|set| set),
                          Rc::new(|nm: Name, set| TrieIntro::name(nm.clone(),
                                                                  TrieIntro::art(cell(nm, set)))))
        }

        fn reverse_edges(graph: &AdjacencyGraph<Node>) -> AdjacencyGraph<Node> {
            let es = Self::edges(graph);
            trie_fold_seq(es,
                          Self::empty(),
                          Rc::new(|((src, dst), ()), g| Self::add_edge(g, name_unit(), dst, src)),
                          Rc::new(|g| g),
                          Rc::new(|nm: Name, g| Self::name(nm, g)))
        }
    }

pub fn adjacency_of_edge_list<X: Hash + Clone + Debug + PartialEq + Eq>(el_graph: &Graph<X>)
                                                                        -> AdjacencyGraph<X> {
    let adj_graph = AdjacencyGraph::empty();
    tree_fold_seq(el_graph.edge_tree.clone(),
                  Dir2::Left,
                  adj_graph,
                  Rc::new(|(src, dst), g| AdjacencyGraph::add_edge(g, name_unit(), src, dst)),
                  Rc::new(|_, set| set),
                  Rc::new(|nm: Name, _, g: AdjacencyGraph<_>| AdjacencyGraph::name(nm, g)))
}

pub fn edge_list_of_adjacency
    <X: Hash + Clone + Debug + PartialEq + Eq>(adj_graph: &AdjacencyGraph<X>)
                                               -> Graph<X> {
    let edge_list = Graph::empty();
    trie_fold_seq(adj_graph.adjacency_map.clone(),
                  edge_list,
                  Rc::new(|(src, dsts): (X, Tree<X>), graph: Graph<X>| {
        let edge_tree = tree_fold_seq(dsts,
                                      Dir2::Left,
                                      graph.edge_tree,
                                      Rc::new(move |dst, g| {
            let edge_list = ns(name_of_str("list_of_tree"),
                               move || list_of_tree(g, Dir2::Left));
            let el = List::cons((src.clone(), dst), edge_list);
            ns(name_of_str("tree_of_list"),
               || tree_of_list::<_, _, Tree<_>, _>(Dir2::Left, el))
        }),
                                      Rc::new(|_, g| g),
                                      Rc::new(|nm: Name, _, g| {
            let edge_list = ns(name_of_str("list_of_tree"),
                               move || list_of_tree(g, Dir2::Left));
            let el = List::name_art(Some(nm), edge_list);
            ns(name_of_str("tree_of_list"),
               || tree_of_list::<_, _, Tree<_>, _>(Dir2::Left, el))
        }));
        Graph::<X> { edge_tree: edge_tree }
    }),
                  Rc::new(|g| g),
                  Rc::new(|nm: Name, g: Graph<X>| Graph::name(nm, g)))
}
<|endoftext|>