wiki:Rules/ChargeRules

Charge validation rules

These rules provide validation for the keys charge and charge:conditional, following the syntax documented on Key:charge.

They check the syntax, but do not check whether the values makes sense (e.g. fuel for €0.01 per m³), that remains up to the user.

Bugs / enhancements?

Please feel free to message in case you find a bug. You can also edit this page if you're familiar with the syntax :)

Structure and maintenance

I tried to keep the rules as simple as possible, both to maintain and for the user. Hence it first validates some 'common' issues with more specific messages, before validating the full syntax with an unspecific message. Usually the regexes are as similar as possible for both validated keys, and in case there are differences, these are indicated in comments above the rules.

The asserts only work when the class selectors aren't present, so to use those you have to temporarily remove the class selector. Note that the asserts only serve to check the syntax, they don't always make sense. Be aware that the negative asserts also require the class selector to be (temporarily) removed, they just don't give any error when they remain enabled, hence they're not commented out by default.

For the conditional tag it usually only validates the part up to the first @ as the condition after the @ may contain any character (especially in fallback rules), thus making it difficult to ensure (with mapcss) that the condition is truly over.

Rules source code

meta {
    title: "Charge validation rules";
    version: "1.3_2024-09-02";
    description: "Rules to validate the charge and charge:conditional keys.";
    author: "Famlam";
    min-josm-version: "6534";
    link: "https://josm.openstreetmap.de/wiki/Rules/ChargeRules";
}



/* Currency symbols (e.g. €) instead of codes (EUR) */
*[charge:conditional=~/\p{Sc}/],
*[charge=~/\p{Sc}/] {
  throwWarning: tr("Expected 3-character currency code in {0}, found {1} instead", "{0.key}", get(regexp_match("^.*(\\p{Sc}).*$", tag("{0.key}")), 1));
  group: tr("Invalid value of charge");
  assertMatch: "node charge=€12";
  assertMatch: "node charge:conditional=\"0.22 $/liter @ (Mo-Fr)\"";
  assertNoMatch: "node charge=\"0.223 USD/m³\"";
  set .hasSimpleChargeFix;
  -osmoseItemClassLevel: "3091/30916:1/3";
}

/* Currency code before the value instead of after */
*[charge:conditional=~/^[A-Z]{3} ?[0-9]/]!.hasSimpleChargeFix,
*[charge=~/^[A-Z]{3} ?[0-9]/]!.hasSimpleChargeFix {
  throwWarning: tr("The value should be before the currency code {0} in {1}", substring(tag("{0.key}"), 0, 3), "{0.key}");
  group: tr("Invalid value of charge");
  /* For the positive assertions, first remove !.hasSimpleChargeFix from the rule; don't forget to restore */
  /*
  assertMatch: "node charge=EUR12.45";
  assertMatch: "node charge=\"EUR 12\"";
  */
  assertNoMatch: "node charge=\"0.223 USD/m³\"";
  set .hasSimpleChargeFix;
  -osmoseItemClassLevel: "3091/30916:2/3";
}

/* Missing mandatory space between value and currency code */
*[charge:conditional=~/^[0-9]+(?:\.[0-9]+)?[A-Z]{3}/]!.hasSimpleChargeFix,
*[charge=~/^[0-9]+(?:\.[0-9]+)?[A-Z]{3}/]!.hasSimpleChargeFix {
  throwWarning: tr("The value and the currency symbol in {0} should be separated by a space", "{0.key}");
  group: tr("Invalid value of charge");
  /* For the positive assertions, first remove !.hasSimpleChargeFix from the rule; don't forget to restore */
  /*
  assertMatch: "node charge=12EUR";
  */
  assertNoMatch: "node charge=\"12 EUR\"";
  assertNoMatch: "node charge=\"0.223 USD/m³\"";
  set .hasSimpleChargeFix;
  -osmoseItemClassLevel: "3091/30916:3/3";
}

/* No currency, just a value */
/* Regexes differ, charge:conditional has "( ?@| ?\/)", charge has "($| ?\/)" at the end */
*[charge:conditional=~/^[0-9]+(?:\.[0-9]+)?( ?@| ?\/)/]!.hasSimpleChargeFix,
*[charge=~/^[0-9]+(?:\.[0-9]+)?($| ?\/)/][charge!=0]!.hasSimpleChargeFix {
  throwWarning: tr("Currency not specified in {0}", "{0.key}");
  group: tr("Invalid value of charge");
  /* For the positive assertions, first remove !.hasSimpleChargeFix from the rule; don't forget to restore */
  /*
  assertMatch: "node charge=12";
  assertMatch: "node charge:conditional=\"12.45/liter @ (Mo-Fr)\"";
  */
  assertNoMatch: "node charge=\"0.223 USD/m³\"";
  assertNoMatch: "node charge=0";
  set .hasSimpleChargeFix;
  -osmoseItemClassLevel: "3091/30916:4/3";
}

/* yes/no instead of the actual fee */
/* Regexes differ, charge:conditional has " ?@", charge has "$" at the end */
*[charge:conditional][charge:conditional=~/^(yes|no) ?@/],
*[charge][charge=~/^(yes|no)$/] {
  throwWarning: tr("Key {0} should contain the amount charged", "{0.key}");
  group: tr("Invalid value of charge");
  suggestAlternative: "fee={0.value}";
  suggestAlternative: "toll={0.value}";
  suggestAlternative: "fee:conditional={0.value} @ ...";
  suggestAlternative: "toll:conditional={0.value} @ ...";
  set .hasSimpleChargeFix;
  -osmoseItemClassLevel: "3091/30916:5/3";
}

/* Comma as decimal separator */
*[charge:conditional=~/(^|\/ ?|; ?)[0-9]+,[0-9]/]!.hasSimpleChargeFix,
*[charge=~/(^|\/ ?|; ?)[0-9]+,[0-9]/]!.hasSimpleChargeFix {
  throwWarning: tr("unusual value of {0}: use . instead of , as decimal separator", "{0.key}");
  group: tr("Invalid value of charge");
  /* For the positive assertions, first remove !.hasSimpleChargeFix from the rule; don't forget to restore */
  /*
  assertMatch: "node charge=\"0,223 USD/30.4 m³\"";
  assertMatch: "node charge=\"0.223 USD/30,4 m³\"";
  assertMatch: "node charge=\"0.223 USD;30,4 USD\"";
  */
  assertNoMatch: "node charge=\"0.223 USD/30.4 m³\"";
  set .hasSimpleChargeFix;
  -osmoseItemClassLevel: "3091/30916:6/3";
}

/* Currency (likely) written out or wrong abbreviation used */
*[charge:conditional=~/^([0-9]+(?:\.[0-9]+)? ?[A-Za-z _-]+|[A-Za-z _-]+ ?[0-9]+(?:\.[0-9]+)?)/][charge:conditional!~/^[0-9]+(?:\.[0-9]+)? ?[A-Z]{3}/]!.hasSimpleChargeFix,
*[charge=~/^([0-9]+(?:\.[0-9]+)? ?[A-Za-z _-]+|[A-Za-z _-]+ ?[0-9]+(?:\.[0-9]+)?)/][charge!~/^[0-9]+(?:\.[0-9]+)? ?[A-Z]{3}/]!.hasSimpleChargeFix {
  throwWarning: tr("Invalid currency code in {0}, should be a 3-letter (uppercase) code after the value", "{0.key}");
  group: tr("Invalid value of charge");
  /* For the positive assertions, first remove !.hasSimpleChargeFix from the rule; don't forget to restore */
  /*
  assertMatch: "node charge=\"12 euro\"";
  assertMatch: "node charge=\"12 US dollar\"";
  assertMatch: "node charge=\"E12.95\"";
  assertMatch: "node charge=\"12.95E\"";
  assertMatch: "node charge=\"usdollar 12.95\"";
  assertMatch: "node charge=\"US Dollar 12.95\"";
  assertMatch: "node charge:conditional=\"12.45E @ (Mo-Fr)\"";
  */
  assertNoMatch: "node charge=\"0.223 USD/m³\"";
  assertNoMatch: "node charge=\"0.223USD\"";
  assertNoMatch: "node charge:conditional=\"0.223 USD @ (Mo-Fr)\"";
  set .hasSimpleChargeFix;
  -osmoseItemClassLevel: "3091/30916:7/3";
}

/* Abbreviated time unit */
/* Regexes differ, charge:conditional has "^[^@]+" and " ?@.", charge has "" and "$" at the start and end respectively */
*[charge:conditional=~/^[^@]+\/ ?(?:[0-9]+(?:\.[0-9]+)? ?)?(min|s|h|d|w) ?@./]!.hasSimpleChargeFix,
*[charge=~/\/ ?(?:[0-9]+(?:\.[0-9]+)? ?)?(min|s|h|d|w)$/]!.hasSimpleChargeFix {
  throwWarning: tr("Abbreviated time unit in {0}", "{0.key}");
  group: tr("Invalid value of charge");
  /* For the positive assertions, first remove !.hasSimpleChargeFix from the rule; don't forget to restore */
  /*
  assertMatch: "node charge=\"12 EUR/h\"";
  assertMatch: "node charge:conditional=\"12 EUR/h @ Sa\"";
  assertMatch: "node charge=\"12 EUR/0.5 h\"";
  */
  assertNoMatch: "node charge=\"0.22 USD/liter/hour\"";
  assertNoMatch: "node charge=\"12.223 YEN/12.4 m³/30.1 minutes\"";
  set .hasSimpleChargeFix;
  -osmoseItemClassLevel: "3091/30916:8/3";
}

/* Missing space between value and unit in the optional unit parts */
/* Regexes differ, charge:conditional has "^[^@]+" and "( ?@| ?\/).", charge has "" and "($| ?\/)" at the start and end respectively */
*[charge:conditional=~/^[^@]+\/ ?[0-9]+(?:\.[0-9]+)?[a-zA-Z³]+( ?@| ?\/)./]!.hasSimpleChargeFix,
*[charge=~/\/ ?[0-9]+(?:\.[0-9]+)?[a-zA-Z³]+($| ?\/)/]!.hasSimpleChargeFix {
  throwWarning: tr("No space between value and unit in {0}", "{0.key}");
  group: tr("Invalid value of charge");
  /* For the positive assertions, first remove !.hasSimpleChargeFix from the rule; don't forget to restore */
  /*
  assertMatch: "node charge=\"12 EUR/0.5hour\"";
  assertMatch: "node charge=\"12 EUR/3kWh/0.5 hour\"";
  assertMatch: "node charge=\"12 EUR/3kWh/0.5hour\"";
  assertMatch: "node charge=\"12 EUR/3 kWh/2hours\"";
  assertMatch: "node charge=\"12 EUR/3kWh/hour\"";
  assertMatch: "node charge=\"12 EUR/kWh/0.5hour\"";
  assertMatch: "node charge:conditional=\"12.5 EUR/0.5hour @ Sa\"";
  */
  assertNoMatch: "node charge=\"0.22 USD/liter/hour\"";
  assertNoMatch: "node charge=\"12.223 YEN/12.4 m³/30.1 minutes\"";
  set .hasSimpleChargeFix;
  -osmoseItemClassLevel: "3091/30916:9/3";
}

/* For charge:conditional, only validate the first condition. The @-condition may contain any character (esp. in fallback conditions).*/
/* The regexes are the same until the "(?:; ?(?!$)|$))+$" vs " ?@)."-part at the end */
*[charge:conditional][charge:conditional!~/^(?:[0-9]+(?:\.[0-9]+)? [A-Z]{3}(?: ?\/ ?(?:[0-9]+(?:\.[0-9]+)? )?[a-zA-Z³]+)?(?: ?\/ ?(?:[0-9]+(?:\.[0-9]+)? )?[a-z]{3,})? ?@)./]!.hasSimpleChargeFix,
*[charge][charge!=0][charge!~/^(?:[0-9]+(?:\.[0-9]+)? [A-Z]{3}(?: ?\/ ?(?:[0-9]+(?:\.[0-9]+)? )?[a-zA-Z³]+)?(?: ?\/ ?(?:[0-9]+(?:\.[0-9]+)? )?[a-z]{3,})?(?:; ?(?!$)|$))+$/]!.hasSimpleChargeFix {
  throwWarning: tr("The charge in {0} should be structured as <(decimal) number><space><(uppercase) three letter currency code>[/optional unit][/optional time unit]", "{0.key}");
  group: tr("Invalid value of charge");
  /* For the positive assertions, first remove !.hasSimpleChargeFix from the rule; don't forget to restore */
  /*
  assertMatch: "node charge=€12";
  assertMatch: "node charge=\"0.22 $/liter\"";
  assertMatch: "node charge=12.22";
  assertMatch: "node charge=\"12 EURO\"";
  assertMatch: "node charge=\"12 eur\"";
  assertMatch: "node charge=12EUR";
  assertMatch: "node charge=EUR12";
  assertMatch: "node charge:conditional=\"EUR12 @ (Fr-Su)\"";
  assertMatch: "node charge=\"12 EUR/5\"";
  assertMatch: "node charge=\"12 EUR;\"";
  */
  assertNoMatch: "node charge=0"; /* Zero is zero regardless of the unit, ignore */
  assertNoMatch: "node charge=\"12 EUR\"";
  assertNoMatch: "node charge=\"12 EUR/person; 6 EUR/child\"";
  assertNoMatch: "node charge=\"0.223 USD/liter\"";
  assertNoMatch: "node charge=\"0.22 USD/liter/hour\"";
  assertNoMatch: "node charge=\"0.22 USD / liter / hour\"";
  assertNoMatch: "node charge=\"12.223 YEN/1 person/1 hour\"";
  assertNoMatch: "node charge=\"12.223 YEN/12.4 m³/30.1 minutes\"";
  assertNoMatch: "node charge:conditional=\"12.223 YEN/12.4 m³/30.1 minutes @ Fr-Su\"";
  assertNoMatch: "node charge=\"12.223 YEN/100 kWh/day\"";
  -osmoseItemClassLevel: "3091/30916:999/3";
}

Rules_ChargeRules.validator.mapcss, Rules_ChargeRules.zip

Last modified 9 months ago Last modified on 2024-09-02T21:09:46+02:00
Note: See TracWiki for help on using the wiki.