public static bool FromBasePathAndName_NoThrow(
            string basePath,
            string moduleName,
            Func <string, bool> isPackage,
            Func <string, string, string> getModule,
            bool requireInitPy,
            out ModulePath modulePath,
            out bool isInvalid,
            out bool isMissing,
            out string errorParameter
            )
        {
            modulePath     = default;
            isInvalid      = false;
            isMissing      = false;
            errorParameter = null;

            var bits    = moduleName.Split('.');
            var lastBit = bits.Last();

            if (isPackage == null)
            {
                isPackage = f => !string.IsNullOrEmpty(GetPackageInitPy(f));
            }
            if (getModule == null)
            {
                getModule = (dir, mod) => {
                    var modPath = Path.Combine(dir, mod);
                    var pack    = GetPackageInitPy(modPath);
                    if (pack == null && !requireInitPy && Directory.Exists(modPath))
                    {
                        return(modPath);
                    }
                    else if (File.Exists(pack))
                    {
                        return(pack);
                    }
                    var mods = PathUtils.EnumerateFiles(dir, mod + "*", recurse: false).ToArray();
                    return(mods.FirstOrDefault(p => IsModuleNameMatch(PythonStubRegex, p, mod)) ??
                           mods.FirstOrDefault(p => IsModuleNameMatch(PythonBinaryRegex, p, mod)) ??
                           mods.FirstOrDefault(p => IsModuleNameMatch(PythonFileRegex, p, mod)));
                };
            }

            var   path      = basePath;
            var   allowStub = true;
            Match m;

            foreach (var bit in bits.Take(bits.Length - 1))
            {
                m = PythonPackageRegex.Match(bit);
                if (!m.Success || (!allowStub && m.Groups["stubs"].Success))
                {
                    isInvalid      = true;
                    errorParameter = bit;
                    return(false);
                }
                allowStub = false;

                if (string.IsNullOrEmpty(path))
                {
                    path = bit;
                }
                else
                {
                    path = Path.Combine(path, bit);
                }
                if (!isPackage(path))
                {
                    isMissing      = true;
                    errorParameter = path;
                    return(false);
                }
            }

            m = PythonPackageRegex.Match(lastBit);
            if (!m.Success || (!allowStub && m.Groups["stubs"].Success))
            {
                isInvalid      = true;
                errorParameter = moduleName;
                return(false);
            }
            path = getModule(path, lastBit);
            if (string.IsNullOrEmpty(path))
            {
                isMissing      = true;
                errorParameter = moduleName;
                return(false);
            }

            modulePath = new ModulePath(moduleName, path, basePath);
            return(true);
        }
        public static bool FromBasePathAndFile_NoThrow(
            string basePath,
            string sourceFile,
            bool isPackage,
            out ModulePath modulePath,
            out bool isInvalid,
            out bool isMissing
            )
        {
            modulePath = default;
            isInvalid  = false;
            isMissing  = false;

            if (!PathEqualityComparer.Instance.StartsWith(sourceFile, basePath, allowFullMatch: false))
            {
                return(false);
            }

            var nameMatch = PythonFileRegex.Match(PathUtils.GetFileName(sourceFile));

            if (!nameMatch.Success)
            {
                isInvalid = true;
                return(false);
            }
            var bits = new List <string> {
                nameMatch.Groups["name"].Value
            };

            var path         = PathUtils.TrimEndSeparator(PathUtils.GetParent(sourceFile));
            var lastWasStubs = false;

            while (PathEqualityComparer.Instance.StartsWith(path, basePath, allowFullMatch: false))
            {
                if (!isPackage && string.IsNullOrEmpty(GetPackageInitPy(path)))
                {
                    isMissing = true;
                    return(false);
                }

                if (lastWasStubs)
                {
                    isInvalid = true;
                    return(false);
                }

                var bit = PathUtils.GetFileName(path);
                var m   = PythonPackageRegex.Match(bit);
                if (!m.Success)
                {
                    isInvalid = true;
                    return(false);
                }
                lastWasStubs = m.Groups["stubs"].Success;
                bits.Add(PathUtils.GetFileName(path));
                path = PathUtils.TrimEndSeparator(PathUtils.GetParent(path));
            }

            if (!PathEqualityComparer.Instance.Equals(basePath, path))
            {
                isMissing = true;
                return(false);
            }

            var moduleName = string.Join(".", bits.AsEnumerable().Reverse());

            modulePath = new ModulePath(moduleName, sourceFile, basePath);

            return(true);
        }
 public static bool FromBasePathAndFile_NoThrow(
     string basePath,
     string sourceFile,
     out ModulePath modulePath
     ) => FromBasePathAndFile_NoThrow(basePath, sourceFile, false, out modulePath, out _, out _);