<commit_before>use alloc::arc::Arc;
use alloc::boxed::Box;
use collections::BTreeMap;
use core::sync::atomic::{AtomicUsize, Ordering};
use spin::RwLock;

use context;
use syscall::error::*;
use syscall::scheme::Scheme;
use scheme::{self, SchemeNamespace, SchemeId};
use scheme::user::{UserInner, UserScheme};

pub struct RootScheme {
    scheme_ns: SchemeNamespace,
    scheme_id: SchemeId,
    next_id: AtomicUsize,
    handles: RwLock<BTreeMap<usize, Arc<UserInner>>>,
    ls_handles: RwLock<BTreeMap<usize, AtomicUsize>>,
}

impl RootScheme {
    pub fn new(scheme_ns: SchemeNamespace, scheme_id: SchemeId) -> RootScheme {
        RootScheme {
            scheme_ns: scheme_ns,
            scheme_id: scheme_id,
            next_id: AtomicUsize::new(0),
            handles: RwLock::new(BTreeMap::new()),
            ls_handles: RwLock::new(BTreeMap::new()),
        }
    }
}

impl Scheme for RootScheme {
    fn open(&self, path: &[u8], flags: usize, uid: u32, _gid: u32) -> Result<usize> {
        use syscall::flag::*;

        if uid == 0 {
            if flags & O_DIRECTORY == O_DIRECTORY {
                if flags & O_ACCMODE != O_RDONLY {
                    return Err(Error::new(EACCES));
                }
                let id = self.next_id.fetch_add(1, Ordering::SeqCst);
                self.ls_handles.write().insert(id, AtomicUsize::new(0));
                return Ok(id);
            }

            let context = {
                let contexts = context::contexts();
                let context = contexts.current().ok_or(Error::new(ESRCH))?;
                Arc::downgrade(&context)
            };

            let id = self.next_id.fetch_add(1, Ordering::SeqCst);

            let inner = {
                let path_box = path.to_vec().into_boxed_slice();
                let mut schemes = scheme::schemes_mut();
                let inner = Arc::new(UserInner::new(self.scheme_id, id, path_box.clone(), flags, context));
                schemes.insert(self.scheme_ns, path_box, |scheme_id| {
                    inner.scheme_id.store(scheme_id, Ordering::SeqCst);
                    Arc::new(Box::new(UserScheme::new(Arc::downgrade(&inner))))
                })?;
                inner
            };

            self.handles.write().insert(id, inner);

            Ok(id)
        } else {
            Err(Error::new(EACCES))
        }
    }

    fn dup(&self, file: usize, buf: &[u8]) -> Result<usize> {
        if ! buf.is_empty() {
            return Err(Error::new(EINVAL));
        }

        let mut handles = self.handles.write();
        let inner = {
            let inner = handles.get(&file).ok_or(Error::new(EBADF))?;
            inner.clone()
        };

        let id = self.next_id.fetch_add(1, Ordering::SeqCst);
        handles.insert(id, inner);

        Ok(id)
    }

    fn read(&self, file: usize, buf: &mut [u8]) -> Result<usize> {
        let inner = {
            let handles = self.handles.read();
            handles.get(&file).map(Clone::clone)
        };

        if let Some(inner) = inner {
            inner.read(buf)
        } else {
            let scheme_ns = {
                let contexts = context::contexts();
                let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
                let context = context_lock.read();
                context.ens
            };

            let schemes = scheme::schemes();
            let mut schemes_iter = schemes.iter_name(scheme_ns);

            let num = {
                let handles = self.ls_handles.read();
                let inner = handles.get(&file).ok_or(Error::new(EBADF))?;
                inner.load(Ordering::SeqCst)
            };

            if let Some(scheme) = schemes_iter.nth(num) {
                let mut i = 0;
                while i < buf.len() && i < scheme.0.len() {
                    buf[i] = scheme.0[i];
                    i += 1;
                }

                {
                    let handles = self.ls_handles.read();
                    let inner = handles.get(&file).ok_or(Error::new(EBADF))?;
                    inner.fetch_add(1, Ordering::SeqCst)
                };

                Ok(i)
            } else {
                Ok(0)
            }
        }
    }

    fn write(&self, file: usize, buf: &[u8]) -> Result<usize> {
        let inner = {
            let handles = self.handles.read();
            let inner = handles.get(&file).ok_or(Error::new(EBADF))?;
            inner.clone()
        };

        inner.write(buf)
    }

    fn fevent(&self, file: usize, flags: usize) -> Result<usize> {
        let inner = {
            let handles = self.handles.read();
            let inner = handles.get(&file).ok_or(Error::new(EBADF))?;
            inner.clone()
        };

        inner.fevent(flags)
    }

    fn fpath(&self, file: usize, buf: &mut [u8]) -> Result<usize> {
        let inner = {
            let handles = self.handles.read();
            let inner = handles.get(&file).ok_or(Error::new(EBADF))?;
            inner.clone()
        };

        let mut i = 0;
        let scheme_path = b":";
        while i < buf.len() && i < scheme_path.len() {
            buf[i] = scheme_path[i];
            i += 1;
        }

        let mut j = 0;
        while i < buf.len() && j < inner.name.len() {
            buf[i] = inner.name[j];
            i += 1;
            j += 1;
        }

        Ok(i)
    }

    fn fsync(&self, file: usize) -> Result<usize> {
        let inner = {
            let handles = self.handles.read();
            let inner = handles.get(&file).ok_or(Error::new(EBADF))?;
            inner.clone()
        };

        inner.fsync()
    }

    fn close(&self, file: usize) -> Result<usize> {
        if self.handles.write().remove(&file).is_none() {
            self.ls_handles.write().remove(&file).ok_or(Error::new(EBADF))?;
        }
        Ok(0)
    }
}
<commit_msg>Allow listing of root scheme<commit_after>use alloc::arc::Arc;
use alloc::boxed::Box;
use collections::{BTreeMap, Vec};
use core::str;
use core::sync::atomic::{AtomicUsize, Ordering};
use spin::{Mutex, RwLock};

use context;
use syscall::data::Stat;
use syscall::error::*;
use syscall::flag::{O_CREAT, MODE_FILE, MODE_DIR};
use syscall::scheme::Scheme;
use scheme::{self, SchemeNamespace, SchemeId};
use scheme::user::{UserInner, UserScheme};

struct FolderInner {
    data: Box<[u8]>,
    pos: Mutex<usize>
}

impl FolderInner {
    fn read(&self, buf: &mut [u8]) -> Result<usize> {
        let mut i = 0;
        let mut pos = self.pos.lock();

        while i < buf.len() && *pos < self.data.len() {
            buf[i] = self.data[*pos];
            i += 1;
            *pos += 1;
        }

        Ok(i)
    }
}

#[derive(Clone)]
enum Handle {
    Scheme(Arc<UserInner>),
    File(Arc<Box<[u8]>>),
    Folder(Arc<FolderInner>)
}

pub struct RootScheme {
    scheme_ns: SchemeNamespace,
    scheme_id: SchemeId,
    next_id: AtomicUsize,
    handles: RwLock<BTreeMap<usize, Handle>>,
}

impl RootScheme {
    pub fn new(scheme_ns: SchemeNamespace, scheme_id: SchemeId) -> RootScheme {
        RootScheme {
            scheme_ns: scheme_ns,
            scheme_id: scheme_id,
            next_id: AtomicUsize::new(0),
            handles: RwLock::new(BTreeMap::new()),
        }
    }
}

impl Scheme for RootScheme {
    fn open(&self, path: &[u8], flags: usize, uid: u32, _gid: u32) -> Result<usize> {
        let path_utf8 = str::from_utf8(path).or(Err(Error::new(ENOENT)))?;
        let path_trimmed = path_utf8.trim_matches('/');

        //TODO: Make this follow standards for flags and errors
        if flags & O_CREAT == O_CREAT {
            if uid == 0 {
                let context = {
                    let contexts = context::contexts();
                    let context = contexts.current().ok_or(Error::new(ESRCH))?;
                    Arc::downgrade(&context)
                };

                let id = self.next_id.fetch_add(1, Ordering::SeqCst);

                let inner = {
                    let path_box = path_trimmed.as_bytes().to_vec().into_boxed_slice();
                    let mut schemes = scheme::schemes_mut();
                    let inner = Arc::new(UserInner::new(self.scheme_id, id, path_box.clone(), flags, context));
                    schemes.insert(self.scheme_ns, path_box, |scheme_id| {
                        inner.scheme_id.store(scheme_id, Ordering::SeqCst);
                        Arc::new(Box::new(UserScheme::new(Arc::downgrade(&inner))))
                    })?;
                    inner
                };

                self.handles.write().insert(id, Handle::Scheme(inner));

                Ok(id)
            } else {
                Err(Error::new(EACCES))
            }
        } else if path_trimmed.is_empty() {
            let scheme_ns = {
                let contexts = context::contexts();
                let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
                let context = context_lock.read();
                context.ens
            };

            let mut data = Vec::new();
            {
                let schemes = scheme::schemes();
                for (name, _scheme_id) in schemes.iter_name(scheme_ns) {
                    data.extend_from_slice(name);
                    data.push(b'\n');
                }
            }

            let inner = Arc::new(FolderInner {
                data: data.into_boxed_slice(),
                pos: Mutex::new(0)
            });

            let id = self.next_id.fetch_add(1, Ordering::SeqCst);
            self.handles.write().insert(id, Handle::Folder(inner));
            Ok(id)
        } else {
            let inner = Arc::new(
                path_trimmed.as_bytes().to_vec().into_boxed_slice()
            );

            let id = self.next_id.fetch_add(1, Ordering::SeqCst);
            self.handles.write().insert(id, Handle::File(inner));
            Ok(id)
        }
    }

    fn dup(&self, file: usize, buf: &[u8]) -> Result<usize> {
        if ! buf.is_empty() {
            return Err(Error::new(EINVAL));
        }

        let mut handles = self.handles.write();
        let inner = {
            let inner = handles.get(&file).ok_or(Error::new(EBADF))?;
            inner.clone()
        };

        let id = self.next_id.fetch_add(1, Ordering::SeqCst);
        handles.insert(id, inner);

        Ok(id)
    }

    fn read(&self, file: usize, buf: &mut [u8]) -> Result<usize> {
        let handle = {
            let handles = self.handles.read();
            let handle = handles.get(&file).ok_or(Error::new(EBADF))?;
            handle.clone()
        };

        match handle {
            Handle::Scheme(inner) => {
                inner.read(buf)
            },
            Handle::File(_) => {
                Err(Error::new(EBADF))
            },
            Handle::Folder(inner) => {
                inner.read(buf)
            }
        }
    }

    fn write(&self, file: usize, buf: &[u8]) -> Result<usize> {
        let handle = {
            let handles = self.handles.read();
            let handle = handles.get(&file).ok_or(Error::new(EBADF))?;
            handle.clone()
        };

        match handle {
            Handle::Scheme(inner) => {
                inner.write(buf)
            },
            Handle::File(_) => {
                Err(Error::new(EBADF))
            },
            Handle::Folder(_) => {
                Err(Error::new(EBADF))
            }
        }
    }

    fn fevent(&self, file: usize, flags: usize) -> Result<usize> {
        let handle = {
            let handles = self.handles.read();
            let handle = handles.get(&file).ok_or(Error::new(EBADF))?;
            handle.clone()
        };

        match handle {
            Handle::Scheme(inner) => {
                inner.fevent(flags)
            },
            Handle::File(_) => {
                Err(Error::new(EBADF))
            },
            Handle::Folder(_) => {
                Err(Error::new(EBADF))
            }
        }
    }

    fn fpath(&self, file: usize, buf: &mut [u8]) -> Result<usize> {
        let handle = {
            let handles = self.handles.read();
            let handle = handles.get(&file).ok_or(Error::new(EBADF))?;
            handle.clone()
        };

        let mut i = 0;
        let scheme_path = b":";
        while i < buf.len() && i < scheme_path.len() {
            buf[i] = scheme_path[i];
            i += 1;
        }

        match handle {
            Handle::Scheme(inner) => {
                let mut j = 0;
                while i < buf.len() && j < inner.name.len() {
                    buf[i] = inner.name[j];
                    i += 1;
                    j += 1;
                }
            },
            Handle::File(inner) => {
                let mut j = 0;
                while i < buf.len() && j < inner.len() {
                    buf[i] = inner[j];
                    i += 1;
                    j += 1;
                }
            },
            Handle::Folder(_) => ()
        }

        Ok(i)
    }

    fn fstat(&self, file: usize, stat: &mut Stat) -> Result<usize> {
        let handle = {
            let handles = self.handles.read();
            let handle = handles.get(&file).ok_or(Error::new(EBADF))?;
            handle.clone()
        };

        match handle {
            Handle::Scheme(_) => {
                stat.st_mode = MODE_FILE;
                stat.st_uid = 0;
                stat.st_gid = 0;
                stat.st_size = 0;
            },
            Handle::File(_) => {
                stat.st_mode = MODE_FILE;
                stat.st_uid = 0;
                stat.st_gid = 0;
                stat.st_size = 0;
            },
            Handle::Folder(inner) => {
                stat.st_mode = MODE_DIR;
                stat.st_uid = 0;
                stat.st_gid = 0;
                stat.st_size = inner.data.len() as u64;
            }
        }

        Ok(0)
    }

    fn fsync(&self, file: usize) -> Result<usize> {
        let handle = {
            let handles = self.handles.read();
            let handle = handles.get(&file).ok_or(Error::new(EBADF))?;
            handle.clone()
        };

        match handle {
            Handle::Scheme(inner) => {
                inner.fsync()
            },
            Handle::File(_) => {
                Err(Error::new(EBADF))
            },
            Handle::Folder(_) => {
                Err(Error::new(EBADF))
            }
        }
    }

    fn close(&self, file: usize) -> Result<usize> {
        self.handles.write().remove(&file);
        Ok(0)
    }
}
<|endoftext|>


<commit_before>//! TODO Fill in

use cairo::{Format, ImageSurface};
use glib::translate::ToGlibPtr;
use rustwlc::Geometry;
use std::fmt::{self, Display, Formatter};
use std::default::Default;
use std::rc::Rc;
use rlua::{self, Table, Lua, UserData, ToLua, Value, LightUserData};
use super::object::{Object, Objectable};
use super::class::{self, Class};
use super::property::Property;

#[derive(Clone, Debug)]
pub struct DrawableState {
    // TODO Pixmap?
    pub surface: Option<ImageSurface>,
    geo: Geometry,
    refreshed: bool,
    // TODO Refresh callback
}

pub struct Drawable<'lua>(Table<'lua>);

impl_objectable!(Drawable, DrawableState);

impl Default for DrawableState {
    fn default() -> Self {
        DrawableState {
            surface: None,
            geo: Geometry::zero(),
            refreshed: false
        }
    }
}

impl <'lua> Drawable<'lua> {
    pub fn new(lua: &Lua) -> rlua::Result<Object> {
        let class = class::class_setup(lua, "drawable")?;
        Ok(Drawable::allocate(lua, class)?.build())
    }

    pub fn get_geometry(&self) -> rlua::Result<Geometry> {
        let drawable = self.state()?;
        Ok(drawable.geo)
    }

    pub fn get_surface(&self) -> rlua::Result<Value<'lua>> {
        let drawable = self.state()?;
        Ok(match drawable.surface {
            None => Value::Nil,
            Some(ref surface) => {
                let stash = surface.to_glib_none();
                let ptr = stash.0 as _;
                // So that it lives _forever_ heheheh.
                ::std::mem::forget(stash);
                Value::LightUserData(LightUserData(ptr))
            }
        })
    }

    /// Sets the geometry, and allocates a new surface.
    pub fn set_geometry(&mut self, geometry: Geometry) -> rlua::Result<()> {
        use rlua::Error::RuntimeError;
        let mut drawable = self.state()?;
        let size_changed = drawable.geo != geometry;
        drawable.geo = geometry;
        if size_changed {
            drawable.surface = None;
            let size = geometry.size;
            if size.w > 0 && size.h > 0 {
                drawable.surface = Some(ImageSurface::create(Format::ARgb32,
                                                        size.w as i32,
                                                        size.h as i32)
                    .map_err(|err| RuntimeError(format!("Could not allocate {:?}", err)))?);
                // TODO emity property::surface
            }
        }
        self.set_state(drawable)
    }
}

impl Display for DrawableState {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "Drawable: {:p}", self)
    }
}

impl <'lua> ToLua<'lua> for Drawable<'lua> {
    fn to_lua(self, lua: &'lua Lua) -> rlua::Result<Value<'lua>> {
        self.0.to_lua(lua)
    }
}

impl UserData for DrawableState {}

pub fn init(lua: &Lua) -> rlua::Result<Class> {
    Class::builder(lua, Some(Rc::new(Drawable::new)), None, None)?
        .method("geometry".into(), lua.create_function(geometry))?
        .property(Property::new("surface".into(),
                                None,
                                Some(lua.create_function(get_surface)),
                                None))?
        .save_class("drawable")?
        .build()
}

fn get_surface<'lua>(_: &'lua Lua, table: Table<'lua>) -> rlua::Result<Value<'lua>> {
    let drawable = Drawable::cast(table.clone().into())?;
    drawable.get_surface()
}

fn geometry<'lua>(lua: &'lua Lua, table: Table<'lua>) -> rlua::Result<Table<'lua>> {
    use rustwlc::{Point, Size};
    let drawable = Drawable::cast(table.into())?;
    let geometry = drawable.get_geometry()?;
    let Point { x, y } = geometry.origin;
    let Size { w, h } = geometry.size;
    let table = lua.create_table();
    table.set("x", x)?;
    table.set("y", y)?;
    table.set("width", w)?;
    table.set("height", h)?;
    Ok(table)
}
<commit_msg>Fixed comments and default refresh value<commit_after>//! TODO Fill in

use cairo::{Format, ImageSurface};
use glib::translate::ToGlibPtr;
use rustwlc::Geometry;
use std::fmt::{self, Display, Formatter};
use std::default::Default;
use std::rc::Rc;
use rlua::{self, Table, Lua, UserData, ToLua, Value, LightUserData};
use super::object::{Object, Objectable};
use super::class::{self, Class};
use super::property::Property;

#[derive(Clone, Debug)]
pub struct DrawableState {
    pub surface: Option<ImageSurface>,
    geo: Geometry,
    // TODO Use this to determine whether we draw this or not
    refreshed: bool,
}

pub struct Drawable<'lua>(Table<'lua>);

impl_objectable!(Drawable, DrawableState);

impl Default for DrawableState {
    fn default() -> Self {
        DrawableState {
            surface: None,
            geo: Geometry::zero(),
            refreshed: true
        }
    }
}

impl <'lua> Drawable<'lua> {
    pub fn new(lua: &Lua) -> rlua::Result<Object> {
        let class = class::class_setup(lua, "drawable")?;
        Ok(Drawable::allocate(lua, class)?.build())
    }

    pub fn get_geometry(&self) -> rlua::Result<Geometry> {
        let drawable = self.state()?;
        Ok(drawable.geo)
    }

    pub fn get_surface(&self) -> rlua::Result<Value<'lua>> {
        let drawable = self.state()?;
        Ok(match drawable.surface {
            None => Value::Nil,
            Some(ref surface) => {
                let stash = surface.to_glib_none();
                let ptr = stash.0 as _;
                // So that it lives _forever_ heheheh.
                ::std::mem::forget(stash);
                Value::LightUserData(LightUserData(ptr))
            }
        })
    }

    /// Sets the geometry, and allocates a new surface.
    pub fn set_geometry(&mut self, geometry: Geometry) -> rlua::Result<()> {
        use rlua::Error::RuntimeError;
        let mut drawable = self.state()?;
        let size_changed = drawable.geo != geometry;
        drawable.geo = geometry;
        if size_changed {
            drawable.surface = None;
            let size = geometry.size;
            if size.w > 0 && size.h > 0 {
                drawable.surface = Some(ImageSurface::create(Format::ARgb32,
                                                        size.w as i32,
                                                        size.h as i32)
                    .map_err(|err| RuntimeError(format!("Could not allocate {:?}", err)))?);
                // TODO emity property::surface
            }
        }
        self.set_state(drawable)
    }
}

impl Display for DrawableState {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "Drawable: {:p}", self)
    }
}

impl <'lua> ToLua<'lua> for Drawable<'lua> {
    fn to_lua(self, lua: &'lua Lua) -> rlua::Result<Value<'lua>> {
        self.0.to_lua(lua)
    }
}

impl UserData for DrawableState {}

pub fn init(lua: &Lua) -> rlua::Result<Class> {
    Class::builder(lua, Some(Rc::new(Drawable::new)), None, None)?
        .method("geometry".into(), lua.create_function(geometry))?
        .property(Property::new("surface".into(),
                                None,
                                Some(lua.create_function(get_surface)),
                                None))?
        .save_class("drawable")?
        .build()
}

fn get_surface<'lua>(_: &'lua Lua, table: Table<'lua>) -> rlua::Result<Value<'lua>> {
    let drawable = Drawable::cast(table.clone().into())?;
    drawable.get_surface()
}

fn geometry<'lua>(lua: &'lua Lua, table: Table<'lua>) -> rlua::Result<Table<'lua>> {
    use rustwlc::{Point, Size};
    let drawable = Drawable::cast(table.into())?;
    let geometry = drawable.get_geometry()?;
    let Point { x, y } = geometry.origin;
    let Size { w, h } = geometry.size;
    let table = lua.create_table();
    table.set("x", x)?;
    table.set("y", y)?;
    table.set("width", w)?;
    table.set("height", h)?;
    Ok(table)
}
<|endoftext|>


<commit_before>//! The `ztext` module contains encoding functions to encode text in z-ascii characters.
//!

use super::zbytes::Bytes;

pub static ALPHABET: [char; 78] = [
    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
    'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',

    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
    'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',

    ' ', '\n', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.',
    ',', '!', '?', '_', '#', '\'','"', '/', '\\','-', ':', '(', ')'];

/// encodes an string to z-characters
/// and returns the length of the used bytes
/// TODO: Only works with lower case letters
///
/// # Examples
///
/// ```ignore
/// let data = Bytes{bytes: Vec::new()};
/// let byteLength = data.encode("hello");
/// ```
pub fn encode(data: &mut Bytes, content: &str, unicode_table: &Vec<u16>) -> u16 {
    let zchars: Vec<u8> = string_to_zchar(content, unicode_table);

    let mut two_bytes: u16 = 0;
    let len = zchars.len();
    for i in 0..len {
        let zasci_id =zchars[i];

        two_bytes |= shift(zasci_id as u16, i);

        if i % 3 == 2 {
            data.write_u16(two_bytes, pos_to_index(i));
            two_bytes = 0;
        }

        // end of string
        if i == len -1 {
            if i % 3 != 2 {
                for j in (i % 3) + 1..3 {
                    two_bytes |= shift(0x05 as u16, j);
                }

                data.write_u16(two_bytes, pos_to_index(i));
            }

            // end bit is written to the first bit of the next to last byte
            let end_index: usize = data.bytes.len() - 2 as usize;
            let mut end_byte: u8 = data.bytes[end_index];

            end_byte |= 0x80;
            data.write_byte(end_byte, end_index as usize);
        }
    }

    data.bytes.len() as u16
}

/// reads the content and converts it to a zasci vector
fn string_to_zchar(content: &str, unicode_table: &Vec<u16>) -> Vec<u8> {
    //let string_bytes = content.to_string().into_bytes();
    let mut zchars: Vec<u8> = Vec::new();

    for character in content.chars() {

        let mut byte: u8 = character as u8;
        let alpha_index = pos_in_alpha(byte as u8);
        if character as u16 <= 126 && alpha_index != -1 {

            if byte == 0x0A {
                // newline
                zchars.push(0x05);
                zchars.push(7);
            } else if byte == 0x20 {
                // space
                zchars.push(0x00);
            } else {
                if alpha_index > 51 {
                    // in A2
                    zchars.push(0x05);
                    zchars.push(alpha_index as u8 % 26 + 6);
                } else if alpha_index < 26 {
                    // in A0
                    zchars.push(alpha_index as u8 % 26 + 6);
                } else {
                    // in A1
                    zchars.push(0x04);
                    zchars.push(alpha_index as u8 % 26 + 6);
                }
            }
        } else {
            // not in alphabet or unicode

            // to change alphabet
            zchars.push(0x05);

            // for special char (10 bit z-ascii)
            zchars.push(0x06);

            //let mut byte: u8;
            if character as u16 <= 126 {
                // not in alphabet, but still ascii
                byte = character as u8;
            } else {
                // unicode
                let unicode_index = pos_in_unicode(character as u16, unicode_table);
                byte = unicode_index as u8 + 155;
            }

            zchars.push(byte >> 5);
            zchars.push(byte & 0x1f);
        }
    }
    zchars
}

/// shifts the z-char in a 2 bytes-array to the right position
/// shift_length has 3 possibilities: 10, 5, 0
/// an z-char use 5 bytes
/// look into two bytes:
/// ____byte one____ ____byte two____
/// 1 2 3 4 5 6  7 8 1 2 3  4 5 6 7 8
///   ^          ^          1
///   1 2 3 4 5  1 2 3 4 5  1 2 3 4 5
///   1. zchar   2. zchar   3. zchar
///   10         5          0
fn shift(zchar: u16, position: usize) -> u16 {
    let shift_length = 10 - (position % 3) as u16 * 5;
    zchar << shift_length
}

/// Returns the location of the character of the specified index in the zcode character array
///
/// # Examples
///
fn pos_in_alpha(letter: u8) -> i8 {
    for i in 0..ALPHABET.len() {
        if ALPHABET[i] as u8 == letter {
            return i as i8
        }
    }

    return -1
}

/// returns the position in the unicode-table
pub fn pos_in_unicode(letter: u16, unicode_table: &Vec<u16>) -> i8 {
    for (i, character) in unicode_table.iter().enumerate() {
        if *character == letter {
            return i as i8
        }
    }

    return -1
}

/// position in the vector from the position of an character in the string
/// every 3 zchars encode() writes 2 bytes
/// for example "helloworld"
/// "h": nothing
/// "e": nothing
/// "l": write 2 bytes to position 0   (2 * 2/3))
/// "l": nothing
/// "o": nothing
/// "w": write 2 bytes to position 2   (2 * 5/3))
/// ...
///
/// # Examples
///
fn pos_to_index(position: usize) -> usize {
    2 * (position / 3)
}

#[test]
fn test_pos_in_alpha() {
    assert_eq!(pos_in_alpha('a' as u8), 0);
    assert_eq!(pos_in_alpha('b' as u8), 1);
    assert_eq!(pos_in_alpha('c' as u8), 2);
    assert_eq!(pos_in_alpha('A' as u8), 26);
    assert_eq!(pos_in_alpha('B' as u8), 27);
    assert_eq!(pos_in_alpha('C' as u8), 28);
}

#[test]
fn test_pos_to_index() {
    assert_eq!(pos_to_index(5), 2);
}

#[test]
fn test_shift() {
    assert_eq!(shift(6,2), 6);
    assert_eq!(shift(26,5), 26);
}

#[test]
fn test_string_to_zchar() {
    let mut vec: Vec<u16> = Vec::new();
    assert_eq!(string_to_zchar("i am a string, please test me, no unicode",&vec), vec![14, 0, 6, 18, 0, 6, 0, 24, 25, 23, 14, 19, 12, 5, 19, 0, 21, 17, 10, 6, 24, 10, 0, 25, 10, 24, 25, 0, 18, 10, 5, 19, 0, 19, 20, 0, 26, 19, 14, 8, 20, 9, 10]);
    vec.push('€' as u16);
    assert_eq!(string_to_zchar("nasty char: €",&vec), vec![19, 6, 24, 25, 30, 0, 8, 13, 6, 23, 5, 29, 0, 5, 6, 4, 27]);
}<commit_msg>add a little doku to ztext<commit_after>//! The `ztext` module contains encoding functions to encode text in z-ascii characters.
//!

use super::zbytes::Bytes;

/// the ascii-alphabet with the 3 parts:
/// lower-case, upper-case and numbers / special characters
pub static ALPHABET: [char; 78] = [
    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
    'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',

    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
    'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',

    ' ', '\n', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.',
    ',', '!', '?', '_', '#', '\'','"', '/', '\\','-', ':', '(', ')'];

/// encodes an string to z-characters
/// and returns the length of the used bytes
/// TODO: Only works with lower case letters
///
/// # Examples
///
/// ```ignore
/// let data = Bytes{bytes: Vec::new()};
/// let byteLength = data.encode("hello");
/// ```
pub fn encode(data: &mut Bytes, content: &str, unicode_table: &Vec<u16>) -> u16 {
    let zchars: Vec<u8> = string_to_zchar(content, unicode_table);

    let mut two_bytes: u16 = 0;
    let len = zchars.len();
    for i in 0..len {
        let zasci_id =zchars[i];

        two_bytes |= shift(zasci_id as u16, i);

        if i % 3 == 2 {
            data.write_u16(two_bytes, pos_to_index(i));
            two_bytes = 0;
        }

        // end of string
        if i == len -1 {
            if i % 3 != 2 {
                for j in (i % 3) + 1..3 {
                    two_bytes |= shift(0x05 as u16, j);
                }

                data.write_u16(two_bytes, pos_to_index(i));
            }

            // end bit is written to the first bit of the next to last byte
            let end_index: usize = data.bytes.len() - 2 as usize;
            let mut end_byte: u8 = data.bytes[end_index];

            end_byte |= 0x80;
            data.write_byte(end_byte, end_index as usize);
        }
    }

    data.bytes.len() as u16
}

/// reads the content and converts it to a zasci vector
fn string_to_zchar(content: &str, unicode_table: &Vec<u16>) -> Vec<u8> {
    //let string_bytes = content.to_string().into_bytes();
    let mut zchars: Vec<u8> = Vec::new();

    for character in content.chars() {

        let mut byte: u8 = character as u8;
        let alpha_index = pos_in_alpha(byte as u8);
        if character as u16 <= 126 && alpha_index != -1 {

            if byte == 0x0A {
                // newline
                zchars.push(0x05);
                zchars.push(7);
            } else if byte == 0x20 {
                // space
                zchars.push(0x00);
            } else {
                if alpha_index > 51 {
                    // in A2
                    zchars.push(0x05);
                    zchars.push(alpha_index as u8 % 26 + 6);
                } else if alpha_index < 26 {
                    // in A0
                    zchars.push(alpha_index as u8 % 26 + 6);
                } else {
                    // in A1
                    zchars.push(0x04);
                    zchars.push(alpha_index as u8 % 26 + 6);
                }
            }
        } else {
            // not in alphabet or unicode

            // to change alphabet
            zchars.push(0x05);

            // for special char (10 bit z-ascii)
            zchars.push(0x06);

            //let mut byte: u8;
            if character as u16 <= 126 {
                // not in alphabet, but still ascii
                byte = character as u8;
            } else {
                // unicode
                let unicode_index = pos_in_unicode(character as u16, unicode_table);
                byte = unicode_index as u8 + 155;
            }

            zchars.push(byte >> 5);
            zchars.push(byte & 0x1f);
        }
    }
    zchars
}

/// shifts the z-char in a 2 bytes-array to the right position
/// shift_length has 3 possibilities: 10, 5, 0
/// an z-char use 5 bytes
/// look into two bytes:
/// ____byte one____ ____byte two____
/// 1 2 3 4 5 6  7 8 1 2 3  4 5 6 7 8
///   ^          ^          1
///   1 2 3 4 5  1 2 3 4 5  1 2 3 4 5
///   1. zchar   2. zchar   3. zchar
///   10         5          0
fn shift(zchar: u16, position: usize) -> u16 {
    let shift_length = 10 - (position % 3) as u16 * 5;
    zchar << shift_length
}

/// Returns the location of the character of the specified index in the zcode character array
///
/// # Examples
///
fn pos_in_alpha(letter: u8) -> i8 {
    for i in 0..ALPHABET.len() {
        if ALPHABET[i] as u8 == letter {
            return i as i8
        }
    }

    return -1
}

/// returns the position in the unicode-table
pub fn pos_in_unicode(letter: u16, unicode_table: &Vec<u16>) -> i8 {
    for (i, character) in unicode_table.iter().enumerate() {
        if *character == letter {
            return i as i8
        }
    }

    return -1
}

/// position in the vector from the position of an character in the string
/// every 3 zchars encode() writes 2 bytes
/// for example "helloworld"
/// "h": nothing
/// "e": nothing
/// "l": write 2 bytes to position 0   (2 * 2/3))
/// "l": nothing
/// "o": nothing
/// "w": write 2 bytes to position 2   (2 * 5/3))
/// ...
///
/// # Examples
///
fn pos_to_index(position: usize) -> usize {
    2 * (position / 3)
}

#[test]
fn test_pos_in_alpha() {
    assert_eq!(pos_in_alpha('a' as u8), 0);
    assert_eq!(pos_in_alpha('b' as u8), 1);
    assert_eq!(pos_in_alpha('c' as u8), 2);
    assert_eq!(pos_in_alpha('A' as u8), 26);
    assert_eq!(pos_in_alpha('B' as u8), 27);
    assert_eq!(pos_in_alpha('C' as u8), 28);
}

#[test]
fn test_pos_to_index() {
    assert_eq!(pos_to_index(5), 2);
}

#[test]
fn test_shift() {
    assert_eq!(shift(6,2), 6);
    assert_eq!(shift(26,5), 26);
}

#[test]
fn test_string_to_zchar() {
    let mut vec: Vec<u16> = Vec::new();
    assert_eq!(string_to_zchar("i am a string, please test me, no unicode",&vec), vec![14, 0, 6, 18, 0, 6, 0, 24, 25, 23, 14, 19, 12, 5, 19, 0, 21, 17, 10, 6, 24, 10, 0, 25, 10, 24, 25, 0, 18, 10, 5, 19, 0, 19, 20, 0, 26, 19, 14, 8, 20, 9, 10]);
    vec.push('€' as u16);
    assert_eq!(string_to_zchar("nasty char: €",&vec), vec![19, 6, 24, 25, 30, 0, 8, 13, 6, 23, 5, 29, 0, 5, 6, 4, 27]);
}<|endoftext|>


<commit_before>// Copyright 2016 coroutine-rs Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://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.

#![cfg_attr(feature = "nightly", feature(panic_propagate, recover, std_panic))]

extern crate context;

#[cfg(not(feature = "nightly"))]
mod imp {
    pub fn run() {
        println!("Rust Nightly is required for this example!");
    }
}

#[cfg(feature = "nightly")]
mod imp {
    use std::panic;

    use context::{Context, Transfer};
    use context::stack::ProtectedFixedSizeStack;

    // This struct is used to demonstrate that the stack is actually being unwound.
    struct Dropper;

    impl Drop for Dropper {
        fn drop(&mut self) {
            println!("Dropping a Dropper!");
        }
    }

    // This struct is used to carry the actual context
    struct Carrier {
        context: Option<Context>,
    }

    fn take_some_stack_from_transfer(t: &Transfer) -> Option<ProtectedFixedSizeStack> {
        let stack_ref = unsafe { &mut *(t.1 as *mut Option<ProtectedFixedSizeStack>) };
        stack_ref.take()
    }

    fn stack_ref_from_some_stack(some_stack: &mut Option<ProtectedFixedSizeStack>) -> usize {
        some_stack as *mut Option<ProtectedFixedSizeStack> as usize
    }

    // This method is used to force unwind a foreign context function.
    extern "C" fn unwind_stack(t: Transfer) -> Transfer {
        println!("Unwinding stack by panicking!");

        // We have to store the `t.context` in the `Carrier` object. because this function won't
        // be able to return normally.
        let Transfer(ctx, data) = t;
        let carrier = unsafe { &mut *(data as *mut Carrier) };

        carrier.context = Some(ctx);

        // Unwind the current stack by panicking.
        // We use std::panic::propagate() here however because panic!() will call the panic handler
        // which aborts the process if more than one panic is triggered on a thread.
        // This is problematic for coroutines though (the most popular use case for this crate),
        // because by their very definition multiple seperate stacks per thread are used.
        //
        // Thus the following problem can occur:
        //   Let's say we have a `Context` function (a coroutine) which _owns_ a list of coroutines.
        //   If that primary coroutine unwinds it's stack by panicking it will drop the list and thus
        //   all the contained coroutines are dropped. When they unwind their stack due to this the
        //   process will be aborted by Rust's runtime with "thread panicked while it is panicking".
        //
        // The downside of this technique is however that the internaL PANIC_COUNT is off by one
        // (it's still zero) and thus it won't abort the process anymore if drop() panics.
        // This could be fixed however by setting a custom panic handler using panic::set_handler().
        struct ForceUnwind;
        panic::propagate(Box::new(ForceUnwind));
    }

    // This method is used to defer stack deallocation after it's not used anymore.
    extern "C" fn delete_stack(t: Transfer) -> Transfer {
        println!("Deleting stack!");
        let _ = take_some_stack_from_transfer(&t);

        t
    }

    // This method is used as the "main" context function.
    extern "C" fn context_function(t: Transfer) -> ! {
        println!("Entering context_function...");

        // Take over the stack from the main function, because we want to manage it ourselves.
        // The main function could safely return after this in theory.
        let mut some_stack = take_some_stack_from_transfer(&t);
        let stack_ref = stack_ref_from_some_stack(&mut some_stack);

        let (result, context) = {
            let mut carrier = Carrier {
                context: Some(t.0),
            };

            let carrier_ptr = &mut carrier as *mut _ as usize;

            // Use `std::panic::recover()` to catch panics from `unwind_stack()`.
            let r = panic::recover(|| {
                // We use an instance of `Dropper` to demonstrate
                // that the stack is actually being unwound.
                let _dropper = Dropper;

                let carrier = unsafe { &mut *(carrier_ptr as *mut Carrier) };

                // We've set everything up! Go back to `main()`!
                println!("Everything's set up!");
                let context = carrier.context.take().unwrap();
                let Transfer(context, _) = context.resume(carrier_ptr);
                carrier.context = Some(context);

                for i in 0usize.. {
                    print!("Yielding {} => ", i);
                    let context = carrier.context.take().unwrap();
                    let Transfer(context, _) = context.resume(i);
                    carrier.context = Some(context);
                }
            });

            (r, carrier.context.take().unwrap())
        };

        match result {
            Ok(..) => println!("Finished loop without panicking (this should not happen here)!"),
            Err(..) => println!("Recovered from a panic!"),
        }

        // We own the stack (`main()` gave it to us) and we need to delete it.
        // Since it would be unsafe to do so while we're still in the context function running on
        // that particular stack, we defer deletion of it by resuming `main()` and running the ontop
        // function `delete_stack()` before `main()` returns from it's call to `resume_ontop()`.
        println!("Defer stack deallocation by returning to main()!");
        context.resume_ontop(stack_ref, delete_stack);

        unreachable!();
    }

    pub fn run() {
        // Allocate some stack.
        let mut some_stack = Some(ProtectedFixedSizeStack::default());
        let stack_ref = stack_ref_from_some_stack(&mut some_stack);

        // Allocate a Context on the stack.
        let mut ctx = Context::new(some_stack.as_ref().unwrap(), context_function);

        // Yield to context_function(). This important since the returned `Context` reference is
        // different than the one returned by `Context::new()` (since it points to the entry function).
        // It's important that we do this first or else calling `Context::resume_ontop()` will crash.
        // See documentation of `Context::resume_ontop()` for more information.
        // Furthermore we pass a reference to the Option<ProtectedFixedSizeStack> along with it
        // so it can delete it's own stack (which is important for stackful coroutines).
        let Transfer(context, data) = ctx.resume(stack_ref);
        ctx = context;

        // Store the pointer to the Carrier for `unwind_stack`.
        let carrier_ptr = data;

        // Yield 10 times to `context_function()`.
        for _ in 0..10 {
            // Yield to the "frozen" state of `context_function()`.
            // The `data` value is not used in this example and is left at 0.
            print!("Resuming => ");
            let Transfer(context, data) = ctx.resume(0);
            ctx = context;

            println!("Got {}", data);
        }

        // Resume `context_function()` with the ontop function `unwind_stack()`.
        // Before it returns from it's own call to `resume()` it will call `unwind_stack()`.
        println!("Resuming context with unwind_stack() ontop!");
        ctx.resume_ontop(carrier_ptr, unwind_stack);

        match some_stack {
            Some(..) => println!("Stack is still there (this should not happen here)!"),
            None => println!("Stack has been deleted!"),
        }

        println!("Finished!");
    }
}

fn main() {
    imp::run();
}
<commit_msg>fixed how_to_ontop example<commit_after>// Copyright 2016 coroutine-rs Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://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.

#![cfg_attr(feature = "nightly", feature(panic_propagate, recover, std_panic))]

extern crate context;

#[cfg(not(feature = "nightly"))]
mod imp {
    pub fn run() {
        println!("Rust Nightly is required for this example!");
    }
}

#[cfg(feature = "nightly")]
mod imp {
    use std::panic;

    use context::{Context, Transfer};
    use context::stack::ProtectedFixedSizeStack;

    // This struct is used to demonstrate that the stack is actually being unwound.
    struct Dropper;

    impl Drop for Dropper {
        fn drop(&mut self) {
            println!("Dropping a Dropper!");
        }
    }

    // This struct is used to carry the actual context
    struct Carrier {
        context: Option<Context>,
    }

    fn take_some_stack_from_transfer(t: &Transfer) -> Option<ProtectedFixedSizeStack> {
        let stack_ref = unsafe { &mut *(t.data as *mut Option<ProtectedFixedSizeStack>) };
        stack_ref.take()
    }

    fn stack_ref_from_some_stack(some_stack: &mut Option<ProtectedFixedSizeStack>) -> usize {
        some_stack as *mut Option<ProtectedFixedSizeStack> as usize
    }

    // This method is used to force unwind a foreign context function.
    extern "C" fn unwind_stack(t: Transfer) -> Transfer {
        println!("Unwinding stack by panicking!");

        // We have to store the `t.context` in the `Carrier` object. because this function won't
        // be able to return normally.
        let Transfer { context: ctx, data } = t;
        let carrier = unsafe { &mut *(data as *mut Carrier) };

        carrier.context = Some(ctx);

        // Unwind the current stack by panicking.
        // We use std::panic::propagate() here however because panic!() will call the panic handler
        // which aborts the process if more than one panic is triggered on a thread.
        // This is problematic for coroutines though (the most popular use case for this crate),
        // because by their very definition multiple seperate stacks per thread are used.
        //
        // Thus the following problem can occur:
        //   Let's say we have a `Context` function (a coroutine) which _owns_ a list of coroutines.
        //   If that primary coroutine unwinds it's stack by panicking it will drop the list and thus
        //   all the contained coroutines are dropped. When they unwind their stack due to this the
        //   process will be aborted by Rust's runtime with "thread panicked while it is panicking".
        //
        // The downside of this technique is however that the internaL PANIC_COUNT is off by one
        // (it's still zero) and thus it won't abort the process anymore if drop() panics.
        // This could be fixed however by setting a custom panic handler using panic::set_handler().
        struct ForceUnwind;
        panic::propagate(Box::new(ForceUnwind));
    }

    // This method is used to defer stack deallocation after it's not used anymore.
    extern "C" fn delete_stack(t: Transfer) -> Transfer {
        println!("Deleting stack!");
        let _ = take_some_stack_from_transfer(&t);

        t
    }

    // This method is used as the "main" context function.
    extern "C" fn context_function(t: Transfer) -> ! {
        println!("Entering context_function...");

        // Take over the stack from the main function, because we want to manage it ourselves.
        // The main function could safely return after this in theory.
        let mut some_stack = take_some_stack_from_transfer(&t);
        let stack_ref = stack_ref_from_some_stack(&mut some_stack);

        let (result, context) = {
            let mut carrier = Carrier {
                context: Some(t.context),
            };

            let carrier_ptr = &mut carrier as *mut _ as usize;

            // Use `std::panic::recover()` to catch panics from `unwind_stack()`.
            let r = panic::recover(|| {
                // We use an instance of `Dropper` to demonstrate
                // that the stack is actually being unwound.
                let _dropper = Dropper;

                let carrier = unsafe { &mut *(carrier_ptr as *mut Carrier) };

                // We've set everything up! Go back to `main()`!
                println!("Everything's set up!");
                let context = carrier.context.take().unwrap();
                let Transfer { context, .. } = context.resume(carrier_ptr);
                carrier.context = Some(context);

                for i in 0usize.. {
                    print!("Yielding {} => ", i);
                    let context = carrier.context.take().unwrap();
                    let Transfer { context, .. } = context.resume(i);
                    carrier.context = Some(context);
                }
            });

            (r, carrier.context.take().unwrap())
        };

        match result {
            Ok(..) => println!("Finished loop without panicking (this should not happen here)!"),
            Err(..) => println!("Recovered from a panic!"),
        }

        // We own the stack (`main()` gave it to us) and we need to delete it.
        // Since it would be unsafe to do so while we're still in the context function running on
        // that particular stack, we defer deletion of it by resuming `main()` and running the ontop
        // function `delete_stack()` before `main()` returns from it's call to `resume_ontop()`.
        println!("Defer stack deallocation by returning to main()!");
        context.resume_ontop(stack_ref, delete_stack);

        unreachable!();
    }

    pub fn run() {
        // Allocate some stack.
        let mut some_stack = Some(ProtectedFixedSizeStack::default());
        let stack_ref = stack_ref_from_some_stack(&mut some_stack);

        // Allocate a Context on the stack.
        let mut ctx = Context::new(some_stack.as_ref().unwrap(), context_function);

        // Yield to context_function(). This important since the returned `Context` reference is
        // different than the one returned by `Context::new()` (since it points to the entry function).
        // It's important that we do this first or else calling `Context::resume_ontop()` will crash.
        // See documentation of `Context::resume_ontop()` for more information.
        // Furthermore we pass a reference to the Option<ProtectedFixedSizeStack> along with it
        // so it can delete it's own stack (which is important for stackful coroutines).
        let Transfer { context, data } = ctx.resume(stack_ref);
        ctx = context;

        // Store the pointer to the Carrier for `unwind_stack`.
        let carrier_ptr = data;

        // Yield 10 times to `context_function()`.
        for _ in 0..10 {
            // Yield to the "frozen" state of `context_function()`.
            // The `data` value is not used in this example and is left at 0.
            print!("Resuming => ");
            let Transfer { context, data } = ctx.resume(0);
            ctx = context;

            println!("Got {}", data);
        }

        // Resume `context_function()` with the ontop function `unwind_stack()`.
        // Before it returns from it's own call to `resume()` it will call `unwind_stack()`.
        println!("Resuming context with unwind_stack() ontop!");
        ctx.resume_ontop(carrier_ptr, unwind_stack);

        match some_stack {
            Some(..) => println!("Stack is still there (this should not happen here)!"),
            None => println!("Stack has been deleted!"),
        }

        println!("Finished!");
    }
}

fn main() {
    imp::run();
}
<|endoftext|>


<commit_before>use std::io::{Result, Write};

use analysis;
use library;
use super::general::tabs;

pub fn generate<W: Write>(w: &mut W, analysis: &analysis::functions::Info,
    in_trait: bool, only_declaration: bool, indent: i32) -> Result<()> {

    let comment_prefix = if analysis.comented { "//" } else { "" };
    let pub_prefix = if in_trait { "" } else { "pub " };
    let declaration = declaration(analysis);
    let suffix = if only_declaration { ";" } else { " {" };

    try!(writeln!(w, "{}{}{}{}{}", tabs(indent),
        comment_prefix, pub_prefix, declaration, suffix));

    if !only_declaration {
        let body = body(analysis);
        for s in body {
            try!(writeln!(w, "{}{}", tabs(indent + 1), s));
        }
        try!(writeln!(w, "{}}}", tabs(indent)));
    }

    Ok(())
}

pub fn declaration(analysis: &analysis::functions::Info) -> String {
    //TODO: Optional constructors if any
    //TODO: return values
    let return_str = if analysis.kind == library::FunctionKind::Constructor {
        " -> Self"  //TODO: actual type
    } else {
        "TODO"
    };
    format!("fn {}(TODO: Params){}", analysis.name, return_str)
}

pub fn body(analysis: &analysis::functions::Info) -> Vec<String> {
    let mut v: Vec<String> = Vec::new();
    //TODO: real generation
    //TODO: use trait Write
    v.push("unsafe {".into());
    v.push(format!("{}TODO: call ffi:{}()", tabs(1), analysis.glib_name).into());
    v.push("}".into());
    v
}
<commit_msg>Add macro write_to_vec for codegen::function::body()<commit_after>use std::io::{Result, Write};
use std::fmt;

use analysis;
use library;
use super::general::tabs;

pub fn generate<W: Write>(w: &mut W, analysis: &analysis::functions::Info,
    in_trait: bool, only_declaration: bool, indent: i32) -> Result<()> {

    let comment_prefix = if analysis.comented { "//" } else { "" };
    let pub_prefix = if in_trait { "" } else { "pub " };
    let declaration = declaration(analysis);
    let suffix = if only_declaration { ";" } else { " {" };

    try!(writeln!(w, "{}{}{}{}{}", tabs(indent),
        comment_prefix, pub_prefix, declaration, suffix));

    if !only_declaration {
        let body = body(analysis);
        for s in body {
            try!(writeln!(w, "{}{}", tabs(indent + 1), s));
        }
        try!(writeln!(w, "{}}}", tabs(indent)));
    }

    Ok(())
}

pub fn declaration(analysis: &analysis::functions::Info) -> String {
    //TODO: Optional constructors if any
    //TODO: return values
    let return_str = if analysis.kind == library::FunctionKind::Constructor {
        " -> Self"  //TODO: actual type
    } else {
        "TODO"
    };
    format!("fn {}(TODO: Params){}", analysis.name, return_str)
}

macro_rules! write_to_vec(
    ($dst:expr, $($arg:tt)*) => (
        $dst.push(fmt::format(format_args!($($arg)*)))
    )
);

pub fn body(analysis: &analysis::functions::Info) -> Vec<String> {
    let mut v: Vec<String> = Vec::new();
    //TODO: real generation
    write_to_vec!(v, "unsafe {{");
    write_to_vec!(v, "{}TODO: call ffi:{}()", tabs(1), analysis.glib_name);
    write_to_vec!(v, "}}");
    v
}
<|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/. */

<%namespace name="helpers" file="/helpers.mako.rs" />

<% data.new_style_struct("InheritedBox", inherited=True, gecko_name="Visibility") %>

// TODO: collapse. Well, do tables first.
${helpers.single_keyword("visibility",
                         "visible hidden",
                         extra_gecko_values="collapse",
                         gecko_ffi_name="mVisible",
                         animation_value_type="ComputedValue",
                         spec="https://drafts.csswg.org/css-box/#propdef-visibility")}

// CSS Writing Modes Level 3
// https://drafts.csswg.org/css-writing-modes-3
${helpers.single_keyword("writing-mode",
                         "horizontal-tb vertical-rl vertical-lr",
                         extra_gecko_values="sideways-rl sideways-lr",
                         extra_gecko_aliases="lr=horizontal-tb lr-tb=horizontal-tb \
                                              rl=horizontal-tb rl-tb=horizontal-tb \
                                              tb=vertical-rl   tb-rl=vertical-rl",
                         servo_pref="layout.writing-mode.enabled",
                         animation_value_type="discrete",
                         spec="https://drafts.csswg.org/css-writing-modes/#propdef-writing-mode",
                         servo_restyle_damage="rebuild_and_reflow")}

${helpers.single_keyword("direction", "ltr rtl", animation_value_type="discrete",
                         spec="https://drafts.csswg.org/css-writing-modes/#propdef-direction",
                         needs_conversion=True,
                         servo_restyle_damage="rebuild_and_reflow")}

${helpers.single_keyword("text-orientation",
                         "mixed upright sideways",
                         extra_gecko_aliases="sideways-right=sideways",
                         products="gecko",
                         animation_value_type="discrete",
                         spec="https://drafts.csswg.org/css-writing-modes/#propdef-text-orientation")}

// CSS Color Module Level 4
// https://drafts.csswg.org/css-color/
${helpers.single_keyword("color-adjust",
                         "economy exact", products="gecko",
                         gecko_pref="layout.css.color-adjust.enabled",
                         animation_value_type="discrete",
                         spec="https://drafts.csswg.org/css-color/#propdef-color-adjust")}

<% image_rendering_custom_consts = { "crisp-edges": "CRISPEDGES",
                                     "-moz-crisp-edges": "CRISPEDGES" } %>
// According to to CSS-IMAGES-3, `optimizespeed` and `optimizequality` are synonyms for `auto`
// And, firefox doesn't support `pixelated` yet (https://bugzilla.mozilla.org/show_bug.cgi?id=856337)
${helpers.single_keyword("image-rendering",
                         "auto",
                         extra_gecko_values="optimizespeed optimizequality -moz-crisp-edges",
                         extra_servo_values="pixelated crisp-edges",
                         custom_consts=image_rendering_custom_consts,
                         animation_value_type="discrete",
                         spec="https://drafts.csswg.org/css-images/#propdef-image-rendering")}

${helpers.predefined_type("image-orientation",
                          "ImageOrientation",
                          "computed::ImageOrientation::zero()",
                          products="gecko",
                          animation_value_type="discrete",
                          gecko_pref="layout.css.image-orientation.enabled",
                          spec="https://drafts.csswg.org/css-images/#propdef-image-orientation, \
                    /// additional values in https://developer.mozilla.org/en-US/docs/Web/CSS/image-orientation")}
<commit_msg>style: Reformat most of inherited_box.mako.rs.<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/. */

<%namespace name="helpers" file="/helpers.mako.rs" />

<% data.new_style_struct("InheritedBox", inherited=True, gecko_name="Visibility") %>

// TODO: collapse. Well, do tables first.
${helpers.single_keyword(
    "visibility",
    "visible hidden",
    extra_gecko_values="collapse",
    gecko_ffi_name="mVisible",
    animation_value_type="ComputedValue",
    spec="https://drafts.csswg.org/css-box/#propdef-visibility",
)}

// CSS Writing Modes Level 3
// https://drafts.csswg.org/css-writing-modes-3
${helpers.single_keyword(
    "writing-mode",
    "horizontal-tb vertical-rl vertical-lr",
    extra_gecko_values="sideways-rl sideways-lr",
    extra_gecko_aliases="lr=horizontal-tb lr-tb=horizontal-tb \
                         rl=horizontal-tb rl-tb=horizontal-tb \
                         tb=vertical-rl   tb-rl=vertical-rl",
    servo_pref="layout.writing-mode.enabled",
    animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-writing-modes/#propdef-writing-mode",
    servo_restyle_damage="rebuild_and_reflow",
)}

${helpers.single_keyword(
    "direction",
    "ltr rtl",
    animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-writing-modes/#propdef-direction",
    needs_conversion=True,
    servo_restyle_damage="rebuild_and_reflow",
)}

${helpers.single_keyword(
    "text-orientation",
    "mixed upright sideways",
    extra_gecko_aliases="sideways-right=sideways",
    products="gecko",
    animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-writing-modes/#propdef-text-orientation",
)}

// CSS Color Module Level 4
// https://drafts.csswg.org/css-color/
${helpers.single_keyword(
    "color-adjust",
    "economy exact", products="gecko",
    gecko_pref="layout.css.color-adjust.enabled",
    animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-color/#propdef-color-adjust",
)}

<% image_rendering_custom_consts = { "crisp-edges": "CRISPEDGES",
                                     "-moz-crisp-edges": "CRISPEDGES" } %>
// According to to CSS-IMAGES-3, `optimizespeed` and `optimizequality` are synonyms for `auto`
// And, firefox doesn't support `pixelated` yet (https://bugzilla.mozilla.org/show_bug.cgi?id=856337)
${helpers.single_keyword(
    "image-rendering",
    "auto",
    extra_gecko_values="optimizespeed optimizequality -moz-crisp-edges",
    extra_servo_values="pixelated crisp-edges",
    custom_consts=image_rendering_custom_consts,
    animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-images/#propdef-image-rendering",
)}

${helpers.predefined_type("image-orientation",
                          "ImageOrientation",
                          "computed::ImageOrientation::zero()",
                          products="gecko",
                          animation_value_type="discrete",
                          gecko_pref="layout.css.image-orientation.enabled",
                          spec="https://drafts.csswg.org/css-images/#propdef-image-orientation, \
                    /// additional values in https://developer.mozilla.org/en-US/docs/Web/CSS/image-orientation")}
<|endoftext|>


<commit_before>pub type blkcnt_t = i64;
pub type blksize_t = i64;
pub type c_char = i8;
pub type c_long = i64;
pub type c_ulong = u64;
pub type fsblkcnt_t = ::c_ulong;
pub type fsfilcnt_t = ::c_ulong;
pub type ino_t = u64;
pub type nlink_t = u64;
pub type off_t = i64;
pub type rlim_t = ::c_ulong;
pub type suseconds_t = i64;
pub type time_t = i64;
pub type wchar_t = i32;

s! {
    pub struct stat {
        pub st_dev: ::c_ulong,
        st_pad1: [::c_long; 2],
        pub st_ino: ::ino_t,
        pub st_mode: ::mode_t,
        pub st_nlink: ::nlink_t,
        pub st_uid: ::uid_t,
        pub st_gid: ::gid_t,
        pub st_rdev: ::c_ulong,
        st_pad2: [::c_ulong; 1],
        pub st_size: ::off_t,
        st_pad3: ::c_long,
        pub st_atime: ::time_t,
        pub st_atime_nsec: ::c_long,
        pub st_mtime: ::time_t,
        pub st_mtime_nsec: ::c_long,
        pub st_ctime: ::time_t,
        pub st_ctime_nsec: ::c_long,
        pub st_blksize: ::blksize_t,
        st_pad4: ::c_long,
        pub st_blocks: ::blkcnt_t,
        st_pad5: [::c_long; 7],
    }

    pub struct stat64 {
        pub st_dev: ::c_ulong,
        st_pad1: [::c_long; 2],
        pub st_ino: ::ino64_t,
        pub st_mode: ::mode_t,
        pub st_nlink: ::nlink_t,
        pub st_uid: ::uid_t,
        pub st_gid: ::gid_t,
        pub st_rdev: ::c_ulong,
        st_pad2: [::c_long; 2],
        pub st_size: ::off64_t,
        pub st_atime: ::time_t,
        pub st_atime_nsec: ::c_long,
        pub st_mtime: ::time_t,
        pub st_mtime_nsec: ::c_long,
        pub st_ctime: ::time_t,
        pub st_ctime_nsec: ::c_long,
        pub st_blksize: ::blksize_t,
        st_pad3: ::c_long,
        pub st_blocks: ::blkcnt64_t,
        st_pad5: [::c_long; 7],
    }

    pub struct pthread_attr_t {
        __size: [::c_ulong; 7]
    }

    pub struct sigaction {
        pub sa_flags: ::c_int,
        pub sa_sigaction: ::sighandler_t,
        pub sa_mask: sigset_t,
        _restorer: *mut ::c_void,
    }

    pub struct stack_t {
        pub ss_sp: *mut ::c_void,
        pub ss_size: ::size_t,
        pub ss_flags: ::c_int,
    }

    pub struct sigset_t {
        __size: [::c_ulong; 16],
    }

    pub struct siginfo_t {
        pub si_signo: ::c_int,
        pub si_code: ::c_int,
        pub si_errno: ::c_int,
        _pad: ::c_int,
        _pad2: [::c_long; 14],
    }

    pub struct ipc_perm {
        pub __key: ::key_t,
        pub uid: ::uid_t,
        pub gid: ::gid_t,
        pub cuid: ::uid_t,
        pub cgid: ::gid_t,
        pub mode: ::c_uint,
        pub __seq: ::c_ushort,
        __pad1: ::c_ushort,
        __unused1: ::c_ulong,
        __unused2: ::c_ulong
    }

    pub struct shmid_ds {
        pub shm_perm: ::ipc_perm,
        pub shm_segsz: ::size_t,
        pub shm_atime: ::time_t,
        pub shm_dtime: ::time_t,
        pub shm_ctime: ::time_t,
        pub shm_cpid: ::pid_t,
        pub shm_lpid: ::pid_t,
        pub shm_nattch: ::shmatt_t,
        __unused4: ::c_ulong,
        __unused5: ::c_ulong
    }

    pub struct statfs {
        pub f_type: ::c_long,
        pub f_bsize: ::c_long,
        pub f_frsize: ::c_long,
        pub f_blocks: ::fsblkcnt_t,
        pub f_bfree: ::fsblkcnt_t,
        pub f_files: ::fsblkcnt_t,
        pub f_ffree: ::fsblkcnt_t,
        pub f_bavail: ::fsblkcnt_t,
        pub f_fsid: ::fsid_t,

        pub f_namelen: ::c_long,
        f_spare: [::c_long; 6],
    }

    pub struct msghdr {
        pub msg_name: *mut ::c_void,
        pub msg_namelen: ::socklen_t,
        pub msg_iov: *mut ::iovec,
        pub msg_iovlen: ::size_t,
        pub msg_control: *mut ::c_void,
        pub msg_controllen: ::size_t,
        pub msg_flags: ::c_int,
    }

    pub struct termios {
        pub c_iflag: ::tcflag_t,
        pub c_oflag: ::tcflag_t,
        pub c_cflag: ::tcflag_t,
        pub c_lflag: ::tcflag_t,
        pub c_line: ::cc_t,
        pub c_cc: [::cc_t; ::NCCS],
    }

    pub struct sysinfo {
        pub uptime: ::c_long,
        pub loads: [::c_ulong; 3],
        pub totalram: ::c_ulong,
        pub freeram: ::c_ulong,
        pub sharedram: ::c_ulong,
        pub bufferram: ::c_ulong,
        pub totalswap: ::c_ulong,
        pub freeswap: ::c_ulong,
        pub procs: ::c_ushort,
        pub pad: ::c_ushort,
        pub totalhigh: ::c_ulong,
        pub freehigh: ::c_ulong,
        pub mem_unit: ::c_uint,
        pub _f: [::c_char; 0],
    }

    // FIXME this is actually a union
    pub struct sem_t {
        __size: [::c_char; 32],
        __align: [::c_long; 0],
    }
}

pub const __SIZEOF_PTHREAD_CONDATTR_T: usize = 4;
pub const __SIZEOF_PTHREAD_MUTEXATTR_T: usize = 4;
pub const __SIZEOF_PTHREAD_MUTEX_T: usize = 40;
pub const __SIZEOF_PTHREAD_RWLOCK_T: usize = 56;

pub const EADDRINUSE: ::c_int = 125;
pub const EADDRNOTAVAIL: ::c_int = 126;
pub const ECONNABORTED: ::c_int = 130;
pub const ECONNREFUSED: ::c_int = 146;
pub const ECONNRESET: ::c_int = 131;
pub const EDEADLK: ::c_int = 45;
pub const ENOSYS: ::c_int = 89;
pub const ENOTCONN: ::c_int = 134;
pub const ETIMEDOUT: ::c_int = 145;
pub const FIOCLEX: ::c_ulong = 0x6601;
pub const FIONBIO: ::c_ulong = 0x667e;
pub const MAP_ANON: ::c_int = 0x800;
pub const O_ACCMODE: ::c_int = 3;
pub const O_APPEND: ::c_int = 8;
pub const O_CREAT: ::c_int = 256;
pub const O_EXCL: ::c_int = 1024;
pub const O_NONBLOCK: ::c_int = 128;
pub const PTHREAD_STACK_MIN: ::size_t = 131072;
pub const RLIM_INFINITY: ::rlim_t = 0xffffffffffffffff;
pub const SA_ONSTACK: ::c_int = 0x08000000;
pub const SA_SIGINFO: ::c_int = 0x00000008;
pub const SIGBUS: ::c_int = 10;
pub const SIG_SETMASK: ::c_int = 3;
pub const SOCK_DGRAM: ::c_int = 1;
pub const SOCK_STREAM: ::c_int = 2;
pub const SOL_SOCKET: ::c_int = 0xffff;
pub const SO_BROADCAST: ::c_int = 32;
pub const SO_ERROR: ::c_int = 4103;
pub const SO_RCVTIMEO: ::c_int = 4102;
pub const SO_REUSEADDR: ::c_int = 4;
pub const SO_SNDTIMEO: ::c_int = 4101;

#[link(name = "util")]
extern {
    pub fn ioctl(fd: ::c_int, request: ::c_ulong, ...) -> ::c_int;
}
<commit_msg>Auto merge of #370 - japaric:mips64, r=alexcrichton<commit_after>pub type blkcnt_t = i64;
pub type blksize_t = i64;
pub type c_char = i8;
pub type c_long = i64;
pub type c_ulong = u64;
pub type fsblkcnt_t = ::c_ulong;
pub type fsfilcnt_t = ::c_ulong;
pub type ino_t = u64;
pub type nlink_t = u64;
pub type off_t = i64;
pub type rlim_t = ::c_ulong;
pub type suseconds_t = i64;
pub type time_t = i64;
pub type wchar_t = i32;

s! {
    pub struct stat {
        pub st_dev: ::c_ulong,
        st_pad1: [::c_long; 2],
        pub st_ino: ::ino_t,
        pub st_mode: ::mode_t,
        pub st_nlink: ::nlink_t,
        pub st_uid: ::uid_t,
        pub st_gid: ::gid_t,
        pub st_rdev: ::c_ulong,
        st_pad2: [::c_ulong; 1],
        pub st_size: ::off_t,
        st_pad3: ::c_long,
        pub st_atime: ::time_t,
        pub st_atime_nsec: ::c_long,
        pub st_mtime: ::time_t,
        pub st_mtime_nsec: ::c_long,
        pub st_ctime: ::time_t,
        pub st_ctime_nsec: ::c_long,
        pub st_blksize: ::blksize_t,
        st_pad4: ::c_long,
        pub st_blocks: ::blkcnt_t,
        st_pad5: [::c_long; 7],
    }

    pub struct stat64 {
        pub st_dev: ::c_ulong,
        st_pad1: [::c_long; 2],
        pub st_ino: ::ino64_t,
        pub st_mode: ::mode_t,
        pub st_nlink: ::nlink_t,
        pub st_uid: ::uid_t,
        pub st_gid: ::gid_t,
        pub st_rdev: ::c_ulong,
        st_pad2: [::c_long; 2],
        pub st_size: ::off64_t,
        pub st_atime: ::time_t,
        pub st_atime_nsec: ::c_long,
        pub st_mtime: ::time_t,
        pub st_mtime_nsec: ::c_long,
        pub st_ctime: ::time_t,
        pub st_ctime_nsec: ::c_long,
        pub st_blksize: ::blksize_t,
        st_pad3: ::c_long,
        pub st_blocks: ::blkcnt64_t,
        st_pad5: [::c_long; 7],
    }

    pub struct pthread_attr_t {
        __size: [::c_ulong; 7]
    }

    pub struct sigaction {
        pub sa_flags: ::c_int,
        pub sa_sigaction: ::sighandler_t,
        pub sa_mask: sigset_t,
        _restorer: *mut ::c_void,
    }

    pub struct stack_t {
        pub ss_sp: *mut ::c_void,
        pub ss_size: ::size_t,
        pub ss_flags: ::c_int,
    }

    pub struct sigset_t {
        __size: [::c_ulong; 16],
    }

    pub struct siginfo_t {
        pub si_signo: ::c_int,
        pub si_code: ::c_int,
        pub si_errno: ::c_int,
        _pad: ::c_int,
        _pad2: [::c_long; 14],
    }

    pub struct ipc_perm {
        pub __key: ::key_t,
        pub uid: ::uid_t,
        pub gid: ::gid_t,
        pub cuid: ::uid_t,
        pub cgid: ::gid_t,
        pub mode: ::c_uint,
        pub __seq: ::c_ushort,
        __pad1: ::c_ushort,
        __unused1: ::c_ulong,
        __unused2: ::c_ulong
    }

    pub struct shmid_ds {
        pub shm_perm: ::ipc_perm,
        pub shm_segsz: ::size_t,
        pub shm_atime: ::time_t,
        pub shm_dtime: ::time_t,
        pub shm_ctime: ::time_t,
        pub shm_cpid: ::pid_t,
        pub shm_lpid: ::pid_t,
        pub shm_nattch: ::shmatt_t,
        __unused4: ::c_ulong,
        __unused5: ::c_ulong
    }

    pub struct statfs {
        pub f_type: ::c_long,
        pub f_bsize: ::c_long,
        pub f_frsize: ::c_long,
        pub f_blocks: ::fsblkcnt_t,
        pub f_bfree: ::fsblkcnt_t,
        pub f_files: ::fsblkcnt_t,
        pub f_ffree: ::fsblkcnt_t,
        pub f_bavail: ::fsblkcnt_t,
        pub f_fsid: ::fsid_t,

        pub f_namelen: ::c_long,
        f_spare: [::c_long; 6],
    }

    pub struct msghdr {
        pub msg_name: *mut ::c_void,
        pub msg_namelen: ::socklen_t,
        pub msg_iov: *mut ::iovec,
        pub msg_iovlen: ::size_t,
        pub msg_control: *mut ::c_void,
        pub msg_controllen: ::size_t,
        pub msg_flags: ::c_int,
    }

    pub struct termios {
        pub c_iflag: ::tcflag_t,
        pub c_oflag: ::tcflag_t,
        pub c_cflag: ::tcflag_t,
        pub c_lflag: ::tcflag_t,
        pub c_line: ::cc_t,
        pub c_cc: [::cc_t; ::NCCS],
    }

    pub struct sysinfo {
        pub uptime: ::c_long,
        pub loads: [::c_ulong; 3],
        pub totalram: ::c_ulong,
        pub freeram: ::c_ulong,
        pub sharedram: ::c_ulong,
        pub bufferram: ::c_ulong,
        pub totalswap: ::c_ulong,
        pub freeswap: ::c_ulong,
        pub procs: ::c_ushort,
        pub pad: ::c_ushort,
        pub totalhigh: ::c_ulong,
        pub freehigh: ::c_ulong,
        pub mem_unit: ::c_uint,
        pub _f: [::c_char; 0],
    }

    // FIXME this is actually a union
    pub struct sem_t {
        __size: [::c_char; 32],
        __align: [::c_long; 0],
    }
}

pub const __SIZEOF_PTHREAD_CONDATTR_T: usize = 4;
pub const __SIZEOF_PTHREAD_MUTEXATTR_T: usize = 4;
pub const __SIZEOF_PTHREAD_MUTEX_T: usize = 40;
pub const __SIZEOF_PTHREAD_RWLOCK_T: usize = 56;

pub const EADDRINUSE: ::c_int = 125;
pub const EADDRNOTAVAIL: ::c_int = 126;
pub const ECONNABORTED: ::c_int = 130;
pub const ECONNREFUSED: ::c_int = 146;
pub const ECONNRESET: ::c_int = 131;
pub const EDEADLK: ::c_int = 45;
pub const ENOSYS: ::c_int = 89;
pub const ENOTCONN: ::c_int = 134;
pub const ETIMEDOUT: ::c_int = 145;
pub const FIOCLEX: ::c_ulong = 0x6601;
pub const FIONBIO: ::c_ulong = 0x667e;
pub const MAP_ANON: ::c_int = 0x800;
pub const O_ACCMODE: ::c_int = 3;
pub const O_APPEND: ::c_int = 8;
pub const O_CREAT: ::c_int = 256;
pub const O_EXCL: ::c_int = 1024;
pub const O_NONBLOCK: ::c_int = 128;
pub const PTHREAD_STACK_MIN: ::size_t = 131072;
pub const RLIM_INFINITY: ::rlim_t = 0xffffffffffffffff;
pub const SA_ONSTACK: ::c_int = 0x08000000;
pub const SA_SIGINFO: ::c_int = 0x00000008;
pub const SIGBUS: ::c_int = 10;
pub const SIGSTKSZ: ::size_t = 0x2000;
pub const SIG_SETMASK: ::c_int = 3;
pub const SOCK_DGRAM: ::c_int = 1;
pub const SOCK_STREAM: ::c_int = 2;
pub const SOL_SOCKET: ::c_int = 0xffff;
pub const SO_BROADCAST: ::c_int = 32;
pub const SO_ERROR: ::c_int = 4103;
pub const SO_RCVTIMEO: ::c_int = 4102;
pub const SO_REUSEADDR: ::c_int = 4;
pub const SO_SNDTIMEO: ::c_int = 4101;

#[link(name = "util")]
extern {
    pub fn ioctl(fd: ::c_int, request: ::c_ulong, ...) -> ::c_int;
}
<|endoftext|>


<commit_before>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Alex Lyon <arcterus@mail.com>
//  *
//  * For the full copyright and license information, please view the LICENSE
//  * file that was distributed with this source code.

// spell-checker:ignore (ToDO) RFILE refsize rfilename fsize tsize

#[macro_use]
extern crate uucore;

use clap::{crate_version, App, Arg};
use std::convert::TryFrom;
use std::fs::{metadata, OpenOptions};
use std::io::ErrorKind;
use std::path::Path;
use uucore::display::Quotable;
use uucore::error::{UIoError, UResult, USimpleError, UUsageError};
use uucore::parse_size::{parse_size, ParseSizeError};

#[derive(Debug, Eq, PartialEq)]
enum TruncateMode {
    Absolute(usize),
    Extend(usize),
    Reduce(usize),
    AtMost(usize),
    AtLeast(usize),
    RoundDown(usize),
    RoundUp(usize),
}

impl TruncateMode {
    /// Compute a target size in bytes for this truncate mode.
    ///
    /// `fsize` is the size of the reference file, in bytes.
    ///
    /// # Examples
    ///
    /// ```rust,ignore
    /// let mode = TruncateMode::Extend(5);
    /// let fsize = 10;
    /// assert_eq!(mode.to_size(fsize), 15);
    /// ```
    fn to_size(&self, fsize: usize) -> usize {
        match self {
            TruncateMode::Absolute(size) => *size,
            TruncateMode::Extend(size) => fsize + size,
            TruncateMode::Reduce(size) => fsize - size,
            TruncateMode::AtMost(size) => fsize.min(*size),
            TruncateMode::AtLeast(size) => fsize.max(*size),
            TruncateMode::RoundDown(size) => fsize - fsize % size,
            TruncateMode::RoundUp(size) => fsize + fsize % size,
        }
    }
}

static ABOUT: &str = "Shrink or extend the size of each file to the specified size.";

pub mod options {
    pub static IO_BLOCKS: &str = "io-blocks";
    pub static NO_CREATE: &str = "no-create";
    pub static REFERENCE: &str = "reference";
    pub static SIZE: &str = "size";
    pub static ARG_FILES: &str = "files";
}

fn usage() -> String {
    format!("{0} [OPTION]... [FILE]...", uucore::execution_phrase())
}

fn get_long_usage() -> String {
    String::from(
        "
    SIZE is an integer with an optional prefix and optional unit.
    The available units (K, M, G, T, P, E, Z, and Y) use the following format:
        'KB' =>           1000 (kilobytes)
        'K'  =>           1024 (kibibytes)
        'MB' =>      1000*1000 (megabytes)
        'M'  =>      1024*1024 (mebibytes)
        'GB' => 1000*1000*1000 (gigabytes)
        'G'  => 1024*1024*1024 (gibibytes)
    SIZE may also be prefixed by one of the following to adjust the size of each
    file based on its current size:
        '+'  => extend by
        '-'  => reduce by
        '<'  => at most
        '>'  => at least
        '/'  => round down to multiple of
        '%'  => round up to multiple of",
    )
}

#[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
    let usage = usage();
    let long_usage = get_long_usage();

    let matches = uu_app()
        .override_usage(&usage[..])
        .after_help(&long_usage[..])
        .get_matches_from(args);

    let files: Vec<String> = matches
        .values_of(options::ARG_FILES)
        .map(|v| v.map(ToString::to_string).collect())
        .unwrap_or_default();

    if files.is_empty() {
        return Err(UUsageError::new(1, "missing file operand"));
    } else {
        let io_blocks = matches.is_present(options::IO_BLOCKS);
        let no_create = matches.is_present(options::NO_CREATE);
        let reference = matches.value_of(options::REFERENCE).map(String::from);
        let size = matches.value_of(options::SIZE).map(String::from);
        truncate(no_create, io_blocks, reference, size, files).map_err(|e| {
            match e.kind() {
                ErrorKind::NotFound => {
                    // TODO Improve error-handling so that the error
                    // returned by `truncate()` provides the necessary
                    // parameter for formatting the error message.
                    let reference = matches.value_of(options::REFERENCE).map(String::from);
                    USimpleError::new(
                        1,
                        format!(
                            "cannot stat {}: No such file or directory",
                            reference.as_deref().unwrap_or("").quote()
                        ),
                    ) // TODO: fix '--no-create' see test_reference and test_truncate_bytes_size
                }
                _ => uio_error!(e, ""),
            }
        })
    }
}

pub fn uu_app<'a>() -> App<'a> {
    App::new(uucore::util_name())
        .version(crate_version!())
        .about(ABOUT)
        .arg(
            Arg::new(options::IO_BLOCKS)
            .short('o')
            .long(options::IO_BLOCKS)
            .help("treat SIZE as the number of I/O blocks of the file rather than bytes (NOT IMPLEMENTED)")
        )
        .arg(
            Arg::new(options::NO_CREATE)
            .short('c')
            .long(options::NO_CREATE)
            .help("do not create files that do not exist")
        )
        .arg(
            Arg::new(options::REFERENCE)
            .short('r')
            .long(options::REFERENCE)
            .required_unless_present(options::SIZE)
            .help("base the size of each file on the size of RFILE")
            .value_name("RFILE")
        )
        .arg(
            Arg::new(options::SIZE)
            .short('s')
            .long(options::SIZE)
            .required_unless_present(options::REFERENCE)
            .help("set or adjust the size of each file according to SIZE, which is in bytes unless --io-blocks is specified")
            .value_name("SIZE")
        )
        .arg(Arg::new(options::ARG_FILES)
             .value_name("FILE")
             .multiple_occurrences(true)
             .takes_value(true)
             .required(true)
             .min_values(1))
}

/// Truncate the named file to the specified size.
///
/// If `create` is true, then the file will be created if it does not
/// already exist. If `size` is larger than the number of bytes in the
/// file, then the file will be padded with zeros. If `size` is smaller
/// than the number of bytes in the file, then the file will be
/// truncated and any bytes beyond `size` will be lost.
///
/// # Errors
///
/// If the file could not be opened, or there was a problem setting the
/// size of the file.
fn file_truncate(filename: &str, create: bool, size: usize) -> std::io::Result<()> {
    let path = Path::new(filename);
    let f = OpenOptions::new().write(true).create(create).open(path)?;
    f.set_len(u64::try_from(size).unwrap())
}

/// Truncate files to a size relative to a given file.
///
/// `rfilename` is the name of the reference file.
///
/// `size_string` gives the size relative to the reference file to which
/// to set the target files. For example, "+3K" means "set each file to
/// be three kilobytes larger than the size of the reference file".
///
/// If `create` is true, then each file will be created if it does not
/// already exist.
///
/// # Errors
///
/// If the any file could not be opened, or there was a problem setting
/// the size of at least one file.
fn truncate_reference_and_size(
    rfilename: &str,
    size_string: &str,
    filenames: Vec<String>,
    create: bool,
) -> std::io::Result<()> {
    let mode = match parse_mode_and_size(size_string) {
        Ok(m) => match m {
            TruncateMode::Absolute(_) => {
                crash!(1, "you must specify a relative '--size' with '--reference'")
            }
            _ => m,
        },
        Err(e) => crash!(1, "Invalid number: {}", e.to_string()),
    };
    let fsize = usize::try_from(metadata(rfilename)?.len()).unwrap();
    let tsize = mode.to_size(fsize);
    for filename in &filenames {
        file_truncate(filename, create, tsize)?;
    }
    Ok(())
}

/// Truncate files to match the size of a given reference file.
///
/// `rfilename` is the name of the reference file.
///
/// If `create` is true, then each file will be created if it does not
/// already exist.
///
/// # Errors
///
/// If the any file could not be opened, or there was a problem setting
/// the size of at least one file.
fn truncate_reference_file_only(
    rfilename: &str,
    filenames: Vec<String>,
    create: bool,
) -> std::io::Result<()> {
    let tsize = usize::try_from(metadata(rfilename)?.len()).unwrap();
    for filename in &filenames {
        file_truncate(filename, create, tsize)?;
    }
    Ok(())
}

/// Truncate files to a specified size.
///
/// `size_string` gives either an absolute size or a relative size. A
/// relative size adjusts the size of each file relative to its current
/// size. For example, "3K" means "set each file to be three kilobytes"
/// whereas "+3K" means "set each file to be three kilobytes larger than
/// its current size".
///
/// If `create` is true, then each file will be created if it does not
/// already exist.
///
/// # Errors
///
/// If the any file could not be opened, or there was a problem setting
/// the size of at least one file.
fn truncate_size_only(
    size_string: &str,
    filenames: Vec<String>,
    create: bool,
) -> std::io::Result<()> {
    let mode = match parse_mode_and_size(size_string) {
        Ok(m) => m,
        Err(e) => crash!(1, "Invalid number: {}", e.to_string()),
    };
    for filename in &filenames {
        let fsize = match metadata(filename) {
            Ok(m) => m.len(),
            Err(_) => 0,
        };
        let tsize = mode.to_size(fsize as usize);
        match file_truncate(filename, create, tsize) {
            Ok(_) => continue,
            Err(e) if e.kind() == ErrorKind::NotFound && !create => continue,
            Err(e) => return Err(e),
        }
    }
    Ok(())
}

fn truncate(
    no_create: bool,
    _: bool,
    reference: Option<String>,
    size: Option<String>,
    filenames: Vec<String>,
) -> std::io::Result<()> {
    let create = !no_create;
    // There are four possibilities
    // - reference file given and size given,
    // - reference file given but no size given,
    // - no reference file given but size given,
    // - no reference file given and no size given,
    match (reference, size) {
        (Some(rfilename), Some(size_string)) => {
            truncate_reference_and_size(&rfilename, &size_string, filenames, create)
        }
        (Some(rfilename), None) => truncate_reference_file_only(&rfilename, filenames, create),
        (None, Some(size_string)) => truncate_size_only(&size_string, filenames, create),
        (None, None) => unreachable!(), // this case cannot happen anymore because it's handled by clap
    }
}

/// Decide whether a character is one of the size modifiers, like '+' or '<'.
fn is_modifier(c: char) -> bool {
    c == '+' || c == '-' || c == '<' || c == '>' || c == '/' || c == '%'
}

/// Parse a size string with optional modifier symbol as its first character.
///
/// A size string is as described in [`parse_size`]. The first character
/// of `size_string` might be a modifier symbol, like `'+'` or
/// `'<'`. The first element of the pair returned by this function
/// indicates which modifier symbol was present, or
/// [`TruncateMode::Absolute`] if none.
///
/// # Panics
///
/// If `size_string` is empty, or if no number could be parsed from the
/// given string (for example, if the string were `"abc"`).
///
/// # Examples
///
/// ```rust,ignore
/// assert_eq!(parse_mode_and_size("+123"), (TruncateMode::Extend, 123));
/// ```
fn parse_mode_and_size(size_string: &str) -> Result<TruncateMode, ParseSizeError> {
    // Trim any whitespace.
    let mut size_string = size_string.trim();

    // Get the modifier character from the size string, if any. For
    // example, if the argument is "+123", then the modifier is '+'.
    if let Some(c) = size_string.chars().next() {
        if is_modifier(c) {
            size_string = &size_string[1..];
        }
        parse_size(size_string).map(match c {
            '+' => TruncateMode::Extend,
            '-' => TruncateMode::Reduce,
            '<' => TruncateMode::AtMost,
            '>' => TruncateMode::AtLeast,
            '/' => TruncateMode::RoundDown,
            '%' => TruncateMode::RoundUp,
            _ => TruncateMode::Absolute,
        })
    } else {
        Err(ParseSizeError::ParseFailure(size_string.to_string()))
    }
}

#[cfg(test)]
mod tests {
    use crate::parse_mode_and_size;
    use crate::TruncateMode;

    #[test]
    fn test_parse_mode_and_size() {
        assert_eq!(parse_mode_and_size("10"), Ok(TruncateMode::Absolute(10)));
        assert_eq!(parse_mode_and_size("+10"), Ok(TruncateMode::Extend(10)));
        assert_eq!(parse_mode_and_size("-10"), Ok(TruncateMode::Reduce(10)));
        assert_eq!(parse_mode_and_size("<10"), Ok(TruncateMode::AtMost(10)));
        assert_eq!(parse_mode_and_size(">10"), Ok(TruncateMode::AtLeast(10)));
        assert_eq!(parse_mode_and_size("/10"), Ok(TruncateMode::RoundDown(10)));
        assert_eq!(parse_mode_and_size("%10"), Ok(TruncateMode::RoundUp(10)));
    }
}
<commit_msg>truncate: make better use of UResult<commit_after>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Alex Lyon <arcterus@mail.com>
//  *
//  * For the full copyright and license information, please view the LICENSE
//  * file that was distributed with this source code.

// spell-checker:ignore (ToDO) RFILE refsize rfilename fsize tsize
use clap::{crate_version, App, Arg};
use std::convert::TryFrom;
use std::fs::{metadata, OpenOptions};
use std::io::ErrorKind;
use std::path::Path;
use uucore::display::Quotable;
use uucore::error::{FromIo, UResult, USimpleError, UUsageError};
use uucore::parse_size::{parse_size, ParseSizeError};

#[derive(Debug, Eq, PartialEq)]
enum TruncateMode {
    Absolute(usize),
    Extend(usize),
    Reduce(usize),
    AtMost(usize),
    AtLeast(usize),
    RoundDown(usize),
    RoundUp(usize),
}

impl TruncateMode {
    /// Compute a target size in bytes for this truncate mode.
    ///
    /// `fsize` is the size of the reference file, in bytes.
    ///
    /// # Examples
    ///
    /// ```rust,ignore
    /// let mode = TruncateMode::Extend(5);
    /// let fsize = 10;
    /// assert_eq!(mode.to_size(fsize), 15);
    /// ```
    fn to_size(&self, fsize: usize) -> usize {
        match self {
            TruncateMode::Absolute(size) => *size,
            TruncateMode::Extend(size) => fsize + size,
            TruncateMode::Reduce(size) => fsize - size,
            TruncateMode::AtMost(size) => fsize.min(*size),
            TruncateMode::AtLeast(size) => fsize.max(*size),
            TruncateMode::RoundDown(size) => fsize - fsize % size,
            TruncateMode::RoundUp(size) => fsize + fsize % size,
        }
    }
}

static ABOUT: &str = "Shrink or extend the size of each file to the specified size.";

pub mod options {
    pub static IO_BLOCKS: &str = "io-blocks";
    pub static NO_CREATE: &str = "no-create";
    pub static REFERENCE: &str = "reference";
    pub static SIZE: &str = "size";
    pub static ARG_FILES: &str = "files";
}

fn usage() -> String {
    format!("{0} [OPTION]... [FILE]...", uucore::execution_phrase())
}

fn get_long_usage() -> String {
    String::from(
        "
    SIZE is an integer with an optional prefix and optional unit.
    The available units (K, M, G, T, P, E, Z, and Y) use the following format:
        'KB' =>           1000 (kilobytes)
        'K'  =>           1024 (kibibytes)
        'MB' =>      1000*1000 (megabytes)
        'M'  =>      1024*1024 (mebibytes)
        'GB' => 1000*1000*1000 (gigabytes)
        'G'  => 1024*1024*1024 (gibibytes)
    SIZE may also be prefixed by one of the following to adjust the size of each
    file based on its current size:
        '+'  => extend by
        '-'  => reduce by
        '<'  => at most
        '>'  => at least
        '/'  => round down to multiple of
        '%'  => round up to multiple of",
    )
}

#[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
    let usage = usage();
    let long_usage = get_long_usage();

    let matches = uu_app()
        .override_usage(&usage[..])
        .after_help(&long_usage[..])
        .get_matches_from(args);

    let files: Vec<String> = matches
        .values_of(options::ARG_FILES)
        .map(|v| v.map(ToString::to_string).collect())
        .unwrap_or_default();

    if files.is_empty() {
        return Err(UUsageError::new(1, "missing file operand"));
    } else {
        let io_blocks = matches.is_present(options::IO_BLOCKS);
        let no_create = matches.is_present(options::NO_CREATE);
        let reference = matches.value_of(options::REFERENCE).map(String::from);
        let size = matches.value_of(options::SIZE).map(String::from);
        truncate(no_create, io_blocks, reference, size, files)
    }
}

pub fn uu_app<'a>() -> App<'a> {
    App::new(uucore::util_name())
        .version(crate_version!())
        .about(ABOUT)
        .arg(
            Arg::new(options::IO_BLOCKS)
            .short('o')
            .long(options::IO_BLOCKS)
            .help("treat SIZE as the number of I/O blocks of the file rather than bytes (NOT IMPLEMENTED)")
        )
        .arg(
            Arg::new(options::NO_CREATE)
            .short('c')
            .long(options::NO_CREATE)
            .help("do not create files that do not exist")
        )
        .arg(
            Arg::new(options::REFERENCE)
            .short('r')
            .long(options::REFERENCE)
            .required_unless_present(options::SIZE)
            .help("base the size of each file on the size of RFILE")
            .value_name("RFILE")
        )
        .arg(
            Arg::new(options::SIZE)
            .short('s')
            .long(options::SIZE)
            .required_unless_present(options::REFERENCE)
            .help("set or adjust the size of each file according to SIZE, which is in bytes unless --io-blocks is specified")
            .value_name("SIZE")
        )
        .arg(Arg::new(options::ARG_FILES)
             .value_name("FILE")
             .multiple_occurrences(true)
             .takes_value(true)
             .required(true)
             .min_values(1))
}

/// Truncate the named file to the specified size.
///
/// If `create` is true, then the file will be created if it does not
/// already exist. If `size` is larger than the number of bytes in the
/// file, then the file will be padded with zeros. If `size` is smaller
/// than the number of bytes in the file, then the file will be
/// truncated and any bytes beyond `size` will be lost.
///
/// # Errors
///
/// If the file could not be opened, or there was a problem setting the
/// size of the file.
fn file_truncate(filename: &str, create: bool, size: usize) -> std::io::Result<()> {
    let path = Path::new(filename);
    let f = OpenOptions::new().write(true).create(create).open(path)?;
    f.set_len(u64::try_from(size).unwrap())
}

/// Truncate files to a size relative to a given file.
///
/// `rfilename` is the name of the reference file.
///
/// `size_string` gives the size relative to the reference file to which
/// to set the target files. For example, "+3K" means "set each file to
/// be three kilobytes larger than the size of the reference file".
///
/// If `create` is true, then each file will be created if it does not
/// already exist.
///
/// # Errors
///
/// If the any file could not be opened, or there was a problem setting
/// the size of at least one file.
fn truncate_reference_and_size(
    rfilename: &str,
    size_string: &str,
    filenames: Vec<String>,
    create: bool,
) -> UResult<()> {
    let mode = match parse_mode_and_size(size_string) {
        Err(e) => return Err(USimpleError::new(1, format!("Invalid number: {}", e))),
        Ok(TruncateMode::Absolute(_)) => {
            return Err(USimpleError::new(
                1,
                String::from("you must specify a relative '--size' with '--reference'"),
            ))
        }
        Ok(m) => m,
    };
    let metadata = metadata(rfilename).map_err(|e| match e.kind() {
        ErrorKind::NotFound => USimpleError::new(
            1,
            format!(
                "cannot stat {}: No such file or directory",
                rfilename.quote()
            ),
        ),
        _ => e.map_err_context(String::new),
    })?;
    let fsize = metadata.len() as usize;
    let tsize = mode.to_size(fsize);
    for filename in &filenames {
        file_truncate(filename, create, tsize).map_err_context(String::new)?;
    }
    Ok(())
}

/// Truncate files to match the size of a given reference file.
///
/// `rfilename` is the name of the reference file.
///
/// If `create` is true, then each file will be created if it does not
/// already exist.
///
/// # Errors
///
/// If the any file could not be opened, or there was a problem setting
/// the size of at least one file.
fn truncate_reference_file_only(
    rfilename: &str,
    filenames: Vec<String>,
    create: bool,
) -> UResult<()> {
    let metadata = metadata(rfilename).map_err(|e| match e.kind() {
        ErrorKind::NotFound => USimpleError::new(
            1,
            format!(
                "cannot stat {}: No such file or directory",
                rfilename.quote()
            ),
        ),
        _ => e.map_err_context(String::new),
    })?;
    let tsize = metadata.len() as usize;
    for filename in &filenames {
        file_truncate(filename, create, tsize).map_err_context(String::new)?;
    }
    Ok(())
}

/// Truncate files to a specified size.
///
/// `size_string` gives either an absolute size or a relative size. A
/// relative size adjusts the size of each file relative to its current
/// size. For example, "3K" means "set each file to be three kilobytes"
/// whereas "+3K" means "set each file to be three kilobytes larger than
/// its current size".
///
/// If `create` is true, then each file will be created if it does not
/// already exist.
///
/// # Errors
///
/// If the any file could not be opened, or there was a problem setting
/// the size of at least one file.
fn truncate_size_only(size_string: &str, filenames: Vec<String>, create: bool) -> UResult<()> {
    let mode = parse_mode_and_size(size_string)
        .map_err(|e| USimpleError::new(1, format!("Invalid number: {}", e)))?;
    for filename in &filenames {
        let fsize = match metadata(filename) {
            Ok(m) => m.len(),
            Err(_) => 0,
        };
        let tsize = mode.to_size(fsize as usize);
        match file_truncate(filename, create, tsize) {
            Ok(_) => continue,
            Err(e) if e.kind() == ErrorKind::NotFound && !create => continue,
            Err(e) => return Err(e.map_err_context(String::new)),
        }
    }
    Ok(())
}

fn truncate(
    no_create: bool,
    _: bool,
    reference: Option<String>,
    size: Option<String>,
    filenames: Vec<String>,
) -> UResult<()> {
    let create = !no_create;
    // There are four possibilities
    // - reference file given and size given,
    // - reference file given but no size given,
    // - no reference file given but size given,
    // - no reference file given and no size given,
    match (reference, size) {
        (Some(rfilename), Some(size_string)) => {
            truncate_reference_and_size(&rfilename, &size_string, filenames, create)
        }
        (Some(rfilename), None) => truncate_reference_file_only(&rfilename, filenames, create),
        (None, Some(size_string)) => truncate_size_only(&size_string, filenames, create),
        (None, None) => unreachable!(), // this case cannot happen anymore because it's handled by clap
    }
}

/// Decide whether a character is one of the size modifiers, like '+' or '<'.
fn is_modifier(c: char) -> bool {
    c == '+' || c == '-' || c == '<' || c == '>' || c == '/' || c == '%'
}

/// Parse a size string with optional modifier symbol as its first character.
///
/// A size string is as described in [`parse_size`]. The first character
/// of `size_string` might be a modifier symbol, like `'+'` or
/// `'<'`. The first element of the pair returned by this function
/// indicates which modifier symbol was present, or
/// [`TruncateMode::Absolute`] if none.
///
/// # Panics
///
/// If `size_string` is empty, or if no number could be parsed from the
/// given string (for example, if the string were `"abc"`).
///
/// # Examples
///
/// ```rust,ignore
/// assert_eq!(parse_mode_and_size("+123"), (TruncateMode::Extend, 123));
/// ```
fn parse_mode_and_size(size_string: &str) -> Result<TruncateMode, ParseSizeError> {
    // Trim any whitespace.
    let mut size_string = size_string.trim();

    // Get the modifier character from the size string, if any. For
    // example, if the argument is "+123", then the modifier is '+'.
    if let Some(c) = size_string.chars().next() {
        if is_modifier(c) {
            size_string = &size_string[1..];
        }
        parse_size(size_string).map(match c {
            '+' => TruncateMode::Extend,
            '-' => TruncateMode::Reduce,
            '<' => TruncateMode::AtMost,
            '>' => TruncateMode::AtLeast,
            '/' => TruncateMode::RoundDown,
            '%' => TruncateMode::RoundUp,
            _ => TruncateMode::Absolute,
        })
    } else {
        Err(ParseSizeError::ParseFailure(size_string.to_string()))
    }
}

#[cfg(test)]
mod tests {
    use crate::parse_mode_and_size;
    use crate::TruncateMode;

    #[test]
    fn test_parse_mode_and_size() {
        assert_eq!(parse_mode_and_size("10"), Ok(TruncateMode::Absolute(10)));
        assert_eq!(parse_mode_and_size("+10"), Ok(TruncateMode::Extend(10)));
        assert_eq!(parse_mode_and_size("-10"), Ok(TruncateMode::Reduce(10)));
        assert_eq!(parse_mode_and_size("<10"), Ok(TruncateMode::AtMost(10)));
        assert_eq!(parse_mode_and_size(">10"), Ok(TruncateMode::AtLeast(10)));
        assert_eq!(parse_mode_and_size("/10"), Ok(TruncateMode::RoundDown(10)));
        assert_eq!(parse_mode_and_size("%10"), Ok(TruncateMode::RoundUp(10)));
    }
}
<|endoftext|>