Exheredludis/paludis/fuzzy_finder.cc
Saleem Abdulrasool 1288dd799f fuzzy-finder: convert to c++11, hoist check
Hoist the length check out of the loop.  This avoids an unnecessary call
each iteration through the loop.  Use some C++11 constructs to simplify
the logic a bit.
2017-08-07 18:58:12 -07:00

237 lines
7.2 KiB
C++

/* vim: set sw=4 sts=4 et foldmethod=syntax : */
/*
* Copyright (c) 2007 Fernando J. Pereda
*
* This file is part of the Paludis package manager. Paludis is free software;
* you can redistribute it and/or modify it under the terms of the GNU General
* Public License version 2, as published by the Free Software Foundation.
*
* Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <paludis/fuzzy_finder.hh>
#include <paludis/environment.hh>
#include <paludis/repository.hh>
#include <paludis/package_id.hh>
#include <paludis/name.hh>
#include <paludis/user_dep_spec.hh>
#include <paludis/generator.hh>
#include <paludis/filter.hh>
#include <paludis/filter_handler.hh>
#include <paludis/filtered_generator.hh>
#include <paludis/selection.hh>
#include <paludis/util/pimp-impl.hh>
#include <paludis/util/wrapped_forward_iterator-impl.hh>
#include <paludis/util/damerau_levenshtein.hh>
#include <paludis/util/options.hh>
#include <paludis/util/stringify.hh>
#include <paludis/util/set-impl.hh>
#include <paludis/util/sequence-impl.hh>
#include <list>
#include <algorithm>
#include <set>
#include <cctype>
using namespace paludis;
namespace
{
bool char_0_cost(char c)
{
return (c == '-' || c == '_');
}
std::string tolower_0_cost(const std::string & s)
{
std::string res(s);
std::string::iterator e(std::remove_if(res.begin(), res.end(), char_0_cost));
std::string res2(res.begin(), e);
std::transform(res.begin(), e, res2.begin(), ::tolower);
return res2;
}
class FuzzyPackageNameFilterHandler :
public AllFilterHandlerBase
{
private:
std::string _package;
DamerauLevenshtein _distance_calculator;
unsigned _threshold;
char _first_char;
public:
FuzzyPackageNameFilterHandler(const std::string & package) :
_package(package),
_distance_calculator(tolower_0_cost(package)),
_threshold(package.length() <= 4 ? 1 : 2),
_first_char(tolower(package[0]))
{
}
std::shared_ptr<const QualifiedPackageNameSet> packages(
const Environment * const,
const std::shared_ptr<const RepositoryNameSet> &,
const std::shared_ptr<const QualifiedPackageNameSet> &) const override;
std::string as_string() const override
{
return "packages fuzzily like " + _package;
}
};
std::shared_ptr<const QualifiedPackageNameSet>
FuzzyPackageNameFilterHandler::packages(const Environment * const,
const std::shared_ptr<const RepositoryNameSet> &,
const std::shared_ptr<const QualifiedPackageNameSet> & pkgs) const
{
auto result = std::make_shared<QualifiedPackageNameSet>();
if (_package.length() < 3)
return result;
for (const auto & p : *pkgs)
if ((std::string::npos != stringify(p.package()).find(_package)) || (
tolower(p.package().value()[0]) == _first_char &&
_distance_calculator.distance_with(tolower_0_cost(p.package().value())) <= _threshold))
result->insert(p);
return result;
}
class FuzzyPackageName :
public Filter
{
public:
FuzzyPackageName(const std::string & p) :
Filter(std::make_shared<FuzzyPackageNameFilterHandler>(p))
{
}
};
}
namespace paludis
{
template <>
struct Imp<FuzzyCandidatesFinder>
{
std::list<QualifiedPackageName> candidates;
};
template <>
struct WrappedForwardIteratorTraits<FuzzyCandidatesFinder::CandidatesConstIteratorTag>
{
typedef std::list<QualifiedPackageName>::const_iterator UnderlyingIterator;
};
}
FuzzyCandidatesFinder::FuzzyCandidatesFinder(const Environment & e, const std::string & name, const Filter & filter) :
_imp()
{
Generator g = generator::All();
std::string package(name);
if (std::string::npos != name.find('/'))
{
PackageDepSpec pds(parse_user_package_dep_spec(name, &e, { }));
if (pds.package_ptr())
{
g = g & generator::Category(pds.package_ptr()->category());
package = stringify(pds.package_ptr()->package());
}
if (pds.in_repository_ptr())
g = g & generator::InRepository(*pds.in_repository_ptr());
if (pds.from_repository_ptr())
g = g & generator::FromRepository(*pds.from_repository_ptr());
}
std::shared_ptr<const PackageIDSequence> ids(e[selection::BestVersionOnly(g | FuzzyPackageName(package) | filter)]);
for (PackageIDSequence::ConstIterator i(ids->begin()), i_end(ids->end())
; i != i_end ; ++i)
_imp->candidates.push_back((*i)->name());
}
FuzzyCandidatesFinder::~FuzzyCandidatesFinder() = default;
FuzzyCandidatesFinder::CandidatesConstIterator
FuzzyCandidatesFinder::begin() const
{
return CandidatesConstIterator(_imp->candidates.begin());
}
FuzzyCandidatesFinder::CandidatesConstIterator
FuzzyCandidatesFinder::end() const
{
return CandidatesConstIterator(_imp->candidates.end());
}
namespace paludis
{
template <>
struct Imp<FuzzyRepositoriesFinder>
{
std::list<RepositoryName> candidates;
};
template <>
struct WrappedForwardIteratorTraits<FuzzyRepositoriesFinder::RepositoriesConstIteratorTag>
{
typedef std::list<RepositoryName>::const_iterator UnderlyingIterator;
};
}
FuzzyRepositoriesFinder::FuzzyRepositoriesFinder(const Environment & e, const std::string & name) :
_imp()
{
DamerauLevenshtein distance_calculator(tolower_0_cost(name));
unsigned threshold(name.length() <= 4 ? 1 : 2);
if (0 != name.compare(0, 2, std::string("x-")))
{
RepositoryName xname(std::string("x-" + name));
if (e.has_repository_named(xname))
{
_imp->candidates.push_back(xname);
return;
}
}
for (const auto & repository : e.repositories())
if (distance_calculator.distance_with(tolower_0_cost(stringify(repository->name()))) <= threshold)
_imp->candidates.push_back(repository->name());
}
FuzzyRepositoriesFinder::~FuzzyRepositoriesFinder() = default;
FuzzyRepositoriesFinder::RepositoriesConstIterator
FuzzyRepositoriesFinder::begin() const
{
return RepositoriesConstIterator(_imp->candidates.begin());
}
FuzzyRepositoriesFinder::RepositoriesConstIterator
FuzzyRepositoriesFinder::end() const
{
return RepositoriesConstIterator(_imp->candidates.end());
}
namespace paludis
{
template class WrappedForwardIterator<FuzzyCandidatesFinder::CandidatesConstIteratorTag, const QualifiedPackageName>;
template class WrappedForwardIterator<FuzzyRepositoriesFinder::RepositoriesConstIteratorTag, const RepositoryName>;
}