<commit_before>//! User encryption keys are used alongside user encryption keys to
//! allow for authorized uploading of configuration and files to a
//! Habitat Supervisor network.
//!
//! User secret keys are used from workstations to encrypt
//! configuration and file rumors targeted to a specific service group
//! in a Habitat network. The service's public key must also be
//! present in order to encrypt.
//!
//! As encryption keys, this allows services to know who sent a given
//! encrypted rumor. It also allows operators to control who can send
//! such rumors by controlling which user public keys are present on a
//! Supervisor.
use crate::{crypto::keys::{encryption::{primitives,
                                        SignedBox,
                                        PUBLIC_BOX_KEY_VERSION,
                                        PUBLIC_KEY_SUFFIX,
                                        SECRET_BOX_KEY_SUFFIX,
                                        SECRET_BOX_KEY_VERSION},
                           Key,
                           NamedRevision,
                           ServicePublicEncryptionKey},
            error::{Error,
                    Result},
            fs::Permissions};

/// Given the name of a user, generate a new encryption key pair.
///
/// The resulting keys will need to be saved to a cache in order to
/// persist.
pub fn generate_user_encryption_key_pair(user_name: &str)
                                         -> (UserPublicEncryptionKey, UserSecretEncryptionKey) {
    let named_revision = NamedRevision::new(user_name.to_string());
    let (pk, sk) = primitives::gen_keypair();
    let public = UserPublicEncryptionKey { named_revision: named_revision.clone(),
                                           key:            pk, };
    let secret = UserSecretEncryptionKey { named_revision,
                                           key: sk };
    (public, secret)
}

////////////////////////////////////////////////////////////////////////

gen_key!(UserPublicEncryptionKey,
         key_material: primitives::PublicKey,
         file_format_version: PUBLIC_BOX_KEY_VERSION,
         file_extension: PUBLIC_KEY_SUFFIX,
         file_permissions: crate::fs::DEFAULT_PUBLIC_KEY_PERMISSIONS);

////////////////////////////////////////////////////////////////////////

gen_key!(UserSecretEncryptionKey,
         key_material: primitives::SecretKey,
         file_format_version: SECRET_BOX_KEY_VERSION,
         file_extension: SECRET_BOX_KEY_SUFFIX,
         file_permissions: crate::fs::DEFAULT_SECRET_KEY_PERMISSIONS);

impl UserSecretEncryptionKey {
    /// Encrypt some data with a user's private key for decryption by
    /// a receiving service's private key.
    pub fn encrypt_for_service(&self,
                               data: &[u8],
                               receiving_service: &ServicePublicEncryptionKey)
                               -> SignedBox {
        let nonce = primitives::gen_nonce();
        let ciphertext = primitives::seal(data, &nonce, receiving_service.key(), self.key());
        SignedBox::new(self.named_revision.clone(),
                       receiving_service.named_revision().clone(),
                       ciphertext,
                       nonce)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::crypto::test_support::fixture_key;

    #[test]
    fn encryption() {
        let service: ServicePublicEncryptionKey =
            fixture_key("keys/service-key-valid.default@acme-20160509181736.pub");
        let user: UserSecretEncryptionKey = fixture_key("keys/ruby-rhod-20200813204159.box.key");

        let message = "HOT, HOT, HAAAAAWWT!".to_string();

        let signed = user.encrypt_for_service(message.as_bytes(), &service);

        // Not a whole lot we can specifically test here, since the
        // ciphertext will be different each time. If we've got the
        // right sender and recipient, and haven't raised a panic
        // before now, assume we're good.
        assert_eq!(signed.encryptor(), user.named_revision());
        assert_eq!(signed.decryptor(), service.named_revision());
    }

    // Choosing to put the "round trip" encryption test over in
    // `service_key.rs`, since it involves both user and service, and
    // service is the one that does the decrypting.
}
<commit_msg>[PR FEEDBACK] Fix copy-paste documentation error<commit_after>//! User encryption keys are used alongside service encryption keys to
//! allow for authorized uploading of configuration and files to a
//! Habitat Supervisor network.
//!
//! User secret keys are used from workstations to encrypt
//! configuration and file rumors targeted to a specific service group
//! in a Habitat network. The service's public key must also be
//! present in order to encrypt.
//!
//! As encryption keys, this allows services to know who sent a given
//! encrypted rumor. It also allows operators to control who can send
//! such rumors by controlling which user public keys are present on a
//! Supervisor.
use crate::{crypto::keys::{encryption::{primitives,
                                        SignedBox,
                                        PUBLIC_BOX_KEY_VERSION,
                                        PUBLIC_KEY_SUFFIX,
                                        SECRET_BOX_KEY_SUFFIX,
                                        SECRET_BOX_KEY_VERSION},
                           Key,
                           NamedRevision,
                           ServicePublicEncryptionKey},
            error::{Error,
                    Result},
            fs::Permissions};

/// Given the name of a user, generate a new encryption key pair.
///
/// The resulting keys will need to be saved to a cache in order to
/// persist.
pub fn generate_user_encryption_key_pair(user_name: &str)
                                         -> (UserPublicEncryptionKey, UserSecretEncryptionKey) {
    let named_revision = NamedRevision::new(user_name.to_string());
    let (pk, sk) = primitives::gen_keypair();
    let public = UserPublicEncryptionKey { named_revision: named_revision.clone(),
                                           key:            pk, };
    let secret = UserSecretEncryptionKey { named_revision,
                                           key: sk };
    (public, secret)
}

////////////////////////////////////////////////////////////////////////

gen_key!(UserPublicEncryptionKey,
         key_material: primitives::PublicKey,
         file_format_version: PUBLIC_BOX_KEY_VERSION,
         file_extension: PUBLIC_KEY_SUFFIX,
         file_permissions: crate::fs::DEFAULT_PUBLIC_KEY_PERMISSIONS);

////////////////////////////////////////////////////////////////////////

gen_key!(UserSecretEncryptionKey,
         key_material: primitives::SecretKey,
         file_format_version: SECRET_BOX_KEY_VERSION,
         file_extension: SECRET_BOX_KEY_SUFFIX,
         file_permissions: crate::fs::DEFAULT_SECRET_KEY_PERMISSIONS);

impl UserSecretEncryptionKey {
    /// Encrypt some data with a user's private key for decryption by
    /// a receiving service's private key.
    pub fn encrypt_for_service(&self,
                               data: &[u8],
                               receiving_service: &ServicePublicEncryptionKey)
                               -> SignedBox {
        let nonce = primitives::gen_nonce();
        let ciphertext = primitives::seal(data, &nonce, receiving_service.key(), self.key());
        SignedBox::new(self.named_revision.clone(),
                       receiving_service.named_revision().clone(),
                       ciphertext,
                       nonce)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::crypto::test_support::fixture_key;

    #[test]
    fn encryption() {
        let service: ServicePublicEncryptionKey =
            fixture_key("keys/service-key-valid.default@acme-20160509181736.pub");
        let user: UserSecretEncryptionKey = fixture_key("keys/ruby-rhod-20200813204159.box.key");

        let message = "HOT, HOT, HAAAAAWWT!".to_string();

        let signed = user.encrypt_for_service(message.as_bytes(), &service);

        // Not a whole lot we can specifically test here, since the
        // ciphertext will be different each time. If we've got the
        // right sender and recipient, and haven't raised a panic
        // before now, assume we're good.
        assert_eq!(signed.encryptor(), user.named_revision());
        assert_eq!(signed.decryptor(), service.named_revision());
    }

    // Choosing to put the "round trip" encryption test over in
    // `service_key.rs`, since it involves both user and service, and
    // service is the one that does the decrypting.
}
<|endoftext|>


<commit_before>//! `AstTransformer`s are functions that replace nodes in an AST and can be easily combined.
use rustc_hash::FxHashMap;

use hir::{PathResolution, SemanticsScope};
use ra_ide_db::RootDatabase;
use ra_syntax::ast::{self, AstNode};

pub trait AstTransform<'a> {
    fn get_substitution(&self, node: &ra_syntax::SyntaxNode) -> Option<ra_syntax::SyntaxNode>;

    fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a>;
    fn or<T: AstTransform<'a> + 'a>(self, other: T) -> Box<dyn AstTransform<'a> + 'a>
    where
        Self: Sized + 'a,
    {
        self.chain_before(Box::new(other))
    }
}

struct NullTransformer;

impl<'a> AstTransform<'a> for NullTransformer {
    fn get_substitution(&self, _node: &ra_syntax::SyntaxNode) -> Option<ra_syntax::SyntaxNode> {
        None
    }
    fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a> {
        other
    }
}

pub struct SubstituteTypeParams<'a> {
    source_scope: &'a SemanticsScope<'a, RootDatabase>,
    substs: FxHashMap<hir::TypeParam, ast::TypeRef>,
    previous: Box<dyn AstTransform<'a> + 'a>,
}

impl<'a> SubstituteTypeParams<'a> {
    pub fn for_trait_impl(
        source_scope: &'a SemanticsScope<'a, RootDatabase>,
        db: &'a RootDatabase,
        // FIXME: there's implicit invariant that `trait_` and  `source_scope` match...
        trait_: hir::Trait,
        impl_def: ast::ImplDef,
    ) -> SubstituteTypeParams<'a> {
        let substs = get_syntactic_substs(impl_def).unwrap_or_default();
        let generic_def: hir::GenericDef = trait_.into();
        let substs_by_param: FxHashMap<_, _> = generic_def
            .params(db)
            .into_iter()
            // this is a trait impl, so we need to skip the first type parameter -- this is a bit hacky
            .skip(1)
            .zip(substs.into_iter())
            .collect();
        return SubstituteTypeParams {
            source_scope,
            substs: substs_by_param,
            previous: Box::new(NullTransformer),
        };

        // FIXME: It would probably be nicer if we could get this via HIR (i.e. get the
        // trait ref, and then go from the types in the substs back to the syntax)
        fn get_syntactic_substs(impl_def: ast::ImplDef) -> Option<Vec<ast::TypeRef>> {
            let target_trait = impl_def.target_trait()?;
            let path_type = match target_trait {
                ast::TypeRef::PathType(path) => path,
                _ => return None,
            };
            let type_arg_list = path_type.path()?.segment()?.type_arg_list()?;
            let mut result = Vec::new();
            for type_arg in type_arg_list.type_args() {
                let type_arg: ast::TypeArg = type_arg;
                result.push(type_arg.type_ref()?);
            }
            Some(result)
        }
    }
    fn get_substitution_inner(
        &self,
        node: &ra_syntax::SyntaxNode,
    ) -> Option<ra_syntax::SyntaxNode> {
        let type_ref = ast::TypeRef::cast(node.clone())?;
        let path = match &type_ref {
            ast::TypeRef::PathType(path_type) => path_type.path()?,
            _ => return None,
        };
        let path = hir::Path::from_ast(path)?;
        let resolution = self.source_scope.resolve_hir_path(&path)?;
        match resolution {
            hir::PathResolution::TypeParam(tp) => Some(self.substs.get(&tp)?.syntax().clone()),
            _ => None,
        }
    }
}

impl<'a> AstTransform<'a> for SubstituteTypeParams<'a> {
    fn get_substitution(&self, node: &ra_syntax::SyntaxNode) -> Option<ra_syntax::SyntaxNode> {
        self.get_substitution_inner(node).or_else(|| self.previous.get_substitution(node))
    }
    fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a> {
        Box::new(SubstituteTypeParams { previous: other, ..self })
    }
}

pub struct QualifyPaths<'a> {
    target_scope: &'a SemanticsScope<'a, RootDatabase>,
    source_scope: &'a SemanticsScope<'a, RootDatabase>,
    db: &'a RootDatabase,
    previous: Box<dyn AstTransform<'a> + 'a>,
}

impl<'a> QualifyPaths<'a> {
    pub fn new(
        target_scope: &'a SemanticsScope<'a, RootDatabase>,
        source_scope: &'a SemanticsScope<'a, RootDatabase>,
        db: &'a RootDatabase,
    ) -> Self {
        Self { target_scope, source_scope, db, previous: Box::new(NullTransformer) }
    }

    fn get_substitution_inner(
        &self,
        node: &ra_syntax::SyntaxNode,
    ) -> Option<ra_syntax::SyntaxNode> {
        // FIXME handle value ns?
        let from = self.target_scope.module()?;
        let p = ast::Path::cast(node.clone())?;
        if p.segment().and_then(|s| s.param_list()).is_some() {
            // don't try to qualify `Fn(Foo) -> Bar` paths, they are in prelude anyway
            return None;
        }
        let hir_path = hir::Path::from_ast(p.clone());
        let resolution = self.source_scope.resolve_hir_path(&hir_path?)?;
        match resolution {
            PathResolution::Def(def) => {
                let found_path = from.find_use_path(self.db, def)?;
                let mut path = path_to_ast(found_path);

                let type_args = p
                    .segment()
                    .and_then(|s| s.type_arg_list())
                    .map(|arg_list| apply(self, arg_list));
                if let Some(type_args) = type_args {
                    let last_segment = path.segment().unwrap();
                    path = path.with_segment(last_segment.with_type_args(type_args))
                }

                Some(path.syntax().clone())
            }
            PathResolution::Local(_)
            | PathResolution::TypeParam(_)
            | PathResolution::SelfType(_) => None,
            PathResolution::Macro(_) => None,
            PathResolution::AssocItem(_) => None,
        }
    }
}

pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: N) -> N {
    let syntax = node.syntax();
    let result = ra_syntax::algo::replace_descendants(syntax, |element| match element {
        ra_syntax::SyntaxElement::Node(n) => {
            let replacement = transformer.get_substitution(&n)?;
            Some(replacement.into())
        }
        _ => None,
    });
    N::cast(result).unwrap()
}

impl<'a> AstTransform<'a> for QualifyPaths<'a> {
    fn get_substitution(&self, node: &ra_syntax::SyntaxNode) -> Option<ra_syntax::SyntaxNode> {
        self.get_substitution_inner(node).or_else(|| self.previous.get_substitution(node))
    }
    fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a> {
        Box::new(QualifyPaths { previous: other, ..self })
    }
}

pub(crate) fn path_to_ast(path: hir::ModPath) -> ast::Path {
    let parse = ast::SourceFile::parse(&path.to_string());
    parse.tree().syntax().descendants().find_map(ast::Path::cast).unwrap()
}
<commit_msg>One more assert<commit_after>//! `AstTransformer`s are functions that replace nodes in an AST and can be easily combined.
use rustc_hash::FxHashMap;

use hir::{PathResolution, SemanticsScope};
use ra_ide_db::RootDatabase;
use ra_syntax::ast::{self, AstNode};

pub trait AstTransform<'a> {
    fn get_substitution(&self, node: &ra_syntax::SyntaxNode) -> Option<ra_syntax::SyntaxNode>;

    fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a>;
    fn or<T: AstTransform<'a> + 'a>(self, other: T) -> Box<dyn AstTransform<'a> + 'a>
    where
        Self: Sized + 'a,
    {
        self.chain_before(Box::new(other))
    }
}

struct NullTransformer;

impl<'a> AstTransform<'a> for NullTransformer {
    fn get_substitution(&self, _node: &ra_syntax::SyntaxNode) -> Option<ra_syntax::SyntaxNode> {
        None
    }
    fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a> {
        other
    }
}

pub struct SubstituteTypeParams<'a> {
    source_scope: &'a SemanticsScope<'a, RootDatabase>,
    substs: FxHashMap<hir::TypeParam, ast::TypeRef>,
    previous: Box<dyn AstTransform<'a> + 'a>,
}

impl<'a> SubstituteTypeParams<'a> {
    pub fn for_trait_impl(
        source_scope: &'a SemanticsScope<'a, RootDatabase>,
        db: &'a RootDatabase,
        // FIXME: there's implicit invariant that `trait_` and  `source_scope` match...
        trait_: hir::Trait,
        impl_def: ast::ImplDef,
    ) -> SubstituteTypeParams<'a> {
        let substs = get_syntactic_substs(impl_def).unwrap_or_default();
        let generic_def: hir::GenericDef = trait_.into();
        let substs_by_param: FxHashMap<_, _> = generic_def
            .params(db)
            .into_iter()
            // this is a trait impl, so we need to skip the first type parameter -- this is a bit hacky
            .skip(1)
            .zip(substs.into_iter())
            .collect();
        return SubstituteTypeParams {
            source_scope,
            substs: substs_by_param,
            previous: Box::new(NullTransformer),
        };

        // FIXME: It would probably be nicer if we could get this via HIR (i.e. get the
        // trait ref, and then go from the types in the substs back to the syntax)
        fn get_syntactic_substs(impl_def: ast::ImplDef) -> Option<Vec<ast::TypeRef>> {
            let target_trait = impl_def.target_trait()?;
            let path_type = match target_trait {
                ast::TypeRef::PathType(path) => path,
                _ => return None,
            };
            let type_arg_list = path_type.path()?.segment()?.type_arg_list()?;
            let mut result = Vec::new();
            for type_arg in type_arg_list.type_args() {
                let type_arg: ast::TypeArg = type_arg;
                result.push(type_arg.type_ref()?);
            }
            Some(result)
        }
    }
    fn get_substitution_inner(
        &self,
        node: &ra_syntax::SyntaxNode,
    ) -> Option<ra_syntax::SyntaxNode> {
        let type_ref = ast::TypeRef::cast(node.clone())?;
        let path = match &type_ref {
            ast::TypeRef::PathType(path_type) => path_type.path()?,
            _ => return None,
        };
        let path = hir::Path::from_ast(path)?;
        let resolution = self.source_scope.resolve_hir_path(&path)?;
        match resolution {
            hir::PathResolution::TypeParam(tp) => Some(self.substs.get(&tp)?.syntax().clone()),
            _ => None,
        }
    }
}

impl<'a> AstTransform<'a> for SubstituteTypeParams<'a> {
    fn get_substitution(&self, node: &ra_syntax::SyntaxNode) -> Option<ra_syntax::SyntaxNode> {
        self.get_substitution_inner(node).or_else(|| self.previous.get_substitution(node))
    }
    fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a> {
        Box::new(SubstituteTypeParams { previous: other, ..self })
    }
}

pub struct QualifyPaths<'a> {
    target_scope: &'a SemanticsScope<'a, RootDatabase>,
    source_scope: &'a SemanticsScope<'a, RootDatabase>,
    db: &'a RootDatabase,
    previous: Box<dyn AstTransform<'a> + 'a>,
}

impl<'a> QualifyPaths<'a> {
    pub fn new(
        target_scope: &'a SemanticsScope<'a, RootDatabase>,
        source_scope: &'a SemanticsScope<'a, RootDatabase>,
        db: &'a RootDatabase,
    ) -> Self {
        Self { target_scope, source_scope, db, previous: Box::new(NullTransformer) }
    }

    fn get_substitution_inner(
        &self,
        node: &ra_syntax::SyntaxNode,
    ) -> Option<ra_syntax::SyntaxNode> {
        // FIXME handle value ns?
        let from = self.target_scope.module()?;
        let p = ast::Path::cast(node.clone())?;
        if p.segment().and_then(|s| s.param_list()).is_some() {
            // don't try to qualify `Fn(Foo) -> Bar` paths, they are in prelude anyway
            return None;
        }
        let hir_path = hir::Path::from_ast(p.clone());
        let resolution = self.source_scope.resolve_hir_path(&hir_path?)?;
        match resolution {
            PathResolution::Def(def) => {
                let found_path = from.find_use_path(self.db, def)?;
                let mut path = path_to_ast(found_path);

                let type_args = p
                    .segment()
                    .and_then(|s| s.type_arg_list())
                    .map(|arg_list| apply(self, arg_list));
                if let Some(type_args) = type_args {
                    let last_segment = path.segment().unwrap();
                    path = path.with_segment(last_segment.with_type_args(type_args))
                }

                Some(path.syntax().clone())
            }
            PathResolution::Local(_)
            | PathResolution::TypeParam(_)
            | PathResolution::SelfType(_) => None,
            PathResolution::Macro(_) => None,
            PathResolution::AssocItem(_) => None,
        }
    }
}

pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: N) -> N {
    let syntax = node.syntax();
    let result = ra_syntax::algo::replace_descendants(syntax, |element| match element {
        ra_syntax::SyntaxElement::Node(n) => {
            let replacement = transformer.get_substitution(&n)?;
            Some(replacement.into())
        }
        _ => None,
    });
    N::cast(result).unwrap()
}

impl<'a> AstTransform<'a> for QualifyPaths<'a> {
    fn get_substitution(&self, node: &ra_syntax::SyntaxNode) -> Option<ra_syntax::SyntaxNode> {
        self.get_substitution_inner(node).or_else(|| self.previous.get_substitution(node))
    }
    fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a> {
        Box::new(QualifyPaths { previous: other, ..self })
    }
}

pub(crate) fn path_to_ast(path: hir::ModPath) -> ast::Path {
    let parse = ast::SourceFile::parse(&path.to_string());
    parse
        .tree()
        .syntax()
        .descendants()
        .find_map(ast::Path::cast)
        .unwrap_or_else(|| panic!("failed to parse path {:?}, `{}`", path, path))
}
<|endoftext|>


<commit_before>use std::collections::BTreeMap;
use std::str::FromStr;

use analysis::functions::Info as FuncInfo;
use analysis::functions::Visibility;
use analysis::imports::Imports;

#[derive(Clone, Copy, Eq, Debug, Ord, PartialEq, PartialOrd)]
pub enum Type {
    Compare,
    Copy,
    Equal,
    Free,
    Ref,
    ToString,
    Unref,
}

impl FromStr for Type {
    type Err = ();

    fn from_str(s: &str) -> Result<Type, ()> {
        use self::Type::*;
        match s {
            "compare" => Ok(Compare),
            "copy" => Ok(Copy),
            "equal" => Ok(Equal),
            "free" => Ok(Free),
            "is_equal" => Ok(Equal),
            "ref" => Ok(Ref),
            "to_string" => Ok(ToString),
            "unref" => Ok(Unref),
            _ => Err(()),
        }
    }
}

pub type Infos = BTreeMap<Type, String>; //Type => glib_name

pub fn extract(functions: &mut Vec<FuncInfo>) -> Infos {
    let mut specials = BTreeMap::new();

    for func in functions.iter_mut() {
        if let Ok(type_) = Type::from_str(&func.name) {
            if func.visibility != Visibility::Comment {
                func.visibility = visibility(type_);
            }
            // I assume `to_string` functions never return `NULL`
            if type_ == Type::ToString {
                if let Some(par) = func.ret.parameter.as_mut() {
                    *par.nullable = false;
                }
            }
            specials.insert(type_, func.glib_name.clone());
        }
    }

    specials
}

fn visibility(t: Type) -> Visibility {
    use self::Type::*;
    match t {
        Copy |
            Free |
            Ref |
            Unref => Visibility::Hidden,
        Compare |
            Equal |
            ToString => Visibility::Private,
    }
}

// Some special functions (e.g. `copy` on refcounted types) should be exposed
pub fn unhide(functions: &mut Vec<FuncInfo>, specials: &Infos, type_: Type) {
    if let Some(func) = specials.get(&type_) {
        let func = functions.iter_mut()
            .filter(|f| f.glib_name == *func && f.visibility != Visibility::Comment)
            .next();
        if let Some(func) = func {
            func.visibility = Visibility::Public;
        }
    }
}

pub fn analyze_imports(specials: &Infos, imports: &mut Imports) {
    use self::Type::*;
    for type_ in specials.keys() {
        match *type_ {
            Compare => imports.add("std::cmp", None),
            ToString => imports.add("std::fmt", None),
            _ => {}
        }
    }
}
<commit_msg>Detect mangled 'ref' method name<commit_after>use std::collections::BTreeMap;
use std::str::FromStr;

use analysis::functions::Info as FuncInfo;
use analysis::functions::Visibility;
use analysis::imports::Imports;

#[derive(Clone, Copy, Eq, Debug, Ord, PartialEq, PartialOrd)]
pub enum Type {
    Compare,
    Copy,
    Equal,
    Free,
    Ref,
    ToString,
    Unref,
}

impl FromStr for Type {
    type Err = ();

    fn from_str(s: &str) -> Result<Type, ()> {
        use self::Type::*;
        match s {
            "compare" => Ok(Compare),
            "copy" => Ok(Copy),
            "equal" => Ok(Equal),
            "free" => Ok(Free),
            "is_equal" => Ok(Equal),
            "ref" | "ref_" => Ok(Ref),
            "to_string" => Ok(ToString),
            "unref" => Ok(Unref),
            _ => Err(()),
        }
    }
}

pub type Infos = BTreeMap<Type, String>; //Type => glib_name

pub fn extract(functions: &mut Vec<FuncInfo>) -> Infos {
    let mut specials = BTreeMap::new();

    for func in functions.iter_mut() {
        if let Ok(type_) = Type::from_str(&func.name) {
            if func.visibility != Visibility::Comment {
                func.visibility = visibility(type_);
            }
            // I assume `to_string` functions never return `NULL`
            if type_ == Type::ToString {
                if let Some(par) = func.ret.parameter.as_mut() {
                    *par.nullable = false;
                }
            }
            specials.insert(type_, func.glib_name.clone());
        }
    }

    specials
}

fn visibility(t: Type) -> Visibility {
    use self::Type::*;
    match t {
        Copy |
            Free |
            Ref |
            Unref => Visibility::Hidden,
        Compare |
            Equal |
            ToString => Visibility::Private,
    }
}

// Some special functions (e.g. `copy` on refcounted types) should be exposed
pub fn unhide(functions: &mut Vec<FuncInfo>, specials: &Infos, type_: Type) {
    if let Some(func) = specials.get(&type_) {
        let func = functions.iter_mut()
            .filter(|f| f.glib_name == *func && f.visibility != Visibility::Comment)
            .next();
        if let Some(func) = func {
            func.visibility = Visibility::Public;
        }
    }
}

pub fn analyze_imports(specials: &Infos, imports: &mut Imports) {
    use self::Type::*;
    for type_ in specials.keys() {
        match *type_ {
            Compare => imports.add("std::cmp", None),
            ToString => imports.add("std::fmt", None),
            _ => {}
        }
    }
}
<|endoftext|>


<commit_before>use std::collections::VecDeque;

use bytes::{Buf, Bytes};

use futures::future;
use futures::stream;
use futures::stream::Stream;
use futures::stream::StreamExt;

use crate::error::*;
use crate::proto::grpc_frame_parser::GrpcFrameParser;
use crate::result;
use futures::task::Context;
use httpbis::DataOrTrailers;
use httpbis::HttpStreamAfterHeaders;
use std::pin::Pin;
use std::task::Poll;

fn write_u32_be(v: u32) -> [u8; 4] {
    [
        (v >> 24) as u8,
        (v >> 16) as u8,
        (v >> 8) as u8,
        (v >> 0) as u8,
    ]
}

pub const GRPC_HEADER_LEN: usize = 5;

/// Return frame len
pub fn parse_grpc_frame_0(stream: &[u8]) -> result::Result<Option<usize>> {
    let mut stream_copy = stream;
    let len = match parse_grpc_frame_header(&mut stream_copy)? {
        Some(len) => len,
        None => return Ok(None),
    };

    let end = len as usize + GRPC_HEADER_LEN;
    if end > stream.len() {
        return Ok(None);
    }

    Ok(Some(len as usize))
}

pub fn parse_grpc_frame_header<B: Buf>(stream: &mut B) -> result::Result<Option<u32>> {
    if stream.remaining() < GRPC_HEADER_LEN {
        return Ok(None);
    }

    let compressed = match stream.get_u8() {
        0 => false,
        1 => true,
        _ => return Err(Error::Other("unknown compression flag")),
    };
    if compressed {
        return Err(Error::Other("compression is not implemented"));
    }
    Ok(Some(stream.get_u32()))
}

// return message and size consumed
pub fn parse_grpc_frame(stream: &[u8]) -> result::Result<Option<(&[u8], usize)>> {
    parse_grpc_frame_0(stream).map(|o| {
        o.map(|len| {
            (
                &stream[GRPC_HEADER_LEN..len + GRPC_HEADER_LEN],
                len + GRPC_HEADER_LEN,
            )
        })
    })
}

pub fn parse_grpc_frame_from_bytes(stream: &mut Bytes) -> result::Result<Option<Bytes>> {
    if let Some(len) = parse_grpc_frame_0(&stream)? {
        let r = stream.slice(GRPC_HEADER_LEN..len + GRPC_HEADER_LEN);
        stream.advance(len + GRPC_HEADER_LEN);
        Ok(Some(r))
    } else {
        Ok(None)
    }
}

pub fn parse_grpc_frames_completely(stream: &[u8]) -> result::Result<Vec<&[u8]>> {
    let mut r = Vec::new();
    let mut pos = 0;
    while pos < stream.len() {
        let frame_opt = parse_grpc_frame(&stream[pos..])?;
        match frame_opt {
            None => return Err(Error::Other("not complete frames")),
            Some((frame, len)) => {
                r.push(frame);
                pos += len;
            }
        }
    }
    Ok(r)
}

/// Encode data into grpc frame (add frame prefix)
pub fn write_grpc_frame_cb<F, E>(stream: &mut Vec<u8>, frame: F) -> Result<(), E>
where
    F: FnOnce(&mut Vec<u8>) -> Result<(), E>,
{
    stream.push(0); // compressed flag
    let size_pos = stream.len();
    stream.extend_from_slice(&[0, 0, 0, 0]); // len
    let frame_start = stream.len();
    frame(stream)?;
    let frame_size = stream.len() - frame_start;
    assert!(frame_size <= u32::max_value() as usize);
    stream[size_pos..size_pos + 4].copy_from_slice(&write_u32_be(frame_size as u32));
    Ok(())
}

/// Encode data into grpc frame (add frame prefix)
pub fn write_grpc_frame(stream: &mut Vec<u8>, frame: &[u8]) {
    assert!(frame.len() <= u32::max_value() as usize);
    stream.reserve(5 + frame.len());
    stream.push(0); // compressed flag
    stream.extend(&write_u32_be(frame.len() as u32));
    stream.extend(frame);
}

pub fn write_grpc_frame_to_vec(frame: &[u8]) -> Vec<u8> {
    let mut r = Vec::new();
    write_grpc_frame(&mut r, frame);
    r
}

trait RequestOrResponse {
    fn need_trailing_header() -> bool;
}

pub struct GrpcFrameFromHttpFramesStreamRequest {
    http_stream_stream: HttpStreamAfterHeaders,
    buf: GrpcFrameParser,
    parsed_frames: VecDeque<Bytes>,
    error: Option<stream::Once<future::Ready<crate::Result<Bytes>>>>,
}

impl GrpcFrameFromHttpFramesStreamRequest {
    pub fn _new(http_stream_stream: HttpStreamAfterHeaders) -> Self {
        GrpcFrameFromHttpFramesStreamRequest {
            http_stream_stream,
            buf: GrpcFrameParser::default(),
            parsed_frames: VecDeque::new(),
            error: None,
        }
    }
}

impl Stream for GrpcFrameFromHttpFramesStreamRequest {
    type Item = crate::Result<Bytes>;

    fn poll_next(
        mut self: Pin<&mut Self>,
        cx: &mut Context<'_>,
    ) -> Poll<Option<crate::Result<Bytes>>> {
        loop {
            if let Some(ref mut error) = self.error {
                return error.poll_next_unpin(cx);
            }

            let p_r = self.buf.next_frames();

            self.parsed_frames.extend(match p_r {
                Ok(r) => r,
                Err(e) => {
                    self.error = Some(stream::once(future::ready(Err(e))));
                    continue;
                }
            });

            if let Some(frame) = self.parsed_frames.pop_front() {
                return Poll::Ready(Some(Ok(frame)));
            }

            let part_opt = match self.http_stream_stream.poll_next_unpin(cx)? {
                Poll::Pending => return Poll::Pending,
                Poll::Ready(part_opt) => part_opt,
            };
            let part = match part_opt {
                None => match self.buf.check_empty() {
                    Ok(()) => return Poll::Ready(None),
                    Err(e) => {
                        self.error = Some(stream::once(future::err(e)));
                        continue;
                    }
                },
                Some(part) => part,
            };

            match part {
                // unexpected but OK
                DataOrTrailers::Trailers(..) => (),
                DataOrTrailers::Data(data, ..) => {
                    self.buf.enqueue(data);
                }
            }
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::bytesx::bytes_extend_from_slice;

    #[test]
    fn test_parse_grpc_frame() {
        assert_eq!(None, parse_grpc_frame(b"").unwrap());
        assert_eq!(None, parse_grpc_frame(b"1").unwrap());
        assert_eq!(None, parse_grpc_frame(b"14sc").unwrap());
        assert_eq!(
            None,
            parse_grpc_frame(b"\x00\x00\x00\x00\x07\x0a\x05wo").unwrap()
        );
        assert_eq!(
            Some((&b"\x0a\x05world"[..], 12)),
            parse_grpc_frame(b"\x00\x00\x00\x00\x07\x0a\x05world").unwrap()
        );
    }

    #[test]
    fn test_parse_grpc_frames_from_bytes() {
        fn t(r: &[&[u8]], input: &[u8], trail: &[u8]) {
            let mut b = Bytes::new();
            bytes_extend_from_slice(&mut b, input);
            bytes_extend_from_slice(&mut b, trail);

            let r: Vec<Bytes> = r.into_iter().map(|&s| Bytes::copy_from_slice(s)).collect();

            let rr = parse_grpc_frames_from_bytes(&mut b).unwrap();
            assert_eq!(r, rr);
            assert_eq!(trail, b.as_ref());
        }

        t(&[], &[], &[]);
        t(&[], &[], &b"1"[..]);
        t(&[], &[], &b"14sc"[..]);
        t(
            &[&b"\x0a\x05world"[..]],
            &b"\x00\x00\x00\x00\x07\x0a\x05world"[..],
            &[],
        );
        t(
            &[&b"ab"[..], &b"cde"[..]],
            &b"\0\x00\x00\x00\x02ab\0\x00\x00\x00\x03cde"[..],
            &b"\x00"[..],
        );
    }

    #[test]
    fn test_write_grpc_frame_cb() {
        let mut f = Vec::new();
        write_grpc_frame_cb(&mut f, |f| Ok::<_, ()>(f.extend_from_slice(&[0x17, 0x19]))).unwrap();
        assert_eq!(&b"\x00\x00\x00\x00\x02\x17\x19"[..], f.as_slice());
    }
}
<commit_msg>Fix test compilation<commit_after>use std::collections::VecDeque;

use bytes::{Buf, Bytes};

use futures::future;
use futures::stream;
use futures::stream::Stream;
use futures::stream::StreamExt;

use crate::error::*;
use crate::proto::grpc_frame_parser::GrpcFrameParser;
use crate::result;
use futures::task::Context;
use httpbis::DataOrTrailers;
use httpbis::HttpStreamAfterHeaders;
use std::pin::Pin;
use std::task::Poll;

fn write_u32_be(v: u32) -> [u8; 4] {
    [
        (v >> 24) as u8,
        (v >> 16) as u8,
        (v >> 8) as u8,
        (v >> 0) as u8,
    ]
}

pub const GRPC_HEADER_LEN: usize = 5;

/// Return frame len
pub fn parse_grpc_frame_0(stream: &[u8]) -> result::Result<Option<usize>> {
    let mut stream_copy = stream;
    let len = match parse_grpc_frame_header(&mut stream_copy)? {
        Some(len) => len,
        None => return Ok(None),
    };

    let end = len as usize + GRPC_HEADER_LEN;
    if end > stream.len() {
        return Ok(None);
    }

    Ok(Some(len as usize))
}

pub fn parse_grpc_frame_header<B: Buf>(stream: &mut B) -> result::Result<Option<u32>> {
    if stream.remaining() < GRPC_HEADER_LEN {
        return Ok(None);
    }

    let compressed = match stream.get_u8() {
        0 => false,
        1 => true,
        _ => return Err(Error::Other("unknown compression flag")),
    };
    if compressed {
        return Err(Error::Other("compression is not implemented"));
    }
    Ok(Some(stream.get_u32()))
}

// return message and size consumed
pub fn parse_grpc_frame(stream: &[u8]) -> result::Result<Option<(&[u8], usize)>> {
    parse_grpc_frame_0(stream).map(|o| {
        o.map(|len| {
            (
                &stream[GRPC_HEADER_LEN..len + GRPC_HEADER_LEN],
                len + GRPC_HEADER_LEN,
            )
        })
    })
}

pub fn parse_grpc_frame_from_bytes(stream: &mut Bytes) -> result::Result<Option<Bytes>> {
    if let Some(len) = parse_grpc_frame_0(&stream)? {
        let r = stream.slice(GRPC_HEADER_LEN..len + GRPC_HEADER_LEN);
        stream.advance(len + GRPC_HEADER_LEN);
        Ok(Some(r))
    } else {
        Ok(None)
    }
}

pub fn parse_grpc_frames_completely(stream: &[u8]) -> result::Result<Vec<&[u8]>> {
    let mut r = Vec::new();
    let mut pos = 0;
    while pos < stream.len() {
        let frame_opt = parse_grpc_frame(&stream[pos..])?;
        match frame_opt {
            None => return Err(Error::Other("not complete frames")),
            Some((frame, len)) => {
                r.push(frame);
                pos += len;
            }
        }
    }
    Ok(r)
}

/// Encode data into grpc frame (add frame prefix)
pub fn write_grpc_frame_cb<F, E>(stream: &mut Vec<u8>, frame: F) -> Result<(), E>
where
    F: FnOnce(&mut Vec<u8>) -> Result<(), E>,
{
    stream.push(0); // compressed flag
    let size_pos = stream.len();
    stream.extend_from_slice(&[0, 0, 0, 0]); // len
    let frame_start = stream.len();
    frame(stream)?;
    let frame_size = stream.len() - frame_start;
    assert!(frame_size <= u32::max_value() as usize);
    stream[size_pos..size_pos + 4].copy_from_slice(&write_u32_be(frame_size as u32));
    Ok(())
}

/// Encode data into grpc frame (add frame prefix)
pub fn write_grpc_frame(stream: &mut Vec<u8>, frame: &[u8]) {
    assert!(frame.len() <= u32::max_value() as usize);
    stream.reserve(5 + frame.len());
    stream.push(0); // compressed flag
    stream.extend(&write_u32_be(frame.len() as u32));
    stream.extend(frame);
}

pub fn write_grpc_frame_to_vec(frame: &[u8]) -> Vec<u8> {
    let mut r = Vec::new();
    write_grpc_frame(&mut r, frame);
    r
}

trait RequestOrResponse {
    fn need_trailing_header() -> bool;
}

pub struct GrpcFrameFromHttpFramesStreamRequest {
    http_stream_stream: HttpStreamAfterHeaders,
    buf: GrpcFrameParser,
    parsed_frames: VecDeque<Bytes>,
    error: Option<stream::Once<future::Ready<crate::Result<Bytes>>>>,
}

impl GrpcFrameFromHttpFramesStreamRequest {
    pub fn _new(http_stream_stream: HttpStreamAfterHeaders) -> Self {
        GrpcFrameFromHttpFramesStreamRequest {
            http_stream_stream,
            buf: GrpcFrameParser::default(),
            parsed_frames: VecDeque::new(),
            error: None,
        }
    }
}

impl Stream for GrpcFrameFromHttpFramesStreamRequest {
    type Item = crate::Result<Bytes>;

    fn poll_next(
        mut self: Pin<&mut Self>,
        cx: &mut Context<'_>,
    ) -> Poll<Option<crate::Result<Bytes>>> {
        loop {
            if let Some(ref mut error) = self.error {
                return error.poll_next_unpin(cx);
            }

            let p_r = self.buf.next_frames();

            self.parsed_frames.extend(match p_r {
                Ok(r) => r,
                Err(e) => {
                    self.error = Some(stream::once(future::ready(Err(e))));
                    continue;
                }
            });

            if let Some(frame) = self.parsed_frames.pop_front() {
                return Poll::Ready(Some(Ok(frame)));
            }

            let part_opt = match self.http_stream_stream.poll_next_unpin(cx)? {
                Poll::Pending => return Poll::Pending,
                Poll::Ready(part_opt) => part_opt,
            };
            let part = match part_opt {
                None => match self.buf.check_empty() {
                    Ok(()) => return Poll::Ready(None),
                    Err(e) => {
                        self.error = Some(stream::once(future::err(e)));
                        continue;
                    }
                },
                Some(part) => part,
            };

            match part {
                // unexpected but OK
                DataOrTrailers::Trailers(..) => (),
                DataOrTrailers::Data(data, ..) => {
                    self.buf.enqueue(data);
                }
            }
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::bytesx::bytes_extend_from_slice;

    fn parse_grpc_frames_from_bytes(stream: &mut Bytes) -> result::Result<Vec<Bytes>> {
        let mut r = Vec::new();
        loop {
            match parse_grpc_frame_from_bytes(stream)? {
                Some(bytes) => {
                    r.push(bytes);
                }
                None => {
                    return Ok(r);
                }
            }
        }
    }

    #[test]
    fn test_parse_grpc_frame() {
        assert_eq!(None, parse_grpc_frame(b"").unwrap());
        assert_eq!(None, parse_grpc_frame(b"1").unwrap());
        assert_eq!(None, parse_grpc_frame(b"14sc").unwrap());
        assert_eq!(
            None,
            parse_grpc_frame(b"\x00\x00\x00\x00\x07\x0a\x05wo").unwrap()
        );
        assert_eq!(
            Some((&b"\x0a\x05world"[..], 12)),
            parse_grpc_frame(b"\x00\x00\x00\x00\x07\x0a\x05world").unwrap()
        );
    }

    #[test]
    fn test_parse_grpc_frames_from_bytes() {
        fn t(r: &[&[u8]], input: &[u8], trail: &[u8]) {
            let mut b = Bytes::new();
            bytes_extend_from_slice(&mut b, input);
            bytes_extend_from_slice(&mut b, trail);

            let r: Vec<Bytes> = r.into_iter().map(|&s| Bytes::copy_from_slice(s)).collect();

            let rr = parse_grpc_frames_from_bytes(&mut b).unwrap();
            assert_eq!(r, rr);
            assert_eq!(trail, b.as_ref());
        }

        t(&[], &[], &[]);
        t(&[], &[], &b"1"[..]);
        t(&[], &[], &b"14sc"[..]);
        t(
            &[&b"\x0a\x05world"[..]],
            &b"\x00\x00\x00\x00\x07\x0a\x05world"[..],
            &[],
        );
        t(
            &[&b"ab"[..], &b"cde"[..]],
            &b"\0\x00\x00\x00\x02ab\0\x00\x00\x00\x03cde"[..],
            &b"\x00"[..],
        );
    }

    #[test]
    fn test_write_grpc_frame_cb() {
        let mut f = Vec::new();
        write_grpc_frame_cb(&mut f, |f| Ok::<_, ()>(f.extend_from_slice(&[0x17, 0x19]))).unwrap();
        assert_eq!(&b"\x00\x00\x00\x00\x02\x17\x19"[..], f.as_slice());
    }
}
<|endoftext|>


<commit_before>#![feature(plugin)]
#![plugin(clippy)]

fn get_number() -> usize {
    10
}

fn get_reference(n : &usize) -> &usize {
    n
}

#[allow(many_single_char_names)]
#[allow(unused_variables)]
#[deny(deref_addrof)]
fn main() {
    let a = 10;
    let aref = &a;

    let b = *&a;
    //~^ERROR immediately dereferencing a reference
    //~|HELP try this
    //~|SUGGESTION a

    let b = *&get_number();
    //~^ERROR immediately dereferencing a reference
    //~|HELP try this
    //~|SUGGESTION get_number()

    let b = *get_reference(&a);

    let bytes : Vec<usize> = vec![1, 2, 3, 4];
    let b = *&bytes[1..2][0];
    //~^ERROR immediately dereferencing a reference
    //~|HELP try this
    //~|SUGGESTION bytes[1..2][0]

    let b = *(&a);
    //~^ERROR immediately dereferencing a reference
    //~|HELP try this
    //~|SUGGESTION a

    let b = *&&a;
    //~^ERROR immediately dereferencing a reference
    //~|HELP try this
    //~|SUGGESTION &a

    let b = **&aref;
    //~^ERROR immediately dereferencing a reference
    //~|HELP try this
    //~|SUGGESTION aref

    //This produces a suggestion of 'let b = *&a;' which is still incorrect
    let b = **&&a;
    //~^ERROR immediately dereferencing a reference
    //~|HELP try this
    //~|SUGGESTION a

    {
        let mut x = 10;
        let y = *&mut x;
        //~^ERROR immediately dereferencing a reference
        //~|HELP try this
        //~|SUGGESTION x
    }

    {
        //This produces a suggestion of 'let y = *&mut x' which is still incorrect
        let mut x = 10;
        let y = **&mut &mut x;
        //~^ERROR immediately dereferencing a reference
        //~|HELP try this
        //~|SUGGESTION x
    }
}
<commit_msg>Make deref_addrof suggestions stricter<commit_after>#![feature(plugin)]
#![plugin(clippy)]

fn get_number() -> usize {
    10
}

fn get_reference(n : &usize) -> &usize {
    n
}

#[allow(many_single_char_names)]
#[allow(unused_variables)]
#[deny(deref_addrof)]
fn main() {
    let a = 10;
    let aref = &a;

    let b = *&a;
    //~^ERROR immediately dereferencing a reference
    //~|HELP try this
    //~|SUGGESTION let b = a;

    let b = *&get_number();
    //~^ERROR immediately dereferencing a reference
    //~|HELP try this
    //~|SUGGESTION let b = get_number();

    let b = *get_reference(&a);

    let bytes : Vec<usize> = vec![1, 2, 3, 4];
    let b = *&bytes[1..2][0];
    //~^ERROR immediately dereferencing a reference
    //~|HELP try this
    //~|SUGGESTION let b = bytes[1..2][0];

    let b = *(&a);
    //~^ERROR immediately dereferencing a reference
    //~|HELP try this
    //~|SUGGESTION let b = a;

    let b = *&&a;
    //~^ERROR immediately dereferencing a reference
    //~|HELP try this
    //~|SUGGESTION let b = &a;

    let b = **&aref;
    //~^ERROR immediately dereferencing a reference
    //~|HELP try this
    //~|SUGGESTION let b = *aref;

    //This produces a suggestion of 'let b = *&a;' which
    //will trigger the 'deref_addrof' lint again
    let b = **&&a;
    //~^ERROR immediately dereferencing a reference
    //~|HELP try this
    //~|SUGGESTION let b = *&a;

    {
        let mut x = 10;
        let y = *&mut x;
        //~^ERROR immediately dereferencing a reference
        //~|HELP try this
        //~|SUGGESTION let y = x;
    }

    {
        //This produces a suggestion of 'let y = *&mut x' which
        //will trigger the 'deref_addrof' lint again
        let mut x = 10;
        let y = **&mut &mut x;
        //~^ERROR immediately dereferencing a reference
        //~|HELP try this
        //~|SUGGESTION let y = *&mut x;
    }
}
<|endoftext|>


<commit_before>extern crate minifb;
extern crate bgfx_rs;
extern crate viewdock;

use bgfx_rs::Bgfx;
use libc::{c_void, c_int};
use minifb::{Scale, WindowOptions, MouseMode, MouseButton, Key, KeyRepeat};
use core::view_plugins::{ViewHandle, ViewPlugins, ViewInstance};
use core::session::{Sessions, Session, SessionHandle};
use core::reader_wrapper::ReaderWrapper;
use self::viewdock::{Workspace, Rect, Direction, DockHandle, SplitHandle};
use menu::{Menu, MENU_DEBUG_STEP_IN};
use imgui_sys::Imgui;
use prodbg_api::ui_ffi::{PDVec2};
use prodbg_api::view::CViewCallbacks;

const WIDTH: usize = 1280;
const HEIGHT: usize = 800;

enum State {
    Default,
    DraggingSlider,
}

pub struct MouseState {
    handle: Option<(SplitHandle, Direction)>,
    state: State,
    prev_mouse: (f32, f32)
}

impl MouseState {
    pub fn new() -> MouseState {
        MouseState {
            handle: None,
            state: State::Default,
            prev_mouse: (0.0, 0.0),
        }
    }
}

pub struct Window<'a> {
    /// minifb window
    pub win: minifb::Window,
    pub menu: Menu<'a>,

    /// Views in this window
    pub views: Vec<ViewHandle>,

    ///
    pub ws: Workspace,

    pub mouse_state: MouseState,
}

struct WindowState {
    pub showed_popup: u32,
    pub should_close: bool,
}

///! Windows keeps track of all different windows that are present with in the application
///! There are several ways windows can be created:
///!
///! 1. User opens a new window using a shortcut or menu selection.
///! 2. User "undocks" a view from an existing window giving it it's own floating window.
///! 3. etc

pub struct Windows<'a> {
    /// All the windows being tracked
    windows: Vec<Window<'a>>,
    current: usize,
}

impl<'a> Windows<'a> {
    pub fn new() -> Windows<'a> {
        Windows {
            windows: Vec::new(),
            current: 0,
        }
    }

    /// Create a default window which will only be created if there are no other
    pub fn create_default(&mut self) {
        if self.windows.len() > 0 {
            return;
        }

        let window = Self::create_window_with_menus().expect("Unable to create window");

        self.windows.push(window)
   }

    pub fn create_window(width: usize, height: usize) -> minifb::Result<Window<'a>> {
        let res = minifb::Window::new("ProDBG",
                                      width,
                                      height,
                                      WindowOptions {
                                          resize: true,
                                          scale: Scale::X1,
                                          ..WindowOptions::default()
                                      });
        match res {
            Ok(win) => {
                Bgfx::create_window(win.get_window_handle() as *const c_void,
                                    width as c_int,
                                    height as c_int);
                Ok(Window {
                    win: win,
                    menu: Menu::new(),
                    views: Vec::new(),
                    mouse_state: MouseState::new(),
                    ws: Workspace::new(Rect::new(0.0, 0.0, width as f32, (height - 20) as f32)).unwrap(),
                })
            }
            Err(err) => Err(err),
        }
    }

    pub fn create_window_with_menus() -> minifb::Result<Window<'a>> {
        let mut window = try!(Self::create_window(WIDTH, HEIGHT));

        // we ignore the results because we likely brake this on Linux otherwise
        // TODO: Figure out how to deal with this on Linux
        let _ = window.win.add_menu("File", &window.menu.file_menu);
        let _ = window.win.add_menu("Debug", &window.menu.debug_menu);

        Ok(window)
    }

    pub fn update(&mut self, sessions: &mut Sessions, view_plugins: &mut ViewPlugins) {
        for i in (0..self.windows.len()).rev() {
            self.windows[i].update(sessions, view_plugins);

            if !self.windows[i].win.is_open() {
                self.windows.swap_remove(i);
            }
        }
    }

    pub fn get_current(&mut self) -> &'a mut Window {
        let current = self.current;
        &mut self.windows[current]
    }

    /// Checks if application should exit (all window instances closed)
    pub fn should_exit(&self) -> bool {
        self.windows.len() == 0
    }

    /// Save the state of the windows (usually done when exiting the application)
    pub fn save(_filename: &str) {}

    /// Load the state of all the views from a previous run
    pub fn load(_filename: &str) {}
}

impl<'a> Window<'a> {
    fn is_inside(v: (f32, f32), pos: PDVec2, size: PDVec2) -> bool {
        let x0 = pos.x;
        let y0 = pos.y;
        let x1 = pos.x + size.x;
        let y1 = pos.y + size.y;

        if (v.0 >= x0 && v.0 < x1) && (v.1 >= y0 && v.1 < y1) {
            true
        } else {
            false
        }
    }

    fn update_view(&self, instance: &mut ViewInstance, session: &mut Session, show_context_menu: bool, mouse: (f32, f32)) -> WindowState {
        let ui = instance.ui;

        if let Some(rect) = self.ws.get_rect_by_handle(DockHandle(instance.handle.0)) {
            Imgui::set_window_pos(rect.x, rect.y);
            Imgui::set_window_size(rect.width, rect.height);
        }

        let open = Imgui::begin_window(&instance.name, true);
        Imgui::init_state(ui.api);

        let pos = ui.get_window_pos();
        let size = ui.get_window_size();

        if Self::is_inside(mouse, pos, size) && show_context_menu {
            Imgui::mark_show_popup(ui.api, true);
        } else {
            Imgui::mark_show_popup(ui.api, false);
        }

        // Make sure we move the cursor to the start of the stream here
        ReaderWrapper::reset_reader(&mut session.reader);

        unsafe {
            let plugin_funcs = instance.plugin_type.plugin_funcs as *mut CViewCallbacks;
            ((*plugin_funcs).update.unwrap())(instance.plugin_data,
                                                ui.api as *mut c_void,
                                                session.reader.api as *mut c_void,
                                                session.get_current_writer().api as *mut c_void);
        }

        let has_shown_menu = Imgui::has_showed_popup(ui.api);

        Imgui::end_window();

        WindowState {
            showed_popup: has_shown_menu,
            should_close: !open,
        }
    }

    pub fn remove_views(&mut self, view_plugins: &mut ViewPlugins, views: &Vec<ViewHandle>) {
        for view in views {
            view_plugins.destroy_instance(*view);
            self.ws.delete_by_handle(DockHandle(view.0));
        }
    }

    fn update_mouse_state(&mut self, mouse_pos: (f32, f32)) {
        match self.mouse_state.state {
            State::Default => {
                if let Some(h) = self.ws.is_hovering_sizer(mouse_pos) {
                    if h.1 == Direction::Vertical {
                        Bgfx::cursor_set_type(2);
                    } else {
                        Bgfx::cursor_set_type(1);
                    }

                    if self.win.get_mouse_down(MouseButton::Left) {
                        self.mouse_state.handle = Some(h);
                        self.mouse_state.state = State::DraggingSlider;
                    }
                } else {
                    Bgfx::cursor_set_type(0);
                }
            },

            State::DraggingSlider => {
                let pm = self.mouse_state.prev_mouse;
                let delta = (pm.0 - mouse_pos.0, pm.1 - mouse_pos.1);

                if self.win.get_mouse_down(MouseButton::Left) {
                    let t = self.mouse_state.handle.unwrap();
                    if t.1 == Direction::Vertical {
                        Bgfx::cursor_set_type(2);
                    } else {
                        Bgfx::cursor_set_type(1);
                    }
                    self.ws.drag_sizer(t.0, delta);
                } else {
                    self.mouse_state.handle = None;
                    self.mouse_state.state = State::Default;
                    Bgfx::cursor_set_type(0);
                }
            }
        }

        /*
        if let Some(handle) = ws.is_hovering_sizer(mouse_pos) {
            let delta = (prev_mouse.0 - mouse_pos.0, prev_mouse.1 - mouse_pos.1);

            if window.get_mouse_down(MouseButton::Left) {
                println!("drangging sizer");
                ws.drag_sizer(handle, delta);
            }
        }
        */

        self.mouse_state.prev_mouse = mouse_pos;
    }

    pub fn update(&mut self, sessions: &mut Sessions, view_plugins: &mut ViewPlugins) {
        let mut views_to_delete = Vec::new();
        let mut has_shown_menu = 0u32;

        self.win.update();
        self.ws.update();

        let mouse = self.win.get_mouse_pos(MouseMode::Clamp).unwrap_or((0.0, 0.0));

        self.update_mouse_state(mouse);

        Bgfx::set_mouse_pos(mouse);
        Bgfx::set_mouse_state(0, self.win.get_mouse_down(MouseButton::Left));

        let show_context_menu = self.win.get_mouse_down(MouseButton::Right);

        for view in &self.views {
            if let Some(ref mut v) = view_plugins.get_view(*view) {
                if let Some(ref mut s) = sessions.get_session(v.session_handle) {

                    // TODO: Only do this on the correct session

                    self.win.is_menu_pressed().map(|menu_id| {
                        if menu_id == MENU_DEBUG_STEP_IN {
                            println!("action_step");
                            s.action_step();
                        }
                    });

                    let state = Self::update_view(self, v, s, show_context_menu, mouse);

                    if state.should_close {
                        views_to_delete.push(*view);
                    }

                    has_shown_menu |= state.showed_popup;
                }
            }
        }

        if self.win.is_key_pressed(Key::Down, KeyRepeat::No) {
            self.ws.dump_tree();
        }

        if self.win.is_key_pressed(Key::Up, KeyRepeat::No) {
            self.save_layout("data/layout_temp.json", view_plugins);
        }

        if self.win.is_key_pressed(Key::Right, KeyRepeat::No) {
            self.load_layout("data/layout_temp.json", view_plugins);
        }

        // test

        // if now plugin has showed a menu we do it here
        // TODO: Handle diffrent cases when attach menu on to plugin menu or not

        if has_shown_menu == 0 && show_context_menu {
            Self::show_popup(self, true, mouse, view_plugins);
        } else {
            Self::show_popup(self, false, mouse, view_plugins);
        }

        Self::remove_views(self, view_plugins, &views_to_delete);
    }

    fn split_view(&mut self, name: &String, view_plugins: &mut ViewPlugins, pos: (f32, f32), direction: Direction) {
        let ui = Imgui::create_ui_instance();
        if let Some(handle) = view_plugins.create_instance(ui, name, SessionHandle(0)) {
            if let Some(dock_handle) = self.ws.get_hover_dock(pos) {
                let new_handle = DockHandle(handle.0);
                self.ws.split_by_dock_handle(direction, dock_handle, new_handle);
                //self.ws.set_name_to_handle(name, new_handle);
                //self.ws.dump_tree_linear();
            } else {
                self.ws.new_split(DockHandle(handle.0), direction);
                //println!("no split");
            }

            self.views.push(handle);
        }
    }

    fn show_popup(&mut self, show: bool, mouse_pos: (f32, f32), view_plugins: &mut ViewPlugins) {
        let ui = Imgui::get_ui();

        if show {
            ui.open_popup("plugins");
        }

        if ui.begin_popup("plugins") {
            let plugin_names = view_plugins.get_plugin_names();

            if ui.begin_menu("Split Horizontally", true) {
                for name in &plugin_names {
                    if ui.menu_item(name, false, true) {
                        Self::split_view(self, &name, view_plugins, mouse_pos, Direction::Horizontal);
                    }
                }
                ui.end_menu();
            }

            if ui.begin_menu("Split Vertically", true) {
                for name in &plugin_names {
                    if ui.menu_item(name, false, true) {
                        Self::split_view(self, &name, view_plugins, mouse_pos, Direction::Vertical);
                    }
                }
                ui.end_menu();
            }

            ui.end_popup();
        }
    }

    fn save_layout(&mut self, filename: &str, view_plugins: &mut ViewPlugins) {
        for split in &mut self.ws.splits {
            let iter = split.left_docks.docks.iter_mut().chain(split.right_docks.docks.iter_mut());

            for dock in iter {
                if let Some(ref plugin) = view_plugins.get_view(ViewHandle(dock.handle.0)) {
                    let (plugin_name, data) = plugin.get_plugin_data();
                    dock.plugin_name = plugin_name;
                    dock.plugin_data = data;
                } else {
                    println!("Unable to find plugin for {:?} - this should never happen", dock);
                }
            }
        }

        let _ = self.ws.save(filename);
    }

    fn load_layout(&mut self, filename: &str, view_plugins: &mut ViewPlugins) {
        let ws = Workspace::load(filename);
        let docks = ws.get_docks();
        self.views.clear();

        for dock in &docks {
            let ui = Imgui::create_ui_instance();
            let handle = ViewHandle(dock.handle.0);
            view_plugins.create_instance_with_handle(ui, &dock.plugin_name, &dock.plugin_data, SessionHandle(0), ViewHandle(dock.handle.0));
            self.views.push(handle);
        }

        self.ws = ws;
    }
}
<commit_msg>Show "new view" menu if there are no splits<commit_after>extern crate minifb;
extern crate bgfx_rs;
extern crate viewdock;

use bgfx_rs::Bgfx;
use libc::{c_void, c_int};
use minifb::{Scale, WindowOptions, MouseMode, MouseButton, Key, KeyRepeat};
use core::view_plugins::{ViewHandle, ViewPlugins, ViewInstance};
use core::session::{Sessions, Session, SessionHandle};
use core::reader_wrapper::ReaderWrapper;
use self::viewdock::{Workspace, Rect, Direction, DockHandle, SplitHandle};
use menu::{Menu, MENU_DEBUG_STEP_IN};
use imgui_sys::Imgui;
use prodbg_api::ui_ffi::{PDVec2};
use prodbg_api::view::CViewCallbacks;

const WIDTH: usize = 1280;
const HEIGHT: usize = 800;

enum State {
    Default,
    DraggingSlider,
}

pub struct MouseState {
    handle: Option<(SplitHandle, Direction)>,
    state: State,
    prev_mouse: (f32, f32)
}

impl MouseState {
    pub fn new() -> MouseState {
        MouseState {
            handle: None,
            state: State::Default,
            prev_mouse: (0.0, 0.0),
        }
    }
}

pub struct Window<'a> {
    /// minifb window
    pub win: minifb::Window,
    pub menu: Menu<'a>,

    /// Views in this window
    pub views: Vec<ViewHandle>,

    ///
    pub ws: Workspace,

    pub mouse_state: MouseState,
}

struct WindowState {
    pub showed_popup: u32,
    pub should_close: bool,
}

///! Windows keeps track of all different windows that are present with in the application
///! There are several ways windows can be created:
///!
///! 1. User opens a new window using a shortcut or menu selection.
///! 2. User "undocks" a view from an existing window giving it it's own floating window.
///! 3. etc

pub struct Windows<'a> {
    /// All the windows being tracked
    windows: Vec<Window<'a>>,
    current: usize,
}

impl<'a> Windows<'a> {
    pub fn new() -> Windows<'a> {
        Windows {
            windows: Vec::new(),
            current: 0,
        }
    }

    /// Create a default window which will only be created if there are no other
    pub fn create_default(&mut self) {
        if self.windows.len() > 0 {
            return;
        }

        let window = Self::create_window_with_menus().expect("Unable to create window");

        self.windows.push(window)
   }

    pub fn create_window(width: usize, height: usize) -> minifb::Result<Window<'a>> {
        let res = minifb::Window::new("ProDBG",
                                      width,
                                      height,
                                      WindowOptions {
                                          resize: true,
                                          scale: Scale::X1,
                                          ..WindowOptions::default()
                                      });
        match res {
            Ok(win) => {
                Bgfx::create_window(win.get_window_handle() as *const c_void,
                                    width as c_int,
                                    height as c_int);
                Ok(Window {
                    win: win,
                    menu: Menu::new(),
                    views: Vec::new(),
                    mouse_state: MouseState::new(),
                    ws: Workspace::new(Rect::new(0.0, 0.0, width as f32, (height - 20) as f32)).unwrap(),
                })
            }
            Err(err) => Err(err),
        }
    }

    pub fn create_window_with_menus() -> minifb::Result<Window<'a>> {
        let mut window = try!(Self::create_window(WIDTH, HEIGHT));

        // we ignore the results because we likely brake this on Linux otherwise
        // TODO: Figure out how to deal with this on Linux
        let _ = window.win.add_menu("File", &window.menu.file_menu);
        let _ = window.win.add_menu("Debug", &window.menu.debug_menu);

        Ok(window)
    }

    pub fn update(&mut self, sessions: &mut Sessions, view_plugins: &mut ViewPlugins) {
        for i in (0..self.windows.len()).rev() {
            self.windows[i].update(sessions, view_plugins);

            if !self.windows[i].win.is_open() {
                self.windows.swap_remove(i);
            }
        }
    }

    pub fn get_current(&mut self) -> &'a mut Window {
        let current = self.current;
        &mut self.windows[current]
    }

    /// Checks if application should exit (all window instances closed)
    pub fn should_exit(&self) -> bool {
        self.windows.len() == 0
    }

    /// Save the state of the windows (usually done when exiting the application)
    pub fn save(_filename: &str) {}

    /// Load the state of all the views from a previous run
    pub fn load(_filename: &str) {}
}

impl<'a> Window<'a> {
    fn is_inside(v: (f32, f32), pos: PDVec2, size: PDVec2) -> bool {
        let x0 = pos.x;
        let y0 = pos.y;
        let x1 = pos.x + size.x;
        let y1 = pos.y + size.y;

        if (v.0 >= x0 && v.0 < x1) && (v.1 >= y0 && v.1 < y1) {
            true
        } else {
            false
        }
    }

    fn update_view(&self, instance: &mut ViewInstance, session: &mut Session, show_context_menu: bool, mouse: (f32, f32)) -> WindowState {
        let ui = instance.ui;

        if let Some(rect) = self.ws.get_rect_by_handle(DockHandle(instance.handle.0)) {
            Imgui::set_window_pos(rect.x, rect.y);
            Imgui::set_window_size(rect.width, rect.height);
        }

        let open = Imgui::begin_window(&instance.name, true);
        Imgui::init_state(ui.api);

        let pos = ui.get_window_pos();
        let size = ui.get_window_size();

        if Self::is_inside(mouse, pos, size) && show_context_menu {
            Imgui::mark_show_popup(ui.api, true);
        } else {
            Imgui::mark_show_popup(ui.api, false);
        }

        // Make sure we move the cursor to the start of the stream here
        ReaderWrapper::reset_reader(&mut session.reader);

        unsafe {
            let plugin_funcs = instance.plugin_type.plugin_funcs as *mut CViewCallbacks;
            ((*plugin_funcs).update.unwrap())(instance.plugin_data,
                                                ui.api as *mut c_void,
                                                session.reader.api as *mut c_void,
                                                session.get_current_writer().api as *mut c_void);
        }

        let has_shown_menu = Imgui::has_showed_popup(ui.api);

        Imgui::end_window();

        WindowState {
            showed_popup: has_shown_menu,
            should_close: !open,
        }
    }

    pub fn remove_views(&mut self, view_plugins: &mut ViewPlugins, views: &Vec<ViewHandle>) {
        for view in views {
            view_plugins.destroy_instance(*view);
            self.ws.delete_by_handle(DockHandle(view.0));
        }
    }

    fn update_mouse_state(&mut self, mouse_pos: (f32, f32)) {
        match self.mouse_state.state {
            State::Default => {
                if let Some(h) = self.ws.is_hovering_sizer(mouse_pos) {
                    if h.1 == Direction::Vertical {
                        Bgfx::cursor_set_type(2);
                    } else {
                        Bgfx::cursor_set_type(1);
                    }

                    if self.win.get_mouse_down(MouseButton::Left) {
                        self.mouse_state.handle = Some(h);
                        self.mouse_state.state = State::DraggingSlider;
                    }
                } else {
                    Bgfx::cursor_set_type(0);
                }
            },

            State::DraggingSlider => {
                let pm = self.mouse_state.prev_mouse;
                let delta = (pm.0 - mouse_pos.0, pm.1 - mouse_pos.1);

                if self.win.get_mouse_down(MouseButton::Left) {
                    let t = self.mouse_state.handle.unwrap();
                    if t.1 == Direction::Vertical {
                        Bgfx::cursor_set_type(2);
                    } else {
                        Bgfx::cursor_set_type(1);
                    }
                    self.ws.drag_sizer(t.0, delta);
                } else {
                    self.mouse_state.handle = None;
                    self.mouse_state.state = State::Default;
                    Bgfx::cursor_set_type(0);
                }
            }
        }

        /*
        if let Some(handle) = ws.is_hovering_sizer(mouse_pos) {
            let delta = (prev_mouse.0 - mouse_pos.0, prev_mouse.1 - mouse_pos.1);

            if window.get_mouse_down(MouseButton::Left) {
                println!("drangging sizer");
                ws.drag_sizer(handle, delta);
            }
        }
        */

        self.mouse_state.prev_mouse = mouse_pos;
    }

    pub fn update(&mut self, sessions: &mut Sessions, view_plugins: &mut ViewPlugins) {
        let mut views_to_delete = Vec::new();
        let mut has_shown_menu = 0u32;

        self.win.update();
        self.ws.update();

        let mouse = self.win.get_mouse_pos(MouseMode::Clamp).unwrap_or((0.0, 0.0));

        self.update_mouse_state(mouse);

        Bgfx::set_mouse_pos(mouse);
        Bgfx::set_mouse_state(0, self.win.get_mouse_down(MouseButton::Left));

        let show_context_menu = self.win.get_mouse_down(MouseButton::Right);

        for view in &self.views {
            if let Some(ref mut v) = view_plugins.get_view(*view) {
                if let Some(ref mut s) = sessions.get_session(v.session_handle) {

                    // TODO: Only do this on the correct session

                    self.win.is_menu_pressed().map(|menu_id| {
                        if menu_id == MENU_DEBUG_STEP_IN {
                            println!("action_step");
                            s.action_step();
                        }
                    });

                    let state = Self::update_view(self, v, s, show_context_menu, mouse);

                    if state.should_close {
                        views_to_delete.push(*view);
                    }

                    has_shown_menu |= state.showed_popup;
                }
            }
        }

        if self.win.is_key_pressed(Key::Down, KeyRepeat::No) {
            self.ws.dump_tree();
        }

        if self.win.is_key_pressed(Key::Up, KeyRepeat::No) {
            self.save_layout("data/layout_temp.json", view_plugins);
        }

        if self.win.is_key_pressed(Key::Right, KeyRepeat::No) {
            self.load_layout("data/layout_temp.json", view_plugins);
        }

        // test

        // if now plugin has showed a menu we do it here
        // TODO: Handle diffrent cases when attach menu on to plugin menu or not

        if has_shown_menu == 0 && show_context_menu {
            Self::show_popup(self, true, mouse, view_plugins);
        } else {
            Self::show_popup(self, false, mouse, view_plugins);
        }

        Self::remove_views(self, view_plugins, &views_to_delete);
    }

    fn split_view(&mut self, name: &String, view_plugins: &mut ViewPlugins, pos: (f32, f32), direction: Direction) {
        let ui = Imgui::create_ui_instance();
        if let Some(handle) = view_plugins.create_instance(ui, name, SessionHandle(0)) {
            if let Some(dock_handle) = self.ws.get_hover_dock(pos) {
                let new_handle = DockHandle(handle.0);
                self.ws.split_by_dock_handle(direction, dock_handle, new_handle);
                //self.ws.set_name_to_handle(name, new_handle);
                //self.ws.dump_tree_linear();
            } else {
                self.ws.new_split(DockHandle(handle.0), direction);
                //println!("no split");
            }

            self.views.push(handle);
        }
    }

    fn show_popup_menu_no_splits(&mut self, plugin_names: &Vec<String>, mouse_pos: (f32, f32), view_plugins: &mut ViewPlugins) {
        let ui = Imgui::get_ui();

        if ui.begin_menu("New View", true) {
            for name in plugin_names {
                if ui.menu_item(name, false, true) {
                    Self::split_view(self, &name, view_plugins, mouse_pos, Direction::Horizontal);
                }
            }
            ui.end_menu();
        }
    }

    fn show_popup_regular(&mut self, plugin_names: &Vec<String>, mouse_pos: (f32, f32), view_plugins: &mut ViewPlugins) {
        let ui = Imgui::get_ui();

        if ui.begin_menu("Split Horizontally", true) {
            for name in plugin_names {
                if ui.menu_item(name, false, true) {
                    Self::split_view(self, &name, view_plugins, mouse_pos, Direction::Horizontal);
                }
            }
            ui.end_menu();
        }

        if ui.begin_menu("Split Vertically", true) {
            for name in plugin_names {
                if ui.menu_item(name, false, true) {
                    Self::split_view(self, &name, view_plugins, mouse_pos, Direction::Vertical);
                }
            }
            ui.end_menu();
        }
    }

    fn show_popup(&mut self, show: bool, mouse_pos: (f32, f32), view_plugins: &mut ViewPlugins) {
        let ui = Imgui::get_ui();

        if show {
            ui.open_popup("plugins");
        }

        if ui.begin_popup("plugins") {
            let plugin_names = view_plugins.get_plugin_names();

            if self.ws.get_hover_dock(mouse_pos).is_none() {
                self.show_popup_menu_no_splits(&plugin_names, mouse_pos, view_plugins);
            } else {
                self.show_popup_regular(&plugin_names, mouse_pos, view_plugins);
            }

            ui.end_popup();
        }
    }

    fn save_layout(&mut self, filename: &str, view_plugins: &mut ViewPlugins) {
        for split in &mut self.ws.splits {
            let iter = split.left_docks.docks.iter_mut().chain(split.right_docks.docks.iter_mut());

            for dock in iter {
                if let Some(ref plugin) = view_plugins.get_view(ViewHandle(dock.handle.0)) {
                    let (plugin_name, data) = plugin.get_plugin_data();
                    dock.plugin_name = plugin_name;
                    dock.plugin_data = data;
                } else {
                    println!("Unable to find plugin for {:?} - this should never happen", dock);
                }
            }
        }

        let _ = self.ws.save(filename);
    }

    fn load_layout(&mut self, filename: &str, view_plugins: &mut ViewPlugins) {
        let ws = Workspace::load(filename);
        let docks = ws.get_docks();
        self.views.clear();

        for dock in &docks {
            let ui = Imgui::create_ui_instance();
            let handle = ViewHandle(dock.handle.0);
            view_plugins.create_instance_with_handle(ui, &dock.plugin_name, &dock.plugin_data, SessionHandle(0), ViewHandle(dock.handle.0));
            self.views.push(handle);
        }

        self.ws = ws;
    }
}
<|endoftext|>