<commit_before>//! Applies changes to the IDE state transactionally.

use std::sync::Arc;

use base_db::{
    salsa::{Database, Durability},
    Change, SourceRootId,
};
use profile::{memory_usage, Bytes};
use rustc_hash::FxHashSet;

use crate::{symbol_index::SymbolsDatabase, RootDatabase};

impl RootDatabase {
    pub fn request_cancellation(&mut self) {
        let _p = profile::span("RootDatabase::request_cancellation");
        self.salsa_runtime_mut().synthetic_write(Durability::LOW);
    }

    pub fn apply_change(&mut self, change: Change) {
        let _p = profile::span("RootDatabase::apply_change");
        self.request_cancellation();
        tracing::info!("apply_change {:?}", change);
        if let Some(roots) = &change.roots {
            let mut local_roots = FxHashSet::default();
            let mut library_roots = FxHashSet::default();
            for (idx, root) in roots.iter().enumerate() {
                let root_id = SourceRootId(idx as u32);
                if root.is_library {
                    library_roots.insert(root_id);
                } else {
                    local_roots.insert(root_id);
                }
            }
            self.set_local_roots_with_durability(Arc::new(local_roots), Durability::HIGH);
            self.set_library_roots_with_durability(Arc::new(library_roots), Durability::HIGH);
        }
        change.apply(self);
    }

    // Feature: Memory Usage
    //
    // Clears rust-analyzer's internal database and prints memory usage statistics.
    //
    // |===
    // | Editor  | Action Name
    //
    // | VS Code | **rust-analyzer: Memory Usage (Clears Database)**
    // |===
    // image::https://user-images.githubusercontent.com/48062697/113065592-08559f00-91b1-11eb-8c96-64b88068ec02.gif[]
    pub fn per_query_memory_usage(&mut self) -> Vec<(String, Bytes)> {
        let mut acc: Vec<(String, Bytes)> = vec![];
        macro_rules! purge_each_query {
            ($($q:path)*) => {$(
                let before = memory_usage().allocated;
                $q.in_db(self).purge();
                let after = memory_usage().allocated;
                let q: $q = Default::default();
                let name = format!("{:?}", q);
                acc.push((name, before - after));
            )*}
        }
        purge_each_query![
            // SourceDatabase
            base_db::ParseQuery
            base_db::CrateGraphQuery

            // SourceDatabaseExt
            base_db::FileTextQuery
            base_db::FileSourceRootQuery
            base_db::SourceRootQuery
            base_db::SourceRootCratesQuery

            // AstDatabase
            hir::db::AstIdMapQuery
            hir::db::MacroArgTextQuery
            hir::db::MacroDefQuery
            hir::db::ParseMacroExpansionQuery
            hir::db::MacroExpandQuery
            hir::db::HygieneFrameQuery
            hir::db::InternMacroCallQuery

            // DefDatabase
            hir::db::FileItemTreeQuery
            hir::db::BlockDefMapQuery
            hir::db::CrateDefMapQueryQuery
            hir::db::FieldsAttrsQuery
            hir::db::VariantsAttrsQuery
            hir::db::FieldsAttrsSourceMapQuery
            hir::db::VariantsAttrsSourceMapQuery
            hir::db::StructDataQuery
            hir::db::UnionDataQuery
            hir::db::EnumDataQuery
            hir::db::ImplDataQuery
            hir::db::TraitDataQuery
            hir::db::TypeAliasDataQuery
            hir::db::FunctionDataQuery
            hir::db::ConstDataQuery
            hir::db::StaticDataQuery
            hir::db::BodyWithSourceMapQuery
            hir::db::BodyQuery
            hir::db::ExprScopesQuery
            hir::db::GenericParamsQuery
            hir::db::AttrsQuery
            hir::db::CrateLangItemsQuery
            hir::db::LangItemQuery
            hir::db::ImportMapQuery

            // HirDatabase
            hir::db::InferQueryQuery
            hir::db::TyQuery
            hir::db::ValueTyQuery
            hir::db::ImplSelfTyQuery
            hir::db::ImplTraitQuery
            hir::db::FieldTypesQuery
            hir::db::CallableItemSignatureQuery
            hir::db::GenericPredicatesForParamQuery
            hir::db::GenericPredicatesQuery
            hir::db::GenericDefaultsQuery
            hir::db::InherentImplsInCrateQuery
            hir::db::TraitEnvironmentQuery
            hir::db::TraitImplsInCrateQuery
            hir::db::TraitImplsInDepsQuery
            hir::db::AssociatedTyDataQuery
            hir::db::AssociatedTyDataQuery
            hir::db::TraitDatumQuery
            hir::db::StructDatumQuery
            hir::db::ImplDatumQuery
            hir::db::FnDefDatumQuery
            hir::db::ReturnTypeImplTraitsQuery
            hir::db::InternCallableDefQuery
            hir::db::InternTypeOrConstParamIdQuery
            hir::db::InternImplTraitIdQuery
            hir::db::InternClosureQuery
            hir::db::AssociatedTyValueQuery
            hir::db::TraitSolveQueryQuery
            hir::db::InternTypeOrConstParamIdQuery

            // SymbolsDatabase
            crate::symbol_index::ModuleSymbolsQuery
            crate::symbol_index::LibrarySymbolsQuery
            crate::symbol_index::LocalRootsQuery
            crate::symbol_index::LibraryRootsQuery

            // LineIndexDatabase
            crate::LineIndexQuery

            // InternDatabase
            hir::db::InternFunctionQuery
            hir::db::InternStructQuery
            hir::db::InternUnionQuery
            hir::db::InternEnumQuery
            hir::db::InternConstQuery
            hir::db::InternStaticQuery
            hir::db::InternTraitQuery
            hir::db::InternTypeAliasQuery
            hir::db::InternImplQuery
        ];

        acc.sort_by_key(|it| std::cmp::Reverse(it.1));
        acc
    }
}
<commit_msg>minor: Change tracing event level in apply_change<commit_after>//! Applies changes to the IDE state transactionally.

use std::sync::Arc;

use base_db::{
    salsa::{Database, Durability},
    Change, SourceRootId,
};
use profile::{memory_usage, Bytes};
use rustc_hash::FxHashSet;

use crate::{symbol_index::SymbolsDatabase, RootDatabase};

impl RootDatabase {
    pub fn request_cancellation(&mut self) {
        let _p = profile::span("RootDatabase::request_cancellation");
        self.salsa_runtime_mut().synthetic_write(Durability::LOW);
    }

    pub fn apply_change(&mut self, change: Change) {
        let _p = profile::span("RootDatabase::apply_change");
        self.request_cancellation();
        tracing::trace!("apply_change {:?}", change);
        if let Some(roots) = &change.roots {
            let mut local_roots = FxHashSet::default();
            let mut library_roots = FxHashSet::default();
            for (idx, root) in roots.iter().enumerate() {
                let root_id = SourceRootId(idx as u32);
                if root.is_library {
                    library_roots.insert(root_id);
                } else {
                    local_roots.insert(root_id);
                }
            }
            self.set_local_roots_with_durability(Arc::new(local_roots), Durability::HIGH);
            self.set_library_roots_with_durability(Arc::new(library_roots), Durability::HIGH);
        }
        change.apply(self);
    }

    // Feature: Memory Usage
    //
    // Clears rust-analyzer's internal database and prints memory usage statistics.
    //
    // |===
    // | Editor  | Action Name
    //
    // | VS Code | **rust-analyzer: Memory Usage (Clears Database)**
    // |===
    // image::https://user-images.githubusercontent.com/48062697/113065592-08559f00-91b1-11eb-8c96-64b88068ec02.gif[]
    pub fn per_query_memory_usage(&mut self) -> Vec<(String, Bytes)> {
        let mut acc: Vec<(String, Bytes)> = vec![];
        macro_rules! purge_each_query {
            ($($q:path)*) => {$(
                let before = memory_usage().allocated;
                $q.in_db(self).purge();
                let after = memory_usage().allocated;
                let q: $q = Default::default();
                let name = format!("{:?}", q);
                acc.push((name, before - after));
            )*}
        }
        purge_each_query![
            // SourceDatabase
            base_db::ParseQuery
            base_db::CrateGraphQuery

            // SourceDatabaseExt
            base_db::FileTextQuery
            base_db::FileSourceRootQuery
            base_db::SourceRootQuery
            base_db::SourceRootCratesQuery

            // AstDatabase
            hir::db::AstIdMapQuery
            hir::db::MacroArgTextQuery
            hir::db::MacroDefQuery
            hir::db::ParseMacroExpansionQuery
            hir::db::MacroExpandQuery
            hir::db::HygieneFrameQuery
            hir::db::InternMacroCallQuery

            // DefDatabase
            hir::db::FileItemTreeQuery
            hir::db::BlockDefMapQuery
            hir::db::CrateDefMapQueryQuery
            hir::db::FieldsAttrsQuery
            hir::db::VariantsAttrsQuery
            hir::db::FieldsAttrsSourceMapQuery
            hir::db::VariantsAttrsSourceMapQuery
            hir::db::StructDataQuery
            hir::db::UnionDataQuery
            hir::db::EnumDataQuery
            hir::db::ImplDataQuery
            hir::db::TraitDataQuery
            hir::db::TypeAliasDataQuery
            hir::db::FunctionDataQuery
            hir::db::ConstDataQuery
            hir::db::StaticDataQuery
            hir::db::BodyWithSourceMapQuery
            hir::db::BodyQuery
            hir::db::ExprScopesQuery
            hir::db::GenericParamsQuery
            hir::db::AttrsQuery
            hir::db::CrateLangItemsQuery
            hir::db::LangItemQuery
            hir::db::ImportMapQuery

            // HirDatabase
            hir::db::InferQueryQuery
            hir::db::TyQuery
            hir::db::ValueTyQuery
            hir::db::ImplSelfTyQuery
            hir::db::ImplTraitQuery
            hir::db::FieldTypesQuery
            hir::db::CallableItemSignatureQuery
            hir::db::GenericPredicatesForParamQuery
            hir::db::GenericPredicatesQuery
            hir::db::GenericDefaultsQuery
            hir::db::InherentImplsInCrateQuery
            hir::db::TraitEnvironmentQuery
            hir::db::TraitImplsInCrateQuery
            hir::db::TraitImplsInDepsQuery
            hir::db::AssociatedTyDataQuery
            hir::db::AssociatedTyDataQuery
            hir::db::TraitDatumQuery
            hir::db::StructDatumQuery
            hir::db::ImplDatumQuery
            hir::db::FnDefDatumQuery
            hir::db::ReturnTypeImplTraitsQuery
            hir::db::InternCallableDefQuery
            hir::db::InternTypeOrConstParamIdQuery
            hir::db::InternImplTraitIdQuery
            hir::db::InternClosureQuery
            hir::db::AssociatedTyValueQuery
            hir::db::TraitSolveQueryQuery
            hir::db::InternTypeOrConstParamIdQuery

            // SymbolsDatabase
            crate::symbol_index::ModuleSymbolsQuery
            crate::symbol_index::LibrarySymbolsQuery
            crate::symbol_index::LocalRootsQuery
            crate::symbol_index::LibraryRootsQuery

            // LineIndexDatabase
            crate::LineIndexQuery

            // InternDatabase
            hir::db::InternFunctionQuery
            hir::db::InternStructQuery
            hir::db::InternUnionQuery
            hir::db::InternEnumQuery
            hir::db::InternConstQuery
            hir::db::InternStaticQuery
            hir::db::InternTraitQuery
            hir::db::InternTypeAliasQuery
            hir::db::InternImplQuery
        ];

        acc.sort_by_key(|it| std::cmp::Reverse(it.1));
        acc
    }
}
<|endoftext|>


<commit_before>//! See `CompletionItem` structure.

use std::fmt;

use hir::{Documentation, Mutability};
use ide_db::{
    helpers::{
        import_assets::LocatedImport,
        insert_use::{self, ImportScope, InsertUseConfig},
        mod_path_to_ast, SnippetCap,
    },
    SymbolKind,
};
use stdx::{format_to, impl_from, never};
use syntax::{algo, TextRange};
use text_edit::TextEdit;

/// `CompletionItem` describes a single completion variant in the editor pop-up.
/// It is basically a POD with various properties. To construct a
/// `CompletionItem`, use `new` method and the `Builder` struct.
#[derive(Clone)]
pub struct CompletionItem {
    /// Used only internally in tests, to check only specific kind of
    /// completion (postfix, keyword, reference, etc).
    #[allow(unused)]
    pub(crate) completion_kind: CompletionKind,
    /// Label in the completion pop up which identifies completion.
    label: String,
    /// Range of identifier that is being completed.
    ///
    /// It should be used primarily for UI, but we also use this to convert
    /// genetic TextEdit into LSP's completion edit (see conv.rs).
    ///
    /// `source_range` must contain the completion offset. `insert_text` should
    /// start with what `source_range` points to, or VSCode will filter out the
    /// completion silently.
    source_range: TextRange,
    /// What happens when user selects this item.
    ///
    /// Typically, replaces `source_range` with new identifier.
    text_edit: TextEdit,

    insert_text_format: InsertTextFormat,

    /// What item (struct, function, etc) are we completing.
    kind: Option<CompletionItemKind>,

    /// Lookup is used to check if completion item indeed can complete current
    /// ident.
    ///
    /// That is, in `foo.bar$0` lookup of `abracadabra` will be accepted (it
    /// contains `bar` sub sequence), and `quux` will rejected.
    lookup: Option<String>,

    /// Additional info to show in the UI pop up.
    detail: Option<String>,
    documentation: Option<Documentation>,

    /// Whether this item is marked as deprecated
    deprecated: bool,

    /// If completing a function call, ask the editor to show parameter popup
    /// after completion.
    trigger_call_info: bool,

    /// Score is useful to pre select or display in better order completion items
    score: Option<CompletionScore>,

    /// Indicates that a reference or mutable reference to this variable is a
    /// possible match.
    ref_match: Option<(Mutability, CompletionScore)>,

    /// The import data to add to completion's edits.
    import_to_add: Option<ImportEdit>,
}

// We use custom debug for CompletionItem to make snapshot tests more readable.
impl fmt::Debug for CompletionItem {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut s = f.debug_struct("CompletionItem");
        s.field("label", &self.label()).field("source_range", &self.source_range());
        if self.text_edit().len() == 1 {
            let atom = &self.text_edit().iter().next().unwrap();
            s.field("delete", &atom.delete);
            s.field("insert", &atom.insert);
        } else {
            s.field("text_edit", &self.text_edit);
        }
        if let Some(kind) = self.kind().as_ref() {
            s.field("kind", kind);
        }
        if self.lookup() != self.label() {
            s.field("lookup", &self.lookup());
        }
        if let Some(detail) = self.detail() {
            s.field("detail", &detail);
        }
        if let Some(documentation) = self.documentation() {
            s.field("documentation", &documentation);
        }
        if self.deprecated {
            s.field("deprecated", &true);
        }
        if let Some(score) = &self.score {
            s.field("score", score);
        }
        if self.trigger_call_info {
            s.field("trigger_call_info", &true);
        }
        s.finish()
    }
}

#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq)]
pub enum CompletionScore {
    /// If only type match
    TypeMatch,
    /// If type and name match
    TypeAndNameMatch,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CompletionItemKind {
    SymbolKind(SymbolKind),
    Attribute,
    Binding,
    BuiltinType,
    Keyword,
    Method,
    Snippet,
    UnresolvedReference,
}

impl_from!(SymbolKind for CompletionItemKind);

impl CompletionItemKind {
    #[cfg(test)]
    pub(crate) fn tag(&self) -> &'static str {
        match self {
            CompletionItemKind::SymbolKind(kind) => match kind {
                SymbolKind::Const => "ct",
                SymbolKind::ConstParam => "cp",
                SymbolKind::Enum => "en",
                SymbolKind::Field => "fd",
                SymbolKind::Function => "fn",
                SymbolKind::Impl => "im",
                SymbolKind::Label => "lb",
                SymbolKind::LifetimeParam => "lt",
                SymbolKind::Local => "lc",
                SymbolKind::Macro => "ma",
                SymbolKind::Module => "md",
                SymbolKind::SelfParam => "sp",
                SymbolKind::Static => "sc",
                SymbolKind::Struct => "st",
                SymbolKind::Trait => "tt",
                SymbolKind::TypeAlias => "ta",
                SymbolKind::TypeParam => "tp",
                SymbolKind::Union => "un",
                SymbolKind::ValueParam => "vp",
                SymbolKind::Variant => "ev",
            },
            CompletionItemKind::Attribute => "at",
            CompletionItemKind::Binding => "bn",
            CompletionItemKind::BuiltinType => "bt",
            CompletionItemKind::Keyword => "kw",
            CompletionItemKind::Method => "me",
            CompletionItemKind::Snippet => "sn",
            CompletionItemKind::UnresolvedReference => "??",
        }
    }
}

#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub(crate) enum CompletionKind {
    /// Parser-based keyword completion.
    Keyword,
    /// Your usual "complete all valid identifiers".
    Reference,
    /// "Secret sauce" completions.
    Magic,
    Snippet,
    Postfix,
    BuiltinType,
    Attribute,
}

#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum InsertTextFormat {
    PlainText,
    Snippet,
}

impl CompletionItem {
    pub(crate) fn new(
        completion_kind: CompletionKind,
        source_range: TextRange,
        label: impl Into<String>,
    ) -> Builder {
        let label = label.into();
        Builder {
            source_range,
            completion_kind,
            label,
            insert_text: None,
            insert_text_format: InsertTextFormat::PlainText,
            detail: None,
            documentation: None,
            lookup: None,
            kind: None,
            text_edit: None,
            deprecated: None,
            trigger_call_info: None,
            score: None,
            ref_match: None,
            import_to_add: None,
        }
    }

    /// What user sees in pop-up in the UI.
    pub fn label(&self) -> &str {
        &self.label
    }
    pub fn source_range(&self) -> TextRange {
        self.source_range
    }

    pub fn insert_text_format(&self) -> InsertTextFormat {
        self.insert_text_format
    }

    pub fn text_edit(&self) -> &TextEdit {
        &self.text_edit
    }

    /// Short one-line additional information, like a type
    pub fn detail(&self) -> Option<&str> {
        self.detail.as_deref()
    }
    /// A doc-comment
    pub fn documentation(&self) -> Option<Documentation> {
        self.documentation.clone()
    }
    /// What string is used for filtering.
    pub fn lookup(&self) -> &str {
        self.lookup.as_deref().unwrap_or(&self.label)
    }

    pub fn kind(&self) -> Option<CompletionItemKind> {
        self.kind
    }

    pub fn deprecated(&self) -> bool {
        self.deprecated
    }

    pub fn score(&self) -> Option<CompletionScore> {
        self.score
    }

    pub fn trigger_call_info(&self) -> bool {
        self.trigger_call_info
    }

    pub fn ref_match(&self) -> Option<(Mutability, CompletionScore)> {
        self.ref_match
    }

    pub fn import_to_add(&self) -> Option<&ImportEdit> {
        self.import_to_add.as_ref()
    }
}

/// An extra import to add after the completion is applied.
#[derive(Debug, Clone)]
pub struct ImportEdit {
    pub import: LocatedImport,
    pub scope: ImportScope,
}

impl ImportEdit {
    /// Attempts to insert the import to the given scope, producing a text edit.
    /// May return no edit in edge cases, such as scope already containing the import.
    pub fn to_text_edit(&self, cfg: InsertUseConfig) -> Option<TextEdit> {
        let _p = profile::span("ImportEdit::to_text_edit");

        let rewriter =
            insert_use::insert_use(&self.scope, mod_path_to_ast(&self.import.import_path), cfg);
        let old_ast = rewriter.rewrite_root()?;
        let mut import_insert = TextEdit::builder();
        algo::diff(&old_ast, &rewriter.rewrite(&old_ast)).into_text_edit(&mut import_insert);

        Some(import_insert.finish())
    }
}

/// A helper to make `CompletionItem`s.
#[must_use]
#[derive(Clone)]
pub(crate) struct Builder {
    source_range: TextRange,
    completion_kind: CompletionKind,
    import_to_add: Option<ImportEdit>,
    label: String,
    insert_text: Option<String>,
    insert_text_format: InsertTextFormat,
    detail: Option<String>,
    documentation: Option<Documentation>,
    lookup: Option<String>,
    kind: Option<CompletionItemKind>,
    text_edit: Option<TextEdit>,
    deprecated: Option<bool>,
    trigger_call_info: Option<bool>,
    score: Option<CompletionScore>,
    ref_match: Option<(Mutability, CompletionScore)>,
}

impl Builder {
    pub(crate) fn build(self) -> CompletionItem {
        let _p = profile::span("item::Builder::build");

        let mut label = self.label;
        let mut lookup = self.lookup;
        let mut insert_text = self.insert_text;

        if let Some(original_path) = self
            .import_to_add
            .as_ref()
            .and_then(|import_edit| import_edit.import.original_path.as_ref())
        {
            lookup = lookup.or_else(|| Some(label.clone()));
            insert_text = insert_text.or_else(|| Some(label.clone()));

            let original_path_label = original_path.to_string();
            if original_path_label.ends_with(&label) {
                label = original_path_label;
            } else {
                format_to!(label, " ({})", original_path)
            }
        }

        let text_edit = match self.text_edit {
            Some(it) => it,
            None => {
                TextEdit::replace(self.source_range, insert_text.unwrap_or_else(|| label.clone()))
            }
        };

        CompletionItem {
            source_range: self.source_range,
            label,
            insert_text_format: self.insert_text_format,
            text_edit,
            detail: self.detail,
            documentation: self.documentation,
            lookup,
            kind: self.kind,
            completion_kind: self.completion_kind,
            deprecated: self.deprecated.unwrap_or(false),
            trigger_call_info: self.trigger_call_info.unwrap_or(false),
            score: self.score,
            ref_match: self.ref_match,
            import_to_add: self.import_to_add,
        }
    }
    pub(crate) fn lookup_by(mut self, lookup: impl Into<String>) -> Builder {
        self.lookup = Some(lookup.into());
        self
    }
    pub(crate) fn label(mut self, label: impl Into<String>) -> Builder {
        self.label = label.into();
        self
    }
    pub(crate) fn insert_text(mut self, insert_text: impl Into<String>) -> Builder {
        self.insert_text = Some(insert_text.into());
        self
    }
    pub(crate) fn insert_snippet(
        mut self,
        _cap: SnippetCap,
        snippet: impl Into<String>,
    ) -> Builder {
        self.insert_text_format = InsertTextFormat::Snippet;
        self.insert_text(snippet)
    }
    pub(crate) fn kind(mut self, kind: impl Into<CompletionItemKind>) -> Builder {
        self.kind = Some(kind.into());
        self
    }
    pub(crate) fn text_edit(mut self, edit: TextEdit) -> Builder {
        self.text_edit = Some(edit);
        self
    }
    pub(crate) fn snippet_edit(mut self, _cap: SnippetCap, edit: TextEdit) -> Builder {
        self.insert_text_format = InsertTextFormat::Snippet;
        self.text_edit(edit)
    }
    pub(crate) fn detail(self, detail: impl Into<String>) -> Builder {
        self.set_detail(Some(detail))
    }
    pub(crate) fn set_detail(mut self, detail: Option<impl Into<String>>) -> Builder {
        self.detail = detail.map(Into::into);
        if let Some(detail) = &self.detail {
            if never!(detail.contains('\n'), "multiline detail:\n{}", detail) {
                self.detail = Some(detail.splitn(2, '\n').next().unwrap().to_string());
            }
        }
        self
    }
    #[allow(unused)]
    pub(crate) fn documentation(self, docs: Documentation) -> Builder {
        self.set_documentation(Some(docs))
    }
    pub(crate) fn set_documentation(mut self, docs: Option<Documentation>) -> Builder {
        self.documentation = docs.map(Into::into);
        self
    }
    pub(crate) fn set_deprecated(mut self, deprecated: bool) -> Builder {
        self.deprecated = Some(deprecated);
        self
    }
    pub(crate) fn set_score(mut self, score: CompletionScore) -> Builder {
        self.score = Some(score);
        self
    }
    pub(crate) fn trigger_call_info(mut self) -> Builder {
        self.trigger_call_info = Some(true);
        self
    }
    pub(crate) fn add_import(mut self, import_to_add: Option<ImportEdit>) -> Builder {
        self.import_to_add = import_to_add;
        self
    }
    pub(crate) fn ref_match(mut self, ref_match: (Mutability, CompletionScore)) -> Builder {
        self.ref_match = Some(ref_match);
        self
    }
}
<commit_msg>Cleanup<commit_after>//! See `CompletionItem` structure.

use std::fmt;

use hir::{Documentation, Mutability};
use ide_db::{
    helpers::{
        import_assets::LocatedImport,
        insert_use::{self, ImportScope, InsertUseConfig},
        mod_path_to_ast, SnippetCap,
    },
    SymbolKind,
};
use stdx::{format_to, impl_from, never};
use syntax::{algo, TextRange};
use text_edit::TextEdit;

/// `CompletionItem` describes a single completion variant in the editor pop-up.
/// It is basically a POD with various properties. To construct a
/// `CompletionItem`, use `new` method and the `Builder` struct.
#[derive(Clone)]
pub struct CompletionItem {
    /// Used only internally in tests, to check only specific kind of
    /// completion (postfix, keyword, reference, etc).
    #[allow(unused)]
    pub(crate) completion_kind: CompletionKind,
    /// Label in the completion pop up which identifies completion.
    label: String,
    /// Range of identifier that is being completed.
    ///
    /// It should be used primarily for UI, but we also use this to convert
    /// genetic TextEdit into LSP's completion edit (see conv.rs).
    ///
    /// `source_range` must contain the completion offset. `insert_text` should
    /// start with what `source_range` points to, or VSCode will filter out the
    /// completion silently.
    source_range: TextRange,
    /// What happens when user selects this item.
    ///
    /// Typically, replaces `source_range` with new identifier.
    text_edit: TextEdit,

    insert_text_format: InsertTextFormat,

    /// What item (struct, function, etc) are we completing.
    kind: Option<CompletionItemKind>,

    /// Lookup is used to check if completion item indeed can complete current
    /// ident.
    ///
    /// That is, in `foo.bar$0` lookup of `abracadabra` will be accepted (it
    /// contains `bar` sub sequence), and `quux` will rejected.
    lookup: Option<String>,

    /// Additional info to show in the UI pop up.
    detail: Option<String>,
    documentation: Option<Documentation>,

    /// Whether this item is marked as deprecated
    deprecated: bool,

    /// If completing a function call, ask the editor to show parameter popup
    /// after completion.
    trigger_call_info: bool,

    /// Score is useful to pre select or display in better order completion items
    score: Option<CompletionScore>,

    /// Indicates that a reference or mutable reference to this variable is a
    /// possible match.
    ref_match: Option<(Mutability, CompletionScore)>,

    /// The import data to add to completion's edits.
    import_to_add: Option<ImportEdit>,
}

// We use custom debug for CompletionItem to make snapshot tests more readable.
impl fmt::Debug for CompletionItem {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut s = f.debug_struct("CompletionItem");
        s.field("label", &self.label()).field("source_range", &self.source_range());
        if self.text_edit().len() == 1 {
            let atom = &self.text_edit().iter().next().unwrap();
            s.field("delete", &atom.delete);
            s.field("insert", &atom.insert);
        } else {
            s.field("text_edit", &self.text_edit);
        }
        if let Some(kind) = self.kind().as_ref() {
            s.field("kind", kind);
        }
        if self.lookup() != self.label() {
            s.field("lookup", &self.lookup());
        }
        if let Some(detail) = self.detail() {
            s.field("detail", &detail);
        }
        if let Some(documentation) = self.documentation() {
            s.field("documentation", &documentation);
        }
        if self.deprecated {
            s.field("deprecated", &true);
        }
        if let Some(score) = &self.score {
            s.field("score", score);
        }
        if self.trigger_call_info {
            s.field("trigger_call_info", &true);
        }
        s.finish()
    }
}

#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq)]
pub enum CompletionScore {
    /// If only type match
    TypeMatch,
    /// If type and name match
    TypeAndNameMatch,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CompletionItemKind {
    SymbolKind(SymbolKind),
    Attribute,
    Binding,
    BuiltinType,
    Keyword,
    Method,
    Snippet,
    UnresolvedReference,
}

impl_from!(SymbolKind for CompletionItemKind);

impl CompletionItemKind {
    #[cfg(test)]
    pub(crate) fn tag(&self) -> &'static str {
        match self {
            CompletionItemKind::SymbolKind(kind) => match kind {
                SymbolKind::Const => "ct",
                SymbolKind::ConstParam => "cp",
                SymbolKind::Enum => "en",
                SymbolKind::Field => "fd",
                SymbolKind::Function => "fn",
                SymbolKind::Impl => "im",
                SymbolKind::Label => "lb",
                SymbolKind::LifetimeParam => "lt",
                SymbolKind::Local => "lc",
                SymbolKind::Macro => "ma",
                SymbolKind::Module => "md",
                SymbolKind::SelfParam => "sp",
                SymbolKind::Static => "sc",
                SymbolKind::Struct => "st",
                SymbolKind::Trait => "tt",
                SymbolKind::TypeAlias => "ta",
                SymbolKind::TypeParam => "tp",
                SymbolKind::Union => "un",
                SymbolKind::ValueParam => "vp",
                SymbolKind::Variant => "ev",
            },
            CompletionItemKind::Attribute => "at",
            CompletionItemKind::Binding => "bn",
            CompletionItemKind::BuiltinType => "bt",
            CompletionItemKind::Keyword => "kw",
            CompletionItemKind::Method => "me",
            CompletionItemKind::Snippet => "sn",
            CompletionItemKind::UnresolvedReference => "??",
        }
    }
}

#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub(crate) enum CompletionKind {
    /// Parser-based keyword completion.
    Keyword,
    /// Your usual "complete all valid identifiers".
    Reference,
    /// "Secret sauce" completions.
    Magic,
    Snippet,
    Postfix,
    BuiltinType,
    Attribute,
}

#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum InsertTextFormat {
    PlainText,
    Snippet,
}

impl CompletionItem {
    pub(crate) fn new(
        completion_kind: CompletionKind,
        source_range: TextRange,
        label: impl Into<String>,
    ) -> Builder {
        let label = label.into();
        Builder {
            source_range,
            completion_kind,
            label,
            insert_text: None,
            insert_text_format: InsertTextFormat::PlainText,
            detail: None,
            documentation: None,
            lookup: None,
            kind: None,
            text_edit: None,
            deprecated: false,
            trigger_call_info: None,
            score: None,
            ref_match: None,
            import_to_add: None,
        }
    }

    /// What user sees in pop-up in the UI.
    pub fn label(&self) -> &str {
        &self.label
    }
    pub fn source_range(&self) -> TextRange {
        self.source_range
    }

    pub fn insert_text_format(&self) -> InsertTextFormat {
        self.insert_text_format
    }

    pub fn text_edit(&self) -> &TextEdit {
        &self.text_edit
    }

    /// Short one-line additional information, like a type
    pub fn detail(&self) -> Option<&str> {
        self.detail.as_deref()
    }
    /// A doc-comment
    pub fn documentation(&self) -> Option<Documentation> {
        self.documentation.clone()
    }
    /// What string is used for filtering.
    pub fn lookup(&self) -> &str {
        self.lookup.as_deref().unwrap_or(&self.label)
    }

    pub fn kind(&self) -> Option<CompletionItemKind> {
        self.kind
    }

    pub fn deprecated(&self) -> bool {
        self.deprecated
    }

    pub fn score(&self) -> Option<CompletionScore> {
        self.score
    }

    pub fn trigger_call_info(&self) -> bool {
        self.trigger_call_info
    }

    pub fn ref_match(&self) -> Option<(Mutability, CompletionScore)> {
        self.ref_match
    }

    pub fn import_to_add(&self) -> Option<&ImportEdit> {
        self.import_to_add.as_ref()
    }
}

/// An extra import to add after the completion is applied.
#[derive(Debug, Clone)]
pub struct ImportEdit {
    pub import: LocatedImport,
    pub scope: ImportScope,
}

impl ImportEdit {
    /// Attempts to insert the import to the given scope, producing a text edit.
    /// May return no edit in edge cases, such as scope already containing the import.
    pub fn to_text_edit(&self, cfg: InsertUseConfig) -> Option<TextEdit> {
        let _p = profile::span("ImportEdit::to_text_edit");

        let rewriter =
            insert_use::insert_use(&self.scope, mod_path_to_ast(&self.import.import_path), cfg);
        let old_ast = rewriter.rewrite_root()?;
        let mut import_insert = TextEdit::builder();
        algo::diff(&old_ast, &rewriter.rewrite(&old_ast)).into_text_edit(&mut import_insert);

        Some(import_insert.finish())
    }
}

/// A helper to make `CompletionItem`s.
#[must_use]
#[derive(Clone)]
pub(crate) struct Builder {
    source_range: TextRange,
    completion_kind: CompletionKind,
    import_to_add: Option<ImportEdit>,
    label: String,
    insert_text: Option<String>,
    insert_text_format: InsertTextFormat,
    detail: Option<String>,
    documentation: Option<Documentation>,
    lookup: Option<String>,
    kind: Option<CompletionItemKind>,
    text_edit: Option<TextEdit>,
    deprecated: bool,
    trigger_call_info: Option<bool>,
    score: Option<CompletionScore>,
    ref_match: Option<(Mutability, CompletionScore)>,
}

impl Builder {
    pub(crate) fn build(self) -> CompletionItem {
        let _p = profile::span("item::Builder::build");

        let mut label = self.label;
        let mut lookup = self.lookup;
        let mut insert_text = self.insert_text;

        if let Some(original_path) = self
            .import_to_add
            .as_ref()
            .and_then(|import_edit| import_edit.import.original_path.as_ref())
        {
            lookup = lookup.or_else(|| Some(label.clone()));
            insert_text = insert_text.or_else(|| Some(label.clone()));

            let original_path_label = original_path.to_string();
            if original_path_label.ends_with(&label) {
                label = original_path_label;
            } else {
                format_to!(label, " ({})", original_path)
            }
        }

        let text_edit = match self.text_edit {
            Some(it) => it,
            None => {
                TextEdit::replace(self.source_range, insert_text.unwrap_or_else(|| label.clone()))
            }
        };

        CompletionItem {
            source_range: self.source_range,
            label,
            insert_text_format: self.insert_text_format,
            text_edit,
            detail: self.detail,
            documentation: self.documentation,
            lookup,
            kind: self.kind,
            completion_kind: self.completion_kind,
            deprecated: self.deprecated,
            trigger_call_info: self.trigger_call_info.unwrap_or(false),
            score: self.score,
            ref_match: self.ref_match,
            import_to_add: self.import_to_add,
        }
    }
    pub(crate) fn lookup_by(mut self, lookup: impl Into<String>) -> Builder {
        self.lookup = Some(lookup.into());
        self
    }
    pub(crate) fn label(mut self, label: impl Into<String>) -> Builder {
        self.label = label.into();
        self
    }
    pub(crate) fn insert_text(mut self, insert_text: impl Into<String>) -> Builder {
        self.insert_text = Some(insert_text.into());
        self
    }
    pub(crate) fn insert_snippet(
        mut self,
        _cap: SnippetCap,
        snippet: impl Into<String>,
    ) -> Builder {
        self.insert_text_format = InsertTextFormat::Snippet;
        self.insert_text(snippet)
    }
    pub(crate) fn kind(mut self, kind: impl Into<CompletionItemKind>) -> Builder {
        self.kind = Some(kind.into());
        self
    }
    pub(crate) fn text_edit(mut self, edit: TextEdit) -> Builder {
        self.text_edit = Some(edit);
        self
    }
    pub(crate) fn snippet_edit(mut self, _cap: SnippetCap, edit: TextEdit) -> Builder {
        self.insert_text_format = InsertTextFormat::Snippet;
        self.text_edit(edit)
    }
    pub(crate) fn detail(self, detail: impl Into<String>) -> Builder {
        self.set_detail(Some(detail))
    }
    pub(crate) fn set_detail(mut self, detail: Option<impl Into<String>>) -> Builder {
        self.detail = detail.map(Into::into);
        if let Some(detail) = &self.detail {
            if never!(detail.contains('\n'), "multiline detail:\n{}", detail) {
                self.detail = Some(detail.splitn(2, '\n').next().unwrap().to_string());
            }
        }
        self
    }
    #[allow(unused)]
    pub(crate) fn documentation(self, docs: Documentation) -> Builder {
        self.set_documentation(Some(docs))
    }
    pub(crate) fn set_documentation(mut self, docs: Option<Documentation>) -> Builder {
        self.documentation = docs.map(Into::into);
        self
    }
    pub(crate) fn set_deprecated(mut self, deprecated: bool) -> Builder {
        self.deprecated = deprecated;
        self
    }
    pub(crate) fn set_score(mut self, score: CompletionScore) -> Builder {
        self.score = Some(score);
        self
    }
    pub(crate) fn trigger_call_info(mut self) -> Builder {
        self.trigger_call_info = Some(true);
        self
    }
    pub(crate) fn add_import(mut self, import_to_add: Option<ImportEdit>) -> Builder {
        self.import_to_add = import_to_add;
        self
    }
    pub(crate) fn ref_match(mut self, ref_match: (Mutability, CompletionScore)) -> Builder {
        self.ref_match = Some(ref_match);
        self
    }
}
<|endoftext|>


<commit_before>use libc::{c_int, c_char, c_double, c_void};
use ogr_enums::*;

#[link(name="gdal")]
extern {
    pub fn OSRNewSpatialReference(pszWKT: *const c_char) -> *mut c_void;
    pub fn OSRClone(hSRS: *const c_void) -> *mut c_void;
    pub fn OSRDestroySpatialReference(hSRS: *mut c_void) -> c_void;
    pub fn OSRImportFromEPSG(hSRS: *const c_void, nCode: c_int) -> OGRErr;
    pub fn OSRImportFromProj4(hSRS: *mut c_void, proj4_string: *const c_char) -> OGRErr;
    pub fn OSRExportToWkt(hSRS: *const c_void, ppszReturn: &mut *const c_char) -> OGRErr;
    pub fn OSRExportToPrettyWkt(hSRS: *const c_void, ppszReturn: &mut *const c_char, bSimplify: c_int) -> OGRErr;
    pub fn OSRExportToProj4(hSRS: *const c_void, ppszReturn: &mut *const c_char) -> OGRErr;
    pub fn OSRIsSame(hSRS1: *const c_void, hSRS2: *const c_void) -> bool;
    pub fn OCTNewCoordinateTransformation(hSourceSRS: *const c_void, hTargetSRS: *const c_void) -> *mut c_void;
    pub fn OCTDestroyCoordinateTransformation(hCT: *mut c_void) -> c_void;
    pub fn OCTTransform(hCT: *const c_void, nCount: c_int, x: *mut c_double, y: *mut c_double, z: *mut c_double) -> bool;
}

pub const OGRERR_NONE: c_int = 0;
<commit_msg>Remove unused value<commit_after>use libc::{c_int, c_char, c_double, c_void};
use ogr_enums::*;

#[link(name="gdal")]
extern {
    pub fn OSRNewSpatialReference(pszWKT: *const c_char) -> *mut c_void;
    pub fn OSRClone(hSRS: *const c_void) -> *mut c_void;
    pub fn OSRDestroySpatialReference(hSRS: *mut c_void) -> c_void;
    pub fn OSRImportFromEPSG(hSRS: *const c_void, nCode: c_int) -> OGRErr;
    pub fn OSRImportFromProj4(hSRS: *mut c_void, proj4_string: *const c_char) -> OGRErr;
    pub fn OSRExportToWkt(hSRS: *const c_void, ppszReturn: &mut *const c_char) -> OGRErr;
    pub fn OSRExportToPrettyWkt(hSRS: *const c_void, ppszReturn: &mut *const c_char, bSimplify: c_int) -> OGRErr;
    pub fn OSRExportToProj4(hSRS: *const c_void, ppszReturn: &mut *const c_char) -> OGRErr;
    pub fn OSRIsSame(hSRS1: *const c_void, hSRS2: *const c_void) -> bool;
    pub fn OCTNewCoordinateTransformation(hSourceSRS: *const c_void, hTargetSRS: *const c_void) -> *mut c_void;
    pub fn OCTDestroyCoordinateTransformation(hCT: *mut c_void) -> c_void;
    pub fn OCTTransform(hCT: *const c_void, nCount: c_int, x: *mut c_double, y: *mut c_double, z: *mut c_double) -> bool;
}
<|endoftext|>


<commit_before>extern mod std;

// These tests used to be separate files, but I wanted to refactor all
// the common code.

use cmp::Eq;
use std::ebml2;
use io::Writer;
use std::serialization2::{Serializable, Deserializable, deserialize};
use std::prettyprint2;

fn test_ser_and_deser<A:Eq Serializable Deserializable>(
    a1: A,
    expected: ~str
) {
    // check the pretty printer:
    let s = do io::with_str_writer |w| {
        a1.serialize(&prettyprint2::Serializer(w))
    };
    debug!("s == %?", s);
    assert s == expected;

    // check the EBML serializer:
    let bytes = do io::with_bytes_writer |wr| {
        let ebml_w = &ebml2::Serializer(wr);
        a1.serialize(ebml_w)
    };
    let d = ebml2::Doc(@bytes);
    let a2: A = deserialize(&ebml2::Deserializer(d));
    assert a1 == a2;
}

#[auto_serialize2]
enum Expr {
    Val(uint),
    Plus(@Expr, @Expr),
    Minus(@Expr, @Expr)
}

impl Expr : cmp::Eq {
    pure fn eq(other: &Expr) -> bool {
        match self {
            Val(e0a) => {
                match *other {
                    Val(e0b) => e0a == e0b,
                    _ => false
                }
            }
            Plus(e0a, e1a) => {
                match *other {
                    Plus(e0b, e1b) => e0a == e0b && e1a == e1b,
                    _ => false
                }
            }
            Minus(e0a, e1a) => {
                match *other {
                    Minus(e0b, e1b) => e0a == e0b && e1a == e1b,
                    _ => false
                }
            }
        }
    }
    pure fn ne(other: &Expr) -> bool { !self.eq(other) }
}

impl AnEnum : cmp::Eq {
    pure fn eq(other: &AnEnum) -> bool {
        self.v == other.v
    }
    pure fn ne(other: &AnEnum) -> bool { !self.eq(other) }
}

impl Point : cmp::Eq {
    pure fn eq(other: &Point) -> bool {
        self.x == other.x && self.y == other.y
    }
    pure fn ne(other: &Point) -> bool { !self.eq(other) }
}

impl<T:cmp::Eq> Quark<T> : cmp::Eq {
    pure fn eq(other: &Quark<T>) -> bool {
        match self {
            Top(ref q) => {
                match *other {
                    Top(ref r) => q == r,
                    Bottom(_) => false
                }
            },
            Bottom(ref q) => {
                match *other {
                    Top(_) => false,
                    Bottom(ref r) => q == r
                }
            },
        }
    }
    pure fn ne(other: &Quark<T>) -> bool { !self.eq(other) }
}

impl CLike : cmp::Eq {
    pure fn eq(other: &CLike) -> bool {
        self as int == *other as int
    }
    pure fn ne(other: &CLike) -> bool { !self.eq(other) }
}

#[auto_serialize2]
type Spanned<T> = {lo: uint, hi: uint, node: T};

impl<T:cmp::Eq> Spanned<T> : cmp::Eq {
    pure fn eq(other: &Spanned<T>) -> bool {
        self.lo == other.lo && self.hi == other.hi && self.node == other.node
    }
    pure fn ne(other: &Spanned<T>) -> bool { !self.eq(other) }
}

#[auto_serialize2]
type SomeRec = {v: ~[uint]};

#[auto_serialize2]
enum AnEnum = SomeRec;

#[auto_serialize2]
type Point = {x: uint, y: uint};

#[auto_serialize2]
enum Quark<T> {
    Top(T),
    Bottom(T)
}

#[auto_serialize2]
enum CLike { A, B, C }

fn main() {
    test_ser_and_deser(Plus(@Minus(@Val(3u), @Val(10u)),
                            @Plus(@Val(22u), @Val(5u))),
                       ~"Plus(@Minus(@Val(3u), @Val(10u)), \
                        @Plus(@Val(22u), @Val(5u)))");

    test_ser_and_deser({lo: 0u, hi: 5u, node: 22u},
                       ~"{lo: 0u, hi: 5u, node: 22u}");

    test_ser_and_deser(AnEnum({v: ~[1u, 2u, 3u]}),
                       ~"AnEnum({v: ~[1u, 2u, 3u]})");

    test_ser_and_deser({x: 3u, y: 5u}, ~"{x: 3u, y: 5u}");

    test_ser_and_deser(@[1u, 2u, 3u], ~"@[1u, 2u, 3u]");

    test_ser_and_deser(Top(22u), ~"Top(22u)");
    test_ser_and_deser(Bottom(222u), ~"Bottom(222u)");

    test_ser_and_deser(A, ~"A");
    test_ser_and_deser(B, ~"B");
}
<commit_msg>Add struct to auto_serialize2 test<commit_after>extern mod std;

// These tests used to be separate files, but I wanted to refactor all
// the common code.

use cmp::Eq;
use std::ebml2;
use io::Writer;
use std::serialization2::{Serializable, Deserializable, deserialize};
use std::prettyprint2;

fn test_ser_and_deser<A:Eq Serializable Deserializable>(
    a1: &A,
    +expected: ~str
) {
    // check the pretty printer:
    let s = do io::with_str_writer |w| {
        a1.serialize(&prettyprint2::Serializer(w))
    };
    debug!("s == %?", s);
    assert s == expected;

    // check the EBML serializer:
    let bytes = do io::with_bytes_writer |wr| {
        let ebml_w = &ebml2::Serializer(wr);
        a1.serialize(ebml_w)
    };
    let d = ebml2::Doc(@bytes);
    let a2: A = deserialize(&ebml2::Deserializer(d));
    assert *a1 == a2;
}

#[auto_serialize2]
enum Expr {
    Val(uint),
    Plus(@Expr, @Expr),
    Minus(@Expr, @Expr)
}

impl Expr : cmp::Eq {
    pure fn eq(other: &Expr) -> bool {
        match self {
            Val(e0a) => {
                match *other {
                    Val(e0b) => e0a == e0b,
                    _ => false
                }
            }
            Plus(e0a, e1a) => {
                match *other {
                    Plus(e0b, e1b) => e0a == e0b && e1a == e1b,
                    _ => false
                }
            }
            Minus(e0a, e1a) => {
                match *other {
                    Minus(e0b, e1b) => e0a == e0b && e1a == e1b,
                    _ => false
                }
            }
        }
    }
    pure fn ne(other: &Expr) -> bool { !self.eq(other) }
}

impl AnEnum : cmp::Eq {
    pure fn eq(other: &AnEnum) -> bool {
        self.v == other.v
    }
    pure fn ne(other: &AnEnum) -> bool { !self.eq(other) }
}

impl Point : cmp::Eq {
    pure fn eq(other: &Point) -> bool {
        self.x == other.x && self.y == other.y
    }
    pure fn ne(other: &Point) -> bool { !self.eq(other) }
}

impl<T:cmp::Eq> Quark<T> : cmp::Eq {
    pure fn eq(other: &Quark<T>) -> bool {
        match self {
            Top(ref q) => {
                match *other {
                    Top(ref r) => q == r,
                    Bottom(_) => false
                }
            },
            Bottom(ref q) => {
                match *other {
                    Top(_) => false,
                    Bottom(ref r) => q == r
                }
            },
        }
    }
    pure fn ne(other: &Quark<T>) -> bool { !self.eq(other) }
}

impl CLike : cmp::Eq {
    pure fn eq(other: &CLike) -> bool {
        self as int == *other as int
    }
    pure fn ne(other: &CLike) -> bool { !self.eq(other) }
}

#[auto_serialize2]
type Spanned<T> = {lo: uint, hi: uint, node: T};

impl<T:cmp::Eq> Spanned<T> : cmp::Eq {
    pure fn eq(other: &Spanned<T>) -> bool {
        self.lo == other.lo && self.hi == other.hi && self.node == other.node
    }
    pure fn ne(other: &Spanned<T>) -> bool { !self.eq(other) }
}

#[auto_serialize2]
type SomeRec = {v: ~[uint]};

#[auto_serialize2]
enum AnEnum = SomeRec;

#[auto_serialize2]
struct Point {x: uint, y: uint}

#[auto_serialize2]
enum Quark<T> {
    Top(T),
    Bottom(T)
}

#[auto_serialize2]
enum CLike { A, B, C }

fn main() {
    test_ser_and_deser(&Plus(@Minus(@Val(3u), @Val(10u)),
                             @Plus(@Val(22u), @Val(5u))),
                       ~"Plus(@Minus(@Val(3u), @Val(10u)), \
                        @Plus(@Val(22u), @Val(5u)))");

    test_ser_and_deser(&{lo: 0u, hi: 5u, node: 22u},
                       ~"{lo: 0u, hi: 5u, node: 22u}");

    test_ser_and_deser(&AnEnum({v: ~[1u, 2u, 3u]}),
                       ~"AnEnum({v: ~[1u, 2u, 3u]})");

    test_ser_and_deser(&Point {x: 3u, y: 5u}, ~"Point {x: 3u, y: 5u}");

    test_ser_and_deser(&@[1u, 2u, 3u], ~"@[1u, 2u, 3u]");

    test_ser_and_deser(&Top(22u), ~"Top(22u)");
    test_ser_and_deser(&Bottom(222u), ~"Bottom(222u)");

    test_ser_and_deser(&A, ~"A");
    test_ser_and_deser(&B, ~"B");
}
<|endoftext|>


<commit_before>tag foo[T] {
  arm(T);
}

fn altfoo[T](foo[T] f) {
  auto hit = false;
  alt (f) {
    case (arm[T](?x)) {
      log "in arm";
      hit = true;
    }
  }
  check (hit);
}

fn main() {
  altfoo[int](arm[int](10));
}
<commit_msg>rustc: Make test/run-pass/generic-tag-alt.rs valid Rust; it was passing a dynamically-sized value by value instead of by alias<commit_after>tag foo[T] {
  arm(T);
}

fn altfoo[T](&foo[T] f) {
  auto hit = false;
  alt (f) {
    case (arm[T](?x)) {
      log "in arm";
      hit = true;
    }
  }
  check (hit);
}

fn main() {
  altfoo[int](arm[int](10));
}
<|endoftext|>


<commit_before>use bytes::Buf;
use futures::{Async, Future, Poll};
use h2::{Reason, SendStream};
use http::HeaderMap;
use http::header::{CONNECTION, TRANSFER_ENCODING};

use ::body::Payload;

mod client;
mod server;

pub(crate) use self::client::Client;
pub(crate) use self::server::Server;

fn strip_connection_headers(headers: &mut HeaderMap) {
    if headers.remove(TRANSFER_ENCODING).is_some() {
        trace!("removed illegal Transfer-Encoding header");
    }
    if headers.contains_key(CONNECTION) {
        warn!("Connection header illegal in HTTP/2");
        //TODO: actually remove it, after checking the value
        //and removing all related headers
    }
}

// body adapters used by both Client and Server

struct PipeToSendStream<S>
where
    S: Payload,
{
    body_tx: SendStream<SendBuf<S::Data>>,
    stream: S,
}

impl<S> PipeToSendStream<S>
where
    S: Payload,
{
    fn new(stream: S, tx: SendStream<SendBuf<S::Data>>) -> PipeToSendStream<S> {
        PipeToSendStream {
            body_tx: tx,
            stream: stream,
        }
    }
}

impl<S> Future for PipeToSendStream<S>
where
    S: Payload,
{
    type Item = ();
    type Error = ::Error;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        loop {
            // TODO: make use of flow control on SendStream
            // If you're looking at this and thinking of trying to fix this TODO,
            // you may want to look at:
            // https://docs.rs/h2/0.1.*/h2/struct.SendStream.html
            //
            // With that doc open, we'd want to do these things:
            // - check self.body_tx.capacity() to see if we can send *any* data
            // - if > 0:
            // -   poll self.stream
            // -   reserve chunk.len() more capacity (because its about to be used)?
            // -   send the chunk
            // - else:
            // -   try reserve a smallish amount of capacity
            // -   call self.body_tx.poll_capacity(), return if NotReady
            match self.stream.poll_data() {
                Ok(Async::Ready(Some(chunk))) => {
                    let is_eos = self.stream.is_end_stream();
                    trace!(
                        "send body chunk: {} bytes, eos={}",
                        chunk.remaining(),
                        is_eos,
                    );

                    let buf = SendBuf(Some(chunk));
                    self.body_tx.send_data(buf, is_eos)
                        .map_err(::Error::new_body_write)?;

                    if is_eos {
                        return Ok(Async::Ready(()))
                    }
                },
                Ok(Async::Ready(None)) => {
                    trace!("send body eos");
                    self.body_tx.send_data(SendBuf(None), true)
                        .map_err(::Error::new_body_write)?;
                    return Ok(Async::Ready(()));
                },
                Ok(Async::NotReady) => return Ok(Async::NotReady),
                Err(err) => {
                    let err = ::Error::new_user_body(err);
                    trace!("send body user stream error: {}", err);
                    self.body_tx.send_reset(Reason::INTERNAL_ERROR);
                    return Err(err);
                }
            }
        }
    }
}

struct SendBuf<B>(Option<B>);

impl<B: Buf> Buf for SendBuf<B> {
    #[inline]
    fn remaining(&self) -> usize {
        self.0
            .as_ref()
            .map(|b| b.remaining())
            .unwrap_or(0)
    }

    #[inline]
    fn bytes(&self) -> &[u8] {
        self.0
            .as_ref()
            .map(|b| b.bytes())
            .unwrap_or(&[])
    }

    #[inline]
    fn advance(&mut self, cnt: usize) {
        self.0
            .as_mut()
            .map(|b| b.advance(cnt));
    }
}
<commit_msg>fix(http2): send trailers if Payload includes them<commit_after>use bytes::Buf;
use futures::{Async, Future, Poll};
use h2::{Reason, SendStream};
use http::HeaderMap;
use http::header::{CONNECTION, TRANSFER_ENCODING};

use ::body::Payload;

mod client;
mod server;

pub(crate) use self::client::Client;
pub(crate) use self::server::Server;

fn strip_connection_headers(headers: &mut HeaderMap) {
    if headers.remove(TRANSFER_ENCODING).is_some() {
        trace!("removed illegal Transfer-Encoding header");
    }
    if headers.contains_key(CONNECTION) {
        warn!("Connection header illegal in HTTP/2");
        //TODO: actually remove it, after checking the value
        //and removing all related headers
    }
}

// body adapters used by both Client and Server

struct PipeToSendStream<S>
where
    S: Payload,
{
    body_tx: SendStream<SendBuf<S::Data>>,
    data_done: bool,
    stream: S,
}

impl<S> PipeToSendStream<S>
where
    S: Payload,
{
    fn new(stream: S, tx: SendStream<SendBuf<S::Data>>) -> PipeToSendStream<S> {
        PipeToSendStream {
            body_tx: tx,
            data_done: false,
            stream: stream,
        }
    }

    fn on_err(&mut self, err: S::Error) -> ::Error {
        let err = ::Error::new_user_body(err);
        trace!("send body user stream error: {}", err);
        self.body_tx.send_reset(Reason::INTERNAL_ERROR);
        err
    }

    fn send_eos_frame(&mut self) -> ::Result<()> {
        trace!("send body eos");
        self.body_tx.send_data(SendBuf(None), true)
            .map_err(::Error::new_body_write)
    }
}

impl<S> Future for PipeToSendStream<S>
where
    S: Payload,
{
    type Item = ();
    type Error = ::Error;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        loop {
            if !self.data_done {
                // TODO: make use of flow control on SendStream
                // If you're looking at this and thinking of trying to fix this TODO,
                // you may want to look at:
                // https://docs.rs/h2/0.1.*/h2/struct.SendStream.html
                //
                // With that doc open, we'd want to do these things:
                // - check self.body_tx.capacity() to see if we can send *any* data
                // - if > 0:
                // -   poll self.stream
                // -   reserve chunk.len() more capacity (because its about to be used)?
                // -   send the chunk
                // - else:
                // -   try reserve a smallish amount of capacity
                // -   call self.body_tx.poll_capacity(), return if NotReady
                match try_ready!(self.stream.poll_data().map_err(|e| self.on_err(e))) {
                    Some(chunk) => {
                        let is_eos = self.stream.is_end_stream();
                        trace!(
                            "send body chunk: {} bytes, eos={}",
                            chunk.remaining(),
                            is_eos,
                        );

                        let buf = SendBuf(Some(chunk));
                        self.body_tx.send_data(buf, is_eos)
                            .map_err(::Error::new_body_write)?;

                        if is_eos {
                            return Ok(Async::Ready(()))
                        }
                    },
                    None => {
                        let is_eos = self.stream.is_end_stream();
                        if is_eos {
                            return self.send_eos_frame().map(Async::Ready);
                        } else {
                            self.data_done = true;
                            // loop again to poll_trailers
                        }
                    },
                }
            } else {
                match try_ready!(self.stream.poll_trailers().map_err(|e| self.on_err(e))) {
                    Some(trailers) => {
                        self.body_tx.send_trailers(trailers)
                            .map_err(::Error::new_body_write)?;
                        return Ok(Async::Ready(()));
                    },
                    None => {
                        // There were no trailers, so send an empty DATA frame...
                        return self.send_eos_frame().map(Async::Ready);
                    },
                }
            }
        }
    }
}

struct SendBuf<B>(Option<B>);

impl<B: Buf> Buf for SendBuf<B> {
    #[inline]
    fn remaining(&self) -> usize {
        self.0
            .as_ref()
            .map(|b| b.remaining())
            .unwrap_or(0)
    }

    #[inline]
    fn bytes(&self) -> &[u8] {
        self.0
            .as_ref()
            .map(|b| b.bytes())
            .unwrap_or(&[])
    }

    #[inline]
    fn advance(&mut self, cnt: usize) {
        self.0
            .as_mut()
            .map(|b| b.advance(cnt));
    }
}
<|endoftext|>


<commit_before>extern mod extra;

use std::io::net::ip::SocketAddr;
use std::io::net::get_host_addresses;
use std::from_str::from_str;
use std::io::net::tcp::TcpStream;

use extra::url::Url;

use enums::*;
use connection::Connection;

mod macros;

pub struct Client {
    priv addr: SocketAddr,
    priv db: i64,
}

impl Client {

    /// creates a client.  The client will immediately connect but it will
    /// close the connection again until get_connection() is called.  The name
    /// resolution currently only happens initially.
    pub fn open(uri: &str) -> Result<Client, ConnectFailure> {
        let parsed_uri = try_unwrap!(from_str::<Url>(uri), Err(InvalidURI));
        ensure!(parsed_uri.scheme == ~"redis", Err(InvalidURI));

        let ip_addrs = try_unwrap!(get_host_addresses(parsed_uri.host), Err(InvalidURI));
        let ip_addr = try_unwrap!(ip_addrs.iter().next(), Err(HostNotFound));
        let port = try_unwrap!(from_str::<u16>(parsed_uri.port.clone()
            .unwrap_or(~"6379")), Err(InvalidURI));
        let db = from_str::<i64>(parsed_uri.path.trim_chars(&'/')).unwrap_or(0);

        let addr = SocketAddr {
            ip: *ip_addr,
            port: port
        };
        let _ = try_unwrap!(TcpStream::connect(addr), Err(ConnectionRefused));

        Ok(Client {
            addr: addr,
            db: db,
        })
    }

    /// returns an independent connection for this client.  This currently
    /// does not put it into a pool.
    pub fn get_connection(&self) -> Result<Connection, ConnectFailure> {
        Connection::new(self.addr, self.db)
    }
}
<commit_msg>Properly handle connection failure from condition (ugly)<commit_after>extern mod extra;

use std::io::net::ip::SocketAddr;
use std::io::net::get_host_addresses;
use std::io::io_error;
use std::io::net::tcp::TcpStream;
use std::from_str::from_str;

use extra::url::Url;

use enums::*;
use connection::Connection;

mod macros;

pub struct Client {
    priv addr: SocketAddr,
    priv db: i64,
}

impl Client {

    /// creates a client.  The client will immediately connect but it will
    /// close the connection again until get_connection() is called.  The name
    /// resolution currently only happens initially.
    pub fn open(uri: &str) -> Result<Client, ConnectFailure> {
        let parsed_uri = try_unwrap!(from_str::<Url>(uri), Err(InvalidURI));
        ensure!(parsed_uri.scheme == ~"redis", Err(InvalidURI));

        let ip_addrs = try_unwrap!(get_host_addresses(parsed_uri.host), Err(InvalidURI));
        let ip_addr = try_unwrap!(ip_addrs.iter().next(), Err(HostNotFound));
        let port = try_unwrap!(from_str::<u16>(parsed_uri.port.clone()
            .unwrap_or(~"6379")), Err(InvalidURI));
        let db = from_str::<i64>(parsed_uri.path.trim_chars(&'/')).unwrap_or(0);

        let addr = SocketAddr {
            ip: *ip_addr,
            port: port
        };

        // pretty ugly way to figure out if we failed to connect.  Is there a
        // nicer way?
        let mut failed = false;
        io_error::cond.trap(|_e| {
            failed = true;
        }).inside(|| {
            let _ = try_unwrap!(TcpStream::connect(addr), failed = true);
        });
        if failed {
            return Err(ConnectionRefused);
        }

        Ok(Client {
            addr: addr,
            db: db,
        })
    }

    /// returns an independent connection for this client.  This currently
    /// does not put it into a pool.
    pub fn get_connection(&self) -> Result<Connection, ConnectFailure> {
        Connection::new(self.addr, self.db)
    }
}
<|endoftext|>


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

//! resource

pub mod usage;
pub use self::usage::*;
<commit_msg>inreoucing ResourceDesc<commit_after>// Copyright 2017 Dasein Phaos aka. Luxko
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! resource

pub mod usage;
pub use self::usage::*;

use format::*;
use swapchain::SampleDesc;

/// resource description
#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub struct ResourceDesc {
    pub dimension: ResourceDimension,
    pub alignment: u64,
    pub width: u64,
    pub height: u32,
    pub depth: u16,
    pub mip_levels: u16,
    pub format: DxgiFormat,
    pub sample_desc: SampleDesc,
    pub layout: TextureLayout,
    pub flags: ResourceFlags,
}

impl ResourceDesc{
    /// a buffer description
    #[inline]
    pub fn buffer(alignment: u64, size: u64, flags: ResourceFlags) -> ResourceDesc{
        ResourceDesc{
            dimension: RESOURCE_DIMENSION_BUFFER,
            alignment,
            width: size,
            height: 1,
            depth: 1,
            mip_levels: 1,
            format: DXGI_FORMAT_UNKNOWN,
            sample_desc: Default::default(),
            layout: TEXTURE_LAYOUT_ROW_MAJOR, // TODO: double check unknown
            flags: flags,
        }
    }

    /// a tex1d description
    #[inline]
    pub fn tex1d(
        alignment: u64, length: u64, array_size: u16,
        mip_levels: u16, format: DxgiFormat, flags: ResourceFlags
    ) -> ResourceDesc{
        ResourceDesc{
            dimension: RESOURCE_DIMENSION_TEXTURE1D,
            alignment,
            width: length,
            height: 1,
            depth: array_size,
            mip_levels: mip_levels,
            format: format,
            sample_desc: Default::default(),
            layout: TEXTURE_LAYOUT_UNKNOWN,
            flags: flags,
        }
    }

    /// a tex2d description
    #[inline]
    pub fn tex2d(
        alignment: u64, width: u64, height: u32,
        array_size: u16, mip_levels: u16,
        format: DxgiFormat, flags: ResourceFlags
    ) -> ResourceDesc{
        ResourceDesc{
            dimension: RESOURCE_DIMENSION_TEXTURE1D,
            alignment,
            width: width,
            height: height,
            depth: array_size,
            mip_levels: mip_levels,
            format: format,
            sample_desc: Default::default(),
            layout: TEXTURE_LAYOUT_UNKNOWN,
            flags: flags,
        }
    }

    /// a tex3d description
    #[inline]
    pub fn tex3d(
        alignment: u64, width: u64, height: u32,
        depth: u16, mip_levels: u16,
        format: DxgiFormat, flags: ResourceFlags
    ) -> ResourceDesc{
        ResourceDesc{
            dimension: RESOURCE_DIMENSION_TEXTURE1D,
            alignment, width, height, depth,
            mip_levels: mip_levels,
            format: format,
            sample_desc: Default::default(),
            layout: TEXTURE_LAYOUT_UNKNOWN,
            flags: flags,
        }
    }
}

impl From<ResourceDesc> for ::winapi::D3D12_RESOURCE_DESC {
    #[inline]
    fn from(desc: ResourceDesc) -> Self {
        unsafe{ ::std::mem::transmute(desc)}
    }
}

bitflags!{
    /// dimension i.e. type of the resource
    #[repr(C)]
    pub struct ResourceDimension: u32 {
        const RESOURCE_DIMENSION_UNKNOWN    = 0;
        const RESOURCE_DIMENSION_BUFFER     = 1;
        const RESOURCE_DIMENSION_TEXTURE1D  = 2;
        const RESOURCE_DIMENSION_TEXTURE2D  = 3;
        const RESOURCE_DIMENSION_TEXTURE3D  = 4;
    }
}

bitflags! {
    /// texture layout
    #[repr(C)]
    pub struct TextureLayout: u32 {
        /// adapter-dependent layout. driver choose optimal layout
        /// during resource creation
        const TEXTURE_LAYOUT_UNKNOWN                 = 0;
        /// data for the texture is stored in row-major order.
        /// only the following texture properties are supported:
        ///
        /// - `RESOURCE_DIMENSION_TEXTURE2D`
        /// - single mip level
        /// - single array slice
        /// 64kb alignment
        /// non-MSAA
        /// no `RESOURCE_FLAG_ALLOW_DEPTH_STENCIL`
        /// cannot be a YUV format
        ///
        /// Note that buffers are row major
        const TEXTURE_LAYOUT_ROW_MAJOR               = 1;
        const TEXTURE_LAYOUT_64KB_UNDEFINED_SWIZZLE  = 2;
        const TEXTURE_LAYOUT_64KB_STANDARD_SWIZZLE   = 3;
    }
}

impl Default for TextureLayout {
    #[inline]
    fn default() -> Self {
        TEXTURE_LAYOUT_UNKNOWN
    }
}

bitflags!{
    /// misc flags for resources. [more info](https://msdn.microsoft.com/zh-cn/library/windows/desktop/dn986742(v=vs.85).aspx)
    #[repr(C)]
    pub struct ResourceFlags: u32 {
        const RESOURCE_FLAG_NONE                       = 0;
        const RESOURCE_FLAG_ALLOW_RENDER_TARGET        = 0x1;
        const RESOURCE_FLAG_ALLOW_DEPTH_STENCIL        = 0x2;
        const RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS     = 0x4;
        const RESOURCE_FLAG_DENY_SHADER_RESOURCE       = 0x8;
        const RESOURCE_FLAG_ALLOW_CROSS_ADAPTER        = 0x10;
        const RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS  = 0x20;
    }
}

impl Default for ResourceFlags {
    #[inline]
    fn default() -> Self {
        RESOURCE_FLAG_NONE
    }
}

// TODO: find out a sound way to work with different types of resources
<|endoftext|>


<commit_before>use std::fs::read_to_string;
use std::fs::File;
use std::io::Read;
use std::path::Path;

use anyhow::{bail, Context, Result};
use pueue_lib::network::message::*;
use snap::read::FrameDecoder;
use tempfile::TempDir;

use crate::fixtures::*;
use crate::helper::*;

/// This function creates files `[1-20]` in the specified directory.
/// The return value is the expected output.
///
/// If `partial == true`, the expected output are only the last 5 lines.
fn create_test_files(path: &Path, partial: bool) -> Result<String> {
    // Convert numbers from 1 to 01, so they're correctly ordered when using `ls`.
    let names: Vec<String> = (0..20)
        .map(|number| {
            if number < 10 {
                let mut name = "0".to_string();
                name.push_str(&number.to_string());
                name
            } else {
                number.to_string()
            }
        })
        .collect();

    for name in &names {
        File::create(path.join(name.to_string()))?;
    }

    // Only return the last 5 lines if partial output is requested.
    if partial {
        return Ok((15..20).fold(String::new(), |mut full, name| {
            full.push_str(&name.to_string());
            full.push('\n');
            full
        }));
    }

    // Create the full expected output.
    let mut expected_output = names.join("\n");
    expected_output.push('\n');
    Ok(expected_output)
}

// Log output is send in a compressed form from the daemon.
// We have to unpack it first.
fn decompress_log(bytes: Vec<u8>) -> Result<String> {
    let mut decoder = FrameDecoder::new(&bytes[..]);
    let mut output = String::new();
    decoder
        .read_to_string(&mut output)
        .context("Failed to decompress remote log output")?;

    Ok(output)
}

#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
/// Make sure that receiving partial output from the daemon works.
async fn test_full_log() -> Result<()> {
    let daemon = daemon().await?;
    let shared = &daemon.settings.shared;

    // Create a temporary directory and put some files into it.
    let tempdir = TempDir::new().unwrap();
    let tempdir_path = tempdir.path();
    let expected_output =
        create_test_files(tempdir_path, false).context("Failed to create test files.")?;

    // Add a task that lists those files and wait for it to finish.
    let command = format!("ls {:?}", tempdir_path);
    assert_success(add_task(shared, &command, false).await?);
    wait_for_task_condition(shared, 0, |task| task.is_done()).await?;

    // Request all log lines
    let log_message = LogRequestMessage {
        task_ids: vec![0],
        send_logs: true,
        lines: None,
    };
    let response = send_message(shared, Message::Log(log_message)).await?;
    let logs = match response {
        Message::LogResponse(logs) => logs,
        _ => bail!("Received non LogResponse: {:#?}", response),
    };

    // Get the received output
    let logs = logs.get(&0).unwrap();
    let output = logs
        .output
        .clone()
        .context("Didn't find output on TaskLogMessage")?;
    let output = decompress_log(output)?;

    // Make sure it's the same
    assert_eq!(output, expected_output);

    Ok(())
}

#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
/// Make sure that receiving partial output from the daemon works.
async fn test_partial_log() -> Result<()> {
    let daemon = daemon().await?;
    let shared = &daemon.settings.shared;

    // Create a temporary directory and put some files into it.
    let tempdir = TempDir::new().unwrap();
    let tempdir_path = tempdir.path();
    let expected_output =
        create_test_files(tempdir_path, true).context("Failed to create test files.")?;

    // Add a task that lists those files and wait for it to finish.
    let command = format!("ls {:?}", tempdir_path);
    assert_success(add_task(shared, &command, false).await?);
    wait_for_task_condition(shared, 0, |task| task.is_done()).await?;

    // Debug output to see what the file actually looks like:
    let real_log_path = shared.pueue_directory().join("task_logs").join("0.log");
    let content = read_to_string(real_log_path).context("Failed to read actual file")?;
    println!("Actual log file contents: \n{}", content);

    // Request a partial log for task 0
    let log_message = LogRequestMessage {
        task_ids: vec![0],
        send_logs: true,
        lines: Some(5),
    };
    let response = send_message(shared, Message::Log(log_message)).await?;
    let logs = match response {
        Message::LogResponse(logs) => logs,
        _ => bail!("Received non LogResponse: {:#?}", response),
    };

    // Get the received output
    let logs = logs.get(&0).unwrap();
    let output = logs
        .output
        .clone()
        .context("Didn't find output on TaskLogMessage")?;
    let output = decompress_log(output)?;

    // Make sure it's the same
    assert_eq!(output, expected_output);

    Ok(())
}

#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
/// Ensure that stdout and stderr are properly ordered in log output.
async fn test_correct_log_order() -> Result<()> {
    let daemon = daemon().await?;
    let shared = &daemon.settings.shared;

    // Add a task that lists those files and wait for it to finish.
    let command = "echo 'test' && echo 'error' && echo 'test'";
    assert_success(add_task(shared, &command, false).await?);
    wait_for_task_condition(shared, 0, |task| task.is_done()).await?;

    // Request all log lines
    let log_message = LogRequestMessage {
        task_ids: vec![0],
        send_logs: true,
        lines: None,
    };
    let response = send_message(shared, Message::Log(log_message)).await?;
    let logs = match response {
        Message::LogResponse(logs) => logs,
        _ => bail!("Received non LogResponse: {:#?}", response),
    };

    // Get the received output
    let logs = logs.get(&0).unwrap();
    let output = logs
        .output
        .clone()
        .context("Didn't find output on TaskLogMessage")?;
    let output = decompress_log(output)?;

    // Make sure it's the same
    assert_eq!(output, "test\nerror\ntest\n");

    Ok(())
}
<commit_msg>fix: pueue-lib test clippy issues<commit_after>use std::fs::read_to_string;
use std::fs::File;
use std::io::Read;
use std::path::Path;

use anyhow::{bail, Context, Result};
use pueue_lib::network::message::*;
use snap::read::FrameDecoder;
use tempfile::TempDir;

use crate::fixtures::*;
use crate::helper::*;

/// This function creates files `[1-20]` in the specified directory.
/// The return value is the expected output.
///
/// If `partial == true`, the expected output are only the last 5 lines.
fn create_test_files(path: &Path, partial: bool) -> Result<String> {
    // Convert numbers from 1 to 01, so they're correctly ordered when using `ls`.
    let names: Vec<String> = (0..20)
        .map(|number| {
            if number < 10 {
                let mut name = "0".to_string();
                name.push_str(&number.to_string());
                name
            } else {
                number.to_string()
            }
        })
        .collect();

    for name in &names {
        File::create(path.join(name.to_string()))?;
    }

    // Only return the last 5 lines if partial output is requested.
    if partial {
        return Ok((15..20).fold(String::new(), |mut full, name| {
            full.push_str(&name.to_string());
            full.push('\n');
            full
        }));
    }

    // Create the full expected output.
    let mut expected_output = names.join("\n");
    expected_output.push('\n');
    Ok(expected_output)
}

// Log output is send in a compressed form from the daemon.
// We have to unpack it first.
fn decompress_log(bytes: Vec<u8>) -> Result<String> {
    let mut decoder = FrameDecoder::new(&bytes[..]);
    let mut output = String::new();
    decoder
        .read_to_string(&mut output)
        .context("Failed to decompress remote log output")?;

    Ok(output)
}

#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
/// Make sure that receiving partial output from the daemon works.
async fn test_full_log() -> Result<()> {
    let daemon = daemon().await?;
    let shared = &daemon.settings.shared;

    // Create a temporary directory and put some files into it.
    let tempdir = TempDir::new().unwrap();
    let tempdir_path = tempdir.path();
    let expected_output =
        create_test_files(tempdir_path, false).context("Failed to create test files.")?;

    // Add a task that lists those files and wait for it to finish.
    let command = format!("ls {:?}", tempdir_path);
    assert_success(add_task(shared, &command, false).await?);
    wait_for_task_condition(shared, 0, |task| task.is_done()).await?;

    // Request all log lines
    let log_message = LogRequestMessage {
        task_ids: vec![0],
        send_logs: true,
        lines: None,
    };
    let response = send_message(shared, Message::Log(log_message)).await?;
    let logs = match response {
        Message::LogResponse(logs) => logs,
        _ => bail!("Received non LogResponse: {:#?}", response),
    };

    // Get the received output
    let logs = logs.get(&0).unwrap();
    let output = logs
        .output
        .clone()
        .context("Didn't find output on TaskLogMessage")?;
    let output = decompress_log(output)?;

    // Make sure it's the same
    assert_eq!(output, expected_output);

    Ok(())
}

#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
/// Make sure that receiving partial output from the daemon works.
async fn test_partial_log() -> Result<()> {
    let daemon = daemon().await?;
    let shared = &daemon.settings.shared;

    // Create a temporary directory and put some files into it.
    let tempdir = TempDir::new().unwrap();
    let tempdir_path = tempdir.path();
    let expected_output =
        create_test_files(tempdir_path, true).context("Failed to create test files.")?;

    // Add a task that lists those files and wait for it to finish.
    let command = format!("ls {tempdir_path:?}");
    assert_success(add_task(shared, &command, false).await?);
    wait_for_task_condition(shared, 0, |task| task.is_done()).await?;

    // Debug output to see what the file actually looks like:
    let real_log_path = shared.pueue_directory().join("task_logs").join("0.log");
    let content = read_to_string(real_log_path).context("Failed to read actual file")?;
    println!("Actual log file contents: \n{}", content);

    // Request a partial log for task 0
    let log_message = LogRequestMessage {
        task_ids: vec![0],
        send_logs: true,
        lines: Some(5),
    };
    let response = send_message(shared, Message::Log(log_message)).await?;
    let logs = match response {
        Message::LogResponse(logs) => logs,
        _ => bail!("Received non LogResponse: {:#?}", response),
    };

    // Get the received output
    let logs = logs.get(&0).unwrap();
    let output = logs
        .output
        .clone()
        .context("Didn't find output on TaskLogMessage")?;
    let output = decompress_log(output)?;

    // Make sure it's the same
    assert_eq!(output, expected_output);

    Ok(())
}

#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
/// Ensure that stdout and stderr are properly ordered in log output.
async fn test_correct_log_order() -> Result<()> {
    let daemon = daemon().await?;
    let shared = &daemon.settings.shared;

    // Add a task that lists those files and wait for it to finish.
    let command = "echo 'test' && echo 'error' && echo 'test'";
    assert_success(add_task(shared, command, false).await?);
    wait_for_task_condition(shared, 0, |task| task.is_done()).await?;

    // Request all log lines
    let log_message = LogRequestMessage {
        task_ids: vec![0],
        send_logs: true,
        lines: None,
    };
    let response = send_message(shared, Message::Log(log_message)).await?;
    let logs = match response {
        Message::LogResponse(logs) => logs,
        _ => bail!("Received non LogResponse: {:#?}", response),
    };

    // Get the received output
    let logs = logs.get(&0).unwrap();
    let output = logs
        .output
        .clone()
        .context("Didn't find output on TaskLogMessage")?;
    let output = decompress_log(output)?;

    // Make sure it's the same
    assert_eq!(output, "test\nerror\ntest\n");

    Ok(())
}
<|endoftext|>