WaitTime and AIModel look like enums. GPAL.WithWaitFor(WaitTime.Forever) or .WithModel(AIModel.Claude35Sonnet). But they are actually small readonly structs with an implicit conversion from a plain value, so any string or number you type by hand works too. It is a pattern most C# developers have never needed, because most APIs do not face this exact problem.
A real C# enum is a fixed, closed set of named values baked into the compiled assembly. That is perfect when the set of valid values is genuinely fixed -- but some GPAL settings are not. WaitFor needs named special cases like WaitTime.Forever, WaitTime.Never, and WaitTime.Immediate, but also any millisecond value a caller might supply. WithModel needs well-known model IDs like AIModel.Claude35Sonnet and AIModel.GPT4o, but AI providers release new models constantly and GPAL cannot ship an update every time they do. A real enum cannot represent 'one of these named values, or literally anything else' -- the underlying type is fixed, and there is no implicit conversion from an arbitrary int or string into an enum value.
Both WaitTime and AIModel are public readonly structs with a single private field. An int or a string. The named options are static readonly fields pre-built with a particular value. The key piece is an implicit conversion operator: because it is implicit, a named constant, a string literal, and a runtime string variable all compile to the same parameter. ToString() unwraps the value when GPAL needs it.
public readonly struct AIModel
{
private readonly string _value;
internal AIModel(string value) => _value = value;
// Named constants - just pre-built instances of the struct
public static readonly AIModel GPT4o = new AIModel("gpt-4o");
public static readonly AIModel Claude35Sonnet = new AIModel("claude-3-5-sonnet-20241022");
public static readonly AIModel Grok2Latest = new AIModel("grok-2-latest");
// The escape hatch: any string is also a valid AIModel
public static implicit operator AIModel(string modelId) => new AIModel(modelId);
public override string ToString() => _value;
}
// All three of these compile and work:
GPAL.AI.WithModel(AIModel.Claude35Sonnet); // named constant
GPAL.AI.WithModel("claude-3-haiku-20240307"); // freehand string literal
GPAL.AI.WithModel(modelIdFromConfigFile); // freehand string variable
Most closed sets are genuinely closed. A day of the week, a log level. The pattern earns its keep when an external system (an AI provider's model catalog) or a special sentinel (wait forever vs. Wait specific milliseconds) means the set must stay open at one end. WaitTime.Forever, WaitTime.Never, and WaitTime.Immediate read like named timeouts, but GPAL.WithWaitFor(5_000) is equally valid. AIModel works the same: use a curated constant, or pass any new model ID string.
Typing AIModel. Or WaitTime. In the IDE still shows the curated named constants first. Discoverability is preserved, and the freehand path is only needed when a value is not yet on the list.
Showing off some plain text in these paragraphs eligendi laboriosam illo nostrum corporis at libero vel voluptas? Expedita, facere dolores voluptatem ad ab rem assumenda soluta!
Lorem ipsum dolor sit amet consectetur adipisicing elit. Obcaecati, iste distinctio veritatis eligendi laboriosam illo nostrum corporis at libero vel voluptas? Expedita, facere dolores voluptatem ad ab rem assumenda soluta!
Lorem ipsum dolor sit amet consectetur adipisicing elit. Obcaecati, iste distinctio veritatis eligendi laboriosam illo nostrum corporis at libero vel voluptas? Expedita, facere dolores voluptatem ad ab rem assumenda soluta!
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quo veniam mollitia excepturi animi eum illum non libero sapiente provident assumenda, delectus voluptatum nobis sed dolorem adipisci laudantium incidunt. Error, ratione?
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quo veniam mollitia excepturi animi eum illum non libero sapiente provident assumenda, delectus voluptatum nobis sed dolorem adipisci laudantium incidunt. Error, ratione?
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quo veniam mollitia excepturi animi eum illum non libero sapiente provident assumenda, delectus voluptatum nobis sed dolorem adipisci laudantium incidunt. Error, ratione?
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quo veniam mollitia excepturi animi eum illum non libero sapiente provident assumenda, delectus voluptatum nobis sed dolorem adipisci laudantium incidunt. Error, ratione?
Here you can find different accents and emphasis sit amet consectetur adipisicing elit. Obcaecati, iste distinctio veritatis eligendi laboriosam illo nostrum corporis at libero vel voluptas? Expedita, facere dolores voluptatem ad ab rem assumenda soluta!
This is a link and how it could look like bestlinkinthebeautifulworld. Obcaecati, iste distinctio veritatis eligendi laboriosam illo nostrum corporis at libero vel voluptas? Expedita, facere dolores voluptatem ad ab rem assumenda soluta!
Here's just some classic bold text adipisicing elit. Obcaecati, iste distinctio veritatis eligendi laboriosam notBoldSecondbestlinkinthebeautifulworld illo nostrum corporis at libero vel voluptas? Expedita, facere dolores voluptatem ad ab rem assumenda soluta!
Obcaecati, iste distinctio veritatis eligendi laboriosam adipisicing elit illo nostrum corporis at adipisicing elit libero vel voluptas? Expedita, adipisicing facere dolores voluptatem ad ab rem assumenda soluta!
Other cuple of colors in case we want to emphasize several ways adipisicing elit. Obcaecati, iste distinctio veritatis eligendi laboriosam adipisicing elit illo nostrum corporis at voluptatem libero vel voluptas? Expedita, facere dolores voluptatem ad ab rem assumenda soluta!
Lorem ipsum dolor sit amet consectetur adipisicing elit. Obcaecati, iste distinctio veritatis eligendi laboriosam illo nostrum corporis at libero vel voluptas? Expedita, facere dolores voluptatem ad ab rem assumenda soluta! Lorem ipsum dolor, sit amet consectetur adipisicing elit. Quod veniam, quam ad expedita laborum sed at voluptates culpa ipsam ut vel. Ullam temporibus a mollitia quod aliquam ratione exercitationem nesciunt.
Lorem ipsum dolor sit amet consectetur adipisicing elit. Obcaecati, iste distinctio veritatis eligendi laboriosam illo nostrum corporis at libero vel voluptas? Expedita, facere dolores voluptatem ad ab rem assumenda soluta! Lorem ipsum dolor, sit amet consectetur adipisicing elit. Quod veniam, quam ad expedita laborum sed at voluptates culpa ipsam ut vel. Ullam temporibus a mollitia quod aliquam ratione exercitationem nesciunt.
Lorem ipsum dolor sit amet consectetur adipisicing elit. Obcaecati, iste distinctio veritatis eligendi laboriosam illo nostrum corporis at libero vel voluptas? Expedita, facere dolores voluptatem ad ab rem assumenda soluta!
Lorem ipsum dolor sit amet consectetur adipisicing elit. Repudiandae quas consequuntur illo numquam assumenda autem exercitationem distinctio perspiciatis in natus. Eius dicta similique ipsam ipsa minima, nemo quae enim tempore.
GPAL
.CallIfNotFound(GenericCallIfNotFound)
.WithPublishToConsole();
//System.Drawing.Rectangle windowSize = new System.Drawing.Rectangle(10, 10, 1500, 1024);
// NOTE: we have to set browser = before we execute any steps
// this is due to the 'GenericCallIfNotFound' which might throw an exception, and BankScraper will not have the browser set when it calls scraper.Close()
// until the complete fluent line gets executed (meaning every step, meaning browser is not set until everything else succeeds)
browser = GPAL.Browser
.WithBrowserType(Enums.BrowserType.Chrome)
.WithProfileDataDirectory(ChromeProfileLocation)
.WithUseAutomationEngine(AutomationEngine.Selenium)
.WithWindowSize(new System.Drawing.Rectangle(0,0,1920,1080))
.ToGPALObject();