// Path: url/src/main/java/com/danisola/restify/url/types/Opt.java
// public class Opt<T> implements VarType<T> {
// 
//     public static <T> Opt<T> opt(VarType<T> varType) {
//         return new Opt<>(varType);
//     }
// 
//     private final VarType<T> varType;
// 
//     private Opt(VarType<T> varType) {
//         this.varType = varType;
//     }
// 
//     @Override
//     public String getId() {
//         return varType.getId();
//     }
// 
//     @Override
//     public String getVarPattern() {
//         return varType.getVarPattern();
//     }
// 
//     @Override
//     public T convert(String value) throws Exception {
//         if ("".equals(value)) {
//             return null;
//         }
//         return varType.convert(value);
//     }
// }
// 
// Path: url/src/main/java/com/danisola/restify/url/types/VarType.java
// public interface VarType<T> {
// 
//     public String getId();
// 
//     public String getVarPattern();
// 
//     public T convert(String value) throws Exception;
// }
// 
// Path: url/src/main/java/com/danisola/restify/url/ParsedUrl.java
// static List<NameValuePair> parseParams(String query) {
//     if (query == null || query.isEmpty()) {
//         return Collections.emptyList();
//     }
// 
//     List<NameValuePair> pairs = new ArrayList<>();
//     int startIndex = 0;
//     int queryLength = query.length();
//     while (startIndex < queryLength) {
//         int equalsIndex = query.indexOf("=", startIndex);
//         checkState(equalsIndex > 0, "Parameters are not well formatted: %s", query);
//         String paramName = query.substring(startIndex, equalsIndex);
//         String paramValue;
//         int andIndex = query.indexOf("&", equalsIndex);
//         if (andIndex > equalsIndex) {
//             paramValue = query.substring(equalsIndex + 1, andIndex);
//             startIndex = andIndex + 1;
//         } else {
//             paramValue = query.substring(equalsIndex + 1);
//             startIndex = queryLength;
//         }
//         pairs.add(new NameValuePair(paramName, paramValue));
//     }
//     return pairs;
// }
// 
// Path: url/src/main/java/com/danisola/restify/url/RestUrl.java
// static RestUrl invalidRestUrl(String errorMsg) {
//     return new RestUrl(null, null, errorMsg);
// }
// 
// Path: url/src/main/java/com/danisola/restify/url/RestUrl.java
// static RestUrl restUrl(VarType[] types, Object[] values) {
//     return new RestUrl(types, values, null);
// }


import com.danisola.restify.url.types.Opt;
import com.danisola.restify.url.types.VarType;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static com.danisola.restify.url.ParsedUrl.parseParams;
import static com.danisola.restify.url.RestUrl.invalidRestUrl;
import static com.danisola.restify.url.RestUrl.restUrl;
        Matcher pathMatcher = pathPattern.matcher(path);
        if (!pathMatcher.matches()) {
            return RestUrl.invalidRestUrl("Path '" + path + "' does not match the pattern '" + pathPattern + "'");
        }

        for (VarType type : pathTypes) {
            try {
                varIndex = extract(pathMatcher, type, varIndex, restUrl);
            } catch (Exception ex) {
                return RestUrl.invalidRestUrl("Invalid value for variable '" + type.getId() + "': " + pathMatcher.group(type.getId()));
            }
        }

        // Processing param variables
        List<NameValuePair> valuePairs = parseParams(queryString);
        for (ParamVar paramVar : paramVars) {
            String paramName = paramVar.getName();
            NameValuePair valuePair = getValuePairWithName(paramName, valuePairs);
            if (valuePair == null) {
                if (areOptional(paramVar.getTypes())) {
                    varIndex = setNull(paramVar.getTypes(), varIndex, restUrl);
                    continue;
                } else {
                    return RestUrl.invalidRestUrl("Parameter '" + paramName + "' was not set");
                }
            }

            String paramValue = valuePair.getValue();
            Matcher paramMatcher = paramVar.getValuePattern().matcher(paramValue);
            if (!paramMatcher.matches()) {
// Path: url/src/main/java/com/danisola/restify/url/types/Opt.java
// public class Opt<T> implements VarType<T> {
// 
//     public static <T> Opt<T> opt(VarType<T> varType) {
//         return new Opt<>(varType);
//     }
// 
//     private final VarType<T> varType;
// 
//     private Opt(VarType<T> varType) {
//         this.varType = varType;
//     }
// 
//     @Override
//     public String getId() {
//         return varType.getId();
//     }
// 
//     @Override
//     public String getVarPattern() {
//         return varType.getVarPattern();
//     }
// 
//     @Override
//     public T convert(String value) throws Exception {
//         if ("".equals(value)) {
//             return null;
//         }
//         return varType.convert(value);
//     }
// }
// 
// Path: url/src/main/java/com/danisola/restify/url/types/VarType.java
// public interface VarType<T> {
// 
//     public String getId();
// 
//     public String getVarPattern();
// 
//     public T convert(String value) throws Exception;
// }
// 
// Path: url/src/main/java/com/danisola/restify/url/ParsedUrl.java
// static List<NameValuePair> parseParams(String query) {
//     if (query == null || query.isEmpty()) {
//         return Collections.emptyList();
//     }
// 
//     List<NameValuePair> pairs = new ArrayList<>();
//     int startIndex = 0;
//     int queryLength = query.length();
//     while (startIndex < queryLength) {
//         int equalsIndex = query.indexOf("=", startIndex);
//         checkState(equalsIndex > 0, "Parameters are not well formatted: %s", query);
//         String paramName = query.substring(startIndex, equalsIndex);
//         String paramValue;
//         int andIndex = query.indexOf("&", equalsIndex);
//         if (andIndex > equalsIndex) {
//             paramValue = query.substring(equalsIndex + 1, andIndex);
//             startIndex = andIndex + 1;
//         } else {
//             paramValue = query.substring(equalsIndex + 1);
//             startIndex = queryLength;
//         }
//         pairs.add(new NameValuePair(paramName, paramValue));
//     }
//     return pairs;
// }
// 
// Path: url/src/main/java/com/danisola/restify/url/RestUrl.java
// static RestUrl invalidRestUrl(String errorMsg) {
//     return new RestUrl(null, null, errorMsg);
// }
// 
// Path: url/src/main/java/com/danisola/restify/url/RestUrl.java
// static RestUrl restUrl(VarType[] types, Object[] values) {
//     return new RestUrl(types, values, null);
// }


// Path: url/src/main/java/com/danisola/restify/url/RestParser.java
import com.danisola.restify.url.types.Opt;
import com.danisola.restify.url.types.VarType;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static com.danisola.restify.url.ParsedUrl.parseParams;
import static com.danisola.restify.url.RestUrl.invalidRestUrl;
import static com.danisola.restify.url.RestUrl.restUrl;

        Matcher pathMatcher = pathPattern.matcher(path);
        if (!pathMatcher.matches()) {
            return RestUrl.invalidRestUrl("Path '" + path + "' does not match the pattern '" + pathPattern + "'");
        }

        for (VarType type : pathTypes) {
            try {
                varIndex = extract(pathMatcher, type, varIndex, restUrl);
            } catch (Exception ex) {
                return RestUrl.invalidRestUrl("Invalid value for variable '" + type.getId() + "': " + pathMatcher.group(type.getId()));
            }
        }

        // Processing param variables
        List<NameValuePair> valuePairs = parseParams(queryString);
        for (ParamVar paramVar : paramVars) {
            String paramName = paramVar.getName();
            NameValuePair valuePair = getValuePairWithName(paramName, valuePairs);
            if (valuePair == null) {
                if (areOptional(paramVar.getTypes())) {
                    varIndex = setNull(paramVar.getTypes(), varIndex, restUrl);
                    continue;
                } else {
                    return RestUrl.invalidRestUrl("Parameter '" + paramName + "' was not set");
                }
            }

            String paramValue = valuePair.getValue();
            Matcher paramMatcher = paramVar.getValuePattern().matcher(paramValue);
            if (!paramMatcher.matches()) {
                return RestUrl.invalidRestUrl("Parameter '" + paramName + "=" + paramValue + "' does not match pattern");