use pretty_assertions::assert_eq; use php_codegen::attribute::AttributeGroup; use php_codegen::class::Class; use php_codegen::comment::Document; use php_codegen::constant::ClassConstant; use php_codegen::constant::Constant; use php_codegen::data_type::DataType; use php_codegen::enum_case::EnumCase; use php_codegen::file::File; use php_codegen::function::Function; use php_codegen::interface::Interface; use php_codegen::literal::Value; use php_codegen::method::Method; use php_codegen::modifiers::Modifier; use php_codegen::modifiers::VisibilityModifier; use php_codegen::parameter::Parameter; use php_codegen::property::Property; use php_codegen::property::PropertyHook; use php_codegen::property::PropertySetHookParameter; use php_codegen::r#enum::Enum; use php_codegen::r#trait::Trait; use php_codegen::usage::Usage; use php_codegen::Indentation; #[test] fn test_code_generation() { let file = File::new() .namespaced("Foo") .declare("strict_types", 1) .uses("Foo\\Bar\\Baz as Qux") .uses("Throwable") .uses("DateTime") .uses("DateTimeImmutable") .uses_function("strlen") .uses_function("array_map") .uses_function("array_filter") .uses_constant("Foo\\HELLO") .uses_constant("Foo\\HEY") .constant(Constant::new("A").valued("Hello World!")) .constant(Constant::new("B").valued(())) .constant(Constant::new("C").valued(1)) .constant(Constant::new("D").valued(false)) .constant(Constant::new("E")) .constant(("F", 213.412)) .constant(("G", vec![1, 25])) .function( Function::new("hello") .document( Document::new() .text("This is a simple hello function.") .empty_line() .tag("param", "non-empty-string $firstname") .empty_line() .tag("return", "string") .empty_line() .simple_tag("pure"), ) .attributes(AttributeGroup::new().add("Qux", Some("foo: 1, bar: 2"))) .parameter( Parameter::new("firstname") .typed(DataType::String) .attributes( AttributeGroup::new() .add("Validation\\NotBlank", None) .add("Validation\\Length", Some("min: 2, max: 10")), ), ) .parameter( Parameter::new("lastname") .typed(DataType::String) .default(Value::Literal("Qux::Foo".to_string())), ) .returns(DataType::String) .body("return 'Hello ' . $firstname . ' ' . $lastname . '!';"), ) .function(Function::new("nothing").returns(DataType::Void)) .function( Function::new("format") .parameter(Parameter::new("template").typed(DataType::String)) .parameter( Parameter::new("args") .variadic() .typed(DataType::Union(vec![ DataType::Integer, DataType::Float, DataType::String, DataType::Null, ])), ) .returns(DataType::String) .body(vec![ "return sprintf($template, ...array_map(", " static fn ($arg) => is_float($arg) ? number_format($arg, 2) : $arg,", " array_filter($args, static fn ($arg) => $arg !== null)", "));", ]), ) .class( Class::new("Example") .extends("Foo\\Bar\\Baz") .implements("Foo\\Bar\\BazInterface") .document( Document::new() .text("This is an example class.") .empty_line() .simple_tag("immutable"), ) .modifier(Modifier::Abstract) .using("A") .using(vec!["B", "C"]) .using( Usage::new(vec![ "D".to_string(), "E".to_string(), "F".to_string(), "G".to_string(), ]) .rename("E::bar", "baz") .alias("D::foo", "bar", VisibilityModifier::Public) .public("E::qux") .protected("D::format") .private("D::d") .precede("D::drop", vec!["E"]) .precede("G::something", vec!["E", "F", "D"]) .visibility("E::e", VisibilityModifier::Protected), ) .constant( ClassConstant::new("A") .valued("Hello World!") .modifier(Modifier::Final), ) .constant(ClassConstant::new("B").valued(()).protected()) .constant(ClassConstant::new("C").valued(1).private()) .constant(ClassConstant::new("D").valued(false).public()) .constant( ClassConstant::new("E") .typed(DataType::Boolean) .valued(false) .public(), ) .property(Property::new("foo").typed(DataType::String).private()) .property(Property::new("bar").typed(DataType::String).protected()) .property( Property::new("baz") .typed(DataType::Union(vec![DataType::String, DataType::Integer])) .public() .default("Hello World!"), ) .method( Method::new("hello") .returns(DataType::String) .parameter(Parameter::new("firstname").typed(DataType::String)) .parameter( Parameter::new("lastname") .typed(DataType::String) .default(Value::Literal("Qux::Foo".to_string())), ) .body("return 'Hello ' . $firstname . ' ' . $lastname . '!';") .attributes( AttributeGroup::new() .add("Qux", Some("foo: 1, bar: 2")) .add("Qux", Some("foo: 1, bar: 2")), ) .document( Document::new() .text("This is a simple hello function.") .empty_line() .tag("param", "non-empty-string $firstname") .empty_line() .tag("return", "string") .empty_line() .simple_tag("pure"), ), ) .method( Method::new("x") .public() .returns(DataType::Mixed) .body("return 'Hello!';") .attributes( AttributeGroup::new() .add("Foo", Some("foo: 1, bar: 2")) .add("Bar", Some("foo: 1, bar: 2")), ) .attributes(AttributeGroup::new().add("Baz", None).add("Qux", None)) .document( Document::new() .text("This is a simple x function.") .empty_line() .simple_tag("pure"), ), ) .method( Method::new("poop") .public() .modifier(Modifier::Abstract) .returns(DataType::Void) .document(Document::new().text("This is a simple poop function.")), ) .method( Method::new("helloWorld") .public() .modifier(Modifier::Final) .returns(DataType::Void) .document(Document::new().text("This is a simple echo function.")) .body(|indentation: Indentation, level| { indentation.indent("echo 'Hello World!';", level) }), ), ) .class( Class::new("SimpleUser") .property( Property::new("firstName") .typed(DataType::String) .visibility(VisibilityModifier::Private) .default(Value::String("Jane".to_string())), ) .property( Property::new("lastName") .typed(DataType::String) .visibility(VisibilityModifier::Private) .default(Value::String("Doe".to_string())), ) .property( Property::new("fullname") .typed(DataType::String) .visibility(VisibilityModifier::Public) .hook(PropertyHook::Get( false, vec!["return $this->firstName . ' ' . $this->lastName;"].into(), )) .hook(PropertyHook::Set( Some( PropertySetHookParameter::new("$fullname").typed(DataType::String), ), vec![ "[$first, $last] = explode(' ', $fullname);", "$this->firstName = $first;", "$this->lastName = $last;", ] .into(), )), ), ) .class( Class::new("SimpleUser2") .property( Property::new("firstName") .typed(DataType::String) .visibility(VisibilityModifier::Private) .default(Value::String("Jane".to_string())), ) .property( Property::new("lastName") .typed(DataType::String) .visibility(VisibilityModifier::Private) .default(Value::String("Doe".to_string())), ) .property( Property::new("fullname") .typed(DataType::String) .visibility(VisibilityModifier::Public) .hook(PropertyHook::Get( false, vec!["return $this->firstName . ' ' . $this->lastName;"].into(), )), ), ) .r#trait( Trait::new("ExampleTrait") .document(Document::new().text("This is an example trait.")) .using("A") .using(vec!["B", "C"]) .using( Usage::new(vec![ "D".to_string(), "E".to_string(), "F".to_string(), "G".to_string(), ]) .rename("E::bar", "baz") .alias("D::foo", "bar", VisibilityModifier::Public) .public("E::qux") .protected("D::format") .private("D::d") .precede("D::drop", vec!["E"]) .precede("G::something", vec!["E", "F", "D"]) .visibility("E::e", VisibilityModifier::Protected), ) .property(Property::new("foo").typed(DataType::String).private()) .property(Property::new("bar").typed(DataType::String).protected()) .property( Property::new("baz") .typed(DataType::Union(vec![DataType::String, DataType::Integer])) .public() .default("Hello World!"), ) .method( Method::new("hello") .returns(DataType::String) .parameter(Parameter::new("firstname").typed(DataType::String)) .parameter( Parameter::new("lastname") .typed(DataType::String) .default(Value::Literal("Qux::Foo".to_string())), ) .body("return 'Hello ' . $firstname . ' ' . $lastname . '!';") .attributes( AttributeGroup::new() .add("Qux", Some("foo: 1, bar: 2")) .add("Qux", Some("foo: 1, bar: 2")), ) .document( Document::new() .text("This is a simple hello function.") .empty_line() .tag("param", "non-empty-string $firstname") .empty_line() .tag("return", "string") .empty_line() .simple_tag("pure"), ), ) .method( Method::new("x") .public() .returns(DataType::Mixed) .body("return 'Hello!';") .attributes( AttributeGroup::new() .add("Foo", Some("foo: 1, bar: 2")) .add("Bar", Some("foo: 1, bar: 2")), ) .attributes(AttributeGroup::new().add("Baz", None).add("Qux", None)) .document( Document::new() .text("This is a simple x function.") .empty_line() .simple_tag("pure"), ), ) .method( Method::new("poop") .public() .modifier(Modifier::Abstract) .returns(DataType::Void) .document(Document::new().text("This is a simple poop function.")), ) .method( Method::new("helloWorld") .public() .modifier(Modifier::Final) .returns(DataType::Void) .document(Document::new().text("This is a simple echo function.")) .body(|indentation: Indentation, level| { indentation.indent("echo 'Hello World!';", level) }), ), ) .r#enum( Enum::new("ExampleUnitEnum") .document(Document::new().text("This is an example unit enum.")) .using("A") .using(vec!["B", "C"]) .using( Usage::new(vec![ "D".to_string(), "E".to_string(), "F".to_string(), "G".to_string(), ]) .rename("E::bar", "baz") .alias("D::foo", "bar", VisibilityModifier::Public) .public("E::qux") .protected("D::format") .private("D::d") .precede("D::drop", vec!["E"]) .precede("G::something", vec!["E", "F", "D"]) .visibility("E::e", VisibilityModifier::Protected), ) .case(EnumCase::new("Foo").document(Document::new().text("This is a foo case."))) .case(EnumCase::new("Bar").document(Document::new().text("This is a bar case."))) .case("Baz"), ) .r#enum( Enum::new("ExampleStringBackEnum") .document(Document::new().text("This is an example string backed enum.")) .string_backed() .case( EnumCase::new("Foo") .document(Document::new().text("This is a foo case.")) .valued("foo value"), ) .case( EnumCase::new("Bar") .document(Document::new().text("This is a bar case.")) .valued("bar value"), ) .case(("Baz", "baz value")), ) .interface( Interface::new("Formatter") .document( Document::new() .text("This is a simple formatter interface.") .empty_line() .simple_tag("immutable"), ) .attributes( AttributeGroup::new() .add("Foo", Some("foo: 1, bar: 2")) .add("Bar", Some("foo: 1, bar: 2")), ) .extends("Foo") .extends("Bar") .extends("Qux") .method( Method::new("format") .parameter(Parameter::new("template").typed(DataType::String)) .parameter( Parameter::new("args") .variadic() .typed(DataType::Union(vec![ DataType::Integer, DataType::Float, DataType::String, DataType::Null, ])), ) .returns(DataType::String), ), ); assert_eq!(include_str!("complete.php"), file.to_string(),); }