# Duckling (Rust)

A Rust port of Facebook's [Duckling](https://github.com/facebook/duckling) — a library for parsing natural language into structured data.

Given a string like `"tomorrow at 3pm"` or `"42 degrees fahrenheit"`, Duckling extracts "dimensions" such as URLs and times that may be of interest.

## Usage

```rust
use duckling::{parse, Entity, Locale, Lang, Context, Options, DimensionKind, DimensionValue,
               MeasurementValue, TimeValue, TimePoint, Grain};
use chrono::{NaiveDate, TimeZone, Utc};

let locale = Locale::new(Lang::EN, None);
let context = Context {
    reference_time: Utc.with_ymd_and_hms(2013, 2, 12, 4, 30, 0).unwrap(),
    ..Context::default()
};
let options = Options::default();

// Time — "tomorrow at 3pm" parses as a naive (wall-clock) time
let results = parse("tomorrow at 3pm", &locale, &[DimensionKind::Time], &context, &options);
assert_eq!(results, vec![Entity {
    body: "tomorrow at 3pm".into(),
    start: 0, end: 15, latent: None,
    value: DimensionValue::Time(TimeValue::Single(TimePoint::Naive {
        value: NaiveDate::from_ymd_opt(2013, 2, 13).unwrap().and_hms_opt(15, 0, 0).unwrap(),
        grain: Grain::Hour,
    })),
}]);

// Temperature
let results = parse("80 degrees fahrenheit", &locale, &[DimensionKind::Temperature], &context, &options);
assert_eq!(results, vec![Entity {
    body: "80 degrees fahrenheit".into(),
    start: 0, end: 21, latent: None,
    value: DimensionValue::Temperature(MeasurementValue::Value {
        value: 80.0, unit: "fahrenheit".into(),
    }),
}]);

// Numerals
let results = parse("forty-two", &locale, &[DimensionKind::Numeral], &context, &options);
assert_eq!(results, vec![Entity {
    body: "forty-two".into(),
    start: 0, end: 9, latent: None,
    value: DimensionValue::Numeral(42.0),
}]);
```

## Supported dimensions

Time, Numeral, Ordinal, Temperature, Distance, Volume, Quantity, AmountOfMoney, Duration, Email, PhoneNumber, Url, CreditCardNumber.

## Time: instant vs naive

Time values distinguish between absolute instants and wall-clock/calendar times:

- **Naive** — `"5 pm"`, `"tomorrow"`, `"March 15th"` — wall-clock times with no timezone assumption
- **Instant** — `"now"`, `"in 2 hours"`, `"5 pm EST"` — pinned to a specific UTC moment

```text
// Naive: no timezone baked in
TimeValue::Single(TimePoint::Naive { value: NaiveDateTime, grain })

// Instant: absolute UTC moment
TimeValue::Single(TimePoint::Instant { value: DateTime<Utc>, grain })
```

An explicit timezone (e.g. `"3pm CET"`) promotes any naive time to an instant.

## Acknowledgements

This is a Rust rewrite of [facebook/duckling](https://github.com/facebook/duckling), originally written in Haskell.
